/* ==========================================================================
   cirt-term - CIRT CTF 2026
   A recovered incident-response workstation, rendered as an amber phosphor CRT.

   No webfonts and no build step: the event runs on an internal network where
   fonts.googleapis.com may not resolve, so everything here is system fonts and
   hand-written CSS.
   ========================================================================== */

:root {
  --bg:        #0a0806;
  --bg-panel:  #12100c;
  --bg-raised: #1a1710;
  --line:      #3a2f1c;
  --line-hot:  #6b5426;

  --fg:        #ffb000;   /* amber phosphor */
  --fg-dim:    #a8741a;
  --fg-faint:  #6b4d16;
  --fg-bright: #ffd479;

  --ok:        #4fd67a;   /* solved / correct */
  --warn:      #ff8c42;
  --bad:       #ff4d4d;
  --locked:    #5a5348;

  --fg-hot:    #ffcf3a;   /* meter fill / peak phosphor */
  --scan:      rgba(255, 176, 0, 0.035);

  /* The one cold colour on an amber tube. A 1980s phosphor screen physically
     cannot make this, which is the point: cyan marks the parts of the range
     that are machine, not workstation - the two live models, the AI Range
     artifacts, the compromised output node in the logo. Used nowhere else, so
     it stays a signal instead of becoming a second theme colour. */
  --ai:        #47e0ff;
  --ai-dim:    #2a93b0;
  --ai-glow:   rgba(71, 224, 255, 0.45);

  --glow: 0 0 2px rgba(255, 176, 0, 0.55);
  --mono: ui-monospace, "Cascadia Mono", "Cascadia Code", Consolas,
          "DejaVu Sans Mono", "Liberation Mono", Menlo, monospace;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--mono);
  font-size: 14px;
  line-height: 1.5;
}

/* The whole page is one workstation that fills the glass, top to bottom, at
   every width. 100dvh (not vh) so the mobile URL bar does not leave a gap. */
html { height: 100%; }
body {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

/* A faint phosphor grid behind everything, so the parts of the screen the
   content does not reach read as terminal glass rather than dead black. Fixed
   so it never scrolls, and drawn under the CRT overlays. */
body {
  text-shadow: var(--glow);
  background-color: var(--bg);
  background-image:
    linear-gradient(rgba(255, 176, 0, 0.035) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 176, 0, 0.035) 1px, transparent 1px),
    radial-gradient(ellipse 90% 60% at 50% -10%, rgba(255, 176, 0, 0.06), transparent 70%);
  background-size: 42px 42px, 42px 42px, 100% 100%;
  background-attachment: fixed;
}
html.crt-off body { background-image: none; }

a { color: var(--fg-bright); text-decoration: none; }
a:hover { text-decoration: underline; }

::selection { background: var(--fg); color: var(--bg); text-shadow: none; }

/* --- CRT overlays -------------------------------------------------------- */
/* Both are decorative and pointer-events:none so they never eat a click.
   .crt-off on <html> disables them; the toggle persists in localStorage,
   because two weeks of staring at scanlines is a genuine accessibility
   problem and some people just want to read the text. */

.crt-scanlines,
.crt-vignette {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 9000;
}

.crt-scanlines {
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0) 0px,
    rgba(0, 0, 0, 0) 2px,
    rgba(0, 0, 0, 0.22) 3px,
    rgba(0, 0, 0, 0.22) 4px
  );
  opacity: 0.55;
}

.crt-vignette {
  background: radial-gradient(
    ellipse at center,
    rgba(0, 0, 0, 0) 55%,
    rgba(0, 0, 0, 0.55) 100%
  );
}

html.crt-off .crt-scanlines,
html.crt-off .crt-vignette { display: none; }
html.crt-off body { text-shadow: none; }

@media (prefers-reduced-motion: reduce) {
  .crt-scanlines { opacity: 0.25; }
  * { animation: none !important; }
}

/* --- Shell --------------------------------------------------------------- */

.term-shell {
  flex: 1;
  display: flex;
  flex-direction: column;
  width: 100%;
  /* Wide enough to use a big monitor; capped so text lines never sprawl. The
     command center inside spends the width on panels, not on stretched rows. */
  max-width: 1680px;
  margin: 0 auto;
  padding: 14px clamp(12px, 2.2vw, 30px) 12px;
  gap: 0;
}

.term-titlebar {
  display: flex;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
  border: 1px solid var(--line-hot);
  border-bottom: none;
  background: var(--bg-panel);
  padding: 7px 12px;
}

.term-titlebar .host { color: var(--fg-bright); font-weight: 700; }
.term-titlebar .sep { color: var(--fg-faint); }
.term-titlebar .meta { color: var(--fg-dim); font-size: 12px; }
.term-titlebar .spacer { flex: 1 1 auto; }

.term-nav {
  display: flex;
  gap: 2px;
  flex-wrap: wrap;
  border: 1px solid var(--line-hot);
  border-bottom: none;
  background: var(--bg);
  padding: 0 6px;
}

.term-nav a {
  padding: 6px 12px;
  color: var(--fg-dim);
  border-bottom: 2px solid transparent;
  /* CTFd Pages arrive with human-cased titles ("Rules"); lowercase them so
     admin-authored pages still read like shell commands. */
  text-transform: lowercase;
}
.term-nav a:hover { color: var(--fg); text-decoration: none; background: var(--bg-raised); }
.term-nav a.active { color: var(--fg-bright); border-bottom-color: var(--fg); }

.term-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;            /* let children scroll instead of the page growing */
  border: 1px solid var(--line-hot);
  background: var(--bg-panel);
  padding: 16px;
}
/* The evidence browser is an application surface, not an article: it fills the
   body and manages its own internal scroll. Plain pages (rules, profile) keep
   the padded document flow above. */
.term-body.app { padding: 14px; gap: 12px; }

.term-footer {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  color: var(--fg-faint);
  font-size: 12px;
  padding: 8px 2px;
}

.term-footer button {
  background: none;
  border: 1px solid var(--line);
  color: var(--fg-faint);
  font: inherit;
  font-size: 11px;
  padding: 1px 7px;
  cursor: pointer;
}
.term-footer button:hover { color: var(--fg); border-color: var(--fg-dim); }

/* --- Generic bits -------------------------------------------------------- */

.kicker {
  color: var(--fg-faint);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

h1, h2, h3 { font-weight: 700; margin: 0 0 10px; }
h1 { font-size: 19px; }
h2 { font-size: 16px; }
h3 { font-size: 13px; color: var(--fg-dim); letter-spacing: 0.08em; text-transform: uppercase; }

hr { border: none; border-top: 1px solid var(--line); margin: 16px 0; }

code, pre {
  background: var(--bg);
  border: 1px solid var(--line);
  color: var(--fg-bright);
}
code { padding: 0 4px; }
pre { padding: 10px; overflow-x: auto; }

.rule { color: var(--fg-faint); user-select: none; overflow: hidden; white-space: nowrap; }

/* --- Forms --------------------------------------------------------------- */

.field { margin-bottom: 12px; }
.field label { display: block; color: var(--fg-dim); font-size: 12px; margin-bottom: 3px; }

input[type=text], input[type=password], input[type=email], input[type=url],
textarea, select {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--line-hot);
  color: var(--fg-bright);
  font: inherit;
  padding: 7px 9px;
}
input:focus, textarea:focus, select:focus {
  outline: none;
  border-color: var(--fg);
  box-shadow: 0 0 0 1px var(--fg-faint);
}

.btn {
  background: var(--bg-raised);
  border: 1px solid var(--fg-dim);
  color: var(--fg-bright);
  font: inherit;
  padding: 7px 16px;
  cursor: pointer;
}
.btn:hover:not(:disabled) { background: var(--fg); color: var(--bg); text-shadow: none; }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }

.alert { border: 1px solid var(--line-hot); padding: 8px 11px; margin-bottom: 12px; }
.alert-bad  { border-color: var(--bad);  color: var(--bad); }
.alert-ok   { border-color: var(--ok);   color: var(--ok); }

/* --- Boot / login -------------------------------------------------------- */

/* The boot container centers in the filled body (both axes) rather than
   floating near the top over a slab of black. Login widens into two columns;
   the single-form pages (register, reset) keep a centered panel. */
.boot {
  width: 100%;
  max-width: 940px;
  margin: auto;
  padding: 24px 16px;
}

.boot-head {
  display: flex;
  flex-direction: column;
  gap: 2px;
  border-bottom: 1px solid var(--line);
  padding-bottom: 14px;
  margin-bottom: 18px;
}
.boot-head .op {
  color: var(--fg-faint);
  font-size: 11px;
  letter-spacing: 0.42em;
  text-transform: uppercase;
}
.boot-head .name {
  font-size: clamp(26px, 5vw, 42px);
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--fg-bright);
  line-height: 1;
  text-shadow: 0 0 12px rgba(255, 176, 0, 0.35), var(--glow);
  animation: phosphor 5.5s ease-in-out infinite;
}
.boot-head .name b { color: var(--fg); }
.boot-head .tag { color: var(--fg-dim); font-size: 12.5px; margin-top: 5px; }

.boot-cols {
  display: grid;
  grid-template-columns: 1.05fr minmax(300px, 400px);
  gap: 20px;
  align-items: start;
}

.boot-log,
.boot pre {
  background: none;
  border: none;
  padding: 0;
  color: var(--fg-dim);
  font-size: 12.5px;
  line-height: 1.5;
  white-space: pre-wrap;
  margin: 0;
}
.boot-log { border-left: 1px solid var(--line); padding-left: 16px; }

.boot .panel {
  border: 1px solid var(--line-hot);
  background: var(--bg-panel);
  padding: 18px;
  max-width: 460px;
  width: 100%;
  margin-inline: auto;
}
.boot h1 { color: var(--fg-bright); }

@media (max-width: 720px) {
  .boot-cols { grid-template-columns: 1fr; }
  .boot-log { border-left: none; padding-left: 0; border-top: 1px solid var(--line); padding-top: 14px; order: 2; }
}

/* Login: a wide centred logo banner over a two-column body - the AI Security
   Edition pitch on the left, the sign-in panel on the right - filling the
   workstation top to bottom. */
.boot.login2 {
  max-width: 1360px; margin: 0 auto; flex: 1 1 auto;
  padding: clamp(22px, 4vh, 52px) clamp(16px, 3vw, 52px);
  display: flex; flex-direction: column; gap: clamp(26px, 5vh, 54px);
}
.login-brand {
  display: flex; justify-content: center;
  padding-bottom: clamp(22px, 4vh, 42px); border-bottom: 1px solid var(--line);
}
.login-brand .brandlock { gap: clamp(18px, 2.4vw, 32px); }
.login-brand .logomark { width: clamp(96px, 9vw, 132px); height: clamp(96px, 9vw, 132px); }
.login-brand .bw-name { font-size: clamp(34px, 5.6vw, 62px); }

.login-body {
  flex: 1 1 auto;
  display: grid; gap: clamp(28px, 4vw, 64px);
  grid-template-columns: minmax(0, 1.25fr) minmax(320px, 0.8fr);
  align-items: center;
}
.login-panel { display: flex; align-items: center; justify-content: center; min-width: 0; }
.login-panel .panel { max-width: 440px; }

@media (max-width: 860px) {
  .boot.login2 { flex: 0 0 auto; }
  .login-body { grid-template-columns: 1fr; align-items: stretch; }
  .login-panel .panel { max-width: 460px; margin-inline: auto; }
}

.cursor {
  display: inline-block;
  width: 8px;
  height: 15px;
  background: var(--fg);
  vertical-align: text-bottom;
  animation: blink 1.05s step-end infinite;
}
@keyframes blink { 50% { opacity: 0; } }

/* --- Brand: logo mark, wordmark lockup, AI Security Edition hero ---------- */

/* The mark. Sized by whatever contains it; colours inherit the amber theme. */
.logomark { width: 120px; height: 120px; display: block; flex: 0 0 auto; overflow: visible;
  filter: drop-shadow(0 0 5px rgba(255, 176, 0, 0.30)); }
.logomark .lm-hex-o { fill: none; stroke: var(--fg); stroke-width: 3.4; stroke-linejoin: round; }
.logomark .lm-hex-i { fill: rgba(255, 176, 0, 0.04); stroke: var(--line-hot); stroke-width: 1.4; stroke-linejoin: round; }
.logomark .lm-edges line { stroke: var(--fg-dim); stroke-width: 1.4; opacity: 0.55; }
.logomark .lm-nodes circle { fill: var(--fg-bright); }
/* The output node is the only cold thing in the mark: the network's answer is
   the part of this incident you cannot trust. */
.logomark .lm-out { fill: var(--ai); filter: drop-shadow(0 0 5px var(--ai-glow));
  animation: lm-pulse 2.4s ease-in-out infinite; }
@keyframes lm-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }

/* Horizontal lockup: mark + wordmark. */
.brandlock { display: flex; align-items: center; gap: 18px; }
.brandlock .logomark { width: 88px; height: 88px; }
.brandwords { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.bw-op { color: var(--fg-faint); font-size: 10px; letter-spacing: 0.42em; text-transform: uppercase; }
.bw-name {
  font-size: clamp(28px, 4.6vw, 46px); font-weight: 700; line-height: 0.95;
  letter-spacing: 0.03em; color: var(--fg-bright);
  text-shadow: 0 0 14px rgba(255, 176, 0, 0.35), var(--glow);
  animation: phosphor 5.5s ease-in-out infinite;
}
.bw-name b { color: var(--fg); }
.bw-yr { color: var(--fg-dim); font-size: 0.42em; vertical-align: super;
  letter-spacing: 0.1em; margin-left: 0.35em; }
.bw-sub { font-size: clamp(11px, 1.5vw, 14px); letter-spacing: 0.32em;
  text-transform: uppercase; color: var(--fg-faint); }
.bw-sub .bw-ai {
  color: var(--bg); background: var(--ai); font-weight: 700;
  padding: 1px 6px; border-radius: 2px; letter-spacing: 0.08em; margin-right: 4px;
  box-shadow: 0 0 12px var(--ai-glow);
}

/* "Ctrl + Alt + AI" - the event name as a keyboard combo. AI is the hot key. */
.bw-combo { display: inline-flex; align-items: center; gap: 6px; margin-top: 4px; }
.bw-combo kbd {
  font: inherit; font-size: clamp(11px, 1.4vw, 13px); line-height: 1;
  color: var(--fg-bright); background: var(--bg-raised);
  border: 1px solid var(--line-hot); border-bottom-width: 2px; border-radius: 3px;
  padding: 3px 8px; letter-spacing: 0.03em;
}
.bw-combo .k-ai {
  color: var(--bg); background: var(--ai); border-color: var(--ai); font-weight: 700;
  box-shadow: 0 0 14px var(--ai-glow);
}
.bw-combo .plus { color: var(--fg-faint); font-size: 12px; }

.boot-brand { display: flex; align-items: center; justify-content: space-between;
  gap: 20px; flex-wrap: wrap; margin-bottom: 18px; }

/* Utility auth pages (register / reset / confirm): the lockup centred over the
   form, so they read as the same product as the login screen. */
.auth-brand { justify-content: center; gap: 16px;
  border-bottom: 1px solid var(--line); padding-bottom: 18px; margin-bottom: 22px; }

/* The AI Security Edition hero ribbon - the loud, instantly-legible statement
   that this year's event is about AI security. */
.ai-hero {
  position: relative; overflow: hidden;
  border: 1px solid var(--line-hot); border-left: 3px solid var(--ai);
  background:
    linear-gradient(120deg, rgba(71, 224, 255, 0.10), rgba(255, 176, 0, 0.03) 45%, transparent 72%),
    var(--bg-panel);
  padding: 16px clamp(14px, 2.4vw, 26px); margin-bottom: 22px;
}
.ai-hero::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: repeating-linear-gradient(90deg, transparent 0 7px, rgba(255, 176, 0, 0.02) 7px 8px);
}
.ai-hero > * { position: relative; }
.ai-hero-badge {
  display: inline-flex; align-items: center; gap: 8px; margin-bottom: 10px;
  color: var(--ai); font-size: 11px; font-weight: 700;
  letter-spacing: 0.26em; text-transform: uppercase;
}
.ai-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto;
  background: var(--ai); box-shadow: 0 0 9px var(--ai-glow);
  animation: led-pulse 1.6s ease-in-out infinite; }
.ai-hero-line { margin: 0 0 8px; font-size: clamp(20px, 3vw, 32px); line-height: 1.12;
  color: var(--fg-bright); font-weight: 700; }
.ai-hero-line b { color: var(--ai); text-shadow: 0 0 16px var(--ai-glow); }
.ai-hero-aside { color: var(--fg-dim); font-style: italic; font-weight: 400; }
.ai-hero-sub { margin: 0 0 14px; max-width: 62ch; color: var(--fg-dim); font-size: 13.5px; line-height: 1.55; }
.ai-hero-sub b { color: var(--fg-bright); font-weight: 700; }
.ai-tags { display: flex; flex-wrap: wrap; gap: 8px; }
.ai-tags span {
  font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--fg);
  border: 1px solid var(--line-hot); background: rgba(255, 176, 0, 0.05);
  padding: 4px 11px; border-radius: 2px;
}

/* A compact AI Security Edition pill, reused in the masthead led strip. */
.ai-pill {
  display: inline-flex; align-items: center; gap: 7px;
  color: var(--ai); font-size: 10px; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
  border: 1px solid color-mix(in srgb, var(--ai) 45%, var(--line));
  background: rgba(71, 224, 255, 0.08); padding: 2px 9px; border-radius: 2px;
}
.ai-pill .ai-dot { width: 7px; height: 7px; }

/* Placements: a small mark in the titlebar, a medium one in the masthead. */
.term-titlebar .logomark { width: 22px; height: 22px; align-self: center; }
.term-titlebar .ai-flag { color: var(--ai); letter-spacing: 0.12em; }
.cmd-banner > .logomark { width: 66px; height: 66px; }

@media (max-width: 720px) {
  .term-titlebar .ai-flag { display: none; }
  .cmd-banner > .logomark { width: 52px; height: 52px; }
}

/* --- File manager (challenges) ------------------------------------------- */

.fm {
  display: grid;
  grid-template-columns: 246px 1fr;
  gap: 0;
  border: 1px solid var(--line);
  height: 100%;
  min-height: 0;
}

/* The right side of the file manager: a fixed path bar over a listing that
   scrolls on its own, so long directories never push the page taller. */
.fm-main { display: flex; flex-direction: column; min-height: 0; min-width: 0; }

/* Left column of the file manager: the /evidence tree that scrolls, with the
   relay map pinned below it. */
.fm-side {
  display: flex;
  flex-direction: column;
  min-height: 0;
  border-right: 1px solid var(--line);
}
.fm-tree {
  background: var(--bg);
  padding: 10px 0;
  flex: 1 1 auto;                 /* the tree scrolls; the map stays pinned below */
  min-height: 0;
  overflow-y: auto;
}
.fm-tree .root { padding: 2px 12px 8px; color: var(--fg-faint); font-size: 12px; }

/* The relay map, sitting under the tree in the left column. */
.fm-map { flex: 0 0 auto; border-top: 1px solid var(--line); background: var(--bg-panel); }
.fm-map > h3 {
  margin: 0; padding: 6px 12px; border-bottom: 1px solid var(--line);
  background: var(--bg-panel); color: var(--fg-dim);
  display: flex; align-items: center; gap: 8px;
}
.fm-map > h3 .tag {
  margin-left: auto; color: var(--fg-faint); font-size: 9px; letter-spacing: 0.16em;
}
/* The relay map: a compact node schematic of the seized stack. Reads top-down
   from the analyst's evidence mount, through the seized relay and the tls edge,
   down to the microservices that host each challenge. */
.netmap { padding: 10px 12px 12px; display: flex; flex-direction: column; }

.netmap .node {
  display: flex; align-items: center; gap: 9px;
  padding: 6px 9px;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: var(--bg);
  position: relative;
}
.netmap .node .dot {
  flex: 0 0 auto; width: 7px; height: 7px; border-radius: 50%;
  background: var(--fg-dim); box-shadow: 0 0 5px currentColor; color: var(--fg-dim);
}
.netmap .node .dot.ok  { background: var(--ok);  color: var(--ok); }
.netmap .node .dot.bad { background: var(--bad); color: var(--bad);
  animation: netpulse 1.6s ease-in-out infinite; }
@keyframes netpulse { 0%,100% { opacity: 1; } 50% { opacity: 0.35; } }

.netmap .nb { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }
.netmap .nb b { color: var(--fg-bright); font-weight: 700; font-size: 11px; letter-spacing: 0.02em; }
.netmap .nb i { color: var(--fg-faint); font-style: normal; font-size: 9px; letter-spacing: 0.03em; }

.netmap .pin {
  margin-left: auto; flex: 0 0 auto;
  padding: 1px 6px; border: 1px solid var(--line); border-radius: 999px;
  color: var(--fg-dim); font-size: 9px; letter-spacing: 0.06em;
}
.netmap .pin.ok  { color: var(--ok);  border-color: color-mix(in srgb, var(--ok) 45%, transparent); }
.netmap .pin.bad { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 45%, transparent); }

/* the analyst's own node and the seized relay get a tinted ground */
.netmap .node.src   { border-color: color-mix(in srgb, var(--ok) 40%, var(--line)); }
.netmap .node.relay { border-color: color-mix(in srgb, var(--bad) 45%, var(--line));
  background: color-mix(in srgb, var(--bad) 8%, var(--bg)); }
/* The AI Range node is the only live machine on the schematic, so it is the
   only cold one - same rule as the output node in the logo mark. */
.netmap .node.bob { border-color: color-mix(in srgb, var(--ai) 42%, var(--line));
  background: color-mix(in srgb, var(--ai) 7%, var(--bg)); }
.netmap .node.bob .dot { background: var(--ai); color: var(--ai); }
.netmap .node.bob .nb b { color: var(--ai); }
.netmap .node.bob .pin { color: var(--ai-dim);
  border-color: color-mix(in srgb, var(--ai) 45%, transparent); }

/* vertical connectors between the stacked top nodes */
.netmap .wire {
  align-self: stretch; text-align: center;
  color: var(--fg-faint); font-size: 8px; letter-spacing: 0.18em; text-transform: uppercase;
  padding: 3px 0; position: relative;
}
.netmap .wire::before {
  content: ""; position: absolute; left: 50%; top: 0; bottom: 0;
  border-left: 1px dashed var(--fg-faint); transform: translateX(-0.5px); z-index: 0;
}
.netmap .wire span {
  position: relative; z-index: 1; background: var(--bg-panel); padding: 0 6px;
}

/* the tls edge bar sits between the relay and the service fan */
.netmap .edge {
  margin: 8px 0 2px; padding: 4px 9px;
  display: flex; align-items: baseline; gap: 7px;
  border-left: 2px solid var(--fg-dim);
  color: var(--fg-faint); font-size: 9px; letter-spacing: 0.08em; text-transform: uppercase;
}
.netmap .edge b { color: var(--fg); font-size: 10px; letter-spacing: 0.1em; }

/* the service fan: a spine on the left, each service branching off it */
.netmap .fan { position: relative; padding-left: 14px; display: flex; flex-direction: column; gap: 6px; }
.netmap .fan::before {
  content: ""; position: absolute; left: 4px; top: -6px; bottom: 14px;
  border-left: 1px solid var(--line);
}
.netmap .fan .node::before {
  content: ""; position: absolute; left: -10px; top: 50%; width: 10px;
  border-top: 1px solid var(--line);
}

.fm-dir {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  background: none;
  border: none;
  border-left: 2px solid transparent;
  color: var(--fg-dim);
  font: inherit;
  text-align: left;
  padding: 4px 12px;
  cursor: pointer;
}
.fm-dir:hover { background: var(--bg-raised); color: var(--fg); }
.fm-dir.active { border-left-color: var(--fg); color: var(--fg-bright); background: var(--bg-raised); }
.fm-dir .count { margin-left: auto; color: var(--fg-faint); font-size: 11px; }
.fm-dir.done .count { color: var(--ok); }

.fm-pane { flex: 1; padding: 0; overflow: auto; min-height: 0; }

.fm-path {
  border-bottom: 1px solid var(--line);
  padding: 7px 12px;
  color: var(--fg-dim);
  font-size: 12px;
  background: var(--bg);
}
.fm-path b { color: var(--fg-bright); font-weight: 400; }

table.fm-list { width: 100%; border-collapse: collapse; }
table.fm-list th {
  text-align: left;
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-faint);
  border-bottom: 1px solid var(--line);
  padding: 6px 12px;
}
table.fm-list td { padding: 5px 12px; border-bottom: 1px solid rgba(58, 47, 28, 0.4); }
table.fm-list tr.file { cursor: pointer; }
/* Hover raises the ground and brightens the neutral columns only. The blanket
   `color: var(--fg-bright)` this replaces also repainted .name and .stat, so
   pointing at an analysed row made it look unanalysed for as long as the
   cursor sat on it - the one row state a player most wants to trust. */
table.fm-list tr.file:hover td { background: var(--bg-raised); }
table.fm-list tr.file:hover td.perm,
table.fm-list tr.file:hover td.size,
table.fm-list tr.file:hover td.date,
table.fm-list tr.file:hover td.pts { color: var(--fg-dim); }
table.fm-list tr.file:not(.solved):hover td.name { color: var(--fg-bright); }

td.name { color: var(--fg); white-space: nowrap; }
td.perm, td.size, td.date { color: var(--fg-faint); font-size: 12px; white-space: nowrap; }
td.pts { color: var(--fg-dim); text-align: right; white-space: nowrap; }
td.stat { white-space: nowrap; font-size: 12px; }

tr.solved td.name { color: var(--ok); }
tr.solved td.stat { color: var(--ok); }
tr.locked { cursor: not-allowed; }
tr.locked td { color: var(--locked); }
tr.locked:hover td { background: none; color: var(--locked); }

.fm-empty { padding: 26px 12px; color: var(--fg-faint); }

/* --- Command line -------------------------------------------------------- */

.cli {
  border: 1px solid var(--line);
  border-top: none;
  background: var(--bg);
  padding: 8px 12px;
}
.cli-out {
  margin: 0 0 6px;
  padding: 0;
  border: none;
  background: none;
  color: var(--fg-dim);
  font-size: 12.5px;
  max-height: 190px;
  overflow-y: auto;
  white-space: pre-wrap;
}
.cli-in { display: flex; align-items: center; gap: 8px; }
.cli-in .ps1 { color: var(--ok); white-space: nowrap; }
.cli-in input {
  flex: 1;
  background: none;
  border: none;
  color: var(--fg-bright);
  font: inherit;
  padding: 2px 0;
}
.cli-in input:focus { outline: none; box-shadow: none; }

/* --- Challenge inspector (modal) ----------------------------------------- */
/* The inspector is the file-open window: an evidence dossier framed like the
   rest of the console - window titlebar, HUD brackets, marked-up attachments,
   and a terminal flag prompt. */

.modal[hidden] { display: none; }
.modal {
  position: fixed;
  inset: 0;
  z-index: 9500;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 6vh 16px 24px;
  overflow-y: auto;
}
.modal-backdrop {
  position: fixed;
  inset: 0;
  background:
    radial-gradient(ellipse 80% 50% at 50% 0%, rgba(255, 176, 0, 0.05), transparent 70%),
    rgba(4, 6, 4, 0.82);
}
.modal-card {
  position: relative;
  width: min(760px, 100%);
  background:
    linear-gradient(180deg, rgba(255, 176, 0, 0.04), transparent 140px),
    var(--bg-panel);
  border: 1px solid var(--fg-dim);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.6), 0 22px 70px rgba(0, 0, 0, 0.75);
  animation: modal-in 0.18s ease-out;
}
@keyframes modal-in {
  from { opacity: 0; transform: translateY(8px) scale(0.99); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .modal-card { animation: none; } }

.modal-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  border-bottom: 1px solid var(--line-hot);
  background: var(--bg-raised);
  padding: 8px 12px;
  color: var(--fg-dim);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.modal-bar::before {
  content: "";
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--ok); box-shadow: 0 0 6px var(--ok);
}
.modal-bar .sep { color: var(--fg-faint); }
.modal-bar #modal-name { color: var(--fg-bright); text-transform: none; letter-spacing: 0; }
.modal-bar .spacer { flex: 1; }
.modal-close {
  background: none;
  border: 1px solid var(--line-hot);
  color: var(--fg-dim);
  font: inherit;
  font-size: 11px;
  line-height: 1;
  padding: 2px 8px 3px;
  cursor: pointer;
}
.modal-close:hover { color: var(--bad); border-color: var(--bad); }

.chal { padding: 18px 20px 22px; }
.chal .kicker { color: var(--fg-faint); }
.chal h2 {
  color: var(--fg-bright);
  font-size: 20px;
  margin: 2px 0 12px;
  text-shadow: var(--glow);
}

/* Meta as status chips rather than a run-on line. */
.chal-meta { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; }
.chal-meta span {
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--fg-dim);
  font-size: 11px;
  padding: 2px 9px;
}
.chal-meta .solved { border-color: var(--ok); color: var(--ok); }

/* Section headers read as console sections: a // marker and a trailing rule. */
.chal h3 {
  display: flex;
  align-items: center;
  gap: 9px;
  color: var(--fg-dim);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin: 20px 0 9px;
}
.chal h3::before { content: "//"; color: var(--fg-faint); }
.chal h3::after { content: ""; flex: 1; border-top: 1px solid var(--line); }

.chal-desc { margin: 12px 0 4px; color: var(--fg); }
.chal-desc p { margin: 0 0 10px; }
.chal-desc em { color: var(--fg-dim); }

.tags { display: flex; gap: 6px; flex-wrap: wrap; margin: 4px 0 12px; }
.tags span { border: 1px solid var(--line); color: var(--fg-faint); font-size: 11px; padding: 0 6px; }

/* Attachments look like rows on the evidence share: a marker, the filename,
   and a get action that lights up on hover. */
.files a {
  display: flex;
  align-items: center;
  gap: 9px;
  border: 1px solid var(--line-hot);
  background: var(--bg);
  padding: 8px 11px;
  margin-bottom: 6px;
  color: var(--fg-bright);
}
.files a::before { content: "\25b8"; color: var(--fg-dim); }
.files a::after {
  content: "[ get ]";
  margin-left: auto;
  color: var(--fg-faint);
  font-size: 11px;
  letter-spacing: 0.1em;
}
.files a:hover { border-color: var(--fg); text-decoration: none; background: var(--bg-raised); }
.files a:hover::after { color: var(--ok); }
.files .sz { color: var(--fg-faint); font-size: 11px; }

/* Connection info is the live endpoint - the one thing in here you act on. */
.conn {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  border: 1px solid var(--line-hot);
  background: var(--bg);
  padding: 9px 11px;
  margin-bottom: 14px;
}
.conn .kicker { color: var(--fg-faint); }
.conn a, .conn code { color: var(--fg-bright); }
.conn a { border-bottom: 1px solid var(--fg-faint); }
.conn a::after { content: " \2197"; }

.hint {
  border: 1px solid var(--line);
  background: var(--bg);
  padding: 9px 11px;
  margin-bottom: 6px;
}
.hint strong { color: var(--fg-dim); }
.hint .cost { color: var(--fg-faint); font-size: 12px; }
.hint button { margin-left: 8px; }

.attempts { color: var(--fg-dim); font-size: 12px; margin-top: 12px; }
.attempts.low { color: var(--warn); }

/* The flag prompt: one bordered box that reads as a terminal input line. */
.submit-row {
  display: flex;
  align-items: stretch;
  margin-top: 16px;
  border: 1px solid var(--fg-dim);
  background: var(--bg);
}
.submit-row:focus-within { border-color: var(--fg); box-shadow: 0 0 0 1px var(--fg-faint); }
.submit-row .flag-ps1 {
  display: flex;
  align-items: center;
  padding: 0 10px;
  color: var(--ok);
  border-right: 1px solid var(--line);
  white-space: nowrap;
  user-select: none;
}
.submit-row input {
  flex: 1;
  border: none;
  background: none;
  color: var(--fg-bright);
  padding: 9px 10px;
}
.submit-row input:focus { outline: none; box-shadow: none; }
.submit-row button {
  border: none;
  border-left: 1px solid var(--line-hot);
  background: var(--bg-raised);
  color: var(--fg-bright);
  padding: 9px 20px;
  cursor: pointer;
}
.submit-row button:hover:not(:disabled) { background: var(--fg); color: var(--bg); text-shadow: none; }
.submit-row button:disabled { opacity: 0.45; cursor: not-allowed; }

.result { margin-top: 10px; padding: 8px 10px; border: 1px solid transparent; }
.result:empty { display: none; }
.result.correct   { border-color: var(--ok);  color: var(--ok); }
.result.correct::before { content: "[ok] "; }
.result.incorrect { border-color: var(--bad); color: var(--bad); }
.result.incorrect::before { content: "[x] "; }
/* The poisoned-flag response gets its own treatment. It is the moment the
   whole event is built around; it should not look like a typo. */
.result.poisoned {
  border-color: var(--warn);
  color: var(--warn);
  background: rgba(255, 140, 66, 0.07);
  animation: poison-flash 0.5s ease-out;
}
.result.poisoned::before { content: "[!] "; font-weight: 700; }
@keyframes poison-flash {
  0%   { background: rgba(255, 140, 66, 0.4); }
  100% { background: rgba(255, 140, 66, 0.07); }
}

/* --- Scoreboard ---------------------------------------------------------- */

/* The process table fills the body and scrolls under a pinned header. */
.ps-wrap {
  flex: 1;
  min-height: 0;
  overflow: auto;
  border: 1px solid var(--line);
  background: var(--bg);
}
.ps-wrap > .kicker { padding: 20px 12px; }

table.ps { width: 100%; border-collapse: collapse; }
table.ps th {
  text-align: left;
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-faint);
  background: var(--bg-panel);
  border-bottom: 1px solid var(--line-hot);
  padding: 8px 12px;
  position: sticky;
  top: 0;
  z-index: 1;
}
table.ps td { padding: 6px 12px; border-bottom: 1px solid rgba(58, 47, 28, 0.4); }
table.ps td.num { text-align: right; color: var(--fg-dim); font-variant-numeric: tabular-nums; }
table.ps td.rank { color: var(--fg-faint); width: 1%; white-space: nowrap; }
table.ps td.team { color: var(--fg); }
table.ps td.pts { color: var(--fg-bright); }
/* Poisoned submissions. Zero stays faint - it is the resting state, not an
   achievement. A nonzero count is lit in the warning hue because it is the
   thing that loses a tie. */
table.ps td.poison { color: var(--fg-faint); }
table.ps td.poison.hit { color: var(--warn); }
table.ps tr:hover td { background: var(--bg-raised); }
table.ps tr.me td { color: var(--fg-bright); background: var(--bg-raised); }
table.ps tr.me td.rank { border-left: 2px solid var(--fg); }
/* The leaders get a phosphor lift so the top of the board reads at a glance. */
table.ps tr.top td.team { color: var(--fg-bright); }
table.ps tr.top td.rank { color: var(--fg-hot); }
.ps-bar {
  display: inline-block;
  height: 6px;
  margin-left: 8px;
  background: var(--fg-dim);
  vertical-align: middle;
  min-width: 1px;
}
table.ps tr.top .ps-bar { background: var(--fg); }
table.ps tr.me .ps-bar { background: var(--fg-hot); }

/* --- Analyst-console flare ------------------------------------------------ */
/* HUD corner brackets: two diagonally opposite L-marks that frame a panel like
   a targeting reticle. Decorative, pointer-safe. */
.hud { position: relative; }
.hud::before, .hud::after {
  content: "";
  position: absolute;
  width: 12px; height: 12px;
  border: 1px solid var(--line-hot);
  pointer-events: none;
}
.hud::before { top: -1px; left: -1px; border-right: none; border-bottom: none; }
.hud::after { bottom: -1px; right: -1px; border-left: none; border-top: none; }

/* Status LEDs: a live component readout - IDS / RELAY / VAULT. */
.leds { display: flex; gap: 16px; flex-wrap: wrap; align-items: center; }
.led {
  display: inline-flex; align-items: center; gap: 7px;
  color: var(--fg-faint);
  font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase;
}
.led::before {
  content: "";
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--ok);
  box-shadow: 0 0 7px currentColor;
  color: var(--ok);
}
.led.warn::before { background: var(--warn); color: var(--warn); }
.led.bad::before  { background: var(--bad);  color: var(--bad); }
.led.pulse::before { animation: led-pulse 1.6s ease-in-out infinite; }
@keyframes led-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.3; } }

/* Live telemetry: a tail -f of the relay's IDS log. Seeded on load so the
   first frame already reads as a running console, then appended to live. */
.feed {
  height: 100%;
  min-height: 150px;
  overflow-y: auto;
  padding: 8px 10px;
  background: var(--bg);
  font-size: 11.5px;
  line-height: 1.55;
}
.feed .ln {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--fg-faint);
  /* Explicitly flat: a log is uniform rows. Also immunises these lines against
     the global .alert component, which is a bordered box and was boxing every
     alert-severity row. Severity is carried by colour alone. */
  border: none;
  padding: 0;
  margin: 0;
  background: none;
}
.feed .ln .t { color: var(--fg-faint); opacity: 0.7; }
.feed .ln .src { color: var(--fg-dim); }
/* sev- prefixed so they cannot collide with theme components named .alert/.ok. */
.feed .ln.sev-warn  { color: var(--warn); }
.feed .ln.sev-alert { color: var(--bad); }
.feed .ln.sev-ok    { color: var(--ok); }
.feed .ln.new { animation: feed-in 0.35s ease-out; }
@keyframes feed-in {
  from { opacity: 0; transform: translateX(-6px); }
  to   { opacity: 1; transform: none; }
}
.rail-panel.feedpanel { flex: 1 1 200px; display: flex; flex-direction: column; min-height: 180px; }
.rail-panel.feedpanel > h3 { display: flex; align-items: center; gap: 8px; }
.rail-panel.feedpanel > h3 .live {
  margin-left: auto; color: var(--ok); font-size: 9px; letter-spacing: 0.16em;
}
.rail-panel.feedpanel > h3 .live::before {
  content: "\25cf "; animation: led-pulse 1.6s ease-in-out infinite;
}

/* Merged "live incident status report": a short brief over the live solve
   ticker, filling the rail. */
.rail-panel.statuspanel { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
.rail-panel.statuspanel > h3 { display: flex; align-items: center; gap: 8px; text-transform: uppercase; }
.rail-panel.statuspanel > h3 .live {
  margin-left: auto; color: var(--ok); font-size: 9px; letter-spacing: 0.16em;
}
.rail-panel.statuspanel > h3 .live::before {
  content: "\25cf "; animation: led-pulse 1.6s ease-in-out infinite;
}
.statuspanel .brief { padding: 10px 12px; border-bottom: 1px solid var(--line); }
.statuspanel .brief p { margin: 0; color: var(--fg-dim); font-size: 12px; line-height: 1.5; }
.statuspanel .brief .who  { color: var(--fg-bright); }
.statuspanel .brief .code { color: var(--warn); }
.statuspanel .brief .kicker {
  display: block; margin-top: 6px; color: var(--fg-faint);
  font-size: 10.5px; letter-spacing: 0.05em;
}
.statuspanel .feed { flex: 1 1 auto; }

/* Live solve rows: "{team} {verb} {challenge}". These wrap (unlike the old
   fixed-width telemetry) and colour the three parts distinctly. */
.feed .ln.solve { white-space: normal; color: var(--fg-dim); }
.feed .ln.solve b.team { color: var(--fg-bright); font-weight: 700; }
.feed .ln.solve .verb  { color: var(--ok); }
.feed .ln.solve .chal  { color: var(--fg); }
.feed .ln.solve.meta   { color: var(--warn); }
.feed .ln.quiet { color: var(--fg-faint); font-style: italic; white-space: normal; }

/* --- Command center (challenges) ----------------------------------------- */
/* The evidence browser opens on a masthead + live containment readout, then a
   working area that fills the rest of the glass: file manager on the left,
   intel rail on the right where the width allows. */

.cmd-banner {
  border: 1px solid var(--line-hot);
  background:
    linear-gradient(180deg, rgba(255,176,0,0.05), transparent 60%),
    var(--bg);
  padding: 12px clamp(12px, 2vw, 22px);
  display: flex;
  align-items: center;
  gap: clamp(14px, 3vw, 40px);
  flex-wrap: wrap;
}
/* The masthead now carries the CIRT CTF / Ctrl + Alt + AI lockup, sized down
   from the login banner, with the intrusion tag beneath it. */
.cmd-brand { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.cmd-banner .brandlock { gap: 14px; }
.cmd-banner .brandlock .logomark { width: 58px; height: 58px; }
.cmd-banner .brandlock .bw-name { font-size: clamp(22px, 3vw, 34px); }
.cmd-brand .tag { color: var(--fg-dim); font-size: 12px; }

.cmd-wordmark { display: flex; flex-direction: column; gap: 2px; }
.cmd-wordmark .op {
  color: var(--fg-faint);
  font-size: 11px;
  letter-spacing: 0.42em;
  text-transform: uppercase;
}
.cmd-wordmark .name {
  font-size: clamp(22px, 3.4vw, 36px);
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--fg-bright);
  line-height: 1;
  text-shadow: 0 0 10px rgba(255, 176, 0, 0.35), var(--glow);
}
.cmd-wordmark .name b { color: var(--fg); font-weight: 700; }
.cmd-wordmark .tag { color: var(--fg-dim); font-size: 12px; margin-top: 4px; }

.cmd-banner .spacer { flex: 1 1 24px; }

/* Scoreboard masthead: drop the readout onto its own full-width line so the
   stat cells sit under the PS AUX heading (left-aligned) rather than out to
   its right. The logo stays beside the heading on the first line. */
.cmd-banner.stacked .spacer { display: none; }
.cmd-banner.stacked .readout { flex-basis: 100%; }

/* Live readout: a row of stat cells and the containment meter. */
.readout { display: flex; align-items: stretch; gap: 10px; flex-wrap: wrap; }
.stat {
  border: 1px solid var(--line);
  background: var(--bg);
  padding: 6px 12px;
  min-width: 74px;
}
.stat .k {
  color: var(--fg-faint);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.stat .v {
  color: var(--fg-bright);
  font-size: 20px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}
.stat .v small { font-size: 12px; color: var(--fg-dim); font-weight: 400; }
/* poisoned-submission count in the masthead: lit warn-amber once you own one, matching the scoreboard column. */
.stat.poison-hit .v { color: var(--warn); }

.meter { min-width: 200px; flex: 1 1 220px; align-self: center; }
.meter .k {
  display: flex; justify-content: space-between;
  color: var(--fg-faint); font-size: 10px; letter-spacing: 0.14em;
  text-transform: uppercase; margin-bottom: 4px;
}
.meter .k b { color: var(--fg-bright); font-weight: 700; font-variant-numeric: tabular-nums; }
.meter .track {
  height: 12px;
  border: 1px solid var(--line-hot);
  background:
    repeating-linear-gradient(90deg, transparent 0 5px, rgba(0,0,0,0.35) 5px 6px),
    var(--bg);
  overflow: hidden;
}
.meter .fill {
  height: 100%;
  width: 0;
  background: linear-gradient(90deg, var(--warn), var(--fg), var(--fg-hot));
  box-shadow: 0 0 8px rgba(255, 176, 0, 0.6);
  transition: width 0.8s cubic-bezier(.2,.7,.2,1);
}

/* Working area: file manager + intel rail, filling the remaining height. */
.workspace {
  flex: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 316px;
  grid-template-rows: minmax(0, 1fr);   /* bound the row so columns scroll, not the page */
  gap: 12px;
  min-height: 0;
}

.rail {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-height: 0;
  overflow-y: auto;
}
.rail-panel { border: 1px solid var(--line); background: var(--bg); }
.rail-panel > h3 {
  margin: 0;
  padding: 7px 12px;
  border-bottom: 1px solid var(--line);
  background: var(--bg-panel);
  color: var(--fg-dim);
}
.rail-panel .body { padding: 12px; }

.briefing p { margin: 0 0 9px; color: var(--fg-dim); font-size: 12.5px; }
.briefing p:last-child { margin-bottom: 0; }
.briefing .who { color: var(--fg-bright); }
.briefing .code { color: var(--warn); }

/* The relay schematic - box-drawing 'map', the callback to last year. */
.asciimap {
  margin: 0;
  padding: 12px;
  border: none;
  background: none;
  color: var(--fg-dim);
  font-size: 12px;
  line-height: 1.25;
  white-space: pre;
  overflow-x: auto;
}
.asciimap b { color: var(--fg-bright); font-weight: 400; }
.asciimap .hot { color: var(--warn); }
.asciimap .you { color: var(--ok); }

/* Per-category progress inside the directory tree. */
.fm-dir { flex-wrap: wrap; row-gap: 5px; padding: 6px 12px; }
.fm-dir .barwrap {
  flex-basis: 100%;
  height: 3px;
  background: var(--line);
  overflow: hidden;
}
.fm-dir .barwrap i {
  display: block;
  height: 100%;
  background: var(--fg-dim);
  transition: width 0.6s ease;
}
.fm-dir.done .barwrap i { background: var(--ok); }
.fm-dir.active .barwrap i { background: var(--fg); }

/* Subtle phosphor breathing on the masthead wordmark. Decorative, and it
   respects reduced-motion via the global rule below. */
@keyframes phosphor { 0%,100% { opacity: 1; } 47% { opacity: 0.94; } 62% { opacity: 0.985; } }
.cmd-wordmark .name { animation: phosphor 5.5s ease-in-out infinite; }

/* --- Small screens ------------------------------------------------------- */

/* Below this the intel rail moves under the file manager rather than beside it,
   so the working area is never cramped into two thin columns. */
@media (max-width: 1120px) {
  .workspace { grid-template-columns: 1fr; grid-auto-rows: min-content; overflow-y: auto; }
  .fm { min-height: 60vh; }
  .rail { flex-direction: row; flex-wrap: wrap; overflow: visible; }
  .rail-panel { flex: 1 1 260px; }
  .rail-panel.statuspanel { min-height: 260px; }
}

@media (max-width: 760px) {
  .cmd-banner { gap: 12px; }
  .readout { width: 100%; }
  .fm { grid-template-columns: 1fr; }
  .fm-side { border-right: none; border-bottom: 1px solid var(--line); max-height: 46vh; }
  table.fm-list td.perm, table.fm-list th.perm,
  table.fm-list td.date, table.fm-list th.date { display: none; }
  .rail { flex-direction: column; }
}

/* --- Home landing (~) ---------------------------------------------------- */
.landing { display: flex; flex-direction: column; gap: clamp(20px, 3vw, 34px); }

.landing-hero {
  position: relative; overflow: hidden;
  display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr);
  align-items: center; gap: clamp(24px, 4vw, 56px);
  border: 1px solid var(--line-hot);
  background:
    radial-gradient(120% 90% at 50% -20%, rgba(255,176,0,0.10), transparent 60%),
    linear-gradient(180deg, rgba(255,140,66,0.05), transparent 55%),
    var(--bg-panel);
  padding: clamp(30px, 5vw, 66px) clamp(18px, 4vw, 60px);
}

/* One phosphor sweep as the landing paints, the way a tube settles after
   power-on. Purely decorative: the hero is fully readable at rest, it runs once,
   and it disappears under crt: off. Under prefers-reduced-motion the global
   animation kill leaves it parked above the hero, which overflow:hidden clips,
   so it is never seen there either. */
.landing-hero::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 3;
  background: linear-gradient(180deg,
    transparent 0%, rgba(255, 212, 121, 0.10) 42%, rgba(255, 212, 121, 0.20) 50%,
    rgba(255, 212, 121, 0.10) 58%, transparent 100%);
  transform: translateY(-100%);
  animation: power-on 1150ms cubic-bezier(0.2, 0.6, 0.2, 1) 120ms 1 both;
}
@keyframes power-on {
  from { transform: translateY(-100%); }
  to   { transform: translateY(100%); }
}
html.crt-off .landing-hero::after { display: none; }

.lh-body { position: relative; z-index: 1; min-width: 0; max-width: 840px; text-align: left;
  display: flex; flex-direction: column; align-items: flex-start; gap: 18px; }
.lh-body .brandlock { gap: clamp(16px, 2vw, 26px); }
.lh-body .brandlock .logomark { width: clamp(84px, 8vw, 120px); height: clamp(84px, 8vw, 120px); }
/* The lockup is the identity, not the headline. It was set larger than the
   sentence beneath it, which left the brand shouting while the line that
   actually has the personality whispered. The sentence leads now; the lockup
   sits just under it. */
.lh-body .brandlock .bw-name { font-size: clamp(28px, 4vw, 42px); }
.lh-body .brandwords { align-items: flex-start; }

.lh-title { margin: 6px 0 0; font-size: clamp(23px, 3.7vw, 44px); line-height: 1.14;
  letter-spacing: -0.012em;
  color: var(--fg-bright); font-weight: 700; text-wrap: balance; }
.lh-title b { color: var(--fg); text-shadow: 0 0 18px rgba(255, 176, 0, 0.45), var(--glow); }
.lh-aside { display: block; margin-top: 2px; font-size: 0.74em;
  color: var(--fg-dim); font-style: italic; font-weight: 400; }
.lh-sub { margin: 2px 0 0; max-width: 62ch; color: var(--fg-dim);
  font-size: 14.5px; line-height: 1.65; }

.lh-cta { display: flex; flex-wrap: wrap; gap: 12px; justify-content: flex-start; margin-top: 6px; }
.btn-primary, .btn-ghost {
  font: inherit; font-size: 13px; letter-spacing: 0.06em; text-transform: uppercase;
  padding: 11px 20px; border-radius: 3px; border: 1px solid var(--line-hot);
  cursor: pointer; transition: transform 0.12s ease, box-shadow 0.2s ease, background 0.2s ease;
}
.btn-primary { background: var(--fg); color: var(--bg); border-color: var(--fg);
  font-weight: 700; box-shadow: 0 0 18px rgba(255,176,0,0.35); }
.btn-primary:hover { text-decoration: none; transform: translateY(-1px);
  box-shadow: 0 0 26px rgba(255,176,0,0.55); }
.btn-ghost { background: rgba(255,176,0,0.04); color: var(--fg-bright); }
.btn-ghost:hover { text-decoration: none; transform: translateY(-1px);
  border-color: var(--fg-dim); background: rgba(255,176,0,0.09); }

.lh-tags { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-start; margin-top: 8px; }
.lh-tags span { font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--fg-dim); border: 1px solid var(--line); padding: 3px 10px; border-radius: 2px; }
/* The three AI attack surfaces read cold; the classic categories stay amber, so
   the row shows at a glance what is new this year. */
.lh-tags span.t-ai { color: var(--ai); border-color: color-mix(in srgb, var(--ai) 38%, var(--line));
  background: rgba(71, 224, 255, 0.06); }

/* A single illustrated exhibit, shared by the entry pages. No imagery behind
   working text, no motion, and no external asset or font dependency. */
.evidence-plate { min-width: 0; width: 100%; margin: 0; padding: 10px;
  border: 1px solid var(--line); background: var(--bg); text-shadow: none; }
.evidence-plate img { display: block; width: 100%; height: auto; opacity: 0.85; }
.ep-label, .evidence-plate figcaption { display: flex; justify-content: space-between;
  flex-wrap: wrap; gap: 4px 12px; color: var(--fg-dim); font-size: 10px;
  letter-spacing: 0.08em; text-transform: uppercase; }
.ep-label { padding: 2px 2px 9px; border-bottom: 1px solid var(--line); }
.evidence-plate figcaption { padding: 9px 2px 2px; border-top: 1px solid var(--line); }
.ai-hero .evidence-plate { margin-top: 22px; max-width: 360px; }
.login2 .login-panel { align-self: start; }
.ai-hero-line b { color: var(--fg); text-shadow: var(--glow); }

@media (max-width: 1100px) {
  .landing-hero { grid-template-columns: 1fr; }
  .landing-hero > .evidence-plate { max-width: 560px; }
}
@media (max-width: 860px) {
  /* Keep authentication close to the introduction on narrow screens. */
  .login2 .evidence-plate { display: none; }
  .login2 .login-panel { grid-row: 1; }
}

/* One instrument readout rather than four floating cards: a single housing,
   hairline dividers between the cells, and the figures left-aligned on a
   baseline the way a rack-mounted gauge reads. */
.landing-stats {
  display: grid; grid-template-columns: repeat(4, 1fr);
  border: 1px solid var(--line-hot);
  background: linear-gradient(180deg, var(--bg-raised), var(--bg-panel) 70%);
  position: relative; overflow: hidden;
}
.landing-stats::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--fg), transparent); opacity: 0.45; }
.lstat { border-left: 1px solid var(--line); background: none;
  padding: 15px clamp(12px, 1.6vw, 20px); text-align: left; position: relative; }
.lstat:first-child { border-left: none; }
.lstat b { display: block; font-size: clamp(24px, 3.4vw, 34px); color: var(--fg-bright);
  line-height: 1; text-shadow: var(--glow); font-variant-numeric: tabular-nums; }
.lstat span { display: block; margin-top: 7px; font-size: 10px; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--fg-faint); }
/* The live models are the one cold reading on the panel. */
.lstat.ai b { color: var(--ai); text-shadow: 0 0 14px var(--ai-glow); }
.lstat.ai span { color: var(--ai-dim); }

/* Two panels doing different jobs, so they should not look identical. The
   narrative is the thing to read first: it gets the housing and the warm wash.
   "Before you start" is reference material and sits flat beside it. */
.landing-brief { display: grid; grid-template-columns: 1.25fr 1fr; gap: 16px; align-items: start; }
.lb-panel { border: 1px solid var(--line); background: var(--bg-panel); padding: 20px 22px; }
.lb-panel:first-child {
  border-color: var(--line-hot); border-left: 2px solid var(--fg-dim);
  background: linear-gradient(135deg, rgba(255, 176, 0, 0.045), transparent 55%), var(--bg-panel);
}
.lb-panel:first-child p:first-of-type { color: var(--fg-bright); font-size: 14px; }
.lb-panel:last-child { background: none; }
.lb-panel h3 { display: flex; align-items: center; gap: 9px; margin: 0 0 12px;
  color: var(--fg-dim); font-size: 12px; letter-spacing: 0.14em; text-transform: uppercase; }
.lb-panel h3 .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--ok);
  box-shadow: 0 0 7px currentColor; color: var(--ok); flex: 0 0 auto; }
.lb-panel h3 .dot.warn { background: var(--warn); color: var(--warn); }
.lb-panel p { margin: 0 0 10px; color: var(--fg-dim); font-size: 13.5px; line-height: 1.6; }
.lb-panel p:last-child { margin-bottom: 0; }
.lb-panel code { color: var(--fg-bright); }
.lb-list { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 8px; }
.lb-list li { color: var(--fg-dim); font-size: 13.5px; line-height: 1.5; }
.lb-list li b { color: var(--fg-bright); }
.lb-foot { margin-top: 14px !important; color: var(--fg-faint) !important; font-size: 12px !important; }

/* The readout is one housing with hairline dividers, so when it wraps to fewer
   columns the dividers have to move: no rule down the left edge, and a rule
   between the rows instead. */
@media (max-width: 820px) {
  .landing-stats { grid-template-columns: repeat(3, 1fr); }
  .lstat:nth-child(3n + 1) { border-left: none; }
  .lstat:nth-child(n + 4) { border-top: 1px solid var(--line); }
  .landing-brief { grid-template-columns: 1fr; }
}
@media (max-width: 520px) {
  .landing-stats { grid-template-columns: repeat(2, 1fr); }
  .lstat { border-left: 1px solid var(--line); }
  .lstat:nth-child(2n + 1) { border-left: none; }
  .lstat:nth-child(n + 3) { border-top: 1px solid var(--line); }
}

/* --- Documents (rules and any other CTFd Page) ---------------------------- */
/* These pages were rendering through .chal-desc, which is sized for the narrow
   inspector modal - so on a 1680px shell the rules ran the full width of the
   glass with no measure, no rhythm and no heading treatment. A CMS page is a
   document and gets document typography: a capped measure, a section index in
   the space that measure leaves over, and the same // console headers the
   challenge inspector uses, so it still reads as the same machine. */

/* Track the measure, not the shell: if the first column were 1fr the index
   would be pinned to the far edge of a 1680px panel, half a screen away from
   the text it indexes. Sizing the column to the measure keeps the pair
   together and centres them in the glass. */
.doc-wrap {
  display: grid; grid-template-columns: minmax(0, 78ch) 190px;
  justify-content: center;
  gap: clamp(22px, 3vw, 52px); align-items: start;
}
.doc { max-width: 78ch; color: var(--fg-dim); font-size: 14.5px; line-height: 1.75; }
.doc > :first-child { margin-top: 0; }
.doc > :last-child { margin-bottom: 0; }

/* The banner block at the top of the document reads as a header plate, not as
   a code sample. */
.doc > pre:first-child {
  background: linear-gradient(135deg, rgba(255, 176, 0, 0.05), transparent 60%), var(--bg-raised);
  border: 1px solid var(--line-hot); border-left: 2px solid var(--fg-dim);
  color: var(--fg-dim); font-size: 12px; line-height: 1.6;
  padding: 14px 16px; margin: 0 0 26px;
}

.doc h2 {
  margin: 0 0 6px; font-size: clamp(20px, 2.6vw, 28px); line-height: 1.2;
  color: var(--fg-bright); letter-spacing: -0.01em; text-transform: none;
}
.doc h2 + * { margin-top: 0; }

/* Section headers match the challenge inspector: a // marker and a trailing
   rule that runs to the edge of the measure. */
.doc h3 {
  display: flex; align-items: center; gap: 9px;
  margin: 34px 0 12px; scroll-margin-top: 18px;
  color: var(--fg); font-size: 11.5px; letter-spacing: 0.16em; text-transform: uppercase;
}
.doc h3::before { content: "//"; color: var(--fg-faint); }
.doc h3::after { content: ""; flex: 1; border-top: 1px solid var(--line); }

.doc p { margin: 0 0 14px; }
.doc strong, .doc b { color: var(--fg-bright); font-weight: 700; }
.doc em { color: var(--fg-dim); }
.doc a { text-decoration: underline; text-underline-offset: 3px; }
.doc code { font-size: 0.94em; }

.doc ul, .doc ol { margin: 0 0 14px; padding-left: 0; list-style: none;
  display: flex; flex-direction: column; gap: 9px; }
.doc li { position: relative; padding-left: 20px; }
.doc ul > li::before { content: "\25b8"; position: absolute; left: 2px; top: 0;
  color: var(--fg-faint); }
.doc ol { counter-reset: docitem; }
.doc ol > li::before { counter-increment: docitem; content: counter(docitem) ".";
  position: absolute; left: 0; top: 0; color: var(--fg-faint);
  font-variant-numeric: tabular-nums; }

.doc hr { margin: 30px 0 18px; border: none; height: 1px;
  background: linear-gradient(90deg, var(--line-hot), transparent); }
/* The sign-off after the final rule is an aside, not another instruction. */
.doc hr + p { color: var(--fg-faint); font-style: italic; }

/* Section index. Built by term.js from the document's own headings, so it
   appears on any page long enough to need it and nowhere else. */
.doc-index { position: sticky; top: 14px; display: flex; flex-direction: column; gap: 2px;
  border-left: 1px solid var(--line); padding-left: 14px; }
.doc-index .k { margin-bottom: 6px; }
.doc-index a { color: var(--fg-faint); font-size: 12px; padding: 3px 0;
  border-left: 2px solid transparent; margin-left: -16px; padding-left: 14px; }
.doc-index a:hover { color: var(--fg); text-decoration: none; }
.doc-index a.active { color: var(--fg-bright); border-left-color: var(--fg); }

@media (max-width: 900px) {
  .doc-wrap { grid-template-columns: 1fr; }
  .doc-index { display: none; }
}

/* --- Team roster ---------------------------------------------------------- */

.roster-search { display: flex; gap: 8px; flex-wrap: wrap; max-width: 560px; margin: 0 0 16px; }
.roster-search select { flex: 0 0 auto; width: auto; }
.roster-search input { flex: 1 1 200px; }
.roster-search .btn { flex: 0 0 auto; }

/* --- Standby / empty states ----------------------------------------------- */
/* The scoreboard sits empty until the first solve, which means this is the
   state most people see first. It should look like equipment waiting, not like
   a page that failed to load. */
.board-empty {
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  text-align: center; padding: clamp(34px, 7vw, 74px) 24px;
  border: 1px solid var(--line); background:
    radial-gradient(120% 80% at 50% 0%, rgba(255, 176, 0, 0.05), transparent 65%), var(--bg-panel);
}
.board-empty .be-led { width: 10px; height: 10px; border-radius: 50%;
  background: var(--fg-dim); box-shadow: 0 0 10px var(--fg-dim);
  animation: led-pulse 2.6s ease-in-out infinite; }
.board-empty .be-title { margin: 0; color: var(--fg-bright); font-size: 13px;
  letter-spacing: 0.3em; text-transform: uppercase; }
.board-empty .be-sub { margin: 0; max-width: 46ch; color: var(--fg-faint);
  font-size: 13px; line-height: 1.7; }
.pager { margin-top: 14px; }

/* --- AI Range accent on the evidence browser ------------------------------ */
/* Everywhere else in the theme, cold means machine. The browser is where a
   player spends the event, and until now the one directory holding live models
   looked exactly like the six holding dead files. Identity is coloured here;
   progress is not - an analysed artifact stays green whatever it is, because
   once you hold the code, what you still have left to do is the louder fact. */
.fm-dir.ai { color: var(--ai-dim); }
.fm-dir.ai:hover { color: var(--ai); }
.fm-dir.ai.active { color: var(--ai); border-left-color: var(--ai); }

table.fm-list.ai tr.file:not(.solved) td.name { color: var(--ai); }
table.fm-list.ai tr.file:not(.solved):hover td.name { color: var(--ai);
  text-shadow: 0 0 10px var(--ai-glow); }

.chal .kicker.ai { color: var(--ai); }
