/* Self-hosted, not a CDN link. A webfont fetched over a satellite link is the wrong
   dependency for an engine room, but that was never an argument for the system stack —
   Helvetica at 15px with tight leading is what made this read like a 1998 intranet.
   Inter is variable (one 343 KB file covers every weight) and JetBrains Mono carries
   anything a machine wrote. Both OFL, both in vendor/fonts, both work offline.
   2026-08-22 / andre — "het lijkt echt jaren 90", and it did. */
@font-face {
  font-family: "Inter";
  src: url("vendor/fonts/Inter.woff2") format("woff2-variations");
  font-weight: 100 900; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("vendor/fonts/Mono.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}

/* =====================================================================================
   The Rulebook — interface

   Who reads this: a commissioning engineer standing at a rack with a tablet, a lead
   approving a rule between meetings, a reviewer who has to decide whether to trust it.
   What they came to do: see what is not proven, decide, and record it.
   How it should feel: an instrument, not a dashboard. Precise, quiet, legible in bad
   light and on paper taped to a rack door.

   The palette comes from the room this tool is used in — the technical space, not the
   saloon: anodised aluminium, the blue-black of a chart plotter at night, annunciator
   amber, the red of an emergency stop, white label tape on a black cable tie, the green
   LED on a switch faceplate. Hence the token names. If they read like any other project,
   the palette was applied to the product instead of taken from it.

   Depth: borders only, committed. Shadows do not print, and on a technical instrument
   they read as decoration.

   Type: the system stack, deliberately. A webfont over a satellite link is exactly the
   wrong dependency for an engine room, so hierarchy is carried by the three levers that
   cost nothing — size, weight, and colour — with monospace reserved for anything a
   machine wrote. Self-hosted IBM Plex is the upgrade when this leaves a laptop.
   ===================================================================================== */

:root {
  color-scheme: light;

  /* ---------------------------------------------------------------------------------
     Palette moved to the reference dashboard on 2026-08-22, at the owner's request.

     What changed: the warm anodised neutrals became the reference's near-neutral light
     greys with white cards and near-black ink, and its pill tints came along with it.
     What did NOT change: the four status colours. Those were contrast-computed, they are
     what the rack-room board is read by, and they are the one thing in here that carries
     meaning rather than style.

     THE ONE THING TO WATCH. Green now does two jobs: it is the accent (links, the active
     nav item) and it is "confirmed on the box". They are kept apart by shape and context —
     a status is always a filled cell or pill carrying a glyph and a word, an accent is
     never a fill behind data — and the matrix only ever uses the status scale. If anybody
     ever misreads a board because of it, the accent moves and the status stays.
     --------------------------------------------------------------------------------- */

  /* Surfaces — the page is a light ground, content is white on it */
  --plane:         #ebebec;   /* the ground the page is */
  --canvas:        #ffffff;   /* content sits on the plane */
  --raised:        #ffffff;   /* one above content */
  --anodised:      #f4f4f5;   /* a quiet band inside a surface: table heads, hover */
  --anodised-deep: #e3e3e5;
  --inset:         #f7f7f8;   /* inputs are inset: they receive content */

  /* Ink — four levels, because two is a flat hierarchy. ink-3 is 4.93:1 on white. */
  --chart-ink:     #141416;
  --ink-2:         #4a4a4e;
  --ink-3:         #6a6a6f;   /* 4.52:1 on the grey plane, 5.38:1 on white — computed, not eyeballed */
  --ink-4:         #94949a;   /* 3.02:1 on white; non-text use only */

  /* Edges — findable, never the first thing you see */
  --edge:          rgba(20, 20, 22, 0.10);
  --edge-soft:     rgba(20, 20, 22, 0.05);
  --edge-strong:   rgba(20, 20, 22, 0.22);

  /* Accent: the reference's green, darkened until it is legible as text (4.61:1 on white).
     Structure is grey; colour communicates. */
  --plotter:       #0e7a4d;
  --plotter-lift:  #0a5c3a;

  /* Action. The reference does not use its green for buttons — its primary control is
     near-black, and the green is left to the data. That is why its accent never competes
     with its charts, and it is worth copying exactly. */
  --action:        #16181a;
  --action-lift:   #2a2d30;

  /* Data-viz green — fills only, never text. The bright mint from the reference. */
  --viz:           #22c55e;

  /* State is reserved and never reused for identity. Unchanged, and not up for restyling. */
  --led-good:      #14663a;
  --annunciator:   #825200;
  --estop:         #a3161b;
  --led-good-bg:   #dcfce7;
  --annunciator-bg:#fef3c7;
  --estop-bg:      #fee2e2;

  /* Label tape — the anchor, printed white on black, like a Brady label on a cable */
  --tape:          #16181a;
  --tape-ink:      #f4f4f5;

  /* ---------------------------------------------------------------------------------
     Type scale. Size, weight, tracking and leading travel together as one decision — the
     Apple rule is that tracking is size-specific and a single letter-spacing is wrong
     somewhere. Sizes are in rem so a reader who enlarges their text enlarges the layout
     with it, rather than breaking it.

     Added 2026-08-22 after the owner reported the type and field sizes felt off. They were:
     the controls on one form measured 26, 32, 38, 40 and 41px tall with 12.8px, 13.44px and
     13.6px type — not a scale, just what .8rem, .84rem and .85rem happen to round to.
     --------------------------------------------------------------------------------- */
  --t-micro: .6875rem;   /* 11px — column heads, eyebrows. Caps, +tracking. */
  --t-small: .8125rem;   /* 13px — meta, captions, grid cells */
  --t-ui:    .875rem;    /* 14px — labels and control text */
  --t-body:  .9375rem;   /* 15px — reading text */
  --t-h3:    1rem;
  --t-h2:    1.25rem;
  --t-h1:    1.75rem;

  --track-tight: -.022em;  /* large type reads too loose as it grows */
  --track-body:   0;
  --track-caps:  .07em;    /* small caps need air to stay legible */

  /* Control scale. One ladder, so nothing next to anything else is a different height by
     accident. Coarse pointers get the 44px touch target on everything (see the media query
     at the end of this file). */
  --ctl-sm: 1.875rem;    /* 30px — toolbar buttons, in-grid fields */
  --ctl-md: 2.25rem;     /* 36px — the default */
  --ctl-lg: 2.75rem;     /* 44px — primary actions, touch */
  --ctl-font: var(--t-body);
  --ctl-pad: .625rem;

  --u: 4px;                      /* the grid. Every spacing value is a multiple. */
  /* Concentric: a control inside a container is one step smaller, never the same. */
  --radius-s: 8px;               /* inputs, buttons, chips */
  --radius-m: 12px;              /* tables, panels, fieldsets */
  --radius-l: 16px;              /* cards */

  /* Elevation. The reference lifts a white card off a grey ground with almost nothing —
     a hairline ring and a very soft drop. Dark mode collapses to the ring alone. */
  --lift: 0 0 0 1px rgba(20,20,22,.04), 0 1px 2px -1px rgba(20,20,22,.05), 0 8px 20px -8px rgba(20,20,22,.10);
  --lift-sm: 0 0 0 1px rgba(20,20,22,.04), 0 1px 2px -1px rgba(20,20,22,.05);

  /* Material: the chrome is a translucent layer content passes under, not an opaque strip
     that owns a band of the screen (apple-design §12). */
  --material:      rgba(255, 255, 255, 0.72);
  --material-blur: saturate(180%) blur(20px);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    /* The reference's dark frames are a rebuild, not an inversion: the ground goes to
       near-black, the cards lift only a little, and the green stays exactly where it was. */
    --plane:         #0b0b0c;
    --canvas:        #17181a;
    --raised:        #1f2023;
    --anodised:      #1d1e21;
    --anodised-deep: #292a2e;
    --inset:         #121315;
    --chart-ink:     #f2f2f3;
    --ink-2:         #b4b4b8;
    --ink-3:         #8e8e94;
    --ink-4:         #6a6a70;
    --material:      rgba(23, 24, 26, 0.72);
    --edge:          rgba(242, 242, 243, 0.12);
    --edge-soft:     rgba(242, 242, 243, 0.065);
    --edge-strong:   rgba(242, 242, 243, 0.28);
    --plotter:       #4ade80;
    --plotter-lift:  #86efac;
    --action:        #f2f2f3;
    --action-lift:   #ffffff;
    --viz:           #22c55e;
    --led-good:      #4ade80;
    --annunciator:   #e0b252;
    --estop:         #f08a8f;
    --led-good-bg:   #10281a;
    --annunciator-bg:#2b2312;
    --estop-bg:      #2e1618;
    --tape:          #f2f2f3;
    --tape-ink:      #0f1012;
    --lift:    0 0 0 1px rgba(242,242,243,.07);
    --lift-sm: 0 0 0 1px rgba(242,242,243,.06);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --plane: #0b0b0c; --canvas: #17181a; --raised: #1f2023;
  --anodised: #1d1e21; --anodised-deep: #292a2e; --inset: #121315;
  --chart-ink: #f2f2f3; --ink-2: #b4b4b8; --ink-3: #8e8e94; --ink-4: #6a6a70;
  --material: rgba(23, 24, 26, 0.72);
  --edge: rgba(242,242,243,.12); --edge-soft: rgba(242,242,243,.065); --edge-strong: rgba(242,242,243,.28);
  --plotter: #4ade80; --plotter-lift: #86efac;
  --action: #f2f2f3; --action-lift: #ffffff; --viz: #22c55e;
  --led-good: #4ade80; --annunciator: #e0b252; --estop: #f08a8f;
  --led-good-bg: #10281a; --annunciator-bg: #2b2312; --estop-bg: #2e1618;
  --tape: #f2f2f3; --tape-ink: #0f1012;
  --lift: 0 0 0 1px rgba(242,242,243,.07); --lift-sm: 0 0 0 1px rgba(242,242,243,.06);
}

/* the old names, kept so twenty views do not have to change at once */
:root {
  --ink: var(--chart-ink); --dim: var(--ink-3); --line: var(--edge);
  --bg: var(--canvas); --panel: var(--anodised);
  --bad: var(--estop); --warn: var(--annunciator); --ok: var(--led-good); --link: var(--plotter);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  /* 0.9375rem = 15px at the reader's default, and it scales with their setting */
  font: 0.9375rem/1.55 "Inter", -apple-system, "Segoe UI", Roboto, sans-serif;
  font-feature-settings: "cv05" 1, "cv08" 1, "ss03" 1;   /* disambiguated 1 / l / I */
  font-optical-sizing: auto;
  /* Tracking is size-specific, never one value for the page: body sits at zero, headings
     tighten as they grow, small caps loosen. A single letter-spacing is wrong somewhere. */
  letter-spacing: 0;
  color: var(--chart-ink);
  background: var(--plane);
  font-size: var(--t-body);
  /* Inter is a variable font with an optical-size axis; letting the browser drive it is
     what keeps small text from looking like shrunk large text (Apple §15). */
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--plotter); text-underline-offset: 2px; }
a:hover { color: var(--plotter-lift); }
:focus-visible { outline: 2px solid var(--plotter); outline-offset: 2px; }

/* ---------------------------------------------------------------- the top rail
   Same ground as the canvas — a different colour would split the screen into
   "navigation world" and "content world". A hairline is enough. */
/* Chrome as material: content passes under it rather than stopping at it, and the edge is a
   soft fade instead of a hairline rule (apple-design §12). */
header {
  position: sticky; top: 0; z-index: 20;
  display: flex; gap: calc(var(--u) * 6); align-items: baseline; flex-wrap: wrap;
  padding: calc(var(--u) * 3.5) calc(var(--u) * 7);
  background: var(--material);
  -webkit-backdrop-filter: var(--material-blur);
  backdrop-filter: var(--material-blur);
  border-bottom: 1px solid var(--edge-soft);
}
header::after {
  content: ""; position: absolute; left: 0; right: 0; top: 100%; height: 12px;
  background: linear-gradient(to bottom, rgba(28,26,23,.05), transparent);
  pointer-events: none;
}
/* Vibrancy: text over a translucent layer needs a little more weight than the same text on a
   solid one, or it dissolves into whatever is scrolling underneath. */
header .brand, header nav a[aria-current="page"] { font-weight: 620; }

@media (prefers-reduced-transparency: reduce) {
  header { background: var(--canvas); -webkit-backdrop-filter: none; backdrop-filter: none; }
  header::after { display: none; }
}
@media (prefers-contrast: more) {
  header { background: var(--canvas); border-bottom-color: var(--edge-strong); }
}
header .brand {
  font-weight: 600; font-size: .95rem; letter-spacing: -0.01em;
  text-decoration: none; color: var(--chart-ink);
}
header nav { display: flex; gap: calc(var(--u) * 4); flex-wrap: wrap; }
header nav a {
  text-decoration: none; color: var(--ink-2); font-size: var(--t-ui);
  padding-bottom: calc(var(--u) * 0.5); border-bottom: 2px solid transparent;
}
header nav a:hover { color: var(--chart-ink); }
header nav a[aria-current="page"] { color: var(--chart-ink); font-weight: 600; border-bottom-color: var(--chart-ink); }
header .who { margin-left: auto; color: var(--ink-3); font-size: .78rem; }

main { padding: calc(var(--u) * 8) calc(var(--u) * 7) calc(var(--u) * 20); max-width: 1320px; }
footer {
  padding: calc(var(--u) * 4) calc(var(--u) * 5); margin-top: calc(var(--u) * 12);
  border-top: 1px solid var(--edge-soft); color: var(--ink-4); font-size: .75rem;
}

/* ------------------------------------------------------------------ type scale
   Ratio 1.2 from a 15px base: 12 · 15 · 18 · 22 · 26. Weight and colour carry more of
   the hierarchy than size does. */
h1 { font-size: 1.7rem; line-height: 1.12; margin: 0 0 calc(var(--u) * 2);
     letter-spacing: -0.03em; font-weight: 650; text-wrap: balance; }
h2 { font-size: 1.1rem; line-height: 1.3; margin: calc(var(--u) * 10) 0 calc(var(--u) * 3);
     font-weight: 620; letter-spacing: -0.016em; text-wrap: balance; }
h3 { font-size: .95rem; margin: 0 0 calc(var(--u) * 1); font-weight: 600; }
p  { margin: 0 0 calc(var(--u) * 2); max-width: 74ch; text-wrap: pretty; line-height: 1.6; }
.rb-lead { color: var(--ink-2); max-width: 66ch; }
p.meta, .note, .rb-small { color: var(--ink-3); font-size: var(--t-small); line-height: 1.5;
                        letter-spacing: .004em; }   /* small text wants a touch more air */
.note code { font-size: .78rem; }
code.block { display: block; white-space: pre-wrap; background: var(--anodised);
             border: 1px solid var(--edge-soft); padding: calc(var(--u) * 2); margin-top: calc(var(--u) * 1); }
.mono, pre, code { font-family: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;
                   font-variant-ligatures: none; letter-spacing: 0; }
.right { text-align: right; }
.dim { color: var(--ink-4); }
.unknown { color: var(--ink-4); font-style: italic; }

/* ---------------------------------------------------------------------- tables
   The tool is mostly tables, so this is where the density decision lives: 6px/8px cells,
   hairline rules, header set small and tracked. Numbers are tabular everywhere. */
/* A table is a surface, not a stack of lines on the page. The header band is one step
   quieter than the rows, so the columns read as a group without a heavy rule under them. */
table { border-collapse: collapse; width: 100%; margin: calc(var(--u) * 2) 0 calc(var(--u) * 6);
        font-variant-numeric: tabular-nums;
        background: var(--canvas); border-radius: var(--radius-m); box-shadow: var(--lift);
        overflow: hidden; }
th, td { text-align: left; padding: calc(var(--u) * 2) calc(var(--u) * 2.5);
         border-bottom: 1px solid var(--edge-soft); vertical-align: top; }
thead th, table > tr:first-child th {
  font-size: .68rem; text-transform: uppercase; letter-spacing: .07em;
  color: var(--ink-3); font-weight: 500; border-bottom: 1px solid var(--edge);
  background: var(--anodised);
}
thead tr:first-child th:first-child, table > tr:first-child th:first-child { border-top-left-radius: var(--radius-m); }
thead tr:first-child th:last-child,  table > tr:first-child th:last-child  { border-top-right-radius: var(--radius-m); }
tbody tr:hover, table tr:hover { background: var(--anodised); }
tbody tr:last-child td { border-bottom: 0; }
table.kv th { width: 13rem; text-transform: none; letter-spacing: 0; font-size: var(--t-small);
              color: var(--ink-3); font-weight: 400; }

/* ------------------------------------------------------------- the readout strip
   Not four cards. One instrument panel: hairline-separated cells, label above in small
   tracked caps, figure below in tabular numerals. The figure leads on weight and size;
   the label is demoted on both. */
/* The stat strip. Renamed from .card on 2026-08-22: Tabler owns .card now (ADR 0006), and
   the two definitions collided silently — this one's column-reverse flipped every grid card
   so the toolbar rendered under the table. Two design systems, one class name; exactly the
   drift the ADR exists to bound. */
.readouts {
  display: flex; flex-wrap: wrap; margin: calc(var(--u) * 3) 0 calc(var(--u) * 6);
  border-radius: var(--radius-l); background: var(--canvas); box-shadow: var(--lift);
  overflow: hidden;
}
.readout {
  flex: 1 1 8rem; min-width: 0; padding: calc(var(--u) * 3) calc(var(--u) * 4);
  display: flex; flex-direction: column-reverse; gap: calc(var(--u) * 0.5);
  border-right: 1px solid var(--edge-soft);
}
.readout:last-child { border-right: 0; }
.readout b { font-size: 1.7rem; font-weight: 600; line-height: 1.1; font-variant-numeric: tabular-nums;
          letter-spacing: -0.02em; }
.readout span { font-size: .68rem; text-transform: uppercase; letter-spacing: .07em; color: var(--ink-3); }

/* ---------------------------------------------------------------------- notices */
.flash { padding: calc(var(--u) * 2.5) calc(var(--u) * 3); border: 1px solid var(--edge);
         border-radius: 0 var(--radius-m) var(--radius-m) 0;
         border-left: 3px solid var(--led-good); background: var(--led-good-bg);
         margin: 0 0 calc(var(--u) * 4); font-size: var(--t-ui); }
.flash.bad  { border-left-color: var(--estop); background: var(--estop-bg); }
.flash.warn { border-left-color: var(--annunciator); background: var(--annunciator-bg); }
.flash ul { margin: calc(var(--u) * 1) 0 0 calc(var(--u) * 4); padding: 0; }

/* ----------------------------------------------------------------------- forms */
form.rb-form { max-width: 56rem; }
form.narrow { max-width: 21rem; }
fieldset { border: 0; border-radius: var(--radius-m); box-shadow: var(--lift);
           background: var(--canvas); margin: 0 0 calc(var(--u) * 5);
           padding: calc(var(--u) * 5) calc(var(--u) * 5); }
legend { font-weight: 600; font-size: var(--t-micro); padding: 0 calc(var(--u) * 1);
         text-transform: uppercase; letter-spacing: var(--track-caps); color: var(--ink-3); }
label { display: block; margin: 0 0 calc(var(--u) * 3); font-size: var(--t-ui); color: var(--ink-2); }
label.chk { display: inline-block; margin: 0 calc(var(--u) * 3) calc(var(--u) * 1) 0; }
label.attr { display: grid; grid-template-columns: 14rem 1fr; align-items: center; gap: calc(var(--u) * 2); }
.set { display: flex; flex-wrap: wrap; }
input, select, textarea {
  width: 100%; font-family: inherit; color: var(--chart-ink);
  font-size: var(--ctl-font); line-height: 1.35;
  height: var(--ctl-md);
  padding: 0 var(--ctl-pad);
  border: 1px solid var(--edge); border-radius: var(--radius-s);
  background: var(--inset);   /* inset: it receives content */
  transition: border-color 120ms ease-out, box-shadow 120ms ease-out, background 120ms ease-out;
}
/* A textarea has no single line to centre, so it keeps padding and grows instead. */
textarea { height: auto; min-height: calc(var(--ctl-md) * 2); padding: .5rem var(--ctl-pad); line-height: 1.5; }
select { padding-right: calc(var(--ctl-pad) * 2); }

input:focus, select:focus, textarea:focus { border-color: var(--plotter); background: var(--canvas);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--plotter) 18%, transparent); outline: none; }
input:hover:not(:focus), select:hover:not(:focus), textarea:hover:not(:focus) { border-color: var(--edge-strong); }
input::placeholder, textarea::placeholder { color: var(--ink-4); }

/* A checkbox is not a text field. The blanket width: 100% above stretched every tick box
   across its row and pushed its own label away from it. */
input[type="checkbox"], input[type="radio"] {
  width: 1.05rem; height: 1.05rem; min-height: 0; margin-right: calc(var(--u) * 1.5);
  padding: 0; vertical-align: -2px; accent-color: var(--plotter);
}
input[type="file"] { background: transparent; border: 1px dashed var(--edge-strong);
                     height: auto; padding: .5rem var(--ctl-pad); }
label.chk { display: inline-flex; align-items: center; color: var(--chart-ink); }
textarea.mono { font-family: "JetBrains Mono", ui-monospace, Menlo, monospace; font-size: var(--t-small); }
.rb-row { display: flex; gap: calc(var(--u) * 3); flex-wrap: wrap; }
.rb-row > label { flex: 1 1 12rem; }

button, .button {
  font-family: inherit; font-size: var(--t-ui); font-weight: 500; line-height: 1;
  height: var(--ctl-md);
  padding: 0 calc(var(--ctl-pad) * 1.6);
  border: 1px solid var(--action); border-radius: var(--radius-s);
  background: var(--action); color: var(--canvas); box-shadow: var(--lift-sm);
  cursor: pointer; text-decoration: none;
  display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
  /* Apple §1: the press is acknowledged the instant the pointer goes down, not on release.
     110ms is under the threshold where a control starts to feel like it is thinking. */
  transition: transform 110ms cubic-bezier(0.23, 1, 0.32, 1), background 110ms ease-out;
}
.button { background: transparent; color: var(--chart-ink); border-color: var(--edge-strong); box-shadow: none; }
.button:hover { background: var(--anodised); color: var(--chart-ink); }
button:hover { background: var(--action-lift); border-color: var(--action-lift); }
button:focus-visible, .button:focus-visible {
  outline: 2px solid var(--plotter); outline-offset: 2px;
}
button:active, .button:active { transform: scale(.97); }

form.inline { display: flex; gap: calc(var(--u) * 2); align-items: center;
              margin-bottom: calc(var(--u) * 4); flex-wrap: wrap; }
form.inline input { width: 18rem; }
/* Specificity, not intent: `form.inline input` outranked the checkbox rule above and stretched
   every tick box to 18rem, pushing its label half a row away. */
form.inline input[type="checkbox"], form.inline input[type="radio"] { width: auto; }

/* ------------------------------------------------------------------ small type
   Tags carry state or lifecycle; they are never decoration, so each one earns a colour. */
/* .rb-tag was removed on 2026-08-22. It was the same idea as .rb-pill in tabler-bridge.css —
   a small status label — with its own slightly different sizes and colours, which is how two
   screens end up disagreeing about what "published" looks like. One idea, one component. */
.risk { font-size: .68rem; text-transform: uppercase; letter-spacing: .05em; font-weight: 550;
        padding: 2px calc(var(--u) * 2); border: 1px solid currentColor; border-radius: 999px; }
.risk.critical, .risk.high { color: var(--estop); }
.risk.medium { color: var(--annunciator); }
.risk.low { color: var(--ink-4); }

/* --------------------------------------------------------------- the signature
   The anchor is what makes a config on a box traceable back to a rule, so it is the one
   object in this interface that gets its own physical form: printed label tape. */
.anchor, .mono.anchor, table.detail .anchor {
  display: inline-block; background: var(--anodised-deep); color: var(--ink-2);
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace; font-size: .66rem;
  letter-spacing: 0; padding: 2px calc(var(--u) * 1.75); border-radius: var(--radius-s);
}
/* Full label-tape only where the anchor IS the subject — on a capture, and on hover, where
   it is the thing you are about to copy. Everywhere else it supports the title. */
.anchor:hover, .capture-anchor .anchor { background: var(--tape); color: var(--tape-ink); }

/* --------------------------------------------------------------------- blocks */
.version { border: 0; border-radius: var(--radius-m); box-shadow: var(--lift);
           padding: calc(var(--u) * 5); margin-bottom: calc(var(--u) * 4); background: var(--canvas); }
.intent { font-size: 1rem; max-width: 68ch; color: var(--chart-ink); }
.issue { border: 1px solid var(--edge); border-left: 3px solid var(--edge-strong);
         border-radius: 0 var(--radius-m) var(--radius-m) 0;
         padding: calc(var(--u) * 3) calc(var(--u) * 4); margin-bottom: calc(var(--u) * 3);
         background: var(--canvas); }
.issue.blocking { border-left-color: var(--estop); background: var(--estop-bg); }
.issue .ack { color: var(--chart-ink); font-weight: 600; }
pre { white-space: pre-wrap; word-break: break-word; margin: 0; }
pre.artefact { background: var(--canvas); border: 1px solid var(--edge); border-radius: var(--radius-m);
               padding: calc(var(--u) * 3) calc(var(--u) * 4); font-size: .78rem; line-height: 1.55; }
details summary { cursor: pointer; }
a.current { font-weight: 600; }

@media print {
  header { position: static; background: #fff; -webkit-backdrop-filter: none; backdrop-filter: none; }
  header::after { display: none; }
  header nav, footer, .button, button, .demo-banner, .no-print { display: none; }
  body { font-size: 9.5pt; color: #000; background: #fff; }
  table, .readouts, fieldset, .version, .issue { background: #fff; }
  .readouts { border-color: #999; }
  a { color: #000; text-decoration: none; }
}

/* The status scale is fixed and validated: good / warning / serious / critical. Never themed,
   never reused for identity, and always paired with a glyph so the board reads without colour
   at all — which is also what makes it survive the A3 print on a rack door. The ink on each
   fill was computed rather than eyeballed: good, warning and serious take the dark ink
   (5.3 / 9.6 / 6.7:1), critical takes white (4.8:1). */
:root {
  /* good / warning / serious / critical are the validated scale and do not move. neutral and
     muted are ours, so they follow the warm neutrals rather than sitting cold against them. */
  --st-good: #0ca30c; --st-warning: #fab219; --st-serious: #ec835a; --st-critical: #d03b3b;
  --st-neutral: #96908a; --st-muted: #eae5de;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --st-muted: #2b2823; --st-neutral: #635d55; }
}
:root[data-theme="dark"] { --st-muted: #2b2823; --st-neutral: #635d55; }

/* Namespaced on 2026-08-22 (ADR 0006). Tabler defines .card, .row, .grid, .lead, .legend,
   .small and .tag for its own components; ours meant different things and the two silently
   fought — Tabler's .legend is a 12px colour swatch, which collapsed the fleet key on top of
   the matrix, and its .row is a grid container, which mangled every form. Prefixed classes
   cannot collide, so this class of bug is now closed rather than fixed one screenshot at a
   time. Anything unprefixed in a view is Tabler's and is meant to be.

   A paragraph by tag, a control strip by job — so it must not inherit the 74ch measure
   that keeps running text readable. */
.rb-legend { display: flex; flex-wrap: wrap; gap: calc(var(--u) * 4); align-items: center;
          margin: calc(var(--u) * 3) 0 calc(var(--u) * 4); max-width: none; }
.st { display: inline-flex; align-items: center; gap: .3rem; font-size: var(--t-small); color: var(--dim); }
.st .g { display: inline-grid; place-items: center; width: 1.15rem; height: 1.15rem;
         font-weight: 700; font-size: .78rem; border: 1px solid var(--line); }
.st-good .g     { background: var(--st-good);     color: #16191d; border-color: var(--st-good); }
.st-warning .g  { background: var(--st-warning);  color: #16191d; border-color: var(--st-warning); }
.st-serious .g  { background: var(--st-serious);  color: #16191d; border-color: var(--st-serious); }
.st-critical .g { background: var(--st-critical); color: #fff;    border-color: var(--st-critical); }
.st-neutral .g  { background: var(--st-neutral);  color: #16191d; border-color: var(--st-neutral); }
.st-muted .g    { background: var(--st-muted);    color: var(--dim); }

.scroll { overflow-x: auto; }
table.matrix { border-collapse: separate; border-spacing: 2px; width: auto; }
table.matrix th, table.matrix td { border: 0; padding: .2rem .4rem; }
table.matrix th.rowhead { text-align: left; text-transform: none; letter-spacing: 0;
  color: var(--ink); font-size: var(--t-ui); white-space: nowrap; }
table.matrix th.rowhead .rb-small { color: var(--dim); font-weight: 400; margin-left: .3rem; }
/* Horizontal by default; rotated only when the columns stop fitting (see fleet.php). */
table.matrix th.cat { font-size: .68rem; text-transform: uppercase; letter-spacing: .06em;
  padding: 0 calc(var(--u) * 2) calc(var(--u) * 1.5); white-space: nowrap;
  vertical-align: bottom; text-align: center; }
table.matrix th.cat-rot { writing-mode: vertical-rl; transform: rotate(180deg);
  height: 9rem; padding: calc(var(--u) * 0.5) 0; text-align: left; }

/* With few columns the cells take the room that is there: a bigger target on a tablet, and
   a board that occupies the screen instead of huddling in one corner. */
table.matrix-wide td.cell { width: 4.5rem; height: 2.4rem; font-size: .95rem; }
table.matrix-wide th.rowhead { padding-right: calc(var(--u) * 4); }
table.matrix td.cell { width: 2rem; text-align: center; font-weight: 700; font-size: var(--t-small);
  line-height: 1.5rem; }
table.matrix td.cell .n { font-size: .62rem; font-weight: 400; margin-left: .12rem; opacity: .75; }
td.cell.tone-good     { background: var(--st-good);     color: #16191d; }
td.cell.tone-warning  { background: var(--st-warning);  color: #16191d; }
td.cell.tone-serious  { background: var(--st-serious);  color: #16191d; }
td.cell.tone-critical { background: var(--st-critical); color: #fff; }
/* ink, not white: on the warmed neutral, white sat at 3.16:1 and ink sits at 5.59:1 */
td.cell.tone-neutral  { background: var(--st-neutral);  color: #16191d; }
td.cell.tone-muted    { background: var(--st-muted);    color: var(--dim); }

@media print {
  /* Toner, and a rack door. The glyph carries the meaning; the fill becomes an outline. */
  table.matrix td.cell { background: #fff !important; color: #000 !important;
                         border: 1px solid #999 !important; }
  .scroll { overflow: visible; }
  details { display: block; }
  details > summary { display: none; }
}
@media (forced-colors: active) {
  td.cell { forced-color-adjust: none; background: Canvas; color: CanvasText; border: 1px solid CanvasText; }
}

/* ============================================================== iPad and iPhone
   The specification's own target user is "an engineer on a tablet in an engine room, on a
   satellite link, during commissioning" — so this is not a nice-to-have, it is the primary
   device. What follows fixes the things that actually break there.

   Note what is deliberately absent: no gesture layer, no springs, no motion. This is a
   server-rendered tool that someone reads standing up with dirty hands; the Apple principle
   that applies here is flexibility and craft, not fluidity. Adding physics to a table would
   be decoration. */

/* iOS zooms any field whose text is under 16px the moment it gains focus, and leaves the
   page zoomed afterwards. It is the single most common reason a form is unusable on an
   iPhone, and it is invisible on a desktop. */
@media (pointer: coarse) {
  /* A finger is not a mouse. Everything reaches the 44px target, and control text goes to
     16px — below that, iOS zooms the page on focus, which on a form in an engine room is
     the difference between filling it in and giving up. */
  input, select, textarea, button, .button { min-height: var(--ctl-lg); height: auto; }
  input, select, textarea { font-size: 1rem; padding: .5rem var(--ctl-pad); }

  /* Apple's minimum comfortable target is 44pt. Dense tables cannot give every row that
     height without becoming unreadable, so the padding goes on the control, not the row. */
  button, .button { padding: .5rem 1rem; font-size: 1rem; }
  header nav a { padding: .5rem 0; }
  table td a, table th a { display: inline-block; padding: .35rem 0; }
  details summary { padding: .4rem 0; }
  label.chk { padding: .3rem .8rem .3rem 0; }
}

/* Respond on press, not on release — the press state is the feedback, and it has to be
   instant. */
button:active, .button:active { transform: scale(.985); }
button, .button { transition: transform 90ms ease-out; }
@media (prefers-reduced-motion: reduce) {
  button, .button { transition: none; }
  button:active, .button:active { transform: none; }
}

@media (max-width: 820px) {
  /* Type and spacing in rem so the reader's own text-size setting scales the layout with
     the text instead of breaking it. */
  main { padding: 1rem; }
  header { padding: .5rem .9rem calc(.5rem + env(safe-area-inset-bottom, 0px)); gap: .8rem; }
  header .who { margin-left: 0; width: 100%; order: 3; }
  footer { padding-bottom: calc(1rem + env(safe-area-inset-bottom, 0px)); }

  h1 { font-size: 1.3rem; }

  /* The attribute editor is a two-column grid on a desktop. At this width the label and the
     field stack, or every value ends up three characters wide. */
  label.attr { grid-template-columns: 1fr; gap: .2rem; }
  .rb-row { gap: .5rem; }
  .rb-row > label { flex: 1 1 100%; }

  form.inline { flex-wrap: wrap; }
  form.inline input { width: 100%; }

  /* Wide tables scroll inside their own container; the page body never does. */
  .scroll, table { -webkit-overflow-scrolling: touch; }
  table { font-size: .85rem; }
  table th, table td { padding: .3rem .35rem; }
  table.kv th { width: auto; }
  pre.artefact { font-size: .72rem; }
}

/* The fleet board is the one screen that genuinely cannot fit: a hundred vessels against
   every category. It scrolls sideways, and the vessel column stays put — a matrix you can
   scroll but not identify is a matrix you cannot read. */
@media (max-width: 1100px) {
  table.matrix th.rowhead {
    position: sticky; left: 0; z-index: 1;
    background: var(--bg);
    box-shadow: 1px 0 0 var(--line);
  }
  table.matrix th.rowhead .rb-small { display: block; margin-left: 0; }
}

/* Very narrow: the matrix is the point of this screen, so everything that is not the matrix
   gives up its width first. Lifecycle and the capture date are one tap away on the vessel. */
@media (max-width: 640px) {
  .secondary { display: none; }
}

@media (max-width: 520px) {
  header nav { gap: .55rem .8rem; font-size: .88rem; }
  header .brand { font-size: 1rem; }
  /* The stat strip. Renamed from .card on 2026-08-22: Tabler owns .card now (ADR 0006), and
   the two definitions collided silently — this one's column-reverse flipped every grid card
   so the toolbar rendered under the table. Two design systems, one class name; exactly the
   drift the ADR exists to bound. */
.readouts { gap: .4rem; }
  .readout { flex: 1 1 6rem; min-width: 0; }
  .readout b { font-size: 1.3rem; }
  table.matrix th.cat { height: 7rem; font-size: .62rem; }
  table.matrix td.cell { width: 1.7rem; }
}

/* ------------------------------------------------------------- fleet drill-down
   The scope bar is the navigation for a filtered view: it states the current scope as data
   rather than as a breadcrumb, because this is a filter and not a tree — you arrive from a
   cell, a column, a row or a legend entry, and any of them can be dropped independently.
   Depth stays borders-only, matching the rest of the tool. */
.scopebar { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; margin: .6rem 0 1rem; }
.scopebar .scope-all { font-size: .85rem; }
.scope {
  display: inline-flex; align-items: stretch; border: 1px solid var(--line);
  font-size: .8rem; line-height: 1.9;
}
.scope .k { padding: 0 .45rem; color: var(--dim); background: var(--panel);
            border-right: 1px solid var(--line); text-transform: uppercase;
            font-size: .68rem; letter-spacing: .05em; align-self: center; line-height: 1.9; }
.scope .v { padding: 0 .5rem; font-weight: 600; }
.scope .drop { padding: 0 .45rem; border-left: 1px solid var(--line); text-decoration: none;
               color: var(--dim); }
.scope .drop:hover, .scope .drop:focus { background: var(--bad); color: #fff; }

/* One size, three tiers: the title leads on weight, the anchor recedes on colour, the
   reason sits between them. Size alone would flatten all three. */
table.detail .lead-cell { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; }
table.detail .rule-title { font-weight: 600; text-decoration: none; }
table.detail .rule-title:hover { text-decoration: underline; }

table.detail .why { max-width: 30rem; }
/* An identifier and the name beside it are one object; breaking them across two lines makes
   the reader reassemble it every row. */
table.detail td:nth-child(3), table.detail td:nth-child(1) { white-space: nowrap; }
table.detail td:nth-child(3) .rb-small { white-space: normal; }
table.detail .why a { white-space: nowrap; }
table.detail td { vertical-align: top; }
a.risk { text-decoration: none; }
a.st { text-decoration: none; }
a.st:hover .g, .st a:focus .g { outline: 2px solid currentColor; outline-offset: 1px; }
td.cell a { display: block; text-decoration: none; color: inherit; }
td.cell a:focus-visible { outline: 2px solid var(--ink); outline-offset: -2px; }
th.cat a { color: inherit; text-decoration: none; }
th.cat a:hover { text-decoration: underline; }

/* --------------------------------------------------------------- saved views
   A person's own way in, listed above the board. Quiet: it is a shortcut, not the subject
   of the page, so it sits in the same tonal band as the note text and earns attention only
   through the link. */
.views { margin: .8rem 0 1rem; }
.views-h { font-size: .8rem; text-transform: uppercase; letter-spacing: .05em;
           color: var(--dim); border: 0; margin: 0 0 .3rem; padding: 0; }
ul.viewlist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .25rem; }
ul.viewlist li { display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap; font-size: .85rem; }
ul.viewlist a { font-weight: 600; }
.scope-sum { color: var(--dim); }
.by { color: var(--dim); font-size: .75rem; border: 1px solid var(--line); padding: 0 .3rem; }
.inline-del { display: inline; margin: 0; }
button.linkish { background: none; border: 0; color: var(--dim); font: inherit; font-size: .78rem;
                 padding: 0; cursor: pointer; text-decoration: underline; }
button.linkish:hover, button.linkish:focus { color: var(--bad); }
.saveview { margin-top: 2rem; padding-top: 1rem; border-top: 1px solid var(--line); }
.saveview h2 { font-size: 1rem; }
.saveview form.inline input[name="view_name"] { width: 22rem; max-width: 100%; }

/* The demo banner is deliberately not dismissible. A demo record that looks like a real one
   is the whole risk; a banner you can hide is a banner that gets hidden. */
.demo-banner { margin-bottom: 1rem; }
.demo-banner code { background: transparent; border: 1px solid var(--line); }

/* ------------------------------------------------- classification and promotion */
.facet { display: inline-flex; border: 1px solid var(--line); font-size: .78rem; margin-right: .3rem; }
.facet .k { padding: 0 .35rem; background: var(--panel); color: var(--dim);
            border-right: 1px solid var(--line); }
.facet .v { padding: 0 .4rem; }
details.classify, details.promote { border: 1px solid var(--line); padding: .5rem .8rem; margin: .6rem 0; }
details.classify summary, details.promote summary { font-weight: 600; font-size: .9rem; }
details.promote[open] summary { margin-bottom: .6rem; }
.panel-review { border: 1px solid var(--line); border-left: 3px solid var(--ink);
                padding: .8rem 1rem; margin: .6rem 0; display: flex; flex-direction: column; gap: .5rem; }
.panel-review h3 { margin: 0; font-size: .95rem; }
.panel-review .bad { color: var(--bad); font-weight: 600; }
.facet-block { margin-bottom: 1.2rem; }

/* An identifier in a plain table: same typeface, tabular figures, slightly tight. Monospace
   is reserved for values whose characters are the subject — a hash, a config body. See the
   note in tabler-bridge.css; changed 2026-08-22 because a second face in a data cell reads
   as a terminal pasted into an interface. */
.rb-ident { font-variant-numeric: tabular-nums; letter-spacing: -.004em; }
