/* ── The terminal's own corrections to the design system ──────────────────────
   Four groups of rules, and every one of them exists because the thing it
   corrects is unreachable from anywhere else.

   This lives in src/ rather than vendor/ deliberately. vendor/ is
   byte-identical to the canvas export and must stay that way, so a component
   that sets a broken value inline can only be corrected from here.

   What used to be in this file was the v3 round's page styles — about five
   hundred lines drawing four screens that no longer exist. Those went back to
   v2 with the design. These stayed, because none of them is a v3 style: they
   correct components v2 uses too, and three of the four are accessibility
   failures rather than matters of taste.

   Anything here that graduates into a real component should move to the canvas
   and out of this file.
   ──────────────────────────────────────────────────────────────────────────── */



/* ── 3 · A notice whose action does not sit on its own title ──────────────────
   `Notice` lays its title and its action in one flex row and gives the title
   `white-space:nowrap` inline. At 390px the confirm-your-email title measures
   204px in a 161px box, so the buttons sat on top of the words.

   The action claims the whole line instead, which forces the wrap the
   container would not make. From 901px there is room beside the title and it
   sits back on the same row.
   ──────────────────────────────────────────────────────────────────────────── */
.noticeact{display:flex;gap:var(--space-5);flex:1 0 100%}
@media (min-width:901px){
  .noticeact{flex:0 1 auto}
}

/* ── 4 · Figures that explain themselves, and the tables behind a chart ───────
   See src/explain.jsx for why this is flow content and not a tooltip: the
   board's table scrolls inside its own wrapper and would clip anything
   positioned, and the phones this is aimed at have no hover.

   `[hidden]` is asserted rather than assumed. The attribute is honoured by the
   UA stylesheet, but the rules below set `display`, and a later `display` on
   the same element beats it — so the panels would open themselves.
   ──────────────────────────────────────────────────────────────────────────── */
[hidden]{display:none !important}

/* Padded to a comfortable tap — 44px in practice, measured — without setting
   a height, which would push the label off its own line. */
.explain{
  appearance:none;background:none;border:0;cursor:pointer;
  display:inline-block;margin:2px 0 0;padding:6px 0;
  /* Was mono + uppercase + --tracking-label at label size: the complete
     recipe vendor/tokens/typography.css retired because "they were the expert
     signal, and they are the hardest thing on the screen for a second-language
     reader." Applied to the one control in the product whose entire purpose is
     helping that reader — so the question shouted WHAT DO THESE WORDS MEAN?
     and .explainbody answered, correctly, in sentence case. The dashed
     underline is what marks it as a disclosure; that stays. */
  font-size:var(--size-body-sm);
  color:var(--text-secondary);text-decoration:underline dashed var(--text-label);
  text-underline-offset:3px;
}
.explain:hover,.explain[aria-expanded="true"]{color:var(--accent);text-decoration-color:var(--accent)}

.explainbody{
  margin:6px 0 2px;padding:10px 12px;
  border-left:2px solid var(--accent);
  background:var(--bg-recessed);border-radius:0 var(--radius-1) var(--radius-1) 0;
  font-size:var(--size-body-sm);line-height:1.55;color:var(--text-secondary);
  max-width:62ch;
}
.explainbody b{display:block;color:var(--text-primary);margin-bottom:4px}

/* InfoTip: a glyph-sized button, its tap area grown to 44px by padding and a
   negative margin so it does not push the label it sits beside. */
.infotip{
  appearance:none;background:none;border:0;cursor:pointer;
  display:inline-flex;align-items:center;vertical-align:-2px;
  padding:14px;margin:-14px -12px -14px -10px;min-height:0;
  color:var(--text-label);
}
.infotip:hover,.infotip:focus-visible{color:var(--accent)}
/* ReasonTip: the badge is the button. No chrome of its own, and the hit area
   grown to 44px by padding the margin pays back. */
.reasontip{appearance:none;background:none;border:0;cursor:pointer;display:inline-flex;align-items:center;padding:12px 8px;margin:-12px -8px;min-height:0;font:inherit;color:inherit}
.reasontip:focus-visible{outline:2px solid var(--accent);outline-offset:-6px;border-radius:var(--radius-1)}
.infotipbody{
  border:1px solid var(--edge);border-radius:var(--radius-2);padding:0;
  background:var(--bg-chassis);color:var(--text-secondary);
  width:min(420px,calc(100vw - 32px));
  text-align:left;letter-spacing:normal;white-space:normal;
  font-family:var(--font-ui);font-weight:var(--weight-regular);
}
.infotipbody::backdrop{background:rgb(0 0 0 / .4)}
.infotipbody > div{padding:16px 18px 10px}
.infotipbody b{display:block;color:var(--text-primary);font-size:var(--size-body);margin-bottom:6px}
.infotipbody p{margin:0 0 4px;font-size:var(--size-body-sm);line-height:1.55}

/* The numbers behind a chart. A picture with no text alternative is a figure
   that exists nowhere else on the screen. */
.ladtab{margin-top:14px;overflow-x:auto;border:1px solid var(--edge);border-radius:var(--radius-2);background:var(--bg-chassis)}
.ladtab table{width:100%;min-width:0;font-size:var(--size-data-sm)}
.ladtab thead th{text-align:left;font:var(--weight-semibold) var(--size-label-sm)/1.3 var(--font-ui);color:var(--text-label);border-bottom:1px solid var(--rule);white-space:nowrap}
.ladtab td,.ladtab th{padding:11px var(--pad-cell-x)}
.ladtab td{border-bottom:1px solid var(--rule-soft);color:var(--text-secondary)}
.ladtab td:last-child,.ladtab th:last-child{text-align:right;font-family:var(--font-mono);font-variant-numeric:tabular-nums;white-space:nowrap}

/* ── 5 · The loading label's own animation ───────────────────────────────────
   `LoadingLabel` renders `className="dh-blink"` and `motion.css` only defines
   `.dh-anim-blink`, so the label never animated. Defined here rather than in
   vendor/, which is byte-identical to the canvas export, and with the same
   reduced-motion guard the system's own animation classes carry.

   Remove once the design system defines it.
   ──────────────────────────────────────────────────────────────────────────── */
.dh-blink{animation:dh-blink 1.4s var(--ease-pulse) infinite}
@media (prefers-reduced-motion: reduce){ .dh-blink{animation:none} }

/* ── 6 · The phone's bottom tab bar, and what More lists ─────────────────────
   Chrome.jsx decides *whether* these render — `isPhone(bp)`, one predicate,
   measured from the shell's own box. There is deliberately no width query
   here: a media query measures the viewport instead, and v3 shipped the same
   decision in both places and had them disagree at the boundary (cdf1bfa).
   These rules only describe the bar once something has chosen to draw it.

   `--tabbar-h` (64px) is a design-system token from the v3 export that was
   read by nothing after the revert. `env(safe-area-inset-bottom)` is the home
   gesture bar on a modern iPhone: without it the labels sit under it.

   **v3's structure, v2's finish — and two deliberate departures from the
   artboard.** The measurements, the grid, the icon-over-label stack and the
   inset accent rule are `explorations/v3-today-390.html` exactly. Two things
   are not, both for the same reason: v2 is the design now, and the tab bar is
   part of v2's chrome rather than a v3 screen embedded in it.

     - The labels are uppercase with `--tracking-label`, because that is what
       the vendored `NavTabs` does to every other destination in this product.
       The artboard sets them in sentence case, which is v3's voice.
     - The top border is `--accent-edge`, matching the header's own
       `borderBottom` in Chrome.jsx. The artboard uses `--rule`, which belongs
       to v3's quieter chassis; using it here would leave the two edges of one
       chrome disagreeing.

   Revert both to follow the artboard literally if v3's voice is wanted back —
   they are two declarations.
   ──────────────────────────────────────────────────────────────────────────── */
.dh-tabbar{
  display:grid;
  position:fixed;bottom:0;left:0;right:0;z-index:20;
  height:calc(var(--tabbar-h) + env(safe-area-inset-bottom,0px));
  padding-bottom:env(safe-area-inset-bottom,0px);
  border-top:1px solid var(--accent-edge);
  background:var(--bg-chassis);
}
.dh-tabbar button{
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;
  border:0;background:none;cursor:pointer;min-width:0;
  color:var(--text-label);
  font:var(--weight-medium) var(--size-label-sm)/1 var(--font-ui);
  letter-spacing:var(--tracking-label);
}
/* Marked three ways — weight, an inset rule and an accent stroke — because
   colour alone is not a distinction. */
.dh-tabbar button[aria-current="page"]{
  color:var(--text-primary);font-weight:var(--weight-semibold);
  box-shadow:inset 0 -3px 0 var(--accent);
}
.dh-tabbar button[aria-current="page"] svg{stroke:var(--accent)}

/* The rows on More. Full-width targets over the 44px floor, with the chevron
   saying they lead somewhere rather than toggling something. */
.dh-morelist{display:flex;flex-direction:column;gap:1px;background:var(--edge);
  border:1px solid var(--accent-edge);border-radius:var(--radius-2);overflow:hidden}
.dh-morelist button{
  display:flex;align-items:center;justify-content:space-between;gap:var(--space-6);
  min-height:52px;padding:0 var(--pad-panel);
  border:0;background:var(--bg-well);cursor:pointer;text-align:left;
  color:var(--text-primary);font:var(--weight-semibold) var(--size-body)/1.3 var(--font-ui);
}
.dh-morelist button:hover{background:var(--bg-row-hover)}
.dh-morelist button[aria-current="page"]{color:var(--accent);box-shadow:inset 3px 0 0 var(--accent)}
.dh-morelist svg{width:20px;height:20px;stroke:var(--text-label);stroke-width:1.75;fill:none;
  stroke-linecap:round;stroke-linejoin:round;flex:none}

/* ────────────────────────────────────────────────────────────────────────────
   A team's name as a way in to that team's own page.
   Used on the board and on the team page itself, where an opponent leads to
   the next team.

   The resting state is deliberately identical to the plain name beside it. A
   board of twenty underlined rows reads as twenty warnings, and the column is
   already the row's first and heaviest cell — the name does not need help
   being found. What it needs is confirmation once the pointer is on it, and a
   focus ring for anyone who never uses one.
   ──────────────────────────────────────────────────────────────────────────── */
/* ────────────────────────────────────────────────────────────────────────────
   The board row is the team link, on a phone.

   It was a `<button>` around the club's name, and base.css floors every button
   at --touch-min — so the name cost 44px and, with Track on the line below,
   two stacked touch targets were 88px of a 139px row. Measured against the
   category, which reaches 56px with one tap target: the row.

   The row now carries `role="button"` from RecordRow's own onClick handling,
   so it needs the affordances a control needs and did not have: a hover state
   that says it is tappable, and a visible focus ring.
   ──────────────────────────────────────────────────────────────────────────── */
.dh-rowlink{cursor:pointer}
.dh-rowlink:hover{background:var(--bg-row-hover)}
.dh-rowlink:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
/* A row that opens a team says so at its right edge, the way a phone list
   does. Positioned rather than placed: RecordRow is a grid, and a pseudo
   element in normal flow would take a cell. */
.dh-rowlink{position:relative;padding-right:calc(var(--pad-cell-x) + 14px) !important}
.dh-rowlink::after{content:"\203A";position:absolute;right:10px;top:50%;transform:translateY(-50%);font-size:20px;line-height:1;color:var(--text-label);pointer-events:none}
.dh-rowlink:hover::after{color:var(--link)}

/* A team name that opens the team. The cue is there at rest — a faint dotted
   underline in the text's own colour — because a phone has no hover to reveal
   it. Not teal at rest: on the board teal means a draw. The pill under the
   pointer or finger is padded out and pulled back by the same amount, so it
   never moves the name. */
.dh-teamlink{background:none;border:0;font:inherit;color:inherit;cursor:pointer;border-radius:var(--radius-1);padding:2px 6px;margin:-2px -6px}
.dh-teamlink .dh-teamname{text-decoration:underline dotted;text-decoration-color:color-mix(in srgb,var(--text-label) 60%,transparent);text-underline-offset:3px;text-decoration-thickness:1px}
.dh-teamlink:hover,.dh-teamlink:active{color:var(--link);background:var(--bg-row-hover)}
.dh-teamlink:hover .dh-teamname,.dh-teamlink:active .dh-teamname{text-decoration-style:solid;text-decoration-color:currentColor}
.dh-teamlink:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* The team page's month pager. Round, like the club fixture pages it echoes. */
.dh-monthstep{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:50%;border:1px solid var(--edge);background:var(--bg-recessed);color:var(--text-primary);cursor:pointer;padding:0}
.dh-monthstep:hover:not(:disabled){border-color:var(--accent-edge);color:var(--accent)}
.dh-monthstep:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.dh-monthstep:disabled{opacity:.35;cursor:default}
.dh-fixture + .dh-fixture{border-top:1px solid var(--edge)}
/* An opponent in the pager: a button reset; the cue is .dh-teamlink's. */
.dh-club{display:inline-flex;min-width:0;max-width:100%}

/* ────────────────────────────────────────────────────────────────────────────
   A wide table that says it is wider than its box.

   `DataTable` puts its rows in an `overflow-x:auto` wrapper and the shell sets
   `html,body{overflow-x:hidden}`, so a column past the right edge was simply
   gone — no scrollbar on a trackpad, no hint, nothing. On the board that
   column was the Track button: the only action on the primary screen sat off
   the side of a table nobody could see scrolled.

   Two shadows that stay put and two fades that travel with the content, so the
   shadow shows only on the side there is more to reach. `background-attachment:
   local` on the fades is what does it — no JS, no measurement, no resize
   listener, and nothing to disagree with the breakpoint the shell already owns.

   The wrapper also takes `tabIndex={0}` at the call site: a scrollable region
   with no focusable child cannot be reached by keyboard at all (WCAG 2.1.1).
   ──────────────────────────────────────────────────────────────────────────── */
.dh-hscroll{
  background-image:
    linear-gradient(to right,  var(--bg-chassis), transparent),
    linear-gradient(to left,   var(--bg-chassis), transparent),
    linear-gradient(to right,  rgba(0,0,0,.16), transparent),
    linear-gradient(to left,   rgba(0,0,0,.16), transparent);
  background-position:left center,right center,left center,right center;
  background-repeat:no-repeat;
  background-size:40px 100%,40px 100%,12px 100%,12px 100%;
  background-attachment:local,local,scroll,scroll;
}
.dh-hscroll:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}

/* Touch targets for Switch, Checkbox, IconButton and Slider, and the text
   field focus ring, now live in the components themselves (vendor.PATCHES.md
   delta 11). They were !important overrides here. */
/* An icon-only `Button` (not IconButton) is 44px tall from the button floor
   and as narrow as its glyph. */
.dh-iconhit{ min-width:var(--touch-min); }

/* ────────────────────────────────────────────────────────────────────────────
   A placeholder is a value the field does not have yet, not a field you cannot
   use.

   `Input` styles its box, its text and its hint and never touches
   `::placeholder`, so the empty state of every field in the product is left to
   the browser's default — a grey around #a9a9a9 that, sitting inside the
   recessed fill the box already has, reads as a control that has been switched
   off. The Tables bar shows three of them side by side ("any", "1", "last"),
   which is the place it is most obviously wrong: the whole row looks disabled
   on a board where nothing is filtered.

   `--text-label` is the token for text that is quiet on purpose, and it is
   what the field's own label is drawn in — so the placeholder now matches the
   label above it rather than undershooting both. The tokens themselves are not
   touched; guards.test.ts has a rule about that, and it is right.

   `opacity:1` because Firefox applies a default opacity to placeholders and
   would otherwise fade the token back towards the grey this replaces. A
   pseudo-element cannot be set inline, so unlike the corrections above this
   one needs no `!important`.
   ──────────────────────────────────────────────────────────────────────────── */
input::placeholder{
  color:var(--text-label);
  opacity:1;
}

/* ────────────────────────────────────────────────────────────────────────────
   The Try → Live → Settled strip on Sandbox, Tracker and Results, at every
   width. It was hidden at 820px and up because the header tabs name the same
   three screens, but the tabs say where you can go, not that these three come
   in order — and the order is what a new reader was missing.

   Three equal columns that stack under 340px rather than clip, and every step
   a real button, so each clears the --touch-min floor from base.css. The
   current step is marked three ways, as the tab bar's is.
   ──────────────────────────────────────────────────────────────────────────── */
.dh-journey ol{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(100px,1fr));
  gap:1px;margin:0;padding:0;list-style:none;
  background:var(--edge);border:1px solid var(--edge);border-radius:var(--radius-2);overflow:hidden;
}
.dh-journey .dh-journey-step{
  display:flex;align-items:center;gap:var(--space-4);width:100%;height:100%;
  min-height:var(--touch-min);padding:0 var(--space-6);
  border:0;background:var(--bg-well);cursor:pointer;text-align:left;
  color:var(--text-secondary);font:var(--weight-medium) var(--size-body-sm)/1.2 var(--font-ui);
}
.dh-journey button.dh-journey-step:hover{background:var(--bg-row-hover)}
.dh-journey .dh-journey-step[aria-current="step"]{
  cursor:default;color:var(--text-primary);font-weight:var(--weight-semibold);
  box-shadow:inset 0 -3px 0 var(--accent);
}
.dh-journey-n{
  display:inline-grid;place-items:center;flex:none;width:22px;height:22px;border-radius:50%;
  border:1px solid var(--edge);color:var(--text-label);font-size:var(--size-label-sm);
}
.dh-journey .dh-journey-step[aria-current="step"] .dh-journey-n{border-color:var(--accent);color:var(--accent)}
.dh-journey-stage{color:var(--text-label)}
.dh-journey-count{font-family:var(--font-mono);color:var(--text-secondary)}
.dh-journey .dh-journey-step[aria-current="step"] .dh-journey-stage{color:var(--text-secondary)}
/* Under 480px the number sits above the words. Side by side, a third of a
   360px strip left the text about 54px, and "Tracker (1)" is one unbreakable
   unit of about 70px, so the grid cell clipped it to "Tracker (1". Stacked,
   the text has the whole column, and a two-digit count still fits at 360. */
@media (max-width:479px){
  .dh-journey .dh-journey-step{
    flex-direction:column;justify-content:center;gap:var(--space-2);
    padding:var(--space-3) var(--space-3);text-align:center;
  }
}

/* ── 6 · A running ladder, rung by rung, inside a tracker card ────────────────
   Not the kit's DataTable: that holds a 640px minimum, and a tracker card is
   about 330px, so Committed and the "you are here" marker scrolled out of
   sight. Four columns, with each rung's status under its label, fit the card
   down to a 320px phone: the status wraps rather than set the first column's
   width ("Next · you are here" on one line pushed If drawn past the card's
   edge below about 360px). The next rung takes the opportunity tint
   and edge, the same pair DataTable gives a row that asks for action.
   ──────────────────────────────────────────────────────────────────────────── */
.rungtab{width:100%;border-collapse:collapse;font-size:var(--size-data-sm);background:var(--bg-chassis);border:1px solid var(--edge);border-radius:var(--radius-2)}
.rungtab th{text-align:right;vertical-align:bottom;font:var(--weight-semibold) var(--size-label-sm)/1.3 var(--font-ui);color:var(--text-label);padding:8px 8px;border-bottom:1px solid var(--rule)}
.rungtab th:first-child,.rungtab td:first-child{text-align:left}
.rungtab td{padding:6px 8px;border-bottom:1px solid var(--rule-soft);color:var(--text-secondary);text-align:right;font-family:var(--font-mono);font-variant-numeric:tabular-nums;white-space:nowrap}
.rungtab td:first-child{font-family:var(--font-ui);color:var(--text-primary);font-weight:var(--weight-bold);box-shadow:inset 2px 0 0 transparent}
.rungtab td small{display:block;white-space:normal;font:var(--weight-regular) var(--size-label-sm)/1.3 var(--font-ui);color:var(--text-label)}
.rungtab tr[data-next] td{background:var(--sig-opportunity-bg)}
.rungtab tr[data-next] td:first-child{box-shadow:inset 2px 0 0 var(--sig-opportunity-fg);color:var(--sig-opportunity-fg)}
.rungtab tr[data-next] td:first-child small{color:var(--sig-opportunity-fg)}
.rungtab tr[data-projected] td{color:var(--text-label)}
.rungtab tbody tr:last-child td{border-bottom:0}
.rungtab tr[data-drew] td:first-child small{color:var(--sig-opportunity-fg)}
/* The same lamps as .steplights, down the ladder's step column: placed lit,
   the next one glowing, the rest dark. */
.rungtab td:first-child::before{content:"";display:inline-block;width:8px;height:8px;margin-right:8px;border-radius:50%;vertical-align:middle;background:var(--sig-opportunity-fg)}
.rungtab tr[data-projected] td:first-child::before{background:transparent;box-shadow:inset 0 0 0 1px var(--text-label)}
.rungtab tr[data-next] td:first-child::before{box-shadow:0 0 0 3px var(--sig-opportunity-bg),0 0 8px var(--sig-opportunity-fg)}
/* The sandbox ladder: a plan not yet placed has no lamps to light. */
.rungtab[data-plain] td:first-child::before{content:none}
.steplights{display:inline-flex;gap:3px;align-items:center;flex-wrap:wrap;max-width:100%}
.steplights i{width:7px;height:7px;border-radius:50%;box-shadow:inset 0 0 0 1px var(--text-label)}
.steplights i[data-lit]{background:var(--sig-opportunity-fg);box-shadow:none}
.steplights i[data-next]{background:var(--sig-opportunity-fg);box-shadow:0 0 6px var(--sig-opportunity-fg);animation:dh-lamp var(--dur-pulse) var(--ease-pulse) infinite}
@keyframes dh-lamp{50%{opacity:.45}}
@media (prefers-reduced-motion:reduce){.steplights i[data-next]{animation:none}}
.dh-chevron{display:inline-flex;transition:transform var(--dur-base) var(--ease-out)}
.dh-chevron[data-open]{transform:rotate(180deg)}

/* The record's rows align to the top. A closed ladder opens its rungs inside
   the What cell, and centred cells left the date, result and net floating
   halfway down a row that had grown to eleven lines. */
.dh-toprows td{vertical-align:top}

/* ── 7 · Motion: showing that something changed ──────────────────────────────
   motion.css ships the tokens and keyframes, but nothing in src/ used them, so
   every screen change, opened panel and "Saved" line arrived in a single
   frame and was easy to miss. The system's rules still hold: entrance once,
   control transitions of 120–180ms, and a figure never animates its own
   value. Rows and panels fade in as a whole; no number counts or slides.

   Every duration is a token, so the system's reduced-motion override (every
   duration set to 0ms) covers it, and each animation also has an explicit
   `animation:none` below, as the system's own classes do. guards.test.ts
   checks both.
   ──────────────────────────────────────────────────────────────────────────── */
@keyframes dh-enter{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
@keyframes dh-drop{from{opacity:0;transform:translateY(-2px)}to{opacity:1;transform:none}}
@keyframes dh-fade{from{opacity:0}to{opacity:1}}

/* A new screen. TerminalApp keys this wrapper on the screen, so it plays on
   navigation and not when a query inside the screen changes. dh-anim-reveal
   is 600ms and 20px, which suits a landing page but is too slow here. */
.dh-enter{animation:dh-enter var(--dur-base) var(--ease-out) both}

/* The first rows settle in one after another, whenever they are inserted:
   when the data arrives, or when a filter brings in new rows. Opacity only,
   so no row moves under a pointer. After eight rows the rest appear at once,
   because a long cascade makes the board wait for its own decoration. */
.dh-enter tbody tr:nth-child(-n+8){animation:dh-fade var(--dur-base) var(--ease-out) both}
.dh-enter tbody tr:nth-child(2){animation-delay:20ms}
.dh-enter tbody tr:nth-child(3){animation-delay:40ms}
.dh-enter tbody tr:nth-child(4){animation-delay:60ms}
.dh-enter tbody tr:nth-child(5){animation-delay:80ms}
.dh-enter tbody tr:nth-child(6){animation-delay:100ms}
.dh-enter tbody tr:nth-child(7){animation-delay:120ms}
.dh-enter tbody tr:nth-child(8){animation-delay:140ms}

/* Revealed panels: the glossary, a Disclosure's body, a tracker's full
   ladder. They drop in on opening; closing stays instant, because what
   someone closed should be gone as soon as they close it. */
.explainbody:not([hidden]),
.dh-disclosure > button[aria-expanded="true"] + div,
.dh-reveal{animation:dh-drop var(--dur-fast) var(--ease-out) both}

/* The loading label waits 150ms before it appears. A fast response then
   never flashes the label for a frame before the content replaces it. */
.dh-loading{animation:dh-fade var(--dur-fast) var(--ease-out) both;animation-delay:150ms}

/* Hover and selection ease in instead of snapping. */
.dh-tabbar button,.dh-morelist button,.dh-rowlink,.dh-journey .dh-journey-step{transition:var(--transition-control)}

@media (prefers-reduced-motion: reduce){
  .dh-enter,
  .dh-enter tbody tr:nth-child(-n+8),
  .explainbody:not([hidden]),
  .dh-disclosure > button[aria-expanded="true"] + div,
  .dh-reveal,
  .dh-loading{animation:none}
}

/* ── 8 · Motion, second pass: what is due, and what just changed ─────────────
   Section 7 gave the product entrances. This adds the system's second job,
   liveness, in the one place that asks for action now (a bet is due), and
   feedback where an action changed the screen without saying where. Same
   rules: tokens for durations, a reduced-motion stop for every animation,
   and no figure animating its own value. The flash moves a card's edge, not
   its numbers.
   ──────────────────────────────────────────────────────────────────────────── */
@keyframes dh-flash{from{box-shadow:0 0 0 2px var(--sig-opportunity-fg)}to{box-shadow:0 0 0 2px transparent}}

/* Card grids and readout strips enter as table rows do: the first eight
   children fade in, 20ms apart. A newly opened plan's card scales in instead,
   at once, since it is the one the reader was sent to look at. */
.dh-enter .dh-stagger > :nth-child(-n+8){animation:dh-fade var(--dur-base) var(--ease-out) both}
.dh-enter .dh-stagger > :nth-child(2){animation-delay:20ms}
.dh-enter .dh-stagger > :nth-child(3){animation-delay:40ms}
.dh-enter .dh-stagger > :nth-child(4){animation-delay:60ms}
.dh-enter .dh-stagger > :nth-child(5){animation-delay:80ms}
.dh-enter .dh-stagger > :nth-child(6){animation-delay:100ms}
.dh-enter .dh-stagger > :nth-child(7){animation-delay:120ms}
.dh-enter .dh-stagger > :nth-child(8){animation-delay:140ms}
.dh-enter .dh-stagger > .dh-anim-scale-in{animation:dh-scale-in var(--dur-slow) var(--ease-emphatic) both;animation-delay:0ms}

/* A bet is due. The ring pings three times and stops; the dot and the words
   stay. Three rather than infinite: a ring that never stops is one people
   learn to ignore, and the count is keyed so a new due bet rings it again. */
.dh-due-dot{position:relative;display:inline-block;width:6px;height:6px;margin-left:6px;
  border-radius:50%;background:var(--sig-opportunity-fg);vertical-align:middle}
.dh-due-dot::after{content:"";position:absolute;inset:0;border-radius:50%;background:inherit;
  animation:dh-ping var(--dur-pulse) var(--ease-out) 3}
.dh-tabicon{position:relative;display:inline-flex}
.dh-tabicon .dh-due-dot{position:absolute;top:-1px;right:-5px;margin:0}

/* The card that took a step or a skip. No fill mode: once it ends, the
   panel's own shadow is back. */
.dh-flash{animation:dh-flash var(--dur-reveal) var(--ease-out)}

/* The theme switch cross-fades (useTheme in Chrome.jsx), at the system's
   slow duration rather than the browser's default. */
::view-transition-old(root),::view-transition-new(root){animation-duration:var(--dur-slow)}

.explain,.dh-teamlink,.dh-seg button,.dh-monthstep,.dh-club{transition:var(--transition-control)}

@media (prefers-reduced-motion: reduce){
  .dh-enter .dh-stagger > :nth-child(-n+8),
  .dh-enter .dh-stagger > .dh-anim-scale-in,
  .dh-due-dot::after,
  .dh-flash,
  ::view-transition-old(root),::view-transition-new(root){animation:none}
  /* Lamp's blink is an inline style with a literal 1.4s, so the token
     override never reaches it. An inline style can only be beaten by
     !important; this is the one place that needs it. */
  .dh-lamp > span{animation:none !important}
}

/* ── Traditional Chinese (zh-HK) ───────────────────────────────────────────
   Figtree and Anybody carry no Han glyphs, so zh-HK text falls to the
   system's Hong Kong face. Named here rather than left to per-glyph fallback,
   which picks Simplified or Japanese shapes on some systems. No webfont: a
   CJK face is megabytes, and every platform the terminal targets ships one.
   Tracking goes to zero because the Latin tracking cramps Han characters, and
   weights above bold render as faux-bold in these faces. */
:root:lang(zh-HK){
  --font-ui:"Figtree","PingFang HK","PingFang TC","Noto Sans HK","Noto Sans TC","Microsoft JhengHei",ui-sans-serif,system-ui,sans-serif;
  --tracking-display:0;
  --tracking-label-wide:0;
  --tracking-data:0;
  --weight-black:700;
}

/* ── 9 · A slider you can also type into ─────────────────────────────────────
   `TypedSlider` (fields.jsx) puts a number box where the slider's value chip
   was. The chip is the last span of the slider's track row; hidden here so
   the figure is shown once, in the box that takes it.
   ──────────────────────────────────────────────────────────────────────────── */
.dh-typed-track > div > div > span{display:none}

/* ── 10 · League tables share one column grid ──────────────────────────────
   Standings.jsx draws one table per league and gives every column but Team a
   fixed width. `fixed` makes those widths binding, so the blocks line up
   down the page; Team, the second column, takes the rest and truncates.
   The minimum is the kit's inline 640px raised to the fixed columns plus
   Team's floor, set per table from Standings.jsx; an inline style is only
   beaten by !important. */
.dh-standings table{table-layout:fixed;min-width:var(--dh-table-min) !important}
.dh-standings td:nth-child(2){overflow:hidden;text-overflow:ellipsis}

/* Layout primitives for the screens' own markup.
   The screens repeated the same four style objects (a column with a gap, a
   wrapping row of buttons, an icon beside a word, a two-up hairline grid)
   dozens of times inline. As classes they read as what they are, and a gap
   changes in one place. Tokens only: no new measurements live here. */
.dh-stack{display:flex;flex-direction:column;gap:var(--space-6);min-width:0}
.dh-stack-lg{display:flex;flex-direction:column;gap:var(--space-8);min-width:0}
.dh-actions{display:flex;gap:var(--gap-inline);flex-wrap:wrap}
.dh-split{display:flex;align-items:center;justify-content:space-between;gap:var(--gap-inline);flex-wrap:wrap}
.dh-iconword{display:inline-flex;align-items:center;gap:4px;vertical-align:middle}
.dh-hairline-2{display:grid;grid-template-columns:1fr 1fr;gap:1px;background:var(--edge)}
.dh-grow{flex:1 1 140px}
/* Secondary text on the screens' own markup: the label grey, and the short
   explanatory note under a control, which was the same three declarations
   in five places. */
.dh-muted{color:var(--text-label)}
/* The licence credit's links: 15px of text, padded to a 44px tap and pulled
   back so the status line keeps its height. */
.dh-footlink{display:inline-block;padding:14px 2px;margin:-14px -2px}
.dh-note{color:var(--text-label);font-size:var(--size-body-sm);max-width:72ch}
