/* =====================================================================
   3270Web — UI polish layer
   ---------------------------------------------------------------------
   Loaded AFTER style.css. Everything here derives from the per-theme
   custom properties already defined in style.css (--bg, --panel,
   --panel-2, --border, --fg, --fg-muted, --accent, --accent-2), so all
   built-in themes plus user-authored custom themes pick these upgrades
   up automatically without any per-theme work.

   Layout-critical geometry is deliberately left alone:
     * .screen-container keeps its exact JS-computed width (screen-size.js
       sizes it to cols * charWidth + 2, and box-sizing is border-box, so
       adding horizontal padding there would clip terminal columns).
       All terminal chrome therefore lives on the .terminal-shell parent.
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. Derived design tokens
   ---------------------------------------------------------------------
   These MUST be declared on `body`, not `:root`.

   style.css puts the default (green) palette on `:root` and every theme
   override on `body.theme-*`. A custom property containing var() is
   substituted using the values on the element where it is *declared*, so
   declaring these on :root would bake in :root's dark-green --panel/--fg
   and every theme would inherit that same green surface — which is
   exactly what made the light theme render dark toolbars. Declared on
   `body`, they resolve against whichever theme class is active (and
   against the inline custom-theme properties html carries, since body
   inherits those).
   --------------------------------------------------------------------- */
body {
  /* Elevation — tuned for dark surfaces; the light theme overrides below. */
  --elev-1: 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 1px rgba(0, 0, 0, 0.18);
  --elev-2: 0 4px 12px -2px rgba(0, 0, 0, 0.42), 0 2px 5px -2px rgba(0, 0, 0, 0.28);
  --elev-3: 0 14px 32px -8px rgba(0, 0, 0, 0.52), 0 6px 14px -8px rgba(0, 0, 0, 0.34);
  --elev-4: 0 32px 68px -16px rgba(0, 0, 0, 0.66), 0 14px 28px -14px rgba(0, 0, 0, 0.45);

  /* Radii */
  --r-xs: 6px;
  --r-sm: 9px;
  --r-md: 12px;
  --r-lg: 16px;
  --r-xl: 22px;
  --r-pill: 999px;

  /* Motion */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
  --dur-1: 120ms;
  --dur-2: 200ms;
  --dur-3: 320ms;

  /* Derived surfaces + lines */
  --surface-raised: linear-gradient(
    160deg,
    color-mix(in srgb, var(--panel-2) 82%, var(--fg) 3%),
    color-mix(in srgb, var(--panel) 94%, transparent)
  );
  --surface-sunken: color-mix(in srgb, var(--bg) 72%, var(--panel));
  --glass: color-mix(in srgb, var(--panel) 82%, transparent);
  --glass-strong: color-mix(in srgb, var(--panel) 92%, transparent);
  --hairline: color-mix(in srgb, var(--border) 78%, transparent);
  --hairline-soft: color-mix(in srgb, var(--border) 45%, transparent);
  --top-sheen: color-mix(in srgb, var(--fg) 9%, transparent);

  /* Accent derivatives */
  --accent-soft: color-mix(in srgb, var(--accent) 14%, transparent);
  --accent-softer: color-mix(in srgb, var(--accent) 8%, transparent);
  --accent-line: color-mix(in srgb, var(--accent) 42%, var(--border));
  --glow-sm: 0 0 0 1px color-mix(in srgb, var(--accent) 22%, transparent),
    0 4px 14px -4px color-mix(in srgb, var(--accent) 30%, transparent);
  --glow-md: 0 0 0 1px color-mix(in srgb, var(--accent) 32%, transparent),
    0 10px 30px -8px color-mix(in srgb, var(--accent) 38%, transparent);

  /* Focus ring */
  --ring: 0 0 0 2px var(--bg), 0 0 0 4px color-mix(in srgb, var(--accent) 72%, transparent);

  /* Scrim behind modals */
  --scrim: color-mix(in srgb, var(--bg) 72%, rgba(0, 0, 0, 0.72));

  /* Semantic status colours, blended toward the theme so they never clash */
  --ok: color-mix(in srgb, #35d07f 76%, var(--accent));
  --warn: #f5b544;
  --danger: #ff6b6b;
  --info: color-mix(in srgb, #4aa8ff 70%, var(--accent));

  /* Masked icons (tinted with currentColor via mask-image). */
  --icon-power: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M13 3h-2v10h2V3zm4.83 2.17-1.42 1.42A6.92 6.92 0 0 1 19 12a7 7 0 0 1-14 0 6.92 6.92 0 0 1 2.59-5.41L6.17 5.17A8.93 8.93 0 0 0 3 12a9 9 0 0 0 18 0 8.93 8.93 0 0 0-3.17-6.83z'/%3E%3C/svg%3E");
}

/* Light theme needs softer, cooler shadows and a lighter scrim. */
body.theme-light,
body.theme-not3270,
body.theme-io-daylight {
  --elev-1: 0 1px 2px rgba(60, 45, 25, 0.1);
  --elev-2: 0 4px 12px -3px rgba(60, 45, 25, 0.16), 0 2px 5px -2px rgba(60, 45, 25, 0.1);
  --elev-3: 0 14px 32px -10px rgba(60, 45, 25, 0.22), 0 6px 14px -8px rgba(60, 45, 25, 0.12);
  --elev-4: 0 32px 68px -18px rgba(60, 45, 25, 0.3), 0 14px 28px -16px rgba(60, 45, 25, 0.18);
  --top-sheen: rgba(255, 255, 255, 0.85);
  --scrim: color-mix(in srgb, #3a2f22 44%, transparent);
  --surface-sunken: color-mix(in srgb, var(--panel-2) 60%, var(--panel));
}

/* ---------------------------------------------------------------------
   2. Typography + base
   --------------------------------------------------------------------- */
body {
  font-family: "Inter", "Inter var", "Segoe UI Variable Text", "Segoe UI",
    system-ui, -apple-system, "Helvetica Neue", sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  font-feature-settings: "cv02", "cv03", "cv04", "ss01";
}

.card-header h1,
.card-header h2 {
  letter-spacing: -0.018em;
  font-weight: 650;
}

/* Uppercase micro-labels used across the menus and panels. */
.appmenu-heading,
.copilot-panel-title,
.settings-subgroup-legend,
.active-run-label {
  letter-spacing: 0.09em;
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------------
   3. Controls — buttons, inputs, selects
   --------------------------------------------------------------------- */
button,
.h3270-key,
.controls button {
  background: var(--surface-raised);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
  transition: transform var(--dur-1) var(--ease-out),
    border-color var(--dur-2) var(--ease-out),
    box-shadow var(--dur-2) var(--ease-out),
    background-color var(--dur-2) var(--ease-out),
    color var(--dur-2) var(--ease-out);
  will-change: transform;
}

button:hover:not(:disabled),
.h3270-key:hover {
  border-color: var(--accent-line);
  box-shadow: var(--glow-sm);
  transform: translateY(-1px);
}

button:active:not(:disabled),
.h3270-key:active {
  transform: translateY(0) scale(0.975);
  box-shadow: inset 0 2px 6px -2px rgba(0, 0, 0, 0.45);
  transition-duration: 60ms;
}

button:disabled {
  opacity: 0.42;
  transform: none;
  box-shadow: none;
}

/* A single, consistent focus ring for every interactive element. */
button:focus-visible,
a:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[role="button"]:focus-visible,
[tabindex]:focus-visible {
  outline: none;
  box-shadow: var(--ring);
  border-radius: var(--r-sm);
}

input,
select,
textarea {
  background: var(--surface-sunken);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
  transition: border-color var(--dur-2) var(--ease-out),
    box-shadow var(--dur-2) var(--ease-out),
    background-color var(--dur-2) var(--ease-out);
}

input:hover:not(:disabled),
select:hover:not(:disabled),
textarea:hover:not(:disabled) {
  border-color: color-mix(in srgb, var(--accent) 28%, var(--border));
}

input:focus,
select:focus,
textarea:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
  outline: none;
}

/* Checkboxes: replace the stark browser default with a themed control. */
input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 18px;
  height: 18px;
  min-width: 18px;
  padding: 0;
  border: 1.5px solid color-mix(in srgb, var(--fg-muted) 55%, var(--border));
  border-radius: 5px;
  background: var(--surface-sunken);
  display: inline-grid;
  place-content: center;
  cursor: pointer;
  position: relative;
  vertical-align: -3px;
}

input[type="checkbox"]::after {
  content: "";
  width: 10px;
  height: 10px;
  transform: scale(0);
  transition: transform var(--dur-2) var(--ease-spring);
  background: var(--bg);
  clip-path: polygon(14% 44%, 0 65%, 40% 100%, 100% 16%, 84% 0%, 38% 68%);
}

input[type="checkbox"]:checked {
  background: var(--accent);
  border-color: var(--accent);
}

input[type="checkbox"]:checked::after {
  transform: scale(1);
}

input[type="checkbox"]:hover:not(:disabled) {
  border-color: var(--accent);
}

/* style.css styles `label` alongside input/select/button, so caption labels
   inherit a bordered, filled, padded box and read as editable fields. Only
   labels that genuinely wrap a control should look like one — strip the
   chrome from the captions. (.settings-field > label is already reset in
   style.css; these are the ones that were missed.) */
.chaos-hint-field-label,
.connect-row > label,
.saved-hosts-row > label,
.toolbar > label,
.copilot-model-label,
.settings-theme-row > label,
.chaos-map-hints > label,
.workflow-modal .workflow-field > span {
  background: transparent;
  border: none;
  padding: 0;
  border-radius: 0;
}

/* Icon buttons get a circular hover halo. */
.icon-button {
  border-radius: var(--r-sm);
  position: relative;
  overflow: hidden;
  color: var(--fg-muted);
}

.icon-button::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at center, var(--accent-soft), transparent 72%);
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease-out);
}

.icon-button:hover:not(:disabled) {
  color: var(--accent);
}

.icon-button:hover:not(:disabled)::before {
  opacity: 1;
}

.icon-button svg {
  position: relative;
  transition: transform var(--dur-2) var(--ease-spring);
}

.icon-button:hover:not(:disabled) svg {
  transform: scale(1.12);
}

.icon-button-record {
  color: var(--danger);
}

.icon-button-record:hover:not(:disabled) {
  color: var(--danger);
}

/* Recording button pulses while armed so the state is unmissable. */
[data-recording-stop]:not([hidden]) .icon-button-stop {
  color: var(--danger);
  animation: polish-record-pulse 1.8s var(--ease-out) infinite;
}

@keyframes polish-record-pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--danger) 45%, transparent); }
  50% { box-shadow: 0 0 0 6px transparent; }
}

.icon-button-stop {
  color: var(--warn);
}

/* Destructive actions read as destructive. */
button.danger,
.danger {
  border-color: color-mix(in srgb, var(--danger) 45%, var(--border));
  color: var(--danger);
  background: linear-gradient(
    160deg,
    color-mix(in srgb, var(--danger) 14%, var(--panel-2)),
    color-mix(in srgb, var(--danger) 6%, transparent)
  );
}

button.danger:hover:not(:disabled) {
  border-color: var(--danger);
  color: var(--bg);
  background: var(--danger);
  box-shadow: 0 6px 18px -6px color-mix(in srgb, var(--danger) 60%, transparent);
}

/* A destructive button focused for confirmation should not be ringed in the
   "safe" accent colour. */
button.danger:focus-visible,
.appmenu-item-danger:focus-visible {
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px color-mix(in srgb, var(--danger) 78%, transparent);
}

/* ---------------------------------------------------------------------
   4. Page shell + card
   --------------------------------------------------------------------- */
.card {
  border-radius: var(--r-xl);
  border-color: var(--hairline);
  background:
    linear-gradient(180deg, var(--top-sheen), transparent 120px),
    linear-gradient(168deg, color-mix(in srgb, var(--panel-2) 42%, var(--panel)), var(--panel) 62%);
  box-shadow: var(--elev-4), 0 0 0 1px var(--hairline-soft),
    0 0 90px -30px color-mix(in srgb, var(--accent) 30%, transparent);
  position: relative;
  isolation: isolate;
}

/* Hairline highlight along the top edge — reads as a lit bevel. */
.card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 1px;
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  background: linear-gradient(
    90deg,
    transparent,
    color-mix(in srgb, var(--accent) 55%, transparent) 22%,
    color-mix(in srgb, var(--accent) 55%, transparent) 78%,
    transparent
  );
  opacity: 0.55;
  pointer-events: none;
  z-index: 2;
}

.card-header {
  padding-bottom: 16px;
  border-bottom: 1px solid var(--hairline-soft);
  margin-bottom: 16px;
}

/* Mark and wordmark on one baseline; see templates/brand.html for the mark. */
.brand-lockup {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.brand-mark {
  /* The mark's viewBox is 96x88, so the box is sized to match its ratio and
     avoid the letterboxing a square would introduce. */
  width: 37px;
  height: 34px;
  flex: none;
  /* The drawing already casts an accent glow; this only seats it on the card. */
  filter: drop-shadow(0 2px 6px color-mix(in srgb, var(--accent) 24%, transparent));
}

/* On the connect card the mark carries the page, so it gets a little more room. */
.page-wrap > .card > .card-header .brand-mark {
  width: 46px;
  height: 42px;
}

@media (max-width: 640px) {
  .brand-mark {
    width: 30px;
    height: 27px;
  }
}

/* Brand wordmark with an accent gradient. */
.card-header [data-chrome-title] h2 {
  background: linear-gradient(96deg, var(--fg), var(--accent) 55%, var(--accent-2));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  font-size: 1.5rem;
}

/* "Connected session" gains a live status dot. */
.card-header [data-chrome-title] .subtle {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 0.82rem;
  letter-spacing: 0.01em;
}

.card-header [data-chrome-title] .subtle::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ok);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--ok) 55%, transparent);
  animation: polish-live-pulse 2.4s var(--ease-out) infinite;
  flex: none;
}

@keyframes polish-live-pulse {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--ok) 55%, transparent); }
  70% { box-shadow: 0 0 0 7px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}

/* ---------------------------------------------------------------------
   5. The menu bar
   ---------------------------------------------------------------------
   The bar itself lives in menu-bar.css. What stays here is the handful of
   controls that sit on it and are styled with the rest of the system.
   --------------------------------------------------------------------- */
/* Copilot toggle is the panel's primary entry point — give it presence. */
.copilot-toggle-button[data-copilot-toggle] {
  color: var(--accent);
  border-color: var(--accent-line);
  background: linear-gradient(160deg, var(--accent-soft), transparent);
}

body.copilot-open .copilot-toggle-button[data-copilot-toggle] {
  background: var(--accent);
  color: var(--bg);
  border-color: var(--accent);
}

/* Active-run row becomes a status banner with a moving progress sheen. */
.active-run-row {
  border-radius: var(--r-md);
  border-color: var(--hairline);
  background: var(--surface-raised);
  box-shadow: var(--elev-1);
  position: relative;
  overflow: hidden;
  transition: border-color var(--dur-2) var(--ease-out),
    box-shadow var(--dur-2) var(--ease-out);
}

.active-run-row:hover {
  border-color: var(--accent-line);
  box-shadow: var(--glow-sm);
}

.active-run-chip {
  border-radius: var(--r-pill);
  font-weight: 700;
  letter-spacing: 0.08em;
  font-size: 0.66rem;
  padding: 4px 10px;
}

.active-run-chip[data-run-mode="recording"] {
  animation: polish-record-pulse 1.8s var(--ease-out) infinite;
}

/* ---------------------------------------------------------------------
   6. Terminal shell — CRT bezel + OIA status bar
   --------------------------------------------------------------------- */
.terminal-shell {
  position: relative;
  padding: 14px;
  border-radius: var(--r-lg);
  border: 1px solid var(--hairline);
  background:
    linear-gradient(180deg, var(--top-sheen), transparent 60px),
    linear-gradient(165deg, color-mix(in srgb, var(--panel-2) 70%, var(--panel)), var(--panel));
  box-shadow:
    var(--elev-3),
    inset 0 1px 0 var(--top-sheen),
    0 0 0 1px var(--hairline-soft);
  transition: box-shadow var(--dur-3) var(--ease-out),
    border-color var(--dur-3) var(--ease-out);
}

/* The bezel lights up when the operator is actually typing into the screen. */
.terminal-shell:focus-within {
  border-color: var(--accent-line);
  box-shadow:
    var(--elev-3),
    inset 0 1px 0 var(--top-sheen),
    0 0 0 1px color-mix(in srgb, var(--accent) 30%, transparent),
    0 0 42px -10px color-mix(in srgb, var(--accent) 42%, transparent);
}

.screen-container {
  border-radius: var(--r-sm) var(--r-sm) 0 0;
  border-color: color-mix(in srgb, var(--border) 90%, black);
  box-shadow:
    inset 0 0 40px -12px color-mix(in srgb, var(--accent) 22%, transparent),
    inset 0 2px 10px -4px rgba(0, 0, 0, 0.7);
  position: relative;
}

/* Very subtle phosphor bloom over the glass. Suppressed on light themes
   and on the themes that ship their own CRT treatment. */
.screen-container::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: inherit;
  background:
    radial-gradient(120% 90% at 50% 0%, color-mix(in srgb, var(--accent) 7%, transparent), transparent 62%),
    radial-gradient(130% 120% at 50% 50%, transparent 58%, rgba(0, 0, 0, 0.34) 100%);
  z-index: 3;
}

body.theme-light .screen-container::after,
body.theme-not3270 .screen-container::after,
body.theme-io-daylight .screen-container::after,
body.theme-yorkshire .screen-container::after,
body.theme-authentic .screen-container::after {
  display: none;
}

/* ---------------------------------------------------------------- */
/* Operator Information Area                                          */
/* ---------------------------------------------------------------- */
/* Two things here are load-bearing and everything else is trim: the
   input-inhibit indicator ("X SYSTEM" / "X -f") and the online block.
   They keep a fixed position and a monospace width so an operator can
   find them without reading, the way they do on a real terminal. */
.screen-oia {
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--panel-2) 88%, var(--bg)), color-mix(in srgb, var(--bg) 62%, var(--panel-2)));
  color: var(--fg-muted);
  border: 1px solid color-mix(in srgb, var(--border) 90%, black);
  border-top: 1px solid var(--hairline-soft);
  border-radius: 0 0 var(--r-sm) var(--r-sm);
  display: flex;
  align-items: stretch;
  width: 100%;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  gap: 0;
  font-family: var(--mono);
  font-size: calc(var(--terminal-cell-size) * 0.82);
  line-height: 1.9;
  letter-spacing: 0.03em;
  white-space: nowrap;
  font-kerning: none;
  font-variant-ligatures: none;
  font-feature-settings: "liga" 0, "calt" 0, "kern" 0;
  box-shadow: inset 0 1px 0 var(--top-sheen);
}

/* The classic left-hand block: "4" online, "4-A" online and owned by an
   application. */
.screen-oia .oia-online {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 4.5ch;
  padding: 0 10px;
  border-right: 1px solid var(--hairline-soft);
  color: var(--ok);
  font-weight: 700;
}

/* Reserved width so the indicator appearing does not shove the rest of
   the bar sideways — a jumping OIA is harder to read at a glance than a
   mostly-empty one. */
.screen-oia .oia-indicator {
  display: inline-flex;
  align-items: center;
  min-width: 13ch;
  padding: 0 12px;
  border-right: 1px solid var(--hairline-soft);
  font-weight: 700;
  letter-spacing: 0.1em;
  transition: color var(--dur-2) var(--ease-out);
}

.screen-oia .oia-indicator[data-oia-state="ready"] { color: transparent; }
.screen-oia .oia-indicator[data-oia-state="wait"]  { color: var(--warn); }
.screen-oia .oia-indicator[data-oia-state="error"] { color: var(--danger); }

/* X -f is the one state the operator must act on, so it gets the only
   motion in the bar. */
.screen-oia .oia-indicator[data-oia-state="error"] {
  animation: oia-error-pulse 1.4s var(--ease-out) infinite;
}

@keyframes oia-error-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.55; }
}

@media (prefers-reduced-motion: reduce) {
  .screen-oia .oia-indicator[data-oia-state="error"] { animation: none; }
}

.screen-oia .oia-spacer {
  flex: 1 1 auto;
  border-right: 1px solid var(--hairline-soft);
}

/* Insert-mode caret, in the position the real OIA puts it. */
.screen-oia .oia-flag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 3ch;
  padding: 0 8px;
  border-right: 1px solid var(--hairline-soft);
  color: var(--accent);
  font-weight: 700;
}

.screen-oia .oia-flag[hidden] { display: none; }

.screen-oia .oia-field {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 12px;
  border-right: 1px solid var(--hairline-soft);
  color: color-mix(in srgb, var(--fg-muted) 82%, transparent);
  font-size: 0.86em;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.screen-oia .oia-field:last-of-type { border-right: none; }

.screen-oia .oia-field > span {
  color: var(--fg);
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: none;
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------- */
/* Workspace mode toggle                                              */
/* ---------------------------------------------------------------- */
/* Reads as a state label rather than a button: which surface you are on
   matters more than the fact that it can be clicked. */
/* ================================================================== */
/* Focus mode                                                         */
/* ================================================================== */
/* The terminal, filling the screen, and almost nothing else.
   Everything here works by relocating the REAL controls rather than
   duplicating them, so tooltips, event handlers and command-palette
   entries all keep working. See focus-mode.js. */

body[data-focus="on"] {
  overflow: hidden;
  /* The rail overlays the page, so the terminal reserves its height rather
     than sliding under it. Overlaying transiently would hide row 1 — which
     on a 3270 is the title and message line — every time someone reached
     for the toolbar. Reserving costs a fixed 44px of terminal height and is
     never wrong; the alternative is briefly wrong at the worst moment. */
  --focus-rail-h: 44px;
}

/* The decorative layer earns its keep on the connect page and the normal
   layout. In focus mode it is just noise behind a terminal. */
body[data-focus="on"] .bg-overlay {
  display: none;
}

/* Collapse every layer of framing between the viewport and the screen:
   page padding, the card, its border, its shadow. */
body[data-focus="on"] .page-wrap {
  max-width: none;
  width: 100vw;
  height: 100vh;
  height: 100dvh;
  margin: 0;
  padding: 0;
  display: flex;
}

body[data-focus="on"] .card {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
  margin: 0;
  padding: 0;
  border: none;
  border-radius: 0;
  background: var(--bg);
  box-shadow: none;
  /* The normal layout caps the card at 100vw - 40px so it floats inside the
     page gutter. Focus mode has no gutter to sit in: leaving the cap on left
     the terminal bezel 20px short of each edge, which is where its side
     lines used to disappear to. */
  width: 100%;
  min-width: 0;
  max-width: none;
}

body[data-focus="on"] .card > .stack,
body[data-focus="on"] .terminal-width-column {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
  gap: 0;
  width: 100%;
  max-width: none;
}

/* The terminal takes the space the chrome gave up, and stays centred so a
   24x80 screen on a wide display does not sit awkwardly against one edge. */
body[data-focus="on"] .terminal-shell {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 0;
  /* Horizontal margin stays auto. The bezel is width: fit-content — which is
     also what lets terminal-controls.js size the terminal, since it fits by
     measuring the shell's own box against the viewport — so zeroing the
     margin does not widen it, it just parks it against the left edge with
     dead space to its right. */
  margin: 0 auto;
  padding: clamp(8px, 1.5vh, 20px);
  /* Space for the rail, so it never covers the terminal's top row. */
  padding-top: calc(var(--focus-rail-h) + clamp(8px, 1.5vh, 20px));
  border-radius: 0;
  /* All four sides of the bezel stay drawn. A frame that is open on the left
     and right does not read as a terminal bezel, it reads as a rendering
     fault — and the focus-within accent, which is how the operator sees the
     screen has the keyboard, only tracks round a closed frame. */
  border-width: 1px;
  border-style: solid;
}

body[data-focus="on"] .screen-container {
  margin-inline: auto;
}

/* The OIA is the operator's instrument panel — it stays, always. Hiding it
   would be hiding X SYSTEM, which is the one thing they need. */
body[data-focus="on"] .screen-oia {
  width: 100%;
  max-width: var(--terminal-width, 100%);
  margin-inline: auto;
}

/* ---- the auto-hiding rail --------------------------------------- */

body[data-focus="on"] [data-focus-rail-zone] {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 10px;
  height: var(--focus-rail-h);
  padding: 0 12px;
  background: color-mix(in srgb, var(--panel-2) 92%, var(--bg));
  border-bottom: 1px solid var(--hairline);
  box-shadow: 0 8px 24px color-mix(in srgb, var(--bg) 70%, transparent);
  transform: translateY(-100%);
  transition: transform var(--dur-2) var(--ease-out);
}

body[data-focus="on"][data-focus-rail="shown"] [data-focus-rail-zone] {
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  body[data-focus="on"] [data-focus-rail-zone] {
    transition: none;
  }
}

/* A hairline at the very top edge, always visible: without it the rail is
   undiscoverable, and a mode with no visible way out is a trap. */
body[data-focus="on"] [data-focus-rail-zone]::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -5px;
  transform: translateX(-50%);
  width: 46px;
  height: 3px;
  border-radius: 0 0 3px 3px;
  background: color-mix(in srgb, var(--accent) 55%, transparent);
  opacity: 1;
  transition: opacity var(--dur-1) var(--ease-out);
}

body[data-focus="on"][data-focus-rail="shown"] [data-focus-rail-zone]::after {
  opacity: 0;
}

/* Inside the rail the menu bar lays itself out along one line; see
   menu-bar.css for the rest of it. */
body[data-focus="on"] [data-focus-rail-zone] .session-tab-bar {
  margin: 0;
  border-bottom: none;
  flex: 1 1 auto;
  min-width: 0;
}

/* The wordmark is the first thing to go — you know which application you
   are in when it is the only thing on screen. */
body[data-focus="on"] [data-chrome-title] {
  display: none;
}

body[data-focus="on"] .session-tab {
  margin-bottom: 0;
}

/* In the rail the button keeps its icon and drops its words. Space is the
   scarce thing there, and by the time somebody is in focus mode they have
   met the labelled version on the ordinary screen. */
body[data-focus="on"] [data-focus-rail-zone] .session-tab-new-label {
  display: none;
}

body[data-focus="on"] [data-focus-rail-zone] .session-tab-new {
  margin-left: 6px;
  padding-inline: 6px;
}

/* Scale the rail's controls down to fit its height without cramping. */
body[data-focus="on"] [data-focus-rail-zone] .icon-button,
body[data-focus="on"] [data-focus-rail-zone] button,
body[data-focus="on"] [data-focus-rail-zone] a {
  padding-block: 4px;
  font-size: 0.74rem;
}

body[data-focus="on"] [data-focus-rail-zone] .icon-button svg {
  width: 15px;
  height: 15px;
}

body[data-focus="on"] [data-focus-rail-zone] .session-tab-label {
  padding-block: 3px;
}

/* Toasts stack in the bottom-right, which in focus mode is where the OIA
   now reaches. Lift them clear so a notification never sits on top of the
   inhibit indicator. */
body[data-focus="on"] #h3270-notification-container {
  bottom: 96px;
}

body[data-focus="on"] .focus-toggle-button[aria-pressed="true"] {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
}

/* ---------------------------------------------------------------- */
/* Sample-app provenance chip                                         */
/* ---------------------------------------------------------------- */
/* Was a full-width banner reading "Sample App 1 — Running on port 3274"
   above the terminal. That is which local test server is running, which
   is a development detail; it is a compact chip now, and workspace-mode
   keeps it out of the business surface entirely. */
.sample-status {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  align-self: flex-start;
  margin: 0 0 8px;
  padding: 3px 10px;
  border: 1px solid var(--hairline-soft);
  border-radius: 999px;
  background: color-mix(in srgb, var(--panel-2) 60%, transparent);
  font-size: 0.72rem;
  line-height: 1.6;
}

.sample-status[hidden] {
  display: none;
}

.sample-status .sample-app-title {
  font-weight: 600;
  font-size: 0.72rem;
  color: var(--fg-muted);
  letter-spacing: 0.02em;
}

.sample-status .subtle {
  font-family: var(--mono);
  font-size: 0.68rem;
  font-variant-numeric: tabular-nums;
}

body[data-focus="on"] .sample-status {
  display: none;
}

/* ---------------------------------------------------------------- */
/* Session tabs                                                       */
/* ---------------------------------------------------------------- */
/* Sits directly above the toolbar and reads as a row of connections,
   not another group of buttons — which is why the active tab is joined
   to what is below it rather than outlined as a control. */
.session-tab-bar {
  display: flex;
  align-items: stretch;
  gap: 4px;
  margin-bottom: 10px;
  padding-bottom: 0;
  border-bottom: 1px solid var(--hairline);
  overflow-x: auto;
  scrollbar-width: thin;
}

.session-tab-bar[hidden] {
  display: none;
}

/* The tabs scroll; the New session button does not go with them. Somebody
   with six sessions open should not have to scroll the bar to find the
   control that made them.

   `0 1 auto` rather than `1 1 auto` so the button sits immediately after the
   last tab — the position it holds in every tabbed interface people already
   use — instead of floating alone at the far right. Once the tabs are wide
   enough to fill the row the list takes the space and the button ends up
   pinned right anyway, which is where it is wanted by then. */
.session-tab-list {
  display: flex;
  align-items: stretch;
  gap: 4px;
  flex: 0 1 auto;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: thin;
}

/* Carries its own words. It is deliberately not an .icon-button: this is
   the entry point to a feature, and an unlabelled square in a row of
   unlabelled squares is how the feature stayed invisible. */
.session-tab-new {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  align-self: center;
  margin-left: 8px;
  padding: 4px 10px;
  background: transparent;
  border: 1px dashed var(--border);
  border-radius: var(--r-sm);
  color: var(--fg-muted);
  font-size: 0.74rem;
  letter-spacing: 0.02em;
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out);
}

.session-tab-new svg {
  width: 14px;
  height: 14px;
  fill: currentColor;
  flex: 0 0 auto;
}

.session-tab-new:hover:not(:disabled),
.session-tab-new:focus-visible {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
  border-style: solid;
}

.session-tab-new:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.session-tab-new:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* Narrow screens keep the icon and drop the words — by the time the bar is
   this tight the button has already done its teaching. */
@media (max-width: 640px) {
  .session-tab-new-label {
    display: none;
  }

  .session-tab-new {
    padding: 4px 6px;
  }
}

.session-tab {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  flex: 0 0 auto;
  max-width: 22ch;
  padding: 0 2px 0 0;
  border: 1px solid transparent;
  border-bottom: none;
  border-radius: var(--r-sm) var(--r-sm) 0 0;
  background: transparent;
  /* Overlap the bar's own border so the active tab reads as continuous
     with the terminal below it. */
  margin-bottom: -1px;
}

.session-tab.is-active {
  background: var(--panel-2);
  border-color: var(--hairline);
  box-shadow: inset 0 2px 0 var(--accent);
}

.session-tab-label {
  flex: 1 1 auto;
  min-width: 0;
  padding: 6px 4px 6px 10px;
  background: transparent;
  border: none;
  color: var(--fg-muted);
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}

.session-tab.is-active .session-tab-label {
  color: var(--fg);
  font-weight: 600;
  cursor: default;
}

.session-tab:not(.is-active):hover .session-tab-label {
  color: var(--fg);
}

.session-tab-close {
  flex: 0 0 auto;
  padding: 0 6px;
  background: transparent;
  border: none;
  border-radius: var(--r-xs);
  color: var(--fg-muted);
  font-size: 0.95rem;
  line-height: 1;
  cursor: pointer;
  opacity: 0.55;
}

.session-tab-close:hover,
.session-tab-close:focus-visible {
  opacity: 1;
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 14%, transparent);
}

.session-tab-label:focus-visible,
.session-tab-close:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* ---------------------------------------------------------------- */
/* File transfer (IND$FILE)                                           */
/* ---------------------------------------------------------------- */
.transfer-modal {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.transfer-modal[hidden] { display: none; }

.transfer-modal-backdrop {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

.transfer-modal-content {
  position: relative;
  width: min(96vw, 620px);
  max-height: 88vh;
  overflow: auto;
  padding: 18px 20px;
  background: var(--glass-strong);
  backdrop-filter: blur(18px) saturate(130%);
  -webkit-backdrop-filter: blur(18px) saturate(130%);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  box-shadow: var(--elev-3);
}

.transfer-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}

.transfer-modal-header h3 { margin: 0; font-size: 1.02rem; }

.transfer-modal-content form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.transfer-direction {
  display: flex;
  gap: 16px;
  padding: 8px 10px;
  border: 1px solid var(--hairline-soft);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--panel-2) 45%, transparent);
}

/* Native checkboxes and radios default to the browser's blue, which reads
   as a foreign object against every one of the terminal themes. */
.transfer-modal input[type="radio"],
.transfer-modal input[type="checkbox"],
.profiles-modal input[type="checkbox"],
.profiles-modal input[type="radio"] {
  accent-color: var(--accent);
}

.transfer-direction label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.82rem;
  cursor: pointer;
}

.transfer-modal-content label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.76rem;
  color: var(--fg-muted);
}

.transfer-modal-content input[type="text"],
.transfer-modal-content input[type="number"],
.transfer-modal-content input[type="file"],
.transfer-modal-content select {
  width: 100%;
  box-sizing: border-box;
}

.transfer-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 10px;
}

/* Direction and mode drive which fields are relevant. Hiding the
   irrelevant ones beats disabling them: an operator receiving a file has
   no reason to read about record formats at all. */
.transfer-modal[data-transfer-direction="receive"] .transfer-send-only { display: none; }
.transfer-modal[data-transfer-mode="binary"] .transfer-ascii-only { display: none; }

.transfer-advanced {
  border: 1px solid var(--hairline-soft);
  border-radius: var(--r-sm);
  padding: 8px 10px;
  background: color-mix(in srgb, var(--panel-2) 35%, transparent);
}

.transfer-advanced summary {
  cursor: pointer;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--fg-muted);
}

.transfer-advanced > .subtle { font-size: 0.72rem; margin: 6px 0 10px; }

.transfer-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  justify-content: flex-end;
}

.transfer-status {
  flex: 1 1 auto;
  font-size: 0.76rem;
  color: var(--fg-muted);
}

.transfer-status.is-error { color: var(--danger); }

/* A transfer runs at 3270 data-stream speed, so the wait is real and the
   indicator has to look alive. */
.transfer-status.is-busy {
  color: var(--accent);
  animation: transfer-pulse 1.4s var(--ease-out) infinite;
}

@keyframes transfer-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}

@media (prefers-reduced-motion: reduce) {
  .transfer-status.is-busy { animation: none; }
}

.transfer-note { font-size: 0.72rem; margin: 0; }

/* ---------------------------------------------------------------- */
/* Guided Business Tasks                                              */
/* ---------------------------------------------------------------- */
.tasks-modal {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.tasks-modal[hidden] { display: none; }

.tasks-modal-backdrop {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

.tasks-modal-content {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: min(96vw, 560px);
  max-height: 88vh;
  padding: 18px 20px;
  background: var(--glass-strong);
  backdrop-filter: blur(18px) saturate(130%);
  -webkit-backdrop-filter: blur(18px) saturate(130%);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  box-shadow: var(--elev-3);
}

.tasks-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.tasks-modal-header h3 { margin: 0; font-size: 1.02rem; }

/* The chooser and the form are one dialog in two states, so picking a task
   and filling it in do not feel like two different places. */
.tasks-modal[data-task-view="list"] .tasks-form,
.tasks-modal[data-task-view="form"] .tasks-list { display: none; }

.tasks-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  overflow-y: auto;
  min-height: 0;
}

.task-row {
  display: grid;
  gap: 2px;
  text-align: left;
  padding: 10px 12px;
  border: 1px solid var(--hairline-soft);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--panel-2) 40%, transparent);
  cursor: pointer;
  transition: border-color var(--t-fast) var(--ease-out), transform var(--t-fast) var(--ease-out);
}

.task-row:hover,
.task-row:focus-visible {
  border-color: var(--accent);
  transform: translateY(-1px);
}

.task-row-name { font-weight: 600; font-size: 0.88rem; }
.task-row-desc { font-size: 0.78rem; color: var(--fg-muted); }
.task-row-meta { font-size: 0.7rem; color: var(--fg-muted); opacity: 0.8; }

.task-empty { font-size: 0.8rem; line-height: 1.5; margin: 0; }

.tasks-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
  overflow-y: auto;
  min-height: 0;
}

.task-form-title { margin: 0; font-size: 0.94rem; }
.task-form-desc { margin: 0; font-size: 0.8rem; }

.task-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.task-field-label {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 0.8rem;
  font-weight: 600;
}

.task-field-required {
  font-size: 0.66rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--fg-muted);
}

.task-field input { width: 100%; box-sizing: border-box; }
.task-field-hint { font-size: 0.72rem; }

.task-form-actions,
.task-card-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: flex-end;
}

.task-form-status {
  flex: 1 1 180px;
  font-size: 0.76rem;
  color: var(--fg-muted);
}

.task-form-status.is-error { color: var(--danger); }

.task-run-btn { font-weight: 600; }

/* ---- the run card ------------------------------------------------
   Deliberately a corner card rather than a modal: a task run has to leave
   the terminal visible. Operators build trust in the automation by watching
   the host do the work, and when a run stops early the screen it stopped on
   is the diagnosis. */
.task-card {
  position: fixed;
  left: 20px;
  /* Set by tasks.js from the OIA's measured position, so the card clears it
     whatever the terminal model, window size or focus mode. The fallback is
     only used if the OIA is not on screen yet. */
  bottom: var(--task-card-bottom, 96px);
  z-index: 65;
  width: min(92vw, 380px);
  max-height: min(70vh, 620px);
  overflow-y: auto;
  padding: 14px 16px;
  background: var(--glass-strong);
  backdrop-filter: blur(16px) saturate(130%);
  -webkit-backdrop-filter: blur(16px) saturate(130%);
  border: 1px solid var(--hairline);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-md);
  box-shadow: var(--elev-3);
  animation: task-card-in var(--t-med) var(--ease-out);
}

@keyframes task-card-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .task-card { animation: none; }
}

/* The left edge carries the outcome, so it reads at a glance from across
   a desk without needing the text. */
.task-card[data-task-state="done"] { border-left-color: var(--success, var(--accent)); }
.task-card[data-task-state="failed"] { border-left-color: var(--danger); }

.task-card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 8px;
}

.task-card-head strong { font-size: 0.88rem; }
.task-card-took { font-size: 0.72rem; }

.task-card-step,
.task-card-reason {
  margin: 0 0 10px;
  font-size: 0.8rem;
  line-height: 1.45;
}

.task-card[data-task-state="running"] .task-card-step {
  color: var(--accent);
  animation: task-pulse 1.4s var(--ease-out) infinite;
}

@keyframes task-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}

@media (prefers-reduced-motion: reduce) {
  .task-card[data-task-state="running"] .task-card-step { animation: none; }
}

.task-result-values,
.task-card-diff {
  display: grid;
  grid-template-columns: minmax(90px, auto) 1fr;
  gap: 4px 12px;
  margin: 0 0 12px;
  font-size: 0.8rem;
}

.task-result-values dt,
.task-card-diff dt {
  color: var(--fg-muted);
  font-size: 0.74rem;
}

.task-result-values dd,
.task-card-diff dd {
  margin: 0;
  font-family: var(--mono, monospace);
  overflow-wrap: anywhere;
}

/* A value that could not be read is never shown as a blank: an empty row
   reads as "the answer is nothing", which is not what happened. */
.task-result-values dd.is-missing {
  color: var(--danger);
  font-family: inherit;
  font-style: italic;
}

.task-card-screen { margin-bottom: 10px; }

.task-card-screen summary {
  cursor: pointer;
  font-size: 0.76rem;
  color: var(--fg-muted);
}

.task-card-screen pre {
  margin: 8px 0 0;
  padding: 8px;
  max-height: 240px;
  overflow: auto;
  font-size: 0.62rem;
  line-height: 1.35;
  white-space: pre;
  background: var(--panel-2);
  border: 1px solid var(--hairline-soft);
  border-radius: var(--r-sm);
}

.task-card-note { margin: 0 0 10px; font-size: 0.72rem; }

/* ---------------------------------------------------------------- */
/* Task authoring wizard                                              */
/* ---------------------------------------------------------------- */
.wizard-modal {
  position: fixed;
  inset: 0;
  z-index: 72;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.wizard-modal[hidden] { display: none; }

.wizard-modal-backdrop {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--bg) 74%, transparent);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

.wizard-modal-content {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: min(96vw, 860px);
  max-height: 90vh;
  padding: 18px 20px;
  background: var(--glass-strong);
  backdrop-filter: blur(18px) saturate(130%);
  -webkit-backdrop-filter: blur(18px) saturate(130%);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  box-shadow: var(--elev-3);
}

.wizard-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.wizard-modal-header h3 { margin: 0; font-size: 1.02rem; }

.wizard-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding-right: 4px;
}

.wizard-section { display: flex; flex-direction: column; gap: 8px; }
.wizard-section h4 { margin: 0; font-size: 0.86rem; }
.wizard-section > .subtle { margin: 0; font-size: 0.76rem; line-height: 1.45; }

/* The server's assumptions, shown rather than buried. This is the difference
   between a wizard that asks and one that quietly guesses. */
.wizard-notes {
  border: 1px solid var(--hairline-soft);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-sm);
  padding: 8px 12px;
  background: color-mix(in srgb, var(--panel-2) 40%, transparent);
}

.wizard-notes summary {
  cursor: pointer;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--fg-muted);
}

.wizard-notes ul { margin: 8px 0 0; padding-left: 18px; }
.wizard-notes li { font-size: 0.74rem; line-height: 1.5; margin-bottom: 4px; }

/* The stage bar. The wizard is five stages rather than one long scroll
   because the questions it asks are different in kind — what the task is
   called, what it asks for, what it checks, what it answers with — and a
   single form makes all four look like the same routine detail. */
.wizard-stepper {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  padding-bottom: 2px;
  border-bottom: 1px solid var(--hairline-soft);
}

.wizard-step {
  font-size: 0.74rem;
  padding: 5px 10px;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--fg-muted);
  cursor: pointer;
  white-space: nowrap;
}

.wizard-step:hover { color: var(--fg); background: color-mix(in srgb, var(--panel-2) 50%, transparent); }
.wizard-step.is-done { color: var(--fg); }

.wizard-step.is-active {
  color: var(--fg);
  font-weight: 600;
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 16%, transparent);
}

.wizard-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 12px;
  border: 1px solid var(--hairline-soft);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--panel-2) 30%, transparent);
}

/* An unguarded step is the one defect that makes a task dangerous rather
   than merely wrong, so it is marked on the card itself. */
.wizard-card.is-unguarded { border-left: 3px solid var(--danger, #d9534f); }

.wizard-card-head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.wizard-card-head strong { font-size: 0.8rem; }
.wizard-card-head .subtle { font-size: 0.72rem; }
.wizard-card-head .wizard-output-label { flex: 1 1 160px; }

.wizard-facts { margin: 0; padding-left: 18px; }
.wizard-facts li { font-size: 0.76rem; line-height: 1.6; }

.wizard-param {
  display: grid;
  grid-template-columns: minmax(120px, 1.4fr) minmax(140px, auto) minmax(90px, 1fr) auto;
  align-items: center;
  gap: 8px;
}

.wizard-param input,
.wizard-param select,
.wizard-output input { width: 100%; box-sizing: border-box; font-size: 0.78rem; }

.wizard-param-where,
.wizard-output .subtle { font-size: 0.7rem; white-space: nowrap; }

.wizard-check {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 0.72rem;
  white-space: nowrap;
  color: var(--fg-muted);
}

.wizard-check input { width: auto; }
.wizard-check.is-disabled { opacity: 0.45; }

.wizard-warning {
  margin: 0;
  font-size: 0.72rem;
  line-height: 1.5;
  color: var(--fg-muted);
  border-left: 3px solid var(--accent);
  padding-left: 8px;
}

.wizard-warning.is-error {
  color: var(--danger, #d9534f);
  border-left-color: var(--danger, #d9534f);
}

.wizard-advanced { font-size: 0.74rem; }
.wizard-advanced summary { cursor: pointer; color: var(--fg-muted); }

.wizard-advanced-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 8px;
  margin-top: 8px;
}

.wizard-advanced-grid input { width: 100%; box-sizing: border-box; font-size: 0.78rem; }
.wizard-advanced .subtle { margin: 8px 0 0; font-size: 0.7rem; line-height: 1.5; }

/* Guards ------------------------------------------------------------ */
.wizard-guards { display: flex; flex-direction: column; gap: 6px; }

.wizard-guard {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.wizard-guard > input[type="text"] {
  flex: 1 1 180px;
  min-width: 120px;
  font-family: var(--mono, monospace);
  font-size: 0.76rem;
}

.wizard-number {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.7rem;
  color: var(--fg-muted);
}

.wizard-number input { width: 68px; font-size: 0.76rem; }

.wizard-candidates {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
}

.wizard-candidates .subtle { font-size: 0.7rem; }

.wizard-candidate {
  font-family: var(--mono, monospace);
  font-size: 0.72rem;
  padding: 3px 8px;
  border-radius: var(--r-sm);
}

.wizard-add { font-size: 0.72rem; align-self: flex-start; }

.wizard-input-param {
  flex: 1 1 180px;
  font-family: var(--mono, monospace);
  font-size: 0.76rem;
  padding: 4px 8px;
  border-radius: var(--r-sm);
  background: var(--panel-2);
  color: var(--fg-muted);
}

.wizard-inline-actions { display: flex; gap: 8px; flex-wrap: wrap; }

.wizard-screen-wrap { display: flex; flex-direction: column; gap: 6px; }
.wizard-screen-wrap .subtle { margin: 0; font-size: 0.72rem; }

/* The screen the flow ended on, as a static picking surface. Rendered as a
   plain <pre> so the same cell arithmetic the live terminal uses applies
   here — see screenGrid.metricsFor. */
.wizard-screen {
  position: relative;
  overflow: auto;
  max-height: 42vh;
  padding: 8px;
  background: var(--term-bg, #000);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
  cursor: crosshair;
  /* Text selection would fight the drag gesture for the same pointer. */
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}

.wizard-screen pre {
  margin: 0;
  /* Hugging its own text, not the panel. A block-level <pre> is as wide as
     the container, and the cell geometry is that box divided by the column
     count — so a screen narrower than the panel put every cell about 50%
     too far right, and a click on a value marked whatever sat well to the
     left of it. */
  display: inline-block;
  min-width: max-content;
  font-family: var(--term-font, monospace);
  font-size: 0.68rem;
  line-height: 1.25;
  white-space: pre;
  color: var(--term-fg, #33ff66);
}

.wizard-marquee {
  position: absolute;
  pointer-events: none;
  border: 1px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 24%, transparent);
  border-radius: 2px;
}

.wizard-marquee[hidden] { display: none; }

/* Everything already marked stays drawn on the screen, numbered to match its
   card. Marking four values on a dense screen without that is guesswork about
   which one you already have. */
.wizard-regions { position: absolute; inset: 0; pointer-events: none; }

.wizard-region {
  position: absolute;
  border: 1px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  border-radius: 2px;
}

.wizard-region.is-absent {
  border-color: var(--danger, #d9534f);
  border-style: dashed;
  background: color-mix(in srgb, var(--danger, #d9534f) 14%, transparent);
}

.wizard-region-badge {
  position: absolute;
  top: -7px;
  left: -7px;
  min-width: 14px;
  height: 14px;
  padding: 0 3px;
  border-radius: 7px;
  background: var(--accent);
  color: var(--bg);
  font-size: 0.6rem;
  line-height: 14px;
  text-align: center;
  font-weight: 700;
}

.wizard-card-head .wizard-region-badge { position: static; }

/* Outputs ----------------------------------------------------------- */
.wizard-outputs { display: flex; flex-direction: column; gap: 8px; }
.wizard-outputs > .subtle { margin: 0; font-size: 0.74rem; line-height: 1.5; }

.wizard-output-region {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.wizard-output-reads {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  font-size: 0.72rem;
}

.wizard-output-sample {
  font-family: var(--mono, monospace);
  font-size: 0.74rem;
  padding: 2px 6px;
  border-radius: 3px;
  background: var(--panel-2);
  overflow-wrap: anywhere;
}

.wizard-output-sample.is-missing { color: var(--danger, #d9534f); }
.wizard-output-pattern input { font-family: var(--mono, monospace); }

/* Review ------------------------------------------------------------ */
.wizard-review {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px;
  border: 1px solid var(--hairline-soft);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--panel-2) 30%, transparent);
}

.wizard-review-head { display: flex; flex-direction: column; gap: 3px; }
.wizard-review-head strong { font-size: 0.9rem; }
.wizard-review-head .subtle { font-size: 0.76rem; }
.wizard-review .task-result-values { margin: 0; }

/* Where the task can be run from once it is saved. A task is a menu entry,
   an MCP tool and an API operation from the moment it exists; saying so here
   is the only place the person who built it is looking. */
.wizard-uses {
  border: 1px solid var(--hairline-soft);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-sm);
  padding: 8px 12px;
  background: color-mix(in srgb, var(--panel-2) 40%, transparent);
}

.wizard-uses summary {
  cursor: pointer;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--fg-muted);
}

.wizard-uses-list { margin: 8px 0 0; font-size: 0.74rem; line-height: 1.55; }
.wizard-uses-list dt { font-weight: 600; margin-top: 8px; }
.wizard-uses-list dd { margin: 2px 0 0; color: var(--fg-muted); }

.wizard-copyable {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--mono, monospace);
  padding: 1px 4px 1px 6px;
  border-radius: 3px;
  background: var(--panel-2);
}

.wizard-snippet {
  position: relative;
  margin: 6px 0 0;
  padding: 8px 10px;
  overflow-x: auto;
  font-family: var(--mono, monospace);
  font-size: 0.68rem;
  line-height: 1.5;
  white-space: pre;
  background: var(--panel-2);
  border-radius: var(--r-sm);
}

.wizard-snippet .wizard-copy { position: absolute; top: 6px; right: 6px; }
.wizard-copy { font-size: 0.64rem; padding: 1px 6px; }

.wizard-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  justify-content: flex-end;
}

.wizard-actions .task-form-status { margin-right: auto; }
.wizard-nav { display: inline-flex; gap: 8px; }

@media (max-width: 620px) {
  .wizard-param { grid-template-columns: 1fr 1fr; }
  .wizard-stepper { font-size: 0.7rem; }
}

/* ---------------------------------------------------------------- */
/* Keyboard mapping                                                   */
/* ---------------------------------------------------------------- */
.keymap-modal {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.keymap-modal[hidden] { display: none; }

.keymap-modal-backdrop {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

.keymap-modal-content {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: min(96vw, 660px);
  max-height: 88vh;
  padding: 18px 20px;
  background: var(--glass-strong);
  backdrop-filter: blur(18px) saturate(130%);
  -webkit-backdrop-filter: blur(18px) saturate(130%);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  box-shadow: var(--elev-3);
}

.keymap-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.keymap-modal-header h3 { margin: 0; font-size: 1.02rem; }

.keymap-filter { width: 100%; box-sizing: border-box; }

/* The action list is the only part that scrolls, so the filter box and the
   import/export row stay reachable however long the list gets — 24 PF keys
   plus the editing actions is far more than fits on screen. */
.keymap-list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding-right: 4px;
  border: 1px solid var(--hairline-soft);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--panel-2) 35%, transparent);
}

.keymap-group {
  position: sticky;
  top: 0;
  z-index: 1;
  margin: 0;
  padding: 8px 10px 6px;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
  background: var(--panel);
  border-bottom: 1px solid var(--hairline-soft);
}

.keymap-row {
  display: grid;
  grid-template-columns: 1fr minmax(120px, 1.2fr) auto auto;
  align-items: center;
  gap: 8px;
  padding: 5px 10px;
  border-bottom: 1px solid var(--hairline-soft);
}

.keymap-row:last-child { border-bottom: none; }

.keymap-row.is-capturing {
  background: color-mix(in srgb, var(--accent) 16%, transparent);
}

.keymap-action {
  font-size: 0.8rem;
  font-weight: 600;
}

.keymap-keys {
  font-family: var(--mono, monospace);
  font-size: 0.76rem;
  color: var(--fg);
  overflow-wrap: anywhere;
}

/* A key shown greyed is the built-in default, not something the operator
   chose — the distinction matters when deciding what is safe to rebind.
   The muted colour carries that on its own; the 0.75 opacity that used to sit
   on top of it took the label to 4.04:1 against the panel, under the 4.5:1
   that WCAG 1.4.3 asks of body text. */
.keymap-keys.is-default {
  color: var(--fg-muted);
}

/* No keyboard default at all — the label is prose, not a key name. */
.keymap-keys.is-unbound {
  font-family: inherit;
  font-style: italic;
}

.keymap-keys.is-waiting {
  color: var(--accent);
  font-style: italic;
}

.keymap-action-btn {
  padding: 3px 10px;
  font-size: 0.72rem;
  white-space: nowrap;
}

.keymap-action-btn[disabled] { opacity: 0.4; cursor: default; }

.keymap-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: flex-end;
}

.keymap-status {
  flex: 1 1 220px;
  font-size: 0.76rem;
  color: var(--fg-muted);
}

.keymap-status.is-error { color: var(--danger); }

.keymap-note { font-size: 0.72rem; margin: 0; }

@media (max-width: 560px) {
  .keymap-row { grid-template-columns: 1fr 1fr; }
  .keymap-action,
  .keymap-keys { grid-column: 1 / -1; }
}

/* ---------------------------------------------------------------- */
/* Connection profiles                                                */
/* ---------------------------------------------------------------- */
.profiles-modal {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.profiles-modal[hidden] { display: none; }

.profiles-modal-backdrop {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

.profiles-modal-content {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 14px;
  width: min(96vw, 760px);
  max-height: 88vh;
  overflow: auto;
  padding: 18px 20px;
  background: var(--glass-strong);
  backdrop-filter: blur(18px) saturate(130%);
  -webkit-backdrop-filter: blur(18px) saturate(130%);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  box-shadow: var(--elev-3);
}

.profiles-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.profiles-modal-header h3 { margin: 0; font-size: 1.02rem; }

.profiles-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.profile-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border: 1px solid var(--hairline-soft);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--panel-2) 45%, transparent);
}

.profile-row:hover { border-color: var(--border); }

.profile-row-main {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  grid-template-columns: minmax(8ch, 18ch) 1fr;
  grid-template-areas: "name target" "desc desc";
  gap: 2px 12px;
  text-align: left;
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  color: inherit;
}

.profile-row-name {
  grid-area: name;
  font-weight: 600;
  font-size: 0.86rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The target reads exactly as s3270 will see it, so what an operator checks
   is what actually gets dialled. */
.profile-row-target {
  grid-area: target;
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--fg-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.profile-row-desc {
  grid-area: desc;
  font-size: 0.72rem;
}

.profile-row-desc:empty { display: none; }

.profile-row-tls {
  flex: 0 0 auto;
  padding: 2px 7px;
  border-radius: 999px;
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--ok);
  background: color-mix(in srgb, var(--ok) 16%, transparent);
}

/* Encrypted-but-unverified is weaker than TLS and should not wear the same
   badge as the real thing. */
.profile-row-tls.is-unverified {
  color: var(--warn);
  background: color-mix(in srgb, var(--warn) 18%, transparent);
}

.profile-row-action {
  flex: 0 0 auto;
  padding: 3px 9px;
  font-size: 0.72rem;
}

.profile-row-action.danger { color: var(--danger); }

/* The bundled sample apps, and the typed-address box beside them. Both belong
   to picking a session to open, so both are drawn only in that mode — see
   renderSamples() in connection-profiles.js. */
.profiles-samples,
.profiles-direct {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-top: 12px;
  border-top: 1px solid var(--hairline-soft);
}

/* [hidden] has to win: both blocks above set display, which would otherwise
   override it and leave them on in the manage view. */
.profiles-samples[hidden],
.profiles-direct[hidden] {
  display: none;
}

.profiles-samples h4 {
  margin: 0;
  font-size: 0.82rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-muted);
}

.profiles-samples p { margin: 0; font-size: 0.74rem; }

.profiles-sample-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* Wider name column than a profile row: a sample app's name is the whole of
   what identifies it, and the port beside it is three digits. */
.profile-sample-row {
  grid-template-columns: 1fr auto;
  align-items: baseline;
  padding: 8px 10px;
  border: 1px solid var(--hairline-soft);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--panel-2) 45%, transparent);
}

.profile-sample-row:hover { border-color: var(--border); }

.profiles-direct-row {
  display: flex;
  align-items: flex-end;
  gap: 8px;
}

.profiles-direct-row label {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.76rem;
  color: var(--fg-muted);
}

.profiles-direct-row input {
  width: 100%;
  box-sizing: border-box;
}

.profiles-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 12px;
  border-top: 1px solid var(--hairline-soft);
}

.profiles-form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 10px;
}

.profiles-form label,
.profiles-check {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.76rem;
  color: var(--fg-muted);
}

.profiles-check {
  flex-direction: row;
  align-items: center;
  gap: 8px;
}

.profiles-form input[type="text"],
.profiles-form input[type="number"],
.profiles-form select {
  width: 100%;
  box-sizing: border-box;
}

/* The bundled sample apps, offered above the connection fields they fill in. */
.profiles-sample {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.profiles-sample[hidden] {
  display: none;
}

.profiles-sample-hint {
  margin: 2px 0 0;
  font-size: 0.72rem;
  line-height: 1.45;
}

.profiles-form-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  justify-content: flex-end;
}

.profiles-status {
  flex: 1 1 auto;
  font-size: 0.74rem;
  color: var(--fg-muted);
}

.profiles-status.is-error { color: var(--danger); }

/* In "pick a profile and connect" mode the editor is out of the way — you
   came here to connect, not to administer. */
.profiles-modal[data-profile-mode="pick"] .profiles-form {
  opacity: 0.72;
}

/* ---------------------------------------------------------------- */
/* Screen history                                                     */
/* ---------------------------------------------------------------- */
.history-modal {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.history-modal[hidden] {
  display: none;
}

.history-modal-backdrop {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--bg) 70%, transparent);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

.history-modal-content {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: min(96vw, 1100px);
  max-height: 88vh;
  padding: 16px 18px 14px;
  background: var(--glass-strong);
  backdrop-filter: blur(18px) saturate(130%);
  -webkit-backdrop-filter: blur(18px) saturate(130%);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  box-shadow: var(--elev-3);
}

.history-modal-header {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}

.history-modal-header h3 {
  margin: 0;
  font-size: 1rem;
}

.history-modal-actions {
  display: flex;
  gap: 6px;
  margin-left: auto;
  flex-wrap: wrap;
}

.history-modal-meta {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--fg-muted);
  font-variant-numeric: tabular-nums;
}

/* The history view is a plain monospace block, not the interactive form —
   history is read-only, and rendering it as fields would invite typing
   into a screen that no longer exists. */
.history-modal-view {
  margin: 0;
  padding: 12px 14px;
  overflow: auto;
  flex: 1 1 auto;
  min-height: 0;
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  border: 1px solid var(--hairline-soft);
  border-radius: var(--r-sm);
  color: var(--fg);
  font-family: var(--mono);
  font-size: 0.78rem;
  line-height: 1.35;
  white-space: pre;
  font-kerning: none;
  font-variant-ligatures: none;
  font-feature-settings: "liga" 0, "calt" 0, "kern" 0;
}

.history-modal-note {
  font-size: 0.72rem;
}

/* ---------------------------------------------------------------- */
/* Hotspots                                                           */
/* ---------------------------------------------------------------- */
/* Invisible until hovered. The legend text the application printed is
   already legible — decorating it permanently would be redecorating the
   host's screen, which is not this layer's business. The affordance is
   the cursor plus an underline on approach. */
.screen-hotspot {
  position: absolute;
  z-index: 4;
  margin: 0;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: 2px;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  color: inherit;
  font: inherit;
  transition: background var(--dur-1) var(--ease-out), box-shadow var(--dur-1) var(--ease-out);
}

.screen-hotspot:hover,
.screen-hotspot:focus-visible {
  background: color-mix(in srgb, var(--accent) 26%, transparent);
  box-shadow: inset 0 -2px 0 var(--accent);
  outline: none;
}

.screen-hotspot:focus-visible {
  box-shadow: inset 0 -2px 0 var(--accent), 0 0 0 2px var(--accent);
}

.screen-hotspot-url:hover,
.screen-hotspot-url:focus-visible {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
}

body[data-hotspots="off"] .screen-hotspot {
  display: none;
}

/* ---------------------------------------------------------------- */
/* Find on screen                                                     */
/* ---------------------------------------------------------------- */
.screen-find-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 auto 8px;
  padding: 6px 10px;
  width: 100%;
  max-width: var(--terminal-width, 100%);
  box-sizing: border-box;
  background: var(--glass-strong);
  backdrop-filter: blur(14px) saturate(130%);
  -webkit-backdrop-filter: blur(14px) saturate(130%);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
  box-shadow: var(--elev-2);
}

.screen-find-bar[hidden] {
  display: none;
}

.screen-find-bar > svg {
  width: 15px;
  height: 15px;
  flex: 0 0 auto;
  fill: var(--fg-muted);
}

.screen-find-input {
  flex: 1 1 auto;
  min-width: 0;
  background: transparent;
  border: none;
  outline: none;
  color: var(--fg);
  font-family: var(--mono);
  font-size: 0.82rem;
  padding: 2px 0;
}

.screen-find-status {
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--fg-muted);
  font-variant-numeric: tabular-nums;
  min-width: 7ch;
  text-align: right;
}

.screen-find-btn {
  flex: 0 0 auto;
  padding: 2px 7px;
  font-size: 0.8rem;
  line-height: 1.2;
  background: transparent;
  border: 1px solid var(--hairline-soft);
  border-radius: var(--r-xs);
  color: var(--fg-muted);
  cursor: pointer;
}

.screen-find-btn:hover {
  color: var(--fg);
  border-color: var(--border);
}

/* Every hit is marked; the current one is picked out more strongly, the
   same convention as a browser's own find. */
.screen-find-match {
  position: absolute;
  pointer-events: none;
  z-index: 3;
  border-radius: 2px;
  background: color-mix(in srgb, var(--warn) 34%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--warn) 60%, transparent);
}

.screen-find-match.is-active {
  background: color-mix(in srgb, var(--accent) 46%, transparent);
  box-shadow: inset 0 0 0 1px var(--accent), 0 0 10px color-mix(in srgb, var(--accent) 45%, transparent);
}

/* ---------------------------------------------------------------- */
/* Rectangular block selection                                        */
/* ---------------------------------------------------------------- */
/* The marquee sits above the screen but must never eat pointer events —
   the drag that draws it is handled on the container underneath. */
.screen-container {
  position: relative;
}

/* Every absolutely-positioned decoration lives here rather than directly in
   .screen-container — see overlayLayer() in screen-grid.js. The layer itself
   is inert; its children opt back into pointer events where they need them. */
.screen-overlay-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.screen-overlay-layer > .screen-hotspot {
  pointer-events: auto;
}

/* Connection details — see host-details.js. Values are monospaced because most
   of them are protocol strings an operator compares character by character
   against what a profile asked for. */
.modal-host-details {
  max-width: 720px;
}

.host-details-list {
  display: grid;
  grid-template-columns: minmax(9rem, auto) 1fr;
  gap: 4px 16px;
  margin: 12px 0 0;
  align-items: baseline;
}

.host-details-list dt {
  color: var(--fg-muted);
  font-size: 0.82rem;
}

.host-details-list dd {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.82rem;
  overflow-wrap: anywhere;
}

.host-details-list dd[data-empty] {
  color: var(--fg-muted);
  font-style: italic;
}

.host-details-list-raw {
  max-height: 40vh;
  overflow-y: auto;
  padding-right: 4px;
}

.host-details-status {
  margin-top: 8px;
  font-size: 0.82rem;
  color: var(--fg-muted);
}

.host-details-status[data-error] {
  color: var(--danger, #d9534f);
}

.host-details-status[hidden] {
  display: none;
}

.host-details-expand {
  font-size: 0.78rem;
  padding: 1px 8px;
}

/* Printer session — see printer.js. The panel is a short form and a list, and
   the list is the part that matters: an operator opens this because something
   was printed, not because they want to admire the options.

   The dialog is 420px like every other one, so everything here is written to
   fit that rather than to widen it: labels sit above their inputs, and both
   the form and the fieldset carry min-width: 0. A fieldset does not shrink
   below its content on its own — its default min-width is min-content — which
   is exactly how a form like this ends up hanging out of the dialog it is in. */
.printer-status {
  margin-top: 8px;
  font-size: 0.82rem;
  color: var(--fg-muted);
}

.printer-status[data-error] {
  color: var(--danger, #d9534f);
}

.printer-status[hidden] {
  display: none;
}

.printer-form {
  min-width: 0;
}

.printer-form fieldset {
  min-width: 0;
  border: 1px solid var(--hairline-soft);
  border-radius: 6px;
  margin: 12px 0 0;
  padding: 10px 12px 12px;
}

.printer-form legend {
  padding: 0 6px;
  font-size: 0.78rem;
  color: var(--fg-muted);
}

/* A disabled fieldset means a printer is already bound; the greying is the
   only cue that the form is not the thing to use next. */
.printer-form fieldset[disabled] {
  opacity: 0.55;
}

.printer-choice {
  display: flex;
  gap: 8px;
  align-items: baseline;
  font-size: 0.85rem;
  margin-bottom: 6px;
}

.printer-field {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin: 8px 0;
  font-size: 0.82rem;
  min-width: 0;
}

.printer-field-label {
  color: var(--fg-muted);
}

.printer-form input[type="text"],
.printer-form input[type="number"] {
  width: 100%;
  min-width: 0;
  max-width: 100%;
  margin: 0;
}

.printer-check {
  display: block;
  font-size: 0.82rem;
  margin: 4px 0;
}

.printer-advanced {
  margin-top: 10px;
}

.printer-advanced summary {
  cursor: pointer;
  font-size: 0.8rem;
  color: var(--fg-muted);
}

.printer-jobs-heading {
  margin: 16px 0 6px;
  font-size: 0.85rem;
}

.printer-jobs-empty {
  margin: 0;
  font-size: 0.82rem;
  color: var(--fg-muted);
}

.printer-job-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 32vh;
  overflow-y: auto;
}

.printer-job {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: baseline;
  justify-content: space-between;
  padding: 6px 0;
  border-bottom: 1px solid var(--hairline-soft);
}

.printer-job-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: baseline;
  font-size: 0.82rem;
  min-width: 0;
}

.printer-job-size {
  color: var(--fg-muted);
  font-family: var(--mono);
}

.printer-job-truncated {
  color: var(--danger, #d9534f);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.printer-job-actions {
  display: flex;
  gap: 8px;
  align-items: baseline;
}

.printer-job-download,
.printer-job-delete {
  font-size: 0.78rem;
}

/* The cursor when it is resting on protected text — see the "Cursor over
   protected text" section of keyboard.js. It is drawn as an underscore rather
   than a block so the character underneath stays readable, which is what a
   3270's own cursor does. */
.screen-free-cursor {
  position: absolute;
  pointer-events: none;
  z-index: 6;
  border-bottom: 2px solid var(--accent, currentColor);
  animation: screen-free-cursor-blink 1.06s steps(1, end) infinite;
}

.screen-free-cursor[hidden] {
  display: none;
}

@keyframes screen-free-cursor-blink {
  0%, 55% { opacity: 1; }
  56%, 100% { opacity: 0.15; }
}

/* An operator with reduced-motion set still needs to see the cursor; what they
   do not need is it flashing at them. */
@media (prefers-reduced-motion: reduce) {
  .screen-free-cursor {
    animation: none;
  }
}

/* Two visible carets would be worse than none. While the cursor is out on
   protected text the fields' own carets are hidden, so there is exactly one
   place on the screen that looks like the cursor. */
.terminal-shell[data-free-cursor] .screen-container input {
  caret-color: transparent;
}

.screen-block-selection {
  position: absolute;
  pointer-events: none;
  z-index: 5;
  background: color-mix(in srgb, var(--accent) 22%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 75%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--bg) 40%, transparent) inset;
}

.screen-block-selection[hidden] {
  display: none;
}

#screen-copy-hint {
  display: block;
  padding: 4px 10px;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--fg-muted);
  background: color-mix(in srgb, var(--panel-2) 70%, transparent);
  border-top: 1px solid var(--hairline-soft);
}

#screen-copy-hint[hidden] {
  display: none;
}

/* A locked keyboard is worth noticing at the bezel, not just in the OIA. */
.terminal-shell[data-kb-locked="true"] {
  border-color: color-mix(in srgb, var(--warn) 42%, var(--border));
}

/* Keep the classic inverted OIA slab only for "Authentic 3270", which
   deliberately reproduces a real terminal. Every other theme — including
   the Yorkshire default — gets the readable segmented bar. */
body.theme-authentic .screen-oia {
  background: var(--fg);
  color: var(--bg);
  border-radius: 0;
}

body.theme-authentic .screen-oia .oia-field,
body.theme-authentic .screen-oia .oia-online,
body.theme-authentic .screen-oia .oia-flag,
body.theme-authentic .screen-oia .oia-indicator,
body.theme-authentic .screen-oia .oia-spacer {
  border-right-color: color-mix(in srgb, var(--bg) 30%, transparent);
  color: color-mix(in srgb, var(--bg) 78%, transparent);
}

body.theme-authentic .screen-oia .oia-field > span,
body.theme-authentic .screen-oia .oia-online {
  color: var(--bg);
}

/* On a real terminal the indicator is the same phosphor as everything
   else — it earns attention by being the only text in an empty region,
   not by colour. */
body.theme-authentic .screen-oia .oia-indicator[data-oia-state="wait"],
body.theme-authentic .screen-oia .oia-indicator[data-oia-state="error"] {
  color: var(--bg);
}

body.theme-authentic .terminal-shell {
  border-radius: 0;
  box-shadow: none;
  background: none;
  border-color: transparent;
  padding: 0;
}

/* Keypad */
.controls.h3270-keypad {
  border-radius: var(--r-lg);
  border: 1px solid var(--hairline-soft);
  background: var(--glass);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  box-shadow: var(--elev-2), inset 0 1px 0 var(--top-sheen);
}

.h3270-key {
  border-radius: var(--r-xs);
}

.h3270-key.is-active {
  background: var(--accent);
  color: var(--bg);
  border-color: var(--accent);
}

/* Workflow status widget */
.workflow-status-widget {
  border-radius: var(--r-lg);
  border: 1px solid var(--hairline);
  background: var(--glass-strong);
  backdrop-filter: blur(16px) saturate(130%);
  -webkit-backdrop-filter: blur(16px) saturate(130%);
  box-shadow: var(--elev-3);
}

.workflow-status-widget-header {
  background: color-mix(in srgb, var(--panel-2) 55%, transparent);
  border-bottom: 1px solid var(--hairline-soft);
}

/* Minimised, the fixed 220px width left no room for the title next to the
   tracking switch and the two icon buttons, so it collapsed to "W…".
   Size to content instead. */
.workflow-status-widget.is-minimized,
html.workflow-status-minimized .workflow-status-widget {
  width: max-content;
  min-width: 220px;
  max-width: calc(100vw - 40px);
}

.workflow-status-widget.is-minimized .workflow-status-widget-header strong,
html.workflow-status-minimized .workflow-status-widget-header strong {
  overflow: visible;
  text-overflow: clip;
  padding-right: 4px;
}

.workflow-status-event {
  border-radius: var(--r-xs);
}

.terminal-control-btn.is-active,
.terminal-control-btn-primary {
  border-color: var(--accent-line);
  color: var(--accent);
  background: var(--accent-soft);
}

/* ---------------------------------------------------------------------
   7. Modals — one unified system
   ---------------------------------------------------------------------
   style.css grew five independent modal implementations (.modal-backdrop,
   .workflow-modal, .logs-modal, .settings-modal, .copilot-modal-backdrop)
   with different radii, borders, scrims and shadows. They are reconciled
   here so every dialog in the app reads as the same component.
   --------------------------------------------------------------------- */
.modal-backdrop,
.workflow-modal-backdrop,
.logs-modal-backdrop,
.settings-modal-backdrop,
.logs-confirm-backdrop,
.settings-confirm-backdrop,
.theme-save-confirm-backdrop,
.copilot-modal-backdrop {
  background: var(--scrim);
  backdrop-filter: blur(10px) saturate(115%);
  -webkit-backdrop-filter: blur(10px) saturate(115%);
}

.modal-backdrop:not([hidden]),
.workflow-modal:not([hidden]),
.logs-modal:not([hidden]),
.settings-modal:not([hidden]),
.copilot-modal-backdrop:not([hidden]) {
  animation: polish-fade-in var(--dur-2) var(--ease-out);
}

@keyframes polish-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes polish-modal-in {
  from { opacity: 0; transform: translateY(12px) scale(0.972); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

.modal,
.workflow-modal-content,
.logs-modal-content,
.settings-modal-content,
.logs-confirm-content,
.settings-confirm-content,
.theme-save-confirm-modal,
.copilot-modal {
  border-radius: var(--r-lg);
  border: 1px solid var(--hairline);
  background:
    linear-gradient(180deg, var(--top-sheen), transparent 90px),
    linear-gradient(168deg, color-mix(in srgb, var(--panel-2) 38%, var(--panel)), var(--panel) 58%);
  box-shadow: var(--elev-4), 0 0 0 1px var(--hairline-soft),
    0 0 80px -30px color-mix(in srgb, var(--accent) 26%, transparent);
  animation: polish-modal-in var(--dur-3) var(--ease-spring);
}

/* The logs + settings dialogs used a hard 2px accent frame; soften them
   to match every other dialog. */
.logs-modal-content,
.settings-modal-content {
  border: 1px solid var(--hairline);
}

.modal-header,
.workflow-modal-header,
.logs-modal-header,
.settings-modal-header {
  padding-bottom: 14px;
  border-bottom: 1px solid var(--hairline-soft);
  margin-bottom: 2px;
}

.modal-header h3,
.workflow-modal-header h3,
.logs-modal-header h3,
.settings-modal-header h3 {
  font-size: 1.06rem;
  letter-spacing: -0.012em;
  color: var(--fg);
}

/* A small accent rule before each dialog title. */
.modal-header h3::before,
.logs-modal-header h3::before,
.settings-modal-header h3::before,
.workflow-modal-header h3::before {
  content: "";
  display: inline-block;
  width: 3px;
  height: 1em;
  margin-right: 10px;
  vertical-align: -0.14em;
  border-radius: var(--r-pill);
  background: linear-gradient(180deg, var(--accent), var(--accent-2));
}

.modal-close {
  border-radius: var(--r-xs);
  font-size: 0.84rem;
  color: var(--fg-muted);
}

.modal-close:hover {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 45%, var(--border));
}

.modal-actions {
  padding-top: 14px;
  border-top: 1px solid var(--hairline-soft);
}

/* The confirm/cancel pair reads better when the primary action is filled. */
.modal-actions > button:last-child:not(.danger),
.settings-actions > button:last-child,
.logs-confirm-actions > button:last-child {
  border-color: var(--accent-line);
  color: var(--accent);
  background: var(--accent-soft);
}

.settings-modal-body,
.logs-modal-body,
.workflow-modal-body {
  scroll-behavior: smooth;
}

.settings-tab {
  border-radius: var(--r-pill);
  transition: all var(--dur-2) var(--ease-out);
}

.settings-tab.is-active {
  background: var(--accent);
  color: var(--bg);
  border-color: var(--accent);
  box-shadow: 0 6px 16px -6px color-mix(in srgb, var(--accent) 60%, transparent);
}

.settings-group,
.settings-subgroup,
.chaos-run-item,
.chaos-map-card {
  border-radius: var(--r-md);
  border-color: var(--hairline-soft);
  transition: border-color var(--dur-2) var(--ease-out),
    box-shadow var(--dur-2) var(--ease-out),
    transform var(--dur-2) var(--ease-out);
}

.chaos-run-item:hover,
.chaos-map-card:hover {
  border-color: var(--accent-line);
  box-shadow: var(--glow-sm);
}

.chaos-run-item.is-selected {
  border-color: var(--accent);
  box-shadow: var(--glow-sm);
}

.settings-field input,
.settings-field select,
.settings-field textarea,
.chaos-hint-field input,
.chaos-hint-field textarea {
  border-radius: var(--r-sm);
}

/* ...but not checkboxes: --r-sm on an 18px box renders as a circle, which
   reads as a radio button. */
.settings-field input[type="checkbox"],
.chaos-hint-field input[type="checkbox"],
.logs-toggle-label input[type="checkbox"] {
  border-radius: 5px;
}

/* Dialogs with a fixed height leave a large void on short tabs; let them
   hug their content and only cap the maximum. */
.settings-modal-content {
  height: auto;
  min-height: 320px;
  max-height: min(820px, 92vh);
}

.settings-modal.is-maximized .settings-modal-content {
  height: calc(100vh - 24px);
  max-height: none;
}

.logs-content,
.workflow-preview,
.chaos-report-viewer {
  border-radius: var(--r-md);
  border-color: var(--hairline-soft);
  background: var(--surface-sunken);
  box-shadow: inset 0 2px 8px -4px rgba(0, 0, 0, 0.5);
}

/* ---------------------------------------------------------------------
   8. Notifications
   --------------------------------------------------------------------- */
.h3270-notification {
  border-radius: var(--r-md);
  border: 1px solid var(--hairline);
  background: var(--glass-strong);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  box-shadow: var(--elev-3);
  border-left-width: 3px;
}

.h3270-notification--success { border-left-color: var(--ok); }
.h3270-notification--error { border-left-color: var(--danger); }
.h3270-notification--warning { border-left-color: var(--warn); }
.h3270-notification--info { border-left-color: var(--info); }

/* ---------------------------------------------------------------------
   9. Copilot side panel
   --------------------------------------------------------------------- */
.copilot-panel {
  border-left: 1px solid var(--hairline);
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--panel-2) 40%, var(--panel)), var(--panel) 220px);
  box-shadow: -24px 0 60px -24px rgba(0, 0, 0, 0.7);
  animation: polish-panel-in var(--dur-3) var(--ease-out);
}

@keyframes polish-panel-in {
  from { transform: translateX(24px); opacity: 0; }
  to { transform: translateX(0); opacity: 1; }
}

.copilot-panel-header {
  background: var(--glass);
  backdrop-filter: blur(14px) saturate(130%);
  -webkit-backdrop-filter: blur(14px) saturate(130%);
  border-bottom: 1px solid var(--hairline-soft);
  padding: 12px 14px;
  position: relative;
  z-index: 1;
}

.copilot-panel-title {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 0.72rem;
  background: linear-gradient(96deg, var(--fg), var(--accent));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}

.copilot-panel-title::before {
  content: "";
  width: 20px;
  height: 20px;
  border-radius: var(--r-xs);
  flex: none;
  background: linear-gradient(140deg, var(--accent), var(--accent-2));
  -webkit-text-fill-color: initial;
  box-shadow: 0 3px 10px -3px color-mix(in srgb, var(--accent) 70%, transparent);
  -webkit-mask: none;
  mask: none;
}

.copilot-icon-button {
  border-radius: var(--r-xs);
  padding: 5px 9px;
  transition: all var(--dur-2) var(--ease-out);
}

.copilot-automode-btn[aria-pressed="true"] {
  background: var(--accent);
  color: var(--bg);
  border-color: var(--accent);
  box-shadow: 0 4px 14px -4px color-mix(in srgb, var(--accent) 65%, transparent);
}

.copilot-panel-messages {
  padding: 14px;
  gap: 12px;
  scroll-behavior: smooth;
}

/* Chat turns become bubbles with role affordances. */
.copilot-msg {
  border-radius: var(--r-md);
  padding: 10px 12px;
  box-shadow: var(--elev-1);
  animation: polish-msg-in var(--dur-3) var(--ease-out);
  position: relative;
}

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

.copilot-msg-user {
  background: linear-gradient(150deg, color-mix(in srgb, var(--accent) 20%, var(--panel-2)), color-mix(in srgb, var(--accent) 8%, var(--panel-2)));
  border: 1px solid color-mix(in srgb, var(--accent) 34%, var(--border));
  border-bottom-right-radius: var(--r-xs);
  margin-left: 18px;
}

.copilot-msg-assistant {
  background: var(--surface-raised);
  border: 1px solid var(--hairline);
  border-bottom-left-radius: var(--r-xs);
  margin-right: 18px;
}

.copilot-msg-error {
  border-left: 3px solid var(--danger);
}

/* Role labels above each bubble so a long thread stays scannable. */
.copilot-msg-user::before,
.copilot-msg-assistant::before,
.copilot-msg-error::before {
  display: block;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom: 6px;
  opacity: 0.72;
}

.copilot-msg-user::before {
  content: "You";
  text-align: right;
  color: var(--accent);
}

.copilot-msg-assistant::before {
  /* Set by copilot-panel.js to whichever provider answered, so a reply is
     never labelled with a backend the user didn't pick. */
  content: var(--ai-provider-label, "Assistant");
  color: var(--fg-muted);
}

.copilot-msg-error::before {
  content: "Error";
  color: var(--danger);
  opacity: 1;
}

/* Tool-call cards get a status rail, coloured from the lifecycle state
   that ui-polish.js mirrors onto [data-tool-state]. */
.copilot-tool-call {
  border-radius: var(--r-md);
  border-left: 3px solid var(--info);
  background: var(--surface-sunken);
  box-shadow: var(--elev-1);
  overflow: hidden;
  transition: border-left-color var(--dur-2) var(--ease-out);
}

.copilot-tool-call[data-tool-state="pending"] { border-left-color: var(--warn); }
.copilot-tool-call[data-tool-state="running"] { border-left-color: var(--info); }
.copilot-tool-call[data-tool-state="done"] { border-left-color: var(--ok); }
.copilot-tool-call[data-tool-state="failed"] { border-left-color: var(--danger); }
.copilot-tool-call[data-tool-state="skipped"] { border-left-color: var(--fg-muted); }

.copilot-tool-call-status {
  font-size: 0.7rem;
  font-weight: 650;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: var(--r-pill);
  border: 1px solid var(--hairline);
  background: color-mix(in srgb, var(--panel-2) 70%, transparent);
  white-space: nowrap;
}

.copilot-tool-call[data-tool-state="pending"] .copilot-tool-call-status {
  color: var(--warn);
  border-color: color-mix(in srgb, var(--warn) 45%, var(--border));
}

.copilot-tool-call[data-tool-state="done"] .copilot-tool-call-status {
  color: var(--ok);
  border-color: color-mix(in srgb, var(--ok) 45%, var(--border));
}

.copilot-tool-call[data-tool-state="failed"] .copilot-tool-call-status {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 45%, var(--border));
}

/* A running tool advertises that it is doing something. */
.copilot-tool-call[data-tool-state="running"] .copilot-tool-call-status {
  color: var(--info);
  border-color: color-mix(in srgb, var(--info) 45%, var(--border));
  animation: polish-status-breathe 1.6s var(--ease-out) infinite;
}

@keyframes polish-status-breathe {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}

.copilot-tool-call-name {
  color: var(--accent);
  font-weight: 650;
}

.copilot-tool-call-args,
.copilot-tool-call-result {
  border-radius: var(--r-xs);
  background: color-mix(in srgb, var(--bg) 80%, var(--panel));
}

.copilot-btn-run {
  border-color: var(--accent-line);
  background: var(--accent);
  color: var(--bg);
  font-weight: 600;
  border-radius: var(--r-xs);
}

.copilot-btn-run:hover:not(:disabled) {
  box-shadow: 0 6px 18px -6px color-mix(in srgb, var(--accent) 70%, transparent);
}

.copilot-btn-skip {
  border-radius: var(--r-xs);
}

.copilot-example-chip {
  border-radius: var(--r-pill);
  transition: all var(--dur-2) var(--ease-out);
  text-align: left;
}

.copilot-example-chip:hover {
  border-color: var(--accent);
  background: var(--accent-soft);
  transform: translateY(-1px);
  box-shadow: var(--glow-sm);
}

.copilot-empty-state {
  padding: 18px 4px;
}

.copilot-empty-title {
  background: linear-gradient(96deg, var(--fg), var(--accent));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}

.copilot-panel-form {
  border-top: 1px solid var(--hairline-soft);
  background: var(--glass);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  padding: 12px;
  gap: 8px;
}

.copilot-panel-form textarea {
  border-radius: var(--r-md);
  background: var(--surface-sunken);
  transition: border-color var(--dur-2) var(--ease-out),
    box-shadow var(--dur-2) var(--ease-out);
}

.copilot-panel-form textarea:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.copilot-send {
  border-radius: var(--r-md);
  background: linear-gradient(150deg, var(--accent), color-mix(in srgb, var(--accent) 70%, var(--accent-2)));
  color: var(--bg);
  border-color: var(--accent);
  font-weight: 650;
}

.copilot-send:hover:not(:disabled) {
  box-shadow: 0 8px 22px -8px color-mix(in srgb, var(--accent) 70%, transparent);
}

.copilot-stop {
  border-radius: var(--r-md);
  border-color: color-mix(in srgb, var(--danger) 45%, var(--border));
  color: var(--danger);
}

.copilot-connect-btn {
  border-radius: var(--r-md);
  background: linear-gradient(150deg, var(--accent), var(--accent-2));
  color: var(--bg);
  font-weight: 650;
  border: none;
  padding: 10px 18px;
  box-shadow: 0 10px 26px -10px color-mix(in srgb, var(--accent) 70%, transparent);
}

/* Signed out, the panel showed a top-aligned pitch and a bottom-aligned
   sign-in button with a tall void between them. Centre the empty state so
   the two read as one block. */
.copilot-panel.copilot-disconnected .copilot-panel-messages {
  justify-content: center;
}

.copilot-connect-prompt {
  border-top: 1px solid var(--hairline-soft);
  background: color-mix(in srgb, var(--panel-2) 40%, transparent);
  padding: 16px 14px;
}

.copilot-model-bar {
  border-top: 1px solid var(--hairline-soft);
  padding: 8px 12px;
}

.copilot-model-select {
  border-radius: var(--r-sm);
}

.copilot-typing-dot {
  background: var(--accent);
}

/* Only .page-wrap was reflowed when the panel opens, so every fixed-position
   widget (terminal tools, workflow status, toasts) slid underneath it. Move
   them clear of the panel too — and animate it, since the panel is
   drag-resizable. */
/* The run-status widget is anchored bottom-LEFT and so needs no offset;
   only the right-hand furniture has to step aside for the panel. */
body.copilot-open #h3270-notification-container {
  right: calc(var(--copilot-panel-width) + 20px);
}

#h3270-notification-container {
  transition: right var(--dur-2) var(--ease-out);
}

body.copilot-resizing #h3270-notification-container,
body.copilot-resizing .page-wrap {
  transition: none;
}

/* Drag handle for resizing the panel (wired up in ui-polish.js). */
.copilot-resize-handle {
  position: absolute;
  top: 0;
  left: -3px;
  width: 7px;
  height: 100%;
  cursor: col-resize;
  z-index: 3;
  background: transparent;
  transition: background var(--dur-2) var(--ease-out);
}

.copilot-resize-handle::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 2px;
  width: 3px;
  height: 44px;
  margin-top: -22px;
  border-radius: var(--r-pill);
  background: var(--hairline);
  transition: background var(--dur-2) var(--ease-out);
}

.copilot-resize-handle:hover::after,
body.copilot-resizing .copilot-resize-handle::after {
  background: var(--accent);
  box-shadow: 0 0 12px color-mix(in srgb, var(--accent) 60%, transparent);
}

body.copilot-resizing {
  cursor: col-resize;
  user-select: none;
}

/* ---------------------------------------------------------------------
   10. Command palette
   --------------------------------------------------------------------- */
.cmdk {
  position: fixed;
  inset: 0;
  z-index: 1200;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: clamp(48px, 12vh, 140px) 20px 20px;
  background: var(--scrim);
  backdrop-filter: blur(10px) saturate(115%);
  -webkit-backdrop-filter: blur(10px) saturate(115%);
  animation: polish-fade-in var(--dur-2) var(--ease-out);
}

.cmdk[hidden] {
  display: none;
}

.cmdk-panel {
  width: min(620px, 100%);
  max-height: min(60vh, 520px);
  display: flex;
  flex-direction: column;
  border-radius: var(--r-lg);
  border: 1px solid var(--hairline);
  background:
    linear-gradient(180deg, var(--top-sheen), transparent 80px),
    linear-gradient(168deg, color-mix(in srgb, var(--panel-2) 40%, var(--panel)), var(--panel) 58%);
  box-shadow: var(--elev-4), 0 0 0 1px var(--hairline-soft),
    0 0 90px -30px color-mix(in srgb, var(--accent) 34%, transparent);
  overflow: hidden;
  animation: polish-modal-in var(--dur-3) var(--ease-spring);
}

.cmdk-search {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--hairline-soft);
}

.cmdk-search svg {
  width: 18px;
  height: 18px;
  fill: var(--fg-muted);
  flex: none;
}

.cmdk-input {
  flex: 1;
  border: none;
  background: transparent;
  color: var(--fg);
  font-size: 1rem;
  padding: 2px 0;
  outline: none;
}

.cmdk-input:focus {
  border: none;
  box-shadow: none;
}

.cmdk-input::placeholder {
  color: var(--fg-muted);
}

.cmdk-hintkey {
  font-family: var(--mono);
  font-size: 0.68rem;
  padding: 3px 7px;
  border-radius: var(--r-xs);
  border: 1px solid var(--hairline);
  background: var(--surface-sunken);
  color: var(--fg-muted);
  flex: none;
}

.cmdk-list {
  overflow-y: auto;
  padding: 8px;
  margin: 0;
  list-style: none;
  min-height: 0;
}

.cmdk-group {
  padding: 10px 10px 6px;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-muted);
}

.cmdk-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border-radius: var(--r-sm);
  cursor: pointer;
  color: var(--fg);
  border: 1px solid transparent;
  transition: background var(--dur-1) var(--ease-out),
    border-color var(--dur-1) var(--ease-out);
}

.cmdk-item[aria-selected="true"] {
  background: var(--accent-soft);
  border-color: var(--accent-line);
}

.cmdk-item-icon {
  width: 26px;
  height: 26px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: var(--r-xs);
  background: color-mix(in srgb, var(--panel-2) 70%, transparent);
  border: 1px solid var(--hairline-soft);
  color: var(--accent);
}

.cmdk-item-icon svg {
  width: 14px;
  height: 14px;
  fill: currentColor;
}

.cmdk-item-body {
  min-width: 0;
  flex: 1;
}

.cmdk-item-label {
  font-size: 0.9rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.cmdk-item-label mark {
  background: transparent;
  color: var(--accent);
  font-weight: 700;
}

.cmdk-item-sub {
  font-size: 0.74rem;
  color: var(--fg-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.cmdk-empty {
  padding: 32px 16px;
  text-align: center;
  color: var(--fg-muted);
  font-size: 0.9rem;
}

.cmdk-footer {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 14px;
  border-top: 1px solid var(--hairline-soft);
  font-size: 0.72rem;
  color: var(--fg-muted);
  background: color-mix(in srgb, var(--panel-2) 40%, transparent);
}

.cmdk-footer kbd {
  font-family: var(--mono);
  font-size: 0.68rem;
  padding: 2px 6px;
  border-radius: 5px;
  border: 1px solid var(--hairline);
  background: var(--surface-sunken);
  margin-right: 4px;
}

/* ---------------------------------------------------------------------
   11. Scroll affordance for long dialog bodies
   --------------------------------------------------------------------- */
.polish-scroll-shadow {
  position: relative;
}

.polish-scroll-shadow::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 36px;
  pointer-events: none;
  background: linear-gradient(to top, var(--panel), transparent);
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease-out);
}

.polish-scroll-shadow.is-scrollable::after {
  opacity: 0.9;
}

/* The same idea sideways, for the toolbars.

   They are overflow-x: auto with the scrollbar deliberately hidden, which on a
   narrow screen leaves the controls past the right edge with nothing to say
   they are there — the row simply looks like it ends. A fade at whichever edge
   still has content behind it is the affordance a hidden scrollbar removed. */
.polish-hscroll {
  position: relative;
}

.polish-hscroll::before,
.polish-hscroll::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 40px;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease-out);
  z-index: 2;
}

.polish-hscroll::before {
  left: 0;
  background: linear-gradient(to right, var(--panel), transparent);
}

.polish-hscroll::after {
  right: 0;
  background: linear-gradient(to left, var(--panel), transparent);
}

.polish-hscroll.has-scroll-start::before,
.polish-hscroll.has-scroll-end::after {
  opacity: 0.95;
}

/* ---------------------------------------------------------------------
   12. Motion + contrast preferences
   --------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }

  button:hover:not(:disabled),
  .h3270-key:hover,
  .copilot-example-chip:hover {
    transform: none;
  }
}

/* Backdrop blur is expensive on low-end GPUs and unsupported in a few
   engines; the solid fallbacks below keep everything legible. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .appmenu-panel,
  .copilot-panel-header,
  .copilot-panel-form,
  .h3270-notification,
  .controls.h3270-keypad {
    background: var(--panel-2);
  }

  .modal-backdrop,
  .workflow-modal-backdrop,
  .logs-modal-backdrop,
  .settings-modal-backdrop,
  .copilot-modal-backdrop,
  .cmdk {
    background: color-mix(in srgb, var(--bg) 40%, rgba(0, 0, 0, 0.82));
  }
}

@media (max-width: 820px) {
  .cmdk {
    padding: 24px 12px 12px;
  }

  .terminal-shell {
    padding: 8px;
  }
}

/* Macro import — see macro-import.js. The dialog is a report, and it is 420px
   like every other one, so the report is written to fit that: each line the
   translator would not take gets its own block, with the macro's own text in
   the terminal face above the reason in prose. A file with sixty untranslated
   lines makes a long dialog, and the dialog itself scrolls — no inner scroll
   region, for the reason .modal gives in style.css. */
.macro-import-status {
  margin-top: 8px;
  font-size: 0.82rem;
  color: var(--fg-muted);
}

.macro-import-status[data-error] {
  color: var(--danger, #d9534f);
}

.macro-import-status[hidden] {
  display: none;
}

.macro-import-summary {
  margin: 12px 0 0;
  font-size: 0.86rem;
  line-height: 1.5;
}

.macro-import-summary:empty {
  display: none;
}

.macro-import-advisories {
  margin: 10px 0 0;
  padding-left: 18px;
  font-size: 0.8rem;
  line-height: 1.5;
  color: var(--fg-muted);
}

.macro-import-notes-heading {
  margin: 0 0 6px;
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
}

.macro-import-notes-wrap {
  margin-top: 14px;
}

.macro-import-notes {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--border);
}

.macro-import-notes li {
  padding: 8px 2px;
  border-bottom: 1px solid color-mix(in srgb, var(--border) 55%, transparent);
}

.macro-import-note-head {
  display: flex;
  gap: 8px;
  align-items: baseline;
}

.macro-import-note-head code {
  font-family: var(--mono);
  font-size: 0.78rem;
  overflow-wrap: anywhere;
}

.macro-import-line {
  flex: 0 0 auto;
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  color: var(--fg-muted);
  white-space: nowrap;
}

.macro-import-note-reason {
  margin: 4px 0 0;
  font-size: 0.78rem;
  line-height: 1.45;
  color: var(--fg-muted);
}
