/* ==========================================================================
   v3.css — VARIATION 3
   Loaded ONLY by index-v3.html, after style.css and versions.css.
   Every rule is scoped `body.v3` so v1 / v2 are untouched — same mechanism
   as v2.css. index.html IS index-v3.html, so this file skins the live page.

   ==== R68 (2026-08-03): RE-SKINNED TO THE 2026 AESTHETIC GUIDELINES ====

   The founder's 2026 Aesthetic Guidelines (9pp PDF) became readable on
   2026-08-03 and replaced the brand's visual canon. Transcription:
   Root: projects/Project Tools/Project ImgDst/Brand-Doc/_source-conversions/
         2026_aesthetic_guidelines.md
   Tokens: .claude/skills/stunning-dentistry-design/colors_and_type.css

   THE 2026 PALETTE — seven colours, no eighth:
     SOFT SURFACE WHITE  #F5F5F7   the default canvas
     PURE WHITE          #FFFFFF   cards, elevated surfaces
     PLATINUM GREY       #E5E5EA   bands, dividers, card grounds
     CHARCOAL GREY       #1D1D1F   body text — the default ink
     COOL SLATE GREY     #6E6E73   secondary text, subtle elements
     CHAMPAGNE GOLD      #F7B844   THE LOGO'S COLOUR AND NOTHING ELSE'S
     ROYAL BLUE          #0D395E   highlights + signature words, 1-5%

     SIGNATURE GOLD GRADIENT  #F9E3A3 -> #E2B35A -> #B8832F, 15deg up from
     the left (105deg in CSS). Smile arcs, CTAs, select logo/icon grounds.
     Every gold surface that is not the logo is made of these three.

   WHAT THIS REPLACED — his 2026-07-27 brief ("two colors: beige and white,
   our yellow ... like we used in the brand books") built the warm skin that
   stood here through R67. The guidelines have no cream in them at all, and
   on 2026-08-03 he ruled the guidelines govern. The beiges are gone:
     #FFF9ED light cream  -> #F5F5F7 Soft Surface White
     #EDDDC0 deep sand    -> #E5E5EA Platinum Grey
     #C9A84C flat gold    -> var(--v3-gold-grad), the signature gradient
     #8D6E1B gold text    -> #1D1D1F charcoal (his call: gold text goes ink)
     rgba(46,33,8) warm scrim -> rgba(29,29,31) charcoal scrim

   ==== ONE KNOWING EXCEPTION, ON HIS EXPLICIT INSTRUCTION ====
   The guidelines cap Royal Blue at 1-5%, "text highlights and signature
   words only". A full-width top bar is far more than 5%. Asked directly on
   2026-08-03 — white header, charcoal header, or Royal Blue — he chose
   ROYAL BLUE. This is therefore a documented, deliberate departure from the
   1-5% rule, not an oversight. If a brand-QA pass flags the header, this
   comment is the answer: it is his call, made with the rule in front of him.

   RHYTHM — Soft Surface White and Pure White alternate; Platinum Grey is
   reserved for the three anchors that used to take deep sand (the pack
   offer, 1M+ Smiles, the footer). No two greys ever touch.
   ========================================================================== */

/* ---- SF Pro Display — the comms face (2026 Guidelines p.5).
        Self-hosted, five weights, harvested from the live .in site so these are
        the same faces production already serves. Declared here rather than in
        style.css because that file is shared with v1/v2, which stay on Inter. ---- */
@font-face { font-family: 'SF Pro Display'; font-style: normal; font-weight: 400; font-display: swap; src: url('../fonts/SFProDisplay-Regular.woff2') format('woff2'); }
@font-face { font-family: 'SF Pro Display'; font-style: normal; font-weight: 500; font-display: swap; src: url('../fonts/SFProDisplay-Medium.woff2') format('woff2'); }
@font-face { font-family: 'SF Pro Display'; font-style: normal; font-weight: 600; font-display: swap; src: url('../fonts/SFProDisplay-Semibold.woff2') format('woff2'); }
@font-face { font-family: 'SF Pro Display'; font-style: normal; font-weight: 700; font-display: swap; src: url('../fonts/SFProDisplay-Bold.woff2') format('woff2'); }
@font-face { font-family: 'SF Pro Display'; font-style: normal; font-weight: 800; font-display: swap; src: url('../fonts/SFProDisplay-Heavy.woff2') format('woff2'); }

body.v3 {
  /* ---- Type: SF Pro Display for every communication. Inter stays loaded by
          style.css but is now reserved for the logo/brand mark alone. ---- */
  --font-sans:    'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
  --font-heading: var(--font-sans);
  --font-logo:    'Inter', 'Helvetica Neue LT Std Extended', sans-serif;

  /* ---- The 2026 palette ---- */
  --v3-surface:    #F5F5F7;   /* Soft Surface White — default canvas */
  --v3-white:      #fff;      /* Pure White */
  --v3-platinum:   #E5E5EA;   /* Platinum Grey — bands, anchors */
  --v3-ink:        #1D1D1F;   /* Charcoal Grey — body text */
  --v3-slate:      #6E6E73;   /* Cool Slate Grey — secondary */
  --v3-blue:       #0D395E;   /* Royal Blue */
  --v3-champagne:  #F7B844;   /* LOGO ONLY — never fill anything else */

  /* ---- R69: the section scheme goes WARM. His call 2026-08-03, sampled from a
          reference site he pointed at. This reverses R68's #FFF9ED -> #F5F5F7
          migration, but ONLY for section grounds: text, borders, gold and the
          header chrome all keep the 2026 palette. The three tones are
          deliberately near-identical (cream/cream 1.045:1, cream/white 1.081:1)
          — far too close for a fill change alone to read as a section change,
          which is why --v3-seam is load-bearing, not decoration. ---- */
  --v3-cream-deep: #F9F6EC;   /* warm band A */
  --v3-cream-soft: #FDFBF5;   /* warm band B */
  --v3-seam:       #E7DFCC;   /* 1px section divider — makes the rhythm legible */

  /* The Signature Gold Gradient — 15deg up from the left.
     R77: PALE CHAMPAGNE, his dictated colour, sent as two swatches (a "Get a
     Quote" pill and a "Free E-Consult" pill). Soft champagne fill, charcoal
     text. Same 105deg axis and same three-stop shape as before; only the
     values moved. Was: #B8832F -> #E2B35A -> #F9E3A3.

     ---- Do not "correct" this back toward saturation. ----
     This colour is lighter than a brand gold usually goes and that is the
     point: he chose it from a rendered button, not from a palette. An earlier
     pass in this same round read his first swatch as the BEFORE rather than
     the TARGET and drove the ramp the other way, to #AB7C2A -> #C9973F ->
     #E8C87E. That was wrong. The champagne is the brief.

     Contrast is comfortable, not marginal: charcoal (--v3-ink) on the three
     stops scores 13.31 / 11.70 / 9.78:1, every one of them far past WCAG AA's
     4.5:1. Going pale moved contrast the GOOD way — the deep ramp it replaced
     bottomed out at 4.53:1. */
  --v3-gold-grad:  linear-gradient(105deg, #F7E3B5 0%, #F0D49B 50%, #E7C078 100%);
  --v3-gold-vert:  linear-gradient(180deg, #F7E3B5 0%, #F0D49B 50%, #E7C078 100%);
  --v3-gold-mid:   #F0D49B;
  --v3-gold-light: #F7E3B5;

  /* ---- THE FILL AND THE LINE ARE NOW TWO DIFFERENT GOLDS ----
     --v3-gold-dark used to be both "the gradient's dark stop" and "the colour
     of every gold hairline, focus ring and rule" — one token doing two jobs,
     which worked only while the gradient was dark enough to draw a line with.

     At champagne it is not. This token feeds ~19 line-work call sites via
     --brand-gold and --v3-gold: the subStrip pill outline, the sticky bar's
     border and inset outline, .v3Upload / .v3Back hover edges, an SVG fill,
     .toolPill's icon chip, and a CHECKBOX whose tick is drawn in white. A 1px
     #E7C078 hairline on white is effectively invisible, and a white tick on
     champagne is unreadable. Painting the line work the fill's colour would
     have quietly deleted a lot of edges.

     So the fill goes pale and the line stays deep. #B8832F is the previous
     ramp's dark stop, kept deliberately: it is the gold this page has drawn
     its edges in since R43 and those edges were never what he asked to
     change. */
  --v3-gold-dark:  #B8832F;   /* LINE WORK ONLY — borders, rules, focus rings,
                                 icon chips, the checkbox fill. NOT a gradient
                                 stop any more; --v3-gold-light/-mid are. */
  --v3-gold-deep:  #E7C078;   /* the gradient's deepest stop, for anything that
                                 needs to match the FILL rather than the line */

  --v3-scrim: 29, 29, 31;     /* charcoal scrim, replaces the warm brown */

  /* ---- Old token names kept as aliases so R43-R67 rules re-skin for free.
          Renaming ~600 call sites would be churn with no visual gain. ---- */
  --v3-navy:      #00081E;              /* Midnight Navy — top-bar chrome, and the
                                           bar is its ONLY call site. Sampled from
                                           the LIVE site 2026-08-03 at his
                                           direction. Not one of the seven palette
                                           colours: it is dark chrome, not a brand
                                           surface. Do not spread it. */
  --v3-navy-lift: var(--v3-blue);       /* flat: one blue, no second hue */
  --v3-cream:     var(--v3-cream-soft); /* R69: back to warm. R68 had this on
                                           --v3-surface (#F5F5F7). */
  --v3-sand:      var(--v3-cream-deep); /* R69: the Platinum fold-in. This alias
                                           carried the three "anchor" sections;
                                           he chose to retire them as a distinct
                                           tone, so they join band A. The
                                           "no two greys ever touch" note below
                                           is now historical. */
  --v3-gold:      var(--v3-gold-dark);  /* flat-gold call sites -> gradient dark stop */
  --v3-gold-text: var(--v3-ink);        /* his call: gold text becomes charcoal */

  /* ---- Remap style.css's own :root tokens under body.v3. style.css is
          shared with v1/v2 and is never edited; overriding the tokens here
          re-skins its 209 var() call sites in one move, v3 only. ---- */
  --section-surface:     var(--v3-surface);
  --section-surface-alt: var(--v3-platinum);
  --brand-cream:         var(--v3-surface);

  --brand-blue:       var(--v3-blue);
  --brand-blue-hover: #0A2C48;              /* Royal Blue, pressed */
  --brand-blue-light: var(--v3-blue);
  --brand-blue-deep:  var(--v3-ink);        /* deep-navy call sites -> charcoal */

  --brand-gold:       var(--v3-gold-dark);
  --brand-gold-dark:  var(--v3-ink);        /* this token IS the gold text -> charcoal */
  --brand-gold-light: var(--v3-gold-mid);
  --brand-gold-top:   var(--v3-gold-light);
  --brand-gold-mid:   var(--v3-gold-mid);

  --grad-brand-blue:   linear-gradient(135deg, #0D395E 0%, #14496F 100%);
  --grad-deep-navy:    linear-gradient(160deg, #1D1D1F 0%, #0D395E 100%);
  --grad-trust-strip:  linear-gradient(136deg, #0D395E 0%, #1D1D1F 100%);
  --grad-gold-luxury:  var(--v3-gold-vert);
  --grad-gold-cta:     var(--v3-gold-grad);

  --ink:      var(--v3-ink);
  --ink-soft: var(--v3-slate);
}

/* R54: the shared sheet reserves the page's tail clearance for the sticky bar
   (css/style.css:2200, `body { padding-bottom: 64px }` — the later of two such
   rules, so it is the one that wins). That file is shared with the deployed
   page and is never edited, so the V3 bar's new 52px height is tracked here
   instead: 52 + 8px of breathing room. If the bar's padding changes again,
   this moves with it. */
body.v3 { padding-bottom: 60px; }

/* ==========================================================================
   R68 — THE 2026 RE-SKIN OVERRIDES
   style.css is shared with v1/v2 and is never edited, so every hardcoded
   legacy hex it paints on this page is neutralised here, body.v3-scoped.
   The token remap above already handles its 209 var() call sites; this block
   is only for the literals. Grouped by what they were.
   ========================================================================== */

/* ---- Section washes: the beiges become the cool base ---- */
body.v3 .section.cream,
body.v3 .section.cream.reviewsSection,
body.v3 .section.sbSection.sbCrm,
body.v3 .section.alt.workSection,
body.v3 .precisionSection,
body.v3 .section.cream.whySection { background: var(--v3-cream-soft); }

/* ---- R69: these three took Platinum at R68; the fold-in sends them to band A. ---- */
body.v3 .section.alt.packSection,
body.v3 .section.sbSection.sbConsult,
body.v3 .section.white.servicesSection { background: var(--v3-cream-deep); }

/* ---- The brands band was #E8E0D6 to match the implant video's flattened
        backdrop. R68 moved band + marquee fades to Platinum together; R69 moves
        the pair again to band A. The fades MUST track the band — if they hold a
        stale colour the video stops melting into its wall and reads as a
        floating rectangle. ---- */
body.v3 .section.brandsSection,
body.v3 .brandsSection { background: var(--v3-cream-deep); }
body.v3 .vMarquee::before,
body.v3 .vMarquee::after { background: linear-gradient(to right, rgba(249,246,236,.9), rgba(249,246,236,0)); }
body.v3 .vMarquee::after { background: linear-gradient(to left, rgba(249,246,236,.9), rgba(249,246,236,0)); }

/* ---- Hairlines and card borders: warm sand -> Platinum Grey ---- */
body.v3 .tgRule,
body.v3 .wsStat + .wsStat { border-color: var(--v3-platinum); background: var(--v3-platinum); }
body.v3 .workCard img,
body.v3 .baCard img { border-color: var(--v3-platinum); }

/* ---- The navy blocks scattered through style.css. Royal Blue where the
        element is chrome; charcoal where it was a dark ground. ---- */
body.v3 .costCol.aus { background: var(--v3-blue); }
body.v3 .bbCall { background: var(--v3-blue); }
body.v3 .wsPayoff { background: var(--v3-ink); }

/* ---- Gold text left in literals -> charcoal, per his 2026-08-03 call ---- */
body.v3 .tgCard .tgCat { color: var(--v3-ink); }
/* R90, his dictation: both price figures drop their special colour and wear
   the card's own text white — same as the country label and vsList lines.
   (Kills style.css's gold --brand-gold-top on the India price too.) */
body.v3 .costCol .price { color: rgba(255, 255, 255, 0.94); }

/* ---- Flat-gold fills -> the signature gradient. Flat Champagne Gold is the
        logo's alone; a flat gold fill anywhere else is the single easiest way
        to get this brand wrong. ---- */
body.v3 .tileCard .svcBg { background-color: transparent; background-image: var(--v3-gold-grad); }

/* ============ 1. THE TOP BAR — Midnight Navy ============
   versions.css gives v2 + v3 the old CA blue gradient. v3.css loads after it,
   so this wins on source order at equal specificity and v2 keeps its gradient.

   HIS CALL, 2026-08-03, made with the rule in front of him: the guidelines cap
   Royal Blue at 1-5% ("text highlights and signature words only") and a
   full-width bar is far more than that. Offered white / charcoal / Royal Blue,
   he first chose Royal Blue.

   SUPERSEDED the same day: he pointed at the LIVE stunningdentistry.com header
   and asked for that colour. Sampled off the capture rather than eyeballed —
   #00081E, near-black navy, much deeper than Royal Blue. So the bar is no
   longer a Royal Blue surface at all; it reads as the page's dark chrome, and
   the 1-5% Royal Blue cap goes back to being satisfied by text highlights
   alone. The gold logo and the WhatsApp green sit on it unchanged (both
   confirmed present in the same sample).

   Named as its own token, not folded into Charcoal Grey: charcoal is #1D1D1F,
   a neutral, and this is a blue. One is not a substitute for the other. */
body.v3 .siteHeader {
  background: var(--v3-navy);
}

/* ============ 2. USP TRUST STRIP — gold, the page's one gold fill ============
   48px band between hero and the first section. Gold reads as an accent at
   this height, not a canvas. Chips flip from white-on-navy to ink-on-gold. */
body.v3 .uspBar {
  background-image: var(--grad-gold-cta);
  color: #1D1D1F;
}
/* Chips go solid white, not translucent: gold-on-gold erased the pill shape
   entirely, so the chips stopped reading as chips. White restores the
   affordance and keeps the gold band as the only gold fill on the page. */
body.v3 .uspBar .marqueeTrack span {
  background: #fff;
  border-color: rgba(42, 33, 7, 0.18);
  color: #1D1D1F;
  box-shadow: 0 1px 3px rgba(70, 55, 20, 0.18);
}
body.v3 .uspBar .dot { background: var(--v3-gold-text); }
body.v3 .uspBar .arr { color: rgba(29, 29, 31, 0.55); }

/* ============ 3. SECTION RHYTHM ============
   style.css asserts tones past its tonal ramp with TWO classes; `body.v3` +
   the same two classes out-specifies each one. Order below follows the DOM. */

/* R63: sbBiggest is back above the packs. White here is what R60 shipped and it
   still reads: the ticker bar sits on it, the section's own media is full-bleed
   dark, and the sand below gets its break. */
body.v3 .section.sbSection.sbBiggest    { background: var(--v3-white); }
body.v3 .section.alt.packSection        { background: var(--v3-sand); }   /* anchor 1 */
body.v3 .section.sbSection.sbConsult    { background: var(--v3-sand); }  /* R64: his dictation — "the color should be the same as the upper section" = packSection sand */
body.v3 .section.white.servicesSection  { background: var(--v3-white); } /* R58: moved up into the old journey slot — white run with its neighbours is the R57 seamless treatment; the photo cards carry it */
body.v3 .section.alt.costSection        { background: var(--v3-sand); } /* R61: back to the beige family, his dictation ("fun fact background complete should be creamish"). Sand, not cream — he pointed at "the yellow cream of the upper section", which is the pack sand. R57's white-seam concern yields to the explicit call. */
body.v3 .section.sbSection.sbTourism    { background: var(--v3-white); }
body.v3 .section.sbSection.sbCrm        { background: var(--v3-cream); }
body.v3 .section.light.baSection        { background: var(--v3-cream); }
body.v3 .section.alt.workSection        { background: var(--v3-cream); }  /* white workCards need cream to read */
body.v3 .section.dark.smilesSection     { background: var(--v3-sand); }   /* anchor 2 */
body.v3 .section.light.specialistsSection { background: var(--v3-white); }
/* R58: .ctaBand rule gone with the CTA trio section — zero uses left.
   R59: sbCare / reviews / precision / safetyOffice / award / recog / treatGrid
   rows removed FROM THIS BLOCK — they were re-declared by the R46.1 re-walk
   below and the duplicates had silently drifted (sbCare and safetyOffice held
   opposite tones; source order decided). The re-walk block is now the only
   authority for those sections. */
/* brandsSection keeps #E8E0D6 — the implant video's own backdrop was flattened
   to exactly this tone in R19, and the .vMarquee fade gradients are the same
   rgba. Retoning it makes the clip float as a rectangle. It is beige-family,
   so it holds the rule; it is a third beige, so its neighbours stay white. */
body.v3 .section.dark:not(.smilesSection) { background: var(--v3-cream); }  /* Choose India's No.1 — R66, his dictation: cream */
body.v3 .section.cream.whySection       { background: var(--v3-white); }
body.v3 .section.cream                  { background: var(--v3-cream); }  /* Treatment Centers */
body.v3 .footerWrap                     { background: var(--v3-white); }
body.v3 .caption                        { background: var(--v3-sand); }   /* anchor 3 — the page closes on weight */

/* ============ 4. FOREGROUND FLIPS ============
   Every navy fill above paired with white text. style.css:1957-1960 already
   documents this exact regression class in the cost section — flipping a fill
   without its foreground makes the copy vanish. Fill and text move together. */

/* .section.dark's blanket white-text rules (style.css:108-111) */
body.v3 .section.dark { color: var(--ink); }
body.v3 .section.dark .sectionHead { color: var(--brand-blue-deep); }
body.v3 .section.dark .sectionHead span { color: var(--v3-gold-text); }
body.v3 .section.dark .helpingText { color: var(--ink-soft); }

/* Know Your Cost: R57 — the section's .sectionHead became the Fun Fact sticker
   block (see the R57 header rules below), so its flip rules went with it. */

/* Meet Our Specialists.
   R85: these four rules KEEP their .specialistsSection scope even though
   index.html no longer has that section — index-v3.html is the frozen rollback
   page, is also body.v3, and still carries it. Deleting them as "orphaned"
   would silently restyle the rollback page (the same reasoning that kept the
   .reviewsSection rules in R84).

   What R85 DID need is the line below: style.css paints .docCard p white for a
   navy section, and v3.css was overriding that back to ink via the
   .specialistsSection scope ONLY. Moving the carousel into .doctorsSection took
   it out of that scope, so without this the five names would render WHITE on a
   cream card — invisible, and invisible in a way a screenshot of the section
   heading would not show. */
body.v3 .specialistsSection .sectionHead { color: var(--brand-blue-deep); }
body.v3 .specialistsSection .sectionHead span { color: var(--v3-gold-text); }
body.v3 .specialistsSection .helpingText { color: var(--ink-soft); }
body.v3 .specialistsSection .docCard p,
body.v3 .doctorsSection .docCard p { color: var(--ink); }

/* Footer + caption */
body.v3 .footerWrap { color: var(--ink); }
body.v3 .footerWrap .sectionHead { color: var(--brand-blue-deep); }
body.v3 .footerWrap .sectionHead span { color: var(--v3-gold-text); }
body.v3 .footerWrap .helpingText { color: var(--ink-soft); }
/* the glass card was frosted white-on-navy; on sand it becomes a solid card */
body.v3 .glassCard {
  background: #fff;
  border: 1px solid rgba(184, 131, 47, 0.4);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: 0 6px 18px rgba(70, 55, 20, 0.10);
}
body.v3 .glassCard p { color: var(--ink); }
body.v3 .glassCard input {
  border: 1px solid rgba(184, 131, 47, 0.45);
  background: var(--v3-white);
}
body.v3 .caption { color: var(--ink); }
body.v3 .caption h3 { color: var(--brand-blue-deep); }
body.v3 .caption h3 span { color: var(--v3-gold-text); }
body.v3 .caption p { color: var(--ink-soft); opacity: 1; }

/* ============ 5. THE NAVY CARDS ============ */

/* Cost columns — navy cards over monument footage. They keep photography, so
   they keep a scrim; the scrim goes warm and the type stays white on it.
   The card FILLS (the part that showed as flat navy) go warm-dark instead. */
body.v3 .costCol.aus   { background: var(--v3-blue); }
body.v3 .costCol.india { background: linear-gradient(160deg, #1D1D1F 0%, #3A3A3D 100%); }
/* R55: the scrim now has to carry five lines of body text, not just a price.
   Bottom-weighted on purpose — the top stays open enough to read as footage
   (that is the whole point of the monument clips), then goes near-solid under
   the list where the small type sits. The stops are set by the measured
   contrast assertion in the r55 probe, not by eye: every .vsText must clear
   WCAG AA 4.5:1 against the composited backdrop at its worst frame of the
   loop. (R57: .vsColHead is gone from the columns.) */
body.v3 .costCol::after {
  background: linear-gradient(
    180deg,
    rgba(var(--v3-scrim), 0.30) 0%,
    rgba(var(--v3-scrim), 0.52) 26%,
    rgba(var(--v3-scrim), 0.86) 52%,
    rgba(var(--v3-scrim), 0.93) 100%
  );
}
/* (R90: the india price's charcoal override is gone — both prices are card
   white now, see the .costCol .price rule above.) */
/* (R66: the saveBadge kit is gone with its element — his dictation.) */

/* Safety card head */
body.v3 .safetyCardHead {
  background: var(--grad-gold-cta);
  color: #1D1D1F;
}

/* Why Stunning — blue journey rail becomes a cream-and-gold rail */
body.v3 .wsBadge {
  background: var(--grad-gold-cta);
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.9), 0 4px 10px rgba(141, 110, 27, 0.3);
}
body.v3 .wsBadge i { color: #1D1D1F; }
body.v3 .wsCard {
  background: var(--v3-white);
  border: 1px solid rgba(184, 131, 47, 0.45);
  box-shadow: 0 6px 14px rgba(70, 55, 20, 0.12);
}
body.v3 .wsCard::before { background: none; }
body.v3 .wsCardLabel { color: var(--brand-blue-deep); }
body.v3 .wsCardDetail { color: var(--ink-soft); }

/* ============ R81 — THE ELEVEN REASONS BECOME MEDIA TILES ============
   His dictation: take the variation built in the brand book
   (brand-tour-final/.../WhyStunningDetail.tsx) and put it here. A photograph
   band with the number riding its top-left, the label on the picture under its
   own scrim, the detail in a white bar below, gold left edge.

   The four .wsBadge/.wsCard rules ABOVE stay, and they are not dead:
   index-v3.html is also body.v3 and still carries the old card markup. Deleting
   them here would have silently restyled a rollback page.

   Everything below is scoped to .wsRow.tiles, a class added to the three <ol>s
   in index.html ONLY, so index-v3.html keeps its cards and its grid untouched.

   FRAME: rendered in THIS page's tokens, not the tour's. The tour's badge is
   its own flat amber; here the disc is --grad-gold-cta over #1D1D1F ink, the
   idiom the old .wsBadge already used, and the left edge is --v3-gold-dark
   because it is line work — the R77 split holds (fill pale, line deep). */
body.v3 .wsRow.tiles { gap: 10px; }
/* the gold journey rail was drawn between the old badges; there are no badges
   above the tiles to thread, and it would cut across the photographs */
body.v3 .wsRow.tiles::before { display: none; }
body.v3 .wsRow.tiles .wsItem { text-align: left; }
body.v3 .wsTile {
  display: flex;
  flex-direction: column;
  height: 100%;              /* rows are equal-height; details run 38-76 chars */
  overflow: hidden;
  border-radius: 12px;
  background: var(--v3-white);
  border: 1px solid #EAECF0;
  border-left: 3px solid var(--v3-gold-dark);
}
body.v3 .wsTileMedia {
  position: relative;
  display: block;
  height: 88px;
  width: 100%;
  overflow: hidden;
  background: var(--v3-cream-soft);
}
body.v3 .wsTileMedia img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* the per-photo object-position rides inline on the img, carried across from
     the brand book's REASON_PHOTO map. Load-bearing, not polish: several of
     these are tall portraits whose subject sits low in frame, and a centred
     88px crop lands on blank wall. */
  object-position: center;
}
body.v3 .wsTileWash {
  position: absolute;
  inset: 0;
  background: linear-gradient(0deg, rgba(141, 110, 27, 0.25) 0%, rgba(212, 175, 55, 0.14) 50%, rgba(212, 175, 55, 0.05) 100%);
}
/* a SHORT scrim behind the label only, rather than one heavier wash across the
   whole band — the photograph stays clear above it */
body.v3 .wsTileScrim {
  position: absolute;
  inset: auto 0 0 0;
  height: 66%;
  background: linear-gradient(0deg, rgba(0, 0, 0, 0.65) 0%, rgba(0, 0, 0, 0.3) 50%, transparent 100%);
}
body.v3 .wsTileNum {
  position: absolute;
  left: 6px;
  top: 6px;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--grad-gold-cta);
  color: #1D1D1F;
  font-size: 0.62rem;
  font-weight: 700;
  line-height: 1;
}
body.v3 .wsTileLabel {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 1;
  padding: 0 10px 8px;
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1.2;
  color: #fff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
  text-wrap: balance;
}
body.v3 .wsTileDetail {
  display: block;
  padding: 10px 12px;
  font-size: 0.68rem;
  line-height: 1.4;
  color: var(--ink-soft);
  text-wrap: balance;
}
/* R82, his dictation: "remove these middle layers and make it like how we have
   on the brand book, you know, one, two, three, four." The three photographed
   band headers are gone and the eleven tiles run straight through as one grid —
   the same call the brand book itself made earlier the same day ("the run of
   numbers is the structure, and the bands were captioning what the cards
   already say"). The closing payoff line stays: he named the middle layers, not
   the payoff, and the section still ends on it.

   The ladder is declared here in full rather than leaning on style.css's
   .cols3/.cols4, because those classes came off with the groups they sized —
   they existed to give each group a column count matching its own length. One
   row means one grid, and the count now comes from width alone.

   2 / 3 / 4 is the density he settled in R81: copy the tile SIZE, not the brand
   book's literal 2-up, which belongs to a phone-width tour panel. Eleven tiles
   land 4/4/3 at desktop and 2x5+1 on a phone — the same trailing orphan the
   brand book lives with at its own 2-up. */
body.v3 .wsRow.tiles { grid-template-columns: repeat(2, 1fr); }
@media (min-width: 761px) {
  body.v3 .wsRow.tiles { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 1024px) {
  body.v3 .wsRow.tiles { grid-template-columns: repeat(4, 1fr); }
}
body.v3 .wsStatNum { color: var(--v3-gold-text); }
/* payoff card: was a dark navy island inside a light section */
body.v3 .wsPayoff {
  background: var(--v3-sand);
  border: 1px solid rgba(184, 131, 47, 0.45);
}
body.v3 .wsPayoff p { color: var(--brand-blue-deep); }

/* Pack cards — flat navy behind the Gemini studio portraits. The portraits are
   navy-backed images, so the card fill goes warm and the left→right scrim
   (retinted below) carries the text zone. */
body.v3 .packCard.liveCard { background: #1D1D1F; }

/* Footer tool pills carried a seven-colour icon dot set (blue / green / purple /
   coral / gold). "Nothing more" kills the rainbow — all dots go gold. */
body.v3 .toolPill i { background: var(--v3-gold) !important; }

/* Service tiles. R58 FIX: the old rule here re-declared the `background`
   SHORTHAND at body.v3 specificity, which beat every `.svcBg[data-svc]`
   photo rule in style.css:1605+ and painted all 13 concierge tiles flat gold —
   latent since the retone, visible the moment the strip moved up to slot 6b.
   The longhand colour below tints only the pre-load flash; the photos win. */
body.v3 .tileCard .svcBg { background-color: transparent; background-image: var(--v3-gold-grad); }

/* ============ 6. SCRIMS — every navy tint goes warm ============
   These sit over live footage and photography, so they stay dark: they are
   legibility, not colour. White overlay type is unchanged. Alphas preserved
   exactly from style.css so nothing gets more or less readable. */

body.v3 .hero.heroVideoMode .heroBlur {
  background: linear-gradient(90deg, rgba(var(--v3-scrim), 0.45), rgba(var(--v3-scrim), 0.18));
}
body.v3 .hero.heroVideoMode .heroCopy h2 { text-shadow: 0 1px 12px rgba(var(--v3-scrim), 0.65); }
body.v3 .hero.heroVideoMode .heroCopy p  { text-shadow: 0 1px 8px rgba(var(--v3-scrim), 0.6); }
body.v3 .quadText { text-shadow: 0 1px 8px rgba(var(--v3-scrim), 0.6); }
body.v3 .liveCard::after {
  background: linear-gradient(to right,
    rgba(var(--v3-scrim), 0.82) 0%,
    rgba(var(--v3-scrim), 0.45) 45%,
    rgba(var(--v3-scrim), 0) 70%);
}
body.v3 .monument::after {
  background: linear-gradient(to top, rgba(var(--v3-scrim), 0.55), transparent 60%);
}
body.v3 .cube.photo::after {
  background: linear-gradient(180deg, rgba(var(--v3-scrim), 0.72), rgba(var(--v3-scrim), 0.86));
}
body.v3 .wsBand::after {
  background: linear-gradient(to right,
    rgba(var(--v3-scrim), 0.85),
    rgba(var(--v3-scrim), 0.55) 50%,
    rgba(var(--v3-scrim), 0.25));
}
/* split-banner media scrim — the overlay title sits on this */
body.v3 .sbMedia::after {
  background: linear-gradient(to top,
    rgba(var(--v3-scrim), 0.88) 0%,
    rgba(var(--v3-scrim), 0.55) 45%,
    rgba(var(--v3-scrim), 0) 100%);
}
/* full-bleed image banners */
body.v3 .bgBanner::before {
  background: linear-gradient(90deg,
    rgba(var(--v3-scrim), 0.88) 0%,
    rgba(var(--v3-scrim), 0.72) 46%,
    rgba(var(--v3-scrim), 0.28) 78%,
    rgba(var(--v3-scrim), 0.12) 100%);
}
body.v3 .bgBanner.right::before {
  background: linear-gradient(270deg,
    rgba(var(--v3-scrim), 0.88) 0%,
    rgba(var(--v3-scrim), 0.72) 46%,
    rgba(var(--v3-scrim), 0.28) 78%,
    rgba(var(--v3-scrim), 0.12) 100%);
}
@media (max-width: 767px) {
  body.v3 .bgBanner::before,
  body.v3 .bgBanner.right::before {
    background: linear-gradient(180deg, rgba(var(--v3-scrim), 0.82) 0%, rgba(var(--v3-scrim), 0.55) 100%);
  }
}
/* pack card head — navy scrim over the implant render */
body.v3 .packHead {
  background: linear-gradient(135deg, #1D1D1F 0%, #3A3A3D 100%);
}
body.v3 .packCard.implantsBg .packHead {
  background:
    linear-gradient(135deg, rgba(var(--v3-scrim), 0.82) 0%, rgba(60, 46, 14, 0.68) 100%),
    url("../images/gen/implant-render.jpg") center 38% / cover no-repeat;
}
/* lead modal backdrop */
body.v3 .modalOverlay { background: rgba(var(--v3-scrim), 0.55); }

/* ============ CHROME — deliberately NOT changed, flagged for his call ============
   .bottomBar's navy Call pill and the WhatsApp green sit outside "internal
   sections": the bar is chrome like the header, and WhatsApp green is a
   platform mark, not our palette. Left as-is pending his ruling. */

/* ==========================================================================
   ROUND 44 — squares. His call: hero, the consult banner and Know Your Cost
   all go 1:1, the consult banner taking V2's full-bleed view. Every scrim
   below uses the R43 warm token, so V2's navy versions are NOT copied over.
   ========================================================================== */

/* ============ HERO → 1:1 ============
   Ported from v2.css:11-16. style.css pins `object-position: 70% center`
   because the ORIGINAL AU clips framed their subject right-of-frame; at square
   that bias shoves the subject off toward the edge. V3 keeps its three-slide
   carousel (V2 dropped to one clip), so this applies to every slide. */
body.v3 .hero.heroVideoMode { aspect-ratio: 1 / 1; min-height: 0; }
body.v3 .hero.heroVideoMode .heroMedia,
body.v3 .hero.heroVideoMode .heroSlide img { object-position: center; }
/* the left-side blur panel was cut for a 16:8 band; at square it eats half the
   frame, so it goes and a full warm scrim carries the headline instead */
body.v3 .hero.heroVideoMode .heroBlur { display: none; }
/* R80: the scrim existed to carry the HTML headline; the welcome reel now bakes
   its own pill captions, so the scrim only washed the footage and greyed the
   pill. Restore alongside .heroCopy if a caption-free hero ever returns. */
body.v3 .hero.heroVideoMode .heroSlide::after { content: none; }
body.v3 .hero.heroVideoMode .heroCopy {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 2;
  width: auto;
  max-width: none;
  padding: 0 16px 26px;
  text-align: left;
  background: none;
}
/* R61: the headline's two sentences sit on two fixed lines — his dictation.
   Block spans force the break; the h2's own clamp (style.css:377, read-only)
   keeps the longer second line inside 390px at the floor, measured. */
body.v3 .hero.heroVideoMode .heroCopy h2 .hlA,
body.v3 .hero.heroVideoMode .heroCopy h2 .hlB { display: block; }

/* ============ CONSULT BANNER → 1:1, full-bleed (V2's view) ============
   Ported from v2.css:56-103, scoped to sbConsult ONLY — he named this banner,
   so tourism / CRM / care keep their cards. The cream fill, gold border,
   radius and shadow come off; the video IS the section. */
body.v3 .sbSection.sbConsult { padding: 0; }
body.v3 .sbConsult .splitBanner {
  max-width: none;
  margin: 0;
  padding: 0;
  gap: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
}
/* R65: the square comes off — the banner now carries the 16:9 "Your visit" cut,
   and cropping 44% off a landscape video to fill a 1:1 box loses the framing.
   With no ratio on the container the absolutely-positioned video below has no
   box to fill, so the ratio moves onto the video itself in the proven sbCrm
   idiom (see :965) — a 41s reel reads as a band, not a tall block. */
body.v3 .sbConsult .sbMedia {
  height: auto;
  min-height: 0;
}
/* DIRECT children only. A bare `.sbMedia img` also matches the WhatsApp logo
   inside the overlay CTA, and inset:0 + 100%/100% blew that 20px icon up into
   a full-width green band between the sections.
   R65: the video is back in normal flow (it was absolute + inset:0 to fill the
   R44 square) so its own ratio now sets the band's height — the sbCrm idiom at
   :965. An img here keeps the old fill behaviour; only video drives height. */
body.v3 .sbConsult .sbMedia > video {
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: 0;
  box-shadow: none;
}
@media (min-width: 768px) {
  body.v3 .sbConsult .sbMedia > video { aspect-ratio: 21 / 9; }
}
body.v3 .sbConsult .sbMedia > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  object-fit: cover;
  border-radius: 0;
  box-shadow: none;
}
/* R89: the scrim existed to carry the HTML headline; the consult reel now bakes
   its own pill captions, so the scrim only washed the footage and greyed the
   white pill. Restore alongside .sbOverlay if a caption-free consult video ever
   returns (the R80 hero pattern, same reasoning). */
body.v3 .sbConsult .sbMedia::after { content: none; }
/* R90: same for the tourism and CRM banners, same reason — both reels carry
   baked pills now, so their scrims have no text left to make legible. */
body.v3 .sbTourism .sbMedia::after { content: none; }
body.v3 .sbCrm .sbMedia::after { content: none; }
body.v3 .sbConsult .sbMedia .sbOverlay { padding: 0 20px 26px; gap: 14px; }
body.v3 .sbConsult .sbOverlayTitle { font-size: clamp(1.35rem, 5.6vw, 2.1rem); }

/* ============ KNOW YOUR COST → square clip wall ============
   No markup port needed: .costCompare is already two columns each carrying a
   .monVid. The clips stop being a 0.6-opacity wash behind a still and become
   the column, with the price block anchored to the bottom of its own footage.
   His call — the section keeps its frame: heading above, "Why pay" line below. */
body.v3 .costSection .costCompare {
  /* R55: the square is gone. The reason lists moved INSIDE these columns, so the
     card's height is now set by its content — a fixed aspect ratio would either
     crop the list or strand it. */
  max-width: min(760px, 100%);
  border: 2px solid var(--brand-gold);
  border-radius: var(--radius-card);
  box-shadow: 0 14px 34px rgba(70, 55, 20, 0.22);
}
body.v3 .costCol {
  display: flex;
  flex-direction: column;
  /* R55: was flex-end, anchoring a price block to the floor of a square. The
     column now runs top-down: flag, country, price, then the list. */
  justify-content: flex-start;
  /* the bottom is generous because the saveBadge overhangs the card's lower
     edge — at 20px the pill sat on the last bullet of whichever column runs
     longer. Caught in the R55 shot; the assertion only proved it was centred. */
  padding: 18px 12px 46px;
  text-align: center;
}
/* the clip fills its half instead of sitting at 0.6 behind the ::before still */
body.v3 .costCol .monVid,
body.v3 .costCol.aus .monVid { opacity: 1; }
body.v3 .costCol::before { opacity: 1; }
/* prices sit on their own footage — align them, kill the left/right edge hug
   the flat layout used */
body.v3 .costCol.aus,
body.v3 .costCol.india { text-align: center; padding-left: 12px; padding-right: 12px; }
/* R57: "logo bigger, cost smaller" — the flag is the column's identity, the
   figure its caption. Flags are inline SVG now (emoji flags render as bare
   "AU"/"IN" letters on Windows Chrome); the hairline ring keeps the tricolour's
   white band from bleeding into the scrim. */
body.v3 .costCol .flag { line-height: 0; }
body.v3 .costCol .flag svg {
  height: 46px;
  width: auto;
  border-radius: 5px;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.35), 0 4px 12px rgba(var(--v3-scrim), 0.35);
}
body.v3 .costCol .country { margin-top: 8px; font-size: 0.74rem; }
body.v3 .costCol .price { font-size: clamp(1.15rem, 4.6vw, 1.7rem); }
body.v3 .costCol .per { font-size: 0.7rem; }
/* (R66: the saveBadge top-seam kit went with the badge. overflow: visible
   stays — R61's corner re-clip below depends on this exact state — and the
   .costCol top padding stays as the flags' breathing room.) */
body.v3 .costSection .costCompare { position: relative; overflow: visible; margin-top: 30px; }
body.v3 .costCol { padding-top: 34px; }
/* R61: re-clip the videos to the pair's rounded outline. The R58 overflow:
   visible above (needed so the badge can straddle the top seam) stopped
   .costCompare clipping its children, so the columns' square corners — and the
   .monVid inside each — poked past the 18px radius. .costCol already clips its
   own inset:0 layers (style.css:627, overflow:hidden); rounding ONLY its two
   OUTER corners lands that clip back on the card's outline. calc() minus the
   pair's 2px gold border sits the arcs flush; the inner seam stays square. */
body.v3 .costCol.aus {
  border-top-left-radius: calc(var(--radius-card) - 2px);
  border-bottom-left-radius: calc(var(--radius-card) - 2px);
}
body.v3 .costCol.india {
  border-top-right-radius: calc(var(--radius-card) - 2px);
  border-bottom-right-radius: calc(var(--radius-card) - 2px);
}

/* ============ R57 — FUN FACT header ============
   His dictation: "fun fact should be a kind of like... boxes of sticker" with
   the 70% line as a paragraph beneath it. The sticker adapts the .saveBadge
   pattern (style.css:663 + the gold skin at 188 above): gold CTA gradient,
   dark ink, hard white border, shadow — but a small-radius BOX, not a pill,
   tilted a touch so it reads stuck-on. The h2 stays the h2 (heading order). */
body.v3 .funFactHead { text-align: center; margin-bottom: 4px; }
/* (R90: the R58 flanking .ffStar pair is gone with its markup — his
   dictation. The row stays as the sticker's centering wrapper.) */
body.v3 .funFactRow {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
}
body.v3 .funFactSticker {
  display: inline-block;
  background: var(--grad-gold-cta);
  color: #1D1D1F;
  font-family: var(--font-heading);
  font-size: clamp(1.05rem, 3.6vw, 1.35rem);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 8px 18px;
  border-radius: 8px;
  border: 2px solid #fff;
  box-shadow: 0 8px 22px rgba(70, 55, 20, 0.28);
  transform: rotate(-2deg);
  margin: 0 0 14px;
}
body.v3 .funFactPara {
  max-width: 560px;
  margin: 0 auto;
  font-size: clamp(0.82rem, 2.5vw, 1rem);
  line-height: 1.5;
  color: var(--ink-soft);
}
body.v3 .funFactPara strong { color: var(--v3-gold-text); }
/* R90: the paragraph closes the section now — breathing room above it, and
   centered under the two-column grid it used to introduce. */
body.v3 .funFactFoot { margin-top: 26px; text-align: center; }

/* ==========================================================================
   ROUND 46 — his dictated running order.
   Three new sections (India's Biggest 1:1 · Step Inside 1:1 · Let's Compare),
   a second ticker, the merged Real Smiles rows, the AU-vs-IN reason lists, and
   the CTA trio. Plus the re-walked tonal rhythm: moving 18 sections around
   broke the beige/white alternation in eleven places, so every tone below is
   re-declared. Same specificity as the R43 block, later in the file, so source
   order carries the override.
   ========================================================================== */

/* ============ R46.1 — THE RE-WALKED RHYTHM ============
   New order, top to bottom. White always separates the two beiges; deep sand
   stays reserved for its three anchors (pack offer, 1M+ Smiles, the caption). */
/* (R61: sbBiggest row removed with its section.) */
/* R66, his dictation: Real Smiles AND the reviews section both go cream —
   both named, so the cream-on-cream seam is his call. qaSafe above stays
   white and qaTransform below goes white, so the pair reads as one warm
   testimonial zone between whites. */
body.v3 .section.light.realSmiles         { background: var(--v3-cream); }
body.v3 .section.cream.reviewsSection     { background: var(--v3-cream); }
/* R59: "make Australian Patients Are Saying smaller so it's in one line."
   The base .sectionHead span (style.css:88-93, read-only) rides the heading's
   clamp and wraps at 390. Own clamp: the floor is what one line at 390 needs
   (31 characters inside ~350px of content), the ceiling keeps the old
   proportion on desktop. Verified by the probe: span height === one line. */
body.v3 .reviewsSection .sectionHead span { font-size: clamp(1.05rem, 4.6vw, 1.9rem); }
body.v3 .section.light.doctorsSection     { background: var(--v3-cream); }
body.v3 .section.cream.whySection         { background: var(--v3-white); }
/* R66: sbEnter flips to white — Choose India's No.1 above it went cream (his
   dictation) and qaTech below it went cream too; without the flip three creams
   would run. (sbCare + precisionSection rows removed with their sections.) */
body.v3 .section.sbSection.sbEnter        { background: var(--v3-white); }
body.v3 .section.compareSection           { background: var(--v3-white); }
/* (R67, his dictation: the safetyOffice picture section is gone — row removed.) */
body.v3 .awardSection                     { background: var(--v3-white); }
body.v3 .recogSection                     { background: var(--v3-cream); }
body.v3 .section.white.treatGridSection   { background: var(--v3-white); }

/* ============ R56 — ONE TICKER ============
   R46.2 declared a second gold strip here so the probe could tell the two
   apart. It never differed from ticker A: the gradient it set is the same
   `--grad-gold-cta` that `body.v3 .uspBar` already sets at line 70, which is
   exactly why the page read as one message split across two identical bars,
   and why he asked for them combined. They are now a single 7-item marquee in
   ticker A's slot, keeping the `uspTickerB` class as its hook. The rule itself
   goes: it restated the base rule and styled nothing. */

/* ============ R46.3 — THE TWO 1:1 SQUARES (sbBiggest + sbEnter) ============
   R61 removed sbBiggest and took its half of every selector pair below with it,
   sbFacts kit included — a class nothing styles is not state. R63 restores the
   section, so both halves come back together: markup, these rules and the facts
   kit in one edit, the same way they left.
   Both reuse the R44 sbConsult treatment verbatim. Direct-child selectors only —
   a bare `.sbMedia img` also matches the WhatsApp logo inside the overlay CTA
   and inflates that 20px icon into a full-width green band (the R44 trap, and
   it would bite twice here).
   R88: sbTourism + sbCrm join every pair below — his dictation squares the
   packages and 24x7 bands too. Their sources are 16:9 (960x540 / 1280x720), so
   the 1:1 cell crops the sides hard — the same accepted cost as the R86
   choose-film cell; no 1:1 cuts of either clip exist (verified). */
body.v3 .sbSection.sbBiggest,
body.v3 .sbSection.sbEnter,
body.v3 .sbSection.sbTourism,
body.v3 .sbSection.sbCrm { padding: 0; }
body.v3 .sbBiggest .splitBanner,
body.v3 .sbEnter .splitBanner,
body.v3 .sbTourism .splitBanner,
body.v3 .sbCrm .splitBanner {
  max-width: none;
  margin: 0;
  padding: 0;
  gap: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
}
body.v3 .sbBiggest .sbMedia,
body.v3 .sbEnter .sbMedia,
body.v3 .sbTourism .sbMedia,
body.v3 .sbCrm .sbMedia {
  aspect-ratio: 1 / 1;
  height: auto;
  min-height: 0;
}
body.v3 .sbBiggest .sbMedia > video,
body.v3 .sbBiggest .sbMedia > img,
body.v3 .sbEnter .sbMedia > video,
body.v3 .sbEnter .sbMedia > img,
body.v3 .sbTourism .sbMedia > video,
body.v3 .sbTourism .sbMedia > img,
body.v3 .sbCrm .sbMedia > video,
body.v3 .sbCrm .sbMedia > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  object-fit: cover;
  border-radius: 0;
  box-shadow: none;
}
/* R90: .sbTourism leaves this group — its reel carries baked pill captions
   now, so the scrim has no text left to make legible and would only wash the
   footage and grey the white pill (the R89 finding on .sbConsult, same
   reasoning). Its `content: none` sits below, beside the consult one. */
body.v3 .sbBiggest .sbMedia::after,
body.v3 .sbEnter .sbMedia::after {
  height: 100%;
  border-radius: 0;
  background: linear-gradient(to top,
    rgba(var(--v3-scrim), 0.90) 0%,
    rgba(var(--v3-scrim), 0.40) 56%,
    rgba(var(--v3-scrim), 0.18) 100%);
}
body.v3 .sbBiggest .sbMedia .sbOverlay,
body.v3 .sbEnter .sbMedia .sbOverlay,
body.v3 .sbTourism .sbMedia .sbOverlay,
body.v3 .sbCrm .sbMedia .sbOverlay { padding: 0 20px 26px; gap: 14px; }
body.v3 .sbBiggest .sbOverlayTitle,
body.v3 .sbEnter .sbOverlayTitle,
body.v3 .sbTourism .sbOverlayTitle,
body.v3 .sbCrm .sbOverlayTitle {
  font-size: clamp(1.35rem, 5.6vw, 2.1rem);
  /* R88: found by shooting, not assumed — the v3 heading remap paints the
     title's NON-span half ink (29,29,31), invisible on the square's scrim.
     sbConsult already carries this exact fix (see :753); the gold span is
     its own color and unaffected. */
  color: #fff;
}

/* the three scale facts riding on the biggest-clinic square (R63: back with the
   section, values now all doctrine-backed — see the markup comment).
   R87: his dictation removed that overlay, so these rules have NO call site in
   index.html any more. Kept, not deleted: index-v3.html is the frozen rollback
   page, is also body.v3, and still carries the .sbFacts markup — the same
   reason .reviewsSection (R84) and .docTeam/.specialistsSection (R85) stayed.
   .sbOverlay itself is untouched and still live: four other split banners use
   it (video consult, tourism packages, 24x7 support, post-arrival support). */
body.v3 .sbFacts {
  display: flex;
  gap: 18px;
  margin: 0;
  padding: 0;
  list-style: none;
}
body.v3 .sbFacts li { display: flex; flex-direction: column; gap: 1px; }
body.v3 .sbFacts strong {
  font-size: clamp(1.1rem, 4.6vw, 1.6rem);
  font-weight: 700;
  color: var(--v3-ink);
  line-height: 1.1;
}
body.v3 .sbFacts span {
  font-size: 0.68rem;
  color: rgba(255, 255, 255, 0.88);
  text-shadow: 0 1px 6px rgba(var(--v3-scrim), 0.7);
}

/* ============ R46.4 / R57 / R58 — AU vs IN, the reason lists ============
   R57 removed the tick/cross marks and the vsColHead labels from the cost
   columns. R58 finishes the family: the Let's Compare rebuild dropped the last
   .vsMark/.vsTick spans, so those generic rules are deleted — the bands carry
   their own .cbTick. The hairline seam vsColHead carried between price and
   evidence lives on .vsList itself. */
body.v3 .costCol .vsList {
  margin: 14px 0 0;
  padding: 12px 0 0;
  /* a hairline seam between the price argument and the evidence for it */
  border-top: 1px solid rgba(255, 255, 255, 0.22);
  list-style: none;
  display: grid;
  gap: 7px;
  text-align: left;
}
/* R55: white on footage now, not --ink on a white card. */
body.v3 .costCol .vsText { font-size: 0.7rem; line-height: 1.35; color: #fff; }
/* R58, his dictation: "the Indian ones be in green color and the Australian
   ones be in white color but the red line will be there." His explicit call —
   it reopens the status-colour door R57 closed, knowingly. The green is picked
   to clear WCAG AA 4.5:1 on the scrim's near-solid band (probe-sampled); the
   red lives only in the strike line, the AU text itself stays full white. */
body.v3 .costCol.india .vsText { color: #8FE3A8; }
body.v3 .costCol.aus .vsText {
  text-decoration: line-through;
  text-decoration-color: rgba(255, 82, 82, 0.95);
  color: #fff;
}
@media (min-width: 768px) {
  body.v3 .costCol .vsText { font-size: 0.86rem; }
}

/* ============ R46.5 / R54 — REAL SMILES ============
   Five rows in one section: the Brand Book's cases screen (CasesDetail.tsx)
   in its own order, plus his doctors row last. The marquees are the existing
   components moved in; the patient rows are new. */

/* R54: one line. css/style.css:93 gives every .sectionHead span
   `display: block`, which splits "Real / Smiles" at every width — not a wrap,
   a hard break. That file is shared with the deployed page, so the override
   lands here instead. The markup already carries the space between the words.
   `.quiet` and `.costSection` do the same thing there, so this is the file's
   own idiom, not a new one. */
body.v3 .realSmiles .sectionHead span,
body.v3 .doctorsSection .sectionHead span { display: inline; }

/* the Brand Book's TourEyebrow: gold dot, then the uppercase label */
body.v3 .realSmiles .rowLabel,
body.v3 .doctorsSection .rowLabel {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 20px 0 8px;
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--v3-gold-text);
}
body.v3 .realSmiles .rowLabel::before,
body.v3 .doctorsSection .rowLabel::before {
  content: "";
  flex: 0 0 auto;
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--v3-gold);
}
body.v3 .realSmiles .rowLabel:first-of-type,
body.v3 .doctorsSection .rowLabel:first-of-type { margin-top: 12px; }

/* (R58: the whole Patient Stories tile kit — .storySwipe, .journeyGrid,
   .vidTile, .vidPlay, .vidCap, .vidPlayer — went with its section. The moved
   reviews section uses the shared .videoBox/.playBtn from style.css.) */

/* ============ R55 — OUR DOCTORS ============
   Ported from the Brand Book's SpecialistDetail.tsx: the Clinical Director on a
   lead card, then the named team in a static grid (1 col, 2 up at the Book's
   sm: breakpoint). Its tokens are this page's family already — #EAECF0 card
   border, #111827 name ink, #667085 secondary — so nothing here is invented.
   The Book's blue-tinted lead card goes gold instead: this page's accent is
   gold and a blue card would be the only one on the page. */
body.v3 .docLead {
  display: flex;
  align-items: center;
  gap: 16px;                        /* the Book's gap-4 */
  margin-top: 14px;
  padding: 16px;
  background: var(--v3-white);
  border: 1px solid rgba(184, 131, 47, 0.45);
  border-radius: var(--radius-card);
  box-shadow: 0 4px 12px rgba(70, 55, 20, 0.10);
  text-align: left;
}
body.v3 .docLeadPhoto {
  position: relative;
  flex: 0 0 auto;
  width: 80px;                      /* the Book's h-20 w-20 */
  height: 80px;
  border-radius: 50%;
  overflow: hidden;
  box-shadow: 0 0 0 2px #fff, 0 2px 8px rgba(70, 55, 20, 0.18);
}
body.v3 .docLeadPhoto img { width: 100%; height: 100%; object-fit: cover; display: block; }
body.v3 .docLeadBody { min-width: 0; }
body.v3 .docLeadEyebrow {
  margin: 0;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--v3-gold-text);
}
body.v3 .docLeadName {
  margin: 4px 0 0;
  font-family: var(--font-heading);
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.25;
  color: var(--ink);
}
body.v3 .docLeadCred { margin: 2px 0 0; font-size: 0.74rem; color: var(--ink-soft); }
body.v3 .docLeadSpec { margin: 2px 0 0; font-size: 0.74rem; line-height: 1.4; color: var(--ink-soft); }

body.v3 .docTeam {
  display: grid;
  grid-template-columns: 1fr;
  gap: 8px;
}
body.v3 .docTeamCard {
  display: flex;
  align-items: center;
  gap: 10px;                        /* the Book's gap-2.5 */
  padding: 10px;
  background: #fff;
  border: 1px solid var(--v3-platinum);  /* R68: was the Book's #EAECF0 */
  border-radius: var(--radius-card);
  text-align: left;
}
body.v3 .docTeamPhoto {
  position: relative;
  flex: 0 0 auto;
  width: 40px;                      /* the Book's 36px, one step up: this row
                                       carries a two-line specialty, not one */
  height: 40px;
  border-radius: 50%;
  overflow: hidden;
}
body.v3 .docTeamPhoto img { width: 100%; height: 100%; object-fit: cover; display: block; }
body.v3 .docTeamName {
  margin: 0;
  font-size: 0.78rem;
  font-weight: 700;
  line-height: 1.25;
  color: var(--ink);
}
body.v3 .docTeamSpec {
  margin: 2px 0 0;
  font-size: 0.68rem;
  line-height: 1.35;
  color: var(--ink-soft);
}
@media (min-width: 640px) {
  body.v3 .docTeam { grid-template-columns: 1fr 1fr; }   /* the Book's sm:grid-cols-2 */
}
@media (min-width: 768px) {
  body.v3 .docLead { gap: 20px; padding: 20px; }
  body.v3 .docLeadName { font-size: 1.15rem; }
  body.v3 .docLeadCred,
  body.v3 .docLeadSpec { font-size: 0.84rem; }
  body.v3 .docTeamName { font-size: 0.88rem; }
  body.v3 .docTeamSpec { font-size: 0.76rem; }
}
/* (R58: .storyCard rules went — zero uses in the DOM since the R55 split settled.) */
@media (min-width: 768px) {
  body.v3 .realSmiles .rowLabel,
  body.v3 .doctorsSection .rowLabel { font-size: 0.82rem; }
}

/* (R58: the R46.6 CTA-trio rules went with the section — the bottom bar
   carries the same three actions.) */

/* ============ R46.7 / R58 / R61 — LET'S COMPARE, one fused table ============
   R58 rebuild, his dictation: no left-right scroll, all data in view, the
   open-circle legend gone. Each criterion is a BAND — label on top, then the
   four destinations as compact cells on one shared 4-column grid, Stunning's
   cell gold.
   R61, his dictation: "connect all up to down so it looks like one complete
   section, a kind of table" — the card chrome moves off the eight bands onto
   .cmpBands ONCE (border, radius, shadow, clip), the bands divide by gold
   hairline with a cream zebra, and the >=900px two-up split is gone: a single
   column at every width is the only reading of "one complete section", and it
   lines the eight gold Stunning cells into one continuous winning stripe.
   overflow:hidden here is safe (nothing inside overhangs — the opposite of the
   .costCompare badge case above). Width capped to .costCompare's own 760px so
   the two comparison units on the page read as one family. */
/* R80, his dictation: the bands become VARIANT B, the sheet — a real table
   again, our column tinted and led, the criterion column sticky.

   WHY THE SIDEWAYS SCROLL IS BACK, having been the exact thing R58 removed:
   he picked B from three rendered options, and five columns do not fit 390px.
   Two rules keep it honest. The scroll is CONFINED to .cmpScroll, so the page
   body still never scrolls sideways (the actual defect R58 was fixed for).
   And .cmpCrit is sticky, so the criterion never leaves the screen: whatever
   is scrolled to, the reader can always see what is being compared.

   border-collapse is SEPARATE, not collapse, and that is load-bearing rather
   than stylistic. Collapsed borders belong to the table, not the cell, so they
   do not travel with a position:sticky cell — the sticky column's edge detaches
   and smears as it scrolls. Separate borders stay on their own cells. Every
   divider below is therefore a per-cell border-top. */
body.v3 .cmpSheet {
  position: relative;
  /* Variant B took 1020px because five columns needed the room. R88 merged
     Bali+Thailand and swapped the wordmark for the S mark ("decrease the width
     of the sections"), so four columns hand back what the fifth was using. */
  max-width: 840px;
  margin: 14px auto 0;

  /* The pale champagne at ~45% on white. Derived from --v3-gold-light rather
     than mixed by eye, so a later move of the gold ramp drags this with it.
     A COLUMN tint, not a button fill: --v3-gold-grad at full strength across
     eight rows turns the table into one gold slab and the price stops leading. */
  --v3-gold-wash: #FBF2DE;
}
body.v3 .cmpScroll {
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;   /* a sideways swipe here is not a page back */
  -webkit-overflow-scrolling: touch;
  background: var(--v3-white);
  border: 1px solid rgba(184, 131, 47, 0.45);
  border-radius: var(--radius-card);
  box-shadow: 0 4px 12px rgba(70, 55, 20, 0.08);
}
/* R80e: table-layout FIXED, and this is the mechanism the whole round rests on.
   Under auto layout the browser negotiates every column from its content, so a
   declared width is a suggestion — R80d's `width: 12%` only landed at 86px via
   the cell's padding, and a narrower Stunning column simply would not have
   taken. Fixed layout honours the declared widths exactly, which is what makes
   "our column is 110px and Thailand's edge shows" specifiable rather than
   coaxed.

   The R88 width budget at 390, where the scroll box is 352px (four columns
   now — Bali & Thailand merged, the S mark instead of the wordmark):
     criterion  19.5% of 440 ->  86px   sticky, unchanged from R80d
     Stunning   16.4%        ->  72px   an icon, "Yes", a price
     each rival 32.05%       -> 141px   (the remainder, split two ways)
   86 + 72 + 141 = 299, so Australia's leading edge sits ~53px inside the box —
   the same "more sections on the right" affordance R80e specified, with more
   of it visible because two columns went to one. */
body.v3 .cmpTable {
  width: 100%;
  min-width: 440px;   /* R88: 601px was the five-column basis */
  table-layout: fixed;
  border-collapse: separate;
  border-spacing: 0;
  text-align: left;
}
body.v3 .cmpCrit {
  position: sticky;
  left: 0;
  z-index: 2;
  /* R80c, his dictation: the labels are short now, so the column that holds
     them stops hoarding width. What it gives up goes to Australia, Bali and
     Thailand, which is the whole point — more of the comparison visible before
     the reader has to swipe. "Single trip, start to finish" wrapping to two
     lines is the intended consequence, not a casualty of it.

     R80d, his dictation: "make it width half" — 24% to 12%, roughly 173px down
     to 86px at 390. This column is position:sticky, so it permanently covers
     screen rather than merely taking a share of the table; the 86px it gives
     back is real visible width for the rival columns, not a redistribution
     inside the 720px scroll box. The accepted cost, put to him at the
     checkpoint and taken: most labels now wrap to two lines and the rows grow
     from roughly 38px to 52px. Fewer rows on screen, more of each row readable
     without a swipe. */
  /* R80e: the same 86px R80d settled on — the percentage moves only because
     the table it is a percentage OF changes basis (R88: 19.5% of 440px). */
  width: 19.5%;
  min-width: 60px;
  /* R80e keeps 8px while every other cell drops to 6px. Under fixed layout this
     padding no longer sets the column's WIDTH, but it still sets how much text
     fits per line, and cutting it would re-wrap the labels he settled in R80d. */
  padding: 11px 8px;
  background: var(--v3-white);
  font-size: 0.7rem;
  font-weight: 700;
  line-height: 1.3;
  color: var(--brand-blue-deep);
  text-align: left;
  /* drawn as a shadow, not a border: a sticky cell's own right border sits
     inside its box and reads as part of the label, while this reads as the
     edge the rival columns pass under */
  box-shadow: 1px 0 0 rgba(184, 131, 47, 0.28);
}
body.v3 .cmpTable thead th {
  /* R80e: side padding 9px -> 6px. He asked for the padding down, and it also
     returns horizontal room to the column budget above. */
  padding: 11px 6px;
  text-align: center;
  vertical-align: bottom;
  /* R90, his dictation: "make Bali and Thailand and the other text a little
     bigger because now this fits inside". The R88 column merge freed the room;
     the rival header steps 0.55 -> 0.68rem and its body cells follow below. */
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  line-height: 1.2;
  color: var(--ink-soft);
}
body.v3 .cmpTable thead th.cmpUs {
  /* Lands ON the body wash rather than stopping at it. --v3-gold-vert at full
     strength made the header a darker cap sitting on the column instead of the
     top of it, with a hard tonal step at the first row. Ramping the mid stop
     down INTO --v3-gold-wash makes the eight rows plus their header read as one
     continuous winning stripe, which is the whole argument of the form. */
  background: linear-gradient(180deg, var(--v3-gold-mid) 0%, var(--v3-gold-wash) 100%);
  color: var(--v3-ink);
  /* R80e: the header cell's own height comes down with the logo it holds */
  padding-top: 5px;
  padding-bottom: 5px;
  /* R80e: our column pays for the rivals' visible edge — it is the cheapest
     width on the table. R88: cheaper still, the wordmark became the S mark
     ("you can just add the icon here and decrease the width"). */
  width: 16.4%;
}
/* R80d, his dictation: "in stunning dentistry add stunning dentistry logo".
   The plate is load-bearing, not decoration. images/logo.png is a GOLD wordmark
   on transparent and this cell is a gold gradient, so straight on the wash it
   is close to invisible. A white ground gives it contrast without recolouring
   the header, which would have broken the continuous gold stripe from header
   down through the rows — the whole argument of the form (see the note above).
   alt carries the column's accessible name, so the <th> reads the same to a
   screen reader as the text it replaced. */
body.v3 .cmpTable thead th.cmpUs .cmpLogo {
  display: inline-block;
  max-width: 100%;
  /* R80e: "decrease the margin and padding" — the plate was reading as a big
     white card in a small header rather than a ground under a small mark. */
  padding: 2px 4px;
  background: var(--v3-white);
  border-radius: 5px;
  box-shadow: 0 1px 2px rgba(70, 55, 20, 0.12);
}
/* R80e, his dictation: "the font of stunning dentistry is too big, make it much
   smaller, around how [Thailand] is written". Sized from a MEASUREMENT, not an
   eyeball. logo.png is 328x88 and its two ink bands were scanned out of the
   alpha channel: "STUNNING" occupies 41px of the 88 (0.4659), the letterspaced
   "DENTISTRY" line 15px. The rival labels' rendered cap height is 7px, measured
   the same way — font-size is NOT cap height, and "the size Thailand is written"
   is a statement about letter height. 7 / 0.4659 = 15px of image height, and
   15 * (328/88) = 56px of width. The wordmark now matches the labels exactly.

   The known cost, put to him before doing it: at this scale "DENTISTRY" renders
   about 2.5px tall and reads as a texture rather than a word. The S-mark and
   "STUNNING" stay legible. That is what matching the label size means here, not
   a defect that appeared afterwards. */
body.v3 .cmpTable thead th.cmpUs .cmpLogo img {
  display: block;
  width: 100%;
  max-width: 56px;
  height: auto;
}
/* R88: the wordmark became the S mark (favicon.png, 48x48) so the column
   could narrow. The R80e cap-height derivation above described logo.png and
   no longer applies — a square icon sizes like the roundel it is. The white
   plate stays for the same contrast reason. */
body.v3 .cmpTable thead th.cmpUs .cmpMark img { max-width: 30px; margin: 0 auto; }
body.v3 .cmpTable tbody td {
  padding: 11px 6px;   /* R80e: 9px -> 6px, same reason as the header */
  text-align: center;
  font-size: 0.78rem;  /* R90: 0.65 -> 0.78rem, his "a little bigger" */
  font-weight: 600;
  line-height: 1.25;
  color: var(--ink);
  border-top: 1px solid rgba(184, 131, 47, 0.25);
}
body.v3 .cmpTable tbody th.cmpCrit { border-top: 1px solid rgba(184, 131, 47, 0.25); }
body.v3 .cmpTable td.cmpUs {
  background: var(--v3-gold-wash);
  border-top-color: rgba(184, 131, 47, 0.42);
  /* R90: steps with the rivals so Stunning's column stays the dominant one */
  font-size: 0.84rem;
  font-weight: 700;
  color: var(--v3-ink);
  white-space: nowrap;
}
/* R80b, his dictation: everything in the table down one step. Our column keeps
   its lead over the rivals (0.7 against 0.65) — the step shrank, the hierarchy
   did not, and the tick shrinks with the type so it stays a mark beside a word
   rather than a bullet in front of one. */
/* R80d, his dictation: "this black dot you have added remove that in front of
   yes". The disc goes, the mark stays — a checkmark drawn in the line-work gold
   instead of a white tick on a black circle. Scoped to .cmpTable so the base
   .cbTick below is untouched for the v1/v2/v37 rollback pages that share this
   stylesheet. --v3-gold-dark is the LINE half of the R77 split (see :127); the
   mark must never pick up the champagne fill. */
body.v3 .cmpTable .cbTick {
  display: inline;
  width: auto;
  height: auto;
  background: none;
  border-radius: 0;
  color: var(--v3-gold-dark);
  font-size: 0.85em;
}
body.v3 .cmpTable td.cmpUs .cbTick { margin-right: 4px; vertical-align: baseline; }
/* R80c: the two cells that carry real text instead of a tick. nowrap above is
   right for "Yes" and for a price, and wrong for "AAID, AACD, AAO, BACD" —
   held on one line it would force the whole column wider than the argument
   needs and push the rivals back off screen. */
body.v3 .cmpTable td.cmpUs.cmpUsText {
  white-space: normal;
  font-size: 0.72rem;  /* R90: steps with its neighbours */
  line-height: 1.3;
}
/* R80d: .cmpNa is gone. It existed to soften "Not applicable" as a non-answer,
   and his dictation replaced that wording with "Not standard" — which already
   sits unstyled in six other cells. Keeping the class would have rendered the
   same sentence in two different sizes and colours, so it came off the three
   cells and its rules came out with it ([[dead-styling-say-so]]). */
/* The scroll affordance, painted ONLY where the table actually overflows.
   .cmpScroll's usable width is the viewport less the .section padding.

   R80e moved this breakpoint 760px -> 637px, and it is a REAL fix, not tidying.
   The 760 was derived from the old 720px table. Narrowing the table to 601px
   moved the point where it stops overflowing down to 638px of viewport, so
   between 638 and 760 the fade was painting over a table that could not scroll
   — precisely the veil-over-a-column-that-never-moves this rule exists to
   avoid. Measured by bisection, not assumed: 636 overflows, 638 does not. Any
   future change to the table's min-width has to move this number with it.
   R88 moved it again for the same reason: min-width 601 -> 440 puts the
   stop-overflowing point at 477px of viewport (the same ~37px of section
   padding), bisection-checked in the R88 probe. */
@media (max-width: 477px) {
  body.v3 .cmpSheet::after {
    content: "";
    position: absolute;
    top: 1px;
    right: 1px;
    bottom: 1px;
    width: 34px;
    pointer-events: none;
    border-radius: 0 var(--radius-card) var(--radius-card) 0;
    background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.94) 100%);
    transition: opacity 180ms ease;
  }
  /* Scrolled to the end: there is nothing further right, so the fade stops
     claiming there is. js/main.js sets .atEnd — see the R80 note there. */
  body.v3 .cmpSheet.atEnd::after { opacity: 0; }
}
/* the tick is dark-on-gold, INSIDE the gold cell — no green here; the reopened
   status-green stays confined to the cost columns where he asked for it */
body.v3 .cbTick {
  width: 13px;
  height: 13px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: #1D1D1F;
  color: var(--v3-ink);
  font-size: 9px;
  line-height: 1;
}
body.v3 .cbTick::before { content: "\2713"; }
body.v3 .compareSection .sectionHead { color: var(--brand-blue-deep); }
/* R61: "Let's Compare" on one line — style.css:93 gives every .sectionHead span
   display:block, a hard break, not a wrap. Same idiom as realSmiles above. */
body.v3 .compareSection .sectionHead span { display: inline; color: var(--v3-gold-text); }
@media (min-width: 900px) {
  /* R61: the 1fr 1fr split is gone — see the fused-table note above.
     R80: the same idea on the sheet — one size up once there is room, and the
     rows breathe. Nothing here changes the layout, only the type and padding. */
  /* R80d: the side padding comes down with the column. 18px a side inside an
     86px column is most of the column, and it would have pushed labels to three
     lines here that sit on two at 390. */
  body.v3 .cmpCrit { font-size: 0.82rem; padding: 14px 12px; }
  /* R90: the desktop step keeps its distance above the new base sizes. */
  body.v3 .cmpTable thead th { font-size: 0.74rem; padding: 14px 12px; }
  body.v3 .cmpTable tbody td { font-size: 0.88rem; padding: 14px 12px; }
  body.v3 .cmpTable td.cmpUs { font-size: 0.94rem; }
  /* R80c: the two text cells need a desktop step of their own. Without it they
     stay at their 390px size while every neighbour grows, and the accreditation
     and CRM cells visibly dip below the "Yes" cells they sit between. Still
     under .cmpUs's own size, because they carry far more characters. */
  body.v3 .cmpTable td.cmpUs.cmpUsText { font-size: 0.8rem; }
  /* the rival labels step 0.55rem -> 0.6rem here, so the logo tracks them */
  /* R88: the S mark steps with them too, from 30px (it is .cmpLogo AND
     .cmpMark, so this later rule would otherwise drag it back to wordmark
     size at desktop) */
  body.v3 .cmpTable thead th.cmpUs .cmpLogo img { max-width: 61px; }
  body.v3 .cmpTable thead th.cmpUs .cmpMark img { max-width: 34px; }
}

/* ============ R58 — THE REAL-SMILES NEIGHBOURS, SHORTER ============
   His dictation: scrolling to Real Smiles, "decrease the height of the section
   above and the below one". Above = the CRM banner: its height IS its video's
   aspect-ratio (style.css .sbMedia video, 16:9), so the scoped override widens
   the ratio instead of clipping content — 2:1 on phones, 21:9 where the banner
   runs near its 980px cap. Below = the moved reviews section: it inherits the
   page's LARGEST default section padding; trimmed toward the .tight scale.
   R88 retired the CRM half: sbCrm now rides the R46.3 square block above
   (aspect-ratio 1/1 on .sbMedia, video absolute), so the 2:1 / 21:9 video
   overrides here would fight it and are gone. */
body.v3 .section.cream.reviewsSection { padding-top: 14px; padding-bottom: 14px; }

/* ============ R55 — MARQUEE HOLD ============
   The counterpart to section 11 of js/v3-forms.js. Press a marquee and it
   stops until release, so a tile inside one can actually be tapped. The track
   animation is declared in css/style.css:237 (shared, read-only); pausing it
   from here needs nothing but the play-state, and applies to the track of the
   held marquee only, not to every marquee on the page. */
body.v3 [data-marquee].v3-mqHold .marqueeTrack { animation-play-state: paused; }

/* ============ R59 — THE ctaSolo BAND ============
   One-CTA doctrine: no CTAs inside sections; instead one centered gold button
   every ~2 sections, cycling the sticky bar's three keys (endtoend, pricelist,
   videoconsult). White ground so a band between two beiges still reads as the
   separating white the rhythm law wants; tight padding so it is a beat, not a
   section. .btnGold itself is style.css's (read-only), untouched. */
body.v3 .ctaSolo {
  background: var(--v3-white);
  display: flex;
  justify-content: center;
  padding: 26px 16px;
}
/* R61: the Calculate Your Cost band only — his dictation: its white becomes
   "the yellow cream of the upper section", so the pack sand runs on through
   the band into the consult banner. The other bands keep the separating white
   the rhythm law wants. */
body.v3 .ctaSolo.ctaSand { background: var(--v3-sand); }

/* (R61: the R60 Moments From The Clinic kit — .reelsRow / .reelTile — went
   with its section. The reel plays in the consult banner now, on the shared
   sbMedia component; no reel-specific CSS remains.) */

/* ============ R62 — AUSSIE QUESTIONS, ANSWERED ============
   The objection trio (smooth / stay / safe) + the tech reel + the
   transformation finale, one shared family. Question-form sectionHead,
   centered gold eyebrow, a one-line direct answer, then the section's own
   media. Grounds follow the R57 rhythm and are set per-section here so the
   run reads: sbCrm cream -> smooth white -> stay cream -> safe white. */
body.v3 .qaSection.qaSmooth { background: var(--v3-white); }
body.v3 .qaSection.qaStay { background: var(--v3-cream); }

body.v3 .qaEyebrow {
  margin: 0 0 2px;
  text-align: center;
  font-size: 0.72rem;                 /* the sbEyebrow kit, v3-forms.css:1240 —
                                         same type, dark gold because it sits on
                                         a light ground, not a video scrim */
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--brand-gold-dark);
}
body.v3 .qaLead {
  margin: 0 auto 16px;
  max-width: 56ch;
  text-align: center;
  font-size: clamp(0.88rem, 2.6vw, 1.02rem);
  line-height: 1.5;
  color: var(--ink-soft);
}
body.v3 .qaGrid {
  max-width: 860px;
  margin: 0 auto;
  display: grid;
  gap: 18px;
  align-items: center;
}
/* R62 craft pass: at desktop the media column is a 9:16 phone ~530px tall while
   the text beside it is 3-4 short rows. Centering left the list floating in
   ~160px of dead air, reading as an unfinished column; topping both columns out
   puts the first row on the same line as the frame's top edge. */
@media (min-width: 860px) {
  body.v3 .qaGrid { align-items: start; }
}

/* R64: his dictation — the stay/safe/transform trio becomes two columns at
   EVERY width, video LEFT, text right, and on a phone the text shrinks so the
   rows sit beside the clip instead of stacking under it. One shared modifier
   replaces the three per-section desktop rules. */
body.v3 .qaGrid.qaSideBy {
  grid-template-columns: minmax(0, 44%) minmax(0, 1fr);
  gap: 14px;
  align-items: start;
}
@media (min-width: 860px) {
  body.v3 .qaGrid.qaSideBy { grid-template-columns: 300px 1fr; gap: 34px; }
}
@media (max-width: 859px) {
  body.v3 .qaSideBy .qaPhone { max-width: none; }
  body.v3 .qaSideBy .qaPhoneCap { font-size: 0.66rem; margin-top: 6px; }
  /* compact type: sync the text column's height with the 9:16 clip */
  body.v3 .qaSideBy .qaDay { padding: 0 0 10px 18px; }
  body.v3 .qaSideBy .qaDay::before { width: 8px; height: 8px; top: 6px; }
  body.v3 .qaSideBy .qaDay:not(:last-child)::after { left: 3px; top: 18px; }
  body.v3 .qaSideBy .qaDayNum { font-size: 0.6rem; padding: 2px 8px; margin-bottom: 2px; }
  body.v3 .qaSideBy .qaDayBody strong { font-size: 0.76rem; }
  body.v3 .qaSideBy .qaDayBody p { font-size: 0.68rem; line-height: 1.4; }
  body.v3 .qaSideBy .qaFacts { gap: 9px; }
  body.v3 .qaSideBy .qaFacts li { padding-left: 16px; }
  body.v3 .qaSideBy .qaFacts li::before { width: 8px; height: 8px; top: 4px; }
  body.v3 .qaSideBy .qaFacts strong { font-size: 0.76rem; }
  body.v3 .qaSideBy .qaFacts p { font-size: 0.68rem; line-height: 1.4; }
}

/* --- §2's 7-day timeline: rendered by CODE (the infographic law) --- */
body.v3 .qaTimeline {
  list-style: none;
  margin: 0;
  padding: 0;
}
body.v3 .qaDay {
  position: relative;
  padding: 0 0 16px 26px;
}
body.v3 .qaDay:last-child { padding-bottom: 0; }
/* the rail: a gold dot per day, one continuous line between them */
body.v3 .qaDay::before {
  content: "";
  position: absolute;
  left: 0;
  top: 7px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--grad-gold-cta);
  box-shadow: 0 0 0 3px rgba(184, 131, 47, 0.22);
}
body.v3 .qaDay:not(:last-child)::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 22px;
  bottom: 2px;
  width: 2px;
  background: rgba(184, 131, 47, 0.35);
}
body.v3 .qaDayNum {
  display: inline-block;
  margin-bottom: 4px;
  font-size: 0.76rem;                 /* the page's own pill, style.css:311-321 */
  font-weight: 700;
  color: var(--brand-blue-deep);
  background: #fff;
  border: 1px solid var(--brand-gold);
  border-radius: 999px;
  padding: 4px 11px;
  white-space: nowrap;
}
body.v3 .qaDayBody strong {
  display: block;
  font-size: 0.95rem;
  font-weight: 800;
  color: var(--brand-blue-deep);
  line-height: 1.3;
}
body.v3 .qaDayBody p {
  margin: 2px 0 0;
  font-size: 0.85rem;
  line-height: 1.45;
  color: var(--ink-soft);
}

/* --- the phone inset: journey-louisa is 360x640 native, shown at or under
   native width so it never upscales — the frame is the honesty device: this
   is a patient's own vertical phone video, presented as exactly that. --- */
body.v3 .qaPhone {
  margin: 0 auto;
  width: 100%;
  max-width: 280px;
}
body.v3 .qaPhone video {
  display: block;
  width: 100%;
  aspect-ratio: 9 / 16;
  object-fit: cover;
  background: #000;
  border-radius: 22px;
  border: 1px solid rgba(184, 131, 47, 0.45);  /* the compare table's hairline */
  box-shadow: var(--shadow-card);
}
body.v3 .qaPhoneCap {
  margin-top: 8px;
  text-align: center;
  font-size: 0.78rem;
  color: var(--ink-soft);
}

/* --- §3 Is it safe: white ground (cream sits above in §2); layout comes from
   the shared .qaSideBy modifier (R64) — video left in DOM = video left drawn,
   the old order-flip rules retired with it. --- */
body.v3 .qaSection.qaSafe { background: var(--v3-white); }
body.v3 .qaFacts {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 14px;
}
body.v3 .qaFacts li { position: relative; padding-left: 26px; }
body.v3 .qaFacts li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 5px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--grad-gold-cta);
  box-shadow: 0 0 0 3px rgba(184, 131, 47, 0.22);
}
body.v3 .qaFacts strong {
  display: block;
  font-size: 0.95rem;
  font-weight: 800;
  color: var(--brand-blue-deep);
  line-height: 1.3;
}
body.v3 .qaFacts p {
  margin: 2px 0 0;
  font-size: 0.85rem;
  line-height: 1.45;
  color: var(--ink-soft);
}

/* --- §4 The Tech Aussies Come For: the reel is cut from 16:9 sources, so
   this one is the 1:1 square card (the insta-post shape) instead of the
   9:16 phone. White ground between sbEnter's cream and the specialists. --- */
body.v3 .qaSection.qaTech { background: var(--v3-cream); }  /* R66, his dictation: cream (sbEnter above flipped white) */
/* §5 finale — R66, his dictation: white, now that the two testimonial
   sections above it both carry the cream. */
body.v3 .qaSection.qaTransform { background: var(--v3-white); }
/* R64: layout via the shared .qaSideBy modifier — video left at every width. */
body.v3 .qaSquare {
  max-width: 420px;
  margin: 0 auto;
}
body.v3 .qaSquare video {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  background: #000;
  border-radius: var(--radius-card);
  border: 1px solid rgba(184, 131, 47, 0.45);  /* the compare table's hairline */
  box-shadow: var(--shadow-card);
}
/* (R67, his dictation: qaTech's Microscope/Waterlase/CAD-CAM/CEREC pills went
   the same way qaSmooth's did in R64 — no .qaPillRow left on the page, rule
   removed.) */

/* ============ R64 — SERVICE TILES PLAY THE BRAND BOOK'S MICRO-VIDEOS ============
   The clip fills the tile's media band; the CSS background still underneath is
   the loading/fallback frame, and the gold scrim ::after keeps painting on top
   (pseudo-element draws after child content). hotel/dining/move have no clip
   and stay stills. */
body.v3 .tileCard .svcBg video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ============ R66 — FINAL TOUCH-UPS (his dictation) ============
   Tones moved in their own blocks above (Choose No.1 cream, sbEnter white,
   realSmiles + reviews cream, qaTech cream, qaTransform white, the price-list
   band sand). Below: the two one-line headings, the bigger right-hand text
   columns in qaSafe/qaTransform, and the denture clip's native frame. */

/* "Is it safe should be in one line" — the base span is display:block
   (style.css:93, read-only); inline keeps the gold italic on the same line. */
body.v3 .qaSafe .sectionHead span { display: inline; }

/* "Receive a free video consult in Australia should be just one line" — same
   block-span cause (style.css:1776). Inline at the heading's own size, and the
   clamp floor is what one line needs inside the 1:1 banner at 390 (the
   reviewsSection precedent, above). */
body.v3 .sbConsult .sbOverlayTitle span { display: inline; font-size: 1em; margin-top: 0; }
body.v3 .sbConsult .sbOverlayTitle { font-size: clamp(0.94rem, 2.9vw, 1.6rem); white-space: nowrap; }
/* R67, his dictation: "All-Inclusive Dental Tourism Packages" in one line —
   same treatment as the consult title above. */
body.v3 .sbTourism .sbOverlayTitle span { display: inline; font-size: 1em; margin-top: 0; }
body.v3 .sbTourism .sbOverlayTitle { font-size: clamp(0.94rem, 2.9vw, 1.6rem); white-space: nowrap; }

/* "increase the font of NABH... and lifetime warranty — there's a little gap"
   + "increase the right section size" (qaTransform): the facts column grows in
   both named sections; qaStay's timeline is deliberately untouched. */
body.v3 .qaSafe .qaFacts, body.v3 .qaTransform .qaFacts { gap: 18px; }
body.v3 .qaSafe .qaFacts strong, body.v3 .qaTransform .qaFacts strong { font-size: 1.08rem; }
body.v3 .qaSafe .qaFacts p, body.v3 .qaTransform .qaFacts p { font-size: 0.95rem; }
@media (max-width: 859px) {
  body.v3 .qaSideBy.qaSideBy .qaFacts { gap: 12px; }  /* doubled class outranks the R64 compact row */
  body.v3 .qaSafe .qaFacts strong, body.v3 .qaTransform .qaFacts strong { font-size: 0.84rem; }
  body.v3 .qaSafe .qaFacts p, body.v3 .qaTransform .qaFacts p { font-size: 0.75rem; }
}

/* The all-on-x clip is 480x600 native — the frame takes the clip's own 4:5 so
   nothing crops (the 9:16 phone ratio belongs to the vertical patient clips). */
body.v3 .qaTransform .qaPhone video { aspect-ratio: 4 / 5; }
body.v3 .qaTransform .qaPhone { max-width: 300px; }

/* ============ R83 — the stripped question sections ============
   His dictation: the repeated eyebrow goes, each answer line is cut to its
   first sentence, and every sub-line under the day rows and the safety facts
   goes with them — "just the headline". Two consequences handled here.

   1. §1's answer line now sits BELOW the video, so its margin flips.
   2. qaStay and qaSafe lose roughly half their text-column height while the
      9:16 clip beside them keeps its own, so the surviving labels centre
      against the frame and grow into the weight the sub-lines used to carry.

   Scoped on .qaTrimmed, which exists only on the grids that were stripped.
   R85 CORRECTION: this block used to say "qaTransform and qaTech keep their
   sub-lines and never match". That is now false for qaTransform — his R85
   dictation stripped it the same way, so it carries .qaTrimmed too and there
   are THREE stripped grids, not two. qaTech still has no .qaFacts at all, and
   index-v3.html is also body.v3 but still carries the OLD markup, so neither
   matches. The .qaSafe/.qaTransform sub-line rules at 1762/1766 above now have
   no call site on this page and are kept for that same rollback page.

   Placement is load-bearing: the .qaSafe rules above have IDENTICAL specificity
   to these, so they are only beaten by coming later in the file. The gap rules
   need more than a tie — .qaSideBy.qaSideBy (1764) outranks three classes — so
   they carry .qaGrid as well. Do not move this block above line 1760. */
body.v3 .qaSmooth .qaLead { margin: 16px auto 0; }
/* the heading's own margin is only 6px (style.css:91) — the answer line used to
   sit in this slot and supply the separation. With it moved below the video the
   heading butted straight onto the frame (0px at 1440, 6px at 390), so §1 pays
   for its own gap now. Scoped: no other sectionHead moves. */
body.v3 .qaSmooth .sectionHead { margin-bottom: 18px; }

body.v3 .qaGrid.qaSideBy.qaTrimmed { align-items: center; }

body.v3 .qaGrid.qaSideBy.qaTrimmed .qaFacts { gap: 26px; }
body.v3 .qaTrimmed .qaFacts strong { font-size: 1.28rem; }
body.v3 .qaTrimmed .qaDay { padding-bottom: 22px; }
/* the rule above ties with .qaDay:last-child (1587) and would win on source
   order, re-adding the trailing gap that rule exists to remove */
body.v3 .qaTrimmed .qaDay:last-child { padding-bottom: 0; }
body.v3 .qaTrimmed .qaDayBody strong { font-size: 1.12rem; }
@media (max-width: 859px) {
  body.v3 .qaGrid.qaSideBy.qaTrimmed .qaFacts { gap: 16px; }
  body.v3 .qaTrimmed .qaFacts strong { font-size: 0.95rem; }
  body.v3 .qaSideBy.qaTrimmed .qaDay { padding-bottom: 14px; }
  body.v3 .qaSideBy.qaTrimmed .qaDayBody strong { font-size: 0.86rem; }
}

/* ============ R68 — TYPE + BASE GROUND ============
   body/html font-family is set on the element in style.css (not via the token),
   so the token remap alone does not reach it. Restate here, v3-scoped. */
body.v3,
body.v3 h1, body.v3 h2, body.v3 h3, body.v3 h4, body.v3 h5, body.v3 h6,
body.v3 p, body.v3 a, body.v3 li, body.v3 span, body.v3 button, body.v3 input,
body.v3 select, body.v3 textarea, body.v3 label {
  font-family: var(--font-sans);
}
/* The page ground itself. R69: band B, not the cool surface. This is also what
   shows through .qaTransform and .qaTech — the two sections that set no
   background of their own in any sheet. Colouring the ground gives them a real
   tone instead of a hole, so they need no rules of their own. */
body.v3 { background: var(--v3-cream-soft); color: var(--v3-ink); }

/* The logo keeps Inter, per the guidelines: it is the brand mark's face. */
body.v3 .logo { font-family: var(--font-logo); }

/* Logo safezone — 65% of the mark's height (guidelines p.6). The header logo
   sets at 34px, so 22px of clear space on every side. */
body.v3 .siteHeader .logo { display: inline-block; padding: 0 22px; }

/* ============ R68 — THE LAST LITERALS ============
   Traced by out/r68-trace.mjs against the rendered page, not read off the
   source: these are style.css's hardcoded greys and creams, which the token
   remap cannot reach because they were never tokens. Mapped by ROLE:
     the near-blacks (#111827 #101828)  -> Charcoal   #1D1D1F
     the mid greys  (#344054 #4B5563)   -> Charcoal at body weight
     the muted greys(#667085)           -> Cool Slate #6E6E73
     the creams     (#FAF6EC #FFF9ED)   -> Soft Surface White / Pure White
     the golds      (#8D6E1B #856915)   -> Charcoal (his gold-text call)     */

/* Near-black + mid-grey text -> Charcoal */
body.v3 .btnGold, body.v3 .svcLbl, body.v3 .toolPill, body.v3 .toolPill i,
body.v3 .ciTitle, body.v3 h3, body.v3 input, body.v3 .caSub {
  color: var(--v3-ink);
}
/* Muted secondary text -> Cool Slate */
body.v3 .wsEyebrow, body.v3 .wsEyebrowDot, body.v3 .wsStatCap {
  color: var(--v3-slate);
}
/* Cream card grounds -> Pure White on the soft canvas */
body.v3 .tgMedia { background: var(--v3-white); }
body.v3 .splitBanner.flip.solo { background: var(--v3-cream-soft); }
/* The italic signature em was gold text -> Charcoal, per his call */
body.v3 em { color: var(--v3-ink); }

/* Final trace pass — the last style.css greys, by role. */
body.v3 .caLead, body.v3 .tgName, body.v3 input, body.v3 .caClose,
body.v3 .privacyRow { color: var(--v3-ink); }
body.v3 .tgTag { color: var(--v3-slate); }

/* R68 addendum — the BORDER literals. The traces above swept text colour and
   backgrounds; hairline borders were never sampled, so style.css's blue-tinted
   #E6ECF8 card edge survived the re-skin. Platinum Grey is exactly this role
   (bands, dividers, card grounds). Scoped here, not fixed at source: style.css
   is shared with v1/v2.

   Swept by ROLE, not by class: style.css scatters the same two hairlines
   (#EAECF0 neutral, #E6ECF8 blue-tinted) across ~10 card selectors, and the
   #F9FAFB section ground is an eighth grey. Listing classes would leave the
   next one to leak, so the borders map wholesale to Platinum Grey. */
body.v3 .recogCard, body.v3 .bentoBox, body.v3 .tgCard, body.v3 .caInnerCard,
body.v3 .docTeamCard, body.v3 .toolPill { border-color: var(--v3-platinum); }
body.v3 .recogSection { background: var(--v3-cream-deep); }

/* .vsText stays mint (#8FE3A8) DELIBERATELY. It is white-ish type sitting on
   moving monument footage inside the cost columns, and its contrast is not a
   guess: the R55 probe composites each column's video frame with the scrim
   alpha at every text run's y and asserts WCAG AA at the worst sampled frame
   (12/12 clear, worst 6.52:1). Recolouring it to a palette grey would break a
   measured accessibility guarantee to satisfy a palette rule. Raise it with
   him as a deliberate exception, do not silently "fix" it. */

/* The sticky bar's gold button is fixed at ITS source (v3-forms.css:988) —
   that file loads after this one and uses `.bottomBtns .bbV3.isGold`, which
   outranks anything scoped only `body.v3` here. Overriding from this file
   would have meant an !important; correcting the literal was cleaner. */

/* ============ R69. THE CALL PILL — charcoal, not white ============
   style.css:285 sets `color:#fff` on the gold gradient and no v3 rule ever
   touched it. Measured against the gradient's three stops that scores
   1.27:1 / 1.94:1 / 3.32:1 — a WCAG AA failure at every stop and effectively
   invisible at the light end. Charcoal scores 5.07 / 8.68 / 13.26 — AA
   throughout. His call; the measurement agrees with him.

   The text-shadow goes with it: a dark shadow behind dark text is no longer
   separating anything, it just muddies the glyph edges. `.callPill` is a bare
   class in style.css, so `body.v3 .callPill` outranks it without !important. */
body.v3 .callPill {
  color: var(--v3-ink);
  text-shadow: none;
}

/* ============ R69. THE SECTION SEAM ============
   The three tones he chose sit 1.045:1 (cream to cream) and 1.081:1 (cream to
   white) apart. That is below the point where a fill change reads as a section
   change — without help the page is one flat sheet on a phone in daylight. The
   reference site he pointed at solves it with a 1px warm rule at the boundary,
   sampled at #E7DFCC. So this divider is load-bearing, not decoration.

   By ROLE, not class — same lesson as the R68 border sweep above: naming
   classes leaves the next section to leak. Every top-level band is covered. */
body.v3 .section,
body.v3 .qaSection,
body.v3 .awardSection,
body.v3 .recogSection,
body.v3 .brandsSection,
body.v3 .footerWrap {
  border-top: 1px solid var(--v3-seam);
}

/* The gold uspBar is itself a divider — a hairline directly under it, or on the
   first band after the hero, would double the rule. */
body.v3 .uspBar + *,
body.v3 .hero + * { border-top: none; }

/* Three boundaries fall between two IDENTICAL fills — sbConsult under the
   packSection CTA, reviewsSection under realSmiles, awardSection under a white
   ctaSolo. A hairline there is not a boundary, it is a rule floating in an
   unbroken field. The seam earns its place only where the tone actually
   changes. (Measured, not guessed: 26 of 29 boundaries do change tone.) */
body.v3 .section.sbSection.sbConsult,
body.v3 .section.cream.reviewsSection,
body.v3 .awardSection { border-top: none; }

/* ============ R77. THE BUTTONS ANSWER THE POINTER ============
   His note: hover and click "are not coming right" on the sub-header trio and
   on the Price List / Cost Calculator buttons. Three separate causes, one per
   block below. Everything here animates transform / opacity / filter only —
   no layout property is touched on a state change.

   Scoped in THIS file, not fixed at source: css/style.css is shared with
   v1/v2/index.html and is never edited (the same reason the R68 border sweep
   lives here). */

/* ---- 1. .btnGold — the mid-page gold CTAs -------------------------------
   style.css:135 transitions `transform, box-shadow` only, but its hover at
   style.css:1088 also sets `filter: brightness(1.05)`. A property absent from
   the transition list snaps instantly, so the fill flashed brighter while the
   lift and shadow eased in behind it — two clocks on one gesture, which is
   what reads as "not right". Adding filter to the list puts them on one.

   The press had the opposite problem: `:active {transform: scale(.97)}` fights
   the hover's translateY(-2px) — the pointer is always hovering when it
   clicks, so the shorthand REPLACED the lift and the button jumped down-and-up
   rather than pressing. Both transforms are now written in one declaration.

   And there was no :focus-visible at all, so keyboard users got the browser's
   default ring on a pill-shaped button. */
body.v3 .btnGold {
  transition: transform 0.2s var(--ease-out),
              box-shadow 0.2s var(--ease-out),
              filter 0.2s var(--ease-out);
}
body.v3 .btnGold:active {
  transform: translateY(-1px) scale(0.97);
  box-shadow: 0 4px 12px rgba(184, 131, 47, 0.32);
  filter: brightness(0.94);
}
body.v3 .btnGold:focus-visible {
  outline: 2px solid var(--v3-blue);
  outline-offset: 3px;
}

/* ---- 2. .bbV3 — the sticky bar's three pills ----------------------------
   Hover changed the border and outline colour and nothing else. Against the
   Charcoal-on-white pill that is a ~1px colour shift at the very edge of the
   control: real, but under the threshold where a pointer feels answered. It
   gains the same small lift the gold CTAs use, and the white pills brighten
   their fill toward the page ground so the change is visible in the middle of
   the button rather than only at its rim.

   The gold pill (Video Consult) had `:hover::before {brightness(1.05)}` but no
   press state on the fill — .bbV3:active moved the pill while the gold stayed
   put. It now darkens on press like every other gold surface. */
body.v3 .bottomBtns .bbV3 {
  transition: border-color 200ms ease-out, outline-color 200ms ease-out,
              color 150ms ease, transform 150ms ease, box-shadow 200ms ease-out;
}
body.v3 .bottomBtns .bbV3:hover {
  transform: translateY(-1px);
  box-shadow: 0 6px 16px rgba(184, 131, 47, 0.22);
}
body.v3 .bottomBtns .bbV3:hover::before { filter: brightness(0.98); }
body.v3 .bottomBtns .bbV3:active {
  transform: translateY(1px) scale(0.98);
  box-shadow: none;
}
body.v3 .bottomBtns .bbV3.isGold:active::before { filter: brightness(0.92); }

/* ---- 3. .subStrip — the three top buttons -------------------------------
   These had NO hover or press state anywhere: style.css:311 styles the resting
   pill and stops, and no v3 rule had ever touched the strip. They are the
   page's first three CTAs and they were the only inert ones.

   Given the same vocabulary as the bar pills — gold fill on hover, charcoal
   text, a 1px press — so the two CTA rows behave identically. The strip is
   fixed chrome, so hover lifts nothing that could collide with the header. */
body.v3 .subStrip button {
  cursor: pointer;
  transition: background-color 180ms ease-out, border-color 180ms ease-out,
              color 180ms ease-out, transform 140ms ease,
              box-shadow 180ms ease-out;
}
body.v3 .subStrip button:hover {
  background: var(--v3-gold-light);
  border-color: var(--v3-gold-dark);
  color: var(--v3-ink);
  box-shadow: 0 4px 12px rgba(184, 131, 47, 0.22);
}
body.v3 .subStrip button:active {
  transform: translateY(1px) scale(0.98);
  background: var(--v3-gold-mid);
  box-shadow: none;
}
body.v3 .subStrip button:focus-visible {
  outline: 2px solid var(--v3-blue);
  outline-offset: 2px;
}

/* Pointer-driven polish only. A touch device has no hover, and leaving these
   on means the tapped button keeps its hover paint until the next tap lands
   elsewhere — the "stuck highlight" bug. */
@media (hover: none) {
  body.v3 .btnGold:hover,
  body.v3 .bottomBtns .bbV3:hover,
  body.v3 .subStrip button:hover {
    transform: none;
    box-shadow: none;
    filter: none;
  }
  body.v3 .subStrip button:hover {
    background: #fff;
    border-color: var(--brand-gold);
  }
  body.v3 .bottomBtns .bbV3:hover::before { filter: none; }
}

@media (prefers-reduced-motion: reduce) {
  body.v3 .btnGold,
  body.v3 .bottomBtns .bbV3,
  body.v3 .subStrip button { transition-duration: 0.01ms; }
  body.v3 .btnGold:hover,
  body.v3 .bottomBtns .bbV3:hover { transform: none; }
}

/* ============ R84 — THE PATIENT-JOURNEY ROW ============
   Row 4 of the Brand Book's cases section, returning to the page after R55
   moved it off. The Book renders it as `grid grid-cols-3 gap-2`; the same
   decision holds here for the same reason — these are long films a viewer
   chooses between, and a marquee would carry them past while someone was still
   reading the caption.

   Scoped to .jRow, which exists only in index.html. index-v3.html is also
   body.v3 and has no journey row, so nothing here can reach the rollback page.

   3-up at every width, like the Book. The tiles are 9:16 portrait sources, so
   three across a phone is genuinely readable at ~110px wide — the same call the
   qaPhone frames already make on this page. */
body.v3 .jRow {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin-top: 22px;
}
body.v3 .jTile {
  position: relative;
  margin: 0;
  overflow: hidden;
  border-radius: 12px;
  background: #000;
  border: 1px solid #EAECF0;   /* the Book's card border, the page's own hairline */
  cursor: pointer;
}
body.v3 .jTile video {
  display: block;
  width: 100%;
  aspect-ratio: 9 / 16;
  object-fit: cover;
  background: #000;
}
/* the caption sits ON the film, bottom-weighted, so the tile stays one object
   rather than a picture with a label under it — and so three tiles keep equal
   height whatever the caption length */
/* NO caption layer, his call at the R84 checkpoint. The first build put one on
   each tile and the shot review killed it: all three films already carry their
   own burned-in titles ("Our Beautiful Australian Patient", the AUSTRALIA road
   sign, the flag-over-map), so our text was a second label over a frame that
   was already labelled — and at 113px wide it rendered at 9.3px, small enough
   to read as noise on the artwork rather than as information.
   The identity is not lost: each tile's aria-label still names its film, so the
   accessible name is unchanged while the visual layer stops competing. */
/* the play mark is the .videoBox idiom, one size down for the smaller tile */
body.v3 .jTile .playBtn {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 38px;
  height: 38px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: rgba(16, 24, 40, 0.65);
  border: 2px solid var(--brand-gold);
  pointer-events: none;
  transition: opacity 180ms ease;
}
body.v3 .jTile .playBtn::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 54%;
  width: 0;
  height: 0;
  transform: translate(-50%, -50%);
  border-left: 11px solid #fff;
  border-top: 7px solid transparent;
  border-bottom: 7px solid transparent;
}
/* once it plays, the mark and the caption get out of the way of the film and
   its native controls — js/main.js sets .playing */
body.v3 .jTile.playing .playBtn { opacity: 0; }
/* (the two .jTile.playing figcaption rules came off with the caption layer —
   nothing renders a figcaption in this row any more) */

@media (min-width: 900px) {
  body.v3 .jRow { gap: 12px; margin-top: 26px; max-width: 760px; margin-inline: auto; }
  body.v3 .jTile .playBtn { width: 54px; height: 54px; }
}

/* ============ R91 — the shortened AU/IN evidence lists ============
   His dictation: two lines a side, centred under the price — lifetime
   warranty and the coordinator/CRM pair. The AU strike and the India green
   are the existing .costCol rules above; only the axis changes. The hairline
   seam stays: it still separates the price argument from its evidence. */
body.v3 .costCol .vsList.vsShort {
  text-align: center;
  justify-items: center;
  gap: 9px;
}
body.v3 .costCol .vsList.vsShort .vsText {
  font-size: 0.82rem;
  font-weight: 600;
}
@media (min-width: 768px) {
  body.v3 .costCol .vsList.vsShort .vsText { font-size: 0.98rem; }
}

/* R91: the specialist cards carry a department role under the name. Quiet by
   design — the name leads, the role explains. */
body.v3 .doctorsSection .docCard .docRole {
  padding: 0 8px 12px;
  margin-top: -8px;
  font-size: clamp(0.62rem, 2.1vw, 0.72rem);
  font-weight: 600;
  line-height: 1.3;
  /* Cool Slate, the palette's secondary — the same colour .docLeadSpec and
     .helpingText use in this section. NOT --v3f-ink-soft (#667085): that is
     the forms surface's blue-tinted grey, and it is the exact value an
     earlier round already migrated OFF (see the note at the palette map
     below). It rendered visibly blue beside the slate lines around it. */
  color: var(--v3-slate);
  text-transform: none;
  letter-spacing: 0;
}

/* R91: .costSection .sectionHead has been `color:#fff` since the section was
   NAVY; R61 turned it sand and the rule went stale but stayed invisible —
   the section carried the gold sticker, not a heading, until R91 put one back.
   White on sand measured 1.1:1. Ink and the dark gold restore the same
   treatment every other light section's heading gets. */
body.v3 .costSection .sectionHead { color: var(--v3-ink); }
/* (the R91 span-to-charcoal line that sat here is gone — R92's global
   heading-accent rule at the end of this file owns span colour now) */

/* ============ R92 — the heading accents go GOLD ============
   His dictation, heading by heading ("a gold shade that we use"): the italic
   span words in every section head take the line-work gold. This REVERSES the
   earlier "gold text becomes charcoal" call FOR HEADING ACCENTS ONLY — the
   --v3-gold-text / --brand-gold-dark remaps stay, because their other call
   sites (sticky bar, pills, subStrip) were not part of this dictation.
   #B8832F probed at ~4.3:1 on the sand grounds; headings are large-bold, so
   the bar is 3:1. Every section is light under the v3 skin (probed live —
   even .section.dark is reskinned cream), so one gold works everywhere.
   This block must stay LAST for the spans: it out-cascades the per-section
   span rules above (:331, :351, :359, :1494) at equal specificity. */
body.v3 .sectionHead span,
body.v3 .caHead span { color: var(--v3-gold-dark); }
/* qaSmooth: R92 narrowed its span to "All-on-4" mid-sentence; block would
   orphan ", Really?" onto its own line */
body.v3 .qaSmooth .sectionHead span { display: inline; }
/* the three spans the base rule cannot reach — .section.dark (:331),
   .compareSection (:1494) and the smilesSection's dark-family rule carry
   higher specificity, so R92 names them explicitly */
body.v3 .section.dark .sectionHead span,
body.v3 .compareSection .sectionHead span,
body.v3 .smilesSection .sectionHead span { color: var(--v3-gold-dark); }
