/* ═══ components.css — Box Office Watch shared components (v1, Oct 10 2026) ═══
   The shared component layer of the design system. Every class is prefixed
   ds- and follows design-system/SPEC.md; the living reference is
   /_design-system/ (noindex).

   HOW TO USE
   - Link it AFTER nav.css (nav.css @imports tokens.css):
       <link rel="stylesheet" href="/assets/nav.css">
       <link rel="stylesheet" href="/assets/components.css">
       <script src="/assets/components.js" defer></script>   (optional helper)
   - components.js adds .has-more / .is-scrolled to .ds-table-wrap and shows
     the poster title fallback when a poster image fails. Without it the page
     still renders; you just lose the table edge fade and the fallback swap.

   RULES FOR THIS FILE
   - Components use tokens only: var(--bow-…, fallback). The fallback is the
     token's own value, so a page without tokens.css renders the same.
   - The only literals are the alpha fills SPEC.md fixes (badge, rank, the
     secondary-button border) and 1px hairlines.
   - Phone breakpoint: max-width 700px, the site's existing one.
   - Quiet structure, loud data: hairlines and space, no boxed tiles. Green is
     for page-level actions only; charts and bars use --bow-data-*. */

/* ── Section header ───────────────────────────────────────────────────────
   <header class="ds-head">
     <div><p class="ds-head__eyebrow">Weekend</p>
          <h2 class="ds-head__title">Top 5 this weekend</h2></div>
     <a class="ds-head__link" href="…">Full chart</a>      (arrow is added)
   </header>
   48px above on phone, 64px on desktop. .ds-head--flush drops the top space
   (first header on a page, or under a hero). */
.ds-head{
  display:flex;align-items:flex-end;justify-content:space-between;
  gap:var(--bow-space-4,16px);
  margin:var(--bow-space-8,64px) 0 var(--bow-space-4,16px);
  text-align:left;
}
.ds-head--flush{margin-top:0}
.ds-head > :first-child{min-width:0}
.ds-head__eyebrow{
  margin:0 0 var(--bow-space-1,4px);
  font-size:var(--bow-fs-1,11px);font-weight:var(--bow-fw-semi,600);
  line-height:var(--bow-lh-data,1.35);
  text-transform:uppercase;letter-spacing:var(--bow-ls-caps,.08em);
  color:var(--bow-ink-3,#8C8C90);
}
.ds-head__title{
  margin:0;
  font-size:var(--bow-fs-5,17px);font-weight:var(--bow-fw-semi,600);
  line-height:var(--bow-lh-title,1.3);letter-spacing:0;
  color:var(--bow-ink-1,#FFFFFF);text-wrap:balance;
}
.ds-head__link{
  flex:none;white-space:nowrap;
  font-size:var(--bow-fs-3,13px);font-weight:var(--bow-fw-semi,600);
  line-height:var(--bow-lh-title,1.3);
  color:var(--bow-ink-2,#B5B5B8);text-decoration:none;
  transition:color var(--bow-dur-1,150ms) var(--bow-ease,cubic-bezier(.2,0,0,1));
}
.ds-head__link::after,
.ds-link::after{
  content:"\2192";display:inline-block;margin-left:var(--bow-space-1,4px);
  transition:transform var(--bow-dur-1,150ms) var(--bow-ease,cubic-bezier(.2,0,0,1));
}
.ds-head__link:hover{color:var(--bow-ink-1,#FFFFFF)}
.ds-head__link:hover::after,
.ds-link:hover::after{transform:translateX(2px)}

/* ── Stat group ───────────────────────────────────────────────────────────
   <div class="ds-stats" style="--cols:4">
     <div class="ds-stat">
       <p class="ds-stat__label">Domestic</p>
       <p class="ds-stat__value">$620.97M</p>
       <p class="ds-stat__sub">Week 12</p>
     </div> …
   </div>
   2 columns on phone (--cols-phone to override), --cols (default 4) on
   desktop. Hairlines between columns, and between rows on phone. The group
   is pulled out by one column gutter and clipped, so the first column's text
   lines up with the content edge and no divider shows on the outer edges. */
.ds-stats{
  --ds-stat-px:var(--bow-space-4,16px);
  --ds-stat-py:var(--bow-space-3,12px);
  display:grid;
  grid-template-columns:repeat(var(--cols,4),minmax(0,1fr));
  row-gap:var(--bow-space-5,24px);
  margin:0 0 0 calc(-1px - var(--ds-stat-px));
  clip-path:inset(0 0 0 calc(1px + var(--ds-stat-px)));
}
.ds-stat{min-width:0}
.ds-stats > .ds-stat{
  padding:0 var(--ds-stat-px,16px);
  border-left:1px solid var(--bow-line,rgba(255,255,255,.1));
}
.ds-stat__label{
  margin:0 0 var(--bow-space-2,8px);
  font-size:var(--bow-fs-2,12px);font-weight:var(--bow-fw-semi,600);
  line-height:var(--bow-lh-data,1.35);letter-spacing:0;
  color:var(--bow-ink-3,#8C8C90);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.ds-stat__value{
  margin:0;
  font-size:var(--bow-fs-7,30px);font-weight:var(--bow-fw-heavy,800);
  line-height:1;letter-spacing:var(--bow-ls-tight,-.02em);
  font-variant-numeric:tabular-nums;
  color:var(--bow-ink-1,#FFFFFF);white-space:nowrap;
}
.ds-stat--lead .ds-stat__value{font-size:var(--bow-fs-8,44px)}
.ds-stat__sub{
  margin:var(--bow-space-1,4px) 0 0;
  font-size:var(--bow-fs-2,12px);font-weight:var(--bow-fw-regular,400);
  line-height:var(--bow-lh-data,1.35);
  color:var(--bow-ink-3,#8C8C90);text-wrap:balance;
}
@media (max-width:700px){
  .ds-stats{
    grid-template-columns:repeat(var(--cols-phone,2),minmax(0,1fr));
    row-gap:0;
    margin:calc(-1px - var(--ds-stat-py)) 0 calc(-1 * var(--ds-stat-py)) calc(-1px - var(--ds-stat-px));
    clip-path:inset(calc(1px + var(--ds-stat-py)) 0 0 calc(1px + var(--ds-stat-px)));
  }
  .ds-stats > .ds-stat{
    padding:var(--ds-stat-py,12px) var(--ds-stat-px,16px);
    border-top:1px solid var(--bow-line,rgba(255,255,255,.1));
  }
  .ds-stat__value{font-size:var(--bow-fs-6,22px)}
  .ds-stat--lead .ds-stat__value{font-size:var(--bow-fs-7,30px)}
}

/* Inline stats: label and value on one line, for meta rows
   (legs · budget · rank). Wraps between items, never inside one; the
   divider at the start of a wrapped line is clipped away like the grid's. */
.ds-stats--inline{
  --ds-stat-px:var(--bow-space-3,12px);
  display:flex;flex-wrap:wrap;align-items:center;
  row-gap:var(--bow-space-2,8px);
  margin:0 0 0 calc(-1px - var(--ds-stat-px));
  clip-path:inset(0 0 0 calc(1px + var(--ds-stat-px)));
}
.ds-stats--inline > .ds-stat{
  display:inline-flex;align-items:baseline;gap:6px;white-space:nowrap;
  padding:0 var(--ds-stat-px,12px);border-top:0;
}
.ds-stats--inline .ds-stat__label{
  margin:0;font-size:var(--bow-fs-2,12px);overflow:visible;
}
.ds-stats--inline .ds-stat__value{
  font-size:var(--bow-fs-3,13px);font-weight:var(--bow-fw-semi,600);
  line-height:var(--bow-lh-data,1.35);letter-spacing:0;
}
.ds-stats--inline .ds-stat__sub{margin:0}
@media (max-width:700px){
  .ds-stats--inline{
    margin:0 0 0 calc(-1px - var(--ds-stat-px));
    clip-path:inset(0 0 0 calc(1px + var(--ds-stat-px)));
  }
  .ds-stats--inline > .ds-stat{padding:0 var(--ds-stat-px,12px);border-top:0}
  .ds-stats--inline .ds-stat__value{font-size:var(--bow-fs-3,13px)}
}

/* ── Badge ────────────────────────────────────────────────────────────────
   Neutral by default. --accent only for live states (Live, Locked,
   New record); --warn for Tracking and estimates. Release type shows only
   when it isn't Wide, in the neutral style. */
.ds-badge{
  display:inline-flex;align-items:center;flex:none;
  height:20px;padding:0 var(--bow-space-2,8px);
  border-radius:var(--bow-r-sm,4px);
  font-size:var(--bow-fs-1,11px);font-weight:var(--bow-fw-semi,600);
  line-height:1;letter-spacing:0;text-transform:none;white-space:nowrap;
  vertical-align:middle;
  background:var(--bow-fill-neutral,rgba(255,255,255,.08));color:var(--bow-ink-2,#B5B5B8);
}
.ds-badge--accent{
  color:var(--bow-green,#22C55E);
  background:rgba(34,197,94,.12);
  background:color-mix(in srgb,var(--bow-green,#22C55E) 12%,transparent);
}
.ds-badge--warn{
  color:var(--bow-amber,#E2B451);
  background:rgba(226,180,81,.12);
  background:color-mix(in srgb,var(--bow-amber,#E2B451) 12%,transparent);
}

/* ── Segmented control ────────────────────────────────────────────────────
   <div class="ds-seg" role="tablist" aria-label="Region">
     <button class="ds-seg__opt" role="tab" aria-selected="true">Domestic</button>
     <button class="ds-seg__opt" role="tab" aria-selected="false">Worldwide</button>
   </div>
   Active = .is-active or [aria-selected=true]. Scrolls sideways (no
   scrollbar) when it has more options than fit. The focus ring sits inside
   the option so the scroller can't clip it. */
.ds-seg{
  display:inline-flex;align-items:center;gap:2px;
  max-width:100%;padding:3px;
  border-radius:var(--bow-r-full,999px);
  background:var(--bow-surface-1,#141416);
  overflow-x:auto;overscroll-behavior-x:contain;
  scrollbar-width:none;-webkit-overflow-scrolling:touch;
}
.ds-seg::-webkit-scrollbar{display:none}
.ds-seg__opt{
  -webkit-appearance:none;appearance:none;
  display:inline-flex;align-items:center;justify-content:center;flex:none;
  height:32px;padding:0 14px;margin:0;border:0;
  border-radius:var(--bow-r-full,999px);
  background:transparent;
  font:inherit;font-size:var(--bow-fs-3,13px);font-weight:var(--bow-fw-semi,600);
  line-height:1;letter-spacing:0;white-space:nowrap;text-decoration:none;
  color:var(--bow-ink-2,#B5B5B8);cursor:pointer;
  transition:color var(--bow-dur-2,250ms) var(--bow-ease,cubic-bezier(.2,0,0,1)),
             background-color var(--bow-dur-2,250ms) var(--bow-ease,cubic-bezier(.2,0,0,1));
}
.ds-seg__opt:hover{color:var(--bow-ink-1,#FFFFFF)}
.ds-seg__opt.is-active,
.ds-seg__opt[aria-selected="true"]{
  background:var(--bow-green,#22C55E);color:var(--bow-green-ink,#052E16);
}
.ds-seg__opt:disabled,
.ds-seg__opt[aria-disabled="true"]{color:var(--bow-ink-4,#5C5C60);cursor:default}

/* ── Buttons and text link ────────────────────────────────────────────────
   .ds-btn--primary: at most one per screen region. Header Sign up is
   --secondary. "See more / full results" is always .ds-link, never an
   outlined pill. Type the → yourself in buttons; .ds-link adds its own. */
.ds-btn{
  -webkit-appearance:none;appearance:none;
  display:inline-flex;align-items:center;justify-content:center;gap:6px;flex:none;
  height:40px;padding:0 18px;margin:0;
  border:1px solid transparent;border-radius:var(--bow-r-full,999px);
  background:transparent;
  font:inherit;font-size:var(--bow-fs-3,13px);font-weight:var(--bow-fw-semi,600);
  line-height:1;letter-spacing:0;text-transform:none;white-space:nowrap;
  text-decoration:none;color:var(--bow-ink-1,#FFFFFF);cursor:pointer;
  transition:background-color var(--bow-dur-1,150ms) var(--bow-ease,cubic-bezier(.2,0,0,1)),
             border-color var(--bow-dur-1,150ms) var(--bow-ease,cubic-bezier(.2,0,0,1)),
             transform var(--bow-dur-1,150ms) var(--bow-ease,cubic-bezier(.2,0,0,1));
}
.ds-btn:active{transform:scale(.98)}
.ds-btn--primary{
  background:var(--bow-green,#22C55E);color:var(--bow-green-ink,#052E16);
}
.ds-btn--primary:hover{
  background:color-mix(in srgb,var(--bow-green,#22C55E) 88%,var(--bow-ink-1,#FFFFFF));
}
.ds-btn--secondary{
  border-color:var(--bow-line-strong,rgba(255,255,255,.16));color:var(--bow-ink-1,#FFFFFF);
}
.ds-btn--secondary:hover{background:var(--bow-surface-1,#141416)}
.ds-btn--sm{height:32px;padding:0 14px}
.ds-btn:disabled,
.ds-btn[aria-disabled="true"]{opacity:.4;cursor:default;pointer-events:none}

.ds-link{
  display:inline-flex;align-items:baseline;
  font-size:var(--bow-fs-3,13px);font-weight:var(--bow-fw-semi,600);
  line-height:var(--bow-lh-title,1.3);
  color:var(--bow-ink-1,#FFFFFF);text-decoration:none;white-space:nowrap;
}
.ds-link:hover{
  text-decoration:underline;text-decoration-thickness:1px;
  text-underline-offset:3px;text-decoration-color:var(--bow-ink-3,#8C8C90);
}

/* ── Poster card ──────────────────────────────────────────────────────────
   <a class="ds-poster" href="/movies/verity">
     <img class="ds-poster__img" src="…" alt="Verity (2026) poster" loading="lazy">
     <span class="ds-poster__fallback" aria-hidden="true">Verity</span>
     <span class="ds-rank">1</span>                                (optional)
     <span class="ds-poster__title">Verity</span>
     <span class="ds-poster__meta"><span>$32.03M</span><span class="ds-muted">New</span><span>Wk 1</span></span>
   </a>
   The fallback tile sits behind the image (it also shows while loading);
   components.js adds .is-broken to the card when the image fails, which hides
   the broken image. Meta order: gross · change · week. */
.ds-poster{
  position:relative;display:block;min-width:0;
  color:inherit;text-decoration:none;
}
.ds-poster__img{
  position:relative;z-index:1;
  display:block;width:100%;height:auto;aspect-ratio:2/3;object-fit:cover;
  border-radius:var(--bow-r-md,8px);
  background:var(--bow-surface-1,#141416);
  color:transparent; /* hides alt text inside a broken image */
}
.ds-poster__fallback{
  position:absolute;top:0;left:0;z-index:0;
  display:flex;align-items:center;justify-content:center;
  width:100%;aspect-ratio:2/3;padding:var(--bow-space-3,12px);
  box-sizing:border-box;overflow:hidden;
  border-radius:var(--bow-r-md,8px);
  background:var(--bow-surface-2,#1C1C1F);
  font-size:var(--bow-fs-3,13px);font-weight:var(--bow-fw-semi,600);
  line-height:var(--bow-lh-title,1.3);text-align:center;text-wrap:balance;
  color:var(--bow-ink-2,#B5B5B8);
}
.ds-poster.is-broken .ds-poster__img{visibility:hidden}
.ds-poster__title{
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;
  overflow:hidden;margin-top:var(--bow-space-2,8px);
  font-size:var(--bow-fs-3,13px);font-weight:var(--bow-fw-semi,600);
  line-height:var(--bow-lh-title,1.3);text-wrap:balance;
  color:var(--bow-ink-1,#FFFFFF);
}
.ds-poster__meta{
  /* Each item carries a leading dot; the group is pulled left by one dot
     width and clipped, so the first item on every line (including a wrapped
     line) shows no dot and no line ends with one. */
  display:flex;flex-wrap:wrap;margin:2px 0 0 -11px;
  clip-path:inset(0 0 0 11px);
  font-size:var(--bow-fs-2,12px);line-height:var(--bow-lh-data,1.35);
  font-variant-numeric:tabular-nums;color:var(--bow-ink-3,#8C8C90);
}
.ds-poster__meta > *{white-space:nowrap}
.ds-poster__meta > *::before{
  content:"\00B7";display:inline-block;width:11px;text-align:center;
  color:var(--bow-ink-3,#8C8C90);
}
a.ds-poster:hover .ds-poster__title{
  text-decoration:underline;text-decoration-thickness:1px;
  text-underline-offset:3px;text-decoration-color:var(--bow-ink-3,#8C8C90);
}

/* Rank chip: one style everywhere. No medals, no gold, no serif "1st".
   Top-left of a poster, or inline anywhere else. */
.ds-rank{
  display:inline-flex;align-items:center;justify-content:center;flex:none;
  min-width:20px;height:20px;padding:0 4px;box-sizing:border-box;
  border-radius:var(--bow-r-sm,4px);
  background:rgba(0,0,0,.65);color:var(--bow-ink-1,#FFFFFF);
  font-size:var(--bow-fs-1,11px);font-weight:var(--bow-fw-heavy,800);
  line-height:1;font-variant-numeric:tabular-nums;
}
.ds-poster > .ds-rank{position:absolute;top:6px;left:6px;z-index:2}

/* ── Rail ─────────────────────────────────────────────────────────────────
   Horizontal scroller of posters or cards, 12px gaps, snapping on phone.
   Item width: --rail-item (default 156px phone, 168px desktop).
   .ds-rail--bleed runs the rail to the screen edge on phone (needs the
   standard 16px page gutter around it). */
.ds-rail{
  display:flex;gap:var(--bow-space-3,12px);
  overflow-x:auto;overscroll-behavior-x:contain;
  /* room for focus rings inside the scroller */
  padding:4px;margin:-4px;scroll-padding-inline:4px;
  scrollbar-width:thin;scrollbar-color:var(--bow-ink-4,#5C5C60) transparent;
  -webkit-overflow-scrolling:touch;
}
.ds-rail > *{flex:0 0 var(--rail-item,168px);min-width:0}
@media (max-width:700px){
  .ds-rail{scroll-snap-type:x mandatory;scrollbar-width:none}
  .ds-rail::-webkit-scrollbar{display:none}
  .ds-rail > *{flex-basis:var(--rail-item,156px);scroll-snap-align:start}
  .ds-rail--bleed{
    margin-inline:calc(-1 * var(--bow-gutter,16px));
    padding-inline:var(--bow-gutter,16px);
    scroll-padding-inline:var(--bow-gutter,16px);
  }
}

/* ── Data table ───────────────────────────────────────────────────────────
   <div class="ds-table-wrap" tabindex="0" role="region" aria-label="Weekend chart">
     <table class="ds-table">
       <thead><tr><th>Film</th><th class="n">Weekend</th>…</tr></thead>
       <tbody><tr><td>Verity</td><td class="n">$32.03M</td>…</tr></tbody>
     </table>
   </div>
   The wrapper always scrolls sideways when the table is wider than it, with
   a sticky first column and a right-edge fade while columns are hidden
   (.has-more, set by components.js). Sticky cells paint --ds-table-bg
   (surface-0); set it to the surface the table sits on. */
.ds-table-wrap{
  --ds-table-bg:var(--bow-surface-0,#0A0A0B);
  position:relative;display:flex;max-width:100%;
  overflow-x:auto;overscroll-behavior-x:contain;
  -webkit-overflow-scrolling:touch;scrollbar-width:none;
}
.ds-table-wrap::-webkit-scrollbar{display:none}
.ds-table-wrap > .ds-table{flex:0 0 100%}
.ds-table-wrap::after{
  content:"";position:sticky;right:0;z-index:3;flex:0 0 40px;margin-left:-40px;
  pointer-events:none;opacity:0;
  background:linear-gradient(to right,transparent,var(--ds-table-bg));
  transition:opacity var(--bow-dur-2,250ms) var(--bow-ease,cubic-bezier(.2,0,0,1));
}
.ds-table-wrap.has-more::after{opacity:1}

.ds-table{
  width:100%;border-collapse:separate;border-spacing:0;
  font-size:var(--bow-fs-3,13px);line-height:var(--bow-lh-data,1.35);
  color:var(--bow-ink-1,#FFFFFF);
}
.ds-table th,
.ds-table td{
  padding:var(--bow-space-1,4px) var(--bow-space-3,12px);
  text-align:left;font-weight:var(--bow-fw-regular,400);
}
.ds-table th:first-child,
.ds-table td:first-child{
  position:sticky;left:0;z-index:2;padding-left:0;
  background:var(--ds-table-bg,#0A0A0B);
  transition:box-shadow var(--bow-dur-2,250ms) var(--bow-ease,cubic-bezier(.2,0,0,1));
}
.ds-table td:first-child{min-width:8.5em;text-wrap:balance}
.ds-table th:last-child,
.ds-table td:last-child{padding-right:0}
.ds-table-wrap.is-scrolled th:first-child,
.ds-table-wrap.is-scrolled td:first-child{box-shadow:inset -1px 0 0 var(--bow-line,rgba(255,255,255,.1))}
.ds-table th{
  vertical-align:bottom;padding-bottom:var(--bow-space-2,8px);
  font-size:var(--bow-fs-2,12px);font-weight:var(--bow-fw-semi,600);
  letter-spacing:0;text-transform:none;white-space:nowrap;
  color:var(--bow-ink-3,#8C8C90);
  border-bottom:1px solid var(--bow-line,rgba(255,255,255,.1));
}
.ds-table td{
  height:40px;box-sizing:border-box;vertical-align:middle;
  border-bottom:1px solid var(--bow-line,rgba(255,255,255,.1));
}
.ds-table tbody tr:last-child td{border-bottom:0}
.ds-table .n{
  text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums;
}
.ds-table a{color:inherit;text-decoration:none}
.ds-table a:hover{text-decoration:underline;text-underline-offset:3px;text-decoration-color:var(--bow-ink-3,#8C8C90)}

/* Inline bar for a table cell: muted data colour, never green or red.
   <span class="ds-bar" style="--v:.62"></span>  (--v = 0…1) */
.ds-bar{
  display:block;width:100%;min-width:48px;height:6px;
  border-radius:var(--bow-r-sm,4px);
  background:linear-gradient(to right,
    var(--bar-color,var(--bow-data-2,#A8A29A)) calc(var(--v,0) * 100%),
    rgba(255,255,255,.06) 0);
}
.ds-bar--1{--bar-color:var(--bow-data-1,#E8E0CF)}
.ds-bar--3{--bar-color:var(--bow-data-3,#6E6A64)}

/* ── Sign-up card, light ──────────────────────────────────────────────────
   One line of copy plus one secondary button, on surface-1. Content pages
   only; the homepage card stays as it is. No background image.
   <aside class="ds-signup"><p class="ds-signup__text">…</p>
     <a class="ds-btn ds-btn--secondary ds-btn--sm" href="/signup">Sign up</a></aside> */
.ds-signup{
  display:flex;align-items:center;justify-content:space-between;
  flex-wrap:wrap;gap:var(--bow-space-3,12px) var(--bow-space-4,16px);
  padding:var(--bow-space-4,16px) 20px;
  border-radius:var(--bow-r-lg,16px);
  background:var(--bow-surface-1,#141416);
}
.ds-signup__text{
  flex:1 1 16em;margin:0;
  font-size:var(--bow-fs-4,15px);line-height:var(--bow-lh-title,1.3);
  color:var(--bow-ink-2,#B5B5B8);text-wrap:balance;
}

/* ── Hero (page-type templates) ───────────────────────────────────────────
   <section class="ds-hero">
     <img class="ds-hero__img" src="…backdrop…" alt="">
     <div class="ds-hero__body">
       <p class="ds-hero__kicker">Universal · July 17, 2026</p>
       <h1 class="ds-hero__title">The Odyssey</h1>
       <p class="ds-hero__sub">…</p>
     </div>
   </section>
   Image covers the top; text sits on a scrim of ≥60% black at the bottom.
   At most 60vh tall on phone. One headline number at most (.ds-stat--lead).
   Only use an image that relates to the content. */
.ds-hero{
  position:relative;isolation:isolate;overflow:hidden;
  display:flex;flex-direction:column;justify-content:flex-end;
  min-height:440px;
  padding:var(--bow-space-7,48px) 40px 40px;
  background:var(--bow-surface-1,#141416);
  color:var(--bow-ink-1,#FFFFFF);text-align:center;
}
.ds-hero__img{
  position:absolute;inset:0;z-index:-2;
  width:100%;height:100%;object-fit:cover;object-position:center 30%;
}
.ds-hero::after{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:linear-gradient(to top,
    rgba(0,0,0,.85) 0%,rgba(0,0,0,.7) 35%,rgba(0,0,0,.6) 50%,
    rgba(0,0,0,.2) 75%,rgba(0,0,0,0) 100%);
}
.ds-hero__body{
  position:relative;max-width:46rem;margin:0 auto;
  display:flex;flex-direction:column;align-items:center;
}
.ds-hero__kicker{
  margin:0 0 var(--bow-space-2,8px);
  font-size:var(--bow-fs-1,11px);font-weight:var(--bow-fw-semi,600);
  line-height:var(--bow-lh-data,1.35);
  text-transform:uppercase;letter-spacing:var(--bow-ls-caps,.08em);
  color:var(--bow-ink-2,#B5B5B8);
}
.ds-hero__title{
  margin:0;
  font-size:var(--bow-fs-8,44px);font-weight:var(--bow-fw-heavy,800);
  line-height:var(--bow-lh-display,1.1);letter-spacing:var(--bow-ls-tight,-.02em);
  color:var(--bow-ink-1,#FFFFFF);text-wrap:balance;
}
.ds-hero__sub{
  margin:var(--bow-space-3,12px) 0 0;max-width:36rem;
  font-size:var(--bow-fs-4,15px);line-height:var(--bow-lh-body,1.5);
  color:var(--bow-ink-2,#B5B5B8);text-wrap:balance;
}
.ds-hero .ds-stat{margin-top:var(--bow-space-5,24px)}
.ds-hero .ds-stat__label{color:var(--bow-ink-2,#B5B5B8)}
@media (max-width:700px){
  .ds-head{margin-top:var(--bow-space-7,48px)}
  .ds-head--flush{margin-top:0}
  .ds-hero{
    min-height:min(52vh,420px);max-height:60vh;
    padding:var(--bow-space-6,32px) var(--bow-gutter,16px) var(--bow-space-5,24px);
  }
  .ds-hero__title{font-size:var(--bow-fs-7,30px)}
  .ds-hero__sub{font-size:var(--bow-fs-3,13px);line-height:var(--bow-lh-data,1.35)}
  .ds-hero .ds-stat{margin-top:var(--bow-space-4,16px)}
}

/* ── Colour utilities ─────────────────────────────────────────────────────
   Rise and fall are text only, never fills. "New" / "—" use .ds-muted. */
.ds-up{color:var(--bow-up,#6FBF8B)}
.ds-down{color:var(--bow-down,#E07A6F)}
.ds-muted{color:var(--bow-ink-3,#8C8C90)}
.ds-num{font-variant-numeric:tabular-nums}

/* ── Focus ring: 2px green outline, 2px offset, on every interactive ds- ── */
.ds-btn:focus-visible,
.ds-link:focus-visible,
.ds-head__link:focus-visible,
.ds-poster:focus-visible,
.ds-table-wrap:focus-visible,
.ds-rail:focus-visible,
.ds-seg:focus-visible,
.ds-signup a:focus-visible,
.ds-table a:focus-visible{
  outline:2px solid var(--bow-green,#22C55E);outline-offset:2px;
  border-radius:var(--bow-r-sm,4px);
}
.ds-btn:focus-visible{border-radius:var(--bow-r-full,999px)}
.ds-poster:focus-visible{border-radius:var(--bow-r-md,8px)}
/* inside the scrolling segmented control the ring sits inset */
.ds-seg__opt:focus-visible{
  outline:2px solid var(--bow-green,#22C55E);outline-offset:-2px;
}
.ds-seg__opt.is-active:focus-visible,
.ds-seg__opt[aria-selected="true"]:focus-visible{outline-color:var(--bow-green-ink,#052E16)}

/* ── Reduced motion: every ds- transition and animation stops ── */
@media (prefers-reduced-motion:reduce){
  [class*="ds-"],[class*="ds-"]::before,[class*="ds-"]::after{
    transition:none !important;animation:none !important;scroll-behavior:auto !important;
  }
  .ds-btn:active{transform:none}
}
