/* ==========================================================================
   Novus website — V2 IMAGERY LAYER  (additive; loaded AFTER site.css)
   Purpose: give the text-heavy P2 site visual impact through a disciplined
   photography + figure system. This file ONLY ADDS components; it never
   restyles v1 classes destructively. Every value derives from tokens.css
   role tokens — no raw colours. Mobile-first, like site.css.

   Canon alignment (Novus_Context.md / tokens.css §13-H):
     • FIGURES ARE FRAMED (--neutral-300 hairline), logos are UNBOXED.
     • IMAGES NEVER STRETCH — object-fit:cover/contain, explicit box.
     • Photographic grounds carry a SCRIM + the WHITE logo (handled in markup).
     • No hover on non-clickable media; hover-lift only on clickable cards.
     • Section band alternation stays intact (this file adds no section grounds).

   PROPOSED tokens (see V2_AUDIT.md → ratify into all 3 canon sources):
     --ratio-wide 16/9 · --ratio-photo 4/3 · --ratio-portrait 3/4
     --scrim-photo (bottom gradient) · --duotone-accent (brand wash)
   Until ratified they live here as LOCAL fallbacks so v1/canon stay untouched.
   ========================================================================== */

:root{
  /* local fallbacks — mirror the proposed canon tokens 1:1 */
  --v2-ratio-wide:16 / 9;
  --v2-ratio-photo:4 / 3;
  --v2-ratio-portrait:3 / 4;
  --v2-ratio-square:1 / 1;
  --v2-scrim-photo:linear-gradient(to top, rgba(14,35,54,.82) 0%, rgba(14,35,54,.45) 34%, rgba(14,35,54,0) 68%);
  --v2-frame:1px solid var(--neutral-300);
}

/* ---------- 1. FRAMED MEDIA (real photo) --------------------------------- */
/* figure.media wraps a real <img>. Framed hairline + clipped radius; the img
   fills the box with object-fit:cover so it can never distort. */
.media{margin:0;border:var(--v2-frame);border-radius:var(--radius-lg);
  overflow:hidden;background:var(--bg-subtle);position:relative;
  aspect-ratio:var(--ratio, var(--v2-ratio-photo));}
.media>img,.media>picture>img{width:100%;height:100%;object-fit:cover;display:block;}
.media--wide{--ratio:var(--v2-ratio-wide);}
.media--photo{--ratio:var(--v2-ratio-photo);}
.media--portrait{--ratio:var(--v2-ratio-portrait);}
.media--square{--ratio:var(--v2-ratio-square);}
.media--auto{aspect-ratio:auto;}
/* optional caption under a figure — reads as a hairline-lifted note */
.media__cap{display:block;margin-top:var(--space-2);font-size:var(--text-sm);
  color:var(--text-secondary);line-height:1.4;}

/* Brand duotone: a soft accent wash so third-party / stock photography
   harmonises with the deep-blue system. Kept subtle (mastery without drama). */
.media--duotone::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:var(--accent);opacity:.10;mix-blend-mode:multiply;}
[data-theme="dark"] .media--duotone::after{opacity:.22;}

/* Scrim for text/logo over a photo (canon: dark gradient + white logo). */
.media--scrim::before{content:"";position:absolute;inset:0;z-index:1;
  background:var(--v2-scrim-photo);pointer-events:none;}
.media__overlay{position:absolute;z-index:2;left:0;right:0;bottom:0;
  padding:var(--space-5);color:#fff;display:flex;flex-direction:column;gap:var(--space-1);}
.media__overlay .eyebrow{color:rgba(255,255,255,.82);}
.media__overlay h2,.media__overlay h3,.media__overlay p{color:#fff;margin:0;}

/* ---------- 2. IMAGE PLACEHOLDER (drop-in slot) -------------------------- */
/* figph = "figure placeholder": a framed, clearly-labelled zone that TELLS the
   author what image belongs here. Swap to a real image by replacing the inner
   markup with  <img src="assets/img/<id>.jpg" alt="…">  (keep the figure).   */
.figph{margin:0;position:relative;display:flex;flex-direction:column;
  align-items:center;justify-content:center;text-align:center;gap:var(--space-2);
  padding:var(--space-5);border:2px dashed var(--neutral-300);
  border-radius:var(--radius-lg);background:var(--bg-subtle);color:var(--text-secondary);
  aspect-ratio:var(--ratio, var(--v2-ratio-photo));overflow:hidden;}
.figph::before{content:"";position:absolute;inset:0;pointer-events:none;
  background:repeating-linear-gradient(-45deg,transparent 0 22px,
    color-mix(in srgb,var(--neutral-300) 22%,transparent) 22px 24px);opacity:.5;}
.figph>*{position:relative;z-index:1;}
.figph--wide{--ratio:var(--v2-ratio-wide);}
.figph--photo{--ratio:var(--v2-ratio-photo);}
.figph--portrait{--ratio:var(--v2-ratio-portrait);}
.figph--square{--ratio:var(--v2-ratio-square);}
.figph--tall{--ratio:var(--v2-ratio-portrait);}
.figph__icon{width:2.4rem;height:2.4rem;color:var(--accent-text);opacity:.9;
  fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;}
.figph__tag{font-size:var(--text-sm);letter-spacing:.14em;text-transform:uppercase;
  font-weight:var(--weight-bold);color:var(--accent-text);}
.figph__desc{font-size:var(--text-base);color:var(--text);max-width:46ch;line-height:1.45;}
.figph__desc b{color:var(--text);}
.figph__meta{font-size:var(--text-sm);color:var(--text-secondary);max-width:52ch;}
.figph__meta code{font-family:var(--font-mono);font-size:.9em;
  background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-sm);
  padding:.02em .4em;color:var(--accent-text);}

/* ---------- 3. SPLIT: media beside copy --------------------------------- */
/* Mobile-first single column; ≥900px two columns. --media-right flips order. */
.split{display:grid;grid-template-columns:1fr;gap:var(--space-5);align-items:center;}
.split__media{align-self:stretch;min-width:0;}
.split__media .media,.split__media .figph{height:100%;}
@media (min-width:900px){
  .split{grid-template-columns:1.05fr .95fr;gap:var(--space-7);}
  .split--media-right .split__copy{order:1;} .split--media-right .split__media{order:2;}
  .split__media .media,.split__media .figph{aspect-ratio:var(--ratio, var(--v2-ratio-photo));}
}

/* ---------- 4. CARD MEDIA (Solutions & Perspectives) -------------------- */
/* A media zone at the TOP of a clickable card. The card already lifts on hover
   (v1 .card--link) — the affordance rule stays satisfied. Image bleeds to the
   card edges via negative margins over the card padding. */
.card--media{padding-top:0;overflow:hidden;}
.card__media{position:relative;margin:0 calc(-1 * var(--space-5)) var(--space-4);
  aspect-ratio:var(--ratio,16 / 10);overflow:hidden;background:var(--bg-subtle);
  border-bottom:1px solid var(--border);}
.card__media>img{width:100%;height:100%;object-fit:cover;display:block;}
.card__media.figph{border:0;border-bottom:2px dashed var(--neutral-300);border-radius:0;padding:var(--space-4);}
.card__media.figph{aspect-ratio:var(--ratio,16 / 10);}
/* subtle accent hairline at the top of a product card's image, echoing its theme */
.card--media .card__media::after{content:"";position:absolute;left:0;right:0;top:0;height:3px;
  background:var(--accent);opacity:.9;}

/* ---------- 5. LOGO STRIP (client proof / connectivity) ----------------- */
/* Canon: client + scheme logos shown FULL COLOUR on white tiles, non-clickable,
   NO hover. Compact, dense. */
.logostrip{--logo-h:30px;}
.logostrip__label{font-size:var(--text-sm);letter-spacing:.1em;text-transform:uppercase;
  color:var(--text-secondary);font-weight:var(--weight-bold);margin:0 0 var(--space-3);}
.logostrip__row{display:grid;gap:var(--space-3);
  grid-template-columns:repeat(auto-fill,minmax(96px,1fr));align-items:stretch;}
.logostrip__tile{display:flex;align-items:center;justify-content:center;
  background:#fff;border:1px solid var(--neutral-200);border-radius:var(--radius-md);
  padding:.55rem .7rem;min-height:52px;}
.logostrip__tile img{max-width:100%;max-height:var(--logo-h);width:auto;object-fit:contain;}
.logostrip--schemes{--logo-h:24px;}
.logostrip--schemes .logostrip__row{grid-template-columns:repeat(auto-fill,minmax(74px,1fr));}

/* ---------- 6. FEATURE BAND (full-bleed photo moment) ------------------- */
/* One high-impact photographic moment (e.g. Proven impact). Photo ground →
   white text on scrim. Non-clickable, so no hover. */
.feature{position:relative;border-radius:var(--radius-xl);overflow:hidden;
  border:var(--v2-frame);background:var(--blue-900);isolation:isolate;
  min-height:clamp(320px,52vw,460px);display:flex;align-items:flex-end;}
.feature>img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0;}
.feature.figph{border:2px dashed var(--neutral-300);align-items:center;justify-content:center;}
.feature__scrim{position:absolute;inset:0;z-index:1;background:var(--v2-scrim-photo);}
.feature__body{position:relative;z-index:2;padding:clamp(var(--space-5),5vw,var(--space-7));
  color:#fff;max-width:64ch;}
.feature__body .eyebrow{color:rgba(255,255,255,.82);}
.feature__body h2,.feature__body h3,.feature__body p{color:#fff;}
.feature__stats{display:grid;grid-template-columns:repeat(2,auto);gap:var(--space-4) var(--space-6);
  margin-top:var(--space-4);}
@media (min-width:700px){.feature__stats{grid-template-columns:repeat(4,auto);}}
.feature__stats .stat__value{color:#fff;}
.feature__stats .stat__label{color:rgba(255,255,255,.78);}

/* ---------- 7. GALLERY / FIGURE ROW ------------------------------------- */
.figrow{display:grid;grid-template-columns:1fr;gap:var(--space-4);}
@media (min-width:600px){.figrow--2{grid-template-columns:repeat(2,1fr);}
  .figrow--3{grid-template-columns:repeat(2,1fr);}}
@media (min-width:900px){.figrow--3{grid-template-columns:repeat(3,1fr);}
  .figrow--4{grid-template-columns:repeat(4,1fr);}}

/* ---------- 8. SMALL POLISH (v2 impact tweaks, additive) ---------------- */
/* Eyebrow gets a short accent tick before the label — sharpens section starts. */
.section .wrap>.eyebrow::before{content:"";display:inline-block;width:1.4rem;height:2px;
  background:var(--accent);vertical-align:middle;margin-right:.55rem;
  transform:translateY(-.18em);}
/* Let a lede breathe a touch more on wide screens for readability. */
@media (min-width:900px){.lede{line-height:1.62;}}
/* Reduced-motion & dark handled by tokens; duotone opacity already stepped. */

/* Why-Novus reason icons — small line marks for scannability (info tiles, no hover). */
.why-icon{width:1.9rem;height:1.9rem;color:var(--accent-text);fill:none;stroke:currentColor;
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;margin-bottom:var(--space-2);display:block;}

/* ==========================================================================
   V2.1 — IMAGERY RESTRAINT PASS (2026-08-13)
   Photography becomes punctuation, not substrate. Measured band occupancy
   38.9% -> ~21% (editorial/B2B norm is 15-25%; NN/g/web.dev-grounded).
   Additive only; every value derives from tokens.css role tokens.
   tokens.css itself is LOCKED and untouched.
   ========================================================================== */
:root{
  --v2-ratio-cinema:21 / 9;   /* content-width figures + the single anchor */
  --v2-ratio-band:3 / 2;      /* split / inset figures                     */
  --v2-ratio-thumb:2 / 1;     /* card thumbnails                           */
  --v2-h-anchor:460px;        /* height ceilings = the busy-ness governor  */
  --v2-h-split:340px;
  --v2-h-card:240px;
  --v2-h-thumb:180px;
}
/* T1 ANCHOR — one full-width photographic moment, cinematic not 16:9 */
.media--wide{--ratio:var(--v2-ratio-cinema);max-height:var(--v2-h-anchor);}
/* T1-demote / inset — can never rival the anchor */
.media--inset{--ratio:var(--v2-ratio-cinema);max-width:900px;margin-inline:auto;max-height:386px;}
/* SPLIT figures — shorter, and stop the copy floating beside a tall image */
@media (min-width:900px){
  .split{align-items:start;}
  .split__media .media,.split__media .figph{
    aspect-ratio:var(--ratio, var(--v2-ratio-band));max-height:var(--v2-h-split);}
}
/* CARD MEDIA — thumbnail by default; --lg for cover/case cards */
.card__media{--ratio:var(--v2-ratio-thumb);max-height:var(--v2-h-thumb);}
.card__media--lg{--ratio:16 / 9;max-height:var(--v2-h-card);}
/* accent tick: 3px across six stacked cards reads as noise */
.card--media .card__media::after{height:2px;opacity:.85;}
/* HIERARCHY — card thumbs sit inside an already-framed card; drop the double frame */
.card__media{border-radius:0;}
.card--media .card__media{border-bottom:1px solid var(--border);}
/* FEATURE BAND — shorter, and a scrim that actually reaches the copy (WCAG 1.4.3) */
.feature{min-height:clamp(240px,24vw,340px);}
.feature__scrim{background:linear-gradient(to top,
  rgba(14,35,54,.92) 0%, rgba(14,35,54,.78) 45%,
  rgba(14,35,54,.52) 75%, rgba(14,35,54,.34) 100%);}
/* GALLERY ROWS — spec'd so the rhythm can't regress if used later */
.figrow--2 .media{--ratio:var(--v2-ratio-band);max-height:220px;}
.figrow--3 .media{--ratio:var(--v2-ratio-band);max-height:180px;}
.figrow--4 .media{--ratio:var(--v2-ratio-square);max-height:150px;}
/* Deep product sections no longer carry a duplicated photo — let the intro
   span the full column instead of reserving the old two-column art slot. */
@media (min-width:900px){
  .ppage__head{grid-template-columns:1fr;}
  .ppage__head .lede{max-width:72ch;}
}

/* --------------------------------------------------------------------------
   FEATURE BAND — brand ground (replaces the third-party ATM Go poster).
   The old asset was RCBC marketing creative: four third-party marks, baked-in
   ad copy, and 2.68:1 contrast under the white overlay text (WCAG 1.4.3 fail).
   A token-derived deep-blue ground gives 9.85:1–16:1 for white text and keeps
   the stats as the content. No photograph is needed to close this section.
   -------------------------------------------------------------------------- */
.feature--brand{
  background:
    radial-gradient(120% 140% at 82% 8%, color-mix(in srgb,var(--blue-600) 46%, transparent) 0%, transparent 58%),
    radial-gradient(90% 120% at 8% 100%, color-mix(in srgb,var(--blue-700) 40%, transparent) 0%, transparent 62%),
    linear-gradient(135deg, var(--blue-900) 0%, var(--blue-800) 52%, var(--blue-700) 100%);
  border-color:color-mix(in srgb,var(--blue-700) 60%, transparent);
}
/* faint hairline grid — texture without noise; hidden from AT and print */
.feature--brand::before{
  content:"";position:absolute;inset:0;z-index:1;pointer-events:none;opacity:.16;
  background-image:
    linear-gradient(to right, rgba(255,255,255,.55) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255,255,255,.35) 1px, transparent 1px);
  background-size:96px 100%, 100% 72px;
  -webkit-mask-image:radial-gradient(90% 90% at 78% 12%, #000 0%, transparent 72%);
  mask-image:radial-gradient(90% 90% at 78% 12%, #000 0%, transparent 72%);
}
.feature--brand .feature__body{position:relative;z-index:2;}
@media print{.feature--brand::before{display:none;}}
/* With no photograph to sit clear of, the brand band can use its full width. */
.feature--brand .feature__body{max-width:none;}
@media (min-width:700px){
  .feature--brand .feature__stats{grid-template-columns:repeat(4,minmax(0,1fr));
    gap:var(--space-4) var(--space-5);max-width:78ch;}
}

/* --------------------------------------------------------------------------
   PROVEN-IMPACT CASE CARDS — brand ground instead of client photography.
   Rick 2026-08-13: RCBC + People's Bank cards keep the gradient. Removes the
   last client-branded photography from the page and matches .feature--brand,
   so the whole "proof" family reads as one system. The stats are the proof.
   -------------------------------------------------------------------------- */
.card__media--brand{
  aspect-ratio:auto;height:112px;max-height:112px;width:auto;display:block;
  position:relative;border-bottom:1px solid var(--border);
  background:
    radial-gradient(120% 160% at 85% 0%, color-mix(in srgb,var(--blue-600) 42%, transparent) 0%, transparent 60%),
    linear-gradient(135deg, var(--blue-900) 0%, var(--blue-800) 55%, var(--blue-700) 100%);
}
.card__media--brand::before{
  content:"";position:absolute;inset:0;opacity:.15;pointer-events:none;
  background-image:
    linear-gradient(to right, rgba(255,255,255,.55) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255,255,255,.35) 1px, transparent 1px);
  background-size:64px 100%, 100% 48px;
  -webkit-mask-image:radial-gradient(85% 95% at 80% 10%, #000 0%, transparent 70%);
  mask-image:radial-gradient(85% 95% at 80% 10%, #000 0%, transparent 70%);
}

/* ==========================================================================
   V2.2 — ONE PICTURE STYLE: the narrow, full-bleed top band (Rick 2026-08-13)
   "Follow the narrow top band style ... cover the entire box left to right
    with no gap ... less visual space and less intrusive."

   ROOT CAUSE of the gap: `aspect-ratio` + a clamped `max-height` makes the
   browser derive WIDTH from the constrained height (240px x 16/9 = 427px on a
   566px card), so the band stopped short of the card edge. Fixed height with
   no aspect-ratio lets the block fill its box edge to edge.
   ========================================================================== */
:root{ --v2-band-card:168px; --v2-band-split:270px; }

.card__media,
.card__media--lg,
.card__media--brand{
  aspect-ratio:auto;
  height:var(--v2-band-card);
  max-height:var(--v2-band-card);
  width:auto;              /* block fills the card; negative margins bleed it */
  display:block;
  overflow:hidden;
}
.card__media>img{width:100%;height:100%;object-fit:cover;object-position:center 42%;display:block;}
/* subjects sit slightly high in the ops/NOC and card-tap frames */
.card__media>img[src*="wp-delivery-channel"],
.card__media>img[src*="wp-payment-card"]{object-position:center 50%;}

/* Split figures adopt the same restraint — the 4:3 blocks were the tallest
   remaining pictures on the page. */
@media (min-width:900px){
  .split__media .media{
    aspect-ratio:auto;height:var(--v2-band-split);max-height:var(--v2-band-split);}
}

/* ==========================================================================
   V2.3 — THE BAND LANGUAGE APPLIED TO *EVERY* PICTURE (Rick 2026-08-13)
   "Still oversized ... or not filling left to right. Either make the picture
    smaller next to the text, or a vertically narrow picture stretching side
    to side, not occupying too much space."

   Two shapes only, no exceptions:
     A. FULL-WIDTH BAND  — edge to edge in the content column, ~280-300px.
     B. COMPACT SIDE FIGURE — top-aligned beside the copy, ~210px, never
        taller than the text block it accompanies.
   ========================================================================== */
:root{ --v2-band-wide:300px; --v2-band-inset:280px; --v2-band-side:210px; }

/* A — the anchor: cinematic, edge to edge (was 21:9 -> 646px tall at 1150px) */
.media--wide{
  aspect-ratio:auto;height:var(--v2-band-wide);max-height:var(--v2-band-wide);
  width:auto;display:block;
}
/* A — the platform figure was inset to 900px, so it never reached the edges */
.media--inset{
  max-width:none;margin-inline:0;
  aspect-ratio:auto;height:var(--v2-band-inset);max-height:var(--v2-band-inset);
  width:auto;display:block;
}
/* B — split figures sit BESIDE the copy, topped-aligned and short, so the
      column no longer towers over a three-line paragraph. */
@media (min-width:900px){
  .split{align-items:start;}
  .split__media{align-self:start;}
  .split__media .media,
  .split__media .figph{
    aspect-ratio:auto;height:var(--v2-band-side);max-height:var(--v2-band-side);
    width:auto;display:block;}
}
/* Keep the subject centred in every band crop. */
.media>img,.media>picture>img{object-position:center 45%;}
/* Contact: cut sky, reveal Marina Bay water + the full ArtScience Museum,
   without truncating Marina Bay Sands. */
.split__media .media>img[src*="closing-singapore"]{object-position:center 75%;}

/* Who-we-are anchor: crop so the FULL seven-market list reads. Centre-cropping
   this frame truncates the list mid-way ("Sri Lanka / Botswana" only), which
   looks like an accident rather than a claim. */
/* The NOVUS lockup is painted across 29.7%-60.5% of ops-floor.jpg (measured by
   sampling saturated rows - the photos behind it are greyscale, the lettering is
   not). At 78% the crop window sat at 34.7%-90.2% of the image, so the tops of
   the letters were sliced off on desktop. Phones never showed it because the
   figure takes the image's own ratio there, which leaves no overflow for
   object-position to act on.
   Valid range at the widest box (1152px) is 11.2%-66.8%; 39% centres the lockup
   and keeps it clear at 720px and 976px too. */
.media--wide>img[src*="ops-floor"]{object-position:center 39%;}
/* Platform band: crop to the payment moment — both faces, the phone-to-terminal
   tap and the connected-service icons all read at 280px. */
.media--inset>img[src*="platform"]{object-position:center 60%;}
/* New platform visual: centre the convergence node so channels (left) and
   processing/security/ops (right) both read inside the 280px band. */
.media--inset>img[src*="platform"]{object-position:center 55%;}

/* Anchor section: heading + copy live INSIDE the split so the photo tops out
   level with the eyebrow instead of starting below the lede. */
.split--flush{margin-top:0;}
.section .split__copy>.eyebrow::before{content:"";display:inline-block;width:1.4rem;height:2px;
  background:var(--accent);vertical-align:middle;margin-right:.55rem;transform:translateY(-.18em);}
.split__copy>h2{margin-top:0;}
/* novaai card: lift the crop so the whole AI graphic and its five capability
   labels sit inside the 168px band (a centre crop clips them at the top). */
.card__media>img[src*="novaai"]{object-position:center 20%;}

/* Hero CTAs: site.css carries a global `.hero .row .btn{width:100%}` (0,3,0)
   that outranks the responsive `.row .btn{width:auto}` (0,2,0), so the two
   buttons stayed full-width and stacked at every breakpoint. Match the
   specificity to let them sit side by side at their natural width. */
@media (min-width:600px){
  .hero .row{gap:var(--space-3);align-items:center;}
  .hero .row .btn{width:auto;flex:0 0 auto;}
}

/* ==========================================================================
   INCLUSION & IMPACT — full-bleed photographic band (Rick 2026-08-14)
   The brand gradient becomes a scrim OVER the six-panel inclusion photo, and
   the band breaks out of the content column to run edge to edge.
   ========================================================================== */
html,body{overflow-x:clip;}          /* 100vw + scrollbar would otherwise scroll */
.feature--brand{
  margin-left:calc(50% - 50vw);
  margin-right:calc(50% - 50vw);
  width:100vw;max-width:100vw;
  border-radius:0;border-left:0;border-right:0;
  background-image:
    linear-gradient(100deg,
      rgba(7,51,85,.955) 0%,
      rgba(7,51,85,.90)  40%,
      rgba(14,35,54,.80) 70%,
      rgba(14,35,54,.72) 100%),
    url("img/impact-band.826d337d.jpg");
  background-size:cover;
  background-position:center 42%;
  background-repeat:no-repeat;
}
.feature--brand::before{display:none;}            /* no hairline grid over a photo */
.feature--brand .feature__body{
  max-width:var(--container);margin-inline:auto;width:100%;
  padding-inline:var(--gutter);
}

/* --------------------------------------------------------------------------
   IMPACT BAND v2 (Rick 2026-08-14): NOT viewport-wide — align it to the same
   column as the RCBC / People's Bank case cards above, and let the photograph
   read through. The scrim now only carries enough weight where the copy sits
   (left ~60%), opening up towards the right so the imagery stays visible.
   -------------------------------------------------------------------------- */
html,body{overflow-x:visible;}              /* breakout no longer used */
.feature--brand{
  margin-left:0;margin-right:0;
  width:auto;max-width:none;
  border-radius:var(--radius-xl);
  border:var(--v2-frame);
  background-image:
    linear-gradient(100deg,
      rgba(7,51,85,.86)  0%,
      rgba(7,51,85,.72) 34%,
      rgba(10,43,70,.50) 58%,
      rgba(14,35,54,.30) 80%,
      rgba(14,35,54,.22) 100%),
    url("img/impact-band.826d337d.jpg");
  background-position:center 45%;
}
.feature--brand .feature__body{
  max-width:none;margin-inline:0;width:auto;
  padding-inline:clamp(var(--space-5),5vw,var(--space-7));
}
/* keep white type crisp where the photo is lighter behind it */
.feature--brand .feature__body .eyebrow,
.feature--brand .feature__body h3,
.feature--brand .feature__body .stat__value,
.feature--brand .feature__body .stat__label{
  text-shadow:0 1px 3px rgba(6,20,33,.55), 0 0 14px rgba(6,20,33,.35);
}

/* --------------------------------------------------------------------------
   CONTACT figure: the skyline is 1.51:1 but the side slot is a 2.5:1 band, so
   any crop had to lose either Marina Bay Sands' roof or the waterfront. Sky is
   now cut from the SOURCE file instead, and the figure adopts the picture's own
   ratio — nothing is truncated. Height stays in side-figure territory.
   -------------------------------------------------------------------------- */
@media (min-width:900px){
  .split__media .media--fit{
    aspect-ratio:1448 / 936;
    height:auto;max-height:236px;
    width:auto;max-width:100%;
    margin-left:auto;                 /* sit to the right edge of its column */
  }
}
.split__media .media--fit>img{object-position:center center;}

/* Impact band: keep the photograph open, but guarantee AA where the copy sits.
   A soft dark wash is anchored to the copy block only (bottom-left), instead of
   flattening the whole image. Labels lift from .78 -> .94 white. */
.feature--brand{
  background-image:
    radial-gradient(115% 165% at 4% 78%,
      rgba(5,18,30,.90) 0%, rgba(5,18,30,.72) 30%,
      rgba(5,18,30,.40) 52%, rgba(5,18,30,0) 72%),
    linear-gradient(100deg,
      rgba(7,51,85,.72)  0%,
      rgba(7,51,85,.56) 34%,
      rgba(10,43,70,.36) 58%,
      rgba(14,35,54,.22) 80%,
      rgba(14,35,54,.16) 100%),
    url("img/impact-band.826d337d.jpg");
}
.feature--brand .feature__stats .stat__label{color:rgba(255,255,255,.94);}
.feature--brand .feature__body .eyebrow{color:rgba(255,255,255,.92);}

/* Impact band v3 (Rick): left side still too dark — swap the heavy bottom-left
   radial for a shallow ~12deg linear scrim. Dark sits under the copy at the
   bottom and clears quickly upward, so the six panels read much more openly. */
.feature--brand{
  background-image:
    linear-gradient(12deg,
      rgba(5,18,30,.88)  0%,
      rgba(6,30,50,.70) 26%,
      rgba(8,40,66,.46) 52%,
      rgba(12,45,72,.24) 76%,
      rgba(14,35,54,.14) 100%),
    linear-gradient(100deg,
      rgba(7,51,85,.34) 0%,
      rgba(7,51,85,.22) 40%,
      rgba(14,35,54,.10) 75%,
      rgba(14,35,54,.06) 100%),
    url("img/impact-band.826d337d.jpg");
  background-position:center 45%;
}

/* Impact band v4: the 12deg angle darkened the bottom, but the eyebrow/headline
   sit higher up, so contrast failed there (3.25:1). A VERTICAL scrim instead —
   fully clear across the top third (photo reads openly, left and right alike)
   and solid only across the band the copy actually occupies. */
.feature--brand{
  background-image:
    linear-gradient(to top,
      rgba(5,18,30,.92)  0%,
      rgba(5,18,30,.90) 42%,
      rgba(6,26,44,.84) 58%,
      rgba(7,36,60,.55) 70%,
      rgba(9,42,68,.24) 82%,
      rgba(12,45,72,.06) 92%,
      rgba(14,35,54,0)  100%),
    url("img/impact-band.826d337d.jpg");
  background-position:center 38%;
}
/* Impact band: lift the crop window toward the top of the photo so the faces in
   the upper row of panels are fully in frame (heads were being cut). */
.feature--brand{background-position:center 8%;}
/* Impact band: lighter scrim again — more of the photography reads through.
   Held above WCAG AA (measured, not eyeballed). */
.feature--brand{
  background-image:
    linear-gradient(to top,
      rgba(5,18,30,.78)  0%,
      rgba(5,18,30,.75) 42%,
      rgba(6,26,44,.66) 58%,
      rgba(7,36,60,.40) 70%,
      rgba(9,42,68,.17) 82%,
      rgba(12,45,72,.04) 92%,
      rgba(14,35,54,0)  100%),
    url("img/impact-band.826d337d.jpg");
}

/* Impact band FINAL scrim (measured sweep, 2026-08-14).
   Fall-off concentrated high in the band: solid only across the copy, clearing
   fast above it. Labels go pure white to buy contrast headroom, which lets the
   scrim sit lighter. P70 is the lightest setting that still clears WCAG AA
   (5.40:1); P64 measured 4.43:1 and was rejected. */
.feature--brand{
  background-image:
    linear-gradient(to top,
      rgba(5,18,30,.70)  0%,
      rgba(5,18,30,.68) 60%,
      rgba(6,26,44,.56) 68%,
      rgba(7,36,60,.22) 76%,
      rgba(9,42,68,.07) 84%,
      rgba(14,35,54,0)  92%),
    url("img/impact-band.826d337d.jpg");
  background-position:center 8%;
}
.feature--brand .feature__stats .stat__label,
.feature--brand .feature__body .eyebrow{color:#fff;}

/* ============================================================================
   V2.4 — social links (footer).  Added 2026-08-17.
   Tokens only; no hardcoded colours.  Reduced-motion respected.
   ========================================================================== */
.social{display:flex;gap:var(--space-2);margin-top:var(--space-3);}
.social__link{
  display:inline-flex;align-items:center;justify-content:center;
  width:40px;height:40px;                      /* >=40px touch target */
  border-radius:var(--radius-md);
  border:1px solid var(--border);
  color:var(--text-muted);
  transition:background-color .18s ease,border-color .18s ease,color .18s ease;
}
.social__link svg{width:22px;height:22px;fill:currentColor;display:block;}
/* The LinkedIn and Facebook marks are KNOCKOUT designs: the letters are holes
   that show the button background through. Filling the button on hover therefore
   destroys the mark. Tint the mark and the border instead, leave the background. */
.social__link:hover,
.social__link:focus-visible{
  border-color:var(--accent);
  color:var(--accent);
}
@media (prefers-reduced-motion:reduce){.social__link{transition:none;}}

/* ============================================================================
   V2.5 — expanding cards must not stretch their neighbours.  Added 2026-08-18.

   `.grid` and `.compose__row` are CSS grids, and a grid item stretches to the
   row height by default.  So opening one <details> grew EVERY sibling to match:
   only one card had content, but all four became tall and empty — which reads
   as "all four boxes opened".

   `.chain` (flex) and `.arch` (grid) already set align-items:start, which is why
   those two sections looked right.  This brings the rest of the family into line.
   ========================================================================== */
.grid > .discard,
.compose__row > .cmp-prod-wrap,
.compose__row > .arch__ai-wrap{align-self:start;}

/* ============================================================================
   V2.6 — accessibility + print.  Added 2026-08-18 (batch 2 of the re-audit).
   Tokens only. v1's site.css is untouched.
   ========================================================================== */

/* --- Focus ring -------------------------------------------------------------
   The base rule set outline-width:0 with a transparent outline, so the visible
   focus indicator measured 1.97:1 against its background — below the 3:1 that
   WCAG 2.2 SC 1.4.11 requires for a non-text indicator. A two-tone ring reads
   on both light and dark grounds without depending on either. */
:where(a, button, summary, label[role="button"], [tabindex]):focus-visible{
  outline:3px solid var(--accent);
  outline-offset:2px;
  border-radius:var(--radius-sm, 4px);
  box-shadow:0 0 0 5px var(--surface, #fff);
}
[data-theme="dark"] :where(a, button, summary, label[role="button"], [tabindex]):focus-visible{
  box-shadow:0 0 0 5px var(--blue-900);
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme]) :where(a, button, summary, label[role="button"], [tabindex]):focus-visible{
    box-shadow:0 0 0 5px var(--blue-900);
  }
}

/* --- Scroll anchoring for exclusive accordions ------------------------------
   Closing a tall panel above the one you just opened used to yank the tapped
   summary up to 462px off-screen. scroll-margin keeps the heading in view. */
details[data-group] > summary{ scroll-margin-top:6rem; }

/* --- Print ------------------------------------------------------------------
   site-v2.js opens every panel before printing; this is the CSS belt-and-braces
   for browsers that print without firing beforeprint, plus general print tidy. */
@media print{
  details > *{ display:revert !important; }
  .appbar, .menu, .menu__backdrop, .theme-toggle, .social, .skip{ display:none !important; }
  .lmodal{ display:none !important; }
  a[href^="http"]::after{ content:" (" attr(href) ")"; font-size:.8em; color:#555; }
  .section, .feature, .card{ break-inside:avoid; }
  h2, h3, h4{ break-after:avoid; }
  body{ background:#fff !important; }
}

/* ============================================================================
   V2.7 — office directory.  Added 2026-08-18.
   Six offices, seven markets — Botswana is run from Singapore (canon).
   ========================================================================== */
.offices{margin-top:var(--space-7);padding-top:var(--space-6);border-top:1px solid var(--border);}
.offices__title{margin:0;}
.offices__lede{margin:var(--space-1) 0 var(--space-5);}
.offices__grid{
  list-style:none;margin:0;padding:0;
  display:grid;gap:var(--space-4);
  grid-template-columns:repeat(auto-fit,minmax(240px,1fr));
}
.office{
  padding:var(--space-4);
  border:1px solid var(--border);
  border-radius:var(--radius-lg);
  background:var(--surface);
}
.office--hq{border-color:var(--accent);}
.office__country{
  margin:0 0 var(--space-1);font-size:var(--text-base);
  display:flex;align-items:center;gap:.5rem;
}
.office__badge{
  font-size:.6rem;letter-spacing:.08em;text-transform:uppercase;
  padding:.15rem .4rem;border-radius:999px;
  background:var(--accent);color:var(--text-on-accent);
}
.office__legal{margin:0 0 var(--space-2);font-size:var(--text-sm);color:var(--text-secondary);}
.office__addr{font-style:normal;font-size:var(--text-sm);color:var(--text-muted);line-height:1.5;}
.office__addr span{display:block;}
.office__tel{display:inline-block;margin-top:var(--space-2);font-size:var(--text-sm);}
.offices__note{margin-top:var(--space-4);font-size:var(--text-sm);}
@media print{ .offices__grid{grid-template-columns:repeat(3,1fr);} .office{break-inside:avoid;} }

/* ============================================================================
   V2.8 — REVIEWER FEEDBACK PASS.  Added 2026-08-24.
   Source: the "10-minute review" thread (Hitesh Naik, Ariel Gumabao, Jim
   Geovedi, Elie Estephan, Lahan Gajasinghaarachchi).  Owner approved items
   1-7 plus 9, 10, 11 and the Group-3 placeholders.  Tokens only; v1's
   site.css is untouched.
   ========================================================================== */

/* --- 1. Sticky-nav anchor offset + gentle in-page motion ---------------------
   tokens.css §7 LOCKED: "the nav is sticky, so the scroll container (html) must
   carry scroll-padding-top ~= nav height (5rem) — otherwise in-page jumps land
   under the header and hide the target section's title."  It was missing, so
   every menu and card link landed under the header AND jumped instantly.
   The reduced-motion block in tokens.css already forces scroll-behavior:auto,
   so this is opt-out by default for anyone who asked for less motion. */
html{scroll-padding-top:5rem;scroll-behavior:smooth;}

/* --- 2. Cards in a row share a height ---------------------------------------
   Supersedes V2.5.  V2.5 stopped grid items stretching because opening ONE
   <details> grew every sibling — which read as "all four boxes opened".  That
   reason is gone: the owner's 2026-08-18 decision opens a whole group together,
   so every card in a row now has content at the same time.  Stretching is
   correct again, and it is what the reviewers asked for. */
.grid > .discard,
.compose__row > .cmp-prod-wrap,
.compose__row > .arch__ai-wrap{align-self:stretch;}
.grid > .discard{display:flex;flex-direction:column;}
.grid > .discard > .discard__body{flex:1 1 auto;}
@media (min-width:48em){
  .chain{align-items:stretch;}
  .chain__step{display:flex;flex-direction:column;}
  .chain__desc{flex:1 1 auto;}
}

/* --- 3. The step meters are removed -----------------------------------------
   The bars under the four value-chain cards were filled 25/50/75/100% to show
   position in a sequence.  They look like data and measure nothing, so a
   reader reasonably asks "75% of what".  tokens.css §8 is explicit that a
   number must never be ambiguous.  The numbered badges already carry the
   order, so the bar is removed rather than restyled. */
.chain__meter{display:none;}

/* --- 4. Placeholder marker ---------------------------------------------------
   Content agreed in principle but still awaiting a value (a LinkedIn URL, an
   office switchboard, a post link).  Deliberately loud: this must never reach
   production unnoticed.  novus_seo.PUBLISH=True is the gate that fails the
   build while any of these remain. */
.tbc{display:inline-flex;align-items:center;gap:.3rem;font-size:var(--text-xs,.75rem);
  font-weight:var(--weight-bold);letter-spacing:.06em;text-transform:uppercase;
  padding:.12rem .45rem;border-radius:var(--radius-pill,999px);
  background:var(--amber-50);color:var(--amber-700);border:1px dashed var(--amber-400);
  white-space:nowrap;vertical-align:middle;}
[data-theme="dark"] .tbc{background:rgba(232,163,0,.14);color:var(--amber-200);}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .tbc{background:rgba(232,163,0,.14);color:var(--amber-200);}
}
.office__tel--tbc{color:var(--text-tertiary,var(--text-secondary));}

/* --- 5. From LinkedIn band ---------------------------------------------------
   Curated cards, not an embedded third-party widget: a widget loads foreign
   tracking onto every visitor, breaks the offline preview and the CSP, and
   shows whatever the network decides to show. These are our words, each
   linking out to the real post.

   COMPACT (owner, 25 Aug 2026): date + headline only. Eight of these should
   read as a quiet strip along the page, not as a second article. The whole
   card is one link, so there is no separate call-to-action row to pay for. */
.li-grid{display:grid;grid-template-columns:1fr;gap:var(--space-3);
  margin-top:var(--space-5);align-items:stretch;}
@media (min-width:40em){ .li-grid{grid-template-columns:repeat(2,1fr);} }
@media (min-width:64em){ .li-grid{grid-template-columns:repeat(4,1fr);} }

.li-card{display:flex;flex-direction:column;gap:.45rem;position:relative;
  border:1px solid var(--border);border-radius:var(--radius-lg);
  background:var(--surface);padding:var(--space-3) var(--space-4) 2.1rem;
  text-decoration:none;color:inherit;box-shadow:var(--shadow-1);
  transition:transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out),
             border-color var(--dur) var(--ease-out);}
a.li-card:hover{transform:translateY(-2px);box-shadow:var(--shadow-2);
  border-color:var(--accent);}
a.li-card:focus-visible{outline:none;box-shadow:var(--focus-ring);}

.li-card__top{display:flex;align-items:center;gap:.4rem;}
.li-card__mark{width:15px;height:15px;flex:0 0 auto;color:var(--text-secondary);}
.li-card__when{font-size:var(--text-xs,.75rem);color:var(--text-secondary);
  font-variant-numeric:tabular-nums;letter-spacing:.02em;white-space:nowrap;}
.li-card__body{font-size:var(--text-sm);font-weight:var(--weight-bold);
  line-height:1.32;color:var(--text);}
.li-card__by{font-size:var(--text-xs,.75rem);color:var(--text-secondary);font-style:italic;}
.li-card__go{position:absolute;right:var(--space-4);bottom:.85rem;
  color:var(--accent-text);font-weight:var(--weight-bold);}
a.li-card:hover .li-card__go{transform:translateX(2px);}
.li-card--tbc{border-style:dashed;background:transparent;}

/* All four cards in a 2x2 block share ONE height, not one height per row.
   Hitesh Naik raised height consistency twice, so rows are equalised too. */
@media (min-width:48em){
  .grid--even{grid-auto-rows:1fr;}
  .offices__list{grid-auto-rows:1fr;}
}

/* Date stamp and third-party byline on a LinkedIn card. The date is decoded from
   the post id, so the order on the page is the real chronological order. */
.li-card__when{margin-left:auto;font-size:var(--text-xs,.75rem);color:var(--text-secondary);
  font-variant-numeric:tabular-nums;white-space:nowrap;}
.li-card__by{margin:0;font-size:var(--text-xs,.75rem);color:var(--text-secondary);
  font-style:italic;}
.li-card__body{font-weight:var(--weight-bold);line-height:1.35;}
.li-grid{grid-auto-rows:1fr;}

/* --- 6. Talk to us: the form (reviewer item 8) -------------------------------
   A mail link asks the visitor to leave the page, open a client, and invent the
   subject line themselves. Most don't. The form asks four short questions and
   keeps the email and phone alongside for anyone who prefers them.

   Deference: no boxed panel, no heavy chrome. Fields are quiet until focused. */
.cform{margin-top:var(--space-5);max-width:44rem;}
.cform__row{display:grid;grid-template-columns:1fr;gap:var(--space-4);}
@media (min-width:40em){ .cform__row{grid-template-columns:1fr 1fr;} }
.cform__f{display:flex;flex-direction:column;gap:.35rem;margin-bottom:var(--space-4);}
.cform__row .cform__f{margin-bottom:0;}
.cform__row + .cform__f,
.cform__row + .cform__row{margin-top:var(--space-4);}

.cform__f label{font-size:var(--text-sm);font-weight:var(--weight-bold);color:var(--text);}
.cform input,
.cform select,
.cform textarea{
  font:inherit;font-size:var(--text-base);color:var(--text);
  background:var(--surface);
  border:1px solid var(--border);border-radius:var(--radius-md);
  padding:.7rem .85rem;width:100%;
  transition:border-color var(--dur-fast) var(--ease-out),
             box-shadow var(--dur-fast) var(--ease-out);
  -webkit-appearance:none;appearance:none;}
.cform textarea{resize:vertical;min-height:6.5rem;line-height:1.5;}
/* The chevron is drawn from two gradients rather than an inline SVG: a data: URI
   would force img-src to allow data:, and this site's CSP allows 'self' only.
   currentColor means it follows the text colour into dark mode for free. */
.cform select{
  background-image:linear-gradient(45deg,transparent 50%,currentColor 50%),
                   linear-gradient(135deg,currentColor 50%,transparent 50%);
  background-position:calc(100% - 1.2rem) calc(50% + 1px),calc(100% - .85rem) calc(50% + 1px);
  background-size:6px 6px,6px 6px;background-repeat:no-repeat;padding-right:2.4rem;}
.cform input:hover,
.cform select:hover,
.cform textarea:hover{border-color:var(--border-strong);}
.cform input:focus-visible,
.cform select:focus-visible,
.cform textarea:focus-visible{outline:none;border-color:var(--accent);box-shadow:var(--focus-ring);}
.cform input[aria-invalid="true"],
.cform textarea[aria-invalid="true"]{border-color:var(--danger);}

/* Per-field message, written by the script. Empty until it has something to say,
   so it takes no vertical space on a clean form. */
.cform__err{font-size:var(--text-sm);color:var(--danger-text,var(--danger));}
.cform__err:empty{display:none;}

/* Honeypot. Off-screen rather than display:none — some bots skip hidden fields. */
.cform__hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden;}

.cform__actions{display:flex;flex-wrap:wrap;align-items:center;
  gap:var(--space-3) var(--space-5);margin-top:var(--space-5);}
.cform__alt{margin:0;font-size:var(--text-sm);color:var(--text-secondary);}
.cform__alt a{color:inherit;text-underline-offset:.18em;}
.cform__alt a:hover{color:var(--accent-text);}

.cform__note{margin:var(--space-3) 0 0;font-size:var(--text-sm);color:var(--text-secondary);}
.cform__note:empty{display:none;}
.cform__note--bad{color:var(--danger-text,var(--danger));}
.cform__privacy{margin:var(--space-3) 0 0;font-size:var(--text-sm);color:var(--text-muted,var(--text-secondary));}

.cform button[type="submit"][disabled]{opacity:.6;cursor:progress;}

/* --- 7. "Request this paper" (owner, 10 Sep 2026) ----------------------------
   Each white paper now carries its own way in. The link jumps to the form and
   the script writes the paper's title into the hidden topic field, so we know
   which one was asked for without the visitor having to type it. */
.card__ask{display:inline-flex;align-items:center;gap:.35rem;margin-top:auto;
  padding-top:var(--space-3);
  font-size:var(--text-sm);font-weight:var(--weight-bold);
  color:var(--accent-text);text-decoration:none;
  transition:gap var(--dur-fast) var(--ease-out);}
.card__ask:hover{gap:.6rem;text-decoration:underline;text-underline-offset:.2em;}
.card__ask:focus-visible{outline:none;box-shadow:var(--focus-ring);border-radius:var(--radius-sm);}
/* .card is a block, so margin-top:auto alone can't reach the bottom. Make only
   the cards that carry an ask into a column, and the four links land on one
   baseline whatever the copy length above them does. */
.card:has(> .card__ask){display:flex;flex-direction:column;}
.card:has(> .card__ask) > .card__media{flex:0 0 auto;}

/* --- 8. Terms of use (terms.html) -------------------------------------------
   A reading page, not a marketing one: one column, measure capped near 68
   characters, headings that step down clearly. Same chrome as the site so it
   never feels like it was bolted on by a different hand. */
.wrap--prose{max-width:min(var(--container,1200px), 42rem);}
.legal h1{margin:.2rem 0 var(--space-4);}
.legal h2{margin:var(--space-7) 0 var(--space-3);font-size:var(--text-lg);}
.legal p{line-height:1.68;margin:0 0 var(--space-3);}
.legal .lede{margin-bottom:var(--space-4);}
.legal__stamp{font-size:var(--text-sm);color:var(--text-secondary);
  padding-bottom:var(--space-5);border-bottom:1px solid var(--border);}
.legal__back{margin-top:var(--space-7);padding-top:var(--space-5);
  border-top:1px solid var(--border);font-weight:var(--weight-bold);}
.legal a{color:var(--accent-text);}

/* ============================================================================
   V2.9 — MOBILE FIT PASS.  Added 2026-09-14.
   Three changes, all below the 48em breakpoint so desktop keeps its ratified
   metrics: fluid heading scale, denser section padding, and small-screen
   guard rails for the pieces that were built desktop-first.
   ========================================================================== */
/* --- 1. Fluid headings ------------------------------------------------------
   The ratified scale tops out at 3rem for h1, which is right on desktop but
   overflows three-word lines on a 360px phone. clamp() keeps the desktop
   value exactly and only steps in below ~700px. */
@media (max-width:48em){
  h1{font-size:clamp(1.9rem, 5.5vw + .55rem, 3rem);line-height:1.12;}
  h2{font-size:clamp(1.5rem, 3.5vw + .55rem, 2.25rem);}
  /* .hero .statrow gap override removed 2026-09-15 - not in Ricardos's HTML,
     which keeps the kit's 16px/24px row/column gap. */
}
/* --- 2. Denser section rhythm on phones ------------------------------------
   REMOVED 2026-09-15. This block narrowed .wrap and .card from 24px to 16px
   below 34em. Ricardos's HTML has no such rule, so it was the source of the
   "white spacing different from html" and "right margin terlalu deket edge"
   reports: every grid inside .wrap rendered 358px wide against the HTML's
   342px, and cards sat 8px tighter to the screen edge.
   Parity with the HTML wins over the extra 16px of phone width. */
/* --- 3. Spec tables never force the page wide -------------------------------
   Long values ("RaaS, recruitment, contract, exec search, TPO") wrapped fine,
   but the two-column th/td grid fought 320px viewports. Stack them instead. */
@media (max-width:34em){
  .spec-table th,.spec-table td{display:block;width:100%;}
  .spec-table th{padding-bottom:.1rem;}
  .spec-table td{padding:0 0 var(--space-3);}
  .spec-table tr:last-child td{padding-bottom:0;}
}
/* --- 4. Footer columns stack with air ---------------------------------------
   (site.css already drops to one column at the bottom of its scale; nothing
   extra needed, kept as a comment so the pass reads as one unit.) */

/* ==========================================================================
   DARK-MODE PARITY FIX  (2026-09-15)

   tokens.css scopes the product-accent and wordmark overrides to
   [data-theme="dark"] only, with no @media (prefers-color-scheme: dark)
   counterpart. A phone on OS dark that never tapped the toggle therefore gets
   the LIGHT accents (--green-700, --amber-700, --indigo-600, --blue-700) on a
   dark navy ground: the dim, low-contrast wordmarks, stat values and
   "Works with" links reported by the team.

   The design system's own rule (tokens.css §2, LOCKED): "A device merely SET
   to dark - or the JS-less file preview - must look identical to the toggled
   state. Never rely on the toggle alone."

   Fixed here rather than in tokens.css so the vendored file stays re-syncable.
   Values mirror the [data-theme="dark"] block exactly. Report upstream.
   ========================================================================== */
@media (prefers-color-scheme: dark) {
  /* Two :root tokens are redefined in the [data-theme="dark"] block but NOT in
     tokens.css's own prefers-color-scheme block, so they kept their light
     values on OS dark. Verified by diffing all 302 declared tokens across both
     dark paths: these were the only two that drifted. Values copied verbatim
     from tokens.css lines 96 and 118. */
  :root:not([data-theme]) {
    --scrim: rgba(0,0,0,.62);
    --focus-ring: 0 0 0 3px color-mix(in srgb, var(--accent-active) 55%, transparent);
  }

  :root:not([data-theme]) .theme-novus,
  :root:not([data-theme]) .theme-novapay  { --accent-text:var(--blue-300);   --accent-subtle:rgba(59,154,224,.16); }
  :root:not([data-theme]) .theme-novabank { --accent-text:var(--green-300);  --accent-subtle:rgba(43,178,102,.16); }
  :root:not([data-theme]) .theme-novastore{ --accent-text:var(--amber-300);  --accent-subtle:rgba(232,163,0,.16); }
  :root:not([data-theme]) .theme-novatrust{ --accent-text:var(--blue-300);   --accent-subtle:rgba(59,154,224,.16); }
  :root:not([data-theme]) .theme-novaai   { --accent-text:var(--indigo-300); --accent-subtle:rgba(83,74,183,.22); }
  :root:not([data-theme]) .theme-novaboost{ --accent-text:var(--orange-300); --accent-subtle:rgba(232,120,48,.16); }

  /* LOCKED: every product & platform lockup is two shades of light blue in dark. */
  :root:not([data-theme]) .wm .nm      { color:var(--blue-200); }
  :root:not([data-theme]) .wm .sf      { color:var(--blue-300); }
  :root:not([data-theme]) .vlogo .vmark{ stroke:var(--blue-300); }

  :root:not([data-theme]) .media--duotone::after { opacity:.22; }
}

/* ==========================================================================
   CONTRAST + MARGIN FIXES  (2026-09-15, from team screenshots)
   ========================================================================== */

/* 1. Legible text on a photograph that stays bright.
      A heavy full-frame scrim worked but flattened the photography, which is
      the point of the band. Instead, filter the BACKDROP only where the type
      sits: blur removes the high-frequency detail that fights letterforms
      (most of the legibility win), and a modest brightness() supplies the
      rest - far less darkening than an opaque scrim needs. Everything outside
      the treated area keeps full brightness and colour.

      The effect lives on ::after, not on .feature__body, because the body is a
      centred column only 63% of the band width at desktop - putting the filter
      on it left a hard vertical edge slicing across the photo. ::after spans
      the whole band and is masked to a soft shape instead.

      Measured (white text, worst backdrop pixel behind each element):
        390px  eyebrow 7.3:1  h3 9.1:1  stats 15.3:1
        1440px eyebrow 15.2:1 h3 15.7:1 stats 16.7:1
      Design system §11.F still holds: type never sits on raw photograph. */
/* Desktop band height. The launch-kit scrim is a vertical `to top` gradient
   whose stops are percentages of the BAND, and it was tuned against a band
   where the copy covers about 74% of the height - which is what happens at
   1440px, where min-height resolves to 340px and the copy is 253px.

   `min-height:clamp(240px,24vw,340px)` resolves to only 238-264px between
   992px and 1100px, so the copy covered 96-99% of the band and the eyebrow sat
   above the scrim's solid region, on raw photograph. Measured with the
   launch-kit treatment restored:

        992px  1.31:1      1100px  1.44:1      1280px  4.43:1      1440px  6.41:1

   Giving the band its full height from 62rem up restores the proportion the
   gradient expects, so the same gradient clears AA at every desktop width. The
   scrim itself is untouched - still the launch kit's, still vertical, still no
   darkening down the left. */
@media (min-width:62rem){
  .feature--brand{min-height:340px;}
}

/* The desktop band carries NO ::after. The launch-kit CSS has none either: its
   final state is the vertical bottom-up scrim already declared above, over the
   photo. An earlier fix here added a blurred ::after masked with
   `radial-gradient(... at 26% 50% ...)`, which is what put a dark wash down the
   LEFT of the frame - not in Ricardos's design. Removed 2026-09-16.
   ::after still exists below 62rem, where it is repurposed as the stacked
   photograph rather than a scrim. */

/* 2. The "+" on the novaai band overlapped the label.
      RESOLVED BY THE GUTTER FIX, no padding change needed. The overlap came
      from our phone override narrowing .wrap to 16px, which widened the card
      and let the centred label run under the absolutely-positioned "+".
      With the HTML's 24px gutter restored, measured clearance is 6px at 320px,
      26px at 360px, 41px at 390px - no overlap at any width, same as the HTML.

      WATCH: the HTML's own clearance is only ~4px around 430px. If the label
      ever grows or the font falls back, it will touch. Not changed here because
      that is Ricardos's spacing and it does not currently overlap. */

/* 3. novaboost keeps its orange in BOTH themes - HTML parity.
      Measured against Ricardos's HTML: novaboost is the one product whose
      wordmark and pictograph stay --orange-400 (#E87830) even in dark, because
      `.theme-novaboost .wm .sf{color:var(--accent)}` outranks the all-blue
      lockup rule there. Every other product goes two-shade blue.

      ACCESSIBILITY NOTE: as text, #E87830 is 2.93:1 on white and 2.73:1 on the
      tinted band, below the 4.5:1 AA minimum. Kept deliberately for brand
      fidelity on the owner's instruction - not an oversight. Revisit with
      Ricardos if AA compliance is required. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .theme-novaboost .wm .sf       { color:var(--accent); }
  :root:not([data-theme]) .theme-novaboost .vlogo .vmark { stroke:var(--accent); }
}

/* ==========================================================================
   OVERFLOW FIXES  (2026-09-15, from /bug/overlap.png + a full-page scan)

   Both of these are inherited from the launch-kit HTML, not regressions from
   the port - verified by running the same measurement against
   Novus_Website_V2_Launch_Kit/site/index.html, which overflows identically.
   ========================================================================== */

/* 1. Product cards pushed the "+" outside the box.
      `.compose__products` was a hard `1fr 1fr` at every width. The card is a
      flex row (pictograph + wordmark, then the "+" via ::after), and a flex
      item will not shrink below its content, so once the column is narrower
      than the content the "+" is pushed past the border.

      Measured content width is 163px at the default 16px root font, so two
      columns need ~380px of container; a 390px phone only offers 342px.
      Overflow was +2px at 390px, +15px at 360px, +35px at 320px, and worse at
      the larger root sizes people set for readability.

      auto-fit with a real minimum lets it fall to one column when two do not
      fit, which also matches how the platform cards below already stack. */
.compose__products{
  grid-template-columns:repeat(auto-fit, minmax(11.5rem, 1fr));
}
/* The HTML switches to 4 columns at 600px, but 4 columns of ~129px cannot hold
   163px of content: measured on the HTML itself, all five cards overflow by
   43px at 600px, 18px at 700px and 5px at 768px. Four columns only genuinely
   fit from about 896px (203px each), so that is where we switch - matching the
   HTML's 4-across at desktop while letting auto-fit handle everything below. */
@media (min-width:56rem){
  .compose__products{grid-template-columns:repeat(4, minmax(0, 1fr));}
}
/* Defensive: never let a card expand its own grid track. */
.cmp-prod-wrap{min-width:0;}
.cmp-prod{min-width:0;}
.cmp-prod .vlogo{min-width:0;}

/* 2. Leadership grid overflowed on long names.
      `.lead-grid` tracks are minmax(112px,1fr) and "Lahan Gajasinghaarachchi"
      is a single 24-character token, which cannot wrap at a space. +5px at
      430px. Let long names break rather than widen the track. */
.lead__name{overflow-wrap:anywhere;}

/* ==========================================================================
   CONTRAST SWEEP  (2026-09-15) — the last two AA failures on the page.

   These were previously reverted for exact HTML parity. Re-applied on the
   instruction to clear contrast issues in both themes, choosing the SMALLEST
   change that satisfies AA so the design still reads as Ricardos's.
   ========================================================================== */

/* novaboost wordmark, LIGHT mode only.
   --orange-400 (#E87830) as text: 2.93:1 on white, 2.73:1 on the tinted band.
   Stepping one stop to --orange-600 (#B5511A) gives 5.05:1 and 4.70:1 - still
   unmistakably novaboost orange, unlike --orange-700 which reads brown.
   DARK mode is untouched: #E87830 on #0B1620 already measures 6.19:1.
   Only the wordmark TEXT changes; the card's top border and the pictograph
   keep --orange-400, so the brand colour still carries at full strength. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .theme-novaboost .wm .sf { color:var(--orange-600); }
}
:root[data-theme="light"] .theme-novaboost .wm .sf { color:var(--orange-600); }

/* Contact-form privacy line: --text-muted (#6B7886) is 4.19:1 on the tinted
   band, just under AA. --text-secondary (#51606F) gives 6.00:1 - the same
   grey role, one step darker, visually near-identical. */
.cform__privacy { color:var(--text-secondary); }

/* ==========================================================================
   UI FIXES  (2026-09-15, round 3)
   ========================================================================== */

/* 1. The "Our anchor" band cropped the NOVUS lockup on phones.
      `.media--wide` is locked to a fixed height (--v2-band-wide, 300px) at every
      width, while the photograph is 1800x844 (2.13:1). `object-fit:cover` then
      crops whatever does not fit. Measured against the launch-kit HTML, which
      behaves identically, so this is inherited rather than a port regression:

        320px  box 0.91:1  ->  57% of the width cropped
        360px  box 1.04:1  ->  51%
        390px  box 1.14:1  ->  47%   (reads "OVU" - the blue N and S are gone)
        560px  box 1.71:1  ->  20%
        688px+            ->  box is wider than the image; crops vertically only

      The lockup spans the full width of the photograph, so any horizontal crop
      destroys it. Below the width where the box is naturally wider than the
      image, give the figure the image's own aspect ratio: `cover` then has
      nothing to crop and the whole lockup survives.

      Desktop is deliberately left alone - all five letters are visible there and
      it matches Ricardos's HTML. */
@media (max-width: 43em) {
  .media--wide {
    height: auto;
    max-height: none;
    aspect-ratio: 1800 / 844;
  }
}

/* --------------------------------------------------------------------------
   INCLUSION & IMPACT — stacked below 62rem, overlay above it.  2026-09-15

   Why the overlay had to go on phones. Measured .feature__body against the
   band it sits in:

        320px  body 346 / band 348  = 99.4% covered, 1px of photo clear
        390px  body 301 / band 303  = 99.3%
        560px  body 284 / band 286  = 99.3%
        768px  body 234 / band 240  = 97.5%
        992px  body 253 / band 255  = 99.2%

   So there was no photograph left to protect: the copy covered the frame, and
   the ::after mask -- which fades across the first and last 5% of the band,
   about 17px -- had one pixel of clearance to fade into. The fade landed on the
   copy instead of clearing it, which is the smudged seam the team reported. No
   amount of scrim tuning fixes that, because the problem is not the scrim's
   shape; it is that a 240px band cannot hold 346px of type and still be a
   photograph.

   Below 62rem the band therefore stops being an overlay and becomes a stack,
   which is the ordinary answer when the text block would cover the media. The
   photograph goes on top at its own 1672x941 ratio, so all six panels are in
   frame, uncropped, at full brightness, full colour, and with no filter over
   them at all. The copy goes below on the token brand ground -- the same
   9.85:1 to 16:1 surface .feature--brand shipped with before the photo was
   introduced. Nothing is layered, so there is no transition to be smooth.

   From 62rem up nothing changes. There the radial mask clears the right of the
   frame, which is where the photo reads on desktop, and it measures 14.55:1.

   This block sits last in the file on purpose: it has to outrank the
   `@supports not (backdrop-filter)` fallback above, which is unlayered and so
   settles on source order.
   -------------------------------------------------------------------------- */
@media (max-width:61.99rem){
  .feature--brand{
    display:flex;flex-direction:column;align-items:stretch;
    min-height:0;
    /* The shorthand deliberately resets the photo layer and every
       background-position/size left by the eight tunings above it. */
    background:
      radial-gradient(120% 140% at 82% 8%,
        color-mix(in srgb,var(--blue-600) 46%, transparent) 0%, transparent 58%),
      radial-gradient(90% 120% at 8% 100%,
        color-mix(in srgb,var(--blue-700) 40%, transparent) 0%, transparent 62%),
      linear-gradient(135deg, var(--blue-900) 0%, var(--blue-800) 52%,
        var(--blue-700) 100%);
  }
  /* ::after stops being a scrim and becomes the photograph: in flow, ordered
     ahead of the copy, unfiltered and unmasked.

     The ratio is the file's own 1672x941, so `cover` has nothing to crop. Do
     not add a max-height to shorten the band at the top of this range: the
     photo is a 3x2 grid of panels, and any vertical crop cuts the lower row
     across the face. Tried and reverted at 20rem/center 34%, which cut the
     phone and laptop panels just below the eyes. 785px at 991px wide is the
     worst case and it is an ordinary editorial size for a 16:9 photo that
     wide; every width a phone actually reports lands between 477 and 573. */
  .feature--brand::after{
    content:"";                       /* ::after is no longer declared anywhere else */
    order:-1;position:static;flex:0 0 auto;
    width:100%;aspect-ratio:1672 / 941;
    background:url("img/impact-band.826d337d.jpg") center center / cover no-repeat;
    backdrop-filter:none;-webkit-backdrop-filter:none;
    -webkit-mask-image:none;mask-image:none;
  }
  .feature--brand .feature__body{flex:1 1 auto;}
  /* The ground is opaque, so the shadow the overlay needed is just grime. */
  .feature--brand .feature__body .eyebrow,
  .feature--brand .feature__body h3,
  .feature--brand .feature__body .stat__value,
  .feature--brand .feature__body .stat__label{text-shadow:none;}
}
