/* LEGALIZER -- Federal Legal Compliance Terminal
   Physical-terminal redesign: the machine is ONE dominant CRT-monitor
   silhouette (thick bezel, near-4:3 curved glass, small lower chassis),
   not a wide three-panel cabinet. The physical shell (housing, bezel,
   glass base, reflection, screws, vents, chassis) is a single
   illustrated inline SVG (`.crt-shell`) -- geometry and shading read far
   more convincingly painted that way than approximated purely with
   box-shadow layers on rectangles. The real, interactive screen
   (scanlines, live text, the actual <textarea>) is a normal HTML layer
   (`.crt-screen`) positioned on top of the SVG's glass cutout, aligned
   by percentage so it tracks the SVG at every viewport size. Only that
   HTML layer uses the green-phosphor terminal aesthetic; the SVG shell
   stays dark olive/charcoal plastic, and the page around it stays a
   restrained near-black. No external fonts, no CDN, no framework --
   system font stacks only. */

:root {
  /* Page / room */
  --room-bg: #17140f;
  --room-bg-2: #0f0d0a;

  /* Dark olive/charcoal casing (SVG shell colors are hardcoded to match
     these, since inline SVG doesn't read CSS custom properties reliably
     across all contexts -- kept here too so HTML-side hardware bits
     that still exist, i.e. the control strip, share the same palette). */
  --case-1: #454937;
  --case-2: #2f3226;
  --case-3: #181a12;
  --case-ink: #0a0a08;
  --case-text: #d9d4bd;
  --case-text-dim: #8f8a6d;

  /* CRT glass */
  --crt-bg: #051108;
  --crt-bg-2: #0a2013;
  --crt-green: #8fe6ac;
  --crt-green-bright: #e3ffec;
  --crt-green-dim: #4f8f68;
  --crt-amber: #ffcf7d;
  --crt-red: #ff9a86;
  --crt-glow: rgba(143, 230, 172, 0.55);

  /* LEDs */
  --led-off: #4a4531;
  --led-power: #ffcf7d;
  --led-ready: #7be39a;
  --led-processing: #ffcf7d;
  --led-warning: #ff6b57;

  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, "Consolas", "SFMono-Regular", "Cascadia Mono",
    "Liberation Mono", "Courier New", monospace;
  --radius: 4px;
}

* {
  box-sizing: border-box;
}

html, body {
  height: 100%;
}

body {
  margin: 0;
  color: var(--case-text);
  font-family: var(--font-sans);
  font-size: 17px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* Room + CRT spill light: the machine is one of the scene's own light
   sources, so a faint green glow sits behind it in the page's own
   background. Beneath that, a dark-room dimmer wash over the supplied
   room photo replaces the old flat gradient -- the terminal reads as
   standing inside an actual photographed basement/utility room instead
   of a plain vignette.

   This lives on a dedicated ::before layer, sized and pinned to the
   viewport with `position: fixed`, rather than on body's own
   `background-attachment: fixed`. The two look identical (photo stays
   put while the page scrolls) but `background-attachment: fixed` is not
   its own compositor layer in most browsers -- it repaints the whole
   background on every scroll frame, which is what made scrolling feel
   heavy and made the photo's edge visibly lag/shift next to the
   terminal. A `position: fixed` element gets its own GPU layer and
   costs nothing on scroll. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(ellipse 900px 520px at 50% 46%, rgba(90, 190, 130, 0.07) 0%, transparent 70%),
    linear-gradient(rgba(10, 10, 8, 0.55), rgba(8, 8, 6, 0.72)),
    url("/room-bg.jpg") no-repeat 88% 4% / 118% auto,
    radial-gradient(ellipse at 50% 18%, var(--room-bg) 0%, var(--room-bg-2) 75%);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
  padding: 0;
  margin: -1px;
}

/* ---------------- Page shell ---------------- */

/* 15% larger than the original 760px -- legibility wins over how small
   the "wall-mounted unit" reads. Padding-top still clears room for the
   room photo's own ceiling light fixture to show above it (see body's
   background-position below). */
.page {
  max-width: 874px;
  margin: 0 auto;
  /* Top padding reduced (was 6rem) at the operator's request to raise
     the terminal higher on the page -- still leaves some of the room
     photo's ceiling light fixture visible above it, just less than
     before. */
  padding: 3rem 1rem 6rem;
}

/* ---------------- Intro block (SEO commit; collapsible UX commit) ----------------
   Static "What is LEGALIZER?" copy, a sibling of .page-header sitting
   directly on the room photo, outside the CRT monitor illustration. Now
   a native <details>/<summary> disclosure (closed by default -- see
   index.html, no `open` attribute) instead of an always-open block, so
   the collapsed state stays a small terminal-style control rather than
   a full card competing with the machine below it. */
.intro-block {
  margin: 0 0 1.75rem;
  font-family: var(--font-mono);
}

/* Collapsed control: a compact green terminal label, same family as
   .crt-heading (bright, letter-spaced, glowing) -- not a button, but
   styled to clearly read as one. The native disclosure triangle is
   suppressed in favor of a "[+]"/"[-]" marker via ::before so the glyph
   matches the rest of the site's bracketed terminal-label vocabulary
   (see the CTRL+ENTER hint, the CASH bill-acceptor label, etc.). */
.intro-summary {
  display: flex;
  align-items: baseline;
  gap: 0.4em;
  cursor: pointer;
  list-style: none;
  user-select: none;
  padding: 0.5rem 0.75rem;
  background: rgba(6, 10, 7, 0.72);
  border: 1px solid rgba(79, 143, 104, 0.35);
  border-radius: var(--radius);
}

.intro-summary::-webkit-details-marker {
  display: none;
}

.intro-summary::before {
  content: "[+]";
  color: var(--crt-green-dim);
  text-shadow: 0 0 6px var(--crt-glow);
}

.intro-details[open] > .intro-summary::before {
  content: "[-]";
}

.intro-summary:hover::before,
.intro-summary:focus-visible::before {
  color: var(--crt-green-bright);
}

.intro-heading {
  margin: 0;
  font-size: 0.8rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--crt-green-bright);
  text-shadow: 0 0 6px var(--crt-glow);
}

/* Expanded panel: the same pale "printed report" card app.js already
   uses for the statute/argument/turn-log text (.argument-body,
   .turn-log-terminal, .reaction-terminal -- see those rules), reused
   verbatim (background/text color/border/radius) rather than a new
   "modern card" look, plus a dedicated scanline overlay reusing
   .crt-scanlines' own gradient values, since this panel lives outside
   .crt-screen and isn't already under that ambient overlay. */
.intro-panel {
  position: relative;
  overflow: hidden;
  max-width: 100%;
  margin-top: 0.6rem;
  padding: 0.7rem 0.85rem;
  background: rgba(226, 240, 226, 0.95);
  border: 1px solid rgba(0, 0, 0, 0.18);
  border-radius: var(--radius);
}

.intro-panel::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.18) 0px,
    rgba(0, 0, 0, 0.18) 1px,
    transparent 1px,
    transparent 3px
  );
  mix-blend-mode: multiply;
}

.intro-text {
  margin: 0 0 0.6rem;
  font-size: 0.85rem;
  line-height: 1.55;
  color: #16241a;
  text-shadow: none;
  overflow-wrap: break-word;
}

.intro-disclaimer {
  margin: 0;
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  color: #8a5a12;
}

/* ---------------- Terminal unit ---------------- */

.terminal-unit {
  position: relative;
}

/* .crt-monitor holds ONE illustrated SVG shell -- a single boxy CRT
   monitor unit, moderate corner rounding, no hinge, no separate
   keyboard deck -- plus several real, interactive HTML layers stacked
   on top of it, each aligned to a specific spot on the illustration by
   percentage so everything tracks the SVG at every viewport width.
   aspect-ratio matches the SVG's own viewBox (640x640, square) exactly. */
/* A stronger, two-layer shadow than the original single drop-shadow --
   a tight contact shadow plus a much wider, softer halo -- so the
   machine visibly separates from the room photo behind it instead of
   blending into its texture, especially where the wall itself is
   already fairly dark. */
.crt-monitor {
  position: relative;
  width: 100%;
  aspect-ratio: 640 / 640;
  filter:
    drop-shadow(0 18px 30px rgba(0, 0, 0, 0.7))
    drop-shadow(0 40px 90px rgba(0, 0, 0, 0.65));
  /* filter (unlike box-shadow) isn't promoted to its own compositor
     layer automatically -- without this hint, some browsers repaint
     this whole 640x640 filtered region on every scroll frame instead
     of just repositioning a cached layer. */
  will-change: filter;
}

.crt-shell {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

/* The real application surface -- aligned to the SVG shell's glass
   cutout (x=95 y=80 w=450 h=330 inside the 640x640 viewBox):
   left 14.84%, top 12.5%, width 70.31%, height 51.56%. border-radius is
   deliberately a little gentler than the SVG glass's own rx/ry (6.2%/
   7.3%) -- a lesson from an earlier pass, where matching an aggressive
   curve exactly clipped the header text against the corners. A 1px dark
   border stands in for the "inner rubber/plastic gasket" between the
   illustrated bezel and the real glass. */
.crt-screen {
  position: absolute;
  left: 14.84%;
  top: 12.5%;
  width: 70.31%;
  height: 51.56%;
  border: 3px solid #020302;
  border-radius: 7% / 8%;
  overflow: hidden;
  background: radial-gradient(ellipse at 48% 32%, var(--crt-bg-2) 0%, var(--crt-bg) 80%);
  box-shadow:
    inset 0 0 40px rgba(0, 0, 0, 0.9),
    inset 0 0 20px var(--crt-glow),
    inset 0 0 70px rgba(0, 0, 0, 0.4);
  color: var(--crt-green);
  font-family: var(--font-mono);
  line-height: 1.55;
  /* Three layers even on ordinary body text: a tight bright core, the
     established mid-range bloom, and now a faint wide halo -- "soft
     green glow around bright characters... slight variation in phosphor
     intensity," kept subtle enough to stay readable rather than reading
     as neon. */
  /* Phase 10F: the base ambient glow (inherited by every plain body
     paragraph -- .argument-body, .field-value, .case-statement, etc. --
     that doesn't declare its own stronger text-shadow) was tuned for a
     short line of bright text, not multi-line result paragraphs (FEDERAL
     FINDING/STATUTORY BASIS/APPLICATION/ADDITIONAL CONCERNS/DETERMINATION):
     the wider two blur layers compounded across wrapped lines and read as
     smeared rather than glowing. Narrower radius + lower alpha on those
     two layers only; the 1px core stays (it's what keeps letterforms
     crisp, not what blurs them). Elements with their own explicit,
     stronger text-shadow (.crt-heading, .crt-line--bright,
     .verdict-status, .data-leak-siren, etc.) are untouched. */
  text-shadow: 0 0 1px rgba(227, 255, 236, 0.85), 0 0 3px rgba(143, 230, 172, 0.38), 0 0 8px rgba(143, 230, 172, 0.12);
  display: flex;
  flex-direction: column;
}

/* The LED cluster (POWER/READY/BUSY/ALERT -- Submit/Clear used to live
   here too but are now positioned separately, see
   .button-row--power-slot), mounted directly on the housing surface
   below the screen (SVG position x=60 y=500 w=340 h=44 inside the
   640x640 viewBox). No background/border here -- this sits ON the
   illustrated housing, which already supplies the surrounding
   material. */
.control-strip {
  position: absolute;
  left: 9.375%;
  top: 78.125%;
  width: 53.125%;
  height: 6.875%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  flex-wrap: nowrap;
}

/* "Illuminated lamp button" look, now used by SPEAKER and BACKLIGHT,
   stacked one above the other (POWER moved into the plain .led-row
   instead -- see the comment above .control-strip): a recessed dark
   bezel containing a glowing led-dot lens. Both are real <button>s,
   state driven by aria-pressed; their icons (swapped for an on/off
   glyph, not the dot+label used by led-row items) are defined further
   down. Height shrunk (5.94% -> 3.6%) from the original single-button
   size so two of these stack, with a small gap, in the space between
   the screen and .control-strip that used to hold only one. */
.lamp-btn {
  position: absolute;
  width: 11.25%;
  height: 3.6%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: 0;
  border: 2px solid #0c0d09;
  border-radius: 6px;
  background: linear-gradient(180deg, #2a281c 0%, #16150f 100%);
  box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.6);
  transition: box-shadow 0.4s ease;
  cursor: default;
}

button.lamp-btn {
  cursor: pointer;
  font: inherit;
}

.lamp-btn:focus-visible {
  outline: 3px solid var(--led-warning);
}

.lamp-btn .led-dot {
  width: 68%;
  height: 60%;
  border-radius: 4px;
  box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.6);
}

.lamp-btn .led-dot::after {
  display: none;
}

.lamp-btn .led-label {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translateX(-50%) translateY(-50%);
  white-space: nowrap;
  font-size: 0.56rem;
  font-weight: 700;
  color: var(--case-text-dim);
  pointer-events: none;
}

/* POWER used to live here as its own .power-lamp (a big decorative
   lamp-button, aria-hidden, no click handler). It's now a plain
   .led--power item inside .led-row instead (same generic .led styling as
   READY/BUSY/ALERT, same --led-power color via the .led--power[data-state]
   rule near the bottom of this file) -- this slot belongs to
   .button-row--power-slot now, see below. */

/* .speaker-lamp: beside the vent slot on the housing's other side
   (SVG-mirrored position, left inset from the edge). Still the real
   <button id="sound-toggle">, state driven by aria-pressed. */
.speaker-lamp {
  left: 13.91%;
  top: 70.47%;
}

/* .backlight-lamp: stacked directly below SPEAKER, same left inset, in
   the gap between it (70.47% + 3.6% height = 74.07%) and
   .control-strip's own row (78.125%) -- a small ~0.1%/0.3% margin on
   each side of this button rather than exactly touching either
   neighbor. Real <button id="backlight-toggle">; unlike SPEAKER/POWER
   its state has a visible effect beyond its own lamp (see
   .crt-screen.backlight-on further down and app.js's
   backlightToggle click handler). */
.backlight-lamp {
  left: 13.91%;
  top: 74.2%;
}

/* Submit/Clear, relocated into POWER's old slot (beside the vent, right
   side), sized to nearly fill the recessed socket rect drawn into the
   housing SVG (x=378 y=447 w=191 h=50 in the 640 viewBox) -- this box is
   that same rect with a small ~3-unit margin on every side
   (right:11.56%/top:70.31%/width:28.9%/height:6.88%), not centered
   loosely inside it. `align-items: stretch` + each .term-btn's own
   `flex: 1 1 0` below make the two buttons fill this box directly
   (height included), rather than sitting at their own intrinsic
   padding-driven size in the middle of a much bigger box -- that gap
   between "button size" and "box size" was the actual complaint,
   independent of how big the box itself was. flex-wrap stays enabled as
   a safety net (falls back to stacking) rather than allowing overflow if
   a viewport ever makes this slot too narrow for both buttons side by
   side -- the mobile override below compensates for that instead. */
.button-row--power-slot {
  position: absolute;
  right: 11.56%;
  top: 70.31%;
  width: 28.9%;
  height: 6.88%;
  margin: 0;
  align-items: stretch;
  justify-content: center;
}

.button-row--power-slot .term-btn {
  flex: 1 1 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 0.4rem;
  font-size: 1rem;
}

/* Light spill onto the surrounding plastic -- an old indicator bulb
   throws a small amount of its own light onto the housing right around
   it, it doesn't stay perfectly contained in its bezel. Same treatment
   for both SPEAKER and BACKLIGHT (green family for both -- "on" reads as
   the same color regardless of which feature is active; the icon shape,
   not the glow color, is what tells the two apart). */
.speaker-lamp[aria-pressed="true"],
.backlight-lamp[aria-pressed="true"] {
  box-shadow:
    inset 0 2px 5px rgba(0, 0, 0, 0.6),
    0 0 16px 5px rgba(123, 227, 154, 0.35),
    0 0 34px 10px rgba(123, 227, 154, 0.12);
}

/* Redrawn as an actual sound-on/muted speaker glyph instead of a plain
   glowing dot + "SPEAKER" text label -- the state (on vs. off) now reads
   from the icon shape itself, not just from color/brightness. The
   original .led-dot/.led-label markup is still in the DOM (nothing in
   index.html changed here) but hidden; the icon is a ::after pseudo-
   element using CSS mask-image (so ONE inline SVG shape can be recolored
   with `color`/currentColor, matching whichever state is active) with
   two different shapes swapped by the [aria-pressed] attribute, same
   technique as .crt-noise's own inline-SVG background above. */
.speaker-lamp .led-dot,
.speaker-lamp .led-label,
.backlight-lamp .led-dot,
.backlight-lamp .led-label {
  display: none;
}

/* Icon size fixed in px, not % of the button box -- .lamp-btn's box is
   now a short wide rectangle (11.25% x 3.6%, ~3:1), and a
   percentage-sized icon would be squashed to match that aspect ratio.
   A fixed square keeps both icon families (speaker glyph, bulb glyph
   below) crisp and undistorted regardless of the button's own shape. */
.speaker-lamp::after,
.backlight-lamp::after {
  content: "";
  display: block;
  width: 15px;
  height: 15px;
  background-color: var(--case-text-dim);
  transition: background-color 0.3s ease;
}

.speaker-lamp::after {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M3 9v6h4l5 5V4L7 9H3z'/><path d='M19.8 12l2.5-2.5-1.4-1.4-2.5 2.5-2.5-2.5-1.4 1.4 2.5 2.5-2.5 2.5 1.4 1.4 2.5-2.5 2.5 2.5 1.4-1.4z'/></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M3 9v6h4l5 5V4L7 9H3z'/><path d='M19.8 12l2.5-2.5-1.4-1.4-2.5 2.5-2.5-2.5-1.4 1.4 2.5 2.5-2.5 2.5 1.4 1.4 2.5-2.5 2.5 2.5 1.4-1.4z'/></svg>") center / contain no-repeat;
}

.speaker-lamp[aria-pressed="true"]::after {
  background-color: var(--led-ready);
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M3 9v6h4l5 5V4L7 9H3z'/><path d='M16.5 12c0-1.77-.77-3.29-2-4.32v8.64c1.23-1.03 2-2.55 2-4.32z'/><path d='M14.5 3.23v2.06c2.89 1.02 5 3.76 5 6.71s-2.11 5.69-5 6.71v2.06c4-1.06 7-4.72 7-8.77s-3-7.71-7-8.77z'/></svg>");
  mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M3 9v6h4l5 5V4L7 9H3z'/><path d='M16.5 12c0-1.77-.77-3.29-2-4.32v8.64c1.23-1.03 2-2.55 2-4.32z'/><path d='M14.5 3.23v2.06c2.89 1.02 5 3.76 5 6.71s-2.11 5.69-5 6.71v2.06c4-1.06 7-4.72 7-8.77s-3-7.71-7-8.77z'/></svg>");
}

/* BACKLIGHT: a sun glyph in both states -- same circle+8-rays
   composition either way (a bare ring alone, tried first, didn't read as
   "brightness" out of context), just hollow/thin when off vs.
   filled/bold when on, the same convention most OS brightness/theme
   toggles use. That shared silhouette is what makes it legible as "this
   controls light" even before the state itself is known, and the
   dim-outline -> filled-bright transition still matches SPEAKER's own
   on/off language. */
.backlight-lamp::after {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='5' fill='none' stroke='black' stroke-width='2'/><g stroke='black' stroke-width='2' stroke-linecap='round'><path d='M12 1v3'/><path d='M12 20v3'/><path d='M1 12h3'/><path d='M20 12h3'/><path d='M4.5 4.5l2.1 2.1'/><path d='M17.4 17.4l2.1 2.1'/><path d='M4.5 19.5l2.1-2.1'/><path d='M17.4 6.6l2.1-2.1'/></g></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='5' fill='none' stroke='black' stroke-width='2'/><g stroke='black' stroke-width='2' stroke-linecap='round'><path d='M12 1v3'/><path d='M12 20v3'/><path d='M1 12h3'/><path d='M20 12h3'/><path d='M4.5 4.5l2.1 2.1'/><path d='M17.4 17.4l2.1 2.1'/><path d='M4.5 19.5l2.1-2.1'/><path d='M17.4 6.6l2.1-2.1'/></g></svg>") center / contain no-repeat;
}

.backlight-lamp[aria-pressed="true"]::after {
  background-color: var(--led-ready);
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='5' fill='black'/><g stroke='black' stroke-width='2.2' stroke-linecap='round'><path d='M12 1v3'/><path d='M12 20v3'/><path d='M1 12h3'/><path d='M20 12h3'/><path d='M4.5 4.5l2.1 2.1'/><path d='M17.4 17.4l2.1 2.1'/><path d='M4.5 19.5l2.1-2.1'/><path d='M17.4 6.6l2.1-2.1'/></g></svg>");
  mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='5' fill='black'/><g stroke='black' stroke-width='2.2' stroke-linecap='round'><path d='M12 1v3'/><path d='M12 20v3'/><path d='M1 12h3'/><path d='M20 12h3'/><path d='M4.5 4.5l2.1 2.1'/><path d='M17.4 17.4l2.1 2.1'/><path d='M4.5 19.5l2.1-2.1'/><path d='M17.4 6.6l2.1-2.1'/></g></svg>");
}

/* The effect BACKLIGHT actually controls -- a uniform brightness/
   saturation boost on the whole live screen (text, glow, scanlines,
   vignette all lighten together via `filter`, rather than hand-tuning
   each layer's own opacity separately). Transition lives on the base
   .crt-screen rule so switching off animates just as smoothly as
   switching on. */
.crt-screen {
  transition: filter 0.3s ease;
}

.crt-screen.backlight-on {
  filter: brightness(1.4) saturate(1.15);
}

/* The glare/glint a brighter screen actually catches on its own curved
   glass -- reuses .crt-glass-reflection's existing highlight shapes
   (same positions/spread) rather than adding new ones, just pushed
   noticeably brighter while backlight is on, so the glass itself visibly
   reacts to the extra light instead of only the phosphor content
   underneath it. */
.crt-screen.backlight-on .crt-glass-reflection {
  background:
    radial-gradient(ellipse 62% 40% at 26% 20%, rgba(255, 255, 255, 0.34) 0%, transparent 68%),
    radial-gradient(ellipse 30% 20% at 78% 74%, rgba(255, 255, 255, 0.18) 0%, transparent 70%),
    linear-gradient(118deg, rgba(255, 255, 255, 0.12) 0%, transparent 30%),
    radial-gradient(ellipse 16% 8% at 62% 58%, rgba(255, 255, 255, 0.14) 0%, transparent 75%),
    radial-gradient(ellipse 10% 30% at 40% 85%, rgba(255, 255, 255, 0.09) 0%, transparent 75%);
}

/* Brushed-metal ID plaque -- deliberately NOT the case's molded-plastic
   material (bare steel gradient + a fine vertical brushed-metal texture
   + engraved-looking text: dark recessed ink with a light bevel
   underneath, rather than raised/printed like the button caps) so it
   reads as a separate riveted part. CORRECTED vertical budget: the
   previous pass only used the housing-top-to-bezel-top gap (SVG y=20 to
   y=52, 3.125%-8.125%, a ~5%-tall band) as the safe zone. The bezel
   itself (y=52 to the glass cutout's own top edge at y=80, 8.125%-12.5%)
   is ALSO safe -- it's dark plastic bezel material, not the live
   .crt-screen content (.crt-screen starts at 12.5%), so the plaque can
   overlap down onto it without ever covering real screen output. Full
   safe band: y=20 to y=80 (3.125%-12.5%, ~9.4% tall), center 7.8125% --
   nearly double the previous headroom, enough for a substantially bigger,
   genuinely readable plate (0.62rem -> 0.9rem) with real margin to
   spare, still centered clear of both top corner screws. */
.id-plate {
  position: absolute;
  left: 50%;
  top: 7.8125%;
  transform: translate(-50%, -50%);
  width: max-content;
  height: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid #2b2b28;
  border-radius: 4px;
  background:
    repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.09) 0, rgba(255, 255, 255, 0.09) 1px, transparent 1px, transparent 3px),
    linear-gradient(180deg, #d9d9d3 0%, #aaaaa1 40%, #86867d 62%, #c6c6bc 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.5),
    inset 0 -1px 2px rgba(0, 0, 0, 0.4),
    0 3px 5px rgba(0, 0, 0, 0.45);
  font-family: var(--font-mono);
  font-size: 0.9rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: #3a3a35;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.65);
  white-space: nowrap;
  padding: 0.42rem 14px;
}

.crt-header {
  flex: 0 0 auto;
  padding: 0.5rem 1.35rem 0.4rem;
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  color: var(--crt-green-dim);
  border-bottom: 1px solid rgba(143, 230, 172, 0.18);
  position: relative;
  z-index: 1;
}

.crt-scanlines,
.crt-vignette,
.crt-noise,
.crt-glass-reflection {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.crt-scanlines {
  /* Phase 10F: 0.26 -> 0.18 -- the multiply-blend line darkens whatever
     text sits under it every 3px, which compounds over a tall paragraph
     block far more than over one short field-label line. Still a clearly
     visible scanline texture, just less of a contrast tax on long body
     copy. */
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.18) 0px,
    rgba(0, 0, 0, 0.18) 1px,
    transparent 1px,
    transparent 3px
  );
  mix-blend-mode: multiply;
  z-index: 2;
}

/* Slightly deeper than a flat rectangle's edge darkening would need --
   reinforces the illusion that the glass is convex, curving away from
   the viewer toward the corners. */
.crt-vignette {
  background: radial-gradient(ellipse at 50% 42%, transparent 40%, rgba(0, 0, 0, 0.72) 100%);
  z-index: 2;
}

/* Faint fixed noise texture -- a tiny embedded SVG turbulence filter,
   not a photo or external asset -- screen-level grain (dust on glass),
   separate from the reflection layer below. */
.crt-noise {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  opacity: 0.05;
  mix-blend-mode: overlay;
  z-index: 2;
}

/* The actual curved-glass reflection -- a real layer sitting ON TOP of
   the live phosphor text (unlike an earlier attempt that drew this
   shape into the SVG housing behind the opaque HTML screen, where it
   was never visible at all). Two irregular soft highlights instead of
   one flat diagonal band: a broad one in the upper-left quadrant (where
   an overhead light would catch a convex surface) and a fainter,
   smaller one lower-right, plus a couple of faint streak/smudge marks.
   mix-blend-mode: screen means every layer here can only ever lighten
   what's beneath it, so text contrast is never reduced no matter how
   these are tuned. */
.crt-glass-reflection {
  background:
    radial-gradient(ellipse 62% 40% at 26% 20%, rgba(255, 255, 255, 0.16) 0%, transparent 68%),
    radial-gradient(ellipse 30% 20% at 78% 74%, rgba(255, 255, 255, 0.07) 0%, transparent 70%),
    linear-gradient(118deg, rgba(255, 255, 255, 0.05) 0%, transparent 30%),
    radial-gradient(ellipse 16% 8% at 62% 58%, rgba(255, 255, 255, 0.05) 0%, transparent 75%),
    radial-gradient(ellipse 10% 30% at 40% 85%, rgba(255, 255, 255, 0.03) 0%, transparent 75%);
  mix-blend-mode: screen;
  z-index: 4;
  transition: background 0.3s ease;
}

.crt-flicker {
  animation: crtFlicker 6s ease-in-out infinite;
}

@keyframes crtFlicker {
  0%, 96%, 100% { opacity: 1; }
  97% { opacity: 0.94; }
  98% { opacity: 1; }
  99% { opacity: 0.97; }
}

.crt-content {
  position: relative;
  z-index: 1;
  flex: 1;
  min-height: 0;
  padding: 0.6rem 1.1rem 0.85rem;
  /* Phase 10F: 0.94rem -> 1rem, and an explicit (slightly looser than the
     .crt-screen default of 1.55) line-height -- both scoped to this
     element only, so elements with their own fixed font-size
     (.field-label, .hint, badges, .statute-text's own 0.84rem/1.55, the
     mobile max-width:600px override below) are unaffected. Desktop/tablet
     reading size only; mobile's already-tuned compact sizing is untouched
     since the 600px query below still wins there. */
  font-size: 1rem;
  line-height: 1.62;
  overflow-y: auto;
  overflow-x: hidden;
}

.crt-content::-webkit-scrollbar,
.statute-text::-webkit-scrollbar {
  width: 9px;
}

.crt-content::-webkit-scrollbar-track,
.statute-text::-webkit-scrollbar-track {
  background: rgba(0, 0, 0, 0.4);
}

.crt-content::-webkit-scrollbar-thumb,
.statute-text::-webkit-scrollbar-thumb {
  background: var(--crt-green-dim);
  border-radius: 6px;
}

.crt-view {
  outline: none;
}

.crt-line {
  margin: 0 0 0.5rem;
}

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

.crt-line--bright {
  color: var(--crt-green-bright);
  font-weight: 700;
  text-shadow: 0 0 2px rgba(227, 255, 236, 0.9), 0 0 10px var(--crt-glow), 0 0 22px rgba(143, 230, 172, 0.35);
}

.crt-heading {
  margin: 0 0 0.6rem;
  color: var(--crt-green-bright);
  font-size: 0.92rem;
  letter-spacing: 0.1em;
  text-shadow: 0 0 2px rgba(227, 255, 236, 0.9), 0 0 10px var(--crt-glow);
}

.crt-rule {
  border: none;
  border-top: 1px dashed var(--crt-green-dim);
  margin: 0.75rem 0;
  opacity: 0.6;
}

.system-exception {
  text-align: center;
  letter-spacing: 0.06em;
  color: var(--crt-amber) !important;
  text-shadow: 0 0 3px rgba(255, 235, 200, 0.9), 0 0 14px rgba(255, 207, 125, 0.5) !important;
}

/* Boot log */

/* Same printed-report card as .argument-body (see that rule's own
   comment) -- the boot sequence is the terminal talking to the player
   before any case exists, so it gets the same treatment. */
.boot-log {
  margin: 0;
  padding: 0.55rem 0.75rem;
  white-space: pre-wrap;
  font-family: var(--font-mono);
  color: #16241a;
  background: rgba(226, 240, 226, 0.95);
  border: 1px solid rgba(0, 0, 0, 0.18);
  border-radius: var(--radius);
  text-shadow: none;
}

/* Intake / prompt */

/* Small, label-tier caption (same size/letter-spacing register as
   .crt-header) so it reads as noticeable without competing with the
   input line or the submit action below it -- amber, the design's
   existing accent color for anything the user should specifically
   notice (focus outline, the Trump easter egg heading), not a new
   color introduced for this. */
.crt-disclaimer {
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  color: var(--crt-amber);
}

.crt-prompt {
  color: var(--crt-green-dim);
  margin-bottom: 0.6rem;
}

.crt-input-line {
  display: flex;
  align-items: flex-start;
  gap: 0.4rem;
}

.crt-input {
  flex: 1;
  background: transparent;
  /* Phase 10F: was `border: none` (no border at all outside focus) with a
     bright amber `outline` appearing only on focus -- that outline read as
     a jarring beige/gold frame fighting the green CRT palette. Replaced
     with one quiet, always-visible dark-green border (so the field reads
     as an input even before it's focused) that only brightens, in the
     same green family, on focus -- see :focus-visible below. */
  border: 1px solid rgba(79, 143, 104, 0.45);
  border-radius: var(--radius);
  outline: none;
  resize: vertical;
  color: var(--crt-green-bright);
  font-family: var(--font-mono);
  font-size: 0.94rem;
  line-height: 1.55;
  min-height: 4.5rem;
  padding: 0.3rem 0.45rem;
  caret-color: var(--crt-green-bright);
  text-shadow: 0 0 6px var(--crt-glow);
}

.crt-input::placeholder {
  /* Phase 10F: --crt-green-dim (#4f8f68) was hard to read across a full
     placeholder sentence; --crt-green (#8fe6ac) is still clearly dimmer
     than typed text (--crt-green-bright) so the two remain visually
     distinct, just legible now. Explicit opacity:1 overrides browsers
     (Firefox) that otherwise fade placeholder color by default. */
  color: var(--crt-green);
  opacity: 1;
}

.crt-input:focus-visible {
  outline: none;
  border-color: rgba(143, 230, 172, 0.85);
  box-shadow: 0 0 6px rgba(143, 230, 172, 0.35);
}

.crt-hint {
  margin-top: 0.7rem;
  display: flex;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
  color: var(--crt-green-dim);
  font-size: 0.66rem;
  letter-spacing: 0.03em;
}

/* Processing view */

.processing-log {
  margin: 0 0 0.75rem;
}

.processing-log .crt-line {
  color: var(--crt-green-dim);
  font-size: 0.85rem;
}

.processing-log .crt-line--active {
  color: var(--crt-green-bright);
}

.progress-bar-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-top: 0.4rem;
}

.progress-bar {
  font-family: var(--font-mono);
  letter-spacing: -0.05em;
  color: var(--crt-green-bright);
  font-size: 0.85rem;
}

.progress-pct {
  color: var(--crt-green-dim);
  font-size: 0.78rem;
  min-width: 3em;
  text-align: right;
}

/* ---------------- Badges ---------------- */

.badge {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.64rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 0.18rem 0.5rem;
  border-radius: var(--radius);
  border: 1px solid currentColor;
  margin: 0.15rem 0.3rem 0.15rem 0;
}

.badge-neutral { color: var(--crt-green-dim); }
.badge-good { color: var(--crt-green-bright); }
.badge-warn { color: var(--crt-amber); }
.badge-bad { color: var(--crt-red); }

/* ---------------- Verdict ---------------- */

.verdict-card {
  border: 1px solid var(--crt-green-dim);
  border-left-width: 5px;
  border-radius: var(--radius);
  padding: 0.75rem 0.9rem;
  margin-bottom: 0.6rem;
  text-align: center;
  background: rgba(255, 255, 255, 0.02);
}

.verdict-eyebrow {
  margin: 0 0 0.3rem;
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--crt-green-dim);
}

.verdict-status {
  margin: 0;
  font-size: 1.35rem;
  font-weight: 800;
  line-height: 1.2;
  color: var(--crt-green-bright);
  text-shadow: 0 0 3px rgba(227, 255, 236, 0.95), 0 0 14px var(--crt-glow), 0 0 30px rgba(143, 230, 172, 0.4);
}

.verdict-card.severity-charged {
  border-left-color: var(--crt-red);
}
.verdict-card.severity-charged .verdict-status {
  color: var(--crt-red);
  text-shadow: 0 0 3px rgba(255, 220, 210, 0.9), 0 0 14px rgba(255, 154, 134, 0.55), 0 0 30px rgba(255, 154, 134, 0.3);
}

.verdict-card.severity-innocent {
  border-left-color: var(--crt-green-bright);
}

.verdict-card.severity-no-basis {
  border-left-color: var(--crt-amber);
}
.verdict-card.severity-no-basis .verdict-status {
  color: var(--crt-amber);
  text-shadow: 0 0 3px rgba(255, 235, 200, 0.9), 0 0 14px rgba(255, 207, 125, 0.5), 0 0 30px rgba(255, 207, 125, 0.28);
}

.verdict-card.severity-trump {
  border-left-color: var(--crt-amber);
  background-image: repeating-linear-gradient(
    135deg, transparent, transparent 12px, rgba(255, 207, 125, 0.08) 12px, rgba(255, 207, 125, 0.08) 24px
  );
}
.verdict-card.severity-trump .verdict-status {
  color: var(--crt-amber);
  text-shadow: 0 0 3px rgba(255, 235, 200, 0.9), 0 0 14px rgba(255, 207, 125, 0.5), 0 0 30px rgba(255, 207, 125, 0.28);
}

.grounding-indicator {
  margin: 0 0 0.9rem;
  padding: 0.5rem 0.7rem;
  border: 1px dashed var(--crt-green-dim);
  border-radius: var(--radius);
  color: var(--crt-green-dim);
  font-size: 0.8rem;
  text-align: center;
}

.grounding-indicator.grounding-good { border-color: var(--crt-green-bright); color: var(--crt-green-bright); }
.grounding-indicator.grounding-warn { border-color: var(--crt-amber); color: var(--crt-amber); }
.grounding-indicator.grounding-bad { border-color: var(--crt-red); color: var(--crt-red); }

/* ---------------- Field blocks (result content) ---------------- */

.field-block {
  margin-bottom: 0.95rem;
}

.field-label {
  font-size: 0.66rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--crt-green-dim);
  margin: 0 0 0.3rem;
}

/* The argument-block labels (FEDERAL FINDING/STATUTORY BASIS/APPLICATION/
   ADDITIONAL CONCERNS/DETERMINATION) -- bigger than the shared
   .field-label base (0.66rem -> 0.86rem) so they read as real section
   headings for the long paragraph blocks that follow each one, not just
   small captions. The trailing colon is in the label text itself (see
   app.js's renderArgument()), not CSS content, so it's real, selectable
   text. */
.field-label--argument {
  font-size: 0.86rem;
}

.field-value {
  margin: 0 0 0.4rem;
  color: var(--crt-green-bright);
}

.field-hint {
  margin: 0.2rem 0 0.45rem;
  color: var(--crt-green-dim);
  font-size: 0.78rem;
}

.hint {
  margin: 0;
  font-size: 0.68rem;
  color: var(--crt-green-dim);
}

.statute-block {
  border-left: 3px solid var(--crt-green-dim);
  padding-left: 0.7rem;
}

.stretch-gloss {
  margin: 0.3rem 0 0.45rem;
  font-size: 0.75rem;
  color: var(--crt-green-dim);
}

details {
  margin-top: 0.4rem;
}

details > summary {
  cursor: pointer;
  font-size: 0.7rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--crt-green-bright);
  border: 1px solid var(--crt-green-dim);
  border-radius: var(--radius);
  padding: 0.32rem 0.6rem;
  display: inline-block;
  list-style: none;
}

details > summary::-webkit-details-marker {
  display: none;
}

details > summary::after {
  content: " \25BC";
  font-size: 0.65em;
}

details[open] > summary::after {
  content: " \25B2";
}

.statute-text {
  white-space: pre-wrap;
  font-size: 0.84rem;
  line-height: 1.55;
  color: var(--crt-green);
  background: rgba(0, 0, 0, 0.35);
  padding: 0.7rem;
  border: 1px dashed var(--crt-green-dim);
  border-radius: var(--radius);
  margin-top: 0.5rem;
  max-height: 13rem;
  overflow-y: auto;
}

.caveats-list {
  margin: 0.4rem 0 0;
  padding-left: 1.1rem;
  color: var(--crt-amber);
  font-size: 0.8rem;
}

.claims-list {
  list-style: none;
  margin: 0.5rem 0 0;
  padding: 0;
}

.claims-list li {
  border-top: 1px dotted var(--crt-green-dim);
  padding: 0.45rem 0;
}

.claims-list li:first-child {
  border-top: none;
}

.fictional-panel {
  border: 2px dashed var(--crt-amber);
  border-radius: var(--radius);
  color: var(--crt-amber);
  padding: 0.7rem;
}

.fictional-panel .field-label {
  color: var(--crt-amber);
}

.tip-panel {
  border: 1px solid var(--crt-green-dim);
  border-radius: var(--radius);
  padding: 0.7rem;
  text-align: center;
}

.tip-amounts {
  display: flex;
  gap: 0.4rem;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 0.4rem;
}

.tip-amount {
  border: 1px solid var(--crt-green-dim);
  border-radius: var(--radius);
  color: var(--crt-green-bright);
  padding: 0.25rem 0.65rem;
}

/* ---------------- Phase 9F: multi-turn case rendering ---------------- */
/* Additive only -- every rule below is new markup introduced by the
   game_engine integration (case header, itemized fee table, turn
   history, DATA_LEAK takeover, reply prompt, closure banner). Existing
   classes (.field-block/.field-label/.statute-block/.statute-text/
   .fictional-panel/.verdict-card family/.hint/.crt-input-line/
   .crt-prompt/.crt-input) are reused verbatim by frontend/app.js's
   render functions, not redefined here. */

.case-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin: 0 0 0.7rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px dotted var(--crt-green-dim);
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  color: var(--crt-green-dim);
}

.case-statement {
  font-style: italic;
}

.target-value {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--crt-amber);
  text-shadow: 0 0 3px rgba(255, 235, 200, 0.85), 0 0 12px rgba(255, 207, 125, 0.4);
}

.statute-name {
  margin: 0.1rem 0;
  font-weight: 700;
  color: var(--crt-amber);
}

.statute-section {
  margin: 0 0 0.3rem;
  font-size: 0.78rem;
  color: var(--crt-green-dim);
}

/* Terminal message cards (FEDERAL FINDING / STATUTORY BASIS / APPLICATION /
   ADDITIONAL CONCERNS / DETERMINATION body text): a printed-report insert
   -- light, slightly green-tinted paper with near-black text -- instead of
   green phosphor-on-black, so these read as a distinct "printed message"
   handed to the reader rather than more raw terminal output. text-shadow
   is explicitly reset since .crt-screen's ambient green-glow shadow
   (meant for bright text on the dark screen) would otherwise still apply
   and look like a halo behind dark text on a light card. */
.argument-body {
  margin: 0 0 0.6rem;
  padding: 0.55rem 0.75rem;
  color: #16241a;
  background: rgba(226, 240, 226, 0.95);
  border: 1px solid rgba(0, 0, 0, 0.18);
  border-radius: var(--radius);
  text-shadow: none;
}

.argument-determination {
  font-weight: 700;
}

.fee-rows {
  margin-top: 0.3rem;
}

.fee-row {
  display: flex;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.2rem 0;
  border-bottom: 1px dotted rgba(79, 143, 104, 0.35);
  font-size: 0.82rem;
}

.fee-label {
  color: var(--crt-green-dim);
}

.fee-amount {
  color: var(--crt-green-bright);
  white-space: nowrap;
}

.fee-total {
  border-bottom: none;
  border-top: 1px solid var(--crt-green-dim);
  margin-top: 0.3rem;
  padding-top: 0.4rem;
  font-weight: 700;
}

.fee-total .fee-label,
.fee-total .fee-amount {
  color: var(--crt-green-bright);
}

.turn-log {
  margin-bottom: 0.9rem;
}

.turn-log-entry {
  border-top: 1px dotted var(--crt-green-dim);
  padding: 0.5rem 0;
}

.turn-log-entry:first-child {
  border-top: none;
}

.turn-log-user {
  margin: 0 0 0.25rem;
  color: var(--crt-green-dim);
}

/* Same printed-report card as .argument-body -- this is the terminal's
   own response text within the turn log, so it gets the same treatment. */
.turn-log-terminal {
  margin: 0 0 0.3rem;
  padding: 0.5rem 0.7rem;
  color: #16241a;
  background: rgba(226, 240, 226, 0.95);
  border: 1px solid rgba(0, 0, 0, 0.18);
  border-radius: var(--radius);
  text-shadow: none;
}

.turn-event {
  margin: 0 0 0.2rem;
  font-size: 0.78rem;
  color: var(--crt-amber);
}

/* Phase 10M: per-turn status/fee delta in the trajectory log -- same
   size/weight family as .turn-event (a small annotation line under the
   exchange), status change in amber (echoes .verdict-status's own
   attention color), fee change in the same bright green .fee-amount
   already uses elsewhere. */
.turn-log-status {
  margin: 0 0 0.2rem;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--crt-amber);
}

.turn-log-status--unchanged {
  font-weight: 400;
  color: var(--crt-green-dim);
}

.turn-log-fee {
  margin: 0;
  font-size: 0.78rem;
  color: var(--crt-green-bright);
}

.data-leak-block {
  border: 2px solid var(--crt-red);
  border-radius: var(--radius);
  padding: 0.8rem;
  text-align: center;
  background-image: repeating-linear-gradient(
    135deg, transparent, transparent 10px, rgba(255, 154, 134, 0.08) 10px, rgba(255, 154, 134, 0.08) 20px
  );
}

.data-leak-siren {
  margin: 0 0 0.5rem;
  font-size: 1.1rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  color: var(--crt-red);
  text-shadow: 0 0 3px rgba(255, 220, 210, 0.9), 0 0 14px rgba(255, 154, 134, 0.55);
}

.data-leak-org {
  margin: 0.3rem 0;
  font-size: 1rem;
  font-weight: 700;
  color: var(--crt-amber);
}

.reply-block {
  border-top: 1px solid var(--crt-green-dim);
  padding-top: 0.7rem;
  margin-top: 0.4rem;
}

/* Phase 10M: "your action -> system response -> case status", shown
   right before the verdict card on every turn after the first. Same
   divider language as .reply-block/.closure-block above (a project-wide
   convention for "a new section of the case view starts here"). */
.reaction-block {
  border-top: 1px solid var(--crt-green-dim);
  padding-top: 0.7rem;
  margin-top: 0.4rem;
  margin-bottom: 0.7rem;
}

.reaction-user {
  margin: 0 0 0.5rem;
  color: var(--crt-green-dim);
  font-style: italic;
}

/* Same printed-report card as .argument-body -- the terminal's own
   response to the player's latest quick action, so it gets the same
   treatment. */
.reaction-terminal {
  margin: 0 0 0.5rem;
  padding: 0.5rem 0.7rem;
  color: #16241a;
  background: rgba(226, 240, 226, 0.95);
  border: 1px solid rgba(0, 0, 0, 0.18);
  border-radius: var(--radius);
  text-shadow: none;
}

.reaction-status {
  margin: 0;
  font-weight: 700;
}

.reaction-status--changed {
  color: var(--crt-amber);
  text-shadow: 0 0 3px rgba(255, 235, 200, 0.85), 0 0 10px rgba(255, 207, 125, 0.35);
}

.reaction-status--unchanged {
  color: var(--crt-green-dim);
  font-weight: 400;
}

.reaction-fee {
  margin: 0.25rem 0 0;
  font-weight: 700;
  color: var(--crt-red);
}

/* Phase 10G: the post-verdict decision menu (WHAT WILL YOU DO?) --
   same divider treatment as .reply-block above (same border-top/padding/
   margin) since the two are mutually exclusive siblings occupying the
   same spot in the layout, never both present at once. */
.action-menu-block {
  border-top: 1px solid var(--crt-green-dim);
  padding-top: 0.7rem;
  margin-top: 0.4rem;
  text-align: center;
}

/* "WHAT WILL YOU DO?" -- bigger than the shared .field-label base
   (0.66rem), since this is the heading for the whole decision point, not
   a small caption next to a value. Scoped to this block only, same
   reasoning as .field-label--argument above. */
.action-menu-block .field-label {
  font-size: 0.9rem;
  margin-bottom: 0.6rem;
}

/* Stacked, full-width buttons rather than a row -- the simplest way to
   guarantee a large touch target on mobile without a separate breakpoint
   just for this menu, and it still reads as a CRT terminal's own
   vertical option list, not a modern web button row. */
.action-menu {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 0.4rem;
}

/* Reuses .term-btn's existing raised-cap look verbatim -- only width
   changes (full-width, stacked, rather than the inline auto-width caps
   in .button-row). Base look is a plain dim-green outline for all four;
   .action-btn--withdraw below overrides it to the amber "deliberate
   closing action" treatment (Phase 10N -- see that rule's own comment).

   Overrides .term-btn's "physical plastic cap" look (dark ink text on a
   light warm gradient) -- that reads fine for the small housing-mounted
   Submit/Clear buttons OUTSIDE the glass, but sits inside .crt-content
   here, under the same scanline/vignette overlay as the phosphor text,
   where dark-on-light contrast turns muddy and hard to read. Recolored
   to the screen's own native language instead: dark/transparent
   background, bright green text with the same restrained glow readable
   body text uses, thin dim-green border -- same family as .crt-input's
   own on-screen look, not the housing hardware's.

   Selectors are `.action-menu .action-btn` (specificity 0,2,0), not bare
   `.action-btn` (0,1,0) -- a first pass used the bare class and .term-btn
   itself (also 0,1,0, defined LATER in this file) silently won the
   cascade for every button except the one also carrying a second
   modifier class (a 0,2,0 compound selector). Scoping under .action-menu
   fixes this for all four regardless of any future reordering in this
   file. */
.action-menu .action-btn {
  width: 100%;
  font-size: 1rem;
  padding: 0.65rem 0.8rem;
  color: var(--crt-green-bright);
  background: rgba(10, 32, 19, 0.6);
  border: 1px solid var(--crt-green-dim);
  box-shadow: none;
  text-shadow: 0 0 2px rgba(227, 255, 236, 0.85), 0 0 6px rgba(143, 230, 172, 0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
}

/* One small icon per action, same inline-SVG mask-image technique as
   SPEAKER/BACKLIGHT above (a single shape recolored via currentColor,
   so it always matches whichever button/state it's on with no separate
   color rule needed). Purely decorative reinforcement of what each
   button already says in words -- never the only way to tell them
   apart. */
.action-menu .action-btn::before {
  content: "";
  display: inline-block;
  flex: none;
  width: 18px;
  height: 18px;
  background-color: currentColor;
}

/* Phase 10N: WITHDRAW's icon -- a checked/boxed document (banking the
   current result), replacing "Continue"'s plain arrow. Deliberately NOT
   the same glyph family as CANCEL's X below -- one closes the file, the
   other tears it up. */
.action-menu .action-btn[data-action="withdraw"]::before {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='4' y='3' width='16' height='18' rx='1.5' fill='none' stroke='black' stroke-width='2'/><path d='M8 12l2.5 2.5L16 9' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='4' y='3' width='16' height='18' rx='1.5' fill='none' stroke='black' stroke-width='2'/><path d='M8 12l2.5 2.5L16 9' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/></svg>") center / contain no-repeat;
}

.action-menu .action-btn[data-action="tip"]::before {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='2' y='6' width='20' height='12' rx='2' fill='none' stroke='black' stroke-width='2'/><circle cx='12' cy='12' r='3' fill='none' stroke='black' stroke-width='2'/></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='2' y='6' width='20' height='12' rx='2' fill='none' stroke='black' stroke-width='2'/><circle cx='12' cy='12' r='3' fill='none' stroke='black' stroke-width='2'/></svg>") center / contain no-repeat;
}

.action-menu .action-btn[data-action="trump"]::before {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='8' r='4' fill='none' stroke='black' stroke-width='2'/><path d='M4 20c0-4 3.6-7 8-7s8 3 8 7' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'/></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='8' r='4' fill='none' stroke='black' stroke-width='2'/><path d='M4 20c0-4 3.6-7 8-7s8 3 8 7' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'/></svg>") center / contain no-repeat;
}

.action-menu .action-btn[data-action="cancel"]::before {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M6 6l12 12M18 6L6 18' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round'/></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M6 6l12 12M18 6L6 18' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round'/></svg>") center / contain no-repeat;
}

.action-menu .action-btn:active:not(:disabled) {
  transform: none;
  background: rgba(5, 17, 8, 0.8);
  box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.5);
}

/* Phase 10N: WITHDRAW banks the current result -- a deliberate, positive
   closing action, not a discard -- so it gets the terminal's amber
   accent (already used for the "(again)" repeat badge, i.e. "notice
   this") rather than the plain dim-green outline every other quick
   action uses. Visually and semantically distinct from CANCEL just
   below on purpose: one keeps the result, the other throws it away. */
.action-menu .action-btn--withdraw {
  color: var(--crt-amber);
  background: rgba(56, 40, 10, 0.35);
  border-color: var(--crt-amber);
  box-shadow: 0 0 10px rgba(255, 207, 125, 0.25);
  text-shadow: 0 0 2px rgba(255, 240, 214, 0.85), 0 0 6px rgba(255, 207, 125, 0.35);
}

/* CANCEL discards the case outright (no fee charged, nothing banked) --
   kept visually the quietest/weakest of the four buttons so it never
   reads as equivalent to WITHDRAW. */
.action-menu .action-btn--cancel {
  opacity: 0.75;
}

/* The additional per-strategy buttons (apologize, demand a supervisor,
   threaten to sue, etc. -- see STRATEGY_BUTTONS in app.js) reuse the
   same button look as tip/trump. Slightly smaller/dimmer than the
   primary four so the button stack still reads tip/trump first, the
   wider strategy list second, withdraw/cancel last. */
.action-menu .action-btn--secondary {
  font-size: 0.88rem;
  opacity: 0.9;
}

/* Each of these nine gets its own icon glyph below (same inline-SVG
   mask technique as tip/trump/withdraw/cancel above) -- deliberately
   amber/yellow rather than currentColor, so the icon reads as its own
   accent against the otherwise green secondary-button family instead of
   just matching the text. The `[data-action="..."]` rules further down
   each carry their own -webkit-mask/mask (higher specificity than this
   one via the added attribute selector, so they layer their shape on
   top of the color/box set here). */
.action-menu .action-btn--secondary::before {
  content: "";
  color: var(--crt-amber);
}

.action-menu .action-btn[data-action="federal"]::before {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12 2l7 3v6c0 5-3.5 8.5-7 10-3.5-1.5-7-5-7-10V5l7-3z' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'/></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12 2l7 3v6c0 5-3.5 8.5-7 10-3.5-1.5-7-5-7-10V5l7-3z' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'/></svg>") center / contain no-repeat;
}

.action-menu .action-btn[data-action="supervisor"]::before {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M6 14l6-6 6 6' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/><path d='M6 20l6-6 6 6' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round' opacity='0.55'/></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M6 14l6-6 6 6' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/><path d='M6 20l6-6 6 6' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round' opacity='0.55'/></svg>") center / contain no-repeat;
}

.action-menu .action-btn[data-action="lawsuit"]::before {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><g fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><rect x='12.2' y='2.3' width='4.4' height='9.2' rx='1.5' transform='rotate(45 14.4 6.9)'/><line x1='11' y1='10.5' x2='6' y2='15.5'/><line x1='4' y1='20' x2='12' y2='20'/></g></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><g fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><rect x='12.2' y='2.3' width='4.4' height='9.2' rx='1.5' transform='rotate(45 14.4 6.9)'/><line x1='11' y1='10.5' x2='6' y2='15.5'/><line x1='4' y1='20' x2='12' y2='20'/></g></svg>") center / contain no-repeat;
}

.action-menu .action-btn[data-action="constitutional"]::before {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><g fill='none' stroke='black' stroke-linecap='round'><rect x='5' y='2' width='14' height='20' rx='1.5' stroke-width='2'/><line x1='8' y1='7' x2='16' y2='7' stroke-width='1.6'/><line x1='8' y1='11' x2='16' y2='11' stroke-width='1.6'/><line x1='8' y1='15' x2='13' y2='15' stroke-width='1.6'/></g></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><g fill='none' stroke='black' stroke-linecap='round'><rect x='5' y='2' width='14' height='20' rx='1.5' stroke-width='2'/><line x1='8' y1='7' x2='16' y2='7' stroke-width='1.6'/><line x1='8' y1='11' x2='16' y2='11' stroke-width='1.6'/><line x1='8' y1='15' x2='13' y2='15' stroke-width='1.6'/></g></svg>") center / contain no-repeat;
}

.action-menu .action-btn[data-action="authority"]::before {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='9' fill='none' stroke='black' stroke-width='2'/><line x1='6' y1='6' x2='18' y2='18' stroke='black' stroke-width='2' stroke-linecap='round'/></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='9' fill='none' stroke='black' stroke-width='2'/><line x1='6' y1='6' x2='18' y2='18' stroke='black' stroke-width='2' stroke-linecap='round'/></svg>") center / contain no-repeat;
}

.action-menu .action-btn[data-action="apology"]::before {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M4 5h16a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H9l-4 4v-4H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1z' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'/><path d='M12 13.4c-2.1-1.5-3.2-2.7-3.2-4.1 0-1.1.9-1.9 1.9-1.9.6 0 1.1.3 1.3.9.2-.6.7-.9 1.3-.9 1 0 1.9.8 1.9 1.9 0 1.4-1.1 2.6-3.2 4.1z' fill='black' stroke='none'/></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M4 5h16a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H9l-4 4v-4H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1z' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'/><path d='M12 13.4c-2.1-1.5-3.2-2.7-3.2-4.1 0-1.1.9-1.9 1.9-1.9.6 0 1.1.3 1.3.9.2-.6.7-.9 1.3-.9 1 0 1.9.8 1.9 1.9 0 1.4-1.1 2.6-3.2 4.1z' fill='black' stroke='none'/></svg>") center / contain no-repeat;
}

.action-menu .action-btn[data-action="plea"]::before {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12 20s-7-4.5-9-9.5C1.5 7 3 4 6.2 4 8.4 4 10 5.4 12 8c2-2.6 3.6-4 5.8-4C21 4 22.5 7 21 10.5 19 15.5 12 20 12 20z' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'/></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12 20s-7-4.5-9-9.5C1.5 7 3 4 6.2 4 8.4 4 10 5.4 12 8c2-2.6 3.6-4 5.8-4C21 4 22.5 7 21 10.5 19 15.5 12 20 12 20z' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'/></svg>") center / contain no-repeat;
}

.action-menu .action-btn[data-action="insult"]::before {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M4 5h16a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H9l-4 4v-4H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1z' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'/><line x1='12' y1='8' x2='12' y2='12' stroke='black' stroke-width='2' stroke-linecap='round'/><circle cx='12' cy='14.6' r='1.05' fill='black' stroke='none'/></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M4 5h16a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H9l-4 4v-4H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1z' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'/><line x1='12' y1='8' x2='12' y2='12' stroke='black' stroke-width='2' stroke-linecap='round'/><circle cx='12' cy='14.6' r='1.05' fill='black' stroke='none'/></svg>") center / contain no-repeat;
}

.action-menu .action-btn[data-action="confusion"]::before {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M9 9a3 3 0 1 1 4.5 2.6c-.9.5-1.5 1-1.5 2.4' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/><circle cx='12' cy='18' r='1.1' fill='black' stroke='none'/></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M9 9a3 3 0 1 1 4.5 2.6c-.9.5-1.5 1-1.5 2.4' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/><circle cx='12' cy='18' r='1.1' fill='black' stroke='none'/></svg>") center / contain no-repeat;
}

/* Marks a strategy already attempted this case -- never implies an
   amount or an increased/decreased chance. A proper pill/chip (fixed
   rem font-size, its own background+border) rather than plain small
   text: an `em`-sized suffix inside an already-small secondary-button
   label (.action-btn--secondary is 0.78rem) shrank to ~9px and read as
   near-illegible noise under the scanline overlay. */
.action-repeat-badge {
  display: inline-block;
  margin-left: 0.4em;
  padding: 0.05rem 0.4rem;
  border-radius: 3px;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--crt-amber);
  background: rgba(255, 207, 125, 0.16);
  border: 1px solid rgba(255, 207, 125, 0.45);
  text-shadow: none;
  vertical-align: middle;
}

/* Phase 10M: "how did this case go" -- sits between the fee block and
   .closure-block (CASE CLOSED stays the final conceptual endpoint,
   unchanged from Phase 10K). Reuses .field-label/.field-hint/.field-value
   verbatim, same look as every other labeled field in the case view. */
.case-summary-block {
  border-top: 1px solid var(--crt-green-dim);
  padding-top: 0.7rem;
  margin-top: 0.4rem;
}

.closure-block {
  border-top: 1px solid var(--crt-green-dim);
  padding-top: 0.7rem;
  margin-top: 0.4rem;
  text-align: center;
}

.closure-actions {
  margin-top: 0.6rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
}

/* PRINT VERDICT / SUBMIT NEW REQUEST -- .term-btn's default look (dark
   ink on a light plastic gradient) is tuned for the small housing-mounted
   Submit/Clear buttons OUTSIDE the glass; sitting inside .crt-content
   here, under the same scanline/vignette overlay as the phosphor text,
   that dark-on-light contrast turns muddy and hard to read (the exact
   problem .action-menu .action-btn was already recolored to avoid --
   this reuses that same screen-native look: bright green text on a
   dark/transparent field, not the housing hardware's plastic cap).
   Selector is `.closure-actions .closure-btn` (specificity 0,2,0), not
   bare `.closure-btn` (0,1,0) -- same reason `.action-menu .action-btn`
   isn't bare `.action-btn` either: .term-btn (also 0,1,0, defined LATER
   in this file) would otherwise win the cascade on source order alone. */
.closure-actions .closure-btn {
  color: var(--crt-green-bright);
  background: rgba(10, 32, 19, 0.6);
  border: 1px solid var(--crt-green-dim);
  box-shadow: none;
  text-shadow: 0 0 2px rgba(227, 255, 236, 0.85), 0 0 6px rgba(143, 230, 172, 0.35);
  /* .term-btn's base font-size/padding (0.68rem, 0.45rem 0.65rem) is
     sized for the tiny housing-mounted Submit/Clear buttons OUTSIDE the
     glass -- too small to read comfortably here, under the scanline
     overlay. Bumped up closer to .action-menu .action-btn's own size. */
  font-size: 0.82rem;
  padding: 0.6rem 1.1rem;
  min-width: 13rem;
}

.closure-actions .closure-btn:active:not(:disabled) {
  transform: none;
  background: rgba(5, 17, 8, 0.8);
  box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.5);
}

/* SUBMIT NEW REQUEST is a different kind of action from PRINT VERDICT --
   printing is a side effect on the CURRENT case, this one abandons it
   and starts a fresh one -- so it gets its own accent instead of reading
   as a twin of Print Verdict. Reuses the same amber "notice this, it's a
   deliberate next step" treatment .action-btn--withdraw already uses. */
.closure-actions .closure-btn--new-case {
  color: var(--crt-amber);
  background: rgba(56, 40, 10, 0.35);
  border-color: var(--crt-amber);
  box-shadow: 0 0 10px rgba(255, 207, 125, 0.25);
  text-shadow: 0 0 2px rgba(255, 240, 214, 0.85), 0 0 6px rgba(255, 207, 125, 0.35);
}

.technical-details summary {
  color: var(--crt-green-dim);
}

.provenance-block {
  font-size: 0.68rem;
  color: var(--crt-green-dim);
  padding-top: 0.4rem;
  margin-top: 0.25rem;
  word-break: break-all;
}

/* Errors */

.error-title {
  margin: 0 0 0.35rem;
  font-size: 0.78rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--crt-red);
}

.error-message {
  margin: 0;
  color: var(--crt-green-bright);
}

/* ---------------- Control strip contents ---------------- */
/* .control-strip's own position/sizing is declared earlier (with
   .crt-screen/.lamp-btn) since it's part of the same
   percentage-of-.crt-monitor overlay system, not a separate panel. */

.led-row {
  display: flex;
  gap: 0.9rem;
  flex-wrap: wrap;
}

.led {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}

/* A recessed panel-mounted socket the lamp sits in, built entirely from
   layered (non-inset) box-shadow rings -- each successive shadow paints
   behind the previous one without any z-index/pseudo-element stacking
   risk, so the lens (the element's own background/foreground) always
   stays visually on top of its collar. Reads as a hole drilled into the
   housing with a lens seated in it, not a flat colored sticker. */
.led-dot {
  position: relative;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--led-off);
  box-shadow:
    inset 0 0 1.5px rgba(0, 0, 0, 0.5),
    0 0 0 2.5px #100f0a,
    0 0 0 4.5px rgba(0, 0, 0, 0.4),
    0 0.5px 0 4.5px rgba(255, 255, 255, 0.06);
}

.led-dot::after {
  content: "";
  position: absolute;
  top: 1.8px;
  left: 1.8px;
  width: 3.3px;
  height: 2.5px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.65);
}

.led-label {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  color: var(--case-text-dim);
}

.led[data-state="on"] .led-dot {
  background: var(--led-on-color, var(--led-ready));
  box-shadow: 0 0 6px 1px var(--led-on-color, var(--led-ready));
}

.led--power[data-state="on"] .led-dot { --led-on-color: var(--led-power); }
.led--ready[data-state="on"] .led-dot { --led-on-color: var(--led-ready); }
.led--processing[data-state="on"] .led-dot { --led-on-color: var(--led-processing); }
.led--warning[data-state="on"] .led-dot { --led-on-color: var(--led-warning); }

.led[data-state="blink"] .led-dot {
  background: var(--led-on-color, var(--led-ready));
  animation: ledBlink 0.9s steps(1) infinite;
}

@keyframes ledBlink {
  0%, 49% { opacity: 1; box-shadow: 0 0 6px 1px var(--led-on-color, var(--led-ready)); }
  50%, 100% { opacity: 0.35; box-shadow: none; }
}

.button-row {
  display: flex;
  gap: 0.4rem;
  flex-wrap: wrap;
}

/* Each button sits in a slightly recessed "socket" (the wrapper's own
   inset shadow) so the cap reads as a raised, separately-molded part
   rather than a flat rectangle drawn on the panel. */
.term-btn {
  position: relative;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--case-ink);
  background: linear-gradient(180deg, #cfd0b8 0%, #9a9a7c 45%, #6a6a4f 100%);
  border: 1px solid var(--case-ink);
  border-radius: 3px;
  padding: 0.45rem 0.65rem;
  cursor: pointer;
  box-shadow:
    0 3px 0 rgba(0, 0, 0, 0.4),
    0 4px 3px rgba(0, 0, 0, 0.35),
    0 0 0 2px rgba(0, 0, 0, 0.22),
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 -2px 2px rgba(0, 0, 0, 0.12);
}

.term-btn:active:not(:disabled) {
  transform: translateY(3px);
  box-shadow:
    0 0 0 rgba(0, 0, 0, 0.4),
    inset 0 2px 4px rgba(0, 0, 0, 0.35),
    inset 0 1px 0 rgba(255, 255, 255, 0.25);
}

.term-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.term-btn:focus-visible {
  outline: 3px solid var(--led-warning);
  outline-offset: 2px;
}

.term-btn--primary {
  color: var(--crt-bg);
  background: linear-gradient(180deg, #c3ecc9 0%, #8bc09a 45%, #4a8461 100%);
  border-color: #234a30;
}

/* Regression guard (originally Phase 7.4): the unqualified `display:
   inline-block`/`block` treatment on control buttons must not win over
   the browser's default `[hidden] { display: none }` UA rule. Covered
   by tests/test_frontend.py::test_reset_button_hidden_rule_has_sufficient_specificity. */
.submit-btn[hidden],
.button-row[hidden] {
  display: none;
}

/* ---------------- Bill acceptor sub-panel ---------------- */

/* Solid plastic backing behind the whole vent/acceptor/neon/clip
   cluster (see index.html's comment). Raised from the housing's own
   bottom edge (90.625% = 580/640) up onto the lower housing face itself
   (86%, just below .control-strip's own row, which bottoms out at
   78.125%+6.875%=85% -- a 1-percentage-point gap keeps it clear) so the
   whole cluster reads as mounted ON the visible terminal panel instead
   of hanging on the wall below the monitor illustration -- shrinks the
   page's total scroll height by the same ~4.6% of .crt-monitor's own
   height. A little wider than .bill-assembly/.wall-mount so there's a
   visible plastic margin around the hardware it carries instead of them
   floating directly over the wall photo. */
.bill-backplate {
  position: absolute;
  top: 86%;
  left: 50%;
  transform: translateX(-50%);
  width: 66%;
  max-width: 380px;
  height: calc(7% + 14px + 9px + 10px + 10px);
  border-radius: 0 0 10px 10px;
  background: linear-gradient(180deg, var(--case-1) 0%, var(--case-2) 55%, var(--case-3) 100%);
  box-shadow:
    inset 0 -3px 6px rgba(0, 0, 0, 0.35),
    0 10px 18px rgba(0, 0, 0, 0.5);
}

/* Small fixture-style neon strip -- a dark housing bar (the element's
   own background) around a bright green glass tube (::before), echoing
   the room photo's own fluorescent fixture in miniature. Mounted BELOW
   .bill-assembly (with a clear gap under the slot, not overlapping the
   housing above it), and held up by its own bracket clip (.bill-neon-clip)
   right underneath so it reads as bolted to something rather than
   hanging in empty space.

   Redrawn again against a real photo reference of a fluorescent tube:
   the previous pass glowed glass all the way to both ends; a real tube
   has opaque metal/ceramic end caps, with the glass (and its glow)
   starting a visible margin inboard of them. .bill-neon's OWN background
   is now that metal cap material (a steel gradient, same family as
   .id-plate/.screwGrad) spanning the tube's full length; ::before (the
   glass) insets further in from each end (4px -> 8px) so the caps show
   past it. The glass gradient itself gained a vertical (top-to-bottom)
   white-hot core fading to green at the top/bottom edges -- a real
   tube's cylindrical surface catches light across its height, not just
   along its length -- and the outer glow gained one more, much softer
   and wider layer to match a real tube's broad ambient halo. A thin
   specular highlight streak still runs along the top of the tube
   (::after). Height 9px (unchanged from the prior pass); .bill-neon-clip
   and .wall-mount below both still add the tube's own height to their
   top offset to stay flush against it. */
.bill-neon {
  position: absolute;
  top: calc(86% + 7% + 14px);
  left: 50%;
  transform: translateX(-50%);
  width: 30%;
  max-width: 180px;
  height: 9px;
  border-radius: 999px;
  background: linear-gradient(180deg, #e4e4dc 0%, #aeaea4 38%, #7c7c72 68%, #5a5a50 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.35),
    inset 0 -1px 1px rgba(0, 0, 0, 0.3),
    0 2px 4px rgba(0, 0, 0, 0.5);
}

.bill-neon::before {
  content: "";
  position: absolute;
  inset: 1.5px 8px;
  border-radius: 999px;
  background:
    linear-gradient(180deg, rgba(150, 255, 190, 0.55) 0%, #f6fff9 45%, #f6fff9 55%, rgba(150, 255, 190, 0.55) 100%),
    linear-gradient(90deg, rgba(180, 255, 200, 0.5) 0%, #eaffef 12%, #eaffef 88%, rgba(180, 255, 200, 0.5) 100%);
  box-shadow:
    0 0 5px 1.5px rgba(210, 255, 225, 0.95),
    0 0 13px 4px var(--led-ready),
    0 0 34px 9px rgba(123, 227, 154, 0.32),
    0 10px 22px 4px rgba(123, 227, 154, 0.4);
}

/* The specular highlight a real glass tube's curved surface catches --
   a thin bright streak along the top third, fading out near each end
   rather than running edge-to-edge (a straight full-width highlight
   would read as a flat painted stripe, not a curved glass surface). */
.bill-neon::after {
  content: "";
  position: absolute;
  left: 7px;
  right: 7px;
  top: 2px;
  height: 1.5px;
  border-radius: 999px;
  background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.9) 18%, rgba(255, 255, 255, 0.9) 82%, transparent 100%);
  pointer-events: none;
}

/* The bracket the tube hangs from -- same case material as the wall
   brackets below, a short stub directly under the tube's center so
   there's a visible physical link instead of the tube floating free. */
.bill-neon-clip {
  position: absolute;
  top: calc(86% + 7% + 14px + 9px);
  left: 50%;
  transform: translateX(-50%);
  width: 16px;
  height: 10px;
  border-radius: 0 0 4px 4px;
  background: linear-gradient(180deg, var(--case-1) 0%, var(--case-2) 55%, var(--case-3) 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    0 3px 5px rgba(0, 0, 0, 0.45);
}

/* The whole cluster (vent / acceptor / vent) anchored once at the same
   86% mark as .bill-backplate above (raised up onto the housing's own
   lower face, see that comment); the three pieces are laid out as flex
   siblings so the acceptor box stays centered under the housing
   regardless of viewport width. */
.bill-assembly {
  position: absolute;
  top: 86%;
  left: 50%;
  transform: translateX(-50%);
  width: 54%;
  max-width: 320px;
  height: 7%;
  min-height: 26px;
  display: flex;
  align-items: stretch;
  justify-content: center;
  gap: 8px;
}

/* Flanking vent grilles -- same case material as the acceptor box and
   the main housing, textured with the same "dark slats over lit plastic"
   language as the SVG's own lower-center vent, so the assembly reads as
   more of the terminal's own hardware, not just a bolted-on slot. */
.bill-vent {
  flex: 1 1 0;
  border-radius: 4px;
  background: linear-gradient(180deg, var(--case-1) 0%, var(--case-2) 55%, var(--case-3) 100%);
  box-shadow:
    inset 0 2px 0 rgba(255, 255, 255, 0.08),
    inset 0 -3px 5px rgba(0, 0, 0, 0.4),
    0 6px 10px rgba(0, 0, 0, 0.4);
  background-image:
    repeating-linear-gradient(90deg, rgba(0, 0, 0, 0.6) 0 2px, transparent 2px 6px),
    linear-gradient(180deg, var(--case-1) 0%, var(--case-2) 55%, var(--case-3) 100%);
  background-position: center;
  background-repeat: no-repeat;
  background-size: calc(100% - 10px) calc(100% - 10px), 100% 100%;
}

/* The acceptor box itself: a relative-positioned frame so the slot
   (absolutely centered on the box) and the CASH/status cluster
   (absolutely pinned upper-right) are independent layers instead of
   sharing one centered flex row -- the slot's midpoint no longer shifts
   off-center to make room for the lights beside it. */
.bill-acceptor {
  position: relative;
  flex: 2.6 1 0;
  background: linear-gradient(180deg, var(--case-1) 0%, var(--case-2) 55%, var(--case-3) 100%);
  border-radius: 5px;
  box-shadow:
    inset 0 2px 0 rgba(255, 255, 255, 0.08),
    inset 0 -3px 5px rgba(0, 0, 0, 0.4),
    0 6px 10px rgba(0, 0, 0, 0.4);
}

.bill-slot {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 68%;
  height: 5px;
  border-radius: 3px;
  background: linear-gradient(180deg, #000000 0%, #1c1912 100%);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.9), 0 1px 0 rgba(255, 255, 255, 0.05);
}

/* A molded plastic nameplate riveted onto the acceptor box's own top
   edge -- `bottom: 100%` + `translateY(50%)` straddles that seam
   exactly (its lower half overlapping down into the box, its upper half
   rising above it), so it reads as a physically mounted part of the
   terminal rather than text floating in the empty air above the panel.
   Same molded-cap gradient/bevel as the term-btn button caps (raised
   highlight top, dark ink text) instead of a flat dark text chip. */
.bill-cash-label {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translate(-50%, 50%);
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--case-ink);
  background: linear-gradient(180deg, #cfd0b8 0%, #9a9a7c 45%, #6a6a4f 100%);
  border: 1px solid var(--case-ink);
  border-radius: 3px;
  padding: 2px 7px;
  line-height: 1.4;
  white-space: nowrap;
  box-shadow:
    0 2px 0 rgba(0, 0, 0, 0.4),
    0 3px 3px rgba(0, 0, 0, 0.35),
    inset 0 1px 0 rgba(255, 255, 255, 0.5);
}

.bill-leds {
  position: absolute;
  top: 10%;
  right: 6%;
  display: flex;
  align-items: center;
  gap: 6px;
}

.bill-led {
  position: relative;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  flex: none;
  /* the "obodok" -- a distinct ring standing off from the glow itself,
     on the wrapper element so it never participates in the blink
     animation below (that only ever touches the ::before glow-dot) --
     a blinking ring reads as broken hardware, not a status light. */
  outline: 1.5px solid rgba(217, 212, 189, 0.55);
  outline-offset: 1.5px;
}

.bill-led::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
}

.bill-led--a::before {
  background: var(--led-ready);
  box-shadow: 0 0 4px 1px var(--led-ready);
  animation: bill-led-blink 2.6s ease-in-out infinite;
}

.bill-led--b::before {
  background: var(--led-warning);
  box-shadow: 0 0 4px 1px var(--led-warning);
  opacity: 0.3;
  animation: bill-led-blink 2.6s ease-in-out infinite 1.3s;
}

@keyframes bill-led-blink {
  0%, 55%, 100% { opacity: 1; }
  75% { opacity: 0.25; }
}

/* ---------------- Wall mount ---------------- */

/* Replaces the old floor-standing pedestal: the machine now hangs on
   the wall, so this is just an invisible row that places the two
   gusset brackets symmetrically, one at each side. Anchored below
   .bill-neon-clip now (86% + 7% band + the neon strip's own gap/
   height/clip stack) instead of directly under .bill-assembly, since
   the neon fixture sits between the two. */
.wall-mount {
  position: absolute;
  top: calc(86% + 7% + 14px + 9px + 10px);
  left: 50%;
  transform: translateX(-50%);
  width: 62%;
  max-width: 360px;
  height: 34px;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
}

/* Each bracket: a diagonal gusset arm (::before, angled outward/down
   away from the housing so the two lean apart like a real brace pair)
   running from the housing down to a small bolted wall plate (::after
   + .wall-bolt), same case material as the housing so it still reads as
   part of the same molded machine. */
.wall-bracket {
  position: relative;
  width: 46px;
  height: 100%;
}

.wall-bracket::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: calc(100% - 10px);
  background:
    linear-gradient(115deg, rgba(255, 255, 255, 0.14) 8%, transparent 30%),
    linear-gradient(180deg, var(--case-1) 0%, var(--case-2) 55%, var(--case-3) 100%);
  box-shadow:
    inset -3px 0 6px rgba(0, 0, 0, 0.35),
    inset 2px 0 0 rgba(255, 255, 255, 0.05);
}

.wall-bracket--left::before {
  clip-path: polygon(55% 0%, 100% 0%, 45% 100%, 0% 100%);
}

.wall-bracket--right::before {
  clip-path: polygon(0% 0%, 45% 0%, 100% 100%, 55% 100%);
}

.wall-bracket::after {
  content: "";
  position: absolute;
  bottom: 0;
  width: 22px;
  height: 12px;
  border-radius: 3px;
  background: linear-gradient(180deg, var(--case-2) 0%, var(--case-3) 85%);
  box-shadow:
    0 8px 14px rgba(0, 0, 0, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.1),
    inset 0 -2px 3px rgba(0, 0, 0, 0.4);
}

.wall-bracket--left::after { left: 0; }
.wall-bracket--right::after { right: 0; }

.wall-bolt {
  position: absolute;
  bottom: 3px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 28%, #f5eed6 0%, #b7a874 45%, #7d724c 80%, #453e29 100%);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.6);
}

.wall-bracket--left .wall-bolt { left: 7px; }
.wall-bracket--right .wall-bolt { right: 7px; }

/* ---------------- Footer ---------------- */

.page-footer {
  text-align: center;
  padding-top: 1.75rem;
}

.disclaimer {
  font-size: 0.68rem;
  color: #8b8570;
  letter-spacing: 0.01em;
  margin: 0;
  max-width: 560px;
  margin-left: auto;
  margin-right: auto;
}

/* ---------------- Site shell: secondary UI ----------------
   A three-column desktop layout around the LEGALIZER terminal: Etsy shop
   sidebar / the untouched terminal (`.page`) / Jackal.Today news sidebar
   -- each sidebar followed by its OWN ad-banner slot directly beneath it,
   in the same column (matching the spin.jackal.today reference layout:
   one ad per column, immediately under that column's content, rather
   than a single strip spanning the bottom of the page). Nothing in this
   section changes `.page` or anything inside it (header, .terminal-unit,
   the CRT shell/screen/controls) -- it only wraps that existing block in
   a grid and adds new, independent peripheral modules beside/below it,
   rendered by frontend/sidebar.js from plain data arrays (never by
   app.js).

   Default (no media query below): a single column -- game first, then
   each sidebar immediately followed by its own ad slot -- so on any
   viewport too narrow for the flanking layout, the game keeps its own
   full natural width instead of being squeezed to make room for
   peripheral content. Two breakpoints below progressively introduce more
   of the "flanking" layout as there's comfortably enough width for it. */
.site-shell {
  display: grid;
  grid-template-areas:
    "center"
    "etsy"
    "news";
  grid-template-columns: minmax(0, 1fr);
  column-gap: 1.75rem;
  row-gap: 1.25rem;
  max-width: 1600px;
  margin: 0 auto;
  padding: 0 1rem;
  align-items: start;
}

/* `.page` already centers itself (max-width: 874px; margin: 0 auto, set
   above) -- this only stops it stretching to fill a grid track wider than
   that, and cancels its own horizontal padding since .site-shell's own
   padding above now supplies that same edge inset (avoids doubling it up
   now that .page is nested instead of body's direct child). */
.site-shell > .page {
  grid-area: center;
  width: 100%;
  padding-left: 0;
  padding-right: 0;
}

/* Each sidebar + its own ad slot is ONE grid item (.sidebar-column), not
   two -- the sidebar and ad are plain stacked flow children inside it,
   so the ad always sits immediately below the sidebar's own content no
   matter how tall the "center" game column is. An earlier version put
   the sidebar and its ad in two separate grid ROWS instead; at the
   desktop breakpoint (where etsy/center/news share one row) that made
   the first row as tall as the game, leaving a large empty gap below a
   much-shorter sidebar before its ad row even started. A single
   flex-column item sidesteps that entirely. */
.sidebar-column {
  display: flex;
  flex-direction: column;
}

.sidebar-column--etsy { grid-area: etsy; }
.sidebar-column--news { grid-area: news; }

/* Squared touching corners where the sidebar meets its ad slot (sidebar's
   bottom corners squared to match, see .sidebar below) make the pair
   read as one continuous card split by a seam, not two separate boxes. */
.ad-slot--etsy,
.ad-slot--news {
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}

/* ---- Peripheral module chrome: a muted, non-glowing sibling of the CRT
   screen's own green-phosphor terminal look (same font/colors/border
   radius as the rest of the page, no scanlines/vignette/flicker/heavy
   glow) -- reads as another module in the same "LEGALIZER terminal /
   Jackal.Today ecosystem," not a bolted-on ad widget. ---- */
.sidebar {
  font-family: var(--font-mono);
  background: rgba(5, 17, 8, 0.5);
  border: 1px solid rgba(79, 143, 104, 0.35);
  border-radius: var(--radius);
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
  padding: 0.9rem 1rem 1.1rem;
}

.sidebar-eyebrow {
  margin: 0 0 0.15rem;
  font-size: 0.58rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--crt-green-dim);
}

.sidebar-header {
  margin: 0 0 0.15rem;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--crt-green-bright);
  text-shadow: 0 0 6px var(--crt-glow);
  padding-bottom: 0.45rem;
  border-bottom: 1px dotted rgba(79, 143, 104, 0.4);
}

.sidebar-brand {
  margin: 0.45rem 0 0.7rem;
  font-size: 0.66rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--crt-amber);
}

.sidebar-empty-note {
  margin: 0.6rem 0 0;
  font-size: 0.68rem;
  line-height: 1.5;
  color: var(--crt-green-dim);
}

.sidebar-site-link {
  display: inline-block;
  margin-top: 0.8rem;
  font-size: 0.68rem;
  letter-spacing: 0.03em;
  color: var(--crt-green-bright);
  text-decoration: none;
  border-bottom: 1px dashed rgba(143, 230, 172, 0.5);
}

.sidebar-site-link:hover,
.sidebar-site-link:focus-visible {
  text-shadow: 0 0 6px var(--crt-glow);
}

/* A terminal system-status readout -- "LISTINGS OFFLINE" / "NEWS FEED
   OFFLINE" -- reusing the same amber "operator should notice this"
   language as .system-exception/the ALERT led, rather than a bare empty
   box or a developer TODO string. Used by both sidebars' empty states. */
.module-status {
  padding: 0.7rem 0;
}

.module-status-headline {
  margin: 0 0 0.3rem;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--crt-amber);
  text-shadow: 0 0 4px rgba(255, 207, 125, 0.35);
}

.module-status-subline {
  margin: 0;
  font-size: 0.64rem;
  line-height: 1.5;
  letter-spacing: 0.02em;
  color: var(--crt-green-dim);
}

/* Media box shared by shop/news card thumbnails: a fixed aspect-ratio
   reserves the space before any image loads (or if it never loads/
   errors, see sidebar.js's onerror handlers) so nothing ever shifts once
   content arrives -- and a missing image reads as a deliberate
   reserved-inventory placeholder, not a broken-image icon. */
.media-box {
  position: relative;
  width: 100%;
  overflow: hidden;
  border-radius: var(--radius);
  background: repeating-linear-gradient(
    135deg, rgba(79, 143, 104, 0.08), rgba(79, 143, 104, 0.08) 6px,
    transparent 6px, transparent 12px
  );
  border: 1px dashed rgba(79, 143, 104, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}

.media-box img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.media-box-fallback {
  font-size: 0.52rem;
  letter-spacing: 0.06em;
  color: var(--crt-green-dim);
  text-transform: uppercase;
  display: none;
  padding: 0 0.2rem;
}

.media-box--empty .media-box-fallback {
  display: block;
}

/* ---- Jackal Shop (Etsy) cards ----
   A compact terminal list, not an ecommerce card grid: each listing is
   one row (small square thumbnail + title/price/CTA), separated by the
   same dotted rule this project already uses for turn-log/claims-list
   entries -- so the module reads as part of the same system, not a
   bolted-on widget, and stays dense even with several listings. */

.shop-card-list {
  display: flex;
  flex-direction: column;
}

.shop-card {
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
  padding: 0.55rem 0;
  border-top: 1px dotted rgba(79, 143, 104, 0.4);
  color: inherit;
  text-decoration: none;
}

.shop-card:first-child {
  border-top: none;
  padding-top: 0;
}

.shop-card-media {
  flex: 0 0 76px;
  width: 76px;
  aspect-ratio: 1 / 1;
}

.shop-card-body {
  min-width: 0;
  flex: 1;
}

.shop-card-title {
  margin: 0 0 0.3rem;
  font-size: 0.72rem;
  line-height: 1.35;
  color: var(--case-text);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.shop-card-meta-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}

.shop-card-price {
  font-size: 0.74rem;
  font-weight: 700;
  color: var(--crt-amber);
}

.shop-card-cta {
  font-size: 0.6rem;
  letter-spacing: 0.03em;
  color: var(--crt-green-dim);
  white-space: nowrap;
}

a.shop-card:hover,
a.shop-card:focus-visible {
  background: rgba(143, 230, 172, 0.05);
}

a.shop-card:hover .shop-card-title,
a.shop-card:focus-visible .shop-card-title {
  color: var(--crt-green-bright);
}

a.shop-card:hover .shop-card-cta,
a.shop-card:focus-visible .shop-card-cta {
  color: var(--crt-green-bright);
  text-decoration: underline;
}

/* ---- Jackal.Today news cards ----
   Same compact row/dotted-separator pattern as the shop list above, plus
   a category badge (reusing the game's own .badge/.badge-neutral look)
   and a date -- both kept visually subordinate to the headline itself,
   which is the one thing this module needs to sell at a glance. */

.news-card-list {
  display: flex;
  flex-direction: column;
}

.news-card {
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
  padding: 0.55rem 0;
  border-top: 1px dotted rgba(79, 143, 104, 0.4);
  color: inherit;
  text-decoration: none;
}

.news-card:first-child {
  border-top: none;
  padding-top: 0;
}

.news-card-media {
  flex: 0 0 84px;
  width: 84px;
  aspect-ratio: 4 / 3;
}

.news-card-body {
  min-width: 0;
}

.news-card-category {
  margin: 0 0 0.3rem;
  font-size: 0.54rem;
  padding: 0.08rem 0.32rem;
}

.news-card-title {
  margin: 0;
  font-size: 0.78rem;
  font-weight: 600;
  line-height: 1.32;
  color: var(--case-text);
  /* Long headlines are clamped rather than left to wrap freely, so one
     long headline can't stretch a card and destroy the sidebar's vertical
     rhythm. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

a.news-card:hover .news-card-title,
a.news-card:focus-visible .news-card-title {
  color: var(--crt-green-bright);
}

.news-card-date {
  margin: 0.3rem 0 0;
  font-size: 0.58rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--crt-green-dim);
}

/* ---- Per-sidebar ad-banner slots ----
   One slot directly beneath each sidebar, in the same column (see the
   spin.jackal.today reference: an ad card sits right under the shop
   panel and right under the news panel, not in a separate bottom strip).
   Each hosts a real Google AdSense 300x250 medium-rectangle unit
   (see index.html) -- min-height matches that unit so the box never
   clips or squeezes it. */
.banner-slot {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.2rem;
  min-height: 250px;
  border-radius: var(--radius);
  overflow: hidden;
  position: relative;
  text-align: center;
}

/* ---- CASE ARCHIVE (main-page module) ----
   Moved out of the sidebar entirely -- this now lives in the CENTER
   column, as a direct sibling of .terminal-unit inside .page (see
   index.html), so it's automatically exactly as wide as the terminal and
   automatically appears directly beneath it, before either sidebar, at
   every breakpoint (.page is always the "center" grid area -- see
   .site-shell below) without any reordering media query of its own.

   Visual concept: not a card, a physical steel records compartment
   bolted underneath the terminal -- same dark olive/charcoal casing
   material as the CRT housing itself (--case-1/2/3, the exact colors
   .crt-shell's inline SVG already uses), a thick dark frame, corner
   rivets, a drawer-pull handle, and the terminal's own .led family for
   status lights -- reusing existing tokens/classes throughout rather
   than inventing a second material palette. */

.archive-vault {
  margin-top: 1.75rem;
}

.archive-vault-frame {
  position: relative;
  font-family: var(--font-mono);
  color: var(--case-text);
  background:
    /* Faint grime patches, same technique .crt-shell's SVG uses for its
       "uneven aged-plastic" housing coloration -- a few low-opacity
       radial patches over the base metal gradient, never a flat tint. */
    radial-gradient(ellipse 55% 60% at 88% 6%, rgba(255, 255, 255, 0.05) 0%, transparent 70%),
    radial-gradient(ellipse 40% 45% at 8% 96%, rgba(0, 0, 0, 0.25) 0%, transparent 70%),
    linear-gradient(180deg, var(--case-1) 0%, var(--case-2) 45%, var(--case-3) 100%);
  border: 3px solid var(--case-ink);
  border-radius: 10px;
  /* Padding now lives on .archive-vault-summary/.archive-vault-panel
     instead of here -- see the filing-drawer disclosure rules below --
     so the CLOSED state (summary only) doesn't inherit the larger
     padding meant for the full open panel. */
  padding: 0;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    inset 0 -3px 10px rgba(0, 0, 0, 0.55),
    0 10px 24px rgba(0, 0, 0, 0.5);
}

/* Filing-drawer disclosure (UX redesign): closed by default (see
   index.html -- no `open` attribute), same <details>/<summary> pattern
   as .intro-details above. .archive-vault-summary is the compact
   "cabinet label plate" control shown when closed; .archive-vault-panel
   (the header/LEDs/body/CTA/close-button that used to sit directly in
   .archive-vault-frame) only exists in the render tree while open, so
   the closed state costs almost nothing vertically. */
.archive-vault-summary {
  display: flex;
  align-items: center;
  gap: 0.5em;
  cursor: pointer;
  list-style: none;
  user-select: none;
  padding: 0.75rem 1.3rem;
  font-size: 0.78rem;
  font-weight: 800;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--crt-green-bright);
  text-shadow: 0 0 6px var(--crt-glow);
}

.archive-vault-summary::-webkit-details-marker {
  display: none;
}

/* "[+]"/"[-]" marker, same bracketed-terminal-label vocabulary as
   .intro-summary::before -- kept as generated content (decorative) so
   the control's accessible name comes from its real text, "ACCESS CASE
   ARCHIVE". */
.archive-vault-summary::before {
  content: "[+]";
  color: var(--crt-green-dim);
  text-shadow: 0 0 6px var(--crt-glow);
}

.archive-vault-details[open] > .archive-vault-summary::before {
  content: "[-]";
}

.archive-vault-summary:hover::before,
.archive-vault-summary:focus-visible::before {
  color: var(--crt-green-bright);
}

.archive-vault-panel {
  padding: 0 1.2rem 1.3rem;
  border-top: 1px dotted rgba(79, 143, 104, 0.35);
}

/* "[ CLOSE DRAWER ]" -- a plain dashed-outline text control (no metal
   plate needed here, .archive-vault-cta above it already reads as the
   drawer's "solid" affordance) that just clears the `open` attribute so
   the drawer collapses back to the compact summary control. */
.archive-vault-close {
  display: block;
  width: 100%;
  margin-top: 0.6rem;
  padding: 0.5rem 0.8rem;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-align: center;
  color: var(--crt-green-dim);
  background: rgba(6, 10, 7, 0.55);
  border: 1px dashed rgba(79, 143, 104, 0.4);
  border-radius: var(--radius);
  cursor: pointer;
}

.archive-vault-close:hover,
.archive-vault-close:focus-visible {
  color: var(--crt-green-bright);
  border-color: rgba(79, 143, 104, 0.7);
}

/* Corner rivets -- same washer-and-fastener recipe as the CRT housing's
   own #screwArt (radial highlight -> mid metal -> dark edge), reproduced
   in plain CSS here since this box is real HTML, not inline SVG. */
.archive-vault-rivet {
  position: absolute;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  overflow: hidden;
  background: radial-gradient(circle at 35% 30%, #ddd3a4 0%, #8f8256 45%, #362f1c 90%);
  box-shadow:
    inset 0 0 1px rgba(0, 0, 0, 0.6),
    0 0 0 1.5px rgba(0, 0, 0, 0.5),
    0 1px 1px rgba(255, 255, 255, 0.12);
}

/* Screwdriver slot -- a canted bar across the head, angle varied per
   corner so the four screws don't read as machine-aligned. */
.archive-vault-rivet::after { content: ""; position: absolute; top: 50%; left: 1.5px; right: 1.5px; height: 1.3px; background: linear-gradient(90deg, transparent 0%, rgba(0, 0, 0, 0.9) 18%, rgba(0, 0, 0, 0.9) 82%, transparent 100%); box-shadow: 0 1px 0 rgba(255, 255, 255, 0.2); border-radius: 1px; }

.archive-vault-rivet--tl::after { transform: translateY(-50%) rotate(-18deg); }
.archive-vault-rivet--tr::after { transform: translateY(-50%) rotate(24deg); }
.archive-vault-rivet--bl::after { transform: translateY(-50%) rotate(40deg); }
.archive-vault-rivet--br::after { transform: translateY(-50%) rotate(-34deg); }

.archive-vault-rivet--tl { top: 10px; left: 10px; }
.archive-vault-rivet--tr { top: 10px; right: 10px; }
.archive-vault-rivet--bl { bottom: 10px; left: 10px; }
.archive-vault-rivet--br { bottom: 10px; right: 10px; }

/* Drawer-pull handle -- a small bolted-on latch centered on the frame's
   top edge, reading as the physical means of opening this compartment
   (purely decorative, aria-hidden, matches the terminal's own
   non-interactive hardware details like .id-plate/.bill-assembly). */
.archive-vault-handle {
  position: absolute;
  top: -9px;
  left: 50%;
  transform: translateX(-50%);
  width: 64px;
  height: 14px;
  background: linear-gradient(180deg, #6f6f63 0%, #3a3a32 55%, #232019 100%);
  border: 1px solid #050403;
  border-radius: 3px;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.18),
    inset 0 -2px 3px rgba(0, 0, 0, 0.5),
    0 2px 4px rgba(0, 0, 0, 0.5);
}

.archive-vault-handle::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 8px;
  right: 8px;
  height: 1px;
  background: rgba(0, 0, 0, 0.5);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.08);
}

.archive-vault-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.8rem;
  flex-wrap: wrap;
  /* Left/right padding clears the frame's own corner rivets (11px wide,
     inset 10px from the frame border -- past this padding box's own
     19.2px inset) so the eyebrow/classification text never sits under
     a screw head. */
  padding: 0 9px 0.7rem;
  margin-bottom: 0.7rem;
  border-bottom: 1px dotted rgba(79, 143, 104, 0.35);
}

.archive-vault-eyebrow {
  margin: 0 0 0.2rem;
  font-size: 0.6rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--crt-green-dim);
}

.archive-vault-title {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--crt-green-bright);
  text-shadow: 0 0 6px var(--crt-glow);
}

.archive-vault-classification {
  flex: 0 0 auto;
  white-space: nowrap;
  align-self: center;
}

.archive-vault-leds {
  display: flex;
  gap: 1.1rem;
  flex-wrap: wrap;
  margin-bottom: 0.9rem;
}

/* The "compartment" itself -- a darker recessed slot the case entries
   sit inside, distinct from the lighter steel frame around it. */
.archive-vault-body {
  background: rgba(4, 10, 6, 0.55);
  border: 1px solid rgba(0, 0, 0, 0.45);
  border-radius: 6px;
  padding: 0.3rem 0.9rem;
  box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.55);
}

.archive-card-list {
  display: flex;
  flex-direction: column;
}

.archive-card {
  display: block;
  padding: 0.7rem 0;
  border-top: 1px dotted rgba(79, 143, 104, 0.3);
  color: inherit;
  text-decoration: none;
}

.archive-card:first-child {
  border-top: none;
}

.archive-card-id {
  margin: 0 0 0.25rem;
  font-size: 0.66rem;
  letter-spacing: 0.05em;
  color: var(--crt-green-dim);
}

.archive-card-subject {
  margin: 0 0 0.3rem;
  font-size: 0.78rem;
  line-height: 1.35;
  color: var(--case-text);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.archive-card-status-line {
  margin: 0 0 0.3rem;
  font-size: 0.72rem;
  letter-spacing: 0.02em;
}

.archive-card-outcome {
  color: var(--crt-amber);
  font-weight: 700;
}

.archive-card-closure {
  color: var(--crt-green-dim);
}

.archive-card-cta {
  display: inline-block;
  font-size: 0.62rem;
  letter-spacing: 0.05em;
  color: var(--crt-green-dim);
}

a.archive-card:hover,
a.archive-card:focus-visible {
  background: rgba(143, 230, 172, 0.05);
}

a.archive-card:hover .archive-card-subject,
a.archive-card:focus-visible .archive-card-subject {
  color: var(--crt-green-bright);
}

a.archive-card:hover .archive-card-cta,
a.archive-card:focus-visible .archive-card-cta {
  color: var(--crt-green-bright);
  text-decoration: underline;
}

/* "VIEW FULL FEDERAL ARCHIVE ->" -- a riveted steel panel control bolted
   into the vault (same casing gradient + brushed-metal streaks as
   .id-plate, two small slotted screws flanking the label), instead of
   the screen-native green plate used elsewhere -- this lives on the
   vault's physical housing, not behind CRT glass. Hover/focus reads as
   the panel being pressed in (shadow flips to inset). */
.archive-vault-cta { display: block; position: relative; margin-top: 1rem; padding: 0.6rem 1.3rem; text-align: center; font-family: var(--font-mono); font-size: 0.72rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; text-decoration: none; color: var(--case-text); background: repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.06) 0, rgba(255, 255, 255, 0.06) 1px, transparent 1px, transparent 3px), linear-gradient(180deg, #565a45 0%, #383b2c 48%, #1e2017 100%); border: 1px solid var(--case-ink); border-radius: var(--radius); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.22), inset 0 -2px 4px rgba(0, 0, 0, 0.55), 0 2px 4px rgba(0, 0, 0, 0.4); text-shadow: 0 1px 0 rgba(255, 255, 255, 0.12), 0 -1px 1px rgba(0, 0, 0, 0.55); }

.archive-vault-cta::before, .archive-vault-cta::after { content: ""; position: absolute; top: 50%; width: 8px; height: 8px; border-radius: 50%; transform: translateY(-50%); background: linear-gradient(115deg, transparent 44%, rgba(0, 0, 0, 0.85) 44%, rgba(0, 0, 0, 0.85) 56%, transparent 56%), radial-gradient(circle at 35% 30%, #ddd3a4 0%, #8f8256 45%, #362f1c 90%); box-shadow: inset 0 0 1px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(0, 0, 0, 0.5), 0 1px 1px rgba(255, 255, 255, 0.12); }

.archive-vault-cta::before { left: 10px; }
.archive-vault-cta::after { right: 10px; }

.archive-vault-cta:hover, .archive-vault-cta:focus-visible { color: #f4efd8; background: repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.06) 0, rgba(255, 255, 255, 0.06) 1px, transparent 1px, transparent 3px), linear-gradient(180deg, #454838 0%, #2b2e22 48%, #15160f 100%); box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.65), inset 0 -1px 0 rgba(255, 255, 255, 0.08); text-shadow: 0 0 6px rgba(217, 212, 189, 0.35); }

/* ---- CASE ARCHIVE standalone pages (/archive, /archive/{case_id}) ----
   Its own top-level page shell (NOT nested inside .crt-monitor/.crt-screen
   -- that illustrated CRT apparatus is specific to the live game unit),
   but built entirely from the same shared tokens/classes as the rest of
   the page: body's own dark room background + green-phosphor palette,
   .field-block/.field-label/.field-value, .verdict-card + severity
   variants, .turn-log*, .badge, .module-status, .sidebar-site-link --
   this reads as another subsystem of the same terminal, not a separate
   design system. */
.archive-shell {
  max-width: 874px;
  margin: 0 auto;
  padding: 3rem 1rem 6rem;
  font-family: var(--font-mono);
  color: var(--crt-green);
}

/* This shell sits directly over body::before's room photo with no card
   of its own beneath it (see the file-level comment above) -- fine over
   the photo's dark fabric/wall areas, but the eyebrow/title/nav-link
   went unreadable wherever the photo itself is bright (the accent panel
   near the top). A dark scrim panel behind the whole header guarantees
   contrast everywhere, independent of what's in the photo underneath. */
.archive-header {
  margin-bottom: 2rem;
  padding: 1rem 1.1rem 1.25rem;
  background: rgba(6, 10, 7, 0.72);
  border: 1px solid rgba(79, 143, 104, 0.35);
  border-radius: var(--radius);
}

.archive-eyebrow {
  margin: 0 0 0.3rem;
  font-size: 0.66rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--crt-green-dim);
}

.archive-title {
  margin: 0 0 0.3rem;
  font-size: 1.6rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--crt-green-bright);
  text-shadow: 0 0 6px var(--crt-glow);
}

.archive-subtitle {
  margin: 0 0 0.9rem;
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--crt-amber);
}

/* Overrides .sidebar-site-link's plain dashed-underline text-link look
   (that one's shared with the sidebars, kept as-is there) into a real
   button, centered under the header's other lines instead of trailing
   left-aligned under the subtitle -- same screen-native green-on-dark
   look as .closure-actions .closure-btn elsewhere in this project. */
.archive-nav-link {
  display: table;
  margin: 1rem auto 0;
  padding: 0.5rem 1.3rem;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--crt-green-bright);
  background: rgba(10, 32, 19, 0.6);
  border: 1px solid var(--crt-green-dim);
  border-radius: var(--radius);
  text-shadow: 0 0 2px rgba(227, 255, 236, 0.85), 0 0 6px rgba(143, 230, 172, 0.35);
}

.archive-nav-link:hover,
.archive-nav-link:focus-visible {
  border-color: var(--crt-green-bright);
  text-shadow: 0 0 6px var(--crt-glow);
}

/* Same fix as .archive-header above and for the same reason: this is
   the ONLY backing content in the page gets (the case list rows AND a
   single case's full file both render into this one container), and it
   sits directly over body::before's room photo with no card of its own
   -- unreadable wherever that photo is light. One scrim panel behind
   everything below the header, instead of one per card, since the
   individual pieces inside (.field-block, .verdict-card, .archive-row...)
   were never designed with their own opaque backgrounds. */
.archive-root {
  min-width: 0;
  padding: 1.1rem 1.2rem 1.4rem;
  background: rgba(6, 10, 7, 0.72);
  border: 1px solid rgba(79, 143, 104, 0.35);
  border-radius: var(--radius);
}

.archive-loading {
  color: var(--crt-green-dim);
  font-size: 0.85rem;
}

/* Dense index list (/archive): each case is one row, laid out as a
   flex-wrapping band of labeled cells rather than an HTML <table> --
   an actual table forces horizontal scroll the moment the combined
   column widths exceed the viewport, which a wrapping flex row never
   does (cells simply drop to additional lines at narrow widths, see the
   max-width:600px tuning below), keeping the "no horizontal overflow at
   any viewport" guarantee structural rather than something to retest per
   breakpoint. */
.archive-table {
  display: flex;
  flex-direction: column;
}

.archive-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 1rem;
  row-gap: 0.3rem;
  padding: 0.85rem 0;
  border-top: 1px dotted rgba(79, 143, 104, 0.4);
  color: inherit;
  text-decoration: none;
}

.archive-row:first-child {
  border-top: none;
  padding-top: 0;
}

.archive-cell {
  font-size: 0.78rem;
  color: var(--case-text);
}

.archive-cell--id {
  flex: 0 0 auto;
  font-size: 0.72rem;
  color: var(--crt-green-dim);
  white-space: nowrap;
}

.archive-cell--subject {
  flex: 1 1 240px;
  min-width: 0;
}

.archive-cell--disposition {
  flex: 0 0 auto;
}

.archive-cell--closure {
  flex: 0 0 auto;
  font-size: 0.68rem;
  color: var(--crt-green-dim);
}

.archive-cell--turns {
  flex: 0 0 auto;
  font-size: 0.7rem;
  color: var(--crt-green-dim);
  white-space: nowrap;
}

.archive-cell--date {
  flex: 0 0 auto;
  font-size: 0.68rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--crt-green-dim);
  white-space: nowrap;
}

.archive-cell--cta {
  flex: 0 0 auto;
  font-size: 0.68rem;
  letter-spacing: 0.03em;
  color: var(--crt-green-dim);
  white-space: nowrap;
  margin-left: auto;
}

a.archive-row:hover,
a.archive-row:focus-visible {
  background: rgba(143, 230, 172, 0.05);
}

a.archive-row:hover .archive-cell--subject,
a.archive-row:focus-visible .archive-cell--subject {
  color: var(--crt-green-bright);
}

a.archive-row:hover .archive-cell--cta,
a.archive-row:focus-visible .archive-cell--cta {
  color: var(--crt-green-bright);
  text-decoration: underline;
}

/* Case file (/archive/{case_id}) -- reuses .field-block/.field-label/
   .field-value/.verdict-card+severity/.turn-log*/.closure-block verbatim
   (see archive.js); only the few elements below have no existing
   equivalent to reuse. */
.case-file-header {
  margin-bottom: 1.1rem;
}

.case-file-id {
  margin: 0;
  font-size: 1.1rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--crt-green-bright);
  text-shadow: 0 0 4px var(--crt-glow);
}

.case-history-block {
  margin-top: 1.4rem;
}

.archive-turn-index {
  margin: 0 0 0.3rem;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--crt-green-dim);
}

.archive-detail-nav {
  margin-top: 1.5rem;
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
}

/* Tablet: sidebars sit side by side below the game instead of each
   taking the full width on their own. Each .sidebar-column still carries
   its own ad slot internally (see .sidebar-column above), so it just
   needs its one grid area here, same as desktop. */
@media (min-width: 700px) {
  .site-shell {
    grid-template-areas:
      "center center"
      "etsy news";
    grid-template-columns: 1fr 1fr;
  }
}

/* Desktop: the full three-column layout, game flanked by both sidebars.
   874px (the game's own max-width) plus two ~220-300px sidebar columns
   plus gaps needs roughly this much room before the flanking layout has
   space to breathe without squeezing the center column down from its
   natural size -- below this width the game stays the full-width center
   track from the tablet layout above instead of shrinking to make room
   for the sidebars (see this project's own layout-rule requirement: the
   game must never be squeezed to fit them). A single row now (no second
   "-ad" row needed -- each sidebar-column's ad lives inside it), so
   .sidebar-column's own `align-items: start` on .site-shell keeps it
   pinned to the top of this row instead of stretching to the game's
   full height. */
@media (min-width: 1400px) {
  .site-shell {
    grid-template-areas: "etsy center news";
    /* Widened (was 220-300px) at the operator's request so product/
       article thumbnails read as more than a postage stamp -- still
       capped well under the game's own minimum (480px), so the sidebars
       can't approach, let alone overtake, the terminal's width. */
    grid-template-columns: minmax(260px, 360px) minmax(480px, 874px) minmax(260px, 360px);
  }
}

/* ---------------- Responsive ---------------- */

@media (max-width: 600px) {
  .page {
    padding: 1.1rem 0.6rem 2.25rem;
  }

  /* At narrow widths the fixed-ratio monitor's glass becomes short in
     absolute pixels; the geometry itself stays correct (unchanged
     aspect-ratio, so the SVG shell and the screen overlay never drift
     apart), but the intake view's chrome is tightened so the input and
     its hint line both fit without an immediate scroll on first paint. */
  .crt-header {
    font-size: 0.52rem;
    padding: 0.3rem 0.5rem 0.25rem;
  }

  .crt-content {
    font-size: 0.78rem;
    line-height: 1.42;
    padding: 0.4rem 0.5rem 0.5rem;
  }

  .crt-input {
    font-size: 0.78rem;
    line-height: 1.42;
    height: 2.6rem;
    min-height: 2.6rem;
  }

  .crt-line {
    margin-bottom: 0.3rem;
  }

  .crt-hint {
    margin-top: 0.35rem;
    font-size: 0.58rem;
  }

  .verdict-status {
    font-size: 1.05rem;
  }

  .field-label {
    font-size: 0.6rem;
  }

  .field-label--argument {
    font-size: 0.74rem;
  }

  /* .control-strip keeps its row layout (never stacks to a column) at
     every width -- it occupies a fixed-height band on the illustrated
     housing (a percentage of .crt-monitor), so it shrinks its contents
     rather than reflowing them. */
  .control-strip {
    gap: 0.3rem;
  }

  .led-row {
    gap: 0.35rem;
  }

  .led-label {
    display: none;
  }

  .button-row {
    gap: 0.25rem;
  }

  .term-btn {
    padding: 0.3rem 0.35rem;
    font-size: 0.56rem;
  }

  /* Phase 10G: the generic .term-btn mobile shrink just above (tuned for
     the small housing-mounted Submit/Clear caps) would otherwise also
     shrink these -- explicitly kept close to their desktop size instead,
     since the action menu's whole point is a large, easy mobile tap
     target, not a compact hardware button. Scoped under .action-menu
     (same reasoning as the desktop rule above) rather than the bare
     class. */
  .action-menu .action-btn {
    padding: 0.65rem 0.8rem;
    font-size: 0.9rem;
  }

  .lamp-btn .led-label {
    font-size: 0.48rem;
  }

  /* Submit/Clear's power-slot position is a fixed 24%-of-housing-width
     box (sized for desktop, where that's plenty for two side-by-side
     buttons) -- at mobile absolute housing widths that's too narrow and
     the default flex-wrap fallback stacks the two buttons instead of
     keeping them in one row. Widened + buttons shrunk further so they
     still fit side by side here too, verified live (375px). */
  .button-row--power-slot {
    right: 8%;
    width: 34%;
  }

  .button-row--power-slot .term-btn {
    padding: 0.22rem 0.3rem;
    font-size: 0.56rem;
  }

  /* Same "simplify decorative hardware on mobile, keep the terminal
     concept" precedent this project already used for the caution
     sticker/serial tag: kept hidden below 600px -- not retested for
     overlap at its new top-of-housing position, so left as-is rather
     than assumed safe. */
  .id-plate {
    display: none;
  }

  /* Phase 10B (mobile UX fix, docs/LEGALIZER_PHASE_10A_PRODUCT_AUDIT.md
     Should-Fix #1): at 375-600px the illustrated glass (.crt-screen,
     51.56% of the square .crt-monitor by default) showed only ~9% of a
     full case's content at once, against ~32% on desktop -- forcing far
     more internal scrolling on mobile than the same case needs on
     desktop. Two changes, both scoped to internal block sizing/spacing
     only (no aesthetic change, no new layout pattern):

     1. Grow the glass itself, but ONLY into the plain decorative housing
        gap below it that holds no interactive element -- the LED/button
        control-strip starts at top:70.47% (see .lamp-btn above), so
        58% keeps a 4.4-percentage-point buffer below the new screen
        bottom (12.5%+58%=70.5%) before reaching it. This is the SAME
        square illustration, same aspect-ratio, same bezel artwork,
        undistorted -- just a taller inset within it. Verified visually
        (Playwright screenshot) to show no overlap with the control
        strip/lamps at 375/390px before shipping.
     2. Shrink the vertical rhythm of the result content itself (field
        spacing, card padding, the excerpt box's own internal max-height)
        so a full case is also LESS TALL in total, not just viewed
        through a somewhat bigger window -- the two changes compound.
        Desktop is untouched (this entire block is inside the existing
        max-width:600px query). */
  .crt-screen {
    height: 58%;
  }

  .crt-content {
    padding: 0.3rem 0.4rem 0.4rem;
    line-height: 1.32;
  }

  .crt-heading {
    margin: 0 0 0.4rem;
  }

  .crt-rule {
    margin: 0.5rem 0;
  }

  .case-header {
    margin: 0 0 0.4rem;
    padding-bottom: 0.25rem;
  }

  .field-block {
    margin-bottom: 0.55rem;
  }

  .field-value {
    margin: 0 0 0.25rem;
  }

  .verdict-card {
    padding: 0.5rem 0.65rem;
    margin-bottom: 0.4rem;
  }

  .verdict-status {
    font-size: 1.05rem;
  }

  .fictional-panel,
  .tip-panel {
    padding: 0.5rem;
  }

  /* Still its own independently-scrollable box (unchanged behavior,
     unchanged excerpt text) -- only how much of the OUTER scroll region
     it occupies before a reader has to scroll within it changes. */
  .statute-text {
    padding: 0.5rem;
    max-height: 8rem;
  }

  .fee-row {
    padding: 0.12rem 0;
  }

  .turn-log {
    margin-bottom: 0.55rem;
  }

  .turn-log-entry {
    padding: 0.35rem 0;
  }

  .crt-input {
    min-height: 3.5rem;
  }

  .site-shell {
    row-gap: 1.25rem;
    padding: 0 0.6rem;
  }

  .sidebar {
    padding: 0.7rem 0.8rem 0.9rem;
  }

  .news-card-media {
    flex-basis: 56px;
    width: 56px;
  }

  .shop-card-media {
    flex-basis: 44px;
    width: 44px;
  }

  .archive-vault {
    margin-top: 1.2rem;
  }

  .archive-vault-summary {
    padding: 0.65rem 0.9rem;
    font-size: 0.72rem;
  }

  .archive-vault-panel {
    padding: 0 0.9rem 1.05rem;
  }

  .archive-vault-title {
    font-size: 0.92rem;
  }

  .archive-vault-body {
    padding: 0.2rem 0.7rem;
  }

  .archive-shell {
    padding: 1.6rem 0.8rem 3rem;
  }

  .archive-title {
    font-size: 1.2rem;
  }

  .archive-subtitle {
    font-size: 0.7rem;
  }

  /* The index row already wraps at any width (see .archive-row's
     flex-wrap above) -- here each cell is additionally forced to its own
     full-width line instead of several short cells sharing a row, since
     several 3-4-word cells side by side at 375px still read as cramped
     even though they never overflow. */
  .archive-row {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.2rem;
    padding: 0.7rem 0;
  }

  /* .archive-cell--subject/--id/etc.'s `flex: ... 240px` above sets
     flex-basis for the desktop ROW direction (a width hint); flipping
     .archive-row to flex-direction:column just above makes flex-basis
     apply to the CROSS axis instead -- i.e. HEIGHT -- which without this
     override left .archive-cell--subject rendered ~240px tall, a large
     blank gap before the next cell. `.archive-row .archive-cell`
     (specificity 0,2,0) deliberately outranks every `.archive-cell--*`
     modifier rule above (0,1,0) regardless of source order, so one reset
     covers all of them. */
  .archive-row .archive-cell {
    flex: none;
    width: 100%;
  }

  .archive-cell--cta {
    margin-left: 0;
  }

  .archive-detail-nav {
    flex-direction: column;
    gap: 0.6rem;
  }
}

/* Respect reduced-motion preferences: no flicker, no LED blink animation
   (state is still conveyed by color + text label, not lost), boot
   sequence appears instantly (handled in JS). */
@media (prefers-reduced-motion: reduce) {
  .crt-flicker {
    animation: none;
  }
  .led[data-state="blink"] .led-dot {
    animation: none;
    opacity: 1;
  }
}

/* ---------------- Phase 10J: PRINT VERDICT ---------------- */
/* window.print() (app.js's PRINT VERDICT handler) triggers this. Two
   jobs: (1) hide every decorative/hardware/interactive layer -- the SVG
   housing, scanlines/vignette/noise/glass-reflection, the room photo,
   LEDs, physical buttons, the boot/intake/loading/error views, and the
   print/reset buttons themselves -- so only the actual case record
   prints; (2) break #result-section and its ancestors OUT of the
   fixed-aspect-ratio/percentage/absolute-positioned glass-cutout layout
   that makes sense on screen but would clip a printed page to whatever
   happened to fit in the on-screen glass. Restoring normal static block
   flow with overflow:visible lets the browser's own print pagination
   flow the FULL case (every field, the full fee table, the full closure
   text) across as many pages as it needs -- never just the current
   scroll position. */
@media print {
  body::before,
  .crt-shell,
  .crt-scanlines,
  .crt-vignette,
  .crt-noise,
  .crt-glass-reflection,
  .crt-header,
  .control-strip,
  .speaker-lamp,
  .backlight-lamp,
  .id-plate,
  .bill-backplate,
  .bill-assembly,
  .bill-neon,
  .bill-neon-clip,
  .wall-mount,
  #boot-view,
  #intake-view,
  #loading-section,
  #error-section,
  .closure-actions,
  .page-footer,
  /* Secondary UI (Etsy shop / Jackal.Today news / ad slots) -- a printed
     verdict is the case record (statement, statute, argument, fee
     breakdown, disposition), never the peripheral site chrome around it. */
  .sidebar,
  .banner-slot,
  /* CASE ARCHIVE (Phase 10Q) -- another sibling of .terminal-unit inside
     .page, same reasoning as the sidebars: it's site chrome browsing
     OTHER closed cases, not part of THIS verdict's record. */
  .archive-vault {
    display: none !important;
  }

  .page,
  .terminal-unit,
  .crt-monitor,
  .crt-screen,
  #crt-content,
  #result-section {
    position: static !important;
    display: block !important;
    inset: auto !important;
    width: 100% !important;
    height: auto !important;
    max-width: none !important;
    max-height: none !important;
    aspect-ratio: auto !important;
    overflow: visible !important;
    padding: 0 !important;
    margin: 0 !important;
    border: none !important;
    box-shadow: none !important;
    background: #fff !important;
    filter: none !important;
    animation: none !important;
    transform: none !important;
  }

  /* Flat black-on-white text -- the green-phosphor glow/text-shadow
     look is screen-only; it would waste ink and hurt legibility on
     paper. */
  body,
  #result-section,
  #result-section * {
    color: #000 !important;
    text-shadow: none !important;
    background: transparent !important;
    box-shadow: none !important;
  }

  .verdict-card,
  .statute-block,
  .fee-row,
  .fee-total,
  .field-block {
    border-color: #999 !important;
  }
}
