/* ============================================================
   Pack — clinical / style.css

   Two layers below:
     1. SKELETON DEFAULT STYLE — the cinematic-instrument skeleton's
        baseline component finish, ported in from the brusco theme
        promotion on 2026-06-28. ~4200 lines: body typography, hero,
        wheel, statement pin + horizontal scroll, composite, wall_of_love,
        faq, gallery + runway, team_editorial + hero-grad + cards row,
        transition_ring, footer + mask, menu_overlay.
     2. PACK OVERLAY (further down, after the delimiter) — the
        clinical pack's slate-canvas + cool-accent overrides. ~500
        lines. These win the cascade because they come after.

   Cascade discipline: wrapped in @layer pack so structure rules
   from core.css never collide. Per-client palette overlays are
   unlayered and always win.

   Motion rules live in motion.css. The divider's static visual
   properties (height, width, background, border-radius, margin)
   stay here so the divider remains a visible line if a future
   client opts out of motion entirely.
   ============================================================ */

/* Kill trackpad rubber-band bounce at scroll boundaries. Hard requirement
   tied to the services-stack work — no elastic overshoot at page top/bottom
   nor inside any sticky transition. */
html, body { overscroll-behavior: none; }

@layer pack {

  /* ---- Body typography defaults ---------------------------- */
  body {
    font-family: var(--font-body);
    font-size: var(--text-body);
    line-height: var(--leading-body);   /* preserves pre-existing undefined-fallback */
    color: var(--ink);
    background: var(--bg);
  }

/* ============================================================
   Fullscreen menu overlay
   ------------------------------------------------------------
   Layout/proportion measured directly from live Brusco at 1440x900
   via Chrome DevTools Protocol (the SSR HTML hid the media node
   inside a v-if; the CSS file alone was misleading about the
   nesting). Brand axes stay ours: Inter at weight 300 for the
   huge-light-uppercase register, --accent for the current-item
   highlight, --bg for the cream surface.

   Source measurements at 1440 viewport (verified via
   getBoundingClientRect on live DOM):
     - pre column:  x=28.8  width=333.6  (= var(--col-3))
     - media tile:  x=145.6 width=184.5 height=115.2
       (margin-left ~100.5 = var(--col-1); max-width = col-2*0.85)
     - label:       x=362.4 (right at pre.right)

   The media tile is a CHILD of .menu__item__pre — it sits INSIDE
   the eyebrow column, after the "01"/"02"/"03"/"04" index. The
   label is the next sibling of pre.

   Grid math mirrors Brusco's: --col = (96vw - 11*16px) / 12
   so at 1440 viewport: col=100.5, col-1=100.5, col-2=217,
   col-3=333.6 — identical to the live measurement.

   Behaviors:
     - Backdrop fade transparent → rgba(0,0,0,.3) over 1s in, 2s out
     - Panel translateY(-100%) → 0 over 1.2s expo-out
     - Tile border-radius 10rem (100px pill) → 2rem (20px square)
       over 1s expo-out on parent hover
     - Label 3D flip: perspective 500px, two-copy rotateX flip
     - Inter-item hairlines: scaleX(0)→1 staggered --delay×0.15s
   ============================================================ */

/* Local grid math — mirrors Brusco's --col at 12-col desktop. */
.hd-menu-wrap {
  --menu-offset: clamp(16px, 2vw, 40px);
  --menu-offset-double: calc(var(--menu-offset) * 2);
  --menu-gutter: 16px;
  --menu-col:   calc((96vw - 11 * var(--menu-gutter)) / 12);
  --menu-col-1: var(--menu-col);
  --menu-col-2: calc(var(--menu-col) * 2 + var(--menu-gutter));
  --menu-col-3: calc(var(--menu-col) * 3 + var(--menu-gutter) * 2);
  /* row height shared by pre, media, label */
  --menu-row-h: calc(min(10vw, 20vh) - var(--menu-offset));

  position: fixed;
  top: 0;
  left: 0;
  height: 100dvh;
  width: 100vw;
  pointer-events: none;
  background: transparent;
  z-index: 60;                       /* sits above existing nav */
  will-change: background;
  transition: background 2s cubic-bezier(.19, 1, .22, 1);
}
.is-menu-open .hd-menu-wrap {
  background: rgba(0, 0, 0, 0.3);
  pointer-events: auto;
  transition: background 1s cubic-bezier(.19, 1, .22, 1);
}

.hd-menu {
  background: var(--bg);
  display: flex;
  flex-direction: column;
  height: 100dvh;
  position: fixed;
  inset: 0;
  width: 100vw;
  will-change: transform;
  transform: translateY(-100%);
  overflow: hidden;
  transition: transform 1.2s cubic-bezier(.19, 1, .22, 1);
}
.is-menu-open .hd-menu { transform: translateY(0); }

.hd-menu__inner {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  position: fixed;
  inset: 0;
  padding: var(--menu-offset-double) var(--menu-offset) var(--menu-offset);
  width: 100vw;
}

/* ---- Top bar lives in the nav variant (sections/variants/nav-elongated)
       when sections.menu_overlay is present. See `.hd-nav-wrap--menu`
       block below for that bar's styling. The panel itself has no
       internal top bar. -------------------------------------------- */

/* ---- Items list -------------------------------------------- */
.hd-menu__list {
  display: flex;
  flex-direction: column;
  flex: 1;
  list-style: none;
  padding: 0;
  margin: 0;
}

.hd-menu__item {
  align-items: center;
  display: flex;
  flex: 1;
  justify-content: center;
  overflow: hidden;
  position: relative;
  font-family: var(--menu-display-font);
  font-weight: var(--menu-display-weight);
  font-size: calc(min(16vw, 24vh) - var(--menu-offset));
  line-height: 1;
  text-transform: uppercase;
  color: var(--ink);
}
@media screen and (min-width: 960px) {
  .hd-menu__item {
    font-size: calc(min(12vw, 24vh) - var(--menu-offset));
    justify-content: flex-start;
  }
}
.hd-menu__item.is-current { color: var(--accent); }

/* Hairlines — scaleX-reveal staggered by --delay.
   To avoid double rules between adjacent items, draw the top rule on every
   item via ::before, and the bottom rule ONLY on the last item via ::after.
   Result: 5 visible rules for 4 items (one above each + one below the last). */
.hd-menu__item::before,
.hd-menu__item:last-child::after {
  content: "";
  background: var(--ink);
  height: 1px;
  left: 0;
  position: absolute;
  width: 100%;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 1s cubic-bezier(.19, 1, .22, 1);
}
.hd-menu__item::before { top: 0; }
.hd-menu__item:last-child::after { bottom: 0; }
.is-menu-open .hd-menu__item::before,
.is-menu-open .hd-menu__item:last-child::after {
  transition: transform 2s cubic-bezier(.19, 1, .22, 1) calc(var(--delay, 0) * 0.15s);
  transform: scaleX(1);
}

.hd-menu__link {
  align-items: center;
  display: flex;
  justify-content: flex-start;
  width: 100%;
  color: inherit;
  text-decoration: none;
}
@media screen and (max-width: 959.98px) {
  .hd-menu__link { justify-content: center; }
}

/* ---- Pre column: contains the eyebrow index + the media tile -- */
.hd-menu__item__pre {
  position: relative;
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  width: 100%;
  height: var(--menu-row-h);
}
@media screen and (min-width: 960px) {
  .hd-menu__item__pre {
    width: var(--menu-col-3);          /* col-3 = 333.6 at 1440 */
  }
}

.hd-menu__item__index {
  display: inline-block;
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 400;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink);
  line-height: 1;
  align-self: flex-end;
  padding-bottom: calc(var(--menu-offset) / 2);
}

/* ---- Media tile (child of pre) — the pill→rectangle morph --- */
.hd-menu__item__media {
  display: none;
  align-self: center;
  margin-left: var(--menu-col-1);             /* 100.5 at 1440 */
  max-width: calc(var(--menu-col-2) * 0.85);  /* 184.5 at 1440 */
  height: var(--menu-row-h);                   /* 115.2 at 1440 */
  aspect-ratio: 1920 / 1080;                   /* matches live computed style */
  flex: 0 0 auto;
  /* Brusco uses html{font-size:10px} so its `10rem`/`2rem` are 100px/20px.
     We keep our default rem base; use absolute px so the morph endpoints
     match the source exactly: 100px pill at rest → 20px square on hover. */
  border-radius: 100px;
  overflow: hidden;
  background: var(--border);                   /* fallback while media loads */
  transition: border-radius 1s cubic-bezier(.19, 1, .22, 1);
  will-change: border-radius;
}
@media screen and (min-width: 960px) {
  .hd-menu__item__media { display: flex; }
}
.hd-menu__item__media__el {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* The morph — only the radius changes; dimensions stay fixed */
.hd-menu__item:hover .hd-menu__item__media,
.hd-menu__item.is-hover .hd-menu__item__media {
  border-radius: 20px;
}

/* ---- Label (sibling of pre) — 3D flip on hover ------------- */
.hd-menu__item__label {
  position: relative;
  perspective: 500px;
  transition: color 0.3s cubic-bezier(.19, 1, .22, 1);
  flex: 1 1 auto;
}
@media screen and (min-width: 960px) {
  .hd-menu__item__label {
    height: var(--menu-row-h);
    line-height: calc(var(--menu-row-h) - var(--menu-offset) / 2);
  }
  .hd-menu__item__label::after {
    content: attr(data-label);
    position: absolute;
    left: 0;
    top: 0;
    transform: translateY(200%) rotateX(-30deg);
    transition: transform 1s cubic-bezier(.19, 1, .22, 1);
  }
  .hd-menu__item:hover .hd-menu__item__label::after,
  .hd-menu__item.is-hover .hd-menu__item__label::after {
    transform: translateY(0) rotateX(0deg);
  }
}
.hd-menu__item__label span {
  display: inline-flex;
  transition: transform 1s cubic-bezier(.19, 1, .22, 1);
}
@media screen and (min-width: 960px) {
  .hd-menu__item:hover .hd-menu__item__label span,
  .hd-menu__item.is-hover .hd-menu__item__label span {
    transform: translateY(-200%) rotateX(30deg);
  }
}

/* ---- Footer row ------------------------------------------- */
.hd-menu__footer {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
  justify-content: space-between;
  align-items: center;
  padding-top: var(--sp-5);
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
}
.hd-menu__footer__item {
  text-decoration: none;
  color: inherit;
  opacity: 0.85;
  transition: opacity 0.3s cubic-bezier(.19, 1, .22, 1);
}
.hd-menu__footer__item:hover { opacity: 1; }

/* ============================================================
   Nav variant — MENU/CLOSE + CONTACT bar (Brusco-style)
   ------------------------------------------------------------
   Emitted by nav-elongated.html.j2 when `sections.menu_overlay`
   is present. Floats above the menu panel at z-index 70 so the
   same trigger is reachable in both states. CLOSE replaces MENU
   when .is-menu-open is on <html>.
   ============================================================ */
.hd-nav-wrap--menu {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  z-index: 70;                              /* above .hd-menu-wrap (z-60) */
  padding: var(--menu-offset, clamp(16px, 2vw, 40px));
  pointer-events: none;                     /* let children handle clicks */
}
/* Override default .hd-nav pill chrome — this variant is a plain
   transparent bar with just two text labels (MENU/CLOSE + CONTACT). */
.hd-nav.hd-nav--menu {
  background: transparent;
  border-radius: 0;
  box-shadow: none;
  max-width: none;
  width: 100%;
  margin: 0;
  padding: 0;
  pointer-events: none;
}
.hd-nav--menu .hd-nav__row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0;
}
.hd-nav__menu-trigger,
.hd-nav__contact {
  pointer-events: auto;
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
  position: relative;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  height: 1.2em;
  line-height: 1.2;
  text-decoration: none;
  transition: color 0.4s cubic-bezier(.19, 1, .22, 1);
}
/* When the hero is rendered, the nav floats OVER it (no cream gutter at top).
   Switch the nav text to white so it reads against the hero media. When the
   menu opens it goes back to ink against the cream menu surface. */
body[data-hd-has-hero] .hd-nav__menu-trigger,
body[data-hd-has-hero] .hd-nav__contact {
  /* Interpolate ink → white as the hero scroll-expand progresses.
     At rest the nav sits on the cream strip above the card → ink reads cleanly.
     At full-bleed the hero fills the viewport behind the nav → white reads cleanly.
     --hd-hero-progress is set on documentElement by motion.js. */
  color: color-mix(in srgb,
    var(--ink) calc((1 - var(--hd-hero-progress, 0)) * 100%),
    var(--accent-ink, #fff));
  /* Drop-shadow fades in with progress so the cream-strip state stays clean. */
  text-shadow:
    0 1px 2px rgba(0, 0, 0, calc(var(--hd-hero-progress, 0) * 0.55)),
    0 0 1px rgba(0, 0, 0, calc(var(--hd-hero-progress, 0) * 0.45));
}
/* When the menu opens, force ink against the cream menu backdrop regardless of progress. */
html.is-menu-open body[data-hd-has-hero] .hd-nav__menu-trigger,
html.is-menu-open body[data-hd-has-hero] .hd-nav__contact {
  color: var(--ink);
  text-shadow: none;
}
body[data-hd-has-hero].is-menu-open .hd-nav__menu-trigger,
body[data-hd-has-hero].is-menu-open .hd-nav__contact,
html.is-menu-open body[data-hd-has-hero] .hd-nav__menu-trigger,
html.is-menu-open body[data-hd-has-hero] .hd-nav__contact { color: var(--ink); }
.hd-nav__menu-trigger__inner {
  display: inline-flex;
  position: relative;
  height: 1.2em;
}
/* MENU / CLOSE label flip driven by .is-menu-open on <html> */
.hd-nav__menu-trigger__open  { display: inline-block; height: 1.2em; line-height: 1.2; }
.hd-nav__menu-trigger__close { display: none;        height: 1.2em; line-height: 1.2; }
.is-menu-open .hd-nav__menu-trigger__open  { display: none; }
.is-menu-open .hd-nav__menu-trigger__close { display: inline-block; }
/* CONTACT — rising-replace hover (Brusco data-link pattern) */
.hd-nav__contact__inner {
  display: inline-flex;
  position: relative;
  transition: transform 0.55s cubic-bezier(.19, 1, .22, 1);
}
.hd-nav__contact__inner::after {
  content: attr(data-label);
  position: absolute;
  top: 110%;
  left: 0;
  white-space: nowrap;
}
.hd-nav__contact:hover .hd-nav__contact__inner {
  transform: translateY(-110%);
}

/* ============================================================
   Page-content slide (driven by .is-menu-open on <html>)
   The wrapper always exists (page.html.j2); the transition only
   fires when the overlay is open. No effect on builds without
   the overlay slot.
   ============================================================ */
.hd-page-content {
  transition: transform 1.5s cubic-bezier(.19, 1, .22, 1);
  transform: translateY(0);
  will-change: transform;
}
.is-menu-open .hd-page-content {
  transform: translateY(15vh);
  pointer-events: none;
}

/* ============================================================
   Expand panel — scroll-pinned section with a centered card that
   grows from a small frame to full-bleed (Lava-style transition)
   ------------------------------------------------------------
   Structure: section is 200vh tall (scroll runway). Inside is a
   sticky stage (100vh, mint surface). Inside that is the card,
   which interpolates from a small framed rectangle to 100vw × 100vh
   driven by --hd-expand-progress (0 → 1) set by motion.js per rAF.

   --hd-expand-progress = 0 at top of section, 1 when the card has
   fully expanded. After progress hits 1, the user scrolls through
   the remaining runway before exiting the sticky.
   ============================================================ */
/* (Statement section below now owns the scroll-pinned pill expansion —
   no separate panel section.) */

/* ============================================================
   Statement — bold inline-media headline (NOWW style)
   ------------------------------------------------------------
   Huge editorial display: uppercase sans-serif on multiple lines,
   mixed weights (light default + heavy bold on accent words),
   with an inline pill-shaped media container that flows with the
   text. The pill sits inline-block so the surrounding words wrap
   around it naturally.

   Display font: uses --font-display (Bricolage Grotesque in the
   classic pack → falls back to body Inter in brusco) at weight
   300 with weight 800 for .is-bold accent words.

   Pill sizing: height matches text cap height so the media reads
   as a "word" embedded mid-sentence. Aspect ~2.5:1.
   ============================================================ */
/* Scroll-pinned pill expansion lives INSIDE the statement section. The
   section becomes a 230vh runway; an inner sticky stage pins for 100vh
   while the inline pill's absolute twin grows from inline-size →
   100vw × 100vh. motion.js sets --hd-statement-progress (0 → 1) per rAF,
   pre-eased with easeOutCubic so the pill SETTLES into full-bleed.

   ONE clean continuous motion — Lava mechanic:
     - The pill is just a clip-window. Its width, height, border-radius,
       and translate scroll-link to progress. That's the only motion the
       user sees throughout the whole runway.
     - The composite (THENN content) is a stationary layer behind the
       pill, anchored to viewport coords (0,0)→(100vw,100vh). As the pill
       grows, more of the composite is revealed THROUGH it. Composite
       elements never fade or translate — they're just clipped less.
     - At the very start (0 → eased 0.50, ≈ raw 0..20%) the smile image
       inside the pill crossfades to 0 and the headline behind the pill
       fades to 0 over the same window — one synced handoff, not two
       sequential beats. After that, it's pure pill growth.
     - 30vh hold at p=1 so the composite reads cleanly before unpin. */
.hd-statement {
  --hd-statement-progress: 0;
  /* Inline pill dimensions — computed from the display font size and
     the 4.5:1 aspect ratio. These are the START of the interpolation. */
  --hd-pill-h: calc(clamp(48px, 8vw, 120px) * 0.82);
  --hd-pill-w: calc(var(--hd-pill-h) * 4.5);
  --hd-statement-fill-bg: #d6e9d9;
  position: relative;
  width: 100%;
  /* 100vh entry/pin + 100vh expand + 30vh hold-and-read.
     When the section also hosts the services card-deck (--has-deck), the
     runway extends 300vh further to give 100vh per card-transition × 3
     transitions, hosting the entire pill→composite→deal sequence inside
     ONE pinned section. See .hd-statement--has-deck override below. */
  height: 230vh;
  margin: clamp(60px, 9vh, 120px) 0;
  padding: 0;
  background: var(--bg);
}
/* Extended runway when the deck is present. Sticky child sticks for
   (height − 100vh) = 630vh: 100vh expand + 30vh hold + 450vh deal +
   50vh card-4-hold-before-release. ENTRY phase removed from the scroll
   budget — deck arrives fully assembled (200ms CSS fade) when the user
   crosses into the deck phase, and the WHOLE deal budget is reallocated
   to the 3 transitions (150vh each, was 90vh — 67% more deliberate). */
.hd-statement--has-deck { height: 730vh; }
.hd-statement__sticky {
  position: sticky;
  top: 0;
  width: 100%;
  height: 100vh;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
}
/* Cream margin strips — mask the cards' drop shadow that darkens the
   LEFT/RIGHT viewport margins during the deck pin. Active ONLY when
   --hd-deck-progress reaches 0.98+ (all dealing cards exited, card 4
   alone at center). During the deal these are opacity 0 so they
   cannot cover a flying card — verified against motion.js fly geometry
   (EXIT_X_VW = 26vw, opa = 0 by FLY_END = 0.95 within each transition;
   last card invisible by pDeck ≈ 0.983). Touches nothing on the cards
   or their motion. */
.hd-statement__sticky::before,
.hd-statement__sticky::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 3vw;
  background: var(--bg);
  z-index: 11;
  pointer-events: none;
  opacity: clamp(0, calc((var(--hd-deck-progress, 0) - 0.98) * 50), 1);
}
.hd-statement__sticky::before { left: 0; }
.hd-statement__sticky::after  { right: 0; }
/* Bottom seam mask — covers the cream gap below card 4 where the box-shadow
   halo from the cards darkens the cream. Card 4 (94vw × 88vh) is centered in
   the sticky, so its bottom edge sits at sticky_bottom − 6vh. The mask sits
   at sticky bottom with height 7vh so it covers the strongest shadow zone
   (~6-8vh band of darkened cream) without overlapping card 4 itself. Same
   opacity gate as the side strips → invisible during the deal, fades in by
   pDeck = 1 (card 4 in hold; no card travels into this zone). */
.hd-statement__seam-mask {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  /* Height = exactly card 4's bottom margin (6vh — card is 88vh centered in
     100vh sticky). Strip's top edge sits AT card 4's bottom edge — no overlap
     with the card, no clipping of the rounded bottom corners. */
  height: 6vh;
  background: var(--bg);
  z-index: 11;
  pointer-events: none;
  opacity: clamp(0, calc((var(--hd-deck-progress, 0) - 0.98) * 50), 1);
}
/* Card-corner fillers — two 32×32 pseudos that paint ONLY the small exposed
   cream triangle OUTSIDE card 4's bottom-corner curves. Card 4 has
   border-radius: 32px ([style.css:1889](library/themes/brusco/style.css#L1889));
   each bottom-corner leaves a small "anti-corner" of cream visible outside the
   card's curve. The pseudos sit at (x = 3vw / right 3vw, y = -32px from
   seam-mask top = card_bottom − 32), exactly aligned with card 4's bottom
   corner bounding boxes.

   Each background uses a radial-gradient with a SHARP step at radius 32px,
   centered on the card's INNER corner point. Inside the disc (where card 4
   paints) the gradient is transparent → no overlap with the card. Outside
   the disc (the exposed cream triangle) the gradient is solid var(--bg) →
   masks the shadow-tinted cream. This is the geometric inverse of card 4's
   curve and covers the exposed cream WITHOUT clipping any card pixel. */
.hd-statement__seam-mask::before,
.hd-statement__seam-mask::after {
  content: "";
  position: absolute;
  top: -32px;
  width: 32px;
  height: 32px;
  pointer-events: none;
}
.hd-statement__seam-mask::before {
  left: 3vw;
  background: radial-gradient(circle 32px at 100% 0%,
                              transparent 32px,
                              var(--bg) 32px);
}
.hd-statement__seam-mask::after {
  right: 3vw;
  background: radial-gradient(circle 32px at 0% 0%,
                              transparent 32px,
                              var(--bg) 32px);
}
.hd-statement__container {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 clamp(24px, 5vw, 80px);
  text-align: center;
  position: relative;
  z-index: 1;
  /* Single short fade tied to the same handoff as the smile image.
     --hd-statement-progress is already eased (easeOutCubic). The
     headline opacity reaches 0 when eased progress hits 0.45, which
     corresponds to ~18% of the raw scroll runway — early enough that
     the headline is gone before the pill is large enough to make its
     edges look awkward. No scale shrink — opacity only, one curve,
     part of the same motion as the pill takeover. */
  opacity: calc(1 - var(--hd-statement-progress) / 0.45);
  will-change: opacity;
}

.hd-statement__eyebrow {
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  opacity: 0.6;
  margin: 0 0 clamp(32px, 5vh, 56px);
}

.hd-statement__text {
  font-family: var(--font-display, var(--font-body));
  font-size: clamp(48px, 8vw, 120px);
  line-height: 1.02;
  letter-spacing: -0.025em;
  text-transform: uppercase;
  color: var(--ink);
  font-weight: 300;
  margin: 0;
  /* Stack each line vertically — explicit line structure (not auto-wrap)
     so the layout always reads as the reference no matter the viewport. */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.06em;
}
.hd-statement__line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0 0.32em;
}

.hd-statement__word {
  display: inline-block;
  /* keep cap-height centered next to the media pill */
  line-height: 1;
}
.hd-statement__word.is-bold {
  font-weight: 800;
  letter-spacing: -0.035em;   /* tighter, like in the reference */
}

.hd-statement__media {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Height = cap height of the surrounding type so the pill
     reads as inline with the words. cap-height ≈ 0.74em. */
  height: 0.82em;
  /* Long capsule (~4.5:1). The intended content is a tight close-up
     of teeth/mouth so the source asset is naturally letterboxed wide. */
  aspect-ratio: 4.5 / 1;
  border-radius: 999px;       /* full pill (capsule) */
  overflow: hidden;
  background: var(--border);
  vertical-align: middle;
  transform: translateY(-0.04em);
}
.hd-statement__media-el {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Bias the crop toward the lower portion of the source image so the
     mouth / smile area lands in the visible window even if the slot
     ships a full-face photo. Real client config should ship a clip
     that's already a smile close-up. */
  object-position: 50% 78%;
  display: block;
}

/* Expanding pill — absolute overlay, centered in the sticky stage.
   Sized to match the inline pill at progress 0; grows to full viewport
   at progress 1. Sits on top of the headline (z-index 2) so the inline
   pill underneath stays in flow (preserving the headline layout)
   while the expansion happens visually on top. */
.hd-statement__expand {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  z-index: 2;
}
.hd-statement__expand-pill {
  position: relative;
  width: calc(
    var(--hd-pill-w) + (100vw - var(--hd-pill-w)) * var(--hd-statement-progress)
  );
  height: calc(
    var(--hd-pill-h) + (100vh - var(--hd-pill-h)) * var(--hd-statement-progress)
  );
  border-radius: calc(999px * (1 - var(--hd-statement-progress)));
  background: var(--hd-statement-fill-bg);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  will-change: width, height, border-radius, transform;
  /* The inline pill sits in line 2 "Your [pill]", so its center is offset
     right of viewport center. JS measures the offset once and sets these
     vars on the section; the pill translates to inline-pill position at
     progress 0, back to viewport center at progress 1. */
  transform: translate(
    calc(var(--hd-pill-origin-x, 0px) * (1 - var(--hd-statement-progress))),
    calc(var(--hd-pill-origin-y, 0px) * (1 - var(--hd-statement-progress)))
  );
}
.hd-statement__expand-image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 78%;
  /* z-index 2 keeps the smile photo ABOVE the composite (.hd-statement
     __expand-content has z-index 1) until the opacity ramp pulls it
     out. Without this, the composite's red-tinted portrait card peeks
     through behind the pill at progress 0 as a "red hairline" — the
     image was being painted UNDER the composite because both inherit
     z-auto in DOM order otherwise. */
  z-index: 2;
  /* ONE quick handoff at the start. --hd-statement-progress is already
     eased (easeOutCubic), so opacity 0 at eased=0.50 corresponds to
     ~20% of the raw scroll runway. Before that the small pill reads
     as a smile; after the handoff the composite shows through and the
     rest of the runway is pure pill growth — no further fades. */
  opacity: calc(1 - var(--hd-statement-progress) / 0.50);
}
/* Composite layer — sized 100vw × 100vh and POSITIONED at the inverse of
   the pill's screen top-left, so the composite is anchored to viewport
   coords (0,0)→(100vw,100vh) regardless of the pill's current size or
   position. The growing pill is just a clip-window over a stationary
   layer — the Lava mechanic. No internal stagger, no opacity ramps —
   the visible motion is the pill outline only. */
.hd-statement__expand-content {
  position: absolute;
  width: 100vw;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
  /* Pill's screen top-left (within the sticky stage, which IS the viewport
     during the pin):
       x = 50vw - pw/2 + originX * (1 - p)
       y = 50vh - ph/2 + originY * (1 - p)
     Composite's local position inside the pill is the NEGATIVE of that
     so its viewport position stays at (0,0).
     pw = pill_w + (100vw - pill_w) * p
     ph = pill_h + (100vh - pill_h) * p */
  left: calc(
    -50vw
    + (var(--hd-pill-w) + (100vw - var(--hd-pill-w)) * var(--hd-statement-progress)) / 2
    - var(--hd-pill-origin-x, 0px) * (1 - var(--hd-statement-progress))
  );
  top: calc(
    -50vh
    + (var(--hd-pill-h) + (100vh - var(--hd-pill-h)) * var(--hd-statement-progress)) / 2
    - var(--hd-pill-origin-y, 0px) * (1 - var(--hd-statement-progress))
  );
  will-change: left, top;
}
.hd-statement__expand-content > .hd-composite {
  width: 100vw;
  margin: 0;
  /* Inherit the pill's cream surface instead of stacking another solid
     bg above the smile image — kills the visible "cream rectangle"
     that muddied the mid-expansion. */
  background: transparent;
}
.hd-statement__expand-text {
  font-family: var(--font-display, var(--font-body));
  font-size: clamp(40px, 6vw, 88px);
  line-height: 1.05;
  letter-spacing: -0.025em;
  color: var(--ink);
  font-weight: 300;
  margin: 0;
}

@media (prefers-reduced-motion: reduce) {
  .hd-statement { height: auto; }
  .hd-statement__sticky {
    position: static; height: auto;
    padding: clamp(60px, 12vh, 140px) 0;
  }
  .hd-statement__container { opacity: 1; }
  .hd-statement__expand { display: none; }
  .hd-statement__media-el { animation: none; }
}

/* ============================================================
   Team wheel — Brusco /team carousel (scope team-block-19.css)
   ------------------------------------------------------------
   Mechanic:
     - .hd-wheel-section is a clipping band (overflow hidden) with
       top-padding so only the lower arc of the disc shows through.
     - .hd-wheel is the static, full-disc-size container.
     - .hd-wheel-shell sits inside, same dimensions, rotates around
       its center to bring different cards into view.
     - .hd-wheel-card elements are absolutely positioned by motion.js
       at mount: each card lands on the disc edge at angle θ=(360/N)*i
       and rotates outward so portraits stay upright relative to disc.
     - Click ←/→: motion.js spins the shell by ±(360/N) over ~720ms
       with cubic-bezier(.87, 0, .13, 1) S-curve (Brusco's accent ease).
     - On inview: settle from rotate(5°) → rotate(0°), 2s expo-out.

   Disc scaled down from Brusco's 380vw to 200vw because we ship 12
   demo cards instead of Brusco's 44 (same 30° vs 8.18° per card gives
   visually comparable density in the visible arc).
   ============================================================ */
/* Outer wrapper holds wheel-section + nav as siblings (Brusco mirror).
   Margin-bottom uses Brusco's --sp-medium calc(1.7094vw + 33.59px) so
   the spacing to whatever follows matches the reference. */
.hd-wheel-wrapper {
  position: relative;
  margin-bottom: calc(1.7094vw + 33.59px);  /* Brusco --sp-medium */
}

.hd-wheel-section {
  position: relative;
  width: 100vw;
  /* EXACT Brusco values from block-00.css + team-block-19.css:
     - height: --col-6 = 6×col + 5×gutter = 48vw − 8px at viewport ≥1280
     - padding-top: --col-3 = 24vw − 12px
     At 1440: section = 683.2px, padding-top = 333.6px. */
  height: clamp(540px, calc(48vw - 8px), 920px);
  padding-top: clamp(180px, calc(24vw - 12px), 460px);
  overflow: hidden;
  background: var(--bg);
  box-sizing: border-box;
  margin-bottom: 0;
}

.hd-wheel {
  position: absolute;
  left: 50%;
  /* NO explicit top: — uses the static position, which IS at the section
     content-area top edge (after padding-top). This is exactly how
     Brusco's .wheel sits inside its .wheel-section. */
  width: 380vw;   /* Brusco's exact disc width */
  height: 380vw;
  transform: translateX(-50%);
  pointer-events: none;
}

.hd-wheel-shell {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  transform-origin: center center;
  /* motion.js sets the initial rotation inline so the middle card lands at
     top center. Click rotations also write the inline transform.
     The .is-spinning class enables a 0.72s S-curve transition during
     a click; otherwise the transform snaps (initial mount). */
  will-change: transform;
}

.hd-wheel-shell.is-spinning {
  transition: transform 0.72s cubic-bezier(.87, 0, .13, 1);
}

.hd-wheel-card {
  position: absolute;
  top: 0;
  left: 0;
  /* Card width = --col-3 (Brusco's exact value) = 24vw − 12px ≈ 333.6 @ 1440.
     Media inside fills 100% and uses aspect-ratio 604/843. */
  width: clamp(240px, calc(24vw - 12px), 460px);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-decoration: none;
  color: var(--ink);
  pointer-events: auto;
  /* z-index assigned by motion.js based on distance from current center,
     so the centered card sits in front of its neighbors. */
  will-change: transform;
}

.hd-wheel-card__media {
  width: 100%;
  aspect-ratio: 604 / 843;
  border-radius: 32px;     /* Brusco --border-radius = 3.2rem at html font 10px */
  overflow: hidden;
  /* Was var(--border) = rgb(214,219,225) light blue-gray, ~41 units off
     the page cream var(--bg) = rgb(255,252,247). The photo's cutout
     transparency around the dentists revealed that blue-gray, painting
     the visible horizontal band Andre marked at the bottom of the
     wheel cards in his 9.30 PM screenshot. Matching --bg removes the
     band; the photo content still reads as a card via border-radius. */
  background: var(--bg);
  margin-bottom: 10px;     /* Brusco's exact value from team-block-19.css */
}
.hd-wheel-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Anchor to upper portion of source image so faces stay visible.
     Many of the demo portraits are landscape, so default center crop
     ends up showing torso only. Biasing the crop window upward. */
  object-position: 50% 15%;
  display: block;
}

.hd-wheel-card__description {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.4;
  letter-spacing: 0.04em;
  width: 100%;
  /* Name + role are always visible — every card identifies its dentist as
     the wheel rotates past, not just the centered ±1 trio. */
  opacity: 1;
}

.hd-wheel-card__name {
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 12px;
  margin-bottom: 4px;
  color: var(--ink);
}
.hd-wheel-card__role {
  color: var(--ink-dim, var(--ink));
  opacity: 0.7;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
}

/* ---- Wheel navigation arrows ---------------------------------- */
/* Sibling of .hd-wheel-section inside .hd-wheel-wrapper. Natural flow
   position is BELOW the section (because the wheel inside is absolute
   and contributes no flow height). translateY(-150%) of its own 18px
   height (=−27px) pulls it up into the visible band so it lands ~78px
   below the card bottoms (Brusco's measured navToCardGap). */
.hd-wheel-nav {
  position: relative;
  z-index: 2;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: clamp(16px, 2vw, 40px);     /* Brusco --offset = 28.8px @ 1440 */
  transform: translateY(-150%);     /* Brusco's exact value */
  pointer-events: auto;
}

.hd-wheel-nav__btn {
  position: relative;
  /* +15% over the 26×18 base for a slightly bolder presence.
     SVG inside scales via width/height: 100% (viewBox 0 0 26 18 is preserved
     so the path geometry stays intact). */
  width: 30px;
  height: 21px;
  border: 0;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  overflow: hidden;
  display: block;
  padding: 0;
}
.hd-wheel-nav__btn svg {
  width: 100%;
  height: 100%;
}

.hd-wheel-nav__icon {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.75s cubic-bezier(.19, 1, .22, 1);
}

/* Ghost icon — sits off-canvas in the opposite direction at rest,
   slides into place on hover while the primary icon slides out. */
.hd-wheel-nav__btn--right .hd-wheel-nav__icon { transform: translateX(0); }
.hd-wheel-nav__btn--right .hd-wheel-nav__icon--ghost { transform: translateX(-150%); }
.hd-wheel-nav__btn--right:hover .hd-wheel-nav__icon { transform: translateX(150%); }
.hd-wheel-nav__btn--right:hover .hd-wheel-nav__icon--ghost { transform: translateX(0); }

.hd-wheel-nav__btn--left .hd-wheel-nav__icon { transform: translateX(0); }
.hd-wheel-nav__btn--left .hd-wheel-nav__icon--ghost { transform: translateX(150%); }
.hd-wheel-nav__btn--left:hover .hd-wheel-nav__icon { transform: translateX(-150%); }
.hd-wheel-nav__btn--left:hover .hd-wheel-nav__icon--ghost { transform: translateX(0); }

@media (prefers-reduced-motion: reduce) {
  .hd-wheel-shell, .hd-wheel-nav__icon { transition: none; }
}

/* ============================================================
   Description — editorial scroll-fill text
   ------------------------------------------------------------
   Per-word opacity fill scrubbed by scroll position. Each word
   sits at a faded base opacity; as it scrolls from the lower
   third of the viewport up to the upper third, it fades to full.
   Reads as "text fills in as you scroll." Reverses on scroll back.

   Brusco's actual /team uses scroll-scrubbed LINE translate
   (per CDP probe of second .description on /team — lines'
   matrix Y values vary continuously with scroll). The per-word
   opacity-fill here is OUR enhancement that gives the same
   "fills as you scroll" feeling at word granularity, which
   reads more like a "color fill" than line emergence does.

   Typography mix: big light display lede + smaller body line +
   uppercase eyebrow. Optional accent words get serif italic +
   mint accent color (still opacity-driven by scroll).
   ============================================================ */
.hd-description {
  /* STANDARD editorial-section spacing (revised 2026-06-11):
     MARGIN top/bottom — collapses with adjacent section margins to a
     single ~81px gap instead of compounding. Same value as .hd-statement
     so inter-section rhythm stays consistent. */
  margin: clamp(60px, 9vh, 120px) 0;
  padding: 0;
  background: var(--bg);
  position: relative;
}
.hd-description__container {
  max-width: 960px;
  margin: 0 auto;
  padding: 0 clamp(24px, 5vw, 80px);
  text-align: center;
}
.hd-description__eyebrow {
  display: inline-block;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  margin-bottom: clamp(40px, 6vh, 72px);
  opacity: 0.6;
}

.hd-description__lede {
  font-family: var(--font-display, var(--font-body));
  font-size: clamp(40px, 5.5vw, 84px);
  line-height: 1.04;
  font-weight: 300;
  letter-spacing: -0.025em;
  margin: 0 0 clamp(28px, 4vh, 48px);
  color: var(--ink);
}
.hd-description__body {
  font-family: var(--font-body);
  font-size: clamp(18px, 1.8vw, 24px);
  line-height: 1.45;
  font-weight: 400;
  letter-spacing: -0.005em;
  max-width: 640px;
  margin: 0 auto;
  color: var(--ink);
}

/* Word — scroll-fill via color-mix.
   "Empty" state is the ink color softened against the warm cream background
   (NOT gray — it reads as a faint tint of the final ink, so when scroll
   fills it, the word appears to "pour in" with the same color it's already
   suggesting). At --word-fill 0 the mix is 22% ink + 78% bg-warm; at 1 it
   is 100% ink. Linear ramp tied to scroll position. */
.hd-word {
  display: inline;
  color: color-mix(
    in srgb,
    var(--ink) calc((22 + var(--word-fill, 0) * 78) * 1%),
    var(--bg-warm, #f3f0e7)
  );
  transition: color 0.1s linear;
}

/* Accent word — serif italic + mint, same scroll-fill curve. */
.hd-word--accent {
  font-family: var(--font-display, var(--font-body));
  font-style: italic;
  font-weight: 400;
  letter-spacing: -0.01em;
  color: color-mix(
    in srgb,
    var(--accent) calc((22 + var(--word-fill, 0) * 78) * 1%),
    var(--bg-warm, #f3f0e7)
  );
}

/* The wrapper attr exists but no longer animates anything — kept so old
   markup with data-hd-line-reveal still renders cleanly if used. */
[data-hd-scroll-fill] { display: block; }

@media (prefers-reduced-motion: reduce) {
  .hd-word, .hd-word--accent { color: var(--ink); transition: none; }
  .hd-word--accent { color: var(--accent); }
}

/* ============================================================
   Hero — Brusco /team-derived + scroll-expand enhancement
   ------------------------------------------------------------
   Static geometry mirrors team-block-17.css (scope data-v-4ca90edf):
     aspect-ratio 1534/940 desktop, border-radius 32px, padding inside
     card 28.8px (Brusco's --offset at 1440), color white, overflow hidden,
     translateY(-32px) negates rounded top corners under the nav.

   Outer cream gutter HALVED from Brusco's source value:
     Brusco @1440: container.small width=1149.33 → side gutter 145.33px
     Halved:       clamp(16px, 5vw, 80px) — ≈ 72.66px @1440, fluid elsewhere.

   SCROLL-EXPAND (OUR enhancement, NOT in Brusco source):
     .hd-hero-section is 150vh tall (scroll runway).
     .hd-hero-stage is position:sticky, top:0, 100vh.
     motion.js sets --hd-hero-progress on .hd-hero-section: 0 at section top,
     1 at 50vh of section scrolled. CSS interpolates:
       stage padding   → clamp(16px,5vw,80px) × (1 - p)
       hero border-radius → 32px × (1 - p)
       hero translateY → -32px × (1 - p)  (un-pulls under nav as it expands)
     At p=1 the hero fills the viewport edge-to-edge.
   ============================================================ */
.hd-hero-section {
  position: relative;
  height: 150vh;
  --hd-hero-progress: 0;
  background: var(--bg);
}

.hd-hero-stage {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Top padding zero — photo bleeds at top edge.
     Sides: wide enough cream rail that MENU + BOOK NOW (in the fixed nav)
     sit entirely within the cream gutter, never over the photo. Brusco
     uses 145px @ 1440; we use clamp(120px, 9vw, 140px) ≈ 115px @ 1440 —
     less than Brusco but still well clear of the nav text width.
     Bottom stays at the halved gutter. */
  padding-top: 0;
  /* Asymmetric L/R: BOOK NOW (93px) is wider than MENU (49px), so symmetric
     padding makes the visual margins unbalanced. Push the card LEFT by
     adding extra padding on the right side, equal to BOOK NOW − MENU width.
     Result: both nav labels have ≈ matching cream margins to the hero edge. */
  padding-left: calc(clamp(80px, 7vw, 100px) * (1 - var(--hd-hero-progress)));
  padding-right: calc(clamp(120px, 10vw, 144px) * (1 - var(--hd-hero-progress)));
  padding-bottom: calc(clamp(16px, 5vw, 80px) * (1 - var(--hd-hero-progress)));
  box-sizing: border-box;
  will-change: padding;
}

.hd-hero {
  position: relative;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;             /* stage controls dimensions */
  /* Brusco mechanic: all four corners rounded, but the card is pulled UP
     by 32px so the rounded TOP corners go above the viewport — visible top
     edge appears FLAT (photo bleeds at top). Bottom corners visible at rest. */
  border-radius: calc(32px * (1 - var(--hd-hero-progress)));
  overflow: hidden;
  color: var(--accent-ink, #fff);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 28.8px;                /* mirrors Brusco --offset@1440 inside */
  box-sizing: border-box;
  /* translateY(-32px) at rest mirrors Brusco's tuck (rounded top corners
     hidden above viewport); releases to 0 at full bleed. */
  transform: translateY(calc(-32px * (1 - var(--hd-hero-progress))));
  transition: border-radius 0.05s linear, transform 0.05s linear;
  will-change: border-radius, transform;
  /* Soften the hero card's bottom edge so the photo's last row (dark
     green scrubs) fades smoothly into the cream section bg rather than
     cutting hard — that hard cut is the horizontal "long strip of color"
     Andre marked above "Not a clinic. A studio" in his 9:29 PM
     screenshot. The mask keeps the photo fully opaque through 92 % of
     the card height, then fades to transparent at the very bottom over
     the last 8 %, revealing the cream section bg cleanly. */
  -webkit-mask-image: linear-gradient(180deg, #000 0% 92%, transparent 100%);
          mask-image: linear-gradient(180deg, #000 0% 92%, transparent 100%);
}

.hd-hero__picture {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  overflow: hidden;
}
.hd-hero__media {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.hd-hero__counter {
  position: relative;             /* mobile fallback */
  display: flex;
  justify-content: space-between;
  width: 100%;
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 400;
  letter-spacing: 0.04em;
  z-index: 1;
  color: inherit;
}
@media screen and (min-width: 960px) {
  .hd-hero__counter { position: absolute; top: 28.8px; left: 28.8px; right: 28.8px; width: auto; }
}

.hd-hero__content {
  margin-top: auto;               /* bottom-align like Brusco */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  z-index: 1;
  text-align: center;
}

.hd-hero__eyebrow {
  margin: 0 0 16px;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  line-height: 1.4;
  max-width: 480px;               /* ≈ Brusco's col-5 narrow read */
  color: inherit;
}

.hd-hero__title {
  margin: 0;
  font-family: var(--menu-display-font);
  font-weight: var(--menu-display-weight);
  font-size: clamp(64px, 12vw, 220px);
  line-height: 1;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  color: inherit;
}

/* Legibility overlay on the media — only for the bottom TEAM title zone.
   The top no longer needs darkening because the nav sits OUTSIDE the photo
   on the cream strip at rest; at full-bleed the top darken fades in via
   the picture::before below tied to --hd-hero-progress. */
.hd-hero__picture::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to bottom, transparent 60%, rgba(0,0,0,0.55) 100%);
  pointer-events: none;
}
/* Top stripe darken — opacity-driven by scroll progress so the photo is
   clean at rest and the nav has a contrast band only when the hero fills
   the viewport behind it. */
.hd-hero__picture::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 110px;
  background: linear-gradient(to bottom, rgba(0,0,0,0.55), transparent);
  opacity: var(--hd-hero-progress, 0);
  pointer-events: none;
  z-index: 1;
}

/* ============================================================
   Custom cursor — Brusco extraction
   ------------------------------------------------------------
   Mounted at <body> root by structure/sections/cursor.html.j2.
   Behaviour driven by motion.js (GSAP runtime). CSS here sets:
     - Static dimensions (200x40 wrapper, 40x40 inner)
     - Mint accent + mix-blend-mode multiply
     - Cursor text typography (Inter 700 uppercase, 14/40)

   GSAP-set inline transforms drive every dynamic property
   (position, scale, width-expand, text fade) — see motion.js.

   Source values verified at viewport 1440x900 against live DOM
   (see playbooks/.../site-02-brusco/forensic-map.md §2f).
   ============================================================ */
.hd-cursor {
  position: fixed;
  top: 0;
  left: 0;
  width: 200px;
  height: 40px;
  /* Above .hd-menu-wrap (z 60) and .hd-nav-wrap--menu (z 70) so the
     mint disc stays visible across nav + open menu. Source's z 40 was
     tuned to a different stack (theirs maxed at 35). */
  z-index: 80;
  opacity: 0;
  pointer-events: none;
  will-change: transform, opacity;
  mix-blend-mode: multiply;
  /* GSAP sets transform inline: translate(-50%,-50%) translate(Xpx, Ypx) */
}

/* Velocity-stretch wrapper. motion.js sets transform inline per frame:
   rotate(angle) scaleX(parallel) scaleY(perpendicular) rotate(-angle).
   Only active in silent mode (no label). transform-origin: center so the
   stretch radiates outward symmetrically. */
.hd-cursor__shape {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  transform-origin: 50% 50%;
  will-change: transform;
}

.hd-cursor__inner {
  background: var(--accent);
  border-radius: 100px;
  width: 40px;
  height: 40px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  will-change: transform, width;
  mix-blend-mode: multiply;
  /* GSAP sets transform: scale(N) and width: Npx inline. */
}

.hd-cursor__text {
  color: #fff;
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.04em;
  line-height: 40px;
  text-transform: uppercase;
  white-space: nowrap;
  position: absolute;
  top: 0;
  left: 50%;
  opacity: 0;
  z-index: 1;
  /* GSAP sets transform: translate(-50%, 0) translate(Xpx, Ypx) inline. */
}

/* ============================================================
   Reduced motion — skip the slide/flip choreography
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .hd-menu,
  .hd-menu-wrap,
  .hd-menu__item::before,
  .hd-menu__item::after,
  .hd-menu__item__media,
  .hd-menu__item__label::after,
  .hd-menu__item__label span,
  .hd-page-content {
    transition: none !important;
  }
  .hd-menu__item:hover .hd-menu__item__label::after,
  .hd-menu__item:hover .hd-menu__item__label span {
    transform: none !important;
  }
  /* Cursor: hide custom cursor entirely; native cursor returns. */
  .hd-cursor { display: none !important; }
}

/* ============================================================
   Composite — THENN reference layout. Headline (left) +
   editorial column (chips + body + CTAs) on the right, with a
   3-card grid below: portrait story / product feature / contact
   form. This is the section the statement pre-section
   transitions INTO via the pill expansion — its bg matches the
   page --bg so the seam is invisible.
   ============================================================ */
.hd-composite {
  position: relative;
  background: var(--bg);
  margin: clamp(55px, 8.3vh, 110px) 0;
  padding: 0;
}
.hd-composite__container {
  /* Full-viewport composition with thin gutters — THENN reference puts
     the headline ~50px from the left viewport edge. */
  max-width: none;
  margin: 0;
  padding: 0 clamp(29px, 2.8vw, 55px);
  display: flex;
  flex-direction: column;
  gap: clamp(18px, 2.8vh, 37px);
}

/* ---- Top: headline (left) + editorial (right) ----
   Uses the SAME 54fr/27fr/19fr grid as the cards row below, with 24px
   gaps. Headline lives in column 1 (54fr) so its left edge aligns with
   the portrait card; editorial spans columns 2+3 so its left edge
   aligns with the middle card. One consistent vertical grid line. */
.hd-composite__top {
  display: grid;
  /* Top grid matches the cards grid (58/26/16) so the editorial column
     (spanning col 2/-1) shares its LEFT edge with the middle card directly
     below. Per the user annotation, editorial left edge aligns with the
     middle card's left edge. */
  grid-template-columns: 58fr 26fr 16fr;
  gap: 22px;
  align-items: center;
}
.hd-composite__top > .hd-composite__editorial {
  grid-column: 2 / -1;
}
.hd-composite__headline { min-width: 0; }
.hd-composite__text {
  font-family: var(--font-display, var(--font-body));
  /* Huge editorial display — "YOUR SMILE IS" fills ~90% of the headline
     column at 1980 viewport, matching THENN's column-fill ratio.
     6.8vw caps at 125px so line 1 stays unwrapped at desktop widths. */
  font-size: clamp(44px, 5.5vw, 115px);
  line-height: 1.0;
  letter-spacing: -0.025em;
  text-transform: uppercase;
  color: var(--ink);
  font-weight: 300;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.06em;
  text-align: left;
}
.hd-composite__line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 0.28em;
}
.hd-composite__word { display: inline-block; line-height: 1; }
.hd-composite__word.is-bold {
  font-weight: 800;
  letter-spacing: -0.035em;
}
.hd-composite__media {
  /* Pill has a CAPPED width so it doesn't stretch to the end of the line —
     user wants it to end around where the smile sits, not extend through
     to where "SMILE IS" ends above. */
  display: inline-flex;
  flex: 0 0 auto;
  width: clamp(220px, 32vw, 620px);
  align-items: center;
  justify-content: center;
  height: 0.82em;
  border-radius: 999px;
  overflow: hidden;
  background: var(--border);
  vertical-align: middle;
  transform: translateY(-0.04em);
}
.hd-composite__media-el {
  width: 100%; height: 100%;
  object-fit: cover;
  /* Land the crop on the lip / smile band of the source portrait. */
  object-position: 50% 48%;
  display: block;
}

/* ---- Editorial column (THENN Q2) ----
   ALL sizes are viewport-relative so the editorial holds THENN's
   proportions at every viewport. THENN was authored at 1980 viewport:
   pill is 11% vw, arrow circle 2.6% vw, editorial offset 9% vh below
   composite top, chips ~3.3% vh tall. Encoded as `clamp(min, Xvw|Xvh,
   max)` with the max equal to THENN's pixel measurement and the Xvw|vh
   = (px / 1980 or 1080) × 100. The min floor is for tiny viewports. */
.hd-composite__editorial {
  display: flex;
  flex-direction: column;
  /* No padding-top — vertical centering is handled by .hd-composite__top
     align-items: center, which centers this column's midpoint against
     the headline (lands at line 2 / between "YOUR SMILE IS" and
     "SUPERPOWER"). */
  padding-top: 0;
}
/* Chips — EXPERIENCE outlined 154×36, SCIENCE filled 127×36, gap 19px. */
.hd-composite__chips {
  display: flex;
  gap: clamp(13px, 0.96vw, 19px);
  flex-wrap: wrap;
  /* Tightened so the whole editorial column fits above SUPERPOWER. */
  margin-bottom: clamp(10px, 1.4vh, 15px);
}
.hd-composite__chip {
  display: inline-flex;
  align-items: center;
  /* 36 tall = 2 × 10 padding + 16 font. Scale all three together. */
  padding: clamp(7px, 0.93vh, 10px) clamp(10px, 0.7vw, 14px);
  border: 1.5px solid var(--ink);
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: clamp(12px, 0.81vw, 16px);
  font-weight: 600;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink);
  background: transparent;
  line-height: 1.0;
}
.hd-composite__chip:nth-child(2) {
  background: var(--ink);
  color: var(--bg);
  border-color: var(--ink);
}
/* Body — THENN at 22px Inter on 1080vh, line-height 1.5, ~540 wide
   wrapping to 4 lines. Font ~1.1vw scales to 22 at 1980, 16 at 1440.
   Max-width also scales so wrap stays at 4 lines across viewports. */
.hd-composite__body {
  font-family: var(--font-body);
  font-size: clamp(15px, 1.1vw, 22px);
  /* Tight leading so the body block is compact and CTAs land above the
     SUPERPOWER line while editorial center stays aligned with line 2. */
  line-height: 1.35;
  color: var(--ink);
  margin: 0 0 clamp(10px, 1.5vh, 18px) 0;
  max-width: clamp(380px, 27vw, 540px);
}
/* CTAs row — SHOP NOW pill + detached arrow circle + Learn more.
   No flex gap; per-item right-margin so each spacing is independent. */
.hd-composite__ctas {
  display: flex;
  align-items: center;
  gap: 0;
  flex-wrap: wrap;
}
/* SHOP NOW pill — 218×52 in THENN, vw-scaled.
   218 / 1980 = 11.0% vw → height = 52 / 1980 = 2.63% vw. */
.hd-composite__cta-primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: clamp(36px, 2.63vw, 52px);
  padding: 0 clamp(34px, 2.53vw, 50px);
  background: #FF1313;
  color: #FFFFFF;
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: clamp(13px, 0.96vw, 19px);
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  /* 19 / 1980 = 0.96% vw */
  margin-right: clamp(13px, 0.96vw, 19px);
  transition: filter 0.2s ease;
}
.hd-composite__cta-primary:hover { filter: brightness(0.95); }
/* Arrow circle — perfect 52×52 disc in THENN, vw-scaled. */
.hd-composite__cta-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: clamp(36px, 2.63vw, 52px);
  height: clamp(36px, 2.63vw, 52px);
  background: #FF1313;
  color: #FFFFFF;
  border-radius: 50%;
  text-decoration: none;
  /* 32 / 1980 = 1.62% vw */
  margin-right: clamp(22px, 1.62vw, 32px);
  transition: filter 0.2s ease;
}
.hd-composite__cta-arrow:hover { filter: brightness(0.95); }
.hd-composite__cta-arrow svg {
  display: block;
  width: clamp(16px, 1.21vw, 24px);
  height: clamp(16px, 1.21vw, 24px);
}
.hd-composite__cta-secondary {
  font-family: var(--font-body);
  /* 17 / 1980 = 0.86% vw */
  font-size: clamp(13px, 0.86vw, 17px);
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 6px;
  text-decoration-thickness: 1px;
  opacity: 0.7;
}

/* ---- 3-card grid (THENN Q3/Q4) ----
   SINGLE SOURCE OF TRUTH for card sizing:
     - WIDTH is controlled by the grid tracks (48fr 26fr 22fr).
     - HEIGHT is one fixed row height (330px). Plain px, no calc/clamp.
   No aspect-ratio, no intrinsic-image sizing is allowed to size a card. */
.hd-composite__cards {
  display: grid;
  grid-template-columns: 58fr 26fr 16fr;
  gap: 22px;
  align-items: stretch;
  height: 304px;
}
/* Items must not push their column wider than the fr track. */
.hd-composite__cards > * { min-width: 0; }
.hd-composite__card {
  position: relative;
  display: block;
  border-radius: 28px;
  text-decoration: none;
  color: inherit;
  background: var(--border);
  isolation: isolate;
  /* Cards FILL their grid track in both axes — no card can shrink and
     leave page bg visible inside its track. min-width/min-height: 0 lets
     them shrink past intrinsic content (the image) when needed. overflow
     hidden bottom-crops the image instead of letting it push outward. */
  width: 100%;
  height: 100%;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
}
/* Photo wrapper — clips the image to the card's rounded rectangle.
   Sibling of the notch/foot, so the notch can extend outside this
   clipping context. */
.hd-composite__card-photo {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
  z-index: 1;
}
/* ============================================================
   ⚠️  PORTRAIT-CARD BL CRESCENT FIX — DO NOT REMOVE OR REVERT
   ============================================================
   What it is: a 1px red crescent that flickers at the BOTTOM-LEFT
   CORNER of the portrait card ("What's behind your smile?") during
   scroll. Sub-pixel rendering artifact at the rounded clip arc.

   Root cause: the card body AND the photo wrapper both used to paint
   solid red (#c83a3a) at the BL 28px corner, and the rounded
   overflow:hidden clip arc anti-aliased to slightly different
   device-pixel rows than the inner red rectangles — leaving a 1px
   red sliver visible at the corner during transform / scroll.

   The fix (NOT a cover-up — it eliminates the SOURCE of the red
   pixels at the BL corner): a radial-gradient that goes TRANSPARENT
   in the BL 28×28 region on BOTH the card body (line ~1688) AND the
   photo wrapper (this rule), plus a matching mask on the photo
   wrapper to cut the image rendering in the same region. With no
   red SOURCE at the BL corner, sub-pixel anti-aliasing has nothing
   to leak. Single layer per surface, no two-layer mismatch.

   History: confirmed in session 65bbb385-f39a-48ee-bb26-baab0c29b958
   after 15 hours of failed cover-up attempts (border-radius tweaks,
   oversized cream pseudo, outline shadow, clip-path, etc.). User
   locked it in to git: "It's done — confirmed. Lock it in."
   See [[feedback-root-cause-not-coverup]].

   DO NOT remove these gradients. DO NOT replace with a solid color.
   DO NOT add a cream ::after pseudo "to be safe" — that was the
   failed approach this fix replaced. If a "red hairline" recurs,
   it is a DIFFERENT artifact (different edge, different element);
   diagnose separately and do not touch this rule. */
.hd-composite__card--portrait .hd-composite__card-photo {
  background: radial-gradient(circle at bottom left, transparent 28px, #c83a3a 29px);
  -webkit-mask: radial-gradient(circle at bottom left, transparent 28px, black 29px);
  mask: radial-gradient(circle at bottom left, transparent 28px, black 29px);
}
.hd-composite__card-image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  position: absolute;
  inset: 0;
}

/* Portrait card — fills its 48fr × 330px grid cell. No aspect-ratio (the
   row height is fixed). Image with red multiply, crop on the smile/face. */
.hd-composite__card--portrait {
  /* ⚠️  PORTRAIT-CARD BL CRESCENT FIX (paired with the photo-wrapper rule
     above) — DO NOT REMOVE OR REVERT.
     The card body's bg uses a radial-gradient that goes TRANSPARENT in
     the BL 28px corner. The page bg (cream) shows through the BL 28×28;
     the foot pill paints cream on top. No red SOURCE at BL → no
     sub-pixel leak, no flicker during scroll. Matches the photo
     wrapper's gradient + mask above.
     History: confirmed-working in session 65bbb385... after 15 hours of
     failed cover-up attempts; locked into git. See the long comment on
     the .__card-photo rule above for full details, and
     [[feedback-root-cause-not-coverup]] in memory. */
  background: radial-gradient(circle at bottom left, transparent 28px, #c83a3a 29px);
}
.hd-composite__card--portrait .hd-composite__card-image {
  mix-blend-mode: multiply;
  opacity: 0.92;
  object-position: 50% 22%;
}
/* NOTCH (label) at the bottom-left of the card. Sibling of the photo
   wrapper, NOT inside it — so it is never clipped by the photo wrapper's
   overflow:hidden. Width shrunk to 30% (max 340) so the notch ends well
   left of the subject's chin. Height 96px. Page-bg fill. Only the INNER
   TOP-RIGHT corner is rounded (28px); BOTTOM-LEFT also rounded (28px) to
   match the card's own corner; TL and BR are 0. */
.hd-composite__card-foot {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 35%;
  min-width: 212px;
  max-width: 258px;
  height: 88px;
  background: var(--bg);
  border-radius: 0 28px 0 0;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 18px 14px 4px;
  z-index: 2;
}
/* Concave fillet ABOVE the notch on the card's left edge.
   28×28 box sitting above the notch; radial-gradient centered at its
   TOP-RIGHT corner so the page-bg fills the BOTTOM-LEFT region beyond
   28px from the center, creating a quarter-circle CONCAVE curve from
   the card's photo edge into the notch's TL corner. */
.hd-composite__card-foot::before {
  content: "";
  position: absolute;
  left: 0;
  bottom: 100%;
  width: 28px;
  height: 28px;
  background: radial-gradient(circle at 100% 0, transparent 27px, var(--bg) 28px);
}
/* Concave fillet to the RIGHT of the notch on the card's bottom edge.
   Same mechanic — the page-bg fills the BL of the 28×28 box, carving a
   concave quarter-circle from the notch's BR corner along the card's
   bottom edge to the right. */
.hd-composite__card-foot::after {
  content: "";
  position: absolute;
  left: 100%;
  bottom: 0;
  width: 28px;
  height: 28px;
  background: radial-gradient(circle at 100% 0, transparent 27px, var(--bg) 28px);
}
.hd-composite__card-caption {
  font-family: var(--font-body);
  /* Reduced 8% from 21px after notch shrink (min-width 212, max 258). */
  font-size: 19px;
  font-weight: 400;
  color: var(--ink);
  line-height: 1.15;
  /* Wraps to 2 lines: "What's behind" / "your smile?". */
  max-width: 100%;
}
.hd-composite__card-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: clamp(52px, 3.7vw, 66px);
  height: clamp(52px, 3.7vw, 66px);
  border-radius: 50%;
  background: var(--ink);
  color: var(--bg);
  flex-shrink: 0;
}
.hd-composite__card-arrow svg {
  display: block;
  width: clamp(20px, 1.7vw, 34px);
  height: clamp(20px, 1.7vw, 34px);
}

/* Feature card — title top (padded), image full-bleed below */
.hd-composite__card--feature {
  background: #eceae3;
  padding: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  height: 100%;
  min-height: 0;
}
/* Full-bleed dentist photo — width: 100%, no horizontal inset, fills the
   remaining flex space below the head. Bottom-crops via card overflow. */
.hd-composite__card--feature .hd-composite__card-image {
  position: relative;
  inset: auto;
  margin-top: auto;
  width: 100%;
  max-height: 100%;
  min-height: 0;
  object-fit: cover;
  object-position: top;
  flex: 1;
  border-radius: 0;
}
.hd-composite__card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: 24px;
  z-index: 2;
}
.hd-composite__card-title {
  font-family: var(--font-display, var(--font-body));
  font-size: clamp(15px, 1.45vw, 22px);
  line-height: 1.15;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--ink);
  margin: 0;
  max-width: 18ch;
}
.hd-composite__card--feature .hd-composite__card-title {
  font-size: clamp(17px, 1.3vw, 24px);
}

/* Form card — dark panel with inputs */
.hd-composite__card--form {
  background: #1c1d1f;
  color: var(--bg);
  padding: 24px;
  display: flex;
  flex-direction: column;
  /* Heading pinned top, dropdowns + GET STARTED grouped at bottom. */
  justify-content: space-between;
  gap: 12px;
  height: 100%;
}
.hd-composite__card--form .hd-composite__card-title {
  color: var(--bg);
  font-size: clamp(13px, 0.95vw, 16px);
}
.hd-composite__form {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.hd-composite__field {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.hd-composite__field-label {
  font-family: var(--font-body);
  font-size: 11px;
  color: rgba(255, 252, 247, 0.65);
}
.hd-composite__select {
  position: relative;
  display: flex;
  align-items: center;
  border-radius: 999px;
  background: rgba(255, 252, 247, 0.08);
}
.hd-composite__select select {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  padding: 13px 32px 13px 14px;
  border: none;
  background: transparent;
  color: var(--bg);
  font-family: var(--font-body);
  font-size: 13px;
  cursor: pointer;
}
.hd-composite__select svg {
  position: absolute;
  right: 12px;
  pointer-events: none;
  color: var(--bg);
}
.hd-composite__form-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 16px 20px;
  /* THENN reference: GET STARTED is cream (off-white) with dark text,
     not the theme accent. */
  background: var(--bg);
  color: var(--ink);
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  margin-top: 4px;
}

@media (max-width: 900px) {
  .hd-composite__top { grid-template-columns: 1fr; }
  .hd-composite__cards { grid-template-columns: 1fr; }
}

/* ============================================================
   STATEMENT DECK — services card-deal layer INSIDE the statement
   sticky stage. Sits on top of the composite (the "big smile that
   expands"); appears once --hd-deck-progress > 0 and animates 4
   service cards through 3 exit phases (RIGHT → LEFT → RIGHT).

   Architecture:
     .hd-statement__deck            — position:absolute layer in the
                                      sticky stage, on top of expand
       .hd-statement__card          — 4 absolute-positioned cards,
                                      hero recipe (border-radius 32px,
                                      padding 28.8px, photo bg,
                                      counter top + content bottom,
                                      bottom-gradient overlay)

   Per-card transforms come from JS-written CSS vars:
     --card-x, --card-y, --card-scale, --card-rotate, --card-opacity
     --card-shadow-y, --card-shadow-blur, --card-shadow-alpha, --card-z
   Defaults below = slot 0 (front) at rest so the front card reads
   correctly even before motion.js has ticked once.

   Mobile (<768px): cards drop to relative position, stack vertically.
   ============================================================ */

.hd-statement__deck {
  position: absolute;
  inset: 0;
  z-index: 3;                       /* above .hd-statement__expand (z 2) */
  /* ARRIVAL: container hidden until the user enters the deck phase, then
     fades in over 200ms ease. No scroll-scrubbed E1; no card-by-card
     scatter→settle. The whole assembled deck simply appears. JS toggles
     the `is-active` class when --hd-deck-progress crosses 0. */
  opacity: 0;
  transition: opacity 200ms ease;
  pointer-events: none;
  /* 3D perspective on the container so card rotateX/rotateY produce
     real foreshortening (trapezoidal card) instead of flat 2D rotation.
     P=1500px = "dramatic" baseline matched to the Phase-1 measurement. */
  perspective: 1500px;
  perspective-origin: 50% 50%;
  transform-style: preserve-3d;
  will-change: opacity;
}
.hd-statement__deck.is-active { opacity: 1; }

/* Cream backdrop — sits under the deck cards, above the composite,
   solid cream within the deck container. Visibility is gated by the
   parent .hd-statement__deck's `.is-active` opacity transition, so this
   layer itself stays at opacity 1 (the parent fade handles the arrival
   smoothing). Replaces the composite as the deck's backdrop. */
.hd-statement__deck-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: var(--bg);
  opacity: 1;
  pointer-events: none;
}

.hd-statement__card {
  position: absolute;
  /* Bigger / less-compressed cards (was 92vw × 76vh). 94vw × 88vh keeps
     the stronger card presence while leaving room for the 4-image grid
     beneath the chip+title+body header. */
  width: 94vw;
  height: 88vh;
  left: 50%;
  top: 50%;
  box-sizing: border-box;
  border-radius: 32px;
  /* Internal padding scales with the available space so the header row
     and the 4-image grid both breathe at every viewport. */
  padding: clamp(36px, 4vh, 64px) clamp(36px, 3vw, 64px);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  /* Gap between header and image grid — director directive: 4vh
     (clamp 36px–52px). Image row grows back to absorb the recovered
     vertical space; headline & paragraph keep their positions. */
  gap: clamp(36px, 4vh, 52px);
  /* Per-card transform vars — JS writes these each rAF tick. Defaults
     correspond to slot 0 at rest (front of the deck) so SSR/CI renders
     read OK before the first tick. */
  --card-x: 0vw;
  --card-y: 0vh;
  --card-scale: 1;                 /* 1.0 always except exit-squeeze in fly */
  --card-rotate: 0deg;             /* rotateZ — 2D tilt (existing) */
  --card-rotate-x: 0deg;           /* rotateX — perspective fold, peel→fly */
  --card-rotate-y: 0deg;           /* rotateY — perspective swing, fly only */
  --card-origin-x: 50%;            /* transform-origin x — JS sets to 0/100% for exiting card */
  --card-origin-y: 50%;            /* transform-origin y — JS sets to 0% (top) for exiting card */
  --card-opacity: 1;
  --card-shadow-y: 30px;
  --card-shadow-blur: 80px;
  --card-shadow-alpha: 0.25;
  --card-z: 40;
  /* Transform stack (CSS applies inner→outer): rotateX folds the card in
     perspective FIRST (around the exit-side top corner), rotateY swings
     it sideways during fly, rotateZ adds the in-plane tilt, scale applies
     the exit-squeeze (1.0 normally, 0.93 at end of fly for exiting card),
     translate positions the card. Order matters at non-trivial angles —
     scale between translate and rotations so the squeeze is applied in
     the card's local frame (around the same origin as the rotations). */
  transform:
    translate(calc(-50% + var(--card-x, 0vw)),
              calc(-50% + var(--card-y, 0vh)))
    scale(var(--card-scale, 1))
    rotateZ(var(--card-rotate, 0deg))
    rotateY(var(--card-rotate-y, 0deg))
    rotateX(var(--card-rotate-x, 0deg));
  transform-origin: var(--card-origin-x, 50%) var(--card-origin-y, 50%);
  opacity: var(--card-opacity, 1);
  z-index: var(--card-z, 10);
  /* Drop-shadow + bright top rim. Inset box-shadow paints inside the
     padding box on top of the background, so it follows the 32px rounded
     corners and rides with the 3D warp. Cards 1-3 default to a strong
     bright rim; card 4 overrides via --card-rim below. */
  box-shadow:
    inset 0 1px 0 var(--card-rim, rgba(255, 255, 255, 0.9)),
    0 var(--card-shadow-y, 30px) var(--card-shadow-blur, 80px)
      rgba(0, 0, 0, var(--card-shadow-alpha, 0.25));
  /* 3D-warp hygiene: hide the mirror side so deep rotateY doesn't ghost;
     antialias the ink type so it doesn't crawl on non-integer angles. */
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  -webkit-font-smoothing: antialiased;
  will-change: transform, transform-origin, opacity, box-shadow;
  /* Per-card ink colour — every child element that needs an ink-tone uses
     var(--card-ink). Cards 1-3 (frosted glass) → dark ink #1C1D1B.
     Card 4 (charcoal glass) → white. Override below. */
  --card-ink: #1C1D1B;
  color: var(--card-ink);
}

/* Light bloom — soft radial highlight in the top-left corner, painted
   BEHIND content via z-index 0. ::before + the content-layer z-index: 1
   below keeps the chip / headline / paragraph / image grid above the
   bloom. Rides with the 3D warp because it lives inside the card's
   transformed coordinate space. No live blur, no motion-coupled light. */
.hd-statement__card::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(
    180px circle at 0% 0%,
    var(--card-bloom, rgba(255, 255, 255, 0.55)) 0%,
    transparent 100%
  );
  pointer-events: none;
  z-index: 0;
}

/* Content layer above the bloom — keeps 4vh gap / inner structure
   unchanged but stacks the chip + headline + image grid over the bloom. */
.hd-statement__card .h-panel__header,
.hd-statement__card .h-panel__grid {
  position: relative;
  z-index: 1;
}

/* Per-card backgrounds — FROSTED-GLASS gradients (135°, achromatic).
   Light-top-left → deeper-bottom-right. No hue, frost only. */
.hd-statement__card--1 {
  background: linear-gradient(135deg, #FBFAF8 0%, #F3F2EE 60%, #ECEBE5 100%);
}
.hd-statement__card--2 {
  background: linear-gradient(135deg, #F4F3F0 0%, #E8E7E2 58%, #DCDBD4 100%);
}
.hd-statement__card--3 {
  background: linear-gradient(135deg, #E9E8E3 0%, #DAD9D2 58%, #C9C8C0 100%);
}
.hd-statement__card--4 {
  background: linear-gradient(135deg, #2C2D2A 0%, #1E1F1C 55%, #141513 100%);
  /* Card 4 is the dark anchor — invert ink + dial rim and bloom down. */
  --card-ink: #ffffff;
  --card-rim: rgba(255, 255, 255, 0.22);
  --card-bloom: rgba(255, 255, 255, 0.14);
}

/* ───── Card content: header row + 4-image grid ───── */

.hd-statement__card .h-panel__header {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(40px, 5vw, 90px);
  align-items: start;
  padding: 0;
}

.hd-statement__card .h-panel__left {
  display: flex;
  flex-direction: column;
  gap: clamp(20px, 2.5vh, 36px);
}

.hd-statement__card .h-panel__chip {
  display: inline-flex;
  align-items: center;
  padding: clamp(8px, 0.8vh, 12px) clamp(20px, 1.6vw, 28px);
  border: 1px solid var(--card-ink);
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: clamp(11px, 0.85vw, 14px);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--card-ink);
  width: fit-content;
}

.hd-statement__card .h-panel__title {
  font-family: var(--font-display, var(--font-body));
  /* Capped so all four service names fit on one line in the 50%
     header-grid column. Same proportion as the IDEASS reference. */
  font-size: clamp(48px, 6vw, 116px);
  line-height: 0.95;
  letter-spacing: -0.02em;
  font-weight: 300;
  color: var(--card-ink);
  margin: 0;
}

.hd-statement__card .h-panel__right {
  display: flex;
  flex-direction: column;
  gap: clamp(20px, 2.2vh, 32px);
  /* Tiny top inset so the accent line lands at chip's vertical center. */
  padding-top: clamp(14px, 1.7vh, 22px);
}

.hd-statement__card .h-panel__accent {
  display: block;
  width: clamp(80px, 8vw, 140px);
  height: 3px;
  background: var(--card-ink);
}

.hd-statement__card .h-panel__body {
  font-family: var(--font-body);
  font-size: clamp(14px, 1.05vw, 19px);
  line-height: 1.55;
  color: var(--card-ink);
  margin: 0;
  max-width: 38em;
  opacity: 0.92;
}

/* 4-image grid: tall left (p1, spans both rows) + large center (p2,
   spans both rows) + 2 stacked small right (p3 top, p4 bottom). */
.hd-statement__card .h-panel__grid {
  display: grid;
  grid-template-columns: 1.8fr 3.7fr 1fr;
  grid-template-rows: 1fr 1fr;
  grid-template-areas:
    "p1 p2 p3"
    "p1 p2 p4";
  gap: clamp(16px, 1.4vw, 24px);
  /* Grows back to absorb the recovered space after gap reduction. Image
     proportions preserved by object-fit: cover on each cell. */
  flex: 1 1 auto;
  min-height: clamp(380px, 55vh, 720px);
  padding: 0;
  box-sizing: border-box;
}
.hd-statement__card .h-panel__cell--1 { grid-area: p1; }
.hd-statement__card .h-panel__cell--2 { grid-area: p2; }
.hd-statement__card .h-panel__cell--3 { grid-area: p3; }
.hd-statement__card .h-panel__cell--4 { grid-area: p4; }

.hd-statement__card .h-panel__cell {
  position: relative;
  overflow: hidden;
  border-radius: clamp(10px, 1vw, 18px);
  background: rgba(255, 255, 255, 0.08);
}

.hd-statement__card .h-panel__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Mobile fallback: deck drops out of the pin — cards stack inline. */
@media (max-width: 767px) {
  .hd-statement__deck {
    position: relative;
    opacity: 1;
    pointer-events: auto;
  }
  .hd-statement__card {
    position: relative;
    width: auto;
    height: auto;
    min-height: 80vh;
    left: auto;
    top: auto;
    margin: clamp(20px, 4vw, 40px);
    transform: none !important;
    opacity: 1 !important;
  }
  .hd-statement__card .h-panel__header { grid-template-columns: 1fr; }
  .hd-statement__card .h-panel__grid { height: 50vh; }
}

@media (prefers-reduced-motion: reduce) {
  .hd-statement__deck { display: none; }
}

/* ============================================================
   Wall of Love — structural rebuild to match the reference layout.

   Skeleton:
     .hd-wall                   — section, cream page bg shows around
       .hd-wall__panel          — inset rounded white panel
         .hd-wall__chip         — small pill above the heading
         .hd-wall__head         — centered editorial heading
         .hd-wall__viewport     — carousel
           .hd-wall__track       — items
           .hd-wall__edge--*     — disc nav buttons (per-edge hover)
         .hd-wall__cta-wrap     — dark pill CTA centered below

   Card aspect 4:5 (≈ 0.80). Edge-to-edge photo, no gradient backdrop.
   Title overlay top-left = white tracked caps + small subtitle.
   Track motion = 0.72s cubic-bezier(.87,0,.13,1) (matches team carousel).
   ============================================================ */
.hd-wall {
  margin: 0 0 clamp(80px, 12vh, 160px);    /* HARD CUT — wall starts at deck section's end */
  padding: 0;
  background: var(--bg);
  position: relative;
}

/* Inset white panel — content card. Width matches the deck section's
   visible card surface (.hd-statement__card width: 94vw) so the seam
   transition reads as one continuous column from deck to wall. Horizontal
   padding is ZERO so the carousel bleeds to the panel edge per Charle.
   Heading carries its own inner pad. */
.hd-wall__panel {
  width: 94vw;
  max-width: none;
  margin: 0 auto;
  /* Cream-on-cream surface: was #ffffff which produced an 8-unit RGB
     step against --bg (rgb 255,252,247) on both the panel's top edge
     (B3, statement→wall boundary) and bottom edge (B4, wall→faq
     boundary). The card identity now reads through the box-shadow
     elevation + 36px corner radius rather than a bg-color contrast.
     Audited and signed off after the backdrop-mechanism rebuild
     (2026-06-26 — the only CSS-level color seam left on the page). */
  background: var(--bg);
  border-radius: 36px;
  /* Asymmetric vertical padding — tighter top (title sits more "top-left"),
     more generous bottom (room for the bigger CTA + breathing room). */
  padding: clamp(40px, 5vh, 64px) 0 clamp(72px, 10vh, 128px);
  position: relative;
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.06);
  overflow: hidden;                            /* clip cards to panel rounded corners */
}

/* LEFT-aligned editorial heading — Bricolage 300. No width cap so the
   line "Real stories from real smiles." sits clean on 1–2 lines. Inner
   horizontal padding matches .hd-statement__container so the heading
   text aligns vertically with the previous section's content. */
.hd-wall__head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  margin: 0 0 clamp(48px, 6vh, 80px);
  padding: 0 clamp(24px, 5vw, 80px);
}
.hd-wall__title {
  font-family: var(--font-display, var(--font-body));
  font-size: clamp(44px, 6vw, 96px);
  line-height: 1.04;
  font-weight: 300;
  letter-spacing: -0.025em;
  color: var(--ink);
  margin: 0;
}

.hd-wall__viewport {
  position: relative;
  overflow: hidden;
  padding: 0;
  text-align: left;             /* reset panel's text-align for the cards */
}
.hd-wall__track {
  display: flex;
  gap: 56px;                                          /* approved inter-card gap */
  transform: translateX(0);
  transition: transform 0.72s cubic-bezier(.87, 0, .13, 1);
  will-change: transform;
  user-select: none;                                  /* clean drag — no text select */
}
.hd-wall__track.is-dragging {
  transition: none;                                   /* during drag: 1:1 cursor */
  cursor: grabbing;
}

.hd-wall__item {
  flex: 0 0 auto;
  /* 3-up across the panel inner: 1500-2×80(panel pad)=1340 → 3 cards
     ~420 + 2 × 56 gap = 1372, plus edge peeks via overflow. */
  width: clamp(280px, 26vw, 420px);
  display: flex;
  flex-direction: column;
  gap: clamp(20px, 2.2vh, 28px);                      /* image → info-row gap */
}

/* Reel container — 4:5 portrait (matches Charle ref @ W/H ≈ 0.79),
   10px corners, photo edge-to-edge. No gradient backdrop. Wall-only
   override; team-card source untouched. */
.hd-wall__media-wrap {
  width: 100%;
  aspect-ratio: 4 / 5;
  border-radius: 10px;
  overflow: hidden;
  background: #e8e6df;
  position: relative;
}
.hd-wall__media {
  width: 100%;
  height: 100%;
  position: relative;
}
.hd-wall__video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Title overlay TOP-LEFT — white tracked-caps + subtle local scrim.
   The ::before paints a small radial dark wash JUST behind the caption
   (top-left corner of the card only) so the white text reads on light
   posters. Image stays edge-to-edge; scrim covers ~38% × 30% of the
   card surface, anchored to the top-left corner. */
.hd-wall__caption {
  position: absolute;
  top: clamp(20px, 2.2vh, 28px);
  left: clamp(20px, 1.6vw, 28px);
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.hd-wall__caption::before {
  content: "";
  position: absolute;
  top: calc(-1 * clamp(20px, 2.2vh, 28px));
  left: calc(-1 * clamp(20px, 1.6vw, 28px));
  width: 70%;
  height: 56%;
  background: radial-gradient(
    ellipse at top left,
    rgba(0, 0, 0, 0.55) 0%,
    rgba(0, 0, 0, 0.32) 38%,
    rgba(0, 0, 0, 0) 78%
  );
  pointer-events: none;
  z-index: -1;
}
.hd-wall__caption-main {
  font-family: var(--font-body);
  font-size: clamp(13px, 1vw, 16px);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #ffffff;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.45);
  line-height: 1;
  white-space: nowrap;        /* FIX 4 — caption never wraps */
}

/* Info row beneath the card — thumbnail plate + title + subtitle.
   Typography inherits the team-section system:
     name  → uppercase 12px, weight 600, letter-spacing 0.12em, --ink
     role  → uppercase 11px, --ink-dim, opacity 0.7, letter-spacing 0.12em
   so the wall info row reads as part of the same brusco type family. */
.hd-wall__info {
  display: flex;
  gap: 12px;
  align-items: center;
  padding: 0 2px;
}
.hd-wall__icon {
  width: 52px;
  height: 52px;
  border-radius: 10px;
  /* Was var(--bg-warm). Matching --bg keeps consistency with the rest
     of the wall section's now-cream surfaces; the inset shadow + SVG
     content provide the icon frame and contrast. */
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink);
  flex-shrink: 0;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.06);
}
.hd-wall__icon svg {
  width: 28px;
  height: 28px;
  display: block;
}
.hd-wall__text {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.hd-wall__service {
  font-family: var(--font-body);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 13px;
  color: var(--ink);
  margin: 0;
  line-height: 1;
}
.hd-wall__outcome {
  font-family: var(--font-body);
  font-weight: 400;
  letter-spacing: 0;
  font-size: 13px;
  color: var(--ink);
  opacity: 1;
  margin: 0;
  line-height: 1.35;
}

/* Edge-anchored disc nav areas. Hidden by default; only the edge the
   cursor is over reveals its disc (JS toggles `.is-hot` based on
   mouse x-position inside the viewport). */
.hd-wall__edge {
  position: absolute;
  top: 0;
  bottom: 60px;                /* sit over the photo, not the info row */
  width: clamp(56px, 5vw, 96px);
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.3s ease;
  z-index: 3;
}
.hd-wall__edge--left  { left: clamp(8px, 1vw, 20px); }
.hd-wall__edge--right { right: clamp(8px, 1vw, 20px); }
.hd-wall__edge.is-hot { opacity: 1; pointer-events: auto; }

/* FIX 5 — Bare team-section arrow inside edge zone: no disc, accent-coloured.
   Glyph alone, larger, in --accent (brusco cursor teal). On hover the glyph
   nudges horizontally in its own direction (left-arrow nudges further left,
   right-arrow further right) via a small keyframed bounce. The two-icon
   ghost-swap from the team variant is killed off here so the bounce reads
   cleanly without competing motion. */
.hd-wall__edge .hd-wheel-nav__btn {
  width: 44px;
  height: 32px;
  background: transparent;
  box-shadow: none;
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
}
.hd-wall__edge .hd-wheel-nav__btn svg {
  width: 44px;
  height: 32px;
  display: block;
}
.hd-wall__edge .hd-wheel-nav__btn .hd-wheel-nav__icon--ghost { display: none; }
.hd-wall__edge .hd-wheel-nav__btn .hd-wheel-nav__icon {
  position: static;
  transition: transform 0.3s cubic-bezier(.34, 1.56, .64, 1);
}
.hd-wall__edge--left  .hd-wheel-nav__btn:hover .hd-wheel-nav__icon { animation: hd-wall-nudge-left 0.55s ease-out both; }
.hd-wall__edge--right .hd-wheel-nav__btn:hover .hd-wheel-nav__icon { animation: hd-wall-nudge-right 0.55s ease-out both; }
@keyframes hd-wall-nudge-left {
  0%   { transform: translateX(0); }
  35%  { transform: translateX(-8px); }
  60%  { transform: translateX(2px); }
  100% { transform: translateX(0); }
}
@keyframes hd-wall-nudge-right {
  0%   { transform: translateX(0); }
  35%  { transform: translateX(8px); }
  60%  { transform: translateX(-2px); }
  100% { transform: translateX(0); }
}
.hd-wall__edge .hd-wheel-nav__btn[disabled] {
  opacity: 0;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .hd-wall__edge .hd-wheel-nav__btn:hover .hd-wheel-nav__icon { animation: none; }
}

/* Single CTA pill below the carousel — DARK (var(--ink)) to match
   reference's dark-pill placement. Generous top spacing for breathing room. */
.hd-wall__cta-wrap {
  display: flex;
  justify-content: center;
  margin-top: clamp(112px, 14vh, 168px);
}
.hd-wall__cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: clamp(52px, 4vw, 64px);
  padding: 0 clamp(36px, 3vw, 56px);
  background: var(--ink);
  color: var(--bg);
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: clamp(15px, 1.2vw, 18px);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-decoration: none;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.15);
}
.hd-wall__cta:hover {
  transform: translateY(-1px);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.20);
}

@media (max-width: 767px) {
  .hd-wall__panel { padding: 48px 24px; border-radius: 24px; }
  .hd-wall__item { width: 78vw; }
  .hd-wall__edge { display: none; }
}

/* ============================================================
   FAQ — 2-column layout (left visual, right header + accordion).
   Proportions extracted from a live reference probe; skin reskinned
   to brusco (cream bg, Bricolage/Inter, accent teal, ink ink).
     - Section: full bleed, 40px H padding, 2-col grid 1fr/1fr, 30px col gap
     - Visual: aspect ~0.90 portrait, no border-radius
     - h2 title: Bricolage 300, clamp(40,4.4vw,68)
     - Lede: Inter 16/1.5, mt 20, max-width 65% of column
     - Accordion item: 90px row, 30px top / 20px bottom padding,
                       1px bottom divider in --divider
     - Toggle button: 56×56 square, 4px radius, var(--accent) bg
   ============================================================ */
.hd-faq {
  background: var(--bg);
  margin: clamp(80px, 12vh, 160px) 0 0;
  padding: 0;
}
.hd-faq__container {
  width: 100%;
  max-width: 1440px;
  margin: 0 auto;
  padding: 0 clamp(24px, 2.8vw, 40px);
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: auto auto;
  column-gap: clamp(20px, 2.1vw, 30px);
  row-gap: clamp(20px, 2.4vh, 32px);
  align-items: start;
}
/* LEFT visual — STICKY. Pins at top of viewport while the right column
   (brand card + 5 Q&A items) scrolls past. */
.hd-faq__visual {
  grid-column: 1;
  grid-row: 1 / -1;
  width: 100%;
  aspect-ratio: 650 / 720;
  max-height: clamp(440px, 70vh, 640px);
  overflow: hidden;
  /* Was var(--bg-warm) which sat 16 units off page cream. Image covers
     the container so the bg only shows during load — matching --bg
     keeps that transient state seamless on the cream FAQ section. */
  background: var(--bg);
  border-radius: 18px;
  position: sticky;
  top: clamp(24px, 4vh, 64px);
  align-self: start;
}
.hd-faq__visual-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
@media (max-width: 900px) {
  .hd-faq__container { grid-template-columns: 1fr; grid-template-rows: auto; }
  .hd-faq__visual { grid-column: 1; grid-row: auto; aspect-ratio: 4 / 3; }
}

/* RIGHT TOP — brand card. Icon + name on row 1 (horizontal), body on row 2. */
.hd-faq__brand {
  grid-column: 2;
  grid-row: 1;
  background: #ffffff;
  border-radius: 18px;
  padding: clamp(24px, 2.4vw, 36px) clamp(24px, 2.4vw, 36px) clamp(28px, 2.8vw, 40px);
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-rows: auto auto;
  column-gap: clamp(16px, 1.6vw, 24px);
  row-gap: clamp(14px, 1.4vw, 22px);
  align-items: center;
  box-shadow: 0 1px 0 rgba(14, 34, 51, 0.03);
}
.hd-faq__icon {
  grid-column: 1;
  grid-row: 1;
  width: clamp(56px, 4.8vw, 72px);
  height: clamp(56px, 4.8vw, 72px);
  border-radius: 14px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--accent);
  background: transparent;
}
.hd-faq__icon-img,
.hd-faq__icon svg {
  width: 100%;
  height: 100%;
  display: block;
  border-radius: inherit;
}
.hd-faq__brand-name {
  grid-column: 2;
  grid-row: 1;
  font-family: var(--font-display, var(--font-body));
  font-size: clamp(32px, 3vw, 48px);
  line-height: 1.05;
  font-weight: 300;
  letter-spacing: -0.02em;
  color: var(--ink);
  margin: 0;
}
.hd-faq__brand-body {
  grid-column: 1 / -1;
  grid-row: 2;
  font-family: var(--font-body);
  font-size: clamp(14px, 1vw, 16px);
  line-height: 1.55;
  font-weight: 400;
  color: var(--ink-dim, var(--ink));
  opacity: 0.85;
  margin: 0;
  max-width: 52ch;
}

/* RIGHT BOTTOM — accordion list: each item is its own white card. Small gap
   between cards; no row dividers. Compact text scale per reference.
   Bottom padding adds a small breathing buffer before the gallery; 8 items
   already make the right column taller than the sticky image. */
.hd-faq__list {
  grid-column: 2;
  grid-row: 2;
  list-style: none;
  margin: 0;
  padding: 0 0 clamp(40px, 6vh, 96px);
  display: flex;
  flex-direction: column;
  gap: clamp(10px, 1vh, 14px);
}
.hd-faq__item {
  background: #ffffff;
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 1px 0 rgba(14, 34, 51, 0.03);
  transition: box-shadow 0.2s ease;
}
.hd-faq__item.is-open {
  box-shadow: 0 1px 0 rgba(14, 34, 51, 0.03), 0 8px 28px rgba(14, 34, 51, 0.05);
}

.hd-faq__row {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(14px, 1.4vw, 22px);
  padding: clamp(28px, 2.8vw, 38px) clamp(22px, 2vw, 28px);
  background: transparent;
  border: 0;
  text-align: left;
  cursor: pointer;
  color: var(--ink);
  font: inherit;
}

.hd-faq__q {
  font-family: var(--font-body);
  font-size: clamp(15px, 1.1vw, 17px);
  font-weight: 600;
  letter-spacing: -0.005em;
  line-height: 1.35;
  color: var(--ink);
}

/* Toggle — accent-filled small square, plus/minus icons swap on open. */
.hd-faq__toggle {
  flex: none;
  width: clamp(30px, 2.6vw, 38px);
  height: clamp(30px, 2.6vw, 38px);
  border-radius: 5px;
  background: var(--accent);
  color: var(--accent-ink, #ffffff);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 0.2s ease;
}
.hd-faq__toggle svg {
  width: 55%;
  height: 55%;
}
.hd-faq__toggle .hd-faq__toggle-plus  { display: block; }
.hd-faq__toggle .hd-faq__toggle-minus { display: none; }
.hd-faq__item.is-open .hd-faq__toggle .hd-faq__toggle-plus  { display: none; }
.hd-faq__item.is-open .hd-faq__toggle .hd-faq__toggle-minus { display: block; }

/* Answer — fully collapsed when closed, expands on .is-open. Uses the
   classic max-height + overflow:hidden technique so the closed state is
   guaranteed 0 px tall (grid-rows 0fr was leaking the natural line height
   in this layout). */
.hd-faq__answer {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.4s cubic-bezier(.2,.8,.2,1);
}
.hd-faq__item.is-open .hd-faq__answer {
  max-height: 600px;
}
.hd-faq__a {
  font-family: var(--font-body);
  font-size: clamp(13px, 0.9vw, 14.5px);
  line-height: 1.55;
  color: var(--ink-dim, var(--ink));
  opacity: 0.85;
  margin: 0;
  padding: 0 clamp(18px, 1.8vw, 24px) clamp(16px, 1.6vw, 22px);
  max-width: 58ch;
}

/* ============================================================
   Smile Gallery — copied from signature/sections/gallery.html.j2
   Two horizontal marquee strips wrapping a centered tag / title /
   subtitle / CTA. Cards bob via the 8-step sine wave pattern
   [w0,w1,w2,w3,w4,w3,w2,w1] so they appear to "float" as the
   strip translates left/right. Pure-CSS marquee + sine offsets,
   no JS. Reskinned to Brusco: cream bg, Bricolage/Inter, accent
   teal, dark pill CTA matching .hd-wall__cta.
   ============================================================ */
.hd-gallery {
  background: var(--bg);
  /* Bottom padding moved to .hd-gallery__runway so the runway's tweened
     bg (cream→charcoal) fills flush down to the team_editorial seam
     instead of leaving a 120 px cream band that wouldn't darken. Top
     padding stays here — it's above the runway, marquee-side. */
  padding: clamp(64px, 8vh, 112px) 0 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 48px;
  overflow: hidden;
}
/* Runway — wraps the eyebrow + title + form card. Full section width.
   Owns the bottom padding that used to live on .hd-gallery, so its
   bg ends at the team_editorial top edge with no cream gap. Default bg
   is cream (matches the marquee row above) when the runway tween is
   not active; the clinical pack opts into the cream→charcoal scrub
   via --gallery-runway-cover: 1 (see pack-style.css) which
   motion.js → initGalleryRunwayTransition gates on. */
.hd-gallery__runway {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  background: var(--bg);
  /* Bottom padding bumped to clamp(200,24vh,320) for substantial
     breathing room between the form card and team_editorial. With the
     fixed-backdrop transition mechanism, this padding is no longer
     load-bearing for the seam (the backdrop handles color continuity
     edge-to-edge); it's purely visual separation per Andre's request
     for more room after the form. */
  padding: 0 0 clamp(200px, 24vh, 320px);
}

/* ============================================================
   Runway backdrop — fixed full-viewport color layer behind page
   content, used by the clinical pack to fade cream → composited
   charcoal as the user scrolls gallery → team_editorial.

   Why fixed (not part of a section's bg): a fixed layer has no
   on-screen edge at any scroll position, so the color change is a
   PROPERTY of the viewport rather than the BORDER of an element.
   That structurally eliminates the horizontal "seam line sweeping
   color" bug that every previous mechanism (section bg tween,
   gradient feather, sticky cover, runway-internal scrub) hit at
   the meeting point of a darkening element and a still-cream one
   above it.

   Default state is cream (matches var(--bg)). When the pack does NOT
   opt in (--gallery-runway-cover !== "1"), motion.js never adds
   the html.hd-runway-backdrop-active class and the transparency
   overrides below stay dormant; every section above and below
   gallery covers the backdrop with its own opaque bg, so non-opt-in
   builds see no visual change.

   z-index: 0 + main z-index: 1 puts main content above the backdrop
   without invalidating the existing stacking context. pointer-events
   off so the backdrop never intercepts clicks; will-change
   background-color hints the compositor to keep the tween smooth.
   ============================================================ */
.hd-runway-backdrop {
  position: fixed;
  inset: 0;
  z-index: 0;
  background-color: var(--bg);
  pointer-events: none;
  will-change: background-color;
}
main.hd-page-content {
  position: relative;
  z-index: 1;
}

/* Section transparency overrides — only active when motion.js has
   confirmed the opt-in flag and added the activation class to <html>.
   This keeps the gate tight: classic / signature builds never hit
   these rules. Making .hd-gallery + .hd-gallery__runway transparent
   lets the backdrop's color show through the whole gallery surface,
   so as the backdrop tweens cream → charcoal there's no on-screen
   edge anywhere. team_editorial STAYS opaque (its --ink-deep + 4%
   white hero overlay produce a composited rgb(33,33,36) which
   matches the backdrop's end color exactly — pixel-flush seam). */
html.hd-runway-backdrop-active .hd-gallery,
html.hd-runway-backdrop-active .hd-gallery__runway,
html.hd-runway-backdrop-active .hd-team-editorial {
  background: transparent;
}

/* Hero overlay zeroed under the same gate.
   Why: team's hero carries `background: rgba(255,255,255,0.04)` —
   a 4% white wash whose original job was to lift the photo card
   off the section's solid --ink-deep bg. With the section now
   transparent over a backdrop that fades to that exact charcoal,
   the lift is no longer doing anything useful. Worse, it's the
   sole arithmetic source of every row-step in the no-edge gate:
   step at the hero boundary = 0.04 × (255 − backdrop), which
   grows as the backdrop darkens (peaks at 9 units at full
   charcoal, exceeds the 5-unit threshold at progress ≥ 0.7).
   Zeroing the overlay zeroes every step at the hero boundary —
   structurally, not by tuning. The photo card reads identically
   against charcoal with or without the 4% wash; the lift is
   imperceptible at that opacity. Locked-list scope unchanged
   (title, photo, parallax, text, z-index, crop all untouched). */
html.hd-runway-backdrop-active .hd-team-editorial__hero {
  background: transparent;
}

/* Hero gradient — re-color its rgba stops from hardcoded (20,20,20)
   to (24,24,27) under the same gate.
   Why: the hero-grad overlay fades the photo's bottom into the
   section bg for cards-row legibility. The original stops use
   `rgba(20,20,20, 0.25/0.75)` — but 20,20,20 is NOT the same as
   the section's --ink-deep (24,24,27). When team's section was
   opaque rgb(24,24,27) the mismatch composed to a subtle
   darker-than-bg band (rgb~21) right before the gradient's final
   solid-ink stop (rgb 24,24,27), creating a visible 3-unit step
   at the gradient's 86 % → 100 % marker. With the section now
   transparent over the backdrop, the same arithmetic compounds —
   pixel-sampling Andre's 9 PM screenshot showed exactly this:
   rgb(22,22,25) at y=1300 → rgb(24,24,26) at y=1320 = the
   "black line" he flagged. Replacing 20,20,20 with 24,24,27
   throughout the gradient makes every stop interpolate against
   the backdrop with zero color mismatch — the dim still works
   on the photo's opaque pixels, but the gradient is invisible
   against the cutout's transparent areas. Locked-list scope
   unchanged (title, photo, parallax, text, z-index, crop). */
html.hd-runway-backdrop-active .hd-team-editorial__hero-grad {
  background: linear-gradient(
    180deg,
    transparent 0%,
    transparent 45%,
    rgba(24, 24, 27, 0.25) 65%,
    rgba(24, 24, 27, 0.75) 86%,
    var(--ink-deep) 100%
  );
}
.hd-gallery__ticker {
  width: 100%;
  overflow: hidden;
  position: relative;
}
.hd-gallery__track {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 24px;
  width: max-content;
  will-change: transform;
}
@keyframes hd-marquee-left {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
@keyframes hd-marquee-right {
  from { transform: translateX(-50%); }
  to   { transform: translateX(0); }
}
.hd-gallery__ticker--top    .hd-gallery__track { animation: hd-marquee-left  60s linear infinite; }
.hd-gallery__ticker--bottom .hd-gallery__track { animation: hd-marquee-right 60s linear infinite; }
@media (prefers-reduced-motion: reduce) {
  .hd-gallery__track { animation: none !important; transform: none !important; }
}
/* Each card is a 115×270 lane; inside, a 115×110 figure sits at one of 5
   vertical stops (w0..w4) → sine-wave bob across consecutive cards. */
.hd-gallery__card {
  flex: none;
  width: 115px;
  height: 270px;
  position: relative;
  overflow: visible;
  background: transparent;
}
.hd-gallery__card img {
  width: 115px;
  height: 110px;
  border-radius: 12px;
  object-fit: cover;
  display: block;
  position: absolute;
  left: 0;
}
.hd-gallery__card--w0 img { top:   0px; }
.hd-gallery__card--w1 img { top:  40px; }
.hd-gallery__card--w2 img { top:  80px; }
.hd-gallery__card--w3 img { top: 120px; }
.hd-gallery__card--w4 img { top: 160px; }
.hd-gallery__center {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(28px, 3vh, 48px);
  width: 100%;
  max-width: 880px;
  padding: 0 clamp(24px, 5vw, 80px);
  text-align: center;
}
.hd-gallery__titlegroup {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}
/* Eyebrow above the headline — visual match to .hd-description__eyebrow:
   13px Inter, 500 weight, 0.16em tracking, uppercase, 60% opacity. No
   margin-bottom because .hd-gallery__titlegroup's flex `gap: 16px`
   handles spacing to the headline. Center alignment is inherited from
   the parent `align-items: center`. */
.hd-gallery__eyebrow {
  display: inline-block;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  opacity: 0.6;
}
.hd-gallery__title {
  font-family: var(--font-display);
  /* Cap at 72px (not 84px) so "See what's possible" always fits on one
     line within the 880px container at wide viewports (≥1600px). The
     existing <br> in the slot then deterministically produces 2 lines
     instead of natural-wrap-plus-br producing 3. */
  font-size: clamp(40px, 5.5vw, 72px);
  line-height: 1.04;
  font-weight: 300;
  letter-spacing: -0.025em;
  color: var(--ink);
  margin: 0;
  text-align: center;
}
.hd-gallery__sub {
  font-family: var(--font-body);
  font-size: clamp(15px, 1.2vw, 18px);
  font-weight: 400;
  line-height: 1.55;
  letter-spacing: -0.005em;
  color: var(--ink-dim, var(--ink));
  opacity: 0.7;
  margin: 0;
  max-width: 56ch;
  text-align: center;
}
/* Tag pill — small bullet + label used to label this section.
   Mirrors the signature `.hd-tag-pill` recipe from oralcare. */
.hd-gallery .hd-tag-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.hd-gallery .hd-tag-pill::before {
  content: "";
  display: block;
  width: 4px;
  height: 4px;
  border-radius: 30px;
  background: var(--ink);
  flex: none;
}
.hd-gallery .hd-tag-pill__label {
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--ink);
}
/* Brusco-skinned CTA — same dark pill recipe as .hd-wall__cta so the
   buttons read as a consistent system across the site. */
.hd-gallery .hd-btn--primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: clamp(52px, 4vw, 64px);
  padding: 0 clamp(36px, 3vw, 56px);
  background: var(--ink);
  color: var(--bg);
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: clamp(15px, 1.2vw, 18px);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-decoration: none;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.15);
}
.hd-gallery .hd-btn--primary:hover {
  transform: translateY(-1px);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.20);
}

/* ---- Callback form card — bigger / impact size, ~half-viewport wide ---- */
.hd-gallery__form {
  width: 50vw;
  max-width: 760px;
  min-width: min(100%, 480px);
  margin: 0 auto;
  /* Was #ffffff which produced an 8-unit step against the cream page
     bg AND a hard contrast band against the backdrop tween as it scrubs
     cream → charcoal. Card identity now reads through box-shadow
     elevation; bg matches --bg so the form blends with the surrounding
     cream at the start of the gallery and the box-shadow becomes the
     elevation cue throughout the runway scrub. */
  background: var(--bg);
  border-radius: 24px;
  padding: clamp(36px, 3.6vw, 56px) clamp(40px, 4vw, 64px);
  display: flex;
  flex-direction: column;
  gap: clamp(14px, 1.6vh, 22px);
  text-align: left;
  box-shadow: 0 1px 0 rgba(14, 34, 51, 0.04), 0 40px 100px rgba(14, 34, 51, 0.08);
}
.hd-gallery__form-title {
  font-family: var(--font-display, var(--font-body));
  font-size: clamp(28px, 2.4vw, 40px);
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.1;
  color: var(--ink);
  margin: 0;
}
.hd-gallery__form-sub {
  font-family: var(--font-body);
  font-size: clamp(15px, 1.1vw, 18px);
  line-height: 1.45;
  color: var(--ink-dim, var(--ink));
  opacity: 0.75;
  margin: 0 0 clamp(6px, 1vh, 12px);
}
.hd-gallery__form-input {
  width: 100%;
  height: clamp(58px, 4.4vw, 70px);
  padding: 0 24px;
  background: var(--bg-warm, #f3f0e7);
  border: 1px solid transparent;
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: clamp(15px, 1.1vw, 17px);
  color: var(--ink);
  -webkit-appearance: none;
  appearance: none;
  transition: border-color 0.18s ease, background 0.18s ease;
}
.hd-gallery__form-input::placeholder {
  color: var(--ink-dim, var(--ink));
  opacity: 0.55;
}
.hd-gallery__form-input:focus {
  outline: none;
  background: #ffffff;
  border-color: var(--accent);
}
.hd-gallery__form-submit {
  margin-top: clamp(4px, 0.8vh, 10px);
  width: 100%;
  height: clamp(62px, 4.8vw, 76px);
  border: 0;
  border-radius: 999px;
  background: var(--ink);
  color: var(--bg);
  font-family: var(--font-body);
  font-size: clamp(17px, 1.3vw, 20px);
  font-weight: 600;
  letter-spacing: 0.02em;
  cursor: pointer;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.20);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.hd-gallery__form-submit:hover {
  transform: translateY(-1px);
  box-shadow: 0 14px 30px rgba(0, 0, 0, 0.24);
}
.hd-gallery__form-disclaimer {
  font-family: var(--font-body);
  font-size: clamp(12px, 0.85vw, 14px);
  color: var(--ink-dim, var(--ink));
  opacity: 0.6;
  text-align: center;
  margin: clamp(4px, 0.8vh, 8px) 0 0;
}

/* ============================================================
   Editorial team section — Aventura editorial composition.

   Architecture (post-2026-06-16 revision):
     Aventura is NOT a pinned scroll choreography. It's natural scroll
     past a tall, absolute-positioned composition:
       - Hero image: 5:4 aspect, ~149 vh tall (1676×1340 on a 1440
         viewport), wider than viewport (full-bleed beyond edges)
       - Title: position: absolute at top:64px of the hero, no transform
         on Y — scrolls with the page like normal text
       - Cards: position: absolute at bottom of the hero, 3-col grid
         of text overlays (one composite photo, NOT three photos)
       - Band + pill: normal flow below the hero
     Hero image has a subtle Y-parallax (~3.7 % of scroll speed),
     applied by a small rAF listener in motion.js. No GSAP needed.

   Aventura-extracted values:
     • Hero: 1676×1340 (aspect 5:4), 116 % of viewport width
     • Title: position absolute, top 64px (≈ 4.8 % of hero height),
       InstrumentSerif 276px → brusco-skinned Bricolage clamp(80,16vw,256px) w300
     • Cards: x=32, 712, 1210 in 1440 viewport (3-col grid)
       NAME 20/21.8 w600  → brusco clamp(18,1.5vw,22px) Bricolage 500
       ROLE 15/16.95 w700 → brusco clamp(13,1vw,15px) Inter 600 (NOT uppercase)
       BIO  12/14.28 w500 → brusco clamp(12,0.9vw,14px) Inter 400
     • Tagline: 20/21.8 w600, line-2 opacity 0.4
     • Big text: InstrumentSerif 55px → Bricolage clamp(32,3.8vw,56px) w300
     • Pill: CENTERED below band, "Meet The Team" w700 with "+" icon
   ============================================================ */
.hd-team-editorial {
  position: relative;
  /* Near-black to match the ring transition's --ink-deep and avoid
     a grey-to-black seam at the team→ring boundary. Text color
     remains --bg (cream) so legibility is unchanged. */
  background: var(--ink-deep);
  color: var(--bg);
  margin: 0;
  /* No min-height — section size comes naturally from the tall hero
     (5:4 aspect ≈ 149 vh) plus the band + pill rows beneath.
     overflow: hidden clips the 116 %-wide hero so it doesn't cause
     horizontal scroll. */
  overflow: hidden;
}

/* Title + hero stack — restored to commit 26e607e values per user
   direction. __top is the grid wrapper (one row, one column); __hero
   and __title both sit at grid-column:1; grid-row:1 so the title
   overlays the upper portion of the photo. */
.hd-team-editorial__top {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: auto;
  width: 100%;
}
.hd-team-editorial__hero {
  grid-column: 1;
  grid-row: 1;
  width: 100%;
  aspect-ratio: 1 / 1;
  background: rgba(255, 255, 255, 0.04);
  overflow: hidden;
  z-index: 2;
  /* Containing block for the absolutely-positioned __cards row so the
     cards anchor to hero's bottom edge instead of falling through to the
     next positioned ancestor (the section) and overlapping the band. */
  position: relative;
}
.hd-team-editorial__hero-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  /* Transform owned entirely by GSAP (initEditorialTeamParallax in
     motion.js) which sets yPercent 4 as the base and animates to -6
     across the scroll. CSS does NOT pre-apply translateY here, because
     GSAP composes its own transform STRING on top of any existing CSS
     transform — that produced a stacked translate(0%, 4%) translate(0px, 68px)
     bug where the heads sat at 8% instead of 4%. The reduced-motion
     override below restores the +4% base for users without GSAP. */
  will-change: transform;
}
/* Bottom gradient over the hero so the cards' text remains legible
   without darkening the dentists' faces. */
.hd-team-editorial__hero-grad {
  position: absolute;
  inset: 0;
  /* End color matches the section's --ink-deep so the hero's bottom
     blends seamlessly into the band/pill area beneath (no hard
     horizontal line at the hero edge). */
  background: linear-gradient(
    180deg,
    transparent 0%,
    transparent 45%,
    rgba(20, 20, 20, 0.25) 65%,
    rgba(20, 20, 20, 0.75) 86%,
    var(--ink-deep) 100%
  );
  pointer-events: none;
  /* z-index 2 stacks the gradient ABOVE the cut-out image so it
     darkens the bottom of the dentists for cards' legibility.
     Cards (z-3) still sit above it. */
  z-index: 2;
}

/* Title — absolute at TOP of hero, viewport-centered. Renders BELOW
   the cut-out image (z-index 0) so the image's transparent pixels let
   the title peek through and the dentists' opaque pixels occlude it.

   font-size here is a fallback. The real value is set by
   `initEditorialTitleFit` in motion.js — it measures the title's
   container width and scales the font-size so "Team of Experts"
   (Bricolage, nowrap) fills ~99 % of the container. Recomputes on
   resize and after fonts load. */
/* Title — restored to commit 26e607e values per user direction.
   Grid-stacked with the hero inside .hd-team-editorial__top (template
   wrapper). align-self: start + margin-top: 14% drops it ~14% from the
   top of the grid stack; full-width LEFT-aligned; static clamp drives
   size (NOT the JS fit helper — initEditorialTitleFit stays disabled). */
.hd-team-editorial__title {
  z-index: 1;
  align-self: start;
  grid-column: 1;
  grid-row: 1;
  margin-top: 3%;
  margin-bottom: 0;
  padding: 0 clamp(24px, 4vw, 64px);
  text-align: center;
  font-family: var(--font-display, var(--font-body));
  font-size: clamp(48px, 13vw, 240px);
  line-height: 0.84;
  font-weight: 300;
  letter-spacing: -0.035em;
  white-space: nowrap;
  color: var(--bg);
  width: 100%;
  pointer-events: none;
  /* Transform is owned by GSAP ScrollTrigger (initEditorialTeamParallax)
     in motion.js: title rises (yPercent 0 → -22) + subtle scale-up
     (1.0 → 1.06) as the user scrolls down. NEVER translates on X. */
  will-change: transform;
  transform-origin: center top;
}
@media (prefers-reduced-motion: reduce) {
  /* Title settles at no-transform (no rise, no scale). */
  .hd-team-editorial__title { transform: none !important; }
  /* Hero img keeps the +8 % base framing for reduced-motion users —
     matches the GSAP start position (yPercent: 8) so the visual position
     of the dentists is consistent whether or not the parallax runs. */
  .hd-team-editorial__hero-img { transform: translateY(8%) !important; }
  /* Cards settle visible at their natural position (no fade, no rise). */
  .hd-team-editorial__card { transform: none !important; opacity: 1 !important; }
}

/* Cards — 3-column text overlay anchored to the BOTTOM of the hero.
   Aventura's "cards" are text-only labels positioned on top of the
   single composite trio photo (no per-card photos).
   Like the title, this is VIEWPORT-centered, not anchored to the
   116 %-wide hero's offset edges (which would push the left card
   off-screen). */
.hd-team-editorial__cards {
  position: absolute;
  /* Raised from 10% to 13% so the per-dentist text sits higher inside
     the hero box (cards were reading too low at the bottom of frame). */
  bottom: clamp(120px, 13%, 200px);
  left: 50%;
  transform: translateX(-50%);
  width: min(calc(100vw - 64px), 1376px);
  max-width: 1376px;
  z-index: 3;
  /* Aventura's 680/498/198 uneven editorial grid — the central card
     gets less width because the central dentist's pose extends right
     into the next column. */
  display: grid;
  grid-template-columns: 47fr 35fr 14fr;
  gap: 0;
  pointer-events: none;
}
.hd-team-editorial__card {
  display: flex;
  flex-direction: column;
  gap: clamp(6px, 0.8vh, 12px);
  color: var(--bg);
}
.hd-team-editorial__name {
  font-family: var(--font-display, var(--font-body));
  font-size: clamp(18px, 1.5vw, 22px);
  line-height: 1.09;               /* matches Aventura's 21.8/20 */
  font-weight: 500;
  letter-spacing: -0.005em;
  color: var(--bg);
  margin: 0;
  will-change: transform, opacity;
}
.hd-team-editorial__role {
  font-family: var(--font-body);
  font-size: clamp(13px, 1vw, 15px);
  line-height: 1.13;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--bg);
  opacity: 0.62;
  margin: 0;
  will-change: transform, opacity;
}
.hd-team-editorial__bio {
  font-family: var(--font-body);
  font-size: clamp(12px, 0.9vw, 14px);
  line-height: 1.42;               /* matches Aventura 14.28/12 */
  font-weight: 400;
  color: var(--bg);
  opacity: 0.84;
  margin: clamp(6px, 0.8vh, 12px) 0 0;
  max-width: 28ch;
  will-change: transform, opacity;
}

/* Band — sits in normal flow BELOW the hero, full-bleed dark.
   Tagline LEFT, big-text-with-typewriter RIGHT. */
.hd-team-editorial__band {
  position: relative;
  width: 100%;
  max-width: 1440px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: start;
  gap: clamp(40px, 5vw, 80px);
  /* Top padding reduced from 8vh → 5vh and bottom from 4vh → 3vh so the
     tagline + descriptor sit higher (closer to the hero above) instead
     of dropping into the lower portion of the dark band. */
  padding: clamp(28px, 5vh, 80px) clamp(24px, 4vw, 80px) clamp(20px, 3vh, 48px);
}
.hd-team-editorial__tagline {
  display: flex;
  flex-direction: column;
  gap: clamp(2px, 0.4vh, 6px);
  font-family: var(--font-display, var(--font-body));
  font-size: clamp(16px, 1.4vw, 22px);
  line-height: 1.09;
  font-weight: 500;
  color: var(--bg);
  margin: 0;
  max-width: 30ch;
  letter-spacing: -0.005em;
}
.hd-team-editorial__tagline-l1 {
  opacity: 1;
}
.hd-team-editorial__tagline-l2 {
  opacity: 0.4;                    /* matches Aventura's line-2 opacity */
}
.hd-team-editorial__desc {
  font-family: var(--font-display, var(--font-body));
  /* Aventura measured ~55px InstrumentSerif. Brusco's right column is
     ~696px wide at 1440 viewport; at 56px the text wraps to 2 lines as
     Aventura does. Larger sizes overflow into 4 lines. */
  font-size: clamp(32px, 3.8vw, 56px);
  line-height: 1.0;
  font-weight: 300;
  letter-spacing: -0.025em;
  color: var(--bg);
  margin: 0;
}
/* Prefix flows inline with the cycle so the natural wrap matches
   Aventura's "Our team is dedicated to / providing [cycle]" two-line
   layout. Forcing display:block on the prefix wraps everything into 3
   lines as soon as the cycle word is longer than a single short word. */
.hd-team-editorial__desc-prefix {
  display: inline;
}
.hd-team-editorial__desc-cycle {
  display: inline-block;
  position: relative;
  white-space: nowrap;
  /* Reserve baseline space so the line layout doesn't collapse when the
     typewriter is mid-cycle (text temporarily empty between delete and
     retype phases). */
  min-width: 7ch;
}
/* Blinking cursor on the cycling word */
.hd-team-editorial__desc-cycle::after {
  content: "";
  display: inline-block;
  width: 0.05em;
  height: 0.82em;
  margin-left: 0.08em;
  background: currentColor;
  vertical-align: -0.06em;
  animation: hd-tw-blink 0.9s steps(1, end) infinite;
}
@keyframes hd-tw-blink {
  0%, 49% { opacity: 1; }
  50%, 100% { opacity: 0; }
}

/* Pill — centered row in normal flow beneath the band. */
.hd-team-editorial__pill-wrap {
  position: relative;
  display: flex;
  justify-content: center;
  /* Bottom padding reduced from 12vh → 8vh so the pill (and the band
     content above it) sit higher in the section instead of leaving a
     deep dead space below the pill. */
  padding: 0 clamp(24px, 4vw, 80px) clamp(48px, 8vh, 112px);
}
.hd-team-editorial__pill {
  display: inline-flex;
  align-items: center;
  gap: 18px;
  /* Bumped from 44-52px to 60-76px for prominence — this is the
     primary conversion CTA of the dark section, so it earns the
     visual weight. Padding scaled in tandem to read as LONGER
     (more horizontal) without becoming a stretched bar. */
  height: clamp(60px, 4.5vw, 76px);
  padding: 0 clamp(36px, 3.2vw, 56px) 0 clamp(40px, 3.6vw, 60px);
  background: var(--bg);
  color: var(--ink);
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: clamp(15px, 1.15vw, 18px);
  font-weight: 700;
  letter-spacing: 0;
  text-decoration: none;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.32);
}
.hd-team-editorial__pill:hover {
  transform: translateY(-1px);
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.36);
}
.hd-team-editorial__pill-icon {
  display: block;
  flex-shrink: 0;
}

/* Reduced-motion fallback: keep the photo's +4 % base framing (set in the
   .hd-team-editorial__hero-img rule above and reinforced by the earlier
   reduced-motion block); disable only the typewriter cursor here. The
   earlier block already handles the title/cards transform/opacity reset. */
@media (prefers-reduced-motion: reduce) {
  .hd-team-editorial__desc-cycle::after { animation: none; opacity: 1; }
}

@media (max-width: 900px) {
  .hd-team-editorial__hero { width: 100%; margin-left: 0; aspect-ratio: 4 / 5; }
  .hd-team-editorial__cards { grid-template-columns: 1fr; bottom: clamp(32px, 5%, 60px); gap: clamp(16px, 2vh, 24px); }
  .hd-team-editorial__band { grid-template-columns: 1fr; gap: clamp(20px, 4vh, 40px); }
  .hd-team-editorial__title { font-size: clamp(56px, 14vw, 96px); }
}

/* ============================================================
   Footer — Yucca-style layout, Brusco skin.

   Real CDP measurements from yucca.co.za @ 1440 viewport:
     • Content max-width 1133.3 px, side gutters 153.3 px.
     • Header row 66.7 px tall (back-to-top button height).
     • Cards row: 4-cell grid, gap 20 px; each card 268.3 × 274.7,
       border 1 px, radius 13.33 px, padding 20 px.
     • Bottom pill: 1133.3 × 53.3, border 1 px, radius 6.67 px,
       padding 0 20 px.
     • Headline 46.67 px / 56 lh / w300; pill links 13.33 px.
     • Card :hover settles to border-radius 83.33 px + border
       transparent over 1 s cubic-bezier(0.22, 1, 0.36, 1).
     • Back-to-top transition: border-radius 0.4 s same curve (no
       observed end-state delta — kept as a subtle interaction).

   Brusco-skinned:
     bg     = var(--bg)     = #FFFCF7   (cream)
     ink    = var(--ink)    = #0E2233   (charcoal)
     accent = var(--accent) = #1478A2   (teal, hover/icon fill)
     type   = var(--font-display) = Bricolage Grotesque
   ============================================================ */

/* === Ring transition (team-editorial → footer) ===============
   Two-circle smile → vesica → rotate → exit motion, sticky-pinned,
   with a scroll-linked dark → cream background that handshakes
   with the cream footer above it.

   Structure (matches transition_ring.html.j2):
     .hd-ring-transition           — 300vh outer = 200vh of pinned
                                     scroll budget + 100vh of post-
                                     sticky tail (covered by footer).
     .hd-ring-transition__pin      — 100vh sticky canvas. Background
                                     interpolates --ink-deep → --bg
                                     via --ring-bg-progress (motion.js).
     .hd-ring-transition__top-fade — 140px scrim at viewport top that
                                     hides the rising statement from
                                     the fixed MENU / BOOK NOW nav.
     .hd-ring-transition__statement — "We are here / to Enhance /
                                     Your Smile" — Instrument Serif,
                                     centered, fades p=0.85 → 1.
     .hd-ring-transition__stage--{left,right} — two 900×900 SVG
                                     canvases that overlap at the
                                     viewport center by default and
                                     drive the motion via per-stage
                                     --ring-a-{tx,ty,rot} and
                                     --ring-b-{tx,ty,rot} transforms.
     .hd-ring-transition__label    — "Why choose us?" sits 48.55 px
                                     above viewport center, fades with
                                     the statement.

   Each SVG holds a full <circle r=401 cx=401.5 cy=401.5> in
   viewBox 0 0 803 803, stroked with a vertical double-cusp white
   gradient (opacity 1→0→1 at stops 0/0.1/0.2/0.8/0.9/1) so the
   visible perimeter reads as two glowing arcs at the top and bottom
   of each circle — the source of the "smile" shape during overlap.

   Tunables:
     --ring-warm-start: 0.5   (when dark→cream interpolation begins)
     --ring-warm-end:   1.0   (when interpolation reaches cream)
   ============================================================ */
.hd-ring-transition {
  /* Dark→cream warm window. Starts at p=0.5 — the same scroll
     position the footer begins entering the viewport from below
     (margin-top: -75vh) — so the pin's bg has already begun
     warming when the footer's mask-fade reveals it through. By
     p=1 the pin is cream, the footer's cream is at viewport top,
     and the seam is invisible. */
  --ring-warm-start: 0.5;
  --ring-warm-end:   1.0;
  --ring-stmt-opacity: 1;
  /* Per-circle starting transforms (S1: A's smile visible at the
     viewport bottom, B fully off-screen above). All values are
     scrubbed by motion.js across the pinned scroll. */
  --ring-a-tx:   0px;
  --ring-a-ty:   -149px;
  --ring-a-rot:  0deg;
  --ring-b-tx:   250px;
  --ring-b-ty:   -1000px;
  --ring-b-rot:  0deg;
  --ring-bg-progress: 0;

  position: relative;
  width: 100%;
  height: 300vh;
  z-index: 1;
  background: var(--ink-deep);
  isolation: isolate;
}

/* Sticky pin — 100vh canvas, in view for the section's 200vh pin
   range. bg + text color both interpolate dark → cream via
   --ring-bg-progress (already mapped through warm-start/end in
   motion.js). Dark end uses --ink-deep (near black) so the pin
   matches the section's bg at progress=0. */
.hd-ring-transition__pin {
  --ring-pin-bg: color-mix(
    in srgb,
    var(--ink-deep) calc((1 - var(--ring-bg-progress)) * 100%),
    var(--bg) calc(var(--ring-bg-progress) * 100%)
  );
  position: sticky;
  top: 0;
  width: 100%;
  height: 100vh;
  overflow: hidden;
  background: var(--ring-pin-bg);
  /* color interpolates between cream (for cream bg) and ink (for dark bg)
     so the statement / label flips to ink as the cream comes in. */
  color: color-mix(
    in srgb,
    var(--bg) calc((1 - var(--ring-bg-progress)) * 100%),
    var(--ink) calc(var(--ring-bg-progress) * 100%)
  );
}

/* Top-fade scrim — opaque pin-bg at the top 140 px, fading to
   transparent below. Hides anything that would otherwise collide
   with the fixed MENU / BOOK NOW nav. Inherits the pin's warmed
   bg so it tracks the dark→cream interpolation. z-index above
   the statement (z:3) and label (z:4). */
.hd-ring-transition__top-fade {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 140px;
  background: linear-gradient(
    to bottom,
    var(--ring-pin-bg) 0%,
    var(--ring-pin-bg) 50%,
    transparent 100%
  );
  pointer-events: none;
  z-index: 5;
}

/* Statement: "We are here / to Enhance / Your Smile". Centered
   vertically; fades out at p=0.85 → 1 via --ring-stmt-opacity so
   the footer arrives over clean cream with nothing bleeding
   through its mask. */
.hd-ring-transition__statement {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  transform: translateY(-50%);
  opacity: var(--ring-stmt-opacity);
  z-index: 3;
  pointer-events: none;
}
.hd-ring-transition__title {
  margin: 0;
  /* SECTION-LOCAL SERIF EXCEPTION (per user 2026-06-17):
     This statement uses Instrument Serif (Didone-style high-contrast)
     to match Aventura's visual character. Italics on `here` and
     `Enhance` ride on the italic axis. Everything else in brusco stays
     Bricolage — see `.hd-ring-transition__label` (Inter, unchanged)
     and the rest of the theme. */
  font-family: "Instrument Serif", "Times New Roman", Georgia, serif;
  /* Serif metrics: Instrument Serif is narrower than Bricolage at the
     same px-size, so we can run it bigger and still fit the 1376
     container with a generous gap. Aventura's reference was 153 px;
     we land at 144 max — close to source. */
  font-size: clamp(64px, 10vw, 144px);
  font-weight: 400;
  letter-spacing: -0.03em;             /* matches Aventura's −3 % em */
  line-height: 1;                      /* tight */
  color: inherit;
  text-align: center;
}
.hd-ring-transition__line {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  width: min(100% - 64px, 1376px);
  margin: 0 auto;
}
/* Line 2 (= "Your Smile") is centered, no gap. */
.hd-ring-transition__line--2 {
  display: block;
  text-align: center;
  margin-top: 0;
}
.hd-ring-transition__phrase {
  display: inline-block;
}
.hd-ring-transition__italic {
  font-style: italic;
}

/* Stages: each is a 900 × 900 SVG canvas (rendered circle radius
   ≈ 449 px). Both stages share the same horizontal position
   (left: calc(50% - 450px)), so they overlap at the viewport
   center by default — the wide visible "smile" at S1 is the
   combined bottom-cusp arcs of both circles. Rotation pivots at
   stage center (transform-origin 50% 50%) so each circle rotates
   in place during the rotate+separate phase. Per-stage transforms
   (--ring-a-* on LEFT, --ring-b-* on RIGHT) are scrubbed by
   motion.js. */
.hd-ring-transition__stage {
  position: absolute;
  width: 900px;
  height: 900px;
  top: 50%;
  margin-top: -450px;
  transform-origin: 50% 50%;
  will-change: transform;
  z-index: 1;
  pointer-events: none;
}

.hd-ring-transition__stage--left,
.hd-ring-transition__stage--right {
  left: calc(50% - 450px);
}

.hd-ring-transition__stage--left {
  transform:
    translate(var(--ring-a-tx), var(--ring-a-ty))
    rotate(var(--ring-a-rot));
}

.hd-ring-transition__stage--right {
  transform:
    translate(var(--ring-b-tx), var(--ring-b-ty))
    rotate(var(--ring-b-rot));
}

.hd-ring-transition__svg {
  width: 100%;
  height: 100%;
  display: block;
  overflow: visible;
}

/* Label "Why choose us?" — pinned 48.55 px above viewport center,
   sits in the statement's gap. Fades with the statement via the
   same --ring-stmt-opacity. */
.hd-ring-transition__label {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, calc(-50% - 48.55px));
  opacity: var(--ring-stmt-opacity);
  font-family: var(--font-body);             /* Inter — matches Aventura's InterTight */
  /* Bumped from clamp(12px, 0.95vw, 14px) so the label reads at section
     scale without competing with the 144px statement. The new max (18px)
     stays well inside the gap between the arc phrases — "Still waiting?"
     at 18px is ≈108px wide vs the ≥500px label-gap available at 1100px+. */
  font-size: clamp(15px, 1.1vw, 18px);
  font-weight: 500;
  letter-spacing: -0.01em;
  color: inherit;
  white-space: nowrap;
  z-index: 4;
  pointer-events: none;
}

/* === Footer ==================================================
   Yucca (yucca.co.za) layout, Brusco skin. Cream canvas, headline
   + CTA pill + back-to-top in the top row, 4-cell card grid below,
   outlined utility pill at the bottom.

   Boundary with the ring-transition section above:
     margin-top: -75vh   — pulls the footer up over the section's
                           100vh post-sticky tail, leaving a 25vh
                           cream "breath" between motion-end (sticky
                           disengage) and footer-content arriving.
     z-index: 2          — sits above the ring section's z:1 so the
                           footer's cream covers the pin during the
                           handoff.
     mask-image          — the footer's top 90vh fades transparent →
                           opaque so the warming pin shows through
                           the overlap, blending the dark→cream
                           gradient instead of a hard cream edge.
   ============================================================ */
.hd-footer {
  position: relative;
  background: var(--bg);
  color: var(--ink);
  margin-top: -75vh;
  z-index: 2;
  -webkit-mask-image: linear-gradient(
    to bottom,
    transparent 0%,
    rgba(0, 0, 0, 0.4) 30vh,
    rgba(0, 0, 0, 0.85) 60vh,
    #000 90vh
  );
          mask-image: linear-gradient(
    to bottom,
    transparent 0%,
    rgba(0, 0, 0, 0.4) 30vh,
    rgba(0, 0, 0, 0.85) 60vh,
    #000 90vh
  );
  isolation: isolate;
}

/* Inner container — matches Yucca's 1133.3 px content width with the
   exact 153.3 px gutters at 1440 viewport. No horizontal padding here:
   the gutter comes from `width: min(1133, 100% - 48px)` + margin auto,
   so the headline + logo + cards row + pill ALL share one left edge at
   viewport x ≈ 153 — same as Yucca's headline-x = logo-x alignment.
   Top padding = 240 (seam) + 33.8 (Yucca's top-of-footer gap above the
   headline) ≈ 274 px. Bottom padding mirrors Yucca's ~33 px. */
.hd-footer__inner {
  position: relative;
  z-index: 1;
  width: min(1133px, 100% - 48px);
  margin: 0 auto;
  /* Lowered again per inbox red-line annotation — at the user's normal
     scroll position the headline still sat at the level of the menu nav.
     +60 px more so the headline drops below the nav bar area and sits
     in clear cream space. */
  padding: clamp(520px, 30vh + 480px, 574px) 0 clamp(32px, 4vh, 50px);
  display: flex;
  flex-direction: column;
  gap: 0;
}

/* === Top row: headline + CTA pill LEFT, back-to-top RIGHT === */
.hd-footer__header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: clamp(24px, 4vw, 64px);
  /* Yucca's measured header-bottom → cards-top gap = 419 px at 1440 ×
     900. The clamp lets the gap breathe with the viewport while pinning
     it to exactly 419 px at our reference viewport. */
  margin-bottom: clamp(200px, 47vh, 419px);
}

/* Headline + CTA pill grouped on the LEFT; CTA bottom-aligns with the
   headline's second line. */
.hd-footer__header-left {
  display: flex;
  align-items: flex-end;
  gap: clamp(20px, 2.4vw, 36px);
  flex-wrap: wrap;
}

.hd-footer__headline {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(28px, 3.25vw, 46.67px);
  line-height: 1.2;
  font-weight: 300;
  letter-spacing: -0.01em;
  color: var(--ink);
  /* No max-width — the headline uses an explicit `<br>` for the
     two-line break, so width is determined by the wider line. */
}

/* === CTA pill (Make A Call) — dark fill, cream text, arrow circle === */
.hd-footer__cta-pill {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  height: 48px;
  padding: 0 8px 0 22px;
  background: var(--ink);
  color: var(--bg);
  border-radius: 999px;
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.005em;
  text-decoration: none;
  transition: background-color 0.3s ease, color 0.3s ease, transform 0.3s ease;
}
.hd-footer__cta-pill:hover {
  background: var(--accent);
  color: var(--bg);
}
.hd-footer__cta-pill-label {
  white-space: nowrap;
}
.hd-footer__cta-pill-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  background: var(--bg);
  color: var(--ink);
  border-radius: 50%;
  flex-shrink: 0;
}
.hd-footer__cta-pill-arrow svg {
  display: block;
}

.hd-footer__back-to-top {
  flex-shrink: 0;
  width: 66.67px;
  height: 66.67px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--ink);
  border-radius: 6.67px;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  padding: 0;
  transition: border-radius 0.4s cubic-bezier(0.22, 1, 0.36, 1),
              color 0.4s cubic-bezier(0.22, 1, 0.36, 1),
              border-color 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
.hd-footer__back-to-top:hover {
  /* Subtle softening — radius grows, arrow shifts to accent.
     (Yucca's probe showed no end-state delta but the transition
     CSS suggests interactive feedback; brusco gives a small one.) */
  border-radius: 22px;
  color: var(--accent);
  border-color: var(--accent);
}
.hd-footer__back-icon {
  width: 33.33px;
  height: 33.33px;
  display: block;
}

/* === Cards row: 4-cell grid, logo + 3 cards === */
.hd-footer__content {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 20px;
  margin-bottom: 20px;
}

.hd-footer__logo-cell {
  /* Bottom-align the logo glyph so its baseline lines up with the
     cards' bottom edge — one tidy band across the row. */
  display: flex;
  align-items: flex-end;
  justify-content: flex-start;
  height: 274.7px;
  overflow: hidden;
}
.hd-footer__logo {
  width: 100%;
  height: 100%;
  color: var(--ink);
  display: block;
  /* viewBox is cropped to the glyph's exact extent (30,18 → 125,149
     in the path's 160-unit coord space) and preserveAspectRatio
     "xMinYMax meet" anchors the glyph to the bottom-left of the cell.
     Net: the B's bottom = cell bottom, its top reaches the cell top,
     so logo height matches card height. */
}
/* Image variant — when brand_mark.image is set. Bottom-aligned to match
   the cell's flex-end so the image base lines up with cards' bottom edge. */
.hd-footer__logo--img {
  width: auto;
  max-width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: left bottom;
}
/* Text variant — when brand_mark.text is set (no svg / image override).
   Renders the character in the brand display font at a heavy display size
   tuned to feel like a monogram. Tracking pulled negative so the letter
   feels chiseled rather than airy. Bottom-aligned via flex-end + line-height
   shaping so the baseline matches the cards' bottom edge. */
.hd-footer__logo--text {
  display: flex;
  align-items: flex-end;
  height: 100%;
  font-family: var(--font-display);
  font-size: 240px;
  font-weight: 600;
  line-height: 0.85;
  letter-spacing: -0.06em;
  color: var(--ink);
  user-select: none;
}

.hd-footer__card {
  /* containing block for the absolutely-positioned card-media wrapper. */
  position: relative;
  overflow: hidden;                 /* clip the oversized inner to the card */
  display: flex;
  align-items: center;
  justify-content: center;          /* Centered both axes per user correction */
  height: 274.7px;
  padding: 20px;
  border: 1px solid var(--ink);
  border-radius: 13.33px;
  background: transparent;
  /* Rest label color is WHITE — sits over the bright clinical photo. */
  color: #fff;
  text-decoration: none;
  text-align: center;
  font-family: var(--font-display);
  font-size: clamp(16px, 1.2vw, 18px);
  font-weight: 300;
  line-height: 1.4;
  /* Hover transition extracted from Yucca: 1 s cubic-bezier on
     border-radius + border-color. */
  transition: border-radius 1s cubic-bezier(0.22, 1, 0.36, 1),
              border-color 1s cubic-bezier(0.22, 1, 0.36, 1),
              color 1s cubic-bezier(0.22, 1, 0.36, 1);
}
.hd-footer__card:hover {
  /* Radius still morphs to the 83.33 pill shape, but per user the
     1 px ink border STAYS visible on hover — end-state is outlined
     pill + black label, not a borderless floating label.
     Hover label = pure black on cream (image faded out). */
  border-radius: 83.33px;
  color: #000;
}
.hd-footer__card-label {
  pointer-events: none;
  /* Sit above the image layer so the label reads on top at rest and
     during the hover fade-out. */
  position: relative;
  z-index: 2;
  /* Local scrim for white-on-bright-photo legibility — soft black
     shadow under the label only. Disappears visually with the photo
     during hover (text becomes black, shadow is invisible on its own). */
  text-shadow:
    0 1px 2px rgba(0, 0, 0, 0.55),
    0 0 14px rgba(0, 0, 0, 0.35);
  transition: text-shadow 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
.hd-footer__card:hover .hd-footer__card-label {
  /* On hover the label is black on cream — kill the shadow. */
  text-shadow: none;
}

/* === Card image stack — INVERTED from Yucca ===
   Same 4-layer structure as Yucca's .f-solution → .media-wrapper →
   .media-inner → <img> (see /tmp/yucca-card-image.json), but inverted
   behavior: image VISIBLE at rest, fades OUT + zooms in on hover so
   the card returns to a clean outlined-pill + label.

   - .__card-media: the wrapper (Yucca's .media-wrapper). Same dark
     backdrop (var(--ink) — brusco's charcoal). overflow: hidden clips
     the oversized inner.
   - .__card-media-inner: oversized by ~26px past each card edge
     (matches Yucca's 319.6 × 327.2 vs card 268.3 × 274.7). At rest
     scale(1) shows the centered crop; on hover scale(1.2) zooms in
     while the wrapper fades to opacity 0.
   - opacity: 0.7 on the inner dims the image so the label stays
     legible at rest (Yucca uses the same 0.7).
   - Easing: cubic-bezier(0.22, 1, 0.36, 1) — same family as the card's
     other transitions. Durations match Yucca: 0.6s opacity, 1.6s
     transform. */
.hd-footer__card-media {
  position: absolute;
  inset: 1px;                         /* sit inside the 1px border */
  overflow: hidden;
  border-radius: inherit;             /* follow card radius (incl. hover morph) */
  /* No dark backdrop — image renders clean against whatever is behind. */
  opacity: 1;                         /* VISIBLE at rest (inverted from Yucca) */
  transition: opacity 0.6s cubic-bezier(0.22, 1, 0.36, 1);
  z-index: 1;                         /* below the label (z:2) */
  pointer-events: none;
}
.hd-footer__card-media-inner {
  position: absolute;
  inset: -26px;                       /* oversize ~26px past each edge */
  background-size: cover;
  background-position: 50% 50%;
  background-repeat: no-repeat;
  /* Full brightness — no dim. Label legibility handled by local
     text-shadow on .hd-footer__card-label, not by darkening the photo. */
  opacity: 1;
  transform: scale(1);                /* natural at rest (inverted from Yucca) */
  transition: transform 1.6s cubic-bezier(0.22, 1, 0.36, 1);
  will-change: transform;
}
/* Video variant of the media-inner — when card.media.type is 'video'.
   Inherits inset/scale/transition from the rule above; only adds the
   <video>-specific bits (object-fit, width/height to fill the oversized box). */
.hd-footer__card-media-inner--video {
  width: calc(100% + 52px);           /* inset: -26px on each side */
  height: calc(100% + 52px);
  object-fit: cover;
  object-position: 50% 50%;
}
.hd-footer__card:hover .hd-footer__card-media {
  opacity: 0;                         /* fades OUT on hover */
}
.hd-footer__card:hover .hd-footer__card-media-inner {
  transform: scale(1.2);              /* zooms IN as the image fades */
}

/* === Bottom pill — pill-left (copyright + socials grouped) / links right === */
.hd-footer__pill {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(16px, 2vw, 32px);
  height: 53.33px;
  padding: 0 20px;
  border: 1px solid var(--ink);
  border-radius: 6.67px;
}
.hd-footer__pill-left {
  display: inline-flex;
  align-items: center;
  gap: clamp(20px, 2.2vw, 32px);
}

.hd-footer__copyright {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-display);
  font-size: 16.67px;
  font-weight: 300;
  color: var(--ink);
}
.hd-footer__copyright-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border: 1px solid currentColor;
  border-radius: 50%;
  font-size: 11px;
  line-height: 1;
}

.hd-footer__socials {
  display: inline-flex;
  align-items: center;
  gap: 20px;             /* 20 px gap between icons (Yucca's 40 px centers) */
}
.hd-footer__social {
  width: 20px;
  height: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--ink);
  text-decoration: none;
  transition: color 0.3s ease;
}
.hd-footer__social svg {
  width: 100%;
  height: 100%;
}
.hd-footer__social:hover {
  color: var(--accent);
}

.hd-footer__links {
  display: inline-flex;
  align-items: center;
  gap: clamp(12px, 1vw, 20px);
}
.hd-footer__link {
  font-family: var(--font-display);
  font-size: 13.33px;
  font-weight: 300;
  color: var(--ink);
  text-decoration: none;
  transition: color 0.3s ease;
}
.hd-footer__link:hover {
  color: var(--accent);
}

@media (max-width: 900px) {
  .hd-footer__inner { padding: 200px clamp(16px, 4vw, 32px) 32px; }
  .hd-footer__header { flex-direction: row; margin-bottom: 48px; }
  .hd-footer__headline { font-size: clamp(24px, 6vw, 36px); }
  .hd-footer__content { grid-template-columns: 1fr 1fr; }
  .hd-footer__logo-cell { height: 200px; grid-column: 1 / -1; }
  .hd-footer__card { height: 180px; }
  .hd-footer__pill { flex-wrap: wrap; height: auto; padding: 12px 16px; row-gap: 8px; }
}

/* ============================================================
   PACK OVERLAY — clinical (was futuristic, was a pack overlay over
   the brusco theme until 2026-06-28). The rules above this line
   originate from the cinematic-instrument skeleton default style;
   the rules below this line are the clinical pack overrides.
   Cascade order preserved: pack rules win where they redefine the
   skeleton-default rules above.
   ============================================================ */

/* ============================================================
   Pack — clinical / style.css   (renamed from futuristic 2026-06-28)

   Component finish for the clinical / cool / precision-instrument
   register. Carries forward — by value, not approximation — the
   exact mechanics from the senior-designer-signed A2-corrected
   test build (/tmp/zinc-hybrid/A2-corrected/override.css).

   All rules wrapped in @layer pack {} per the v3-build contract.
   Per-client overlays written by the build script are unlayered
   and beat these. The cascade resolves predictably; no !important
   anywhere — the layer mechanism handles precedence against the
   default-cascade structure rules.

   Selectors here target the class namespace used by the test build.
   Many of them name brusco-class sections (statement, composite,
   wall, faq, team-editorial) — these rules apply when the pack is
   composed with a structure that emits those class names. On a
   pure signature build they are inert. This is intentional: the
   pack ships its full mechanics ready for the cinematic structure
   (planned home) or any brusco-derived structure (where A2 was
   tested), without requiring shared-tree edits.
   ============================================================ */

@layer pack {

  /* ──────────────────────────────────────────────────────────
     STATEMENT SECTION — pin the expanding-pill fill to the
     canvas. The structure layer emits the section with an inline
     style="--hd-statement-fill-bg: …" that carries the demo-overlay
     warm value. Override the cascade so the var resolves to var(--bg)
     (the clinical slate) instead.
     ────────────────────────────────────────────────────────── */
  [data-hd-statement-expand] {
    --hd-statement-fill-bg: var(--bg);
  }

  /* ──────────────────────────────────────────────────────────
     STATEMENT DECK CARDS — cool-shifted gradient stack. Mirrors
     the same RELATIVE lightness curve as the original warm beige
     stack but inside the cool slate hue family. Card 4 stays the
     dark anchor of the deck composition (the only "darker" surface
     allowed in the futuristic register — it's a deliberate stage,
     not an accidental dark card).
     ────────────────────────────────────────────────────────── */
  .hd-statement__card--1 {
    background: linear-gradient(135deg, #FBFBFD 0%, #F2F4F7 60%, #E9ECF0 100%);
  }
  .hd-statement__card--2 {
    background: linear-gradient(135deg, #F4F5F7 0%, #E8EBEF 58%, #DCE0E6 100%);
  }
  .hd-statement__card--3 {
    background: linear-gradient(135deg, #E9ECF0 0%, #DADFE5 58%, #C9CFD7 100%);
  }
  .hd-statement__card--4 {
    background: linear-gradient(135deg, #2A2D32 0%, #1E2024 55%, #15161A 100%);
  }

  /* ──────────────────────────────────────────────────────────
     COMPOSITE CARDS — service-card variety via --surface-contrast.

     The portrait card stays blended with the canvas; the form
     card stays the "lighter step"; the FEATURE card (middle of
     the row) reads as --surface-contrast — a tone that contrasts
     the active canvas hue. Slate mode: warm cream contrast.
     Cream mode: cool slate contrast (the token flips in
     tokens.css under body.pack-canvas-cream). Result: the row
     reads as three calibrated surfaces, not a monotone stack of
     white/white-variant/blue-white.

     Form card stays the recessive "lighter, not darker" surface
     (--surface-warm). 1px border supplies elevation cue without
     contrast popping.
     ────────────────────────────────────────────────────────── */
  .hd-composite__card--feature {
    /* Was var(--surface-contrast) (warm beige rgb 244,240,230) which sat
       17 units off page bg cream (rgb 255,252,247) and produced a visible
       horizontal step at the card's top and bottom edges in the cream
       statement section. Identity now reads through the 1px border. */
    background: var(--bg);
    border: 1px solid var(--border);
  }
  /* ──────────────────────────────────────────────────────────
     FORM CARD — dark variant. Sits as the high-contrast call-out
     in the composite card row. Uses --ink-deep (pack's full-section
     dark token) so it stays on-brand with team_editorial + ring
     transition. Title in --surface-contrast (warm cream #F4F0E6,
     visible against the deep dark). All other card text light.

     Dropdowns: the pill bg + soft outline live on the
     .hd-composite__select WRAPPER (which already carries the
     border-radius 999px). The native <select> inside is forced
     transparent + borderless so it inherits the wrapper's pill
     visually — preventing a square outline from being painted by
     the select element itself.

     Form CTA: ghost-on-dark. The global ghost rule (.hd-composite__
     form-cta near top of this file) styles ghost-on-LIGHT (ink
     outline, ink label). This higher-specificity rule wins for
     the form-card-on-dark case — white outline + white label on
     transparent fill, with a flip-to-cream on hover.
     ────────────────────────────────────────────────────────── */
  .hd-composite__card--form {
    background: var(--ink-deep);
    color: var(--bg);
    border: none;
    /* The form card lives inside .hd-statement__expand which sets
       pointer-events: none (the pill backdrop is click-through by design).
       That inherits down to the card and silently disables native <select>
       clicks + :hover on the CTA. Re-enable interactivity for this card
       subtree without touching the global ancestor rule. */
    pointer-events: auto;
  }
  .hd-composite__card--form .hd-composite__card-title {
    color: var(--surface-contrast);
    text-align: center;
    white-space: nowrap;
    /* Base rule caps title at max-width: 18ch (~190px). Without override,
       text-align: center only centers within that capped box rather than
       across the card. Forcing full-card width here so the centering is
       visible. */
    max-width: none;
    width: 100%;
  }
  .hd-composite__card--form .hd-composite__card-head,
  .hd-composite__card--form .hd-composite__card-body,
  .hd-composite__card--form label,
  .hd-composite__card--form .hd-composite__field-label,
  .hd-composite__card--form select,
  .hd-composite__card--form input {
    color: var(--bg);
  }
  /* Pill wrapper carries all the dropdown visuals. */
  .hd-composite__card--form .hd-composite__select {
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: 999px;
  }
  /* Native select stays invisible inside the wrapper — transparent fill,
     no border (the brusco rule sets border: none too; restating defensively
     because pack-style.css's ancestor `.hd-composite__card--form select`
     selector elsewhere can otherwise add a square outline). */
  .hd-composite__card--form .hd-composite__select select {
    background: transparent;
    border: none;
    appearance: none;
    -webkit-appearance: none;
    color: var(--bg);
    cursor: pointer;
  }
  .hd-composite__card--form .hd-composite__form-cta {
    background: transparent;
    color: var(--bg);
    border: 1px solid var(--bg);
    transition: background .25s ease, color .25s ease;
  }
  .hd-composite__card--form .hd-composite__form-cta:hover {
    background: var(--bg);
    color: var(--ink-deep);
    border-color: var(--bg);
  }
  /* Defensive belt-and-suspenders — explicitly re-enable on the two
     known interactive descendants in case another rule narrows the
     inheritance. */
  .hd-composite__card--form .hd-composite__select select,
  .hd-composite__card--form .hd-composite__form-cta {
    pointer-events: auto;
  }

  /* ──────────────────────────────────────────────────────────
     PORTRAIT CARD — BL crescent dot uses --accent (replaces the
     reference theme's #c83a3a red literal). The radial-gradient
     mechanic (transparent 28 → accent 29) is preserved verbatim
     from the locked fix; only the color literal changes to a
     token reference so the engine-graft accent flows through.
     ────────────────────────────────────────────────────────── */
  .hd-composite__card--portrait {
    background:
      radial-gradient(circle at bottom left,
        transparent 28px,
        var(--accent) 29px);
  }
  .hd-composite__card--portrait .hd-composite__card-photo {
    background:
      radial-gradient(circle at bottom left,
        transparent 28px,
        var(--accent) 29px);
  }

  /* ──────────────────────────────────────────────────────────
     FAQ — items + brand box recess into the canvas. A 1px border
     is the only elevation cue. The +/− toggle's circular icon
     gets the accent fill on a dark glyph (ink-on-accent reads
     well against the frosted-blue at all states).
     ────────────────────────────────────────────────────────── */
  .hd-faq__item,
  .hd-faq__brand {
    background: var(--bg);
    border: 1px solid var(--border);
    /* Hover transition timing extracted from halodental.com/halo-digital-mirror
       (`.ft-accordion-item__trigger`, transition-duration: 0.6s,
       transition-timing-function: cubic-bezier(.32, .94, .6, 1)).
       Property animated is background-color (our choice — Halo
       animates opacity, but our items live at opacity 1, so a bg
       tint is the cleanest hover affordance). Hover tints to
       --bg-warm (a tasteful surface step, NOT the accent — the
       accent stays precious for active/branded states). Geometry
       unchanged on hover, matching Halo's restraint. */
    transition: background-color .6s cubic-bezier(.32, .94, .6, 1);
  }
  .hd-faq__item:hover {
    background: var(--bg-warm);
  }
  .hd-faq__icon {
    background: var(--accent);
    color: var(--ink);
    transition: background-color .3s cubic-bezier(.32, .94, .6, 1), color .3s cubic-bezier(.32, .94, .6, 1);
  }
  .hd-faq__item:hover .hd-faq__icon {
    background: var(--ink);
    color: var(--bg);
  }

  /* ──────────────────────────────────────────────────────────
     WALL-OF-LOVE — the photo wrapper bg sits one step warmer
     than the canvas (--bg-warm). Subtle separation, no contrast
     pop, no white card.
     ────────────────────────────────────────────────────────── */
  .hd-wall__media-wrap {
    /* Was var(--bg-warm) (rgb 243,240,231) which created 16-unit RGB
       steps where each portrait wrap met the cream wall panel surround.
       Photos use object-fit:cover so the wrap bg only shows briefly
       during load; matching --bg keeps the load state seamless on the
       cream wall panel. */
    background: var(--bg);
  }

  /* ──────────────────────────────────────────────────────────
     CONTRAST-AWARE GHOST BUTTONS — light-bg primaries.

     Every primary CTA that sits on the slate canvas becomes a
     ghost: transparent fill + 1px ink border + ink label. On
     hover the fill inverts to ink, label flips to bg. This is
     the single rule the senior designer wanted reaching EVERY
     primary CTA — composite, form, wall, gallery, footer all
     in one selector list so divergence is impossible.
     ────────────────────────────────────────────────────────── */
  .hd-composite__cta-primary,
  .hd-composite__cta-arrow,
  .hd-composite__form-cta,
  .hd-wall__cta,
  .hd-gallery__form-submit,
  .hd-footer__cta-pill {
    background: transparent;
    color: var(--ink);
    border: 1px solid var(--ink);
    box-shadow: none;
    transition: background .25s ease, color .25s ease;
  }
  .hd-composite__cta-primary:hover,
  .hd-composite__cta-arrow:hover,
  .hd-composite__form-cta:hover,
  .hd-wall__cta:hover,
  .hd-gallery__form-submit:hover,
  .hd-footer__cta-pill:hover {
    background: var(--ink);
    color: var(--bg);
    box-shadow: none;
    transform: none;
  }

  /* Footer pill's inner arrow inverts in tandem with the pill */
  .hd-footer__cta-pill .hd-footer__cta-pill-arrow {
    background: transparent;
    border: 1px solid var(--ink);
    color: var(--ink);
  }
  .hd-footer__cta-pill:hover .hd-footer__cta-pill-arrow {
    background: var(--bg);
    color: var(--ink);
    border-color: var(--bg);
  }

  /* Gallery form inputs share the recessive bg-warm surface so
     they don't pop as bright-white fields against the slate */
  .hd-gallery__form-input {
    background: var(--bg-warm);
    border: 1px solid var(--border);
    color: var(--ink);
  }

  /* ──────────────────────────────────────────────────────────
     DARK-BAND CTAs — frosted glass.

     The team-editorial pill sits on a --ink-deep section. A solid
     ink fill would disappear; a solid accent fill would dominate.
     The frosted treatment (semi-transparent white + 1px light
     border + backdrop-blur) keeps the pill present without
     stealing focus. Hover shifts to the accent color for explicit
     affordance.
     ────────────────────────────────────────────────────────── */
  .hd-team-editorial__pill {
    background: rgba(255, 255, 255, 0.10);
    color: var(--bg);
    border: 1px solid rgba(255, 255, 255, 0.28);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    transition: background .25s ease, color .25s ease, border-color .25s ease;
  }
  .hd-team-editorial__pill:hover {
    /* Warm gold hover via --accent-warm (#E5C97A in pack-tokens.css).
       Dark text on the gold for contrast. The token is independent of
       --accent so a per-client palette graft of --accent leaves the
       warm-hover affordance untouched. */
    background: var(--accent-warm);
    color: var(--ink-deep);
    border-color: var(--accent-warm);
  }

  /* Cursor + emphasis-words color redirects live UNLAYERED at the
     end of this file (see below). They must beat the brusco theme's
     same-selector @layer pack rules regardless of stylesheet load
     order — and the theme's style.css loads AFTER pack-style.css in
     the build's index.html, so a same-layer rule loses the source-order
     tiebreak. Unlayered = wins universally. */
}

/* ============================================================
   UNLAYERED — pin --accent to the futuristic slate-blue across
   every client using this pack.

   The build script (build.py write_tokens_css) appends an
   unlayered per-client overlay to the deployed tokens.css:
       body.client-<slug> { --accent: <slot palette accent>; ... }

   That unlayered overlay beats every @layer pack declaration of
   --accent. We can't override --accent from inside @layer pack
   for the same reason the build script wrote it unlayered.

   The only mechanism to win against an unlayered rule from inside
   pack files is ANOTHER UNLAYERED RULE that loads later in source
   order. This file (pack-style.css) loads AFTER tokens.css in the
   page's <link> order, so an unlayered block here lands later.
   Selector `body[class*="client-"]` has the same specificity as
   `body.client-<slug>` (0,1,1) and matches every client body
   class, so the later source order wins universally.

   Net effect: --accent is pinned to #C5D2DE (clinical palette)
   for every clinical-pack build, regardless of slot palette.
   The emphasis words are unaffected — they read --accent-emphasis
   declared in pack-tokens.css. The cursor stays mint via
   --cursor-fill. Per-client palette overlays can still graft --ink,
   --bg, --bg-warm, --accent-ink without touching the accent identity.
   ============================================================ */
body[class*="client-"] {
  --accent: #C5D2DE;
}

/* ============================================================
   UNLAYERED REDIRECTS — emphasis words + cursor.

   These two rules target selectors that the brusco theme's
   style.css declares inside @layer pack with var(--accent) as
   the color source. The theme's style.css loads AFTER this file
   in the build's index.html, so a same-layer rule in pack-style.css
   would lose the source-order tiebreak even when written below
   the brusco rule. Moving these redirects OUT of @layer pack lifts
   them above ALL layered rules (cascade-layer precedence: unlayered
   beats every layer), making them win universally regardless of
   stylesheet load order.

   The brusco color-mix scroll-fill expression for .hd-word--accent
   is preserved verbatim; only the source color reference swaps
   from --accent to --accent-emphasis.
   ============================================================ */

.hd-word--accent {
  color: color-mix(
    in srgb,
    var(--accent-emphasis) calc((22 + var(--word-fill, 0) * 78) * 1%),
    var(--bg-warm, #f3f0e7)
  );
}
@media (prefers-reduced-motion: reduce) {
  .hd-word--accent { color: var(--accent-emphasis); }
}

.hd-cursor__inner {
  background: var(--cursor-fill);
}

/* ============================================================
   UNLAYERED — type-scale + UI bumps (clinical pack).

   These overrides are unlayered for the same reason as the cursor
   and emphasis-words redirects above: pack-style.css loses the
   same-layer source-order tiebreak to brusco theme/style.css.
   Unlayered = wins universally.
   ============================================================ */

/* FAQ — bump the brand-mark icon size. Background stays transparent
   (brusco baseline preserved); only width + height grow. The icon
   reads slate per the pack's accent split — that color is intentional. */
.hd-faq__icon {
  width: clamp(80px, 6.4vw, 100px);
  height: clamp(80px, 6.4vw, 100px);
}

/* Composite primary CTA — kill the literal #FF1313 red baked into
   the brusco theme and unify with the pack's outlined-pill treatment
   (same shape brusco gives the secondary CTAs and the footer pill).
   Transparent rest → ink-inverted on hover, no filter brightness shift.
   Border + transition declared so theme defaults don't bleed through. */
.hd-composite__cta-primary {
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--ink);
  filter: none;
  transition: background .25s ease, color .25s ease;
}
.hd-composite__cta-primary:hover {
  background: var(--ink);
  color: var(--bg);
  filter: none;
}

/* FAQ — transparent cards + transparent toggles. Outline only, no filled
   surfaces. The brusco theme's @layer pack rules fill the Q cards white
   and paint the +/− toggle as a slate-accent square with white glyph;
   these unlayered rules strip both fills, replace with a 1px ink outline
   on the cards (no border on the toggle for a cleaner read), and flip the
   +/− glyph to ink. The pack-layered hover rule from earlier in this file
   (tint to --bg-warm) keeps working — but the icon-invert-on-hover loses
   to the theme baseline at the same layer; restating it here unlayered
   so it wins. */
.hd-faq__item,
.hd-faq__brand {
  background: transparent;
  border: 1px solid var(--ink);
  box-shadow: none;
}
.hd-faq__item.is-open {
  box-shadow: none;
}
.hd-faq__item:hover {
  background: var(--bg-warm);
}
.hd-faq__toggle {
  background: transparent;
  color: var(--ink);
}
.hd-faq__item:hover .hd-faq__toggle {
  background: transparent;
  color: var(--ink);
}

/* ============================================================
   UNLAYERED — opt-in flag for motion.js → initGalleryRunwayTransition.

   The runtime reads this custom property at init via
   getComputedStyle(document.documentElement). When the value is "1",
   the function wires a scrubbed GSAP timeline that tweens
   .hd-gallery__runway's bg cream → composited-charcoal (rgb 33,33,36
   = white 4 % over --ink-deep, matches team_editorial's hero edge)
   plus the eyebrow + title colors. When unset / any other value, the
   function bails immediately and the original hard cream / dark seam
   stays — that's the contract for classic and any future pack that
   doesn't opt in.

   Lives unlayered so a pack that wants to suppress the runway can
   override with --gallery-runway-cover: 0 without juggling cascade
   layers.
   ============================================================ */
:root {
  --gallery-runway-cover: 1;
}
