/* Glass HUD shell (design/design_handoff_bridge_glass): the fixed backdrop
   and scrim, the frame, header, rail (room tabs + instruments), headline,
   alert chip, the three-pane grid, the detail stack, the loading and
   offline states, and the shell's responsive rules.
   Tokens and shared primitives (.gl-pane, .gl-title, .gl-btn, .gl-dot …)
   live in glass.css; this file only lays the shell out. */

@property --arc { syntax: '<angle>'; inherits: false; initial-value: 0deg; }

/* ── Page ─────────────────────────────────────────────────────────────── */
/* phone.css keeps the home and sign-in pages' scroll padding (145 px under
   their header, a bottom bar's height at the foot); the bridge has neither. */
html:has(> body.glass-bridge) { scroll-padding-top: 16px; scroll-padding-bottom: auto; }
/* A tap flashes the accent, not phone.css's retired brass. */
body.glass-bridge :is(button, a, input, select, textarea, summary) { -webkit-tap-highlight-color: var(--accent-20); }
/* Hyphenated names in room content (goods, ports, "auto-accept") stay whole;
   this was materials.css's one rule still in use. */
body.glass-bridge .kit-word { white-space: nowrap; }
body.glass-bridge {
  /* One first screen of shell content: viewport less the top padding 16,
     header 62, gap 8 and bottom padding 20. */
  --gl-first-h: calc(100vh - 106px);
  --gl-first-h: calc(100svh - 106px);
}

/* ── Backdrop and scrim, fixed behind the frame ───────────────────────── */
body.glass-bridge #bridge-viewport {
  position: fixed; inset: 0; z-index: 0;
  display: block; width: auto; height: auto; min-height: 0; max-height: none;
  margin: 0; padding: 0; border: 0; overflow: hidden;
  background: var(--bg); pointer-events: none;
}
body.glass-bridge .gl-scrim { position: fixed; inset: 0; z-index: 0; pointer-events: none; background: var(--scrim); }

/* ── Frame ────────────────────────────────────────────────────────────── */
body.glass-bridge .gl-frame {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; gap: var(--gap);
  box-sizing: border-box; min-height: 100vh; min-height: 100svh; height: auto;
  max-width: none; margin: 0; padding: 16px 24px 20px;
  background: none; overflow-x: clip;
}

/* ── Header: one pane, 60 px inside its borders ──────────────────────────
   The README's height:60px is content-box in the prototype, so the pane
   renders 62 px tall with its 1 px borders; the rail and headline start at
   y 86 as in the reference screenshots. */
body.glass-bridge .gl-header {
  position: relative; z-index: 3;
  display: flex; align-items: center; gap: 16px;
  /* At least 62 px, never fixed: enlarged text grows the header instead of
     running out of it. */
  box-sizing: border-box; height: auto; min-height: 62px; flex: none; min-width: 0;
  margin: 0; padding: 0 18px;
}
.gl-header .gl-ship { flex: 0 1 auto; min-width: 0; }
body.glass-bridge .gl-header #hdr-ship {
  /* 2 px of padding inside the ellipsis box keeps descenders unclipped; the
     negative margin keeps the 22/1.1 line box in place. */
  margin: -2px 0; padding: 2px 0; font: 600 22px/1.1 var(--font-head); letter-spacing: 0; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
body.glass-bridge .gl-header #hdr-commander {
  margin: 2px 0 0; font: 400 11px/1.4 var(--font-mono); color: var(--ink-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.gl-header-divider { flex: none; width: 1px; height: 32px; background: var(--line); }
.gl-header-label {
  display: block; font: 500 11px/1 var(--font-ui); letter-spacing: .18em;
  text-transform: uppercase; color: var(--accent); white-space: nowrap;
}
.gl-credits { flex: none; display: flex; flex-direction: column; }
.gl-credits-value {
  display: block; margin-top: 4px; font: 400 20px/1 var(--font-mono); color: var(--accent);
  text-shadow: 0 0 10px var(--accent-40); white-space: nowrap; font-variant-numeric: tabular-nums;
}
/* The journey rank button keeps opening the rank sheet; it reads as a label
   over its value, like the credits. */
body.glass-bridge .gl-header .journey-header-rank {
  display: flex; flex-direction: column; align-items: flex-start; flex: none; gap: 0;
  min-width: 0; min-height: 0; margin: 0; padding: 0; border: 0; border-radius: 0;
  background: none; color: var(--ink); font: inherit; letter-spacing: 0; line-height: 1;
  text-align: left; text-transform: none; white-space: nowrap; cursor: pointer;
}
body.glass-bridge .gl-header .journey-header-rank span { display: block; max-width: none; }
body.glass-bridge .gl-header .gl-rank-value { margin-top: 6px; font: 400 14px/1 var(--font-mono); color: var(--ink); letter-spacing: 0; }
body.glass-bridge .gl-header .journey-header-rank .gl-rank-goal { display: inline; }
/* ship-shell.js marks the header tight when the ship's name would be cut;
   the pinned goal gives way to the name. */
body.glass-bridge .gl-header.gl-header--tight .journey-header-rank .gl-rank-goal { display: none; }
body.glass-bridge .gl-header .journey-header-rank:hover .gl-rank-value { color: var(--accent); }
.gl-header .gl-unread { flex: none; }
.gl-header .gl-unread #hdr-badge { display: contents; }
.gl-header-actions { display: flex; align-items: center; gap: 16px; flex: none; margin-left: auto; }
/* Legacy masthead rules (tutorial.css, merchant.css, account.css) outrank the
   .gl-btn primitive on these controls; return them to the ghost button. */
body.glass-bridge .gl-header .tut-handle {
  position: static; inset: auto; flex: none; flex-direction: row; width: auto; min-width: 0; min-height: 0;
  height: 38px; padding: 0 14px; gap: 8px; border: 1px solid var(--accent-60); border-radius: 0;
  background: transparent; color: var(--accent); box-shadow: none;
}
body.glass-bridge .gl-header .tut-handle:hover { background: var(--accent-10); border-color: var(--accent); }
/* The count is mono 13px and keeps the button's .08em tracking, as in the
   prototype (tutorial.css zeroes it). */
body.glass-bridge .gl-header .tut-handle .pip,
body.glass-bridge .gl-header .tut-handle .pip.done {
  min-width: 0; padding: 0; border-radius: 0; background: none; color: inherit;
  font: 400 13px/1 var(--font-mono); letter-spacing: inherit;
}
body.glass-bridge .gl-header .tut-handle .pip:empty { display: none; }
body.glass-bridge .gl-header .tut-handle .vert {
  writing-mode: horizontal-tb; font: inherit; letter-spacing: inherit; text-transform: inherit; color: inherit;
}
body.glass-bridge .gl-header #account-open { min-width: 0; min-height: 0; padding: 0 14px; flex-shrink: 0; }
body.glass-bridge .gl-header #account-open .account-state-mark { width: 12px; height: 12px; margin: 0; color: currentColor; }

/* ── Body grid ────────────────────────────────────────────────────────── */
body.glass-bridge .gl-body {
  flex: 1 0 auto; display: grid; grid-template-columns: 220px minmax(0, 1fr);
  gap: var(--gap); align-items: start; min-width: 0;
}

/* ── Rail: room tabs over the instruments ─────────────────────────────── */
body.glass-bridge .gl-rail {
  position: relative; z-index: 2;
  display: flex; flex-direction: column; gap: var(--gap);
  box-sizing: border-box; min-width: 0; min-height: var(--gl-first-h);
}
/* Sticky beside the room detail, 16 px from the top. The rail is at least one
   first screen tall, so at first paint it ends with the panes, 20 px above
   the fold, and once stuck it stays whole on screen. The README rail (374 px
   of tabs over about 410 px of instruments) needs a screen about 900 px tall.
   On a shorter one (a 1440x790 or 1366x657 laptop window) it keeps the README
   sizes and scrolls with the page until its foot is 20 px above the bottom,
   then sticks by that foot, so the instruments and Messages stay in view
   while the detail scrolls. ship-shell.js measures the rail's height into
   --gl-rail-h. */
@media (min-width: 900px) {
  body.glass-bridge .gl-rail {
    position: sticky;
    top: min(16px, calc(100vh - 20px - var(--gl-rail-h, 0px)));
    top: min(16px, calc(100svh - 20px - var(--gl-rail-h, 0px)));
  }
}
/* The readings (gauges, drive, tick) lay out as the instruments' own rows,
   or as cells of its grid below 900 px wide. */
.gl-readings { display: contents; }
body.glass-bridge .gl-tabs { display: flex; flex-direction: column; flex: none; gap: 0; min-width: 0; margin: 0; padding: 6px; }
body.glass-bridge .gl-tabs .gl-tab {
  position: relative; box-sizing: border-box;
  display: grid; grid-template-columns: 40px minmax(0, 1fr) auto; align-items: center; gap: 3px 10px;
  width: 100%; min-width: 0; min-height: 0; margin: 0; padding: 9px 12px 9px 14px;
  border: 0; border-left: 2px solid transparent; border-radius: 0; box-shadow: none;
  background: transparent; color: var(--ink); font: inherit; text-align: left; text-transform: none;
  letter-spacing: 0; cursor: pointer;
}
body.glass-bridge .gl-tabs .gl-tab:hover { background: rgba(255, 255, 255, .04); }
body.glass-bridge .gl-tabs .gl-tab[aria-pressed="true"] { border-left-color: var(--accent); background: var(--accent-12); color: var(--accent); }
body.glass-bridge .gl-tabs .gl-tab:focus-visible { z-index: 1; }
/* README: background url(art) center/cover. The image repeats (the default),
   so the art continues under the translucent 1 px border as in the
   reference; no-repeat would leave a flat grey ring. */
.gl-tab-thumb {
  grid-row: 1 / 3; box-sizing: content-box; width: 40px; height: 40px;
  background: center / cover; border: 1px solid var(--line); filter: saturate(.5);
}
/* Helm and Ops before the first report: the plain ground their room shows. */
.gl-tab-thumb:not([style*="url("]) { background-color: var(--bg); }
/* Room names fit their tab at every width; enlarged text breaks a name over
   two lines (hyphenated where English allows) rather than cutting it to
   "Engine…". */
body.glass-bridge .gl-tabs .gl-tab .name {
  display: block; min-width: 0; font: 600 15px/1.2 var(--font-head); letter-spacing: 0;
  color: inherit; white-space: normal; overflow-wrap: anywhere; hyphens: auto; text-decoration: none;
}
.gl-tabs .gl-tab > .gl-dot { justify-self: end; }
.gl-tab-state { grid-column: 2 / 4; min-width: 0; font: 400 11px/1.2 var(--font-mono); color: var(--ink-muted); }

/* ── Instruments ──────────────────────────────────────────────────────── */
body.glass-bridge .gl-instruments { flex: 1 0 auto; display: flex; flex-direction: column; min-width: 0; margin: 0; }
/* "Hull 1" never splits; with enlarged text it drops under the title. */
body.glass-bridge .gl-instruments > .gl-head { flex-wrap: wrap; }
body.glass-bridge .gl-instruments > .gl-head > .gl-meta { white-space: nowrap; }
.gl-gauge, .gl-drive {
  display: grid; grid-template-columns: 56px minmax(0, 1fr); gap: 12px; align-items: center;
  min-width: 0; margin: 0; padding: var(--row-pad) 0; border-bottom: 1px solid var(--line-soft);
}
.gl-ring {
  --ring: var(--accent); --ring-glow: var(--accent-20);
  position: relative; display: grid; place-items: center; width: 56px; height: 56px; border-radius: 50%;
  background: conic-gradient(from 225deg, var(--ring) 0 var(--arc), var(--ring-track) var(--arc) 270deg, transparent 270deg);
  box-shadow: 0 0 14px var(--ring-glow);
  transition: --arc var(--t-arc, 300ms) ease-out;
}
.gl-ring::before { content: ''; position: absolute; inset: 5px; border-radius: 50%; background: var(--bg); }
.gl-ring-reading { position: relative; font: 400 12px/1 var(--font-mono); color: var(--ink); white-space: nowrap; }
.gl-gauge[data-tone="bad"] .gl-ring { --ring: var(--bad); --ring-glow: color-mix(in srgb, var(--bad) 20%, transparent); }
.gl-gauge figcaption, .gl-drive > div { display: flex; flex-direction: column; align-items: flex-start; min-width: 0; }
.gl-gauge-name { font: 500 11px/1 var(--font-ui); letter-spacing: .18em; text-transform: uppercase; color: var(--ink-muted); }
body.glass-bridge .gl-gauge-state { display: block; margin-top: 4px; font: 400 13px/1.2 var(--font-mono); color: var(--ink); overflow-wrap: break-word; hyphens: auto; }
/* "Drain paused" sits tight under the state, so a two-line state (Reserve
   for N ticks, under way) still fits the README rows at 1440x900. */
.gl-gauge-note { display: block; margin-top: 1px; font: 400 11px/1 var(--font-mono); color: var(--ink-muted); }
.gl-gauge[data-tone="bad"] .gl-gauge-state, .gl-drive[data-tone="red"] .gl-gauge-state { color: var(--bad); }
.gl-drive > .gl-dot { justify-self: center; }
/* DOM order is label, value, countdown (the tour and screen readers meet the
   tick first); the grid shows label and countdown over the value. */
.gl-tick {
  display: grid; grid-template-columns: auto minmax(0, 1fr); grid-template-areas: "label next" "value value";
  align-items: baseline; min-width: 0; padding: 10px 0 0;
}
.gl-tick > .gl-gauge-name { grid-area: label; }
/* 10 px keeps a long clock message (or its loading bar) off the label. */
body.glass-bridge #hdr-tick { grid-area: next; margin-left: 10px; font: 400 11px/1.2 var(--font-mono); color: var(--ink-muted); text-align: right; }
.gl-tick[data-attention="true"] #hdr-tick { color: var(--bad); }
.gl-tick-value {
  grid-area: value; display: block; margin-top: 6px; font: 400 30px/1 var(--font-mono); color: var(--accent);
  letter-spacing: .08em; text-shadow: 0 0 12px var(--accent-40); font-variant-numeric: tabular-nums;
}
.gl-instruments > .gl-spacer { flex: 1 1 auto; }

/* ── Main column: headline over the art, then the open room ─────────────
   The headline row is flexible. An invisible item spanning the headline and
   pane rows is one first screen tall, so on a desktop the three panes end
   20 px above the fold and the room detail starts below it. The open plate
   and its wrapper are subgrids, so the panes size the second row. */
body.glass-bridge .gl-main {
  display: grid; grid-template-columns: minmax(0, 1fr);
  grid-template-rows: [hero] minmax(var(--hero-min), 1fr) [panes] auto [detail] auto;
  row-gap: var(--gap); min-width: 0;
}
body.glass-bridge .gl-main::before {
  content: ''; grid-column: 1; grid-row: hero / detail; height: var(--gl-first-h);
  visibility: hidden; pointer-events: none;
}
.gl-headline {
  grid-row: hero; grid-column: 1; position: relative;
  display: flex; align-items: flex-end; justify-content: space-between; gap: 24px;
  min-width: 0; min-height: var(--hero-min); padding: 0 8px 18px;
}
.gl-headline-copy { position: relative; isolation: isolate; display: flex; flex-direction: column; gap: 8px; min-width: 0; text-shadow: 0 1px 4px #000; }
/* The headline is the one text drawn straight on the art, and its 11 px
   kicker must keep 4.5:1. From 1400 px wide and 861 px tall the scrim and the
   README text shadow manage that over every room photo and the merchant
   bridge view (Helm under way, and ocean ports), so the art shows through
   untouched; only the other port exteriors (a bright planet limb) bring the
   kicker down to about 4:1, and there a light shade sits behind the copy. On smaller or shorter screens the headline rides
   higher over the art and the kicker falls to about 2:1 in several rooms, so
   the shade is stronger. Either way it is a blurred patch of the ground
   colour: no edge, no pane. */
.gl-headline-copy::before {
  position: absolute; z-index: -1; inset: -28px -56px -20px -36px;
  background: rgb(10 14 11 / .46); filter: blur(24px); border-radius: 48px; pointer-events: none;
}
@media (max-width: 1399px), (max-height: 860px) {
  .gl-headline-copy::before { content: ''; }
}
@media (min-width: 1400px) and (min-height: 861px) {
  body.glass-bridge:has(#bridge-viewport[data-room="helm"]:not([data-art="none"], [data-art="merchant-bridge.webp"])) .gl-headline-copy::before {
    content: ''; background: rgb(10 14 11 / .25);
  }
}
/* Preview mode, and a showroom hull, say so above the kicker. */
.gl-flags { display: flex; flex-wrap: wrap; gap: 6px; margin: 0; text-shadow: none; }
.gl-flag {
  display: inline-block; max-width: 100%; box-sizing: border-box; padding: 3px 6px;
  border: 1px solid var(--accent-60); background: var(--glass);
  font: 400 10px/1.2 var(--font-mono); letter-spacing: .08em; text-transform: uppercase; color: var(--accent);
}
.gl-kicker { margin: 0; font: 500 11px/1 var(--font-ui); letter-spacing: .18em; text-transform: uppercase; color: var(--accent); }
/* 40 px at every width (ruling 4). Every title fits one line from 320 px up.
   With enlarged text, a title that no longer fits breaks inside its box, as
   the room tabs do, instead of being cut off at the viewport edge. It breaks
   at its soft hyphens first: ship-shell.js writes "Engineering" with U+00AD
   at Engi·neer·ing, so every engine breaks it at the same place and draws the
   hyphen. hyphens: auto ignores the dictionary in a word that has soft
   hyphens. Breaking anywhere is only the last resort. */
body.glass-bridge .gl-room-title { margin: 0; font: 600 40px/1 var(--font-head); letter-spacing: 0; color: var(--ink-title); overflow-wrap: anywhere; hyphens: auto; }
.gl-line { margin: 0; font: 400 14px/1.3 var(--font-ui); color: var(--ink); }

/* Alert chip, in the alert ship state only. */
body.glass-bridge .gl-alert {
  /* Above the headline copy's shade where the chip wraps under it. */
  position: relative; z-index: 1;
  display: flex; align-items: center; gap: 12px; flex: 0 1 auto; min-width: 0;
  margin: 0; padding: 10px 14px; border: 1px solid var(--bad); box-shadow: 0 0 18px var(--bad-33);
}
body.glass-bridge .gl-alert[hidden] { display: none; }
.gl-alert-copy { min-width: 0; }
.gl-alert-label { margin: 0; font: 500 11px/1 var(--font-ui); letter-spacing: .18em; text-transform: uppercase; color: var(--bad); }
.gl-alert-text { margin: 3px 0 0; font: 400 13px/1.3 var(--font-ui); color: var(--ink); }
body.glass-bridge .gl-alert .gl-btn { flex: none; margin-left: 8px; padding: 0 12px; }

/* ── Plates: the open room occupies the panes and detail rows ─────────── */
body.glass-bridge .plate-wrap {
  grid-row: panes / span 2; grid-column: 1;
  display: grid; grid-template-rows: subgrid; grid-template-columns: minmax(0, 1fr);
  min-width: 0; min-height: 0; margin: 0;
}
body.glass-bridge .plate { margin: 0; padding: 0; border: 0; border-radius: 0; background: none; box-shadow: none; overflow: visible; }
/* The plate is the shell's container: a room stylesheet that still frames
   its plate (#panel-comms has a painted border and fill) must not frame it. */
body.glass-bridge .plate-wrap > .plate:is(#panel-helm, #panel-ops, #panel-science, #panel-tactical, #panel-comms, #panel-engineering) {
  min-height: 0; margin: 0; padding: 0; border: 0; border-image: none; border-radius: 0;
  background: none; box-shadow: none; color: inherit;
}
body.glass-bridge .plate.open {
  display: grid; grid-row: 1 / span 2; grid-column: 1;
  grid-template-rows: subgrid; grid-template-columns: minmax(0, 1fr);
}
body.glass-bridge .plate.open > .gl-panes { grid-row: 1; }
body.glass-bridge .plate.open > .gl-detail { grid-row: 2; }
@supports not (grid-template-rows: subgrid) {
  body.glass-bridge .gl-main::before { display: none; }
  body.glass-bridge .plate-wrap { display: block; }
  body.glass-bridge .plate.open { display: flex; flex-direction: column; gap: var(--gap); }
}

/* Three panes, 1 : 1 : 1.5. Pane layouts are the .gl-pane-a/b/c primitives. */
body.glass-bridge .gl-panes {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.5fr);
  gap: var(--gap); align-items: stretch; min-width: 0;
}
body.glass-bridge .gl-panes > .gl-pane { min-width: 0; margin: 0; }
.gl-head > .gl-meta:empty { display: none; }

/* What the shell itself renders inside a room's plate (the three pane heads
   and the ship-link card) keeps the primitive look whatever a room
   stylesheet says about its plate's h3, p or button: a rule written as
   #panel-<room> p carries ID weight and would outrank glass.css. These
   repeat the primitives at a weight no room rule reaches. */
body.glass-bridge :is(#panel-helm, #panel-ops, #panel-science, #panel-tactical, #panel-comms, #panel-engineering) > .gl-panes > .gl-pane > .gl-head > .gl-title {
  margin: 0; padding: 0; border: 0;
  font: 500 11px/1 var(--font-ui); letter-spacing: .18em; text-transform: uppercase; color: var(--accent);
}
body.glass-bridge :is(#panel-helm, #panel-ops, #panel-science, #panel-tactical, #panel-comms, #panel-engineering) > .gl-panes > .gl-pane > .gl-head > .gl-meta {
  margin: 0; font: 400 11px/1 var(--font-mono); letter-spacing: 0; text-transform: none; color: var(--ink-muted);
}
body.glass-bridge #shell-link-state > .gl-notice-tag {
  margin: 0; font: 400 10px/1 var(--font-mono); letter-spacing: .08em; text-transform: uppercase; color: var(--tone);
}
body.glass-bridge #shell-link-state > .gl-notice-title {
  margin: 4px 0 0; font: 600 16px/1.2 var(--font-head); letter-spacing: 0; text-transform: none; color: var(--ink);
}
body.glass-bridge #shell-link-state > .gl-notice-body {
  margin: 2px 0 0; font: 400 13px/1.4 var(--font-ui); letter-spacing: 0; text-transform: none; color: var(--ink-muted);
}
body.glass-bridge #shell-link-state > .gl-btn {
  height: auto; min-height: 38px; margin: 10px 0 0; padding: 0 14px;
  font: 500 13px/1 var(--font-ui); letter-spacing: .08em; text-transform: uppercase;
}
/* The same guard in the accessibility modes. A room stylesheet may restyle
   its plate with !important under forced colours, prefers-contrast: more or
   prefers-reduced-transparency: reduce (comms-materials.css paints
   #panel-comms on Canvas with a 2px frame and turns every h3, p, span and
   button inside it to CanvasText, which blanked the Retry button). The plate
   stays unframed and the shell's pane heads and link card keep their colours;
   these !important rules outweigh any room rule. */
@media (forced-colors: active), (prefers-contrast: more), (prefers-reduced-transparency: reduce) {
  body.glass-bridge .plate-wrap > .plate:is(#panel-helm, #panel-ops, #panel-science, #panel-tactical, #panel-comms, #panel-engineering) {
    border: 0 !important; background: none !important; color: inherit !important;
  }
  body.glass-bridge :is(#panel-helm, #panel-ops, #panel-science, #panel-tactical, #panel-comms, #panel-engineering) > .gl-panes > .gl-pane > .gl-head > .gl-title { color: var(--accent) !important; }
  body.glass-bridge :is(#panel-helm, #panel-ops, #panel-science, #panel-tactical, #panel-comms, #panel-engineering) > .gl-panes > .gl-pane > .gl-head > .gl-meta { color: var(--ink-muted) !important; }
  body.glass-bridge #shell-link-state > .gl-notice-tag { color: var(--tone) !important; }
  body.glass-bridge #shell-link-state > .gl-notice-title { color: var(--ink) !important; }
  body.glass-bridge #shell-link-state > .gl-notice-body { color: var(--ink-muted) !important; }
  body.glass-bridge #shell-link-state > .gl-btn { color: var(--on-tone) !important; }
  body.glass-bridge #shell-link-state > .gl-btn:is(:disabled, [aria-disabled="true"]) { color: var(--ink-muted) !important; }
}
@media (forced-colors: active) {
  body.glass-bridge :is(#panel-helm, #panel-ops, #panel-science, #panel-tactical, #panel-comms, #panel-engineering) > .gl-panes > .gl-pane > .gl-head > :is(.gl-title, .gl-meta),
  body.glass-bridge #shell-link-state > :is(.gl-notice-tag, .gl-notice-title, .gl-notice-body) { color: CanvasText !important; }
  body.glass-bridge #shell-link-state > .gl-btn { background: ButtonText !important; color: ButtonFace !important; border: 1px solid ButtonText !important; }
  body.glass-bridge #shell-link-state > .gl-btn:hover { background: Highlight !important; color: HighlightText !important; border-color: Highlight !important; }
  body.glass-bridge #shell-link-state > .gl-btn:is(:disabled, [aria-disabled="true"]) { background: Canvas !important; color: GrayText !important; border: 1px dashed GrayText !important; }
}

/* Detail: the room's full controls, below the fold. */
body.glass-bridge .gl-detail { min-width: 0; margin-top: 12px; }

/* ── Loading: titles and 12 px bars until the first report ────────────── */
.gl-pane-skel { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; min-height: 0; }
.gl-pane-c > .gl-pane-skel { gap: 10px; }
.gl-skel-row {
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
  box-sizing: border-box; min-height: 29px; padding: var(--row-pad) 0; border-bottom: 1px solid var(--line-soft);
}
.gl-skel-row--3 { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 1fr); justify-items: end; }
.gl-skel-row--3 > :first-child { justify-self: start; }
.gl-skel-row--head { min-height: 0; padding: 0 0 6px; border-bottom-color: var(--accent-60); }
.gl-pane-skel .gl-skel { flex: none; }
.gl-skel-notice { display: flex; flex-direction: column; gap: 9px; margin-bottom: 12px; padding: 12px; border: 1px solid var(--line); }
.gl-skel-field { display: flex; align-items: center; box-sizing: border-box; height: 40px; padding: 0 12px; border: 1px solid var(--line); background: var(--field); }
.gl-skel-field .gl-skel { width: var(--skel-w); }
.gl-skel-hint { margin: 13px 0 12px; }
.gl-skel-foot { display: flex; margin-top: 14px; }
.gl-skel-foot--end { justify-content: flex-end; margin-top: 0; }
.gl-skel-button { display: block; box-sizing: border-box; width: 132px; height: 38px; border: 1px dashed var(--line); }
.gl-skel-chart {
  flex: 1 1 auto; box-sizing: border-box; min-height: 252px; border: 1px solid var(--line);
  background-color: rgba(255, 255, 255, .03);
  background-image: repeating-linear-gradient(90deg, var(--line-soft) 0 1px, transparent 1px 48px),
    repeating-linear-gradient(0deg, var(--line-soft) 0 1px, transparent 1px 48px);
}
.gl-skel-legend { display: flex; gap: 18px; }
.gl-skel-seg { display: flex; gap: 6px; }
.gl-skel-seg > span { box-sizing: border-box; width: 72px; height: 28px; border: 1px dashed var(--line); }
.gl-skel-seg > span:last-child { margin-left: 13px; }
/* Shell text slots read as 12 px bars until the first report. */
body.glass-bridge.gl-awaiting :is(#hdr-ship, #hdr-commander, .gl-credits-value, .gl-tab-state:not([data-static]), .gl-gauge-state, .gl-ring-reading, .gl-tick-value, #hdr-tick, .gl-line) {
  color: transparent; text-shadow: none; user-select: none;
  background: linear-gradient(var(--line-soft), var(--line-soft)) 0 50% / 100% 12px no-repeat;
}
/* The countdown's bar is the width of "Next in mm:ss", at the right. */
body.glass-bridge.gl-awaiting #hdr-tick { background-size: 86px 12px; background-position: 100% 50%; }

/* ── Ship link: loading is announced only; offline is the red Pane B card ─ */
body.glass-bridge .gl-link-notice:is([data-state="loading"], [data-state="online"]) {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
body.glass-bridge .gl-link-notice { flex: none; }
body.glass-bridge .gl-link-notice #shell-retry { white-space: nowrap; }

/* ── Responsive ───────────────────────────────────────────────────────── */
/* The README is silent on height: a short desktop screen keeps the README
   tabs and instruments (the rail sticks by its foot, above), never a smaller
   thumbnail or a scrolling box of readings. */
@media (max-width: 1199px) {
  body.glass-bridge .gl-panes { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  body.glass-bridge .gl-panes > .gl-pane-c { grid-column: 1 / -1; }
}
/* Below 1100 px one header row is tight once Rank and Account are there, so
   the rank drops "· pinned goal" (its sheet still names the goal), and from
   900 to 1099 px the divider goes. The ship name keeps its room; it is the
   one item that can still shrink (with an ellipsis). */
@media (max-width: 1099px) {
  body.glass-bridge .gl-header .journey-header-rank .gl-rank-goal { display: none; }
}
@media (min-width: 900px) and (max-width: 1099px) {
  .gl-header-divider { display: none; }
}
/* Below 900 px the rail is a tab strip with the instruments under it.
   Layout only: the headline row's minimum height (--hero-min, 170px in the
   Compact density) drops to 132px here, 116px below 640px and 96px in phone
   landscape, so the panes come up sooner on a short screen. The headline's
   type is unchanged. */
@media (max-width: 899px) {
  body.glass-bridge { --hero-min: 132px; }
  body.glass-bridge .gl-frame { padding: 12px 16px 16px; }
  body.glass-bridge .gl-header { flex-wrap: wrap; height: auto; min-height: 60px; row-gap: 10px; padding: 10px 14px; }
  .gl-header-divider { display: none; }
  body.glass-bridge .gl-body { grid-template-columns: minmax(0, 1fr); }
  body.glass-bridge .gl-rail { min-height: 0; }
  body.glass-bridge .gl-tabs { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 4px; }
  body.glass-bridge .gl-tabs .gl-tab { grid-template-columns: minmax(0, 1fr) auto; column-gap: 6px; padding: 8px 8px 8px 10px; }
  .gl-tab-thumb { display: none; }
  .gl-tab-state { grid-column: 1 / 3; }
  body.glass-bridge .gl-instruments { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: 16px; align-items: stretch; }
  .gl-instruments > .gl-head { grid-column: 1 / -1; }
  .gl-instruments > .gl-spacer { display: none; }
  .gl-instruments .gl-tick { padding: var(--row-pad) 0; border-bottom: 1px solid var(--line-soft); }
  body.glass-bridge .gl-instruments > .shell-feedback { padding: var(--row-pad) 0; border-top: 0; border-bottom: 1px solid var(--line-soft); }
  body.glass-bridge .gl-main::before { display: none; }
  /* The type stays the README's at every width (the room title is 40px;
     "Engineering" is 194px wide, which fits the 320px layout). */
  .gl-headline { flex-wrap: wrap; }
}
/* Six names no longer fit one row: two rows of three. */
@media (max-width: 759px) {
  body.glass-bridge .gl-tabs { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 639px) {
  body.glass-bridge { --hero-min: 116px; }
  body.glass-bridge .gl-frame { padding: 10px 12px 16px; }
  /* Phones: the ship, credits and rank share the first row (the ship name
     gives way, with an ellipsis); the controls (Unread, Tasks, Account, Exit)
     take a row of their own under it as README small buttons (ruling 9:
     12 px text, padding 0 10px, .08em tracking, at least 44 px tall;
     ship-shell.js also marks them gl-btn--sm here), sharing the row's width.
     Four need about 354 px, so below 410 px they pair up in two even rows.
     The header's ::before is the zero-height line break. */
  body.glass-bridge .gl-header { column-gap: 8px; row-gap: 0; padding: 8px 12px; justify-content: flex-start; }
  body.glass-bridge .gl-header .gl-ship { flex: 1 1 0; }
  body.glass-bridge .gl-header .gl-header-actions { display: contents; }
  body.glass-bridge .gl-header::before { content: ''; order: 1; flex: 0 0 100%; height: 0; }
  body.glass-bridge .gl-header :is(#hdr-bell, .tut-handle, #account-open, #signout) {
    order: 2; flex: 1 1 auto; box-sizing: border-box; height: auto; min-height: 44px; margin: 8px 0 0; padding: 0 10px;
    font-size: 12px; letter-spacing: .08em;
  }
  /* The Tasks count stays mono 13px at the README's .08em. */
  body.glass-bridge .gl-header .tut-handle .pip { letter-spacing: .08em; }
  body.glass-bridge .gl-instruments { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 12px; }
  body.glass-bridge .gl-panes { grid-template-columns: minmax(0, 1fr); }
  body.glass-bridge .gl-panes > .gl-pane-c { grid-column: auto; }
  .gl-headline { gap: 12px; padding: 0 4px 12px; }
  body.glass-bridge .gl-alert { flex: 1 1 100%; flex-wrap: wrap; }
  body.glass-bridge .gl-alert .gl-btn { margin-left: auto; }
}
@media (max-width: 409px) {
  body.glass-bridge .gl-header:has(#account-open:not([hidden])) :is(#hdr-bell, .tut-handle, #account-open, #signout) { flex-basis: calc(50% - 4px); }
}
@media (max-width: 374px) {
  body.glass-bridge .gl-tabs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 340px) {
  body.glass-bridge .gl-instruments { grid-template-columns: minmax(0, 1fr); }
}
/* Phone landscape: a short, wide screen keeps the strip and three columns. */
@media (max-width: 1279px) and (max-height: 500px) and (pointer: coarse) {
  body.glass-bridge { --hero-min: 96px; }
  body.glass-bridge .gl-main::before { display: none; }
  body.glass-bridge .gl-rail { min-height: 0; }
}
/* Phone landscape from 780 px wide (844x390 and the like): the first screen
   reaches the headline, its alert chip and the top of Pane A. The header
   keeps one row (the ship name gives way, with an ellipsis); the room tabs
   (two columns of three) and the instruments (the 2x3 grid, rows closed up)
   share one band; the instruments' title is left to screen readers (the hull
   number is in the header). Rings, type and targets keep their sizes. */
@media (min-width: 780px) and (max-width: 899px) and (max-height: 500px) and (pointer: coarse) {
  body.glass-bridge .gl-frame { padding-top: 8px; }
  body.glass-bridge .gl-header { flex-wrap: nowrap; padding-block: 6px; }
  body.glass-bridge .gl-rail { display: grid; grid-template-columns: 260px minmax(0, 1fr); align-items: start; }
  body.glass-bridge .gl-tabs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  body.glass-bridge .gl-tabs .gl-tab { padding-block: 5px; }
  body.glass-bridge .gl-instruments { column-gap: 12px; }
  body.glass-bridge .gl-instruments > .gl-head {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  body.glass-bridge .gl-instruments :is(.gl-gauge, .gl-drive) { padding-block: 3px; }
  body.glass-bridge .gl-instruments .gl-tick { padding-block: 3px; }
  body.glass-bridge .gl-instruments > .shell-feedback { padding-block: 3px; }
  .gl-headline { padding-bottom: 12px; }
}
/* Carried over from the retired ship-shell.css: on phones the in-room
   toolbars scroll with the page rather than sticking over the content, and
   the Comms jump links keep 44 px targets (comms-materials.css gives 40). */
@media (max-width: 760px), (max-width: 1279px) and (max-height: 500px) and (pointer: coarse) {
  body.glass-bridge :is(.nav-toolbar, #ops-mini-hold, #comms-jump-links) { position: static; }
  body.glass-bridge #comms-jump-links a { min-height: 44px; }
}
/* Phones and touch screens: every shell control is at least 44 px tall
   (the same breakpoint as the glass.css primitives). */
@media (max-width: 640px), (pointer: coarse) {
  body.glass-bridge .gl-header :is(.gl-btn, .tut-handle, #account-open, .journey-header-rank) { min-height: 44px; height: auto; }
  body.glass-bridge .gl-tabs .gl-tab { min-height: 44px; }
  body.glass-bridge .gl-alert .gl-btn { min-height: 44px; }
  body.glass-bridge #shell-link-state > .gl-btn { min-height: 44px; }
}

/* ── Motion and contrast ──────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) { .gl-ring { transition: none; } }
@media (forced-colors: active) {
  body.glass-bridge .gl-scrim { display: none; }
  .gl-headline-copy::before { display: none; }
  .gl-flag { border-color: CanvasText; color: CanvasText; background: Canvas; }
  .gl-ring, .gl-tab-thumb { forced-color-adjust: none; }
  /* The rings keep their conic arc (forced-color-adjust: none) but are drawn
     in the system palette: a CanvasText arc on a dim track, a Canvas disc and
     a CanvasText reading (GrayText while the first report is pending). A
     reading on the dark disc was unreadable with a light palette. */
  .gl-ring { --ring: CanvasText; --ring-track: color-mix(in srgb, CanvasText 25%, Canvas); box-shadow: none; }
  .gl-ring::before { background: Canvas; }
  .gl-ring-reading { color: CanvasText; }
  body.glass-bridge .gl-tabs .gl-tab[aria-pressed="true"] { border-left-color: Highlight; color: Highlight; }
  body.glass-bridge .gl-header .tut-handle { background: ButtonFace; color: ButtonText; border-color: ButtonText; }
  body.glass-bridge .gl-header .tut-handle:hover { background: Highlight; color: HighlightText; border-color: Highlight; }
  body.glass-bridge .gl-alert { border-color: CanvasText; }
  body.glass-bridge.gl-awaiting :is(#hdr-ship, #hdr-commander, .gl-credits-value, .gl-tab-state:not([data-static]), .gl-gauge-state, .gl-ring-reading, .gl-tick-value, #hdr-tick, .gl-line) { color: GrayText; background: none; }
}
