:root {
  --navy-canvas: #20272e;
  --navy-toolbar: #1a2026;
  --navy-reference: #1d242a;
  --navy-surface: #2a323a;
  --navy-surface-raised: #252e36;
  --navy-rule: #46525d;
  --navy-rule-subtle: #35404a;
  --navy-text: #dfe4e8;
  --navy-text-strong: #eef2f5;
  --navy-muted: #93a0ab;
  --navy-dim: #83909b;
  --navy-metric: #d7c9a0;
  --navy-focus: #7fd4ff;
  --navy-future: #d2b7ff;
  --navy-count: #8fa3b3;

  /* Typography. IBM Plex, self-hosted in vendor/fonts. One superfamily gives
     a UI face and a data face that share metrics and design, which suits a
     reference tool better than two unrelated families.

     The system fallbacks are load-bearing, not decoration: the data carries
     CJK and kana in ship-name origins and Japanese publisher names, and the
     latin subset has no glyphs for those. The browser falls through per
     character, so the system font at the end of the stack is what actually
     renders them. */
  --font-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;
}

body {
  font-family: var(--font-sans);
  margin: 0;
  /* The page had no background of its own and relied on the browser default.
     That is fine until something composites behind it. */
  background: #fff;
}

body.is-loading #stats-bar,
body.is-loading .view-panel { display: none; }

body.has-dialog { overflow: hidden; }

/* ---- Application loading / failure state ---- */

.app-status {
  min-height: 70vh;
  display: grid;
  place-items: center;
  padding: 2rem;
  box-sizing: border-box;
  background: #eef1ec;
}

.app-status.hidden { display: none; }

.app-status-card {
  display: flex;
  align-items: center;
  gap: 1rem;
  width: min(34rem, 100%);
  padding: 1.15rem 1.3rem;
  border: 1px solid #c8d0c5;
  border-radius: 5px;
  background: #fff;
  box-shadow: 0 2px 10px rgba(31, 42, 36, 0.08);
}

.app-status-card h2 { margin: 0 0 0.2rem; font-size: 1rem; color: #23303d; }
.app-status-card p { margin: 0; color: #5c6670; font-size: 0.85rem; line-height: 1.45; }
.app-status-card.is-error { border-left: 4px solid #a3282f; }

.loading-spinner {
  width: 1.35rem;
  height: 1.35rem;
  flex: 0 0 auto;
  border: 3px solid #d8dee4;
  border-top-color: #2563a8;
  border-radius: 50%;
  animation: loading-spin 0.8s linear infinite;
}

@keyframes loading-spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .loading-spinner { animation: none; border-top-color: #d8dee4; }
}

.app-retry-btn {
  margin-top: 0.75rem;
  padding: 0.35rem 0.75rem;
  border: 1px solid #2563a8;
  border-radius: 3px;
  background: #fff;
  color: #2563a8;
  font: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
}
.app-retry-btn:hover { background: #eaf1fa; }

.image-fallback-message {
  display: grid;
  place-items: center;
  width: 100%;
  min-height: 5rem;
  padding: 1rem;
  box-sizing: border-box;
  background: #e3e7ea;
  color: #6b7480;
  font-size: 0.8rem;
  text-align: center;
}

header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.75rem 1.5rem;
}

header h1 {
  margin: 0;
  font-size: 1.4rem;
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.navy-selector {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  color: #59636d;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.navy-selector select {
  min-width: 9rem;
  padding: 0.35rem 0.55rem;
  border: 1px solid #c7ced5;
  border-radius: 4px;
  background: #fff;
  color: #1d2832;
  font: inherit;
  font-size: 0.78rem;
  letter-spacing: normal;
  text-transform: none;
}

.subtitle {
  margin: 0.2rem 0 0;
  color: #555;
  font-size: 0.9rem;
}

#map-wrap {
  position: relative;
}

#map {
  height: 82vh;
  width: 100%;
}

/* ---- Stats bar ---- */

#stats-bar {
  padding: 0.5rem 1.5rem;
  background: #f4f6f8;
  border-top: 1px solid #e2e5e8;
  border-bottom: 1px solid #e2e5e8;
  font-size: 0.85rem;
  color: #333;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}

#stats-bar .stats-figure {
  font-weight: 700;
  color: #14396b;
}

#stats-bar .stats-assumption-note {
  color: #5b3a9e;
  font-size: 0.78rem;
}

#stats-bar .coverage-link {
  margin-left: auto;
  background: none;
  border: 1px solid #2563a8;
  color: #2563a8;
  border-radius: 4px;
  padding: 0.2rem 0.6rem;
  font-size: 0.8rem;
  cursor: pointer;
}
#stats-bar .coverage-link:hover {
  background: #eaf1fa;
}

#open-about,
#open-global-search {
  padding: 0.36rem 0.55rem 0.36rem 0.7rem;
  border: 1px solid #c7cfd7;
  border-radius: 4px;
  background: #fff;
  color: #3b4650;
  font: inherit;
  font-size: 0.78rem;
  cursor: pointer;
}
#open-about:hover,
#open-about.is-active,
#open-global-search:hover { border-color: #8795a2; background: #f7f9fb; color: #2563a8; }
#open-global-search:disabled { opacity: 0.55; cursor: wait; }
#open-global-search kbd {
  margin-left: 0.45rem;
  padding: 0.08rem 0.3rem;
  border: 1px solid #ccd3da;
  border-bottom-width: 2px;
  border-radius: 3px;
  background: #f3f5f7;
  color: #68737d;
  font: 0.68rem var(--font-mono);
}

.confidence-legend {
  position: relative;
  font-size: 0.78rem;
}

.confidence-legend summary {
  color: #2563a8;
  cursor: pointer;
  white-space: nowrap;
}

.confidence-legend-panel {
  position: absolute;
  top: calc(100% + 0.45rem);
  left: 0;
  z-index: 1900;
  width: 21rem;
  padding: 0.65rem 0.75rem;
  border: 1px solid #ccd3da;
  border-radius: 4px;
  background: #fff;
  box-shadow: 0 3px 12px rgba(35, 48, 61, 0.2);
}

.confidence-legend-panel p {
  display: grid;
  grid-template-columns: 5.5rem 1fr;
  align-items: start;
  gap: 0.45rem;
  margin: 0 0 0.45rem;
  line-height: 1.35;
}
.confidence-legend-panel p:last-child { margin-bottom: 0; }
.confidence-legend-panel .confidence-badge { justify-self: start; }

/* ---- Fleet legend ---- */

.filter-legend {
  margin-top: 0.6rem;
  padding-top: 0.5rem;
  border-top: 1px solid #eee;
  font-size: 0.75rem;
  color: #555;
}
.filter-legend .legend-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.1rem 0;
}
.filter-legend .swatch {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  display: inline-block;
  flex-shrink: 0;
}
.filter-legend .legend-note {
  margin-top: 0.3rem;
  color: #888;
  font-style: italic;
}

/* ---- Base markers (sized by ship count, colored by fleet) ---- */

.base-marker-icon {
  background: none;
  border: none;
}
.base-marker-circle {
  border-radius: 50%;
  border: 2px solid #c9d1d8;
  box-shadow: 0 0 0 1px rgba(15, 20, 24, 0.45), 0 0 7px rgba(0, 0, 0, 0.75);
  cursor: pointer;
}
.base-marker-icon.is-ghosted .base-marker-circle {
  border-style: dashed;
  filter: saturate(0.15);
}

/* ---- Base sidebar ---- */

#base-sidebar {
  position: fixed;
  top: 0;
  right: 0;
  width: 360px;
  max-width: 90vw;
  height: 100vh;
  background: var(--navy-canvas);
  color: var(--navy-text);
  box-shadow: -2px 0 10px rgba(0, 0, 0, 0.2);
  overflow-y: auto;
  z-index: 1000;
  padding: 1.25rem;
  box-sizing: border-box;
  transition: transform 0.2s ease;
}

#base-sidebar.hidden {
  transform: translateX(100%);
  pointer-events: none;
}

.close-btn {
  position: absolute;
  top: 0.5rem;
  right: 0.75rem;
  background: none;
  border: none;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  color: #666;
}
.close-btn:hover {
  color: #000;
}

.sidebar-header h2 {
  margin: 0 0 0.15rem;
  font-size: 1.2rem;
  padding-right: 1.5rem;
}

.sidebar-header .breadcrumb {
  font-size: 0.82rem;
  color: #666;
  margin: 0 0 0.6rem;
}

.sidebar-header .base-notes {
  font-size: 0.85rem;
  color: #333;
  background: #f4f4f4;
  border-left: 3px solid #888;
  padding: 0.5rem 0.7rem;
  margin: 0.5rem 0 0.8rem;
  border-radius: 2px;
}

.documented-units {
  font-size: 0.8rem;
  color: #444;
  margin: 0 0 1rem;
}
.documented-units summary {
  cursor: pointer;
  font-weight: 600;
  color: #555;
}
.documented-units ul {
  margin: 0.4rem 0 0;
  padding-left: 1.1rem;
}

.type-group {
  margin-bottom: 1rem;
}
.type-group h3 {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #888;
  margin: 0 0 0.35rem;
  border-bottom: 1px solid #e5e5e5;
  padding-bottom: 0.2rem;
}

.ship-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  border-bottom: 1px solid #f0f0f0;
  padding: 0.45rem 0.1rem;
  cursor: pointer;
  font: inherit;
}
.ship-item-main {
  flex: 1;
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.ship-item:hover {
  background: #f7f9fc;
}
.ship-item .ship-name {
  font-weight: 600;
  font-size: 0.9rem;
}
.ship-item .ship-sub {
  font-size: 0.78rem;
  color: #666;
  display: block;
  font-weight: 400;
}

.no-ships {
  font-size: 0.88rem;
  color: #666;
  font-style: italic;
}

#base-sidebar .close-btn { color: var(--navy-muted); }
#base-sidebar .close-btn:hover { color: var(--navy-focus); }
#base-sidebar .sidebar-header h2 { color: var(--navy-text-strong); }
#base-sidebar .sidebar-header .breadcrumb { color: var(--navy-muted); }

#base-sidebar .sidebar-header .base-notes {
  color: var(--navy-text);
  background: var(--navy-surface-raised);
  border-left-color: var(--navy-focus);
}

#base-sidebar .documented-units { color: var(--navy-text); }
#base-sidebar .documented-units summary { color: var(--navy-muted); }

#base-sidebar .type-group h3 {
  color: var(--navy-muted);
  border-bottom-color: var(--navy-rule-subtle);
}

#base-sidebar .ship-item { border-bottom-color: var(--navy-rule-subtle); }
#base-sidebar .ship-item:hover { background: var(--navy-surface-raised); }
#base-sidebar .ship-item .ship-name { color: var(--navy-text-strong); }
#base-sidebar .ship-item .ship-sub { color: var(--navy-muted); }
#base-sidebar .ship-item-main:focus-visible { outline-color: var(--navy-focus); }
#base-sidebar .no-ships { color: var(--navy-muted); }

/* ---- Confidence badges ---- */

.confidence-badge {
  display: inline-block;
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 0.12rem 0.4rem;
  border-radius: 10px;
  white-space: nowrap;
}
.confidence-badge.clickable {
  cursor: pointer;
  border: 1px solid transparent;
}
.confidence-badge.clickable:hover {
  border-color: currentColor;
}

/* ---- Map control panels (filter box) ---- */

.control-panel {
  position: absolute;
  z-index: 900;
  background: #fff;
  border-radius: 4px;
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.4);
  padding: 0.6rem 0.75rem;
  font-size: 0.82rem;
}

#type-filter {
  top: 12px;
  right: 12px;
  width: 190px;
}

.map-focus-controls {
  position: absolute;
  z-index: 900;
  left: 52px;
  top: 12px;
  display: flex;
  gap: 0.4rem;
}

.map-focus-controls button {
  padding: 0.42rem 0.65rem;
  border: 1px solid var(--navy-rule);
  border-radius: 4px;
  background: rgba(26, 32, 38, 0.96);
  color: var(--navy-text);
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
}

.map-focus-controls button:hover,
.map-focus-controls button:focus-visible {
  border-color: var(--navy-focus);
  color: var(--navy-focus);
}

.filter-legend .swatch.is-neutral { background: #7b8792; border: 1px dashed #c9d1d8; }

#view-map {
  background: var(--navy-canvas);
  color: var(--navy-text);
}

#view-map .control-panel {
  background: rgba(26, 32, 38, 0.96);
  border: 1px solid var(--navy-rule);
  color: var(--navy-text);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.55);
}

#view-map .link-btn { color: var(--navy-focus); }
#view-map .filter-row input { accent-color: var(--navy-focus); }
#view-map .filter-row .filter-count { color: var(--navy-muted); }
#view-map .filter-legend { border-top-color: var(--navy-rule-subtle); color: var(--navy-text); }
#view-map .filter-legend .legend-note { color: var(--navy-muted); }

#view-map .leaflet-bar {
  border: 1px solid var(--navy-rule);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
}

#view-map .leaflet-bar a,
#view-map .leaflet-bar a:visited {
  border-bottom-color: var(--navy-rule);
  background: var(--navy-toolbar);
  color: var(--navy-text-strong);
}

#view-map .leaflet-bar a:hover,
#view-map .leaflet-bar a:focus {
  background: var(--navy-surface);
  color: var(--navy-focus);
}

#view-map .leaflet-control-attribution {
  background: rgba(26, 32, 38, 0.86);
  color: var(--navy-muted);
}

#view-map .leaflet-control-attribution a { color: var(--navy-focus); }

.filter-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 0.4rem;
  font-size: 0.85rem;
}

.filter-actions {
  font-size: 0.78rem;
}

#filter-collapse {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0;
  border: none;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
  text-align: left;
}

.filter-chevron {
  width: 0.42rem;
  height: 0.42rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform 0.15s ease;
  opacity: 0.75;
}

#type-filter.is-collapsed .filter-chevron { transform: rotate(-45deg); }
#type-filter.is-collapsed #filter-body { display: none; }
#type-filter.is-collapsed .filter-header { margin-bottom: 0; }

/* Collapsing hides what All/None act on, so the shortcuts go with it. */
#type-filter.is-collapsed .filter-actions { display: none; }

@media (prefers-reduced-motion: reduce) {
  .filter-chevron { transition: none; }
}

.link-btn {
  background: none;
  border: none;
  color: #2563a8;
  cursor: pointer;
  padding: 0;
  font-size: inherit;
  text-decoration: underline;
}

.filter-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.12rem 0;
}
.filter-row label {
  flex: 1;
  cursor: pointer;
}
.filter-row .filter-count {
  color: #999;
  font-size: 0.75rem;
}

/* ---- Confidence popover ---- */

#confidence-popover {
  position: fixed;
  z-index: 3000;
  background: #fff;
  border-radius: 6px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.35);
  padding: 0.75rem 0.9rem;
  width: 280px;
  font-size: 0.82rem;
}
#confidence-popover.hidden {
  display: none;
}
#confidence-popover h4 {
  margin: 0 0 0.4rem;
  font-size: 0.85rem;
}
#confidence-popover dl {
  margin: 0;
}
#confidence-popover dt {
  font-weight: 600;
  color: #555;
  margin-top: 0.5rem;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
#confidence-popover dt:first-child {
  margin-top: 0;
}
#confidence-popover dd {
  margin: 0.1rem 0 0;
}

/* ---- Global search palette ---- */

#global-search-backdrop {
  position: fixed;
  inset: 0;
  z-index: 3500;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 9vh;
  background: rgba(14, 20, 25, 0.58);
}
#global-search-backdrop.hidden { display: none; }

#global-search-modal {
  position: relative;
  width: min(680px, calc(100vw - 2rem));
  max-height: 78vh;
  overflow: hidden;
  padding: 1.2rem;
  box-sizing: border-box;
  border-radius: 7px;
  background: #fff;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
}

#global-search-modal h2 { margin: 0 0 0.15rem; padding-right: 2rem; font-size: 1rem; }
.global-search-label { color: #6b7480; font-size: 0.75rem; }
#global-search-input {
  width: 100%;
  margin-top: 0.55rem;
  padding: 0.65rem 0.75rem;
  box-sizing: border-box;
  border: 1px solid #aeb8c1;
  border-radius: 4px;
  color: #1f2a33;
  font: inherit;
  font-size: 0.95rem;
}
#global-search-input:focus { outline: 2px solid #91b8df; outline-offset: 1px; }
.global-search-hint { margin: 0.35rem 0 0.6rem; color: #84909a; font-size: 0.7rem; }
#global-search-results { max-height: 55vh; overflow-y: auto; border-top: 1px solid #e1e5e8; }
.global-search-empty { margin: 1.2rem 0; color: #68737d; font-size: 0.82rem; text-align: center; }
.global-search-counts { display: flex; justify-content: center; gap: 0.8rem; flex-wrap: wrap; }
.global-search-counts span { color: #4d5963; }
.global-search-group h3 {
  margin: 0;
  padding: 0.55rem 0.4rem 0.25rem;
  color: #7a858f;
  font-size: 0.65rem;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}
.global-search-result {
  display: grid;
  grid-template-columns: 4.6rem 1fr;
  gap: 0.55rem;
  width: 100%;
  padding: 0.5rem 0.45rem;
  border: 0;
  border-radius: 3px;
  background: transparent;
  color: #25313b;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.global-search-result:hover,
.global-search-result:focus-visible,
.global-search-result.is-active { background: #eaf1f8; outline: none; }
.global-search-kind {
  align-self: start;
  padding-top: 0.08rem;
  color: #6c7883;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.global-search-result strong { display: block; font-size: 0.82rem; }
.global-search-result small { display: block; margin-top: 0.08rem; color: #69747e; font-size: 0.72rem; line-height: 1.35; }

/* ---- Ship detail modal ---- */

#ship-modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 2000;
  display: flex;
  align-items: center;
  justify-content: center;
}
#ship-modal-backdrop.hidden {
  display: none;
}

#ship-modal {
  position: relative;
  background: #fff;
  width: min(480px, calc(100vw - 2rem));
  max-height: 88vh;
  overflow-y: auto;
  border-radius: 6px;
  padding: 1.5rem;
  box-sizing: border-box;
}

.modal-breadcrumb {
  font-size: 0.78rem;
  color: #777;
  margin: 0 0 0.6rem;
  /* The close button is absolutely positioned over this line's right end. A
     long chain -- "PLAN > Southern Theater Command Navy > South Sea Fleet >
     Yulin Naval Base" -- ran underneath it and lost its last words. */
  padding-right: 2.75rem;
}

/* The frame is shaped to the photograph, not the other way round: each render
   sets aspect-ratio inline from the image's measured proportions, so a wide
   broadside shot keeps its bow and stern. 16:9 is only the fallback for a
   placeholder silhouette or an unmeasured image. */
.modal-image-wrap {
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #eef1f4;
  border-radius: 4px;
  overflow: hidden;
  margin-bottom: 0.75rem;
  display: flex;
  align-items: center;
  justify-content: center;
}
.modal-image-wrap img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Only for images outside the allowed ratio band, where the frame stopped at a
   limit instead of matching the photo. Fit it whole and letterbox the rest. */
.modal-image-wrap.is-letterboxed img {
  object-fit: contain;
}
.modal-image-wrap.is-diagram {
  background: #f4f4f4;
}
.modal-image-wrap.is-diagram img {
  object-fit: contain;
}
/* Breathing room only where the frame is larger than the diagram. When the
   frame matches the drawing's own proportions there is nothing to pad against,
   and padding would just shrink it. */
.modal-image-wrap.is-diagram.is-letterboxed img {
  padding: 1rem;
  box-sizing: border-box;
}
.modal-image-caption {
  font-size: 0.72rem;
  color: #888;
  margin: 0.2rem 0 1rem;
}
.modal-image-caption a {
  color: #888;
}

#modal-content h2 {
  margin: 0 0 0.1rem;
  padding-right: 1.5rem;
  font-size: 1.25rem;
}
.modal-subtitle {
  color: #555;
  font-size: 0.95rem;
  margin: 0 0 0.6rem;
}

.modal-facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.25rem 0.75rem;
  font-size: 0.88rem;
  margin: 0.75rem 0;
}
.modal-facts dt {
  color: #777;
}
.modal-facts dd {
  margin: 0;
}
/* A stated gap, not a missing value: set apart from sourced facts so it is not
   read as one, without the alarm of an error colour. */
.modal-facts dd.fact-unpublished {
  color: #6b6b6b;
  font-style: italic;
}

.modal-notes {
  font-size: 0.85rem;
  background: #f4f4f4;
  border-left: 3px solid #888;
  padding: 0.5rem 0.7rem;
  margin: 0.75rem 0;
  border-radius: 2px;
}

.modal-sources h4 {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #888;
  margin: 0.9rem 0 0.4rem;
}
.modal-sources ul {
  margin: 0;
  padding-left: 1.1rem;
  font-size: 0.85rem;
}
.modal-sources li {
  margin-bottom: 0.35rem;
}

/* ---- Class overview + system chips (in ship modal) ---- */

.modal-divider {
  border: none;
  border-top: 1px solid #e5e5e5;
  margin: 1.1rem 0;
}

.class-overview h4,
.modal-sources h4 {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #888;
  margin: 0 0 0.4rem;
}

.class-overview p {
  font-size: 0.88rem;
  line-height: 1.5;
  margin: 0 0 0.6rem;
  color: #333;
}

.class-technical-specs {
  margin: 0.55rem 0 0.8rem;
  padding: 0.7rem 0.8rem;
  border: 1px solid #d7dee4;
  border-radius: 3px;
  background: #fbfcfd;
}

.class-technical-specs h5 {
  margin: 0 0 0.45rem;
  color: #68737d;
  font-size: 0.7rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.class-spec-grid {
  display: grid;
  grid-template-columns: 8.25rem 1fr;
  gap: 0.3rem 0.75rem;
  margin: 0;
  font-size: 0.82rem;
  line-height: 1.4;
}

.class-spec-grid dt { color: #6c7780; }
.class-spec-grid dd { margin: 0; color: #26333d; }

.class-overview .class-spec-note {
  margin: 0.65rem 0 0;
  color: #6c7780;
  font-size: 0.74rem;
  line-height: 1.4;
}

.class-overview .class-spec-sources {
  margin: 0.45rem 0 0;
  color: #6c7780;
  font-size: 0.72rem;
  line-height: 1.45;
}

.class-spec-sources a { color: #526f86; }
.class-profile-unavailable { color: #888 !important; font-size: 0.82rem !important; }

.system-group {
  margin-top: 0.8rem;
}
.system-group h5 {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: #999;
  margin: 0 0 0.35rem;
}

.system-chip {
  display: inline-block;
  background: #eef3fa;
  border: 1px solid #cfe0f2;
  color: #14396b;
  border-radius: 14px;
  padding: 0.25rem 0.65rem;
  margin: 0 0.35rem 0.35rem 0;
  font-size: 0.78rem;
  cursor: pointer;
}
.system-chip:hover {
  background: #dceafc;
}
.system-chip.weapon-chip {
  background: #fbeeee;
  border-color: #f0d0d0;
  color: #7a2020;
}
.system-chip.weapon-chip:hover {
  background: #f7dcdc;
}

/* ---- System detail modal (topmost layer — reachable from ship modal directly, or from aircraft modal) ---- */

#system-modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.35);
  z-index: 3000;
  display: flex;
  align-items: center;
  justify-content: center;
}
#system-modal-backdrop.hidden {
  display: none;
}
#system-modal {
  position: relative;
  background: #fff;
  width: min(420px, calc(100vw - 2rem));
  max-height: 80vh;
  overflow-y: auto;
  border-radius: 6px;
  padding: 1.4rem;
  box-sizing: border-box;
}
#system-modal h3 {
  margin: 0 0 0.15rem;
  padding-right: 1.5rem;
  font-size: 1.1rem;
}
.system-modal-meta {
  font-size: 0.78rem;
  color: #888;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  margin: 0 0 0.7rem;
}
.system-modal-image {
  width: 100%;
  height: auto;
  object-fit: contain;
  border-radius: 4px;
  margin: 0 0 0.6rem;
  background: #eef1f4;
  display: block;
}
.system-modal-desc {
  font-size: 0.88rem;
  line-height: 1.5;
  color: #333;
  margin: 0 0 0.8rem;
}
.system-modal-sources {
  font-size: 0.8rem;
  color: #777;
}
.system-modal-sources a {
  color: #2563a8;
}

/* ---- Aircraft (air wing) modal — sits between ship modal and system modal ---- */

#aircraft-modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.35);
  z-index: 2500;
  display: flex;
  align-items: center;
  justify-content: center;
}
#aircraft-modal-backdrop.hidden {
  display: none;
}
#aircraft-modal {
  position: relative;
  /* background is set once, on the dark palette, by the shared
     "#ship-modal, #system-modal, #aircraft-modal, ..." rule further down --
     this used to also declare background: #fff at equal specificity, which
     only lost because it happens to sit earlier in the file. Removed so
     there is one declaration to find, not two that depend on source order. */
  width: min(460px, calc(100vw - 2rem));
  max-height: 85vh;
  overflow-y: auto;
  border-radius: 6px;
  padding: 1.4rem;
  box-sizing: border-box;
}
#aircraft-modal h3 {
  margin: 0 0 0.6rem;
  padding-right: 1.5rem;
  font-size: 1.1rem;
}
/* .airwing-list-item is a <button>. Browsers give buttons their own UA
   text colour (the "buttontext" system colour, effectively black) rather
   than inheriting the page's -- background:none was reset here but color
   never was, so every aircraft name rendered black regardless of the panel
   behind it. appearance:none plus an explicit color is the fix; every other
   button-shaped component in this file (.future-card, .info-index-link)
   already sets both, and this one simply never had them. */
.airwing-list-item {
  appearance: none;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  border-bottom: 1px solid var(--panel-rule);
  padding: 0.5rem 0.1rem;
  color: inherit;
  cursor: pointer;
  font: inherit;
}
.airwing-list-item:hover {
  background: var(--navy-surface);
}
.airwing-list-item img {
  width: 56px;
  height: 42px;
  object-fit: cover;
  border-radius: 3px;
  background: var(--panel-inset);
  flex-shrink: 0;
}
.airwing-list-item .aircraft-name {
  font-weight: 600;
  font-size: 0.88rem;
  display: block;
  color: var(--navy-text-strong);
}
.airwing-list-item .aircraft-role {
  font-size: 0.76rem;
  color: var(--navy-muted);
}

.aircraft-back-link {
  background: none;
  border: none;
  color: var(--link);
  cursor: pointer;
  font-size: 0.8rem;
  padding: 0;
  margin: 0 0 0.6rem;
  display: block;
}
.aircraft-back-link:hover { color: var(--link-strong); }

.aircraft-detail-image {
  width: 100%;
  height: auto;
  object-fit: contain;
  border-radius: 4px;
  margin: 0 0 0.5rem;
  background: var(--panel-inset);
  display: block;
}
.aircraft-detail-caption {
  font-size: 0.72rem;
  color: var(--navy-muted);
  margin: 0 0 0.8rem;
}
.aircraft-role-label {
  font-size: 0.82rem;
  color: var(--navy-muted);
  margin: 0 0 0.6rem;
}

/* ---- Coverage/gap modal ---- */

#coverage-modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 2000;
  display: flex;
  align-items: center;
  justify-content: center;
}
#coverage-modal-backdrop.hidden {
  display: none;
}
#coverage-modal {
  position: relative;
  background: #fff;
  width: min(560px, calc(100vw - 2rem));
  max-height: 88vh;
  overflow-y: auto;
  border-radius: 6px;
  padding: 1.5rem;
  box-sizing: border-box;
}
#coverage-modal h2 {
  margin: 0 0 0.2rem;
  padding-right: 1.5rem;
  font-size: 1.2rem;
}
.coverage-intro {
  font-size: 0.85rem;
  color: #555;
  margin: 0 0 1rem;
}
.coverage-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
}
.coverage-table th {
  text-align: left;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: #888;
  border-bottom: 1px solid #ddd;
  padding: 0.3rem 0.4rem;
}
.coverage-table td {
  padding: 0.4rem;
  border-bottom: 1px solid #f0f0f0;
  vertical-align: top;
}
.coverage-bar-wrap {
  background: #eee;
  border-radius: 3px;
  height: 8px;
  width: 100px;
  overflow: hidden;
}
.coverage-bar-fill {
  background: #2563a8;
  height: 100%;
}
.coverage-gap-high { color: #a3282f; font-weight: 600; }
.coverage-assumption-tag { color: #5b3a9e; font-size: 0.78rem; }

/* ---- Top-level view tabs ---- */

#view-tabs {
  display: flex;
  gap: 0.25rem;
  padding: 0 1.5rem;
  border-bottom: 1px solid #e2e5e8;
}

.view-tab {
  appearance: none;
  border: none;
  background: none;
  padding: 0.55rem 1.1rem;
  font: inherit;
  font-size: 0.9rem;
  font-weight: 600;
  color: #6b7480;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}

.view-tab:hover { color: #23303d; }

.view-tab.is-active {
  color: #23303d;
  border-bottom-color: #23303d;
}

.view-panel.hidden { display: none; }

.stats-asof {
  color: #68737d;
  font-size: 0.72rem;
}

/* ---- About / method ---- */

.about-view {
  min-height: 70vh;
  padding: 2.25rem 1.5rem 4rem;
  box-sizing: border-box;
  background: #f4f5f3;
  color: #27313a;
}

.about-article {
  width: min(54rem, 100%);
  margin: 0 auto;
  line-height: 1.65;
}

.about-kicker {
  margin: 0 0 0.3rem;
  color: #2563a8;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.about-article h2 {
  max-width: 43rem;
  margin: 0 0 0.8rem;
  color: #1f2b35;
  font-size: 2rem;
  line-height: 1.15;
}

.about-lede { max-width: 48rem; margin: 0 0 1.5rem; color: #4c5863; font-size: 1.05rem; }
.about-article h3 { margin: 2rem 0 0.4rem; font-size: 1.05rem; }
.about-article h4 { margin: 0 0 0.35rem; font-size: 0.92rem; }
.about-article p { margin-top: 0.35rem; }
.about-article ul { padding-left: 1.2rem; }
.about-article li + li { margin-top: 0.35rem; }

.about-date-card {
  padding: 0.9rem 1rem;
  border-left: 4px solid #2563a8;
  background: #e9eef3;
}

.about-date-card p { margin: 0.15rem 0 0; }

/* A caution rather than a neutral fact, so it is warm-toned and set apart from
   the blue data-currency card without reading as an error. */
.about-caveat {
  padding: 0.9rem 1rem;
  border-left: 4px solid #b4791f;
  background: #faf3e6;
  margin: 1rem 0;
}

.about-caveat p { margin: 0.15rem 0 0; }

.about-method-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem;
}

.about-method-grid section {
  padding: 1rem;
  border: 1px solid #d4d9dd;
  background: #fff;
}

.about-method-grid p { margin-bottom: 0; }
.about-closing { margin-top: 2rem; padding-top: 1rem; border-top: 1px solid #ccd2d6; font-weight: 600; }

/* ---- Fleet + Future Fleet shared classes ----
   Visual grammar borrowed loosely from ONI's recognition guide: a muted
   ground, class blocks led by reporting name over type designation, artwork
   scaled to real hull length, and a plain rule separating class from hulls.
   These rules are shared with the Future Fleet grid and, in part, with the
   ship/class detail modal — do not assume a rule here is Fleet-page-only. */

.fleet-control { display: flex; flex-direction: column; gap: 0.2rem; }

.fleet-control label {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #6b7480;
  font-weight: 600;
}

.fleet-control select,
.fleet-control input {
  font: inherit;
  font-size: 0.85rem;
  padding: 0.32rem 0.5rem;
  border: 1px solid #c8cdd2;
  border-radius: 3px;
  background: #fff;
  min-width: 12rem;
}

.fleet-empty { font-size: 0.9rem; color: #6b7480; padding: 2rem 0; }

.fleet-group {
  background: #dfe6dc;
  border: 1px solid #c4cfc0;
  padding: 1rem 1.15rem 1.15rem;
  margin-bottom: 1.5rem;
}

/* The L-shaped rule capping each class name is the poster's signature device. */
.fleet-group-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
  flex-wrap: wrap;
  border-top: 2px solid #2f3a33;
  padding-top: 0.5rem;
}

.fleet-group-titles h2 {
  margin: 0;
  font-size: 1.15rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #1f2a24;
  display: flex;
  align-items: center;
  gap: 0.45rem;
}

.fleet-swatch {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  display: inline-block;
}

.fleet-designation {
  margin: 0.1rem 0 0;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #55635b;
  font-weight: 600;
}

.fleet-group-counts {
  margin: 0;
  font-size: 0.78rem;
  color: #55635b;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.fleet-type-chip {
  background: #2f3a33;
  color: #eef1ec;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  padding: 0.1rem 0.4rem;
  border-radius: 2px;
}

/* Artwork width tracks hull length, so capital ships dominate the page. */
.fleet-art-row { margin: 0.85rem 0 0.2rem; }

.fleet-art { min-width: 150px; transition: width 0.25s ease; }

/* Box width encodes hull length and cannot be reshaped to the photo, so the
   photo is fitted inside it. `cover` here was cropping a portrait build shot
   down to a horizontal band through its middle. */
.fleet-art img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 190px;
  object-fit: contain;
  border: 1px solid #b6c2b1;
  background: #cdd6c9;
}

.fleet-art-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 76px;
  border: 1px dashed #b6c2b1;
  background: #d6ded2;
}

.fleet-length {
  margin: 0.3rem 0 0;
  text-align: center;
  font-size: 0.78rem;
  font-weight: 600;
  color: #2f3a33;
  letter-spacing: 0.02em;
}

.fleet-length-missing { color: #8a9490; font-weight: 400; font-style: italic; }

.fleet-unverified {
  font-weight: 400;
  font-style: italic;
  color: #7a857e;
  border-bottom: 1px dotted #9aa69f;
  cursor: help;
}

.fleet-spec-note {
  margin: 0.5rem 0 0;
  font-size: 0.74rem;
  color: #5f6b64;
  font-style: italic;
}

.class-snapshot {
  margin: 0.55rem 0 0.8rem;
  padding: 0.65rem 0.75rem;
  border: 1px solid #dbe2e8;
  border-radius: 3px;
  background: #f6f8fa;
}
.class-snapshot h5 { margin: 0 0 0.35rem; color: #68737d; font-size: 0.7rem; letter-spacing: 0.05em; text-transform: uppercase; }
.class-snapshot ul { margin: 0; padding-left: 1.05rem; }
.class-snapshot li { margin: 0.18rem 0; color: #45515b; font-size: 0.77rem; line-height: 1.4; }
.class-snapshot p { margin: 0; color: #68737d; font-size: 0.78rem; }

.related-news-panel {
  margin-top: 1rem;
  padding-top: 0.85rem;
  border-top: 1px solid #e2e6e9;
}
.related-news-panel h4 { margin: 0 0 0.45rem; color: #737d86; font-size: 0.75rem; letter-spacing: 0.05em; text-transform: uppercase; }
.related-news-content h5 { margin: 0 0 0.3rem; color: #65717b; font-size: 0.68rem; letter-spacing: 0.05em; text-transform: uppercase; }
.related-news-content > p { margin: 0; color: #7b858e; font-size: 0.78rem; }
.related-news-content ul { margin: 0; padding: 0; list-style: none; }
.related-news-content li {
  display: grid;
  grid-template-columns: 5.2rem 1fr auto;
  align-items: start;
  gap: 0.45rem;
  padding: 0.38rem 0;
  border-bottom: 1px solid #edf0f2;
  font-size: 0.77rem;
  line-height: 1.35;
}
.related-news-content time { color: #7a858f; white-space: nowrap; }
.related-news-content a { color: #2563a8; text-decoration: none; }
.related-news-content a:hover { text-decoration: underline; }
.related-news-automatic { margin-top: 0.8rem; padding-top: 0.65rem; border-top: 1px dashed #d7dde2; }
.related-news-meta { display: flex; align-items: flex-start; gap: 0.3rem; }
.related-news-source-count { color: #7a858f; font-size: 0.68rem; white-space: nowrap; }
.related-news-content .related-news-caveat { margin: 0.4rem 0 0; color: #7b858e; font-size: 0.7rem; line-height: 1.4; }

.ship-item-main:focus-visible {
  outline: 2px solid #2563a8;
  outline-offset: 2px;
}

.hull-number {
  font-family: var(--font-mono);
  font-size: 1.3rem;
  font-weight: 700;
  line-height: 1.1;
  color: #1f2a24;
}

.hull-name {
  font-size: 0.86rem;
  font-weight: 600;
  color: #23303d;
}

.hull-meta {
  font-size: 0.72rem;
  color: #6b7480;
  margin-bottom: 0.3rem;
}

.hull-footer {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  flex-wrap: wrap;
  margin-top: auto;
}

/* ---- inferred identity ----
   Deliberately NOT styled like a confidence badge. Confidence rates the basing
   claim; this rates whether the pennant on the card is the right one at all.
   Two different questions, so two different visual languages. */

/* The ship modal is a LIGHT surface (white) — unlike the Fleet Infographic,
   which is dark and is where the .info-artwork chips live. These must be
   dark-on-light or the text vanishes, which is exactly what happened the
   first time. */

.identity-badge {
  font-size: 0.6rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #7a3560;
  background: #f9e9f2;
  border: 1px dashed #c08fae;
  border-radius: 2px;
  padding: 0 4px;
  cursor: help;
  white-space: nowrap;
}

.identity-warning {
  border: 1px dashed #b07a9e;
  background: #fdf4f9;
  border-radius: 3px;
  padding: 10px 12px;
  margin: 12px 0;
}

.identity-warning h4 {
  margin: 0 0 8px;
  font-size: 0.8rem;
  color: #7a3560;
}

.identity-warning dl { margin: 0; font-size: 0.78rem; }
.identity-warning dt { color: #8d4f74; font-weight: 600; margin-top: 6px; }
.identity-warning dd { margin: 2px 0 0; color: #3a2f36; }


/* ==========================================================================
   News & Deployments — the dark analytical workspace
   ==========================================================================

   Session 9 deliberately kept News light, separating reading surfaces from the
   exploratory canvas. Session 24 reverses that at the user's direction: News
   now defaults to the same `--navy-*` workspace as Fleet, Future Fleet and the
   Map, so all four tabs read as one application rather than three plus a
   document.

   Light is retained as a real, selectable alternative — not a fallback. This is
   the one long-form reading surface here, and a reader working through a
   chronology at length may simply prefer paper. The choice persists in
   localStorage under `wnfe.newsTheme`.

   Two rules for anything added below:

   * Every rule is scoped to `#view-news[data-news-theme="dark"]`. The light
     path is the file's existing, unscoped News CSS and must keep working —
     both themes are supported surfaces, and check-render.py asserts both.
   * Modals, the stats bar and the header stay light in BOTH themes. That is
     unchanged from Session 9 and is not what the user asked to reverse: the
     dialog stack is shared with every other tab, and re-theming it from a
     News-scoped preference would leak this setting across the application.
   ========================================================================== */

#view-news[data-news-theme="dark"] {
  background: var(--navy-canvas);
  color: var(--navy-text);
}

#view-news[data-news-theme="dark"] #news-controls {
  background: var(--navy-toolbar);
  border-bottom-color: var(--navy-rule-subtle);
}

#view-news[data-news-theme="dark"] .fleet-control label { color: var(--navy-muted); }

#view-news[data-news-theme="dark"] .fleet-control select,
#view-news[data-news-theme="dark"] .fleet-control input {
  border-color: var(--navy-rule);
  background: var(--navy-surface);
  color: var(--navy-text);
  color-scheme: dark;
}

#view-news[data-news-theme="dark"] .fleet-control input::placeholder { color: var(--navy-dim); }

#view-news[data-news-theme="dark"] .fleet-control select:focus-visible,
#view-news[data-news-theme="dark"] .fleet-control input:focus-visible {
  outline: 2px solid var(--navy-focus);
  outline-offset: 2px;
}

#view-news[data-news-theme="dark"] #news-summary,
#view-news[data-news-theme="dark"] .news-auto-note { color: var(--navy-muted); }

#view-news[data-news-theme="dark"] #news-summary strong { color: var(--navy-text-strong); }

/* ---- Band and section headings ---- */

#view-news[data-news-theme="dark"] .news-section-head h3,
#view-news[data-news-theme="dark"] .news-live-head h3 { color: var(--navy-text-strong); }

#view-news[data-news-theme="dark"] .news-drafted-head h3 { color: var(--navy-future); }

#view-news[data-news-theme="dark"] .news-section-head p,
#view-news[data-news-theme="dark"] .news-live-head p,
#view-news[data-news-theme="dark"] .news-drafted-head > p,
#view-news[data-news-theme="dark"] .news-subsection-head p { color: var(--navy-muted); }

#view-news[data-news-theme="dark"] .news-live-head p strong,
#view-news[data-news-theme="dark"] .news-drafted-head > p strong { color: var(--navy-text); }

#view-news[data-news-theme="dark"] .news-subsection-head h4 { color: var(--navy-text-strong); }

#view-news[data-news-theme="dark"] .news-drafted-band { border-top-color: var(--navy-rule-subtle); }

/* ---- Tables ---- */

#view-news[data-news-theme="dark"] .news-table { background: var(--navy-reference); }

#view-news[data-news-theme="dark"] .news-table th {
  color: var(--navy-muted);
  border-bottom-color: var(--navy-rule);
}

#view-news[data-news-theme="dark"] .news-table td { border-bottom-color: var(--navy-rule-subtle); }

#view-news[data-news-theme="dark"] .news-table tr:hover td { background: var(--navy-surface-raised); }

#view-news[data-news-theme="dark"] .news-live-table { background: var(--navy-reference); }

#view-news[data-news-theme="dark"] .news-drafted-table {
  background: #241f2e;
  border-left-color: #6b53914d;
}

#view-news[data-news-theme="dark"] .news-date { color: var(--navy-text-strong); }

#view-news[data-news-theme="dark"] .news-headline { color: var(--navy-text-strong); }

#view-news[data-news-theme="dark"] .news-summary,
#view-news[data-news-theme="dark"] .news-drafted-significance { color: var(--navy-text); }

#view-news[data-news-theme="dark"] .news-live-pub,
#view-news[data-news-theme="dark"] .news-live-also,
#view-news[data-news-theme="dark"] .news-live-foot,
#view-news[data-news-theme="dark"] .news-live-empty,
#view-news[data-news-theme="dark"] .news-movement-why { color: var(--navy-muted); }

#view-news[data-news-theme="dark"] .news-movement-why strong { color: var(--navy-dim); }

#view-news[data-news-theme="dark"] .news-drafted-significance strong,
#view-news[data-news-theme="dark"] .news-drafted-mark { color: var(--navy-future); }

/* ---- Links out to publishers ---- */

#view-news[data-news-theme="dark"] .news-sources a,
#view-news[data-news-theme="dark"] .news-live-also a,
#view-news[data-news-theme="dark"] .news-live-headline { color: var(--navy-focus); }

#view-news[data-news-theme="dark"] .news-live-headline { border-bottom-color: transparent; }

#view-news[data-news-theme="dark"] .news-live-headline:hover {
  color: #b7e6ff;
  border-bottom-color: #b7e6ff;
}

/* ---- Links in to our own records ----
   Cyan is the interactive accent and violet marks future or assumption-tier
   information, exactly as the shared visual system has it. A class reference is
   neither, so it takes a neutral steel rather than borrowing violet's meaning. */

#view-news[data-news-theme="dark"] .news-link {
  color: var(--navy-focus);
  border-bottom-color: #4a7f99;
}

#view-news[data-news-theme="dark"] .news-link:hover { color: #b7e6ff; border-bottom-color: #b7e6ff; }

#view-news[data-news-theme="dark"] .news-link-class {
  color: #b9c8d6;
  border-bottom-color: #5d6c7a;
}

#view-news[data-news-theme="dark"] .news-link-class:hover { color: #dbe5ee; border-bottom-color: #dbe5ee; }

#view-news[data-news-theme="dark"] .news-link-future {
  color: var(--navy-future);
  border-bottom-color: #7e64ab;
}

#view-news[data-news-theme="dark"] .news-link-future:hover { color: #e6d8ff; border-bottom-color: #e6d8ff; }

/* ---- Category chips ---- */

/* ---- Provenance and movement flags ---- */

/* ---- Collection health ---- */

/* ---- Methodology disclosures ---- */

#view-news[data-news-theme="dark"] .news-methodology { color: var(--navy-muted); }

#view-news[data-news-theme="dark"] .news-methodology p,
#view-news[data-news-theme="dark"] .news-live-methodology p {
  background: var(--navy-surface-raised);
  color: var(--navy-text);
  border-color: var(--navy-rule-subtle);
}

#view-news[data-news-theme="dark"] .fleet-empty { color: var(--navy-muted); }

/* ---- Future Fleet view ---- */

#view-future { background: #eef1ec; min-height: 70vh; padding-bottom: 3rem; }

#future-controls,
#news-controls {
  display: flex;
  align-items: flex-end;
  gap: 1.25rem;
  flex-wrap: wrap;
  padding: 0.9rem 1.5rem;
  background: #fff;
  border-bottom: 1px solid #dfe3e6;
}

#future-summary,
#news-summary {
  margin: 0;
  font-size: 0.82rem;
  color: #4a545e;
  flex: 1 1 22rem;
}

#future-grid { padding: 1.25rem 1.5rem 0; }

.future-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(250px, 100%), 1fr));
  gap: 0.7rem;
  margin-top: 1rem;
}

.future-card {
  appearance: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
  background: #fbfcfa;
  border: 1px solid #b6c2b1;
  border-radius: 2px;
  padding: 0.65rem 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.future-card:hover { border-color: #2f3a33; background: #fff; }

.future-card-head { display: flex; flex-direction: column; gap: 0.1rem; }

.stage-badge {
  align-self: flex-start;
  font-size: 0.66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 0.14rem 0.45rem;
  border-radius: 2px;
  white-space: nowrap;
}

/* Warmer as the ship gets closer to joining the fleet. */
.stage-delivery-pending   { background: #d8efdc; color: #1e6b33; border: 1px solid #a9d7b5; }
.stage-sea-trials         { background: #dceef4; color: #1a5c72; border: 1px solid #a6cfdd; }
.stage-fitting-out        { background: #fdf0d5; color: #8a6212; border: 1px solid #e6cf9a; }
.stage-under-construction { background: #eef1f4; color: #4a545e; border: 1px solid #ccd3da; }
.stage-design             { background: #f2eef6; color: #5b4a75; border: 1px dashed #c3b3d8; }

.future-dates {
  margin: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.1rem 0.5rem;
  font-size: 0.74rem;
}

.future-dates dt { color: #7a857e; }
.future-dates dd { margin: 0; color: #23303d; font-weight: 500; }

.future-builder {
  font-size: 0.72rem;
  color: #6b7480;
  font-style: italic;
}

.progress-wrap { display: flex; align-items: center; gap: 0.5rem; cursor: help; }

.progress-track {
  flex: 1;
  height: 7px;
  background: #dfe4e8;
  border-radius: 4px;
  overflow: hidden;
}

.progress-track-lg { height: 12px; margin: 0.5rem 0 0.6rem; }

.progress-fill {
  height: 100%;
  /* Hatched rather than solid — a reminder the figure is inferred, not reported. */
  background: repeating-linear-gradient(45deg, #5a7d63, #5a7d63 4px, #6e9377 4px, #6e9377 8px);
}

.progress-label { font-size: 0.72rem; font-weight: 700; color: #2f3a33; white-space: nowrap; }
.progress-est { font-weight: 400; font-style: italic; color: #7a857e; }

.progress-unknown {
  font-size: 0.72rem;
  font-style: italic;
  color: #8a9490;
  border-top: 1px dashed #c9d2c6;
  padding-top: 0.35rem;
  cursor: help;
}

.progress-detail {
  margin: 1rem 0;
  padding: 0.7rem 0.85rem;
  background: #f4f6f4;
  border: 1px solid #dbe2d9;
}

.progress-detail h4 { margin: 0 0 0.2rem; font-size: 0.82rem; color: #2f3a33; }
.progress-detail p { margin: 0; font-size: 0.78rem; line-height: 1.5; color: #4a545e; }

/* ---- Shared dark workspace theme: Future Fleet ----
   (Formerly shared with the Fleet grid too, before it was deprecated in
   favour of the Fleet Infographic — which has its own, separate dark theme.
   The base-sidebar clauses below remain: confidence/identity badges also
   render in the Map's light-surface base sidebar.) */

#view-future {
  background: var(--navy-canvas);
  color: var(--navy-text);
}

#future-controls {
  background: var(--navy-toolbar);
  border-bottom-color: var(--navy-rule-subtle);
}

#view-future .fleet-control label { color: var(--navy-muted); }

#view-future .fleet-control select,
#view-future .fleet-control input {
  border-color: var(--navy-rule);
  background: var(--navy-surface);
  color: var(--navy-text);
  color-scheme: dark;
}

#view-future .fleet-control input::placeholder { color: var(--navy-dim); }

#view-future .fleet-control select:focus-visible,
#view-future .fleet-control input:focus-visible {
  outline: 2px solid var(--navy-focus);
  outline-offset: 2px;
}

#future-summary { color: var(--navy-muted); }

#future-summary strong { color: var(--navy-future); }

#view-future .fleet-empty { color: var(--navy-muted); }

#view-future .fleet-group {
  background: var(--navy-reference);
  border-color: var(--navy-rule-subtle);
}

#view-future .fleet-group-header { border-top-color: var(--navy-rule); }

#view-future .fleet-group-titles h2 { color: var(--navy-text-strong); }

#view-future .fleet-designation,
#view-future .fleet-group-counts { color: var(--navy-muted); }

#view-future .fleet-type-chip {
  background: var(--navy-surface);
  color: var(--navy-text-strong);
  border: 1px solid var(--navy-rule);
}

#view-future .fleet-art img {
  border-color: var(--navy-rule);
  background: var(--navy-surface);
}

#view-future .fleet-art-fallback {
  border-color: var(--navy-rule);
  background: var(--navy-surface);
  color: var(--navy-muted);
}

#view-future .fleet-length { color: var(--navy-metric); }

#view-future .fleet-length-missing,
#view-future .fleet-unverified { color: var(--navy-dim); }

#view-future .fleet-spec-note { color: var(--navy-muted); }

#view-future .future-card {
  background: var(--navy-surface-raised);
  border-color: var(--navy-rule);
  color: var(--navy-text);
}

#view-future .future-card:hover {
  border-color: var(--navy-focus);
  background: var(--navy-surface);
}

#view-future .future-card:focus-visible {
  outline-color: var(--navy-focus);
}

#view-future .hull-number { color: var(--navy-metric); }

#view-future .hull-name { color: var(--navy-text-strong); }

#view-future .hull-meta { color: var(--navy-muted); }

#view-future .identity-badge,
#base-sidebar .identity-badge {
  color: #e5a9cf;
  background: #3a2935;
  border-color: #9b6287;
}

#future-grid.is-stage-layout {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(430px, 100%), 1fr));
  gap: 1.25rem;
  align-items: start;
}

#future-grid.is-stage-layout .fleet-group { margin-bottom: 0; }

#view-future .stage-delivery-pending   { background: #203b2a; color: #8fd6a1; border-color: #3d6b49; }
#view-future .stage-sea-trials         { background: #203947; color: #8dd7ed; border-color: #3d7184; }
#view-future .stage-fitting-out        { background: #42371f; color: #e5c56f; border-color: #766233; }
#view-future .stage-under-construction { background: #303944; color: #bec9d2; border-color: #536170; }
#view-future .stage-design             { background: #382d4d; color: var(--navy-future); border-color: #7f68a4; }

#view-future .future-dates dt,
#view-future .future-builder { color: var(--navy-muted); }

#view-future .future-dates dd { color: var(--navy-text-strong); }

#view-future .progress-track { background: var(--navy-rule-subtle); }

#view-future .progress-fill {
  background: repeating-linear-gradient(45deg, #5089a4, #5089a4 4px, #65abc7 4px, #65abc7 8px);
}

#view-future .progress-label { color: var(--navy-text-strong); }
#view-future .progress-est,
#view-future .progress-unknown { color: var(--navy-muted); }

#view-future .progress-unknown { border-top-color: var(--navy-rule); }

/* ---- News & Deployments view ---- */

#view-news { background: #f6f7f8; min-height: 70vh; padding-bottom: 3rem; }

#news-table-wrap { padding: 1rem 1.5rem 0; overflow-x: auto; }

.news-methodology {
  margin: 0.7rem 0 0;
  color: #68737d;
  font-size: 0.75rem;
}
.news-methodology summary { cursor: pointer; font-weight: 600; }
.news-methodology p {
  max-width: 64rem;
  margin: 0.45rem 0 0;
  padding: 0.55rem 0.7rem;
  border-left: 3px solid #c8cdd2;
  background: #fff;
  line-height: 1.5;
}
.news-live-methodology { margin: -0.35rem 0 0.8rem; }
.news-live-methodology p { background: #f5f7f8; }

.news-table {
  width: 100%;
  border-collapse: collapse;
  background: #fff;
  font-size: 0.85rem;
  min-width: 640px;
}

.news-table th {
  text-align: left;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #6b7480;
  padding: 0.5rem 0.7rem;
  border-bottom: 2px solid #23303d;
  white-space: nowrap;
}

.news-table td {
  padding: 0.7rem;
  border-bottom: 1px solid #e6e9ec;
  vertical-align: top;
}

.news-table tr:hover td { background: #fafbfc; }

.news-date { white-space: nowrap; font-weight: 600; color: #23303d; width: 1%; }

.news-cat {
  display: inline-block;
  font-size: 0.66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.14rem 0.45rem;
  border-radius: 2px;
  white-space: nowrap;
}

.news-body { max-width: 46rem; }
.news-headline { display: block; font-weight: 600; color: #23303d; margin-bottom: 0.25rem; }
.news-summary { display: block; color: #4a545e; line-height: 1.5; font-size: 0.82rem; }

.news-related {
  display: block;
  margin-top: 0.35rem;
  font-size: 0.74rem;
  color: #6b7480;
  font-style: italic;
}

/* Two tiers of "not independent reporting" share one shape and differ in
   colour: sand for a state-media assertion, steel for a navy's own release.
   .state-media-flag is kept as an alias so no existing markup goes unstyled. */
.source-role-flag,
.state-media-flag {
  display: inline-block;
  font-size: 0.62rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.06rem 0.35rem;
  border-radius: 2px;
  background: #fdf3e0;
  color: #8a6212;
  border: 1px dashed #dcbf88;
  cursor: help;
  vertical-align: middle;
}

.source-role-official-release {
  background: #eaeff4;
  color: #3f5164;
  border-color: #a9b8c7;
}

.news-sources { font-size: 0.76rem; line-height: 1.6; white-space: nowrap; }
.news-sources a { color: #2563a8; }

.news-status-wrap { flex: 1 1 22rem; display: flex; flex-direction: column; gap: 0.25rem; }
.news-status-wrap #news-summary { flex: none; }
.news-auto-note {
  margin: 0;
  font-size: 0.76rem;
  color: #6b7480;
  line-height: 1.45;
}

.news-new-flag {
  display: inline-block;
  font-size: 0.62rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.06rem 0.35rem;
  margin-right: 0.3rem;
  border-radius: 2px;
  background: #e2f1e6;
  color: #1e6b33;
  border: 1px solid #a9d3b6;
  vertical-align: middle;
}

.news-row-new td { background: #f3faf5; }
.news-table tr.news-row-new:hover td { background: #eaf6ee; }
.news-row-new td:first-child { box-shadow: inset 3px 0 0 #4c9c68; }

/* Inline ship/class references. Styled as text links rather than chips: they sit
   mid-sentence, and a row of buttons would break the prose. */

.news-link {
  font: inherit;
  padding: 0;
  border: 0;
  background: none;
  color: #2563a8;
  cursor: pointer;
  border-bottom: 1px dotted #8fb0d4;
  text-align: left;
}
.news-link:hover { color: #17416f; border-bottom-style: solid; }
.news-link-class { color: #5b3a9e; border-bottom-color: #b2a0d8; }
.news-link-class:hover { color: #3d2570; }
.news-link-future { color: #8a6212; border-bottom-color: #dcbf88; }
.news-link-future:hover { color: #5e4209; }

/* Automatically collected reporting remains visually separate from reviewed
   analysis: muted ground, no category coding, publisher headlines outward. */

.news-live-band {
  margin: 0 0 1rem;
  padding-bottom: 1.35rem;
  border-bottom: 3px double #c3cbd3;
}

.news-reviewed-band { margin: 1.35rem 0 1rem; }
.news-section-head h3 {
  margin: 0 0 0.3rem;
  font-size: 0.9rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #4a545e;
}
.news-section-head p {
  margin: 0 0 0.8rem;
  max-width: 62rem;
  color: #6b7480;
  font-size: 0.78rem;
  line-height: 1.55;
}

.news-live-head h3 {
  margin: 0 0 0.3rem;
  font-size: 0.9rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #6b7480;
}

.news-live-head p {
  margin: 0 0 0.8rem;
  max-width: 62rem;
  font-size: 0.78rem;
  line-height: 1.55;
  color: #6b7480;
}

.news-live-head .news-live-health {
  display: inline-block;
  margin: 0 0 0.8rem;
  padding: 0.28rem 0.5rem;
  border-left: 3px solid #5d9270;
  background: #edf6f0;
  color: #365d43;
}
.news-live-head .news-live-health.is-delayed {
  border-left-color: #c6902d;
  background: #fff7e8;
  color: #7a5718;
}
.news-live-head .news-live-health.is-stale,
.news-live-head .news-live-health.is-missing {
  border-left-color: #b84c40;
  background: #fdf0ee;
  color: #87362d;
}

.news-live-head code,
.news-live-empty code {
  font-size: 0.74rem;
  background: #eef1f4;
  padding: 0.05rem 0.28rem;
  border-radius: 2px;
}

/* The automatic band's two subject sections. Deliberately quieter than the
   band's own h3: they divide one tier by subject, and must not read as a third
   provenance tier alongside "automatically collected" and "reviewed". */
.news-subsection { margin: 0.9rem 0 0; }

.news-subsection-head h4 {
  margin: 0 0 0.15rem;
  font-size: 0.82rem;
  font-weight: 700;
  color: #2c3a47;
  letter-spacing: 0.02em;
}

.news-subsection-head p {
  margin: 0 0 0.5rem;
  font-size: 0.76rem;
  color: #6b7480;
  line-height: 1.45;
  max-width: 52rem;
}

.news-movements .news-subsection-head h4::before {
  content: "";
  display: inline-block;
  width: 0.5rem;
  height: 0.5rem;
  margin-right: 0.4rem;
  border-radius: 50%;
  background: #2f7d5c;
  vertical-align: baseline;
}

/* Why the collector read a headline as a movement, in the words that did it.
   A machine judgement that cannot show its working is not one a reader can
   weigh -- the same standard the entity links are held to. */
.news-movement-why {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.72rem;
  color: #6b7480;
  line-height: 1.6;
}

.news-movement-why strong { font-weight: 600; color: #55606c; }

.news-movement-term {
  display: inline-block;
  margin-left: 0.3rem;
  padding: 0.02rem 0.32rem;
  border-radius: 2px;
  background: #e8f1ec;
  color: #2f6c52;
  border: 1px solid #c4dbcf;
  font-size: 0.7rem;
}

.news-live-table { background: #fbfcfd; }
.news-live-table th { color: #8a939c; }
.news-live-pub { font-size: 0.76rem; color: #4a545e; white-space: nowrap; }

.news-live-headline {
  font-weight: 600;
  color: #23303d;
  text-decoration: none;
  border-bottom: 1px solid #ccd4dc;
}
.news-live-headline:hover { color: #2563a8; border-bottom-color: #2563a8; }

.news-live-chips { display: block; margin-top: 0.3rem; }
.news-live-chips .news-link { margin-right: 0.55rem; font-size: 0.78rem; }
.news-live-also { display: block; margin-top: 0.35rem; color: #6b7480; font-size: 0.74rem; line-height: 1.45; }
.news-live-also a { color: #2563a8; }

/* The machine-drafted tier. Deliberately marked as provisional rather than
   styled to match the reviewed table: the whole design depends on a reader
   never mistaking one for the other, so the difference has to be visible
   before anyone reads a word. */
.news-drafted-band {
  margin: 1.35rem 0 1rem;
  padding: 0.9rem 0 0.5rem;
  border-top: 1px solid #dfe3e6;
}

.news-drafted-head h3 {
  margin: 0 0 0.2rem;
  font-size: 0.9rem;
  font-weight: 700;
  color: #6a4b8a;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

.news-drafted-head > p {
  margin: 0 0 0.55rem;
  font-size: 0.79rem;
  color: #5c6570;
  line-height: 1.5;
  max-width: 56rem;
}

.news-drafted-table {
  background: #fcfbfe;
  border-left: 3px solid #c9b6e4;
}

.news-drafted-significance {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.78rem;
  color: #4a545e;
  line-height: 1.5;
}

.news-drafted-significance strong { color: #5b3a9e; font-weight: 600; }

.news-drafted-mark {
  display: block;
  margin-top: 0.4rem;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #6a4b8a;
  cursor: help;
}

.news-movement-flag {
  display: inline-block;
  font-size: 0.62rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.06rem 0.35rem;
  border-radius: 2px;
  background: #e8f1ec;
  color: #2f6c52;
  border: 1px solid #c4dbcf;
  cursor: help;
  vertical-align: middle;
}

.news-live-empty { font-size: 0.82rem; color: #6b7480; padding: 0.4rem 0 1rem; }

.news-live-foot {
  margin: 0.5rem 0 0;
  font-size: 0.74rem;
  color: #8a939c;
}
.news-live-failed { color: #96382c; cursor: help; border-bottom: 1px dotted #d3a49c; }

/* Class modal: the hull list that makes a class page a way in to its ships. */

.class-hull-list { margin-top: 0.9rem; }
.class-hull-list h4 {
  margin: 0 0 0.45rem;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #6b7480;
}
.class-hull-chips { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.class-hull-chip {
  font: inherit;
  font-size: 0.78rem;
  padding: 0.22rem 0.5rem;
  border: 1px solid #d5dbe1;
  border-radius: 3px;
  background: #f7f9fb;
  color: #23303d;
  cursor: pointer;
}
.class-hull-chip:hover { background: #eaf0f6; border-color: #9aa7b3; }
.class-hull-chip .hull-number { font-weight: 700; color: #2563a8; }
.class-hull-empty { font-size: 0.82rem; color: #6b7480; }

/* ---- Fleet Infographic view ----
   An order-of-battle poster: silhouettes tiled under class headings, all drawn
   to one shared scale. Dark ground so the profiles read as the ONI sheet does. */

#view-infographic {
  background: var(--navy-canvas);
  color: var(--navy-text);
  min-height: 70vh;
  padding-bottom: 3rem;
}

#info-controls {
  display: flex;
  align-items: flex-end;
  gap: 1.25rem;
  flex-wrap: wrap;
  padding: 0.9rem 1.5rem;
  background: var(--navy-toolbar);
  border-bottom: 1px solid #2f3942;
}

#info-controls label { color: #9aa6b1; }

#info-controls select,
#info-controls input[type="search"] {
  font: inherit;
  font-size: 0.85rem;
  padding: 0.32rem 0.5rem;
  border: 1px solid #3c4752;
  border-radius: 3px;
  background: var(--navy-surface);
  color: var(--navy-text);
  min-width: 12rem;
}

#info-controls input[type="search"]::placeholder { color: #84919d; }

.info-future-toggle {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  min-height: 2rem;
  color: #c8d0d7 !important;
  font-size: 0.82rem !important;
  cursor: pointer;
  text-transform: none !important;
  letter-spacing: 0 !important;
}

.info-future-toggle input { accent-color: var(--navy-focus); }

#info-reference-bar {
  display: flex;
  align-items: flex-start;
  gap: 1.4rem;
  min-height: 3.4rem;
  padding: 0.65rem 1.5rem;
  box-sizing: border-box;
  border-bottom: 1px solid var(--navy-rule-subtle);
  background: var(--navy-reference);
}

.info-reference-label {
  display: block;
  margin-bottom: 0.3rem;
  color: #84919d;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.info-ruler {
  display: flex;
  align-items: flex-end;
  gap: 0.4rem;
  color: #aeb8c1;
  font: 0.64rem var(--font-mono);
}

.info-ruler-track {
  position: relative;
  display: block;
  min-width: 2px;
  height: 0.65rem;
  border: solid var(--navy-text);
  border-width: 0 1px 1px;
}

.info-ruler-track i {
  position: absolute;
  bottom: 0;
  left: 50%;
  height: 0.35rem;
  border-left: 1px solid var(--navy-text);
}

#info-colour-legend { flex: 1; min-width: 0; }
.info-colour-items { display: flex; flex-wrap: wrap; gap: 0.3rem 0.8rem; }
.info-colour-items span { display: inline-flex; align-items: center; gap: 0.3rem; color: #b4bec6; font-size: 0.66rem; }
.info-colour-items i { width: 0.65rem; height: 0.65rem; border-radius: 50%; }

#info-class-index { position: relative; margin-left: auto; flex: 0 0 auto; }
#info-class-index summary {
  padding: 0.3rem 0.55rem;
  border: 1px solid var(--navy-rule);
  border-radius: 3px;
  color: #c9d1d8;
  font-size: 0.72rem;
  cursor: pointer;
}
#info-class-index-list {
  position: absolute;
  z-index: 100;
  top: calc(100% + 0.45rem);
  right: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(9rem, 1fr));
  gap: 0.22rem;
  width: 24rem;
  max-height: 25rem;
  overflow: auto;
  padding: 0.55rem;
  border: 1px solid var(--navy-rule);
  border-radius: 3px;
  background: #161c21;
  box-shadow: 0 5px 18px rgba(0, 0, 0, 0.42);
}
.info-index-link {
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.3rem 0.4rem;
  border: 0;
  border-radius: 2px;
  background: transparent;
  color: #d9e0e5;
  font: inherit;
  font-size: 0.7rem;
  text-align: left;
  cursor: pointer;
}
.info-index-link:hover,
.info-index-link:focus-visible { background: #2d3841; outline: none; }
.info-index-link span { color: #82909c; }

#info-summary {
  margin: 0;
  font-size: 0.82rem;
  color: #9aa6b1;
  flex: 1 1 22rem;
}

#info-summary strong { color: var(--navy-text-strong); }


/* Column flow, not a grid. CSS Grid aligns rows, so every row was as tall as
   its tallest group: Yushen with four hulls sat beside Renhai with ten and
   left roughly 700px of void beneath it. About 40% of the page was empty.

   Multi-column fills each column before starting the next, so a short class
   is followed immediately by the next one. .info-group already carried
   break-inside: avoid -- the groundwork was here, the container was not.

   Splitting a group across a column boundary is the failure mode to watch.
   It does not arise in practice: the columns balance to roughly a third of a
   very tall page, so even Luyang III at 35 hulls is far shorter than one
   column and break-inside keeps it whole. */
#info-grid {
  padding: 1.25rem 1.5rem 0;
  columns: 440px;
  column-gap: 2.2rem;
}

.info-group {
  min-width: 0;
  break-inside: avoid;
  /* The gap the grid used to supply. */
  margin-bottom: 1.6rem;
}
.info-group:focus,
.info-class-block:focus { outline: none; }
.info-group.is-index-target,
.info-class-block.is-index-target { box-shadow: 0 0 0 2px var(--navy-focus); }

.info-group-header {
  border-bottom: 1px solid var(--navy-rule);
  padding-bottom: 0.25rem;
  margin-bottom: 0.6rem;
}

.info-group-header h3 {
  margin: 0;
  font-size: 0.88rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--navy-text-strong);
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
}

.info-count {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--navy-canvas);
  background: var(--navy-count);
  border-radius: 8px;
  padding: 0.02rem 0.4rem;
}

.info-group-header p {
  margin: 0.1rem 0 0;
  font-size: 0.72rem;
  color: var(--navy-muted);
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.info-nested-classes {
  display: grid;
  gap: 1.15rem;
}

.info-class-block {
  min-width: 0;
  padding-left: 0.65rem;
  border-left: 2px solid #35414b;
  border-radius: 2px;
  transition: box-shadow 0.2s ease;
}

.info-class-subheader {
  margin-bottom: 0.45rem;
}

.info-class-subheader h4 {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  margin: 0;
  color: #dce3e8;
  font-size: 0.76rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.info-class-subheader p {
  margin: 0.08rem 0 0;
  color: var(--navy-dim);
  font-size: 0.65rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.info-length,
.info-displacement { color: var(--navy-metric); margin-left: 0.4rem; }

.silhouette-row {
  display: flex;
  min-width: 0;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.55rem 0.7rem;
}

.silhouette-unit {
  appearance: none;
  background: none;
  border: none;
  padding: 0.15rem 0.1rem 0;
  margin: 0;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.1rem;
  border-radius: 2px;
}

.silhouette-unit svg { display: block; overflow: visible; }

/* Painted on the <g>, not on `.sil-shape path`. Traced profiles are drawn with
   <use> pointing at a single shared <path>, and a descendant selector cannot
   reach inside a <use> shadow tree -- only inherited properties cross it. fill,
   stroke, stroke-width and fill-rule all inherit, so styling the group works
   for both the <use> profiles and the inline family shapes. */
.sil-shape {
  fill: var(--sil-colour, #e8edf1);
  stroke: none;
  transition: fill 0.12s ease;
}

/* Hollow = still an aggregate slot, no pennant number in open sources. */
.silhouette-unit.is-ghost .sil-shape {
  fill: none;
  stroke: var(--sil-colour, #6d7c88);
  stroke-width: 9;
  stroke-linejoin: round;
}

.silhouette-unit.is-future .sil-shape {
  fill: var(--sil-colour, #e8edf1);
  fill-opacity: 0.45;
  stroke: var(--sil-colour, #e8edf1);
  stroke-width: 5;
  stroke-dasharray: 14 9;
  stroke-linejoin: round;
}

.silhouette-unit.is-dimmed {
  opacity: 0.12;
  filter: saturate(0.25);
}

.silhouette-unit.is-search-match {
  background: #293844;
  box-shadow: 0 0 0 1px var(--navy-focus);
}

.silhouette-unit:hover { background: #2d363f; }
.silhouette-unit:hover .sil-shape { fill: var(--navy-focus); }
.silhouette-unit.is-ghost:hover .sil-shape { fill: none; stroke: var(--navy-focus); }

/* The shared <defs> sheet: present in the DOM, never painted. */
.sil-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

.info-artwork {
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 0.1rem 0.4rem;
  border-radius: 3px;
  margin-left: 0.45rem;
  vertical-align: 0.1em;
  cursor: help;
}
.info-artwork.is-schematic { color: #c8b17e; border: 1px dashed #5c4f34; }

.silhouette-label {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  line-height: 1;
  color: #9aa6b1;
  letter-spacing: 0.02em;
}

.silhouette-unit:hover .silhouette-label { color: var(--navy-focus); }

/* The ship's name under its pennant. Constrained to the tile so a long name can
   never widen it -- tile width encodes hull length, and letting text stretch it
   would corrupt the shared scale. Anything too long is clipped with an ellipsis
   and remains readable in full via the tooltip. */
.silhouette-name {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.58rem;
  line-height: 1.1;
  color: #7f8b97;
  letter-spacing: 0.01em;
}

.silhouette-unit:hover .silhouette-name { color: var(--navy-focus); }

.silhouette-state,
.info-future-count {
  color: #d2b7ff;
  font-size: 0.58rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.info-future-count { margin-left: 0.45rem; }

/* ==========================================================================
   Responsive layout, touch targets and focus
   --------------------------------------------------------------------------
   Added in the Phase A interface pass. Until then the whole stylesheet had no
   @media rule except prefers-reduced-motion, so every view scrolled sideways
   on a phone and the fourth tab could not be reached at all.

   Three breakpoints, and only these three. Adding a fourth for one stubborn
   element is how a stylesheet stops being predictable.

     1100px  the toolbars stop fitting on one line
      780px  tablet/phone: the shell stacks, the sidebar becomes a sheet
      560px  small phone: tabs scroll, padding tightens

   Everything here is additive. No rule above this block was width-scoped
   before, so nothing here can change how the desktop layout renders.
   ========================================================================== */

/* ---- Focus, at every width -----------------------------------------------
   There were 14 scattered :focus-visible rules and most controls had none, so
   keyboard users fell back to the browser default ring -- effectively
   invisible on the dark workspace. One rule, applied to everything that takes
   focus, in the palette's own accent. */

:where(a, button, select, input, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--navy-focus);
  outline-offset: 2px;
  border-radius: 2px;
}

/* The light chrome needs a darker ring than the dark workspace does; the cyan
   accent is legible on the navy canvas but washes out on white. */
:where(header, #view-tabs, #stats-bar, #ship-modal, #system-modal,
       #aircraft-modal, #coverage-modal, #global-search-modal)
  :where(a, button, select, input, summary):focus-visible {
  outline-color: #1f6fb8;
}

/* ---- Touch targets -------------------------------------------------------
   44px minimum, applied only where a pointer is coarse OR the viewport is
   narrow. Keeping it off the desktop layout matters: 44px rows would make the
   class index four times its current height on a screen using a mouse.

   .silhouette-unit is deliberately exempt. Its height is proportional to the
   hull's real length on the shared scale printed above the grid -- a 42m
   missile boat MUST draw shorter than a 316m carrier or the tab loses its
   entire premise. That is the WCAG 2.5.8 "essential presentation" exception.
   The accessible route to every class is the class index and the search
   field, both of which do meet the floor. */

@media (max-width: 780px), (pointer: coarse) {
  .view-tab,
  .info-index-link,
  .close-btn,
  .link-btn,
  #open-about,
  #open-global-search,
  #stats-bar .coverage-link,
  .confidence-legend summary,
  #info-class-index > summary,
  .fleet-control select,
  .fleet-control input,
  #info-controls select,
  #info-controls input[type="search"],
  .navy-selector select,
  .map-focus-controls button {
    min-height: 44px;
  }

  .close-btn {
    display: grid;
    place-items: center;
    min-width: 44px;
    top: 0.25rem;
    right: 0.35rem;
  }

  /* The bare 13x13px OS checkbox was both the smallest target in the app and
     the most obviously unstyled thing in it. A 20px mark keeps the toolbar
     proportionate; the label carries the 44px hit area around it. */
  .info-future-toggle {
    min-height: 44px;
    cursor: pointer;
  }
  .info-future-toggle input[type="checkbox"] {
    width: 20px;
    height: 20px;
    accent-color: var(--navy-focus);
  }
}

/* ---- 1100px: toolbars stop fitting -------------------------------------- */

@media (max-width: 1100px) {
  /* The summary sentence competes with the controls for the same line and
     always loses badly. Give it its own row instead of a wrapped fragment. */
  #info-summary,
  #future-summary,
  .news-status-wrap {
    flex-basis: 100%;
    margin-top: 0.15rem;
  }
}

/* ---- 780px: the shell stacks -------------------------------------------- */

@media (max-width: 780px) {
  header {
    flex-direction: column;
    align-items: stretch;
    gap: 0.75rem;
    padding: 0.75rem 1rem;
  }

  header h1 { font-size: 1.15rem; }

  .subtitle {
    font-size: 0.8rem;
    /* Two lines of standfirst is context; four is an obstacle between the
       reader and the tabs. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  /* #open-global-search was the element that pushed every view 29px past the
     right edge, on all four tabs. */
  .header-actions {
    flex-wrap: wrap;
    gap: 0.5rem;
  }
  /* Selector on its own row, both buttons sharing the next one. Letting the
     buttons size to their content instead pushed each onto a row of its own
     and cost about 90px of a phone screen before any content appeared. */
  .navy-selector { flex: 1 1 100%; }
  .navy-selector select { min-width: 0; width: 100%; }
  #open-about,
  #open-global-search {
    flex: 1 1 8rem;
    min-width: 0;
    text-align: center;
  }

  #stats-bar {
    padding: 0.5rem 1rem;
    gap: 0.4rem 0.75rem;
  }
  #stats-bar .coverage-link { margin-left: 0; }

  /* The control rows were the other half of the overflow: two 12rem minimums
     plus gaps and padding cannot fit in 390px. Let them share the row. */
  #info-controls,
  #future-controls,
  #news-controls {
    gap: 0.75rem 1rem;
    padding: 0.85rem 1rem;
  }
  .fleet-control { flex: 1 1 13rem; min-width: 0; }
  .fleet-control select,
  .fleet-control input,
  #info-controls select,
  #info-controls input[type="search"] {
    min-width: 0;
    width: 100%;
  }

  #info-grid,
  #future-grid,
  #info-reference-bar,
  #news-table-wrap { padding-left: 1rem; padding-right: 1rem; }

  /* A 360px drawer sliding in from the right is a desktop pattern. On a phone
     the map is the context you are reading the base against, so the panel
     belongs at the bottom where it covers least of it. */
  #base-sidebar {
    top: auto;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    max-width: none;
    height: auto;
    max-height: 72vh;
    border-top: 1px solid var(--navy-rule);
    border-radius: 10px 10px 0 0;
    box-shadow: 0 -4px 18px rgba(0, 0, 0, 0.45);
    padding-top: 1.5rem;
  }
  #base-sidebar.hidden { transform: translateY(100%); }

  /* The grab handle that tells you it is a sheet and not a stuck panel. */
  #base-sidebar::before {
    content: "";
    position: absolute;
    top: 0.55rem;
    left: 50%;
    transform: translateX(-50%);
    width: 2.25rem;
    height: 0.25rem;
    border-radius: 999px;
    background: var(--navy-rule);
  }

  /* Short enough that the map and the filter bar docked at its foot both sit
     within a typical phone viewport once the chrome above is accounted for.
     The map view still scrolls; this only decides what you see first. */
  #map { height: 60vh; min-height: 20rem; }

  /* Both map panels are absolutely positioned into corners sized for a
     desktop viewport. At this width they cover the map they filter. */
  #type-filter {
    top: auto;
    bottom: 12px;
    right: 12px;
    left: 12px;
    width: auto;
    max-height: 42vh;
    overflow-y: auto;
  }
  /* Leaflet's zoom control owns the top-left corner and is about 64px tall.
     Sharing that corner put these buttons on top of the minus button. */
  .map-focus-controls { left: auto; right: 12px; top: 12px; }
}

/* ---- 560px: small phone ------------------------------------------------- */

@media (max-width: 560px) {
  /* "News & Deployments" sat past the right edge with no way to scroll to it,
     so a quarter of the application was unreachable by tap. */
  #view-tabs {
    overflow-x: auto;
    scrollbar-width: none;
    scroll-snap-type: x proximity;
    padding: 0 1rem;
    gap: 0;
  }
  #view-tabs::-webkit-scrollbar { display: none; }
  .view-tab {
    flex: 0 0 auto;
    scroll-snap-align: start;
    padding: 0.55rem 0.85rem;
    font-size: 0.85rem;
  }

  /* Silhouette captions sat at 10.56px and 9.28px -- readable on a 27in
     monitor at arm's length, not on a phone. */
  .silhouette-label { font-size: 0.72rem; }   /* was 10.56px */
  .silhouette-name  { font-size: 0.7rem; }    /* was 9.28px  */
  .info-artwork     { font-size: 0.7rem; }    /* was 9.92px  */
  /* The reference bar's own labels and the ruler's endpoint ticks. */
  #info-reference-bar,
  #info-reference-bar * { font-size: 0.7rem; }

  /* #ship-modal is not here: it is a flex column whose padding lives on
     #modal-content, so padding on the container would break the layout. */
  #system-modal,
  #aircraft-modal,
  #coverage-modal { padding: 1.15rem 1rem; }

  #global-search-backdrop { padding-top: 4vh; }

  /* On a phone the chrome above the map was taking 405px -- nearly half the
     screen -- before a single base appeared. The navy selector immediately
     below states the same thing the standfirst does, and the About panel
     carries the full scope note. */
  .subtitle { display: none; }
}


/* ---- Numerals ------------------------------------------------------------
   Anywhere digits stack into a column they must share a width, or the column
   shimmers as values change. Plex Mono is fixed-pitch already; this is for
   the sans face carrying counts, pennants, tonnages and dates in aligned
   rows. Deliberately not applied to running prose, where proportional
   figures set better. */

#stats-bar,
.info-count,
.info-length,
.info-displacement,
.silhouette-label,
.filter-count,
.news-date,
.modal-facts dd,
.class-spec-table td,
.future-card time,
.progress-label,
time {
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   One dark workspace
   --------------------------------------------------------------------------
   Phase B. The application was a light theme with dark patches bolted on:
   .control-panel was declared background:#fff and then overridden dark by
   #view-map, News re-themed itself through [data-news-theme] overrides, and
   the header, tab bar and stats bar stayed light above dark workspaces on all
   four tabs. The ship modal -- the tool's primary detail surface -- was a
   white island floating over a dark canvas.

   All four view panels were already dark. This repaints what was left: the
   shell, the loading and About surfaces, and the five modals.

   News keeps its light option. That is a reader's choice on one view, made
   deliberately, which is a different thing from an unresolved default.
   ========================================================================== */

:root {
  --shell:        #1a2026;   /* header, tabs -- the frame around the work   */
  --shell-rule:   #2f3942;
  --reference:    #1d242a;   /* stats bar -- reference strip, not a surface */
  --panel:        #232b33;   /* modals and popovers, raised off the canvas  */
  --panel-rule:   #3a4550;
  --panel-inset:  #1c232a;   /* nested blocks inside a panel                */
  --link:         #8ad4f7;
  --link-strong:  #b7e6ff;
}

body {
  background: var(--navy-canvas);
  color: var(--navy-text);
}

/* ---- Shell: header and tabs --------------------------------------------- */

header {
  background: var(--shell);
  border-bottom: 1px solid var(--shell-rule);
}
header h1 { color: var(--navy-text-strong); }
.subtitle { color: var(--navy-muted); }

.navy-selector { color: var(--navy-dim); }
.navy-selector select,
#open-about,
#open-global-search {
  border-color: var(--panel-rule);
  background: var(--navy-surface);
  color: var(--navy-text);
}
#open-about:hover,
#open-about.is-active,
#open-global-search:hover {
  border-color: var(--navy-focus);
  background: var(--navy-surface-raised);
  color: var(--navy-focus);
}
#open-global-search kbd {
  border-color: var(--panel-rule);
  background: var(--panel-inset);
  color: var(--navy-muted);
}

#view-tabs {
  background: var(--shell);
  border-bottom-color: var(--shell-rule);
}
.view-tab { color: var(--navy-muted); }
.view-tab:hover { color: var(--navy-text-strong); }
.view-tab.is-active { color: var(--navy-text-strong); }

/* ---- Reference strip ----------------------------------------------------- */

#stats-bar {
  background: var(--reference);
  border-top-color: var(--shell-rule);
  border-bottom-color: var(--shell-rule);
  color: var(--navy-text);
}
/* Figures get the sand accent the class specification lines already use, so
   the two places the tool states a number look like the same tool. */
#stats-bar .stats-figure { color: var(--navy-metric); }
#stats-bar .stats-assumption-note { color: var(--navy-future); }
#stats-bar .coverage-link {
  border-color: var(--panel-rule);
  color: var(--navy-text);
}
#stats-bar .coverage-link:hover {
  border-color: var(--navy-focus);
  background: var(--navy-surface);
  color: var(--navy-focus);
}
.confidence-legend summary { color: var(--link); }
.confidence-legend-panel {
  border-color: var(--panel-rule);
  background: var(--panel);
  color: var(--navy-text);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.5);
}

/* ---- Loading and failure ------------------------------------------------- */

.app-status { background: var(--navy-canvas); }
.app-status-card {
  border-color: var(--panel-rule);
  background: var(--panel);
  box-shadow: 0 2px 14px rgba(0, 0, 0, 0.4);
}
.app-status-card h2 { color: var(--navy-text-strong); }
.app-status-card p { color: var(--navy-muted); }
.loading-spinner { border-color: var(--panel-rule); border-top-color: var(--navy-focus); }
.app-retry-btn {
  border-color: var(--navy-focus);
  background: transparent;
  color: var(--navy-focus);
}
.app-retry-btn:hover { background: var(--navy-surface); }
.image-fallback-message { background: var(--panel-inset); color: var(--navy-dim); }

@media (prefers-reduced-motion: reduce) {
  .loading-spinner { border-top-color: var(--panel-rule); }
}

/* ---- About --------------------------------------------------------------- */

#view-about { background: var(--navy-canvas); color: var(--navy-text); }
.about-article h2,
.about-article h3,
.about-article h4 { color: var(--navy-text-strong); }
.about-kicker { color: var(--navy-focus); }
.about-lede { color: var(--navy-text); }
.about-date-card,
.about-method-grid section {
  border-color: var(--panel-rule);
  background: var(--panel);
}
.about-date-card strong { color: var(--navy-text-strong); }
/* The caveat is the one block on this page that must not be skimmed past. */
.about-caveat {
  border-color: #7b6234;
  background: #2b2317;
  color: var(--navy-text);
}
.about-caveat strong { color: var(--navy-metric); }
.about-closing { border-top-color: var(--panel-rule); color: var(--navy-text-strong); }

/* ---- Modals -------------------------------------------------------------- */

#ship-modal,
#system-modal,
#aircraft-modal,
#coverage-modal,
#global-search-modal {
  background: var(--panel);
  color: var(--navy-text);
  border: 1px solid var(--panel-rule);
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.55);
}

:where(#ship-modal, #system-modal, #aircraft-modal, #coverage-modal, #global-search-modal)
  :where(h2, h3, h4, dt, strong, b) { color: var(--navy-text-strong); }

:where(#ship-modal, #system-modal, #aircraft-modal, #coverage-modal, #global-search-modal)
  a { color: var(--link); }
:where(#ship-modal, #system-modal, #aircraft-modal, #coverage-modal, #global-search-modal)
  a:hover { color: var(--link-strong); }

.modal-breadcrumb { color: var(--navy-dim); }
.modal-subtitle { color: var(--navy-muted); }
.modal-divider { border-color: var(--panel-rule); }

.close-btn { color: var(--navy-muted); }
.close-btn:hover { color: var(--navy-text-strong); }

#global-search-results { border-top-color: var(--panel-rule); }
.global-search-label { color: var(--navy-dim); }
.global-search-hint { color: var(--navy-dim); }
#global-search-input {
  border-color: var(--panel-rule);
  background: var(--navy-surface);
  color: var(--navy-text);
}
#global-search-input::placeholder { color: var(--navy-dim); }

/* The focus ring can go back to the palette accent now that nothing it lands
   on is white. The darker ring stays only for the News light option. */
:where(header, #view-tabs, #stats-bar, #ship-modal, #system-modal,
       #aircraft-modal, #coverage-modal, #global-search-modal)
  :where(a, button, select, input, summary):focus-visible {
  outline-color: var(--navy-focus);
}
#view-news[data-news-theme="light"] :where(a, button, select, input, summary):focus-visible {
  outline-color: #1f6fb8;
}

/* ---- Status chips: dark is now the default ------------------------------
   These already had a dark palette, but it was reachable only through
   "#view-future .confidence-high, #base-sidebar .confidence-high" -- a list
   that had to grow by hand every time a badge appeared on a new surface, and
   which the modals were never added to. Inverting it fixes the modals and
   stops the list growing. The light values survive where they are actually
   wanted: the News light option, which is a reader's deliberate choice. */

/* ---- Inside the modals ---------------------------------------------------
   The panels nested in the ship modal kept their own light backgrounds, so
   once the modal went dark the specification table and the caveat note were
   light-on-light and unreadable. */

.modal-image-wrap { background: var(--panel-inset); }
.modal-image-wrap.is-diagram { background: #e8ebee; }   /* line art needs a light ground */

.modal-facts dt { color: var(--navy-dim); }
.modal-facts dd { color: var(--navy-text); }
.modal-facts dd.fact-unpublished { color: var(--navy-dim); }

.modal-notes {
  background: var(--panel-inset);
  border-left-color: var(--navy-rule);
  color: var(--navy-text);
}

.class-technical-specs {
  border-color: var(--panel-rule);
  background: var(--panel-inset);
}
.class-technical-specs h5 { color: var(--navy-muted); }
.class-spec-grid dt { color: var(--navy-dim); }
.class-spec-grid dd { color: var(--navy-text); }
.class-overview .class-spec-note { color: var(--navy-dim); }
.class-overview .class-spec-sources { color: var(--navy-dim); }
.class-spec-sources a { color: var(--navy-muted); }

/* Sensor and weapon chips keep their two-family distinction, moved onto the
   dark ground rather than dropped. */
.system-chip {
  background: #1f2c3a;
  border-color: #34506b;
  color: #a8cdec;
}
.system-chip:hover { background: #26374a; }
.system-chip.weapon-chip {
  background: #332224;
  border-color: #6b3f42;
  color: #e6a8a8;
}
.system-chip.weapon-chip:hover { background: #402a2d; }

.global-search-result { color: var(--navy-text); }
.global-search-result:hover,
.global-search-result:focus-visible,
.global-search-result.is-active { background: var(--navy-surface); }
.global-search-kind { color: var(--navy-dim); }

/* ==========================================================================
   Form controls
   --------------------------------------------------------------------------
   The last obviously-default chrome in the tool. The map's type filter was a
   stack of OS checkboxes with native blue ticks sitting on a dark analytical
   panel, and every select drew the platform's own arrow in the platform's own
   colour. On the two most-viewed tabs.
   ========================================================================== */

/* ---- Checkboxes ---------------------------------------------------------- */

.filter-row input[type="checkbox"],
.info-future-toggle input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  flex: 0 0 auto;
  width: 1.05rem;
  height: 1.05rem;
  margin: 0;
  border: 1px solid var(--navy-rule);
  border-radius: 3px;
  background: var(--panel-inset);
  cursor: pointer;
  display: grid;
  place-content: center;
  transition: background-color 0.12s ease, border-color 0.12s ease;
}

/* The tick is drawn rather than glyphed, so it keeps its weight at every
   size and never depends on a font being present. */
.filter-row input[type="checkbox"]::before,
.info-future-toggle input[type="checkbox"]::before {
  content: "";
  width: 0.32rem;
  height: 0.6rem;
  border: solid var(--navy-canvas);
  border-width: 0 2px 2px 0;
  transform: rotate(42deg) translate(-1px, -1px) scale(0);
  transition: transform 0.12s ease;
}

.filter-row input[type="checkbox"]:checked,
.info-future-toggle input[type="checkbox"]:checked {
  background: var(--navy-focus);
  border-color: var(--navy-focus);
}
.filter-row input[type="checkbox"]:checked::before,
.info-future-toggle input[type="checkbox"]:checked::before {
  transform: rotate(42deg) translate(-1px, -1px) scale(1);
}

.filter-row:hover input[type="checkbox"]:not(:checked) { border-color: var(--navy-muted); }
.filter-row .filter-count { color: var(--navy-dim); }

/* ---- Selects ------------------------------------------------------------- */

.navy-selector select,
.fleet-control select,
#info-controls select {
  appearance: none;
  -webkit-appearance: none;
  /* Two strokes forming a chevron, inline so there is no request and no
     dependency on the CSP allowing an image host. currentColor cannot be used
     in a data URI, hence the literal -- it matches --navy-muted. */
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%2393a0ab' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.6rem center;
  padding-right: 1.9rem;
  cursor: pointer;
}

/* The dropdown list itself is drawn by the OS and cannot be styled, so the
   options are given an explicit ground -- without it, a dark-on-dark select
   opens a white list on some platforms and a black one on others. */
.navy-selector select option,
.fleet-control select option,
#info-controls select option {
  background: var(--navy-surface);
  color: var(--navy-text);
}

#view-news[data-news-theme="light"] .fleet-control select option {
  background: #fff;
  color: #1d2832;
}

/* ==========================================================================
   Motion
   --------------------------------------------------------------------------
   There were four transitions in the entire stylesheet. Tab switches, modal
   entry and filter changes were all hard cuts, which reads as cheap however
   good the layout is. Short and few: 120-180ms, only on things the reader
   just acted on. All inside the existing reduced-motion guard.
   ========================================================================== */

.view-tab { transition: color 0.12s ease, border-color 0.12s ease; }

#open-about,
#open-global-search,
#stats-bar .coverage-link,
.map-focus-controls button,
.info-index-link,
.system-chip,
.future-card {
  transition: background-color 0.14s ease, border-color 0.14s ease, color 0.14s ease;
}

#ship-modal,
#system-modal,
#aircraft-modal,
#coverage-modal,
#global-search-modal {
  animation: panel-in 0.16s ease-out;
}

@keyframes panel-in {
  from { opacity: 0; transform: translateY(0.5rem); }
  to   { opacity: 1; transform: none; }
}

.silhouette-unit .sil-shape { transition: fill 0.14s ease; }

@media (prefers-reduced-motion: reduce) {
  .view-tab,
  #open-about,
  #open-global-search,
  #stats-bar .coverage-link,
  .map-focus-controls button,
  .info-index-link,
  .system-chip,
  .future-card,
  .silhouette-unit .sil-shape,
  .filter-row input[type="checkbox"],
  .filter-row input[type="checkbox"]::before,
  .info-future-toggle input[type="checkbox"],
  .info-future-toggle input[type="checkbox"]::before { transition: none; }

  #ship-modal,
  #system-modal,
  #aircraft-modal,
  #coverage-modal,
  #global-search-modal { animation: none; }
}

/* ==========================================================================
   Toolbars
   --------------------------------------------------------------------------
   The controls and the sentence describing the result were competing on one
   line at the same visual weight, and the sentence always lost -- it wrapped
   into whatever space was left. They are two different things: one you
   operate, one you read. Give them separate rows and different weights.
   ========================================================================== */

#info-controls,
#future-controls,
#news-controls { align-items: flex-end; }

#info-summary,
#future-summary,
.news-status-wrap {
  flex: 1 0 100%;
  order: 2;
  margin: 0.15rem 0 0;
  padding-top: 0.6rem;
  border-top: 1px solid var(--navy-rule-subtle);
  color: var(--navy-muted);
  font-size: 0.82rem;
  line-height: 1.5;
}

/* The figures are the point of the sentence; the prose around them is not. */
#info-summary strong,
#future-summary strong,
.news-status-wrap strong { color: var(--navy-metric); font-weight: 600; }

/* The future-fleet toggle is a mode switch, not a third filter, so it is
   separated from the control cluster rather than sitting as a fourth item. */
.info-future-toggle {
  padding-left: 1.25rem;
  border-left: 1px solid var(--navy-rule-subtle);
  color: var(--navy-text);
  font-size: 0.85rem;
}

@media (max-width: 780px) {
  /* No room for a vertical rule; it becomes a horizontal one. */
  .info-future-toggle {
    flex: 1 0 100%;
    padding-left: 0;
    padding-top: 0.6rem;
    border-left: none;
    border-top: 1px solid var(--navy-rule-subtle);
  }
}

/* ==========================================================================
   Build stage as a track, not a badge
   --------------------------------------------------------------------------
   Future Fleet drew two different kinds of statement in one visual language.
   "FITTING OUT" and "MEDIUM" were the same size, the same shape and drawn
   from the same palette, so nothing told the reader that one describes where
   a hull is in construction and the other describes how well evidenced the
   entry is. A reader had to know the vocabulary to tell them apart.

   The track shows position in a sequence, which is what a build stage is and
   what a pill cannot express. It is monochrome on purpose: colour would put
   it back into conversation with the confidence badge, which owns the
   green/amber/red vocabulary here.
   ========================================================================== */

.stage-track {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}

.stage-track-bar {
  display: flex;
  gap: 2px;
  flex: 0 0 auto;
}

.stage-track-bar i {
  width: 0.85rem;
  height: 0.3rem;
  border-radius: 1px;
  background: var(--navy-rule-subtle);
}

/* Passed stages read as settled; the current one is the only bright mark, so
   the eye lands on where the hull actually is. */
.stage-track-bar i.is-done    { background: var(--navy-dim); }
.stage-track-bar i.is-current { background: var(--navy-text-strong); }
.stage-track-bar i.is-ahead   { background: var(--navy-rule-subtle); }

.stage-track-label {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--navy-text-strong);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.stage-track.is-unknown .stage-track-label { color: var(--navy-muted); }

/* In the modal the track sits in a definition list beside dates, where the
   uppercase treatment would shout over them. */
.modal-facts .stage-track-label,
#future-modal-body .stage-track-label {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 500;
  font-size: 0.85rem;
}

/* The confidence badge is now the only chip on the card, so it can stop
   competing for the same footprint and read as the aside it is. */
#view-future .hull-footer .confidence-badge { font-size: 0.64rem; }

/* ---- The ragged group grid ----------------------------------------------
   Same failure as the fleet grid: auto-fit rows align, so "Delivery pending"
   with one card and "Sea trials" with one card each left a column-height void
   and pushed "Under construction" -- the largest group -- onto a second row
   with two thirds of it empty. Roughly 45% of the viewport was blank. */
#future-grid.is-stage-layout {
  display: block;
  columns: 430px;
  column-gap: 1.25rem;
}
#future-grid.is-stage-layout .fleet-group {
  break-inside: avoid;
  margin-bottom: 1.25rem;
}

/* ==========================================================================
   Ship record: sticky identity and sister-ship navigation
   --------------------------------------------------------------------------
   Two problems with the tool's most important surface. Scrolling the record
   lost every trace of which hull it described -- the name is below a
   photograph, so it left the viewport almost immediately. And comparing a
   hull to its sisters, which is the commonest thing anyone does with a fleet
   reference, meant closing the record, finding the next hull in a grid of 241
   and clicking again, for every single comparison.

   The modal becomes a flex column with its own scrolling body. That is also
   what stops the close button scrolling away: it is absolutely positioned
   against #ship-modal, so while #ship-modal was itself the scroll container
   the button travelled up out of view with the content.
   ========================================================================== */

#ship-modal {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding: 0;
}

/* No padding-top. A sticky element is held so that its MARGIN box clears the
   offset, so pulling the bar up with a negative top margin to cover the
   padding left it stuck 20px below the scrollport instead of flush against
   it. The bar supplies the top spacing itself instead. */
#modal-content {
  overflow-y: auto;
  padding: 0 1.5rem 1.5rem;
  min-height: 0;
}

.ship-nav {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  /* Full-bleed across the body's padding, so the bar reads as a header rather
     than as a floating box, and nothing shows through at its edges. */
  margin: 0 -1.5rem 0.9rem;
  padding: 0.85rem 3.25rem 0.7rem 1.5rem;  /* right clears the close button */
  background: var(--panel);
  border-bottom: 1px solid var(--panel-rule);
}

/* The bar is a stacking context, and the close button is a sibling with no
   z-index of its own, so without this the bar paints straight over it and the
   record has no visible way out. */
#ship-modal .close-btn { z-index: 3; }

.ship-nav-identity {
  font-size: 0.9rem;
  color: var(--navy-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ship-nav-identity b {
  font-family: var(--font-mono);
  font-weight: 600;
  color: var(--navy-metric);
  margin-right: 0.35rem;
}

.ship-nav-controls {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  flex: 0 0 auto;
}

.ship-nav-position {
  font-size: 0.7rem;
  color: var(--navy-dim);
  white-space: nowrap;
  margin-right: 0.15rem;
}

.ship-nav-step {
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  border: 1px solid var(--panel-rule);
  border-radius: 3px;
  background: var(--navy-surface);
  color: var(--navy-text);
  font: inherit;
  font-size: 1.05rem;
  line-height: 1;
  cursor: pointer;
  transition: background-color 0.14s ease, border-color 0.14s ease, color 0.14s ease;
}
.ship-nav-step:hover {
  border-color: var(--navy-focus);
  color: var(--navy-focus);
}
/* Held rather than removed, so the pair does not shift as you reach an end. */
.ship-nav-step.is-empty {
  opacity: 0.28;
  cursor: default;
}

@media (max-width: 560px) {
  .ship-nav {
    margin: 0 -1rem 0.8rem;
    /* Enough right padding that the next-hull button clears the close button
       and its focus ring, which overlapped it at 3rem. */
    padding: 0.75rem 3.5rem 0.6rem 1rem;
  }
  .ship-nav-position { display: none; }   /* the buttons still say enough */
  #modal-content { padding: 0 1rem 1.15rem; }
}

@media (prefers-reduced-motion: reduce) {
  .ship-nav-step { transition: none; }
}

/* ==========================================================================
   Map: labels, key, and the markers you could not see
   ========================================================================== */

/* A marker carried no label at all, so identifying a base meant clicking it
   and reading the panel -- on a map where the Hainan and Hong Kong markers
   overlap each other. */
.leaflet-tooltip.base-tooltip {
  padding: 0.4rem 0.55rem;
  border: 1px solid var(--panel-rule);
  border-radius: 3px;
  background: rgba(26, 32, 38, 0.97);
  color: var(--navy-text);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.55);
  font-family: var(--font-sans);
  font-size: 0.78rem;
  /* Not nowrap. A fleet value can be a whole sentence -- Djibouti's is "None
     -- overseas logistics facility outside the three home-fleet structures"
     -- and on one line that label was wider than a third of the map. */
  white-space: normal;
  max-width: 15rem;
}
.leaflet-tooltip.base-tooltip::before { border-top-color: var(--panel-rule); }

.base-tip { display: flex; flex-direction: column; gap: 0.1rem; }
.base-tip b { color: var(--navy-text-strong); font-weight: 600; }
.base-tip span { color: var(--navy-muted); font-size: 0.74rem; line-height: 1.4; }
.base-tip em { color: var(--navy-focus); font-size: 0.7rem; font-style: normal; }

/* Filtering and reading a key are two different jobs that shared one box with
   a hairline between them. The key now says what it is. */
.filter-legend {
  margin-top: 0.75rem;
  padding-top: 0.65rem;
  border-top: 1px solid var(--navy-rule);
  color: var(--navy-muted);
}
.legend-heading {
  margin-bottom: 0.4rem;
  color: var(--navy-dim);
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.filter-legend .legend-note {
  margin: 0.5rem 0 0;
  color: var(--navy-dim);
  font-size: 0.7rem;
  line-height: 1.45;
  font-style: normal;
}

/* Djibouti is a lone grey marker on a dark landmass and was very nearly
   invisible. Desaturating to 0.15 took almost all of it; the ring is what
   actually separates a marker from the ground, so the ghost keeps a solid
   one and loses only its fill. */
.base-marker-icon.is-ghosted .base-marker-circle {
  border-style: solid;
  border-color: #aab4bd;
  filter: none;
  background-image: linear-gradient(rgba(20, 25, 30, 0.45), rgba(20, 25, 30, 0.45));
}

/* ==========================================================================
   News: provenance without the wall
   --------------------------------------------------------------------------
   The honesty stays; the delivery changes. One status line and one disclosure
   replace a heading, a collection paragraph, a status pill, a separate
   disclosure and two section explainers -- six blocks the reader passed
   through before reaching a single headline.
   ========================================================================== */

.news-live-strip {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.4rem 1rem;
  margin-top: 0.3rem;
}

.news-live-strip .news-live-health {
  margin: 0;
  padding: 0.2rem 0.5rem;
  border-radius: 3px;
  font-size: 0.76rem;
}

.news-live-methodology > summary {
  font-size: 0.76rem;
  cursor: pointer;
  color: var(--link);
}
.news-methodology-body {
  max-width: 62ch;
  margin-top: 0.4rem;
  padding-left: 0.75rem;
  border-left: 2px solid var(--navy-rule-subtle);
  color: var(--navy-muted);
  font-size: 0.8rem;
  line-height: 1.55;
}
.news-methodology-body p { margin: 0 0 0.5rem; }
.news-methodology-body p:last-child { margin-bottom: 0; }

/* A quiet cycle is a one-line statement, not a section. */
.news-subsection.is-quiet { margin: 0.9rem 0 0; }
.news-subsection.is-quiet .news-live-empty {
  margin: 0;
  color: var(--navy-dim);
  font-size: 0.82rem;
}

.news-subsection-head p {
  max-width: 62ch;
  color: var(--navy-muted);
  font-size: 0.8rem;
}

/* Collection telemetry is provenance, not an alert: a feed that did not
   respond is a fact about the collection, and in red it read as something
   being wrong with the page. The theme-scoped rule sets the colour, so this
   has to be scoped too rather than sitting on the bare class.

   The publisher column needed nothing in the end. It was the one text pair
   failing AA at 3.19:1, and moving the shell onto the dark palette put it at
   5.87:1 on its own. */
#view-news[data-news-theme="dark"] .news-live-failed {
  color: var(--navy-metric);
  border-bottom-color: #7b6234;
}

/* ==========================================================================
   Stats bar: the headline at every width, the caveats one tap away on a phone
   --------------------------------------------------------------------------
   Five things shared this strip -- base count, hull coverage, the
   assumption-tier caveat, the confidence guide, the gaps control and the
   review date -- and on a 390px screen that wrapped to three lines and about
   145px before any content appeared.

   The caveat is not dropped and never will be: an "assumption"-tier placement
   is inference, not a sighting, and a reader has to be able to find that out.
   But it does not have to be the first thing between them and the fleet.
   ========================================================================== */

/* display:contents means the children lay out in the bar exactly as they did
   before the wrapper existed, so the desktop strip is untouched. */
.stats-detail { display: contents; }
.stats-expand { display: none; }

@media (max-width: 780px) {
  #stats-bar { row-gap: 0.25rem; }

  /* Shortens "236 of ~247 known hulls placed (~96%)" to "236 of ~247 hulls
     (~96%)", which is what leaves room for the disclosure control on the
     same line. No figure is affected. */
  .stats-longform { display: none; }

  /* 44px of hit area, a 1.6rem mark. A 44px circle would dominate the line it
     annotates, but shrinking the button to match the circle would leave a
     26px target -- which is what the tap-target assertion caught. The button
     carries the size and the circle is drawn inside it. */
  .stats-expand {
    display: grid;
    place-items: center;
    order: 1;
    width: 44px;
    height: 44px;
    padding: 0;
    border: none;
    background: none;
    color: var(--navy-muted);
    font-size: 0.9rem;
    line-height: 1;
    cursor: pointer;
    /* The glyph is a bare text node, so it cannot be lifted above a
       positioned ::before. Isolate instead and drop the circle behind it --
       the isolation is what keeps z-index:-1 from falling through the bar. */
    position: relative;
    isolation: isolate;
  }
  .stats-expand::before {
    content: "";
    position: absolute;
    z-index: -1;
    width: 1.6rem;
    height: 1.6rem;
    border: 1px solid var(--panel-rule);
    border-radius: 50%;
    background: var(--navy-surface);
  }
  .stats-expand:hover,
  #stats-bar.is-expanded .stats-expand { color: var(--navy-focus); }
  .stats-expand:hover::before,
  #stats-bar.is-expanded .stats-expand::before { border-color: var(--navy-focus); }

  .stats-asof {
    order: 2;
    flex: 1 0 100%;
    color: var(--navy-dim);
    font-size: 0.76rem;
  }

  .stats-detail {
    display: none;
    order: 3;
    flex: 1 0 100%;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 0.75rem;
    margin-top: 0.35rem;
    padding-top: 0.5rem;
    border-top: 1px solid var(--shell-rule);
  }
  #stats-bar.is-expanded .stats-detail { display: flex; }

  /* The row it lives in is 44px tall, so the button itself does not need to
     be -- and a 44px circle here would dominate the line it annotates. */
  #stats-bar .coverage-link { min-height: 2.25rem; }
}

/* ==========================================================================
   Semantic status tokens
   --------------------------------------------------------------------------
   Every status colour in the tool was written twice: once light, once again
   under #view-news[data-news-theme="dark"], and for confidence badges a third
   time under #view-future and #base-sidebar. Six colour families, two or
   three copies each, all kept in sync by hand -- and the modals showed how
   that ends, because nobody remembered to add them to the third list.

   A token has one definition and the theme redefines the token, so each
   component is styled once. Adding a surface costs nothing, and changing a
   status colour is one edit rather than a search.

   The names say what the colour MEANS, not what it looks like: ok, warn,
   alert, infer. A name like "green" has to be renamed the day it stops being
   green, and confidence tiers are exactly the kind of thing that gets
   restyled.
   ========================================================================== */

:root {
  --status-ok-bg:        #203b2a;  --status-ok-fg:        #8fd6a1;  --status-ok-rule:        #3d6b49;
  --status-warn-bg:      #42371f;  --status-warn-fg:      #e5c56f;  --status-warn-rule:      #766233;
  --status-alert-bg:     #44282a;  --status-alert-fg:     #ef9a9f;  --status-alert-rule:     #754247;
  --status-infer-bg:     #382d4d;  --status-infer-fg:     #d2b7ff;  --status-infer-rule:     #7f68a4;
  --status-identity-bg:  #3a2935;  --status-identity-fg:  #e5a9cf;  --status-identity-rule:  #9b6287;
  --status-neutral-bg:   #303944;  --status-neutral-fg:   #bec9d2;  --status-neutral-rule:   #536170;
  --status-trials-bg:    #203947;  --status-trials-fg:    #8dd7ed;  --status-trials-rule:    #3d7184;

  /* News category chips and provenance flags: same idea, own families. */
  --cat-deploy-bg:      #1e344f;  --cat-deploy-fg:      #a8ccf0;
  --cat-commission-bg:  #1c3a26;  --cat-commission-fg:  #9dd6ac;
  --cat-construct-bg:   #40340f;  --cat-construct-fg:   #e2c883;
  --cat-tech-bg:        #2f2450;  --cat-tech-fg:        #c6b0f2;
  --cat-exercise-bg:    #45241f;  --cat-exercise-fg:    #eda99c;

  --flag-state-bg:      #3a2f16;  --flag-state-fg:      #e2c883;  --flag-state-rule:      #7a6432;
  --flag-official-bg:   #2b3541;  --flag-official-fg:   #b0c2d4;  --flag-official-rule:   #52657a;
  --flag-movement-bg:   #1c3a2c;  --flag-movement-fg:   #9ed6b8;  --flag-movement-rule:   #3d6b53;
  --movement-dot:       #5fbf90;

  --health-ok-bg:    #1c3a2c;  --health-ok-fg:    #a9dcc0;  --health-ok-rule:    #3d6b53;
  --health-warn-bg:  #40340f;  --health-warn-fg:  #e2c883;  --health-warn-rule:  #7a6432;
  --health-bad-bg:   #45241f;  --health-bad-fg:   #eda99c;  --health-bad-rule:   #8a4438;

  /* Scrims. There were three different blacks behind five modals -- 0.35,
     0.45 and a tinted 0.58 -- and no record of why. */
  --scrim: rgba(14, 20, 25, 0.55);
  --panel-translucent: rgba(26, 32, 38, 0.96);
}

/* The light reading option redefines the tokens. Not one component rule below
   has to know the theme exists. */
#view-news[data-news-theme="light"] {
  --status-ok-bg:        #e3f5e6;  --status-ok-fg:        #1e7b34;  --status-ok-rule:        #a8d9b4;
  --status-warn-bg:      #fdf1d6;  --status-warn-fg:      #92650a;  --status-warn-rule:      #e6cf9a;
  --status-alert-bg:     #fbe3e3;  --status-alert-fg:     #a3282f;  --status-alert-rule:     #ecc0c0;
  --status-infer-bg:     #ece4fa;  --status-infer-fg:     #5b3a9e;  --status-infer-rule:     #b9a3e0;
  --status-identity-bg:  #f9e9f2;  --status-identity-fg:  #7a3560;  --status-identity-rule:  #c08fae;

  --cat-deploy-bg:      #e4edf7;  --cat-deploy-fg:      #23507f;
  --cat-commission-bg:  #e2f1e6;  --cat-commission-fg:  #1e6b33;
  --cat-construct-bg:   #fdf0d5;  --cat-construct-fg:   #8a6212;
  --cat-tech-bg:        #ece4fa;  --cat-tech-fg:        #5b3a9e;
  --cat-exercise-bg:    #fbe6e4;  --cat-exercise-fg:    #96382c;

  --flag-state-bg:      #fdf3e0;  --flag-state-fg:      #8a6212;  --flag-state-rule:      #dcbf88;
  --flag-official-bg:   #eef1f4;  --flag-official-fg:   #4a545e;  --flag-official-rule:   #ccd3da;
  --flag-movement-bg:   #e7f4ec;  --flag-movement-fg:   #256b45;  --flag-movement-rule:   #a9d4bd;
  --movement-dot:       #2f8a5e;

  --health-ok-bg:    #edf6f0;  --health-ok-fg:    #365d43;  --health-ok-rule:    #5d9270;
  --health-warn-bg:  #fff7e8;  --health-warn-fg:  #7a5718;  --health-warn-rule:  #c6902d;
  --health-bad-bg:   #fdf0ee;  --health-bad-fg:   #87362d;  --health-bad-rule:   #b84c40;
}

/* ---- One rule per component --------------------------------------------- */

.confidence-high       { background: var(--status-ok-bg);    color: var(--status-ok-fg);    border: 1px solid var(--status-ok-rule); }
.confidence-medium     { background: var(--status-warn-bg);  color: var(--status-warn-fg);  border: 1px solid var(--status-warn-rule); }
.confidence-low        { background: var(--status-alert-bg); color: var(--status-alert-fg); border: 1px solid var(--status-alert-rule); }
.confidence-assumption { background: var(--status-infer-bg); color: var(--status-infer-fg); border: 1px dashed var(--status-infer-rule); }

.identity-badge      { background: var(--status-identity-bg); color: var(--status-identity-fg); border-color: var(--status-identity-rule); }
.identity-warning    { background: var(--status-identity-bg); border-color: var(--status-identity-rule); }
.identity-warning h4 { color: var(--status-identity-fg); }

.stage-delivery-pending   { background: var(--status-ok-bg);      color: var(--status-ok-fg);      border: 1px solid var(--status-ok-rule); }
.stage-sea-trials         { background: var(--status-trials-bg);  color: var(--status-trials-fg);  border: 1px solid var(--status-trials-rule); }
.stage-fitting-out        { background: var(--status-warn-bg);    color: var(--status-warn-fg);    border: 1px solid var(--status-warn-rule); }
.stage-under-construction { background: var(--status-neutral-bg); color: var(--status-neutral-fg); border: 1px solid var(--status-neutral-rule); }
.stage-design             { background: var(--status-infer-bg);   color: var(--status-infer-fg);   border: 1px dashed var(--status-infer-rule); }

.news-cat-deployment    { background: var(--cat-deploy-bg);     color: var(--cat-deploy-fg); }
.news-cat-commissioning { background: var(--cat-commission-bg); color: var(--cat-commission-fg); }
.news-cat-construction  { background: var(--cat-construct-bg);  color: var(--cat-construct-fg); }
.news-cat-technology    { background: var(--cat-tech-bg);       color: var(--cat-tech-fg); }
.news-cat-exercise      { background: var(--cat-exercise-bg);   color: var(--cat-exercise-fg); }

.source-role-flag,
.state-media-flag             { background: var(--flag-state-bg);    color: var(--flag-state-fg);    border-color: var(--flag-state-rule); }
.source-role-official-release { background: var(--flag-official-bg); color: var(--flag-official-fg); border-color: var(--flag-official-rule); }
.news-movement-term,
.news-movement-flag           { background: var(--flag-movement-bg); color: var(--flag-movement-fg); border-color: var(--flag-movement-rule); }
.news-movements .news-subsection-head h4::before { background: var(--movement-dot); }

.news-live-head .news-live-health {
  background: var(--health-ok-bg);
  color: var(--health-ok-fg);
  border-left-color: var(--health-ok-rule);
}
.news-live-head .news-live-health.is-delayed {
  background: var(--health-warn-bg);
  color: var(--health-warn-fg);
  border-left-color: var(--health-warn-rule);
}
.news-live-head .news-live-health.is-stale,
.news-live-head .news-live-health.is-missing {
  background: var(--health-bad-bg);
  color: var(--health-bad-fg);
  border-left-color: var(--health-bad-rule);
}
.news-live-failed { color: var(--health-bad-fg); border-bottom-color: var(--health-bad-rule); }

/* Five modals, three different blacks behind them. */
#ship-modal-backdrop,
#system-modal-backdrop,
#aircraft-modal-backdrop,
#coverage-modal-backdrop,
#global-search-backdrop { background: var(--scrim); }

#view-map .control-panel,
.map-focus-controls button { background: var(--panel-translucent); }

/* ==========================================================================
   Surfaces the dark pass missed
   --------------------------------------------------------------------------
   Found by auditing computed contrast inside every modal rather than by
   looking, which is the only way this set could have been found: the worst of
   them sat below the fold of the screenshots that signed the dark pass off.

   Two kinds of miss. The first is text that kept a light-theme colour on a
   panel that went dark -- the class overview and the system description were
   #333 on #232b33, which measures 1.13:1 and is simply not there. The second
   is the reverse and was self-inflicted: .class-snapshot keeps a light
   background, so the blanket "headings in this modal are light" rule from the
   dark pass put near-white text on it at 1.06:1.

   The rest are the near-duplicate greys -- #888, #999, #65717b, #737d86,
   #7a857e, #7b858e, #68737d -- which nobody could tell apart and which all
   landed between 2.9:1 and 4.4:1 on the dark panel. Folding them onto the two
   text tokens fixes the contrast and removes the duplicates in one move.
   ========================================================================== */

/* ---- Text that stayed light-theme --------------------------------------- */

.class-overview p,
.system-modal-desc,
.coverage-intro { color: var(--navy-text); }

.system-modal-meta,
.coverage-table th,
.modal-image-caption,
.global-search-empty,
.global-search-label,
.global-search-hint,
.modal-breadcrumb,
.modal-facts dt,
.fleet-unverified,
.related-news-panel h4,
.related-news-content h5,
.related-news-content .related-news-caveat,
#ship-modal h4,
#system-modal h4 { color: var(--navy-muted); }

.modal-image-caption a { color: var(--link); }

/* A link inside the modals, whatever rule set it. The dark pass covered bare
   descendants but not the ones a component rule had already claimed. */
#ship-modal a,
#system-modal a,
#aircraft-modal a,
#coverage-modal a,
#global-search-modal a { color: var(--link); }
#ship-modal a:hover,
#system-modal a:hover,
#aircraft-modal a:hover,
#coverage-modal a:hover,
#global-search-modal a:hover { color: var(--link-strong); }

.coverage-assumption-tag { color: var(--status-infer-fg); }

/* ---- The one block that kept a light ground ------------------------------ */

.class-snapshot {
  border-color: var(--panel-rule);
  background: var(--panel-inset);
}
.class-snapshot li { color: var(--navy-text); }
.class-snapshot strong,
.class-snapshot b { color: var(--navy-text-strong); }

/* ---- Search result counts ------------------------------------------------ */

.global-search-counts span { color: var(--navy-muted); }

/* .class-snapshot h5 and .system-group h5 kept their own greys and outrank a
   bare h5 selector, so they had to be named. */
.class-snapshot h5,
.system-group h5 { color: var(--navy-muted); }

/* ==========================================================================
   Theme-aware text tokens
   --------------------------------------------------------------------------
   The status tokens fixed the chips, but the per-tab work reached for the
   dark text tokens directly -- var(--navy-muted), var(--link),
   var(--navy-metric) -- and those are dark-surface colours. On the News light
   option the same rules put sand on white at 1.65:1 and the link cyan on
   white at 1.52:1: worse than what they replaced, and invisible.

   The lesson is the one the status chips already taught. A component must not
   name a colour that only works on one ground. These four tokens mean the
   surface decides, and the light option redefines them once.

   Found by auditing computed contrast across every view, both themes and
   every modal -- not by looking. Several of these sit below the fold.
   ========================================================================== */

:root {
  --text-body:  #dfe4e8;   /* running text on a dark surface   */
  --text-soft:  #93a0ab;   /* labels, captions, secondary rows */
  --text-faint: #83909b;   /* the quietest thing still read    */
  --text-link:  #8ad4f7;
  --text-figure:#d7c9a0;   /* the number in a sentence         */
}

#view-news[data-news-theme="light"] {
  --text-body:  #23303d;
  --text-soft:  #4a545e;
  --text-faint: #5c6570;
  --text-link:  #1f6fb8;
  --text-figure:#7a5718;
}

/* ---- Rules from the per-tab pass, now surface-independent ---------------- */

#info-summary,
#future-summary,
.news-status-wrap { color: var(--text-soft); }

#info-summary strong,
#future-summary strong,
.news-status-wrap strong { color: var(--text-figure); }

.news-live-methodology > summary { color: var(--text-link); }

.news-methodology-body {
  border-left-color: var(--panel-rule);
  color: var(--text-soft);
}

.news-subsection-head p,
.news-subsection.is-quiet .news-live-empty,
.news-live-empty,
.news-live-foot,
.news-live-table th,
.news-related { color: var(--text-soft); }

#view-news[data-news-theme="light"] .news-methodology-body { border-left-color: #ccd3da; }

/* 9.28px is small enough that the muted tone was landing at 4.34:1. The name
   under a silhouette is the label that identifies the hull, so it reads. */
.silhouette-name { color: var(--navy-muted); }

/* The last three failures were pre-existing light-theme values at 4.41:1 --
   under the 4.5 line by a hair, and the sort of thing that only a measurement
   finds. Taking them to the token clears it at 7.2:1. */
.news-live-head h3,
.news-live-head p,
.news-live-head p strong { color: var(--text-soft); }
#view-news[data-news-theme="dark"] .news-live-head h3 { color: var(--navy-text-strong); }
#view-news[data-news-theme="dark"] .news-live-head p strong { color: var(--navy-text); }

/* .news-section-head p and the remaining muted News text, which sit on the
   light surface at 4.41:1 -- under the line by a hair. */
.news-section-head p,
.news-auto-note,
.news-live-also,
.news-live-empty { color: var(--text-soft); }
