/* poker.kmikeym.com theme, v2: "card shop in daylight".
   A light, airy room where the dark things are the OBJECTS: the card art,
   and the two felt-table RSVP bands. Redesigned 2026-08-18 (Mike: light and
   readable beats dark and moody; system sans over decorative faces).

   Palette = the card rarity ladder (locked 2026-07-15):
   foil gold / sapphire / copper / pewter. Each metal has a -deep variant,
   dark enough to stay legible as text on the light bands (the original
   metals only passed contrast on dark backgrounds).

   Lime is reserved for the ONE primary CTA per page and appears only on
   the near-black "felt" bands, never adjacent to a rarity metal. */
:root {
  --foil: #c9a227;      --foil-deep: #8a6d15;
  --sapphire: #2b5d9e;
  --copper: #b06c3f;    --copper-deep: #8f5227;
  --pewter: #8a8d91;    --pewter-deep: #5e6269;
  --lime: #c6f43a;
  --ink: #1a1c20;
  --muted-ink: #5f646c;   /* secondary text on light bands */
  --paper: #fdfcf9;       /* primary light band: warm near-white */
  --parchment: #f4f1e8;   /* secondary light band: warm tint */
  --felt: #101216;        /* the table felt: CTA bands only */
  --felt-ink: #eceae2;
  --line: rgba(26, 28, 32, .12);  /* hairline borders */
  --seat-green: #2ca060;  /* the visitor's own seat on the puzzle table; not lime */
  --radius: 10px;
  /* The rarity ladder as a single rule: the site's signature. Used on the
     masthead border and section rules; never as text. */
  --rarity-rule: linear-gradient(90deg,
      var(--foil) 0 25%, var(--sapphire) 25% 50%,
      var(--copper) 50% 75%, var(--pewter) 75% 100%);
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  color: var(--ink);
  background: var(--paper);
  line-height: 1.6;
}
img { max-width: 100%; height: auto; }
a { color: var(--sapphire); }
:focus-visible { outline: 2px solid var(--sapphire); outline-offset: 2px; }

/* Masthead. Every page's nav is a light header capped by the rarity-ladder
   rule; the markup still says class="band-dark" so the generators and the
   committed pages did not have to change when the theme lightened. */
nav.band-dark {
  /* The rarity rule is painted as a bottom background strip; border-image
     slices a gradient across all four edges and renders broken segments
     when only one border has width. */
  background:
    var(--rarity-rule) left bottom / 100% 3px no-repeat,
    #fff;
  color: var(--ink);
}
nav.band-dark a { color: var(--ink); text-decoration: none; font-weight: 600; font-size: .95rem; }
nav.band-dark a:hover { color: var(--sapphire); text-decoration: underline; }

/* Section bands. Light pages alternate paper and parchment; the felt is
   reserved for the CTA bands (band-cta) so lime keeps its quarantine. */
.band-light { background: var(--paper); color: var(--ink); padding: 4rem 1.25rem; }
.band-dark  { background: var(--parchment); color: var(--ink); padding: 4rem 1.25rem; }
.band-dark a { color: var(--sapphire); }
.band-cta   { background: var(--felt); color: var(--felt-ink); padding: 3rem 1.25rem; text-align: center; }
/* Arriving from another page's RSVP button (`/#rsvp`) scrolls this band to
   the top of the window, which would put the nav above the fold and, if the
   anchor sat on the form itself, the heading and the card fan too: Mike
   followed one from a puzzle page on 2026-09-21 and landed part way down.
   The anchor is the whole hero band, and this leaves the nav bar's height
   above it so the page arrives looking like the top of the page. */
#rsvp { scroll-margin-top: 4.5rem; }
/* Scoped so it never outranks .btn-primary/.btn-secondary color on the
   felt band (a bare `.band-cta a` beats `.btn-primary` on specificity). */
.band-cta a:not(.btn-primary):not(.btn-secondary) { color: #9dc3f0; }
.band-inner { max-width: 720px; margin: 0 auto; }

/* Type. System stack everywhere; the display voice is weight and tracking,
   not uppercase (headers read, they don't shout). */
.display {
  font-weight: 800;
  letter-spacing: -.02em;
  line-height: 1.15;
}
h1.display { font-size: clamp(2rem, 6vw, 3.4rem); margin: 0 0 .4em; }
h2.display { font-size: clamp(1.4rem, 4vw, 2rem); margin: 0 0 .6em; }
/* .tenure-legend td.num joins this list so the coin page's legend numbers
   read in the same tabular face every other table's numbers do. */
.stat, .ledger td.num, .tenure-legend td.num {
  font-family: "SF Mono", ui-monospace, Menlo, Consolas, monospace;
  font-variant-numeric: tabular-nums;
  font-size: .92em;
}
.band-light p, .band-dark p { color: var(--ink); }
.band-light .stat, .band-dark .stat { color: var(--muted-ink); }

/* The one primary CTA. Lime, on felt, and only ever this. */
.btn-primary {
  display: inline-block;
  background: var(--lime);
  color: #101216;
  font-weight: 800;
  letter-spacing: .01em;
  padding: .9em 2.2em;
  border-radius: 8px;
  border: none;
  text-decoration: none;
  font-size: 1.05rem;
  cursor: pointer;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .3);
}
.btn-primary:hover { filter: brightness(1.06); }
.btn-secondary {
  display: inline-block;
  background: transparent;
  color: inherit;
  border: 1px solid currentColor;
  padding: .7em 1.6em;
  border-radius: 8px;
  text-decoration: none;
  font-weight: 600;
}
.btn-secondary:hover { border-color: var(--sapphire); color: var(--sapphire); }

/* Rarity accents. The -deep variants exist because the raw metals fail
   contrast as small text on light backgrounds. */
.rarity-foil     { border-color: var(--foil);     color: var(--foil-deep); }
.rarity-sapphire { border-color: var(--sapphire); color: var(--sapphire); }
.rarity-copper   { border-color: var(--copper);   color: var(--copper-deep); }
.rarity-pewter   { border-color: var(--pewter);   color: var(--pewter-deep); }
.rule-foil { border: 0; height: 3px; background: var(--rarity-rule); margin: 0; }

/* Ledger tables: bordered card containers with a quiet header row.
   .table-scroll is the container every committed table already has, so the
   card treatment needed no markup changes. */
.table-scroll {
  overflow-x: auto;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 1px 3px rgba(26, 28, 32, .06);
  margin: 1.25em 0;
}
.ledger, .tenure-legend { width: 100%; border-collapse: collapse; font-size: .95em; }
.ledger th, .ledger td, .tenure-legend th, .tenure-legend td { padding: .65em .9em; text-align: left; border-bottom: 1px solid var(--line); }
.ledger tbody tr:last-child td, .tenure-legend tbody tr:last-child td { border-bottom: none; }
.ledger th, .tenure-legend th {
  background: var(--parchment);
  text-transform: uppercase;
  font-size: .72em;
  letter-spacing: .07em;
  color: var(--pewter-deep);
  font-weight: 700;
}
.ledger tr.finish-1 td:first-child { color: var(--foil-deep); font-weight: 800; }
.ledger tr.finish-2 td:first-child,
.ledger tr.finish-3 td:first-child { color: var(--sapphire); font-weight: 700; }
/* The player-page ledger's totals row (tools/render.ts's renderPlayer):
   without this it renders identically to a game row, which on a one-game
   player's page (Drew A., Gene) reads as two copies of the same game. The
   parchment fill and heavier rule borrow `.ledger th`'s own "this is
   structure, not a data row" voice rather than inventing a new one. No
   rarity metal here on purpose: metals in this system mark an earned
   trophy's rarity (see docs/brand.md), never a generic "this is a summary"
   accent, so a totals row must never reach for one. */
.ledger tr.ledger-total td {
  background: var(--parchment);
  border-top: 2px solid var(--line);
  font-weight: 800;
}

/* Card frames (gallery). Dark objects in a light room get real presentation:
   border, lift, and a hover raise. */
.card-frame {
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid var(--line);
  box-shadow: 0 6px 24px rgba(26, 28, 32, .16);
  transition: transform .18s ease, box-shadow .18s ease;
}
.card-frame:hover { transform: translateY(-3px); box-shadow: 0 10px 32px rgba(26, 28, 32, .22); }
.card-frame img { display: block; }
/* 240px keeps desktop cards readable; the 40vw floor keeps mobile at 2-up.
   Captions now live outside the frame as .card-caption (see below), so
   there is no .card-frame figcaption rule here any more. */
.card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(240px, 40vw), 1fr)); gap: 1.25rem; }
/* Browser default figure margins (1em 40px) squeeze the card image inside
   its frame and push captions past the edge. */
.card-grid figure { margin: 0; }
.card-grid img { width: 100%; }

/* Foil shimmer, guarded for reduced motion */
@media (prefers-reduced-motion: no-preference) {
  .shimmer { position: relative; }
  .shimmer::after {
    content: "";
    position: absolute; inset: 0;
    background: linear-gradient(115deg, transparent 30%, rgba(255, 240, 180, .35) 48%, transparent 62%);
    background-size: 260% 100%;
    animation: shimmer 3.2s ease-in-out infinite;
    pointer-events: none;
  }
  @keyframes shimmer { 0% { background-position: 120% 0; } 100% { background-position: -60% 0; } }
  .card-frame:hover { transform: translateY(-3px); }
}
@media (prefers-reduced-motion: reduce) {
  .card-frame, .card-frame:hover { transition: none; transform: none; }
}

/* Wide container + column split (design pass 2026-08-26). band-inner stays
   720px for prose; galleries and tables opt into the wide variant. .cols is
   the site's one desktop layout split; 900px is the only breakpoint. */
.band-inner--wide { max-width: 1080px; margin: 0 auto; }
.cols { display: grid; grid-template-columns: 1fr; gap: 2rem; align-items: center; }
@media (min-width: 900px) { .cols { grid-template-columns: 1fr 1fr; } }
/* Grid `fr` tracks floor at their content's intrinsic min-width, not 0.
   A wide child (like the 3-card fan) can force its whole column past its
   fair share and blow out the page's horizontal scroll, which the brand
   forbids. min-width: 0 removes that floor so tracks actually share space. */
.cols > * { min-width: 0; }

/* Section header as a labeled hairline (Scryfall pattern). Plain --line
   hairlines on purpose: the rarity gradient is masthead-only (brand rule). */
.rule-label {
  display: flex; align-items: center; gap: 1rem; margin: 0 0 1.5rem;
  text-transform: uppercase; font-size: .78rem; letter-spacing: .09em;
  color: var(--pewter-deep); font-weight: 700;
}
.rule-label::before, .rule-label::after { content: ""; flex: 1; height: 1px; background: var(--line); }

/* Stat strip: the game/set fact line as a quiet bordered chip. */
.stat-strip { display: inline-block; background: #fff; border: 1px solid var(--line); border-radius: var(--radius); padding: .5em 1em; }

/* Lens pills: the related-views row on game pages (Limitless tab pattern). */
.pills { display: flex; flex-wrap: wrap; gap: .5rem; margin: 1rem 0; padding: 0; list-style: none; }
.pills a {
  display: inline-block; background: #fff; border: 1px solid var(--line);
  border-radius: 999px; padding: .35em 1em; font-size: .88rem; font-weight: 600;
  text-decoration: none; color: var(--ink);
}
.pills a:hover { border-color: var(--sapphire); color: var(--sapphire); }
/* Scoped per band tone as reorder-proofing. A bare .pills a (0,1,1) TIES
   with the .band-dark a rule above (also 0,1,1) and wins only because it
   comes later in this file; move either rule and pills in a parchment band
   silently go sapphire instead of ink. The scoped pair below wins on
   specificity (0,2,1) regardless of order, so the outcome no longer depends
   on where anyone puts a rule. */
.band-light .pills a, .band-dark .pills a { color: var(--ink); }
.band-light .pills a:hover, .band-dark .pills a:hover { border-color: var(--sapphire); color: var(--sapphire); }

/* Unboxed card caption: one muted line under a card, outside the frame.
   Replaces the white figcaption boxes that fought the dark card art. */
.card-caption { margin: .5em 0 0; text-align: center; font-size: .82em; color: var(--muted-ink); }

/* Trophy tiles on /standings/ (spec 2026-08-17 §4, closed by this pass). */
.tiles { display: grid; grid-template-columns: 1fr; gap: 1.25rem; margin: 1.25em 0; }
@media (min-width: 900px) { .tiles { grid-template-columns: 1fr 1fr; } }
.tile {
  background: #fff; border: 1px solid var(--line); border-left: 4px solid var(--pewter);
  border-radius: var(--radius); padding: 1rem 1.25rem;
  box-shadow: 0 1px 3px rgba(26, 28, 32, .06);
}
.tile--foil { border-left-color: var(--foil); }
.tile h3 { margin: 0 0 .3em; text-transform: uppercase; font-size: .72em; letter-spacing: .07em; color: var(--pewter-deep); }

/* The season grid on /games/ (F1 schedule pattern, 2026-08-26): every game
   a uniform card, chronological. Played cards are white tiles with the
   podium; the NEXT game is the page's single felt card and carries its one
   lime RSVP (lime on felt only, per the brand rule); upcoming games are
   subdued schedule projections wearing the card-back motif. */
.season {
  list-style: none; margin: 1.25em 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 1.25rem;
}
.season-card {
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 1px 3px rgba(26, 28, 32, .06); padding: 1.1rem 1.25rem;
}
.season-date { margin: 0 0 .6em; font-weight: 800; letter-spacing: -.02em; font-size: 1.3rem; }
.season-date a { color: inherit; text-decoration: none; }
.season-date a:hover { color: var(--sapphire); }
.podium { list-style: none; margin: 0 0 .6em; padding: 0; }
.podium li { padding: .15em 0; }
.podium .stat { color: var(--muted-ink); }
.season-links a { font-weight: 600; }
/* The one glowing card: felt, like the CTA bands, so its lime is at home.
   No rarity metals on this card (the lime quarantine keeps them apart). */
.season-card--next { background: var(--felt); color: var(--felt-ink); border-color: var(--felt); }
/* Band rules (.band-light p, .band-light .stat) outrank inherited card color,
   the same cascade trap the pills hit; scope the felt card's text explicitly. */
.band-light .season-card--next p { color: var(--felt-ink); }
.band-light .season-card--next .stat { color: var(--pewter); }
.season-card--next .eyebrow { color: var(--lime); }
.season-card--next .season-links a { color: #9dc3f0; }
/* Upcoming: quieter paper, the unopened pack. */
.season-card--upcoming { background: var(--parchment); box-shadow: none; }
.season-card--upcoming .season-date { color: var(--muted-ink); }
.season-card--upcoming .card-back { margin: .2em 0 .6em; }

/* Set rows on /cards/: name left, six thumbnails right. */
.set-rows { list-style: none; margin: 1.25em 0; padding: 0; display: grid; gap: 1.5rem; }
.set-row {
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 1px 3px rgba(26, 28, 32, .06); padding: 1.25rem; display: grid; gap: 1rem;
}
@media (min-width: 900px) { .set-row { grid-template-columns: 1fr auto; align-items: center; } }
/* Seven thumbs per row, at most. The row is a grid of text | strip, and the
   strip column is sized by its content, so a fourteen-card set (Set 3,
   2026-09-08) would otherwise stretch across the row and squeeze the title
   into a three-line wrap. Capped at seven, it wraps into two even rows on the
   right, the same width the six- and seven-card sets already take. */
.thumb-strip { display: flex; flex-wrap: wrap; gap: .5rem; max-width: calc(7 * 72px + 6 * .5rem); }
@media (min-width: 900px) { .thumb-strip { justify-content: flex-end; } }
.thumb-strip img { width: 72px; border-radius: 4px; border: 1px solid var(--line); }
/* Card back for an unshipped set: felt with a single gold top strip (a
   background strip, not border-image, which breaks on one edge; and not the
   full rarity gradient, which is masthead-only). */
.card-back {
  display: grid; place-items: center; width: 72px; aspect-ratio: 5 / 7;
  border-radius: 4px; padding: .25rem; text-align: center;
  background: linear-gradient(var(--foil), var(--foil)) left top / 100% 3px no-repeat, var(--felt);
  color: var(--felt-ink); font-size: .6rem; text-transform: uppercase; letter-spacing: .05em;
}

/* Game-page results tables: the Notes column wraps, everything else stays
   on one line. Without this the off-screen Notes cell inflates every row's
   height on mobile. Opt-in variant; the standings table has no prose column. */
.ledger--notes td:not(:last-child) { white-space: nowrap; }
.ledger--notes td:last-child { min-width: 18em; }

/* Drawn marks: rarity gems, the Hope Coin. Chrome is drawn, never emoji.
   Use the -deep metal variants (foil/copper/pewter), not the plain ones:
   these marks sit on the light bands (paper/parchment) and the plain foil
   fill measures 2.14:1 there, well under WCAG 1.4.11's 3:1 floor for
   graphical objects. The :root comment already says the -deep variants
   exist for this; this is that promise kept. Sapphire alone stays plain
   (5.88:1 on parchment, passes on its own). */
.mark { display: inline-block; vertical-align: -1px; }
.mark--foil { fill: var(--foil-deep); }
.mark--sapphire { fill: var(--sapphire); }
.mark--copper { fill: var(--copper-deep); }
.mark--pewter { fill: var(--pewter-deep); }
.mark--empty { fill: none; stroke: var(--pewter-deep); stroke-width: 1.2; }
.coin-ring { fill: none; stroke: var(--foil-deep); stroke-width: 1.5; }
.coin-core { fill: var(--foil-deep); }

/* Current page in the masthead. Pages set aria-current="page" on their own
   nav link (generators emit it; committed pages carry it by hand). */
nav.band-dark a[aria-current="page"] { color: var(--sapphire); border-bottom: 2px solid var(--sapphire); }

/* Holo effect (driven by /holo.js writing --hx/--hy). Foil card only:
   scarcity of the effect is what makes the foil mean something. Guarded for
   reduced motion the same way the shimmer above is, but independent of it:
   nothing in this block needs .shimmer on the element. Without JS or with
   reduced motion nothing here fires. */
@media (prefers-reduced-motion: no-preference) {
  .card-frame--holo { --hx: .5; --hy: .5; position: relative; }
  .card-frame--holo.is-holo {
    will-change: transform;
    transition: none;
    transform: perspective(700px)
      rotateY(calc((var(--hx) - .5) * 16deg))
      rotateX(calc((.5 - var(--hy)) * 16deg));
  }
  /* Both overlay layers below (::before glare, ::after rainbow) cover the
     whole frame box, so a .card-frame--holo must hold only the <img>: any
     figcaption inside the frame would sit under both washes. Captions
     belong outside the frame (the unboxed card-caption lines), and
     tools/site.test.ts pins that. */
  .card-frame--holo::before {
    content: ""; position: absolute; inset: 0; z-index: 1;
    pointer-events: none; opacity: 0;
    background: radial-gradient(circle at calc(var(--hx) * 100%) calc(var(--hy) * 100%),
      rgba(255, 255, 255, .45), transparent 55%);
    mix-blend-mode: screen;
  }
  .card-frame--holo.is-holo::before { opacity: 1; }
  .card-frame--holo.is-holo::after {
    /* The next line looks like a duplicate of .shimmer::after (styles.css,
       "Foil shimmer") and is deliberate: this rule owns its own content box,
       so it no longer requires .shimmer on the element. Never delete it as
       redundant. On a .card-frame--holo that is not also .shimmer there is no
       ::after box to inherit, and the whole rainbow sweep would not paint. */
    content: ""; position: absolute; inset: 0; pointer-events: none;
    animation: none;
    background: linear-gradient(115deg, transparent 20%,
      rgba(201, 162, 39, .25) clamp(20%, calc(var(--hx) * 100% - 15%), 80%),
      rgba(43, 93, 158, .25) clamp(20%, calc(var(--hx) * 100%), 80%),
      rgba(176, 108, 63, .25) clamp(20%, calc(var(--hx) * 100% + 15%), 80%),
      transparent 80%);
    mix-blend-mode: color-dodge;
  }
}

/* Hero card fan (design pass 2, 2026-08-26). Committed card PNGs fanned in
   the felt hero: compact strip above the heading on mobile, full fan beside
   the copy on desktop (the .cols split reorders it right). Static art on
   purpose: the foil band below keeps the shimmer and holo exclusively. */
.card-fan { display: flex; justify-content: center; align-items: center; margin: 0 0 1rem; }
.card-fan img {
  position: relative; max-height: 96px; width: auto; border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, .08);
  box-shadow: 0 6px 24px rgba(0, 0, 0, .5);
}
.card-fan img:nth-child(1) { transform: rotate(-8deg) translateX(12px); }
.card-fan img:nth-child(2) { z-index: 1; }
.card-fan img:nth-child(3) { transform: rotate(8deg) translateX(-12px); }
@media (min-width: 900px) {
  .card-fan { order: 2; margin: 0; }
  /* clamp, not a flat 340px: between 900px and roughly 1200px the fan's
     column is narrower than three 340px-tall cards need, and the fr-floor
     fix above only stops the blowout, not the squeeze, so the cards must
     shrink with the viewport (24vw) down to a 180px floor. */
  .card-fan img { max-height: clamp(180px, 24vw, 340px); }
}

/* News tiles: the standings tile grammar at three-up on desktop. */
@media (min-width: 900px) { .tiles--3 { grid-template-columns: 1fr 1fr 1fr; } }

/* The Hope Coin's odometer tiles (/hope-coin/, Task 6, #48): the same
   .tiles grammar at four-up on desktop, same modifier pattern as
   .tiles--3 above. */
@media (min-width: 900px) { .tiles--4 { grid-template-columns: 1fr 1fr 1fr 1fr; } }

/* Eyebrow label, shared voice with .tile h3 and the table headers. */
.eyebrow {
  margin: 0 0 .4em; text-transform: uppercase; font-size: .72em;
  letter-spacing: .07em; color: var(--pewter-deep); font-weight: 700;
}

/* The bounty board (the pass's signature, spec §2): typed notices pinned
   to the parchment band. The rotations are the site's first tilted
   objects; they are static transforms, not motion, so they stay outside
   the reduced-motion guards on purpose. */
.notices { display: grid; grid-template-columns: 1fr; gap: 1.75rem; margin: 1.5em 0; }
@media (min-width: 900px) { .notices { grid-template-columns: 1fr 1fr; } }
.notice {
  position: relative; background: #fff; border: 1px solid var(--line);
  border-radius: 4px; padding: 2rem 1.5rem 1.25rem;
  box-shadow: 0 8px 24px rgba(26, 28, 32, .14);
}
/* The pushpin: a drawn dot on the top edge (drawn chrome, never emoji). */
.notice::before {
  content: ""; position: absolute; top: -9px; left: 50%; margin-left: -9px;
  width: 18px; height: 18px; border-radius: 50%;
  box-shadow: inset 0 -2px 3px rgba(0, 0, 0, .25), 0 2px 3px rgba(26, 28, 32, .3);
}
.notice--copper::before { background: var(--copper); }
.notice--foil::before { background: var(--foil); }
.notice--tilt-l { transform: rotate(-1.2deg); }
.notice--tilt-r { transform: rotate(1deg); }
.notice .eyebrow {
  font-family: "SF Mono", ui-monospace, Menlo, Consolas, monospace;
  letter-spacing: .12em;
}
.notice h3 { margin: 0 0 .5em; font-weight: 800; letter-spacing: -.02em; font-size: 1.3rem; }
/* Reward line: label voice (uppercase is reserved for eyebrows and table
   headers; a reward line is a label, set in the mono stat face). */
.reward {
  margin: 1.2em 0 0; padding-top: .8em; border-top: 1px dashed var(--line);
  font-family: "SF Mono", ui-monospace, Menlo, Consolas, monospace;
  text-transform: uppercase; font-size: .8rem; letter-spacing: .08em;
}
.notice ul.fact { list-style: none; margin: .5em 0 0; padding: 0; }

/* Runtime fact slots (filled by /home-facts.js): invisible until filled so
   the no-JS page never shows an empty-looking hole. */
.fact:empty { display: none; }

/* Chip race, embedded on the game page (design decision 2026-08-26: the
   chart lives ON the game page, restoring spec 2026-08-17 §7; the old
   standalone chip-race.html URLs redirect via _redirects). The figure is a
   dark object in the light room, same doctrine as the card art. Markup comes
   from tools/chip-race.ts --inject; these rules style it. */
.chip-race {
  margin: 1.25em 0; padding: 1rem 1.25rem;
  background: #1c1f26; color: #eceae2;
  border: 1px solid var(--line); border-radius: var(--radius);
}
.chip-race .wrap { overflow-x: auto; }
.chip-race svg { display: block; min-width: 560px; width: 100%; height: auto; }
.chip-race .meta {
  margin: 0 0 .75em;
  font-family: "SF Mono", ui-monospace, Menlo, Consolas, monospace;
  font-size: .85em; color: var(--pewter);
}
.chip-race .chip-legend {
  display: flex; flex-wrap: wrap; gap: .4em 1.2em;
  margin: .75em 0 0; font-size: .85em; font-weight: 600;
}
.chip-race .swatch {
  display: inline-block; width: 10px; height: 10px; border-radius: 2px;
  margin-right: .45em; vertical-align: -1px;
}
/* The chart's time axis, merge marker, and region labels (#54, 2026-09-09:
   the first two-table game). The SVG text and marker lines carry no
   presentation attributes of their own; size and ink live here only, because
   a stylesheet rule beats an attribute and two sources drift apart (the
   Hope Coin route learned this the hard way, 2026-09-07). */
.chip-race .tick line { stroke: var(--pewter); }
.chip-race .tick text, .chip-race .merge text, .chip-race .region {
  font-family: "SF Mono", ui-monospace, Menlo, Consolas, monospace;
  font-size: 11px; fill: var(--pewter);
}
.chip-race .merge line { stroke: var(--foil); stroke-dasharray: 5 4; }
.chip-race .merge text { fill: var(--foil); }
.chip-race .region { font-size: 10px; text-transform: uppercase; letter-spacing: .08em; }
/* A dashed line's legend swatch (the eighth player onward reuses a color
   with a dashed stroke). The inline background color stays; the mask cuts
   it into dashes to match the stroke, so the key reads the same as the line. */
.chip-race .swatch--dashed {
  -webkit-mask-image: repeating-linear-gradient(90deg, #000 0 3px, transparent 3px 5px);
  mask-image: repeating-linear-gradient(90deg, #000 0 3px, transparent 3px 5px);
}
/* Hover: the field dims ONLY while an actual line is under the pointer
   (:has), and that line pops. The first cut dimmed on any svg hover, which
   read as the whole chart darkening for no reason (Mike, 2026-08-26).
   Browsers without :has simply get the line pop without the dim. Hover-
   capable devices only; a hover state is not motion, no reduced-motion gate. */
@media (hover: hover) {
  .chip-race svg:has(polyline:hover) polyline { opacity: .35; }
  .chip-race svg polyline:hover { opacity: 1; stroke-width: 4; }
}

/* Skull marks (Mike, 2026-08-26: skulls should look like skulls). Drawn in
   the house style, never emoji (renders identically everywhere); filled =
   a Hope Slayer kill taken, outline = an open slot. Bone-iron pewter-deep
   clears WCAG 1.4.11 on the light bands; sockets punch through in paper. */
.mark--skull { fill: var(--pewter-deep); }
.mark--skull .socket { fill: var(--paper); }
.mark--skull-empty { fill: none; stroke: var(--pewter); stroke-width: 1.1; }

/* Trophy shapes (player pages, spec 2026-09-02 §4.3): "a new mark shape is
   one SVG constant [in tools/render.ts] and one CSS class [here]". Shield
   and ribbon carry shape only, no fill or stroke of their own: a trophy's
   colour comes from a metal class on the same element (mark mark--shield
   mark--sapphire), and its locked state is the shared .mark--empty outline
   above, so baking a fill in here would fight one of those two classes on
   every trophy tile depending on which was written later in the cascade.
   Pinned to the same 12x12 box every other mark uses (skull, coin, the
   empty diamond) even though neither shield nor ribbon is round, so the
   tight gap in .shelf and the icon column in .trophy don't have to special-
   case one shape; the SVG constants that draw them need no width/height
   of their own, the same way .chip-race svg already lets CSS size an <svg>
   instead of its markup. */
.mark--shield { width: 12px; height: 12px; }
.mark--ribbon { width: 12px; height: 12px; }

/* Trophy case (/player/<slug>/, spec §5.1 step 3): a denser .tiles grid --
   trophy tiles hold one mark plus two short lines, not a stat block, so
   three fit per row on desktop where standings tiles fit two (the
   .tiles--3 grammar above, just under a different name because these tiles
   are never mixed into a plain .tiles grid). 900px stays the only
   breakpoint, per the .cols rule elsewhere in this file. */
.trophy-case { display: grid; grid-template-columns: 1fr; gap: 1rem; margin: 1.25em 0; }
@media (min-width: 900px) { .trophy-case { grid-template-columns: 1fr 1fr 1fr; } }
.trophy {
  display: flex; align-items: flex-start; gap: .7rem;
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
  padding: .8rem 1rem; box-shadow: 0 1px 3px rgba(26, 28, 32, .06);
}
.trophy .mark { flex: none; margin-top: .2em; }
/* These h3/p selectors assume tools/render.ts marks up a trophy tile's name
   as an <h3> and its earn line and count/date line as <p> elements, inside
   the .trophy-text block (since 2026-09-05; before that they were direct
   children of .trophy on one row). Nothing enforces the tag choice: if a
   future change marks up those lines with different tags, these rules
   silently style nothing, no test fails, and the text just renders unstyled.
   If that happens, fix it here by matching the tags actually used, not by
   inventing a new class. */
.trophy h3, .trophy p { margin: 0; }
.trophy h3 { font-size: .95em; font-weight: 700; }
.trophy p { font-size: .85em; color: var(--muted-ink); }
/* Locked reads as the unopened pack, the same quiet parchment
   .season-card--upcoming already uses for "not yet" states. The mark itself
   goes grey via .mark--empty on the SVG element, never here: this rule must
   not carry its own fill or stroke, or the two greys could drift apart the
   day only one of them gets edited. */
.trophy--locked { background: var(--parchment); box-shadow: none; }
.trophy--locked p { color: var(--pewter-deep); }

/* Standings Trophies column (spec §5.3): a dense run of earned marks in one
   table cell, capped at six with a "+N" overflow so one player's trophy
   count never wraps the whole row taller. */
.shelf { display: inline-flex; align-items: center; gap: .2em; vertical-align: -1px; }
.shelf-more {
  margin-left: .35em;
  font-family: "SF Mono", ui-monospace, Menlo, Consolas, monospace;
  font-size: .78em; color: var(--muted-ink);
}

/* Standings trophy legend (spec follow-up 2026-09-03, beneath the ledger
   the .shelf column above sits in): a quiet key pairing all fifteen
   registry marks with their names, generated by tools/render.ts
   trophyLegend() straight off TROPHIES so a sixteenth trophy needs no
   second edit here. Fifteen items on an already dense page is meant to
   read as one small-print key, not a second table - a plain wrapping
   inline list, muted text, no border or background of its own, the same
   "quiet, not a card" restraint .card-caption already uses below. Marks
   keep their own colors (the metals are the accent, same as everywhere
   else on the site); this rule adds no color of its own and never reaches
   for --lime, which is reserved for the one RSVP CTA. */
.trophy-legend {
  list-style: none; margin: 1.5em 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: .35em 1.25em;
  font-size: .82em; color: var(--muted-ink);
}
.trophy-legend li { display: inline-flex; align-items: center; gap: .4em; }

/* Card gallery (/player/<slug>/, spec §5.1 step 2): one figure per set a
   player was carded in. A fixed basis, not .card-grid's auto-fill/minmax
   grid, because a player owns at most a handful of cards; auto-fill reserves
   phantom empty tracks up to the container width, which would strand a
   lone card in a narrow column with dead space beside it instead of letting
   it sit at a natural size. Figure margin reset for the same reason
   .card-grid resets it: browser default figure margins (1em 40px) squeeze
   the frame inside itself and push the caption past the edge. */
.card-gallery { display: flex; flex-wrap: wrap; gap: 1.5rem; margin: 1.25em 0; }
.card-gallery figure { margin: 0; width: min(240px, 40vw); display: flex; flex-direction: column; }
.card-gallery img { width: 100%; }
/* Sets are not all the same height (Set 3 grew 35px when its counter box
   went to three lines, 2026-09-20), so a player carded in two sets gets two
   heights side by side. The row already stretches every figure to the
   tallest; pushing the caption to the bottom keeps the captions on one line
   instead of leaving the shorter card's caption hanging 12px higher. */
.card-gallery figcaption { margin-top: auto; }

/* The Hope Coin's route (/hope-coin/, spec §5.2): a vertical line with each
   holder's stop hanging off it, oldest at the top. A plain hairline
   (--line), not a rarity metal, because this line is structure, not a
   rarity signal (the "gold means something" rule above the rarity accents
   applies here too). */
.route { position: relative; list-style: none; margin: 1.5em 0; padding: 0; }
.route::before {
  content: ""; position: absolute; top: .3em; bottom: .3em; left: 5px;
  width: 2px; background: var(--line);
}
.route-stop { position: relative; margin: 0 0 1.5rem; padding-left: 2rem; }
.route-stop:last-child { margin-bottom: 0; }
/* The bead on the line: paper-filled so the hairline shows through an
   unfilled ring, the same "hollow means not-here" reading .mark--empty
   already uses for a locked mark. */
.route-stop::before {
  content: "";
  position: absolute; left: 0; top: .3em; width: 12px; height: 12px;
  border-radius: 50%; background: var(--paper); border: 2px solid var(--pewter-deep);
}
/* The present holder: filled foil bead, bold text. Foil, not sapphire, on
   purpose -- the coin itself is drawn in foil (.coin-ring/.coin-core above)
   and the current stop should read as the same metal as the trophy it
   marks. */
.route-stop--current { font-weight: 800; }
.route-stop--current::before { background: var(--foil-deep); border-color: var(--foil-deep); }

/* Leg labels between journey stops (Task 6, #48, M3): a distance line, not
   another stop on the route. padding-left matches .route-stop's own so the
   figure's text lines up with stop text on the same spine; the negative
   top margin pulls it in close under the stop above rather than reading as
   its own paragraph-spaced row, and the positive bottom margin keeps the
   stop below it at the route's normal 1.5rem rhythm. */
.route-leg { padding-left: 2rem; margin: -0.75rem 0 0.75rem; }
/* No bead: a leg is a distance, not a place the Coin stopped, so it draws
   none of .route-stop::before's ring on the spine. */
.route-leg::before { display: none; }

/* The stint lines (Task 7, #48, spec §5; a line since 2026-09-07): a road
   trip drawn as one straight line inside the stop it belongs to, a tick
   per place. It shipped as a loop, and Beau, who drove both routes, asked
   for a line when he read the live page. Sized here, never by width/height
   attributes on the svg itself, so every drawing on the page changes size
   together. max-width keeps a nine-name route from stretching its text
   into a headline on a wide screen.
   min-width is the phone half of that (whole-branch review finding 1,
   2026-09-05): a drawing squeezed into a 343px phone column drew its
   place names at four pixels, so below 480px the drawing keeps its size
   and its own .route-scroll box pans instead. The page body still never
   scrolls sideways, because that overflow stops at the scroll box. */
.route-line { display: block; width: 100%; height: auto; max-width: 540px; min-width: 480px; }
/* The drawing's scroll box: .table-scroll supplies the panning and the
   card treatment, and this class supplies only the spacing a drawing
   inside a journey stop needs, which is tighter than a full-width ledger
   table's. */
.route-scroll { margin: .6rem 0 .2rem; }
/* The line itself is a drawn stroke, never a filled shape: light page,
   dark objects (docs/brand.md). Pewter, not a rarity metal, for the same
   reason .route's own spine is a hairline -- this is structure, not a
   signal. */
.route-line-path { fill: none; stroke: var(--pewter-deep); stroke-width: 1.5; }
/* One filled bead per place the Coin rode through, matching the pewter
   ring .route-stop::before already draws on the spine. */
.route-tick circle { fill: var(--pewter-deep); }
/* The one border crossing. Foil is the page's "this one is different"
   accent (the coin itself is drawn in foil above), and it is spent on
   exactly one tick in the whole journey. */
.route-flag { fill: var(--foil-deep); }
/* Place names in the same small monospace face .stat uses. No font-size
   here, on purpose: tools/render.ts emits a font-size attribute on every
   text in the drawing, scaled to that drawing's own viewBox width so a
   wide route and a narrow one print their names at the same size (see
   LINE_REF_WIDTH there), and a stylesheet rule beats an SVG presentation
   attribute whatever the attribute says. An 11px rule sat here from
   2026-09-05 to 2026-09-07 and pinned every name to 11 user units, so
   Beau's Alaska route printed at seven pixels while the renderer's
   attribute was ignored. tools/render.test.ts guards both halves: every
   text carries the attribute, and neither rule here carries a size. */
.route-tick text { font-family: "SF Mono", ui-monospace, Menlo, Consolas, monospace; fill: var(--ink); }
/* The stint's mileage, under the line at its right end. Same face as the
   names, sized the same way, muted so the places read first. */
.route-line-miles { font-family: "SF Mono", ui-monospace, Menlo, Consolas, monospace; fill: var(--muted-ink); }

/* The coin's own photo frame (Task 5, #48): the hero at the top of
   /hope-coin/ (site/hope-coin/assets/coin.png), and the frame that later
   infographics on this page reuse. Copies .card-frame's border and
   box-shadow -- the same "dark object gets real presentation" look every
   card gets -- but circular, not rounded-rectangle, because the coin itself
   is round, and with no hover lift: the hero sits at the top of the page on
   its own, not in a clickable gallery, so nothing about it needs to react
   to a pointer. */
.coin-frame {
  border-radius: 50%;
  overflow: hidden;
  border: 1px solid var(--line);
  box-shadow: 0 6px 24px rgba(26, 28, 32, .16);
}
.coin-frame img { display: block; width: 100%; height: auto; }
/* Browser default figure margin (1em 40px) would push the coin off-center
   in its half of the .cols grid and shove the caption past the edge, the
   same reason .card-grid figure { margin: 0 } exists above. */
.coin-figure { margin: 0; }

/* Trophy tile text block (2026-09-05): a tile is mark + .trophy-text, and the
   text block stacks name, earn line, and (earned only) the count/date line,
   so the earn line can show on EVERY tile without crowding the single row
   the tile used to be. .trophy h3 / .trophy p above still style the lines. */
.trophy-text { display: grid; gap: .15em; min-width: 0; }
.trophy-earn { line-height: 1.35; }
.trophy .stat { font-size: .78em; }

/* Card zoom (site/card-zoom.js): select a card, see it big. The script adds
   .card-frame--zoomable to every frame it wires and appends ONE
   <dialog class="card-zoom"> to the body. Paper scrim, not a dark room: the
   page stays light and the card stays the dark object (docs/brand.md). Lime
   never appears here; the only lime on any page is the RSVP button on felt. */
.card-frame--zoomable { cursor: zoom-in; }
.card-frame--zoomable:focus-visible { outline: 3px solid var(--sapphire); outline-offset: 3px; }
.card-zoom { border: 0; padding: 0; background: transparent; max-width: 100vw; max-height: 100vh; overflow: visible; }
/* --paper (#fdfcf9) at .93. Written out rather than var(--paper) because
   ::backdrop sits outside the document tree and older engines do not let it
   read custom properties; a literal paints the same everywhere. */
.card-zoom::backdrop { background: rgba(253, 252, 249, .93); }
/* The art is 676px wide at full resolution, so never scale it past that.
   Height leaves room for the caption line and the Close button underneath. */
.card-zoom img {
  display: block; width: auto; height: auto;
  max-width: min(92vw, 676px); max-height: calc(100vh - 9rem);
  border-radius: 12px; box-shadow: 0 24px 64px rgba(26, 28, 32, .32);
}
.card-zoom__caption { margin: .75em 0 0; text-align: center; font-size: .9em; color: var(--muted-ink); }
.card-zoom__close {
  display: block; margin: .75em auto 0;
  background: transparent; color: var(--ink); border: 1px solid currentColor;
  border-radius: 8px; padding: .55em 1.4em; font: inherit; font-weight: 600; cursor: pointer;
}
@media (prefers-reduced-motion: no-preference) {
  .card-zoom[open] { animation: card-zoom-in .18s ease; }
  @keyframes card-zoom-in { from { opacity: 0; transform: scale(.96); } }
}
/* The cards index's unminted row carries the RSVP link (Mike, 2026-09-04);
   this gives it air under the copy line above it. */
.set-row .btn-secondary { margin-top: .25em; }

/* "Who has held it" (Task 8, #48, spec section 7): the donut, the tenure
   strip, and the legend beneath the Hope Coin journey. Light page, dark
   objects (docs/brand.md): both drawings are ink, with the current holder
   in foil. Neither carries a fill in CSS at all - every arc and segment
   writes its own fill and fill-opacity as an attribute, because which
   holder is foil and how deep each tint runs is decided by the share order
   in tools/render.ts, and a stylesheet cannot know that order.

   .tenure-legend itself is styled by the .ledger rules above, which name it
   in their own selectors rather than carrying a second copy of the same
   numbers here: the legend is a ledger table, and the two must never drift
   apart. It is not spelled class="ledger tenure-legend" in the markup
   because .ledger's finish-1 and ledger-total rules are about a game's
   results, and a holder's tenure is not a finish. */

/* Both svgs are sized here, never by width/height attributes on the
   element, so one change resizes every copy. max-width keeps the donut from
   inflating into a wall decoration on a wide screen and the strip from
   stretching its year labels apart. */
/* The donut's viewBox is 200 tall and as wide as the labels need (see
   holdersSection in tools/render.ts), so this max-width is chosen for the
   labels rather than the ring: at 400px a 13-character name still reads at
   roughly 10px on screen. */
.coin-donut { display: block; width: 100%; height: auto; max-width: 400px; margin: 0 auto; }
.tenure-strip { display: block; width: 100%; height: auto; max-width: 620px; margin: 0 0 .5rem; }
/* Browser default figure margin (1em 40px) would push the donut off-center
   in its half of the .cols grid, the same reason .coin-figure above sets
   this. */
.donut-figure { margin: 0; }
/* Labels on both drawings use the small monospace face .stat uses, at a
   fixed pixel size: the svgs scale by their viewBox, so an em-based size
   would shrink and grow with the drawing instead of staying legible. 8px
   on the donut is the size tools/render.ts measures its labels against
   (DONUT_CHAR); changing one without the other slides them off-center. */
.donut-label { font-family: "SF Mono", ui-monospace, Menlo, Consolas, monospace; font-size: 8px; fill: var(--ink); }
/* The strip's years. 19, not 11, because this drawing is NOT scaled up the
   way the donut is: its 600-unit box renders around 340px wide on a desktop
   and on a phone alike, so 11 units printed a year at six pixels
   (whole-branch review finding 2, 2026-09-05). STRIP_H in tools/render.ts
   was raised to 48 in the same change to seat this type under the bar. */
.strip-tick { font-family: "SF Mono", ui-monospace, Menlo, Consolas, monospace; font-size: 19px; fill: var(--muted-ink); }
/* The January hairline: a drawn line, pewter like every other piece of
   structure on this page, never a rarity metal (a year boundary is not a
   signal, it is a ruler mark). */
.strip-tick-line { stroke: var(--pewter-deep); stroke-width: 1; }

/* "What would you have done?" (Task 7, #70, spec section 4.4): the puzzle
   table, the action controls, and the reveal. Generated pages under
   site/wwyhd/ reach for these names; the controller at site/wwyhd.js adds
   the two state classes (.wwyhd-seat--acting, .wwyhd-differs) as the hand
   goes.

   Nothing below reaches for the lime custom property. The one primary call
   to action on this site is RSVP's, and a puzzle page has none, which is
   why every button here is .btn-secondary (docs/brand.md, "Lime is
   quarantined"). */

/* The puzzle page is one screen, the way every trainer lays a hand out: a
   short title line, then the table filling the width, then the action bar
   under it, and the hand history folded into a disclosure below that. The
   table is a felt oval (the one near-black object on the page, the card-art
   precedent) inside a parchment rail. Seats are small pucks on the rail in
   preflop action order, the visitor's at the bottom center and the rest
   clockwise from it; each puck's place comes from the renderer as
   --seat-x/--seat-y, percentages of the table box, clamped here so no puck
   hangs off the edge on a narrow screen. Cards are drawn as cards (.pc):
   rank and suit on a white face, red hearts and diamonds, a sapphire back
   for a holding the visitor cannot see. On a phone the oval stands taller
   than wide so seven pucks fit around it; from 640px it lies flat. */
.wwyhd-page .band-inner { max-width: 840px; }
.wwyhd-title { font-size: clamp(1.25rem, 3vw, 1.6rem); font-weight: 800; letter-spacing: -.01em; margin: 0 0 .1em; }
.wwyhd-setup { margin: 0 0 .75rem; color: var(--muted-ink); font-size: .95rem; }
.wwyhd-cue { margin: 0 0 .35rem; font-weight: 700; }
.wwyhd-page .wwyhd-sit { margin: 0 0 1rem; }

.wwyhd-table {
  position: relative; margin: 0 auto; max-width: 520px; aspect-ratio: 3 / 4;
  background: var(--parchment); border: 1px solid var(--line); border-radius: 50% / 50%;
}
.wwyhd-felt {
  position: absolute; inset: 19% 21%; border-radius: 50% / 50%;
  background: var(--felt); color: var(--felt-ink);
  display: grid; place-items: center; align-content: center; gap: .35rem;
  box-shadow: inset 0 0 0 5px rgba(255,255,255,.06);
}
/* Scoped under the felt for the same reason as the caption below: the
   band's `.band-light .stat` muted ink outranks a bare class, and muted ink
   on the black felt is unreadable (Mike, 2026-09-22). */
.wwyhd-felt .wwyhd-center { margin: 0; color: var(--felt-ink); font-size: .85rem; text-align: center; }
.wwyhd-board { margin: 0; display: flex; gap: .25rem; min-height: 3rem; }

.wwyhd-seats { list-style: none; margin: 0; padding: 0; position: absolute; inset: 0; }
.wwyhd-seat {
  position: absolute;
  left: clamp(3.4rem, var(--seat-x), calc(100% - 3.4rem));
  top: clamp(2.6rem, var(--seat-y), calc(100% - 2.6rem));
  transform: translate(-50%, -50%); width: 6.6rem; padding: .3em .45em;
  text-align: center; font-size: .78rem; line-height: 1.25;
  background: var(--paper); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 1px 2px rgba(26,28,32,.12);
}
.wwyhd-seat p { margin: 0; }
/* The visitor's seat wears a green ring and glow so there is never a question
   which seat is theirs (Mike, 2026-09-21). Green, not lime: lime is the RSVP
   button's and nothing else's. The seat to act gets the sapphire ring, and
   when that is the visitor the green stays and the glow widens. */
.wwyhd-seat--you {
  border-color: var(--seat-green); width: 8.6rem;
  box-shadow: 0 0 0 3px var(--seat-green), 0 0 18px 4px rgba(44, 160, 96, .45);
}
.wwyhd-seat--acting:not(.wwyhd-seat--you) { box-shadow: 0 0 0 3px var(--sapphire); }
.wwyhd-seat--acting.wwyhd-seat--you { box-shadow: 0 0 0 3px var(--seat-green), 0 0 28px 8px rgba(44, 160, 96, .6); }
.wwyhd-seat--folded { opacity: .4; }
.wwyhd-seat--folded .wwyhd-hole { visibility: hidden; }
.wwyhd-pos-line { display: flex; justify-content: center; align-items: center; gap: .35em; }
.wwyhd-pos { font-size: .6rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--muted-ink); }
.wwyhd-pos-line .eyebrow { margin: 0; font-size: .6rem; color: var(--seat-green); }
.wwyhd-button {
  display: inline-grid; place-items: center; width: 1.3em; height: 1.3em; border-radius: 50%;
  background: #fff; color: var(--ink); border: 1.5px solid var(--ink); font-size: .65rem; font-weight: 800;
}
.wwyhd-name { font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wwyhd-stack { font-size: .72rem; }
.wwyhd-hole { display: flex; justify-content: center; gap: .2rem; margin-top: .25em !important; }
.wwyhd-front { position: absolute; left: 50%; top: -1.1em; transform: translateX(-50%); }
.wwyhd-blind {
  display: inline-block; padding: .05em .5em; border-radius: 999px; background: var(--pewter); color: #fff;
  font: 700 .66rem "SF Mono", ui-monospace, Menlo, Consolas, monospace;
}
@media (min-width: 640px) {
  .wwyhd-table { max-width: 840px; aspect-ratio: 5 / 3; }
  .wwyhd-felt { inset: 16% 15%; }
  .wwyhd-seat { width: 8.2rem; font-size: .85rem; }
  .wwyhd-seat--you { width: 11rem; }
}

/* A playing card. The seat pucks draw them small; the visitor's own seat,
   the board and the reveal draw them full size. */
.pc {
  display: inline-grid; place-items: center; width: 2.1rem; height: 2.9rem; border-radius: 5px;
  background: #fff; border: 1px solid var(--line); color: var(--ink); font-weight: 800; line-height: 1;
  box-shadow: 0 1px 1px rgba(0,0,0,.15);
}
.pc b { font-size: 1rem; }
.pc i { font-style: normal; font-size: .95rem; }
.pc--h, .pc--d { color: #b3261e; }
.pc--down {
  background: repeating-linear-gradient(45deg, var(--sapphire) 0 3px, #1f4577 3px 6px); border-color: #1f4577;
}
.wwyhd-seat .pc { width: 1.45rem; height: 2rem; }
.wwyhd-seat .pc b { font-size: .78rem; }
.wwyhd-seat .pc i { font-size: .7rem; }
.wwyhd-seat--you .pc { width: 2.1rem; height: 2.9rem; }
.wwyhd-seat--you .pc b { font-size: 1rem; }
.wwyhd-seat--you .pc i { font-size: .95rem; }

/* The paced hand (Mike, 2026-09-22, after Beau asked for a dramatic pause).
   site/wwyhd.js writes the caption, deals the hole cards in one at a time,
   turns live cards over at a showdown and marks whoever won the pot; these
   are the looks for each. Every animation here is decoration only: the
   controller's timeline decides when things happen, so switching them all
   off (reduced motion, below) changes how the hand looks and nothing else. */
/* Scoped under the felt on purpose: `.band-light p` paints every paragraph
   on the page in ink and outranks a bare class, which put the caption in
   near-black on the black felt (caught 2026-09-22 in the first run). */
.wwyhd-felt .wwyhd-caption {
  margin: 0; min-height: 1.3em; max-width: 92%; text-align: center;
  font-weight: 800; font-size: .95rem; line-height: 1.25; color: #fff;
}
.wwyhd-caption--new { animation: wwyhd-caption-in .35s ease-out; }
@keyframes wwyhd-caption-in {
  from { opacity: 0; transform: translateY(.35rem); }
  to   { opacity: 1; transform: none; }
}
.wwyhd-hole .pc { transition: opacity .2s ease-out, transform .2s ease-out; }
.wwyhd-hole .pc--undealt { opacity: 0; transform: translateY(-.6rem) scale(.85); }
.pc--deal { animation: wwyhd-deal .35s ease-out; }
@keyframes wwyhd-deal {
  from { opacity: 0; transform: translateY(-.6rem) scale(.9); }
  to   { opacity: 1; transform: none; }
}
.pc--flip { animation: wwyhd-flip .4s ease-out; }
@keyframes wwyhd-flip {
  from { transform: rotateY(90deg); }
  to   { transform: none; }
}
/* An opponent's holding the log never showed, turned face up at the
   showdown: it says it was chosen for the puzzle, same as the reveal. */
.wwyhd-authored {
  font-size: .55rem; letter-spacing: .06em; text-transform: uppercase; color: var(--muted-ink);
}
/* Whoever the pot went to. Foil, not lime: lime is the RSVP button's. */
.wwyhd-seat--won:not(.wwyhd-seat--you) { box-shadow: 0 0 0 3px var(--foil), 0 0 18px 4px rgba(201, 162, 39, .5); }
.wwyhd-seat--won.wwyhd-seat--you { box-shadow: 0 0 0 3px var(--foil), 0 0 28px 8px rgba(201, 162, 39, .6); }
@media (min-width: 640px) {
  .wwyhd-felt .wwyhd-caption { font-size: 1.15rem; }
}
@media (prefers-reduced-motion: reduce) {
  .wwyhd-caption--new, .pc--deal, .pc--flip { animation: none; }
  .wwyhd-hole .pc { transition: none; }
}

/* The bar under the table: the two fields and Deal before the hand, the
   action buttons during it.

   Both blocks below set `display`, which beats the browser's own rule for
   the `hidden` attribute (a class selector outranks `[hidden]`), so each one
   needs its own `[hidden]` guard or the controller's `form.hidden = true`
   does nothing and the fields sit there while the hand plays. Mike saw
   exactly that on 2026-09-21. tools/styles.test.ts fails on a wwyhd rule
   that sets display without one. */
.wwyhd-sit[hidden], .wwyhd-controls[hidden] { display: none; }
.wwyhd-bar { margin: .75rem 0 0; }
.wwyhd-sit { display: flex; flex-wrap: wrap; gap: .5rem; align-items: end; margin: 0; }
.wwyhd-field { display: grid; gap: .15em; margin: 0; flex: 1 1 10rem; }
.wwyhd-field label { font-size: .68rem; text-transform: uppercase; letter-spacing: .08em; color: var(--muted-ink); }
.wwyhd-field input {
  font: inherit; padding: .45em .6em; border: 1px solid var(--line); border-radius: 8px;
  background: #fff; color: var(--ink); width: 100%; box-sizing: border-box;
}
.wwyhd-field input:focus-visible { outline: 3px solid var(--sapphire); outline-offset: 2px; }
.wwyhd-field--go { flex: 0 0 auto; }
.wwyhd-sit-error { flex-basis: 100%; margin: 0; min-height: 1.2em; }
/* The action bar: size presets and the amount box in a small row above,
   three big equal buttons below. The commit button (Bet / Raise to) is the
   filled one; Fold and Check or Call are outlines. Never lime. */
.wwyhd-controls { display: grid; gap: .5rem; }
/* While the visitor has nothing to decide: what the table is doing, Skip
   beside it, or, once the pot is pushed, the one button to the reveal. */
.wwyhd-status { display: flex; align-items: center; justify-content: space-between; gap: .75rem; min-height: 3rem; }
.wwyhd-status .stat { margin: 0; }
.wwyhd-status .wwyhd-act { flex: 1; }
.wwyhd-actions { display: grid; gap: .5rem; }
.wwyhd-presets { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }
.wwyhd-preset {
  font: inherit; font-size: .8rem; padding: .3em .8em; border-radius: 999px;
  background: var(--parchment); color: var(--ink); border: 1px solid var(--line); cursor: pointer;
}
.wwyhd-preset:hover { border-color: var(--sapphire); color: var(--sapphire); }
.wwyhd-amount {
  font: inherit; font-size: .9rem; width: 6.5rem; padding: .3em .6em; margin-left: auto;
  border: 1px solid var(--line); border-radius: 8px; background: #fff; color: var(--ink);
}
.wwyhd-main { display: grid; grid-template-columns: 1fr 1fr 1.4fr; gap: .5rem; }
.wwyhd-main--two { grid-template-columns: 1fr 1fr; }
.wwyhd-act {
  font: inherit; font-weight: 700; padding: .85em .5em; border-radius: 10px; cursor: pointer;
  background: #fff; color: var(--ink); border: 1.5px solid var(--ink); min-height: 3rem;
}
.wwyhd-act:hover { border-color: var(--sapphire); color: var(--sapphire); }
.wwyhd-act--fold { color: var(--muted-ink); border-color: var(--line); }
.wwyhd-act--go { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.wwyhd-act--go:hover { background: var(--sapphire); border-color: var(--sapphire); color: #fff; }
.wwyhd-act:focus-visible, .wwyhd-preset:focus-visible { outline: 3px solid var(--sapphire); outline-offset: 2px; }
.wwyhd-history { margin: .75rem 0 0; font-size: .85rem; }
.wwyhd-history summary { cursor: pointer; color: var(--muted-ink); }
.wwyhd-log { margin: .4rem 0 0; padding-left: 1.2em; }
.wwyhd-fine { margin: .75rem 0 0; font-size: .75rem; color: var(--muted-ink); }

/* The reveal. */
.wwyhd-again { margin: 1.25rem 0 0; }
.wwyhd-holdings { list-style: none; margin: 0; padding: 0; display: grid; gap: .4rem; }
.wwyhd-holding { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.wwyhd-holding-who { min-width: 12rem; font-weight: 700; }
.wwyhd-holding-cards { display: inline-flex; gap: .2rem; }
.wwyhd-differs td { color: var(--copper); font-weight: 700; }
.wwyhd-you td { font-weight: 700; border-top: 2px solid var(--line); }

/* The index: one row per puzzle, newest first, the newest marked. */
.wwyhd-list { list-style: none; margin: 1.5rem 0; padding: 0; }
.wwyhd-row {
  padding: .9em 0; border-bottom: 1px solid var(--line);
}
.wwyhd-row:last-child { border-bottom: none; }
.wwyhd-row p { margin: 0 0 .2em; }
.wwyhd-row-title { font-weight: 700; font-size: 1.1em; }
.wwyhd-row--current .wwyhd-row-title { font-size: 1.25em; }
