/* saturnus web UI. The calculator is the page; the controls sit in a side
   panel (wide screens) or a bar with a drop-down sheet (narrow screens).
   The drawn calculator keeps its own colours in both themes; only the
   chrome around it follows the colour scheme.

   Every colour, size, radius, shadow and duration the page uses is a token
   below (iteration 26); the rules after them only combine tokens. */

/* ---------------------------------------------------------------- tokens */

:root {
  color-scheme: light dark;

  /* Colour: a warm neutral desk, calm panels, one teal accent. */
  --desk: #dad7ce;
  --panel: #f1efe9;
  --paper: #fbfaf6;
  --ink: #23221e;
  --muted: #6c6962;
  --rule: #cbc7bb;
  --rule-soft: #e0ddd4;
  --control: #ffffff;
  --control-border: #b9b5a8;
  /* A control that cannot act: its text and icon about 4:1 on its own
     background (3.6:1 on the panel), its border fainter; enabled text
     is over 15:1. Not a blanket opacity, which fell under 3:1. */
  --ink-disabled: #807d75;
  --border-disabled: #d6d2c7;
  --hover: #e8e5dc;
  --accent: #2a7f82;
  --accent-ink: #ffffff;
  --accent-soft: #d5e8e6;
  --error: #b2462a;
  --error-soft: #f6e4dd;
  /* Text on a solid error-coloured button (confirm.js): 5.5:1. */
  --error-ink: #ffffff;
  /* A calculator object shown as text: tinted and sunk, like a display. */
  --display: #dde1d3;
  --display-ink: #23271f;
  --backdrop: rgba(20, 20, 19, 0.42);
  /* RPL in the palette's editor: one hue per kind of token, quiet enough
     to read a program by. */
  --syn-delim: #2a7f82;
  --syn-num: #8a5300;
  --syn-str: #4c7425;
  --syn-kw: #7b3f9e;
  --syn-cmd: #1d5c8c;
  --syn-var: #a2401d;
  --syn-com: #6c6962;
  --syn-match: #f2dc8c;

  /* Type: one humanist face for the chrome, a monospace for what the
     calculator says. Sizes step by one point. */
  --font: "Helvetica Neue", Helvetica, Arial, "Liberation Sans", sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  --fs-xs: 11px;
  --fs-sm: 12px;
  --fs-md: 13px;
  --fs-base: 14px;
  --fs-lg: 16px;
  --fs-xl: 18px;
  --fs-2xl: 22px;
  --lh: 1.45;
  --lh-tight: 1.3;
  --track-caps: 0.06em;

  /* Spacing rhythm, in 4 px steps. */
  --s1: 4px;
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 24px;
  --s6: 32px;
  --gutter: 16px;

  /* Radii: controls, cards and sheets, dialogs. */
  --r-sm: 4px;
  --r-md: 7px;
  --r-lg: 12px;
  --r-xl: 16px;
  --r-pill: 999px;

  /* Elevation: flat controls (a border only), raised sheets and popovers,
     floating dialogs. */
  --shadow-1: 0 1px 2px rgba(30, 26, 16, 0.08);
  --shadow-2: 0 8px 24px rgba(30, 26, 16, 0.18), 0 1px 3px rgba(30, 26, 16, 0.08);
  --shadow-3: 0 24px 64px rgba(30, 26, 16, 0.32), 0 2px 8px rgba(30, 26, 16, 0.12);
  --inset: inset 0 1px 3px rgba(0, 0, 0, 0.22);

  /* Motion: short and purposeful; zero under prefers-reduced-motion. */
  --t-fast: 80ms;
  --t: 160ms;
  --ease: cubic-bezier(0.2, 0, 0, 1);

  /* Targets: controls are 32 px tall with a mouse, 44 px with a finger;
     list rows 26 px and 40 px. */
  --tap: 32px;
  --row: 26px;

  /* The device's safe areas (a notch, the home indicator). */
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);

  /* Layout: the side panel's and the memory view's widths (the page sets
     them from what was dragged, app.js, within the minimums there), the
     top bar's height on narrow screens. */
  --panel-w: 252px;
  --layer-w: clamp(400px, 42vw, 640px);
  --bar-h: 48px;
  --bar-top: calc(var(--bar-h) + var(--safe-t));
  --palette-w: min(960px, 100vw - 2 * var(--gutter));
  --palette-h: min(620px, 100vh - 2 * 48px);
}

/* The dark tokens: when the page is set to Dark (`data-theme`,
   web/theme.js), and with the device's dark scheme unless it is set to
   Light. The two sets are the same, kept so by web/test/theme.test.mjs:
   CSS cannot apply one rule under either condition. */
:root[data-theme="dark"] {
  color-scheme: dark;
  --desk: #1c1c1b;
  --panel: #272724;
  --paper: #1e1e1c;
  --ink: #e8e5dd;
  --muted: #a19e95;
  --rule: #3f3e39;
  --rule-soft: #33322e;
  --control: #31312d;
  --control-border: #55534c;
  --ink-disabled: #8f8c84;
  --border-disabled: #43423d;
  --hover: #30302c;
  --accent: #5fbfba;
  --accent-ink: #10201f;
  --accent-soft: #233f3d;
  --error: #e58b6a;
  --error-soft: #3d2a23;
  --error-ink: #1c120d;
  --display: #2b3029;
  --display-ink: #dfe5d6;
  --backdrop: rgba(0, 0, 0, 0.6);
  --syn-delim: #5fbfba;
  --syn-num: #e2b26a;
  --syn-str: #a3cf7c;
  --syn-kw: #cf9ce8;
  --syn-cmd: #79b8e8;
  --syn-var: #ee9a76;
  --syn-com: #a19e95;
  --syn-match: #5a4d1c;
  --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.3);
  --shadow-2: 0 8px 24px rgba(0, 0, 0, 0.45), 0 1px 3px rgba(0, 0, 0, 0.3);
  --shadow-3: 0 24px 64px rgba(0, 0, 0, 0.6), 0 2px 8px rgba(0, 0, 0, 0.35);
  --inset: inset 0 1px 3px rgba(0, 0, 0, 0.45);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --desk: #1c1c1b;
    --panel: #272724;
    --paper: #1e1e1c;
    --ink: #e8e5dd;
    --muted: #a19e95;
    --rule: #3f3e39;
    --rule-soft: #33322e;
    --control: #31312d;
    --control-border: #55534c;
    --ink-disabled: #8f8c84;
    --border-disabled: #43423d;
    --hover: #30302c;
    --accent: #5fbfba;
    --accent-ink: #10201f;
    --accent-soft: #233f3d;
    --error: #e58b6a;
    --error-soft: #3d2a23;
    --error-ink: #1c120d;
    --display: #2b3029;
    --display-ink: #dfe5d6;
    --backdrop: rgba(0, 0, 0, 0.6);
    --syn-delim: #5fbfba;
    --syn-num: #e2b26a;
    --syn-str: #a3cf7c;
    --syn-kw: #cf9ce8;
    --syn-cmd: #79b8e8;
    --syn-var: #ee9a76;
    --syn-com: #a19e95;
    --syn-match: #5a4d1c;
    --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.3);
    --shadow-2: 0 8px 24px rgba(0, 0, 0, 0.45), 0 1px 3px rgba(0, 0, 0, 0.3);
    --shadow-3: 0 24px 64px rgba(0, 0, 0, 0.6), 0 2px 8px rgba(0, 0, 0, 0.35);
    --inset: inset 0 1px 3px rgba(0, 0, 0, 0.45);
  }
}

/* Set to Light on a dark device: the light tokens above, light controls. */
:root[data-theme="light"] {
  color-scheme: light;
}

@media (pointer: coarse) {
  :root {
    --tap: 44px;
    --row: 40px;
  }
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --t-fast: 0ms;
    --t: 0ms;
  }
}

/* ------------------------------------------------------------------ base */

* { box-sizing: border-box; }
[hidden] { display: none !important; }
/* Out of sight, still read by screen readers (a live region that stays). */
.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

html, body { height: 100%; }
body {
  margin: 0;
  background: var(--desk);
  color: var(--ink);
  font: var(--fs-base)/var(--lh) var(--font);
  -webkit-font-smoothing: antialiased;
  display: grid;
  grid-template-columns: var(--panel-w) 1fr;
  grid-template-rows: 100%;
  overflow: hidden;
}
body.panel-hidden { grid-template-columns: 0 1fr; }

/* The icon set: one inline SVG sprite in index.html, drawn with strokes
   in the current colour. */
.ic {
  width: 20px;
  height: 20px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
  vertical-align: -5px;
}
.ic-sm { width: 16px; height: 16px; vertical-align: -3px; }

/* Controls: flat, bordered, one height. */
button, select, input:not([type=checkbox]):not([type=file]), summary {
  font: inherit;
  color: var(--ink);
}
button {
  background: var(--control);
  border: 1px solid var(--control-border);
  border-radius: var(--r-md);
  min-height: var(--tap);
  padding: 0 var(--s3);
  line-height: var(--lh-tight);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  transition: background-color var(--t-fast), border-color var(--t-fast), color var(--t-fast);
}
button:hover:not(:disabled, [aria-disabled="true"]) { border-color: var(--muted); background: var(--panel); }
button:active:not(:disabled, [aria-disabled="true"]) { background: var(--hover); }
/* Disabled: the normal background, fainter text, icon and border (the
   tokens above); a primary button gives up its accent. An Edit that
   stays focusable (aria-disabled, to say why) looks the same. An icon
   button's own colour (below) must not win over it. */
button:is(:disabled, [aria-disabled="true"]) { color: var(--ink-disabled); border-color: var(--border-disabled); cursor: default; }
button.icon:is(:disabled, [aria-disabled="true"]) { border-color: transparent; color: var(--ink-disabled); }
button.primary:is(:disabled, [aria-disabled="true"]) { background: var(--control); border-color: var(--border-disabled); color: var(--ink-disabled); }
button.link:is(:disabled, [aria-disabled="true"]) { color: var(--ink-disabled); }
:is(button, select, input, summary, a, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
button.icon {
  padding: 0;
  flex: none;
  width: var(--tap);
  height: var(--tap);
  min-height: 0;
  background: transparent;
  border-color: transparent;
  color: var(--ink);
}
button.icon:hover:not(:disabled, [aria-disabled="true"]) { background: var(--hover); border-color: transparent; }
button.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); font-weight: 600; }
button.primary:hover:not(:disabled) { background: color-mix(in srgb, var(--accent) 88%, var(--ink)); border-color: transparent; color: var(--accent-ink); }
button.link {
  border: 0;
  background: none;
  min-height: 0;
  padding: 0;
  color: var(--accent);
  font-size: var(--fs-md);
  text-decoration: underline;
  text-underline-offset: 2px;
  justify-content: flex-start;
}
button.link:hover:not(:disabled) { background: none; color: var(--ink); }

select {
  padding: 0 var(--s2);
  min-height: var(--tap);
  background: var(--control);
  border: 1px solid var(--control-border);
  border-radius: var(--r-md);
}
input[type=search], input[type=text] { -webkit-appearance: none; appearance: none; }
input[type=search]::-webkit-search-cancel-button { -webkit-appearance: none; }
.check { display: inline-flex; align-items: center; gap: var(--s2); min-height: var(--tap); }
.check input { margin: 0; width: 16px; height: 16px; accent-color: var(--accent); }

.segmented { display: inline-flex; border: 1px solid var(--control-border); border-radius: var(--r-md); overflow: hidden; align-self: start; }
.segmented button { border: 0; border-radius: 0; min-width: 48px; padding: 0 var(--s2); background: var(--control); }
.segmented button + button { border-left: 1px solid var(--control-border); }
.segmented button[aria-checked=true] { background: var(--accent); color: var(--accent-ink); font-weight: 600; }
.segmented button:focus-visible { outline-offset: -2px; }

kbd {
  font: var(--fs-sm)/1.4 var(--mono);
  padding: 0 var(--s1);
  border: 1px solid var(--rule);
  border-bottom-width: 2px;
  border-radius: var(--r-sm);
  background: var(--control);
  color: var(--ink);
  white-space: nowrap;
}
/* A keyboard hint means nothing to a finger. */
@media (hover: none) and (pointer: coarse) {
  .kbd-hint { display: none !important; }
}

/* Section labels: small caps, tracked, muted. */
.caps, .list th, .flag-topic h3, .entry h4, .shortcuts-body h3, .about-body h3 {
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--muted);
}
.muted { color: var(--muted); }
.num, .tab-count, .list .num, .lamp-grid .cell { font-variant-numeric: tabular-nums; }

/* -------------------------------------------------------------- side panel */

.panel {
  grid-column: 1;
  background: var(--panel);
  border-right: 1px solid var(--rule);
  padding: var(--s3) var(--gutter) var(--s3) calc(var(--gutter) + var(--safe-l));
  overflow: hidden auto;
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}
body.panel-hidden .panel { display: none; }

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  color: inherit;
  text-decoration: none;
  font-size: 19px;
  font-weight: 600;
  letter-spacing: 0.01em;
}
.brand .logo { display: block; }
.panel-head { display: flex; align-items: center; justify-content: space-between; margin-right: calc(-1 * var(--s2)); }
.tagline { margin: -6px 0 0; color: var(--muted); font-size: var(--fs-md); }

.group { display: flex; flex-direction: column; gap: var(--s2); }
/* Buttons with an icon and a label, one per line at the panel's width. */
.group.actions button { justify-content: start; }
.group + .group { border-top: 1px solid var(--rule-soft); padding-top: var(--s3); }

/* The side panel's headings (Model, ROM, Speed, Display, Screen images,
   Theme): body size and ink, set apart by weight from the controls. What
   a .field holds keeps the small, muted, regular text it had. */
.field, .field-label { display: flex; flex-direction: column; gap: var(--s1); font-size: var(--fs-base); font-weight: 600; color: var(--ink); }
.field > * { font-size: var(--fs-sm); font-weight: 400; color: var(--muted); }
.field select, .field input { font-size: var(--fs-base); color: var(--ink); }
.field input[type=file] { max-width: 100%; }
.hint { margin: 0; font-size: var(--fs-sm); color: var(--muted); line-height: var(--lh); }
.contrast-row { display: flex; flex-direction: column; align-items: start; gap: var(--s2); margin-top: var(--s2); }
.contrast-row .segmented button { padding: 0 var(--s3); }
.screen-row .segmented button { padding: 0 var(--s2); white-space: nowrap; }
.screen-actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(9.5em, 1fr)); gap: var(--s2); align-self: stretch; }
.screen-actions button { justify-content: start; white-space: nowrap; }

.help { font-size: var(--fs-md); color: var(--muted); }
.help summary { cursor: pointer; color: var(--ink); min-height: var(--tap); display: flex; align-items: center; gap: var(--s1); list-style: none; margin-left: -3px; }
.help summary::-webkit-details-marker { display: none; }
.help summary .ic { width: 16px; height: 16px; color: var(--muted); transition: transform var(--t-fast); }
.help[open] summary .ic { transform: rotate(90deg); }
.help dl { margin: var(--s2) 0; display: grid; grid-template-columns: auto 1fr; gap: 5px 10px; }
.help dt { white-space: nowrap; }
.help dd { margin: 0; }
.help p { margin: var(--s2) 0 0; }

.about-link, .panel-link { margin: 0; display: flex; align-items: center; gap: var(--s2); }
.about-link button.link, .panel-link button.link { min-height: var(--tap); }

.status {
  margin: auto 0 0;
  padding-top: var(--s3);
  font: var(--fs-sm)/var(--lh) var(--mono);
  color: var(--muted);
  overflow-wrap: anywhere;
}
.status.error { color: var(--error); }

button.floating {
  position: fixed;
  top: calc(10px + var(--safe-t));
  left: calc(10px + var(--safe-l));
  z-index: 3;
  background: var(--panel);
  border-color: var(--rule);
  box-shadow: var(--shadow-1);
}

/* ----------------------------------------------------------------- top bar */

.bar {
  display: none;
  grid-column: 1 / -1;
  height: var(--bar-top);
  align-items: center;
  gap: var(--s1);
  padding: var(--safe-t) calc(var(--s1) + var(--safe-r)) 0 calc(var(--gutter) + var(--safe-l));
  background: var(--panel);
  border-bottom: 1px solid var(--rule);
  position: relative;
  z-index: 5;
}
/* The name gives way to the buttons: on a phone too narrow for it beside
   all of them (the Edit button shown), it wraps onto a second line that
   is clipped, and the logo stays. */
.bar .brand { font-size: 17px; margin-right: auto; min-width: 0; height: 32px; line-height: 32px; flex-wrap: wrap; overflow: hidden; }
.bar .brand .logo { margin-block: 4px; }
.bar button.icon[aria-expanded=true], #bar-palette[aria-expanded=true] { background: var(--accent); color: var(--accent-ink); }

/* ------------------------------------------------------------------- stage */

.stage {
  grid-column: 2;
  position: relative;
  min-width: 0;
  min-height: 0;
  padding: var(--s3) calc(var(--gutter) + var(--safe-r)) calc(var(--s3) + var(--safe-b)) var(--gutter);
  display: grid;
  place-items: center;
  overflow: auto;
}
/* Fullscreen (app.js): the browser's, or the stage laid over the page
   where it has none (fs-page). The calculator edge to edge (sat-calculator's
   setEdge) inside the safe areas: whole on the desk with a mouse, cropped
   to its face on the case's colour (--case) on a finger's screen (fs-crop);
   nothing on it scrolls, so a finger's drag is the key's. */
.stage.fs {
  background: var(--desk);
  padding: calc(var(--s3) + var(--safe-t)) calc(var(--s3) + var(--safe-r)) calc(var(--s3) + var(--safe-b)) calc(var(--s3) + var(--safe-l));
  overflow: hidden;
}
.stage.fs.fs-crop {
  background: var(--case, #141413);
  padding: var(--safe-t) var(--safe-r) var(--safe-b) var(--safe-l);
}
.stage.fs-page { position: fixed; inset: 0; z-index: 6; }
.stage.fs .skin { touch-action: none; }
sat-calculator.crop .skin g.case,
sat-calculator.crop .skin image.logo.off-face { display: none; }
/* The skin, or the face's part in view (web/edge.js), placed by the
   component's margins; cropped, nothing of it drawn outside. */
sat-calculator.edge .skin { align-self: start; justify-self: start; }
sat-calculator.crop .skin svg { overflow: hidden; }
/* The close button and, opposite it, the palette's search icon: small,
   in the corners, readable on any case; the face leaves them a band
   along the top, or room at the sides (web/edge.js). */
button.icon.fs-tool {
  display: none;
  position: absolute;
  top: calc(4px + var(--safe-t));
  z-index: 2;
  border-radius: 50%;
  background: rgba(20, 20, 19, 0.72);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.4), 0 1px 4px rgba(0, 0, 0, 0.5);
  color: #fff;
}
.stage.fs button.icon.fs-tool { display: inline-flex; }
button.icon.fs-tool:hover:not(:disabled) { background: rgba(20, 20, 19, 0.88); }
#leave-fullscreen { right: calc(4px + var(--safe-r)); }
#fs-palette { left: calc(4px + var(--safe-l)); }

/* The tools over the calculator (wide screens; the bar has them below 760 px). */
/* One small toolbar: Edit, Search and the Memory view's toggle, icon
   buttons of one size on one panel; Search's key shows beside it on
   hover and keyboard focus only. */
.stage-tools {
  position: absolute;
  top: 10px;
  right: calc(10px + var(--safe-r));
  z-index: 2;
  display: flex;
  gap: 2px;
  padding: 2px;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-1);
}
.stage.fs .stage-tools { display: none; }
.stage-tools button.tool { position: relative; width: max(36px, var(--tap)); height: max(36px, var(--tap)); border: 0; background: none; }
.stage-tools button.tool:hover:not([aria-disabled=true]) { background: var(--hover); }
.stage-tools .tool-key {
  position: absolute;
  top: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  font-size: var(--fs-sm);
  padding: 1px 5px;
  background: var(--panel);
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--t-fast);
}
#palette-show:hover .tool-key, #palette-show:focus-visible .tool-key { opacity: 1; }
#layer-show[aria-expanded=true] { display: none; }
body.rom-drop .stage { outline: 3px dashed var(--accent); outline-offset: -8px; }

/* The drawn calculator. */
.skin {
  position: relative;
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
}
.skin svg { display: block; width: 100%; height: auto; overflow: visible; }
.skin canvas {
  position: absolute;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
}
/* The glass over the LCD, sunk into its bezel: the lip's shadow falls in
   from the top left, a faint light catches the lower right edge, and a
   little of the room reflects at the top. Sizes in skin units (--u, set
   by the component). */
.skin .glass {
  position: absolute;
  pointer-events: none;
  border-radius: calc(var(--u, 1px) * 5);
  box-shadow:
    inset calc(var(--u, 1px) * 1.5) calc(var(--u, 1px) * 2.5) calc(var(--u, 1px) * 7) rgba(0, 0, 0, 0.36),
    inset calc(var(--u, 1px) * -1) calc(var(--u, 1px) * -1) calc(var(--u, 1px) * 2) rgba(255, 255, 255, 0.16);
  background: linear-gradient(172deg, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0) 38%);
}
/* No ROM for the model shown: over the LCD, in the page's style. */
.skin .no-rom {
  position: absolute;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  padding: var(--s2) var(--s3);
  text-align: center;
  font: var(--fs-md)/var(--lh-tight) var(--font);
  color: var(--ink);
  background: color-mix(in srgb, var(--panel) 90%, transparent);
  border-radius: var(--r-sm);
  overflow: hidden;
}
.skin .no-rom p { margin: 0; }
.skin .no-rom .no-rom-get { font-size: var(--fs-sm); }
.skin .no-rom a, .roms .rom-file-name a { color: var(--accent); text-underline-offset: 2px; }
.skin .no-rom-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--s1) var(--s2); }
.skin .no-rom button { min-height: 0; padding: 5px 12px; font-size: var(--fs-md); }
/* Smaller displays (sat-calculator.js `fitNoRom`): smaller print, then the
   first sentence alone, then "No ROM" beside the buttons. */
.skin .no-rom.compact { gap: var(--s1); padding: var(--s1) var(--s2); font-size: var(--fs-sm); }
.skin .no-rom.compact .no-rom-get { font-size: var(--fs-xs); }
.skin .no-rom.compact button { padding: 2px 10px; font-size: var(--fs-sm); }
.skin .no-rom.short .no-rom-get { display: none; }
.skin .no-rom.short .no-rom-text { cursor: pointer; text-decoration: underline dotted; text-underline-offset: 3px; }
.skin .no-rom.row { flex-direction: row; padding: 2px var(--s2); white-space: nowrap; }
.skin .no-rom.row .no-rom-actions { flex-wrap: nowrap; gap: var(--s1); }
.skin .no-rom.row button { padding: 1px 8px; font-size: var(--fs-xs); }
.skin .no-rom.pulse { animation: no-rom-pulse calc(var(--t) * 4) ease-out; }
@keyframes no-rom-pulse {
  0% { box-shadow: inset 0 0 0 3px var(--accent); background: color-mix(in srgb, var(--accent) 30%, var(--panel)); }
  100% { box-shadow: inset 0 0 0 0 var(--accent); }
}
.skin .skey { cursor: pointer; }
.skin .skey:focus { outline: none; }
.skin .print { pointer-events: none; }
.skin text {
  font-family: var(--font);
  font-weight: 600;
}
@media (prefers-reduced-motion: no-preference) {
  .skin .cap { transition: transform 40ms linear; }
}
/* Ctrl or Option/Alt held (sat-calculator.js `showGlow`): the shift
   labels a click with it reaches light up, every other label fades. */
.skin svg.glow-left .shift-left { filter: url(#glow-left); }
.skin svg.glow-right .shift-right { filter: url(#glow-right); }
.skin svg.glow-left :is(.keys, .print) text:not(.shift-left),
.skin svg.glow-right :is(.keys, .print) text:not(.shift-right) { opacity: 0.4; }
@media (prefers-reduced-motion: no-preference) {
  .skin :is(.keys, .print) text { transition: opacity var(--t) var(--ease); }
}
/* A long send is typing: the last frame stays, dimmed, until it is done. */
sat-calculator.typing #lcd {
  opacity: 0.55;
  cursor: progress;
  transition: opacity var(--t);
}

/* -------------------------------------------------------------- components */

/* The components keep the page's layout: their children are laid out as
   if the elements were not there. */
sat-calculator, sat-controls { display: contents; }

/* -------------------------------------------------------------- dialogs */

/* One look for the About panel and the keyboard shortcuts: a floating
   card with a sticky head; on a phone a full sheet. */
dialog.about, dialog.shortcuts {
  width: min(760px, calc(100vw - 2 * var(--gutter)));
  max-height: calc(100vh - 48px);
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: var(--r-xl);
  background: var(--panel);
  color: var(--ink);
  box-shadow: var(--shadow-3);
  overflow: hidden;
}
dialog.shortcuts { width: min(720px, calc(100vw - 2 * var(--gutter))); }
dialog::backdrop { background: rgba(20, 20, 19, 0.42); background: var(--backdrop); }
/* The question before an action that cannot be undone (confirm.js). */
dialog.confirm {
  width: min(420px, calc(100vw - 2 * var(--gutter)));
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: var(--r-xl);
  background: var(--panel);
  color: var(--ink);
  box-shadow: var(--shadow-3);
}
.confirm-box { padding: var(--s4) var(--gutter) var(--s3); }
.confirm-box h2 { margin: 0 0 var(--s2); font-size: var(--fs-lg); font-weight: 600; line-height: var(--lh-tight); overflow-wrap: anywhere; }
.confirm-box p { margin: 0 0 var(--s4); font-size: var(--fs-base); line-height: var(--lh); color: var(--ink); overflow-wrap: anywhere; }
.confirm-actions { display: flex; justify-content: flex-end; gap: var(--s2); }
button.confirm-danger { background: var(--error); border-color: var(--error); color: var(--error-ink); font-weight: 600; }
button.confirm-danger:hover:not(:disabled) { background: color-mix(in srgb, var(--error) 88%, var(--ink)); border-color: transparent; color: var(--error-ink); }
/* A narrow screen: a sheet at the bottom, the buttons the width of it. */
@media (max-width: 560px) {
  dialog.confirm {
    box-sizing: border-box;
    inset: auto 0 0 0;
    width: 100%;
    max-width: 100%;
    margin: 0;
    border-width: 1px 0 0;
    border-radius: var(--r-xl) var(--r-xl) 0 0;
  }
  .confirm-box { padding-bottom: calc(var(--s3) + var(--safe-b)); }
  .confirm-actions > button { flex: 1; }
}
@media (prefers-reduced-motion: no-preference) {
  dialog[open] { animation: dialog-in var(--t) var(--ease); }
  dialog[open]::backdrop { animation: fade-in var(--t) var(--ease); }
  @keyframes dialog-in { from { opacity: 0; transform: translateY(8px) scale(0.99); } }
  @keyframes fade-in { from { opacity: 0; } }
}
.dialog-head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s3) var(--s3) var(--s3) var(--gutter);
  border-bottom: 1px solid var(--rule);
  background: var(--panel);
}
.dialog-head h2 { margin: 0 auto 0 0; font-size: var(--fs-xl); font-weight: 600; letter-spacing: -0.01em; line-height: var(--lh-tight); }
.dialog-foot { display: flex; align-items: center; gap: var(--s2); padding: var(--s3) var(--gutter); border-top: 1px solid var(--rule); }

/* ---------------------------------------------------------------- about */

.about-box { display: flex; flex-direction: column; max-height: calc(100vh - 50px); }
.about-head .logo { display: block; }
.about-title { display: flex; flex-direction: column; margin-right: auto; }
.about-title h2 { margin: 0; }
.about-sub { font-size: var(--fs-sm); color: var(--muted); }
.about-body { overflow: auto; padding: var(--s2) var(--gutter) var(--s5); font-size: var(--fs-md); overflow-wrap: anywhere; }
.about-body h3 { margin: var(--s5) 0 var(--s2); padding-bottom: var(--s1); border-bottom: 1px solid var(--rule-soft); }
.about-body h3 .muted { font-weight: 400; letter-spacing: 0; text-transform: none; }
/* The literature list, folded: most of the dialog's length. */
.about-literature > summary { list-style: none; cursor: pointer; }
.about-literature > summary::-webkit-details-marker { display: none; }
.about-literature > summary h3 { display: flex; align-items: center; gap: var(--s1); }
.about-literature > summary .ic { width: 16px; height: 16px; color: var(--muted); transition: transform var(--t-fast); }
.about-literature[open] > summary .ic { transform: rotate(90deg); }
.about-body ul, .about-body ol { margin: 0; padding-left: 22px; }
.about-body li { margin: 0 0 var(--s2); line-height: var(--lh); }
.about-body li::marker { color: var(--muted); font-size: var(--fs-sm); }
.about-body a { color: var(--accent); text-underline-offset: 2px; }
.about-body .hint { margin: 0 0 var(--s2); max-width: 64ch; }
.about-version { margin: 0 0 var(--s2); font: var(--fs-sm)/1.4 var(--mono); color: var(--muted); overflow-wrap: anywhere; }
.about-statement { max-width: 64ch; font-size: var(--fs-base); }
.about-statement p { margin: var(--s2) 0; line-height: 1.5; }
.about-statement p:first-child { margin-top: var(--s3); }
.about-where, .about-used { color: var(--muted); font-size: var(--fs-sm); }
.about-where code { font-family: var(--mono); font-size: var(--fs-xs); }
.about-sources li { padding-left: 2px; }

/* ----------------------------------------------------------- memory view */

/* A tool panel beside the calculator: the page's own chrome, calm, with
   the monospace face only where a calculator object is shown. Wide
   windows: a third column. Below 1000px: over the calculator, with a way
   back. */

body.layer-open { grid-template-columns: var(--panel-w) minmax(300px, 1fr) var(--layer-w); }
body.layer-open.panel-hidden { grid-template-columns: 0 minmax(300px, 1fr) var(--layer-w); }

/* The handles on the inner edges of the side panel and of the memory
   view: drag (or arrow keys) to resize, double-click for the default. */
.resize, .resize-tree {
  position: fixed;
  top: 0;
  bottom: 0;
  z-index: 4;
  width: 9px;
  cursor: col-resize;
  touch-action: none;
  background: transparent;
}
.resize::after, .resize-tree::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 4px;
  width: 1px;
  background: transparent;
  transition: background var(--t-fast);
}
.resize:hover::after, .resize:focus-visible::after, .resize.dragging::after,
.resize-tree:hover::after, .resize-tree:focus-visible::after, .resize-tree.dragging::after { width: 3px; left: 3px; background: var(--accent); }
.resize:focus-visible, .resize-tree:focus-visible { outline: none; }
.resize-panel { left: calc(var(--panel-w) - 5px); }
.resize-layer { right: calc(var(--layer-w) - 5px); }
body.panel-hidden .resize-panel, body:not(.layer-open) .resize-layer { display: none; }
body.resizing { cursor: col-resize; user-select: none; -webkit-user-select: none; }
body.resizing * { cursor: col-resize !important; }

sat-explorer {
  grid-column: 3;
  grid-row: 1;
  display: block;
  min-width: 0;
  min-height: 0;
  background: var(--panel);
  border-left: 1px solid var(--rule);
}

.layer {
  height: 100%;
  display: flex;
  flex-direction: column;
  container-type: inline-size;
  font-size: var(--fs-md);
  padding-bottom: var(--safe-b);
}
.layer h2, .layer h3, .layer p { margin: 0; }

.layer-head {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2) var(--s2) 0 var(--gutter);
  min-height: calc(var(--tap) + var(--s2));
}
.layer-title { font-size: 17px; font-weight: 600; letter-spacing: -0.01em; }
.layer-model { color: var(--muted); font-size: var(--fs-sm); margin-right: auto; }
.layer-back { display: none; font-size: var(--fs-md); padding-left: var(--s2); }

.layer-tabs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 var(--s4);
  padding: 0 var(--gutter);
  border-bottom: 1px solid var(--rule);
}
.tabs { display: flex; flex: none; gap: var(--s1); margin-right: auto; }
.tabs button {
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  background: none;
  min-height: calc(var(--tap) + 4px);
  padding: 2px var(--s2) 0;
  margin-bottom: -1px;
  color: var(--muted);
  font-size: var(--fs-base);
}
.tabs button:first-child { margin-left: calc(-1 * var(--s2)); }
.tabs button:hover { color: var(--ink); background: none; }
.tabs button[aria-selected=true] { color: var(--ink); font-weight: 600; border-bottom-color: var(--accent); }
.tabs button:focus-visible { outline-offset: -2px; border-radius: var(--r-sm); }
.tab-count { font-weight: 400; color: var(--muted); }
/* Where the keys go, short (its tooltip says it in full); one line that
   never wraps, so a change of the focus does not move the page. */
.layer-keys {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  padding: 1px 8px 1px 6px;
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  font-size: var(--fs-xs);
  color: var(--muted);
  white-space: nowrap;
  cursor: help;
  transition: color var(--t-fast), border-color var(--t-fast);
}
.layer-keys.own { color: var(--accent); border-color: var(--accent); }
.layer-keys .ic { width: 14px; height: 14px; }
/* The way back, while the keys are in the view: plain to see and to
   click, whole; where the tab bar has no room (a phone) it takes a line
   of its own under the tabs. */
.layer-keys.own { cursor: default; flex: none; margin-bottom: var(--s1); }
.layer-keys-back { font-size: var(--fs-xs); margin-left: var(--s1); padding: 0; min-height: 0; white-space: nowrap; }
/* Beside the tabs (a layer wider than a phone's), the chip never wraps
   the row, whatever the font's width: a first click into the view would
   move what it clicked. It takes the room left (no more than its text),
   and the way back ends in "…" if that is too little; its tooltip says
   it whole. */
@container (min-width: 471px) {
  .layer-keys.own { flex: 1 1 0; max-width: max-content; }
  .layer-keys-back { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
}
/* The keys are here: a bar along the layer's edge says so. */
sat-explorer:focus-within { box-shadow: inset 3px 0 0 var(--accent); }

/* Empty and error states: a heading, one or two sentences, the detail in
   the calculator's face; errors carry a rule in the error colour. */
.layer-empty, .pane-error, .preview-hint, .palette-empty { padding: var(--s5) var(--gutter); max-width: 48ch; }
.layer-empty h3, .pane-error h3, .preview-hint h3 { font-size: 15px; font-weight: 600; margin-bottom: var(--s2); line-height: var(--lh-tight); }
.layer-empty p, .pane-error p, .preview-hint p, .palette-empty { color: var(--muted); line-height: 1.5; }
.layer-empty p + p, .pane-error p + p { margin-top: var(--s2); }
.layer .detail, .layer-empty-detail { font: var(--fs-sm)/1.5 var(--mono); color: var(--muted); overflow-wrap: anywhere; }
.layer-empty-detail:not(:empty) { margin-top: var(--s3) !important; }
.pane-error { border-left: 3px solid var(--error); margin: var(--s3) var(--gutter); padding: var(--s1) var(--s3); }
.preview-hint { padding-top: var(--s4); }

.pane { flex: 1; min-height: 0; display: flex; flex-direction: column; }

.find {
  font: inherit;
  color: var(--ink);
  background: var(--control);
  border: 1px solid var(--control-border);
  border-radius: var(--r-md);
  min-height: var(--tap);
  padding: 0 var(--s2);
  min-width: 0;
  width: 17ch;
  transition: border-color var(--t-fast);
}
.find:hover { border-color: var(--muted); }
.find:focus-visible { outline-offset: 0; border-color: var(--accent); }
.find::placeholder { color: var(--muted); }

/* Variables: the directory tree and the variables of one directory, as
   in a two-pane file manager, the selected object below. */
.vars-bar { display: flex; align-items: center; gap: var(--s3); padding: var(--s2) var(--gutter) var(--s1); }
.crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 1px 2px; margin-right: auto; min-width: 0; }
.crumbs .sep { color: var(--muted); }
.crumb { border: 0; background: none; min-height: 0; padding: 2px 5px; font: 600 var(--fs-md)/1.4 var(--mono); border-radius: var(--r-sm); }
.crumb:first-child { margin-left: -5px; }
.crumb:hover { background: var(--hover); }
.crumb[aria-current] { color: var(--ink); }
.crumb:not([aria-current]) { color: var(--muted); font-weight: 400; }
.vars-where { padding: 0 var(--gutter) var(--s2); font-size: var(--fs-sm); color: var(--muted); }
.vars-where .dot, .here-mark::before {
  content: "";
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-right: 6px;
  border-radius: 50%;
  border: 1.5px solid var(--muted);
}
.vars-where.here .dot, .here-mark::before { background: var(--accent); border-color: var(--accent); }
.vars-where button.link { font-size: var(--fs-sm); }

/* The tree's width: its default, or the one dragged on the divider
   (--tree-w), within limits that keep a name on both sides. */
.vars-split {
  --tree-default: max(104px, 30%);
  --tree-col: clamp(80px, var(--tree-w, var(--tree-default)), max(80px, calc(100% - 160px)));
  position: relative;
  flex: 0 0 40%;
  min-height: 150px;
  display: grid;
  grid-template-columns: var(--tree-col) 1fr;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  background: var(--paper);
}
.tree { overflow: auto; padding: var(--s1) 0; border-right: 1px solid var(--rule); }
/* The divider between the tree and the list, as the page's edges. */
.resize-tree { position: absolute; top: 0; bottom: 0; left: calc(var(--tree-col) - 5px); z-index: 1; }
.node {
  display: flex;
  align-items: center;
  gap: 2px;
  min-height: var(--row);
  padding: 2px 8px 2px calc(4px + var(--d) * 14px);
  cursor: default;
  white-space: nowrap;
  font: var(--fs-md)/1.4 var(--mono);
  transition: background-color var(--t-fast);
}
.node:hover, .list tbody tr:hover, .levels li:hover { background: var(--hover); }
.node.shown, .list tbody tr[aria-selected=true], .levels li[aria-selected=true] { background: var(--accent-soft); }
.node:focus-visible, .list tbody tr:focus-visible, .levels li:focus-visible, .flags-scroll:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
.twist { width: 16px; height: 16px; flex: none; color: var(--muted); display: inline-flex; align-items: center; justify-content: center; cursor: pointer; }
.twist .ic { width: 14px; height: 14px; transition: transform var(--t-fast); }
.twist.open .ic { transform: rotate(90deg); }
.twist.leaf { cursor: default; }
.here-mark { margin-left: 8px; font: var(--fs-xs)/1 var(--font); color: var(--muted); }
.here-mark::before { margin-right: 4px; }
/* A narrow phone: the tree keeps only the dot, its tooltip names it. */
@media (max-width: 399px) {
  .tree .here-mark { font-size: 0; margin-left: 6px; }
  .tree .here-mark::before { margin-right: 0; }
}

.list-wrap { overflow: auto; min-width: 0; }
.list { width: 100%; border-collapse: collapse; }
.list th {
  position: sticky;
  top: 0;
  background: var(--paper);
  text-align: left;
  padding: 6px 8px 5px;
  border-bottom: 1px solid var(--rule);
  white-space: nowrap;
}
.list td { padding: 2px 8px; height: var(--row); white-space: nowrap; cursor: default; }
.list .num { text-align: right; }
.list td.num, .list td.sum { font: var(--fs-sm)/1.5 var(--mono); color: var(--muted); }
.list td.type { color: var(--muted); }
.list td.name { max-width: 0; width: 46%; overflow: hidden; text-overflow: ellipsis; }
.obj-name { font-family: var(--mono); font-weight: 600; }
.list .in { color: var(--muted); font-size: var(--fs-sm); }
.kind { display: inline-block; width: 12px; height: 9px; margin-right: 7px; vertical-align: -1px; border-radius: 2px; }
.kind-dir { background: var(--accent); box-shadow: 0 -2px 0 -0.5px var(--accent); border-top-left-radius: 0; position: relative; }
.kind-dir::before { content: ""; position: absolute; left: 0; top: -2px; width: 5px; height: 2px; background: var(--accent); border-radius: 1px 1px 0 0; }
/* A page with its top right corner folded over: a file, not a checkbox. */
.kind-obj { width: 9px; height: 11px; margin-right: 10px; vertical-align: -2px; border-radius: 1px; background: var(--muted); clip-path: polygon(0 0, 55% 0, 100% 45%, 100% 100%, 0 100%); position: relative; }
.kind-obj::before { content: ""; position: absolute; right: 0; top: 0; width: 45%; height: 45%; background: var(--panel); opacity: 0.7; clip-path: polygon(0 0, 100% 100%, 0 100%); }
.list-empty { padding: var(--s4) 10px; color: var(--muted); }

/* The preview of one object. */
.preview { flex: 1; min-height: 0; overflow: auto; }
.preview-head { display: flex; align-items: flex-start; gap: var(--s3); padding: var(--s3) var(--gutter) var(--s2); }
.preview-title { min-width: 0; margin-right: auto; }
.preview-title h3 { font-size: var(--fs-lg); overflow-wrap: anywhere; line-height: var(--lh-tight); }
.preview-meta { color: var(--muted); font-size: var(--fs-sm); margin-top: 3px !important; }
.preview-body { padding: 0 var(--gutter) var(--s4); display: flex; flex-direction: column; gap: var(--s2); }
pre.obj, .obj-list, .obj-dir, .grid-wrap {
  margin: 0;
  background: var(--display);
  color: var(--display-ink);
  border: 0;
  border-radius: var(--r-sm);
  box-shadow: var(--inset);
}
pre.obj {
  padding: 10px 12px;
  font: var(--fs-md)/1.55 var(--mono);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  user-select: text;
  -webkit-user-select: text;
}
.obj-program .line { display: block; padding-left: calc(var(--d) * 2ch); }
.obj-string::before { content: "“"; color: var(--muted); }
.obj-string::after { content: "”"; color: var(--muted); }
.more, .unavailable { color: var(--muted); font-size: var(--fs-sm); line-height: 1.5; }
.unavailable { font-size: var(--fs-md); max-width: 52ch; }
.preview-error { color: var(--error); }
.nibbles summary { cursor: pointer; color: var(--muted); font-size: var(--fs-sm); }
.nibbles pre { margin-top: 6px; font-size: var(--fs-sm); max-height: 160px; overflow: auto; }

.obj-list, .obj-dir { padding: 0; list-style: none; counter-reset: item; }
.obj-list li, .obj-dir li {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 4px 12px;
  counter-increment: item;
}
.obj-list li + li, .obj-dir li + li { border-top: 1px solid color-mix(in srgb, var(--display-ink) 14%, transparent); }
.obj-list li::before { content: counter(item); flex: none; width: 3ch; text-align: right; color: var(--muted); font: var(--fs-xs)/1.6 var(--mono); }
.obj-list .obj, .levels .obj { font: var(--fs-md)/1.5 var(--mono); min-width: 0; overflow-wrap: anywhere; margin-right: auto; }
.obj-dir .obj-name { margin-right: auto; }
.obj-list .muted, .obj-dir .muted { font-size: var(--fs-sm); white-space: nowrap; }
.partial .obj { color: var(--muted); font-style: italic; }

.grid-wrap { overflow: auto; }
.obj-grid { border-collapse: collapse; font: var(--fs-md)/1.5 var(--mono); }
.obj-grid td { padding: 4px 12px; text-align: right; white-space: nowrap; }
.obj-grid tr + tr td { border-top: 1px solid color-mix(in srgb, var(--display-ink) 14%, transparent); }
.obj-grid td + td { border-left: 1px solid color-mix(in srgb, var(--display-ink) 14%, transparent); }

/* Stack: level 1 at the bottom, as on the calculator. */
.levels-wrap {
  flex: 0 1 auto;
  max-height: 46%;
  overflow: auto;
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
}
.levels { margin: 0; padding: var(--s1) 0; list-style: none; }
.levels li {
  display: grid;
  grid-template-columns: 4ch minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  min-height: var(--row);
  padding: 2px var(--gutter) 2px 8px;
  cursor: default;
  transition: background-color var(--t-fast);
}
.levels .level { text-align: right; color: var(--muted); font: var(--fs-sm)/1.6 var(--mono); }
/* Its track's width, not its text's: an auto margin would size it to the
   unwrapped text and push the row past the list's edge. */
.levels .obj { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-right: 0; }
.levels .type { font-size: var(--fs-sm); white-space: nowrap; }

/* Flags: read-only lamps, not switches. */
.flags-bar { display: flex; align-items: center; gap: var(--s2); padding: var(--s2) var(--gutter); border-bottom: 1px solid var(--rule); }
.flags-bar .find { flex: 1; width: auto; max-width: 28ch; }
.flags-only { font-size: var(--fs-md); }
.flags-only[aria-pressed=true] { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.flags-scroll { flex: 1; min-height: 0; overflow: auto; padding: var(--s3) var(--gutter) var(--s5); }
.flags-sum { font-size: var(--fs-md); }
.flags-scroll > .muted { margin-top: 14px; }
.flags-basis { margin-top: 4px !important; color: var(--muted); font-size: var(--fs-sm); line-height: 1.5; max-width: 62ch; }
.flag-topic { margin-top: var(--s4); }
.flag-topic h3 { padding-bottom: 5px; border-bottom: 1px solid var(--rule); }
.flag-rows { margin: 0; padding: 0; list-style: none; }
/* A single flag's row toggles it as a whole (sat-explorer.js). */
.flag.toggles { cursor: pointer; }
.flag.toggles:hover { background: var(--hover); }
.flag {
  display: grid;
  grid-template-columns: 9ch 16px minmax(0, 1fr);
  gap: 0 10px;
  align-items: start;
  padding: 7px 0;
  border-bottom: 1px solid var(--rule-soft);
}
.flag .num { text-align: right; font: var(--fs-sm)/1.6 var(--mono); color: var(--muted); white-space: nowrap; }
.flag.on .num { color: var(--ink); font-weight: 600; }
.lamps { display: grid; grid-template-columns: 10px; padding-top: 4px; justify-content: center; }
.lamp { width: 10px; height: 10px; border-radius: 50%; border: 1.5px solid var(--control-border); }
.lamp.on { background: var(--accent); border-color: var(--accent); }
.flag-name { font-weight: 600; }
.flag:not(.on) .flag-name { font-weight: 400; }
.flag .state { margin-left: 8px; font-size: var(--fs-xs); font-weight: 400; color: var(--muted); }
.flag .state.on { color: var(--accent); font-weight: 600; }
.flag .tag {
  margin-left: 8px;
  padding: 0 5px;
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  font-size: 10px;
  font-weight: 400;
  color: var(--muted);
  white-space: nowrap;
}
.flag .now { margin-top: 1px; line-height: 1.45; }
.flag .other { color: var(--muted); font-size: var(--fs-sm); line-height: 1.45; }
.lamp-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(36px, 1fr)); gap: 4px; margin-top: 8px; }
.lamp-grid .cell {
  padding: 3px 0;
  text-align: center;
  font: var(--fs-xs)/1.4 var(--mono);
  color: var(--muted);
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
}
.lamp-grid.bits { grid-template-columns: repeat(auto-fill, 36px); margin: 4px 0 5px; }
.lamp-grid .cell.on { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); font-weight: 600; }

.copy-area { position: fixed; left: -100px; top: 0; width: 1px; height: 1px; opacity: 0; }

/* The writes: a flag's lamp and cells are buttons; the preview's actions;
   the rename field and the purge question; files dragged over a
   directory; the last write's message; the busy overlay while one runs. */
.lamps .lamp-toggle {
  min-height: 0;
  padding: 3px;
  margin: -3px;
  border: 0;
  background: none;
  border-radius: 50%;
  cursor: pointer;
}
.lamps .lamp-toggle:hover .lamp { border-color: var(--accent); }
.lamp-grid button.cell { min-height: 0; background: none; cursor: pointer; }
.lamp-grid button.cell:hover { border-color: var(--accent); color: var(--ink); }
.lamp-grid button.cell.on { background: var(--accent); }
.lamp-grid button.cell:disabled, .lamps .lamp-toggle:disabled { cursor: default; }
/* One primary button and a "⋯" for the rest: they never wrap, the
   title gives way instead. */
.preview-actions { display: flex; align-items: center; gap: var(--s1); flex: none; }
.preview-actions > button { font-size: var(--fs-md); }
.preview-title .here-mark { margin-left: 8px; vertical-align: 2px; font-weight: 400; }
button.icon.more { border-color: var(--control-border); background: var(--control); }
button.icon.more:hover:not(:disabled) { border-color: var(--muted); background: var(--panel); }
button.icon.more[aria-expanded=true] { background: var(--hover); border-color: var(--muted); }
.preview-done { font-size: var(--fs-sm); color: var(--accent); padding-right: var(--s1); }
.preview-done:empty { display: none; }

/* A menu (components/menu.js): under its button or at the pointer, one
   column of items with a key on the right; Purge last, after a rule, in
   the error colour. */
/* Why a control that is off does nothing (web/components/note.js). */
.note {
  position: fixed;
  z-index: 30;
  margin: 0;
  max-width: min(320px, calc(100vw - 16px));
  padding: var(--s2) var(--s3);
  font-size: var(--fs-sm);
  line-height: var(--lh-tight);
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-2);
  /* A tap on what is under it still lands there (and closes it). */
  pointer-events: none;
}
.menu {
  position: fixed;
  z-index: 20;
  min-width: 208px;
  overflow: auto;
  padding: var(--s1);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-2);
}
@media (prefers-reduced-motion: no-preference) {
  .menu { animation: menu-in var(--t-fast) var(--ease); }
  @keyframes menu-in { from { opacity: 0; } }
}
.menu [role=menuitem] {
  display: flex;
  width: 100%;
  align-items: center;
  gap: var(--s3);
  min-height: var(--tap);
  padding: 0 var(--s3) 0 var(--s2);
  border: 0;
  border-radius: var(--r-sm);
  background: none;
  justify-content: flex-start;
  text-align: left;
  font-size: var(--fs-md);
  color: var(--ink);
  white-space: nowrap;
}
.menu [role=menuitem]:hover:not([aria-disabled=true]), .menu [role=menuitem]:focus-visible { background: var(--hover); border-color: transparent; }
.menu [role=menuitem]:focus-visible { outline-offset: -2px; }
.menu [role=menuitem] .ic { width: 16px; height: 16px; color: var(--muted); }
.menu [role=menuitem] kbd { margin-left: auto; color: var(--muted); }
.menu [role=menuitem][aria-disabled=true] { color: var(--ink-disabled); cursor: default; }
.menu [role=menuitem].danger, .menu [role=menuitem].danger .ic { color: var(--error); }
.menu [role=separator] { height: 1px; margin: var(--s1) var(--s2); background: var(--rule-soft); }
.menu-note { margin: 0; padding: 2px var(--s2) var(--s1); font-size: var(--fs-xs); color: var(--muted); white-space: normal; max-width: 30ch; }
.edit-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
  margin: 0 var(--gutter) var(--s3);
  padding: var(--s2) var(--s3);
  border-left: 3px solid var(--accent);
  background: var(--hover);
}
.edit-row input {
  font: inherit;
  font-family: var(--mono);
  color: var(--ink);
  background: var(--control);
  border: 1px solid var(--control-border);
  border-radius: var(--r-md);
  min-height: var(--tap);
  padding: 0 var(--s2);
  flex: 1 1 12ch;
  min-width: 0;
}
.edit-row > span { flex: 1 1 100%; }
/* The directory picker of Copy to… and Move to…: the tree's rows, in a
   box of its own that scrolls. */
.pick-row .pick {
  flex: 1 1 100%;
  max-height: 12rem;
  overflow: auto;
  padding: var(--s1) 0;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
}
.pick .node[aria-disabled=true] { color: var(--muted); }
.pick .node[aria-disabled=true]:hover { background: none; }
button.danger { color: var(--error); border-color: var(--error); }
.pane-vars .drop-over { outline: 2px dashed var(--accent); outline-offset: -2px; background: var(--hover); }
.vars-new { flex: none; font-size: var(--fs-md); gap: var(--s1); }
.vars-new .ic { width: 14px; height: 14px; margin-right: -2px; }
.vars-new[aria-expanded=true] { background: var(--hover); border-color: var(--muted); }
.edit-row .edit-error { color: var(--error); }
/* The memory view's status row (sat-explorer.js `renderStatus`): one
   line, always there, so a write's message never moves what is below. */
.layer-status {
  flex: none;
  height: calc(var(--fs-sm) * 1.5 + 2 * var(--s1));
  padding: var(--s1) var(--gutter);
  font-size: var(--fs-sm);
  line-height: 1.5;
  border-bottom: 1px solid var(--rule-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--ink);
}
/* Its two parts share the line: the live one (announced) or the hint. */
.layer-status > span { display: block; overflow: hidden; text-overflow: ellipsis; }
.layer-status > span:empty { display: none; }
.layer-status[data-kind="hint"], .layer-status[data-kind="busy"] { color: var(--muted); }
.layer-status[data-kind="note"] { color: var(--muted); background: var(--hover); }
.layer-status[data-kind="error"] { color: var(--error); background: var(--error-soft); }
.layer { position: relative; }
.layer-busy {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: var(--s2);
  padding: var(--gutter);
  text-align: center;
  background: color-mix(in srgb, var(--panel) 82%, transparent);
  cursor: progress;
}
.layer-busy[hidden] { display: none; }
.layer-busy-text { font-weight: 600; font-size: var(--fs-lg); }
.layer-busy-detail { color: var(--muted); font-size: var(--fs-sm); max-width: 40ch; }

/* The Commands tab. */
.cmds-bar { display: flex; align-items: center; gap: var(--s3); padding: var(--s2) var(--gutter); }
.cmds-bar .find { flex: 1; width: auto; max-width: 30ch; }
.cmds-count { font-size: var(--fs-sm); color: var(--muted); white-space: nowrap; }
.cmds-split {
  flex: 0 0 38%;
  min-height: 150px;
  display: grid;
  grid-template-columns: minmax(120px, 34%) 1fr;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  background: var(--paper);
}
.cmds-menus { overflow: auto; padding: var(--s1) 0; border-right: 1px solid var(--rule); }
.cmds-menus .node.other { font-family: var(--font); color: var(--muted); }
.cmds-menus .node.other.shown { color: var(--ink); }
.cmds-list td.stack { font: var(--fs-sm)/1.5 var(--mono); color: var(--muted); max-width: 0; width: 60%; overflow: hidden; text-overflow: ellipsis; }
.cmds-entry { flex: 1; min-height: 0; overflow: auto; }
.cmds-entry .entry { padding: 14px var(--gutter) 20px; }
.cmds-note { padding: 6px var(--gutter); font-size: var(--fs-sm); color: var(--muted); }

/* A narrow layer (a phone, a small window): fewer columns. Below 620 px
   the keys' chip shows its way back alone ("Back to the calculator
   (Esc)"), so it stays on the tabs' line at the default width; its text
   is hidden from sight only, so the live region still announces it. */
@container (max-width: 620px) {
  .layer-keys-text { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
}
@container (max-width: 470px) {
  .list .sum { display: none; }
  .vars-split { --tree-default: max(92px, 28%); }
  .levels .type { display: none; }
}

/* ------------------------------------------------------------- ROM slots */

.rom-current { display: flex; align-items: center; gap: var(--s2); }
.rom-name {
  flex: 1;
  min-width: 0;
  font-size: var(--fs-base);
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rom-name.error, .roms .rom-file-name.error, .hint.error { color: var(--error); }
.rom-notice {
  font-size: var(--fs-sm);
  color: var(--ink);
  border-left: 3px solid var(--accent);
  padding: 2px 0 2px var(--s2);
}
.rom-notice p { margin: 0; }
.rom-offers { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s1); margin-top: 6px; }
.rom-offers:empty { display: none; }
.rom-offers button { font-size: var(--fs-sm); }
.roms table { width: 100%; border-collapse: collapse; margin: var(--s1) 0 var(--s2); }
.roms th, .roms td { padding: 2px 0; text-align: left; font-weight: 400; vertical-align: middle; }
/* The row's name in two lines: the model, and under it its file, smaller
   and muted (or "none", or the browser's Download link); cut only when
   longer than the cell, the whole name in its tooltip. */
.roms th.rom-label { width: 100%; max-width: 0; padding-right: var(--s2); }
.roms .rom-model, .roms .rom-file-name { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.roms .rom-model { color: var(--ink); }
.roms .rom-file-name { font-size: var(--fs-sm); color: var(--muted); line-height: var(--lh-tight); }
.roms td button { font-size: var(--fs-sm); padding: 0 var(--s2); white-space: nowrap; }
/* A primary button and a "⋯" per row (or a gap of its size): every
   primary one width, every ⋯ one size, so the rows keep one height and
   the buttons line up. */
.roms td.rom-act { width: 1%; }
.rom-row-actions { display: flex; align-items: center; gap: var(--s1); }
.roms td.rom-act .rom-row-button { width: 6.5em; padding: 0 var(--s1); justify-content: center; }
.roms td.rom-act button.icon.more, .rom-more-gap { flex: none; width: var(--tap); height: var(--tap); padding: 0; }
.roms .rom-forget { margin: var(--s2) 0 0; }
/* Whether the browser keeps the ROMs for good, and the way to ask (web/pwa.js). */
.roms .rom-storage { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1) var(--s2); margin: var(--s2) 0 0; font-size: var(--fs-sm); color: var(--muted); }
.roms .rom-storage button { font-size: var(--fs-sm); }

/* --------------------------------------------------------- command palette */

/* One input over the calculator, Alfred-like: the list on the left, the
   selected entry on the right; the calculator stays visible behind a
   dimmed backdrop. On a phone (below 760 px) a full sheet: the input at
   the top, the list under it, the entry as a second step. */

dialog.palette {
  width: var(--palette-w);
  max-width: none;
  height: var(--palette-h);
  max-height: none;
  margin: 48px auto 0;
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: var(--r-xl);
  background: var(--panel);
  color: var(--ink);
  box-shadow: var(--shadow-3);
  overflow: hidden;
}
@media (prefers-reduced-motion: no-preference) {
  dialog.palette[open] { animation: palette-in var(--t) var(--ease); }
  @keyframes palette-in { from { opacity: 0; transform: translateY(-6px) scale(0.995); } }
}
.palette-box { height: 100%; display: flex; flex-direction: column; font-size: var(--fs-md); }

.palette-input {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: 0 var(--s4) 0 var(--s4);
  border-bottom: 1px solid var(--rule);
}
.palette-glyph { color: var(--accent); display: flex; }
.palette-input input {
  flex: 1;
  min-width: 0;
  padding: var(--s4) 0;
  font: 500 19px/1.3 var(--font);
  color: var(--ink);
  background: none;
  border: 0;
  outline: none;
}
.palette-input input:focus-visible { outline: none; }
.palette-input input::placeholder { color: var(--muted); font-weight: 400; }
.palette-model { color: var(--muted); font-size: var(--fs-sm); white-space: nowrap; }
.palette-esc, .palette-key { font-size: var(--fs-xs); color: var(--muted); }
.palette-close { display: none; }
.palette-back { display: none; }
.palette-actions { display: none; }

.palette-state {
  margin: 0;
  padding: 7px var(--s4);
  font-size: var(--fs-sm);
  color: var(--muted);
  background: var(--hover);
  border-bottom: 1px solid var(--rule-soft);
}

.palette-body {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 47%) minmax(0, 1fr);
}
.palette-list { overflow: auto; padding: var(--s2) 0; border-right: 1px solid var(--rule); overscroll-behavior: contain; }
.palette-detail { overflow: auto; background: var(--paper); overscroll-behavior: contain; }

.prow {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto 32px;
  align-items: center;
  gap: 0 10px;
  padding: 6px 12px 6px 13px;
  cursor: default;
  border-left: 3px solid transparent;
  transition: background-color var(--t-fast);
}
.prow[aria-selected=true] { background: var(--accent-soft); border-left-color: var(--accent); }
.prow-kind {
  font-size: 10px;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--muted);
  padding: 1px 6px;
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
}
.prow-command .prow-kind { display: none; }
.prow-kind:empty { visibility: hidden; }
/* An action that cannot run now: greyed, its reason as its description. */
.prow-off :is(.prow-name, .prow-desc) { color: var(--ink-disabled); }
.prow-main { min-width: 0; }
.prow-head { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
.prow-name { font: 600 var(--fs-base)/1.4 var(--mono); white-space: nowrap; }
.prow-title { font-family: var(--font); font-weight: 600; }
.prow-text { white-space: pre-wrap; overflow-wrap: anywhere; }
.prow-stack {
  font: var(--fs-sm)/1.4 var(--mono);
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.prow-desc { font-size: var(--fs-sm); color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.prow[aria-selected=true] .prow-desc { color: var(--ink); }
.prow-hint { visibility: hidden; font-size: 10px; color: var(--muted); text-align: center; }
.prow[aria-selected=true] .prow-hint, .palette-list:hover .prow-hint, .palette-list.hints .prow-hint { visibility: visible; }
.prow-go { display: none; color: var(--muted); }
.palette-empty { margin: 0; max-width: 42ch; }

.palette-foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--s4);
  padding: 7px var(--s4);
  border-top: 1px solid var(--rule);
  font-size: var(--fs-sm);
  color: var(--muted);
}
.palette-foot p { margin: 0; }
.palette-notice {
  margin: 0;
  padding: var(--s2) var(--s4);
  border-top: 1px solid var(--rule);
  font-size: var(--fs-md);
  color: var(--ink);
  background: var(--hover);
}
.palette-notice.error { color: var(--error); font-weight: 600; background: var(--error-soft); }
.palette-notice.busy::before {
  content: "";
  display: inline-block;
  width: 9px;
  height: 9px;
  margin-right: 7px;
  border-radius: 50%;
  border: 2px solid var(--accent);
  border-right-color: transparent;
  vertical-align: -1px;
  animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .palette-notice.busy::before { animation: none; border-right-color: var(--accent); opacity: 0.6; }
}
.palette-hints { white-space: nowrap; }
.palette-hints kbd { margin: 0 1px; }

/* The palette's editor mode: the input grows to a multi-line RPL editor
   (components/rpl-editor.js). A textarea with transparent text lies over
   a <pre> that shows the same text highlighted; both share every metric. */
.palette-box.editing .palette-input,
.palette-box.editing .palette-body,
.palette-box.editing .palette-foot { display: none; }
.palette-editor { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.editor-head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-height: 52px;
  padding: 0 var(--s4);
  border-bottom: 1px solid var(--rule);
}
.editor-title { font: 600 var(--fs-lg)/1.3 var(--font); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.editor-dirty {
  font-size: var(--fs-xs);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--accent);
  white-space: nowrap;
}
.editor-dirty::before { content: "\25CF"; margin-right: 4px; }
.editor-head .palette-esc { margin-left: auto; }
.editor-close { margin-left: calc(-1 * var(--s1)); }
.editor-area { flex: 1; min-height: 0; display: flex; flex-direction: column; background: var(--paper); }
.rpl-editor { position: relative; flex: 1; min-height: 0; }
.rpl-hl, .rpl-text {
  position: absolute;
  inset: 0;
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: var(--s3) var(--s4);
  border: 0;
  border-radius: 0;
  font: 14px/1.55 var(--mono);
  letter-spacing: 0;
  tab-size: 2;
  white-space: pre;
  overflow-wrap: normal;
}
.rpl-hl { color: var(--ink); overflow: hidden; pointer-events: none; }
.rpl-text {
  color: transparent;
  caret-color: var(--ink);
  background: transparent;
  resize: none;
  outline: none;
  overflow: auto;
  overscroll-behavior: contain;
}
.rpl-text::selection { background: color-mix(in srgb, var(--accent) 28%, transparent); color: transparent; }
.rpl-text:focus-visible { outline: none; }
.t-delim { color: var(--syn-delim); font-weight: 600; }
.t-num { color: var(--syn-num); }
.t-str, .t-tag { color: var(--syn-str); }
.t-kw { color: var(--syn-kw); font-weight: 600; }
.t-cmd { color: var(--syn-cmd); }
.t-var { color: var(--syn-var); }
.t-com { color: var(--syn-com); font-style: italic; }
.t-op { color: var(--muted); }
mark.bm { background: var(--syn-match); color: inherit; border-radius: 2px; }
.rpl-complete {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s1);
  padding: var(--s2) var(--s4);
  border-top: 1px solid var(--rule-soft);
  background: var(--panel);
}
button.rpl-choice {
  min-height: 0;
  padding: 2px var(--s2);
  font: var(--fs-sm)/1.4 var(--mono);
  border-radius: var(--r-sm);
}
button.rpl-choice[aria-selected=true] { background: var(--accent-soft); border-color: var(--accent); }
.rpl-choice-kind { margin-left: 6px; font: var(--fs-xs)/1 var(--font); color: var(--muted); }
.editor-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2) var(--s3);
  padding: var(--s2) var(--s4);
  border-top: 1px solid var(--rule);
  font-size: var(--fs-sm);
  color: var(--muted);
}
.editor-hints { margin: 0; flex: 1 1 260px; min-width: 0; }
.editor-hints kbd { margin: 0 1px; }
.editor-buttons { display: flex; gap: var(--s2); margin-left: auto; }

/* The reference entry (palette detail and the explorer's Commands tab). */
.entry { padding: var(--s4) 20px 20px; display: flex; flex-direction: column; gap: 10px; }
.entry h3, .entry h4, .entry p, .entry dl { margin: 0; }
.entry-head { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; }
.entry-name { font: 600 var(--fs-2xl)/1.2 var(--mono); overflow-wrap: anywhere; }
.entry-title { font-family: var(--font); font-size: var(--fs-xl); letter-spacing: -0.01em; }
.entry-models { display: flex; gap: var(--s1); margin-left: auto; }
.chip {
  padding: 1px 8px;
  border: 1px solid var(--control-border);
  border-radius: var(--r-pill);
  font-size: var(--fs-xs);
  color: var(--ink);
  white-space: nowrap;
}
.chip.current { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); font-weight: 600; }
.chip.off { color: var(--muted); border-style: dashed; opacity: 0.6; text-decoration: line-through; }
.entry-stack {
  margin: 0;
  padding: 9px 12px;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  font: var(--fs-md)/1.5 var(--mono);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.entry-stack-wrap { display: flex; flex-direction: column; gap: var(--s1); }
.entry-note, .entry-skip { font-size: var(--fs-sm); color: var(--muted); line-height: 1.5; }
.entry-desc { line-height: 1.5; max-width: 60ch; }
.entry-where { display: grid; grid-template-columns: auto 1fr; gap: 3px 12px; font-size: var(--fs-sm); }
.entry-where dt { color: var(--muted); }
.entry-where dd { margin: 0; }
.entry .menu-path { font: 600 var(--fs-sm)/1.5 var(--mono); }
.entry .sep { color: var(--muted); }
.entry h4 { margin-top: 6px; }
.entry h4 .muted { font-weight: 400; letter-spacing: 0; text-transform: none; }
.entry-examples { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 6px; }
.ex {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px 10px;
  padding: 7px 10px;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
}
.ex-in { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ex-text { font: var(--fs-md)/1.4 var(--mono); white-space: pre-wrap; overflow-wrap: anywhere; }
.ex-effect { font-size: var(--fs-xs); color: var(--muted); }
.ex-arrow { color: var(--accent); font-size: var(--fs-lg); }
.ex-out { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.ex-line { display: flex; gap: 6px; align-items: baseline; }
.ex-level { flex: none; font: var(--fs-xs)/1.4 var(--mono); color: var(--muted); min-width: 2ch; text-align: right; }
.ex-out.error .ex-level { color: var(--error); min-width: 0; }
.ex-out.error .ex-text { color: var(--error); }
.ex-try { font-size: var(--fs-sm); white-space: nowrap; }
.ex-tried { grid-column: 1 / -1; font-size: var(--fs-sm); color: var(--accent); }
.ex-tried.error { color: var(--error); }
.entry-manuals ul { margin: 0; padding-left: 18px; font-size: var(--fs-sm); line-height: 1.6; }
.entry a { color: var(--accent); text-underline-offset: 2px; }
.entry-names { font: var(--fs-sm)/1.7 var(--mono); overflow-wrap: anywhere; }
.entry-enter { font-size: var(--fs-sm); color: var(--muted); line-height: 1.6; padding: 10px 20px 16px; border-top: 1px solid var(--rule-soft); }
.palette-detail .entry { padding-bottom: 14px; }
.entry-enter kbd { margin: 0 1px; }

/* ------------------------------------------------------ keyboard shortcuts */

.shortcuts-box { display: flex; flex-direction: column; max-height: calc(100vh - 50px); }
.shortcuts-body { overflow: auto; padding: var(--s1) var(--gutter) var(--s3); font-size: var(--fs-md); }
.shortcuts-body h3 { margin: var(--s4) 0 var(--s1); }
.shortcuts-body h3 .muted { font-weight: 400; letter-spacing: 0; text-transform: none; }
.shortcuts-body p { margin: var(--s2) 0 0; line-height: 1.5; }
.shortcuts-intro { color: var(--muted); max-width: 70ch; }
.shortcuts-table { width: 100%; border-collapse: collapse; }
.shortcuts-table th, .shortcuts-table td { padding: 7px 0; vertical-align: top; text-align: left; border-top: 1px solid var(--rule-soft); }
.shortcuts-table tr:first-child > * { border-top: 0; }
.shortcuts-table th { width: 38%; padding-right: 14px; font-weight: 400; }
.shortcuts-table:is(.typing, .mouse) th { white-space: nowrap; width: 1%; }
.sc-title { display: block; font-weight: 600; }
.sc-desc { display: block; color: var(--muted); font-size: var(--fs-sm); line-height: 1.4; margin-top: 1px; }
.sc-reset { width: 1%; padding-left: 8px !important; white-space: nowrap; text-align: right !important; }
.sc-reset button.link { font-size: var(--fs-sm); }
.chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.chip-key { display: inline-flex; align-items: center; gap: 1px; border: 1px solid var(--rule); border-radius: var(--r-md); padding: 1px 1px 1px 6px; background: var(--control); min-height: 28px; }
.chip-key kbd { border: 0; background: none; padding: 0 2px; font-size: var(--fs-sm); }
.chip-key.warned { border-color: var(--error); }
.chip-key.fresh { box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 45%, transparent); }
button.icon.chip-remove { width: 24px; height: 24px; color: var(--muted); }
button.icon.chip-remove .ic { width: 14px; height: 14px; }
button.chip-add { font-size: var(--fs-sm); min-height: 28px; padding: 0 9px; }
button.chip-add.recording { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.shortcuts-table select { font-size: var(--fs-md); }
.warns { list-style: none; margin: 5px 0 0; padding: 0; font-size: var(--fs-sm); line-height: 1.45; }
.warns li + li { margin-top: 2px; }
.warn { color: var(--error); }
.warn.minor { color: var(--muted); }
.warn.fresh { color: var(--error); font-weight: 600; }
.warns kbd { font-size: var(--fs-xs); }
.shortcuts-where { margin: 0 auto 0 0; font-size: var(--fs-sm); color: var(--muted); }

/* ------------------------------------------------------- layout: widths */

/* Below 1000px the layer lies over the calculator. */
@media (max-width: 999px) {
  body.layer-open, body.layer-open.panel-hidden { grid-template-columns: var(--panel-w) 1fr; }
  body.layer-open.panel-hidden { grid-template-columns: 0 1fr; }
  sat-explorer { grid-column: 2; z-index: 2; border-left: 0; }
  .layer-back { display: inline-flex; }
  .layer button.layer-close { display: none; }
  .resize-layer { display: none; }
  body.layer-open.panel-hidden #panel-show { display: none; }
}
@media (min-width: 760px) {
  #bar-memory, #bar-palette, #bar-edit { display: none; }
}

/* Narrow screens (phones): the top bar, the controls as a drop-down
   sheet, the layer in the stage's place, dialogs as full sheets, the
   palette as a sheet with the entry as a second step. */
@media (max-width: 759px) {
  body, body.panel-hidden, body.layer-open, body.layer-open.panel-hidden {
    grid-template-columns: 1fr;
    grid-template-rows: var(--bar-top) 1fr;
  }
  .bar { display: flex; grid-row: 1; }
  .stage { grid-column: 1; grid-row: 2; padding: var(--s2) calc(var(--s2) + var(--safe-r)) calc(var(--s2) + var(--safe-b)) calc(var(--s2) + var(--safe-l)); }
  .stage-tools { display: none; }
  sat-explorer { grid-column: 1; grid-row: 2; }
  .panel, body.panel-hidden .panel {
    display: none;
    position: fixed;
    top: var(--bar-top);
    left: 0;
    right: 0;
    max-height: calc(100% - var(--bar-top));
    z-index: 4;
    padding-bottom: calc(var(--s3) + var(--safe-b));
    border-right: 0;
    border-bottom: 1px solid var(--rule);
    border-radius: 0 0 var(--r-lg) var(--r-lg);
    box-shadow: var(--shadow-2);
  }
  body.sheet-open .panel { display: flex; }
  @media (prefers-reduced-motion: no-preference) {
    body.sheet-open .panel { animation: sheet-in var(--t) var(--ease); }
    @keyframes sheet-in { from { opacity: 0; transform: translateY(-8px); } }
  }
  .resize-panel { display: none; }
  /* The tree keeps its default width: no divider to drag on a phone. */
  .resize-tree { display: none; }
  .vars-split { --tree-col: var(--tree-default); }
  .panel-head, .tagline, #panel-show { display: none !important; }
  .layer-head { padding-left: var(--s2); }

  /* Dialogs as full sheets. */
  dialog.about, dialog.shortcuts {
    width: 100%;
    height: 100vh;
    height: 100dvh;
    max-height: none;
    margin: 0;
    border: 0;
    border-radius: 0;
  }
  .about-box, .shortcuts-box { height: 100%; max-height: none; }
  .dialog-head { padding-top: calc(var(--s2) + var(--safe-t)); padding-left: var(--gutter); }
  .about-body, .shortcuts-body { flex: 1; min-height: 0; padding-bottom: calc(var(--s5) + var(--safe-b)); }
  .dialog-foot { padding-bottom: calc(var(--s3) + var(--safe-b)); }
  .shortcuts-table th { width: auto; display: block; border-top: 1px solid var(--rule-soft); padding-bottom: 2px; }
  .shortcuts-table td { display: inline-block; border-top: 0; padding-top: 2px; }
  .shortcuts-table td.sc-keys { width: 75%; }
  .shortcuts-table:is(.typing, .mouse) th { width: auto; }

  /* The palette as a sheet. Its height follows the visual viewport
     (--vvh, set by the component), so the on-screen keyboard never
     covers the list. */
  dialog.palette {
    position: fixed;
    inset: var(--vvt, 0px) 0 auto 0;
    width: 100%;
    height: 100vh;
    height: var(--vvh, 100dvh);
    margin: 0;
    border: 0;
    border-radius: 0;
    box-shadow: none;
  }
  @media (prefers-reduced-motion: no-preference) {
    dialog.palette[open] { animation: fade-in var(--t) var(--ease); }
  }
  .palette-input { padding: var(--safe-t) var(--s2) 0 var(--s4); gap: var(--s2); }
  .palette-input input { padding: var(--s3) 0; min-height: var(--tap); font-size: 17px; }
  .palette-model, .palette-esc, .palette-key { display: none; }
  .palette-close { display: inline-flex; }
  .palette-body { display: block; }
  .palette-list { height: 100%; border-right: 0; padding-bottom: var(--safe-b); }
  .palette-detail { display: none; height: 100%; }
  .palette-box.detail-open .palette-input, .palette-box.detail-open .palette-state, .palette-box.detail-open .palette-list { display: none; }
  .palette-box.detail-open .palette-detail { display: flex; flex-direction: column; flex: 1; min-height: 0; overflow: auto; }
  .palette-box.detail-open .palette-back { display: flex; }
  .palette-back {
    position: sticky;
    top: 0;
    z-index: 1;
    align-items: center;
    gap: var(--s2);
    padding: var(--safe-t) var(--s2) 0 var(--s2);
    min-height: calc(var(--tap) + var(--s2) + var(--safe-t));
    border-bottom: 1px solid var(--rule);
    background: var(--panel);
  }
  .palette-back .palette-back-name { font: 600 var(--fs-lg)/1.3 var(--mono); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .palette-detail .entry { flex: 1; padding-bottom: var(--s4); }
  .entry-enter { display: none; }
  .palette-actions {
    display: flex;
    gap: var(--s2);
    padding: var(--s2) var(--s3) calc(var(--s2) + var(--safe-b));
    border-top: 1px solid var(--rule);
    background: var(--panel);
    position: sticky;
    bottom: 0;
  }
  .palette-actions button { flex: 1; }
  .palette-actions .muted { flex: 1; align-self: center; font-size: var(--fs-sm); }
  .palette-foot { display: none; }
  .editor-head { padding: var(--safe-t) var(--s2) 0 var(--s4); gap: var(--s2); }
  .editor-head .palette-esc { display: none; }
  .editor-close { margin-left: auto; }
  .rpl-hl, .rpl-text { font-size: 15px; padding: var(--s3); }
  .editor-hints { display: none; }
  .editor-bar { padding: var(--s2) var(--s3) calc(var(--s2) + var(--safe-b)); }
  .editor-buttons { flex: 1; }
  .editor-buttons button { flex: 1; min-height: var(--tap); }
  .prow { grid-template-columns: minmax(0, 1fr) auto 20px; min-height: var(--tap); padding: var(--s2) var(--s2) var(--s2) 13px; }
  .prow-hint { display: none; }
  .prow-go { display: block; }
  .prow-desc { white-space: normal; }
  .ex { grid-template-columns: minmax(0, 1fr) auto; }
  .ex-arrow { display: none; }
  .ex-in { grid-column: 1; grid-row: 1; }
  .ex-try { grid-column: 2; grid-row: 1; align-self: start; justify-self: end; }
  .ex-out { grid-column: 1 / -1; grid-row: 2; padding-top: 2px; border-top: 1px solid var(--rule-soft); }
  .ex-tried { grid-row: 3; }
}

/* Coarse pointers: the shortcut chips grow to a finger's size too (after
   their own rules, which they override). */
@media (pointer: coarse) {
  .chip-key, button.chip-add { min-height: var(--tap); }
  button.icon.chip-remove { width: var(--tap); height: var(--tap); }
}

/* A new build of the installed page is ready, or keep a kept ROM for
   good (web/pwa.js), or start fresh (web/fresh.js): a card at the bottom,
   above the home indicator. Not modal; an update notice covers the
   storage one, the question the user just asked for covers both. */
.update-notice, .storage-notice {
  position: fixed;
  left: 50%;
  bottom: calc(var(--s4) + var(--safe-b));
  transform: translateX(-50%);
  z-index: 7;
  width: min(420px, calc(100vw - 2 * var(--gutter)));
  padding: var(--s3) var(--s4);
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-2);
  font-size: var(--fs-md);
  line-height: var(--lh);
}
.storage-notice { z-index: 6; }
/* In the panel, under the ROM row (pwa.js `showStorageOffer`). */
.storage-notice.in-panel {
  position: static;
  transform: none;
  width: auto;
  margin: var(--s1) 0 var(--s2);
  padding: var(--s2) var(--s3);
  box-shadow: none;
  border-left: 3px solid var(--accent);
  font-size: var(--fs-sm);
}
.storage-notice.in-panel .notice-actions { justify-content: flex-start; }
.storage-offer:empty { display: none; }
.update-notice p, .storage-notice p { margin: 0 0 var(--s2); }
.update-actions, .notice-actions { display: flex; justify-content: flex-end; gap: var(--s2); }
/* The outcome: its one line beside OK. */
.storage-notice.outcome { display: flex; align-items: center; gap: var(--s3); }
.storage-notice.outcome p { margin: 0; flex: 1; }

/* A graphic (GROB): its pixels at a whole scale, hard-edged, in the LCD's
   colours; a thumbnail of it in its stack level, the row's height. */
.graphic-wrap { overflow: auto; max-width: 100%; }
canvas.graphic, .levels canvas.thumb { display: block; image-rendering: pixelated; image-rendering: crisp-edges; }
.levels canvas.thumb {
  display: inline-block;
  height: calc(var(--row) - 8px);
  width: auto;
  max-width: 40%;
  object-fit: contain;
  object-position: left center;
  vertical-align: middle;
  margin-right: var(--s1);
}
