/* Challenger Quiz app layer.

   Loads after cq-components.css. The design system covers the shared
   components (cards, tables, stat tiles, notices, buttons, fields, tabs,
   charts). This file only holds what it has no component for: the pieces
   specific to this app (Quiz Health tiles, sortable and capped tables, the searchable select, the operator's dev
   preview panel) plus a few layout helpers that replace old inline styles.

   Rule: read only --cq-* tokens, never a literal colour, so every rule
   here follows the active direction (A, B or C) and dark mode with no
   extra work. Motion follows the design system too: only transform and
   opacity are transitioned. */

/* ------------------------------------------------------------------ page */
/* Scripts show and hide things with the hidden attribute. Components that set
   their own display (.cq-card, .cq-grid, table rows) would otherwise win over
   the browser's [hidden] rule; .cq [hidden] outranks a single class, and
   this file loads last, so it also wins ties. */
.cq [hidden] { display: none; }

.app-main { min-width: 0; }

/* Lists: cq-components.css strips list bullets. Prose lists get them back. */
.app-list { list-style: disc; padding-left: var(--cq-space-6); display: grid; gap: var(--cq-space-2); }
ol.app-list { list-style: decimal; }

/* Body text on the overhaul's templates keeps to about 70 characters a line
   (DESIGN 3, the review checklist's I6); the design system sets a measure
   on its page lead only. */
.cq-tpl__main p, .cq-tpl__side p { max-width: 55ch; }

/* The chance chart's rows (.cq-chance__row, the letter chart) are 4px
   apart, the spacing scale's smallest step: the export has 2px. Its batch 2
   fix went to .cq-keypad__answer. Remove once the export fixes these. */
.cq-chance__scale, .cq-chance__row { row-gap: var(--cq-space-1); }
/* A link in a question row wraps with the question: .cq-link is nowrap, for
   short links. Remove once the export has a wrapping text link. */
.cq-qrow__q .cq-link { display: inline; white-space: normal; }

/* Long-form pages (terms, privacy). */
.app-prose { display: grid; gap: var(--cq-space-4); max-width: 72ch; }
.app-prose h2 { font-size: var(--cq-text-lg); font-weight: var(--cq-weight-strong); margin-top: var(--cq-space-6); }

/* ------------------------------------------------------------------ layout helpers */
.app-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--cq-space-3) var(--cq-space-4); min-width: 0; }
.app-row--tight { gap: var(--cq-space-2); }

.app-row--top { align-items: flex-start; }

/* Field and button rows: the button lines up with the input, not the label,
   and inputs stay level when a long label wraps. */
.app-row--bottom { align-items: flex-end; }
.app-row > .app-grow { flex: 1 1 auto; min-width: 0; }

.app-inline-form { display: inline; }

.app-break { overflow-wrap: anywhere; }
.app-nowrap { white-space: nowrap; }
.app-pre { white-space: pre-wrap; overflow-wrap: anywhere; }

/* A filter form that has just submitted: dimmed until the next page loads. */
.app-submitting { opacity: 0.6; pointer-events: none; }

/* Tab strips: the overflow edge shadow has to match what sits behind the
   strip, the page or a card. Round tabs are tighter (12px either side). */
.cq-card .cq-tabs, .cq-chart .cq-tabs { --cq-tabs-edge: var(--cq-surface); }

/* Buttons, tabs, pills, segmented controls, table headings and standalone
   links (the page header's, a card head's) never wrap onto a second line.
   The design system lets them wrap (listed as a design system fix). */
.cq-btn, .cq-tab, .cq-badge, .cq-seg__btn, .cq-table thead th, .cq-page__actions a, .cq-gauge__head > a { white-space: nowrap; }
/* Info tips (tip_shell): the icon is a button and the note a native popover
   in the top layer, placed by app.js: under 480px a panel 16px in from each
   screen edge with an arrow at the icon; wider, under the icon, leftward
   when the icon is in the right half of its card. The note is display: none
   unless open (an author display value would override the browser's for a
   closed popover), hidden until placed, and its text is left-aligned
   wherever the tip sits, a right-aligned number column included. The
   InfoTip is a <details> that opens inline, where a scroll box clips it
   (listed as a design system fix). */
button.cq-tip__btn { border: 0; background: none; padding: 0; font: inherit; }
.cq-tip__btn[aria-expanded="true"] { color: var(--cq-ink); }
.cq-tip__btn[aria-expanded="true"]::before { opacity: 1; }
.cq-tip > .cq-tip__panel { position: fixed; inset: auto; margin: 0; overflow: visible; text-align: left; white-space: normal;
  font-weight: var(--cq-weight-regular); color: var(--cq-ink); }
.cq-tip > .cq-tip__panel:not(:popover-open):not(.is-open) { display: none; }
.cq-tip > .cq-tip__panel:popover-open, .cq-tip > .cq-tip__panel.is-open { display: block; }
.cq-tip > .cq-tip__panel:not(.is-placed) { visibility: hidden; }
.cq-tip > .cq-tip__panel::backdrop { background: transparent; }
.cq-tip > .cq-tip__panel.is-sheet { max-width: none; }
.cq-tip > .cq-tip__panel.is-sheet::before { content: ""; position: absolute; top: -7px; left: calc(var(--app-tip-arrow, 24px) - 6px);
  width: 12px; height: 12px; background: var(--cq-surface); border-left: 1px solid var(--cq-border-strong);
  border-top: 1px solid var(--cq-border-strong); transform: rotate(45deg); }
.cq-tip > .cq-tip__panel.is-sheet.is-above::before { top: auto; bottom: -7px; transform: rotate(225deg); }

/* Under 480px the brand, the bell and the account button don't fit on one
   line with the account name, so the button is its icon only (its label
   still names the account). */
@media (max-width: 479px) {
  .cq-appbar__end .cq-account__name { display: none; }
  .cq-appbar__end { flex: none; }
}
/* The app bar from 720px: one 64px line with every item centred on it. The
   AppBar lines the quiz name up on the brand's text baseline, which the
   taller logo mark pushes down, and draws the divider as the name's border,
   so it follows the name's line box. Here the divider is its own 24px rule,
   and each nav link fills the bar's height so its underline sits inside the
   bar's bottom edge without moving the link text. */
@container cq (min-width: 720px) {
  .cq-appbar { min-height: 64px; height: 64px; align-items: stretch; }
  .cq-appbar__id, .cq-appbar__end { align-self: center; }
  .cq-appbar__id { align-items: center; }
  /* 2px above and below the 20px line make the 24px box the divider fills
     (the name clips its overflow, so the rule can't reach outside it). */
  .cq-appbar__context { position: relative; margin: 0; padding-block: 2px; border-left: 0; font-size: 14px; line-height: 20px; }
  .cq-appbar__context::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 1px; background: var(--cq-border); }
  .cq-appbar__nav { align-items: stretch; }
  .cq-appbar__nav a { min-height: 0; }
  .cq-appbar__nav a::before { inset: 10px 0; }
}

/* ------------------------------------------------------------------ fields the design system doesn't draw */
textarea.cq-input { min-height: 7em; padding-block: var(--cq-space-3); line-height: var(--cq-lh-body); resize: vertical; }
input[type="file"].cq-input { padding-block: var(--cq-space-3); }
.cq-input--narrow { max-width: 12ch; }
.cq-select--inline { display: inline-block; width: auto; }
.cq-select--inline select { width: auto; }

/* ------------------------------------------------------------------ tables: sort and cap */
/* Sortable headers (app.js): an icon after the label, never a text arrow
   (iPhones draw those as emoji). Faint while unsorted, full once sorted. */
.cq-table th.sortable-th { cursor: pointer; user-select: none; white-space: nowrap; }
.cq-table th.sortable-th .sort-icon { font-size: 14px; margin-left: var(--cq-space-1); vertical-align: -2px; opacity: 0.45; }
.cq-table th.sortable-th[aria-sort] .sort-icon { opacity: 1; color: var(--cq-ink); }
.cq-table th.sortable-th:focus-visible { outline: var(--cq-focus-w) solid var(--cq-focus); outline-offset: -2px; }
@media (hover: hover) { .cq-table th.sortable-th:hover { color: var(--cq-ink); } }

/* Imports table: the week in bold over the quiz date, each its own link,
   and a More menu at the row's end. Nothing wraps from 600px. */

.table-cap-toggle { display: flex; justify-content: center; padding: var(--cq-space-3); border-top: 1px solid var(--cq-border); }

/* Small inline forms and controls inside a table cell. */

/* ------------------------------------------------------------------ searchable select (progressive enhancement in app.js) */
.searchable-select { position: relative; width: 100%; max-width: 360px; }
.searchable-select select { display: none; }
.searchable-select-list { display: none; position: absolute; left: 0; right: 0; top: calc(100% + var(--cq-space-2)); z-index: 20;
  max-height: 260px; overflow-y: auto; background: var(--cq-surface); border: 1px solid var(--cq-border-strong);
  border-radius: var(--cq-radius-md); box-shadow: var(--cq-shadow-2); }
.searchable-select-list.open { display: block; }
.searchable-select-list div { display: flex; align-items: center; min-height: var(--cq-control-h); padding: 0 var(--cq-space-4); cursor: pointer; }

@media (hover: hover) { .searchable-select-list div:hover { background: var(--cq-overlay-hover); } }
.searchable-select-list .searchable-select-empty { color: var(--cq-ink-muted); cursor: default; }
.searchable-select-list .searchable-select-empty:hover { background: none; }

/* ------------------------------------------------------------------ Quiz Health findings */

/* "Show all N teams as a table" under a chart: it stays inside the card
   and its weeks scroll sideways with the team column pinned. The DataTable
   pins a column only in TeamsByWeeks (listed as a design system fix). */
/* Chart headers: at least 24px between the title and description and any
   control beside them; the controls move under the text when both don't
   fit, and on a phone. The ChartContainer's header is a two-column grid
   whose text column can shrink to nothing under wide controls (listed as a
   design system fix). */
@container cq (min-width: 600px) {
  .cq-chart__head { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--cq-space-3) var(--cq-space-6); }
  .cq-chart__head > .cq-chart__titles { flex: 1 1 20em; min-width: 0; }
  .cq-chart__head > :not(.cq-chart__titles) { flex: 0 0 auto; margin-left: auto; }
}

/* A chart's view switch on a phone: the full row, its buttons sharing it
   with tighter padding, so three views fit 360px. */
@container cq (max-width: 479px) {
  .cq-chart__controls > .cq-seg { flex: 1 1 100%; }
  .cq-chart__controls .cq-seg__btn { flex: 1 1 auto; padding-inline: var(--cq-space-2); }
}
/* Below about 1100px of screen (1040px of content) the round chart's
   controls go under its description and the side panel under the chart,
   so neither squeezes the lines. */

.cq-chart__data { min-width: 0; }
.cq-chart__data .cq-table th.cq-col-team { position: sticky; left: 0; z-index: 1; background: var(--cq-card-bg); }
/* Week grids on a phone: the pinned team column is 136px at most, its
   names wrapping, with its edge in the table's own border colour. */
@media (max-width: 479px) {
  .cq-chart__data .cq-table th.cq-col-team, .cq-chart__data .cq-table thead th:first-child {
    width: 136px; max-width: 136px; min-width: 0; white-space: normal; overflow-wrap: anywhere; box-shadow: inset -1px 0 0 var(--cq-border); }
}
/* Ranked tables (standings, badge rankings) on a phone: three columns only.
   The rank in 32px, the team taking the rest and wrapping to two lines at
   most, the main figure in 64px on the right with any second figure under
   it, small and muted. No sort arrows; the viewing team is the row
   highlight with a 4px coral bar on its left edge. The DataTable has no
   three-column phone layout (listed as a design system fix). */

.cq-chart__data .cq-table thead th:first-child { position: sticky; left: 0; z-index: 3; background: var(--cq-th-bg); }

/* The demo bar under the app bar: a Notice with the demo's switches in a
   wrapping row (listed as a design system fix). */
/* It lines up with the page's content column: the page's width (1200px at
   most) less its side padding, centred like the page. */
.app-demo-bar { box-sizing: border-box; width: calc(100% - 2 * var(--cq-page-pad)); margin: var(--cq-space-3) auto 0; }
@media (min-width: 720px) {
  .app-demo-bar { width: calc(min(100%, 1200px) - 2 * var(--cq-page-pad-wide)); }
}
.app-demo-bar__controls { display: flex; flex-wrap: wrap; align-items: center; gap: var(--cq-space-2) var(--cq-space-3);
  margin-top: var(--cq-space-2); }
.app-demo-bar__controls .cq-seg { justify-self: auto; }

/* The guided tour (tour.js) on the GuidedTour shell. Between steps the
   card fades out, the page and the spotlight move in one frame, and the
   card fades back in: only opacity animates (the shell on opening, the
   card between steps). The spotlight never changes size or shadow frame by
   frame, since its shadow is the whole screen's dimming and repainting it
   every frame dropped frames on iPhones. The overlay takes every touch and
   wheel, so the page can't move under it. Under reduced motion every change
   is an instant cut. The shell's spotlight jumps and its card slides in
   once per show, and it has no close button or arrival line (listed as
   design system fixes). */
.app-tour { transition: opacity 150ms var(--cq-ease-standard); touch-action: none; overscroll-behavior: contain; will-change: opacity; }
.app-tour.is-entering { opacity: 0; }
.app-tour .cq-tour__spot { left: 0; top: 0; transform: translate(var(--sx), var(--sy)); }
.app-tour .cq-tour__card { animation: none; transition: opacity 150ms var(--cq-ease-standard); will-change: opacity; }
.app-tour .cq-tour__card.is-out { opacity: 0; }
.app-tour [data-tour-close] { margin-left: auto; margin-right: calc(-1 * var(--cq-space-2)); }
.app-tour .cq-tour__progress { gap: var(--cq-space-3); }

@media (prefers-reduced-motion: reduce) {
  .app-tour, .app-tour .cq-tour__card { transition-duration: 0s; }
}

/* Each row opens in place to its teams; with a desktop pointer the list
   also shows as a tooltip while the row is hovered. Every bar is one colour. */
.qh-dist { list-style: none; margin: 0; padding: 0; font-size: var(--cq-text-sm); }
.qh-dist > li { position: relative; }

/* ------------------------------------------------------------------ notifications inbox */
/* The message stops at 3 lines; the full text is on the page it opens. On a
   phone the row stacks: dot and title, then the message, then the quiz and
   date in small muted text. The Notifications component keeps the time in
   a third column at every width (listed as a design system fix). */
.cq-inbox__sub { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; }
@container cq (max-width: 599px) {
  .cq-inbox__row { grid-template-columns: 10px minmax(0, 1fr); row-gap: var(--cq-space-1); }
  .cq-inbox__time { grid-column: 2; white-space: normal; font-size: var(--cq-text-xs); }
}

/* ------------------------------------------------------------------ team codes: QR login on a phone */

/* Filter (search): a row the search hides stays hidden whatever display
   its table or list gives it (the deprecated LiveFilter carried this rule;
   the Filter has none yet). */
[data-cq-filter] [hidden] { display: none !important; }
/* The week form (log_week.html): attendance shows while its switch is on. */
.app-weekform:not(:has([data-attendance-switch]:checked)) [data-attendance] { display: none; }
/* A Full-screen moment's one secondary link, centred with a 44px tap
   area (the design system's preview sets both inline). */
.app-moment-link { display: inline-flex; align-items: center; min-height: 44px; justify-self: center; }
/* Design system export gap: a fieldset used as a Field or a Form group
   keeps the browser's border and padding (its preview resets them inline). */
fieldset.cq-field, fieldset.cq-form2__fields, fieldset.cq-form2__optional { border: 0; padding: 0; margin: 0; min-width: 0; }
fieldset.cq-form2__optional { padding-top: var(--cq-space-6); border-top: 1px solid var(--cq-border); }
/* A team's login view (team_qr.html): the design system's full-screen
   cq-qr, fixed over the app bar and the bottom navigation, its switch and
   close button on one row and previous and next on another. The design
   system's cq-qr has no done state for a view that also has the live
   parts, so done hides them and shows the done block. */
.app-qr { position: fixed; inset: 0; z-index: 90; overflow-y: auto; background: var(--cq-bg); align-content: start; }
.app-qr__top, .app-qr__nav { justify-self: stretch; width: min(100%, 480px); margin-inline: auto; }
.app-qr__nav { margin-top: auto; }
.app-qr[data-state="done"] [data-qr-live-only], .app-qr[data-state="done"] .cq-qr__live { display: none; }
.app-qr[data-state="done"] .cq-qr__done { display: grid; justify-items: center; gap: var(--cq-space-4); }
/* From 960px the view is on the host's screen for the room to scan: the
   code (and its timer bar) grows to the smaller of 60vh and 520px. On a
   phone it keeps the design system's 280px. */
@media (min-width: 960px) { .app-qr .cq-qr__code, .app-qr .cq-qr__timer { width: min(60vh, 520px); } }

/* The QR shortcut card: phones only (a narrow screen with a touch pointer),
   fixed above the bottom navigation. No backdrop; it slides down behind the
   navigation once the page scrolls. The design system has no floating
   prompt card (listed as a design system fix). */
.app-qr-card { display: none; }
@media (max-width: 599px) and (pointer: coarse) {
  .app-qr-card:not([hidden]) { position: fixed; left: var(--cq-space-4); right: var(--cq-space-4); z-index: 45;
    bottom: calc(var(--cq-nav-h) + var(--cq-space-3) + env(safe-area-inset-bottom, 0px));
    display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: var(--cq-space-2) var(--cq-space-3);
    padding: var(--cq-space-3) var(--cq-space-3) var(--cq-space-2) var(--cq-space-4);
    background: var(--cq-card-bg); border: 1px solid var(--cq-border-strong); border-radius: var(--cq-radius-md); box-shadow: var(--cq-shadow-2);
    transition: transform var(--cq-dur-base) var(--cq-ease-exit); }
  .app-qr-card.is-away { transform: translateY(calc(100% + var(--cq-nav-h) + var(--cq-space-5))); }
}
@media (prefers-reduced-motion: reduce) { .app-qr-card { transition: none; } }
.app-qr-card__title { margin: 0; font-weight: var(--cq-weight-strong); }
.app-qr-card [data-qr-card-close] { grid-column: 2; grid-row: 1; margin: calc(-1 * var(--cq-space-2)) calc(-1 * var(--cq-space-2)) 0 0; }
.app-qr-card > .cq-btn { grid-column: 1 / -1; justify-self: start; }
.app-qr-card__switch { grid-column: 1 / -1; }
.app-qr-card__switch .cq-switch { font-weight: var(--cq-weight-regular); }

/* Every numeric table column, app-wide: right-aligned, tabular figures. */
.cq-table th.cq-col-num, .cq-table td.cq-col-num { text-align: right; font-variant-numeric: tabular-nums lining-nums; }

/* The ramp's mix of the key colour into the card: 1 to 4 keep the ink
   text, 5 (the full colour) the card's colour as text, so both read in
   light and dark. Dark mode's light key colour needs gentler steps for the
   light ink to keep its contrast. */
:root { --app-key-1: 12%; --app-key-2: 25%; --app-key-3: 40%; --app-key-4: 55%; }
@media (prefers-color-scheme: dark) {
  :root:not([data-mode="light"]) { --app-key-1: 10%; --app-key-2: 18%; --app-key-3: 28%; --app-key-4: 40%; }
}
:root[data-mode="dark"] { --app-key-1: 10%; --app-key-2: 18%; --app-key-3: 28%; --app-key-4: 40%; }

/* The team lists' "Teams this season / All teams" switch: its own width
   from 600px, full width on a phone. */
@container cq (min-width: 600px) { .app-team-scope { justify-self: start; align-self: flex-start; } }

/* Tabs under 480px: equal widths across the strip, no sideways scroll and
   no edge fade (the design system's Tabs scroll on a phone; listed as a
   design system fix). */
@container cq (max-width: 479px) {
  .cq-tabs { width: 100%; overflow: visible; background: var(--cq-tabs-bg); scroll-snap-type: none; }
  .cq-tab { flex: 1 1 0; min-width: 0; justify-content: center; padding: 0 var(--cq-space-1); }

}

button.cq-tab { border: 0; background: none; font: inherit; cursor: pointer; }

/* ------------------------------------------------------------------ operator-only dev preview panel */
.dev-preview-panel { position: fixed; right: var(--cq-space-5); bottom: calc(var(--cq-nav-h) + var(--cq-space-5) + env(safe-area-inset-bottom, 0px));
  z-index: 55; display: grid; gap: var(--cq-space-2); padding: var(--cq-space-3) var(--cq-space-4);
  background: var(--cq-surface); border: 1px solid var(--cq-border-strong); border-radius: var(--cq-radius-md); box-shadow: var(--cq-shadow-2); }
.dev-preview-panel__label { font-size: var(--cq-text-xs); font-weight: var(--cq-weight-strong); color: var(--cq-ink-muted); cursor: pointer;
  border: 0; background: none; padding: 0; text-align: left; }
.dev-preview-panel__row { display: flex; gap: var(--cq-space-2); }
.dev-preview-panel__row .cq-select select { min-height: var(--cq-control-sm-h); font-size: var(--cq-text-sm); max-width: 180px; }
.dev-preview-panel__short { display: none; }
/* On a phone the closed panel is a small pill button reading "Dev". */
@container cq (max-width: 480px) {
  .dev-preview-panel__row { display: none; }
  .dev-preview-panel--expanded .dev-preview-panel__row { display: flex; }
  .dev-preview-panel:not(.dev-preview-panel--expanded) { padding: 0; border: 0; background: none; box-shadow: none; }
  .dev-preview-panel:not(.dev-preview-panel--expanded) .dev-preview-panel__label {
    min-width: 44px; min-height: 44px; padding: 0 var(--cq-space-3); border-radius: var(--cq-radius-pill);
    background: var(--cq-surface); border: 1px solid var(--cq-border-strong); box-shadow: var(--cq-shadow-2); text-align: center; }
  .dev-preview-panel:not(.dev-preview-panel--expanded) .dev-preview-panel__short { display: inline; }
  .dev-preview-panel:not(.dev-preview-panel--expanded) .dev-preview-panel__long { display: none; }
}
@container cq (min-width: 720px) {
  .dev-preview-panel { bottom: var(--cq-space-5); }
}

/* ------------------------------------------------------------------ footer */
.app-footer { text-align: center; font-size: var(--cq-text-xs); color: var(--cq-ink-muted); padding-top: var(--cq-space-6); }
.app-footer .cq-link { font-size: var(--cq-text-xs); }

/* page: quiz_badges_leaderboard_partial.html, badge_category_ranking.html */

/* page: invites.html */

/* page: privacy.html, terms.html */
.app-prose h4 { margin-top: var(--cq-space-5); }

/* page: topics.html, team_config.html */

/* page: quiz_detail.html */

/* A text input sharing a row with its button. */
.cq-input.app-input--mid { max-width: 260px; }

/* page: quiz_standings.html */

/* page: team_analytics.html, team_analytics_badges_partial.html, question_list.html, question_detail.html, season_compare.html, round_slot_detail.html */
/* Badges arrive after the page: they fade in once, on the design system's keyframe. */
.app-fade-in { animation: cq-fade var(--cq-dur-base) var(--cq-ease-standard) both; }
/* A stat tile that links to its questions. */
a.cq-stat { color: inherit; text-decoration: none; }
@media (hover: hover) { a.cq-stat:hover .cq-stat__value { color: var(--cq-accent-ink); } }
/* Round by round: the slot summary's count and trend link sit in body text, not the summary's heading weight. */

#teamSections .cq-round__note .cq-icon { color: var(--cq-warning-ink); }
/* Answers keep the line breaks SpeedQuizzing stores in them. */
.app-pre-line { white-space: pre-line; }

/* Question tables (cq-table--stack): week and round are their own columns from
   600px; on a phone they are hidden and repeated in a line under the result.
   The edit link takes its own row under the result. */
.cq-table--stack td[data-area="meta"] { display: none; }
.cq-table--stack td[data-area="act"] { grid-column: 2 / -1; }
@container cq (min-width: 600px) { .cq-table--stack td[data-area="meta"] { display: table-cell; } }

/* page: team_analytics.html, team_analytics_badges_partial.html, question_list.html, question_detail.html, season_compare.html, round_slot_detail.html (app-wide pass) */

/* page: session_leaderboard.html */

/* An ordering answer: the team's order in the right order's numbers, a
   number in the wrong place in the wrong colour. */
.app-ord__n--wrong { color: var(--cq-error-ink); font-weight: var(--cq-weight-strong); }

/* page: admin_analytics.html (operator only) */

/* ------------------------------------------------------------------ desktop
   The design system is phone first, so wide screens got the phone stack
   stretched out. These rules only start at 720px, 960px and 1200px of
   window width (media queries, since the columns below become containers
   of their own), so the phone layout is untouched. The design system has
   no page-level two-column layouts or desktop metric grids (listed as
   design system fixes). */

/* Pages run up to 1200px wide, centred; narrow pages keep their own width. */
.cq-page:not(.cq-page--form) { --cq-page-max: 1200px; }
/* Running text stops at about 70 characters a line. :where keeps this below
   any component's own measure. */
:where(.app-main) p, :where(.app-main) .cq-notice__body, :where(.app-main) .cq-insight__body { max-width: 70ch; }

/* Every table shows all its columns from 720px of window, even inside a
   column narrower than the design system's 600px table breakpoint. */
@media (min-width: 720px) {
  .cq-table .cq-col-opt { display: table-cell; }
  .cq-rowmeta { display: none; }
}

@media (min-width: 960px) {

  /* Team page round cards, three across. */
  .cq-grid--rounds { grid-template-columns: repeat(3, minmax(0, 1fr)); }

}

@media (min-width: 1200px) {
  .cq-grid--rounds { grid-template-columns: repeat(4, minmax(0, 1fr)); }

}

/* ------------------------------------------------------------------ Demo tour and demo bar (launch batch) */

/* The demo bar, pinned to the top of the screen above everything but the
   skip link, so End demo is one tap away at every moment, the tour and the
   full-screen QR code included. The page starts under it, and whatever
   else sticks to the top (the round chip bar) sticks under it. It stays
   still while the tour cross-fades to another page. The design system has
   no demo bar (listed as a design system fix). */
.app-in-demo { --app-sticky-top: calc(56px + env(safe-area-inset-top, 0px)); padding-top: var(--app-sticky-top); }
html:has(> body.app-in-demo) { scroll-padding-top: var(--app-sticky-top, 0px); }
.app-demo-strip { position: fixed; top: 0; left: 0; right: 0; z-index: 95; display: flex; align-items: center; gap: var(--cq-space-2);
  height: var(--app-sticky-top); padding: env(safe-area-inset-top, 0px) var(--cq-page-pad) 0; background: var(--cq-surface);
  border-bottom: 1px solid var(--cq-border-strong); view-transition-name: app-demo-strip; }
.app-demo-strip__label { margin: 0 auto 0 0; min-width: 0; font-weight: var(--cq-weight-strong); white-space: nowrap; }
.app-demo-strip form { margin: 0; }
.app-demo-strip .cq-btn { min-height: 44px; white-space: nowrap; }
.app-in-demo .app-qr { top: var(--app-sticky-top); }

/* The tour card has no step dots and no Skip tour (the close button ends
   it), so Back and Next sit at the right. */
.app-tour .cq-tour__actions { justify-content: flex-end; }

/* Room at the foot of the page while the tour shows, so an element near
   the end can scroll clear of the card (tour.js). */
html.app-touring body { padding-bottom: 100vh; }

/* Moving to another page: the card and the ring fade out and the page
   stays dimmed; the new page stays dimmed with no card (is-waiting) until
   its step is in place. The shell's spotlight is only ever ring and scrim
   together (listed as a design system fix). */
.app-tour.is-leaving .cq-tour__card { opacity: 0; }
.app-tour.is-leaving .cq-tour__spot, .app-tour.is-waiting .cq-tour__spot { box-shadow: 0 0 0 0 transparent, 0 0 0 100vmax var(--cq-scrim); }
.app-tour.is-waiting .cq-tour__card { visibility: hidden; opacity: 0; }

/* The cross-fade between demo pages (base.html opts in on demo, landing and
   tour pages, only without reduced motion), and the fallback for browsers
   without cross-document view transitions: the page fades in on load. */
::view-transition-old(root), ::view-transition-new(root) { animation-duration: 200ms; }
@media (prefers-reduced-motion: no-preference) {
  html.app-fade-in body { animation: app-page-in 200ms var(--cq-ease-standard) both; }
}
@keyframes app-page-in { from { opacity: 0; } }

/* Quiz Health (launch batch) */

/* A tip panel holding more than one paragraph (the sub-score cards' tips:
   what 100 means, then the full figure). */
.cq-tip__panel > * + * { margin-top: var(--cq-space-2); }


/* ------------------------------------------------------------------ season replay */
/* The season replay (season_replay.html, static/js/season_replay.js): fixed
   over the whole window with none of the app's chrome. The table takes 38%
   of the width beside the chart in landscape, the top half above it
   otherwise. Each row's height comes from the pane and the team count
   (--app-replay-row-h, set by the script, 32px at least), and the rows
   that don't fit at 32px become "and N more teams". */
.app-replay { position: fixed; inset: 0; z-index: 1; display: flex; flex-direction: column; gap: 24px;
  padding: 32px; overflow: hidden; background: var(--cq-bg); color: var(--cq-ink); }
.app-replay__head { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: start; gap: 16px; }
.app-replay__title { margin: 0; font-size: 18px; font-weight: var(--cq-weight-strong); color: var(--cq-ink-muted); }
.app-replay__now { text-align: center; }
.app-replay__week { margin: 0; font-size: 40px; line-height: 1.1; font-weight: 600; font-variant-numeric: tabular-nums; }
.app-replay__date { margin: 4px 0 0; font-size: 14px; color: var(--cq-ink-muted); }
.app-replay__back { justify-self: end; font-size: 14px; }
.app-replay__body { flex: 1; min-height: 0; display: grid; grid-template-rows: minmax(0, 1fr) minmax(0, 1fr); gap: 24px; }
@media (orientation: landscape) and (min-width: 720px) {
  .app-replay__body { grid-template-columns: 38% minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); }
}

.app-replay__table { min-height: 0; overflow: hidden; }
.app-replay__rows { margin: 0; padding: 0; list-style: none; }
.app-replay__row, .app-replay__more { height: var(--app-replay-row-h, 32px); font-size: 16px; }
.app-replay__row { display: grid; grid-template-columns: 40px 56px 6px minmax(0, 1fr) auto; align-items: center; column-gap: 8px;
  padding: 0 8px; border-bottom: 1px solid var(--cq-border); background: var(--cq-bg); }
.app-replay__place, .app-replay__pts { font-weight: 600; font-variant-numeric: tabular-nums; }
.app-replay__pts { text-align: right; }
.app-replay__key { width: 6px; height: 20px; border-radius: 3px; }
.app-replay__name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-weight: var(--cq-weight-strong); }
.app-replay__more { margin: 0; display: flex; align-items: center; padding: 0 8px; color: var(--cq-ink-muted); }
.app-replay__more[hidden] { display: none; }
.app-replay__chart { position: relative; min-height: 0; min-width: 0; }

.app-replay__controls { display: flex; align-items: center; gap: 12px; box-sizing: border-box; height: 64px; padding: 12px 16px;
  border-top: 1px solid var(--cq-border); background: var(--cq-bg); transition: opacity 300ms var(--cq-ease-standard); }
.app-replay__controls [hidden] { display: none; }
.app-replay__controls .cq-btn { min-height: 40px; height: 40px; }
.app-replay__play { width: 96px; padding: 0; }
.app-replay__loop[aria-pressed="true"] { background: var(--cq-accent-surface); border-color: var(--cq-accent); }
.app-replay__speed { width: auto; }
.app-replay__speed select { min-height: 40px; height: 40px; }
@media (max-width: 719px) {
  .app-replay__controls { flex-wrap: wrap; height: auto; }
}
.app-replay.is-idle { cursor: none; }
.app-replay.is-idle .app-replay__controls { opacity: 0; pointer-events: none; }

/* The scrubber: an 18px thumb, the week's label ("W6") riding above it and
   a tick per week under the track, each centred on its thumb position. */
.app-replay__scrubwrap { position: relative; flex: 1 1 12rem; min-width: 8rem; height: 40px; }
.app-replay__scrub { position: absolute; left: 0; right: 0; top: 14px; width: 100%; height: 18px; margin: 0;
  -webkit-appearance: none; appearance: none; background: transparent; cursor: pointer; }
.app-replay__scrub::-webkit-slider-runnable-track { height: 4px; border-radius: var(--cq-radius-pill); background: var(--cq-border-strong); }
.app-replay__scrub::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 18px; height: 18px; margin-top: -7px;
  border-radius: 50%; background: var(--cq-accent); border: 2px solid var(--cq-bg); }
.app-replay__scrub::-moz-range-track { height: 4px; border-radius: var(--cq-radius-pill); background: var(--cq-border-strong); }
.app-replay__scrub::-moz-range-thumb { box-sizing: border-box; width: 18px; height: 18px; border-radius: 50%;
  background: var(--cq-accent); border: 2px solid var(--cq-bg); }
.app-replay__scrub:focus-visible { outline: var(--cq-focus-w) solid var(--cq-focus); outline-offset: var(--cq-focus-offset); border-radius: var(--cq-radius-pill); }
.app-replay__thumblabel { position: absolute; top: 0; left: calc(9px + var(--app-replay-p, 0) * (100% - 18px)); transform: translateX(-50%);
  font-size: 12px; line-height: 14px; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.app-replay__ticks { position: absolute; left: 8px; right: 8px; top: 34px; display: flex; justify-content: space-between; }
.app-replay__ticks span { width: 2px; height: 6px; background: var(--cq-ink-muted); }
.app-replay__error { margin: auto; }

/* ------------------------------------------------------------------ week highlights */
/* A team's best catch and worst miss (_week_highlights.html), on the team
   page's week view and in the welcome screen's dialog. The dialog is the
   Overlay as the tour card draws it: a bottom sheet on phones, a 400px card
   from 600px, over the scrim. The design system has no dialog of its own. */
.app-highlights { display: grid; gap: var(--cq-space-5); }
.app-highlight { display: grid; gap: var(--cq-space-1); }
.app-highlight__q { font-weight: var(--cq-weight-strong); line-height: var(--cq-lh-snug); }
.app-highlights-dialog { margin: auto auto 0; width: 100%; max-width: none; max-height: 90vh; overflow-y: auto; border: 0;
  display: none; gap: var(--cq-space-5); padding: var(--cq-space-5) var(--cq-page-pad) calc(var(--cq-space-5) + env(safe-area-inset-bottom, 0px));
  background: var(--cq-surface); color: var(--cq-ink); border-radius: var(--cq-radius-lg) var(--cq-radius-lg) 0 0; box-shadow: var(--cq-shadow-2);
  animation: cq-sheet-in var(--cq-dur-slow) var(--cq-ease-enter) both; }
.app-highlights-dialog[open] { display: grid; }
.app-highlights-dialog::backdrop { background: var(--cq-scrim); }
.app-highlights-dialog__head { display: flex; align-items: center; justify-content: space-between; gap: var(--cq-space-3); }
.app-highlights-dialog form .cq-btn { width: 100%; }
@media (min-width: 600px) {
  .app-highlights-dialog { margin: auto; width: 400px; padding: var(--cq-space-5); border-radius: var(--cq-radius-lg); animation-name: cq-menu-in; }
}
@media (prefers-reduced-motion: reduce) { .app-highlights-dialog { animation: none; } }
