/*
 * The app's one stylesheet (docs/design/report.md §9): its type, colour
 * tokens for both themes, then each page's components in the order the
 * redesign built them. One typeface in two weights, ink on paper in light
 * and white on the night board in dark, and colour only where a sailing's
 * status is.
 */

/* ---------- Type: Overpass, 400 and 700, Latin ----------
   The open digitisation of Highway Gothic, the lettering on every road sign
   on the drive to the ferry. Fontsource's Latin subsets of the OFL release
   (app/assets/fonts/OFL.txt), preloaded by the layout. Each face covers
   Fontsource's Latin range less U+00B7: Overpass's middle dot has no width,
   so "Digital · © 2026" would close up round it. The fallback draws it. */

@font-face {
  font-family: "Overpass";
  src: url("/assets/overpass-latin-400-ccade6a6.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00B6, U+00B8-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Overpass";
  src: url("/assets/overpass-latin-700-d2f336a4.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00B6, U+00B8-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Arial fitted to Overpass's width (99%, measured) and line box (ascent
   0.883 em, descent 0.383 em, no gap), so the swap doesn't shift the layout. */
@font-face {
  font-family: "Overpass Fallback";
  src: local("Arial"), local("ArialMT"), local("Helvetica"), local("Liberation Sans");
  size-adjust: 99%;
  ascent-override: 89.2%;
  descent-override: 38.7%;
  line-gap-override: 0%;
}

:root {
  --font: "Overpass", "Overpass Fallback", system-ui, sans-serif;
  --regular: 400;
  --bold: 700;

  /* The type scale from the heading pass: a major third (1.25), rounded to
     whole pixels, with the next sailing's time on steps of its own, sized
     like a sign. Each heading rank is a step: page title (title, bold) >
     section title (row, bold) > group name = field label (body, bold) > list
     items (body). Nothing below a heading is larger than it. */
  --t-small: 0.9375rem;  /* 15: notes, diagram labels */
  --t-body: 1.125rem;    /* 18: body */
  --t-row: 1.375rem;     /* 22: section titles, board times, status bands */
  --t-title: 1.75rem;    /* 28: page titles, the next sailing's lead */
  --t-display: 2.375rem; /* 38: desktop titles, the vessel's name */
  --t-sign: clamp(5.5rem, 27vw, 10rem);        /* 88 to 160: the next sailing's clock time */
  --t-countdown: clamp(4.25rem, 20vw, 7.5rem); /* 68 to 120: the countdown, a notch below */

  --leading: 1.35;
  --leading-tight: 1.1;

  /* Space on an 8 px grid, with 4 for hairline adjustments. */
  --s-1: 0.25rem;
  --s0: 0.5rem;
  --s1: 0.75rem;
  --s2: 1rem;
  --s3: 1.5rem;
  --s4: 2rem;
  --s5: 3rem;

  --tap: 2.75rem; /* 44 px */

  /* Light: a printed timetable in the sun. Pure black on pure white, so
     nothing washes out; status colours as solid fills with an ink edge. */
  --paper: #ffffff;
  --raised: #ffffff;
  --ink: #000000;
  --ink-2: #4a4a4a;
  --rule: #767676;
  --placeholder: #767676; /* 4.54:1, lighter than --ink-2 so it reads as a placeholder */
  --rule-soft: #d4d4d4;
  --fill: #f0f0f0;
  --on-ink: #ffffff;

  /* Late in two steps (severely_late_minutes in config/wsf.yml): one even
     outlined yellow band, then one solid band. In light, yellow on white is
     only 1.45:1, so the outline is framed in ink hairlines; the words and
     the clock carry the status. */
  --late-accent: #ffd200;
  --late-outline: #ffd200;
  --late-outline-edge: #000000;
  --late-minor-ink: #000000;
  --late-severe-fill: #ffd200;
  --late-severe-ink: #000000;
  --late-edge: #000000;
  --late-text: #000000;
  --cancel-fill: #c8001e;
  --cancel-ink: #ffffff;
  --cancel-edge: #000000;
  --cancel-strike: #c8001e; /* a struck-through cancelled time, 6.04:1 */
  --on-time: #00703c;
  --stale-fill: #e6e6e6;
  --stale-ink: #000000;
  --focus: #000000;
  --backdrop: rgb(0 0 0 / 0.45);

  color-scheme: light;
}

/* Dark: the station board at 5 am. White on deep Sound blue, the board's
   material rather than a colour; late lit in yellow, cancelled in red. It
   follows the system unless the rider chose light, and the same tokens
   apply when they chose dark (data-theme, from Theming#theme). */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #0a2240;
    --raised: #10305a;
    --ink: #ffffff;
    --ink-2: #a9b8c2;
    --rule: #6e86a8;
    --placeholder: #a9b8c2;
    --rule-soft: #1f3f6a;
    --fill: #10305a;
    --on-ink: #0a2240;

    --late-accent: #ffd200;
    /* Dark's outlined step is dimmer and its text plain white, so it's
       clearly unlike the solid lit-yellow step (4.7:1 on the blue). */
    --late-outline: #a68a00;
    --late-outline-edge: transparent;
    --late-minor-ink: #ffffff;
    --late-severe-fill: #ffd200;
    --late-severe-ink: #0a2240;
    --late-edge: #ffd200;
    --late-text: #ffd200;
    --cancel-fill: #d4202e;
    --cancel-ink: #ffffff;
    --cancel-edge: #d4202e;
    --cancel-strike: #ff9d94;
    --on-time: #5edc8f;
    --stale-fill: #24364f;
    --stale-ink: #ffffff;
    --focus: #ffd200;
    --backdrop: rgb(2 10 22 / 0.7);

    color-scheme: dark;
  }
}

:root[data-theme="dark"] {
  --paper: #0a2240;
  --raised: #10305a;
  --ink: #ffffff;
  --ink-2: #a9b8c2;
  --rule: #6e86a8;
  --placeholder: #a9b8c2;
  --rule-soft: #1f3f6a;
  --fill: #10305a;
  --on-ink: #0a2240;

  --late-accent: #ffd200;
  --late-outline: #a68a00;
  --late-outline-edge: transparent;
  --late-minor-ink: #ffffff;
  --late-severe-fill: #ffd200;
  --late-severe-ink: #0a2240;
  --late-edge: #ffd200;
  --late-text: #ffd200;
  --cancel-fill: #d4202e;
  --cancel-ink: #ffffff;
  --cancel-edge: #d4202e;
  --cancel-strike: #ff9d94;
  --on-time: #5edc8f;
  --stale-fill: #24364f;
  --stale-ink: #ffffff;
  --focus: #ffd200;
  --backdrop: rgb(2 10 22 / 0.7);

  color-scheme: dark;
}

/* ---------- Page frame ---------- */

:root {
  --gutter: 16px;
  --max-width: 40rem;
}

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

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body { margin: 0; }

.page {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: var(--gutter);
  padding-left: max(var(--gutter), env(safe-area-inset-left));
  padding-right: max(var(--gutter), env(safe-area-inset-right));
}

/* Running text, such as About. */
.prose h2 { margin: 1.5rem 0 0.25rem; }
.prose p { margin: 0 0 0.75rem; }

.page-title { margin: 0 0 0.5rem; }
.section-title { margin: 1.25rem 0 0.5rem; }

.button {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  padding: 0 1rem;
  border: 1px solid var(--ink);
  border-radius: 0.5rem;
  background: var(--ink);
  color: var(--raised);
  font: inherit;
  font-weight: var(--bold);
  text-decoration: none;
  cursor: pointer;
}

.button--quiet {
  min-height: 2.25rem;
  padding: 0 0.75rem;
  background: transparent;
  color: var(--ink);
}

.picker {
  display: grid;
  gap: 0.5rem;
  margin-bottom: 1.25rem;
}

/* ---------- Map charts: the map pages' panels and the sailings screen's
   route chart, drawn alike (maps/_chart). Water as the page, land as a fill,
   ink lines and terminals. Each page tunes them in its own section. ---------- */

.map-chart {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--rule-soft);
  border-radius: 12px;
  font-family: var(--font);
}

.map-chart__water { fill: var(--paper); }
.map-chart__land { fill: var(--fill); stroke: var(--rule); stroke-width: 0.75; stroke-linejoin: round; }

.map-chart__line { fill: none; stroke: var(--ink); stroke-width: 1.5; stroke-linejoin: round; opacity: 0.55; }
.map-chart__line--faint { stroke: var(--ink-2); stroke-width: 1; stroke-dasharray: 2 4; opacity: 0.5; }

.map-chart__terminal rect { fill: var(--raised); stroke: var(--ink); stroke-width: 1.5; }
.map-chart__terminal text { font-size: 12px; font-weight: 600; fill: var(--ink); paint-order: stroke; stroke: var(--fill); stroke-width: 3px; }
.map-chart__terminal--other rect { stroke: var(--ink-2); }
.map-chart__terminal--other text { font-weight: 400; fill: var(--ink-2); }

/* A vessel under way is an arrow on its heading; one at the dock is a dot. */
.map-chart__vessel path,
.map-chart__vessel circle { fill: var(--ink); stroke: var(--raised); stroke-width: 1; }
.map-chart__vessel--docked circle { fill: var(--ink-2); }
.map-chart__vessel--next path,
.map-chart__vessel--next circle { fill: var(--ink); }
.map-chart__vessel text { font-size: 10px; font-weight: 600; fill: var(--ink); paint-order: stroke; stroke: var(--raised); stroke-width: 3px; }
.map-chart__vessel--next text { font-weight: 800; }

/* A wide, invisible stroke under each route line, so it's easy to tap. */
.region-map__hit { fill: none; stroke: transparent; stroke-width: 14; }
.region-map__route:hover .map-chart__line,
.region-map__route:focus .map-chart__line { stroke-width: 3; opacity: 1; }

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

html { background: var(--paper); }

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--t-body);
  font-weight: var(--regular);
  line-height: var(--leading);
  font-variant-numeric: tabular-nums;
  font-kerning: normal;
  -webkit-font-smoothing: antialiased;
}

strong, b { font-weight: var(--bold); }

a { color: inherit; text-decoration-thickness: 2px; text-underline-offset: 0.18em; }

:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

/* A button given focus back by a dialog closed with a tap or click (the modal controller). */
[data-quiet-focus]:focus-visible { outline: none; }

.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------- Symbols: the sprite and its icons ---------- */

.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.icon {
  width: 1.15em;
  height: 1.15em;
  flex: none;
  vertical-align: -0.2em;
}

/* ---------- Headings: the ranked scale ---------- */

.page-title {
  font-size: var(--t-title);
  font-weight: var(--bold);
  line-height: var(--leading-tight);
  letter-spacing: -0.01em;
}

.section-title, .prose h2 {
  font-size: var(--t-row);
  font-weight: var(--bold);
  line-height: 1.2;
}

.field-label { display: block; font-size: var(--t-body); font-weight: var(--bold); }

/* ---------- Masthead: the wordmark, nothing else ---------- */

.masthead {
  display: flex;
  align-items: center;
  max-width: var(--max-width);
  margin: 0 auto;
  padding: calc(var(--s0) + env(safe-area-inset-top)) var(--gutter) var(--s0);
}

.masthead__name {
  display: flex;
  align-items: center;
  min-height: var(--tap);
  font-weight: var(--bold);
  text-decoration: none;
  line-height: 1.2;
}

/* ---------- Footer: unofficial, the links, who made it ---------- */

.footer {
  display: grid;
  gap: var(--s1);
  max-width: var(--max-width);
  margin: var(--s5) auto 0;
  padding: var(--s2) var(--gutter) calc(var(--s3) + env(safe-area-inset-bottom));
  border-top: 2px solid var(--ink);
  font-size: var(--t-small);
}

.footer p { margin: 0; }
.footer__unofficial { max-width: 40em; color: var(--ink-2); }
.footer__unofficial strong { color: var(--ink); }
.footer__links { display: flex; flex-wrap: wrap; column-gap: var(--s3); margin: 0; padding: 0; list-style: none; }
.footer__link { display: inline-flex; align-items: center; min-height: var(--tap); font-weight: var(--bold); }
.footer__inline-link { display: inline-flex; align-items: center; min-height: var(--tap); }
.footer__signature { color: var(--ink-2); }

/* ---------- Toggletip: an ⓘ that opens a small popover ----------
   Its 44 px target overhangs the line it sits in, so the line keeps its
   height. The bubble sits at the foot of the screen, inside the gutters. */

.toggletip { display: inline-flex; vertical-align: middle; }

.toggletip__button {
  display: inline-grid;
  place-items: center;
  width: var(--tap);
  height: var(--tap);
  margin: calc(-1 * var(--s1)) calc(-1 * var(--s1)) calc(-1 * var(--s1)) calc(-1 * var(--s-1));
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink-2);
  font: inherit;
  cursor: pointer;
}

.toggletip__button:hover { color: var(--ink); }

.toggletip__bubble {
  position: fixed;
  inset: auto var(--gutter) calc(env(safe-area-inset-bottom) + var(--s2));
  max-width: 24rem;
  margin: 0 auto;
  padding: var(--s1) var(--s2);
  border: 2px solid var(--ink);
  background: var(--raised);
  color: var(--ink);
  font-size: var(--t-small);
  font-weight: var(--regular);
  line-height: var(--leading);
}

/* ---------- About: the theme choice ---------- */

/* Three equal choices on one row at any width, each its icon over its name,
   so none wraps onto a second row at 390 px. */
.theme-choice { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s0); max-width: 26rem; margin-top: var(--s1); }

.theme-choice__option {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s-1);
  min-height: var(--tap);
  padding: var(--s0);
  border: 2px solid var(--rule);
  background: none;
  color: inherit;
  font: inherit;
  font-weight: var(--bold);
  line-height: 1.2;
  text-align: center;
  cursor: pointer;
}

.theme-choice__option[aria-pressed="true"] { border-color: var(--ink); background: var(--ink); color: var(--paper); }

/* ---------- Home: the crossing and its next sailing (step 3) ----------
   One column, laid out by gaps rather than margins: the title, the next
   sailing in the walkthrough's order (sentence, band, departure line, line,
   working), WSF's alerts, Later, then the link to the crossing's page. */

.screen { display: grid; gap: var(--s3); padding-top: var(--s1); }
.screen :is(h1, h2, p, ol) { margin: 0; }

/* The crossing as the title, on two lines on every route, however long its
   names: a smaller "Bainbridge to" above a larger destination, with Flip
   centred beside both. Under it, how old the live data is. */
.crossing-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: var(--s2);
  padding-bottom: var(--s1);
  border-bottom: 2px solid var(--ink);
}

.crossing-head__title { font-size: var(--t-title); font-weight: var(--bold); line-height: var(--leading-tight); letter-spacing: -0.01em; }
.crossing-head__from { display: block; font-size: var(--t-body); font-weight: var(--regular); line-height: 1.3; letter-spacing: 0; }
.crossing-head__to { display: block; }
.crossing-head__flip { grid-row: 1; grid-column: 2; margin: 0; }
.crossing-head__updated { grid-column: 1 / -1; margin-top: var(--s0); }

.flip-button {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  min-height: var(--tap);
  padding: 0 var(--s1);
  border: 2px solid var(--ink);
  background: none;
  color: var(--ink);
  font: inherit;
  font-weight: var(--bold);
  text-decoration: none;
  cursor: pointer;
}

.flip-button:active { background: var(--ink); color: var(--paper); }

/* How old the live data is, under the title on every page. */
.updated__age { font-size: var(--t-small); color: var(--ink-2); }

.updated__stale {
  display: flex;
  gap: var(--s0);
  align-items: flex-start;
  margin-top: var(--s0);
  padding: var(--s0) var(--s1);
  background: var(--stale-fill);
  color: var(--stale-ink);
  font-size: var(--t-small);
}

.updated__stale[hidden] { display: none; }
.updated__stale .icon { margin-top: 0.1em; }

/* The next sailing. */
.next { display: grid; gap: var(--s1); }

.next__lead {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 0.3em;
  font-size: var(--t-title);
  font-weight: var(--regular);
  line-height: var(--leading-tight);
}

/* The vessel's name, bold, and underlined once it opens its sheet (.vessel-name, step 7). */
.next__vessel { font-weight: var(--bold); }

/* The clock time, at sign size, on a line of its own. */
.next__time {
  flex-basis: 100%;
  margin-top: var(--s0);
  font-size: var(--t-sign);
  font-weight: var(--bold);
  line-height: 0.86;
  letter-spacing: -0.035em;
}

/* The countdown leads a notch below the sign size of a clock time. */
.next__time--countdown { font-size: var(--t-countdown); letter-spacing: -0.03em; white-space: nowrap; }
/* The space before the meridiem would be sign-sized too; close it up. */
.next__time.time { word-spacing: -0.17em; }
.next__time .time__meridiem { font-size: 0.3em; letter-spacing: 0; word-spacing: normal; }

.next-note { font-size: var(--t-body); }
.next-note + .next { margin-top: calc(-1 * var(--s2)); }

.next__where { max-width: 34em; }

/* A status as a band across the column, its words beside its icon. */
.status-band {
  display: flex;
  align-items: flex-start;
  gap: var(--s1);
  padding: var(--s1) var(--s2) var(--s1) var(--s1);
  font-size: var(--t-row);
  line-height: 1.25;
}

.status-band .icon { margin-top: 0.08em; }

/* Late, under severely_late_minutes: one even yellow outline, framed in ink
   on both sides in light so it holds on white; in dark, a dimmer outline
   with plain text, unlike the solid step. */
.status-band--late {
  border: 4px solid var(--late-outline);
  box-shadow: 0 0 0 1.5px var(--late-outline-edge), inset 0 0 0 1.5px var(--late-outline-edge);
  color: var(--late-minor-ink);
}

/* Late by severely_late_minutes or more: the solid band. */
.status-band--late-severe {
  border: 3px solid var(--late-edge);
  background: var(--late-severe-fill);
  color: var(--late-severe-ink);
}

.status-band--cancelled {
  border-left: 8px solid var(--cancel-edge);
  background: var(--cancel-fill);
  color: var(--cancel-ink);
  font-size: var(--t-body);
}

/* Stale: one grey statement, never a status colour. */
.status-band--stale {
  border-left: 10px solid var(--rule);
  background: var(--stale-fill);
  color: var(--stale-ink);
  font-size: var(--t-body);
}

.status-band--cancelled strong, .status-band--stale strong { display: block; font-size: var(--t-row); }

/* On time is quiet: a check and the words, no band. */
.status-line { display: flex; align-items: center; gap: var(--s0); font-size: var(--t-row); }
.status-line--on-time .icon { width: 1.3em; height: 1.3em; color: var(--on-time); }
.status-line--quiet { font-size: var(--t-body); color: var(--ink-2); }

/* Data that went stale since the render: the bands go grey until the next
   refresh says so in words. */
.page:has(.freshness--stale) :is(.status-band--late, .status-band--late-severe) {
  border-color: var(--rule);
  box-shadow: none;
  background: var(--stale-fill);
  color: var(--stale-ink);
}

.page:has(.freshness--stale) .status-line--on-time .icon { color: inherit; }

/* The departure line: "Leaves about 6:55 pm · arrives about 7:30 pm · 181
   spaces". Space always sits last, in the same place. The dot ends the part
   before it, so a wrapped line never starts with one, and its empty alt text
   keeps screen readers from reading it as "middle dot". */
.departure-line { display: flex; flex-wrap: wrap; align-items: center; column-gap: 0.4em; }
.departure-line > span:not(:last-child)::after { content: "·"; content: "·" / ""; margin-inline-start: 0.4em; color: var(--ink-2); }
.departure-line__space { display: inline-flex; align-items: center; gap: 0.1em; font-weight: var(--bold); }

/* Drive-up space: a three-step meter on the words' x-height, and the words. */
.space { display: inline-flex; align-items: center; gap: 0.35em; }
.space__meter { display: inline-flex; align-items: flex-end; gap: 2px; height: 0.75em; }
.space__meter span { width: 5px; height: 100%; border: 1.5px solid currentColor; }
.space__meter .is-on { background: currentColor; }
.space--full, .space--nearly-full { color: var(--ink); font-weight: var(--bold); }

/* Our estimate's working, right under the line it explains. */
.next__working { max-width: 36em; color: var(--ink-2); font-size: var(--t-small); }
.next__working strong { color: var(--ink); }

/* The line: from where the vessel is, on the left, to the rider, on the
   right, by live position only. Placed by --pos (the vessel) and each
   call's --at, from 0 to 1. The water still to cross is strong ink; behind
   the vessel, its wake, thin. The end labels sit inside the track. */
.line { --hull-w: 2.5rem; max-width: 36rem; margin: var(--s0) 0 0; }

.line__track { position: relative; height: 1.75rem; margin: 0 calc(var(--hull-w) / 2) 1.5rem; }

.line__behind, .line__ahead { position: absolute; top: 50%; }
.line__behind { left: 0; width: calc(var(--pos) * 100%); height: 2px; margin-top: -1px; background: var(--rule); }
.line__ahead { left: calc(var(--pos) * 100%); right: 0; height: 4px; margin-top: -2px; background: var(--ink); }

.line__call {
  position: absolute;
  top: 50%;
  left: calc(var(--at) * 100%);
  width: 4px;
  height: 14px;
  transform: translate(-50%, -50%);
  background: var(--ink);
}

.line__call--here { width: 6px; height: 1.5rem; }

.line__label {
  position: absolute;
  top: calc(100% + 0.2rem);
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  font-size: var(--t-small);
  line-height: 1.2;
  color: var(--ink-2);
}

.line__call--start .line__label { left: 0; transform: none; }
.line__call--here .line__label { top: calc(100% + 0.05rem); right: 0; left: auto; transform: none; color: var(--ink); font-weight: var(--bold); }

.hull { display: block; width: var(--hull-w); height: calc(var(--hull-w) / 2); overflow: visible; }
.hull__body { fill: var(--ink); stroke: var(--paper); stroke-width: 1.5; }
.hull__deck { fill: none; stroke: var(--paper); stroke-width: 1.5; }

.line__vessel {
  position: absolute;
  z-index: 2;
  top: 50%;
  left: calc(var(--pos) * 100%);
  transform: translate(-50%, -50%);
  transition: left 1s linear;
}

/* Late on fresh data: the vessel tinted as the band is. Outlined step, a
   yellow ring (framed in ink in light); solid step, a yellow hull edged in
   ink. The band and the caption say it in words. */
.line--late .hull__body {
  stroke: var(--late-outline);
  stroke-width: 3;
  paint-order: stroke;
  filter: drop-shadow(0 0 0.75px var(--late-outline-edge)) drop-shadow(0 0 0.75px var(--late-outline-edge));
}

.line--late-severe .hull__body { fill: var(--late-severe-fill); stroke: var(--late-edge); stroke-width: 1.75; }
.line--late-severe .hull__deck { stroke: var(--late-edge); }

/* Stale: hollow where it was last seen, nothing moving. */
.line--stale .line__ahead { background: var(--rule); }
.line--stale .hull__body { fill: var(--paper); stroke: var(--ink-2); stroke-width: 2; stroke-dasharray: 3 2; }
.line--stale .hull__deck { stroke: var(--ink-2); }

.line__note { margin: 0; font-size: var(--t-small); line-height: 1.3; color: var(--ink-2); }

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

/* WSF's alerts: WSF's words, as a neutral band, folded. */
.alert-band { border: 2px solid var(--ink); border-left-width: 10px; }

.alert-band summary {
  display: flex;
  gap: var(--s1);
  align-items: flex-start;
  min-height: var(--tap);
  padding: var(--s1);
  cursor: pointer;
  list-style: none;
}

.alert-band summary::-webkit-details-marker { display: none; }
.alert-band summary .icon { margin-top: 0.15em; }
.alert-band__title { font-weight: var(--bold); }
.alert-band__source { display: block; font-size: var(--t-small); font-weight: var(--regular); color: var(--ink-2); }
.alert-band__chevron { margin-left: auto; }
.alert-band[open] .alert-band__chevron { transform: rotate(90deg); }
.alert-band__body { max-width: 40em; padding: 0 var(--s2) var(--s2) calc(1.15em + var(--s1) * 2); }
.alert-band__body p { margin: 0 0 var(--s0); }

/* Later: the board's rows, on one baseline. */
.later-sailings { display: grid; gap: var(--s0); }

.sailing-rows { padding: 0; border-bottom: 1px solid var(--rule-soft); list-style: none; }

.row {
  display: grid;
  grid-template-columns: 5.25rem minmax(0, 1fr) minmax(0, max-content);
  column-gap: var(--s1);
  align-items: baseline;
  padding: var(--s1) 0;
  border-top: 1px solid var(--rule-soft);
}

/* Right-aligned, so every time ends on one edge. */
.row__time { font-size: var(--t-row); font-weight: var(--bold); line-height: 1.2; text-align: right; white-space: nowrap; }
.row__time .time__meridiem { font-size: 0.66em; }
.row__time .time__day { display: block; font-size: var(--t-small); font-weight: var(--regular); }

/* An estimate changed the time: the scheduled one struck, small and grey,
   above the estimate. The row aligns on the estimate. */
.row__was { display: block; font-size: var(--t-small); font-weight: var(--regular); line-height: 1.1; color: var(--ink-2); }
.row--changed .row__time { align-self: last baseline; }

.row__vessel { min-width: 0; font-weight: var(--bold); }
.row__notes { display: flex; flex-wrap: wrap; grid-column: 2 / -1; gap: var(--s-1) var(--s2); margin-top: var(--s-1); }
.row__note { font-size: var(--t-small); color: var(--ink-2); }
.row--cancelled .row__time .time__figures { text-decoration: line-through; text-decoration-color: var(--cancel-strike); text-decoration-thickness: 2px; }
.row--departed :is(.row__time, .row__vessel) { color: var(--ink-2); }

.status {
  display: inline-flex;
  align-items: center;
  gap: 0.3em;
  padding: 0.05rem 0.4rem;
  font-weight: var(--bold);
  line-height: 1.3;
  color: var(--ink-2);
}

/* The board's two late steps, as the band's: outlined, then solid. */
.status--late { padding: 0.15rem 0.55rem; box-shadow: inset 0 0 0 1.5px var(--late-outline-edge), inset 0 0 0 4px var(--late-outline); color: var(--late-minor-ink); }
.status--late-severe { background: var(--late-severe-fill); box-shadow: inset 0 0 0 2px var(--late-edge); color: var(--late-severe-ink); }
.status--cancelled { background: var(--cancel-fill); color: var(--cancel-ink); }
.status--on-time { color: var(--ink); }
.status--on-time .icon { color: var(--on-time); }
.status--departed, .status--scheduled { font-weight: var(--regular); }
/* A long status ("Scheduled · may be late") wraps under itself rather than crowd the vessel. */
.row__status { max-width: 9rem; justify-self: end; text-align: right; }
/* The one status that wraps takes both of the row's lines, so the notes stay
   up under the vessel rather than drop below the wrap (#90). */
.row--may-be-late .row__status { grid-row: 1 / span 2; grid-column: 3; }
.row--may-be-late .row__notes { grid-column: 2; }

/* The crossing's full page: a plain underlined text link, apart from Later. */
.plain-link { display: inline-flex; align-items: center; min-height: var(--tap); font-weight: var(--bold); }

.home-actions { display: flex; flex-wrap: wrap; gap: var(--s0); align-items: center; margin-top: var(--s4); }
.home-actions form { margin: 0; }

/* ---------- The bottom bar: saved routes, then Edit (step 4) ----------
   Fixed to the foot on a phone, in thumb reach. The page's foot leaves room
   for it, so it never covers the footer, and toggletips open above it. */

.has-tab-bar { --tab-bar-height: 3.75rem; }
.has-tab-bar .footer { margin-bottom: calc(var(--tab-bar-height) + env(safe-area-inset-bottom)); }
.has-tab-bar .toggletip__bubble { bottom: calc(var(--tab-bar-height) + env(safe-area-inset-bottom) + var(--s2)); }

.tab-bar {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 10;
  padding-bottom: env(safe-area-inset-bottom);
  background: var(--paper);
  border-top: 2px solid var(--ink);
}

/* Past about three tabs plus Edit, the bar scrolls sideways. */
.tab-bar__tabs {
  display: flex;
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0;
  list-style: none;
  overflow-x: auto;
  scrollbar-width: none;
}

/* Tabs share the width evenly, whatever they show: each is a size container,
   so its width never follows its content (#85). */
.tab-bar__tabs > li { flex: 1 1 6.5rem; min-width: 0; container-type: inline-size; }
.tab-bar__tabs > .tab-bar__edit-item { flex: 0 0 auto; container-type: normal; }

.tab-bar__tab {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  gap: 2px;
  height: var(--tab-bar-height);
  padding: 0 var(--s-1) 0 var(--s1);
  border-right: 1px solid var(--rule-soft);
  color: var(--ink);
  line-height: 1.15;
  text-decoration: none;
}

.tab-bar__tab:focus-visible { outline-offset: -3px; }
.tab-bar__tab[aria-current="page"] { background: var(--ink); color: var(--paper); }

.tab-bar__name {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--t-small);
  font-weight: var(--bold);
}

/* Two tabs from one terminal name their destinations, a touch smaller so
   "to Bainbridge" fits a tab at 390 px. */
.tab-bar__name--to { font-size: 0.875rem; letter-spacing: -0.01em; }
.tab-bar__tab:has(.tab-bar__name--to) { padding-left: var(--s0); }

.tab-bar__next { display: inline-flex; align-items: center; gap: 0.25em; font-size: var(--t-body); font-weight: var(--bold); line-height: 1.2; }
.tab-bar__next .icon { width: 1em; height: 1em; }
.tab-bar__next--late, .tab-bar__next--late-severe { color: var(--late-text); }
.tab-bar__next--late-severe .icon { color: var(--late-severe-ink); background: var(--late-severe-fill); border-radius: 50%; }
.tab-bar__tab[aria-current="page"] :is(.tab-bar__next--late, .tab-bar__next--late-severe) { color: inherit; }

/* The two faces, time and countdown, stacked in one cell, which the bar
   flips between (tab-flip) by turning each about its horizontal axis. The
   cell is as wide as the wider face, in tabular figures, so nothing jitters. */
.tab-bar__faces { display: inline-grid; perspective: 20rem; font-variant-numeric: tabular-nums; }
.tab-bar__face { grid-area: 1 / 1; white-space: nowrap; backface-visibility: hidden; transition: transform 400ms cubic-bezier(0.2, 0.7, 0.3, 1), opacity 400ms linear; }
.tab-bar__face--countdown { transform: rotateX(-90deg); opacity: 0; }
.tab-bar.is-counting .tab-bar__face--time { transform: rotateX(90deg); opacity: 0; }
.tab-bar.is-counting .tab-bar__face--countdown { transform: none; opacity: 1; }

/* Under reduced motion, the countdown only, never flipping. */
@media (prefers-reduced-motion: reduce) {
  .tab-bar__face { transition: none; }
  .tab-bar__face--time { visibility: hidden; }
  .tab-bar__face--countdown { transform: none; opacity: 1; }
}

/* A cancelled sailing ahead of the next running one: ⊘ and its struck time
   in a small red chip before it, red on any tab, selected or not, so the
   running time reads plainly as the next. */
.tab-bar__cancelled {
  display: inline-flex;
  align-items: center;
  gap: 1px;
  padding: 0 0.2rem;
  background: var(--cancel-fill);
  color: var(--cancel-ink);
  font-size: 0.8125rem;
  font-weight: var(--regular);
  line-height: 1.35;
}
.tab-bar__cancelled s { text-decoration-thickness: 1.5px; }
.tab-bar__next .tab-bar__cancelled .icon { width: 0.9em; height: 0.9em; }
/* A tab too narrow for the struck time beside its own, as with three tabs at
   390 px, keeps just the ⊘. Screen readers still hear the time (#85). */
@container (width < 9rem) {
  .tab-bar__cancelled { padding: 0 0.15rem; }
  .tab-bar__cancelled s { display: none; }
}

/* Without times, where each tab goes. */
.tab-bar__to { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--t-small); line-height: 1.1; color: var(--ink-2); }
.tab-bar__tab[aria-current="page"] .tab-bar__to { color: inherit; }

.tab-bar__tab--edit { align-items: center; min-width: 4rem; padding: 0 var(--s2); border-right: 0; border-left: 2px solid var(--ink); }
.tab-bar__tab--edit .icon { width: 1.3em; height: 1.3em; }

/* On a desk, the bar sits under the masthead, not at the foot. It follows
   the page's content in the source, for the keyboard and screen readers, so
   the page is a column that shows it second. */
@media (min-width: 60rem) {
  .has-tab-bar { display: flex; flex-direction: column; }
  .has-tab-bar > :is(.masthead, .tab-bar, .page, .footer) { width: 100%; }
  .has-tab-bar > .masthead { order: -2; }
  .has-tab-bar > .tab-bar { order: -1; }
  .has-tab-bar .footer { margin-bottom: 0; }
  .has-tab-bar .toggletip__bubble { bottom: calc(env(safe-area-inset-bottom) + var(--s2)); }
  .tab-bar { position: static; max-width: var(--max-width); margin: 0 auto; padding: 0 var(--gutter); border-top: 0; background: none; }
  .tab-bar__tabs { border-top: 2px solid var(--ink); border-bottom: 2px solid var(--ink); }
  .tab-bar__tabs > li { flex: 0 1 12rem; }
}

/* ---------- The sailings screen: the crossing's day (step 5) ----------
   One column on a phone: the title, fresh alerts, the sailings, the
   terminal, the chart, earlier alerts. On a desk, two: the sailings left,
   the rest right. The list scrolls with the page, never inside it. */

.screen__grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s3); }
.screen__main, .screen__side { display: grid; gap: var(--s3); align-content: start; min-width: 0; }
.screen__side > section { display: grid; gap: var(--s2); }

.sailings { display: grid; gap: var(--s1); }
#sailings-title { margin-top: var(--s2); }

.day-note { font-size: var(--t-body); padding-top: var(--s0); }

/* Every fold has the one drawn chevron, pointing right when shut and
   turned a quarter when open, and the word for what a tap does. */
.fold > summary {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--s2);
  min-height: var(--tap);
  padding: var(--s0) 0;
  cursor: pointer;
  list-style: none;
}

.fold > summary::-webkit-details-marker { display: none; }
.fold__toggle { display: inline-flex; align-items: center; gap: var(--s-1); font-weight: var(--bold); text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 0.18em; }
.fold[open] > summary .chevron { transform: rotate(90deg); }
.fold[open] > summary .fold__show { display: none; }
/* A toggle's Show or Hide is drawn here, not written twice in the page, so
   its name has only the word that applies (#106). */
.fold__toggle--show-hide::after { content: "Show"; }
.fold[open] > summary .fold__toggle--show-hide::after { content: "Hide"; }

/* The past, folded above the day: a toggle, a hairline above and below. */
.earlier { border-top: 1px solid var(--rule-soft); }
.earlier > summary { color: var(--ink-2); }
.earlier .fold__toggle { color: var(--ink); }
.sailing-rows--earlier { border-bottom: 0; }

/* Load more: once open, its summary goes and the day just continues. */
.load-more { margin-top: calc(-1 * var(--s1)); }
.load-more[open] > summary { display: none; }
.load-more[open] { margin-top: calc(-1 * var(--s1) - 1px); }
.sailing-rows--rest .row:focus { outline-offset: -3px; }

/* The next sailing's "next" badge, beside its vessel on the row's baseline. */
.row__vessel-cell { min-width: 0; }
.row__flag { margin-left: 0.35em; padding: 0 0.3em; background: var(--ink); color: var(--paper); font-size: var(--t-small); font-weight: var(--bold); }
.row--departed .status { color: var(--ink-2); }

/* Data that went stale: what each sailing last heard, in grey. */
.page:has(.freshness--stale) .screen--sailings :is(.status--late, .status--late-severe, .status--on-time, .status--on-time .icon) {
  background: none;
  box-shadow: none;
  color: var(--ink-2);
}

/* At the terminal: cameras wrap in a grid, each a tap to zoom. */
.cameras { display: grid; grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr)); gap: var(--s2) var(--s1); margin: 0; padding: 0; list-style: none; }
.camera { min-width: 0; }
.camera__button { display: grid; gap: var(--s-1); width: 100%; padding: 0; border: 0; background: none; color: inherit; font: inherit; text-align: left; cursor: zoom-in; }
.camera__image { display: block; width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; border: 1px solid var(--rule-soft); border-radius: 0.5rem; background: var(--fill); }
.camera__title { font-size: var(--t-small); line-height: 1.25; }

.camera-view {
  width: min(48rem, calc(100% - 2 * var(--gutter)));
  max-width: none;
  max-height: 90svh;
  padding: var(--s1);
  border: 2px solid var(--ink);
  background: var(--raised);
  color: var(--ink);
}

.camera-view::backdrop { background: var(--backdrop); }
/* The image takes focus as the view opens, to be read first, but isn't a control, so it has no ring. */
.camera-view__image:focus { outline: none; }
.camera-view__image { display: block; width: 100%; height: auto; }
.camera-view__caption { margin: var(--s0) 0 0; padding-right: 3.5rem; font-size: var(--t-small); }

.camera-view__close {
  position: absolute;
  right: var(--s1);
  bottom: var(--s1);
  display: grid;
  place-items: center;
  width: var(--tap);
  height: var(--tap);
  border: 2px solid var(--ink);
  background: var(--raised);
  color: var(--ink);
  cursor: pointer;
}

/* WSF's wait-time notes, in full: they're short and pertinent. */
.wait-notes { display: grid; gap: var(--s0); max-width: 40em; }
.wait-notes :is(h3, p) { margin: 0; }
.wait-notes__title { font-size: var(--t-body); font-weight: var(--bold); }

/* The route chart, the system map's panel (maps/_chart) on the
   tokens, with its credit right under it. */
.screen__side > .route-chart { gap: var(--s1); }
.route-chart__credit { font-size: var(--t-small); color: var(--ink-2); }
.route-chart .map-chart { border: 1px solid var(--rule-soft); border-radius: 0; }
.route-chart .map-chart__water { fill: var(--paper); }
.route-chart .map-chart__land { fill: var(--fill); stroke: var(--rule); }
.route-chart .map-chart__line { stroke: var(--ink); stroke-width: 3; opacity: 1; }
.route-chart .map-chart__line--faint { stroke: var(--rule); stroke-width: 2; stroke-dasharray: 5 4; }
.route-chart .map-chart__terminal rect { fill: var(--paper); stroke: var(--ink); stroke-width: 2; }
.route-chart .map-chart__terminal--other rect { stroke: var(--ink-2); }
.route-chart :is(.map-chart__terminal, .map-chart__vessel) text { font-size: 13px; fill: var(--ink); stroke: var(--paper); }
.route-chart .map-chart__terminal--other text { fill: var(--ink-2); }
.route-chart :is(.map-chart__vessel path, .map-chart__vessel circle) { fill: var(--ink-2); stroke: var(--paper); stroke-width: 1.5; }
.route-chart :is(.map-chart__vessel--next path, .map-chart__vessel--next circle) { fill: var(--ink); }
.route-chart .map-chart__vessel--next text { font-weight: var(--bold); }

/* Earlier alerts, all in one folded row. */
.older-alerts { border-top: 1px solid var(--rule-soft); }
.older-alerts[open] { padding-bottom: var(--s1); }
.older-alerts .alert-band + .alert-band { margin-top: var(--s1); }

/* On a desk, the screen widens to two columns, and the masthead, bar and
   footer widen with it; rows open out to four columns: time, vessel, what
   else matters, status. */
@media (min-width: 60rem) {
  body:has(.screen--sailings) { --max-width: 66rem; }

  .screen--sailings .screen__grid { grid-template-columns: minmax(0, 8fr) minmax(0, 5fr); column-gap: var(--s5); }

  .screen--sailings .row { grid-template-columns: 5.25rem 10rem minmax(0, 1fr) minmax(8rem, max-content); column-gap: var(--s2); }
  .screen--sailings .row__status { grid-column: 4; grid-row: 1; max-width: none; }
  .screen--sailings .row__notes { grid-column: 3; grid-row: 1; margin-top: 0; flex-direction: column; gap: 0; }
  .screen--sailings .space { white-space: nowrap; }
}

/* ---------- The picker and Your crossings (step 6) ----------
   One picker for the first visit and Your crossings, on three levels told
   apart by size, weight and rule: region (22, bold, a full-width ink rule
   above) > group (18, regular, a chevron, a small grey subtitle) > route
   (18, bold, the + that saves). */

.route-arrow { white-space: nowrap; }
.route-arrow__icon { width: 1.05em; height: 1.05em; vertical-align: -0.2em; } /* its arrows span the cap height */

.page-head { display: grid; gap: var(--s1); }
.picker__intro { max-width: 32em; }

.picker__search {
  width: 100%;
  min-height: 3rem;
  margin-top: var(--s0);
  padding: 0 var(--s1);
  border: 2px solid var(--ink);
  border-radius: 0;
  background: var(--paper);
  color: var(--ink);
  font: inherit;
  font-size: var(--t-body);
}

/* Lighter than text in light mode, so it reads as a placeholder (4.5:1). */
.picker__search::placeholder { color: var(--placeholder); opacity: 1; }
.picker__empty { margin-top: var(--s2); }

.picker__region { margin-top: var(--s4); padding-top: var(--s1); border-top: 2px solid var(--ink); }
.picker__region[hidden] { display: none; }
.picker__region-name { margin: 0; padding-bottom: var(--s0); font-size: var(--t-row); font-weight: var(--bold); line-height: 1.2; }

.picker__group { border-top: 1px solid var(--rule-soft); }
.picker__group + .picker__group { border-bottom: 1px solid var(--rule-soft); }
.picker__group-head {
  display: grid;
  grid-template-columns: 1.25rem minmax(0, 1fr);
  column-gap: var(--s0);
  align-items: baseline;
  min-height: 3.25rem;
  padding: var(--s0) 0;
  cursor: pointer;
  list-style: none;
}
.picker__group-head::-webkit-details-marker { display: none; }
.picker__group-head .chevron { grid-row: 1; grid-column: 1; align-self: center; }
.picker__group[open] > .picker__group-head .chevron { transform: rotate(90deg); }
.picker__group-name { grid-column: 2; font-size: var(--t-body); font-weight: var(--regular); }
.picker__group-subtitle { grid-column: 2; font-size: var(--t-small); color: var(--ink-2); }
.picker__group .picker__list { border-bottom: 0; padding-left: calc(1.25rem + var(--s0)); }

.picker__list { margin: 0; padding: 0; list-style: none; border-bottom: 1px solid var(--rule-soft); }
.picker__row { display: flex; align-items: center; border-top: 1px solid var(--rule-soft); }
.picker__row[hidden] { display: none; }
.picker__form { flex: 1; margin: 0; }
.picker__route {
  display: flex;
  flex: 1;
  width: 100%;
  align-items: center;
  justify-content: space-between;
  gap: var(--s1);
  min-height: 3.25rem;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink);
  font: inherit;
  font-size: var(--t-body);
  font-weight: var(--bold);
  text-align: left;
  cursor: pointer;
}
.picker__route--saved { color: var(--ink-2); font-weight: var(--regular); cursor: default; }
.picker__saved { display: inline-flex; align-items: center; gap: var(--s-1); font-size: var(--t-small); font-weight: var(--bold); color: var(--ink); }
.picker__saved .icon { color: var(--on-time); }

/* Your crossings: the title with its toggletip, the saved routes, then the picker. */
.page-head__title { display: flex; align-items: center; gap: var(--s0); }
.add-crossing .picker { margin-top: var(--s2); }

.icon-button, .text-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--tap);
  min-height: var(--tap);
  padding: 0 var(--s0);
  border: 1px solid var(--ink);
  background: none;
  color: var(--ink);
  font: inherit;
  font-weight: var(--bold);
  cursor: pointer;
}
.text-button { padding: 0 var(--s1); border-width: 2px; }
.icon-button:active, .text-button:active { background: var(--ink); color: var(--paper); }

.saved-list { margin: 0; padding: 0; list-style: none; border-bottom: 1px solid var(--rule-soft); }
.saved-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s1);
  min-height: 3.25rem;
  padding: var(--s-1) 0;
  border-top: 1px solid var(--rule-soft);
  transition: opacity 600ms linear;
}
/* The same padding greyed or not, so a removed row's text lines up. */
.saved-row__route { padding: var(--s0) 0; font-weight: var(--bold); text-decoration: none; }
.saved-row__actions { display: flex; gap: var(--s-1); }
.saved-row__form, .saved-row__restore { display: contents; }
.saved-row__gap { width: var(--tap); }
.saved-row--removed { color: var(--ink-2); }
.saved-row--removed .saved-row__route { font-weight: var(--regular); text-decoration: line-through; }
.saved-row.is-going { opacity: 0; }

/* Reordering glides rows to their new places (each has its own
   view-transition-name); the rest of the page just swaps. Under reduced
   motion, rows jump, and a removed one goes at once. */
::view-transition-group(root) { animation-duration: 0s; }
::view-transition-group(*) { animation-duration: 260ms; animation-timing-function: cubic-bezier(0.2, 0, 0, 1); }

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
  .saved-row { transition: none; }
}

/* ---------- The vessel sheet and boat changes (step 7) ----------
   A vessel's name is a button that opens its sheet: underlined like a link,
   in the text around it, with a 44 px target. On a row, the target's height
   comes from padding that its margins take back, so the name keeps the row's
   baseline. */

.vessel-name {
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  font-weight: var(--bold);
  text-align: left;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
  cursor: pointer;
}

.next__vessel.vessel-name { min-height: var(--tap); text-decoration-thickness: 3px; text-underline-offset: 0.16em; }
.row__vessel.vessel-name { display: inline-block; margin: -0.7rem 0; padding: 0.7rem 0; line-height: 1.2; }
:is(.next__where, .line__note) .vessel-name { display: inline-block; margin: -0.75rem 0; padding: 0.75rem 0; }

/* A boat change, plainly: under the hero's sentence, and a note on each row. */
.swap-note { display: flex; gap: var(--s1); align-items: flex-start; padding: var(--s1); border: 2px solid var(--ink); }
.swap-note .icon { flex: none; margin-top: 0.1em; }
.row__note--swap { display: inline-flex; gap: var(--s-1); align-items: flex-start; color: var(--ink); font-weight: var(--bold); }
.row__note--swap .icon { flex: none; margin-top: 0.05em; }

/* The sheet: from the foot on a phone, at the side on a desk. It scrolls
   inside itself, with its name and × kept at the top, and never pulls the
   page behind along (on Android, no pull-to-refresh). */
.sheet {
  position: fixed;
  inset: auto 0 0 0;
  width: 100%;
  max-width: 100%;
  max-height: 88svh;
  margin: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0 var(--gutter) calc(var(--s3) + env(safe-area-inset-bottom));
  border: 0;
  border-top: 6px solid var(--ink);
  background: var(--raised);
  color: var(--ink);
  transition: transform 180ms cubic-bezier(0.2, 0, 0, 1), opacity 180ms linear, display 180ms allow-discrete, overlay 180ms allow-discrete;
}

.sheet[open] { transform: none; opacity: 1; }
.sheet:not([open]) { transform: translateY(1.5rem); opacity: 0; }

@starting-style {
  .sheet[open] { transform: translateY(1.5rem); opacity: 0; }
}

/* Reopened after an auto-refresh, it's simply there. Swiped down, it follows
   the finger, then slides away (the vessel-sheet controller). */
.sheet:is(.is-reopening, .is-dragging) { transition: none; }
.sheet.is-swiping { transition: transform 200ms cubic-bezier(0.3, 0, 1, 1); }

:root:has(.sheet:modal) { overscroll-behavior: none; }

.sheet::backdrop { background: var(--backdrop); }

.sheet__head {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2) 0 var(--s1);
  background: var(--raised);
}

.sheet :is(h2, h3, p, dl, ul) { margin-bottom: 0; }
.sheet__name { margin: 0; font-size: var(--t-display); font-weight: var(--bold); line-height: 1; letter-spacing: -0.02em; }
/* The name takes focus as the sheet opens, to be read first, but isn't a control, so it has no ring. */
.sheet__name:focus { outline: none; }
.sheet__class { margin-top: var(--s0); }

.sheet__close {
  display: grid;
  flex: none;
  place-items: center;
  width: var(--tap);
  height: var(--tap);
  margin-right: calc(-1 * var(--s0));
  padding: 0;
  border: 2px solid var(--ink);
  background: var(--raised);
  color: var(--ink);
  cursor: pointer;
}

.sheet__close:active { background: var(--ink); color: var(--paper); }

.sheet__swap { display: flex; gap: var(--s0); align-items: center; margin-top: var(--s1); font-weight: var(--bold); }

.sheet__numbers {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--s2);
  margin: var(--s2) 0 0;
  padding-top: var(--s2);
  border-top: 2px solid var(--ink);
}

/* Each number reads as it shows: the figure, then under it what it counts. */
.sheet__numbers p { margin: 0; font-size: var(--t-small); color: var(--ink-2); }
.sheet__figure { display: block; font-size: var(--t-row); font-weight: var(--bold); line-height: 1.1; color: var(--ink); font-variant-numeric: tabular-nums; }

.sheet__subhead { margin-top: var(--s3); padding-top: var(--s1); border-top: 1px solid var(--rule-soft); font-size: var(--t-body); font-weight: var(--bold); }

.sheet__aboard { display: grid; gap: var(--s0); margin: var(--s0) 0 0; padding: 0; list-style: none; }
.sheet__aboard li { display: flex; gap: var(--s0); align-items: center; font-weight: var(--bold); }
.sheet__aboard .is-off { font-weight: var(--regular); color: var(--ink-2); }
.sheet__aboard .is-off .icon { opacity: 0.6; }

.sheet__note { margin-top: var(--s1); font-weight: var(--bold); }
.sheet__source { margin-top: var(--s3); font-size: var(--t-small); color: var(--ink-2); }

/* WSDOT's text, folded to about two lines with More, which turns to Less. */
.more-text { max-width: 36em; margin-top: var(--s1); }
.more-text__text p { margin: 0; }
.more-text__text p + p { margin-top: var(--s0); }
.more-text:has(> .more-text__fold:not([open])) .more-text__text { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; }

.more-text__fold > summary {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  min-height: var(--tap);
  font-weight: var(--bold);
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.18em;
  cursor: pointer;
  list-style: none;
}

.more-text__fold > summary::-webkit-details-marker { display: none; }
.more-text__fold[open] .chevron { transform: rotate(90deg); }
/* Drawn here, so the button's name has only the word that applies. */
.more-text__fold > summary::after { content: "More"; }
.more-text__fold[open] > summary::after { content: "Less"; }

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

@media (min-width: 60rem) {
  .sheet {
    inset: 0 0 0 auto;
    width: min(30rem, 100%);
    height: 100%;
    max-height: none;
    padding: 0 var(--s4) var(--s4);
    border-top: 0;
    border-left: 6px solid var(--ink);
  }

  .sheet:not([open]) { transform: translateX(2rem); }

  @starting-style {
    .sheet[open] { transform: translateX(2rem); opacity: 0; }
  }
}

/* ---------- About and the map pages (step 8) ----------
   About: the first design's sections on the ranked scale, with the theme choice. The
   map pages are re-tokenised only, until their own pass (#74): the region
   tabs as they were, squared off; the chart on the tokens; the region's day
   on the sailings screen's rows. */

.about__intro, .about-section { display: grid; gap: var(--s1); max-width: 40em; }
.screen--about .theme-choice { margin-top: 0; }

.screen--map .page-head { gap: var(--s0); }

.crossing-tabs { display: flex; flex-wrap: wrap; gap: var(--s0); margin: 0; padding: 0; list-style: none; }

.crossing-tab {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  padding: 0 var(--s2);
  border: 2px solid var(--rule);
  border-radius: 0;
  background: none;
  color: var(--ink);
  font-size: var(--t-body);
  font-weight: var(--bold);
  text-decoration: none;
}

.crossing-tab[aria-current="page"] { border-color: var(--ink); background: var(--ink); color: var(--paper); }

.region__title { margin-top: var(--s0); margin-bottom: var(--s0); }

.region-map { display: grid; gap: var(--s0); margin: 0; }
.screen--map .map-chart { border: 1px solid var(--rule-soft); border-radius: 0; }
.screen--map .map-chart__line { stroke: var(--ink); stroke-width: 2; opacity: 1; }
.screen--map .region-map__route:is(:hover, :focus) .map-chart__line { stroke-width: 4; }
.screen--map .region-map__route:focus-visible { outline: none; }
.screen--map .region-map__route:focus-visible .map-chart__line { stroke: var(--focus); }
.screen--map .map-chart__terminal rect { fill: var(--paper); stroke: var(--ink); stroke-width: 2; }
.screen--map .map-chart__terminal text { font-size: 13px; font-weight: var(--bold); fill: var(--ink); stroke: var(--paper); }
.screen--map :is(.map-chart__vessel path, .map-chart__vessel circle) { fill: var(--ink-2); stroke: var(--paper); stroke-width: 1.5; }
.screen--map .map-chart__vessel text { font-size: 12px; font-weight: var(--regular); fill: var(--ink); stroke: var(--paper); }
.map-credit { margin: 0; font-size: var(--t-small); color: var(--ink-2); }

/* Each row names its crossing above its vessel, which drops to regular so
   the crossing leads. */
.row__crossing { display: block; font-weight: var(--bold); }
.screen--map .row__vessel { font-weight: var(--regular); }
