/* ═══ 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{flex:1 1 0;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);
  /* proportional figures: values don't line up in columns, and some hold
     text (a studio, a title) whose hyphens tabular figures would widen */
  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, [aria-selected=true] or [aria-current=page]
   (for options that are links to other pages). 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"],
.ds-seg__opt[aria-current="page"]{
  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"],
.ds-btn--primary:disabled,
.ds-btn--primary[aria-disabled="true"]{
  /* ink-3 on surface-1 (5.5:1), no green, hairline border: reads as off
     without dropping below the text-contrast floor */
  opacity:1;cursor:default;pointer-events:none;
  color:var(--bow-ink-3,#8C8C90);background:var(--bow-surface-1,#141416);
  border-color:var(--bow-line,rgba(255,255,255,.1));
}

.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-color:var(--bow-surface-1,#141416);
  background-size:cover;background-position:center;background-repeat:no-repeat;
  color:transparent; /* hides alt text inside a broken image */
}
/* A div/span poster (<span class="ds-poster__img" style="background-image:url(…)">)
   has no error event, so it stays see-through: if the image never arrives the
   title tile behind it shows. */
.ds-poster__img:not(img){background-color:transparent}
.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{
  /* Lines fill from the END: items are ordered last-first and laid out
     row-reverse, and wrap-reverse puts the overflow line on top. So when the
     meta doesn't fit, the gross moves up alone and change · week stay together
     on the line below; the week is never stranded. Visual order still matches
     the DOM (gross · change · week).
     Each item carries a leading dot; the group is pulled left by one dot width
     and clipped, so the first item on every line shows no dot. */
  display:flex;flex-flow:row-reverse wrap-reverse;justify-content:flex-end;
  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);
  color:var(--bow-ink-3,#8C8C90);
}
.ds-poster__meta > *{white-space:nowrap;font-variant-numeric:tabular-nums}
.ds-poster__meta > :nth-last-child(1){order:1}
.ds-poster__meta > :nth-last-child(2){order:2}
.ds-poster__meta > :nth-last-child(3){order:3}
.ds-poster__meta > :nth-last-child(4){order:4}
.ds-poster__meta > :nth-last-child(5){order:5}
.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}
/* A badge on a poster sits top-right; use .ds-badge--on-image there. */
.ds-poster > .ds-badge{position:absolute;top:6px;right: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:none;
  -webkit-overflow-scrolling:touch;
}
.ds-rail::-webkit-scrollbar{display:none}
.ds-rail > *{flex:0 0 var(--rail-item,168px);min-width:0}
@media (max-width:700px){
  .ds-rail{scroll-snap-type:x mandatory}
  .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 24px;margin-left:-24px;
  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);
  /* tokens.css turns tabular figures on for every table; here only .n cells
     get them, so hyphens in titles keep their normal width */
  font-variant-numeric:normal;
}
.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:not(.ds-table--plain):not(.ds-table--freeze) th:first-child,
.ds-table:not(.ds-table--plain):not(.ds-table--freeze) 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:not(.ds-table--plain):not(.ds-table--freeze) td:first-child{min-width:8.5em;text-wrap:balance}
.ds-table th:first-child,
.ds-table td:first-child{padding-left:0}
.ds-table th:last-child,
.ds-table td:last-child{padding-right:0}
.ds-table-wrap.is-scrolled .ds-table:not(.ds-table--plain):not(.ds-table--freeze) th:first-child,
.ds-table-wrap.is-scrolled .ds-table:not(.ds-table--plain):not(.ds-table--freeze) 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:last-of-type 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,#95AEC8)) 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,#B47C52)}

/* ── 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:var(--bow-scrim,linear-gradient(to top,rgba(10,10,11,.85) 0%,rgba(10,10,11,.6) 50%,rgba(10,10,11,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}

/* ═══ Variants added in the second pass (Oct 10 2026) ═══ */

/* Page title header: for page titles that don't sit in a hero.
   <header class="ds-head ds-head--page ds-head--flush"><h1 class="ds-head__title">2026 domestic box office</h1></header> */
.ds-head--page{margin-bottom:var(--bow-space-5,24px)}
.ds-head--page .ds-head__title{
  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);
  text-wrap:balance;
}

/* Header note: a right-hand piece of text that isn't a link ("Updated Sun 9:41am ET").
   Sits right on desktop; on phone it drops under the title, left-aligned, 4px below. */
.ds-head__note{
  flex:0 1 auto;max-width:45%;margin:0;
  font-size:var(--bow-fs-2,12px);font-weight:var(--bow-fw-regular,400);
  line-height:var(--bow-lh-data,1.35);text-align:right;text-wrap:balance;
  color:var(--bow-ink-3,#8C8C90);
}

/* Back link: leading ← instead of the trailing →. */
.ds-link--back::after{content:none}
.ds-link--back::before{
  content:"\2190";display:inline-block;margin-right:var(--bow-space-1,4px);
  transition:transform var(--bow-dur-1,150ms) var(--bow-ease,cubic-bezier(.2,0,0,1));
}
.ds-link--back:hover::before{transform:translateX(-2px)}

/* Badge over artwork: dark glass so it reads on any poster or backdrop. */
.ds-badge--on-image{background:rgba(0,0,0,.65);color:var(--bow-ink-1,#FFFFFF)}

/* Rank outside a poster, on the page background: a surface-2 chip. */
.ds-rank--plain{background:var(--bow-surface-2,#1C1C1F);color:var(--bow-ink-1,#FFFFFF)}

/* Fourth chart colour for table bars. */
.ds-bar--4{--bar-color:var(--bow-data-4,#66625D)}

/* Compact stats for card footers: 15px values on phone, 17px on desktop,
   11px labels, 8px gutters. 3 across by default (--cols), on phone too
   (--cols-phone defaults to --cols), and fits a ~110px column. */
.ds-stats--sm{
  --ds-stat-px:var(--bow-space-2,8px);
  --ds-stat-py:var(--bow-space-2,8px);
  grid-template-columns:repeat(var(--cols,3),minmax(0,1fr));
  row-gap:var(--bow-space-3,12px);
}
.ds-stats--sm .ds-stat__label{margin-bottom:var(--bow-space-1,4px);font-size:var(--bow-fs-1,11px)}
.ds-stats--sm .ds-stat__value{font-size:var(--bow-fs-5,17px);letter-spacing:0}
.ds-stats--sm .ds-stat__sub{margin-top:2px;font-size:var(--bow-fs-1,11px)}

/* Plain table: no sticky first column and no first-column minimum, for tables
   whose first column is a rank, or where freeze.js (data-freeze on the
   wrapper) owns stickiness. .ds-table--freeze is the same thing. */

/* Row grid: the table look for div-based tables. Set the columns once on the
   container; rows and the header row use them.
   <div class="ds-rowgrid" style="--rowgrid-cols:24px minmax(0,1fr) 72px 64px">
     <div class="ds-rowgrid__head"><span>#</span><span>Film</span><span class="n">Weekend</span><span class="n">Change</span></div>
     <a class="ds-rowgrid__row" href="…"><span class="ds-muted">1</span><span>Verity</span><span class="n">$32.03M</span><span class="n ds-muted">New</span></a>
   </div> */
.ds-rowgrid{
  --rowgrid-gap:var(--bow-space-3,12px);
  font-size:var(--bow-fs-3,13px);line-height:var(--bow-lh-data,1.35);
  color:var(--bow-ink-1,#FFFFFF);
}
.ds-rowgrid__head,
.ds-rowgrid__row{
  display:grid;grid-template-columns:var(--rowgrid-cols,minmax(0,1fr) auto);
  column-gap:var(--rowgrid-gap);
  border-bottom:1px solid var(--bow-line,rgba(255,255,255,.1));
}
.ds-rowgrid__head{
  align-items:end;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);
}
.ds-rowgrid__row{
  align-items:center;min-height:40px;padding:var(--bow-space-1,4px) 0;
  box-sizing:border-box;color:inherit;text-decoration:none;
}
.ds-rowgrid__row:last-child{border-bottom:0}
.ds-rowgrid__head > *,
.ds-rowgrid__row > *{min-width:0;text-wrap:balance}
.ds-rowgrid .n{text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums}
a.ds-rowgrid__row:hover > :not(.n):not(.ds-muted){
  text-decoration:underline;text-underline-offset:3px;
  text-decoration-color:var(--bow-ink-3,#8C8C90);
}

/* Sign-up form: heading, one line of sub copy, name + email + button inline.
   Stacks on phone. The one green button in its region.
   <form class="ds-signup ds-signup--form">
     <div class="ds-signup__copy"><h3 class="ds-signup__title">…</h3><p class="ds-signup__text">…</p></div>
     <div class="ds-signup__fields">
       <input class="ds-input" name="name" placeholder="First name" autocomplete="given-name" aria-label="First name">
       <input class="ds-input" type="email" name="email" placeholder="Email" autocomplete="email" aria-label="Email" required>
       <button class="ds-btn ds-btn--primary">Sign up</button>
     </div>
   </form> */
.ds-signup--form{
  align-items:flex-end;gap:var(--bow-space-4,16px) var(--bow-space-5,24px);
  padding:var(--bow-space-5,24px);
}
.ds-signup__copy{flex:1 1 auto;min-width:0}
.ds-signup__title{
  margin:0 0 var(--bow-space-1,4px);
  font-size:var(--bow-fs-5,17px);font-weight:var(--bow-fw-semi,600);
  line-height:var(--bow-lh-title,1.3);color:var(--bow-ink-1,#FFFFFF);text-wrap:balance;
}
.ds-signup--form .ds-signup__text{font-size:var(--bow-fs-3,13px);line-height:var(--bow-lh-data,1.35)}
.ds-signup__fields{display:flex;flex:1 1 26em;gap:var(--bow-space-2,8px);min-width:0}
.ds-signup__fields .ds-input{flex:1 1 0}
.ds-input{
  -webkit-appearance:none;appearance:none;box-sizing:border-box;
  min-width:0;height:40px;margin:0;padding:0 var(--bow-space-3,12px);
  border:1px solid var(--bow-line-strong,rgba(255,255,255,.16));
  border-radius:var(--bow-r-md,8px);
  background:var(--bow-surface-0,#0A0A0B);color:var(--bow-ink-1,#FFFFFF);
  font:inherit;font-size:1rem /* 16px: iOS zooms into inputs set below 16px */;line-height:1.2;
  transition:border-color var(--bow-dur-1,150ms) var(--bow-ease,cubic-bezier(.2,0,0,1));
}
.ds-input::placeholder{color:var(--bow-ink-3,#8C8C90);opacity:1}
.ds-input:hover{border-color:var(--bow-ink-4,#5C5C60)}
.ds-input:focus{outline:none}
.ds-input:focus-visible{outline:2px solid var(--bow-green,#22C55E);outline-offset:2px}
.ds-input:disabled{opacity:.4}

@media (max-width:700px){
  /* header: page title, and the note dropping under the title */
  .ds-head{flex-wrap:wrap;row-gap:var(--bow-space-1,4px)}
  .ds-head__note{order:3;flex:0 0 100%;max-width:none;text-align:left}
  .ds-head--page .ds-head__title{font-size:var(--bow-fs-7,30px)}
  /* 3-up (or more) on phone: --cols-phone:3. Gutters narrow from 16px to
     12px and values step down from 22px to 17px as columns are added. */
  .ds-stats{
    --ds-stat-px:clamp(var(--bow-space-3,12px),calc(var(--bow-space-6,32px) / var(--cols-phone,2)),var(--bow-space-4,16px));
  }
  .ds-stats > .ds-stat .ds-stat__value{
    font-size:clamp(var(--bow-fs-5,17px),calc(var(--bow-fs-6,22px) * 2 / var(--cols-phone,2)),var(--bow-fs-6,22px));
  }
  .ds-stats > .ds-stat--lead .ds-stat__value{font-size:var(--bow-fs-7,30px)}
  /* compact stats keep their own sizes on phone */
  .ds-stats--sm{
    --ds-stat-px:var(--bow-space-2,8px);
    grid-template-columns:repeat(var(--cols-phone,var(--cols,3)),minmax(0,1fr));
  }
  .ds-stats.ds-stats--sm > .ds-stat .ds-stat__value{font-size:var(--bow-fs-4,15px)}
  .ds-stats--inline{--ds-stat-px:var(--bow-space-3,12px)}
  .ds-stats.ds-stats--inline > .ds-stat .ds-stat__value{font-size:var(--bow-fs-3,13px)}
  /* sign-up form stacks */
  .ds-signup--form{padding:20px var(--bow-space-4,16px)}
  .ds-signup__fields{flex-direction:column;flex-basis:100%}
  .ds-signup__fields .ds-input{flex:none;width:100%}
  .ds-signup__fields .ds-btn{width:100%}
}

/* ── 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,
.ds-rowgrid__row:focus-visible,
.ds-rowgrid 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,
.ds-seg__opt[aria-current="page"]: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}
}
