/* Base styles shared by the viewer, operator and login pages.
   Page-specific rules stay in their templates. */

/* ---- Fonts ----
   Served from this binary rather than fonts.googleapis.com. The pages have to
   work on an event LAN with no route to the internet, and an @import in the
   first line of a stylesheet blocks rendering until it resolves -- on an
   isolated network that means waiting out a DNS timeout before anything
   appears. Only the latin and cyrillic subsets are shipped; anything outside
   them falls back to the system font.

   IBM Plex is licensed under the SIL Open Font License 1.1, see
   static/fonts/LICENSE.txt.

   IBM Plex Sans stops at 700, so the 800 used for headings has always been
   the browser's synthetic bold. That is unchanged here. */
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-mono-500-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-mono-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-mono-600-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-mono-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-mono-700-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-mono-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-sans-400-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-sans-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-sans-500-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-sans-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-sans-600-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-sans-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-sans-700-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-sans-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ---- Palette ----
   Every colour in the app comes from a token defined here, so the dark theme
   is a matter of redefining the tokens rather than hunting hex values through
   three templates.

   Theme resolution has three states:
     - no data-theme attribute  -> follow the operating system
     - data-theme="dark"        -> forced dark
     - data-theme="light"       -> forced light
   The dark values are therefore written twice: once behind the media query
   (guarded so an explicit light choice still wins) and once behind the
   attribute (so the forced choice beats the system preference). */

:root {
  --bg: #F4F5F7;
  --surface: #FFFFFF;
  --surface-2: #F8F9FA;
  --surface-3: #EEF0F3;

  --border: #DDE1E6;
  --border-strong: #C5CBD3;
  --divider: #ECEEF1;
  --rule: #C5CBD3;

  --text: #1C2430;
  --text-strong: #0F1720;
  /* Both greys clear 4.5:1 against every ground they are used on: white,
     --bg, the zebra stripe and the accent tint of the viewer's own row. */
  --muted: #535D6B;
  --faint: #5A6370;

  /* One restrained accent for interface chrome. Tours and medals no longer
     carry hues of their own, which is what keeps the pages businesslike. */
  --accent: #1F4E79;
  --accent-strong: #173B5C;
  --accent-solid: #1F4E79;
  --accent-solid-hover: #173B5C;
  --accent-soft: #E8EEF5;
  --accent-ring: rgba(31, 78, 121, 0.18);
  /* Text drawn on top of a filled --accent-solid / --ok-strong / --err-strong
     button. */
  --on-accent: #FFFFFF;
  --on-ok: #FFFFFF;
  --on-err: #FFFFFF;

  /* Green and red carry meaning only -- right and wrong answers, easy and
     hard questions, feedback, deletion, movement -- never decoration. */
  --ok-bg: #E7F1EA;
  --ok-text: #24573A;
  --ok-border: #C4DBCB;
  --ok-strong: #2F6B47;

  --err-bg: #F7E8E8;
  --err-text: #8A2B2B;
  --err-border: #E5C3C3;
  --err-strong: #9E3434;

  /* Answers keep the colours everyone reads without a legend: green for
     right, red for wrong. Both fills are soft so a grid of 90 cells per row
     stays calm, and both differ clearly from the white and zebra grounds,
     which is what separates "answered" from "not asked yet". */
  --answer-correct-bg: #DDEFE3;
  --answer-correct-text: #24573A;
  --answer-wrong-bg: #F6E0E0;
  --answer-wrong-text: #8A2B2B;

  --zebra: #F6F7F9;
  --row-hover: #EDF2F7;

  --shadow-sm: none;
  --shadow-md: 0 1px 2px rgba(15, 23, 32, 0.06);
  --shadow-lg: 0 8px 28px rgba(15, 23, 32, 0.16);
  --shadow-frozen: 3px 0 6px rgba(15, 23, 32, 0.05);
  --shadow-frozen-left: -3px 0 6px rgba(15, 23, 32, 0.05);

  --scrim: rgba(15, 23, 32, 0.45);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #16181B;
    --surface: #1F2226;
    --surface-2: #25292E;
    --surface-3: #2C3036;

    --border: #363B42;
    --border-strong: #464C55;
    --divider: #2A2E33;
    --rule: #464C55;

    --text: #DDE1E6;
    --text-strong: #F3F5F7;
    --muted: #AAB2BC;
    --faint: #9AA2AC;

    --accent: #8DB4DA;
    --accent-strong: #B5CFE8;
    --accent-solid: #2C5F8F;
    --accent-solid-hover: #356DA1;
    --accent-soft: #24394F;
    --accent-ring: rgba(141, 180, 218, 0.25);
    --on-accent: #FFFFFF;
    --on-ok: #16181B;
    --on-err: #16181B;

    --ok-bg: #1C2E24;
    --ok-text: #9CCFAE;
    --ok-border: #2D4A38;
    --ok-strong: #6FB58A;

    --err-bg: #3A2224;
    --err-text: #E3A5A5;
    --err-border: #5A3336;
    --err-strong: #D27C7C;

    --answer-correct-bg: #1F3A2B;
    --answer-correct-text: #9CCFAE;
    --answer-wrong-bg: #43272A;
    --answer-wrong-text: #E3A5A5;

    --zebra: #1B1E21;
    --row-hover: #243140;

    --shadow-sm: none;
    --shadow-md: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-lg: 0 8px 28px rgba(0, 0, 0, 0.6);
    --shadow-frozen: 3px 0 6px rgba(0, 0, 0, 0.35);
    --shadow-frozen-left: -3px 0 6px rgba(0, 0, 0, 0.35);

    --scrim: rgba(0, 0, 0, 0.6);
  }
}

:root[data-theme="dark"] {
  --bg: #16181B;
  --surface: #1F2226;
  --surface-2: #25292E;
  --surface-3: #2C3036;

  --border: #363B42;
  --border-strong: #464C55;
  --divider: #2A2E33;
  --rule: #464C55;

  --text: #DDE1E6;
  --text-strong: #F3F5F7;
  --muted: #AAB2BC;
  --faint: #9AA2AC;

  --accent: #8DB4DA;
  --accent-strong: #B5CFE8;
  --accent-solid: #2C5F8F;
  --accent-solid-hover: #356DA1;
  --accent-soft: #24394F;
  --accent-ring: rgba(141, 180, 218, 0.25);
  --on-accent: #FFFFFF;
  --on-ok: #16181B;
  --on-err: #16181B;

  --ok-bg: #1C2E24;
  --ok-text: #9CCFAE;
  --ok-border: #2D4A38;
  --ok-strong: #6FB58A;

  --err-bg: #3A2224;
  --err-text: #E3A5A5;
  --err-border: #5A3336;
  --err-strong: #D27C7C;

  --answer-correct-bg: #1F3A2B;
  --answer-correct-text: #9CCFAE;
  --answer-wrong-bg: #43272A;
  --answer-wrong-text: #E3A5A5;

  --zebra: #1B1E21;
  --row-hover: #243140;

  --shadow-sm: none;
  --shadow-md: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-lg: 0 8px 28px rgba(0, 0, 0, 0.6);
  --shadow-frozen: 3px 0 6px rgba(0, 0, 0, 0.35);
  --shadow-frozen-left: -3px 0 6px rgba(0, 0, 0, 0.35);

  --scrim: rgba(0, 0, 0, 0.6);
}

/* Tell the browser which form controls and scrollbars to render. */
:root { color-scheme: light; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { color-scheme: dark; }
}
:root[data-theme="dark"] { color-scheme: dark; }

/* Set for one frame while the theme changes -- see repaintForThemeChange()
   in common.js. Without it, anything that transitions a colour keeps the
   previous palette's value after a switch. */
:root[data-theme-switching] *,
:root[data-theme-switching] *::before,
:root[data-theme-switching] *::after {
  transition: none !important;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

body {
  font-family: 'IBM Plex Sans', sans-serif;
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}

/* ---- Focus ----
   Nothing in the app had a visible focus ring, which made the operator
   console impossible to drive from a keyboard. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}
/* Controls that draw their own ring via box-shadow opt out of the outline. */
input:focus-visible,
select:focus-visible,
textarea:focus-visible { outline: none; }

/* ---- Header ---- */
.header {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  padding: 14px 16px;
  text-align: center;
  flex-shrink: 0;
}
.header-label {
  font-size: 10px; font-weight: 600; letter-spacing: 1px;
  color: var(--muted); text-transform: uppercase; margin-bottom: 3px;
}
.header h1 { font-size: 18px; font-weight: 700; color: var(--text-strong); margin-bottom: 2px; }
.header p { font-size: 12px; color: var(--muted); font-weight: 500; }

/* ---- Spinner ---- */
.spinner {
  width: 32px; height: 32px;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ---- Status messages ---- */
.status {
  font-size: 12px; font-weight: 600; text-align: center;
  margin-top: 10px; padding: 8px; border-radius: 6px; display: none;
}
.status.visible { display: block; }
.status.ok  { background: var(--ok-bg); color: var(--ok-text); }
.status.err { background: var(--err-bg); color: var(--err-text); }

/* Banner shown when the page has lost contact with the server. */
.conn-banner {
  display: none; position: fixed; left: 0; right: 0; top: 0; z-index: 2000;
  background: var(--err-bg); color: var(--err-text);
  font-size: 13px; font-weight: 700; text-align: center;
  padding: 9px 16px; border-bottom: 1px solid var(--err-border);
}
.conn-banner.visible { display: block; }

/* ---- Place movement ---- */
.move { font-family: 'IBM Plex Mono', monospace; font-size: 0.75em; font-weight: 700; margin-left: 3px; }
.move-up { color: var(--ok-strong); }
.move-down { color: var(--err-strong); }

/* ---- Motion ----
   Respect a reduced-motion preference: the spinner keeps turning slowly so it
   still reads as "working", but transitions and transforms stop. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .spinner {
    animation-duration: 1.6s !important;
    animation-iteration-count: infinite !important;
  }
}
