/* ==========================================================================
   COMPONENTS  ·  "Paper and Foil". Every one answers :hover (fine pointers only), :active, :focus-visible, :disabled / [aria-disabled="true"], and the classes
   .is-hover .is-pressed .is-focus .is-disabled .is-loading .is-error (the classes mirror the pseudo-classes so a state can be forced in the style guide and in a test).
   The markup each one expects is made by app/js/ui/<component>.js; docs/APP.md lists every component with its props, events and states.
   The source of every number is tools/v2/comps/f1/SPEC.md (and its comp.css: where the CSS and the text disagree, the CSS is right). Paper (the letter, the slips, the results) is css/paper.css.
   Rules that hold for the whole file: every box-shadow is a token (e1 to e3, press, e-paper, e-photo or a ring); no backdrop-filter, no filter, no drop-shadow; no weight over 700; Playfair 400 only;
   every animation and transition lives in css/motion.css, inside prefers-reduced-motion: no-preference (a state is carried by a shape and a word, never by colour or movement alone).

   CONTENTS
     1  Buttons          .hl-btn (primary, secondary, ghost, danger, neutral, icon, sm, lg)      2  The Play ticket       .hl-ticket-wrap > .hl-ticket
     3  Chips, avatars, awards, divider, the title (logo)      4  Cards            .hl-card, .hl-plate, .hl-story, .hl-pip, .hl-facts, .hl-meter, .hl-cert, .hl-notice
     5  The day street and the facade          6  Rows, rankings, the dock         7  Chat          8  Navigation   segmented control, tabs, tab bar, app bar
     9  Overlays         scrim, sheet, dialog, toast, lightbox, banner       10  Inputs   field, PIN, faces, switch       11  Feedback   skeleton, empty state, spinner, pull to refresh, ticker, dots, time bar
     12 The Welcome strip, small additions, short windows, forced colours
   ========================================================================== */

/* ==========================================================================
   1  BUTTONS (SPEC 7.4)
   One gold primary per screen (on Today it is the ticket's Play pill; on Join, Story and Quiz it is pinned in the action slot). A secondary is ruled in a cream 50% line; a ghost is gold text.
   ========================================================================== */
.hl-btn {
  --_fg: var(--hl-text); --_bg: transparent; --_ring: none;
  position: relative; display: inline-flex; align-items: center; justify-content: center; gap: var(--hl-sp-8);
  min-height: 48px; min-width: var(--hl-tap); padding: var(--hl-sp-8) var(--hl-sp-24);
  border: 0; border-radius: var(--hl-r-3); background-color: var(--_bg); color: var(--_fg); box-shadow: var(--_ring);
  font: 600 var(--hl-t-headline-size) / var(--hl-t-headline-lh) var(--hl-font-ui); letter-spacing: .005em; text-align: center; text-decoration: none; text-wrap: balance; max-width: 100%; overflow-wrap: break-word;       /* a label wraps rather than push the page sideways (a long word, a big text size); it never clips */
  cursor: pointer; user-select: none; touch-action: manipulation;
}
.hl-btn--primary { --_bg: var(--hl-accent-gold); --_fg: var(--hl-bg); }
.hl-btn--secondary { --_ring: var(--hl-ring-ctl-in); }
.hl-btn--ghost { --_fg: var(--hl-accent-gold); min-height: var(--hl-tap); padding: 0 var(--hl-sp-12); }
.hl-btn--danger { --_fg: var(--hl-danger); --_ring: inset 0 0 0 1px var(--hl-danger); }
.hl-btn--neutral { --_bg: var(--hl-surface-raised); --_ring: var(--hl-ring-hair-in); }
.hl-btn--icon { padding: 0; width: var(--hl-tap); min-height: var(--hl-tap); }
.hl-btn--block { display: flex; width: 100%; }
.hl-btn--lg { min-height: 52px; }
/* small: 36 px to look at, 44 px to touch (the invisible hit area is the ::before) */
.hl-btn--sm { min-height: 36px; min-width: 36px; padding: 0 var(--hl-sp-16); font-size: var(--hl-t-body-s-size); }
.hl-btn--sm::before { content: ""; position: absolute; inset: calc((100% - var(--hl-tap)) / 2) -2px; }   /* the percentage is of the padding box, so this is exactly 44 px tall */
.hl-btn--sm.hl-btn--icon { width: 36px; padding: 0; }
.hl-btn--sm.hl-btn--icon::before { inset: calc((100% - var(--hl-tap)) / 2); }
/* a ghost sits 8 px under the primary above it (the primary's die ring needs 5; at 4 the ghost's ring lost half its top) */
.hl-btn--block + .hl-btn--ghost, .hl-btn--ghost + .hl-btn--ghost { margin-top: var(--hl-sp-8); }
@media (hover: hover) { .hl-btn:is(:hover, .is-hover):not(:disabled, [aria-disabled="true"], .is-disabled, .is-loading) { background-image: linear-gradient(rgba(255, 243, 218, .04), rgba(255, 243, 218, .04)); } }
.hl-btn:is(:focus-visible, .is-focus) { outline: var(--hl-focus); outline-offset: 3px; }
.hl-btn:is(:disabled, [aria-disabled="true"], .is-disabled) { --_bg: var(--hl-wash); --_fg: var(--hl-text-faint); --_ring: none; cursor: not-allowed; }
.hl-btn.is-loading { pointer-events: none; }
.hl-btn.is-loading > .hl-icon { display: none; }
.hl-btn.is-loading::after { content: ""; flex: none; order: -1; width: 14px; height: 14px; border-radius: 50%; border: 2px solid currentColor; border-right-color: transparent; }          /* a 14 px ring turns in the place of the icon; the label stays */
.hl-btn.is-error { --_ring: var(--hl-ring-rose-in); --_fg: var(--hl-danger); }
.hl-btn--primary.is-error { --_fg: var(--hl-bg); }          /* rose words on the gold fill are 1.4:1: the primary keeps its dark label, the ring and the shake say it did not work */

/* ==========================================================================
   2  THE PLAY TICKET (SPEC 6 and 7.8): flat cranberry card stock, punched with a mask (never a painted disc), a dotted perforation and a gold pill for the action.
   The shadow is a SIBLING (the wrapper's ::before) because a mask would cut it, and so is the focus ring.
   <div class="hl-ticket-wrap"><button class="hl-ticket"><span class="hl-ticket__main">title, meta, prize</span><span class="hl-ticket__stub"><span class="hl-play">Play</span></span></button></div>
   States: ready; locked, ended and offline (flat --s1, a --wash pill; still focusable with aria-disabled); loading (a ring in the pill); error (a rose line under the ticket). Done is the result slip (ui/paper-core.js).
   ========================================================================== */
.hl-ticket-wrap { position: relative; }
.hl-ticket-box { position: relative; }          /* the ticket's own box: the shadow and the ring are drawn round THIS, so the error line under the ticket is outside both */
.hl-ticket-box::before { content: ""; position: absolute; inset: 0; border-radius: var(--hl-r-3); box-shadow: var(--hl-e2); pointer-events: none; }
.hl-ticket-wrap--quiet .hl-ticket-box::before { box-shadow: var(--hl-e1); }
.hl-ticket {
  --stub: clamp(104px, 6.5rem, 140px);          /* the stub grows with the person's text size, to 130 percent and a little more (a pill that says "Locked" must not be cut); the mask's notch and the perforation follow it */
  position: relative; display: flex; width: 100%; min-height: 92px; border: 0; padding: 0; text-align: left; border-radius: var(--hl-r-3); background: var(--hl-cran-card); color: var(--hl-on-cran); font-family: var(--hl-font-ui); cursor: pointer; touch-action: manipulation;
  -webkit-mask: radial-gradient(circle 8px at calc(100% - var(--stub)) 0, #0000 97%, #000) top / 100% 51% no-repeat, radial-gradient(circle 8px at calc(100% - var(--stub)) 100%, #0000 97%, #000) bottom / 100% 51% no-repeat;
          mask: radial-gradient(circle 8px at calc(100% - var(--stub)) 0, #0000 97%, #000) top / 100% 51% no-repeat, radial-gradient(circle 8px at calc(100% - var(--stub)) 100%, #0000 97%, #000) bottom / 100% 51% no-repeat;
}
.hl-ticket__main { flex: 1; min-width: 0; display: flex; flex-direction: column; justify-content: center; gap: 2px; padding: var(--hl-sp-12) var(--hl-sp-16); }
.hl-ticket__title { display: block; overflow-wrap: anywhere; font: 400 var(--hl-t-heading-size) / var(--hl-t-heading-lh) var(--hl-font-display); font-variant-numeric: lining-nums; text-wrap: balance; }
.hl-ticket__meta { display: block; font: 500 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); letter-spacing: .005em; color: var(--hl-on-cran-2); }
.hl-ticket__prize { display: flex; align-items: center; gap: var(--hl-sp-8); font: 600 var(--hl-t-body-s-size) / var(--hl-t-snug-lh) var(--hl-font-ui); letter-spacing: .005em; color: var(--hl-accent-gold); }          /* 14 px: the prize is a message */
.hl-ticket__text { min-width: 0; }
.hl-ticket__stub { position: relative; flex: none; width: var(--stub); display: flex; align-items: center; justify-content: center; }
.hl-ticket__stub::before { content: ""; position: absolute; left: 0; top: 12px; bottom: 12px; width: 2px; background: radial-gradient(circle at 1px 1px, var(--hl-dots) 1px, transparent 1.6px) 0 0 / 2px 8px repeat-y; }          /* the perforation: a column of 2 px dots */
/* the way to play is a button that says Play: flat gold, a pill, a darker ring round it (the die's impression), the word inside */
.hl-play { position: relative; display: flex; align-items: center; justify-content: center; gap: var(--hl-sp-8); width: calc(var(--stub) - 16px); white-space: nowrap; height: 52px; border-radius: var(--hl-r-pill); background: var(--hl-accent-gold); color: var(--hl-on-gold); font: 600 min(var(--hl-t-headline-size), 24px) / var(--hl-t-headline-lh) var(--hl-font-ui); letter-spacing: .005em; box-shadow: var(--hl-ring-die); }          /* (compact chrome: the pill's word stops growing at 24 px, as the doors and the tab bar do at 130 percent) */
.hl-play .hl-icon--fill { width: 14px; height: 14px; }
.hl-play .hl-star { flex: none; width: 16px; height: 16px; }
.hl-ticket:focus-visible { outline: none; }          /* a mask cuts its own outline: the wrapper draws it, round the whole ticket, 3 px off */
.hl-ticket-box:has(.hl-ticket:focus-visible)::after, .hl-ticket-wrap.is-focus .hl-ticket-box::after { content: ""; position: absolute; inset: 0; border-radius: var(--hl-r-3); outline: var(--hl-focus); outline-offset: 3px; pointer-events: none; }
@supports not selector(:has(*)) { .hl-ticket-box:focus-within::after { content: ""; position: absolute; inset: 0; border-radius: var(--hl-r-3); outline: var(--hl-focus); outline-offset: 3px; pointer-events: none; } }
.hl-ticket:is(:disabled, [aria-disabled="true"], .is-disabled, .is-ended, .is-offline) { cursor: not-allowed; }
.hl-ticket--locked, .hl-ticket--ended, .hl-ticket--offline { background: var(--hl-surface); color: var(--hl-text-muted); }
.hl-ticket--locked .hl-ticket__title, .hl-ticket--ended .hl-ticket__title, .hl-ticket--offline .hl-ticket__title { color: var(--hl-text); }
.hl-ticket--locked .hl-ticket__meta, .hl-ticket--ended .hl-ticket__meta, .hl-ticket--offline .hl-ticket__meta { color: var(--hl-text-muted); }
.hl-ticket--locked .hl-play, .hl-ticket--ended .hl-play, .hl-ticket--offline .hl-play { background: var(--hl-wash); color: var(--hl-text-muted); box-shadow: var(--hl-ring-ctl-in); }
.hl-ticket--locked .hl-ticket__stub::before, .hl-ticket--ended .hl-ticket__stub::before, .hl-ticket--offline .hl-ticket__stub::before { background: radial-gradient(circle at 1px 1px, var(--hl-dots-off) 1px, transparent 1.6px) 0 0 / 2px 8px repeat-y; }
.hl-ticket.is-loading { pointer-events: none; }
.hl-ticket.is-loading .hl-play > .hl-icon { display: none; }
.hl-ticket.is-loading .hl-play::after { content: ""; width: 14px; height: 14px; border-radius: 50%; border: 2px solid currentColor; border-right-color: transparent; }
.hl-ticket-error { display: flex; align-items: flex-start; gap: var(--hl-sp-8); margin-top: var(--hl-sp-8); color: var(--hl-danger); font: 500 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); }          /* error: a one-line rose message under the ticket */
.hl-ticket-error .hl-icon { margin-top: 1px; }
.hl-ticket-wrap.is-error .hl-ticket { box-shadow: none; }

/* ==========================================================================
   3  CHIPS, AVATARS, AWARDS, DIVIDER, THE TITLE
   ========================================================================== */
.hl-chip {
  position: relative; display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-height: 32px; padding: 0 var(--hl-sp-12);
  border-radius: var(--hl-r-pill); background: var(--hl-wash); box-shadow: var(--hl-ring-hair-in); color: var(--hl-text);
  max-width: 100%; font: 600 min(var(--hl-t-body-s-size), 18px) / var(--hl-t-headline-lh) var(--hl-font-ui); letter-spacing: .005em; text-align: center; text-decoration: none;
}
.hl-chip__label { min-width: 0; overflow-wrap: anywhere; }   /* a chip is a few words; if it is ever given too little room (a big text size) the words wrap, they are not cut */
.hl-chip .hl-icon { width: 18px; height: 18px; stroke-width: 2; color: var(--hl-accent-gold); }
.hl-chip small { min-width: 0; font: 500 min(var(--hl-t-caption-size), 16px) / var(--hl-t-caption-lh) var(--hl-font-ui); color: var(--hl-text-muted); }
.hl-chip--success { color: var(--hl-success); }
.hl-chip--warning { color: var(--hl-accent-amber); }
.hl-chip--danger { color: var(--hl-danger); }
.hl-chip--info { color: var(--hl-info); }
.hl-chip--success .hl-icon, .hl-chip--warning .hl-icon, .hl-chip--danger .hl-icon, .hl-chip--info .hl-icon { color: currentColor; }
:is(button, a).hl-chip { cursor: pointer; }
:is(button, a).hl-chip::after { content: ""; position: absolute; inset: calc((100% - var(--hl-tap)) / 2) -2px; }  /* the tap area grows to 44 px */
.hl-chip:is(:focus-visible, .is-focus) { outline: var(--hl-focus); outline-offset: 3px; }
.hl-chip:is(:disabled, [aria-disabled="true"], .is-disabled) { color: var(--hl-text-faint); box-shadow: none; cursor: not-allowed; }
.hl-chip:is(:disabled, [aria-disabled="true"], .is-disabled) .hl-icon { color: var(--hl-text-faint); }
.hl-chip.is-loading { color: transparent; overflow: hidden; pointer-events: none; }        /* (a placeholder is not pressed: its click is swallowed anyway, and the band of light is clipped by the chip) */
.hl-chip.is-loading::before { content: ""; position: absolute; inset: 0; background: var(--hl-shimmer); pointer-events: none; }
.hl-chip.is-loading > * { visibility: hidden; }
.hl-chip.is-error { color: var(--hl-danger); box-shadow: var(--hl-ring-rose-in); }

/* one ring, five sizes (SPEC 7.3): 28 in the chat, 40 in the bar, the rows and the picker, 44, 56, 96 on a profile */
.hl-avatar { --_s: 40px; position: relative; display: inline-block; flex: none; width: var(--_s); height: var(--_s); border-radius: 50%; overflow: hidden; background: var(--hl-surface-raised); color: var(--hl-text); box-shadow: var(--hl-ring-face); vertical-align: middle; }
.hl-avatar > :is(img, svg) { display: block; width: 100%; height: 100%; object-fit: contain; }
.hl-avatar--28 { --_s: 28px; } .hl-avatar--40 { --_s: 40px; } .hl-avatar--44 { --_s: 44px; } .hl-avatar--56 { --_s: 56px; } .hl-avatar--96 { --_s: 96px; }
.hl-avatar--you { box-shadow: var(--hl-ring-gold); }
.hl-avatar.is-loading { background: var(--hl-wash); }
.hl-avatar.is-loading::after { content: ""; position: absolute; inset: 0; background: var(--hl-shimmer); pointer-events: none; }
.hl-avlink { display: grid; place-items: center; flex: none; width: var(--hl-tap); height: var(--hl-tap); margin-right: -2px; border-radius: 50%; }

/* award medals (the painted medals of the You page; the day's certificate shows the leader's own face instead): <span class="hl-award hl-award--lg"><svg class="hl-award__art"><use href="#hl-award-champion"/></svg></span> */
.hl-award { --_s: 56px; position: relative; display: inline-flex; flex-direction: column; align-items: center; gap: 4px; width: var(--_s); }
.hl-award__art { display: block; width: var(--_s); height: var(--_s); }
.hl-award--sm { --_s: 40px; } .hl-award--lg { --_s: 96px; }
.hl-award.is-locked .hl-award__art { opacity: .4; }
.hl-award__lock { position: absolute; right: 0; top: calc(var(--_s) - 24px); width: 22px; height: 22px; display: grid; place-items: center; border-radius: 50%; background: var(--hl-surface-inset); box-shadow: var(--hl-ring-hair); color: var(--hl-text-muted); }          /* a locked medal is dimmed AND carries a lock: a state is a shape and a word */
.hl-award__lock .hl-icon { width: 14px; height: 14px; }

/* a section head: a label and a hairline that fades */
.hl-head { display: flex; align-items: center; gap: var(--hl-sp-12); }
.hl-head::after { content: ""; flex: 1; height: 1px; background: linear-gradient(90deg, var(--hl-hair), rgba(255, 243, 218, 0)); }
.hl-head .hl-label { color: var(--hl-text-muted); }
.hl-label { font: 700 var(--hl-t-label-size) / var(--hl-t-label-lh) var(--hl-font-ui); letter-spacing: .16em; text-transform: uppercase; overflow-wrap: anywhere; }
.hl-divider { display: flex; align-items: center; gap: var(--hl-sp-12); margin: var(--hl-sp-16) 0; border: 0; color: var(--hl-hair); }
.hl-divider::before, .hl-divider::after { content: ""; flex: 1; height: 1px; background: currentColor; }
.hl-divider--star > .hl-star { flex: none; width: 18px; height: 18px; }          /* two hairlines and the dark 8-point Star: no gold rule (foil is a stroke on paper, a keyline or a rail) */
hr.hl-divider { display: block; height: 1px; margin: var(--hl-sp-16) 0; background: var(--hl-hair); }
hr.hl-divider::before, hr.hl-divider::after { display: none; }
.hl-foil-line { width: 96px; height: 1px; margin: var(--hl-sp-12) auto 0; background: var(--hl-foil-short); }          /* under the Welcome tagline */

/* the title: an <img> of the drawn wordmark (SPEC 7.1), never type. One src per slot; the boxes come from the files' own viewBox. */
.hl-logo { display: block; flex: none; line-height: 0; }
.hl-logo > img { display: block; width: auto; height: 100%; }
.hl-logo--sm { height: 24px; }             /* the app bar: the small cut, 24 px tall (141 wide); 20 px tall below 360 px */
.hl-logo--md { height: 36px; }             /* the splash and the install hint (the display cut) */
.hl-logo--lg { width: 300px; max-width: 100%; height: auto; margin-inline: auto; }          /* the Welcome hero: 300 px wide (51 tall), centred */
.hl-logo--lg > img { width: 100%; height: auto; }
.hl-logo--xl { width: 340px; max-width: 100%; height: auto; margin-inline: auto; }          /* the full title on the Welcome: wordmark and case-file tag */
.hl-logo--xl > img { width: 100%; height: auto; }
.hl-logo.is-broken > img { display: none; }
.hl-logo.is-broken::after { content: attr(data-name); display: block; color: var(--hl-accent-gold); font: 400 1.75rem / 1.1 var(--hl-font-display); letter-spacing: .08em; text-transform: uppercase; }          /* the picture could not load: the name is set in type instead */
h1.hl-logo { margin: 0; }
.hl-brand { display: flex; align-items: center; flex: none; min-width: var(--hl-tap); min-height: var(--hl-tap); }

/* ==========================================================================
   4  CARDS (SPEC 6, 7.6 to 7.11, 7.16)
   A card is a surface from the ladder with a hairline, a top light and two soft layers (--hl-e1): never a gradient, never a coloured glow.
   ========================================================================== */
.hl-card { position: relative; padding: var(--hl-sp-16); border-radius: var(--hl-r-3); background: var(--hl-surface); box-shadow: var(--hl-e1); color: var(--hl-text); }
.hl-card--raised { background: var(--hl-surface-raised); }
.hl-card--action { display: block; width: 100%; text-align: left; cursor: pointer; font: inherit; touch-action: manipulation; }
.hl-card--action:is(:focus-visible, .is-focus) { outline: var(--hl-focus); outline-offset: 3px; }
.hl-card:is(:disabled, [aria-disabled="true"], .is-disabled) { color: var(--hl-text-faint); cursor: not-allowed; }
.hl-card.is-error { box-shadow: var(--hl-e1), var(--hl-ring-rose-in); }
.hl-card.is-loading { pointer-events: none; }
.hl-card__title { margin: 0; font: 400 var(--hl-t-heading-size) / var(--hl-t-heading-lh) var(--hl-font-display); font-variant-numeric: lining-nums; text-wrap: balance; }
.hl-card__text { margin: var(--hl-sp-4) 0 0; font: 500 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); letter-spacing: .005em; color: var(--hl-text-muted); text-wrap: pretty; }
.hl-listcard { overflow: hidden; border-radius: var(--hl-r-3); background: var(--hl-surface); box-shadow: var(--hl-e1); }
.hl-listcard__head { display: flex; align-items: center; justify-content: space-between; gap: var(--hl-sp-12); min-height: 52px; padding: 0 var(--hl-sp-16); box-shadow: var(--hl-rule-b); }
.hl-listcard__title { margin: 0; font: 400 var(--hl-t-heading-size) / var(--hl-t-heading-lh) var(--hl-font-display); font-variant-numeric: lining-nums; }
.hl-listcard .hl-row { border-radius: 0; background: none; box-shadow: var(--hl-rule-b); }
.hl-listcard .hl-row:last-child { box-shadow: none; }

/* the picture plate: the painting alone at 12:7 (its own 3:2 with the foot, which is only ground, taken off). A poster has its own focal point, --fy (art/MANIFEST.json focalY): nothing sits on a picture. */
.hl-plate { position: relative; overflow: hidden; border-radius: var(--hl-r-3); background: linear-gradient(180deg, var(--hl-s1-hi), var(--hl-surface)); box-shadow: var(--hl-e1); aspect-ratio: 12 / 7; }
.hl-plate > img { width: 100%; height: 100%; object-fit: cover; object-position: 50% var(--fy, 0%); }
.hl-plate--full { aspect-ratio: 3 / 2; }                                       /* a picture shown whole (the pictures inside a letter are 3:2 and never cropped) */
.hl-plate--blank > img { position: absolute; left: 50%; bottom: 0; width: auto; height: 90%; transform: translateX(-50%); object-fit: contain; }          /* Pip with his blank chalkboard: the only real art for "not written yet" */
.hl-plate .hl-star { position: absolute; width: 28px; height: 28px; left: 18%; top: var(--hl-sp-24); opacity: .9; }
.hl-plate--finale .hl-star { left: 50%; top: 50%; width: 112px; height: 112px; transform: translate(-50%, -50%); opacity: 1; }          /* the Star itself: an outline while it waits, flat gold facets once it is lit (no glow) */
.hl-plate.is-broken { background: linear-gradient(180deg, var(--hl-s1-hi), var(--hl-surface)); }

/* the story: the plate and the chapter are ONE link into the letter (SPEC 7.6). The chapter block reserves the height of a two-line teaser, so a day with a one-line teaser does not move the ticket. */
.hl-story { display: block; border-radius: var(--hl-r-3); color: var(--hl-text); }
.hl-story--soon { cursor: default; }
.hl-story:is(a, button):is(:focus-visible, .is-focus) { outline: var(--hl-focus); outline-offset: 3px; }
.hl-chapter { display: block; min-height: calc(var(--hl-t-display-size) * var(--hl-t-display-lh) + var(--hl-sp-4) + 2 * var(--hl-t-body-s-size) * var(--hl-t-body-s-lh)); margin-top: var(--hl-sp-8); }
.hl-chapter__title { display: block; min-width: 0; font: 400 var(--hl-t-display-size) / var(--hl-t-display-lh) var(--hl-font-display); font-variant-numeric: lining-nums; letter-spacing: -.01em; text-wrap: balance; }
.hl-chapter__tease { display: flex; align-items: center; gap: var(--hl-sp-8); margin-top: var(--hl-sp-4); }
.hl-chapter__teaser { display: block; flex: 1; min-width: 0; color: var(--hl-text-muted); font: 500 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); letter-spacing: .005em; text-wrap: pretty; }
.hl-chapter__state { color: var(--hl-accent-gold); font-weight: 600; }
.hl-chapter__go { flex: none; display: grid; place-items: center; width: 32px; height: 32px; color: var(--hl-accent-gold); }          /* the visible cue that this opens the letter */
.hl-story.is-loading .hl-plate > * { visibility: hidden; }
.hl-story.is-loading .hl-plate::after { content: ""; position: absolute; inset: 0; background: var(--hl-shimmer); pointer-events: none; }
.hl-story.is-loading .hl-chapter > * { visibility: hidden; }
/* while it loads the words are held back as the shapes of their lines (a title, two lines of teaser), flat: the band of light crosses the plate. They hang from the top of the chapter, so they hold whether or not a title has come. */
.hl-story.is-loading .hl-chapter { position: relative; --_title: calc(var(--hl-t-display-size) * var(--hl-t-display-lh)); --_line: calc(var(--hl-t-body-s-size) * var(--hl-t-body-s-lh)); }
.hl-story.is-loading .hl-chapter::before, .hl-story.is-loading .hl-chapter::after, .hl-story.is-loading .hl-chapter__title::after { content: ""; position: absolute; left: 0; visibility: visible; height: 14px; border-radius: 7px; background: var(--hl-wash); }
.hl-story.is-loading .hl-chapter::before { top: calc((var(--_title) - 22px) / 2); width: 62%; height: 22px; }
.hl-story.is-loading .hl-chapter::after { top: calc(var(--_title) + var(--hl-sp-4) + (var(--_line) - 14px) / 2); width: 100%; }
.hl-story.is-loading .hl-chapter__title::after { top: calc(var(--_title) + var(--hl-sp-4) + var(--_line) + (var(--_line) - 14px) / 2); width: 64%; }

/* Pip's line: his face and what he says. Two lines at most on Today (ui/pip-row.js picks the candidate that sets in two). */
.hl-pip { display: flex; align-items: flex-start; gap: var(--hl-sp-12); }
.hl-pip__face { flex: none; width: 40px; height: 40px; border-radius: 50%; overflow: hidden; background: var(--hl-surface-raised); box-shadow: var(--hl-ring-face); }
.hl-pip__face > img { width: 100%; height: 100%; object-fit: cover; }
.hl-pip__say { min-width: 0; font: 500 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); letter-spacing: .005em; color: var(--hl-text); text-wrap: pretty; }

/* facts: one strip, hairline dividers, no icon or box (SPEC 7.11) */
.hl-facts { display: grid; grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr)); }
.hl-facts > li { display: flex; flex-direction: column; align-items: center; text-align: center; min-width: 0; }
.hl-facts > li + li { box-shadow: var(--hl-rule-l); }
.hl-fig { font: 600 var(--hl-t-figure-size) / var(--hl-t-figure-lh) var(--hl-font-ui); font-variant-numeric: tabular-nums lining-nums; color: var(--hl-text); }
.hl-fig--empty { color: var(--hl-text-muted); }
.hl-facts .hl-label { color: var(--hl-text-muted); }
.hl-facts .hl-label { overflow-wrap: anywhere; }
.hl-facts.is-loading > li > * { visibility: hidden; }

/* the Star meter (SPEC 7.10): a flat card; an SVG ring with the 8-point Star inside; no glow at any percent, even lit */
.hl-meter { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: var(--hl-sp-16); padding: var(--hl-sp-4) var(--hl-sp-16); min-height: 60px; border-radius: var(--hl-r-3); background: var(--hl-surface); box-shadow: var(--hl-e1); color: var(--hl-text); }
.hl-meter__ring { position: relative; width: 44px; height: 44px; }
.hl-meter__ring > svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.hl-meter__star-lit { opacity: 0; }
.hl-meter__track { stroke: rgba(255, 243, 218, .16); stroke-width: 3; }
.hl-meter__arc { stroke: var(--hl-accent-gold); stroke-width: 3; }
.hl-meter__text { min-width: 0; }
.hl-meter__title { display: block; margin: 0; font: 400 var(--hl-t-heading-size) / var(--hl-t-heading-lh) var(--hl-font-display); font-variant-numeric: lining-nums; }
.hl-meter__sub { display: block; margin: 0; font: 600 var(--hl-t-body-s-size) / var(--hl-t-snug-lh) var(--hl-font-ui); letter-spacing: .005em; color: var(--hl-accent-gold); font-variant-numeric: tabular-nums lining-nums; }
.hl-meter__sub span { color: var(--hl-text-muted); font-weight: 500; }
.hl-meter__you { display: flex; flex-direction: column; align-items: center; padding-left: var(--hl-sp-16); box-shadow: var(--hl-rule-l); min-width: 76px; }
.hl-meter__n { font: 600 var(--hl-t-figure-l-size) / var(--hl-t-figure-l-lh) var(--hl-font-ui); font-variant-numeric: tabular-nums lining-nums; }
.hl-meter__you small { font: 500 min(var(--hl-t-caption-size), 15.6px) / var(--hl-t-caption-lh) var(--hl-font-ui); letter-spacing: .01em; color: var(--hl-text-muted); }
.hl-meter--solo { grid-template-columns: 1fr; justify-items: center; text-align: center; padding: var(--hl-sp-16); }          /* the big, centred version for a day page */
.hl-meter--solo .hl-meter__ring { width: 148px; height: 148px; }
.hl-meter--solo .hl-meter__you { padding-left: 0; box-shadow: none; }
a.hl-meter, button.hl-meter { width: 100%; text-align: left; font: inherit; cursor: pointer; text-decoration: none; }
.hl-meter:is(a, button):is(:focus-visible, .is-focus) { outline: var(--hl-focus); outline-offset: 3px; }
.hl-star { display: block; width: 100%; height: 100%; overflow: visible; }

/* the day's certificate (SPEC 7.16, Rankings, above the list): NOT paper (a public notice is not a letter to you; as parchment it was the brightest mass on a list screen):
   a card on the dark (--s2) with a foil keyline and the leader's own face */
.hl-cert { --kl-bg: var(--hl-foil-border-dk); display: flex; align-items: center; gap: var(--hl-sp-12); padding: var(--hl-sp-12) var(--hl-sp-16); border-radius: var(--hl-r-3); background: var(--hl-surface-raised); box-shadow: var(--hl-e1); }
.hl-cert > .hl-avatar { margin-left: var(--hl-sp-4); }
.hl-cert__body { flex: 1; min-width: 0; }
.hl-cert__body .hl-label { display: block; color: var(--hl-accent-gold); }
.hl-cert__row { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 var(--hl-sp-12); }
.hl-cert__name { margin: 0; font: 400 var(--hl-t-heading-size) / var(--hl-t-heading-lh) var(--hl-font-display); font-variant-numeric: lining-nums; color: var(--hl-text); }
.hl-cert__sub { margin: 0; color: var(--hl-text-muted); font: 500 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); }

/* the foil keyline: a 1 px stroke of foil, 4 px inside the edge of a card or a sheet, drawn with a mask (the ticket's notches are the one other use of mask). Foil is a stroke of 2 px or less: never a body, never on type. */
.hl-keyline { position: relative; }
.hl-keyline::before { content: ""; position: absolute; inset: var(--kl-inset, 4px); border-radius: var(--kl-r, 12px); padding: var(--kl-w, 1px); background: var(--kl-bg, var(--hl-foil-border)); -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask-composite: exclude; pointer-events: none; }

/* a notice (offline, a refusal): an amber icon, the words, one line of what to do */
.hl-notice { display: flex; align-items: flex-start; gap: var(--hl-sp-12); padding: var(--hl-sp-12) var(--hl-sp-16); border-radius: var(--hl-r-3); background: var(--hl-surface); box-shadow: var(--hl-e1); }
.hl-notice > .hl-icon { color: var(--hl-accent-amber); margin-top: 1px; }
.hl-notice p { color: var(--hl-text); font: 500 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); }
.hl-notice .hl-notice__more { display: block; margin-top: 2px; color: var(--hl-text-muted); }

/* ==========================================================================
   5  THE DAY STREET AND THE FACADE (SPEC 7.5, 7.21)
   The street is ONE link to the Calendar: seven arched card-stock doors on a gilt rail and, under them, the whole run of twenty days as a thin line of arches. The doors are a picture; the line is the run.
   The strip does not scroll: it sits at the gutter on the first days, centres on today in the middle, rests against the gutter on the last days (the clamp), and ui/street-layout.js chooses the gap
   (--dgap) and the nudge (--nudge) for the width so that no door shows under 10 px, none is cut by under 10 px, and no whole door stands within 8 px of the edge.
   <a class="hl-street" style="--day:1; --first:1"><div class="hl-street__doors"><ol><li class="hl-day hl-day--today"><span class="hl-door">...</span></li>...</ol></div><div class="hl-street__line">arches</div><span class="hl-street__cap">caption</span></a>
   States of a door: today (lit glass) | played (a lit pane and a check) | next (a lock and the hours) | later (sealed and coming are one) | missed (an amber ring and a 6 px dot) | finale (holds the Star).
   ========================================================================== */
.hl-street { --dgap: 8px; --first: 1; --pre: calc((var(--day, 1) - 1) * (48px + var(--dgap))); --strip: calc(19 * (48px + var(--dgap)) + 56px); display: block; position: relative; isolation: isolate; margin: 0 calc(var(--hl-gutter) * -1); padding: var(--hl-sp-4) 0; overflow: hidden; color: var(--hl-text); text-decoration: none; }
.hl-street__doors { position: relative; height: 72px; }
.hl-street__doors::after { content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px; background: var(--hl-foil-rail); }          /* the gilt rail: foil, 1 px, fading to nothing at both ends */
.hl-street__doors ol { display: flex; align-items: flex-end; gap: var(--dgap); width: max-content; height: 72px; margin-left: calc(clamp(calc(100% - var(--hl-gutter) - var(--strip)), calc(50% - var(--pre) - 28px - var(--nudge, 0px)), var(--hl-gutter)) + (var(--first) - 1) * (48px + var(--dgap))); }
.hl-day { position: relative; flex: none; }
.hl-door {
  position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; width: 48px; height: 60px; border-radius: 24px 24px var(--hl-r-1) var(--hl-r-1);
  background: linear-gradient(180deg, var(--hl-s1-hi), var(--hl-surface)); box-shadow: var(--hl-ring-hair), var(--hl-press); color: var(--hl-text-muted); text-decoration: none;
}
.hl-door__n { font: 400 var(--hl-t-door-size) / 1.1429 var(--hl-font-display); font-variant-numeric: lining-nums; margin-top: var(--hl-sp-8); text-shadow: 0 -1px 0 rgba(255, 243, 218, .08), 0 1px 0 rgba(2, 8, 12, .55); }          /* raised: the only text shadows on the dark */
.hl-door__t { font: 600 min(var(--hl-t-caption-size), 15.6px) / var(--hl-t-caption-lh) var(--hl-font-ui); font-variant-numeric: tabular-nums lining-nums; white-space: nowrap; }
.hl-door .hl-star { width: 28px; height: 28px; margin-top: var(--hl-sp-4); }
/* today: the lit glass, and the street's one glow (a faint spill layer behind it, never a box-shadow halo) */
.hl-day--today .hl-door { width: 56px; height: 68px; border-radius: 28px 28px var(--hl-r-1) var(--hl-r-1); background: var(--hl-lit); box-shadow: var(--hl-lit-edge); color: var(--hl-bg); }
.hl-day--today .hl-door__n { margin-top: 0; text-shadow: none; }
.hl-day--today .hl-door__t { margin-top: 2px; }
.hl-door__lit { position: absolute; inset: 0; border-radius: inherit; background: var(--hl-s1-hi); pointer-events: none; }          /* the lit glass that fades out when a door opens at midnight (ui/day-street.js animates its opacity) */
.hl-day--today::before { content: ""; position: absolute; z-index: -1; left: 50%; top: 50%; width: 92px; height: 108px; transform: translate(-50%, -50%); background: var(--hl-spill); opacity: .8; pointer-events: none; }          /* at rest .8, the middle of the .65 to .9 loop (motion.css); the street isolates and clips it */
/* played: a lit pane inside its card-stock door; a check is the mark */
.hl-day--played .hl-door { color: var(--hl-bg); }
.hl-day--played .hl-door::before { content: ""; position: absolute; inset: 4px; border-radius: 20px 20px 4px 4px; background: var(--hl-lit-pane); box-shadow: var(--hl-lit-top); }
.hl-day--played .hl-door > * { position: relative; }
.hl-day--played .hl-door__n { text-shadow: none; margin-top: 0; line-height: 28px; }
.hl-day--played .hl-door > .hl-icon { margin-top: var(--hl-sp-4); }
/* next (tomorrow, whatever its content): nothing lit, a gold ring (4.53:1 on s1), a lock and the hours left inside */
.hl-day--next .hl-door { box-shadow: var(--hl-ring-gold-state), var(--hl-press); color: var(--hl-accent-gold); gap: 2px; }
/* missed: a card-stock door with an amber ring and a 6 px dot (a mark, never colour alone) */
.hl-day--missed .hl-door { box-shadow: var(--hl-ring-amber), var(--hl-press); }
.hl-day--missed .hl-door::after { content: ""; position: absolute; left: 50%; bottom: var(--hl-sp-8); width: 6px; height: 6px; margin-left: -3px; border-radius: 50%; background: var(--hl-accent-amber); }
.hl-day--missed .hl-door__n { margin-top: 0; }
/* the finale: the arch that holds the Star: dark until the Star is relit; on its own day the lit door with the Star in ink */
.hl-day--finale.hl-day--today .hl-door .hl-star { width: 32px; height: 32px; margin-top: 0; }
.hl-day--finale.hl-day--today .hl-door__t { margin-top: 0; }
.hl-day--finale.hl-day--played .hl-door .hl-star { margin-top: 0; }

/* the progress line: twenty arches, one per day, the whole run at a glance. Played lit, today taller and brightest, a missed day an amber ring with a 6 px dot, the finale wider and holding the Star. */
.hl-street__line { display: flex; align-items: flex-end; justify-content: space-between; height: 16px; margin: var(--hl-sp-4) var(--hl-gutter) 0; }
.hl-arch { position: relative; display: block; flex: none; width: 12px; height: 12px; border-radius: 6px 6px 2px 2px; box-shadow: var(--hl-ring-arch); }
.hl-arch--played { background: var(--hl-lit-pane); box-shadow: none; }
.hl-arch--today { height: 16px; background: var(--hl-lit); box-shadow: none; }
.hl-arch--next { box-shadow: var(--hl-ring-arch-next); }
.hl-arch--missed { box-shadow: var(--hl-ring-arch-missed); }
.hl-arch--missed::after { content: ""; position: absolute; left: 50%; bottom: 1.5px; width: 6px; height: 6px; margin-left: -3px; border-radius: 50%; background: var(--hl-accent-amber); }
.hl-arch--finale { width: 16px; height: 16px; border-radius: 8px 8px 2px 2px; display: grid; place-items: center; }
.hl-arch--finale .hl-star { width: 12px; height: 12px; margin-top: 2px; }
.hl-arch--finale.hl-arch--today, .hl-arch--finale.hl-arch--played { color: var(--hl-bg); }
.hl-street__cap { display: block; margin: var(--hl-sp-4) var(--hl-gutter) 0; color: var(--hl-text-muted); font: 500 var(--hl-t-body-s-size) / var(--hl-t-snug-lh) var(--hl-font-ui); letter-spacing: .005em; }          /* 14 px: it says when tomorrow opens */
/* the focus ring of the street is drawn ABOVE the doors (they paint over a link's own outline): a pseudo-element, 4 px inside the edge */
.hl-street:focus-visible { outline: none; }
.hl-street::after { content: ""; position: absolute; inset: 0; z-index: 3; pointer-events: none; }
.hl-street:is(:focus-visible, .is-focus)::after { outline: var(--hl-focus); outline-offset: -4px; }

/* the Calendar's facade (SPEC 7.21): the street as twenty doors, five to a row, each door a link in one roving group */
.hl-facade { padding: var(--hl-sp-12) var(--hl-sp-24); border-radius: var(--hl-r-3); background: var(--hl-surface); box-shadow: var(--hl-e1); }
.hl-facade__row + .hl-facade__row { margin-top: var(--hl-sp-12); }
.hl-facade__head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0 var(--hl-sp-12); margin-bottom: var(--hl-sp-8); }
.hl-facade__head .hl-label { color: var(--hl-text-muted); }
.hl-facade__head .hl-label b { font-weight: 700; color: var(--hl-accent-gold); }
.hl-facade ol { position: relative; padding-bottom: var(--hl-sp-8); display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); column-gap: var(--hl-sp-8); isolation: isolate; }
.hl-facade ol::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: var(--hl-foil-rail); }
.hl-facade .hl-day { width: 100%; display: flex; flex-direction: column; align-items: center; gap: var(--hl-sp-4); }
.hl-facade .hl-door, .hl-facade .hl-day--today .hl-door { width: 100%; max-width: 72px; height: auto; aspect-ratio: 3 / 4; border-radius: 50% 50% var(--hl-r-1) var(--hl-r-1) / 37.5% 37.5% var(--hl-r-1) var(--hl-r-1); }
.hl-facade .hl-day--today::before { width: 150%; height: 120%; top: 38%; }
.hl-facade .hl-day--finale .hl-star, .hl-facade .hl-day--finale.hl-day--today .hl-star { width: 38px; height: 38px; }
.hl-day__date { color: var(--hl-text-muted); font: 500 var(--hl-t-caption-size) / var(--hl-t-caption-lh) var(--hl-font-ui); white-space: nowrap; }          /* a date is meta, so 12 */
.hl-day--today .hl-day__date { color: var(--hl-text); }
.hl-facade a.hl-door:is(:focus-visible, .is-focus) { outline: var(--hl-focus); outline-offset: 3px; }          /* one tab stop, the arrows move (ui/day-street.js) */
.hl-cal__legend { color: var(--hl-text-muted); text-wrap: balance; }

/* ==========================================================================
   6  ROWS, RANKINGS, THE DOCK (SPEC 7.15)
   A row is 56 px or more and the whole row is the target. Rankings: a rank numeral (1 gold, 2 silver, 3 bronze, text only), a face, a name, ONE number (the power). You are a gold bar and a gold name.
   ========================================================================== */
.hl-list { display: flex; flex-direction: column; gap: var(--hl-sp-8); }
.hl-list--rows { gap: 0; }
.hl-row { display: flex; align-items: center; gap: var(--hl-sp-12); width: 100%; min-height: 56px; padding: var(--hl-sp-8) var(--hl-sp-12); border-radius: var(--hl-r-2); background: var(--hl-surface); box-shadow: var(--hl-ring-hair-in); color: var(--hl-text); text-align: left; text-decoration: none; font: inherit; }
.hl-row__main { flex: 1; min-width: min(5em, 96px); }        /* the title keeps room for a few letters beside a long value (the value is the one that is cut), but not more than 96 px at a big text size */
.hl-player-row .hl-row__main { min-width: 0; }                /* a name gives way (an ellipsis) before the row grows wider than the screen */
.hl-row__title { display: block; font: 600 var(--hl-t-headline-size) / var(--hl-t-headline-lh) var(--hl-font-ui); letter-spacing: .005em; }
.hl-row:not(.hl-player-row) .hl-row__title { white-space: normal; overflow-wrap: anywhere; }      /* a row that names an action or a setting wraps; a player's name is the one title that is cut with an ellipsis */
.hl-player-row .hl-row__title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hl-row__sub { display: block; font: 500 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); letter-spacing: .005em; color: var(--hl-text-muted); }
.hl-row__icon { flex: none; width: 36px; height: 36px; display: grid; place-items: center; border-radius: var(--hl-r-2); background: var(--hl-surface-raised); box-shadow: var(--hl-ring-hair-in); color: var(--hl-accent-gold); }
.hl-row__icon .hl-icon { width: 20px; height: 20px; stroke-width: 1.8; }
.hl-row__end { flex: 0 1 auto; min-width: 0; max-width: 55%; display: flex; align-items: center; gap: var(--hl-sp-8); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--hl-text-muted); font: 600 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); }       /* the value shortens (an ellipsis) before the title does */
.hl-row__chev { flex: none; color: var(--hl-text-muted); }
.hl-row--action { cursor: pointer; touch-action: manipulation; }
.hl-row--action:is(:focus-visible, .is-focus) { outline: var(--hl-focus); outline-offset: 2px; }
.hl-row:is(:disabled, [aria-disabled="true"], .is-disabled) { color: var(--hl-text-faint); cursor: not-allowed; }
.hl-row:is(:disabled, [aria-disabled="true"], .is-disabled) :is(.hl-row__sub, .hl-row__end, .hl-row__chev, .hl-row__icon) { color: var(--hl-text-faint); }
.hl-row.is-error { box-shadow: var(--hl-ring-rose-in); }
.hl-row.is-loading > * { visibility: hidden; }
.hl-row.is-loading { position: relative; overflow: hidden; background: var(--hl-wash); box-shadow: none; pointer-events: none; }
.hl-row.is-loading::after { content: ""; position: absolute; inset: 0; background: var(--hl-shimmer); pointer-events: none; }
.hl-player-row > .hl-chip { margin-left: auto; flex: none; }

/* a ranking row: the numeral, the face, the name over a caption (only "You" on your row), the power on the right (followed by a screen-reader-only " power"). A hairline at the foot of each row, from x = 40, none after the last. */
.hl-rank-row { position: relative; display: flex; align-items: center; gap: var(--hl-sp-12); min-height: 56px; width: 100%; text-align: left; color: var(--hl-text); font: inherit; scroll-snap-align: end; }          /* each row is a snap point by its foot (SPEC 7.15) */
.hl-rank-row::after { content: ""; position: absolute; left: 40px; right: 0; bottom: 0; height: 1px; background: var(--hl-hair); }
li:last-child > .hl-rank-row::after { display: none; }
.hl-rank { flex: none; min-width: max(28px, 2.4ch); text-align: center; font: 600 var(--hl-t-headline-size) / var(--hl-t-headline-lh) var(--hl-font-ui); font-variant-numeric: tabular-nums lining-nums; color: var(--hl-text-muted); }          /* wide enough for two digits at 200 percent in every row, so the faces stay aligned */
.hl-rank--1 { color: var(--hl-accent-gold); } .hl-rank--2 { color: var(--hl-rank-2); } .hl-rank--3 { color: var(--hl-rank-3); }
.hl-rank-row__who { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.hl-rank-row__name { display: block; color: var(--hl-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font: 600 var(--hl-t-headline-size) / var(--hl-t-headline-lh) var(--hl-font-ui); letter-spacing: .005em; }
.hl-rank-row__sub { color: var(--hl-text-muted); font: 500 var(--hl-t-caption-size) / var(--hl-t-caption-lh) var(--hl-font-ui); letter-spacing: .01em; }
.hl-rank-row__pow { text-align: right; }
.hl-rank-row__pow .hl-fig { color: var(--hl-text); }
.hl-rank-row.is-you::before { content: ""; position: absolute; left: calc(var(--hl-gutter) * -1); top: 8px; bottom: 8px; width: 2px; background: var(--hl-accent-gold); }          /* your row in the list: a 2 px gold bar on the screen's left edge and a gold name */
.hl-rank-row.is-you .hl-rank-row__name { color: var(--hl-accent-gold); }
a.hl-rank-row, button.hl-rank-row { cursor: pointer; touch-action: manipulation; }
.hl-rank-row:is(a, button):is(:focus-visible, .is-focus) { outline: var(--hl-focus); outline-offset: 2px; }
.hl-rank-row.is-loading { pointer-events: none; }
.hl-rank-row.is-empty .hl-rank-row__name { white-space: normal; color: var(--hl-text-muted); }
.hl-rank-row.is-empty .hl-rank { color: var(--hl-text-muted); }

/* the dock (SPEC 7.15): your place, pinned above the tab bar, only when your own row is out of the list's view (or you have no row yet); solid, never over text */
.hl-dock { position: relative; min-height: 64px; padding: 0 var(--hl-gutter); display: flex; align-items: center; gap: var(--hl-sp-12); background: linear-gradient(0deg, var(--hl-cream-wash-6), var(--hl-cream-wash-6)), var(--hl-surface); color: var(--hl-text); }          /* cream 6 percent over the card (gold over ink went a flat grey, off the teal ladder) */
.hl-dock::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px; background: var(--hl-accent-gold); }
.hl-dock .hl-rank-row__name { color: var(--hl-accent-gold); }
.hl-dock .hl-rank-row__who { flex: 1; }
.hl-dock__inner { display: flex; align-items: center; gap: var(--hl-sp-12); width: 100%; max-width: calc(var(--hl-content-max) - 2 * var(--hl-gutter)); margin-inline: auto; }
/* on the Rankings route a row rests with its foot on the fade, never cut by a bar (the snapport ends where the fade begins) */
html:has(.hl-snap) { scroll-snap-type: y proximity; }

/* ==========================================================================
   7  CHAT (SPEC 7.22): the day's summary (the only thing the system says), messages, the composer
   ========================================================================== */
.hl-dayrule { display: flex; justify-content: center; padding-top: var(--hl-sp-4); }
.hl-dayrule .hl-label { padding: 0 var(--hl-sp-12); color: var(--hl-text-muted); }
.hl-summary { padding: var(--hl-sp-16); border-radius: var(--hl-r-3); background: var(--hl-surface); box-shadow: var(--hl-e1); }
.hl-summary__head { display: flex; flex-wrap: wrap; align-items: center; gap: 0 var(--hl-sp-8); color: var(--hl-accent-gold); }
.hl-summary__head .hl-label { overflow-wrap: anywhere; }
.hl-summary__title { margin: var(--hl-sp-4) 0 0; font: 400 var(--hl-t-heading-size) / var(--hl-t-heading-lh) var(--hl-font-display); font-variant-numeric: lining-nums; text-wrap: balance; }
.hl-summary__lines { margin-top: var(--hl-sp-8); display: grid; gap: var(--hl-sp-4); font: 500 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); letter-spacing: .005em; text-wrap: pretty; }
.hl-summary__lines > li { display: flex; gap: var(--hl-sp-8); color: var(--hl-text); }
.hl-summary__lines > li > span { min-width: 0; overflow-wrap: anywhere; }          /* a line holds a player's name (20 characters of anything): an unbroken one wraps instead of leaving the card at a large text size */
.hl-summary__lines > li::before { content: ""; flex: none; width: 4px; height: 4px; margin-top: 8px; border-radius: 50%; background: var(--hl-accent-gold); }
.hl-summary__foot { margin-top: var(--hl-sp-12); display: flex; align-items: center; gap: var(--hl-sp-8); color: var(--hl-text-muted); font: 500 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); letter-spacing: .005em; text-wrap: pretty; }
.hl-msg { display: flex; gap: var(--hl-sp-8); align-items: flex-end; }
.hl-msg__body { max-width: calc(100% - 88px); min-width: 0; }          /* a 28 px face + a 44 px more button + two 8 px gaps */
.hl-msg--own .hl-msg__body { max-width: 78%; }
.hl-msg__meta { display: block; margin: 0 0 2px var(--hl-sp-4); color: var(--hl-text-muted); font: 500 var(--hl-t-caption-size) / var(--hl-t-caption-lh) var(--hl-font-ui); letter-spacing: .01em; overflow-wrap: anywhere; }          /* a time is meta, so 12 */
.hl-msg__meta b { color: var(--hl-text-muted); font-weight: 600; }
.hl-msg__bubble { padding: var(--hl-sp-8) var(--hl-sp-12); border-radius: var(--hl-r-2) var(--hl-r-2) var(--hl-r-2) 4px; background: var(--hl-surface); box-shadow: var(--hl-ring-hair-in); font: 500 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); text-wrap: pretty; overflow-wrap: anywhere; }
.hl-msg--own { justify-content: flex-end; }
.hl-msg--own .hl-msg__bubble { border-radius: var(--hl-r-2) var(--hl-r-2) 4px var(--hl-r-2); background: var(--hl-surface-raised); box-shadow: var(--hl-ring-gold-in); }
.hl-msg--own .hl-msg__meta { text-align: right; margin: 0 var(--hl-sp-4) 2px 0; }
.hl-msg__more { flex: none; margin-left: auto; display: grid; place-items: center; width: var(--hl-tap); height: var(--hl-tap); margin-bottom: -6px; border-radius: 50%; color: var(--hl-text-muted); }          /* report and block live here (the safety features stay): 44 x 44 on the far side of every message that is not yours */
.hl-msg__state { display: flex; align-items: center; gap: var(--hl-sp-4); margin-top: 2px; color: var(--hl-text-muted); font: 500 var(--hl-t-body-s-size) / var(--hl-t-snug-lh) var(--hl-font-ui); }          /* sending: a clock and a word */
.hl-msg__state:empty { margin: 0; }          /* the live region is always laid out (a region that is display: none is not reliably spoken when its words arrive); empty, it takes no room */
.hl-msg--own.is-error .hl-msg__bubble { box-shadow: var(--hl-ring-rose-in); }          /* not sent: a rose ring, an alert icon, a word and a button: never opacity or colour alone */
.hl-msg.is-error .hl-msg__state { color: var(--hl-danger); justify-content: flex-end; flex-wrap: wrap; gap: 0 var(--hl-sp-8); }
.hl-msg__retry { display: inline-flex; align-items: center; min-height: var(--hl-tap); padding: 0 var(--hl-sp-8); color: var(--hl-accent-gold); font: 600 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); cursor: pointer; }
.hl-composer { display: flex; align-items: flex-end; gap: var(--hl-sp-8); padding: var(--hl-sp-8) var(--hl-gutter); width: 100%; max-width: var(--hl-content-max); margin-inline: auto; }
.hl-composer .hl-field { flex: 1; min-width: 0; }
.hl-composer .hl-input, .hl-composer .hl-textarea { min-height: 48px; }
.hl-send { flex: none; display: grid; place-items: center; width: 48px; height: 48px; border-radius: var(--hl-r-pill); background: var(--hl-accent-gold); color: var(--hl-bg); }
.hl-send:is(:disabled, [aria-disabled="true"], .is-disabled) { background: var(--hl-wash); color: var(--hl-text-faint); }
.hl-send:is(:focus-visible, .is-focus) { outline: var(--hl-focus); outline-offset: 3px; }

/* ==========================================================================
   8  NAVIGATION
   ========================================================================== */
/* the segmented control (SPEC 7.14): N equal segments in a well; the selected one is raised (--s2) with a gold state ring */
.hl-seg { display: grid; grid-template-columns: repeat(var(--hl-n, 4), minmax(0, 1fr)); gap: 0; min-height: 52px; padding: var(--hl-sp-4); border-radius: var(--hl-r-3); background: var(--hl-surface-inset); box-shadow: var(--hl-ring-hair-in); }
.hl-seg--block { width: 100%; }
.hl-seg__item { min-width: 0; min-height: var(--hl-tap); display: grid; grid-auto-flow: column; place-content: center; gap: 6px; padding: 0 var(--hl-sp-8); border: 0; border-radius: var(--hl-r-2); background: transparent; color: var(--hl-text-muted); font: 600 min(var(--hl-t-body-s-size), 18.2px) / var(--hl-t-body-s-lh) var(--hl-font-ui); letter-spacing: .005em; white-space: nowrap; cursor: pointer; touch-action: manipulation; }
.hl-seg__item:is([aria-selected="true"], [aria-pressed="true"], .is-selected) { background: var(--hl-surface-raised); color: var(--hl-text); box-shadow: var(--hl-ring-gold-state), var(--hl-hi); }
.hl-seg__item:is(:focus-visible, .is-focus) { outline: var(--hl-focus); outline-offset: -4px; }
.hl-seg__item:is(:disabled, [aria-disabled="true"], .is-disabled) { color: var(--hl-text-faint); cursor: not-allowed; }
/* the underlined tabs (a tab list that scrolls sideways when its words are wider than the screen; no bar is drawn) */
.hl-tabs { display: flex; overflow-x: auto; overscroll-behavior-x: contain; padding-bottom: 1px; box-shadow: var(--hl-rule-b-in); }
.hl-tabs__item { position: relative; flex: none; white-space: nowrap; min-height: var(--hl-tap); display: inline-flex; align-items: center; justify-content: center; padding: 0 var(--hl-sp-16); border: 0; background: none; color: var(--hl-text-muted); font: 600 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); letter-spacing: .005em; cursor: pointer; touch-action: manipulation; }
.hl-tabs__item::after { content: ""; position: absolute; left: var(--hl-sp-12); right: var(--hl-sp-12); bottom: -1px; height: 2px; background: var(--hl-accent-gold); opacity: 0; }
.hl-tabs__item:is([aria-selected="true"], .is-selected) { color: var(--hl-accent-gold); }
.hl-tabs__item:is([aria-selected="true"], .is-selected)::after { opacity: 1; }
.hl-tabs__item:is(:focus-visible, .is-focus) { outline: var(--hl-focus); outline-offset: -4px; border-radius: var(--hl-r-1); }
.hl-tabs__item:is(:disabled, [aria-disabled="true"], .is-disabled) { color: var(--hl-text-faint); cursor: not-allowed; }
.hl-tabpanel:focus-visible { outline-offset: -2px; border-radius: var(--hl-r-2); }          /* a panel is as tall as its list: its ring is drawn inside it, where an edge-to-edge panel cannot clip it */

/* the tab bar (SPEC 7.12): solid ink with a top rule, four equal links, ONE 24 x 2 gold indicator on the bar's top edge that moves to the current tab by transform */
.hl-tabbar { --hl-tab-n: 4; --hl-tab-i: 0; position: var(--hl-nav-pos); left: 0; right: 0; bottom: 0; z-index: var(--hl-z-tabbar); padding: 0 max(0px, var(--hl-safe-left)) var(--hl-safe-bottom) max(0px, var(--hl-safe-right)); background: var(--hl-surface-inset); box-shadow: var(--hl-rule-t); }
.hl-tabbar__inner { position: relative; display: flex; max-width: var(--hl-content-max); margin-inline: auto; }
.hl-tabbar__glide { position: absolute; top: 0; left: 0; width: calc(100% / var(--hl-tab-n)); height: 2px; display: flex; justify-content: center; transform: translateX(calc(100% * var(--hl-tab-i))); pointer-events: none; }
.hl-tabbar__glide::after { content: ""; width: 24px; height: 2px; background: var(--hl-accent-gold); border-radius: 0 0 2px 2px; }
.hl-tabbar.is-none .hl-tabbar__glide { opacity: 0; }
.hl-tab { position: relative; flex: 1; min-width: 0; min-height: var(--hl-tabbar-h); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--hl-sp-4); border: 0; background: none; color: var(--hl-tab-off); font: 600 min(var(--hl-t-caption-size), 15.6px) / var(--hl-t-caption-lh) var(--hl-font-ui); letter-spacing: .01em; text-decoration: none; cursor: pointer; touch-action: manipulation; }
.hl-tab .hl-icon { stroke-width: 1.5; }
.hl-tab:is([aria-current="page"], .is-active) { color: var(--hl-accent-gold); }
.hl-tab:is(:focus-visible, .is-focus) { outline: var(--hl-focus); outline-offset: -4px; }
.hl-tab:is(:disabled, [aria-disabled="true"], .is-disabled) { color: var(--hl-text-faint); cursor: not-allowed; }
.hl-tab__dot { position: absolute; top: 8px; left: calc(50% + 8px); width: 10px; height: 10px; border-radius: 50%; background: var(--hl-cranberry); box-shadow: var(--hl-ring-dot); }          /* the unread mark: a 10 px dot with a 2 px moat; the count goes into the tab's name ("Chat, 3 new") */

/* the app bar (SPEC 7.2): 44 px below the top inset, part of the page (it scrolls away). Today: the wordmark, the day and the avatar. Every other screen: its title in Playfair 22/28 (so the title is not repeated on the page), a back or close button, a 44 px spacer or the avatar. */
.hl-appbar { position: relative; z-index: 4; min-height: var(--hl-bar-h); padding: 0 var(--hl-inset-r) 0 var(--hl-inset-l); display: flex; flex-wrap: wrap; align-items: center; column-gap: var(--hl-sp-12); }
.hl-appbar__day { margin-left: auto; display: flex; flex-direction: column; align-items: flex-end; text-align: right; min-width: 0; }
.hl-appbar__day .hl-label { color: var(--hl-text); overflow-wrap: anywhere; }
.hl-appbar__day .hl-cap { color: var(--hl-text-muted); }
.hl-cap { font: 500 var(--hl-t-caption-size) / var(--hl-t-caption-lh) var(--hl-font-ui); letter-spacing: .01em; }
.hl-appbar__title { margin: 0; min-width: 0; font: 400 var(--hl-t-heading-size) / var(--hl-t-heading-lh) var(--hl-font-display); font-variant-numeric: lining-nums; }          /* 22/28: the screen title, not repeated on the page */
.hl-appbar__title:focus { outline: none; }
.hl-appbar__back { flex: none; width: var(--hl-tap); height: var(--hl-tap); margin-left: calc(var(--hl-sp-12) * -1); display: grid; place-items: center; color: var(--hl-text); border-radius: 50%; }
.hl-appbar__back:is(:focus-visible, .is-focus) { outline: var(--hl-focus); outline-offset: -4px; }
.hl-appbar__gap { margin-left: auto; width: var(--hl-tap); }
.hl-appbar__sub { flex-basis: 100%; order: 9; margin: 0; padding: var(--hl-sp-8) 0 var(--hl-sp-4); color: var(--hl-text-muted); font: 500 var(--hl-t-caption-size) / var(--hl-t-caption-lh) var(--hl-font-ui); letter-spacing: .01em; }
.hl-appbar > .hl-avlink, .hl-appbar > .hl-btn { margin-left: auto; }          /* the avatar link (or a visitor's Join) is the last thing on the right ... */
.hl-appbar__day + .hl-avlink, .hl-appbar__day + .hl-btn { margin-left: 0; }          /* ... unless the day's label already took the free room */

/* ==========================================================================
   9  OVERLAYS  (set --hl-overlay-pos: absolute on a frame to preview them inside it)
   Sheets and dialogs: radius 24, --e3, no blur. They live in #overlays, load on first use (ui/lazy.js) and take focus as docs/APP.md says.
   ========================================================================== */
.hl-scrim { position: var(--hl-overlay-pos); inset: 0; z-index: var(--hl-z-scrim); background: var(--hl-scrim); opacity: 0; pointer-events: none; }
.hl-scrim.is-open { opacity: 1; pointer-events: auto; }

.hl-sheet { position: var(--hl-overlay-pos); left: 0; right: 0; bottom: 0; z-index: var(--hl-z-sheet); display: flex; flex-direction: column; max-width: var(--hl-content-max); max-height: calc(100dvh - var(--hl-safe-top) - 24px); margin-inline: auto; padding: var(--hl-sp-8) 0 0; border-radius: var(--hl-r-4) var(--hl-r-4) 0 0; background: var(--hl-surface); box-shadow: var(--hl-e3); color: var(--hl-text); transform: translateY(100%); visibility: hidden; will-change: transform; }
.hl-sheet.is-open { transform: none; visibility: visible; }
.hl-sheet__top { flex: none; touch-action: none; cursor: grab; }   /* the grab handle and the title row: the part you drag */
.hl-sheet__grab { width: 36px; height: 4px; margin: var(--hl-sp-4) auto var(--hl-sp-12); border-radius: 2px; background: var(--hl-dots); }
.hl-sheet__head { display: flex; align-items: flex-start; gap: var(--hl-sp-8); padding: 0 max(var(--hl-gutter), var(--hl-safe-left)) var(--hl-sp-8); }
.hl-sheet__title { flex: 1; min-width: 0; margin: 0 0 var(--hl-sp-4); font: 400 var(--hl-t-heading-size) / var(--hl-t-heading-lh) var(--hl-font-display); font-variant-numeric: lining-nums; text-wrap: balance; }
.hl-sheet__title:focus { outline: none; }
.hl-sheet__close { flex: none; margin: -6px -8px 0 0; }
.hl-sheet__body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 0 max(var(--hl-gutter), var(--hl-safe-left)) var(--hl-sp-8); }
.hl-sheet__actions { flex: none; display: flex; flex-direction: column; gap: var(--hl-sp-8); padding: var(--hl-sp-12) max(var(--hl-gutter), var(--hl-safe-left)) calc(var(--hl-sp-8) + var(--hl-safe-bottom)); }
.hl-sheet__body:last-child { padding-bottom: calc(var(--hl-sp-24) + var(--hl-safe-bottom)); }
.hl-sheet__body.has-more-below { -webkit-mask-image: linear-gradient(180deg, #000 calc(100% - 32px), transparent); mask-image: linear-gradient(180deg, #000 calc(100% - 32px), transparent); }
.hl-sheet__body.has-more-above { -webkit-mask-image: linear-gradient(180deg, transparent, #000 32px); mask-image: linear-gradient(180deg, transparent, #000 32px); }
.hl-sheet__body.has-more-above.has-more-below { -webkit-mask-image: linear-gradient(180deg, transparent, #000 32px, #000 calc(100% - 32px), transparent); mask-image: linear-gradient(180deg, transparent, #000 32px, #000 calc(100% - 32px), transparent); }
.hl-sheet--result .hl-sheet__body { padding-bottom: var(--hl-sp-4); }         /* the game result (SPEC 7.19): the paper slip, then the primary and a ghost; the page behind is the game */
.hl-sheet--result .hl-sheet__actions { padding-top: var(--hl-sp-12); }
.hl-sheet--result .hl-sheet__grab { margin-top: 0; }          /* f1: 8 px of sheet, the 4 px handle, 12 px, the paper */

.hl-dialog-wrap { position: var(--hl-overlay-pos); inset: 0; z-index: var(--hl-z-dialog); display: grid; place-items: center; padding: var(--hl-sp-24) max(var(--hl-gutter), var(--hl-safe-left)); overflow-y: auto; }
.hl-dialog { width: min(100%, 420px); max-height: 100%; overflow: hidden auto; border-radius: var(--hl-r-4); background: var(--hl-surface); box-shadow: var(--hl-e3); text-align: center; }
.hl-dialog__art { position: relative; display: grid; place-items: end center; height: 156px; background: linear-gradient(180deg, var(--hl-s1-hi), var(--hl-surface)); overflow: hidden; }
.hl-dialog__art > img { display: block; height: 138px; width: auto; }
.hl-dialog__art > svg.hl-star { position: static; width: 64px; height: 64px; align-self: center; }
.hl-starfield { display: block; max-width: 100%; }          /* the scattered stars behind a dialog's Star */
.hl-dialog__body { padding: var(--hl-sp-24); }
.hl-dialog__title { margin: 0; font: 400 var(--hl-t-title-size) / var(--hl-t-title-lh) var(--hl-font-display); font-variant-numeric: lining-nums; text-wrap: balance; }
.hl-dialog__title:focus { outline: none; }
.hl-dialog__text { margin: var(--hl-sp-8) 0 0; font: 400 var(--hl-t-body-size) / var(--hl-t-body-lh) var(--hl-font-ui); color: var(--hl-text-muted); text-wrap: pretty; }
.hl-dialog__actions { display: flex; flex-direction: column; gap: var(--hl-sp-8); margin-top: var(--hl-sp-24); }
.hl-dialog__steps { display: flex; justify-content: center; gap: 6px; margin-top: var(--hl-sp-16); }
.hl-dialog__steps > i { width: 8px; height: 8px; border-radius: 50%; background: var(--hl-dots); }
.hl-dialog__steps > i.is-on { background: var(--hl-accent-gold); }

.hl-toast { position: relative; display: flex; align-items: center; gap: var(--hl-sp-12); min-height: 48px; padding: var(--hl-sp-8) var(--hl-sp-16); border-radius: var(--hl-r-3); background: var(--hl-surface); box-shadow: var(--hl-e3); color: var(--hl-text); font: 500 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); letter-spacing: .005em; }
.hl-toast > .hl-icon { color: var(--hl-accent-gold); }
.hl-toast--success > .hl-icon { color: var(--hl-success); }
.hl-toast--danger { box-shadow: var(--hl-e3), var(--hl-ring-rose-in); } .hl-toast--danger > .hl-icon { color: var(--hl-danger); }
.hl-toast--info > .hl-icon { color: var(--hl-info); }
.hl-toast__text { flex: 1; min-width: 0; overflow-wrap: anywhere; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; }
.hl-toast__action { margin-left: auto; min-width: var(--hl-tap); min-height: var(--hl-tap); padding: 0 var(--hl-sp-8); border: 0; background: none; color: var(--hl-accent-gold); font: 600 var(--hl-t-body-s-size) / 1 var(--hl-font-ui); cursor: pointer; }
.hl-toast__action:focus-visible { outline: var(--hl-focus); outline-offset: -4px; border-radius: var(--hl-r-2); }
.hl-toast-stack { position: var(--hl-overlay-pos); left: 0; right: 0; top: calc(var(--hl-safe-top) + 8px); z-index: var(--hl-z-toast); display: flex; flex-direction: column; gap: var(--hl-sp-8); padding: 0 var(--hl-gutter); pointer-events: none; }
.hl-toast-stack > * { pointer-events: auto; max-width: var(--hl-content-max); margin-inline: auto; width: 100%; }

/* the lightbox: a picture on the dark, drag to look around, pinch or double-tap to zoom, swipe down or tap the cross to leave */
.hl-lightbox { position: var(--hl-overlay-pos); inset: 0; z-index: var(--hl-z-dialog); display: block; background: rgba(3, 9, 14, .96); opacity: 0; touch-action: none; -webkit-user-select: none; user-select: none; }
.hl-lightbox.is-open { opacity: 1; }
.hl-lightbox__stage { position: absolute; inset: 0; overflow: hidden; display: grid; place-items: center; }
.hl-lightbox__img { display: block; max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; transform-origin: 50% 50%; will-change: transform; -webkit-user-drag: none; user-select: none; }
.hl-lightbox__close { position: absolute; top: calc(var(--hl-safe-top) + 8px); right: max(12px, var(--hl-safe-right)); z-index: 2; background: var(--hl-surface); }
.hl-lightbox__cap { position: absolute; left: 0; right: 0; bottom: 0; z-index: 1; padding: var(--hl-sp-32) max(var(--hl-gutter), var(--hl-safe-left)) calc(var(--hl-sp-16) + var(--hl-safe-bottom)); background: linear-gradient(180deg, rgba(3, 9, 14, 0), rgba(3, 9, 14, .85)); color: var(--hl-text); font: 500 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); text-align: center; pointer-events: none; }

/* the offline banner: fixed at the very top, it covers instead of pushing, so nothing on the page moves when it arrives */
.hl-banner { position: var(--hl-overlay-pos); left: 0; right: 0; top: 0; z-index: calc(var(--hl-z-toast) + 1); display: flex; align-items: center; justify-content: center; gap: var(--hl-sp-8); min-height: calc(36px + var(--hl-safe-top)); padding: var(--hl-safe-top) var(--hl-gutter) 0; background: var(--hl-surface); box-shadow: var(--hl-e1), var(--hl-edge-amber); color: var(--hl-text); font: 600 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); text-align: center; }
.hl-banner .hl-icon { width: 18px; height: 18px; stroke-width: 2; color: var(--hl-accent-amber); }

/* ==========================================================================
   10  INPUTS (SPEC 7.13)
   <div class="hl-field is-error"><label class="hl-field__label" for="e">E-mail</label><input class="hl-input" id="e"><div class="hl-field__foot"><div class="hl-field__msg">hint</div><div class="hl-field__alert" role="alert">refusal</div></div></div>
   A label (always visible, 4 px above) over a well; the well is --ink-0 with a cream 50% ring; a refusal is a rose ring, an alert icon and a sentence in a container that is always in the page with role="alert".
   ========================================================================== */
.hl-field { display: block; min-width: 0; }
.hl-field__label { display: block; margin-bottom: var(--hl-sp-4); font: 600 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); letter-spacing: .005em; color: var(--hl-text); }
.hl-input, .hl-textarea { display: block; width: 100%; min-height: 48px; padding: 0 var(--hl-sp-12); border: 0; border-radius: var(--hl-r-2); background: var(--hl-surface-inset); box-shadow: var(--hl-ring-ctl-in); color: var(--hl-text); caret-color: var(--hl-accent-gold); font: 400 max(16px, var(--hl-t-body-size)) / var(--hl-t-body-lh) var(--hl-font-ui);       /* never under 16 px: iOS zooms the page into a smaller field */ appearance: none; -webkit-appearance: none; }
.hl-textarea { min-height: 96px; padding: var(--hl-sp-12); resize: none; }
.hl-input::placeholder, .hl-textarea::placeholder { color: var(--hl-text-faint); opacity: 1; }
.hl-input:is(:focus-visible, .is-focus), .hl-textarea:is(:focus-visible, .is-focus) { outline: var(--hl-focus); outline-offset: 3px; }
.hl-input:is(:disabled, .is-disabled), .hl-textarea:is(:disabled, .is-disabled) { background: var(--hl-wash); box-shadow: none; color: var(--hl-text-faint); cursor: not-allowed; }
.hl-input:is([aria-invalid="true"], .is-error), .hl-textarea:is([aria-invalid="true"], .is-error), .hl-field.is-error :is(.hl-input, .hl-textarea) { box-shadow: var(--hl-ring-rose-in); }
.hl-field.is-valid :is(.hl-input, .hl-textarea) { box-shadow: var(--hl-ring-success-in); }
/* (a field has no loading state: an <input> cannot hold a layer of light, and the kit never sets one) */
/* a password with a Show / Hide button inside the well (ui/input.js, props.reveal): the button is a 44 px target at the right end; the text stops before it */
.hl-field__wrap { position: relative; }
.hl-field--reveal .hl-input { padding-inline-end: 64px; }
.hl-field--reveal .hl-input::-ms-reveal, .hl-field--reveal .hl-input::-ms-clear { display: none; }          /* (Edge draws its own eye: one is enough) */
.hl-field__reveal { position: absolute; inset-block: 0; inset-inline-end: 0; min-width: 56px; min-height: 44px; padding: 0 var(--hl-sp-12); border: 0; border-radius: var(--hl-r-2); background: transparent; color: var(--hl-accent-gold); font: 600 var(--hl-t-body-s-size) / 1 var(--hl-font-ui); cursor: pointer; }
.hl-field__reveal:focus-visible { outline: var(--hl-focus); outline-offset: -2px; }
.hl-field__foot { display: flex; align-items: flex-start; }
.hl-field__msg, .hl-field__alert { display: flex; align-items: flex-start; flex: 1; min-width: 0; gap: var(--hl-sp-8); min-height: calc(var(--hl-t-body-s-size) * var(--hl-t-body-s-lh)); margin-top: var(--hl-sp-4); font: 500 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); letter-spacing: .005em; color: var(--hl-text-muted); }          /* a hint is a message: 14/20 */
.hl-field__msg:empty, .hl-field__alert:empty { flex: 0 0 0; min-height: 0; margin: 0; }          /* an empty line is still IN the page (a live region must exist before its text does: display:none would take it out of the accessibility tree), it just takes no room */
.hl-field__msg .hl-icon, .hl-field__alert .hl-icon { margin-top: 2px; }
.hl-field.is-error .hl-field__alert { color: var(--hl-danger); }
.hl-field.is-valid .hl-field__msg { color: var(--hl-success); }
.hl-field__count { margin-left: auto; padding-left: var(--hl-sp-8); margin-top: var(--hl-sp-4); font: 600 var(--hl-t-caption-size) / calc(var(--hl-t-body-s-size) * var(--hl-t-body-s-lh)) var(--hl-font-ui); font-variant-numeric: tabular-nums lining-nums; color: var(--hl-text-muted); }          /* the counter sits on the 20 px line of the message beside it */
.hl-field__count.is-over { color: var(--hl-danger); }
.hl-field__foot:empty { display: none; }

/* the PIN: four cells 52 x 56 (a filled cell shows a 12 px cream dot). The real <input> lies over them, invisible, so the number pad opens when you tap; the focus ring is on the GROUP, so it shows with all four digits in. */
.hl-pin { position: relative; display: flex; gap: var(--hl-sp-8); width: max-content; max-width: 100%; border-radius: var(--hl-r-2); }
.hl-pin__cell { display: grid; place-items: center; width: 52px; height: 56px; border-radius: var(--hl-r-2); background: var(--hl-surface-inset); box-shadow: var(--hl-ring-ctl-in); }
.hl-pin__cell > i { display: block; width: 12px; height: 12px; border-radius: 50%; background: var(--hl-text); opacity: 0; }
.hl-pin__cell.is-on > i { opacity: 1; }
.hl-pin__input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0; border: 0; opacity: 0; font-size: 16px; color: transparent; background: transparent; caret-color: transparent; cursor: pointer; -webkit-appearance: none; appearance: none; }
.hl-pin:focus-within { outline: var(--hl-focus); outline-offset: 3px; }
.hl-pin:focus-within .hl-pin__cell.is-active { box-shadow: var(--hl-ring-gold-state-in); }
.hl-field.is-error .hl-pin__cell { box-shadow: var(--hl-ring-rose-in); }
.hl-field.is-disabled .hl-pin { opacity: .55; }
.hl-field.is-disabled .hl-pin__input { cursor: not-allowed; }

/* the face picker (SPEC 7.13): a radio group of the 18 avatars (96 px thumbs) in 6 columns (5 below 350), each a 52 px button; the selected one is raised with a gold state ring and a gold check */
.hl-faces > .hl-label { display: block; margin-bottom: var(--hl-sp-8); color: var(--hl-text); font: 600 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); letter-spacing: .005em; text-transform: none; }
.hl-faces__grid { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: var(--hl-sp-8); }
.hl-avopt { position: relative; display: grid; place-items: center; min-height: 52px; border-radius: var(--hl-r-2); background: var(--hl-surface); box-shadow: var(--hl-ring-hair-in); }
.hl-avopt .hl-avatar { box-shadow: none; }
.hl-avopt[aria-checked="true"] { background: var(--hl-surface-raised); box-shadow: var(--hl-ring-gold-state-in); }
.hl-avopt__ck { position: absolute; right: 4px; bottom: 4px; width: 16px; height: 16px; border-radius: 50%; background: var(--hl-accent-gold); color: var(--hl-bg); display: none; place-items: center; }
.hl-avopt[aria-checked="true"] .hl-avopt__ck { display: grid; }
.hl-avopt__ck .hl-icon { width: 10px; height: 10px; stroke-width: 3; }

/* the switch: a flat track and a cream knob; on, the track takes a gold state ring and the knob moves (the state is the position AND the ring: never colour alone) */
.hl-toggle { position: relative; flex: none; width: 52px; height: 32px; padding: 0; border: 0; border-radius: var(--hl-r-pill); background: var(--hl-surface-inset); box-shadow: var(--hl-ring-ctl-in); cursor: pointer; touch-action: manipulation; }
.hl-toggle::after { content: ""; position: absolute; inset: calc((100% - var(--hl-tap)) / 2) -4px; }   /* the target is 60 x 44 */
.hl-toggle__knob { position: absolute; left: 4px; top: 50%; width: 24px; height: 24px; margin-top: -12px; border-radius: 50%; background: var(--hl-text-muted); }
.hl-toggle[aria-checked="true"] { background: var(--hl-surface-raised); box-shadow: var(--hl-ring-gold-state-in); }
.hl-toggle[aria-checked="true"] .hl-toggle__knob { transform: translateX(20px); background: var(--hl-text); }
.hl-toggle:is(:focus-visible, .is-focus) { outline: var(--hl-focus); outline-offset: 3px; }
.hl-toggle:is(:disabled, [aria-disabled="true"], .is-disabled) { opacity: .5; cursor: not-allowed; }
.hl-toggle.is-loading { pointer-events: none; }
.hl-toggle.is-loading .hl-toggle__knob { background: var(--hl-text-faint); }

/* ==========================================================================
   11  FEEDBACK: skeleton, spinner, empty state, pull to refresh, number ticker, dots, the game's time bar
   ========================================================================== */
.hl-skel { position: relative; display: block; height: 14px; overflow: hidden; border-radius: 7px; background: var(--hl-wash); }
.hl-skel::after { content: ""; position: absolute; inset: 0; background: var(--hl-shimmer); pointer-events: none; }
.hl-skel--title { height: 22px; width: 62%; } .hl-skel--line { width: 100%; } .hl-skel--short { width: 40%; }
.hl-skel--circle { width: 40px; height: 40px; border-radius: 50%; } .hl-skel--block { height: 120px; border-radius: var(--hl-r-3); }
.hl-skel--chip { width: 96px; height: 44px; border-radius: var(--hl-r-pill); } .hl-skel--row { height: 56px; border-radius: var(--hl-r-2); }
.hl-skel--poster { height: auto; aspect-ratio: 12 / 7; border-radius: var(--hl-r-3); }
.hl-spinner { width: 24px; height: 24px; flex: none; border: 3px solid var(--hl-hair-gold); border-top-color: var(--hl-accent-gold); border-radius: 50%; }
.hl-empty { display: grid; justify-items: center; gap: var(--hl-sp-8); padding: var(--hl-sp-32) var(--hl-sp-24); text-align: center; color: var(--hl-text-muted); }
.hl-empty__art { width: 96px; height: 96px; display: grid; place-items: center; }
.hl-empty__art > svg { width: 64px; height: 64px; }
.hl-empty__art > .hl-icon { width: 32px; height: 32px; stroke-width: 1.125; color: var(--hl-text-muted); }
.hl-empty__art > .hl-star { position: static; }
.hl-empty__art > img { width: 96px; height: 96px; object-fit: contain; }
.hl-empty__art--picture { width: 132px; height: auto; }
.hl-empty__art--picture > img { width: 132px; height: auto; }
.hl-empty__title { margin: var(--hl-sp-4) 0 0; color: var(--hl-text); font: 400 var(--hl-t-heading-size) / var(--hl-t-heading-lh) var(--hl-font-display); font-variant-numeric: lining-nums; text-wrap: balance; }
.hl-empty__title:focus { outline: none; }
.hl-empty__text { margin: 0; max-width: 300px; font: 500 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); letter-spacing: .005em; text-wrap: balance; }
.hl-empty .hl-btn { margin-top: var(--hl-sp-8); }
.hl-empty--card { border-radius: var(--hl-r-3); background: var(--hl-surface); box-shadow: var(--hl-e1); }          /* an empty list inside a screen: a quiet card (no dashed outline: there is one line style) */

/* pull to refresh: a small lit Star drops from the top as you pull, turns while it works */
.hl-ptr { position: fixed; left: 50%; top: calc(var(--hl-safe-top) + 6px); z-index: var(--hl-z-sticky); width: 44px; height: 44px; margin-left: -22px; display: grid; place-items: center; border-radius: 50%; background: var(--hl-surface); box-shadow: var(--hl-e1); opacity: var(--_o, 0); transform: translateY(calc(var(--_y, 0) * 1px - 64px)) scale(var(--_s, .6)); pointer-events: none; will-change: transform, opacity; }
.hl-ptr__star { width: 26px; height: 26px; transform: rotate(calc(var(--_r, 0) * 1deg)); }
.hl-ptr.is-ready { box-shadow: var(--hl-e1), var(--hl-ring-gold-state); }

/* a number that counts: tabular figures so the width does not dance */
.hl-ticker { display: inline-block; font-variant-numeric: lining-nums tabular-nums; }

/* quiz dots (SPEC 7.18): 8 px dots, the current one 24 px long in ink, the others ink at 50% (3:1 on the paper), never a glow. Right and wrong carry a mark and words. They live on paper (css/paper.css). */
.hl-dots { display: flex; align-items: center; gap: var(--hl-sp-8); }
.hl-dots > li { position: relative; flex: none; width: 8px; height: 8px; display: grid; place-items: center; border-radius: 4px; background: currentColor; opacity: .62; }          /* f1 draws them at .5, which its own report measures at 2.62:1 against the 3:1 it states: .62 is 3.48:1 (the same as an answer key) */
.hl-dots > li.is-current { width: 24px; opacity: 1; }
.hl-dots > li.is-done { opacity: .62; }
.hl-dots > li.is-right, .hl-dots > li.is-wrong { width: 16px; height: 16px; border-radius: 50%; opacity: 1; color: inherit; background: none; }
.hl-dots > li .hl-icon { width: 14px; height: 14px; stroke-width: 2.4; }
.hl-dots--ink { color: var(--hl-ink); }

/* the game's time bar: a string of bulbs, one goes dark as each three seconds begin (the games draw their own clock; this one keeps its bulbs and loses their glow) */
.hl-bulb { position: relative; display: block; flex: none; width: 14px; height: 21px; margin-top: 8px; border-radius: 50% 50% 46% 46% / 62% 62% 38% 38%; background: var(--hl-bulb); box-shadow: var(--hl-lit-top); }
.hl-bulb::before { content: ""; position: absolute; left: 50%; top: -7px; width: 7px; height: 6px; transform: translateX(-50%); border-radius: 2px; background: var(--hl-s3); }
.hl-bulb.is-off { background: var(--hl-surface-raised); box-shadow: var(--hl-ring-hair-in); }
.hl-timebar { position: relative; display: flex; justify-content: space-between; padding: 0 var(--hl-inset-r) 0 var(--hl-inset-l); }
.hl-timebar::before { content: ""; position: absolute; left: 0; right: 0; top: 3px; height: 1px; background: var(--hl-hair); }
.hl-countdown { font-variant-numeric: tabular-nums lining-nums; }

/* ==========================================================================
   12  THE WELCOME STRIP, SMALL ADDITIONS, SHORT WINDOWS, FORCED COLOURS
   ========================================================================== */
/* the Welcome's "how it works" strip (SPEC 7.20): three equal columns with rule dividers, centred: a numeral in display, a label, a caption in body-s */
.hl-tri { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.hl-tri > li { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 0 var(--hl-sp-4); min-width: 0; }
.hl-tri > li + li { box-shadow: var(--hl-rule-l); }
.hl-tri__n { font: 400 var(--hl-t-display-size) / var(--hl-t-display-lh) var(--hl-font-display); font-variant-numeric: lining-nums; color: var(--hl-text); }
.hl-tri .hl-label { margin-top: var(--hl-sp-4); color: var(--hl-text-muted); overflow-wrap: anywhere; }
.hl-tri p { margin-top: var(--hl-sp-8); font: 500 var(--hl-t-body-s-size) / var(--hl-t-body-s-lh) var(--hl-font-ui); letter-spacing: .005em; color: var(--hl-text-muted); text-wrap: balance; }
.hl-tagline { color: var(--hl-text-muted); text-align: center; text-indent: .16em; overflow-wrap: anywhere; }
.hl-nb { display: inline-block; max-width: 100%; }          /* a name that stays on one line while it fits and wraps inside its own box when it cannot (a non-breaking space ran "Mistletoe Hollow" off the screen at 150% on 320) */
.hl-layer { position: static; }
.hl-hero-promise { text-align: center; font: 400 var(--hl-t-title-size) / var(--hl-t-title-lh) var(--hl-font-display); font-variant-numeric: lining-nums; text-wrap: balance; }

/* the narrow ticket (SPEC 1): from 389 px the stub is 92 and the padding 12; below 352 px the title is 20/24 and a word wider than the column breaks instead of running into the stub */
@media (max-width: 389px) {
  .hl-ticket { --stub: clamp(92px, 5.75rem, 126px); }
  .hl-ticket__main { padding: var(--hl-sp-12); }
}
@media (max-width: 374px) {
  .hl-logo--lg { width: 288px; }              /* the Welcome's title steps down below 375 (SPEC 7.1: 288 wide; 360 is the commonest Android width) */
  .hl-street { --dgap: 4px; }                 /* 4 px between doors below 375 (ui/street-layout.js picks 4 to 12 for the width) */
  .hl-facts .hl-label { letter-spacing: .10em; }
  .hl-facade { padding: var(--hl-sp-12) var(--hl-sp-16); }
  .hl-tri .hl-label { letter-spacing: .12em; }
  .hl-meter { gap: var(--hl-sp-12); }
  .hl-meter__you { padding-left: var(--hl-sp-12); min-width: 68px; }
}
@media (max-width: 359px) {
  .hl-logo--sm { height: 20px; }              /* the title steps down to 20 px tall (122 wide) so the longest day label (DAY 20 OF 20) and the avatar still fit at 320 */
  .hl-appbar { column-gap: var(--hl-sp-8); }
}
@media (max-width: 351px) {
  .hl-ticket__title { font-size: 1.25rem; line-height: 1.2; }
}
@media (max-width: 349px) {
  .hl-faces__grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }      /* six columns would be 41 px wide at 320: five keep every face 44 px or more */
}

/* windows that are not tall (a phone on its side, a 320 px high window): the picture is the first thing to go, the words and the button must be on the screen */
@media (max-height: 520px) {
  .hl-dialog-wrap { padding: var(--hl-sp-12) max(var(--hl-sp-24), var(--hl-safe-left)); }
  .hl-dialog__art { display: none; }
  .hl-dialog__body { padding: var(--hl-sp-16) var(--hl-sp-24); }
  .hl-dialog__actions, .hl-sheet__actions { flex-direction: row; flex-wrap: wrap; justify-content: center; margin-top: var(--hl-sp-12); }
  .hl-dialog__actions > .hl-btn, .hl-sheet__actions > .hl-btn { flex: 1 1 8em; }
  .hl-sheet { max-height: calc(100dvh - var(--hl-safe-top) - 8px); }
  .hl-sheet__actions { margin-top: 0; padding-top: var(--hl-sp-8); padding-bottom: calc(var(--hl-sp-12) + var(--hl-safe-bottom)); }
  .hl-sheet__body:last-child { padding-bottom: calc(var(--hl-sp-12) + var(--hl-safe-bottom)); }
}

/* Windows high-contrast mode (forced colours) replaces the colours with the person's own and REMOVES every box-shadow: the rings and edges of this system are box-shadows, so every kind of control is given a border or an
   outline of a system colour here; the pictures that must keep their colours (the street, the ticket, the doors, the wordmark) are forced-color-adjust: none on a plate of their own (SPEC 11). This block comes after the component rules it overrides. */
@media (forced-colors: active) {
  body, .hl-app { background-image: none; }
  .hl-app :focus-visible { outline: 2px solid Highlight; }
  .hl-logo > img { forced-color-adjust: none; background: var(--hl-bg); border-radius: var(--hl-r-1); padding: var(--hl-sp-4) var(--hl-sp-8); }
  .hl-street, .hl-ticket, .hl-play, .hl-door, .hl-arch { forced-color-adjust: none; }
  .hl-street { background: var(--hl-bg); color: var(--hl-text); outline: 1px solid CanvasText; outline-offset: -1px; }
  .hl-ticket { outline: 1px solid ButtonText; }
  .hl-facade .hl-door { outline: 1px solid CanvasText; }
  .hl-tab[aria-current] { border-top: 2px solid Highlight; color: Highlight; }
  .hl-tabbar__glide::after { background: Highlight; }
  .hl-tabs__item:is([aria-selected="true"], .is-selected) { color: Highlight; }          /* the underlined tabs: the selected one is its colour and a 2 px bar, and both are lost without these two lines */
  .hl-tabs__item:is([aria-selected="true"], .is-selected)::after { forced-color-adjust: none; background: Highlight; opacity: 1; }
  :is(.hl-btn, .hl-chip, .hl-row, .hl-card, .hl-input, .hl-textarea, .hl-seg, .hl-seg__item, .hl-toggle, .hl-pin__cell, .hl-avopt, .hl-opt, .hl-meter, .hl-facade, .hl-ticket, .hl-summary, .hl-notice, .hl-msg__bubble, .hl-plate, .hl-paper, .hl-cert, .hl-dock, .hl-sheet, .hl-dialog, .hl-toast, .hl-send) { border: 1px solid CanvasText; }
  :is(.hl-btn, .hl-send, .hl-btn--secondary) { border-color: ButtonText; }
  .hl-seg__item[aria-selected="true"] { outline: 2px solid Highlight; outline-offset: -3px; }
  :is(.hl-opt, .hl-avopt)[aria-checked="true"] { border-width: 3px; }
  .hl-pin__cell.is-active { border-color: Highlight; border-width: 2px; }
  .hl-pin__cell > i { forced-color-adjust: none; background: CanvasText; }
  .hl-paper { background-image: none; }
  .hl-toggle { forced-color-adjust: none; border: 2px solid ButtonText; background: Canvas; }
  .hl-toggle .hl-toggle__knob { background: ButtonText; }
  .hl-toggle[aria-checked="true"] { border-color: Highlight; }
  .hl-toggle[aria-checked="true"] .hl-toggle__knob { background: Highlight; }
  .hl-opt__key .hl-icon, .hl-avopt__ck .hl-icon { stroke: currentColor; }
  .hl-day--missed .hl-door::after, .hl-arch--missed::after { forced-color-adjust: none; }
  .hl-meter__ring { forced-color-adjust: none; }
}
