/* Laugh Tale components — "Canon, played straight".
   Tokens live in public/theme.css and this file consumes them: NO raw colours,
   no literal font sizes (the two 16px input restatements in the trailing touch
   block are the sanctioned exception), no hand-picked accent shades.

   The laws this sheet is built to, in short:
   · Content is BARE — hairline-fenced runs, never boxes, cards, tints or
     shadows. The one tint content receives is the pointer-gated row hover.
   · Glass is CHROME ONLY — the sticky masthead and the filter capsules, both
     dual-rimmed, both shipping their reduced-transparency fallbacks.
   · The Opt-In Fill Rule — `button` is a neutral reset and no `button:hover`
     paints; filled identity lives on `.btn` and nowhere else.
   · Recede with INK (--txt -> --m1 -> --tert), never with opacity.
   · Three type rungs, sentence case, tabular numerals, nothing tracks positive.
   · One entrance settle plus one signature (the deck grid's card stagger);
     reduced motion is an enumerated block that keeps the feedback.
   · The card ART is the only colour-rich thing here. Everything around it is
     grey, hairline and amber, and it stays that way. */

/* ---- base ----------------------------------------------------------------- */
* { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scrollbar-gutter: stable;
  /* the sheet lives on the ROOT so overscroll rubber-banding reveals flat
     ground rather than smearing the ambient */
  background: var(--bg);
  overscroll-behavior-y: none;
  /* kills double-tap-to-zoom and its 300ms tap delay; pinch still works */
  touch-action: manipulation;
}

body {
  margin: 0;
  background: transparent;
  color: var(--txt);
  font: 400 var(--fs-b)/1.45 var(--sans);
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

/* THE AMBIENT GROUND. Two very wide, very soft amber radials pooled in the top
   corners — the room air the glass masthead lenses. A flat graphite sheet under
   a blurred bar reads dead: there is nothing for the lens to bend. On a fixed
   PSEUDO, never background-attachment (mobile Safari repaints an attached
   background every scroll frame). Killed under reduced transparency, forced
   contrast and missing color-mix — see the fallbacks near the end. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: var(--amb);
}

a { color: var(--ac-pale); text-decoration: none; }
@media (hover: hover) { a:hover { color: var(--ac-hot); } }

/* No border-radius on the ring: it rounds the ELEMENT, not the outline, and
   every control with a shape declares its own further down. */
:focus-visible { outline: 2px solid var(--ac); outline-offset: 2px; }
main.content:focus-visible { outline: none; }

/* The surfaces we did not draw still belong to the design: selection, caret,
   scrollbars and native control accents all ship with a UA palette that is in
   no design system. */
::selection { background: color-mix(in srgb, var(--ac) 30%, transparent); color: var(--txt); }
:root { scrollbar-color: var(--hairh) transparent; scrollbar-width: thin; accent-color: var(--ac); caret-color: var(--ac); }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: var(--hair); border-radius: var(--r-cap); border: 3px solid transparent; background-clip: padding-box; }
::-webkit-scrollbar-thumb:hover { background: var(--hairh); background-clip: padding-box; border: 3px solid transparent; }
::-webkit-scrollbar-track { background: transparent; }

/* announced, never painted */
.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
/* Hidden by clipping rather than parked off-viewport (an off-screen box still
   counts as horizontal spill), then a capsule in the corner the moment focus
   reaches it — the first thing a keyboard pass lands on. */
.skip-link {
  position: absolute; width: 1px; height: 1px;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
  z-index: 60;
  padding: 10px 14px;
  border-radius: var(--r-cap);
  background: var(--glasssolid);
  color: var(--txt);
  font-size: var(--fs-b);
  box-shadow: 0 1px 2px var(--shc3);
}
.skip-link:focus-visible {
  position: fixed; left: 12px; top: 12px;
  width: auto; height: auto; clip-path: none;
  outline: 2px solid var(--ac); outline-offset: 0;
}

/* THE OPT-IN FILL RULE. The bare element is a neutral reset — no background, no
   border, no radius, no width. Typography stays because a reset owns it; colour
   never does, and no `button:hover` may paint. (A global button:hover
   background is the one P1 this design world has ever shipped.) */
button {
  background: transparent;
  border: 0;
  color: inherit;
  font-family: inherit;
  font-size: var(--fs-ui);
  font-weight: 600;
  cursor: pointer;
}
/* element-level press feedback: it sets no colour, so it cannot leak */
button:active { transform: scale(.97); }

/* ---- Liquid Glass: chrome only, never content -----------------------------
   The DUAL RIM is what makes these read as a lens rather than a frosted card:
   light catches the top edge (--rim) and falls off at the bottom (--rimb).
   Both are sub-threshold on purpose — felt as curvature, never seen as an
   outline. The cast is separate and carries elevation, which is EARNED. */
.masthead, .filter-cap {
  background-color: var(--glass);
  -webkit-backdrop-filter: blur(16px) saturate(170%);
  backdrop-filter: blur(16px) saturate(170%);
}

/* ---- masthead ------------------------------------------------------------- */
/* Full-bleed sticky bar whose CONTENTS sit on the content column's axis — the
   padding does the aligning, so the bar spans the viewport while the brand
   lines up with the first word of the page under it. */
.masthead {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: calc(.5rem + env(safe-area-inset-top, 0px)) max(1.25rem, calc((100% - 68rem) / 2)) .5rem;
  border-bottom: 1px solid var(--hair);
  /* AT REST the bar sits nearly flush: rims only, no cast. */
  box-shadow: inset 0 1px 1px var(--rim), inset 0 -1px 1px var(--rimb);
  transition: box-shadow var(--t-state) ease;
}
/* EARNED ELEVATION. The full cast appears only once content has actually
   scrolled underneath — flagged by the 1px sentinel above the bar. With JS off
   the class is never set and the bar simply stays flush, which is the at-rest
   state, not a broken one. */
.masthead.raised {
  box-shadow: inset 0 1px 1px var(--rim), inset 0 -1px 1px var(--rimb), 0 10px 26px var(--shc);
}
.mast-sentinel { height: 1px; margin-bottom: -1px; }

.mast-brand {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  font-size: var(--fs-data);
  font-weight: 600;
  letter-spacing: var(--tr-data);
  color: var(--txt);
  flex: 0 0 auto;
}
@media (hover: hover) { .mast-brand:hover { color: var(--txt); } }
/* the straw hat, as a hollow ring — the one mark that says which game this is */
.mast-mark { width: 15px; height: 15px; color: var(--ac-pale); flex: 0 0 auto; }

/* The nav dock: a segmented capsule of pills, the same chrome grammar as the
   parent's tab dock. It is not a second glass surface — the masthead is already
   the glass, and stacking a blur inside a blur reads as a smear — so the track
   is a flat gradient layer over it. */
.mast-nav {
  display: flex;
  gap: 2px;
  padding: 3px;
  border-radius: var(--r-cap);
  background-image: linear-gradient(var(--segtrack), var(--segtrack));
  /* Chrome never abbreviates and never ellipsizes: when the labels stop fitting
     the phone, the ROW pans and the document never does. */
  max-width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  /* so the lamp's negative-z pill paints above this track rather than under it */
  isolation: isolate;
}
.mast-nav::-webkit-scrollbar { display: none; }

.nav-link {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  padding: 7px 13px;
  border-radius: var(--r-cap);
  color: var(--m1);
  font-size: var(--fs-b);
  font-weight: 400;
  white-space: nowrap;
  transition: color var(--t-state), background var(--t-state), transform var(--t-press-tile) ease-out;
}
.nav-link svg { width: 15px; height: 15px; flex: 0 0 auto; }
@media (hover: hover) { .nav-link:hover { color: var(--fhov); } }
.nav-link:active { transform: scale(.97); }
/* THE LAMP. The solid pill under the section you are standing in — accent as
   TEXT with no weight change, because weight widens the row and the whole bar
   shifts. It carries the smallest shadow in the system. */
.nav-link.is-on {
  color: var(--ac-pale);
  background: var(--segon);
  box-shadow: 0 1px 2px var(--shc3);
  /* and it GLIDES: named for the cross-document view transition below, so the
     pill travels from the old section to the new one instead of teleporting.
     No script — the browser morphs the two snapshots. */
  view-transition-name: nav-lamp;
}
@media (hover: hover) { .nav-link.is-on:hover { color: var(--ac-hot); } }

.mast-user {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: .8rem;
  flex: 0 0 auto;
}
.mast-name { color: var(--m1); font-size: var(--fs-b); }
.mast-out { margin: 0; display: flex; }

/* ---- layout --------------------------------------------------------------- */
.content {
  width: 100%;
  max-width: 68rem;
  margin: 0 auto;
  padding: 1.1rem 1.25rem 3rem;
  flex: 1 0 auto;
}

.foot {
  border-top: 1px solid var(--hair);
  max-width: 68rem;
  width: 100%;
  margin: 0 auto;
  padding: 1rem 1.25rem calc(1.4rem + env(safe-area-inset-bottom, 0px));
  color: var(--m1);
  font-size: var(--fs-s);
}
.foot p { margin: 0; }

/* THE BARE-CONTENT RULE. A `.run` is a hairline-fenced stretch of the sheet:
   a top rule, generous padding, radius 0, no fill and no shadow. It is not a
   card, and giving it one would be the first step back to a page of boxes. */
.run {
  border-top: 1px solid var(--hair);
  padding: 16px 0 20px;
  margin: 0;
}
.run:first-child { border-top: 0; padding-top: 4px; }

/* the page's one display line */
.display, .hero-title, .deck-title {
  font-size: var(--fs-title);
  font-weight: 600;
  letter-spacing: var(--tr-title);
  line-height: 1.15;
  color: var(--txt);
  margin: 0;
  text-wrap: balance;
}
/* the data rung: section heads, event names, the queue head */
.section-head, .meta-head, .event-name, .deck-grid-head, .errs-head, .confirm-title {
  font-size: var(--fs-data);
  font-weight: 600;
  letter-spacing: var(--tr-data);
  line-height: 1.3;
  color: var(--txt);
  margin: 0 0 .5rem;
  display: flex;
  align-items: baseline;
  gap: .5rem;
  flex-wrap: wrap;
}
/* a count or a format riding beside its own heading: a fact, not a second head */
.meta-format, .deck-count, .queue-count {
  font-size: var(--fs-s);
  font-weight: 400;
  letter-spacing: normal;
  color: var(--m1);
}

/* an empty state is still a run: it takes the hairline fence rather than
   floating loose on the sheet where it reads as a page that failed to load */
.empty {
  color: var(--m1);
  margin: 0;
  border-top: 1px solid var(--hair);
  padding: 1.6rem 0 1.4rem;
}
/* When the empty line is the WHOLE page — the 404s — there is no run above it
   for the fence to separate it from, so a hairline pinned under the masthead is
   a rule dividing nothing. It becomes a centred quiet state instead; the nav
   dock overhead is the way out, so the page needs no second one. */
main.content > .empty:only-child {
  border-top: 0;
  text-align: center;
  padding: clamp(4rem, 22vh, 9rem) 0;
}

/* ---- controls ------------------------------------------------------------- */
/* THE ONE FILLED ELEMENT: an amber capsule. The label is the sheet's own warm
   near-black rather than white — on this accent white measures 2.16:1 and ink
   measures 8.5:1 — and hover moves the fill FURTHER FROM THE GROUND, which on
   graphite means lighter. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  font: 600 var(--fs-ui) var(--sans);
  color: var(--on-fill);
  background: var(--ac-fill);
  border: 0;
  border-radius: var(--r-cap);
  padding: 11px 18px;
  cursor: pointer;
  text-decoration: none;
  transition: background var(--t-state), transform var(--t-press) ease-out;
}
@media (hover: hover) { .btn:hover { background: var(--ac-fill-hot); } }
.btn:active { transform: scale(.97); }
.btn:disabled { background: var(--surface3); color: var(--m1); cursor: default; }
.btn-small { padding: 8px 15px; font-size: var(--fs-b); }
/* the same capsule with the fill taken away — for the places where a row needs
   its own action but the view already has its one filled element */
.btn.secondary { background: var(--surface3); color: var(--txt); font-weight: 400; }
@media (hover: hover) { .btn.secondary:hover { background: var(--hovrow); } }
/* A form is a stretch column, so a button in it would run the full measure. A
   34rem slab of amber is a banner, not a control: the primary capsule sizes to
   its own label everywhere except the gate, where the column IS the control. */
.form > .btn, .event-form > .btn { align-self: flex-start; }

/* the quiet capsule: a grey fill, an ink label, no border */
.btn-quiet {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font: 400 var(--fs-ui) var(--sans);
  color: var(--txt);
  background: var(--surface3);
  border: 0;
  border-radius: var(--r-cap);
  padding: 8px 15px;
  font-size: var(--fs-b);
  cursor: pointer;
  transition: background var(--t-state), transform var(--t-press) ease-out;
}
@media (hover: hover) { .btn-quiet:hover { background: var(--hovrow); } }
.btn-quiet:active { transform: scale(.97); }

/* THE REVEALED-HIT-AREA RULE. A text action carries an invisible padded hit box
   — padding cancelled by an equal negative margin, so the target costs no
   layout — and hover simply shows the box it already had. The affordance and
   the hit area are the same thing. */
.link-btn {
  display: inline-flex;
  align-items: center;
  font: 400 var(--fs-b) var(--sans);
  color: var(--m1);
  background: transparent;
  border: 0;
  border-radius: var(--r-md);
  padding: 10px 8px;
  margin: -10px -8px;
  cursor: pointer;
}
@media (hover: hover) { .link-btn:hover { background: var(--surface3); color: var(--txt); } }
.link-btn.accent { color: var(--ac-pale); }
@media (hover: hover) { .link-btn.accent:hover { color: var(--ac-hot); } }

/* ---- fields --------------------------------------------------------------- */
/* A labelled field: a caption over the control, so the field's name survives
   the first keystroke. Placeholders are hints, never names. */
.form, .event-form {
  display: flex;
  flex-direction: column;
  gap: .9rem;
  max-width: 34rem;
  margin: 0;
}
.field { display: flex; flex-direction: column; gap: .3rem; }
.field-label {
  color: var(--m1);
  font-size: var(--fs-s);
  padding: 0;
}
/* THE CAPSULE-CONTROL RULE: every single-line control is a quiet-fill capsule
   with no border. Focus is the 2px accent outline at zero offset; the fill does
   not change, because a control that repaints on focus reads as a state change
   rather than a caret arriving. */
.field-input {
  width: 100%;
  font: 400 var(--fs-ui) var(--sans);
  font-variant-numeric: tabular-nums;
  color: var(--txt);
  background: var(--surface3);
  border: 0;
  border-radius: var(--r-cap);
  padding: 10px 16px;
}
.field-input::placeholder { color: var(--m1); }
.field-input:focus, .field-input:focus-visible { outline: 2px solid var(--ac); outline-offset: 0; }
select.field-input {
  appearance: none;
  /* the chevron is drawn, not a glyph: two strokes in --tert, the system's
     non-text furniture, inlined so it needs no request */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%238b8880' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 15px center;
  padding-right: 38px;
}
/* Panels are not capsules. The decklist is a writing surface at the panel
   radius, and it is NOT monospace: digits align through tabular numerals and
   the list's shape carries the rest. It grows with what is typed into it
   rather than opening as twenty empty rows of quiet fill; where field-sizing
   is missing the element's own `rows` is the fallback and the max-height still
   stops a long list from running off the page. */
textarea.field-input {
  border-radius: var(--r-xl);
  padding: 12px 16px;
  line-height: 1.7;
  resize: vertical;
  field-sizing: content;
  min-height: 11rem;
  max-height: 26rem;
}
/* the date field's own picker glyph is browser furniture, so it takes the
   furniture level of the ink ramp rather than shouting at full white */
.field-input::-webkit-calendar-picker-indicator { opacity: .55; cursor: pointer; }
.field-input:hover::-webkit-calendar-picker-indicator { opacity: .9; }
/* a group of fields is fenced with a hairline, not walled in a bordered box */
.field-set {
  display: flex;
  flex-direction: column;
  gap: .7rem;
  border: 0;
  border-top: 1px solid var(--hair);
  border-bottom: 1px solid var(--hair);
  margin: 0;
  padding: .9rem 0 1.1rem;
}
.field-set > .field-label {
  padding: 0;
  color: var(--txt);
  font-size: var(--fs-b);
}

/* ---- home: hero + meta snapshot ------------------------------------------- */
.hero { padding: .6rem 0 1.4rem; }
.hero-sub { margin: .35rem 0 0; color: var(--m1); max-width: 46ch; }

.meta-list, .deck-list, .card-grid, .admin-events, .err-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
/* One share bar per leader. Every name, mark, bar and figure shares an axis
   down the run — a share bar is only readable against the bars above it. The
   measure is capped: a bar stretched across a 68rem column puts its own figure
   half a screen from the name it belongs to. */
.meta-list { max-width: 44rem; }
/* The colour marks LEAD the row rather than trailing the name: on a fixed
   label axis a trailing mark ends up floating in the gap after a short name,
   attached to nothing. */
.meta-row {
  display: grid;
  grid-template-columns: 2.6rem minmax(0, 12rem) minmax(0, 1fr) 3.2rem;
  align-items: center;
  gap: .75rem;
  padding: 7px 0;
}
.meta-leader { color: var(--txt); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.meta-bar {
  height: 4px;
  border-radius: var(--r-cap);
  background: var(--track);
  overflow: hidden;
}
.meta-fill { display: block; height: 100%; background: var(--barfill); border-radius: var(--r-cap); }
.meta-share { color: var(--txt); text-align: right; font-variant-numeric: tabular-nums; }

/* ---- the card-colour marks ------------------------------------------------
   The game's own vocabulary, at mark scale only. Every cluster carries the
   colour NAMES as text — visible where there is room, visually-hidden where
   there is not — so the reading never depends on hue alone. */
.colors { display: inline-flex; align-items: center; gap: 3px; flex: 0 0 auto; }
.clr {
  width: 7px; height: 7px;
  border-radius: 50%;
  display: inline-block;
  background: var(--tert);
}
.clr-red { background: var(--c-red); }
.clr-green { background: var(--c-green); }
.clr-blue { background: var(--c-blue); }
.clr-purple { background: var(--c-purple); }
.clr-black { background: var(--c-black); }
.clr-yellow { background: var(--c-yellow); }
.colors-text { color: var(--m1); font-size: var(--fs-s); }

/* ---- deck rows ------------------------------------------------------------ */
.deck-row { border-top: 1px solid var(--hair2); }
.deck-row:first-child { border-top: 0; }
.deck-link {
  display: grid;
  grid-template-columns: 2.6rem 34px minmax(0, 1fr) auto;
  align-items: center;
  gap: .85rem;
  padding: 9px 10px 9px 0;
  margin: 0 -10px;
  border-radius: var(--r-md);
  color: var(--txt);
  transition: background var(--t-quiet) ease;
}
/* the only tint content ever receives, and it is pointer-gated */
@media (hover: hover) { .deck-link:hover { background: var(--hovrow); color: var(--txt); } }
.deck-place {
  color: var(--m1);
  font-variant-numeric: tabular-nums;
  text-align: right;
  padding-left: 10px;
}
/* first place is the whole point of a top-decks list, so it is the one place a
   row spends the accent — and it still says "1st", so the reading is not hue */
.deck-place.is-first { color: var(--ac-pale); }
.deck-thumb {
  width: 34px;
  height: 48px;
  object-fit: cover;
  object-position: top center;
  border-radius: var(--r-sm);
  background: var(--ph);
  display: block;
}
.deck-who { display: flex; align-items: center; gap: .5rem; min-width: 0; }
.deck-leader { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.deck-player { color: var(--m1); text-align: right; white-space: nowrap; }
/* The home page's rows are not grouped under an event head, so the event rides
   in the row itself and fills the column of sheet the ledger would otherwise
   leave between the leader and the player. */
.deck-link.has-event { grid-template-columns: 2.6rem 34px minmax(0, 15rem) minmax(0, 1fr) auto; }
.deck-event {
  display: flex;
  flex-direction: column;
  min-width: 0;
  color: var(--m1);
  font-size: var(--fs-s);
  line-height: 1.35;
}
.deck-event > * { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.deck-date { color: var(--tert); }

/* ---- filter capsules (glass chrome) --------------------------------------- */
.filters { display: flex; flex-direction: column; gap: .45rem; margin: .8rem 0 1.2rem; }
.filter-row { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }
.filter-cap {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  padding: 6px 13px;
  border-radius: var(--r-cap);
  border: 1px solid var(--hair);
  box-shadow: inset 0 1px 1px var(--rim), inset 0 -1px 1px var(--rimb);
  color: var(--m1);
  font-size: var(--fs-b);
  white-space: nowrap;
  transition: color var(--t-state), background-image var(--t-state), transform var(--t-press) ease-out;
}
@media (hover: hover) { .filter-cap:hover { color: var(--txt); } }
.filter-cap:active { transform: scale(.97); }
/* The on state is the lamp again, not a filled accent capsule: a row where two
   groups can each have a selection would otherwise put two filled surfaces on
   one view. Layered as a background-IMAGE so the fallbacks can swap the glass
   background-colour underneath without wiping it. */
.filter-cap.is-on {
  background-image: linear-gradient(var(--segon), var(--segon));
  color: var(--ac-pale);
}
@media (hover: hover) { .filter-cap.is-on:hover { color: var(--ac-hot); } }
/* the on capsule clears its own filter, and it says so with a drawn glyph */
.filter-cap svg { width: 12px; height: 12px; }
.filter-cap .clr { margin-right: -.1rem; }

/* ---- event groups --------------------------------------------------------- */
.event-head { margin-bottom: .5rem; }
.event-meta { margin: 0 0 .5rem; color: var(--m1); font-size: var(--fs-s); }

/* ---- deck page ------------------------------------------------------------ */
/* The leader's ART and the deck's facts are one header row. The art is the
   thing that names this deck, so it leads; the title, the colours, the result
   and the note hang off it in one column instead of floating beside a void. */
.deck-head {
  display: flex;
  align-items: flex-start;
  gap: 1.4rem;
  padding: .4rem 0 1.4rem;
}
.deck-head > .card-img, .deck-head > .is-blank {
  width: 160px;
  flex: 0 0 auto;
  border-radius: var(--r-lg);
  aspect-ratio: 5 / 7;
}
.deck-head-text { min-width: 0; flex: 1 1 auto; }
/* a run of facts, not prose — it takes the column it is given and balances its
   wrap rather than being capped at a reading measure and orphaning "OP-12" */
.deck-sub { margin: .5rem 0 0; color: var(--m1); text-wrap: pretty; }
.deck-leader-name {
  margin: .45rem 0 0;
  display: flex;
  align-items: center;
  gap: .55rem;
  flex-wrap: wrap;
}
.deck-leader-id { color: var(--tert); font-size: var(--fs-s); }
/* a submitter's note is quoted, not boxed: one hairline rule and the ink ramp */
.deck-notes {
  margin: .9rem 0 0;
  padding-left: .85rem;
  border-left: 1px solid var(--hair);
  color: var(--m1);
  max-width: 62ch;
}

/* THE DECK GRID. Fifty pieces of art, aligned to a strict track — the images
   are the colour, so the frame around them is nothing but grid. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  gap: 1rem .8rem;
}
.card-cell { min-width: 0; }
.card-art { display: block; position: relative; }
.card-tile {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: var(--txt);
  text-align: left;
  font: inherit;
  position: relative;
  transition: transform var(--t-press-tile) ease-out;
}
.card-tile:active { transform: scale(.97); }
.card-img {
  display: block;
  width: 100%;
  aspect-ratio: 5 / 7;
  object-fit: cover;
  border-radius: var(--r-sm);
  background: var(--ph);
}
@media (hover: hover) {
  .card-tile:hover .card-img { box-shadow: 0 0 0 1px var(--hairh); }
}
.card-tile:focus-visible { outline: 2px solid var(--ac); outline-offset: 3px; border-radius: var(--r-sm); }
/* A handful of cards have no artwork anywhere. The tile keeps the grid's shape
   and states the id — absence drawn as absence, not as a broken image. */
.is-blank {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 5 / 7;
  border-radius: var(--r-sm);
  background: var(--ph);
  box-shadow: inset 0 0 0 1px var(--hair2);
  color: var(--m1);
  font-size: var(--fs-xs);
  text-align: center;
}
/* The count rides ON the art, because it belongs to that card and nowhere else.
   Not glass — this sits on content, and it has to stay legible over a white
   sail or a black coat alike, so it is a near-opaque plate. */
.card-qty {
  position: absolute;
  left: 5px;
  bottom: 5px;
  min-width: 22px;
  padding: 1px 5px;
  border-radius: var(--r-xs);
  background: var(--badge);
  color: var(--ac-pale);
  font-size: var(--fs-xs);
  font-weight: 600;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.card-name {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-top: .35rem;
  color: var(--m1);
  font-size: var(--fs-xs);
  line-height: 1.35;
}

/* THE SOLID-DATA-PANEL RULE. The card detail holds text you read, so it is a
   solid detached surface with a deep cast — glass is for chrome you look
   through, not surfaces you read through. Native popover: it opens, closes on
   escape and closes on a click outside with no script at all. */
.card-pop {
  width: min(24rem, calc(100vw - 2rem));
  padding: 1.1rem 1.2rem 1.2rem;
  border: 1px solid var(--hair);
  border-radius: var(--r-xl);
  background: var(--surface);
  color: var(--txt);
  box-shadow: 0 14px 40px var(--shc2);
  opacity: 0;
  transform: scale(.97);
  transition: opacity var(--t-lift) ease, transform var(--t-lift) var(--ease-runout),
    overlay var(--t-lift) allow-discrete, display var(--t-lift) allow-discrete;
}
.card-pop:popover-open { opacity: 1; transform: scale(1); }
@starting-style { .card-pop:popover-open { opacity: 0; transform: scale(.97); } }
.card-pop::backdrop { background: var(--scrim); }
.card-pop-name {
  margin: 0 0 .2rem;
  font-size: var(--fs-data);
  font-weight: 600;
  letter-spacing: var(--tr-data);
}
.card-pop-meta { margin: 0; color: var(--m1); font-size: var(--fs-s); }
.card-pop-types { margin: .5rem 0 0; color: var(--tert); font-size: var(--fs-s); }
.card-pop-effect { margin: .7rem 0 0; padding-top: .7rem; border-top: 1px solid var(--hair2); max-width: 62ch; }

/* ---- the gate ------------------------------------------------------------- */
/* A bare centred column on the sheet — not a card, and not a box. */
.gate {
  min-height: calc(100dvh - 20rem);
  display: grid;
  place-items: center;
  padding: 1rem 0 2rem;
}
.gate > * { grid-column: 1; }
.gate-inner { width: min(21rem, 100%); }
.gate-title {
  font-size: var(--fs-gate);
  font-weight: 600;
  letter-spacing: var(--tr-gate);
  line-height: 1.15;
  margin: 0 0 1rem;
  text-align: center;
}
.gate .form { max-width: none; }
.gate .btn { width: 100%; margin-top: .3rem; }
.gate-alt { margin: 1.2rem 0 0; text-align: center; color: var(--m1); font-size: var(--fs-s); }

/* ---- errors --------------------------------------------------------------- */
/* Errors are ink and a mark, never a tinted box: content takes no fills here
   and an alert is still content. */
.form-err {
  margin: 0 0 1rem;
  padding: .7rem 0;
  border-top: 1px solid var(--hair);
  border-bottom: 1px solid var(--hair);
  color: var(--err);
}
.errs {
  margin: 0 0 1.4rem;
  padding: 14px 0 16px;
  border-top: 1px solid var(--hair);
  border-bottom: 1px solid var(--hair);
}
.errs-head { color: var(--err); margin-bottom: .6rem; }
.err-item {
  position: relative;
  padding-left: 1.1rem;
  margin-bottom: .35rem;
  color: var(--txt);
  max-width: 70ch;
}
.err-item::before {
  content: '';
  position: absolute;
  left: 2px;
  top: .48em;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--err);
}
.err-item:last-child { margin-bottom: 0; }

/* ---- submit --------------------------------------------------------------- */
.submit-note, .confirm-note { margin: .35rem 0 1.4rem; color: var(--m1); max-width: 62ch; }
.confirm-note { margin-bottom: 1.6rem; }

/* ---- admin ---------------------------------------------------------------- */
.queue-item {
  border-top: 1px solid var(--hair);
  padding: 16px 0 20px;
}
.queue-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: .4rem;
}
.queue-meta { margin: 0; color: var(--m1); font-size: var(--fs-s); }
.queue-actions form { display: flex; gap: .5rem; align-items: center; margin: 0; }
/* the queue's own deck preview drops the presented leader down a size: it is a
   check, not the deck's own page */
.queue-item .deck-title { font-size: var(--fs-data); letter-spacing: var(--tr-data); }
.queue-item .deck-head { padding-top: 0; gap: 1rem; }
.queue-item .deck-head > .card-img, .queue-item .deck-head > .is-blank { width: 110px; }

.admin-event-row {
  border-top: 1px solid var(--hair2);
  padding: .8rem 0;
}
.admin-event-row:first-child { border-top: 0; }
.admin-event-row .event-form {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
  max-width: none;
}
.admin-event-row .field-input { width: auto; flex: 1 1 8rem; min-width: 0; }
/* the name is the field that identifies the row, so it gets the room */
.admin-event-row .field-input:first-of-type { flex: 2 1 14rem; }
/* the date's own picker glyph lives inside the box, so the box cannot shrink
   under it — the row's longest button was clipping the calendar clean off */
.admin-event-row input[type="date"] { flex: 0 0 auto; width: 11.5rem; }
.admin-event-row .btn { flex: 0 0 auto; align-self: auto; }
/* a pending event is flagged in words first; the mark only repeats it */
.admin-flag {
  display: flex;
  align-items: center;
  gap: .4rem;
  margin: 0 0 .5rem;
  color: var(--err);
  font-size: var(--fs-s);
}
.admin-flag svg { width: 14px; height: 14px; flex: 0 0 auto; }

/* ---- motion: one settle, one signature ------------------------------------ */
@media (prefers-reduced-motion: no-preference) {
  /* THE ENTRANCE. Runs rise on a short stagger; this is the whole cold budget.
     The step is --t-stagger and the multiplier is (n - 1) — six selectors,
     because nth-child cannot do arithmetic, but ONE number. */
  .run { animation: rise var(--t-rise) var(--ease-settle) both; }
  .run:nth-child(2) { animation-delay: calc(var(--t-stagger) * 1); }
  .run:nth-child(3) { animation-delay: calc(var(--t-stagger) * 2); }
  .run:nth-child(4) { animation-delay: calc(var(--t-stagger) * 3); }
  .run:nth-child(5) { animation-delay: calc(var(--t-stagger) * 4); }
  .run:nth-child(6) { animation-delay: calc(var(--t-stagger) * 5); }
  .run:nth-child(7) { animation-delay: calc(var(--t-stagger) * 6); }

  /* THE SIGNATURE: the deck grid deals itself out. Fifty tiles in ten bands of
     five — bands rather than per-tile because fifty steps of anything is a
     loading screen, and because the grid reflows from three columns to eight,
     so a per-column wave would mean something different at every width. A band
     always reads as a sweep across the grid. */
  .card-cell { animation: deal var(--t-card-rise) var(--ease-runout) both; }
  .card-cell:nth-child(n+6)  { animation-delay: calc(var(--t-card-stagger) * 1); }
  .card-cell:nth-child(n+11) { animation-delay: calc(var(--t-card-stagger) * 2); }
  .card-cell:nth-child(n+16) { animation-delay: calc(var(--t-card-stagger) * 3); }
  .card-cell:nth-child(n+21) { animation-delay: calc(var(--t-card-stagger) * 4); }
  .card-cell:nth-child(n+26) { animation-delay: calc(var(--t-card-stagger) * 5); }
  .card-cell:nth-child(n+31) { animation-delay: calc(var(--t-card-stagger) * 6); }
  .card-cell:nth-child(n+36) { animation-delay: calc(var(--t-card-stagger) * 7); }
  .card-cell:nth-child(n+41) { animation-delay: calc(var(--t-card-stagger) * 8); }
  .card-cell:nth-child(n+46) { animation-delay: calc(var(--t-card-stagger) * 9); }
}
@keyframes rise { from { opacity: 0; transform: translateY(8px); } }
@keyframes deal { from { opacity: 0; transform: translateY(10px) scale(.985); } }

/* THE NAV LAMP'S GLIDE, bought with a cross-document view transition and no
   script at all. The ROOT is deliberately left out of the transition
   (view-transition-name: none), so the page itself swaps instantly and the only
   thing the browser animates is the lamp travelling from the old section's pill
   to the new one's. A browser without the API navigates exactly as before. */
@view-transition { navigation: auto; }
:root { view-transition-name: none; }
::view-transition-group(nav-lamp) { animation-duration: var(--t-lamp); animation-timing-function: var(--ease-runout); }

/* ---- reduced motion: named, not blanket -----------------------------------
   Kill the travel, keep the feedback. Hover tints, focus rings and the
   popover's short opacity fade all survive, so "this is on top of that" still
   reads; a blanket 0.01ms rule would take those with it. */
@media (prefers-reduced-motion: reduce) {
  .run, .card-cell { animation: none; }
  /* every press-scale, including the ones written as `.thing:active`, which the
     bare element selector cannot reach */
  button:active, .btn:active, .btn-quiet:active, .card-tile:active,
  .nav-link:active, .filter-cap:active { transform: none; }
  /* the popover keeps the fade and loses the scale landing */
  .card-pop { transform: none; transition: opacity var(--t-press-tile) ease,
    overlay var(--t-press-tile) allow-discrete, display var(--t-press-tile) allow-discrete; }
  .card-pop:popover-open { transform: none; }
  @starting-style { .card-pop:popover-open { transform: none; } }
  /* and the lamp jumps instead of gliding */
  .nav-link.is-on { view-transition-name: none; }
}

/* ---- glass fallbacks ------------------------------------------------------
   Every glass surface ships its fallbacks with it. background-COLOR only,
   never the shorthand: the shorthand would wipe the nav track and the on
   capsule's lamp layer along with the translucency. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .masthead, .filter-cap { background-color: var(--glasssolid); }
}
@media (prefers-reduced-transparency: reduce) {
  .masthead, .filter-cap {
    backdrop-filter: none; -webkit-backdrop-filter: none;
    background-color: var(--glasssolid);
  }
  .card-pop::backdrop { background: var(--scrim-solid); }
  /* the ambient exists to be refracted by the glass; with the glass gone it is
     just haze over a flat sheet, so it goes too */
  body::before { background: none; }
}
/* no color-mix means no ambient: the pseudo goes empty and the flat --bg on
   <html> shows through, which is exactly the pre-ambient look */
@supports not (background: color-mix(in srgb, red 5%, transparent)) {
  body::before { background: none; }
}
/* maximum contrast wants maximum separation between ink and ground, and a
   tinted ground works against that */
@media (prefers-contrast: more) {
  body::before { background: none; }
}
/* Forced colours strips every background to one system fill, which collapses
   six card colours into one mark. The names are already carried as text beside
   every cluster, so the marks simply opt out and nothing else does — text,
   chrome and focus rings still take the user's chosen palette, which is the
   point of the mode. */
@media (forced-colors: active) {
  .clr { forced-color-adjust: none; }
  .masthead, .filter-cap { backdrop-filter: none; -webkit-backdrop-filter: none; }
}

/* ---- narrow layout -------------------------------------------------------- */
@media (max-width: 720px) {
  .content { padding: 1rem .85rem 2.4rem; }
  .masthead { padding-left: .85rem; padding-right: .85rem; gap: .7rem; flex-wrap: wrap; }
  /* The dock drops to its own row under the brand and pans sideways rather
     than shrinking its labels — chrome never abbreviates. The zero-height
     pseudo is what forces the row break: the capsule itself has to stay
     fit-content, or the segmented track runs on past the last pill as an empty
     grey bar. */
  .masthead::after { content: ''; order: 2; flex: 1 0 100%; height: 0; }
  .mast-nav { order: 3; width: fit-content; max-width: 100%; }
  .foot { padding-left: .85rem; padding-right: .85rem; }
  .deck-link, .deck-link.has-event { grid-template-columns: 2.2rem 30px minmax(0, 1fr); gap: .55rem; row-gap: 0; }
  .deck-thumb { width: 30px; height: 42px; }
  /* the event and the player stack under the leader, on the leader's own axis */
  .deck-event { grid-column: 3; flex-direction: row; gap: .4rem; }
  .deck-player { grid-column: 3; text-align: left; font-size: var(--fs-s); }
  /* the bar takes its own line under the name, so it still runs the full
     measure and every row's fill still starts on the same axis */
  .meta-row { grid-template-columns: auto minmax(0, 1fr) auto; row-gap: .35rem; column-gap: .5rem; }
  .meta-row .colors { grid-area: 1 / 1; }
  .meta-leader { grid-area: 1 / 2; }
  .meta-share { grid-area: 1 / 3; }
  .meta-bar { grid-area: 2 / 1 / 3 / 4; }
  .card-grid { grid-template-columns: repeat(auto-fill, minmax(92px, 1fr)); gap: .8rem .55rem; }
  .deck-head { gap: 1rem; }
  .deck-head > .card-img, .deck-head > .is-blank { width: 116px; }
  .gate { min-height: auto; padding-top: 2rem; }
}
@media (max-width: 640px) {
  .display, .hero-title, .deck-title { font-size: var(--fs-title-c); letter-spacing: var(--tr-title-c); }
  /* THE UNNAMED-GLASS RULE'S phone clause: a navigation on a phone is an
     instant swap, never a transition. */
  .nav-link.is-on { view-transition-name: none; }
}

/* ---- the last word: the touch floor, then the input size -------------------
   Both blocks live at the very END of the file, and nowhere else. Media queries
   add no specificity, so a phone rule written above a base rule loses to it in
   silence — declaring last is what makes source order work for the floor. The
   width/pointer split is load-bearing: an iPad is coarse at 1024px, where the
   narrow layout above never applies, so FLOORS follow the pointer and LAYOUT
   follows the width. */
@media (max-width: 720px), (pointer: coarse) {
  .btn { min-height: 44px; }
  .btn-quiet, .btn-small { min-height: 44px; }
  .nav-link { min-height: 44px; }
  .filter-cap { min-height: 44px; }
  .field-input { min-height: 44px; }
  /* a deck row is the most-tapped target on the site */
  .deck-link { min-height: 44px; }
  /* Text actions grow an INVISIBLE hit box: the padding is cancelled by an
     equal negative margin, so the floor costs no layout. The horizontal half of
     that margin is not optional — dropping it optically indents every text
     action by its own padding. The min-height is not optional either: the
     `font:` shorthand on the base rule resets line-height to normal, so 12px
     of padding over a 13px line landed at 39.6px — measured, not assumed. */
  .link-btn { min-height: 44px; padding: 12px 10px; margin: -12px -10px; }
}
/* THE 16px INPUT RULE (the iOS zoom guard). Declared after every `font:`
   shorthand in the file: a shorthand resets font-size, and `font` and
   `font-size` are different property names, so an earlier rule loses at equal
   specificity to something that does not even look like the same property.
   The element floor covers a field added later; the class restates it because
   `.field-input` at (0,1,0) outranks it. */
@media (max-width: 720px), (pointer: coarse) {
  input, textarea, select { font-size: 16px; }
  .field-input { font-size: 16px; }
}
