/* Display geometry and machine chrome.
   The CRT uses a fixed 4:3 tube around live CRTC geometry. The built-in
   640x80 display is a reflective STN panel with physical cell spacing. */

/* ---- CRT: fixed 4:3 tube face, live-geometry raster stretched inside ---- */

/* Container-query tube sizing requires a definite flex height. */
#screen-region {
  flex: 1 1 auto;
  min-height: 0;
}

/* The rounded inset renders the complete CRT surround. */
#screen-region .crt-bezel {
  background: transparent;
  border-radius: 0;
  padding: 0;
  box-shadow: none;
}

#screen-region .crt-tube {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  container-type: size;
}

#screen-region .crt-inset {
  flex: 0 0 auto;
  aspect-ratio: 4 / 3;
  max-height: none;
  /* Largest 4:3 rectangle inside the tube container. */
  width: min(100cqw, calc(100cqh * 4 / 3));
  height: min(100cqh, calc(100cqw * 3 / 4));
}

/* The built-in LCD is seated directly in the warm-white case. Its glass and
   four machine-driven indicators share the dark faceplate used by the real
   unit; the 640:80 raster remains an independent box. */
#screen-region .lcd-bezel {
  background: linear-gradient(180deg, var(--case-hi), var(--case-dark));
  padding: 9px 10px;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.5),
    0 1px 0 rgba(255, 255, 255, 0.22),
    0 2px 5px rgba(0, 0, 0, 0.22);
}

/* ---- LCD: reflective STN chrome (palette itself lives in lcd.js) ---- */

#screen-region .lcd-hardware-panel {
  --lcd-raster-width: 640px;
  --lcd-glass-pad-x: 8px;
  --lcd-glass-pad-y: 5px;
  --lcd-indicator-size: 90px;
  --lcd-panel-gap: 8px;
  display: grid;
  grid-template-columns:
    minmax(0, calc(var(--lcd-raster-width) + var(--lcd-glass-pad-x) + var(--lcd-glass-pad-x)))
    minmax(56px, var(--lcd-indicator-size));
  align-items: stretch;
  gap: var(--lcd-panel-gap);
  width: min(
    100%,
    calc(
      var(--lcd-raster-width) + var(--lcd-glass-pad-x) + var(--lcd-glass-pad-x) +
      var(--lcd-indicator-size) + var(--lcd-panel-gap) + 14px
    )
  );
  margin-inline: auto;
  padding: 6px;
  border: 1px solid rgba(215, 222, 222, 0.45);
  border-radius: 5px;
  background: var(--panel-deep);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.38);
}

#screen-region .lcd-inset {
  width: 100%;
  min-width: 0;
  padding: var(--lcd-glass-pad-y) var(--lcd-glass-pad-x);
  border-radius: 7px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.16), transparent 36%),
    #9ca894;
  overflow: hidden;
  /* Recessed glass seat. */
  box-shadow:
    0 0 0 1px rgba(20, 17, 13, 0.55),
    inset 0 6px 10px -7px rgba(18, 22, 16, 0.4),
    inset 0 0 0 1px rgba(18, 22, 16, 0.22);
}

#screen-region .lcd-indicators {
  display: grid;
  grid-template-rows: repeat(4, minmax(0, 1fr));
  min-width: 0;
  min-height: 0;
  gap: 2px;
  padding: 1px;
  border: 1px solid rgba(215, 222, 222, 0.32);
  background: #202a3d;
}

#screen-region .lcd-indicator {
  display: grid;
  grid-template-columns: minmax(12px, 34%) minmax(0, 1fr);
  align-items: center;
  gap: 5px;
  min-height: 0;
  padding: 1px 5px;
  border: 1px solid rgba(215, 222, 222, 0.28);
  color: #d9dee2;
  background: rgba(255, 255, 255, 0.025);
}

#screen-region .lcd-indicator-lamp {
  display: block;
  width: 100%;
  max-width: 25px;
  height: 7px;
  border: 1px solid rgba(0, 0, 0, 0.62);
  border-radius: 1px;
  background: #183327;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.72);
  transition: background 0.1s, box-shadow 0.1s;
}

#screen-region .lcd-indicator.is-on .lcd-indicator-lamp {
  background: #49c878;
  box-shadow:
    0 0 6px rgba(73, 200, 120, 0.72),
    inset 0 0 2px rgba(226, 255, 234, 0.82);
}

#screen-region .lcd-indicator-power .lcd-indicator-lamp { background: #4b2928; }
#screen-region .lcd-indicator-power.is-on .lcd-indicator-lamp {
  background: var(--seal);
  box-shadow:
    0 0 6px var(--seal-glow),
    inset 0 0 2px rgba(255, 225, 212, 0.78);
}

#screen-region .lcd-indicator-label {
  overflow: hidden;
  font-size: 9px;
  font-weight: 680;
  letter-spacing: 0.08em;
  line-height: 1;
  text-align: center;
  white-space: nowrap;
}

/* The real 0.38 mm pitch gives a 243.2x30.4 mm active area. On screen the
   normal presentation is its native 640x80 raster; narrower viewports shrink
   it proportionally, while devicePixelRatio supplies integer retina backing. */
#screen-region .lcd-active {
  position: relative;
  width: 100%;
  aspect-ratio: 640 / 80;
  border-radius: 3px;
  background: #c0cab5;
  overflow: hidden;
}

#screen-region .lcd-active #lcd {
  width: 100%;
  height: 100%;
  background: transparent;
  /* A 19-unit cell represents the 0.38 mm pitch. Its centred 17-unit square
     is the 0.34 mm energised dot, leaving a 0.04 mm dot-to-dot gap. The canvas
     contains only energised ink, so this mask never marks the blank glass. */
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 19 19'%3E%3Crect x='1' y='1' width='17' height='17' fill='white'/%3E%3C/svg%3E");
  -webkit-mask-position: 0 0;
  -webkit-mask-repeat: repeat;
  -webkit-mask-size: 0.15625% 1.25%;
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 19 19'%3E%3Crect x='1' y='1' width='17' height='17' fill='white'/%3E%3C/svg%3E");
  mask-position: 0 0;
  mask-repeat: repeat;
  mask-size: 0.15625% 1.25%;
}

#screen-region .lcd-inset .crt-glow {
  display: none;
}

/* Enlarged display mode keeps the original canvases in flow. The target is
   captured when the menu action is pressed, so a transient blank CRT frame
   cannot swap displays underneath the user. */
.machine.is-display-enlarged[data-enlarged-display="crt"] #screen-region .lcd-bezel,
.machine.is-display-enlarged[data-enlarged-display="lcd"] #screen-region .crt-bezel {
  display: none;
}

.machine.is-display-enlarged[data-enlarged-display="lcd"] #screen-region,
.machine.is-display-enlarged[data-enlarged-display="lcd"] #screen-region .lcd-bezel {
  flex: 1 1 auto;
}

.machine.is-display-enlarged[data-enlarged-display="lcd"] #screen-region .lcd-bezel {
  display: flex;
  align-items: center;
}

@media (min-width: 1530px) {
  .machine.is-display-enlarged[data-enlarged-display="lcd"] #screen-region .lcd-hardware-panel {
    --lcd-raster-width: 1280px;
    --lcd-indicator-size: 170px;
  }
}

/* CRT appearance. crt.js publishes the face and glow colors and toggles
   .is-crisp for the flat pixel display. */

/* Phosphor bloom and scanlines. */
#screen-region .crt-glow {
  background:
    radial-gradient(120% 90% at 50% 45%,
      rgba(var(--crt-glow-tint, 150, 190, 230), 0.14) 0%, transparent 58%),
    repeating-linear-gradient(0deg,
      rgba(var(--crt-glow-tint, 150, 190, 230), 0.06) 0 1px, transparent 1px 3px);
}

#screen-region .crt-inset { background: var(--crt-face, var(--lcd-bg)); }

#screen-region .crt-off {
  background: var(--crt-face, var(--lcd-bg));
  color: rgba(var(--crt-glow-tint, 216, 232, 240), 0.42);
}

/* ---- TUBE (:not(.is-crisp)) — early-80s monochrome CRT monitor ---------- */

/* Dark underscan keeps the rounded glass outside the live raster. */
#screen-region:not(.is-crisp) .crt-inset {
  border-radius: 20px;
  padding: max(3%, 22px);
  box-shadow:
    0 0 0 3px #2a2824,
    0 0 0 4px rgba(0, 0, 0, 0.6),
    0 5px 12px -3px rgba(0, 0, 0, 0.5),
    inset 0 2px 5px rgba(255, 255, 255, 0.05),
    inset 0 0 38px 6px rgba(0, 0, 0, 0.9),
    inset 0 0 0 1px rgba(0, 0, 0, 0.75);
}

/* Curved-glass reflection and barrel shading. */
#screen-region:not(.is-crisp) .crt-inset::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  border-radius: inherit;
  background:
    radial-gradient(150% 58% at 50% -14%,
      rgba(255, 255, 255, 0.10) 0%, rgba(255, 255, 255, 0.02) 34%, transparent 60%),
    radial-gradient(122% 130% at 50% 44%,
      transparent 45%, rgba(0, 0, 0, 0.5) 100%);
}

/* ---- FLAT PANEL (.is-crisp) --------------------------------------------- */
#screen-region.is-crisp .crt-inset {
  /* The box-shadow bezel stays outside the fit box measured by crt.js. */
  --panel-bezel: 17px;
  --panel-bezel-b: 20px;
  aspect-ratio: var(--raster-ar, 1.6);
  width: min(
    calc(100cqw - 2 * var(--panel-bezel)),
    calc((100cqh - 2 * var(--panel-bezel-b)) * var(--raster-ar, 1.6))
  );
  height: min(
    calc(100cqh - 2 * var(--panel-bezel-b)),
    calc((100cqw - 2 * var(--panel-bezel)) / var(--raster-ar, 1.6))
  );
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 0;
  background: var(--crt-face, #000);
  box-shadow:
    inset 0 0 0 1px rgba(0, 0, 0, 0.6),
    0 0 0 16px #2a2824,
    0 0 0 17px rgba(255, 255, 255, 0.06),
    0 4px 0 16px #2a2824,
    0 6px 16px -4px rgba(0, 0, 0, 0.55);
}
#screen-region.is-crisp #crt {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
}
#screen-region.is-crisp .crt-glow { display: none; }

/* ---- Display-type control ----------------------------------------------- */
.facia-bottom .ctl-display { min-width: 0; }
.facia-bottom .crt-select {
  font: inherit;
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink);
  background: linear-gradient(180deg, var(--case-hi), var(--case-dark));
  border: 1px solid var(--case-deep);
  border-radius: 6px;
  padding: 6px 8px;
  min-width: 0;
  max-width: 96px;
  cursor: pointer;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18),
              inset 0 1px 0 rgba(255, 255, 255, 0.4);
}
.facia-bottom .crt-select option { color: var(--ink); background: var(--case-hi); }
.facia-bottom #crt-mode-label {
  max-width: 140px;
  overflow: hidden;
  color: rgba(239, 242, 246, 0.7);
  font-size: 10px;
  letter-spacing: 0.03em;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.facia-bottom #crt-mode-label:empty { display: none; }

/* Mobile/tablet: the page is height-auto so flex children no longer inherit a
   definite height. Give the CRT tube an explicit viewport-derived fit box;
   otherwise the container-query math above can resolve against a collapsed
   parent and make the CRT effectively disappear. */
@media (max-width: 1024px) {
  #screen-region {
    flex: 0 0 auto;
  }

  #screen-region .crt-bezel {
    flex: 0 0 auto;
  }

  #screen-region .crt-tube {
    flex: 0 0 auto;
    height: clamp(220px, 54vw, 430px);
    min-height: clamp(220px, 54vw, 430px);
  }

  #screen-region .lcd-bezel {
    padding: 8px 9px;
  }
}

@media (max-width: 640px) {
  #screen-region .crt-tube {
    height: clamp(178px, 60vw, 260px);
    min-height: clamp(178px, 60vw, 260px);
  }

  #screen-region:not(.is-crisp) .crt-inset {
    border-radius: 15px;
    padding: max(4%, 12px);
  }

  #screen-region.is-crisp .crt-inset {
    --panel-bezel: 10px;
    --panel-bezel-b: 12px;
  }

  #screen-region .lcd-bezel {
    padding: 6px 7px;
  }

  #screen-region .lcd-hardware-panel {
    --lcd-glass-pad-x: 6px;
    --lcd-glass-pad-y: 4px;
    --lcd-indicator-size: 58px;
    --lcd-panel-gap: 5px;
    grid-template-columns:
      minmax(0, calc(var(--lcd-raster-width) + var(--lcd-glass-pad-x) + var(--lcd-glass-pad-x)))
      minmax(52px, var(--lcd-indicator-size));
    padding: 4px;
  }

  #screen-region .lcd-inset {
    border-radius: 6px;
  }

  #screen-region .lcd-indicator {
    grid-template-columns: minmax(9px, 31%) minmax(0, 1fr);
    gap: 2px;
    padding-inline: 2px;
  }

  #screen-region .lcd-indicator-lamp { height: 5px; }
  #screen-region .lcd-indicator-label { font-size: 7px; letter-spacing: 0; }
}

/* ---- POWER latching switch (repurposes the old passive power-dot) ---- */
.power-cluster .power-switch {
  border: 1px solid var(--case-deep);
  background: linear-gradient(180deg, var(--case-hi), var(--case-dark));
  border-radius: 8px;
  padding: 5px 9px;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 2px rgba(0, 0, 0, 0.28);
  transition: box-shadow 0.1s, border-color 0.1s;
}
/* Latched ON: the switch sits depressed with a live (seal) rim. */
.power-cluster .power-switch.is-on {
  border-color: var(--seal);
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.34), 0 0 8px var(--seal-glow);
}
.power-cluster .power-switch:active { box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.42); }

/* RESET / PAUSE disabled during re-initialisation. */
.power-cluster .btn-round:disabled { opacity: 0.4; cursor: default; }
.power-cluster .btn-round:disabled .btn-glyph { box-shadow: none; }
#reset-glyph { font-size: 17px; line-height: 1; }

/* =====================================================================
   POWER OFF — the machine is dark. The pacing loop is stopped, so app.js
   paints the tube black (crt.powerOff) and the LCD to its bare reflective
   cell (lcd.powerOff); these rules kill every emissive cue (phosphor glow,
   idle plate) so the panel reads as physically unpowered, not merely idle.
   ===================================================================== */
.machine.is-powered-off #screen-region .crt-glow { display: none; }
.machine.is-powered-off #screen-region .crt-inset { background: #000; }
.machine.is-powered-off #screen-region .crt-off { display: none; }
/* Kill the tube's glass optics (specular/barrel) too — an unpowered face has no
   emissive cue; only the physical black glass remains. */
.machine.is-powered-off #screen-region .crt-inset::after { display: none; }
/* Continuous paper emerges above the fixed print line. The surface carries
   the paper and page tiles; the slot apron and carriage remain stationary. */

/* The fixed window onto the paper path. overflow:hidden clips the tall surface.
   The window's own background is the MECHANISM TRAY — dark, recessed — because
   the white paper is a DOM element (.paper-body, built by printer.js) that only
   covers the tray where the feed has actually delivered paper. */
.platen .paper-viewport {
  --print-line: 84%;      /* fallback; printer.js publishes the runtime value */
  position: relative;
  flex: 1 1 auto;
  min-height: 260px;
  overflow: hidden;
  border-radius: 6px;
  background: linear-gradient(180deg, #23262b, #1a1d22 60%, #17191d);
  box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(0, 0, 0, 0.25);
}

/* The native 1320-px-wide printable area, scaled by JS (transform: scale(S),
   S = windowWidth / TOTAL_W_NATIVE) at ONE fixed 1px = 1/180" scale; printer.js
   also sets `left` to the scaled tractor-margin width each resize. The white
   .paper-body child extends STRIP_W_NATIVE beyond each side of this box so the
   punched-hole margins land exactly over the tractor rails. Sits ABOVE the
   rails (z2 vs z1): paper covers mechanism wherever it has emerged. */
.platen .paper-scaler {
  position: absolute;
  top: 0;
  left: 22px;            /* JS-overridden per resize (STRIP_W_NATIVE * S) */
  width: 1320px;         /* == PAPER_WIDTH_STEPS, native px */
  height: 100%;
  transform-origin: top left;
  will-change: transform;
  z-index: 2;
}

/* Roll tiles and the live page move together through the fixed print line. */
.platen .paper-surface {
  position: absolute;
  top: 0;
  left: 0;
  width: 1320px;
  transform: translateY(0);
  will-change: transform;
  transition: transform 0.35s ease-out;
}
.platen .paper-surface canvas { image-rendering: auto; display: block; }
.platen .paper-surface .paper-punch-strip {
  cursor: ns-resize;
  touch-action: none;
}
.platen .paper-viewport.is-paper-dragging .paper-punch-strip {
  cursor: grabbing;
}

@media (prefers-reduced-motion: reduce) {
  .platen .paper-surface { transition: none; }
}

/* TRACTOR RAILS down both edges — mechanism, BENEATH the paper (z1 vs z2), so
   they are what shows in the no-paper state and the emerging paper (with its own
   punched-hole margins) covers them. The drive-pin studs, their 1/2"-pitch size
   and their advance-with-the-feed position are set by printer.js (they depend on
   the live scale); here just the rail chrome + placement, overriding
   web/style.css's old dotted `.sprockets` texture. */
.platen .paper-viewport .sprockets {
  z-index: 1;
  top: 0;
  bottom: 0;
  opacity: 1;
  background-color: rgba(255, 255, 255, 0.04);
  box-shadow:
    inset 0 0 0 1px rgba(0, 0, 0, 0.4),
    inset 2px 0 3px -2px rgba(255, 255, 255, 0.14),
    inset -2px 0 3px -2px rgba(0, 0, 0, 0.5);
}
.platen .paper-viewport .sprockets-left { left: 0; right: auto; }
.platen .paper-viewport .sprockets-right { right: 0; left: auto; }

/* SLOT APRON: the mechanism's front cover from the print line down. The paper
   inside the slot (the band being struck, the supply) is physically hidden; only
   what has emerged above the line is visible. Top edge = the slot lip. */
.platen .paper-viewport::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: var(--print-line); /* == PRINT_LINE_FRAC (printer.js) */
  bottom: 0;
  z-index: 3;
  pointer-events: none;
  background: linear-gradient(180deg, #2e3238, #212429 18%, #1c1f24);
  box-shadow:
    0 -1px 0 rgba(255, 255, 255, 0.10),   /* machined lip highlight */
    0 -2px 4px rgba(0, 0, 0, 0.55),        /* slot shadow above the lip */
    inset 0 1px 0 rgba(0, 0, 0, 0.6);
}

/* Scrollback FOLLOW affordance — shown only while the roll view is detached. */
.platen .paper-viewport .follow-btn {
  position: absolute;
  left: 50%;
  bottom: 12px;
  transform: translateX(-50%);
  z-index: 6;
  border: 1px solid var(--case-deep);
  background: linear-gradient(180deg, var(--case-hi), var(--case-dark));
  color: var(--ink);
  border-radius: 999px;
  padding: 6px 14px;
  cursor: pointer;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.4);
}
.platen .paper-viewport .follow-btn .label { font-size: 10px; }
.platen .paper-viewport .follow-btn:active {
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.35);
}
.platen .paper-viewport .follow-btn.is-hidden { display: none; }

.platen .paper-viewport .paper-reseat {
  position: absolute;
  left: 50%;
  top: 12px;
  transform: translateX(-50%);
  z-index: 6;
  border: 1px solid var(--case-deep);
  background: linear-gradient(180deg, var(--case-hi), var(--case-dark));
  color: var(--ink);
  border-radius: 999px;
  padding: 5px 12px;
  cursor: pointer;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.4);
}
.platen .paper-viewport .paper-reseat b {
  display: block;
  font-size: 10px;
  line-height: 1;
}
.platen .paper-viewport .paper-reseat span {
  display: block;
  margin-top: 1px;
  font-size: 8px;
  line-height: 1;
}
.platen .paper-viewport .paper-reseat:active {
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.35);
}
.platen .paper-viewport .paper-reseat.is-hidden { display: none; }

/* The carriage overlay: a fixed horizontal print line with the sliding head.
   Positioned at PRINT_LINE_FRAC (84%) of the window and vertically centred on
   the line, ABOVE the paper. It never moves vertically — the paper moves past
   it. */
.platen .carriage-overlay {
  position: absolute;
  left: 0;
  right: 0;
  top: var(--print-line); /* == PRINT_LINE_FRAC (printer.js) */
  height: 40px;
  transform: translateY(-50%);
  z-index: 4;
  display: flex;
  align-items: center;
  padding: 0 5px;
  pointer-events: none;
}
/* The chrome guide rod, centred on the print line (web/style.css styles the
   rod's surface; here we just place it vertically across the overlay). */
.platen .carriage-overlay .rail-line {
  top: 50%;
  transform: translateY(-50%);
}
/* Active glow while the head is busy/firing (replaces web/style.css's
   .carriage-rail.is-active, whose class no longer exists on this element). */
.platen .carriage-overlay.is-active .rail-line {
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.12),
              inset 0 1px 1px rgba(0, 0, 0, 0.6),
              0 0 8px var(--seal-glow);
}

/* Legacy #paper-empty placeholder: permanently hidden by printer.js — the
   no-paper initial state IS the empty state (kept display:none defensively in
   case the is-hidden class is ever lost). */
.platen .paper-viewport .paper-empty { display: none; }

/* =====================================================================
   走纸 PLATEN KNOB — analog hand paper-feed.
   A knurled roller knob on the RIGHT platen edge (the physical dot-matrix
   knob lives on the right end of the platen roller). Two stacked ▲/▼
   detents roll the paper up (feed / margin gap) or back (overprint). It
   drives the shared LF accumulator in C (vm86_printer_knob), NOT the BIOS,
   so head.y, the viewport anchor, and every later fire/page follow it.
   Sits at the print line (z5: above the paper + carriage, below follow).
   ===================================================================== */
.platen .paper-viewport .platen-knob {
  position: absolute;
  right: 7px;
  top: var(--print-line);   /* == PRINT_LINE_FRAC — the roller axis */
  transform: translateY(-50%);
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 34px;
  padding: 4px 3px 3px;
  border-radius: 9px;
  background: linear-gradient(90deg, #24282e, #3b4048 46%, #24282e);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12),
              0 2px 6px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 0, 0, 0.45);
}
.platen .paper-viewport .platen-knob .knob-btn {
  width: 28px;
  height: 21px;
  border: 1px solid rgba(0, 0, 0, 0.55);
  border-radius: 5px;
  background: linear-gradient(180deg, #4b515b, #333841);
  color: #d8dde2;
  font-size: 12px;
  line-height: 1;
  padding: 0;
  cursor: pointer;
  touch-action: none;       /* pointer hold-to-repeat, no scroll/gesture hijack */
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18);
}
.platen .paper-viewport .platen-knob .knob-btn:not(:disabled):active {
  background: linear-gradient(180deg, #2f343c, #464c56);
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.5);
}
.platen .paper-viewport .platen-knob .knob-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
/* the knurled grip barrel between the detents — the roller's ridged surface */
.platen .paper-viewport .platen-knob .knob-grip {
  width: 28px;
  height: 11px;
  margin: 3px 0;
  border-radius: 3px;
  background: repeating-linear-gradient(90deg, #20242a 0 2px, #4b525c 2px 4px);
  box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.65),
              inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
.platen .paper-viewport .platen-knob .knob-label {
  margin-top: 3px;
  border: 0;
  background: transparent;
  padding: 1px 0 0;
  width: 28px;
  font-family: inherit;
  font-size: 8px;
  letter-spacing: 2px;
  line-height: 1;
  text-align: center;
  color: #b0b8c1;
  cursor: pointer;
  touch-action: none;
  user-select: none;
}
.platen .paper-viewport .platen-knob .knob-label:not(:disabled):active {
  color: #f1f4f6;
}
.platen .paper-viewport .platen-knob .knob-label:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* ---- 软盘文件 popup --------------------------------------------------- */
/* Shares the help dialog's machine-case modal chrome (.help-overlay /
   .help-dialog, driven by ModalDialog in ui/help.js); opened by the
   管理软盘文件 button in the bottom control bar. The dialog is narrower than
   the help dialog (it's a file list, not prose) and its body is a flex column
   so the listing scrolls inside the dialog while toolbar/footer stay put. */
.disk-dialog { width: min(560px, 100%); }
.disk-dialog-body { display: flex; flex-direction: column; gap: 6px; }
.disk-toolbar { display: flex; align-items: center; gap: 6px; }
.disk-toolbar .mini-led { margin-left: auto; }
.disk-path { font-size: 12px; opacity: 0.8; padding: 0 2px; user-select: none; }
.disk-path .disk-crumb { cursor: pointer; text-decoration: underline; }
.disk-list { flex: 1 1 auto; min-height: 120px; max-height: 46vh; overflow-y: auto;
             display: flex; flex-direction: column; }
.disk-row { display: grid; grid-template-columns: 1fr auto auto; gap: 8px; align-items: center;
            padding: 2px 4px; font-size: 12px; }
.disk-row:hover { background: rgba(255, 255, 255, 0.06); }
.disk-row .disk-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.disk-row .disk-name.is-dir { cursor: pointer; font-weight: 600; }
.disk-row .disk-size { opacity: 0.7; font-variant-numeric: tabular-nums; }
.disk-row .disk-acts { display: flex; gap: 4px; visibility: hidden; }
.disk-row:hover .disk-acts { visibility: visible; }
.disk-act { cursor: pointer; border: none; background: none; color: inherit;
            font-size: 11px; opacity: 0.8; padding: 0 2px; }
.disk-act:hover { opacity: 1; text-decoration: underline; }
.disk-act:disabled { opacity: 0.3; cursor: default; text-decoration: none; }
.disk-foot { font-size: 11px; opacity: 0.7; }
.disk-note { font-size: 12px; padding: 4px; }
.disk-hint { font-size: 10px; opacity: 0.55; padding: 0 2px; line-height: 1.3; }
.disk-rename-input { font: inherit; width: 100%; }
