/* SAB Portal — the typeface, the palette, and every dimension the portal uses.
 *
 * The first of the stylesheets listed in layers.css, and the only one that may
 * name an Open Props property or write a raw length. Everything after it —
 * frame, listing, values, detail, map — refers to a `--sab-` name defined
 * here. That is what makes restyling a state a one-line edit, and what stops
 * it drifting between the tables, the detail pages and the map: there is one
 * definition of it and the other nine files cannot make a second.
 *
 * Phase 1 is deliberately unfinished: simple, clean, sharp, and structured so
 * that the design pass is cheap.
 *
 * Two rules this file exists to enforce:
 *   - Colour is never the only signal. Every state also carries words, so
 *     these classes tint and mark; they never carry the meaning alone.
 *   - Provisional things are drawn provisionally. A listing-only pipe must not
 *     look as solid as one with real detail behind it.
 *
 * Sizes, weights and radii come from Open Props, vendored under
 * portal/static/portal/vendor/open-props/. Open Props supplies *scales* and
 * nothing else — no components, no opinions, no look. Its properties are used
 * in exactly one place, the :root block below, where each becomes a `--sab-`
 * property named for its job. Everything after that block refers to the
 * `--sab-` name.
 *
 * That indirection is the whole point, and it is enforced by
 * portal/tests/test_css_tokens.py:
 *
 *   - A rule that says `gap: var(--size-2)` cannot be read. A rule that says
 *     `gap: var(--sab-gap)` can, and can be changed in one place.
 *   - Open Props stays swappable. If it is ever dropped, the :root block below
 *     is the only thing that has to change.
 *   - A raw length written into a rule is a decision made in the dark. There
 *     is a scale; a value outside it is either a mistake or a new step, and
 *     both belong in this block where they can be seen next to the others.
 */

@layer app {

/* --- The typeface -------------------------------------------------------
 *
 * One superfamily at three widths, because this portal is not prose with some
 * numbers in it — it is measurements and identifiers with some prose around
 * them, and the two want different treatment.
 *
 *   Sans            prose, labels, headings.
 *   Mono            every measured value and every identifier.
 *   Sans Condensed  column headers, and nothing else.
 *
 * The sans/mono split is not decoration: it is a claim the reader can rely
 * on. Monospace means *this came from the fleet* — a capture time, a diameter,
 * a uuid, a count. Proportional means the portal is talking. That convention
 * already existed here (`--sab-mono` is read by eight rules below); this gives
 * it a typeface designed for the job instead of whatever the machine had.
 *
 * Condensed is on the header row alone. A dense table needs its headers
 * separated from its data, and doing that with width costs no rule, no tint
 * and no vertical space — the three things a listing has least of.
 *
 * IBM Plex, SIL Open Font License 1.1, vendored under
 * portal/static/portal/vendor/ibm-plex/ with the licence beside it. Latin and
 * Latin Extended only: Swedish needs å ä ö, and the extended subset covers a
 * street name from anywhere else in the Nordics without rendering a tofu box
 * at a customer. 126 kB over eight files, split by unicode-range so a page
 * that never shows an extended character never fetches one.
 *
 * `font-display: swap` on purpose. The fallback stack is a real stack, and a
 * portal that reports field faults shows its text immediately in a system
 * font rather than showing nothing while a webfont arrives. Display rule 2:
 * a missing system degrades, it does not crash. */

@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 100 700; /* one variable file covers the whole scale */
  font-display: swap;
  src: url("../vendor/ibm-plex/plex-sans-latin.d28498895ed7.woff2") format("woff2");
  unicode-range: U+0000-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: "IBM Plex Sans";
  font-style: normal;
  font-weight: 100 700;
  font-display: swap;
  src: url("../vendor/ibm-plex/plex-sans-latin-ext.d313794bd9fd.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
    U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "IBM Plex Sans Condensed";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../vendor/ibm-plex/plex-sans-condensed-latin.6d1e605a17a7.woff2") format("woff2");
  unicode-range: U+0000-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: "IBM Plex Sans Condensed";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../vendor/ibm-plex/plex-sans-condensed-latin-ext.6eb9ca8382f8.woff2")
    format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
    U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../vendor/ibm-plex/plex-mono-400-latin.814fe1615d4d.woff2") format("woff2");
  unicode-range: U+0000-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: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../vendor/ibm-plex/plex-mono-400-latin-ext.17a5b2a15bf5.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
    U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../vendor/ibm-plex/plex-mono-500-latin.6708425c446b.woff2") format("woff2");
  unicode-range: U+0000-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: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../vendor/ibm-plex/plex-mono-500-latin-ext.216ee1a35b06.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
    U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* The light values, and the one place they are written.
 *
 * `[data-theme="light"]` is here so that a light island inside a dark page —
 * today only the sign-in card, which sits on a dark photograph in either
 * theme — picks up the same tokens rather than a second copy of them. Pico's
 * own variables are scoped the same way, so the two agree. The dark block
 * below overrides `:root` only, which is what leaves the island alone. */
:root,
[data-theme="light"] {
  /* The palette is the fleet's own materials, not a framework's ramp.
   *
   * What this portal describes is a hot pipe and a cooled one running side by
   * side in a concrete trench under a cold street, and boxes in the chambers
   * listening to them at night. So: the grounds are concrete and graphite, and
   * heat is the only warm thing on the page. Every state below is a material a
   * technician would recognise — spruce, copper, iron oxide — rather than a
   * step off a general-purpose scale. Each is taken at full saturation rather
   * than muted towards its ground: a state read at a glance across a room has
   * to arrive as a colour first and as a word second, and a greyed-down green
   * beside a greyed-down amber is two shades of the same nothing.
   *
   * This matters beyond taste. The three device states, the four freshness
   * steps and the two archive marks are *different vocabularies that must not
   * be read as one*, and hues chosen together can be held apart deliberately
   * in a way that green-700/amber-700/red-700 cannot.
   *
   * Every pair below is at or above 4.5:1 against both table row grounds —
   * `--sab-row` and `--sab-row-alt` — and against its own tint. Checked, not
   * assumed. A state a reader cannot read is a state that is not there. */

  /* Device state. Three values, never two: silent is a field fault, never is a
   * commissioning problem, and they go to different teams.
   *
   * Spruce, copper, unlit. The two faults are told apart by temperature and
   * not only by hue: silent is warm because someone has to go and open the
   * box, never is a cold grey because nothing was ever installed to fail.
   * A never-reported box should read as absence, not as alarm — it is a
   * commissioning list, and colouring it like a fault sends the wrong crew. */
  --sab-online: #067a3a;
  --sab-online-bg: #d2f5df;
  --sab-silent: #a15900;
  --sab-silent-bg: #ffecc7;
  --sab-never: #3f4a55;
  --sab-never-bg: #dbe2e8;

  /* The same three states, for a shape rather than for a word.
   *
   * A colour that has to be legible as small text on white has to be dark,
   * and dark is what made the rings and bars look like slate. A filled shape
   * has no such limit — it is read by area, not by letterform — so the bars,
   * the rings and the dots take these, and every word keeps the three above.
   * Same hues, lifted: still spruce, copper and unlit, and still told apart
   * by temperature as well as by hue. */
  --sab-online-vivid: #16a34a;
  --sab-silent-vivid: #e08700;
  --sab-never-vivid: #6b7787;

  /* The map. The two pipes in a trench, and the trench around them.
   *
   * Two hues that are not the state colours and are not each other: forward
   * and return run side by side, a millimetre apart on the screen, and a
   * reader has to be able to say which is which without a legend at that
   * distance. Chosen to hold up on a satellite photograph as well as on paper.
   *
   * NOTE: forward is the hot leg and return the cooled one, and this palette
   * draws forward cool and return warm — thermally backwards. It is kept that
   * way because the assignment is fixed by docs/decisions/map-spec.md and a reader who
   * has learned which line is which must not find them swapped by a design
   * pass. If it is ever revisited, revisit it there and not here.
   *
   * `--sab-map-provisional` is the colour of a pipe nobody has measured. Quiet
   * on purpose: at full weight a listing row stands beside a measured pipe as
   * an equal, and most of this fleet is listing rows. */
  --sab-map-forward: #0f4fdb;
  --sab-map-return: #c14205;
  --sab-map-trench: #c6cbd1;
  --sab-map-trench-edge: #5f6a75;
  --sab-map-provisional: #5f6a75;

  /* Freshness. A separate scale from device state, with its own thresholds.
   *
   * Shares spruce and copper with the two states it agrees with, because a
   * fresh reading and an online box are the same kind of good news and two
   * greens would be read as two claims. Stale is iron oxide — the colour of
   * something that has been left. Future is indigo and deliberately not red:
   * a capture time in the future is a clock that is wrong, not a pipe that is
   * wrong, and it goes to whoever owns the box rather than to the field. */
  --sab-fresh: #067a3a;
  --sab-ageing: #a15900;
  --sab-stale: #c62f12;
  --sab-future: #5a34d6;

  /* What the archive holds for one night. A separate vocabulary from device
   * state and from freshness, even where the hue agrees with one of them: a
   * green box and a green night are different claims, and a design pass that
   * moves one must not silently move the other.
   *
   * Two marks and no third: a night is whole, or it is short of an end. Half a
   * pair is not half an analysis, so it is warned about rather than counted. */
  --sab-held: #067a3a;
  --sab-held-bg: #d2f5df;
  --sab-part-held: #a15900;
  --sab-part-held-bg: #ffecc7;

  /* Absent values. Deliberately quiet but never invisible: a missing value is
   * information, and hiding it would read as a value of nothing. */
  --sab-absent: #5c6773;

  /* The page's strongest text colour, named because `--pico-color` is not
   * that colour everywhere: Pico redefines it inside buttons. Graphite rather
   * than black: the page is a chamber wall, not a printed sheet. */
  --sab-ink: #151920;

  /* The waveform strip. Warm rather than grey, because it is the one thing in
   * the card that came out of the ground rather than out of a database, and a
   * grey envelope on a grey panel read as a decoration nobody had drawn.
   *
   * Two stops, not one. The envelope is densest at the centre line and thins
   * towards its peaks, so the ink follows it: saturated where the shape is
   * solid, pale where it is a single loud sample. That is the same information
   * the height already carries, said a second way — which is display rule 5
   * the right way round, colour supporting a shape rather than replacing it.
   * Neither stop is a state: nothing here means online, ageing or at fault. */
  --sab-wave: #f0850f;
  --sab-wave-quiet: #f8bd7a;

  /* A chosen option among a small fixed set — today only the language. Marked
   * by weight and contrast as well as by tint, because a colour alone is not a
   * signal. */
  --sab-selected: #131820;
  --sab-selected-bg: #dde1e5;

  /* Table rows. Opaque on purpose: the pinned first column inherits its
   * row's background, and Pico's own stripe is rgba(111,120,135,.0375),
   * through which the scrolled columns are plainly visible. */
  --sab-row: #ffffff;
  --sab-row-alt: #f1f3f5;

  /* A quiet panel: something to be read off rather than read into. The
   * script block sits on it and darkens a step when it is about to be
   * copied, which is the whole of the hover affordance. */
  --sab-quiet-bg: #f4f6f8;
  --sab-quiet-bg-hover: #e5e9ee;

  /* An action offered beside data: copy this, take these two files. Pico's own
   * primary, held here under a name of its own so a tinted offer can be told
   * from an ordinary link, and so the three parts of one (ink, ground, edge)
   * move together. Tinted and never filled — the page's data is the loud part,
   * and a solid button beside four measurements out-shouts them. */
  --sab-action: #0b66b8;
  --sab-action-bg: #e0f0fd;
  --sab-action-edge: #9fd0f5;

  /* The sign-in card and the ground behind its photograph. Not themed: the
   * photograph is dark in either theme, so the card is light in both. The
   * ground is the photograph's own average colour — see 30-procedures.md § 16 —
   * so the moment before the picture arrives, and the page a reader is left
   * with if it never does, looks like the page and not like a fault. */
  --sab-login-card: #ffffff;
  --sab-login-ground: #2a3037;
  --sab-login-card-shadow: 0 1px 2px rgba(9, 11, 14, 0.28),
    0 12px 32px rgba(9, 11, 14, 0.38);

  --sab-rule: var(--pico-muted-border-color);

  /* Pico's own accent, handed the portal's. Left to its default, every link on
   * the page is one blue and every tinted offer beside the data is another,
   * half a step apart — which reads as a printing fault rather than as two
   * kinds of thing. One accent, stated here, and the link, the focus ring and
   * the offer are the same colour because they are the same invitation. */
  --pico-primary: var(--sab-action);
  --pico-primary-hover: #06559b;
  --pico-primary-underline: rgba(11, 102, 184, 0.4);
  --pico-primary-hover-underline: var(--sab-action);
  --pico-primary-focus: rgba(11, 102, 184, 0.28);
  --pico-primary-background: var(--sab-action);
  --pico-primary-hover-background: #06559b;
  --pico-primary-border: var(--pico-primary-background);
  --pico-primary-hover-border: var(--pico-primary-hover-background);

  /* --- The three faces ---------------------------------------------------
   *
   * Declared here for the same reason the colours are: so that dropping IBM
   * Plex is an edit to this block and not a search through the file. Each
   * carries a real fallback stack, because the page is complete before the
   * webfont arrives and stays complete if it never does.
   *
   * `--sab-font` is handed to Pico's own variable immediately below, which is
   * what puts it on the body — Pico sets `font-family` on very few elements
   * and reads that property for all of them. */
  --sab-font: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto,
    "Helvetica Neue", sans-serif;
  --sab-font-condensed: "IBM Plex Sans Condensed", "IBM Plex Sans", system-ui,
    sans-serif;
  --sab-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo,
    monospace;

  --pico-font-family: var(--sab-font);
  --pico-font-family-sans-serif: var(--sab-font);
  --pico-font-family-monospace: var(--sab-mono);

  /* --- Spacing ----------------------------------------------------------
   *
   * Open Props' size scale, named for what it spaces rather than for how big
   * it is. `--sab-gap` says what the value is for; `--size-2` says only that
   * it is the second step. A rule written against the second step cannot be
   * read, and cannot be changed without reading every use of it.
   *
   * Six steps, and nothing between them. The point of a scale is that it
   * refuses the seventh: a layout built from 0.3rem, 0.35rem and 0.4rem is
   * not three decisions, it is one decision made three times carelessly. */
  --sab-gap-tight: var(--size-1);   /* .25rem  — inside a control */
  --sab-gap: var(--size-2);         /* .5rem   — between related things */
  --sab-gap-loose: var(--size-3);   /* 1rem    — between controls */
  --sab-gap-wide: var(--size-4);    /* 1.25rem — between columns of controls */
  --sab-block: var(--size-5);       /* 1.5rem  — between blocks on a page */
  --sab-block-wide: var(--size-6);  /* 1.75rem — inside a card */
  --sab-block-loose: var(--size-7); /* 2rem    — between sections */
  --sab-page: var(--size-9);        /* 4rem    — around a page-sized island */
  --sab-page-loose: var(--size-10); /* 5rem    — the sign-in card's own margin */

  /* --- Type -------------------------------------------------------------
   *
   * Split at 1rem, because the portal's reading is not evenly distributed.
   *
   * From 1rem up these are Open Props' steps unchanged: headings are the part
   * of this page that behaves like ordinary prose, and that scale was built
   * for it.
   *
   * Below 1rem they are declared here. Open Props steps .75rem straight to
   * 1rem, and a dense listing spends its entire life inside the gap that
   * skips — column headers, filter inputs, counts and captions all sit between
   * .7 and .9rem, and collapsing them to one step would flatten the hierarchy
   * the reader navigates by.
   *
   * Named by the job, not the size. `--sab-text-meta` and `--sab-text-label`
   * are close enough that they probably want to be one step; that is a
   * judgement to make with every page open at once, and it is a one-line edit
   * here when someone makes it. */
  /* Below the hierarchy rather than a step in it. An archive filename is 47
   * mono characters that must appear whole — half of one is not a shorter
   * name, it is the wrong name — and inside a two-column tile 0.7rem does not
   * fit. Nothing a reader navigates by is set at this size; only strings that
   * are transcribed or copied. */
  --sab-text-nano: 0.62rem;   /* an archive filename, in full, inside a tile */
  --sab-text-micro: 0.7rem;   /* a filename, a checksum — read only if hunted */
  --sab-text-small: 0.75rem;  /* filter inputs, table cells */
  --sab-text-meta: 0.8rem;    /* captions beside a value */
  --sab-text-label: 0.85rem;  /* column headers, secondary controls */
  --sab-text-body: 0.9rem;    /* counts, legends, running text */
  --sab-text-base: var(--font-size-1);    /* 1rem    — Pico's body size */
  --sab-text-lead: var(--font-size-2);    /* 1.1rem  — the wordmark */
  --sab-text-title: var(--font-size-3);   /* 1.25rem — a page's own name */
  --sab-text-display: var(--font-size-5); /* 2rem    — the sign-in card */

  /* --- Weight and rhythm ------------------------------------------------
   *
   * Only the steps something actually uses. A scale is a vocabulary, and a
   * word nobody says is not vocabulary, it is clutter that the next person has
   * to read before deciding it means nothing to them. Add a step when a rule
   * needs it. */
  --sab-weight-normal: var(--font-weight-4);
  --sab-weight-medium: var(--font-weight-5);
  --sab-weight-strong: var(--font-weight-6);
  --sab-leading: var(--font-lineheight-3);

  /* --- Edges ------------------------------------------------------------- */
  --sab-hairline: var(--border-size-1);
  --sab-radius: var(--radius-2);
  /* One step softer, for a panel the reader opened rather than a control they
   * are looking at. A menu with the same corner as the button under it reads
   * as the button having grown. */
  --sab-radius-panel: var(--radius-3);
  /* A card on the page ground. Softer than a control, so a button inside a
   * card never shares the card's corner. */
  --sab-radius-card: 0.75rem;
  /* Lifted by shadow rather than drawn by a rule; the edge stays, faintly,
   * for a printer and for high-contrast mode. */
  --sab-card-edge: rgba(20, 23, 28, 0.07);
  --sab-card-shadow: 0 1px 2px rgba(20, 23, 28, 0.04),
    0 4px 14px rgba(20, 23, 28, 0.05);
  /* Between two segments of a state bar, so three states read as three. */
  --sab-seam: 2px;
  /* Plex opens up at display sizes; large figures and titles are set tighter. */
  --sab-tracking-tight: -0.02em;

  /* --- Layers -------------------------------------------------------------
   *
   * Two numbers, written down together because a stacking order cannot be
   * read one rule at a time.
   *
   * The reason there is a scale at all is the map: Leaflet numbers its own
   * panes 200 to 1000, those numbers are Leaflet's and not ours to renumber,
   * and Pico gives a dropdown 99 — so on the dashboard the language list
   * opened *underneath* the fleet. Anything the portal floats over a page has
   * to clear Leaflet's ceiling, and it only keeps clearing it if the numbers
   * sit next to each other where the next one can be chosen against them. */
  --sab-layer-pinned: 2;    /* held still inside a box that scrolls */
  --sab-layer-panel: 1100;  /* opened by the reader, over everything */

  /* A panel the reader opened, lifted off the page. One shadow for all of
   * them: the language list and the calendar are the same kind of thing and
   * must not look like two. */
  --sab-panel-shadow: 0 0.6rem 1.6rem rgba(20, 23, 28, 0.16);

  /* The sidebar: one surface, edged by a hairline. */
  --sab-bar-bg: #ffffff;

  /* The ground the page sits on. Quieter than a card, so that a card is a
   * thing on a surface rather than a rectangle drawn on the same sheet: the
   * panels on the overview are separated by their own ground and not by a
   * border each. The navigation panel takes the card's colour for the same
   * reason — it is in front of the page, not part of it. */
  --sab-page-bg: #f3f4f2;

  /* --- Fixed dimensions -------------------------------------------------- */
  --sab-table-row-height: 2.75rem;
  /* One height for a search box and every control on its line. */
  --sab-control-height: 2.5rem;
  /* The wordmark's band at the head of the sidebar. */
  --sab-brand-height: 3.5rem;
  /* The navigation column. Wide enough for the longest section word beside
   * its icon without wrapping, and narrow enough that it is read as a margin
   * of the page rather than as half of it. */
  --sab-sidebar-width: 13.5rem;
  /* The widest a page grows. Past this a wide monitor gets margin, not
   * cards stretched across it. */
  --sab-page-max: 100rem;
  /* How far into the column a word sits. One value, so the wordmark, every
   * section and the reader's corner all start on the same line down the
   * panel — the thing the eye actually checks in a column of links. */
  --sab-sidebar-inset: 1rem;
  /* The reader's disc at the foot of the sidebar. */
  --sab-control-tile: 2rem;
  /* The state bar on an owner card. Thick enough that three segments are
   * distinguishable at a glance, thin enough that it reads as a measure of the
   * card above it rather than as a block of colour in its own right. */
  --sab-bar-height: 0.5rem;
  /* The line beside a company's name on the overview. Thinner than the bar on
   * a card: it is a reading of one row, not the statement of a page. */
  --sab-hairline-bar: 0.3rem;
  /* The square on a box's page. Big enough to tell one junction from the next
   * and to carry the two or three labelled dots a box has around it; small
   * enough that the facts beside it and the tabs below it are what the page
   * is. The pipe page's map instead fills to the card beside it, down to this.
   */
  --sab-map-brief: 17rem;
  --sab-map-least: 16rem;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    /* The same materials seen at night. Each is the light-theme hue lifted
     * until it clears its own dark ground at 4.5:1, not a different colour:
     * a reader who learns that copper means silent must not have to learn it
     * twice. The grounds stay graphite for the same reason the light ones stay
     * concrete — the fleet does not change colour after dark. */
    --sab-online: #35e08d;
    --sab-online-bg: #0d3a20;
    --sab-silent: #ffb02e;
    --sab-silent-bg: #3d2a05;
    --sab-never: #9aa6b4;
    --sab-never-bg: #232830;
    /* At night the readable three are already bright, so a shape and a word
     * can share them: lifting them further would glow. */
    --sab-online-vivid: var(--sab-online);
    --sab-silent-vivid: var(--sab-silent);
    --sab-never-vivid: var(--sab-never);
    /* Lifted, not inverted: a trench on a dark map is still ground holding
     * two pipes, so the fill stays lighter than the edge around it. */
    --sab-map-forward: #4d9bff;
    --sab-map-return: #ff8a3d;
    --sab-map-trench: #333a44;
    --sab-map-trench-edge: #9aa6b4;
    --sab-map-provisional: #9aa6b4;
    --sab-fresh: #35e08d;
    --sab-ageing: #ffb02e;
    --sab-stale: #ff7a55;
    --sab-future: #a58cff;
    --sab-held: #35e08d;
    --sab-held-bg: #0d3a20;
    --sab-part-held: #ffb02e;
    --sab-part-held-bg: #3d2a05;
    --sab-absent: #9aa6b4;
    --sab-action: #2bb7ff;
    --sab-action-bg: #082436;
    --sab-action-edge: #1c5f88;
    --sab-ink: #ecf0f5;
    /* Lifted and desaturated a step: the light theme's orange on a near-black
     * panel glows, and a strip that glows is looked at instead of read. */
    --sab-wave: #e08f3f;
    --sab-wave-quiet: #6d4623;
    --sab-quiet-bg: #171a20;
    --sab-quiet-bg-hover: #212630;
    --sab-selected: #f0f2f5;
    --sab-selected-bg: #333a44;
    --sab-row: #12151a;
    --sab-row-alt: #181c22;
    /* Deeper: on a dark page a shadow is most of what separates a panel from
     * the page under it, and a light-theme shadow is invisible there. */
    --sab-panel-shadow: 0 0.6rem 1.6rem rgba(0, 0, 0, 0.55);
    --sab-card-edge: rgba(255, 255, 255, 0.06);
    --sab-card-shadow: 0 1px 2px rgba(0, 0, 0, 0.4),
      0 6px 18px rgba(0, 0, 0, 0.28);
    /* The bar lifts on a dark page instead of tinting down: a surface darker
     * than the page reads as a hole rather than as a bar in front of it. */
    --sab-bar-bg: #1a1e26;
    /* Darker than a card at night, for the reason it is lighter than one by
     * day: the ground is behind everything, and a ground that competes with
     * the cards on it is not a ground. */
    --sab-page-bg: #0d1014;    /* The accent, lifted the same way the states are, so a link at night is
     * the night version of the daytime link and not a second colour. */
    --pico-primary-hover: #7fd2ff;
    --pico-primary-underline: rgba(43, 183, 255, 0.4);
    --pico-primary-focus: rgba(43, 183, 255, 0.3);
    --pico-primary-hover-background: #7fd2ff;
  }
}

}
