:root {
  --bg: #0d1117; --panel: #151b26; --panel2: #1b2230; --text: #e6edf6; --muted: #8b98ac;
  --accent: #4c8dff; --accent-soft: #1b3a6b; --border: #26304a;
  --ok: #35c98f; --ok-bg: #103324; --warn: #ffcf3d; --warn-bg: #3a3110;
  --risk: #ff6b6b; --risk-bg: #3a1717; --info: #b98bff; --info-bg: #241a3a;
  --gray: #6b7688; --gray-bg: #1e2430; --code-bg: #0a0e15;
}
@media (prefers-color-scheme: light) {
  :root {
    --bg: #f4f6fb; --panel: #ffffff; --panel2: #eef1f8; --text: #16202e; --muted: #5b6679;
    --accent: #2456c9; --accent-soft: #dbe6ff; --border: #dde3ee;
    --ok: #157a53; --ok-bg: #e2f7ee; --warn: #8a6600; --warn-bg: #fff3d1;
    --risk: #b3261e; --risk-bg: #fde5e3; --info: #6528c9; --info-bg: #ece2ff;
    --gray: #6b7688; --gray-bg: #eceff5; --code-bg: #eef1f7;
  }
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--bg); color: var(--text);
  font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  line-height: 1.55;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

.topnav {
  display: flex; align-items: center; gap: 4px; flex-wrap: wrap;
  padding: 10px 20px; border-bottom: 1px solid var(--border);
  background: var(--panel); position: sticky; top: 0; z-index: 10;
}
.topnav .brand { font-weight: 800; margin-right: 14px; letter-spacing: .01em; }
.topnav .brand span { color: var(--accent); }
.topnav a {
  color: var(--muted); padding: 7px 12px; border-radius: 7px; font-size: 0.92rem; font-weight: 600;
}
.topnav a:hover, .topnav a.active { background: var(--panel2); color: var(--text); text-decoration: none; }
.topnav .spacer { flex: 1; }
.topnav .updated { color: var(--muted); font-size: 0.8rem; }

/* Group headings in the top bar. Not links, and deliberately quiet: they
   are a signpost for which half of the site you are in, not another thing
   to click. The left border does the separating so the bar does not need a
   second row. */
.topnav .navgroup {
  color: var(--muted);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  font-weight: 700;
  padding: 0 8px 0 12px;
  margin-left: 4px;
  border-left: 1px solid var(--border);
  align-self: center;
  white-space: nowrap;
}

.topnav .navgroup:first-of-type { border-left: none; margin-left: 0; }

.wrap { max-width: 1180px; margin: 0 auto; padding: 28px 20px 80px; }

.hero {
  display: flex; gap: 24px; align-items: center; margin-bottom: 28px; flex-wrap: wrap;
}
.hero img { width: 180px; border-radius: 12px; border: 1px solid var(--border); }
.hero h1 { margin: 0 0 6px; font-size: 1.9rem; }
.hero p { margin: 0; color: var(--muted); max-width: 640px; }

.stat-row { display: flex; gap: 14px; flex-wrap: wrap; margin: 22px 0 30px; }
.stat {
  background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
  padding: 14px 18px; min-width: 130px;
}
.stat .n { font-size: 1.6rem; font-weight: 800; }
.stat .l { color: var(--muted); font-size: 0.8rem; text-transform: uppercase; letter-spacing: .04em; }

h2.section-title {
  font-size: 1.2rem; margin: 40px 0 12px; padding-left: 10px; border-left: 4px solid var(--accent);
}
p.section-sub { color: var(--muted); margin: -6px 0 16px 14px; font-size: 0.92rem; }

.card {
  background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
  padding: 16px 18px; margin-bottom: 14px;
}

table.status-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
table.status-table th, table.status-table td {
  text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--border); vertical-align: top;
}
table.status-table th { color: var(--muted); font-weight: 700; font-size: 0.78rem; text-transform: uppercase; letter-spacing: .03em; }
table.status-table tr:hover td { background: var(--panel2); }
table.status-table td.system { font-weight: 700; }
table.status-table td.notes { color: var(--muted); font-size: 0.87rem; }

.badge {
  display: inline-block; font-size: 0.72rem; font-weight: 700; padding: 3px 10px; border-radius: 999px;
  white-space: nowrap; letter-spacing: .02em;
}
.badge.ok      { background: var(--ok-bg); color: var(--ok); }
.badge.warn    { background: var(--warn-bg); color: var(--warn); }
.badge.risk    { background: var(--risk-bg); color: var(--risk); }
.badge.info    { background: var(--info-bg); color: var(--info); }
.badge.gray    { background: var(--gray-bg); color: var(--gray); }

details.parking {
  background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
  padding: 10px 16px; margin-bottom: 10px;
}
details.parking summary { cursor: pointer; font-weight: 700; padding: 6px 0; }
details.parking ul { margin: 8px 0 6px; padding-left: 22px; color: var(--muted); font-size: 0.9rem; }
details.parking li { margin-bottom: 4px; }

ol.findings, ul.findings { padding-left: 22px; }
ol.findings li, ul.findings li { margin-bottom: 12px; }
ol.findings li strong, ul.findings li strong { color: var(--text); }

.grid-cards {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px;
}
.art-card {
  background: var(--panel); border: 1px solid var(--border); border-radius: 10px; overflow: hidden;
  text-align: center;
}
.art-card .thumb-wrap {
  aspect-ratio: 1 / 1; display: flex; align-items: center; justify-content: center;
  background: var(--panel2); overflow: hidden;
}
.art-card img { width: 100%; height: 100%; object-fit: cover; display: block; }
.art-card .meta { padding: 10px 12px; }
.art-card .meta .title { font-weight: 700; font-size: 0.9rem; }
.art-card .meta .count { color: var(--muted); font-size: 0.78rem; }

.tag-status {
  display: inline-flex; align-items: center; gap: 6px; font-size: 0.75rem; font-weight: 700;
  padding: 2px 8px; border-radius: 6px; margin-left: 6px;
}
.tag-status.placeholder { background: var(--warn-bg); color: var(--warn); }
.tag-status.concept { background: var(--info-bg); color: var(--info); }
.tag-status.final { background: var(--ok-bg); color: var(--ok); }

footer.site-footer {
  margin-top: 60px; padding-top: 20px; border-top: 1px solid var(--border);
  color: var(--muted); font-size: 0.85rem;
}

/* Contact details, appended to every footer by nav.js rather than written into
   twenty pages by hand -- see the comment beside renderContact() there.

   Separated from the regenerate-instructions above it, because those are a note
   to whoever maintains the page and this is addressed to a visitor. */
.footer-contact {
  margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border);
  color: var(--text);
}
.footer-contact a { color: var(--accent); text-decoration: none; }
.footer-contact a:hover { text-decoration: underline; }

.two-col { display: grid; grid-template-columns: 1.4fr 1fr; gap: 20px; }
@media (max-width: 900px) { .two-col { grid-template-columns: 1fr; } }

code { background: var(--code-bg); border: 1px solid var(--border); border-radius: 5px; padding: 1px 6px; font-family: Consolas, monospace; font-size: 0.85em; }

/* ---------------------------------------------------------------------------
   Collapsible detail — the site's default way of showing anything long.
   
   Added 2026-09-25 on the user's direction: "I think we want a smaller table
   that expands out to show all the detail and modifier, instead of making
   everything completely visible all of the time. Collapsible but expandable
   should be the theme on a lot of these major items."
   
   Built on native <details>/<summary> rather than a JS widget, so it works
   with no script, survives a page with a broken script, and is keyboard- and
   screen-reader-accessible for free.
   --------------------------------------------------------------------------- */

details.expand {
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--panel); margin: 6px 0; overflow: hidden;
}
details.expand > summary {
  cursor: pointer; padding: 10px 14px; font-size: 0.9rem;
  list-style: none; display: flex; align-items: center; gap: 10px;
}
details.expand > summary::-webkit-details-marker { display: none; }
details.expand > summary::before {
  content: "▸"; color: var(--accent); font-size: 0.85rem;
  transition: transform .12s ease; flex: none;
}
details.expand[open] > summary::before { transform: rotate(90deg); }
details.expand > summary:hover { background: var(--panel2); }
details.expand .expand-body {
  padding: 2px 14px 14px 32px; font-size: 0.88rem; color: var(--muted);
  border-top: 1px solid var(--border);
}
details.expand .expand-body p { margin: 8px 0; }
details.expand .expand-body ul { margin: 8px 0; padding-left: 20px; }

/* A summary line that carries its own label + badge without wrapping oddly. */
summary .sum-name { font-weight: 700; color: var(--text); }
summary .sum-tag  { color: var(--muted); font-size: 0.82rem; }
summary .sum-spacer { flex: 1 1 auto; }

/* ---------------------------------------------------------------------------
   Kind tags, and the filter/sort bar that goes with them.

   Added 2026-09-25 on the user's direction, after the hazards page grew a
   one-word "kind" per entry: "can we color code those somehow or make the
   table sortable by tag entries too?" -- both, and shared here rather than
   left in one page, because abilities want the same language.

   One colour per kind, chosen to be obviously different at a glance rather
   than to sit on a gradient: these are categories, not a scale, so there is
   no ordering for a colour ramp to encode. Each chip is a tinted background
   with a matching solid border and bright text, which reads on both palettes
   from one set of rules; only the text colour is darkened for light mode.

   Adding a kind = adding one rule here. The pages derive the class name from
   the tag text ("Area denial" -> tag-areadenial), so nothing else changes.
   --------------------------------------------------------------------------- */

.kind-tag {
  display: inline-block; font-size: 0.72rem; font-weight: 700; letter-spacing: .03em;
  text-transform: uppercase; padding: 3px 9px; border-radius: 999px;
  border: 1px solid transparent; white-space: nowrap;
}

.tag-destructible  { background: rgba(229, 83, 83, 0.16);   border-color: #e55353; color: #ff8f8f; }
.tag-movement      { background: rgba(60, 184, 120, 0.16);  border-color: #3cb878; color: #6fe0a5; }
.tag-puckcontrol   { background: rgba(86, 140, 240, 0.16);  border-color: #568cf0; color: #8fb6ff; }
.tag-barrier       { background: rgba(0, 184, 196, 0.16);   border-color: #00b8c4; color: #56dfe8; }
.tag-areadenial    { background: rgba(240, 148, 40, 0.16);  border-color: #f09428; color: #ffb861; }
.tag-obstacle      { background: rgba(150, 150, 165, 0.16); border-color: #9696a5; color: #c3c3cf; }
.tag-movingthreat  { background: rgba(176, 96, 220, 0.16);  border-color: #b060dc; color: #d59cf2; }
.tag-shooting      { background: rgba(229, 83, 83, 0.16);   border-color: #e55353; color: #ff8f8f; }
.tag-puck          { background: rgba(86, 140, 240, 0.16);  border-color: #568cf0; color: #8fb6ff; }

/* Difficulty IS a scale, unlike the kind chips above, so these four DO sit on a
   ramp -- green through amber to red -- and the steps are pushed far apart so
   Medium and Hard can never be mistaken for each other at a glance. */
.tag-easy          { background: rgba(60, 184, 120, 0.16);  border-color: #3cb878; color: #6fe0a5; }
.tag-medium        { background: rgba(224, 190, 40, 0.16);  border-color: #e0be28; color: #f2d861; }
.tag-hard          { background: rgba(240, 122, 40, 0.16);  border-color: #f07a28; color: #ffa463; }
.tag-expert        { background: rgba(214, 48, 48, 0.18);   border-color: #d63030; color: #ff7a7a; }

@media (prefers-color-scheme: light) {
  .tag-destructible { color: #a82121; }
  .tag-movement     { color: #1c7347; }
  .tag-puckcontrol  { color: #234c9e; }
  .tag-barrier      { color: #0b6b72; }
  .tag-areadenial   { color: #9a5a0d; }
  .tag-obstacle     { color: #55555f; }
  .tag-movingthreat { color: #6c2c90; }
  .tag-shooting     { color: #a82121; }
  .tag-puck         { color: #234c9e; }
  .tag-easy         { color: #1c7347; }
  .tag-medium       { color: #7d6a05; }
  .tag-hard         { color: #94450a; }
  .tag-expert       { color: #9c1616; }
}

/* Filter + sort bar. Plain buttons -- no library, no page reload, and the
   filter chips wear their own tag colour so the bar doubles as the legend. */
.tag-controls {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 12px 14px; margin-bottom: 14px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
}
.tag-controls .label {
  font-size: 0.74rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; color: var(--muted); margin-right: 2px;
}
.tag-controls .sep { flex: 1 1 100%; height: 0; }
.tag-filter, .tag-sort {
  cursor: pointer; font: inherit; font-size: 0.78rem;
  background: transparent; color: var(--muted);
  border: 1px solid var(--border); border-radius: 999px; padding: 4px 11px;
}
.tag-filter:hover, .tag-sort:hover { color: var(--text); border-color: var(--accent); }
.tag-filter[aria-pressed="true"], .tag-sort[aria-pressed="true"] {
  color: var(--text); border-color: var(--accent); background: var(--panel2); font-weight: 700;
}
.tag-filter.kind-tag { text-transform: uppercase; }
.tag-count { color: var(--muted); font-size: 0.8rem; margin-left: auto; }

/* ---------------------------------------------------------------------------
   Overflow control.
   
   From the same review: "several items are missized. Look for text blocks that
   overflow their board or table cell. It looks rinky dink and unprofessional."
   
   The cause was long unbroken strings (file paths, identifiers) forcing a
   table cell wider than its column, and multi-hundred-word notes in a cell
   never meant to hold them. Long words now break, and any note that still
   wants to be enormous belongs in a <details> instead.
   --------------------------------------------------------------------------- */

.card, .card p, .card li, table.status-table td, table.status-table th {
  overflow-wrap: anywhere; word-break: normal;
}
.card { overflow: hidden; }
table.status-table { table-layout: auto; }
table.status-table td code, .card code { overflow-wrap: anywhere; }

/* ---------------------------------------------------------------------------
   Column widths on wide tables.

   Second report, 2026-09-25: "formatting on Dashboard is still terrible. The
   last column is WAAY too narrow and the text is only a couple of characters
   wide. Please address. Look for other situations where this may be occurring."

   The first overflow fix above made long strings breakable, which was right and
   is still needed -- but it also created this problem. `overflow-wrap: anywhere`
   drives a cell's min-content width down to roughly ONE CHARACTER, and the
   default `table-layout: auto` hands column widths out in proportion to content.
   So on a wide table whose middle columns carry paragraphs, the browser is free
   to squeeze the last column to a single-character ribbon, and does. The more
   columns a table has, the worse it gets -- which is why the two tables that
   look broken are the 5- and 6-column ones and nothing narrower.

   Fixing it per-table by nudging max-widths does not work, because auto layout
   will just redistribute the slack somewhere else. The fix is to stop asking the
   browser to guess: `.status-table.fixed` switches to fixed layout and takes its
   widths from a <colgroup>, so every column gets the share it was designed to
   have no matter what lands in it. Text still wraps; nothing is clipped.

   Any table with four or more columns should use this. Two- and three-column
   tables are left on auto, where content-proportional widths are genuinely the
   better answer.
   --------------------------------------------------------------------------- */

table.status-table.fixed { table-layout: fixed; }

/* Fixed layout makes vertical alignment matter: without this, a one-word cell
   floats in the middle of a row whose neighbour holds six lines. */
table.status-table.fixed td, table.status-table.fixed th { vertical-align: top; }

/* Below this width there is no honest way to show five columns, so the table
   scrolls sideways inside its card rather than crushing every column at once.
   Each page wraps its wide tables in a div with overflow-x:auto for this. */
@media (max-width: 900px) {
  table.status-table.fixed { min-width: 860px; }
}

/* Keeps a long cell readable without letting it run away down the page. */
td.notes .clamp {
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ---------------------------------------------------------------------------
   Art strip — small ArtBible crops used to break up long pages.
   "feel free to grab photo snippets that make the website more lively and
   interesting from the ArtBible."
   --------------------------------------------------------------------------- */

.art-strip {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px; margin: 14px 0 18px;
}
.art-strip figure { margin: 0; border-radius: 10px; overflow: hidden; border: 1px solid var(--border); }
.art-strip img { width: 100%; height: 120px; object-fit: cover; display: block; }
.art-strip figcaption {
  padding: 6px 10px; font-size: 0.75rem; color: var(--muted);
  background: var(--panel); text-transform: uppercase; letter-spacing: .03em;
}

/* Jump links, so a long page does not have to be scrolled end to end. */
.jumpbar {
  display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 18px;
  padding: 10px 12px; background: var(--panel); border: 1px solid var(--border);
  border-radius: 10px; position: sticky; top: 0; z-index: 5;
}
.jumpbar a {
  font-size: 0.78rem; padding: 4px 10px; border-radius: 999px;
  background: var(--panel2); color: var(--muted); text-decoration: none;
  border: 1px solid var(--border);
}
.jumpbar a:hover { color: var(--text); border-color: var(--accent); }
.jumpbar .jump-label {
  font-size: 0.72rem; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); align-self: center; margin-right: 4px;
}

/* --------------------------------------------------------------------------
   Roadmap progress bar (dashboard.html).

   Four bands, and per the project's standing direction the colour scale is
   made PRONOUNCEDLY distinct rather than engineered for colour-blindness:
   green / amber / red / flat grey are four different lightnesses as well as
   four hues, so the bar still reads as an ordering in greyscale. Each segment
   also carries its own percentage label and a title tooltip, so the colour is
   never the only carrier of the number.
   -------------------------------------------------------------------------- */
.progress-bar {
  display: flex; width: 100%; height: 34px; margin: 14px 0 10px;
  border-radius: 8px; overflow: hidden; border: 1px solid var(--border);
  background: var(--panel2);
}
.progress-bar.mini { height: 14px; margin: 0; border-radius: 4px; }
.progress-bar .seg {
  display: flex; align-items: center; justify-content: center;
  font-size: 0.78rem; font-weight: 700; color: #0d1117;
  min-width: 2px; transition: flex 0.2s ease;
}
.progress-bar.mini .seg { font-size: 0; }

.seg-built      { background: #35c98f; }
.seg-partial    { background: #ffcf3d; }
.seg-blocked    { background: #ff6b6b; }
.seg-notstarted { background: #3a4457; color: #8b98ac; }

@media (prefers-color-scheme: light) {
  .seg-built      { background: #1aa473; }
  .seg-partial    { background: #f0b400; }
  .seg-blocked    { background: #e0483f; }
  .seg-notstarted { background: #c9d0dd; color: #5b6679; }
  .progress-bar .seg { color: #fff; }
  .seg-partial, .seg-notstarted { color: #16202e; }
}

.progress-legend { display: flex; flex-wrap: wrap; gap: 8px 20px; font-size: 0.88rem; }
.progress-legend .lg { display: flex; align-items: center; gap: 7px; color: var(--muted); }
.progress-legend .lg b { color: var(--text); }
.progress-legend .lg i {
  width: 13px; height: 13px; border-radius: 3px; display: inline-block; flex: none;
}
.progress-legend .pc { opacity: 0.75; }

/* Sortable column headers (the dashboard's per-chapter progress breakdown).
   The affordance has to be visible without being loud: a pointer cursor, a
   hover tint, and an arrow only on the column actually in use. Keyboard
   reachable, because a th with a click handler and no tabindex is a control
   only a mouse can use. */
th.sortable { cursor: pointer; user-select: none; }
th.sortable:hover { background: var(--panel2); }
th.sortable:focus-visible { outline: 2px solid var(--info); outline-offset: -2px; }
th.sortable.active { color: var(--text); }

/* Phase bands on the schedule page. Five steps from "now" to "launch", chosen
   to read as a progression rather than as five unrelated categories — and
   distinguishable by lightness as well as hue, per the project's standing
   preference for pronouncedly distinct scales over colour-blind engineering. */
.seg-phase1 { background: #6ee7c0; }
.seg-phase2 { background: #35c98f; }
.seg-phase3 { background: #2a9d8f; }
.seg-phase4 { background: #b98bff; }
.seg-phase5 { background: #ffcf3d; }

@media (prefers-color-scheme: light) {
  .seg-phase1 { background: #41b990; }
  .seg-phase2 { background: #1aa473; }
  .seg-phase3 { background: #157a6e; }
  .seg-phase4 { background: #7b3fd4; }
  .seg-phase5 { background: #d99e00; }
}
