/* Apex Video Console — Gold Circuit.
   Obsidian void base, iridescent gold architectural circuitry, micro-node
   geometry, liquid-gold conduits.

   Every effect is CSS: gradients, shadows, blend modes. No image, no external
   font, no third-party origin. That is not an aesthetic constraint but a policy
   one — the console ships under `default-src 'self'` and shares its origin with
   the API, so a single remote asset would mean widening the policy for the whole
   surface. The system font stack is also already resident and hinted for the
   device, so first paint costs nothing.

   Mobile first throughout. The circuitry degrades to flat surfaces under
   prefers-reduced-motion and prefers-contrast, because a decorative conduit
   must never be the thing that makes a state unreadable. */

:root {
  /* Committed to a single dark identity. This is not a theme with a dark mode —
     obsidian *is* the surface, and a light variant would be a different product.
     color-scheme stays dark so form controls and scrollbars match rather than
     rendering as bright system chrome against the void. */
  color-scheme: dark;

  /* Obsidian void. Three depths, not one: a flat black surface reads as an
     absence, while a graded one reads as space with things in it. */
  --void: #05060a;
  --void-2: #0a0c12;
  --surface: #0e1119;
  --surface-2: #141824;
  --surface-raised: #1a1f2e;

  /* Iridescent gold. A single gold reads as brass; the shift from cool champagne
     through saturated core to warm ember is what makes it read as metal. */
  --gold-100: #fff4d6;
  --gold-200: #ffe9a8;
  --gold-300: #f7d070;
  --gold: #e8b338;
  --gold-600: #c9901f;
  --gold-700: #9a6a13;
  --gold-ink: #0a0c12;

  /* Circuit traces sit below content and must never compete with it. */
  --trace: rgba(232, 179, 56, 0.22);
  --trace-dim: rgba(232, 179, 56, 0.08);
  --node: rgba(255, 233, 168, 0.55);

  --fg: #f2f4f8;
  --muted: #949cb0;
  --line: rgba(232, 179, 56, 0.16);
  --line-strong: rgba(232, 179, 56, 0.34);

  --accent: var(--gold);
  --accent-fg: var(--gold-ink);
  --ok: #4ade80;
  --warn: var(--gold-300);
  --err: #ff6b6b;
  --focus: var(--gold-200);

  --radius: 14px;
  --tap: 44px; /* Apple HIG / WCAG 2.5.5 minimum target. */

  /* Subsurface luminescence: light appearing to come from within the material
     rather than from a source above it. Two shadows — an inner rim and an outer
     bloom — because either alone reads as a flat border or a blur. */
  --glow-sm: 0 0 0 1px var(--line), 0 0 12px -4px rgba(232, 179, 56, 0.35);
  --glow-md:
    0 0 0 1px var(--line-strong), 0 0 24px -8px rgba(232, 179, 56, 0.45),
    inset 0 1px 0 0 rgba(255, 244, 214, 0.06);
  --glow-key:
    0 0 0 1px var(--gold-600), 0 0 32px -6px rgba(232, 179, 56, 0.55),
    inset 0 1px 0 0 rgba(255, 244, 214, 0.12);

  --step--1: clamp(0.8125rem, 0.79rem + 0.11vw, 0.875rem);
  --step-0: clamp(1rem, 0.96rem + 0.18vw, 1.0625rem);
  --step-1: clamp(1.125rem, 1.05rem + 0.36vw, 1.3125rem);
  --step-2: clamp(1.375rem, 1.2rem + 0.8vw, 1.875rem);

  /* Density. High-density means tighter rhythm, not smaller text — the type
     scale above is unchanged. */
  --gap: 0.75rem;
  --pad: 0.875rem;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%; /* Stop iOS inflating text in landscape. */
}

body {
  margin: 0;
  background-color: var(--void);
  /* Architectural circuitry, four layers, painted once and never animated:
     two orthogonal trace grids at different pitches for micro-node geometry,
     and two broad radial washes for subsurface luminescence. Fixed attachment
     so the circuitry reads as the substrate the interface sits on rather than
     as content that scrolls with it. */
  background-image:
    radial-gradient(circle at 18% 12%, rgba(232, 179, 56, 0.1), transparent 42%),
    radial-gradient(circle at 82% 78%, rgba(201, 144, 31, 0.08), transparent 46%),
    linear-gradient(var(--trace-dim) 1px, transparent 1px),
    linear-gradient(90deg, var(--trace-dim) 1px, transparent 1px),
    linear-gradient(var(--trace) 1px, transparent 1px),
    linear-gradient(90deg, var(--trace) 1px, transparent 1px);
  background-size:
    100% 100%,
    100% 100%,
    28px 28px,
    28px 28px,
    140px 140px,
    140px 140px;
  background-attachment: fixed;
  color: var(--fg);
  font-family:
    system-ui,
    -apple-system,
    "Segoe UI",
    Roboto,
    "Helvetica Neue",
    Arial,
    sans-serif;
  /* 16px floor: iOS zooms the viewport on focus for anything smaller, which on a
     form is indistinguishable from the page breaking. */
  font-size: max(16px, var(--step-0));
  line-height: 1.55;
  min-height: 100dvh;
  overflow-x: hidden;
  /* Below the safe area so content clears the home indicator. */
  padding-bottom: env(safe-area-inset-bottom, 0);
}

h1,
h2 {
  margin: 0;
  line-height: 1.2;
  letter-spacing: -0.015em; /* Tighten at display sizes only. */
  text-wrap: balance;
}

p {
  margin: 0;
  text-wrap: pretty;
}

.skip {
  position: absolute;
  left: -9999px;
}
.skip:focus {
  left: 0.5rem;
  top: 0.5rem;
  z-index: 10;
  padding: 0.6rem 0.9rem;
  background: var(--accent);
  color: var(--accent-fg);
  border-radius: 10px;
}

:where(a, button, input, textarea, select, summary):focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
  border-radius: 6px;
}

/* ---------- chrome ---------- */

.topbar {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.75rem 1rem;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: saturate(1.6) blur(12px);
  border-bottom: 1px solid var(--line);
}
.topbar-title {
  font-size: var(--step-1);
  font-weight: 650;
}
.topbar-sub {
  color: var(--muted);
  font-size: var(--step--1);
}

main {
  display: grid;
  gap: 1rem;
  padding: 1rem;
  max-width: 46rem; /* Past this, line length degrades reading. */
  margin-inline: auto;
}

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1rem;
}

.card-title {
  font-size: var(--step-1);
  font-weight: 620;
  margin-bottom: 0.75rem;
}

/* ---------- form ---------- */

.field {
  display: grid;
  gap: 0.375rem;
  margin-bottom: 0.875rem;
}

label,
legend {
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--fg);
  padding: 0;
}

textarea,
input[type="number"] {
  width: 100%;
  /* 16px minimum: anything smaller makes iOS Safari zoom on focus, which yanks
     the layout sideways and is the most common mobile form defect there is. */
  font-size: max(16px, var(--step-0));
  font-family: inherit;
  line-height: 1.5;
  color: var(--fg);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 0.7rem 0.8rem;
  min-height: var(--tap);
}

textarea {
  resize: vertical;
  field-sizing: content; /* Grows with input where supported; ignored elsewhere. */
  max-height: 40vh;
}

.field-foot {
  display: flex;
  justify-content: space-between;
  gap: 0.75rem;
  align-items: baseline;
}
#command-help,
.count {
  font-size: var(--step--1);
  color: var(--muted);
}
.count {
  font-variant-numeric: tabular-nums; /* Stops the counter jittering as it ticks. */
  white-space: nowrap;
}
.count[data-tone="warn"] {
  color: var(--warn);
}
.count[data-tone="error"] {
  color: var(--err);
  font-weight: 650;
}

.with-unit {
  position: relative;
  display: flex;
  align-items: center;
}
.with-unit input {
  padding-right: 3rem;
}
.unit {
  position: absolute;
  right: 0.8rem;
  color: var(--muted);
  font-size: var(--step--1);
  pointer-events: none;
}

.advanced {
  border: 1px solid var(--line);
  border-radius: 10px;
  margin-bottom: 0.875rem;
  background: var(--surface-2);
}
.advanced > summary {
  cursor: pointer;
  padding: 0.7rem 0.8rem;
  min-height: var(--tap);
  display: flex;
  align-items: center;
  font-size: var(--step--1);
  font-weight: 600;
}
.advanced-body {
  padding: 0 0.8rem 0.25rem;
}

.segmented {
  border: 0;
  margin: 0 0 0.875rem;
  padding: 0;
}
.segments {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.375rem;
  margin-top: 0.375rem;
}
.segment {
  position: relative;
}
.segment input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}
.segment span {
  display: grid;
  gap: 0.1rem;
  justify-items: center;
  padding: 0.5rem 0.25rem;
  min-height: var(--tap);
  align-content: center;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface);
  font-size: var(--step--1);
  font-weight: 620;
  font-variant-numeric: tabular-nums;
}
.segment small {
  font-weight: 500;
  color: var(--muted);
  font-size: 0.75em;
}
.segment input:checked + span {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, var(--surface));
}
.segment input:focus-visible + span {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

/* ---------- buttons ---------- */

button,
.download {
  font: inherit;
  min-height: var(--tap);
  border-radius: 10px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  text-decoration: none;
}

.primary {
  width: 100%;
  background: var(--accent);
  color: var(--accent-fg);
  border: 0;
  font-weight: 650;
  font-size: var(--step-0);
  padding: 0.8rem 1rem;
}
.primary:disabled {
  opacity: 0.55;
  cursor: progress;
}

.ghost,
.download {
  background: var(--surface-2);
  color: var(--fg);
  border: 1px solid var(--line);
  font-size: var(--step--1);
  font-weight: 600;
  padding: 0.5rem 0.85rem;
}
.download {
  background: var(--accent);
  color: var(--accent-fg);
  border-color: transparent;
}

.spinner {
  width: 1em;
  height: 1em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  display: none;
}
.primary[data-busy="true"] .spinner {
  display: inline-block;
  animation: spin 0.7s linear infinite;
}
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* ---------- result ---------- */

.result {
  display: block;
  margin-top: 0.75rem;
  font-size: var(--step--1);
  white-space: pre-wrap;
  overflow-wrap: anywhere; /* Long ids and hashes must not widen the page. */
}
.result[data-tone="ok"] {
  color: var(--ok);
}
.result[data-tone="error"] {
  color: var(--err);
}

/* ---------- jobs ---------- */

.jobs-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}
.empty {
  color: var(--muted);
  font-size: var(--step--1);
  padding: 1rem 0;
}
.job-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.75rem;
}
.job {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.875rem;
  display: grid;
  gap: 0.7rem;
}
.job-head {
  display: flex;
  align-items: start;
  justify-content: space-between;
  gap: 0.75rem;
}
.job-title {
  font-weight: 600;
  overflow-wrap: anywhere;
}
.job-meta {
  color: var(--muted);
  font-size: var(--step--1);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

.chip {
  flex: none;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  padding: 0.2rem 0.5rem;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  white-space: nowrap;
}
.chip[data-tone="ok"] {
  color: var(--ok);
  border-color: currentColor;
}
.chip[data-tone="run"] {
  color: var(--accent);
  border-color: currentColor;
}
.chip[data-tone="hold"] {
  color: var(--warn);
  border-color: currentColor;
}
.chip[data-tone="error"] {
  color: var(--err);
  border-color: currentColor;
}

/* Progress rail. Fourteen labels will never fit on a phone, so the labels are
   for assistive technology and wide viewports; the bars carry the meaning at
   small sizes. */
.rail {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(14, 1fr);
  gap: 3px;
}
.rail-step {
  height: 5px;
  border-radius: 999px;
  background: var(--line);
}
.rail-step[data-done="true"] {
  background: var(--accent);
}
.rail-step[data-current="true"] {
  background: var(--accent);
  animation: pulse 1.4s ease-in-out infinite;
}
.rail-dot {
  display: none;
}
.rail-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
@keyframes pulse {
  50% {
    opacity: 0.35;
  }
}

.job-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

/* ---------- wide ---------- */

@media (min-width: 40rem) {
  .job-list {
    grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
  }
  .rail-step {
    height: auto;
    background: none;
    display: grid;
    gap: 3px;
    justify-items: center;
  }
  .rail-step::before {
    content: "";
    width: 100%;
    height: 5px;
    border-radius: 999px;
    background: var(--line);
  }
  .rail-step[data-done="true"]::before,
  .rail-step[data-current="true"]::before {
    background: var(--accent);
  }
  .rail-label {
    position: static;
    width: auto;
    height: auto;
    clip-path: none;
    font-size: 0.5625rem;
    color: var(--muted);
    text-align: center;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
  }
}

/* ---------- preferences ---------- */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

@media (prefers-contrast: more) {
  :root {
    --line: currentColor;
    --muted: var(--fg);
  }
}

/* ── Gold Circuit surfaces ─────────────────────────────────────────────────
   A panel is not a flat rectangle: it is a lit plane with a conduit running
   along its leading edge. The conduit is a pseudo-element rather than a border
   so it can carry a gradient — a border can only carry one colour, and one
   colour reads as brass rather than as metal catching light along its length. */

.panel,
.card,
form,
fieldset {
  position: relative;
  background: linear-gradient(180deg, var(--surface-raised) 0%, var(--surface) 100%);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--glow-md);
  padding: var(--pad);
  /* Contains the conduit's rounded ends without a second radius declaration. */
  overflow: hidden;
}

.panel::before,
.card::before,
form::before,
fieldset::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  inset-block-start: 0;
  block-size: 1px;
  /* Liquid gold: transparent at the ends so the conduit appears to flow through
     the panel rather than terminate at its corners. */
  background: linear-gradient(
    90deg,
    transparent 0%,
    var(--gold-600) 18%,
    var(--gold-200) 50%,
    var(--gold-600) 82%,
    transparent 100%
  );
  opacity: 0.85;
}

/* Micro-node: the small illuminated junction that marks a live element. Drawn
   with a radial gradient so it has a core and a falloff, which is what makes it
   read as a light rather than a dot. */
.node,
.state-rail li[aria-current="step"]::before {
  content: "";
  inline-size: 7px;
  block-size: 7px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--gold-100) 0%, var(--gold) 55%, transparent 72%);
  box-shadow: 0 0 10px -1px var(--gold);
  flex: none;
}

/* ── Type ─────────────────────────────────────────────────────────────────── */

h1,
.title {
  font-size: var(--step-2);
  line-height: 1.15;
  letter-spacing: -0.015em;
  margin: 0 0 var(--gap);
  /* The one place gradient text earns its cost: the product name. Falls back to
     solid gold wherever background-clip: text is unsupported, because the
     fallback colour is declared first. */
  color: var(--gold-200);
  background: linear-gradient(180deg, var(--gold-100) 0%, var(--gold) 62%, var(--gold-700) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

h2 {
  font-size: var(--step-1);
  letter-spacing: -0.01em;
  color: var(--gold-200);
  margin: 0 0 var(--gap);
}

.muted,
small {
  color: var(--muted);
}

/* ── Controls ─────────────────────────────────────────────────────────────── */

button,
.button,
input,
textarea,
select {
  font: inherit;
  font-size: max(16px, var(--step-0));
  min-block-size: var(--tap);
  border-radius: calc(var(--radius) - 4px);
}

input,
textarea,
select {
  inline-size: 100%;
  padding: 0.6rem 0.75rem;
  color: var(--fg);
  background: var(--void-2);
  border: 1px solid var(--line);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.5);
}

input::placeholder,
textarea::placeholder {
  color: var(--muted);
  opacity: 1; /* Firefox dims placeholders further by default. */
}

button,
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-inline-size: var(--tap);
  padding: 0.6rem 1.1rem;
  cursor: pointer;
  color: var(--gold-100);
  background: linear-gradient(180deg, var(--surface-raised), var(--surface-2));
  border: 1px solid var(--line-strong);
  box-shadow: var(--glow-sm);
}

button[type="submit"],
.button--primary {
  /* Machined metal: a bright leading edge, saturated core, dark trailing edge.
     Dark ink on gold rather than gold on dark, so the primary action is the
     highest-contrast thing on the screen. */
  color: var(--gold-ink);
  font-weight: 650;
  background: linear-gradient(180deg, var(--gold-200) 0%, var(--gold) 48%, var(--gold-600) 100%);
  border-color: var(--gold-600);
  box-shadow: var(--glow-key);
}

button:hover:not(:disabled),
.button:hover {
  border-color: var(--gold-300);
}

button:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  box-shadow: none;
}

/* Focus is never removed and never relies on colour alone: a solid ring plus an
   offset, so it is visible against both the gold controls and the void. */
:where(a, button, input, textarea, select, [tabindex]):focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* ── State rail ───────────────────────────────────────────────────────────── */

.state-rail {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.state-rail li {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: var(--step--1);
  color: var(--muted);
  padding: 0.15rem 0.5rem;
  border: 1px solid transparent;
  border-radius: 999px;
}

.state-rail li[data-done="true"] {
  color: var(--gold-300);
  border-color: var(--line);
}

.state-rail li[aria-current="step"] {
  color: var(--gold-100);
  border-color: var(--line-strong);
  background: rgba(232, 179, 56, 0.08);
  box-shadow: var(--glow-sm);
}

/* ── Status, never colour alone ───────────────────────────────────────────── */

.status {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: var(--step--1);
  font-weight: 600;
}

.status::before {
  /* A glyph as well as a hue: colour alone fails WCAG 1.4.1 and fails anyone
     with a monochrome display or a colour vision deficiency. */
  font-variant-emoji: text;
}

.status--ok {
  color: var(--ok);
}
.status--ok::before {
  content: "✓ ";
}
.status--warn {
  color: var(--warn);
}
.status--warn::before {
  content: "▲ ";
}
.status--err {
  color: var(--err);
}
.status--err::before {
  content: "✕ ";
}

/* ── Degradations ─────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

@media (prefers-contrast: more) {
  /* Decoration is the first thing to go. The circuitry, the conduits and the
     glows all sit behind text, and a reader who has asked for more contrast has
     told us the substrate is costing them legibility. */
  :root {
    --line: rgba(255, 244, 214, 0.5);
    --line-strong: var(--gold-200);
    --muted: #c8cedd;
    --glow-sm: 0 0 0 1px var(--line);
    --glow-md: 0 0 0 1px var(--line-strong);
    --glow-key: 0 0 0 2px var(--gold-200);
  }

  body {
    background-image: none;
  }

  .panel::before,
  .card::before,
  form::before,
  fieldset::before {
    display: none;
  }

  h1,
  .title {
    background: none;
    -webkit-text-fill-color: var(--gold-100);
    color: var(--gold-100);
  }
}

/* ── Wide viewports ───────────────────────────────────────────────────────── */

@media (min-width: 44rem) {
  :root {
    --gap: 1rem;
    --pad: 1.25rem;
  }

  main {
    /* Symmetrical composition, and a measure that keeps prose readable rather
       than stretching a line to the width of a desktop display. */
    max-inline-size: 68rem;
    margin-inline: auto;
  }
}

/* ── Sign-in portal ────────────────────────────────────────────────────────
   The document at `/`. Same substrate as the console — the circuitry is on
   <body> and applies here unchanged — but composed as a single centred frame
   rather than a scrolling stack, because it holds one field and one action.

   Symmetrical by construction: the frame is centred on both axes and the node
   mark above the title is a three-point row, so the composition reads the same
   from either edge. */

.portal-body {
  /* The console's topbar is absent here, so the frame owns the whole viewport.
     min-height rather than height: a soft keyboard on a phone shrinks the
     visual viewport, and a fixed height would clip the field being typed in. */
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding: max(1.25rem, env(safe-area-inset-top)) 1.25rem max(1.25rem, env(safe-area-inset-bottom));
}

.portal {
  width: 100%;
  max-width: 26rem;
}

.portal-frame {
  position: relative;
  padding: clamp(1.5rem, 1rem + 2.5vw, 2.5rem);
  border-radius: calc(var(--radius) + 4px);
  background: linear-gradient(180deg, var(--surface-2) 0%, var(--surface) 55%, var(--void-2) 100%);
  box-shadow: var(--glow-md);
  text-align: center;
  isolation: isolate;
}

/* Liquid-gold conduits entering the frame from the top and leaving at the
   bottom, so it reads as a node on a circuit rather than a card floating on a
   background. Pseudo-elements, so there is no extra markup to describe to a
   screen reader. */
.portal-frame::before,
.portal-frame::after {
  content: "";
  position: absolute;
  left: 50%;
  width: 1px;
  height: clamp(1.25rem, 4vw, 2.25rem);
  transform: translateX(-50%);
  z-index: -1;
}

.portal-frame::before {
  bottom: 100%;
  background: linear-gradient(180deg, transparent, var(--gold-600));
}

.portal-frame::after {
  top: 100%;
  background: linear-gradient(0deg, transparent, var(--gold-600));
}

/* Micro-node geometry: three points on a trace, the centre one energised. */
.portal-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin-bottom: 1.25rem;
}

.portal-node {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--gold-700);
  box-shadow: 0 0 0 1px var(--trace);
}

.portal-node:nth-child(2) {
  width: 9px;
  height: 9px;
  background: var(--gold-200);
  box-shadow:
    0 0 14px -1px var(--gold),
    0 0 0 1px var(--gold-600);
}

.portal-title {
  margin: 0;
  font-size: var(--step-2);
  font-weight: 600;
  letter-spacing: 0.02em;
  /* Iridescence: the shift across the glyphs is what separates gold from
     yellow. background-clip on text is universally supported now and degrades
     to a flat --gold fill where it is not, which stays legible. */
  color: var(--gold);
  background: linear-gradient(100deg, var(--gold-300), var(--gold-100) 42%, var(--gold-600));
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

.portal-kicker {
  margin: 0.375rem 0 1.75rem;
  font-size: var(--step--1);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
}

.portal-form {
  display: grid;
  gap: var(--gap);
  text-align: left;
}

/* The widget is a fixed-size cross-origin iframe. Centring it and letting it
   keep its own dimensions avoids the layout shift that comes from trying to
   stretch it, and reserving the height stops the button jumping when it
   resolves. */
.portal .turnstile {
  display: flex;
  justify-content: center;
  min-height: 65px;
}

.portal-hold {
  margin: 0;
  padding: var(--pad);
  border-radius: var(--radius);
  border: 1px solid var(--line-strong);
  background: rgba(232, 179, 56, 0.06);
  color: var(--warn);
  font-size: var(--step--1);
  text-align: left;
}

.portal-note {
  margin: 1.5rem 0 0;
  padding-top: 1.25rem;
  border-top: 1px solid var(--trace-dim);
  font-size: var(--step--1);
  line-height: 1.55;
  color: var(--muted);
}

.portal .result:empty {
  display: none;
}

.portal .result {
  margin-top: var(--gap);
  text-align: left;
}

/* Sign out sits at the far end of the topbar on the console. */
.topbar-action {
  margin-left: auto;
}

@media (prefers-reduced-motion: reduce) {
  .portal-node:nth-child(2) {
    box-shadow: 0 0 0 1px var(--gold-600);
  }
}

@media (prefers-contrast: more) {
  .portal-frame {
    background: var(--void);
    border: 1px solid var(--gold);
  }

  .portal-title {
    -webkit-text-fill-color: var(--gold-100);
    color: var(--gold-100);
  }
}

/* ── Neon elevation ────────────────────────────────────────────────────────
   Source-frequency pass. Everything here is luminance choreography on the
   existing Gold Circuit surfaces — no new markup, no new origin, no layout
   change. Both prefers-reduced-motion blocks above kill every animation and
   transition below via `*` + !important, and prefers-contrast strips the
   glows, so this layer costs nothing to the readers who asked it to. */

/* The energised centre node breathes. A sign-in page has exactly one living
   element, and it is the point the eye should rest on while the widget loads. */
.portal-node:nth-child(2) {
  animation: node-breathe 2.8s ease-in-out infinite;
}

@keyframes node-breathe {
  0%,
  100% {
    box-shadow:
      0 0 10px -2px var(--gold),
      0 0 0 1px var(--gold-600);
  }
  50% {
    box-shadow:
      0 0 22px 0 var(--gold),
      0 0 0 1px var(--gold-300);
  }
}

/* Iridescence drifts across the title glyphs. Slow enough to read as metal
   catching light, never as a marquee. */
.portal-title {
  background-size: 200% 100%;
  animation: iridesce 9s ease-in-out infinite alternate;
}

@keyframes iridesce {
  from {
    background-position: 0% 50%;
  }
  to {
    background-position: 100% 50%;
  }
}

/* Liquid-gold sweep on the one action that matters. The gradient rides the
   button's own colours; hover shifts light across it rather than repainting. */
.primary {
  background-image: linear-gradient(
    100deg,
    var(--gold-600),
    var(--gold) 45%,
    var(--gold-200) 55%,
    var(--gold) 65%,
    var(--gold-600)
  );
  background-size: 250% 100%;
  background-position: 0% 50%;
  transition:
    background-position 0.5s ease,
    box-shadow 0.3s ease;
}

.primary:hover:not(:disabled),
.primary:focus-visible {
  background-position: 100% 50%;
  box-shadow: var(--glow-key);
}

/* Focus is neon-certain. Inputs answer a keyboard with a double ring: hard
   inner line for position, soft outer bloom for state. */
input:focus-visible,
textarea:focus-visible {
  outline: none;
  border-color: var(--gold);
  box-shadow:
    0 0 0 1px var(--gold),
    0 0 18px -4px var(--gold);
}

/* State chips carry their own light: running amber-gold, ready green-white,
   hold steady, error hot. Colour was already there; the bloom makes the state
   legible from arm's length on a phone. */
.chip[data-tone="run"] {
  box-shadow: 0 0 12px -4px var(--gold);
}

.chip[data-tone="ok"] {
  box-shadow: 0 0 12px -4px var(--ok);
}

.chip[data-tone="error"] {
  box-shadow: 0 0 12px -4px var(--err);
}

/* The current pipeline step is the live conductor on the rail. */
.rail-step[data-current="true"] .rail-dot {
  box-shadow:
    0 0 0 1px var(--gold-600),
    0 0 14px -2px var(--gold);
}

/* Job cards lift toward the light on pointer hover only — a phone never sees
   this, and a mouse reads it as depth rather than motion. */
@media (hover: hover) {
  .job {
    transition: box-shadow 0.25s ease;
  }

  .job:hover {
    box-shadow: var(--glow-md);
  }
}

/* ── Portal phases ─────────────────────────────────────────────────────────
   Request or verify, keyed on a data attribute. Display state only — both
   phases are public sign-in machinery, and what a session gates lives on a
   different document. The code input renders in the same register the code
   arrives in: spaced capitals, impossible to misread. */

#portal-phases[data-phase="request"] .portal-phase-verify {
  display: none;
}

#portal-phases[data-phase="verify"] .portal-phase-request {
  display: none;
}

.code-input {
  text-transform: uppercase;
  letter-spacing: 0.45em;
  text-align: center;
  font-variant-numeric: tabular-nums;
  font-size: var(--step-1);
}
