/* ============================================================
   KEYBOARD CONTROL — style.css
   Aesthetic: phosphor-green terminal, CRT monitor, corporate
   ============================================================ */

/* ── Reset & Base ─────────────────────────────────────────── */
*, *::before, *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

:root {
  --green:        #00ff41;
  --green-dim:    #00aa2a;
  --green-dark:   #005015;
  --green-glow:   rgba(0, 255, 65, 0.35);
  --amber:        #ffb700;
  --red:          #ff3333;
  --cyan:         #00e5ff;
  --bg:           #000000;
  --bg-panel:     #030e06;
  --border:       #1a5c2a;
  --text-dim:     #3a7a4a;
  --font:         'Courier New', Courier, monospace;
  --timer-start:  33;
}

html, body {
  height: 100%;
  overflow: hidden;
  background: var(--bg);
  color: var(--green);
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.4;
  -webkit-font-smoothing: none;
  -webkit-tap-highlight-color: transparent;   /* no blue tap-flash on mobile */
}

/* Tappable controls: skip the 300ms double-tap delay and block pinch/double-tap
   zoom so rapid play taps register instantly. */
.key-cell,
.shop-card,
.exp-btn,
.overlay-btn,
.reset-modal-btn,
.tut-tooltip-btn,
.reset-save-btn,
.options-btn,
#return-to-service-btn {
  touch-action: manipulation;
}

/* ── CRT Scanlines Overlay ─────────────────────────────────── */
#scanlines {
  position: fixed;
  inset: 0;
  background: repeating-linear-gradient(
    to bottom,
    transparent 0px,
    transparent 3px,
    rgba(0, 0, 0, 0.18) 3px,
    rgba(0, 0, 0, 0.18) 4px
  );
  pointer-events: none;
  z-index: 100;
}

/* ── Game Container — CSS Grid ─────────────────────────────── */
/* Header and input bar stay pinned; everything else lives in a single
   scrollable column (#content-scroll) so panels never have to be squeezed
   to fit — they just stack and the window scrolls. */
#game-container {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header"
    "content"
    "input";
  width: 100%;
  max-width: 1080px;
  margin: 0 auto;
  height: 100vh;
  padding: 8px;
  gap: 6px;
  position: relative;
  transition: max-width 0.35s ease;
}

/* Map panels to named grid areas */
#header         { grid-area: header; }
#content-scroll { grid-area: content; }
#input-panel    { grid-area: input; }

/* The single scrollable column holding sequence / timer+log / bindings /
   break-room / inventory, in document order. */
#content-scroll {
  display: flex;
  flex-direction: column;
  gap: 6px;
  overflow-y: auto;
  overflow-x: hidden;
  min-height: 0;
  scrollbar-width: thin;
  scrollbar-color: var(--green-dark) transparent;
}

/* Panels keep their natural height and the column scrolls — never squeeze a
   child below its content (which would bleed one panel under the next). This is
   a vertical-space rule, so it applies at every viewport width, not just narrow
   ones; the column always scrolls instead of overlapping. */
#content-scroll > * { flex-shrink: 0; }

#content-scroll::-webkit-scrollbar { width: 4px; }
#content-scroll::-webkit-scrollbar-track { background: transparent; }
#content-scroll::-webkit-scrollbar-thumb { background: var(--green-dark); }

/* Break room: a little extra width since bindings + REQUISITION now both
   run full-width, stacked, instead of side by side. */
#game-container.in-break {
  max-width: 1200px;
}

/* ── Panel label ───────────────────────────────────────────── */
.panel-label {
  font-size: 11px;
  color: var(--text-dim);
  letter-spacing: 0.15em;
  text-transform: uppercase;
  margin-bottom: 4px;
  padding-bottom: 3px;
  border-bottom: 1px solid var(--border);
}

/* ── HEADER ────────────────────────────────────────────────── */
#header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 5px 10px;
  border: 1px solid var(--border);
  background: var(--bg-panel);
  text-shadow: 0 0 8px var(--green-glow);
  letter-spacing: 0.1em;
}

#header-title {
  font-weight: bold;
  color: var(--green);
  white-space: nowrap;
}

#header-stats {
  color: var(--green-dim);
  font-size: 12px;
  letter-spacing: 0.05em;
  white-space: nowrap;
}

#header-stats span {
  color: var(--green);
}

/* ── HEADER STAT GLYPHS ─────────────────────────────────────
   Each counter is labelled with a cryptic glyph instead of a plain word;
   hovering (or focusing, for keyboard/touch) reveals what it means via an
   on-theme readout. Built on data-tip rather than the native title
   attribute, so the browser's own unstyled tooltip never fires alongside it. */
#header-stats .stat-glyph {
  position: relative;
  display: inline-block;
  color: var(--green-dim);
  cursor: help;
}

#header-stats .stat-glyph:hover,
#header-stats .stat-glyph:focus {
  color: var(--green);
  outline: none;
}

.stat-glyph::after {
  content: attr(data-tip);
  position: absolute;
  top: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  padding: 4px 8px;
  border: 1px solid var(--border);
  background: var(--bg-panel);
  color: var(--green);
  font-size: 10px;
  letter-spacing: 0.08em;
  box-shadow: 0 0 8px var(--green-glow);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
  z-index: 60;
}

/* The last couple of glyphs sit near the header's right edge — anchor their
   tooltip to the right instead of centering, so it can't clip offscreen. */
.stat-glyph.tip-left::after {
  left: auto;
  right: 0;
  transform: none;
}

.stat-glyph:hover::after,
.stat-glyph:focus::after {
  opacity: 1;
}

/* ── SEQUENCE PANEL ────────────────────────────────────────── */
#sequence-panel {
  padding: 8px 10px;
  border: 1px solid var(--border);
  background: var(--bg-panel);
  min-height: 90px;
}

#sequence-display {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 8px 0 6px;
  min-height: 38px;
}

.seq-cmd {
  font-size: 13px;
  letter-spacing: 0.05em;
  padding: 2px 5px;
  border: 1px solid transparent;
  border-radius: 2px;
  transition: color 0.1s, background 0.1s;
}

.seq-cmd.done {
  color: var(--green-dim);
  text-decoration: line-through;
  border-color: transparent;
}

.seq-cmd.active {
  color: #ffffff;
  background: var(--green-dark);
  border-color: var(--green);
  text-shadow: 0 0 10px var(--green), 0 0 20px var(--green-glow);
  font-weight: bold;
  animation: pulse-border 1s ease-in-out infinite;
}

.seq-cmd.pending {
  color: var(--green-dim);
  border-color: transparent;
}

.seq-arrow {
  color: var(--text-dim);
  font-size: 12px;
}

/* Progress bar */
#progress-track {
  height: 4px;
  background: #0a1f0e;
  border: 1px solid var(--border);
  border-radius: 2px;
  overflow: hidden;
  margin-top: 6px;
}

#progress-fill {
  height: 100%;
  background: var(--green);
  box-shadow: 0 0 6px var(--green-glow);
  width: 0%;
  transition: width 0.2s ease;
}

#rebind-hint {
  margin-top: 6px;
  font-size: 11px;
  color: var(--cyan);
  letter-spacing: 0.08em;
  text-shadow: 0 0 6px rgba(0, 229, 255, 0.4);
}

/* Clickable break-room exit — mirrors pressing ENTER, so touch players aren't
   stranded in the break room. */
#return-to-service-btn {
  display: block;
  margin: 8px auto 0;
  padding: 8px 28px;
  border: 1px solid var(--cyan);
  background: transparent;
  color: var(--cyan);
  font-family: var(--font);
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  text-shadow: 0 0 6px rgba(0, 229, 255, 0.4);
  transition: background 0.2s, box-shadow 0.2s;
}

#return-to-service-btn:hover,
#return-to-service-btn:focus {
  background: rgba(0, 229, 255, 0.1);
  box-shadow: 0 0 14px rgba(0, 229, 255, 0.4);
  outline: none;
}

/* ── MIDDLE ROW (timer + log) ──────────────────────────────── */
#middle-row {
  display: grid;
  grid-template-columns: 140px 1fr;
  gap: 6px;
  overflow: hidden;
}

/* TIMER */
#timer-panel {
  padding: 8px 10px;
  border: 1px solid var(--border);
  background: var(--bg-panel);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
}

#timer-number {
  font-size: 48px;
  font-weight: bold;
  color: var(--green);
  text-shadow: 0 0 12px var(--green), 0 0 24px var(--green-glow);
  letter-spacing: 0.05em;
  font-variant-numeric: tabular-nums;
  transition: color 0.3s, text-shadow 0.3s;
}

#timer-number.timer-warning {
  color: var(--amber);
  text-shadow: 0 0 12px rgba(255, 183, 0, 0.6);
  animation: flicker 0.4s ease-in-out infinite alternate;
}

#timer-number.timer-danger {
  color: var(--red);
  text-shadow: 0 0 16px rgba(255, 51, 51, 0.8);
  animation: flicker 0.2s ease-in-out infinite alternate;
}

#timer-track {
  width: 100%;
  height: 6px;
  background: #0a1f0e;
  border: 1px solid var(--border);
  border-radius: 2px;
  overflow: hidden;
}

#timer-fill {
  height: 100%;
  width: 100%;
  background: var(--green);
  box-shadow: 0 0 8px var(--green-glow);
  transition: width 1s linear, background 0.5s, box-shadow 0.5s;
  transform-origin: left;
}

#timer-fill.warning {
  background: var(--amber);
  box-shadow: 0 0 8px rgba(255, 183, 0, 0.5);
}

#timer-fill.danger {
  background: var(--red);
  box-shadow: 0 0 8px rgba(255, 51, 51, 0.6);
}

/* TIME FREEZE — countdown held */
#timer-number.timer-frozen {
  color: var(--cyan);
  text-shadow: 0 0 14px rgba(0, 229, 255, 0.7);
  animation: flicker 0.8s ease-in-out infinite alternate;
}

#timer-fill.frozen {
  background: var(--cyan);
  box-shadow: 0 0 10px rgba(0, 229, 255, 0.6);
}

/* MACHINE LOG */
#log-panel {
  padding: 8px 10px;
  border: 1px solid var(--border);
  background: var(--bg-panel);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

#machine-log {
  flex: 1;
  /* Bounded at every viewport so streamed log lines scroll inside the panel
     instead of growing #middle-row — which would push the keyboard down and
     reflow anything measured against it (e.g. the spotlight highlight box). */
  max-height: clamp(120px, 28vh, 360px);
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: thin;
  scrollbar-color: var(--green-dark) transparent;
}

#machine-log::-webkit-scrollbar { width: 4px; }
#machine-log::-webkit-scrollbar-track { background: transparent; }
#machine-log::-webkit-scrollbar-thumb { background: var(--green-dark); }

.log-line {
  font-size: 12px;
  color: var(--green-dim);
  padding: 1px 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  animation: type-in 0.15s ease;
}

.log-line::before {
  content: '> ';
  color: var(--text-dim);
}

.log-line.log-success { color: var(--green); }
.log-line.log-error   { color: var(--red); }
.log-line.log-warning { color: var(--amber); }
.log-line.log-info    { color: var(--cyan); }
.log-line.log-danger  { color: var(--red); font-weight: bold; letter-spacing: 0.1em; }

/* Final-seconds warnings (≤5s) — bigger, louder, and allowed to wrap for impact. */
.log-line.log-critical {
  color: var(--red);
  font-size: 15px;
  font-weight: bold;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-shadow: 0 0 10px var(--red), 0 0 20px rgba(255, 51, 51, 0.6);
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  padding: 2px 0;
  animation: type-in 0.15s ease, flicker 0.18s ease-in-out infinite alternate;
}

/* End-game death messages — each line grows bigger, brighter and glitchier than
   the last, driven by --dying-intensity (0→1, set per line in triggerConsequences).
   The red/cyan split + clip-slice jitter give the glitch-text crescendo. */
.log-line.log-dying {
  color: var(--red);
  font-weight: bold;
  letter-spacing: 0.12em;
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  padding: 2px 0;
  font-size: calc(13px + var(--dying-intensity, 0) * 11px);
  text-shadow:
    0 0 calc(6px + var(--dying-intensity, 0) * 18px) var(--red),
    0 0 calc(10px + var(--dying-intensity, 0) * 26px) rgba(255, 51, 51, calc(0.35 + var(--dying-intensity, 0) * 0.55)),
    1px 0 2px rgba(0, 229, 255, 0.5),
   -1px 0 2px rgba(255, 51, 51, 0.7);
  animation: type-in 0.15s ease, glitch-text 0.16s steps(2, end) infinite;
}

/* ── KEY BINDINGS PANEL ────────────────────────────────────── */
#bindings-panel {
  padding: 8px 10px;
  border: 1px solid var(--border);
  background: var(--bg-panel);
}

#freq-legend {
  color: var(--text-dim);
  font-size: 10px;
}
#freq-legend .freq-high { color: var(--red); }
#freq-legend .freq-med  { color: var(--amber); }
#freq-legend .freq-low  { color: var(--green-dim); }

#bindings-grid {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 2px;
}

/* Wrapper around the three main keyboard rows. Wide layout: a transparent
   vertical stack (identical to the rows living directly in the grid). Narrow
   layout collapses it into one continuous orthographic grid (see media query). */
.keys-main {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.key-row {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  justify-content: center;
}

.key-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-width: 58px;
  padding: 3px 4px;
  border: 1px solid var(--border);
  background: #020b04;
  border-radius: 3px;
  cursor: default;
  transition: border-color 0.15s, background 0.15s, opacity 0.15s;
  user-select: none;
}

.key-cell.highlight {
  border-color: var(--green);
  background: var(--green-dark);
  box-shadow: 0 0 8px var(--green-glow);
}

/* Brief pulse on the exact key cell that was just pressed correctly, layered
   on top of whatever steady state (highlight/precog/etc.) the cell is in. */
.key-cell.correct-flash {
  animation: key-correct-pulse 0.3s ease;
}

@keyframes key-correct-pulse {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.14);
         border-color: #ffffff;
         box-shadow: 0 0 16px var(--green), 0 0 28px var(--green-glow); }
  100% { transform: scale(1); }
}

/* PRECOGNITION — dim preview of the next command's key */
.key-cell.precog {
  border-color: var(--green-dim);
  box-shadow: 0 0 5px rgba(0, 170, 42, 0.4);
  opacity: 0.85;
}

/* KEY REVEAL — every upcoming target illuminated */
.key-cell.revealed {
  border-color: var(--cyan);
  background: rgba(0, 229, 255, 0.06);
  box-shadow: 0 0 6px rgba(0, 229, 255, 0.3);
}

.key-cell.selected {
  border-color: var(--cyan);
  background: rgba(0, 229, 255, 0.08);
  box-shadow: 0 0 8px rgba(0, 229, 255, 0.4);
}

.key-cell.rebindable:hover {
  border-color: var(--cyan);
  cursor: pointer;
}

/* Mouse-click key entry during PLAYING — clicking presses the key (for a fee). */
.key-cell.clickable { cursor: pointer; }
.key-cell.clickable:hover {
  border-color: var(--cyan);
  box-shadow: 0 0 6px rgba(0, 229, 255, 0.3);
}

/* Frequency heat in rebinding mode */
.key-cell.freq-high { border-color: var(--red);   background: rgba(255, 51,  51,  0.06); }
.key-cell.freq-med  { border-color: var(--amber); background: rgba(255, 183, 0,   0.05); }
.key-cell.freq-low  { border-color: var(--green); background: var(--green-dark); }

.key-label {
  font-size: 13px;
  font-weight: bold;
  color: var(--green);
  text-shadow: 0 0 6px var(--green-glow);
  line-height: 1;
}

.key-cmd {
  font-size: 8px;
  color: var(--green-dim);
  letter-spacing: 0.04em;
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 52px;
  text-align: center;
}

/* Locked (not yet unlocked) or blind-and-uncalled keys — dim and inert.
   Both look identical so a blind expansion can't be spotted by position. */
.key-cell.locked {
  opacity: 0.25;
  border-style: dashed;
  background: transparent;
  box-shadow: none;
  cursor: default;
}
.key-cell.locked .key-label { color: var(--text-dim); text-shadow: none; }
.key-cell.locked .key-cmd   { color: var(--text-dim); }

/* Reserved main number row — triggers consumables 1–9, never bindable.
   Empty slots stay dim/inert; slots holding an owned consumable ("usable")
   light up and show the item name + remaining charges. */
.key-cell.reserved {
  opacity: 0.55;
  border-color: var(--green-dark);
  background: #010a05;
  cursor: default;
}
.key-cell.reserved .key-label { color: var(--green-dim); text-shadow: none; }
.key-cell.reserved .key-cmd   { color: var(--text-dim); font-size: 7px; letter-spacing: 0.1em; }

.key-cell.reserved.usable {
  opacity: 1;
  border-color: var(--green-dim);
  cursor: pointer;
}
.key-cell.reserved.usable .key-label { color: var(--green); text-shadow: 0 0 6px var(--green-glow); }
.key-cell.reserved.usable .key-cmd   { color: var(--green-dim); font-size: 8px; letter-spacing: 0.04em; }
.key-cell.reserved.usable:hover {
  border-color: var(--cyan);
  box-shadow: 0 0 6px rgba(0, 229, 255, 0.3);
}

.key-cell.reserved .key-count {
  font-size: 8px;
  color: var(--amber);
  margin-top: 1px;
}

.key-row.reserved-row { margin-bottom: 3px; }
.key-row.reserved-row .key-cell { min-width: 58px; padding: 3px 4px; }

/* Numpad bonus cluster (appears only once it begins unlocking). */
.numpad-label {
  margin: 7px 0 2px;
  font-size: 10px;
  letter-spacing: 0.14em;
  color: var(--cyan);
  text-align: center;
  text-shadow: 0 0 6px rgba(0, 229, 255, 0.4);
}
.key-row.numpad-row .key-cell { min-width: 52px; }

/* ── BREAK-ROOM EXPANSION CHOICE (REVEAL vs BLIND) ───────────── */
#expansion-choice {
  margin-top: 6px;
  border: 1px solid var(--cyan);
  background: rgba(0, 229, 255, 0.04);
  border-radius: 3px;
  padding: 6px 10px;
}

.exp-title {
  font-size: 12px;
  font-weight: bold;
  color: var(--cyan);
  letter-spacing: 0.08em;
  text-shadow: 0 0 6px rgba(0, 229, 255, 0.4);
}

.exp-blurb {
  font-size: 10px;
  color: var(--green-dim);
  margin: 2px 0 6px;
}

.exp-options {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.exp-btn {
  flex: 1;
  min-width: 200px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-align: left;
  padding: 7px 10px;
  border: 1px solid var(--border);
  background: #020b04;
  border-radius: 3px;
  color: var(--green);
  font-family: var(--font);
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}

.exp-btn:hover {
  border-color: var(--cyan);
  box-shadow: 0 0 8px rgba(0, 229, 255, 0.35);
}

.exp-btn.chosen {
  border-color: var(--cyan);
  background: rgba(0, 229, 255, 0.1);
  box-shadow: 0 0 10px rgba(0, 229, 255, 0.4);
}

.exp-btn-name {
  font-size: 12px;
  font-weight: bold;
  letter-spacing: 0.08em;
  text-shadow: 0 0 6px var(--green-glow);
}

.exp-btn-desc {
  font-size: 10px;
  color: var(--green-dim);
  letter-spacing: 0.03em;
}

/* ── BREAK ROOM / REQUISITION SHOP ─────────────────────────── */
/* Sizes to its own content — #content-scroll owns the scrolling now, so this
   panel doesn't need its own inner scroll region. */
#break-room {
  padding: 8px 10px;
  border: 1px solid var(--cyan);
  background: var(--bg-panel);
  box-shadow: inset 0 0 24px rgba(0, 229, 255, 0.05);
}

#break-room .panel-label {
  color: var(--cyan);
  border-bottom-color: var(--cyan);
}

#break-room kbd {
  color: var(--green);
}

#shop-grid {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 4px;
}

/* Drip-feed status line at the top of REQUISITION. */
.shop-drip-note {
  font-size: 10px;
  color: var(--cyan);
  letter-spacing: 0.08em;
  text-shadow: 0 0 6px rgba(0, 229, 255, 0.35);
}

/* Grouped category sections (organised beyond colour coding). */
.shop-category-label {
  font-size: 10px;
  color: var(--text-dim);
  letter-spacing: 0.15em;
  text-transform: uppercase;
  margin-bottom: 4px;
  padding-bottom: 3px;
  border-bottom: 1px solid var(--border);
}

.shop-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  align-content: start;
  gap: 6px;
}

/* Not yet drip-unlocked — visible teaser, inert. */
.shop-card.locked {
  opacity: 0.4;
  border-style: dashed;
  cursor: not-allowed;
}
.shop-card.locked .shop-cost { color: var(--text-dim); }

.shop-card {
  border: 1px solid var(--border);
  background: #020b04;
  border-radius: 3px;
  padding: 6px 8px;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
  user-select: none;
}

.shop-card.affordable:hover {
  border-color: var(--cyan);
  box-shadow: 0 0 8px rgba(0, 229, 255, 0.35);
}

.shop-card.too-expensive {
  opacity: 0.45;
  cursor: not-allowed;
}

.shop-card.owned-max {
  opacity: 0.6;
  cursor: default;
  border-color: var(--green-dark);
}

/* Category accents */
.shop-card.shop-upgrade  { border-left: 3px solid var(--amber); }
.shop-card.shop-cosmetic { border-left: 3px solid var(--cyan); }
.shop-card.shop-consumable { border-left: 3px solid var(--green); }

.shop-name {
  font-size: 12px;
  font-weight: bold;
  color: var(--green);
  text-shadow: 0 0 6px var(--green-glow);
  letter-spacing: 0.05em;
  display: flex;
  justify-content: space-between;
  gap: 6px;
}

.shop-owned {
  color: var(--amber);
  font-weight: normal;
  font-size: 10px;
}

.shop-blurb {
  font-size: 10px;
  color: var(--green-dim);
  line-height: 1.4;
  margin: 3px 0 5px;
}

.shop-cost {
  font-size: 11px;
  color: var(--cyan);
  letter-spacing: 0.1em;
  text-align: right;
}

/* ── INPUT ECHO ────────────────────────────────────────────── */
#input-panel {
  padding: 5px 10px;
  border: 1px solid var(--border);
  background: var(--bg-panel);
  display: flex;
  align-items: center;
  font-size: 14px;
  letter-spacing: 0.05em;
}

.prompt {
  color: var(--green-dim);
}

#input-echo {
  color: var(--green);
  text-shadow: 0 0 8px var(--green-glow);
  letter-spacing: 0.1em;
  min-width: 6ch;
}

#input-echo.echo-flash {
  animation: echo-flash 0.4s ease;
}

#input-echo.echo-flash-error {
  animation: echo-flash-error 0.4s ease;
}

.cursor {
  color: var(--green);
  animation: blink 1s step-end infinite;
  text-shadow: 0 0 6px var(--green-glow);
}

/* ── OVERLAY (start / game-over) ───────────────────────────── */
#overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: rgba(0, 0, 0, 0.88);
  display: flex;
  align-items: center;
  justify-content: center;
}

#overlay.hidden {
  display: none;
}

/* Game-over recap keeps the dead game screen fully blacked out, matching the
   GAME OVER splash (the default translucent backdrop is used by the start screen). */
#overlay.blackout {
  background: #000;
}

.overlay-content {
  border: 2px solid var(--green);
  background: var(--bg-panel);
  box-shadow: 0 0 30px var(--green-glow), inset 0 0 40px rgba(0, 255, 65, 0.04);
  padding: 36px 48px;
  max-width: 640px;
  width: 90%;
  max-height: 92vh;
  overflow-y: auto;
  text-align: center;
}

/* ASCII employee-induction banner (mirrors README.md). Shown alone and
   centered on start, then its glyph cells blink out one by one.
   Block-art only tiles into solid letters when the full-block glyph (U+2588)
   fills the whole character cell. 'Courier New' leaves gaps, so prefer a
   GitHub-style monospace (Consolas/SF Mono/DejaVu) whose block fills the cell;
   line-height:1 keeps rows touching and the glow is kept light so edges stay crisp. */
.overlay-ascii {
  font-family: 'Consolas', 'SFMono-Regular', 'SF Mono', 'Menlo',
               'DejaVu Sans Mono', 'Liberation Mono', monospace;
  color: var(--green);
  text-shadow: 0 0 6px var(--green-glow);
  font-size: clamp(9px, 3vw, 18px);
  /* A touch more than 1 so each row gets a little vertical breathing room —
     some fallback monospace fonts render the full-block glyph (U+2588)
     slightly taller than its nominal em box, which at line-height:1 could
     clip a hair off the banner and force a scrollbar on the box around it. */
  line-height: 1.15;
  letter-spacing: 0;
  /* The banner rows are different lengths. They MUST all start at the same
     left edge or the columns drift and the letters become unreadable, so
     left-align the rows and center the whole block with auto margins +
     fit-content (NOT text-align:center, which centers each row separately). */
  text-align: left;
  width: fit-content;
  max-width: 100%;
  margin: 8px auto 20px;
  white-space: pre;
  overflow-x: auto;
}

.overlay-ascii.gone { display: none; }

/* Each glyph is its own cell so it can blink out individually. Pin every cell
   to a rigid 1-character grid (inline-block + width:1ch, height:1em) so columns
   line up across rows AND rows touch top-to-bottom. The block itself is painted
   as the cell's BACKGROUND rather than drawn as the U+2588 glyph — fonts render
   that glyph slightly smaller than its cell, which produced a tiled/gappy look;
   a background fills the whole box edge-to-edge for solid letters. */
.ascii-cell {
  display: inline-block;
  width: 1ch;
  height: 1em;
  vertical-align: top;
  color: transparent;            /* hide the glyph; the box IS the block */
  transition: opacity 0.12s ease;
}

/* Lit blocks: solid fill + glow. Space cells stay transparent, which is what
   forms the gaps between the letters. */
.ascii-cell:not(.ascii-space) {
  background: var(--green);
  box-shadow: 0 0 6px var(--green-glow);
}

/* Each block extinguishes individually: a brief white flare, then out. */
.ascii-cell.out { animation: ascii-extinguish 0.26s ease forwards; }

@keyframes ascii-extinguish {
  0%   { opacity: 1; background: #ffffff; box-shadow: 0 0 10px #ffffff, 0 0 18px var(--green-glow); }
  35%  { opacity: 1; }
  100% { opacity: 0; }
}

/* The induction text, revealed once the banner has finished dissolving. */
.intro-reveal { animation: intro-fade-in 0.5s ease both; }

@keyframes intro-fade-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Keep the BEGIN SERVICE button and sign-off from clumping at the bottom. */
#intro-body .overlay-btn { margin-top: 8px; }
#intro-body .overlay-flavor { margin-top: 16px; margin-bottom: 0; }

/* Section headings inside the induction body */
.overlay-body .overlay-heading {
  color: var(--green);
  font-weight: bold;
  letter-spacing: 0.12em;
  margin-top: 16px;
  text-shadow: 0 0 8px var(--green-glow);
}
.overlay-body .overlay-heading:first-child {
  margin-top: 0;
}

.overlay-body blockquote {
  margin: 8px 0;
  padding-left: 12px;
  border-left: 2px solid var(--border);
  color: var(--green);
  font-style: italic;
}

.overlay-title {
  font-size: 28px;
  font-weight: bold;
  color: var(--green);
  text-shadow: 0 0 14px var(--green), 0 0 30px var(--green-glow);
  letter-spacing: 0.15em;
  margin-bottom: 8px;
}

.overlay-subtitle {
  font-size: 13px;
  color: var(--green-dim);
  letter-spacing: 0.12em;
  margin-bottom: 24px;
}

.overlay-body {
  font-size: 13px;
  color: var(--green-dim);
  line-height: 2;
  margin-bottom: 20px;
  text-align: left;
}

.overlay-body p {
  padding: 2px 0;
}

.overlay-body .warning {
  color: var(--red);
  letter-spacing: 0.1em;
  margin-top: 10px;
}

.overlay-stats {
  font-size: 12px;
  color: var(--green-dim);
  line-height: 2;
  margin: 16px 0;
  border: 1px solid var(--border);
  padding: 10px 16px;
  text-align: left;
}

.overlay-flavor {
  font-size: 12px;
  color: var(--text-dim);
  font-style: italic;
  margin-bottom: 24px;
  letter-spacing: 0.05em;
}

.overlay-btn {
  display: inline-block;
  padding: 10px 32px;
  border: 2px solid var(--green);
  background: transparent;
  color: var(--green);
  font-family: var(--font);
  font-size: 14px;
  letter-spacing: 0.15em;
  cursor: pointer;
  text-transform: uppercase;
  text-shadow: 0 0 8px var(--green-glow);
  transition: background 0.2s, box-shadow 0.2s;
}

.overlay-btn:hover, .overlay-btn:focus {
  background: rgba(0, 255, 65, 0.1);
  box-shadow: 0 0 16px var(--green-glow);
  outline: none;
}

.overlay-btn + .overlay-btn {
  margin-left: 16px;
  border-color: var(--green-dim);
  color: var(--green-dim);
}

/* Gated ATTEMPT REDEMPTION that the player can see but not yet afford. */
.overlay-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  text-shadow: none;
}
.overlay-btn:disabled:hover {
  background: transparent;
  box-shadow: none;
}

.overlay-redeem-note {
  margin-top: 12px;
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--amber);
}

/* ── CALM MODE toggle (KACS-SAFE-001) ────────────────────────
   Shown on the start screen and in the header options popout — the in-universe
   "ERGONOMIC DISPLAY DIRECTIVE" the Organization begrudgingly complies with.
   Disabling flicker/shake is a safety accommodation, not a cosmetic option,
   so it's styled plainly rather than themed red/amber like the hazards it defuses. */
.calm-toggle {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 16px 0;
  padding: 10px 14px;
  border: 1px solid var(--border);
  font-size: 11px;
  letter-spacing: 0.04em;
  line-height: 1.5;
  color: var(--cyan);
  text-align: left;
  cursor: pointer;
}

.calm-toggle input[type="checkbox"] {
  flex: none;
  margin-top: 2px;
  accent-color: var(--cyan);
  cursor: pointer;
}

.calm-toggle:has(input:disabled) { cursor: default; }
.calm-toggle input:disabled { cursor: default; }

.flash-warning {
  margin: 12px 0 0;
  padding: 10px 14px;
  border: 1px solid var(--amber);
  font-size: 11px;
  letter-spacing: 0.04em;
  line-height: 1.5;
  color: var(--amber);
  text-align: left;
}

/* ── ANIMATIONS ────────────────────────────────────────────── */
@keyframes blink {
  0%, 100% { opacity: 1; }
  50%       { opacity: 0; }
}

@keyframes flicker {
  0%   { opacity: 1; }
  100% { opacity: 0.75; }
}

@keyframes pulse-border {
  0%, 100% { box-shadow: 0 0 6px var(--green-glow); }
  50%       { box-shadow: 0 0 16px var(--green), 0 0 28px var(--green-glow); }
}

@keyframes type-in {
  from { opacity: 0; transform: translateX(-4px); }
  to   { opacity: 1; transform: translateX(0); }
}

@keyframes echo-flash {
  0%   { color: #ffffff; text-shadow: 0 0 14px #ffffff; }
  60%  { color: var(--green); text-shadow: 0 0 8px var(--green-glow); }
  100% { color: var(--green); text-shadow: 0 0 8px var(--green-glow); }
}

@keyframes echo-flash-error {
  0%   { color: var(--red); text-shadow: 0 0 14px var(--red); }
  60%  { color: var(--red); text-shadow: 0 0 6px rgba(255, 51, 51, 0.6); }
  100% { color: var(--green); text-shadow: 0 0 8px var(--green-glow); }
}

/* Shake effect — magnitude driven by --shake-mag (set in JS). Used both for the fixed
   error jolt and the Containment Breach's per-keystroke shake, which grows with the
   crack stage (see shakeContainer() / crackShakeMagnitude() in game.js). */
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  15%       { transform: translateX(calc(-1    * var(--shake-mag, 6px))); }
  30%       { transform: translateX(calc(1     * var(--shake-mag, 6px))); }
  45%       { transform: translateX(calc(-0.67 * var(--shake-mag, 6px))); }
  60%       { transform: translateX(calc(0.67  * var(--shake-mag, 6px))); }
  75%       { transform: translateX(calc(-0.33 * var(--shake-mag, 6px))); }
  90%       { transform: translateX(calc(0.33  * var(--shake-mag, 6px))); }
}

.shake {
  animation: shake 0.4s ease;
}

/* Consequences red flash */
@keyframes consequences {
  0%   { background: #000; }
  10%  { background: #1a0000; }
  30%  { background: #2a0000; }
  50%  { background: #1a0000; }
  70%  { background: #0a0000; }
  100% { background: #000; }
}

.consequences {
  animation: consequences 2s ease infinite;
}

/* ── SCREEN CORRUPTION (timer death throes) ──────────────────── */
/* Two intensity tiers applied to #game-container as the countdown dies:
   .glitch-active (≤15s) is an uneasy jitter + faint chromatic split;
   .glitch-severe (≤5s) tears, skews and blacks out violently. The recap
   overlay and GAME OVER splash are separate fixed elements, so neither is
   distorted by the container animation. */

/* Subtle: occasional 1px jitter, faint flicker, a thin RGB fringe. */
@keyframes glitch-jitter {
  0%, 100% { transform: translate(0, 0);    filter: none; }
  92%      { transform: translate(0, 0);    filter: none; }
  93%      { transform: translate(-1px, 1px);
             filter: drop-shadow(1px 0 var(--red)) drop-shadow(-1px 0 var(--cyan)); }
  95%      { transform: translate(1px, -1px); opacity: 0.92; }
  97%      { transform: translate(-1px, 0);
             filter: drop-shadow(-1px 0 var(--red)) drop-shadow(1px 0 var(--cyan)); }
  99%      { transform: translate(0, 0);     opacity: 1; }
}

.glitch-active {
  animation: glitch-jitter 2.2s steps(1, end) infinite;
  will-change: transform, filter, opacity;
}

/* Violent: large displacement, skew, sliced tearing via clip-path, blackouts. */
@keyframes glitch-tear {
  0%   { transform: translate(0, 0) skewX(0);
         filter: drop-shadow(2px 0 var(--red)) drop-shadow(-2px 0 var(--cyan));
         clip-path: none; opacity: 1; }
  12%  { transform: translate(-4px, 2px) skewX(3deg);
         clip-path: inset(8% 0 60% 0); opacity: 0.85; }
  18%  { transform: translate(5px, -2px) skewX(-4deg);
         filter: drop-shadow(-3px 0 var(--red)) drop-shadow(3px 0 var(--cyan));
         clip-path: inset(55% 0 12% 0); }
  24%  { transform: translate(-3px, 0) skewX(2deg); clip-path: none; opacity: 0.3; }
  26%  { opacity: 1; }
  40%  { transform: translate(6px, 1px) skewX(-5deg);
         clip-path: inset(30% 0 40% 0); }
  55%  { transform: translate(-5px, -2px) skewX(4deg);
         filter: drop-shadow(3px 0 var(--red)) drop-shadow(-3px 0 var(--cyan));
         clip-path: inset(70% 0 5% 0); opacity: 0.8; }
  58%  { opacity: 0.15; }
  60%  { opacity: 1; clip-path: none; }
  78%  { transform: translate(4px, -1px) skewX(-3deg);
         clip-path: inset(5% 0 75% 0); }
  100% { transform: translate(0, 0) skewX(0);
         filter: drop-shadow(2px 0 var(--red)) drop-shadow(-2px 0 var(--cyan));
         clip-path: none; opacity: 1; }
}

.glitch-severe {
  animation: glitch-tear 0.42s steps(2, end) infinite;
  will-change: transform, filter, opacity, clip-path;
}

/* Full-screen tearing bands, shown only during the severe tier. Sits just below
   the scanlines so the CRT grain still rides over the corruption. */
#glitch-overlay {
  position: fixed;
  inset: 0;
  z-index: 90;
  pointer-events: none;
  mix-blend-mode: screen;
  background:
    repeating-linear-gradient(
      to bottom,
      rgba(255, 51, 51, 0.05) 0px,
      rgba(255, 51, 51, 0.05) 2px,
      transparent 2px,
      transparent 9px),
    repeating-linear-gradient(
      to bottom,
      rgba(0, 229, 255, 0.04) 0px,
      rgba(0, 229, 255, 0.04) 1px,
      transparent 1px,
      transparent 6px);
  animation: glitch-bands 0.3s steps(3, end) infinite;
}

@keyframes glitch-bands {
  0%   { background-position: 0 0, 0 0;        opacity: 0.9; }
  30%  { background-position: 0 -40px, 0 24px; opacity: 0.5; }
  31%  { opacity: 1; }
  60%  { background-position: 0 60px, 0 -30px; opacity: 0.7; }
  100% { background-position: 0 -20px, 0 50px; opacity: 0.9; }
}

/* ── THE CONTAINMENT BREACH (cracks → shatter) ──────────────── */
/* A progression-driven corruption, separate from the timer glitch: the display
   fractures as clean SETS accrue, starting at 1-2 screen corners (state.crackCorners)
   and growing toward centre. Sits above #glitch-overlay (z-90) but below the scanlines
   (z-100) so the CRT grain still rides over the cracks. --crack-stage (0..6, set by
   renderCracks) drives growth/width.
   Each crack line is two stacked paths: .crack-glow (bright, blurred, screen-blended —
   the light leaking from the edges) and .crack-void (a near-black core, normally
   blended) painted on top of it. isolation:isolate scopes those blend modes to this
   overlay so the void path can actually read as dark instead of being washed out by a
   screen blend on the whole layer. Stages 1-3 grow the hairline (void stays at 0 width,
   so only the thin glow line shows); stages 4-6 add no new geometry but widen the void
   into the glow, so the same lines read as glass falling away into darkness with a
   brightening glowing rim. */
#crack-overlay {
  position: fixed;
  inset: 0;
  z-index: 92;
  pointer-events: none;
  isolation: isolate;
  --crack-stage: 0;
}

#crack-overlay svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

/* The vast dark behind the glass — kept almost invisible until the very end, then the
   burst floods it to white. The breathing blur only animates while .crack-breathing is
   set (late stages / the shatter) so it isn't burning frames for most of a run. */
.crack-light {
  transform-box: fill-box;
  transform-origin: center;
  mix-blend-mode: screen;
  opacity: calc(max(var(--crack-stage) - 4, 0) * 0.04);
  transition: opacity 0.8s ease;
}

#crack-overlay.crack-breathing .crack-light {
  animation: crack-breathe 3.6s ease-in-out infinite;
}

@keyframes crack-breathe {
  0%, 100% { filter: blur(2px); }
  50%      { filter: blur(5px); }
}

/* Crack strokes: hidden groups are transparent; .lit groups (set in renderCracks)
   draw. The non-scaling stroke keeps crisp px line-widths despite the stretched
   viewBox. */
.crack-grp     { opacity: 0; transition: opacity 0.6s ease; }
.crack-grp.lit { opacity: 1; }

.crack-grp path {
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* The glow: a thin bright hairline at low stages, growing wider and more luminous once
   the widening stages (4-6) kick in — the edges "brighten and glow" as the glass opens. */
.crack-grp path.crack-glow {
  mix-blend-mode: screen;
  stroke: rgba(224, 252, 255, 0.9);
  stroke-width: calc(0.9px + max(var(--crack-stage) - 3, 0) * 2.1px);
  filter: drop-shadow(0 0 calc(1px + max(var(--crack-stage) - 3, 0) * 1.4px) var(--cyan));
}

/* The void: invisible at stages 1-3 (hairline-only growth), then widens on top of the
   glow stroke — always narrower than it, so a glowing rim of the glow stroke remains
   visible at its edges. Painted with a normal blend so it actually darkens the screen. */
.crack-grp path.crack-void {
  stroke: #03080a;
  opacity: 0.94;
  stroke-width: calc(max(var(--crack-stage) - 3, 0) * 1.3px);
}

/* The shatter: every crack (glow and void alike) snaps to white and the light floods
   the whole screen (the white-out). Added in triggerAscension; cleared by clearCracks. */
#crack-overlay.crack-burst .crack-grp { opacity: 1; }

#crack-overlay.crack-burst .crack-grp path {
  stroke: #ffffff;
  opacity: 1;
  animation: crack-burst-paths 1.4s ease-in forwards;
}

#crack-overlay.crack-burst .crack-light {
  animation: crack-burst-light 1.4s ease-in forwards;
}

@keyframes crack-burst-paths {
  0%   { stroke-width: 2.6px;
         filter: drop-shadow(0 0 6px #fff) drop-shadow(0 0 16px var(--cyan)); }
  70%  { stroke-width: 7px; }
  100% { stroke-width: 0;   }   /* consumed by the light */
}

@keyframes crack-burst-light {
  0%   { opacity: 0.4; filter: blur(4px); transform: scale(1); }
  60%  { opacity: 1;   filter: blur(8px); transform: scale(1.5); }
  100% { opacity: 1;   filter: blur(0);   transform: scale(3.2); }
}

/* ── DEATH THROES (.dying — added the instant the timer hits zero) ── */
/* Motion is reduced here: the violent tearing stops and instead every panel
   EXCEPT the machine-status log flickers and glitches to darkness, leaving the
   red end-game messages alone on a blackening screen. */
.dying #header,
.dying #sequence-panel,
.dying #timer-panel,
.dying #bindings-panel,
.dying #break-room,
.dying #input-panel {
  animation: flicker-to-dark 1.6s steps(2, end) forwards;
}

@keyframes flicker-to-dark {
  0%   { opacity: 1;    transform: translateX(0); }
  10%  { opacity: 0.2; }
  14%  { opacity: 0.9;  transform: translateX(-2px); }
  22%  { opacity: 0.1; }
  28%  { opacity: 0.7;  transform: translateX(2px); }
  36%  { opacity: 0.15; transform: translateX(-1px); }
  44%  { opacity: 0.5;  transform: translateX(0); }
  55%  { opacity: 0.08; }
  62%  { opacity: 0.3; }
  78%  { opacity: 0.04; }
  100% { opacity: 0; }
}

/* The machine-status window is the last thing alive; it tightens to a red glow
   as the final messages crescendo. */
.dying #log-panel {
  animation: log-menace 4.5s ease forwards;
}

@keyframes log-menace {
  0%   { border-color: var(--border); box-shadow: none; }
  100% { border-color: var(--red);
         box-shadow: 0 0 18px rgba(255, 51, 51, 0.5), inset 0 0 24px rgba(255, 51, 51, 0.15); }
}

/* Per-line glitch jitter for the end-game messages (see .log-dying). */
@keyframes glitch-text {
  0%   { transform: translate(0, 0);  clip-path: none; }
  25%  { transform: translate(-2px, 0); clip-path: inset(0 0 55% 0); }
  50%  { transform: translate(2px, 0);  clip-path: inset(42% 0 0 0); }
  75%  { transform: translate(-1px, 0); clip-path: inset(0 0 22% 0); }
  100% { transform: translate(0, 0);  clip-path: none; }
}

/* ── HELD GAME OVER SPLASH ───────────────────────────────────── */
/* Dims the dead screen and holds a huge red GAME OVER until the player
   clicks or presses a key, after which the recap overlay is shown. */
#gameover-splash {
  position: fixed;
  inset: 0;
  z-index: 110;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 24px;
  background: #000;
  animation: gameover-dim 1.1s ease both;
  cursor: pointer;
}

@keyframes gameover-dim {
  from { background: rgba(0, 0, 0, 0); }
  to   { background: #000; }
}

.gameover-text {
  font-family: var(--font);
  font-size: clamp(48px, 16vw, 160px);
  font-weight: bold;
  letter-spacing: 0.08em;
  color: var(--red);
  text-shadow: 0 0 18px var(--red), 0 0 48px rgba(255, 51, 51, 0.7);
  line-height: 1;
  animation: intro-fade-in 0.6s ease both;
}

/* The "M" in GA[M]E falters at irregular intervals. */
.gameover-text .go-m {
  display: inline-block;
  animation: m-flicker 3.7s steps(1, end) infinite;
}

@keyframes m-flicker {
  0%, 100% { opacity: 1;   text-shadow: 0 0 18px var(--red), 0 0 48px rgba(255, 51, 51, 0.7); }
  43%      { opacity: 1; }
  44%      { opacity: 0.12; text-shadow: none; }
  46%      { opacity: 1; }
  47%      { opacity: 0.3;  text-shadow: 0 0 6px var(--red); }
  48%      { opacity: 1; }
  72%      { opacity: 1; }
  73%      { opacity: 0.15; text-shadow: none; }
  75%      { opacity: 1; }
}

.gameover-hint {
  font-family: var(--font);
  font-size: 13px;
  letter-spacing: 0.18em;
  color: var(--text-dim);
  text-transform: uppercase;
  animation: blink 1.4s step-end infinite;
}

/* ── HELD ASCENSION SPLASH (the shatter's aftermath) ─────────── */
/* Counterpart to the GAME OVER splash: after the display bursts, the screen settles
   to a white, broken backdrop (the same crack lines, now still and dark against the
   light) holding the cosmic sign-off, until the player clicks/keys to reignite the
   cycle (rebirthRun). The sign-off itself is written in black crayon, one phrase at a
   time, like a child writing slowly (see playCrayonLines in game.js). */
#ascension-splash {
  position: fixed;
  inset: 0;
  z-index: 115;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 28px;
  cursor: pointer;
  background: #f5f3ec;
  animation: ascension-settle 2.2s ease both;
}

@keyframes ascension-settle {
  0%   { background: #ffffff; }
  100% { background: #f5f3ec; }
}

/* The same fracture geometry as #crack-overlay (built via buildCrackSvg), redrawn
   still and dark instead of glowing — the glass that just broke, now lying behind
   the writing. */
.ascension-cracked-glass {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.ascension-cracked-glass svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

.ascension-cracked-glass .crack-light { display: none; }

.ascension-cracked-glass .crack-grp { opacity: 1; }

.ascension-cracked-glass .crack-grp path.crack-glow {
  stroke: rgba(20, 20, 20, 0.10);
  stroke-width: 3px;
  filter: none;
}

.ascension-cracked-glass .crack-grp path.crack-void {
  stroke: rgba(15, 15, 15, 0.5);
  stroke-width: 1.6px;
  opacity: 1;
}

.ascension-text {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  text-align: center;
  min-height: clamp(40px, 10vw, 90px);
}

/* One phrase, written letter by letter, then faded for the next (see playCrayonLines). */
.crayon-line {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  transition: opacity 0.9s ease;
}

.crayon-line.crayon-fade-out {
  opacity: 0;
}

.crayon-letter {
  display: inline-block;
  white-space: pre;
  font-family: 'Segoe Print', 'Bradley Hand', 'Comic Sans MS', cursive;
  font-size: clamp(26px, 6.5vw, 58px);
  color: #1a1a1a;
  transform: rotate(var(--wobble, 0deg));
  text-shadow:
    0.6px 0.4px 0 rgba(0, 0, 0, 0.3),
    -0.4px 0.2px 0 rgba(0, 0, 0, 0.18);
  animation: crayon-stroke 0.3s ease-out both;
}

@keyframes crayon-stroke {
  0%   { opacity: 0; transform: rotate(var(--wobble, 0deg)) scale(0.5) translateY(8px); }
  60%  { opacity: 1; transform: rotate(var(--wobble, 0deg)) scale(1.1) translateY(-2px); }
  100% { opacity: 1; transform: rotate(var(--wobble, 0deg)) scale(1) translateY(0); }
}

/* Appended 2s after the last sign-off line fades away (see playCrayonLines' onDone in
   game.js) — fades up over 1s, then settles into its blink. */
.ascension-hint {
  font-family: var(--font);
  font-size: 13px;
  letter-spacing: 0.18em;
  color: rgba(0, 0, 0, 0.4);
  text-transform: uppercase;
  opacity: 0;
  animation: ascension-hint-in 1s ease forwards, blink 1.4s step-end infinite 1s;
}

@keyframes ascension-hint-in {
  to { opacity: 1; }
}

/* ── HELD RETURNING EMPLOYEE SPLASH ─────────────────────────── */
/* Shown once on resume from a saved break-room checkpoint (a fresh page load
   or a mid-run refresh) so the player understands why they've landed back in
   the break room instead of wherever the timer caught them. Calmer/cyan,
   distinct from the red GAME OVER splash and the white→void ASCENSION one. */
#returning-splash {
  position: fixed;
  inset: 0;
  z-index: 122;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.88);
  cursor: pointer;
}

.returning-modal {
  border: 2px solid var(--cyan);
  background: var(--bg-panel);
  box-shadow: 0 0 24px rgba(0, 229, 255, 0.4), inset 0 0 30px rgba(0, 229, 255, 0.05);
  padding: 28px 32px;
  max-width: 440px;
  width: 90%;
  text-align: center;
}

.returning-title {
  font-size: 18px;
  font-weight: bold;
  color: var(--cyan);
  letter-spacing: 0.12em;
  text-shadow: 0 0 10px rgba(0, 229, 255, 0.6);
  margin-bottom: 12px;
}

.returning-body {
  font-size: 12px;
  color: var(--green-dim);
  line-height: 1.6;
  margin-bottom: 20px;
}

.returning-hint {
  font-family: var(--font);
  font-size: 11px;
  letter-spacing: 0.16em;
  color: var(--text-dim);
  text-transform: uppercase;
  animation: blink 1.4s step-end infinite;
}

/* Utility */
.hidden {
  display: none !important;
}

/* ── Spotlight tutorial (dim screen + highlight box + tooltip) ── */
#tutorial-overlay {
  position: fixed;
  inset: 0;
  z-index: 120;
  pointer-events: none;   /* only the tooltip catches input */
}

/* A clear box over the target; the huge spread shadow dims everything else. */
.tut-highlight {
  position: fixed;
  border: 2px solid var(--cyan);
  border-radius: 4px;
  box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.82), 0 0 18px var(--green-glow);
  pointer-events: none;
}

/* The transition is added only after the first placement, so the box appears
   exactly on its target rather than sliding in from the corner; once positioned
   it animates smoothly as it tracks layout reflow. */
.tut-highlight.positioned {
  transition: top 0.2s ease, left 0.2s ease, width 0.2s ease, height 0.2s ease;
}

.tut-tooltip {
  position: fixed;
  max-width: 320px;
  border: 1px solid var(--cyan);
  background: var(--bg-panel);
  box-shadow: 0 0 18px rgba(0, 229, 255, 0.35);
  padding: 12px 14px;
  pointer-events: auto;
  z-index: 121;
}

.tut-tooltip-title {
  font-size: 13px;
  font-weight: bold;
  color: var(--cyan);
  letter-spacing: 0.08em;
  text-shadow: 0 0 6px rgba(0, 229, 255, 0.4);
  margin-bottom: 6px;
}

/* ══════════════════════════════════════════════════════════════════
   REPETITIVE STRAIN — header meter, debt, and the numeric purge
   ══════════════════════════════════════════════════════════════════ */

/* Header strain readout: calm green by default, amber as it mounts, red once a purge is due. */
#strain-num.strain-warn {
  color: var(--amber);
  text-shadow: 0 0 8px rgba(255, 183, 0, 0.5);
}
#strain-num.strain-high {
  color: var(--red);
  text-shadow: 0 0 10px rgba(255, 51, 51, 0.7);
  animation: flicker 0.5s ease-in-out infinite alternate;
}

/* Credits in the red, literally, once the balance goes negative. */
#credits-num.credits-debt {
  color: var(--red);
  text-shadow: 0 0 10px rgba(255, 51, 51, 0.7);
}

/* During a purge the sequence progress bar becomes the depleting compliance timer:
   crisp linear depletion, amber → red as it empties. */
#progress-fill.purge-bar {
  background: var(--amber);
  box-shadow: 0 0 8px rgba(255, 183, 0, 0.6);
  transition: width 0.1s linear;
}
#progress-fill.purge-bar.purge-bar-low {
  background: var(--red);
  box-shadow: 0 0 10px rgba(255, 51, 51, 0.8);
}

/* Purge digit string reuses the .seq-cmd look (active/done/pending), a touch more spaced. */
.purge-digit { letter-spacing: 0.06em; }

/* The live numpad keys during a purge read a little hotter than a normal clickable cell. */
.key-cell.purge-key .key-cmd {
  color: var(--amber);
  font-size: 15px;
  font-weight: bold;
}

.tut-tooltip-body {
  font-size: 12px;
  color: var(--green-dim);
  line-height: 1.5;
  margin-bottom: 10px;
}

.tut-tooltip-btn {
  display: inline-block;
  padding: 5px 18px;
  border: 1px solid var(--cyan);
  background: transparent;
  color: var(--cyan);
  font-family: var(--font);
  font-size: 11px;
  letter-spacing: 0.12em;
  cursor: pointer;
  text-transform: uppercase;
  text-shadow: 0 0 6px rgba(0, 229, 255, 0.35);
  transition: background 0.2s, box-shadow 0.2s;
}

.tut-tooltip-btn:hover {
  background: rgba(0, 229, 255, 0.1);
  box-shadow: 0 0 12px rgba(0, 229, 255, 0.4);
}

/* ── Responsive adjustment ─────────────────────────────────── */
@media (max-height: 650px) {
  #timer-number { font-size: 32px; }
}

/* ── Narrow / portrait screens — orthographic "grouped blocks" keyboard ──────
   On thin windows and phones held vertically the staggered QWERTY rows give way
   to column-aligned grids of larger keys that fill the width. The number row and
   the letter block share an auto-fill column count (same container width + same
   track sizing ⇒ identical columns) so they line up orthographically; the numpad
   keeps its recognizable 4-wide calculator cluster. Locked keys still render, so
   the grid shape stays stable as the pool unlocks. */
@media (max-width: 600px) {
  /* Header: let title + stats + reset button wrap instead of overflowing. */
  #header {
    flex-wrap: wrap;
    gap: 4px 8px;
    padding: 5px 8px;
  }
  /* Below this width even the tightened nowrap header text no longer fits on
     one line; let it wrap internally rather than clipping at the edge. */
  #header-stats { font-size: 10px; white-space: normal; }
  #header-right { gap: 6px; }

  /* Timer column shrinks so the timer+log pair stays comfortable down to ~320px. */
  #middle-row { grid-template-columns: minmax(92px, 118px) 1fr; }

  /* The "scroll, don't squeeze" guard (#content-scroll > * { flex-shrink: 0 })
     now lives in the base layout so it holds at every width. On phones, give the
     machine log a slightly tighter cap so it can't crowd the larger touch keys. */
  #machine-log { max-height: 30vh; }

  /* Letter keys: one continuous orthographic grid (rows dissolve into it). */
  .keys-main {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
    gap: 5px;
  }
  .keys-main .key-row { display: contents; }

  /* Number row aligns to the same columns as the letter block above the keys. */
  .key-row.reserved-row {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
    gap: 5px;
    margin-bottom: 5px;
  }

  /* Numpad keeps its 4-wide calculator shape. */
  .key-row.numpad-row {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 5px;
  }

  /* Bigger keys / touch targets; cells stretch to fill their grid track. */
  .key-cell,
  .key-row.reserved-row .key-cell,
  .key-row.numpad-row .key-cell {
    min-width: 0;
    min-height: 52px;
    padding: 6px 4px;
  }
  .key-label { font-size: 17px; }
  .key-cmd {
    font-size: 9px;
    max-width: 100%;
  }
  .key-cell.reserved .key-cmd   { font-size: 8px; }
  .key-cell.reserved .key-count { font-size: 9px; }
}

/* ══════════════════════════════════════════════════════════════════
   CALM MODE (KACS-SAFE-001) — flash/flicker/shake remediation.
   html.calm-mode is set by game.js from two independent triggers: the OS
   prefers-reduced-motion query, and the in-game toggle (start screen + break
   room), persisted as kacs_calm_mode. One code path, two triggers — every
   rule below reads only the class, never the media query directly.
   ══════════════════════════════════════════════════════════════════ */

/* Timer/strain flicker → static color states. The color escalation
   (green→amber→red) already carries the information; the flicker is redundant. */
html.calm-mode #timer-number.timer-warning,
html.calm-mode #timer-number.timer-danger,
html.calm-mode #timer-number.timer-frozen,
html.calm-mode #strain-num.strain-high { animation: none; }

/* Shake → none. Error feedback survives via echo-flash-error color. */
html.calm-mode .shake { animation: none; }

/* Death throes → simple fade instead of stepped strobe. */
html.calm-mode .dying #header,
html.calm-mode .dying #sequence-panel,
html.calm-mode .dying #timer-panel,
html.calm-mode .dying #bindings-panel,
html.calm-mode .dying #break-room,
html.calm-mode .dying #input-panel {
  animation: calm-fade-out 1.6s ease forwards;
}
@keyframes calm-fade-out { to { opacity: 0; } }

/* Full-background red pulse and the GAME OVER "M" falter → static instead. */
html.calm-mode .consequences { animation: none; background: #1a0000; }
html.calm-mode .gameover-text .go-m { animation: none; }

/* Reduced motion: drop the breach's heavy animations. The cracks still appear and
   the shatter still resolves to the splash + black void — just without the breathing
   bloom, the flood, or the white-out crossfade (matching the intro's courtesy). */
html.calm-mode .crack-light,
html.calm-mode #crack-overlay.crack-burst .crack-light,
html.calm-mode #crack-overlay.crack-burst .crack-grp path { animation: none; }
html.calm-mode .crack-grp, html.calm-mode .crack-light { transition: none; }
html.calm-mode #ascension-splash { animation: none; background: #f5f3ec; }
html.calm-mode .crayon-letter { animation: none; opacity: 1; transform: none; }
html.calm-mode .crayon-line { transition: none; }

/* ── OPTIONS MENU (header) ─────────────────────────────────── */
/* A single gear button reveals a popout holding the two controls that used to
   sit exposed in the header/break-room: the CALM MODE toggle and the
   destructive RESET SAVE DATA button. Two clicks reach either one: one to
   open the popout, one to act on the control inside it. */
#header-right {
  display: flex;
  align-items: center;
  gap: 10px;
}

.options-menu {
  position: relative;
  flex: none;
}

.options-btn {
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: transparent;
  color: var(--text-dim);
  font-family: var(--font);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, box-shadow 0.15s, transform 0.3s ease;
}

.options-btn:hover,
.options-btn:focus,
.options-btn[aria-expanded="true"] {
  color: var(--green);
  border-color: var(--green);
  box-shadow: 0 0 8px var(--green-glow);
  transform: rotate(45deg);
  outline: none;
}

.options-popout {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 40;
  width: 300px;
  max-width: calc(100vw - 32px);
  padding: 12px;
  border: 1px solid var(--border);
  background: var(--bg-panel);
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.6), 0 0 20px var(--green-glow);
  text-align: left;
}

.options-popout .calm-toggle {
  margin: 0 0 12px;
}

/* The save system's one visible surface, once the popout is open: a plain
   labeled button that turns red on hover to signal it is destructive. */
.reset-save-btn {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-dim);
  font-family: var(--font);
  font-size: 11px;
  letter-spacing: 0.08em;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, box-shadow 0.15s;
}

.reset-save-btn:hover,
.reset-save-btn:focus {
  color: var(--red);
  border-color: var(--red);
  box-shadow: 0 0 8px rgba(255, 51, 51, 0.45);
  outline: none;
}

/* ── RESET CONFIRMATION MODAL ──────────────────────────────── */
#reset-confirm {
  position: fixed;
  inset: 0;
  z-index: 130;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.85);
}

.reset-modal {
  border: 2px solid var(--red);
  background: var(--bg-panel);
  box-shadow: 0 0 24px rgba(255, 51, 51, 0.4), inset 0 0 30px rgba(255, 51, 51, 0.05);
  padding: 24px 28px;
  max-width: 420px;
  width: 90%;
  text-align: center;
}

.reset-modal-title {
  font-size: 18px;
  font-weight: bold;
  color: var(--red);
  letter-spacing: 0.12em;
  text-shadow: 0 0 10px rgba(255, 51, 51, 0.6);
  margin-bottom: 12px;
}

.reset-modal-body {
  font-size: 12px;
  color: var(--green-dim);
  line-height: 1.6;
  margin-bottom: 20px;
}

.reset-modal-actions {
  display: flex;
  gap: 14px;
  justify-content: center;
}

.reset-modal-btn {
  padding: 8px 22px;
  border: 2px solid var(--green-dim);
  background: transparent;
  color: var(--green-dim);
  font-family: var(--font);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 0.2s, box-shadow 0.2s, color 0.2s;
}

.reset-modal-btn:hover,
.reset-modal-btn:focus {
  background: rgba(0, 255, 65, 0.08);
  box-shadow: 0 0 12px var(--green-glow);
  outline: none;
}

.reset-modal-btn.danger {
  border-color: var(--red);
  color: var(--red);
  text-shadow: 0 0 8px rgba(255, 51, 51, 0.5);
}

.reset-modal-btn.danger:hover,
.reset-modal-btn.danger:focus {
  background: rgba(255, 51, 51, 0.12);
  box-shadow: 0 0 14px rgba(255, 51, 51, 0.5);
}
