@import url('https://fonts.googleapis.com/css2?family=Syne:wght@400..800&display=swap');

/* TTT — GUI, final.
   White paper, black ink, one red. The red is the only colour in the system
   and it is never decoration: it marks the live table, the pair who should be
   walking over, the seat that qualified, the match being played right now.
   Blocks sit flat on the page separated by a 2px gutter instead of borders,
   so the grid reads as one object rather than a set of framed boxes.

   This file is the deployed stylesheet with the console brought back to the
   mock. Six things had drifted: a rule under the header, a top pad on main,
   a flat page gutter, stretched table rows, solid-white score boxes with red
   numerals, and hairline dividers between rows. All are fixed in place — no
   appended override block. */

:root{
  --ground:#ffffff;
  --surface:#f2f2f0;
  --raised:#ffffff;
  --edge:#e4e4e0;
  --hair:rgba(10,10,10,.06);
  --chalk:#0a0a0a;
  --muted:#5f5f5f;   /* 6.44:1 on white, 5.35:1 on .card-sub.muted */
  --dim:#767671;     /* 4.57:1 on white — text-safe; prefer --muted for text */
  --signal:#d8241a;
  --signal-dim:rgba(216,36,26,.22);
  --signal-wash:rgba(216,36,26,.07);
  --on-signal:#ffffff;

  --sans:"Syne",ui-sans-serif,system-ui,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  --gap:2px;          /* the gutter between blocks */
  --pad:clamp(16px,3vw,26px);  /* page gutter, breathes with the viewport */
  --r:8px;            /* panels and cards */
  --r-sm:6px;         /* inputs */
  --r-xs:5px;         /* chips and tiny buttons */
}

*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  background:var(--ground);
  color:var(--chalk);
  font-family:var(--sans);
  font-size:16px;
  line-height:1.45;
  font-variant-numeric:tabular-nums;
  -webkit-text-size-adjust:100%;
  padding-bottom:60px;
}
button,input,select,textarea{font:inherit;font-variant-numeric:tabular-nums}
h1,h2,h3{margin:0;font-weight:700;letter-spacing:-.02em}
a{color:var(--signal);text-decoration:none}
a:hover{color:var(--chalk)}

/* ------------------------------------------------------------------- bar
   No rule under the header. The page is separated by gutters, not lines. */
/* The sandbox stripe. Loud on purpose: the one thing that must never happen
   is a real result entered into a simulated evening because the two tabs
   looked the same. */
.sim-bar{
  position:sticky;top:0;z-index:30;
  background:var(--chalk);color:var(--ground);
  padding:8px var(--pad);font-size:13px;font-weight:600;letter-spacing:.02em;
}
.sim-bar.over{background:var(--signal);color:var(--on-signal)}
body.sim .bar{top:34px}          /* clear the stripe, which is sticky too */

.bar{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:14px var(--pad);border-bottom:none;
  position:sticky;top:0;z-index:20;background:var(--ground);
}
.bar-left{display:flex;align-items:center;gap:14px;min-width:0}
.bar-right{display:flex;align-items:center;gap:12px}
/* the ball, still a dot — swap the element for the letters TTT if you want
   the wordmark from the landing page (see CHANGES.md) */
.mark{
  width:12px;height:12px;border-radius:50%;flex:none;background:var(--signal);
}
/* the letters instead of the dot — matches the landing page lockup */
.mark.wordmark{
  width:auto;height:auto;border-radius:0;background:none;
  font-size:20px;font-weight:800;letter-spacing:-.04em;color:var(--chalk);
}
.bar h1{font-size:18px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.role-tag{font-size:13px;font-weight:500;color:var(--muted)}
.pulse{width:9px;height:9px;border-radius:50%;background:var(--dim);transition:background .25s}
.pulse.on{background:var(--signal)}
/* Setup is the one control in the bar — a grey chip, not a transparent word.
   (index.html marks it .ghost; this wins on id specificity.) */
#setup-btn{
  background:var(--surface);color:var(--chalk);
  font-size:14px;font-weight:600;padding:9px 15px;
}
#setup-btn:hover{background:var(--chalk);color:var(--ground)}

/* the sticky cup tabs already end on 14px, so main carries no top pad */
main{padding:0 var(--pad) 40px;max-width:1400px;margin:0 auto}

/* -------------------------------------------------------------- cup tabs */
.cup-tabs[hidden]{display:none}
.cup-tabs{
  display:flex;gap:var(--gap);flex-wrap:wrap;padding:0 var(--pad) 14px;
  position:sticky;top:61px;z-index:19;background:var(--ground);
}
.cup-tabs button{background:var(--surface);border:none;color:var(--muted);padding:9px 16px;font-size:15px;font-weight:600}
.cup-tabs button:hover{background:var(--edge);color:var(--chalk)}
.cup-tabs button.on{background:var(--chalk);color:var(--ground);font-weight:700}

/* ---------------------------------------------------------------- tables
   align-items:start — auto-fit rows stretch by default, which lets one live
   card set the height of every idle card beside it. A card is its content. */
#tables{
  display:grid;gap:var(--gap);align-items:start;
  grid-template-columns:repeat(auto-fit,minmax(300px,1fr));
  margin-bottom:var(--gap);
}
.table-card{
  background:var(--surface);border:none;border-radius:var(--r);
  padding:20px 20px 18px;display:flex;flex-direction:column;gap:14px;
  min-height:210px;position:relative;
}
/* a live table is a red block, legible from the far side of the room */
.table-card.live{background:var(--signal);color:var(--on-signal)}
.table-card.paused{opacity:.5}
/* a free table is quieter than a played one */
.table-card:not(.live):not(.has-match){opacity:.72}
.table-head{display:flex;align-items:baseline;gap:12px}
.table-no{
  font-size:38px;font-weight:800;line-height:.85;letter-spacing:-.05em;
  color:var(--on-signal);
}
.table-card:not(.live) .table-no{color:var(--chalk)}
.table-card.empty .table-no,
.table-card:not(.live):not(.has-match) .table-no{color:var(--dim)}
.table-name{
  font-size:13px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  color:var(--muted);flex:1;
}
.table-state{font-size:13px;font-weight:600;color:var(--muted)}
.table-card.live .table-name{color:rgba(255,255,255,.78)}
.table-card.live .table-state{color:var(--on-signal)}
/* Pause is not part of the head lockup — a rare action, held until the card
   is hovered or the button takes focus. On touch it is always visible. */
.table-head button{
  background:transparent;color:var(--muted);
  padding:7px 12px;font-size:13px;font-weight:600;border-radius:var(--r-xs);
  opacity:0;transition:opacity .12s,background .09s,color .09s;
}
.table-card.live .table-head button{color:rgba(255,255,255,.78)}
.table-card:hover .table-head button,
.table-card .table-head button:focus-visible{opacity:1}
.table-card:not(.live) .table-head button:hover{background:var(--raised);color:var(--chalk)}
.table-card.live .table-head button:hover{background:rgba(255,255,255,.16);color:var(--on-signal)}
@media (hover:none){.table-head button{opacity:1}}

.match-label{font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.08em}
.table-card.live .match-label{color:rgba(255,255,255,.78)}
.versus{display:flex;flex-direction:column;gap:8px;flex:1;justify-content:center}
.side{display:flex;align-items:baseline;gap:10px}
.side-name{font-size:23px;font-weight:700;letter-spacing:-.025em;line-height:1.1}
.side-sub{font-size:13px;color:var(--muted)}
.vs{font-size:12px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);margin:1px 0}
.table-card.live .side-sub{color:rgba(255,255,255,.72)}
.table-card.live .vs{color:rgba(255,255,255,.6)}
.empty-table{color:var(--muted);font-size:16px;display:flex;flex:1;align-items:center}

/* ------------------------------------------------------------- score pad
   The boxes are wells sunk into the red, with white numerals. An earlier
   pass made them solid white with red type so they would read across a
   room; it cost the card its hierarchy — four rectangles brighter than the
   action that ends the match, and the only red type in the app sitting on
   the only red ground. */
.pad{border-top:1px solid var(--edge);padding-top:14px;display:flex;flex-direction:column;gap:11px}
.table-card.live .pad{border-top-color:rgba(255,255,255,.28)}
.games{display:flex;flex-wrap:wrap;gap:8px}
.game{display:flex;align-items:center;gap:5px}
.game input{
  width:52px;padding:9px 4px;text-align:center;
  background:var(--raised);color:var(--chalk);
  border:none;border-radius:var(--r-xs);font-size:18px;font-weight:700;
}
.table-card.live .game input{background:rgba(255,255,255,.16);color:var(--on-signal)}
.table-card.live .game input::placeholder{color:rgba(255,255,255,.5)}
.game input:focus{outline:2px solid var(--chalk);outline-offset:-2px}
.table-card.live .game input:focus{outline-color:var(--on-signal)}
.game .sep{color:var(--muted);font-size:13px;font-weight:600}
.table-card.live .game .sep{color:rgba(255,255,255,.6)}
.pad-row{display:flex;gap:var(--gap);flex-wrap:wrap;align-items:center}
.hint{font-size:13px;color:var(--muted)}
.table-card.live .hint{color:rgba(255,255,255,.78)}

/* --------------------------------------------------------------- buttons */
button{
  background:var(--surface);color:var(--chalk);border:none;
  border-radius:var(--r-sm);padding:10px 15px;cursor:pointer;font-weight:600;
  transition:background .09s,color .09s,transform .09s;
}
button:hover{background:var(--chalk);color:var(--ground)}
button:active{transform:translateY(1px)}
button:focus-visible{outline:2px solid var(--signal);outline-offset:2px}
button.primary{background:var(--signal);color:var(--on-signal);font-weight:700}
button.primary:hover{background:var(--chalk);color:var(--ground)}
button.ghost{background:transparent;color:var(--muted)}
button.ghost:hover{background:var(--surface);color:var(--chalk)}
button.tiny{padding:7px 12px;font-size:13px;border-radius:var(--r-xs);font-weight:700}
button:disabled{opacity:.4;cursor:default}
button:disabled:hover{background:var(--surface);color:var(--chalk)}
/* inside a live card the ground is red, so the buttons invert. One opaque
   object on the red: the action that ends the match. */
.table-card.live button{background:rgba(255,255,255,.16);color:var(--on-signal)}
.table-card.live button:hover{background:rgba(255,255,255,.26)}
.table-card.live button.primary{background:var(--raised);color:var(--signal);font-weight:700}
.table-card.live button.primary:hover{background:var(--chalk);color:var(--ground)}

/* ----------------------------------------------------------------- panels */
.cols{display:grid;gap:var(--gap);grid-template-columns:repeat(auto-fit,minmax(340px,1fr))}
.col{display:flex;flex-direction:column;gap:var(--gap);min-width:0}
.panel{background:var(--surface);border:none;border-radius:var(--r);overflow:hidden}
.panel-head{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:15px 18px;border-bottom:none;
}
.panel-head h2{
  font-size:13px;font-weight:700;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted);
}
.panel-head .note{font-size:13px;font-weight:500;color:var(--muted)}
.panel-body{padding:0 18px 14px}
.panel-body.flush{padding:0 0 6px}
.blank{color:var(--muted);font-size:14px;padding:12px 18px}

/* ------------------------------------------------------------------ rows
   No dividers. --hair on --surface is a 1.5% step: too faint to be a rule,
   too present to be a gap. Rows are told apart by their own air, by weight,
   and by the wash on the pair who are next. */
.row{
  display:flex;align-items:center;gap:12px;
  padding:12px 18px;border-bottom:none;
}
.row .pos{font-size:13px;font-weight:700;color:var(--dim);width:20px;flex:none}
.row .nm{flex:1;min-width:0;font-size:16px;font-weight:500}
.row .meta{font-size:15px;font-weight:700;letter-spacing:-.01em;color:var(--chalk);flex:none}
.row.waiting-long .pos{color:var(--signal)}
.row.blocked{opacity:.6}
.chip{
  font-size:12px;font-weight:700;color:var(--muted);border:none;
  border-radius:var(--r-xs);padding:5px 9px;flex:none;letter-spacing:.04em;
}
.chip.hot{color:var(--signal);background:var(--signal-wash)}
.chip.next{color:var(--on-signal);background:var(--signal)}
.chip.where,.chip.when{color:var(--muted);background:transparent}
/* a state, not an action */
.chip.state{color:var(--chalk);background:var(--edge)}
/* added upstream after v1: a quieter chip, and a chip that warns without
   claiming the live-table red as a fill */
.chip.dim{color:var(--muted);background:transparent}
.chip.warn{color:var(--signal);background:var(--signal-wash)}
/* whoever should be putting their shoes on */
.row.ondeck{background:var(--signal-wash)}
.row.ondeck .pos{color:var(--signal)}
.row.ondeck .nm{font-weight:600}
.row.ondeck .chip.when{color:var(--on-signal);background:var(--signal)}
/* an action that would otherwise be a button on every single row */
.row .on-hover{opacity:0;transition:opacity .12s}
.row.hoverable:hover .on-hover,
.row .on-hover:focus-visible{opacity:1}
@media (hover:none){.row .on-hover{opacity:1}}
.sheet-inner.narrow{max-width:460px}
.warn{
  border:none;border-left:3px solid var(--signal);
  border-radius:var(--r);background:var(--surface);
  padding:12px 16px;font-size:14px;font-weight:500;color:var(--chalk);margin-bottom:18px;
}

button.danger{color:var(--signal);background:var(--signal-wash)}
button.danger:hover{background:var(--signal);color:var(--on-signal)}

table.grid{width:100%;border-collapse:collapse;font-size:15px}
table.grid th{
  text-align:left;font-weight:700;color:var(--muted);font-size:11px;
  letter-spacing:.08em;text-transform:uppercase;
  padding:0 18px 8px;border-bottom:none;
}
table.grid td{padding:11px 18px;border-bottom:none}
table.grid td.n,table.grid th.n{text-align:right;width:1%;white-space:nowrap}
/* the rank column is 1% of the table, not an equal share of it */
table.grid th:first-child,table.grid td:first-child{
  width:1%;white-space:nowrap;text-align:right;padding-right:0;
}
table.grid th:nth-child(2),table.grid td:nth-child(2){padding-left:14px}
table.grid tr.qualified td{color:var(--chalk);font-weight:700}
table.grid tr.qualified td:first-child{box-shadow:inset 3px 0 0 var(--signal);color:var(--signal)}
table.grid tr.qualified td:nth-child(2){letter-spacing:-.01em}

/* --------------------------------------------------------------- bracket */
.bracket{display:flex;gap:var(--gap);overflow-x:auto;padding:0 18px 18px}
.bround{display:flex;flex-direction:column;justify-content:space-around;gap:var(--gap);min-width:165px;flex:1}
.bround h3{
  font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--muted);margin-bottom:4px;
}
.bmatch{border:none;border-radius:var(--r-sm);overflow:hidden;background:var(--raised)}
.bmatch div{padding:9px 12px;font-size:14px;display:flex;justify-content:space-between;gap:8px}
.bmatch div:first-child{border-bottom:1px solid #ededea}
.bmatch .won{color:var(--chalk);font-weight:700}
.bmatch .lost{color:var(--muted);font-weight:500}
.bmatch .tbd{color:var(--dim);font-style:italic}
.bmatch.live{box-shadow:inset 0 0 0 2px var(--signal)}

/* ----------------------------------------------------------------- sheet */
.sheet[hidden]{display:none}
.sheet{position:fixed;inset:0;background:rgba(10,10,10,.5);z-index:50;display:flex;justify-content:center;align-items:flex-start;padding:clamp(12px,4vh,44px) 14px}
.sheet-inner{
  background:var(--ground);border:none;border-radius:10px;
  width:100%;max-width:900px;max-height:90vh;display:flex;flex-direction:column;
  overflow:hidden;
}
.sheet-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 18px;border-bottom:none}
.tabs{display:flex;gap:var(--gap);flex-wrap:wrap}
.tabs button{background:var(--surface);border:none;color:var(--muted);padding:8px 14px;font-size:14px;font-weight:600}
.tabs button:hover{background:var(--edge);color:var(--chalk)}
.tabs button.on{background:var(--chalk);color:var(--ground);font-weight:700}
.sheet-body{padding:4px 18px 22px;overflow-y:auto}

.form{display:flex;flex-direction:column;gap:14px}
.field{display:flex;flex-direction:column;gap:6px}
.field > label{
  font-size:12px;font-weight:600;letter-spacing:.06em;
  text-transform:uppercase;color:var(--muted);
}
.field input,.field select,.field textarea{
  background:var(--raised);color:var(--chalk);border:none;
  border-bottom:2px solid transparent;
  border-radius:var(--r-sm);padding:12px 14px;width:100%;font-size:16px;font-weight:500;
  transition:border-color .11s;
}
.field textarea{resize:vertical;min-height:74px;line-height:1.45}
.field input:focus,.field select:focus,.field textarea:focus{outline:none;border-bottom-color:var(--signal)}
/* on a white sheet the inputs need the grey, not the white */
.sheet-body .field input,.sheet-body .field select,.sheet-body .field textarea{background:var(--surface)}
.card .field input,.card .field select,.card .field textarea{background:var(--raised)}
.inline{display:flex;gap:var(--gap);flex-wrap:wrap;align-items:flex-end}
.inline .field{flex:1;min-width:150px}
.sub{font-size:13px;color:var(--muted);margin:0}
fieldset{border:none;border-radius:var(--r);padding:16px 18px;margin:0;background:var(--surface)}
legend{font-size:12px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);padding:0 6px}
.pickers{display:grid;gap:var(--gap);grid-template-columns:repeat(auto-fill,minmax(165px,1fr));max-height:230px;overflow-y:auto}
.pick{display:flex;align-items:center;gap:8px;font-size:15px;padding:7px 9px;border-radius:var(--r-sm);cursor:pointer;font-weight:500}
.pick:hover{background:var(--surface)}
.pick:has(input:checked){background:var(--raised);font-weight:600}
.pick input[type=checkbox],.pick input[type=radio]{
  accent-color:var(--signal);width:17px;height:17px;flex:none;margin:0;
}
.hr{height:1px;background:var(--edge);margin:8px 0}
/* the share URLs are the one monospace thing in the app; on white they need
   a ground of their own or they read as body copy */
.key{
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;
  color:var(--signal);word-break:break-all;
  background:var(--surface);border-radius:var(--r-xs);padding:6px 9px;
  display:inline-block;max-width:100%;min-width:0;
}
.card .key,.card-sub .key{background:var(--raised)}
/* .key is a direct span child of .drow (app.js:1401) — the grid cell is what
   has to be allowed to shrink, not a wrapper */
.drow > .key{overflow-x:auto;white-space:nowrap;-webkit-overflow-scrolling:touch}

.toast{
  position:fixed;left:50%;transform:translateX(-50%);bottom:26px;z-index:80;
  background:var(--chalk);color:var(--ground);border:none;border-radius:var(--r);
  padding:13px 20px;font-size:15px;font-weight:600;max-width:90vw;
}

/* --------------------------------------------------- setup: the event tab */
.sec{display:flex;align-items:baseline;gap:12px;margin:30px 0 12px}
.sec:first-child{margin-top:14px}
.sec h2{
  font-size:11px;font-weight:700;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted);white-space:nowrap;
}
.sec .line{flex:1;height:1px;background:var(--edge)}

.card{border:none;border-radius:var(--r);background:var(--surface);margin-bottom:var(--gap)}
.card-body{padding:18px;display:flex;flex-direction:column;gap:14px}
.card-sub{border-top:1px solid var(--edge);padding:16px 18px;display:flex;flex-direction:column;gap:12px}
.card-sub.muted{background:rgba(10,10,10,.03)}
/* .card-sub.muted is darker again and needs the white input back explicitly */
.card-sub.muted .field input,.card-sub.muted .field select,
.card-sub.muted .field textarea,
.card-sub.muted .rows input,.card-sub.muted .rows select{background:var(--raised)}
/* inputs inside a grey fieldset need the white back or they vanish */
.sheet-body fieldset .field input,.sheet-body fieldset .field select,
.sheet-body fieldset .field textarea{background:var(--raised)}

/* one header row, then rows that are values — no label per cell */
.rows{display:flex;flex-direction:column}
.hrow,.drow{display:grid;align-items:center;gap:10px;grid-template-columns:var(--cols);padding:11px 0}
.hrow{
  font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);
  padding:0 0 8px;border-bottom:none;
}
.drow{border-bottom:none}
.rows input,.rows select{
  background:var(--raised);color:var(--chalk);border:none;
  border-bottom:2px solid transparent;
  border-radius:var(--r-sm);padding:10px 12px;width:100%;transition:border-color .11s;
}
.rows input:focus,.rows select:focus{outline:none;border-bottom-color:var(--signal)}
.num{color:var(--muted);text-align:right;font-size:13px;font-weight:500}
.acts{display:flex;gap:var(--gap);justify-content:flex-end;flex-wrap:wrap}

/* the one-line summary a collapsed draw shows */
.sumline{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.sumline .what{font-size:15px;font-weight:700}
.sumline .cfg{font-size:14px;font-weight:500;color:var(--muted);flex:1;min-width:0}
.sumline a{color:var(--signal);text-decoration:none}
.sumline a:hover{color:var(--chalk)}

/* prose that is true but not needed every time */
details.why{font-size:13px;color:var(--muted)}
details.why > summary{cursor:pointer;color:var(--muted);list-style:none;width:max-content;font-weight:600}
details.why > summary::-webkit-details-marker{display:none}
details.why > summary::before{content:"?";
  display:inline-grid;place-items:center;width:16px;height:16px;margin-right:7px;
  background:var(--edge);border-radius:50%;font-size:10.5px;font-weight:700;vertical-align:-3px;
}
details.why > summary:hover{color:var(--chalk)}
details.why[open] > summary{margin-bottom:6px}
details.why p{margin:0 0 6px;line-height:1.5}

.warn-in{
  border-left:2px solid var(--signal);background:var(--signal-wash);
  padding:10px 13px;font-size:13px;border-radius:var(--r-sm);
}
.warn-in .inline{margin-top:8px}

/* a field that just wrote itself away, instead of a Save button per row */
@keyframes saved{0%{border-bottom-color:var(--signal)}100%{border-bottom-color:transparent}}
.rows input.just-saved,.field input.just-saved,
.field textarea.just-saved,.rows select.just-saved{animation:saved .9s ease-out}
.drow[data-dim="1"]{opacity:.5}
.tabs .count{
  display:inline-grid;place-items:center;min-width:18px;height:18px;padding:0 5px;
  margin-left:7px;border-radius:9px;background:var(--signal);color:var(--on-signal);
  font-size:11px;font-weight:700;vertical-align:1px;
}
.tabs button.on .count{background:var(--on-signal);color:var(--chalk)}

/* ---------------------------------------------------------- door tab
   One pending entry is one block: two .drow lines inside one .entry. */
.entry{padding:2px 0}
.entry + .entry{border-top:1px solid var(--edge)}
.entry .drow{border-bottom:none;padding:9px 0}
.entry .drow:first-child{padding-top:11px}
.entry .drow:last-child{padding-bottom:11px}
/* the partner line sits under the player, quieter but not grey-on-grey */
.entry .drow + .drow > span:first-child{color:#4a4a46;font-weight:500}
/* Both halves of a pair, under the team they make up: indented and a shade
   quieter, so the row you scan is still the team and the people are detail
   you drop to only when you need to change one. */
.entry .drow.member{padding:5px 0 5px 18px;border-left:2px solid var(--edge);margin-left:2px}
.entry .drow.member input{font-size:14px}
@media (max-width:640px){.entry .drow.member{padding-left:12px}}

/* a note the entrant typed — its own full-width .drow (app.js:1304) */
.entry .sub{font-size:14px;line-height:1.45;color:var(--muted)}
.subsec{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:14px 0 4px}
.subsec h3{margin:0;font-size:12px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--muted)}
.subsec .count{color:var(--signal);margin-left:4px}
.entry .tag{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:10px 0 0}
.dupnote{font-size:13px;font-weight:600;color:var(--signal);background:var(--signal-wash);border-radius:var(--r-xs);padding:8px 10px;margin:2px 0 8px}
.dupnote[hidden]{display:none}
.door-search{max-width:420px}

/* ------------------------------------------------------ destructive tinies
   Red text on a red wash rather than a red fill — a fill competes with the
   live table. (Undo on a result row is .ghost.tiny, app.js:239.) */
button.danger.tiny{background:var(--signal-wash);color:var(--signal)}
button.danger.tiny:hover{background:var(--signal);color:var(--on-signal)}

/* ---------------------------------------------------------------- wizard
   No rules needed: renderWizard (app.js:648) writes its steps into the
   sheet's own #tabs nav as <button class="on" data-wstep="…">1. Event</button>,
   so they inherit .tabs button / .tabs button.on — the wizard's numbered
   steps and the Door/Event/Links/More tabs are deliberately the same control
   in the same place. */

/* --------------------------------------------------------- manual entry
   renderManual (app.js:381) emits a plain .panel-body — no .flush — holding
   .field blocks and a .games row. Only the score row needed air above it. */
#manual-entry .games{margin-top:6px}

/* --------------------------------------------------------- inline editor
   renderEditor (app.js:332) emits .sheet-inner.narrow, capped at 460px. The
   editor has no .sheet-body wrapper of its own, so it needs inner padding. */
#editor .games,#editor .versus,#editor .pad-row{padding-left:18px;padding-right:18px}

/* ------------------------------------------------- landing: mobile polish
   Belongs to site.html but is CSS, so it lives here. */
.site input,.site select,.site textarea{font-size:17px}  /* ≥16px: no iOS zoom on focus */
.site a,.site button,.site input,.site select{-webkit-tap-highlight-color:transparent}
#join{scroll-margin-top:16px}
.hero h1,.cup .name,.join-note h2{text-wrap:pretty}
/* the poster panel should not outgrow the text beside it on a short laptop */
@media (min-width:721px) and (max-height:760px){
  .hero{min-height:0}
  .hero-panel{gap:28px}
}

/* ------------------------------------------------------- narrow columns
   Queue rows, two lines. In a narrow column (a phone, or the half-width
   column on a laptop) four things on one line means clipped names: name
   first, the status chips beneath it aligned to the name, any action at the
   far edge. Names wrap — they matter more than a tidy single line. */
.panel-body{container-type:inline-size}
@container (max-width:560px){
  .row{align-items:center;flex-wrap:wrap;row-gap:6px;column-gap:8px;padding:14px 16px}
  .row .nm{flex:1 1 calc(100% - 28px);white-space:normal;line-height:1.25;overflow-wrap:break-word}
  .row .nm + *{margin-left:28px}
  .row .chip{padding:4px 0}
  .row .chip + .chip{margin-left:0}
  /* actions get their own line beneath the chips, together, at the far edge */
  .row:has(.on-hover)::after{content:"";flex:0 0 100%;height:0;order:2}
  .row .on-hover{order:3;margin-left:auto}
  .row .on-hover ~ .on-hover{margin-left:0}
  .row button.tiny{background:var(--raised);color:var(--chalk);padding:7px 11px}
  .row button.tiny:hover{background:var(--chalk);color:var(--ground)}
  .row button.tiny.danger{background:var(--signal-wash);color:var(--signal)}
}

/* ---------------------------------------------------------------- mobile */
@media (max-width:640px){
  /* names matter more than tidy single lines on a phone */
  .row{align-items:flex-start}
  .row .nm{white-space:normal;line-height:1.25;overflow-wrap:break-word}
  .row .on-hover{opacity:1}
  main{padding:0 12px 30px}
  .bar{padding:12px 12px}
  .cup-tabs{padding:0 12px 12px}
  .table-no{font-size:32px}
  .side-name{font-size:20px}
  .sheet{padding:0}
  .sheet-inner{max-height:100vh;height:100vh;border-radius:0}
  /* the scorepad is the one thing entered standing at a table */
  .game input{width:58px;padding:11px 4px;font-size:19px}
  button{padding:11px 16px}
  /* the setup sheet is where a phone hurts most: four tabs, then grids */
  .sheet-head{flex-wrap:wrap;gap:8px}
  .sheet-head .tabs{order:2;width:100%}
  .sheet-head #sheet-close{margin-left:auto}
  .drow > .key{max-width:100%}
  .hrow{display:none}
  .drow{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:11px 0}
  .drow > input,.drow > select{flex:1 1 130px;min-width:0}
  .drow > .num{flex:0 0 auto;min-width:18px;text-align:left}
  .drow > span:not(.num):not(.acts){flex:1 1 100%;min-width:0}
  .drow > .acts{flex:1 1 100%;justify-content:flex-start}
  .sec{margin:20px 0 10px}
}
@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}


/* =====================================================================
   v2 · live view chapters + setup Event tab
   Appended to "TTT — GUI, final" (sha256 2e8da9bf…68e4). Additive only:
   nothing above this line is edited. Every value here is lifted from the
   reviewed mocks — handoff-v2/reference/*.html. No new tokens.
   ===================================================================== */

/* ------------------------------------------------------------ jump bar
   Phones only. index.html #jump; app.js renderJump() sets --jump-top to
   the bottom edge of the sticky bar + cup tabs above it. */
.jump{display:none}
@media (max-width:640px){
  .jump{
    display:flex;gap:18px;overflow-x:auto;scrollbar-width:none;
    position:sticky;top:var(--jump-top,0px);z-index:18;background:var(--ground);
    padding:14px 12px 0;border-bottom:1px solid var(--edge);
  }
  .jump::-webkit-scrollbar{display:none}
  .jump a{
    padding-bottom:11px;margin-bottom:-1px;border-bottom:3px solid transparent;
    font-size:14px;font-weight:700;color:var(--muted);white-space:nowrap;
  }
  .jump a:hover{color:var(--chalk)}
  .jump a.on{color:var(--chalk);border-bottom-color:var(--chalk)}
  .jump a .count{color:var(--signal);margin-left:5px}
  .jump a .count:empty{display:none}
  .jump a[hidden]{display:none}
  main{padding-top:20px}
}
@media (min-width:641px){main{padding-top:10px}}
#tables,#board,#standings,#recent{scroll-margin-top:calc(var(--jump-top,0px) + 64px)}

/* ---------------------------------------------------- tables: free = one line
   Grey stays reserved for tables. A table with nothing on it collapses to
   number · name · state, so the queue starts higher on a phone. */
.table-card:not(.live){flex-direction:row;align-items:baseline;gap:12px;padding:14px 18px;
                        min-height:auto;align-self:start}
/* style.css dims every non-live card via :not(.live):not(.has-match) (0,3,0);
   match that weight so a free table reads at full strength, per the mocks. */
.table-card:not(.live):not(.has-match){opacity:1}
.table-card:not(.live).paused{opacity:.5}
.table-card:not(.live) .table-head{flex:1;min-width:0}
.table-card:not(.live) .table-no{font-size:24px}
.table-card:not(.live) .empty-table{flex:none;margin-left:auto;font-size:14px;font-weight:600;color:var(--muted)}
@media (min-width:641px){
  .table-card:not(.live){padding:18px 24px}
  .table-card:not(.live) .table-no{font-size:30px}
  .table-card:not(.live) .empty-table{font-size:15px}
}

/* ------------------------------------------------------------ chapters
   Up next, Standings, Knockout and Results sit on white under a 3px ink
   rule and a display title. No panel fill — grey means "table". */
.cols{grid-template-columns:minmax(0,1fr);gap:44px;margin-top:44px}
.col{gap:44px}
.col:not(:has(> :not(:empty))){display:none}
@media (min-width:641px){.cols{grid-template-columns:repeat(2,minmax(0,1fr));gap:56px;margin-top:64px}}
@media (min-width:1100px){.cols{grid-template-columns:minmax(0,1fr) minmax(0,1.15fr) minmax(0,1fr)}}

.cols .panel{background:none;border-radius:0;overflow:visible;border-top:3px solid var(--chalk)}
.cols .panel + .panel{margin-top:44px}
.cols .panel-head{padding:12px 0 0;align-items:baseline;gap:12px}
.cols .panel-head h2{
  font-size:28px;font-weight:800;letter-spacing:-.035em;line-height:1.1;
  text-transform:none;color:var(--chalk);
}
.cols .panel-head .note{font-size:13px;font-weight:600;color:var(--muted);text-align:right}
.cols .panel-head .note:empty{display:none}
.cols .panel-body{padding:14px 0 0}
.cols .panel-body.flush{padding:10px 0 0}
/* app.js writes an inline padding on .blank inside panels */
.cols .panel-body .blank{padding:13px 0!important}
@media (min-width:641px){
  .cols .panel-head{padding-top:14px}
  .cols .panel-head h2{font-size:32px}
  .cols .panel-head .note{font-size:14px}
  .cols .panel-body.flush{padding-top:12px}
}
/* white inputs would vanish on the white chapter ground */
.cols .field input,.cols .field select,.cols .field textarea,.cols .game input{background:var(--surface)}

/* queue rows */
.cols .row{padding:13px 0;gap:14px;align-items:baseline;border-bottom:1px solid var(--edge)}
.cols .row .pos{width:18px;font-size:14px;font-weight:700;color:var(--dim)}
.cols .row .nm{flex:1 1 55%;font-size:17px;font-weight:600;letter-spacing:-.01em;line-height:1.25}
.cols .row .nm + *{margin-left:0}
.cols .row .chip{padding:0}
.cols .row .chip.next{padding:4px 8px}
.cols .row .chip.when{font-size:14px;font-weight:600;letter-spacing:0;color:var(--muted);background:transparent}
.cols .row.ondeck{background:none}
.cols .row.ondeck .pos{color:var(--dim)}
.cols .row.ondeck .chip.when{font-size:13px;font-weight:700;padding:4px 8px;color:var(--on-signal);background:var(--signal)}
@media (min-width:641px){
  .cols .row{padding:14px 0}
  .cols .row .nm{font-size:18px}
}

/* standings */
.cols table.grid{font-size:16px}
.cols table.grid th{padding:0 0 8px 8px;border-bottom:1px solid var(--chalk);font-size:12px}
.cols table.grid td{padding:11px 0 11px 8px;border-bottom:1px solid var(--edge);font-weight:500;color:var(--muted)}
.cols table.grid th:first-child,.cols table.grid td:first-child{padding-left:0;width:22px;text-align:left}
.cols table.grid td:first-child{font-size:14px;font-weight:700;color:var(--dim)}
.cols table.grid td:nth-child(2){padding-left:8px;color:var(--chalk);font-weight:700;letter-spacing:-.01em}
.cols table.grid td.w{color:var(--chalk);font-weight:700}
.cols table.grid tr.qualified td:first-child{padding-left:9px}
.cols .ghead{margin:22px 0 6px;font-size:13px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}
.cols .ghead:first-child{margin-top:0}
@media (max-width:640px){.cols table.grid .opt{display:none}}
@media (min-width:641px){
  .cols table.grid{font-size:17px}
  .cols table.grid td{padding-top:12px;padding-bottom:12px}
  .cols table.grid th:first-child,.cols table.grid td:first-child{width:26px}
}

/* knockout on the white ground */
.cols .bracket{padding:14px 0 0}
.cols .bmatch{background:var(--surface)}
.cols .bmatch div:first-child{border-bottom:2px solid var(--ground)}

/* results: winner and score, loser and score, then the detail */
.cols .row.result{
  display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:12px;row-gap:2px;
  align-items:baseline;padding:14px 0;
}
.cols .row.result::after{content:none}
.cols .result .rw,.cols .result .rl,.cols .result .rs{font-size:17px;line-height:1.25}
.cols .result .rw{min-width:0;font-weight:700;letter-spacing:-.01em;overflow-wrap:anywhere}
.cols .result .rl{min-width:0;font-weight:500;color:var(--muted);overflow-wrap:anywhere}
.cols .result .rs{font-weight:800;text-align:right;color:var(--chalk)}
.cols .result .rs.l{font-weight:600;color:var(--muted)}
.cols .result .rmeta{grid-column:1/-1;margin-top:4px;font-size:12px;font-weight:600;color:var(--dim)}
.cols .result .on-hover{grid-column:1/-1;justify-self:end;order:0;margin:6px 0 0}
@media (min-width:641px){
  .cols .row.result{padding:15px 0}
  .cols .result .rw,.cols .result .rl,.cols .result .rs{font-size:18px}
}

/* ------------------------------------------------- setup: Event tab (2a)
   A settings page, not chapters. 16px ink titles over a hairline; on a
   wide sheet title + one-line description + Why sit in a 200px column. */
.sheet-body{container:sheet / inline-size}
@media (min-width:760px){.sheet-body{padding:0 24px 12px}}
.form:has(> .sblock){gap:0}
.sblock{border-top:1px solid var(--edge);padding:20px 0 24px;display:flex;flex-direction:column;gap:12px}
.shead{display:grid;grid-template-columns:minmax(0,1fr) auto auto;align-items:center;column-gap:4px;row-gap:6px}
.shead h2{grid-column:1;grid-row:1;align-self:baseline;font-size:16px;font-weight:700;letter-spacing:-.01em;color:var(--chalk)}
.shead .stools{grid-column:2;grid-row:1;display:flex;align-items:center;gap:4px}
.shead details.why{grid-column:3;grid-row:1}
.shead details.why[open]{grid-column:1/-1;grid-row:3}
.shead .sdesc{grid-column:1/-1;grid-row:2;margin:0;font-size:13px;line-height:1.45;color:var(--muted);text-wrap:pretty}
.sbody{display:flex;flex-direction:column;gap:12px;min-width:0}
.sbody .card{margin-bottom:0;overflow:hidden}
@container sheet (min-width:720px){
  .sblock{display:grid;grid-template-columns:200px minmax(0,1fr);gap:32px;padding:24px 0}
  .shead{display:flex;flex-direction:column;align-items:flex-start;gap:6px}
  .shead h2{order:0}
  .shead .sdesc{order:1}
  .shead details.why{order:2;margin-top:4px}
  .shead .stools{order:3;margin:6px 0 0 -12px}
}
.sblock .field input,.sblock .field select{padding:11px 13px}

/* phase: a plain status row */
.phase{display:flex;flex-wrap:wrap;align-items:center;gap:10px 14px;background:var(--surface);border-radius:var(--r);padding:12px 14px}
.phase .plabel{font-size:12px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
.phase .chip.state{background:var(--chalk);color:var(--ground)}
.phase .sub{flex:1;min-width:140px;margin:0;font-size:14px}
.phase .field{flex:0 0 190px}
.sheet-body .phase .field select{background:var(--raised);padding:10px 13px;font-size:15px}
@container sheet (max-width:719px){.phase .field{flex:1 1 100%}}

/* a cup: 2px white seams between its parts, one grey throughout */
.sheet-body .card-body{padding:14px 16px;gap:10px}
.sheet-body .card-sub{border-top:2px solid var(--ground);padding:12px 16px;gap:10px}
.sheet-body .card-sub.muted{background:transparent}
.sheet-body .card-sub.muted > .inline:last-child{justify-content:flex-end}
.cuphead{display:flex;align-items:center;gap:10px}
.cuphead .cname{flex:1;min-width:0}
.sheet-body .card .cuphead .cname input{font-size:17px;font-weight:700;letter-spacing:-.015em;padding:10px 13px}
.sumline{gap:8px 12px}
.sumline .lbl{font-size:12px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
.sumline .cfg{min-width:160px}
.sumline .acts{margin-left:auto}
.card button.solid{background:var(--raised);color:var(--chalk)}
.card button.solid:hover{background:var(--chalk);color:var(--ground)}

/* tables list: grey inputs, hairlines between rows */
.trows .hrow{border-bottom:1px solid var(--edge)}
.trows .drow{border-bottom:1px solid var(--edge);padding:6px 0}
.sheet-body .trows input,.sheet-body .trows select{background:var(--surface);padding:9px 12px;font-size:15px;font-weight:500}
.trows .num{font-size:13px;font-weight:600}

/* ------------------------------------------------------------- Telegram
   Optional, so it borrows everything it can: chips, cards, the sheet. The
   only new shapes are a conversation and a code to scan. */
.chip.tg{color:var(--muted);background:transparent;box-shadow:inset 0 0 0 1px var(--edge)}
.thread .thead{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
.thread .thead .when{margin-left:auto}
.thread .thead .count{
  font-size:11px;font-weight:700;background:var(--signal);color:var(--on-signal);
  border-radius:999px;padding:1px 7px}
.thread.unread{box-shadow:inset 3px 0 0 var(--signal)}
.msgs{display:flex;flex-direction:column;gap:6px}
.msg{max-width:85%;padding:8px 11px;border-radius:12px;font-size:14px;line-height:1.4;
  white-space:pre-wrap;overflow-wrap:anywhere;display:flex;flex-direction:column;gap:2px}
.msg i{font-style:normal;font-size:11px;color:var(--muted)}
.msg.in{align-self:flex-start;background:var(--raised);border-bottom-left-radius:4px}
.msg.out{align-self:flex-end;background:var(--chalk);color:var(--ground);border-bottom-right-radius:4px}
.msg.out i{color:rgba(255,255,255,.6)}
.msg.sys{align-self:stretch;max-width:none;background:var(--signal-wash);color:var(--signal);font-weight:600}
.thread .reply{flex-wrap:nowrap}
.thread .reply .field{flex:1;min-width:0}
.thread .reply input{background:var(--raised)}
.qr{display:flex;justify-content:center;padding:8px 0}
.qr[hidden]{display:none}
.stat{display:flex;align-items:center;gap:6px;min-width:0}
.stat .tgmark{font-size:12px;color:var(--muted);line-height:1}
#setup-btn .count{
  font-size:11px;font-weight:700;background:var(--signal);color:var(--on-signal);
  border-radius:999px;padding:1px 7px;margin-left:4px}
.qr img{width:240px;height:240px;max-width:100%}

/* ------------------------------------------------ Telegram, for players
   The one-time "find your name" sheet on the live page. It sits at the
   bottom and leaves the page usable behind it: a nudge, not a gate. */
.tg-btn{font-size:13px;padding:6px 10px;white-space:nowrap}
@media (max-width:480px){.tg-btn span{display:none}}
.nudge{position:fixed;left:0;right:0;bottom:0;z-index:70;display:flex;justify-content:center;
  padding:0 12px calc(12px + env(safe-area-inset-bottom));pointer-events:none}
.nudge[hidden],.tg-btn[hidden]{display:none}
.nudge-card{pointer-events:auto;position:relative;width:100%;max-width:440px;background:var(--raised);
  border-radius:14px;padding:20px 18px 14px;box-shadow:0 10px 40px rgba(0,0,0,.18),0 0 0 1px var(--edge);
  display:flex;flex-direction:column;gap:10px;animation:nudge-up .22s ease-out}
@keyframes nudge-up{from{transform:translateY(24px);opacity:0}}
@media (prefers-reduced-motion:reduce){.nudge-card{animation:none}}
.nudge-card h2{font-size:22px;font-weight:800;letter-spacing:-.02em;margin:0;padding-right:28px}
.nudge-card p{margin:0;color:var(--muted);font-size:14px;line-height:1.45}
.nudge-card input{width:100%;border:none;border-radius:var(--r-sm);padding:12px 13px;font-size:16px;
  background:var(--surface);outline:none}
.nudge-card input:focus{box-shadow:inset 0 0 0 2px var(--chalk)}
.nudge-x{position:absolute;right:10px;top:10px;border:none;background:transparent;font-size:24px;
  line-height:1;color:var(--muted);cursor:pointer;padding:4px 8px}
.nudge-list{display:flex;flex-direction:column;gap:2px;max-height:42vh;overflow:auto}
.nudge-name{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:12px 13px;
  border-radius:var(--r-sm);background:var(--surface);color:var(--chalk);font-weight:700;font-size:16px}
.nudge-name:hover,.nudge-name:active{background:var(--signal);color:var(--on-signal)}
.nudge-name b{color:var(--signal)}
.nudge-name:hover b,.nudge-name:active b{color:var(--on-signal)}
.nudge-foot{display:flex;justify-content:space-between;gap:8px;margin-top:2px}
