:root {
  --paper: #FAF6F0;
  --card: #F5E9E1;
  --wash: #F1E5DB;
  --stock: #F5E9E1;
  --stock-dim: #EFE0D6;
  --ink: #33291F;
  --ink-soft: #8A7A6E;
  --muted: #8A7A6E;
  --line: rgba(51,41,31,.14);
  --coral: #DE5A38;
  --gold: #EFA93F;
  --home: #DE5A38;
  --out: #E8A13D;
  --caregiving: #C9679B;
  --magic: #8B5CF6;
  --wild: #D6438A;
  --unicorn1: #7DD8F0;
  --unicorn2: #C98BF0;
  --unicorn3: #F7B2D9;
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --sheet: #FFFDFA;
  --dot-grid: rgba(51,41,31,.05);
  --bar-bg: rgba(250, 246, 240, .94);
  /* Text that sits *on* --ink. It must flip with the theme, or the toast and
     the flick banner become light-on-light. */
  --ink-invert: #FAF6F0;
  --danger-bg: #F3D9D5;
  --danger-fg: #A33B2E;
  /* Tells the browser what to paint its own furniture in — form controls, the
     scrollbar, the caret. Without it the Us tab's <select> and checkbox stay
     stubbornly light on a dark table. */
  color-scheme: light;
}

/* Deal night is a night activity. Everything below re-points the same variables
   the light theme is built from, so nothing else in the file needs to know.
   The card artwork is printed on white, so it keeps a paper-coloured mount
   rather than being floated on the dark.

   Keyed on the attribute, not on @media (prefers-color-scheme: dark): theme.js
   resolves "follow the phone" into an explicit light/dark before the first
   paint, so this palette is written down once and serves both the preference
   and the manual choice in the Us tab. Two copies would drift. */
:root[data-theme="dark"] {
    color-scheme: dark;
    --paper: #17130F;
    --card: #241D17;
    --wash: #241D17;
    --stock: #241D17;
    --stock-dim: #1E1813;
    --ink: #F0E7DC;
    --ink-soft: #A2917F;
    --muted: #A2917F;
    --line: rgba(240,231,220,.16);
    --coral: #F0785A;
    --gold: #E9B45C;
    --home: #F0785A;
    --out: #E9B45C;
    --caregiving: #E087B7;
    --magic: #A78BFA;
    --wild: #EE6BA8;
    --sheet: #201A15;
    --dot-grid: rgba(240,231,220,.045);
    --bar-bg: rgba(23, 19, 15, .94);
    --ink-invert: #17130F;
    --danger-bg: #4A2620;
    --danger-fg: #F0A090;
}
/* The official card art is printed on white. Floated straight onto a dark
   table it reads as a hole, so it keeps its paper mount and just dims a touch
   to sit with everything else. */
[data-theme="dark"] .artcard { background: #F5E9E1; }
[data-theme="dark"] .artcard .face,
[data-theme="dark"] .cardrow .thumb { filter: brightness(.94); }
[data-theme="dark"] .msc-dot { background: #5A4C3C; }
[data-theme="dark"] .pill.primary { box-shadow: 0 4px 12px rgba(240,120,90,.25); }
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body { margin: 0; height: 100%; overscroll-behavior: none; }
body {
  font-family: 'Karla', system-ui, sans-serif;
  background: var(--paper);
  background-image: radial-gradient(var(--dot-grid) 1px, transparent 1px);
  background-size: 22px 22px;
  color: var(--ink);
  min-height: 100dvh;
  -webkit-font-smoothing: antialiased;
  touch-action: pan-y;
}
h1, h2, .display { font-family: 'Courier Prime', 'Courier New', monospace; letter-spacing: .05em; text-transform: uppercase; }
button { font-family: inherit; cursor: pointer; border: none; }

/* ---------- layout shell ---------- */
#app { min-height: 100dvh; display: flex; flex-direction: column; }
/* --bottom-h is measured from the real bar at runtime, so the balance strip and
   any safe-area inset are always cleared without hard-coding a number. */
.screen { flex: 1; display: none; flex-direction: column; padding: calc(var(--safe-top) + 10px) 16px calc(var(--bottom-h, 96px) + 12px); max-width: 520px; margin: 0 auto; width: 100%; }
.screen.active { display: flex; }
#bottombar {
  position: fixed; bottom: 0; left: 0; right: 0;
  background: var(--bar-bg);
  backdrop-filter: blur(12px);
  border-top: 1px solid var(--line);
  z-index: 40;
}
#nav {
  display: flex; justify-content: space-around;
  padding: 8px 10px calc(var(--safe-bottom) + 8px);
}
#balance { padding: 7px 16px 3px; max-width: 520px; margin: 0 auto; }
#balance .balance { margin: 0; }
#balance .bar { height: 10px; }
/* fixed line box: the strip's height feeds --bottom-h, so it must not wobble
   when the font loads or the counts change width */
#balance .legend { margin: 0 0 5px; font-size: 11px; line-height: 14px; }
#balance .legend .lbl {
  font-size: 10px; letter-spacing: .14em; text-transform: uppercase; opacity: .6; font-weight: 700;
}
/* A card landing in a hand is the thing the whole app is about, so the side that
   gained swells and flashes. inline-block: a plain inline span can't be scaled. */
#bal-1, #bal-2 { display: inline-block; transform-origin: center; will-change: transform; }
#bal-1.pop, #bal-2.pop { animation: balPop .5s cubic-bezier(.2,.9,.3,1.5); }
@keyframes balPop {
  0%   { transform: scale(1); color: var(--muted); }
  28%  { transform: scale(1.55); color: var(--coral); }
  100% { transform: scale(1); color: var(--muted); }
}
/* While the opening deal is on, the bar has to be above the overlay — watching
   the split build as the cards land is half the point of it. */
body.dealing-in #bottombar { z-index: 59; }
/* ...and the overlay has to give it room, or the bar covers your own seat. */
body.dealing-in #dealintro { padding-bottom: calc(var(--bottom-h, 96px) + 16px); }
/* Lifting the bar above the overlay lifts the nav with it, which made the tabs
   tappable *through* the deal: you could land on Us behind the overlay and be
   dropped there when it closed. The balance is the only part meant to show.
   (JS also marks it inert, which covers the keyboard; this covers the rest.) */
body.dealing-in #nav { pointer-events: none; opacity: .45; transition: opacity .3s ease; }
@media (prefers-reduced-motion: reduce) {
  #bal-1.pop, #bal-2.pop { animation: none; }
}
#nav button {
  background: none; color: var(--muted); font-size: 12px; font-weight: 700;
  display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 4px 16px; border-radius: 12px;
  position: relative;
}
#nav button .ic { font-size: 20px; }
#nav button.on { color: var(--coral); }
#nav .badge {
  position: absolute; top: -2px; right: 6px; background: var(--coral); color: #fff;
  min-width: 16px; height: 16px; border-radius: 8px; font-size: 10px; line-height: 16px; text-align: center; padding: 0 4px;
}

/* ---------- shared bits ---------- */
.eyebrow { font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); font-weight: 700; }
.pill {
  border-radius: 999px; padding: 10px 18px; font-weight: 700; font-size: 15px;
  background: var(--wash); color: var(--ink);
}
.pill.primary { background: var(--coral); color: #fff; box-shadow: 0 4px 12px rgba(232,106,94,.35); }
.pill.ghost { background: transparent; border: 1.5px solid var(--line); color: var(--ink); }
.pill:active { transform: scale(.96); }
input[type=text], textarea, select {
  width: 100%; border-radius: 12px; border: 1.5px solid var(--line);
  padding: 10px 12px; font-family: inherit; font-size: 16px; background: var(--sheet); color: var(--ink);
}
.row { display: flex; gap: 10px; align-items: center; }
.spread { justify-content: space-between; }
.stack { display: flex; flex-direction: column; gap: 10px; }

/* ---------- the card ---------- */
.card {
  background: var(--card);
  border-radius: 18px;
  color: var(--ink);
  box-shadow: 0 12px 26px rgba(51,41,31,.2);
  overflow: hidden;
  position: relative;
  user-select: none; -webkit-user-select: none;
}
/* Official artwork fills the whole face — the PNG already carries title, suit and mug. */
.artcard { display: grid; }
.artcard .face { width: 100%; height: 100%; object-fit: contain; object-position: center; }
/* Homemade "house cards" copy the deck's anatomy: rotated suit labels, letterspaced title, mug. */
.housecard { font-family: 'Courier Prime', 'Courier New', monospace; }
.housecard .vlab {
  position: absolute; font-weight: 700; font-size: 13px; letter-spacing: .3em; text-transform: uppercase;
  writing-mode: vertical-rl;
}
.housecard .vlab.tl { top: 14px; left: 12px; }
.housecard .vlab.br { bottom: 14px; right: 12px; transform: rotate(180deg); }
.suit-home .vlab { color: var(--home); }
.suit-out .vlab { color: var(--out); }
.suit-caregiving .vlab { color: var(--caregiving); }
.suit-magic .vlab { color: var(--magic); }
.suit-wild .vlab { color: var(--wild); }
.suit-unicorn .vlab { color: var(--unicorn2); }
.housecard .htitle {
  position: absolute; top: 16px; right: 18px; left: 30%; text-align: right;
  font-weight: 700; font-size: 15px; letter-spacing: .18em; text-transform: uppercase; line-height: 1.5;
}
.housecard .hglyph { position: absolute; inset: 0; display: grid; place-items: center; font-size: 64px; }
.housecard .hmug { position: absolute; bottom: 16px; left: 16px; font-size: 22px; }
.housecard .hkid { position: absolute; bottom: 16px; left: 46px; font-size: 18px; }

/* ---------- table screen ---------- */
#s-table { align-items: stretch; }
.tablehead { display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 2px 0 6px; }
.playerchip { display: flex; align-items: center; gap: 8px; font-weight: 700; font-size: 14px; }
.playerchip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--line); }
.playerchip .dot.here { background: #2FA97C; box-shadow: 0 0 8px rgba(47,169,124,.8); }
.playerchip .av { font-size: 22px; }
#table-zone {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  position: relative; min-height: 340px;
}
#bigcard {
  width: min(78vw, 320px); aspect-ratio: 5 / 7;
  display: flex; flex-direction: column;
  touch-action: none;
  will-change: transform;
  /* Dragging a card must never turn into dragging its artwork or selecting its
     title — both cancel the pointer and kill the swipe on a desktop. */
  user-select: none; -webkit-user-select: none;
}
#bigcard img { -webkit-user-drag: none; user-drag: none; }
#bigcard .htitle { font-size: clamp(14px, 4.4vw, 17px); }
#bigcard .title { font-size: clamp(24px, 7.4vw, 32px); }
#deckcount {
  position: absolute; top: 6px; right: 4px; z-index: 6;
  background: none; padding: 2px; border-radius: 12px;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
}
#deckcount:active { transform: scale(.94); }
#deckcount .peek {
  font-size: 9px; letter-spacing: .12em; text-transform: uppercase;
  font-weight: 800; color: var(--ink-soft);
}
.minideck { position: relative; width: 44px; height: 58px; }
.minideck .mc {
  position: absolute; inset: 0; border-radius: 8px; background: var(--card); /* blush, like real card backs */
  border: 1.5px solid var(--line); box-shadow: 0 3px 8px rgba(46,36,64,.2);
}
.minideck .mc:nth-child(1) { transform: rotate(-6deg) translateX(-3px); }
.minideck .mc:nth-child(2) { transform: rotate(3deg) translateX(2px); }
.minideck .n {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-weight: 700; color: var(--ink); font-size: 15px; z-index: 2;
}
.dropzone {
  position: absolute; font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); opacity: .75; transition: color .15s, transform .15s; text-align: center; pointer-events: none;
}
.dropzone .big { font-size: 20px; display: block; letter-spacing: 0; }
#dz-partner { top: 0; left: 50%; transform: translateX(-50%); }
#dz-me { bottom: 0; left: 50%; transform: translateX(-50%); }
#dz-discard { left: 2px; top: 50%; transform: translateY(-50%); max-width: 74px; }
#dz-park { right: 2px; top: 50%; transform: translateY(-50%); max-width: 74px; }
.dropzone.hot { color: var(--coral); opacity: 1; transform-origin: center; }
#dz-partner.hot { transform: translateX(-50%) scale(1.15); }
#dz-me.hot { transform: translateX(-50%) scale(1.15); }
#dz-discard.hot { transform: translateY(-50%) scale(1.15); }
#dz-park.hot { transform: translateY(-50%) scale(1.15); }
#table-actions { display: flex; justify-content: center; gap: 10px; padding: 12px 0 4px; flex-wrap: wrap; }
/* On a phone, flicking is the whole interface: the directional buttons just
   repeat the four swipes. Skip and End stay — they have no gesture. Wider
   screens get them back, spelled out rather than as bare icons. */
.dir-action, .wide-only { display: none; }
@media (min-width: 600px) {
  .dir-action { display: inline-block; }
  .wide-only { display: inline; }
}
/* Fixed line box so the replay caption (bolder, with an emoji) occupies exactly
   the same height as the normal hint — otherwise the card shifts mid-replay. */
#table-hint { text-align: center; font-size: 13px; color: var(--muted); padding-bottom: 4px; min-height: 26px; line-height: 22px; }

.tablecard-anim-in { animation: dealIn .38s cubic-bezier(.2,.9,.3,1.2); }
@keyframes dealIn {
  from { transform: translateY(-40vh) rotate(9deg) scale(.8); opacity: 0; }
  to { transform: none; opacity: 1; }
}
.fly-up    { transition: transform .35s cubic-bezier(.4,0,.9,.5), opacity .35s; transform: translateY(-90vh) rotate(-8deg) !important; opacity: 0; }
.fly-down  { transition: transform .35s cubic-bezier(.4,0,.9,.5), opacity .35s; transform: translateY(80vh) rotate(6deg) scale(.7) !important; opacity: .2; }
.fly-left  { transition: transform .3s ease-in, opacity .3s; transform: translateX(-110vw) rotate(-20deg) !important; opacity: 0; }
.fly-right { transition: transform .3s ease-in, opacity .3s; transform: translateX(110vw) rotate(20deg) !important; opacity: 0; }
.poof { animation: poof .4s ease-out forwards; }
@keyframes poof { to { transform: scale(.1) rotate(30deg); opacity: 0; filter: blur(6px); } }
.wiggle { animation: wiggle 1.2s ease-in-out infinite; }
@keyframes wiggle { 0%,100% { transform: rotate(-2deg); } 50% { transform: rotate(2.5deg); } }

/* replaying the partner's flick — their move, shown in my seat.
   The card usually animates in place, so .replaying must not re-animate it;
   only a card we had to build from scratch gets the .replay-in entrance. */
.replaying { pointer-events: none; }
.replay-in { animation: replayIn .2s ease-out; }
@keyframes replayIn { from { transform: scale(.95); opacity: .6; } to { transform: none; opacity: 1; } }
.replay-hint { color: var(--ink) !important; font-weight: 600; }
/* Partner's turn: the buttons stay put (so the column keeps its height) but go inert. */
#table-actions.locked { pointer-events: none; opacity: .35; transition: opacity .18s; }
@media (prefers-reduced-motion: reduce) { .replay-in { animation: none; } }

/* empty table / start panel */
.panel {
  background: var(--sheet); border: 1px solid var(--line);
  border-radius: 20px; padding: 20px; text-align: center; box-shadow: 0 6px 18px rgba(46,36,64,.08);
}
.panel h2 { margin: 4px 0 8px; font-size: 24px; }
.panel p { color: var(--muted); font-size: 14px; margin: 0 0 14px; line-height: 1.45; }

/* ---------- decks screen ---------- */
.deckhead { display: flex; align-items: baseline; gap: 8px; margin: 14px 0 8px; }
.deckhead h2 { margin: 0; font-size: 20px; }
.deckhead .count { color: var(--muted); font-size: 13px; font-weight: 700; }
.balance { margin: 8px 0 4px; }
.balance .bar {
  height: 14px; border-radius: 7px; overflow: hidden; display: flex;
  background: var(--wash); border: 1px solid var(--line);
}
.balance .bar .b1 { background: var(--gold); }
.balance .bar .b2 { background: var(--magic); }
/* the split slides rather than jumps, so a claimed card visibly moves it */
.balance .bar .b1, .balance .bar .b2 { transition: width .55s cubic-bezier(.3,.9,.3,1); }
#balance.bumped .bar { animation: balBump .45s ease-out; }
@keyframes balBump { 0%, 100% { transform: scaleY(1); } 35% { transform: scaleY(1.55); } }
@media (prefers-reduced-motion: reduce) {
  .balance .bar .b1, .balance .bar .b2 { transition: none; }
  #balance.bumped .bar { animation: none; }
}
.balance .legend { display: flex; justify-content: space-between; font-size: 12px; color: var(--muted); margin-top: 5px; font-weight: 600; }
/* a header per suit in the deck lists — sticks while you scroll the hand */
.suithead {
  position: sticky; top: 0; z-index: 5;
  display: flex; align-items: center; gap: 8px;
  margin: 14px 0 8px; padding: 6px 2px;
  background: var(--paper);
  font-size: 12px; letter-spacing: .12em; text-transform: uppercase; font-weight: 800;
  color: var(--ink-soft); border-bottom: 1px solid var(--line);
}
.suithead .count { margin-left: auto; letter-spacing: 0; font-weight: 700; color: var(--muted); }
/* browsing the rest of the deal */
#browse { margin-top: 4px; }
#browse .suithead { background: var(--sheet); margin-top: 10px; }
.browserow { padding-right: 10px; }
.browserow .dealbtn { padding: 8px 16px; font-size: 14px; flex: none; }
.browserow .ontable {
  font-size: 11px; letter-spacing: .1em; text-transform: uppercase;
  font-weight: 800; color: var(--coral); flex: none; text-align: right; max-width: 74px; line-height: 1.25;
}
.soundtry { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.soundtry .pill { font-size: 13px; padding: 8px 12px; }
/* Three equal shares rather than pills that hug their labels: they are one
   control with three positions, and ragged widths read as three buttons that
   happen to sit together. min-width:0 so the row still fits a 320px phone
   instead of a flex item refusing to shrink below its text. */
.themepick { display: flex; gap: 8px; }
.themepick .pill {
  flex: 1 1 0; min-width: 0; text-align: center;
  font-size: 13px; padding: 9px 6px; white-space: nowrap;
}
.cardrow {
  background: var(--sheet); color: var(--ink); border-radius: 14px;
  display: flex; align-items: center; gap: 10px; padding: 10px 12px 10px 0; margin-bottom: 8px;
  box-shadow: 0 3px 10px rgba(46,36,64,.1); border: 1px solid var(--line);
  position: relative; overflow: hidden;
}
.cardrow .tab { width: 8px; align-self: stretch; border-radius: 14px 0 0 14px; flex: none; }
.tab.home { background: var(--home); } .tab.out { background: var(--out); }
.tab.caregiving { background: var(--caregiving); } .tab.magic { background: var(--magic); }
.tab.wild { background: var(--wild); }
.tab.unicorn { background: linear-gradient(180deg, var(--unicorn1), var(--unicorn2), var(--unicorn3)); }
.cardrow .t { font-weight: 700; font-size: 15px; flex: 1; line-height: 1.2; }
.cardrow .thumb { width: 34px; height: 46px; object-fit: contain; flex: none; border-radius: 5px; background: var(--card); }
.cardrow .meta { font-size: 12px; color: var(--ink-soft); font-weight: 600; }
.cardrow .icons { font-size: 14px; display: flex; gap: 4px; align-items: center; }
.cardrow .flagchip { font-size: 15px; }
.msc-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; background: #D9CDB8; }

/* ---------- deal progress, options and the sheet stepper ---------- */
/* A hairline that fills as the queue drains. Deliberately quiet — it answers
   "how much longer?" without competing with the card. */
#deal-progress {
  height: 3px; border-radius: 2px; background: var(--line);
  margin: 2px auto 8px; width: min(78vw, 320px); overflow: hidden; flex: none;
}
#deal-progress span {
  display: block; height: 100%; border-radius: 2px;
  background: linear-gradient(90deg, var(--gold), var(--coral));
  transition: width .45s cubic-bezier(.3,.9,.3,1);
}
.dealopt {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  font-size: 13px; font-weight: 600; color: var(--muted); margin: 0 0 12px;
}
.dealopt input { width: 18px; height: 18px; accent-color: var(--coral); }
.dealopt span { opacity: .7; }
.panel .since { font-size: 12px; color: var(--ink-soft); }
.panel .footnote {
  margin: 10px 0 0; font-size: 12px; line-height: 1.45; color: var(--ink-soft);
}
.panel .waiting {
  margin: 12px 0 0; font-size: 13px; color: var(--ink-soft); font-weight: 600;
}
.nudge { margin: 0; font-size: 14px; color: var(--muted); line-height: 1.5; }
.stepper { display: flex; align-items: center; gap: 2px; }
.stepper button {
  background: var(--wash); color: var(--ink); width: 32px; height: 32px;
  border-radius: 10px; font-size: 20px; line-height: 1; font-weight: 700; padding: 0;
}
.stepper button:disabled { opacity: .3; cursor: default; }
.stepper span { font-size: 11px; color: var(--muted); font-weight: 700; min-width: 34px; text-align: center; }
@media (prefers-reduced-motion: reduce) { #deal-progress span { transition: none; } }
.msc-dot.set { background: var(--out); }
.msc-dot.agreed { background: #35B87A; }
.offerrow {
  background: var(--sheet); border: 2px solid var(--coral);
  border-radius: 16px; padding: 12px 14px; margin-bottom: 10px; box-shadow: 0 6px 16px rgba(232,106,94,.18);
}
.offerrow .t { font-weight: 700; }
.offerrow .note { font-size: 13px; color: var(--muted); margin: 4px 0 8px; }
.subtabs { display: flex; gap: 8px; margin-bottom: 6px; }
.subtabs button {
  flex: 1; padding: 9px; border-radius: 12px; font-weight: 700; font-size: 14px;
  background: var(--wash); color: var(--muted);
}
.subtabs button.on { background: var(--ink); color: var(--ink-invert); }

/* ---------- us screen ---------- */
.usrow { background: var(--sheet); border: 1px solid var(--line); border-radius: 16px; padding: 14px; margin-bottom: 10px; box-shadow: 0 4px 12px rgba(46,36,64,.06); }
.usrow h3 { margin: 0 0 8px; font-size: 15px; }
.lovenote { display: flex; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--line); font-size: 14px; }
.lovenote:last-child { border-bottom: none; }
.lovenote .when { color: var(--muted); font-size: 12px; margin-left: auto; flex: none; }

/* ---------- modal ---------- */
#modal-wrap {
  position: fixed; inset: 0; background: rgba(20,10,26,.6); backdrop-filter: blur(4px);
  display: none; align-items: flex-end; justify-content: center; z-index: 60;
}
#modal-wrap.open { display: flex; }
/* Both sheets that rise from the bottom: the card sheet, and the guide's
   single-idea explainer that opens above it. One rule, so the second can't
   quietly drift from the first. */
#modal, .gd-sheet, .ce-sheet {
  background: var(--sheet); color: var(--ink); width: 100%; max-width: 520px;
  border-radius: 24px 24px 0 0; padding: 18px 18px calc(var(--safe-bottom) + 18px);
  max-height: 86dvh; overflow-y: auto;
  animation: sheetUp .28s cubic-bezier(.2,.9,.3,1.05);
}
@keyframes sheetUp { from { transform: translateY(60%); } to { transform: none; } }
#modal h2 { margin: 6px 0 2px; font-size: 24px; }
#modal .sub { color: var(--ink-soft); font-size: 13px; font-weight: 600; margin-bottom: 12px; }
/* the suit chip, and the explanation it discloses */
.suitchip {
  background: none; padding: 4px 0; margin: 0; display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase; font-weight: 700;
  color: var(--ink-soft); border-bottom: 1px dotted var(--line); border-radius: 0;
  min-height: 32px; /* a thumb-sized target despite the small type */
}
.suitchip .chev { font-size: 15px; letter-spacing: 0; transition: transform .2s ease; display: inline-block; }
.suitchip[aria-expanded="true"] { color: var(--coral); border-bottom-color: var(--coral); }
.suitchip[aria-expanded="true"] .chev { transform: rotate(90deg); }
.suitnote {
  background: var(--wash); border: 1px solid var(--line); border-radius: 14px;
  padding: 12px 14px; margin: 2px 0 14px;
}
.suitnote p { margin: 0; font-size: 14px; line-height: 1.5; color: var(--ink); }
.suitnote p.src { margin-top: 8px; font-size: 12px; color: var(--ink-soft); }
@media (prefers-reduced-motion: reduce) { .suitchip .chev { transition: none; } }

/* the official card description */
.cardnote:empty { display: none; }
.cardnote {
  background: var(--wash); border: 1px solid var(--line); border-radius: 14px;
  padding: 12px 14px; margin: 0 0 14px;
}
.cardnote p { margin: 6px 0 0; font-size: 14px; line-height: 1.5; color: var(--ink); }
.cardnote .sec { display: block; margin-top: 14px; }
.cardnote ul { margin: 6px 0 0; padding-left: 18px; }
.cardnote li { font-size: 14px; line-height: 1.45; margin: 4px 0; color: var(--ink); }
.cardnote .msc { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line); }
.cardnote .msc p { font-size: 13px; color: var(--ink-soft); }
/* ---------- a section you can rewrite ----------
   The heading and its affordance are one row: the "✍ tap to edit" belongs to the
   heading it sits beside, and on the right of it so a column of sections reads
   as a column of headings rather than a list of buttons. Baseline-aligned, or
   10px type beside 11px caps sits visibly low. */
.sechead {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; margin-top: 14px; cursor: pointer;
}
.sechead .sec { margin-top: 0; } /* the row owns the spacing now */
.cardnote .msc .sechead { margin-top: 0; }
.edited { font-size: 10px; letter-spacing: .06em; color: var(--out); text-transform: none; }
/* Lower case on purpose, beside a heading that is not: it reads as an invitation
   rather than a second label competing with the section's name. */
.secedit {
  background: none; color: var(--out); font-size: 10px; font-weight: 700;
  white-space: nowrap;
  /* Small type, so the padding is what makes it a thumb-sized target — and
     negative margin keeps that padding from pushing the heading row taller. */
  padding: 8px 6px; margin: -8px -6px -8px 0; border-radius: 8px; flex: none;
}
.secedit:active { background: var(--wash); }
.secempty { font-size: 13px; color: var(--muted); font-style: italic; }
/* Text a person wrote, rendered from Markdown by md.js. Same measurements as the
   official text beside it — a rewritten section must not look like a different
   kind of thing from the one it replaced. */
.usertext p { margin: 6px 0 0; font-size: 14px; line-height: 1.5; color: var(--ink); }
.usertext ul, .usertext ol { margin: 6px 0 0; padding-left: 18px; }
.usertext li { font-size: 14px; line-height: 1.45; margin: 4px 0; color: var(--ink); }
.usertext a { color: var(--out); }
.usertext strong { font-weight: 700; }
.cardnote .msc .usertext p, .cardnote .msc .usertext li { font-size: 13px; color: var(--ink-soft); }
#modal label { font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-soft); display: block; margin: 14px 0 6px; }
.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chips button {
  padding: 8px 14px; border-radius: 999px; background: var(--wash); color: var(--ink); font-weight: 700; font-size: 14px;
  border: 1.5px solid transparent;
}
.chips button.on { background: var(--ink); color: var(--ink-invert); }
.agreebar { display: flex; align-items: center; gap: 10px; margin-top: 14px; padding: 12px; background: var(--wash); border-radius: 12px; font-size: 14px; font-weight: 600; }
.modalactions { display: flex; gap: 10px; margin-top: 16px; flex-wrap: wrap; }
/* `display: flex` outranks the UA sheet's `[hidden] { display: none }`, so a row
   put away with the hidden attribute stays on screen. The editor's Cancel
   confirmation swaps this row out for its own; without this rule both were up at
   once, Save beside Discard. */
.modalactions[hidden] { display: none; }
.modalactions .pill { flex: 1; text-align: center; min-width: 130px; }
.pill.ink { background: var(--ink); color: var(--ink-invert); }
.pill.subtle { background: var(--wash); color: var(--ink); }
.pill.danger { background: var(--danger-bg); color: var(--danger-fg); }

/* ---------- the opening deal ---------- */
/* Above everything including the bottom bar (40) and the flick banner (55), but
   below a modal (60) — nothing should ever open over it, but if something does,
   the sheet wins. */
#dealintro {
  position: fixed; inset: 0; z-index: 58;
  display: flex; flex-direction: column; align-items: center; justify-content: space-between;
  padding: calc(var(--safe-top) + 32px) 16px calc(var(--safe-bottom) + 32px);
  background: var(--paper);
  background-image: radial-gradient(var(--dot-grid) 1px, transparent 1px);
  background-size: 22px 22px;
  opacity: 0; transition: opacity .26s ease;
}
#dealintro.show { opacity: 1; }
#dealintro[hidden] { display: none; }

.di-seat {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  font-weight: 700; font-size: 14px; color: var(--ink); flex: none;
}
.di-seat .av { font-size: 30px; }
.di-seat .nm { letter-spacing: .04em; }

.di-stage {
  flex: 1; width: 100%; position: relative;
  display: flex; align-items: center; justify-content: center;
}

/* the deck: five backs, fanned just enough to read as a stack */
.di-deck { position: relative; width: 92px; height: 128px; }
.di-deck i {
  position: absolute; inset: 0; border-radius: 10px;
  background: var(--card); border: 1.5px solid var(--line);
  box-shadow: 0 4px 12px rgba(51,41,31,.18);
}
.di-deck i:nth-child(1) { transform: rotate(-7deg) translate(-4px, 2px); }
.di-deck i:nth-child(2) { transform: rotate(-3deg) translate(-2px, 1px); }
.di-deck i:nth-child(3) { transform: rotate(0deg); }
.di-deck i:nth-child(4) { transform: rotate(3deg) translate(2px, 1px); }
.di-deck i:nth-child(5) { transform: rotate(7deg) translate(4px, 2px); }

/* the riffle — the stack springs apart and snaps back, twice */
.di-deck.riffle i { animation: riffle .3s ease-in-out 3; }
.di-deck.riffle i:nth-child(1) { animation-delay: 0ms; }
.di-deck.riffle i:nth-child(2) { animation-delay: 26ms; }
.di-deck.riffle i:nth-child(3) { animation-delay: 52ms; }
.di-deck.riffle i:nth-child(4) { animation-delay: 78ms; }
.di-deck.riffle i:nth-child(5) { animation-delay: 104ms; }
@keyframes riffle {
  0%   { transform: translateX(0) rotate(0deg); }
  45%  { transform: translateX(26px) rotate(9deg); }
  100% { transform: translateX(0) rotate(0deg); }
}

/* cards in flight, spawned one pair per beat */
.di-fly { position: absolute; inset: 0; pointer-events: none; }
/* Once *landed* they're readable: the eight cards are the first thing you see of
   this table, and tapping one opens its full entry. Keyed on .landed rather than
   .go, which goes on as the card leaves — a card still in flight is a moving
   target, not a button. */
.di-card.landed { pointer-events: auto; cursor: pointer; }
.di-card.landed:active { filter: brightness(.94); }
.di-card.landed:focus-visible { outline: 3px solid var(--coral); outline-offset: 3px; }
/* Skip places them instead of flying them, so it must not animate. */
.di-card.placed { transition: none; }

/* The deck has done its job; it bows out and the button takes its place. */
.di-deck { transition: opacity .32s ease, transform .32s ease; }
.di-deck.spent { opacity: 0; transform: scale(.8); pointer-events: none; }

/* "To the table" — the deal waits for this rather than dismissing itself. */
.di-done {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%) scale(.9);
  opacity: 0; transition: opacity .28s ease, transform .28s cubic-bezier(.2,.9,.3,1.4);
  white-space: nowrap; z-index: 2;
}
.di-done.in { opacity: 1; transform: translate(-50%, -50%) scale(1); }
@media (prefers-reduced-motion: reduce) {
  .di-deck, .di-done { transition: none; }
  .di-done { opacity: 1; transform: translate(-50%, -50%); }
}
.di-card {
  position: absolute; left: 50%; top: 50%; width: 92px; height: 128px;
  margin: -64px 0 0 -46px;
  border-radius: 10px; overflow: hidden;
  background: var(--card); border: 1.5px solid var(--line);
  box-shadow: 0 8px 20px rgba(51,41,31,.22);
  display: grid; place-items: center;
  transition: transform .52s cubic-bezier(.25,.9,.3,1), opacity .52s ease;
  transform: translateY(0) scale(1); opacity: 1;
}
.di-card img { width: 100%; height: 100%; object-fit: contain; }
.di-card .t {
  font-family: 'Courier Prime', monospace; font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .08em; text-align: center; padding: 6px; color: var(--ink);
}
/* Toward each seat, shrinking as it goes — the same "away from you" language the
   table uses when a card is claimed. They *stay*: the point is watching your
   four collect in front of you, so they fan out and hold rather than fade. */
.di-card.to-me.go {
  transform: translate(var(--x, 0px), 26vh) scale(.44) rotate(var(--r, 0deg));
  opacity: 1;
}
.di-card.to-them.go {
  transform: translate(var(--x, 0px), -26vh) scale(.44) rotate(var(--r, 0deg));
  opacity: 1;
}
/* A short screen has a short stage, and 26vh puts the landed cards on top of the
   seat labels. Pull them in and shrink them a touch. */
@media (max-height: 720px) {
  .di-card.to-me.go {
    transform: translate(var(--x, 0px), 19vh) scale(.36) rotate(var(--r, 0deg));
  }
  .di-card.to-them.go {
    transform: translate(var(--x, 0px), -19vh) scale(.36) rotate(var(--r, 0deg));
  }
  .di-deck { width: 76px; height: 106px; }
}

/* Tucked just under the deck rather than at the foot of the stage: down there it
   sat right on top of the row of cards that had just landed in front of you. */
.di-label {
  position: absolute; left: 0; right: 0; top: calc(50% + 76px); text-align: center;
  font-family: 'Courier Prime', monospace; text-transform: uppercase;
  letter-spacing: .1em; font-size: 13px; font-weight: 700; color: var(--ink-soft);
  min-height: 20px; /* fixed line box: the label must not shift the stage */
}
/* Starting a deal: no seats, just the deck, centred. */
#dealintro.only-deck { justify-content: center; }
#dealintro.only-deck .di-stage { flex: none; height: 190px; }
/* The cut: the top card flicks off the stack toward the table, which is what
   the curtain then lifts onto. */
.di-deck.cut i:nth-child(5) {
  animation: cutCard .3s cubic-bezier(.4,0,.9,.5) forwards;
}
@keyframes cutCard {
  to { transform: translateY(46vh) rotate(10deg) scale(.8); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .di-deck.cut i:nth-child(5) { animation: none; } }

/* ---------- reading a dealt card ---------- */
/* The card you tapped grows to fill the top of the screen and its description
   rises over the lower half, so the card stays on screen — and stays the thing
   you're reading about — with its top peeking above what it means. It is the
   same node throughout: growing a copy would throw away the decoded artwork and
   the card would visibly blink. */
/* The grown card. Same node throughout — growing a copy would throw away the
   decoded artwork and the card would visibly blink. */
.cr-zoom {
  z-index: 60;
  transition: transform .42s cubic-bezier(.2,.8,.25,1), box-shadow .42s ease;
  box-shadow: 0 26px 60px rgba(51,41,31,.34);
  cursor: zoom-out;
  /* On the table the card is a plain flex child, so it needs a stacking
     position of its own before z-index means anything. Harmless in the deal,
     where the cards are already positioned. */
  position: relative;
}
/* In the deal, everything else steps back: the seats, the label, both buttons,
   and the seven cards that aren't the one being read. The table has no such
   furniture to fade, and uses .cr-scrim below instead. */
#dealintro.reading .di-seat,
#dealintro.reading .di-label,
#dealintro.reading .di-skip,
#dealintro.reading .di-done,
#dealintro.reading .di-card:not(.cr-zoom) {
  opacity: 0; pointer-events: none; transition: opacity .24s ease;
}
/* For a host with nothing of its own to recede — the table screen, where the
   header, the hint and the action bar all stay where they are. Sits under the
   card and over everything else. */
.cr-scrim {
  position: fixed; inset: 0; z-index: 59;
  background: rgba(31, 26, 21, .52);
  opacity: 0; transition: opacity .24s ease;
}
.cr-scrim.in { opacity: 1; }
/* The description. Its top is set from the card's measured geometry, so the two
   are placed together rather than each guessing where the other went.

   `fixed`, not `absolute`: it used to live inside #dealintro and could be
   positioned against it, but the table screen scrolls and has no such overlay
   to hang from. The viewport is the one frame both hosts share. */
.cr-read {
  position: fixed; left: 0; right: 0; bottom: 0; top: var(--cr-read-top, 55%);
  /* Capped and centred: a line of card text running the width of a desktop
     window is unreadable, and the card it belongs to is nowhere near that wide.
     Wider than the 520px every other surface here caps at, deliberately — this
     is a page of card text, not a form, and it is read against a card that is
     itself centred on the screen.
     Over-constrained with left/right at 0, so `margin: auto` centres it and the
     slide-in transform stays free for the animation — reaching for
     translateX(-50%) to centre it would fight the transition. */
  max-width: 800px; margin: 0 auto;
  z-index: 61;
  display: flex; flex-direction: column;
  background: var(--card);
  border-top: 1.5px solid var(--line);
  border-radius: 18px 18px 0 0;
  box-shadow: 0 -14px 36px rgba(51,41,31,.22);
  transform: translateY(100%);
  transition: transform .3s cubic-bezier(.2,.9,.3,1);
}
.cr-read.in { transform: translateY(0); }
/* Side edges only once the cap actually bites. On a phone the panel spans the
   screen, where a vertical hairline down each edge is noise rather than an edge —
   which is exactly what shipping it unconditionally looked like. */
@media (min-width: 800px) {
  .cr-read { border-left: 1.5px solid var(--line); border-right: 1.5px solid var(--line); }
}
/* The ✕ rides in a row of its own rather than sticking to scrolled content:
   sticky inside a scroll container is one more thing to get wrong, and the way
   out of a sheet should never scroll away. */
.cr-readbar { flex: none; display: flex; justify-content: flex-end; padding: 10px 12px 0; }
.cr-readclose {
  background: var(--paper); color: var(--ink-soft);
  border-radius: 999px; width: 34px; height: 34px; font-size: 14px; line-height: 1;
}
/* Scrolls on its own. No centring anywhere in here: a flex item centred in a
   scroll container overflows both ends, and the top of a long card would sit
   above scrollTop 0 where nothing can reach it. */
.cr-readbody {
  flex: 1; overflow-y: auto; overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: 0 18px calc(var(--safe-bottom) + 18px);
}
.cr-readbody h2 { margin: 8px 0 2px; }
/* The balance strip is lifted above the deal so its count-up can be watched,
   and the description runs to the bottom of the screen — so while one is open
   the strip goes back under. Its count-up is finished by then: a card can't be
   tapped until it has landed. Must outrank body.dealing-in, hence the later
   rule. On the table the strip is at its usual 40 and this changes nothing. */
body.cr-reading #bottombar { z-index: 40; }
/* Nothing on the table should answer a thumb aimed at the description. The nav
   is inside #bottombar, which the scrim does not cover on its own. */
body.cr-reading #nav { pointer-events: none; }
@media (prefers-reduced-motion: reduce) {
  .cr-zoom, .cr-read, .cr-scrim { transition: none; }
  .cr-read { transform: none; }
  .cr-scrim { opacity: 1; }
}

.di-skip {
  position: absolute; top: calc(var(--safe-top) + 12px); right: 14px;
  background: none; color: var(--ink-soft); font-size: 12px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; padding: 10px 12px;
}

@media (prefers-reduced-motion: reduce) {
  .di-deck.riffle i { animation: none; }
  .di-card { transition: none; }
  /* Stated rather than performed: it's a sentence now, so it reads as one
     instead of shouting in letterspaced caps. */
  #dealintro.still .di-label {
    position: static; font-size: 15px; color: var(--ink); line-height: 1.5;
    text-transform: none; letter-spacing: normal; font-family: 'Karla', system-ui, sans-serif;
    max-width: 300px;
  }
}

/* ---------- toast & confetti ---------- */
#toast {
  position: fixed; left: 50%; bottom: calc(var(--safe-bottom) + 84px); transform: translateX(-50%) translateY(20px);
  background: var(--ink); color: var(--ink-invert); padding: 10px 16px; border-radius: 999px;
  font-weight: 700; font-size: 14px; box-shadow: 0 8px 24px rgba(46,36,64,.35);
  opacity: 0; pointer-events: none; transition: opacity .2s, transform .2s; z-index: 70;
  display: flex; gap: 12px; align-items: center; max-width: 88vw;
}
#toast.show { opacity: 1; transform: translateX(-50%) translateY(0); pointer-events: auto; }
#toast button { background: none; color: var(--gold); font-weight: 800; font-size: 14px; padding: 0; }
#confetti { position: fixed; inset: 0; pointer-events: none; z-index: 80; }

/* the partner's flick, announced loudly.
   Fixed, so it can outlive the card it describes without touching layout. */
#flick {
  /* above the nav (40), below the modal (60) so a sheet always covers it */
  position: fixed; z-index: 55; left: 50%; top: calc(var(--safe-top) + 58px);
  width: min(88vw, 460px); transform: translateX(-50%) translateY(-14px) rotate(-1.2deg);
  padding: 14px 18px; border-radius: 18px; text-align: center;
  background: var(--ink); color: var(--ink-invert);
  box-shadow: 0 14px 34px rgba(51,41,31,.30);
  opacity: 0; pointer-events: none;
  /* Leaving. This rule is what applies when .show comes off, so the exit can be
     brisker than the entrance: gone quickly rather than lingering. */
  transition: opacity .13s ease-in, transform .16s ease-in;
}
#flick.show {
  opacity: 1; transform: translateX(-50%) translateY(0) rotate(-1.2deg);
  /* Arriving: slower, with a little overshoot. */
  transition: opacity .22s ease, transform .28s cubic-bezier(.2,.9,.3,1.3);
}
#flick .who {
  display: block; font-size: 13px; font-weight: 700; letter-spacing: .16em;
  text-transform: uppercase; opacity: .85; margin-bottom: 3px;
}
#flick .what {
  display: block; font-family: "Courier Prime", ui-monospace, monospace;
  font-size: clamp(19px, 5.6vw, 25px); font-weight: 700; line-height: 1.15;
}
#flick .what .ic { font-size: 1.15em; }
/* each outcome gets its own colour, so it reads before you read it */
#flick.act-discard { background: var(--coral); }
#flick.act-park    { background: var(--gold); color: #3A2A16; }
#flick.act-claim   { background: #1F9E8E; }
/* Neither of these resolves the card, so they read as arrivals rather than
   outcomes: the deep blue of something handed over, and a neutral slate for a
   card coming back. */
#flick.act-give    { background: #3D6BC4; }
#flick.act-decline { background: #6B6257; }
@media (prefers-reduced-motion: reduce) {
  #flick { transition: opacity .12s; transform: translateX(-50%) rotate(-1.2deg); }
  #flick.show { transition: opacity .16s; transform: translateX(-50%) rotate(-1.2deg); }
}

/* setup */
#s-setup { justify-content: center; text-align: center; gap: 14px; }
#s-setup .logo { font-size: 44px; }
#s-setup h1 { margin: 0; font-size: 40px; font-weight: 900; }
#s-setup .tag { color: var(--muted); margin: 0 0 8px; }
.whoamI { display: flex; gap: 12px; justify-content: center; }
.whoamI button {
  background: var(--sheet); color: var(--ink); border: 1px solid var(--line); box-shadow: 0 6px 16px rgba(51,41,31,.1); border-radius: 18px; padding: 16px; min-width: 130px;
  font-weight: 700; font-size: 16px; display: flex; flex-direction: column; gap: 6px; align-items: center;
}
.whoamI .av { font-size: 34px; }
.setupfield { text-align: left; background: var(--sheet); border: 1px solid var(--line); padding: 14px; border-radius: 16px; }
.setupfield .eyebrow { display: block; margin-bottom: 8px; }

/* ---------- how to play: the walkthrough, and the ? beside anything ----------
   Two modes on one host. `.tour` is a full screen of its own; `.topic` is a
   sheet that deliberately sits ABOVE the card sheet (z-index 64 vs 60), so the
   ? beside "Done looks like" can be read without losing a half-typed standard. */
#guide { position: fixed; inset: 0; z-index: 64; }

/* ---------- rewriting a section of a card ----------
   Above the card sheet (60), the grown card (61) and the guide (64), because the
   "tap to edit" that opens it lives inside the first two and it must not replace
   the sheet it was opened from — that sheet is holding a half-typed standard of
   care. Below the toast (70), so "saved" is still readable. */
#cardedit {
  position: fixed; inset: 0; z-index: 66; background: rgba(20,10,26,.6); backdrop-filter: blur(4px);
  display: none; align-items: flex-end; justify-content: center;
}
#cardedit.open { display: flex; }
.ce-sheet h2 { margin: 2px 0 2px; font-size: 22px; }
.ce-sheet .sub { color: var(--ink-soft); font-size: 13px; font-weight: 600; margin: 6px 0 10px; }
/* The syntax hint shows the markup it describes, so **bold** reads as the thing
   it does rather than as punctuation. */
.ce-sheet .sub strong, .ce-sheet .sub em { color: var(--ink); }
#ce-text {
  width: 100%; min-height: 150px; font-family: inherit; font-size: 15px; line-height: 1.5;
  resize: vertical;
}
.ce-count { font-size: 11px; color: var(--muted); min-height: 14px; margin-top: 4px; text-align: right; }
/* The other phone's version of this section, while you are writing yours. Above
   the box on purpose: it is context, not a thing to act on, and putting it below
   would push the buttons off a phone. */
#ce-theirs {
  background: var(--wash); border: 2px solid var(--out); border-radius: 12px;
  padding: 10px 12px; margin: 0 0 10px;
}
.ce-whose { margin: 0 0 6px; font-size: 12px; font-weight: 700; color: var(--out); }
#ce-theirs .usertext p:first-child { margin-top: 0; }
/* Not `.pill.subtle`'s own colours: subtle is `--wash`, and so is this panel, so
   on a dark table the button vanished into its own background and read as a line
   of text. It has to look like the thing you can press. */
#ce-theirs .pill {
  margin-top: 10px; font-size: 13px; padding: 8px 14px;
  background: var(--sheet); border: 1.5px solid var(--out); color: var(--out);
}
.ce-confirm p { font-size: 15px; font-weight: 700; margin: 14px 0 0; }
#guide[hidden] { display: none; }

#guide.tour {
  display: flex; flex-direction: column;
  background: var(--paper);
  background-image: radial-gradient(var(--dot-grid) 1px, transparent 1px);
  background-size: 22px 22px;
  padding: calc(var(--safe-top) + 10px) 16px calc(var(--safe-bottom) + 12px);
}
.gd-top { display: flex; align-items: center; gap: 10px; width: 100%; max-width: 520px; margin: 0 auto; }
.gd-dots { display: flex; gap: 6px; flex: 1; align-items: center; }
.gd-dot {
  width: 8px; height: 8px; border-radius: 999px; padding: 0; position: relative;
  background: var(--line); transition: width .25s ease, background .25s ease;
}
/* An 8px dot is a 8px target. The gap between them is 6px, so the hit area can
   grow a long way vertically and a little horizontally without two dots
   overlapping each other. */
.gd-dot::after { content: ""; position: absolute; inset: -13px -3px; }
.gd-dot.done { background: var(--ink-soft); }
.gd-dot.on { width: 22px; background: var(--coral); }
.gd-skip {
  background: none; color: var(--ink-soft); font-size: 12px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; padding: 10px 4px 10px 12px;
}
/* Centred by `margin: auto` on the child, NOT by `align-items: center` — a
   flex item centred that way overflows *both* ends of its scroll container, and
   the top half becomes unreachable: on a 320px phone the recap opened halfway
   down its own list, with the heading scrolled off above scrollTop 0 and no way
   back to it. Auto margins collapse to zero once the child is too tall, so it
   simply scrolls. */
.gd-stage {
  flex: 1; overflow-y: auto; display: flex; align-items: flex-start; justify-content: center;
  width: 100%; max-width: 520px; margin: 0 auto; touch-action: pan-y;
}
.gd-page { width: 100%; max-width: 420px; text-align: center; padding: 10px 0 4px; margin: auto 0; }
.gd-ic { font-size: 44px; line-height: 1; margin-bottom: 10px; }
.gd-page .eyebrow { display: block; margin-bottom: 6px; }
.gd-page h2 { margin: 0 0 10px; font-size: clamp(22px, 6.4vw, 27px); line-height: 1.15; }
.gd-lede { margin: 0; font-size: 16px; line-height: 1.55; color: var(--ink); }
.gd-points {
  list-style: none; margin: 14px 0 0; padding: 4px 14px; text-align: left;
  background: var(--wash); border: 1px solid var(--line); border-radius: 14px;
}
.gd-points li { font-size: 14.5px; line-height: 1.45; padding: 10px 0; border-bottom: 1px solid var(--line); }
.gd-points li:last-child { border-bottom: none; }
.gd-points b { color: var(--coral); }
/* The example, or the line worth remembering. A rule down the side rather than
   a box, so it reads as an aside instead of a second list. */
.gd-aside {
  margin: 14px 0 0; text-align: left; font-size: 14px; line-height: 1.5;
  color: var(--ink-soft); border-left: 3px solid var(--gold); padding: 2px 0 2px 12px;
}
.gd-recap { list-style: none; margin: 14px 0 0; padding: 0; text-align: left; }
.gd-recap li { margin-bottom: 6px; }
.gd-recap button {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  background: var(--wash); color: var(--ink); border-radius: 12px; padding: 10px 12px;
  font-size: 14.5px; font-weight: 700; line-height: 1.25;
}
.gd-recap .ic { font-size: 18px; flex: none; }
.gd-bottom {
  display: flex; gap: 10px; width: 100%; max-width: 420px; margin: 10px auto 0;
}
.gd-bottom .pill { flex: 1; text-align: center; }
.gd-bottom .gd-back { flex: 0 0 auto; }
.gd-bottom .gd-back[disabled] { opacity: .35; pointer-events: none; }
.gd-partner { text-align: center; font-size: 12.5px; color: var(--muted); margin: 6px 0 0; font-weight: 600; }
/* The app's own focus ring — the walkthrough moves focus to the primary button
   on every page, and the browser default is a blue that appears nowhere else. */
#guide button:focus-visible { outline: 3px solid var(--coral); outline-offset: 3px; }
/* Direction is the point: forward pages arrive from the right. */
.gd-page.in-next { animation: gdNext .22s ease-out; }
.gd-page.in-prev { animation: gdPrev .22s ease-out; }
@keyframes gdNext { from { opacity: 0; transform: translateX(22px); } to { opacity: 1; transform: none; } }
@keyframes gdPrev { from { opacity: 0; transform: translateX(-22px); } to { opacity: 1; transform: none; } }

/* One idea on its own, over whatever you were already reading. The sheet itself
   is styled with #modal above — they are the same object to a reader, and
   deliberately the same rule. */
#guide.topic {
  display: flex; align-items: flex-end; justify-content: center;
  background: rgba(20,10,26,.6); backdrop-filter: blur(4px);
}
.gd-sheet .gd-page { max-width: none; padding-top: 6px; margin: 0; }
.gd-sheet .gd-ic { font-size: 34px; }
/* the ? that opens one — small, but never smaller than a thumb */
.helpdot {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; margin-left: 5px; padding: 0; border-radius: 999px;
  background: var(--wash); border: 1px solid var(--line);
  color: var(--ink-soft); font-size: 11px; font-weight: 800; line-height: 1;
  position: relative; vertical-align: middle;
}
.helpdot::after { content: ""; position: absolute; inset: -9px; }
.helpdot:active { background: var(--coral); color: #fff; }
/* The balance's verdict doubles as the way in to "fair isn't equal". The strip's
   height feeds --bottom-h, so this must behave exactly like the <span> it
   replaced: no padding, no border (box-shadow draws the hint instead), the
   legend's own line-height, and `align-self: flex-start` so it hugs its text.
   A stretched button would centre its label against the counts' first line, and
   a fixed height would clip the second line of a long verdict on a narrow
   phone — "Bartholomew is carrying a lot more" is only 202px wide at 320px. */
#bal-word[data-guide] {
  background: none; color: inherit; padding: 0; border-radius: 0;
  line-height: 14px; align-self: flex-start;
  box-shadow: 0 1px 0 var(--line);
}
.gd-more { margin-top: 12px; }
.gd-more summary {
  cursor: pointer; list-style: none; font-size: 12px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-soft);
  padding: 6px 0; display: flex; align-items: center; gap: 6px;
}
.gd-more summary::-webkit-details-marker { display: none; }
.gd-more summary::after { content: "›"; font-size: 15px; transition: transform .2s ease; }
.gd-more[open] summary::after { transform: rotate(90deg); }
.guidechips .gd-recap { margin-top: 4px; }
.linkish {
  background: none; color: var(--ink-soft); font-size: 13px; font-weight: 700;
  padding: 8px 4px; text-decoration: underline; text-underline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}