/*
 * BSBC — drive-thru feature styles.
 *
 * Loaded THIRD on /drivethru/admin/: after /styles.css (site tokens, frame,
 * tables) and /dashboard/dashboard.css (the admin gate, .linkbtn, the admin
 * sign-out button — this page runs on the dashboard's shell and admin gate).
 * Only what the link page adds lives here. Colours come from the site tokens.
 *
 *   1. Link status   the headline box: ok / warn / bad
 *   2. Link form     the masked input and its Save button
 *   3. Stores table  the works / refused / untested label, and a refused
 *                    paste's own result (#linkattempt)
 *   4. Leaderboard   (phase 3) /drivethru/ — the live board, sized for a
 *                    landscape TV: every size below scales with the
 *                    viewport (vh/vw), so twelve stores plus the company
 *                    row fit 1920×1080 without scrolling and stay readable
 *                    across a room; a laptop gets the same layout smaller.
 *                    Phase 5 (2026-11-03): the goal colours (the site's
 *                    cell-good / -warn / -bad as cell backgrounds,
 *                    except on a podium row, where the medal tint keeps
 *                    the background and the goal shows as text colour),
 *                    the podium (medal badge, stripe, tint,
 *                    a rule under 3rd) and the rank-movement effects —
 *                    transform and opacity only, off under reduced motion
 *                    (the arrows stay).
 *   5. Screens       (phase 4) the board gate's "Pair this screen" form and
 *                    the admin page's Screens section: the one-time code
 *                    box and the screens table's buttons.
 */

/* ── 1. Link status ───────────────────────────────────────────────────── */
.linkstatus:empty {
	display: none;
}
.linkstatus {
	margin: 0 0 8px;
	padding: 10px 12px;
	background: var(--panel);
	border: 1px solid var(--rule);
	border-left: 4px solid var(--good);
	font-size: 14px;
}
.linkstatus p {
	margin: 0;
}
.linkstatus .linkheadline {
	font-weight: 700;
}
.linkstatus .linkexpiry {
	margin-top: 4px;
	font-family: var(--mono);
	font-size: 12px;
}
.linkstatus.warn {
	border-left-color: var(--warn);
	background: var(--warn-bg);
}
.linkstatus.bad {
	border-left-color: var(--bad);
	background: var(--bad-bg);
}
.linkstatus.warn .linkheadline {
	color: var(--warn);
}
.linkstatus.bad .linkheadline {
	color: var(--bad);
}

/* ── 2. Link form ─────────────────────────────────────────────────────── */
.linkhelp {
	max-width: 70ch;
	margin: 0 0 10px;
	font-size: 13px;
	color: var(--muted);
}
.linkform {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px 12px;
}
.linkform label {
	flex-basis: 100%;
}
.linkform input {
	flex: 1 1 280px;
	min-width: 0;
	padding: 9px 10px;
	font-family: var(--mono);
	font-size: 13px;
	border: 1px solid var(--rule);
	background: #fafaf9;
	color: var(--ink);
	border-radius: 0;
}
/* The link is a secret but the field is type="text" (see the page's
   comment): masked where the engine supports it. Firefox does not, and
   shows it — it is cleared the moment Save is pressed either way. */
.linkform .secretinput {
	-webkit-text-security: disc;
}
.linkform input:focus {
	outline: 2px solid var(--slate);
	outline-offset: -1px;
	background: #fff;
}
.linkform button {
	padding: 10px 18px;
	background: var(--ink);
	color: #fff;
	border: 0;
	font-family: var(--sans);
	font-size: 13px;
	font-weight: 700;
	cursor: pointer;
}
.linkform button:hover {
	background: var(--slate);
}
.linkform button:disabled {
	background: var(--faint);
	cursor: wait;
}
.linksavemsg {
	min-height: 15px;
	margin: 8px 0 0;
	font-family: var(--mono);
	font-size: 12px;
	color: var(--bad);
	overflow-wrap: anywhere;
}
.linksavemsg.ok {
	color: var(--good);
}
#adminpanel > h2.sec {
	margin-top: 30px;
}
#adminpanel > h2.sec:first-child {
	margin-top: 0;
}

/* ── 3. Stores table ──────────────────────────────────────────────────── */
.linkstate {
	font-family: var(--mono);
	font-size: 12px;
	font-weight: 600;
}
.linkstate.state-works {
	color: var(--good);
}
.linkstate.state-refused {
	color: var(--bad);
}
.linkstate.state-untested {
	color: var(--warn);
}
/* (phase 4) A screen not connected, or a code waiting: nothing wrong. */
.linkstate.state-idle {
	color: var(--muted);
}
.linkattempt {
	margin: 10px 0 0;
	padding: 10px 12px;
	border: 1px solid var(--rule);
	border-left: 4px solid var(--bad);
	background: var(--bad-bg);
}
.linkattempt .attemptsummary {
	margin: 0 0 8px;
	font-size: 13px;
	font-weight: 600;
	color: var(--bad);
}

/* ── 4. Leaderboard ──────────────────────────────────────────────────── */
/* The dashboard frame, slimmed: a shorter top bar, no footer (initShell's
   footer: false, 2026-10-27 — a lobby TV has no use for it), the full width. */
.dtboardpage .topbar .inner {
	max-width: none;
	padding: 1.1vh 2vw 0.9vh;
	align-items: center;
}
.dtboardpage h1.title {
	font-size: clamp(18px, 2.6vh, 32px);
	margin: 2px 0 0;
}
.dtboardpage .wrap {
	max-width: none;
	padding: 0 2vw;
}
.dtboardpage #foot {
	display: none;
}
/* No page tabs on this page (initShell's nav: false, 2026-10-27). */

/* The board's daypart, once, beside the title. */
.dttitlerow {
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	column-gap: 1.2vw;
}
.dtdaypart {
	margin: 0;
	font-family: var(--mono);
	font-size: clamp(13px, 2vh, 24px);
	font-weight: 600;
	color: var(--brand);
	white-space: nowrap;
}
.dtdaypart:empty {
	display: none;
}
/* The chips beside the heading (owner, 2026-11-12): the countdown to the
   next daypart. The heading's own size, one
   line, so the top bar keeps its height and the board still fits a 1080p
   (and 1366×768) screen with it showing. */
.dtchips {
	display: inline-flex;
	align-items: center;
	gap: 0.8vw;
	margin: 0;
	font-family: var(--mono);
	font-size: clamp(12px, 1.8vh, 22px);
	white-space: nowrap;
}
.dtchips:empty {
	display: none;
}
.dtchip {
	display: inline-flex;
	align-items: baseline;
	gap: 0.4em;
	padding: 0.15em 0.6em;
	border: 1px solid var(--rule);
	border-radius: 999px;
	background: var(--panel);
	color: var(--ink);
	font-weight: 600;
}
.dtcountdown-time {
	font-variant-numeric: tabular-nums;
}
/* The last minute: amber, the seconds a little larger. */
.dtcountdown.is-warn {
	border-color: var(--warn);
	color: var(--warn);
	background: var(--warn-bg);
}
.dtcountdown.is-warn .dtcountdown-time {
	font-size: 1.2em;
	line-height: 1;
}
/* The winners strip under the heading (owner, 2026-11-12): all of today's
   daypart winners, oldest first. One line where it fits; it wraps (never
   clips or scrolls sideways) with many dayparts or long names. Hidden
   when empty so the table does not move on an empty day. */
.dtwinners {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 0.15em 0.7em;
	margin: 0.6vh 0 0;
	font-family: var(--mono);
	font-size: clamp(12px, 1.7vh, 21px);
	color: var(--ink);
	min-width: 0;
}
.dtwinners:empty {
	display: none;
}
.dtwin-label {
	color: var(--muted);
	font-weight: 600;
}
.dtwin-sep {
	color: var(--muted);
}
.dtwin-item {
	display: inline-flex;
	align-items: baseline;
	gap: 0.35em;
	min-width: 0;
	overflow-wrap: anywhere;
}
.dtwin-medal {
	align-self: center;
	width: 0.7em;
	height: 0.7em;
	border-radius: 50%;
	background: var(--medal-gold);
	flex: 0 0 auto;
}
.dtwin-no {
	color: var(--muted);
}
.dtwin-time {
	padding: 0 0.25em;
	border-radius: 3px;
	font-variant-numeric: tabular-nums;
}
.dtwin-item.dt-fadein {
	animation: dt-fade-in 1s ease-out both;
	animation-delay: var(--dt-fx-delay, 0s);
}
@keyframes dt-fade-in {
	from {
		opacity: 0;
	}
	to {
		opacity: 1;
	}
}

.dtlive {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	font-family: var(--mono);
	font-size: clamp(11px, 1.4vh, 16px);
	color: var(--muted);
	margin-right: 8px;
}
.dtlive[hidden] {
	display: none;
}
.dtdot {
	width: 0.8em;
	height: 0.8em;
	border-radius: 50%;
	background: var(--faint);
	flex: 0 0 auto;
}
.dtlive.is-live .dtdot {
	background: var(--good);
}
.dtlive.is-live {
	color: var(--good);
}
.dtlive.is-down .dtdot {
	background: var(--warn);
}
.dtlive.is-down {
	color: var(--warn);
}

.dtmsg {
	margin: 0.6vh 0 0;
	font-size: clamp(12px, 1.5vh, 18px);
	color: var(--bad);
}
.dtmsg:empty {
	display: none;
}

/* One size drives the whole table: the smaller of a twentieth of the height
   and a fiftieth of the width — about 31px on a 1080p TV, 23px on a laptop. */
.dtboard {
	--dt-size: min(2.9vh, 1.9vw);
	width: 100%;
	margin-top: 1vh;
	border-collapse: collapse;
	background: var(--panel);
	border: 1px solid var(--rule);
	font-size: var(--dt-size);
	line-height: 1.15;
	font-variant-numeric: tabular-nums;
}
.dtboard:empty {
	display: none;
}
.dtboard th {
	font-family: var(--mono);
	font-size: 0.5em;
	font-weight: 600;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--muted);
	text-align: left;
	padding: 0.9vh 0.8vw;
	background: none;
	border-bottom: 2px solid var(--rule);
	white-space: nowrap;
}
/* The site's generic `th, td` (12.5px, centred) is for dense report
   tables; this one is read from across a room. */
.dtboard td {
	font-size: inherit;
	text-align: left;
	padding: 0.95vh 0.8vw;
	border-bottom: 1px solid var(--rule-soft);
	vertical-align: middle;
	white-space: nowrap;
}
.dtboard .dt-num {
	text-align: right;
}
.dtboard .dt-rank {
	width: 4em;
	text-align: center;
	font-weight: 800;
}
.dtboard tr.unranked .dt-rank {
	color: var(--faint);
	font-weight: 400;
}
.dtboard .dt-store {
	font-family: var(--sans);
	font-weight: 700;
}
.dtboard .dt-avg {
	font-family: var(--mono);
	font-weight: 600;
}
/* ── 4b. The podium (phase 5) ──
   By rank (boardText.js's PODIUM_RANK and medals), not row position, so a
   tie at 3rd marks every tied row bronze. The medal colours are site
   tokens (/styles.css §1, shared with the dashboard rankings since
   2026-11-03), chosen to read on the white panel at TV distance: dark
   numerals on gold and silver, white on the deeper bronze (each well over
   3:1 for this large bold text). */
.dtboard tr.podium .dt-rank {
	color: var(--brand);
}
/* Its negative block margins keep the badge from making a podium row taller
   than the rest — twelve rows must still fit a 1080p screen. A BLOCK flex
   box (auto side margins centre it in the cell), not an inline one: an
   inline-flex badge sat on the text baseline, which is below the middle of
   the line box, so the badge looked pushed to the bottom of the row. As a
   block its margin box is 0.8em tall and the cell's vertical-align: middle
   centres that box, so the 1.4em badge overhangs top and bottom equally. */
.dt-medal {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 1.4em;
	height: 1.4em;
	margin: -0.3em auto;
	border-radius: 50%;
	font-weight: 800;
	overflow: hidden;
}
.dt-medal-gold {
	background: var(--medal-gold);
	color: var(--medal-gold-ink);
}
.dt-medal-silver {
	background: var(--medal-silver);
	color: var(--medal-silver-ink);
}
.dt-medal-bronze {
	background: var(--medal-bronze);
	color: var(--medal-bronze-ink);
}
/* The stripe down the row's left edge and the faint tint. On a podium row
   the tint is the background of EVERY cell, the two average cells included
   (the goal shows there as text colour, §4c). */
.dtboard tr.medal-gold > td {
	background: var(--medal-gold-tint);
}
.dtboard tr.medal-silver > td {
	background: var(--medal-silver-tint);
}
.dtboard tr.medal-bronze > td {
	background: var(--medal-bronze-tint);
}
.dtboard tr.medal-gold > td:first-child {
	box-shadow: inset 0.3em 0 0 var(--medal-gold);
}
.dtboard tr.medal-silver > td:first-child {
	box-shadow: inset 0.3em 0 0 var(--medal-silver);
}
.dtboard tr.medal-bronze > td:first-child {
	box-shadow: inset 0.3em 0 0 var(--medal-bronze);
}
.dtboard tr.medal-gold .dt-store {
	font-weight: 800;
}
/* A thin rule under the podium group. */
.dtboard tr.podium-end > td {
	border-bottom: 2px solid var(--rule);
}

/* ── 4c. Goal colours (phase 5) ──
   The dashboard's cell-good / -warn / -bad (dashboard.css), restated with
   enough weight to win over the company row's paper background. The text
   colour applies everywhere; the coloured BACKGROUND only on rows that are
   not on the podium (owner decision 2026-11-04): a top-3 row keeps its
   medal tint behind the cell and the goal reads as the text colour alone.
   --good/--warn/--bad on the three tints measure about 4.8:1 or better
   (the warn amber on the gold tint is the lowest), and the figures are
   large and bold. The row test is on the medal-* class (set on exactly
   the ranked 1st–3rd rows), the same as the tint rules above. */
.dtboard tr > td.cell-good {
	color: var(--good);
}
.dtboard tr > td.cell-warn {
	color: var(--warn);
}
.dtboard tr > td.cell-bad {
	color: var(--bad);
}
.dtboard tr:not([class*="medal-"]) > td.cell-good {
	background: var(--good-bg);
}
.dtboard tr:not([class*="medal-"]) > td.cell-warn {
	background: var(--warn-bg);
}
.dtboard tr:not([class*="medal-"]) > td.cell-bad {
	background: var(--bad-bg);
}

/* ── 4d. Rank movement (phase 5) ──
   Transform and opacity only — a Fire TV stick cannot afford animated
   blur, box-shadow or filters. Each effect is redrawn with a negative
   --dt-fx-delay (board.js), so a redraw resumes it rather than restarting
   it. The row slide itself is board.js's (Web Animations, FLIP). */
/* Absolute inside the rank cell, so an arrow never moves the number or
   badge (they stay centred as if it were not there). It sits just right of
   the centre. The em here is the arrow's OWN (font-size 0.6em, so 0.6 of the
   cell's): 1.5em = 0.9 cell-em clears the badge's half-width (0.7 cell-em)
   with a small gap, and a two-digit rank; the cell is 4em wide, so the
   arrow can neither reach the Store column nor be clipped. */
.dtboard td.dt-rank {
	position: relative;
}
.dt-move {
	position: absolute;
	left: calc(50% + 1.5em);
	top: 50%;
	transform: translateY(-50%);
	font-size: 0.6em;
	line-height: 1;
	animation: dt-move-fade 60s linear both;
	animation-delay: var(--dt-fx-delay, 0s);
}
.dt-move-up {
	color: var(--good);
}
.dt-move-down {
	color: var(--bad);
}
/* 60 s (owner, 2026-11-12); the last 5% — about 3 s — is the fade. */
@keyframes dt-move-fade {
	0%,
	95% {
		opacity: 1;
	}
	100% {
		opacity: 0;
	}
}
/* A brief green wash over a row that moved up. */
.dtboard tr.dt-glow > td {
	position: relative;
}
.dtboard tr.dt-glow > td::after {
	content: "";
	position: absolute;
	inset: 0;
	background: var(--good);
	opacity: 0;
	pointer-events: none;
	animation: dt-glow 1.6s ease-out both;
	animation-delay: var(--dt-fx-delay, 0s);
}
@keyframes dt-glow {
	0% {
		opacity: 0;
	}
	20% {
		opacity: 0.2;
	}
	100% {
		opacity: 0;
	}
}
/* Once, across the gold badge of a store newly 1st: a light band. */
.dt-medal.dt-shimmer::after {
	content: "";
	position: absolute;
	inset: -20%;
	background: linear-gradient(105deg, transparent 35%, rgba(255, 255, 255, 0.85) 50%, transparent 65%);
	transform: translateX(-120%);
	animation: dt-shimmer 1.4s ease-in-out both;
	animation-delay: var(--dt-fx-delay, 0s);
	pointer-events: none;
}
@keyframes dt-shimmer {
	0% {
		transform: translateX(-120%);
		opacity: 1;
	}
	100% {
		transform: translateX(120%);
		opacity: 1;
	}
}
/* The quiet reset at a daypart change (owner, 2026-11-12): the table body
   fades in and the heading slides in, once, ~0.6 s — opacity and transform
   only. board.js resumes the body's fade on a redraw (--dt-fx-delay) and
   restarts the heading's only on a change. */
.dtboard tbody.dt-quiet {
	animation: dt-quiet-in 0.6s ease-out both;
	animation-delay: var(--dt-fx-delay, 0s);
}
@keyframes dt-quiet-in {
	from {
		opacity: 0;
	}
	to {
		opacity: 1;
	}
}
.dtdaypart.dt-heading-in {
	animation: dt-heading-in 0.6s cubic-bezier(0.2, 0.7, 0.2, 1) both;
}
@keyframes dt-heading-in {
	from {
		opacity: 0;
		transform: translateY(-0.6em);
	}
	to {
		opacity: 1;
		transform: none;
	}
}
/* Reduced motion: the arrow stays (until board.js takes it away at 60 s);
   no fade, glow, shimmer, quiet fade or heading slide — the chips still
   show, without their fade. board.js skips the row slide itself. */
@media (prefers-reduced-motion: reduce) {
	.dt-move,
	.dtboard tbody.dt-quiet,
	.dtdaypart.dt-heading-in,
	.dtwin-item.dt-fadein {
		animation: none;
	}
	.dtboard tr.dt-glow > td::after,
	.dt-medal.dt-shimmer::after {
		display: none;
	}
}
.dtboard td.dt-dpavg,
.dtboard td.dt-queue,
.dtboard td.dt-car {
	font-family: var(--mono);
}
/* The daypart average is what the board ranks on (owner, 2026-11-12):
   as heavy as Today's average beside it. */
.dtboard td.dt-dpavg {
	font-weight: 600;
}
/* The current car's timer is the one figure that moves — set apart a
   little from its neighbours so the eye finds it. */
.dtboard td.dt-car {
	font-weight: 600;
	color: var(--ink);
}
/* "D2" beside a store's daypart average when it is not in the heading's
   daypart yet (the Worker's daypartDiffers) — quiet, but there. */
.dt-dpmark {
	font-size: 0.6em;
	color: var(--muted);
	vertical-align: 0.15em;
}
.dt-state {
	font-size: 0.72em;
	font-weight: 600;
}
.dt-state::before {
	content: "";
	display: inline-block;
	width: 0.6em;
	height: 0.6em;
	margin-right: 0.45em;
	border-radius: 50%;
	background: currentColor;
	vertical-align: 0.05em;
}
.dt-open {
	color: var(--good);
}
.dt-stale {
	color: var(--warn);
}
.dt-unavailable {
	color: var(--bad);
}
.dt-closed,
.dt-waiting {
	color: var(--muted);
}
.dtboard tfoot td {
	border-top: 2px solid var(--ink);
	border-bottom: none;
	background: var(--paper);
	font-weight: 800;
}
/* The board scrolls sideways inside this box at phone width; on a TV it
   never needs to. */
.dtscroll {
	overflow-x: auto;
	-webkit-overflow-scrolling: touch;
}

/* Phone width: the site's 16px gutters, a readable fixed size (the vh/vw
   size is for a TV across the room and would be ~7px here). */
@media (max-width: 700px) {
	.dtboardpage .topbar .inner {
		padding-left: 16px;
		padding-right: 16px;
	}
	.dtboardpage .wrap {
		padding: 0 16px;
	}
	.dtboard {
		--dt-size: 15px;
	}
	.dtboard th,
	.dtboard td {
		padding: 6px 8px;
	}
	.dtboard th {
		font-size: 0.7em;
	}
}

/* The screen wake lock's note: muted, small, out of the way on a TV. Its
   own top margin since the footnote above it went (2026-11-03). */
.dtwake {
	margin: 0.8vh 0 1vh;
	font-size: clamp(10px, 1.2vh, 14px);
	color: var(--muted);
}
.dtwake.is-warn {
	color: var(--warn);
}
.dtwake:empty {
	display: none;
}

/* The Current car caveat under the wake note (2026-11-04): muted like it,
   with only the warning cue in the warn colour so it is noticed, not loud. */
.dtcarnote {
	margin: 0 0 1vh;
	font-size: clamp(10px, 1.2vh, 14px);
	color: var(--muted);
}
.dtcarnote strong {
	color: var(--ink);
}
.dtcarnote-cue {
	color: var(--warn);
}
.dtcarnote:empty {
	display: none;
}

/* ── 5. Screens ───────────────────────────────────────────────────────── */
/* The board gate's pairing form, set apart from the passphrase above it. */
.lockbox .pairform {
	margin-top: 18px;
	padding-top: 16px;
	border-top: 1px solid var(--rule-soft);
}
.lockbox .pairform input {
	text-transform: uppercase;
	letter-spacing: 0.12em;
}
.pairmsg {
	min-height: 18px;
	margin-top: 11px;
	font-family: var(--mono);
	font-size: 12px;
	color: var(--bad);
}
.pairmsg:empty {
	min-height: 0;
}

/* Admin: the code, shown once, large enough to read across a room. */
.paircodebox {
	margin: 8px 0 12px;
	padding: 14px 16px;
	background: var(--panel);
	border: 1px solid var(--rule);
	border-left: 4px solid var(--brand);
}
.paircodebox p {
	margin: 0 0 6px;
}
.paircodebox .paircode {
	font-family: var(--mono);
	font-size: clamp(28px, 8vw, 44px);
	font-weight: 600;
	letter-spacing: 0.14em;
	margin: 6px 0 8px;
	overflow-wrap: anywhere;
}
.paircodebox .paircodetime {
	font-family: var(--mono);
	font-size: 13px;
}
.paircodebox.is-expired .paircode {
	text-decoration: line-through;
	color: var(--faint);
}
.screensummary {
	margin: 0 0 6px;
	font-size: 13px;
	color: var(--muted);
}
#screenstable button {
	padding: 4px 10px;
	font: inherit;
	font-size: 12px;
	font-weight: 700;
	background: none;
	border: 1px solid var(--rule);
	color: var(--bad);
	cursor: pointer;
}
#screenstable button:disabled {
	cursor: wait;
	color: var(--faint);
}
