/* Tap Deck — calm house style. No border-radius, quiet motion.
   Tenant branding arrives as CSS custom properties at runtime. */

:root {
  --primary: #e8c15a;
  --secondary: #23262e;
  --accent: #4a90d9;
  --background: #101218;
  --text: #f2f2ef;
  --muted: #9a978f;
}

* { box-sizing: border-box; margin: 0; padding: 0; border-radius: 0 !important; }
html, body { height: 100%; }
body {
  background: var(--background);
  color: var(--text);
  font-family: system-ui, sans-serif;
  line-height: 1.45;
}

h1, h2, h3 { font-weight: 600; letter-spacing: 0.02em; }
a { color: var(--accent); text-decoration: none; }

.wrap { max-width: 1080px; margin: 0 auto; padding: 24px; }
.bar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 24px; background: var(--secondary);
  border-bottom: 2px solid var(--primary);
}
.bar .wordmark { font-size: 1.2rem; font-weight: 700; color: var(--primary); letter-spacing: 0.12em; }

.card {
  background: var(--secondary); padding: 18px; margin: 12px 0;
  border-left: 3px solid var(--primary);
}
.card h3 { color: var(--primary); margin-bottom: 8px; }
.muted { color: var(--muted); font-size: 0.9rem; }

button, .btn {
  background: var(--primary); color: var(--background);
  border: none; padding: 10px 18px; font-size: 1rem; font-weight: 600;
  cursor: pointer; font-family: inherit;
}
button.ghost { background: transparent; color: var(--primary); border: 1px solid var(--primary); }
button.danger { background: #a33; color: #fff; }
button:disabled { opacity: 0.4; cursor: default; }

input, select, textarea {
  background: var(--background); color: var(--text);
  border: 1px solid var(--muted); padding: 9px 12px; font-size: 1rem;
  font-family: inherit; width: 100%; margin: 4px 0;
}
label { display: block; margin-top: 10px; font-size: 0.9rem; color: var(--muted); }

table { width: 100%; border-collapse: collapse; margin: 10px 0; }
th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid #333; }
th { color: var(--muted); font-weight: 500; font-size: 0.85rem; text-transform: uppercase; }

.tabs { display: flex; flex-wrap: wrap; gap: 2px; margin: 16px 0 0; }
.tabs button {
  background: var(--secondary); color: var(--text); font-weight: 500; padding: 9px 14px;
}
.tabs button.active { background: var(--primary); color: var(--background); }

.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.pill { display: inline-block; padding: 2px 10px; font-size: 0.8rem; background: var(--background); border: 1px solid var(--muted); }
.pill.on { border-color: var(--primary); color: var(--primary); }
.big { font-size: 2.2rem; color: var(--primary); font-weight: 700; }

/* kiosk */
.kiosk { display: flex; flex-direction: column; height: 100vh; overflow: hidden; }
.kiosk-main { flex: 1; display: flex; align-items: center; justify-content: center; padding: 4vh 5vw; text-align: center; }
.kiosk-code { font-size: 16vh; letter-spacing: 0.2em; color: var(--primary); font-variant-numeric: tabular-nums; }
.kiosk h1 { font-size: 6vh; color: var(--primary); }
.kiosk h2 { font-size: 4.5vh; }
.kiosk .slide p { font-size: 3vh; }
.kiosk-footer { padding: 2vh 4vw; background: var(--secondary); display: flex; justify-content: space-between; font-size: 2.4vh; }
/* ---- promo slides (018) ----------------------------------------------------
 *
 * A venue promo gets a LOOK, chosen from a closed set. The style arrives as a
 * name and this file decides what it means — see migration 018 for why it is
 * not stored CSS.
 *
 * Everything here is rendered by the browser rather than composited into an
 * image server-side, for two reasons. `domain/png.ts` draws text with a 5x7
 * bitmap font, so a server-rendered chalkboard would look like a dot-matrix
 * printout. And a fixed-size PNG is soft on a 4K panel, where browser text is
 * sharp at any resolution.
 *
 * Sizes are in `vh` like the rest of the kiosk: the unit that keeps a slide
 * legible from across a room on a screen of unknown size.
 */
.promo {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2vh; padding: 6vh 8vw; text-align: center; overflow: hidden;
}
.promo .promo-head { font-size: 8vh; line-height: 1.05; margin: 0; }
.promo .promo-body { font-size: 3.4vh; line-height: 1.35; margin: 0; max-width: 22ch; }

/* The venue's own mark, small and out of the way. Present on every style: a
   promo is the venue speaking, and an unbranded card on a bar's own screen
   reads as somebody else's advertising. */
.promo .promo-logo {
  position: absolute; top: 4vh; left: 4vw;
  height: 8vh; width: auto; max-width: 22vw; object-fit: contain;
}

/* The background photograph, and the scrim that keeps words on top of it
   readable. A promo whose text is unreadable over its own picture is the most
   likely way this feature fails in a real room. */
.promo .promo-bg {
  position: absolute; inset: 0;
  background-size: cover; background-position: center;
}
.promo .promo-scrim { position: absolute; inset: 0; }

/* The words go ABOVE the picture.
 *
 * `.promo-bg` and `.promo-scrim` are positioned, and a positioned element
 * paints over a static one in the same stacking context — so without this the
 * background covers the headline completely and the slide renders as a
 * photograph with nothing on it. Found by looking at the thing rather than at
 * the markup. */
.promo .promo-head,
.promo .promo-body,
.promo .promo-lane { position: relative; z-index: 2; }
.promo .promo-logo { z-index: 2; }

/* --- chalk ---------------------------------------------------------------
 * The board by the door. Slate ground, chalk-dust lettering, a hand-drawn
 * frame just inside the edge.
 *
 * The chalk feel is carried by COLOUR, TEXTURE and a soft text-shadow rather
 * than by a handwriting font: the kiosk has to render identically on an
 * appliance, a smart TV browser and a stick, and a font that only exists on
 * some of them would make the look a lottery. Google Fonts is also a network
 * call, and this screen must survive losing the network.
 */
.promo.chalk {
  background-color: #232a26;
  background-image:
    radial-gradient(circle at 20% 30%, rgba(255,255,255,0.045) 0 30%, transparent 60%),
    radial-gradient(circle at 78% 68%, rgba(255,255,255,0.035) 0 26%, transparent 55%),
    repeating-linear-gradient(126deg, rgba(255,255,255,0.014) 0 2px, transparent 2px 5px);
  color: #f2efe6;
}
.promo.chalk .promo-head {
  color: #f7f4ea; font-weight: 700; letter-spacing: 0.01em;
  text-shadow: 0 0 1.1vh rgba(255,255,255,0.34), 0 0.2vh 0 rgba(0,0,0,0.22);
}
.promo.chalk .promo-body { color: rgba(242,239,230,0.86); text-shadow: 0 0 0.7vh rgba(255,255,255,0.2); }
/* The frame. Two rules so the corners stay soft rather than mitred, which is
   what a line drawn by hand actually looks like. */
.promo.chalk::after {
  content: ''; position: absolute; inset: 3.2vh 3.2vw;
  border: 0.32vh solid rgba(247,244,234,0.42); border-radius: 0.6vh;
  pointer-events: none;
}
/* Heavy on purpose. Chalk IS the slate — a background image behind it is
   texture, not the subject, and at a lighter value the board stops reading as
   a board and the lettering loses the dark ground it needs. */
.promo.chalk .promo-scrim { background: rgba(30,37,33,0.9); }

/* --- classic -------------------------------------------------------------
 * The venue's own palette and nothing in the way of the words. This is the
 * look a promo gets when the venue has not chosen one, so it must never
 * depend on a background image existing.
 */
.promo.classic { background: var(--background); color: var(--text); }
.promo.classic .promo-head { color: var(--primary); }
.promo.classic .promo-scrim { background: rgba(0,0,0,0.55); }

/* --- bold ----------------------------------------------------------------
 * One line, as large as it will go. For the far end of the room.
 */
.promo.bold { background: var(--primary); color: var(--background); }
.promo.bold .promo-head { font-size: 13vh; font-weight: 800; letter-spacing: -0.02em; color: var(--background); }
.promo.bold .promo-body { font-size: 3.8vh; color: rgba(0,0,0,0.72); }
/* Bold's scrim is the BRAND COLOUR, not black.
 *
 * The style is dark type on a bright field. A background image covers that
 * field, so a black scrim left near-black text on a dark photograph —
 * unreadable, and only visible by looking at it with a picture attached.
 * Painting the primary back over the image keeps the field and demotes the
 * photo to texture. `opacity` rather than an rgba() because the colour is a
 * tenant token and its channels are not known here. */
.promo.bold .promo-scrim { background: var(--primary); opacity: 0.88; }

/* --- photo ---------------------------------------------------------------
 * The picture is the slide. The scrim is heavier at the bottom, where the
 * words sit, so a bright photograph cannot swallow them.
 */
.promo.photo { background: #101010; color: #fff; justify-content: flex-end; }
.promo.photo .promo-head { color: #fff; text-shadow: 0 0.3vh 1.2vh rgba(0,0,0,0.75); }
.promo.photo .promo-body { color: rgba(255,255,255,0.9); text-shadow: 0 0.2vh 0.8vh rgba(0,0,0,0.7); }
.promo.photo .promo-scrim {
  background: linear-gradient(to top, rgba(0,0,0,0.82) 0%, rgba(0,0,0,0.35) 45%, rgba(0,0,0,0.1) 100%);
}

/* The lane label. A local-network ad has to say so — the three ad lanes must
   never blur, and "who is this from" is the part a guest can actually check. */
.promo .promo-lane {
  position: absolute; bottom: 3vh; right: 4vw;
  font-size: 1.7vh; letter-spacing: 0.14em; text-transform: uppercase;
  opacity: 0.72;
}

.fade { animation: fadeIn 0.6s ease; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.choices { display: grid; grid-template-columns: 1fr 1fr; gap: 2vh; margin-top: 3vh; }
.choices div { background: var(--secondary); padding: 3vh; font-size: 3.2vh; border-left: 4px solid var(--accent); }
.choices div.correct { border-color: var(--primary); background: #2c2a1e; }
.standings { margin-top: 3vh; font-size: 2.6vh; }
.qr-hint { font-size: 2.2vh; color: var(--muted); margin-top: 2vh; }

/* Scan-to-play. White plate behind the code on purpose: a QR inverted or tinted
   by a venue palette is markedly harder for a phone camera to lock onto, so the
   code itself is exempt from tenant theming. */
.qr-block { display: flex; flex-direction: column; align-items: center; margin-top: 3vh; }
.qr-img { background: #fff; padding: 1.2vh; line-height: 0; border: 1px solid rgba(0,0,0,0.15); }
.qr-img svg { width: 22vh; height: 22vh; display: block; }
.qr-url { font-family: ui-monospace, monospace; font-size: 1.9vh; color: var(--muted); margin-top: 1vh; }

/* ===========================================================================
 * Trivia in play — the TV (handoff canvas 8a)
 *
 * The kiosk wears the VENUE's brand, so everything here is drawn from the five
 * palette roles the brand kit already produces, not from Midnight Heat. The
 * mapping is the guide's §13 role model onto the variables `applyPalette` sets:
 *
 *   ground        -> --background     the plate everything sits on
 *   display       -> --text           the largest line
 *   script        -> --accent         the answer, the winner, the emphasis
 *   banner        -> --primary        bands, bars, the leader's row
 *   second banner -> --secondary      the alternating band, so two never touch
 *
 * Sizes are in `vh` and are the real ones from the handoff, which specifies a
 * 1920x1080 frame: 84px of 1080 is 7.8vh, and stating them relatively is what
 * makes the same design correct on a 4K panel and on the 55" screen in the bar.
 *
 * Nothing here is clickable. A TV is a display, and every interaction in this
 * game happens on a phone.
 * ======================================================================== */

.tv { display: flex; flex-direction: column; height: 100vh; overflow: hidden; background: var(--background); color: var(--text); }

/* The elapsed rail. 7px of 1080 is 0.65vh — thin enough to read as a clock
   rather than as a band, which is what the banner roles below are for. */
.tv-rail { height: 0.65vh; background: var(--secondary); flex: none; }
.tv-rail > i { display: block; height: 100%; background: var(--accent); transition: width 1s linear; }

.tv-head {
  flex: none; display: flex; align-items: baseline; justify-content: space-between;
  padding: 2.2vh 4vw 0; font-size: 2.6vh; letter-spacing: 0.04em;
}
.tv-head .clock { font-size: 8.1vh; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--accent); line-height: 1; }
.tv-head .answered { opacity: 0.75; }

.tv-body { flex: 1; display: flex; flex-direction: column; justify-content: center; padding: 0 4vw; gap: 3vh; }

.tv-q { font-size: 7.8vh; font-weight: 700; line-height: 1.1; margin: 0; }

/* Four choices, 2x2, alternating banner and second-banner so no two adjacent
   cells share a fill. The letter sits in the ground colour on the band, which
   is the contrasting role the handoff asks for and needs no third colour. */
/*
 * Filled and outlined, alternating — not two different FILLS.
 *
 * The handoff asks for banner and second-banner fills alternating. Built that
 * way it was unreadable on the first real brand kit tried: `--secondary` sat
 * close to `--background`, and dark ink on it disappeared. Two of the four
 * answers were invisible on the TV.
 *
 * That is the failure the handoff itself predicts — "§13's roles assign by
 * luminance", so a kit whose colours cluster produces a pair with no contrast
 * between them. The brand kit checks text-on-background and primary-on-dark; it
 * does not check ink-on-secondary, and nothing can guarantee that pair without
 * doing the contrast maths per venue.
 *
 * So the alternation is carried by FILL vs OUTLINE instead. The rhythm survives,
 * and legibility stops depending on a relationship between two tenant colours
 * that nobody has verified. Filled cells take the background as ink, which the
 * kit does check; outlined cells take `--text` on the page's own ground.
 */
.tv-choices { display: grid; grid-template-columns: 1fr 1fr; gap: 1.6vh; }
.tv-choices > div {
  display: flex; align-items: center; gap: 1.6vw;
  padding: 2.2vh 2.4vw; font-size: 5.9vh; line-height: 1.15;
  background: var(--primary); color: var(--background);
  border: 0.35vh solid var(--primary);
}
/* The checkerboard: cells 2 and 3 outlined, so no two filled cells touch. */
.tv-choices > div:nth-child(2),
.tv-choices > div:nth-child(3) { background: transparent; color: var(--text); }
.tv-choices > div b {
  flex: none; width: 2.2ch; text-align: center;
  background: var(--background); color: var(--text);
  font-size: 4.4vh; padding: 0.6vh 0;
}
/* On an outlined cell the letter inverts, or it would be ground on ground. */
.tv-choices > div:nth-child(2) b,
.tv-choices > div:nth-child(3) b { background: var(--primary); color: var(--background); }

/* The reveal: the question shrinks back, the answer takes the script band. */
.tv-q.small { font-size: 4.1vh; font-weight: 400; opacity: 0.7; }
.tv-answer {
  display: flex; align-items: baseline; justify-content: space-between; gap: 2vw;
  background: var(--accent); color: var(--background);
  padding: 2.4vh 3vw; font-size: 8.1vh; font-weight: 700; line-height: 1.1;
}
.tv-answer .got-it { font-size: 3.4vh; font-weight: 400; flex: none; }

.tv-split { display: grid; grid-template-columns: 1.05fr 1fr; gap: 3vw; }
.tv-split h3 { font-size: 2.6vh; letter-spacing: 0.12em; text-transform: uppercase; opacity: 0.7; margin: 0 0 1.4vh; }

.tv-board { display: flex; flex-direction: column; gap: 0.9vh; }
.tv-board .row {
  display: flex; align-items: baseline; gap: 1.4vw;
  padding: 1.1vh 1.6vw; font-size: 3.4vh; background: var(--secondary); color: var(--background);
}
.tv-board .row.lead { background: var(--primary); }
.tv-board .row .nm { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tv-board .row .sc { font-variant-numeric: tabular-nums; font-weight: 700; }
.tv-board .row .dl { color: var(--background); opacity: 0.75; font-size: 2.4vh; }

/* How the room split. The correct choice takes the script role; everything else
   is second-banner, so the right answer is the only thing that stands out. */
.tv-bars { display: flex; flex-direction: column; gap: 1.1vh; }
.tv-bars .b { display: flex; align-items: center; gap: 1vw; font-size: 2.8vh; }
.tv-bars .b .k { flex: none; width: 2ch; opacity: 0.7; }
.tv-bars .b .t { flex: 1; height: 2.6vh; background: var(--text); opacity: 0.14; }
.tv-bars .b .t > i { display: block; height: 100%; background: var(--secondary); opacity: 1; transition: width 0.4s ease-out; }
.tv-bars .b.right .t > i { background: var(--accent); }
.tv-bars .b .n { flex: none; font-variant-numeric: tabular-nums; opacity: 0.8; }

.tv-note { font-size: 2.8vh; opacity: 0.75; margin: 0; }

.tv-foot {
  flex: none; display: flex; align-items: center; justify-content: space-between; gap: 2vw;
  padding: 1.8vh 4vw; background: var(--primary); color: var(--background); font-size: 3vh;
}
.tv-foot .count { font-size: 4.8vh; font-weight: 700; font-variant-numeric: tabular-nums; }
.tv-foot .joined { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; opacity: 0.85; font-size: 2.6vh; }
.tv-foot.quiet { background: transparent; color: var(--text); opacity: 0.45; font-size: 2.3vh; }

/* Lobby: the join code is the point of the screen, so it gets the script role
   on a display-role plate and the largest type in the game. */
.tv-lobby { display: grid; grid-template-columns: 1.1fr 0.9fr; gap: 4vw; align-items: center; }
.tv-lobby .cta { font-size: 10.7vh; font-weight: 700; line-height: 1.02; margin: 0; }
.tv-lobby .rule { height: 0.7vh; width: 12vw; background: var(--accent); margin: 2.4vh 0; }
.tv-lobby .terms { font-size: 3.1vh; opacity: 0.75; margin: 0; }
.tv-lobby .plate { background: var(--text); color: var(--background); padding: 3vh; text-align: center; }
.tv-lobby .plate img, .tv-lobby .plate svg { width: 26vh; height: 26vh; display: block; margin: 0 auto; }
.tv-lobby .code { font-size: 8.5vh; font-weight: 700; letter-spacing: 0.12em; color: var(--accent); font-variant-numeric: tabular-nums; }

/* Final. The winner is a result, so it takes the script role throughout. */
.tv-final { display: grid; grid-template-columns: 1.05fr 1fr; gap: 4vw; align-items: center; }
.tv-final .lot { font-size: 3.4vh; opacity: 0.75; margin: 0 0 1vh; }
.tv-final .head { font-size: 9.6vh; font-weight: 700; line-height: 1.05; margin: 0; }
.tv-final .who { font-size: 7.4vh; font-weight: 700; color: var(--accent); margin: 1vh 0 0; }
.tv-final .stat { font-size: 3vh; opacity: 0.8; margin: 0.6vh 0 0; }
.tv-final .band {
  margin-top: 2.4vh; background: var(--primary); color: var(--background);
  padding: 1.8vh 2.4vw; font-size: 3.4vh; letter-spacing: 0.06em;
}

/* ===========================================================================
 * Trivia on the phone (handoff canvas 8a, screens 13.5–13.8)
 *
 * TapDeck's own surface, so this stays in the product's system — the kiosk is
 * the one screen that wears the venue's brand. Sizes are the handoff's, and the
 * choice rows are 60px against a 44px floor because this is a dark bar and
 * somebody is tapping with one hand holding a pint.
 * ======================================================================== */

.tq { padding: 0; overflow: hidden; }

/* The only urgency on the screen. A number counting down beside four buttons is
   enough; anything flashing would be worse in a room already full of screens. */
.tq-rail { height: 4px; background: rgba(255,255,255,0.12); }
.tq-rail > i { display: block; height: 100%; background: var(--accent); transition: width 1s linear; }

.tq-q { font-size: 24px; line-height: 1.3; font-weight: 600; margin: 0; padding: 16px 16px 12px; }

.tq-choice {
  display: flex; align-items: center; gap: 12px; width: 100%;
  min-height: 60px; padding: 0 16px; margin: 0;
  background: transparent; border: 0; border-top: 1px solid rgba(255,255,255,0.1);
  color: inherit; font: inherit; font-size: 17px; text-align: left; cursor: pointer;
}
.tq-choice b {
  flex: none; width: 30px; height: 30px; display: grid; place-items: center;
  background: var(--accent); color: var(--background); font-size: 14px;
}
.tq-choice:active { background: rgba(255,255,255,0.06); }

.tq-clock { display: flex; align-items: baseline; gap: 10px; padding: 14px 16px; }
.tq-clock .n { font-size: 32px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--accent); }
.tq-clock .why { font-size: 13px; opacity: 0.7; }

.tq-locked { display: flex; align-items: center; gap: 12px; padding: 16px; font-size: 17px; }
.tq-locked b {
  flex: none; width: 30px; height: 30px; display: grid; place-items: center;
  background: var(--accent); color: var(--background); font-size: 14px;
}
.tq-locked span:nth-of-type(1) { flex: 1; }
.pill-locked {
  flex: none; font-size: 11px; letter-spacing: 0.1em; padding: 4px 8px;
  border: 1px solid var(--accent); color: var(--accent);
}

.tq-wait { display: flex; align-items: center; gap: 10px; padding: 0 16px; font-size: 16px; margin: 0 0 4px; }
.tq-sub { padding: 0 16px 12px; font-size: 13px; opacity: 0.75; margin: 0; }
.tq-score { padding: 0 16px 8px; font-size: 15px; margin: 0; color: var(--accent); }
.tq-final { padding: 0 16px 8px; font-size: 40px; font-weight: 700; margin: 0; color: var(--accent); font-variant-numeric: tabular-nums; }

/* The one permitted pulse: something really is live. Opacity and shadow only —
   no scale, no spring. */
.dot-pulse {
  width: 8px; height: 8px; border-radius: 9999px; background: var(--accent);
  animation: tq-pulse 1.2s ease-in-out infinite;
}
@keyframes tq-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255,255,255,0); }
  50%      { box-shadow: 0 0 10px 3px rgba(255,255,255,0.28); }
}
@media (prefers-reduced-motion: reduce) { .dot-pulse { animation: none; } }

.tq-verdict {
  display: flex; align-items: center; gap: 12px; padding: 16px; font-size: 19px;
  border-left: 4px solid rgba(255,255,255,0.2);
}
.tq-verdict.right { border-left-color: var(--accent); }
.tq-verdict b {
  flex: none; width: 30px; height: 30px; display: grid; place-items: center;
  background: var(--accent); color: var(--background); font-size: 14px;
}

/* The conversion moment. An offer, not a wall — the game is over either way and
   nothing is withheld from somebody who ignores it. */
.tq-save { padding: 14px 16px 16px; border-top: 1px solid rgba(255,255,255,0.1); }
.tq-save p { margin: 0 0 6px; }
.tq-cta {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 18px; margin-top: 8px;
  background: var(--accent); color: var(--background); text-decoration: none; font-weight: 600;
}
