/* Reusable artifact styles. Two web-safe typefaces; no font downloads. */

:root {

  /* --- Colour ------------------------------------------------------------
     REPLACED 22 Sep 2026, taken from the reference package rather than
     re-derived. The source is architecture.html and journey.html, which agree
     with each other exactly and carry the full scale; workspace.css and the
     deck each hold a slightly different version of the same idea, and that
     drift is the thing REFERENCE.md says not to copy.

     Role names are ours and did not change, so nothing downstream breaks.
     The values are theirs — with three measured exceptions.

     THREE TOKENS ARE DARKER THAN THE REFERENCE, and this is deliberate.
     Measured against WCAG AA at the sizes we actually use them, the
     reference's own values fail:

       --accent     #008D8F on paper at 12px = 3.97, needs 4.5
       --ink-faint  #71858B on surface at 12px = 3.37, needs 4.5
       --ink-soft   #5D7077 on rose at 9px = 4.09, needs 4.5

     Each has been darkened by the smallest amount that clears 4.5 against
     every ground it sits on, hue held. The brand survives — it is the same
     teal — and the failure does not. `tools/gate.py --contrast` re-measures
     every pairing, so this cannot quietly regress.

     --accent-deep is ours: white on the accent is 3.97, so a filled button
     needs a darker ground than a text colour does. */

  --ink:        #102D38;  /* headings, strong body, and the dark bar         */
  --ink-soft:   #576970;  /* body copy, secondary lines                      */
  --ink-faint:  #5F7075;  /* source notes, inactive nav items                */
  --accent:     #00797B;  /* eyebrows, the key line, active labels, links    */
  --accent-deep:#008385;  /* the filled button, which needs a darker ground  */
  --accent-lift:#8CD6CF;  /* eyebrow and link on the dark bar, active tab    */
  --paper:      #FFFDF8;  /* cards, table body rows, the nav bar             */
  --surface:    #F3EFE4;  /* the page ground                                 */
  --deep:       #102D38;  /* table headers, statement bars, dark cards       */
  --rule:       #C8D0CA;  /* borders and dividers                            */

  /* The reference has no coral. Where our deck used one colour for sequence,
     direction and alarm, the reference splits the job in three: sequence
     numerals are teal, attention is gold, and only genuine alarm is red. That
     split is better and it is what these now mean. */

  --coral:      #A74332;  /* ALARM ONLY — a blocked item, a risk, a stop     */
  --gold:       #D5962B;  /* attention: an active underline, a third axis    */
  --amber:      #A6761B;  /* the boundary rule — read this before you believe */
  --focus:      #E3A82C;  /* focus ring, and nothing else                    */

  /* --- The categorical scale ---------------------------------------------
     Four low-chroma paper fills that group. They separate domains on a board
     AND they are the evidence states — one scale, so a board and its legend
     cannot drift apart. Each fill has exactly one ink partner; never pair
     across. There is no fifth group. */

  --mint:       #DCEEE8;  /* first group   / observed, available            */
  --blue:       #DFEAF0;  /* second group  / reported                       */
  --sand:       #EFE4C8;  /* third group   / proposed                       */
  --rose:       #F1E1DC;  /* fourth group  / to validate                    */
  --stone:      #ECEBE5;  /* the inapplicable cell, not a fifth group       */

  --on-mint:    #075E62;
  --on-blue:    #285A72;
  --on-sand:    #765500;
  --on-rose:    #833F30;
  --on-stone:   #5C6463;

  --navy:       #173B52;  /* domain border, the heaviest rule we own        */
  --shadow:     0 8px 24px #102D3815;

  /* The reference gives a tile a hard offset shadow rather than a soft one —
     it reads as a printed card rather than a floating panel, which is the
     difference between a working document and a dashboard. */
  --shadow-card: 2px 3px 0 #102D3811;

  /* --- Type --------------------------------------------------------------
     Also the reference's. Georgia carries statements and never runs long:
     headings and short labels only. The body face is the system stack the
     reference actually renders in — its working formats name Inter and never
     load it, so what a client saw was always this. */

  --font-display: Georgia, "Times New Roman", serif;
  --font-sans:    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
                  "Helvetica Neue", Arial, sans-serif;
  --font-mono:    ui-monospace, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;

  /* The steps the reference actually uses, in both registers. Nothing between
     them: "slightly bigger" moves up one step, it does not invent 18.5px. */

  --t-72: 72px;  /* the one number on a slide                */
  --t-58: 58px;  /* Georgia — deck title, top of its clamp   */
  --t-40: 40px;  /* Georgia — page title                     */
  --t-38: 38px;  /* Georgia — application title, top of clamp*/
  --t-30: 30px;  /* Georgia — section title, and a deck title on a phone */
  --t-23: 23px;  /* the key takeaway                         */
  --t-21: 21px;  /* Georgia — card and column title          */
  --t-19: 19px;  /* Georgia — sub-head                       */
  --t-17: 17px;  /* document body                            */
  --t-16: 16px;  /* table cell                               */
  --t-15: 15px;  /* dense table                              */
  --t-14: 14px;  /* application body, card copy              */
  --t-13: 13px;  /* dense caption                            */
  --t-12: 12px;  /* source note, boundary lead               */
  --t-11: 11px;  /* application — card title, control label  */
  --t-10: 10px;  /* uppercase — eyebrow, nav, footer         */
  --t-9:   9px;  /* uppercase — smallest caption             */
  --t-8:   8px;  /* application — pill, card sub-label       */

  --track-caps: 0.08em;

  /* --- Measure -----------------------------------------------------------
     Reference canvas 1280 x 720. Side margin 56px, symmetric. */

  --canvas-w:  1280px;
  --canvas-h:  720px;
  --margin-x:  56px;
  --topbar-h:  69px;

  /* How wide a line of prose is allowed to run. The canvas is 1280 because a
     board, a table and a diagram need the room; a sentence does not, and left
     alone one ran 155 characters.

     `ch` is the width of a zero and real prose averages narrower, so this is
     not 70 characters. It resolves to 662px at 17px, and what that holds
     depends on the words: 82 characters of ordinary body copy, 89 of a lede
     written in shorter ones. Both sit inside the 45-85 band reading research
     gives, at the top of it. It was 78ch first, which measured 93 and was
     outside.

     It is a token because it was six literals in the stylesheet, which is the
     shape every drift here has taken. */
  --measure: 70ch;

  --gap:    24px;   /* between columns and cards */
  --gap-sm: 12px;
  --gap-lg: 40px;   /* between blocks down a page */
}

/* --------------------------------------------------------------------------
   Ground
   -------------------------------------------------------------------------- */

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--surface);
  color: var(--ink-soft);
  font-family: var(--font-sans);
  font-size: var(--t-17);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent); }

/* --------------------------------------------------------------------------
   Page frame — every client-facing page carries this, and it is the strongest
   piece of brand recognition the decks have.
   -------------------------------------------------------------------------- */

.page {
  width: var(--canvas-w);
  /* The canvas is a canvas, not a minimum. Without this the register
     rendered 1155px wide inside a 375px phone and the whole page scrolled
     sideways — 780px of it. The deck and the application registers both had
     a max-width breakpoint; this one, which had no live page for weeks, had
     never been opened narrow by anybody. */
  max-width: 100%;
  min-height: var(--canvas-h);
  margin: 0 auto;
  background: var(--surface);
  display: flex;
  flex-direction: column;
}

/* Anything with a fixed inner width — a table, a board, a poster frame —
   scrolls inside its own box rather than pushing the page out. The reader
   swipes one table; they do not swipe the article. */
@media (max-width: 760px) {
  .page-body { padding: var(--gap-md) var(--gap-md); }

  /* The bar wraps rather than running off the side. */
  .topbar { flex-wrap: wrap; gap: var(--gap-sm); padding: 10px 16px; }
  .topbar-page { flex: 1 1 100%; }
  .topbar-nav { flex-wrap: wrap; }

  /* Rows of fixed-width cells become rows of flexible ones. */
  .flow-row, .moment-row, .stat-row, .dot-legend, .chip-row { flex-wrap: wrap; }
  .stat-tile, .moment { min-width: 0; flex: 1 1 40%; }

  /* A long path in a code span breaks rather than pushing the article out. */
  .page-body code { overflow-wrap: anywhere; }
}

/* Below the canvas, not below a phone. The appendix register was clean at
   375 and 308px over at 768, because the scroll containers sat behind the
   760px query and the canvas is 1280: anything computed at canvas width can
   overflow at any width under it. */
@media (max-width: 1279px) {
  /* A table scrolls inside itself, in whichever register it sits: the
     appendix pages are .app, not .page, and a rule scoped to .page-body
     walked straight past them. display:block is what makes overflow-x apply
     to a table at all — on a table element it is otherwise inert. */
  .data-table { display: block; overflow-x: auto; max-width: 100%; }

  /* Anything drawn at a computed width scrolls in its own box. The reader
     swipes one figure; they do not swipe the article. */
  .page-body .poster-frame,
  .page-body .board { max-width: 100%; overflow-x: auto; }

  /* The swimlane is a matrix and declares its own minimum — 172px of label
     plus a column per stage — so it cannot shrink and must not try. The box
     it sits in scrolls instead. */
  .board-wrap { max-width: 100%; overflow-x: auto; }
}

/* A document page scrolls; a slide-shaped page does not. Both carry the frame. */
.page-body {
  flex: 1;
  padding: var(--gap-lg) var(--margin-x);
}

/* Prose gets a measure. The canvas is 1280px wide because a diagram, a table
   and a board need the room; a sentence does not. Left alone, a paragraph in
   here ran 155 characters to the line — measured, not estimated — while the
   lede directly above it was capped and ran 96. The theme was already
   carrying the decision and applying it to one element.

   Only the skeleton's own prose is constrained. Anything the page puts in a
   column, a card or a table has chosen its width already. */
.page-body > p,
.page-body > ul,
.page-body > ol,
.page-body > blockquote {
  max-width: var(--measure);
}

/* --------------------------------------------------------------------------
   01 — topbar
   Dual logo, the partnership mark, section tabs, page number.
   The x between the logos is a literal multiplication sign, not a decoration.
   -------------------------------------------------------------------------- */

.topbar {
  height: var(--topbar-h);
  flex: none;
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
  display: flex;
  align-items: center;
  gap: var(--gap);
  padding: 0 var(--margin-x);
}

.topbar-logos {
  display: flex;
  align-items: center;
  gap: var(--gap-sm);
  flex: none;
}

.logo {
  font-family: var(--font-sans);
  font-size: var(--t-14);
  font-weight: bold;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--ink);
}

.topbar-mark {
  font-family: var(--font-sans);
  font-size: var(--t-14);
  color: var(--ink-faint);
}

.topbar-nav {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--gap-lg);
  height: 100%;
}

.nav-item {
  display: flex;
  align-items: center;
  height: 100%;
  font-size: var(--t-10);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--ink-faint);
  text-decoration: none;
  border-bottom: 3px solid transparent;
  padding-top: 3px;
}

.nav-item.is-active {
  color: var(--accent);
  border-bottom-color: var(--accent-lift);
}

.topbar-page {
  flex: none;
  margin-left: var(--gap-lg);
  font-size: var(--t-10);
  letter-spacing: var(--track-caps);
  color: var(--ink-faint);
}

/* --------------------------------------------------------------------------
   02 — eyebrow
   -------------------------------------------------------------------------- */

.eyebrow {
  margin: 0 0 var(--gap-sm);
  font-family: var(--font-sans);
  font-size: var(--t-10);
  font-weight: bold;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--accent);
}

/* --------------------------------------------------------------------------
   03 — display heading
   -------------------------------------------------------------------------- */

.display {
  margin: 0 0 var(--gap);
  font-family: var(--font-display);
  font-size: var(--t-40);
  font-weight: normal;
  line-height: 1.15;
  color: var(--ink);
}

/* Sub-head. Georgia, one step down from the card title. */
.subhead {
  margin: 0 0 var(--gap-sm);
  font-family: var(--font-display);
  font-size: var(--t-19);
  font-weight: normal;
  line-height: 1.3;
  color: var(--ink);
}

/* --------------------------------------------------------------------------
   04 — lede
   -------------------------------------------------------------------------- */

/* The lede carries the strong ink, not the soft. It was set in --ink-soft,
   which is also the colour body copy inherits, at the same size body copy
   inherits — so the component changed nothing a reader could see and the
   opening statement of a page read exactly like the paragraph after it.
   The reference sets its lead in the strong colour on every page that has
   one, whatever it does with the body around it. */
.lede {
  margin: 0 0 var(--gap-lg);
  max-width: var(--measure);
  font-size: var(--t-17);
  color: var(--ink);
}

/* Ordinary body copy. */
/* Both restate what a paragraph already inherits, so what they actually add
   is the measure — which is the reason to reach for them when prose sits
   somewhere .page-body's own rule cannot see it. */
.body { font-size: var(--t-17); color: var(--ink-soft); max-width: var(--measure); }
.body-dense { font-size: var(--t-14); color: var(--ink-soft); max-width: var(--measure); }

/* --------------------------------------------------------------------------
   Column grids — how the column components are placed. 3-up or 4-up.
   -------------------------------------------------------------------------- */

.cols {
  display: grid;
  gap: var(--gap);
  align-items: start;
}
.cols-2 { grid-template-columns: repeat(2, 1fr); }
.cols-3 { grid-template-columns: repeat(3, 1fr); }
.cols-4 { grid-template-columns: repeat(4, 1fr); }

/* --------------------------------------------------------------------------
   05 — icon column
   Thin line icon in accent, Georgia title, grey body.
   The icon is an inline SVG drawn with currentColor — no icon font.
   -------------------------------------------------------------------------- */

.icon-col-icon {
  display: block;
  width: 32px;
  height: 32px;
  margin-bottom: var(--gap-sm);
  color: var(--accent);
}
.icon-col-icon svg { width: 100%; height: 100%; display: block; }

.icon-col-title {
  margin: 0 0 6px;
  font-family: var(--font-display);
  font-size: var(--t-21);
  font-weight: normal;
  color: var(--ink);
}

.icon-col-body {
  margin: 0;
  font-size: var(--t-14);
  color: var(--ink-soft);
}

/* --------------------------------------------------------------------------
   06 — numbered column
   Coral 01, Georgia title, grey body. The numeral marks sequence.
   -------------------------------------------------------------------------- */

.num-col-num {
  display: block;
  margin-bottom: 6px;
  font-family: var(--font-sans);
  font-size: var(--t-23);
  font-weight: bold;
  color: var(--accent);
}

.num-col-title {
  margin: 0 0 6px;
  font-family: var(--font-display);
  font-size: var(--t-21);
  font-weight: normal;
  color: var(--ink);
}

.num-col-body {
  margin: 0;
  font-size: var(--t-14);
  color: var(--ink-soft);
}

/* --------------------------------------------------------------------------
   07 — data table
   Deep header row, white bold sans; paper body rows.
   -------------------------------------------------------------------------- */

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--t-16);
  color: var(--ink-soft);
}

.data-table thead th {
  background: var(--deep);
  color: var(--paper);
  font-weight: bold;
  font-size: var(--t-14);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  text-align: left;
  padding: 10px 14px;
}

.data-table tbody td {
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
  padding: 10px 14px;
  vertical-align: top;
}

.data-table tbody tr:last-child td { border-bottom: none; }

/* --------------------------------------------------------------------------
   08 — key line
   The one sentence the page exists to deliver. One per page.
   -------------------------------------------------------------------------- */

.key-line {
  margin: var(--gap-lg) 0 var(--gap-sm);
  font-family: var(--font-sans);
  font-size: var(--t-23);
  font-weight: bold;
  line-height: 1.35;
  color: var(--accent);
}

/* --------------------------------------------------------------------------
   09 — source note
   -------------------------------------------------------------------------- */

.source-note {
  margin: 0;
  font-size: var(--t-12);
  color: var(--ink-faint);
}

/* --------------------------------------------------------------------------
   10 — footer
   Slash-separated, uppercase. Sits at the foot of the frame.
   -------------------------------------------------------------------------- */

.page-footer {
  flex: none;
  padding: var(--gap-sm) var(--margin-x) var(--gap);
  font-size: var(--t-10);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* --------------------------------------------------------------------------
   11 — layer card, and the stack that connects them
   Paper card, bold sans title over grey sub, vertical connector between
   stacked cards, mint for the highlighted one.
   -------------------------------------------------------------------------- */

.layer-stack { display: flex; flex-direction: column; }

.layer-card {
  background: var(--paper);
  border: 1px solid var(--rule);
  padding: 14px 18px;
}

.layer-card.is-highlight {
  background: var(--mint);
  border-color: var(--accent-lift);
}

.layer-card-title {
  margin: 0 0 2px;
  font-family: var(--font-sans);
  font-size: var(--t-16);
  font-weight: bold;
  color: var(--ink);
}

.layer-card-sub {
  margin: 0;
  font-size: var(--t-14);
  color: var(--ink-soft);
}

/* The connector. A hairline between stacked cards, drawn by the gap itself. */
.layer-stack .layer-card + .layer-card { margin-top: 20px; position: relative; }
.layer-stack .layer-card + .layer-card::before {
  content: "";
  position: absolute;
  left: 50%;
  top: -21px;
  width: 1px;
  height: 20px;
  background: var(--rule);
}

/* --------------------------------------------------------------------------
   12 — chip
   Mint pill, accent bold. Used for personas.
   -------------------------------------------------------------------------- */

.chip {
  display: inline-block;
  background: var(--mint);
  color: var(--accent);
  font-size: var(--t-12);
  font-weight: bold;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  padding: 5px 12px;
  border-radius: 999px;
}

.chip-row { display: flex; flex-wrap: wrap; gap: var(--gap-sm); }

/* --------------------------------------------------------------------------
   13 — flow row
   Icon, coral arrow, Georgia label. Left to right.
   -------------------------------------------------------------------------- */

.flow-row {
  display: flex;
  align-items: center;
  gap: var(--gap);
}

.flow-item {
  min-width: 96px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  text-align: center;
}

.flow-item .icon-col-icon { margin-bottom: 0; }

.flow-label {
  font-family: var(--font-display);
  font-size: var(--t-19);
  color: var(--ink);
}

.flow-arrow {
  flex: none;
  color: var(--coral);
  align-self: flex-start;
  margin-top: 5px;
  font-size: var(--t-23);
  line-height: 1;
}

/* --------------------------------------------------------------------------
   14 — statement bar
   Full width, solid deep or coral, small uppercase label left, one sentence
   beside it. Coral variant is for a boundary or a risk, never for emphasis.
   -------------------------------------------------------------------------- */

.statement-bar {
  display: flex;
  align-items: baseline;
  gap: var(--gap);
  background: var(--deep);
  color: var(--paper);
  padding: 18px var(--gap);
}

.statement-bar.is-coral { background: var(--coral); }

.statement-label {
  flex: none;
  min-width: 140px;
  font-size: var(--t-10);
  font-weight: bold;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
}

.statement-text {
  font-family: var(--font-display);
  font-size: var(--t-19);
  line-height: 1.35;
}

/* --------------------------------------------------------------------------
   15 — moment circle
   Filled circle, alternating deep / accent-lift / coral, icon and number
   inside, coral arrows between.
   -------------------------------------------------------------------------- */

.moment-row {
  display: flex;
  align-items: center;
  gap: var(--gap-sm);
}

.moment {
  flex: none;
  width: 108px;
  height: 108px;
  border-radius: 50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  text-align: center;
  color: var(--paper);
}

.moment.is-deep { background: var(--deep); }
.moment.is-lift { background: var(--accent-lift); color: var(--deep); }
.moment.is-coral { background: var(--coral); }

.moment .icon-col-icon { width: 22px; height: 22px; margin: 0; color: inherit; }

.moment-num {
  font-size: var(--t-10);
  font-weight: bold;
  letter-spacing: var(--track-caps);
}

.moment-label {
  font-size: var(--t-9);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  max-width: 88px;
}

/* --------------------------------------------------------------------------
   16 — stat tile
   Very large Arial numeral on deep, caption beneath.
   -------------------------------------------------------------------------- */

.stat-tile {
  background: var(--deep);
  color: var(--paper);
  padding: var(--gap) var(--gap);
}

.stat-num {
  display: block;
  font-family: var(--font-sans);
  font-size: var(--t-40);
  font-weight: bold;
  line-height: 1.05;
}

.stat-caption {
  display: block;
  margin-top: 8px;
  font-size: var(--t-12);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
}

/* --------------------------------------------------------------------------
   17 — media card
   Photograph, name, count. The photograph is supplied per artifact; the
   component provides the frame. .media-photo is the slot — put an <img> in it
   or leave it empty and it reads as a placeholder.
   -------------------------------------------------------------------------- */

.media-card { background: var(--paper); border: 1px solid var(--rule); }

.media-photo {
  display: block;
  width: 100%;
  height: 140px;
  background: var(--rule);
  object-fit: cover;
}

.media-body { padding: var(--gap-sm) 14px; }

.media-name {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--t-19);
  color: var(--ink);
}

.media-count {
  margin: 2px 0 0;
  font-size: var(--t-10);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* --------------------------------------------------------------------------
   18 — dot legend
   Coloured dot plus uppercase label, inline row.
   -------------------------------------------------------------------------- */

.dot-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--gap);
}

.dot-item {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--t-10);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--ink-soft);
}

.dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--ink-faint);
  flex: none;
}

.dot.is-accent { background: var(--accent); }
.dot.is-lift   { background: var(--accent-lift); }
.dot.is-deep   { background: var(--deep); }
.dot.is-coral  { background: var(--coral); }
.dot.is-faint  { background: var(--ink-faint); }

/* --------------------------------------------------------------------------
   19 — panel card
   Solid accent-lift or surface, coral numeral, uppercase title, middot list.
   -------------------------------------------------------------------------- */

.panel-card {
  background: var(--surface);
  border: 1px solid var(--rule);
  padding: var(--gap);
}

.panel-card.is-lift {
  background: var(--accent-lift);
  border-color: var(--accent-lift);
  color: var(--deep);
}

.panel-num {
  display: block;
  margin-bottom: 8px;
  font-family: var(--font-sans);
  font-size: var(--t-23);
  font-weight: bold;
  color: var(--accent);
}

.panel-title {
  margin: 0 0 8px;
  font-family: var(--font-sans);
  font-size: var(--t-14);
  font-weight: bold;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--ink);
}

/* Coral on accent-lift does not carry. The numeral still marks sequence by
   its position; on the lift variant it takes --deep. */
.panel-card.is-lift .panel-num,
.panel-card.is-lift .panel-title { color: var(--deep); }

/* The middot list — a device, not a component. Inline, separated by a middot,
   used wherever a bulleted list would be too heavy.
   Profiles · history · preferences · referral */
.middot-list {
  margin: 0;
  font-size: var(--t-14);
  color: var(--ink-soft);
}

.panel-card.is-lift .middot-list { color: var(--deep); }

/* --------------------------------------------------------------------------
   20 — split cover
   Content left ~52%, full-bleed photograph right. The dark variant puts the
   photograph full-bleed behind a scrim with white type.
   -------------------------------------------------------------------------- */

.split-cover {
  display: grid;
  grid-template-columns: 52% 48%;
  min-height: 420px;
  background: var(--paper);
}

.split-cover-content {
  padding: var(--gap-lg);
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.split-cover-photo {
  background: var(--rule);
  object-fit: cover;
  width: 100%;
  height: 100%;
  display: block;
}

.split-cover.is-dark {
  grid-template-columns: 1fr;
  background: var(--deep);
  position: relative;
}

/* The photograph sits behind a scrim. An empty slot stays out of the way, so
   the panel still reads as deep when no photograph has been supplied yet. */
.split-cover.is-dark .split-cover-photo {
  position: absolute;
  inset: 0;
  background: transparent;
}

.split-cover.is-dark img.split-cover-photo { opacity: 0.35; }

.split-cover.is-dark .split-cover-content {
  position: relative;
  color: var(--paper);
  max-width: 62%;
}

.split-cover.is-dark .display,
.split-cover.is-dark .subhead { color: var(--paper); }

.split-cover.is-dark .lede { color: var(--rule); }

.split-cover.is-dark .eyebrow { color: var(--accent-lift); }

/* --------------------------------------------------------------------------
   Spacing helpers. Layout is free; vocabulary is not. These exist so that a
   page does not reach for an inline style to put air between two blocks.
   -------------------------------------------------------------------------- */

.stack > * + * { margin-top: var(--gap-lg); }
.stack-sm > * + * { margin-top: var(--gap); }
.bleed { margin-left: calc(var(--margin-x) * -1); margin-right: calc(var(--margin-x) * -1); }

/* --------------------------------------------------------------------------
   21 — system map
   Added in phase 1, for the technical map. The frame, the controls and the
   detail panel around a diagram, plus the evidence states the renderer paints
   onto its own output.

   The diagram inside is Mermaid's. Everything here dresses it in the house
   palette and gives it the two behaviours Mermaid has no opinion about:
   evidence, and focus. Both are class swaps — nothing re-renders, because a
   re-render makes every node jump and that is unusable in a live room.
   -------------------------------------------------------------------------- */

/* The map gets the full width by default: Mermaid lays a layered flowchart
   out wide, and anything narrower scales the labels down until they cannot be
   read across a room. `.is-split` puts the detail beside it when the diagram
   is small enough to afford it. */
.map { display: grid; grid-template-columns: 1fr; gap: var(--gap); align-items: start; }
.map.is-split { grid-template-columns: 1fr 320px; }

/* A fixed viewport, not an auto height: svg-pan-zoom needs stable bounds to
   fit and centre into, and on a projector you want the frame to stay put
   while the diagram moves inside it. */
.map-frame {
  background: var(--paper);
  border: 1px solid var(--rule);
  padding: var(--gap);
  overflow: hidden;
}

.map-frame svg { display: block; width: 100%; height: 560px; }

/* Mermaid injects its own stylesheet into the SVG, keyed on the render id —
   an ID selector, which outranks anything here on specificity alone. The
   evidence states are the whole argument of the map, so they are forced.
   This is the only place in the theme that does this, and it is why. */

/* Controls. A chip that is a button — same pill, same palette, pressed state
   added. `aria-pressed` carries the state for anything that is not a mouse. */
button.chip {
  font-family: var(--font-sans);
  border: 1px solid transparent;
  cursor: pointer;
}

button.chip[aria-pressed="true"] {
  background: var(--accent);
  color: var(--paper);
}

/* --- evidence, as drawn on the nodes ------------------------------------ */

.map-node rect,
.map-node polygon,
.map-node circle { stroke-width: 1.5px !important; }

.map-node.ev-observed rect,
.map-node.ev-observed polygon { stroke: var(--deep) !important; fill: var(--mint) !important; }

.map-node.ev-reported rect,
.map-node.ev-reported polygon { stroke: var(--on-blue) !important; fill: var(--blue) !important; }

.map-node.ev-proposed rect,
.map-node.ev-proposed polygon { stroke: var(--on-sand) !important; fill: var(--sand) !important; stroke-dasharray: 4 3 !important; }

.map-node.ev-validate rect,
.map-node.ev-validate polygon { stroke: var(--coral) !important; fill: var(--rose) !important; stroke-dasharray: 4 3 !important; }

/* Mermaid puts the edge class on the <path> itself; other shapes wrap it in a
   <g>. Both are matched, so a change in Mermaid's output cannot silently stop
   the evidence showing. */
path.map-edge.ev-validate,
path.map-edge.ev-proposed,
.map-edge.ev-validate > path,
.map-edge.ev-proposed > path { stroke-dasharray: 5 4 !important; }

.map-node.is-selected rect,
.map-node.is-selected polygon { stroke: var(--accent) !important; stroke-width: 3px !important; }

/* --- the overlay --------------------------------------------------------
   Switch it on in the room and the unknowns are the only thing left lit. The
   map stops being a picture of their systems and becomes the argument for the
   engagement. */

.map-frame.is-evidence .map-node { opacity: 0.32; }

.map-frame.is-evidence .map-node.ev-validate,
.map-frame.is-evidence .map-node.ev-proposed { opacity: 1; }

.map-frame.is-evidence .map-node.ev-validate rect,
.map-frame.is-evidence .map-node.ev-validate polygon { stroke-width: 3px !important; fill: var(--rose) !important; }

.map-frame.is-evidence .map-edge { opacity: 0.22; }

/* --- journey focus ------------------------------------------------------
   Dim the rest, never hide it. Hiding needs a reflow and a reflow needs a
   re-render. */

.map-frame.is-focused .map-node,
.map-frame.is-focused .map-edge { opacity: 0.2; }

.map-frame.is-focused .map-node.is-lit,
.map-frame.is-focused .map-edge.is-lit { opacity: 1; }

.map-frame.is-focused .map-node.is-lit rect,
.map-frame.is-focused .map-node.is-lit polygon { stroke: var(--accent) !important; stroke-width: 2.5px !important; }

.map-frame.is-focused path.map-edge.is-lit,
.map-frame.is-focused .map-edge.is-lit > path {
  stroke: var(--coral) !important;
  stroke-width: 2.5px !important;
}

/* Mermaid's own furniture, held in the house palette. */
.map-frame .cluster rect { fill: var(--surface) !important; stroke: var(--rule) !important; }
.map-frame .cluster text { font-size: var(--t-10); letter-spacing: var(--track-caps); fill: var(--ink-faint) !important; }
.map-frame .nodeLabel, .map-frame .edgeLabel { font-family: var(--font-sans); }
.map-frame .edgeLabel { font-size: var(--t-12); }

/* Mermaid measures a label, then draws it into a foreignObject sized to that
   measurement. Anything that changes the label's font size AFTER the measure
   truncates it mid-word. Reveal's own `p` rule does exactly that, so the map's
   labels are pinned to the size Mermaid measured with, wherever it is embedded. */
.map-frame foreignObject p,
.map-frame .nodeLabel,
.map-frame .nodeLabel p { font-size: var(--t-14) !important; line-height: 1.5 !important; }

/* A map on a slide: fills the frame it is given and letterboxes rather than
   overflowing, because a slide cannot scroll. */
.map-frame.is-slide { height: 460px; padding: var(--gap-sm); }
.map-frame.is-slide svg { width: 100%; height: 100%; }

/* Edge labels live in their own group, so they do not inherit an edge's
   dimming and would otherwise float free of the line they name. */
.map-frame.is-evidence .edgeLabels,
.map-frame.is-focused .edgeLabels { opacity: 0.22; }

/* The short frame, for a small map or a demonstration of the vocabulary. */
.map-frame.is-compact svg { height: 240px; }

/* --------------------------------------------------------------------------
   33 — the acceptance control, and the record devices
   Added 22 Sep 2026, phase 6.

   An artifact that takes input during a meeting produces the meeting's
   output. The reference package does exactly this: the client accepts each
   working format, names an approver, and exports an agreement record before
   anybody leaves the room. That is the difference between a deck somebody
   watched and a decision somebody made.

   Three rules:

   1. An accepted thing SAYS it is accepted, and by whom. A tick that only
      changes a colour is not a record.
   2. The export is one click from where the accepting happens. A record that
      needs hunting for is a record nobody takes.
   3. The honesty note is rendered wherever input is taken, never in a
      footnote. See Record.notice() in lib/record.js.
   -------------------------------------------------------------------------- */

.accept {
  display: flex;
  align-items: center;
  gap: var(--gap-sm);
  flex-wrap: wrap;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--rule);
}

.accept-btn {
  border: 1px solid var(--accent);
  background: transparent;
  color: var(--accent);
  font-family: var(--font-sans);
  font-size: var(--t-12);
  font-weight: bold;
  padding: 7px 11px;
  cursor: pointer;
}
.accept-btn:hover { background: var(--mint); }
.accept-btn.is-accepted { background: var(--accent); border-color: var(--accent); color: var(--paper); }

/* Who accepted it, in their words. Empty until somebody types. */
.accept-by { font-size: var(--t-12); color: var(--ink-soft); }
.accept-by b { color: var(--ink); }
.accept-none { font-size: var(--t-12); color: var(--ink-faint); font-style: italic; }

/* The form inside a record dialog. */
.record-dialog {
  width: min(760px, 94vw);
  max-height: 90vh;
  border: 0;
  border-top: 6px solid var(--accent);
  padding: 26px;
  background: var(--paper);
  color: var(--ink);
  overflow: auto;
}
.record-dialog::backdrop { background: #102D38A8; }
.record-dialog h2 { margin: 0 0 var(--gap-sm); font-family: var(--font-display); font-size: var(--t-23); font-weight: normal; }

.field { display: block; margin: var(--gap-sm) 0; }
.field span {
  display: block;
  margin-bottom: 4px;
  font-size: var(--t-10);
  font-weight: bold;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--ink-faint);
}
.field input, .field textarea {
  width: 100%;
  padding: 9px;
  border: 1px solid var(--rule);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--t-14);
}
.field textarea { min-height: 120px; resize: vertical; }
.field-json textarea { font-family: var(--font-mono); font-size: var(--t-12); min-height: 240px; }

.dialog-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: var(--gap); }

/* The sentence. Never a footnote, never small print — it is the one thing
   that stops a client believing this file is a system. */
.storage-note {
  margin: var(--gap-sm) 0 0;
  padding: 10px 13px;
  background: var(--sand);
  border-left: 4px solid var(--amber);
  font-size: var(--t-12);
  line-height: 1.5;
  color: var(--on-sand);
}

/* --------------------------------------------------------------------------
   34 — the plot
   Added 22 Sep 2026, phase 10. Drawn by lib/plot3d.js in plain canvas.

   Three scores put a thing in space. Rotating it is not decoration: on a flat
   chart two of the three axes fight for the same dimension and the room
   argues about which point is really in front. Turning it settles that live,
   in a second, while somebody is watching.

   The points are DOM nodes over the canvas rather than drawn into it, so they
   stay clickable, focusable, and styled by this file instead of by a
   hard-coded fill somewhere in a script.
   -------------------------------------------------------------------------- */

.plot-frame {
  position: relative;
  background: var(--deep);
  border: 1px solid var(--rule);
  box-shadow: var(--shadow);
  min-height: 460px;
}
.plot-frame canvas { display: block; width: 100%; height: 460px; cursor: grab; }
.plot-frame canvas:active { cursor: grabbing; }
.plot-frame canvas:focus-visible { outline: 3px solid var(--focus); outline-offset: -3px; }

.plot-layer { position: absolute; inset: 0; pointer-events: none; }

.plot-point {
  position: absolute;
  transform: translate(-50%, -50%);
  pointer-events: auto;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 2px solid var(--paper);
  background: var(--accent);
  color: var(--paper);
  font-family: var(--font-sans);
  font-size: var(--t-11);
  font-weight: bold;
  line-height: 1;
  cursor: pointer;
}
.plot-point:hover { background: var(--accent-lift); color: var(--deep); }
.plot-point.is-selected { background: var(--gold); color: var(--deep); border-color: var(--paper); }

/* Blocked stays visible and refuses to be chosen. Hiding it would let the
   room forget the thing it is not allowed to pick, and then ask for it. */
.plot-point.is-blocked { background: var(--coral); cursor: not-allowed; }

.plot-key { margin: 10px 0 0; font-size: var(--t-11); color: var(--ink-soft); }
.plot-key b { color: var(--ink); }

/* --------------------------------------------------------------------------
   35 — the swimlane
   Added 22 Sep 2026, phase 11. Taken from the reference package.

   A board answers "what is there". A diagram answers "what talks to what". A
   swimlane answers the question neither can: **where does this break down?**
   It reads one lifecycle across several patterns at once, so an empty cell in
   a column where every other lane has something is the finding, sitting there
   in plain sight.

   Two sticky edges do the work. The stage header stays while you scroll down;
   the lane label stays while you scroll right. Lose either and a wide matrix
   stops being readable about four columns in.
   -------------------------------------------------------------------------- */

.swimlane { --lane-w: 172px; --stage-w: 208px; min-width: calc(var(--lane-w) + 6 * var(--stage-w)); }

.lane-row {
  display: grid;
  grid-template-columns: var(--lane-w) repeat(var(--stages, 6), var(--stage-w));
}

.stage-row {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--deep);
  color: var(--paper);
}
.stage-corner {
  padding: 11px 9px;
  border-right: 1px solid var(--navy);
  font-size: var(--t-9);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--accent-lift);
}
.stage {
  position: relative;
  padding: 9px 10px;
  border-right: 1px solid var(--navy);
  min-height: 62px;
}
.stage b { display: block; font-family: var(--font-display); font-size: var(--t-14); font-weight: normal; }
.stage span { display: block; margin-top: 3px; font-size: var(--t-8); color: var(--accent-lift); }

/* The arrow between stages. It is the only thing telling the reader this is a
   sequence rather than a set of columns, and it costs nothing. */
.stage:not(:last-child)::after {
  content: "";
  position: absolute;
  right: -7px;
  top: 26px;
  z-index: 2;
  border-left: 7px solid var(--accent-lift);
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
}

.lane-row.is-lane { border-bottom: 1px solid var(--rule); min-height: 96px; }

.lane-label {
  position: sticky;
  left: 0;
  z-index: 3;
  padding: 11px 9px;
  background: var(--surface);
  border-right: 1px solid var(--rule);
}
.lane-label b { display: block; font-family: var(--font-display); font-size: var(--t-15); font-weight: normal; color: var(--ink); }
.lane-label small { display: block; margin-top: 3px; font-size: var(--t-9); color: var(--ink-soft); }

.cell { padding: 7px; border-right: 1px dashed var(--rule); }

.lane-card {
  display: block;
  width: 100%;
  min-height: 80px;
  padding: 0;
  overflow: hidden;
  text-align: left;
  background: var(--paper);
  border: 1px solid var(--rule);
  font-family: var(--font-sans);
  cursor: pointer;
}
.lane-card:hover, .lane-card.is-selected { border-color: var(--accent); box-shadow: inset 0 0 0 2px var(--accent); }
.lane-card.is-linked { border-color: var(--coral); box-shadow: inset 0 0 0 2px var(--coral); }
.lane-card:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

/* The dark strip names the system. It is the thing the eye tracks across a
   row, so it goes at the top and it never wraps. */
.lane-strip {
  display: block;
  padding: 5px 7px;
  background: var(--deep);
  color: var(--paper);
  font-size: var(--t-8);
  font-weight: bold;
  letter-spacing: 0.03em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.lane-body { display: block; padding: 7px; }
.lane-what { display: block; font-size: var(--t-10); line-height: 1.3; color: var(--ink); }
.lane-card .pill { margin-top: 6px; }

/* A cell with no system is not a gap in the drawing. It IS the finding, so it
   is the loudest thing in the row rather than a blank. */
.lane-card.is-gap { background: var(--rose); border-color: var(--coral); }
.lane-card.is-gap .lane-strip { background: var(--coral); }
.lane-card.is-gap .lane-what { color: var(--on-rose); }

.lane-card.is-dimmed { opacity: 0.15; }

@media (prefers-reduced-motion: no-preference) {
  .lane-card { transition: opacity 120ms linear, box-shadow 120ms linear; }
}

@media print {
  .swimlane { min-width: 0; }
  .lane-card, .lane-row { break-inside: avoid; }
  .stage-row, .lane-card, .lane-card.is-gap {
    print-color-adjust: exact; -webkit-print-color-adjust: exact;
  }
}

/* --------------------------------------------------------------------------
   36 — the no-script note, and the skip link
   Added 22 Sep 2026.

   Four of our five formats build their entire content in JavaScript, so with
   script off they are not degraded — they are blank. A client on a locked-down
   corporate browser opens the thing we sent and sees nothing at all, with no
   way to tell whether it is broken or empty.

   So every page says what it is and points at something static that carries
   the same facts. That is what the appendix is for, and it is why the
   appendix is generated as plain HTML with no script of its own.
   -------------------------------------------------------------------------- */

.noscript-note {
  display: block;
  margin: var(--gap-sm) 0;
  padding: 14px 18px;
  background: var(--sand);
  border-left: 5px solid var(--amber);
  font-size: var(--t-14);
  line-height: 1.5;
  color: var(--on-sand);
}
.noscript-note b { display: block; margin-bottom: 4px; color: var(--ink); }

/* A CDN that does not answer is not a script failure, so <noscript> never
   fires and the reader gets an empty box with no explanation. Worse than no
   script at all: at least that says something. Same note, same voice. */
.load-failed {
  display: grid;
  place-items: center;
  padding: var(--gap-lg) var(--gap);
  text-align: center;
}

/* A frame that failed does not need the height it reserved for a drawing.
   Left at 1040px the note sat centred a screen and a half below the fold,
   which is indistinguishable from it not being there at all. */
.map-frame.is-failed { min-height: 0; }
.map-frame.is-failed canvas, .map-frame.is-failed svg { display: none; }
.load-failed .noscript-note { max-width: 62ch; text-align: left; }
.noscript-note a { color: var(--on-sand); }

/* Keyboard users land here first. Off screen until focused, then it is the
   first thing on the page. */
.skip {
  position: absolute;
  left: -9999px;
  z-index: 100;
}
.skip:focus {
  left: var(--gap-sm);
  top: var(--gap-sm);
  padding: 10px 14px;
  background: var(--paper);
  color: var(--ink);
  border: 2px solid var(--accent);
  font-size: var(--t-14);
}

/* --------------------------------------------------------------------------
   THE DECK REGISTER
   Replaced 22 Sep 2026. Reveal is gone; this is the reference's own deck,
   taken rather than re-derived.

   The shape that matters: a slide is NOT full-bleed. It is a paper card on a
   dark ground, with a fixed bar above it and a fixed bar below. That is what
   makes a artifact deck recognisable across a room, and it is the one thing
   Reveal could not be made to do.

   What else came with it, and what Reveal could not give us:

     deep links       every slide is a URL — index.html#slide-15 is shareable
     overview         a dialog built from the live headings, jump anywhere
     session memory   reload mid-meeting and you are on the same slide
     print            @page 16in 9in, one slide per page, so the deck IS the PDF

   INSIDE A DECK, USE .is-step FRAGMENTS. On a document page, use the motion
   primitives. Never both in one file.
   -------------------------------------------------------------------------- */

/* `html` carries --surface for every other page, and the canvas takes its
   colour from html rather than from body — so a dark body showed only beside
   the card. Covering the viewport is enough, and does not need :has(). */
.deck { background: var(--deep); margin: 0; min-height: 100vh; }

.deck-top {
  position: fixed;
  inset: 0 0 auto;
  z-index: 5;
  height: 70px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap);
  padding: 0 4vw;
  background: var(--surface);
}
.deck-top .brandmark img { filter: none; }

.deck-links { display: flex; gap: 8px; flex-wrap: wrap; }
.deck-links a, .deck-links button {
  border: 1px solid var(--accent);
  background: transparent;
  color: var(--accent);
  font-family: var(--font-sans);
  font-size: var(--t-12);
  font-weight: bold;
  padding: 8px 11px;
  text-decoration: none;
  cursor: pointer;
}
.deck-links a:hover, .deck-links button:hover { background: var(--mint); }

.slide {
  display: none;
  width: min(1180px, 94vw);
  min-height: 660px;
  margin: 92px auto 86px;
  padding: 44px 60px;
  background: var(--surface);
}
.slide.is-active { display: block; }

.slide .eyebrow { font-size: var(--t-13); margin-bottom: 10px; }
/* The reference clamps its deck title between 32 and 58 and ours sat locked
   at 40, which is why a slide of ours read quieter than a slide of theirs
   side by side. A deck is seen across a room; it gets the top of the scale.
   Both ends are steps on the scale — a clamp may not invent a size. */
.slide h1 {
  margin: 0 0 8px;
  font-family: var(--font-display);
  font-size: clamp(var(--t-30), 3.6vw, var(--t-58));
  font-weight: normal;
  line-height: 1.08;
  color: var(--ink);
}
.slide-summary { margin: 0 0 24px; max-width: 86ch; font-size: var(--t-17); color: var(--ink-soft); }

/* A fragment. Hidden until stepped to, never animated in from a direction —
   a slide that slides is a slide nobody reads. */
.is-step { opacity: 0; }
.is-step.is-shown { opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  .is-step { transition: opacity 260ms linear; }
}

/* --- deck devices, all from the reference -------------------------------- */

/* The agenda row: a numeral, then the point. Numerals here are teal because
   this IS a sequence — that is the only time a numeral earns one. */
.agenda { display: grid; gap: 9px; }
.agenda article {
  display: grid;
  grid-template-columns: 48px 1fr;
  align-items: baseline;
  border-left: 5px solid var(--accent);
  background: var(--paper);
  padding: 10px 14px;
}
.agenda article b { font-family: var(--font-display); font-size: var(--t-21); color: var(--accent); }
.agenda article p { margin: 2px 0; font-size: var(--t-17); color: var(--ink); }
.agenda article small { display: block; margin-top: 2px; font-size: var(--t-14); color: var(--ink-soft); }

/* The card grid. Top rule in accent, 2-up or 3-up. */
.deck-grid { display: grid; gap: 14px 30px; }
.deck-grid-2 { grid-template-columns: 1fr 1fr; }
.deck-grid-3 { grid-template-columns: repeat(3, 1fr); }
.deck-grid article { border-top: 4px solid var(--accent); padding: 13px 5px; }
.deck-grid article b { font-family: var(--font-display); font-size: var(--t-21); color: var(--accent); }
.deck-grid article p { margin: 5px 0; font-size: var(--t-17); color: var(--ink-soft); }

/* A card that holds a link out to a working format. */
.deck-card {
  background: var(--paper);
  border-top: 4px solid var(--accent);
  padding: 13px;
  min-height: 135px;
}
.deck-card > b { font-family: var(--font-display); font-size: var(--t-21); color: var(--accent); }
.deck-card > span { display: block; margin: 8px 0 13px; font-family: var(--font-display); font-size: var(--t-17); color: var(--ink); }

/* The band. Mint for a shared note, deep for the sentence the slide exists
   to deliver. One per slide, never two. */
.deck-note {
  margin-top: 18px;
  padding: 16px;
  background: var(--mint);
  border-left: 5px solid var(--accent);
  font-size: var(--t-17);
  color: var(--ink);
}
/* .deck-note takes plain text and .deck-statement takes a label plus a
   sentence. They look alike enough that the first person to reach for a
   labelled mint band reached for this one, and the label printed hard against
   the sentence with nothing to catch it. If a label turns up here anyway, it
   gets its own line rather than running into the text. */
.deck-note b { display: block; margin-bottom: 5px; }
.deck-statement {
  display: flex;
  align-items: baseline;
  gap: 20px;
  margin-top: 24px;
  padding: 16px 20px;
  background: var(--deep);
  color: var(--paper);
}
.deck-statement b {
  flex: none;
  font-size: var(--t-12);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--accent-lift);
}
.deck-statement span { font-size: var(--t-17); }

/* The question slide. Each question is a card; the thing being asked for is
   in accent so the client can find it while someone is talking. */
.question-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px 30px; }
.question {
  border-top: 4px solid var(--accent);
  background: var(--paper);
  padding: 11px 14px;
}
.question h2 { margin: 6px 0; font-family: var(--font-display); font-size: var(--t-19); font-weight: normal; color: var(--ink); }
.question p { margin: 4px 0; font-size: var(--t-14); color: var(--ink-soft); }
.question strong { color: var(--accent); }

/* The lifecycle strip and the chip row that goes under it. */
.lifecycle { display: flex; gap: 4px; margin-top: 24px; }
.lifecycle span { flex: 1; padding: 11px 4px; background: var(--mint); text-align: center; font-size: var(--t-12); color: var(--ink); }

/* --- the bottom bar ------------------------------------------------------- */

.deck-bottom {
  position: fixed;
  inset: auto 0 0;
  z-index: 5;
  height: 66px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 18px;
  background: var(--surface);
  border-top: 1px solid var(--rule);
}
.deck-bottom button {
  border: 0;
  background: var(--deep);
  color: var(--paper);
  font-family: var(--font-sans);
  font-size: var(--t-14);
  padding: 8px 14px;
  cursor: pointer;
}
.deck-bottom button:disabled { opacity: 0.35; cursor: default; }
.deck-position { font-variant-numeric: tabular-nums; font-size: var(--t-14); color: var(--ink-soft); }

.deck-overview { width: min(900px, 94vw); border: 0; border-top: 6px solid var(--accent); padding: 24px; background: var(--paper); color: var(--ink); }
.deck-overview::backdrop { background: #102D38A8; }
.deck-overview h2 { margin: 0 0 var(--gap-sm); font-family: var(--font-display); font-size: var(--t-23); font-weight: normal; }
.overview-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; }
.overview-grid button {
  border: 1px solid var(--rule);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--t-14);
  text-align: left;
  padding: 8px;
  cursor: pointer;
}
.overview-grid button:hover { border-color: var(--accent); }

@media (max-width: 760px) {
  /* A fixed bar with four links cannot hold 70px on a phone: the links wrap
     into a column and push the lockup out of the bar. Let the header scroll
     away instead — the bottom bar is the control you actually need, and it
     stays. A client opening an emailed deck on a phone sees a header, not a
     stack of buttons over a clipped logo. */
  .deck-top {
    position: static;
    height: auto;
    flex-wrap: wrap;
    gap: var(--gap-sm);
    padding: 12px 16px;
  }
  .deck-links a, .deck-links button { font-size: var(--t-11); padding: 7px 9px; }

  .slide { margin: 16px auto 86px; padding: 24px 18px; min-height: 0; }
  .slide h1 { font-size: var(--t-30); }
  .slide-summary { font-size: var(--t-16); margin-bottom: 18px; }

  .deck-grid-2, .deck-grid-3, .question-grid { grid-template-columns: 1fr; }
  .lifecycle { flex-wrap: wrap; }
  .lifecycle span { flex: 1 1 22%; }
  .agenda article { grid-template-columns: 36px 1fr; padding: 9px 11px; }
  .deck-statement { display: block; }
  .deck-statement b { display: block; margin-bottom: 6px; }
}

/* Print: the deck IS the PDF. One slide per page at 16:9, every fragment
   shown, all chrome gone. This is why the runtime is ours. */
@media print {
  @page { size: 16in 9in; margin: 0; }
  .deck { background: white; }
  .deck-top, .deck-bottom, dialog { display: none !important; }
  .slide, .slide.is-active {
    display: block !important;
    width: 16in;
    height: 9in;
    min-height: 0;
    margin: 0;
    padding: 0.55in 0.8in;
    overflow: hidden;
    break-after: page;
    page-break-after: always;
  }
  .slide:last-of-type { break-after: auto; }
  .is-step { opacity: 1 !important; }

  /* A printing engine lays out against its own viewport, not the page box, so
     the narrow-screen rule can win on a 16in page and stack what should sit
     two-up. State the print grid rather than hoping. */
  .deck-grid-2, .question-grid { grid-template-columns: 1fr 1fr !important; }
  .deck-grid-3 { grid-template-columns: repeat(3, 1fr) !important; }
  .agenda article, .deck-card, .deck-note, .deck-statement, .question, .lifecycle span {
    print-color-adjust: exact;
    -webkit-print-color-adjust: exact;
  }
}

/* --------------------------------------------------------------------------
   22 — line art, inline emphasis, hero stat
   ADDITIONS, 21 September 2026. Recorded in THEME.md with their reasons.

   These exist because a slide is NOT a page with less on it. A page carries
   its detail in prose; a slide has to carry it in structure, or it reads as a
   document someone cut into pieces.
   -------------------------------------------------------------------------- */

/* A diagram drawn in text. Mono, so the columns line up and the thing reads as
   a mechanism rather than a picture. Put the failing element in .hit — that is
   alarm, which is what coral is for. */
.lineart {
  margin: 0;
  padding: var(--gap) var(--gap-lg);
  background: var(--paper);
  border: 1px solid var(--rule);
  font-family: var(--font-mono);
  font-size: var(--t-14);
  line-height: 1.75;
  color: var(--ink-soft);
  white-space: pre;
  overflow-x: auto;
}
.lineart .dim { color: var(--ink-faint); }
.lineart .em  { color: var(--ink); font-weight: bold; }
.lineart .hit { color: var(--coral); font-weight: bold; }

/* Inline emphasis. Half a sentence recedes so three words can come forward.
   This is most of what separates a considered slide from a flat paragraph. */
.dim { color: var(--ink-faint); }
.em-accent { color: var(--accent); font-weight: bold; }

/* One number, alone, at the size the room needs. Exactly one per deck. */
.stat-tile.is-hero { padding: var(--gap-lg); }
.stat-tile.is-hero .stat-num { font-size: var(--t-72); letter-spacing: -0.02em; }

/* ==========================================================================
   THE APPLICATION REGISTER
   Added 22 Sep 2026, phase 4.

   Everything above this line is the DOCUMENT register: 17px on a 1280x720
   frame, for a thing the client reads — a proposal, a page of findings, a
   slide.

   Everything below is the APPLICATION register: 14px in a fluid shell, for a
   thing the client OPERATES — filters it, selects in it, types into it,
   exports a record from it. A capability map is not a picture of a proposal.
   It is a tool, and a tool is denser.

   The two never mix on one page. A page is one register or the other, and
   `.app` is the switch. Type steps --t-11 and --t-8 exist only in here.
   -------------------------------------------------------------------------- */

.app {
  font-size: var(--t-14);
  line-height: 1.42;
  background: var(--surface);
  color: var(--ink);
}

/* --------------------------------------------------------------------------
   23 — application shell and header
   Fluid, wide, and dark-headed. The dark bar is what tells the client at a
   glance that this is the instrument and not the document.
   -------------------------------------------------------------------------- */

.app-top {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--gap);
  padding: 14px 22px;
  background: var(--deep);
  color: var(--paper);
}

.app-top .eyebrow { color: var(--accent-lift); margin: 0; }

.app-title {
  margin: 4px 0 0;
  font-family: var(--font-display);
  font-size: clamp(var(--t-23), 2.7vw, var(--t-38));
  font-weight: normal;
  line-height: 1.05;
  color: var(--paper);
}

.app-sub { margin: 3px 0 0; font-size: var(--t-11); color: var(--accent-lift); }

.app-actions { display: flex; gap: 7px; flex-wrap: wrap; }

.app-btn {
  border: 1px solid var(--ink-faint);
  background: transparent;
  color: var(--paper);
  font-family: var(--font-sans);
  font-size: var(--t-11);
  font-weight: bold;
  padding: 8px 11px;
  text-decoration: none;
  cursor: pointer;
}
.app-btn:hover { border-color: var(--accent-lift); }

/* One filled button per page: the thing that leaves. Everything else on the
   bar is an outline, so the filled one cannot be missed and cannot be
   confused with navigation. */
.app-btn.is-primary { background: var(--accent-deep); border-color: var(--accent-deep); color: var(--paper); }
.app-btn.is-primary:hover { background: var(--accent-lift); border-color: var(--accent-lift); color: var(--deep); }

.app-shell { max-width: 1800px; margin: 0 auto; padding: 14px 20px 30px; }

/* Board beside inspector. Collapses to one column under 1100px. */
.app-layout { display: grid; grid-template-columns: minmax(0, 1fr) 365px; gap: 14px; }
/* minmax(0, 1fr), not 1fr: a grid child at 1fr keeps min-width:auto and
   will not shrink below its content, so the swimlane pushed the page out
   by 59px on a phone while sitting inside a box that already said
   overflow:auto. */
@media (max-width: 1100px) { .app-layout { grid-template-columns: minmax(0, 1fr); } }

/* --------------------------------------------------------------------------
   24 — boundary bar
   What this page does NOT establish, stated before the client reads it.

   This is the most important component added in phase 4 and the one with no
   equivalent anywhere else. A map that shows a system without saying how we
   know it is there is a map that invents. The sand left border is the tell:
   read this before you believe the board.
   -------------------------------------------------------------------------- */

.boundary {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 16px;
  align-items: center;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-left: 5px solid var(--on-sand);
  padding: 10px 14px;
}
.boundary b { display: block; font-size: var(--t-12); color: var(--ink); }
.boundary span:not(.pill) { display: block; font-size: var(--t-11); color: var(--ink-soft); }
/* A pill keeps its own size anywhere it lands; .boundary span would otherwise
   outrank .pill on specificity and silently inflate it to 11px. */

/* --------------------------------------------------------------------------
   25 — evidence pill, and the legend that must accompany a board

   Four states, and they are a claim about how we know, not about quality:

     observed   we watched it happen, or read it out of the running system
     reported   someone told us, and we wrote down who
     proposed   it does not exist yet; this is the proposal
     validate   nobody could say — this is the question for the client

   Kept `observed` rather than the reference’s `documented`: a document saying a thing
   exists is weaker than having seen it, and the stronger claim is the one we
   are actually able to make. Took `proposed` over our `planned`, which
   implied a commitment nobody made, and `validate` over `unknown`, which
   names the action instead of the absence.

   The second axis is about a CELL, not a claim: available / na / unmapped.
   Use it in a matrix where the question is whether a box applies at all.
   -------------------------------------------------------------------------- */

.pill {
  display: inline-block;
  border-radius: 999px;
  padding: 3px 7px;
  font-family: var(--font-sans);
  font-size: var(--t-8);
  font-weight: bold;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}

.pill.observed, .pill.available { background: var(--mint);  color: var(--on-mint); }
.pill.reported                  { background: var(--blue);  color: var(--on-blue); }
.pill.proposed                  { background: var(--sand);  color: var(--on-sand); }
.pill.validate, .pill.unmapped  { background: var(--rose);  color: var(--on-rose); }
/* On a rose row the rose pill vanished. Give it the paper ground back. */
tr.is-gap .pill.validate, tr.is-gap .pill.proposed { background: var(--paper); }
.pill.na                        { background: var(--stone); color: var(--on-stone); }

/* Every board carries one. A board without its legend is not shippable. */
.pill-row { display: flex; gap: 5px; flex-wrap: wrap; align-items: center; }

/* --------------------------------------------------------------------------
   26 — segmented control
   Lens switching. One board, several views of it, no page load.
   -------------------------------------------------------------------------- */

.segmented { display: flex; }
.segmented button {
  border: 1px solid var(--rule);
  border-right: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--t-11);
  font-weight: bold;
  padding: 7px 12px;
  cursor: pointer;
}
.segmented button:last-child { border-right: 1px solid var(--rule); }
.segmented button.is-active { background: var(--deep); border-color: var(--deep); color: var(--paper); }

/* A lens is shown or it is not. */
.lens { display: none; }
.lens.is-active { display: block; }

/* --------------------------------------------------------------------------
   27 — filter row
   -------------------------------------------------------------------------- */

.controls { display: flex; justify-content: space-between; align-items: flex-end; gap: 12px; margin: 12px 0; flex-wrap: wrap; }
.filters { display: flex; gap: 7px; align-items: flex-end; flex-wrap: wrap; }
.filters label {
  display: block;
  font-size: var(--t-9);
  font-weight: bold;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--ink-faint);
}
.filters select {
  display: block;
  margin-top: 3px;
  min-width: 130px;
  padding: 6px 10px;
  border: 1px solid var(--rule);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--t-11);
}

/* --------------------------------------------------------------------------
   28 — board
   A scrolling surface with a sticky dark header row. The board scrolls
   inside its frame; the page does not scroll sideways, ever.
   -------------------------------------------------------------------------- */

.board-wrap {
  overflow: auto;
  background: var(--paper);
  border: 1px solid var(--rule);
  box-shadow: var(--shadow);
}
.board { padding: 10px; min-width: 0; }

.board-head {
  position: sticky;
  top: 0;
  z-index: 5;
  display: grid;
  background: var(--deep);
  color: var(--paper);
}
.board-corner {
  padding: 11px 9px;
  border-right: 1px solid var(--navy);
  font-size: var(--t-9);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--accent-lift);
}
.board-col {
  padding: 9px 8px;
  border-right: 1px solid var(--navy);
  min-height: 62px;
}
.board-col b { display: block; font-family: var(--font-display); font-size: var(--t-14); font-weight: normal; }
.board-col span { display: block; margin-top: 3px; font-size: var(--t-8); color: var(--accent-lift); }

/* --------------------------------------------------------------------------
   29 — panel, and the tiles inside it

   REPLACED 22 Sep 2026. This was `.cap` / `.caps`, and it existed only to be
   demonstrated: every board built its own `.tile` in page-local CSS instead,
   so the theme was teaching a shape no artifact followed. These are the
   classes the boards actually use, promoted so the next one is free.

   The categorical scale earns itself here: --domain-fill is set per panel
   from the four fills, so adjacent panels read apart at a glance.
   -------------------------------------------------------------------------- */

.map-grid { display: grid; grid-template-columns: repeat(12, 1fr); grid-auto-rows: minmax(48px, auto); gap: 7px; }

/* Panels tile the full width. auto-fit rather than a fixed count, because a
   board with seven groups and a board with ten should both fill. */
.panels {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(296px, 1fr));
  gap: 8px;
  align-items: start;
}

.panel, .domain {
  border: 2px solid var(--navy);
  background: var(--domain-fill, var(--mint));
  padding: 9px;
  min-width: 0;
}
.panel-head { margin-bottom: 7px; }
.panel-name, .domain-title {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--t-12);
  font-weight: bold;
  letter-spacing: 0.055em;
  text-transform: uppercase;
  color: var(--ink);
}
/* The scope line: a count, then the half that is a decision, in bold. */
.panel-note, .domain-sub { margin: 3px 0 0; font-size: var(--t-9); color: var(--ink-soft); }
.panel-note b { color: var(--ink); font-weight: bold; }
.panel-note .is-alarm { color: var(--coral); }

.domain-mint { --domain-fill: var(--mint); }
.domain-blue { --domain-fill: var(--blue); }
.domain-sand { --domain-fill: var(--sand); }
.domain-rose { --domain-fill: var(--rose); }

/* Flex, not grid. A grid of fixed tracks leaves an empty cell whenever a
   panel holds an odd number, and most panels hold an odd number. */
.tiles { display: flex; flex-wrap: wrap; gap: 5px; }

.tile {
  flex: 1 1 132px;
  min-width: 132px;
  min-height: 62px;
  display: block;
  border: 1px solid var(--rule);
  background: var(--paper);
  box-shadow: var(--shadow-card);
  padding: 7px 8px;
  text-align: left;
  font-family: var(--font-sans);
  cursor: pointer;
}
.tile-name { display: block; font-size: var(--t-11); font-weight: bold; color: var(--ink); line-height: 1.25; }
.tile-meta { display: block; margin-top: 4px; font-size: var(--t-9); color: var(--ink-soft); line-height: 1.35; }
.tile-sub  { display: block; margin-top: 2px; font-size: var(--t-9); color: var(--ink-faint); line-height: 1.3; }
.tile-flag { display: inline-block; width: 6px; height: 6px; margin-right: 5px; vertical-align: 1px; border-radius: 50%; }

.flag-observed { background: var(--on-mint); }
.flag-reported { background: var(--on-blue); }
.flag-proposed { background: var(--on-sand); }
.flag-validate { background: var(--coral); }

.tile.is-proposed { border-style: dashed; }

/* A tile nobody owns is the finding, so it is the loudest thing in its panel:
   the fill changes, not just an edge. A thin rule was too quiet to find
   across ten panels. */
.tile.is-unowned {
  border-color: var(--coral);
  border-left-width: 3px;
  background: var(--rose);
}
.tile.is-unowned .tile-meta { color: var(--on-rose); }

.tile:hover, .tile.is-selected { border-color: var(--accent); box-shadow: inset 0 0 0 2px var(--accent); }
.tile.is-linked { border-color: var(--coral); box-shadow: inset 0 0 0 2px var(--coral); }
.tile.is-dimmed { opacity: 0.15; }
.tile:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

@media (prefers-reduced-motion: no-preference) {
  .tile { transition: opacity 120ms linear, box-shadow 120ms linear; }
}

/* Nothing matched. A board of dimmed tiles with no explanation reads as
   broken, and the reader cannot tell whether they filtered it to nothing or
   we shipped them an empty page. Say which, and offer the way back — an empty
   screen is an invitation to act. */
.board-empty {
  grid-column: 1 / -1;
  padding: var(--gap-lg) var(--gap);
  border: 1px dashed var(--rule);
  background: var(--paper);
  text-align: center;
}
.board-empty b { display: block; font-family: var(--font-display); font-size: var(--t-19); font-weight: normal; color: var(--ink); }
.board-empty span { display: block; margin: 6px auto 0; max-width: 56ch; font-size: var(--t-12); color: var(--ink-soft); }
.board-empty button { margin-top: var(--gap-sm); }

/* --------------------------------------------------------------------------
   30 — inspector
   The panel that makes a board interrogable. Select on the board, read here.
   -------------------------------------------------------------------------- */

.inspector {
  align-self: start;
  position: sticky;
  top: 14px;
  background: var(--paper);
  border: 1px solid var(--rule);
  box-shadow: var(--shadow);
  padding: 13px;
  max-height: calc(100vh - 40px);
  overflow: auto;
}
.inspector-title { margin: 4px 0 8px; font-family: var(--font-display); font-size: var(--t-19); font-weight: normal; color: var(--ink); }

/* --------------------------------------------------------------------------
   31 — metric strip
   Counts that move when a filter moves. Georgia numeral, tiny caps label.
   -------------------------------------------------------------------------- */

.metric-strip { display: flex; gap: 7px; flex-wrap: wrap; }
.metric { min-width: 86px; border-left: 1px solid var(--rule); padding-left: 10px; }
.metric-num { display: block; font-family: var(--font-display); font-size: var(--t-21); color: var(--ink); font-variant-numeric: tabular-nums; }
.metric-label {
  display: block;
  font-size: var(--t-9);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* --------------------------------------------------------------------------
   32 — the client lockup
   Added 22 Sep 2026.

   Two supplied images with the multiplication sign between them, exactly as
   the reference package does it.

   A client logo is theirs. We never redraw one, and we never invent a mark
   for a vendor — we tried that once, generated marks for every system on a
   board, and it looked cheap. The reference package names every system in
   words and contains exactly two images, both of them here.

   Where we have no file for a client, .brandmark-missing says so rather than
   leaving a hole or inventing a wordmark.
   -------------------------------------------------------------------------- */

.brandmark {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: var(--gap-sm);
  flex: none;
  white-space: nowrap;
}

.brandmark img { display: block; height: 22px; width: auto; }

/* Our own mark is a single dark ink on transparent, so on the dark bar it
   disappears. Knock it to paper rather than shipping a second file: one
   asset, and it can never drift from the other. A CLIENT logo is never
   filtered — theirs may be two colours and inverting it would be wrong. */


.brandmark .topbar-mark { color: var(--ink-faint); }
.app-top .brandmark .topbar-mark { color: var(--accent-lift); }

.brandmark-missing {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 10px;
  border: 1px dashed var(--rule);
  font-size: var(--t-10);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* --------------------------------------------------------------------------
   The application register on a phone.

   Every page carrying .app-top and .boundary was broken at 390px in the same
   two ways, and each page had been fixing it separately or not at all. Both
   are theme components, so both are fixed here — a layout bug patched per
   page is a layout bug the next artifact inherits.

     .app-top     the action buttons stacked into a tall column beside the
                  title, pushing everything down
     .boundary    `1fr auto` squeezed the sentence into a 110px ribbon while
                  the counts kept their width. Two or three words a line.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   37 — poster
   The one-canvas estate. Clusters are placed by lib/poster.js from the model,
   so every position here comes from JS; the theme owns what they look like,
   never where they are. The canvas is wider than any page and scrolls inside
   its own frame rather than making the document scroll sideways.
   -------------------------------------------------------------------------- */

.poster-frame {
  overflow: auto;
  border: 1px solid var(--rule);
  background: var(--paper);
  padding: 4px;
}
.poster { position: relative; margin: 0 auto; }

.poster-flows { position: absolute; left: 0; top: 0; pointer-events: none; }
.poster-flow {
  fill: none;
  stroke: var(--ink-faint);
  stroke-width: 1.2;
  stroke-dasharray: 4 3;
}
.poster-arrowhead { fill: var(--ink-faint); }
.poster-flow-label {
  font-family: var(--font-sans);
  font-size: var(--t-9);
  fill: var(--ink-faint);
}

.poster-cluster {
  position: absolute;
  box-sizing: border-box;
  padding: 13px;
  border: 1px solid var(--rule);
  background: var(--surface);
}
.poster-cluster.is-mint  { background: var(--mint);  border-color: var(--on-mint); }
.poster-cluster.is-blue  { background: var(--blue);  border-color: var(--on-blue); }
.poster-cluster.is-sand  { background: var(--sand);  border-color: var(--on-sand); }
.poster-cluster.is-rose  { background: var(--rose);  border-color: var(--on-rose); }
.poster-cluster.is-stone { background: var(--stone); border-color: var(--on-stone); }

.poster-cluster-title {
  margin: 0 0 8px;
  font-size: var(--t-10);
  font-weight: bold;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
}
.poster-cells { display: flex; flex-wrap: wrap; gap: 7px; }

.poster-cell {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 7px;
  border: 1px solid var(--rule);
  background: var(--paper);
}
/* A figurative icon in a cell: a person, a group, a stack of documents —
   for the boxes on a client's diagram that are not systems. The icon
   component, sized for its host the way .flow-item and .moment already size
   it for theirs. This is not the mark slot and must never be used as one: a
   mark is a vendor's and arrives as a file. See theme/marks/MARKS.md. */
.poster-cell .icon-col-icon { flex: none; width: 20px; height: 20px; margin: 0; }

/* Proposed is not estate. It is drawn, and it is drawn as not-yet. */
.poster-cell.is-proposed { border-style: dashed; }
/* Nobody could account for it. The board says so and so does the poster. */
.poster-cell.is-validate { border-color: var(--on-rose); background: var(--rose); }

/* A cell that is a picture rather than a line of text. The drawio originals
   draw the customer, the business user and the reports this way — an
   illustration with its name under it, not an icon beside it. */
.poster-cell.is-figure {
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  padding: 6px;
}
.poster-cell-image {
  display: block;
  max-width: 100%;
  max-height: 58px;
  object-fit: contain;
}
.poster-cell-caption {
  font-size: var(--t-10);
  font-weight: bold;
  color: var(--ink);
  line-height: 1.2;
  text-align: center;
}

.poster-cell-text { min-width: 0; }
.poster-cell-name {
  display: block;
  font-size: var(--t-10);
  font-weight: bold;
  color: var(--ink);
  line-height: 1.2;
}
.poster-cell-meta {
  display: block;
  margin-top: 1px;
  font-size: var(--t-9);
  color: var(--ink-soft);
  line-height: 1.2;
}

/* A note in the margin. The drawio originals all carried two or three of
   these — a decision not yet taken, a thing deliberately out of the first
   phase — sitting beside the picture rather than inside it. */
.poster-note {
  padding: 11px 13px;
  border-left: 2px solid var(--accent);
  background: var(--surface);
}
.poster-note h3 {
  margin: 0 0 5px;
  font-family: var(--font-display);
  font-size: var(--t-14);
  font-weight: normal;
  color: var(--ink);
}
.poster-note p { margin: 0; font-size: var(--t-11); color: var(--ink-soft); max-width: var(--measure); }

/* --------------------------------------------------------------------------
   38 — application page furniture
   The instruction under the title, the note under the content, the two halves
   of the boundary bar, and the legend line.

   These lived in four page-local stylesheets, one copy each, because .boundary
   and .metric were promoted to the theme and their children were not. Half a
   component in the theme is worse than none: G5 approved every copy, since a
   page may define its own classes, so the drift had nowhere to surface. It had
   already happened — diagram.css carried .boundary-lead at 92ch against 86ch
   elsewhere and had lost .boundary-body's measure entirely, so the same bar
   set differently on one page out of four.
   -------------------------------------------------------------------------- */

.app-instruction { margin: 6px 0 0; font-size: var(--t-12); color: var(--accent-lift); }
.app-note { margin: 0; font-size: var(--t-11); color: var(--ink-faint); }

.boundary-lead {
  display: block;
  max-width: 86ch;
  font-size: var(--t-12);
  color: var(--ink);
  line-height: 1.45;
}
.boundary-body {
  display: block;
  margin-top: 4px;
  max-width: 110ch;
  font-size: var(--t-11);
  color: var(--ink-soft);
}

.legend-line {
  display: flex;
  gap: var(--gap-sm);
  align-items: baseline;
  flex-wrap: wrap;
  padding: 7px 0;
  font-size: var(--t-11);
  color: var(--ink-soft);
}

/* --------------------------------------------------------------------------
   39 — mark
   A vendor mark, received as a file. Square box, contained rather than
   stretched, and greyscale-neutral so a mark we were sent in any palette sits
   beside our own colours without arguing with them.

   It is always optional. Nothing in this repo draws a mark: when the file is
   missing the component keeps the name it was already showing. See
   theme/marks/MARKS.md.
   -------------------------------------------------------------------------- */

/* Height is fixed and width follows, rather than both. A square slot is the
   obvious thing to write and it is wrong: plenty of marks are wordmarks, and
   our own lockup is 132x22. Forced into a square it rendered as an illegible
   smear about 15px wide, which the gallery showed immediately and no gate
   could have. The cap stops a very wide one from pushing a tile's name out. */
.mark {
  flex: none;
  width: auto;
  height: 18px;
  max-width: 72px;
  object-fit: contain;
  object-position: left center;
  vertical-align: -3px;
  margin-right: 5px;
}
.mark.is-sm { height: 14px; max-width: 56px; vertical-align: -2px; }
.mark.is-lg { height: 26px; max-width: 104px; }

/* --------------------------------------------------------------------------
   40 — the engine's own furniture
   Everything lib/board.js and lib/swimlane.js create at runtime: the lens
   tabs, the removable filter chips, the inspector's sections and pairs, the
   exchange list, and the inline name link that jumps to a tile.

   These lived in board.css and journey.css, one copy each, which worked for
   exactly as long as there was one board. The second one — a different estate
   on the same engine — rendered with an unstyled inspector and unstyled chips,
   because an engine that emits a class and does not own its style is an engine
   that only works on the page it was born on. G18 counts them now.

   .insp-pair's label column stays overridable: 82px suits a system, 148px
   suits a scored gap, and that is content, not drift.
   -------------------------------------------------------------------------- */

.lens-tabs { display: flex; }
.lens-tabs button {
  border: 1px solid var(--rule);
  border-right: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--t-12);
  font-weight: bold;
  line-height: 1.25;
  text-align: left;
  padding: 10px 16px;
  min-width: 132px;
  cursor: pointer;
}
.lens-tabs button:last-child { border-right: 1px solid var(--rule); }
.lens-tabs button.is-active { background: var(--deep); border-color: var(--deep); color: var(--paper); }
.lens-tabs small { display: block; margin-top: 2px; font-size: var(--t-9); font-weight: normal; opacity: 0.75; }

.active-filters { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; min-height: 26px; margin-top: 8px; }
.active-filters .muted-note { font-size: var(--t-11); color: var(--ink-faint); }

.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--rule);
  background: var(--paper);
  padding: 4px 8px;
  font-family: var(--font-sans);
  font-size: var(--t-11);
  color: var(--ink);
  cursor: pointer;
}
.filter-chip b { font-weight: bold; }
.filter-chip:hover { border-color: var(--coral); }
.filter-chip .x { color: var(--ink-faint); }
.filter-chip:hover .x { color: var(--coral); }

.insp-section { border-top: 1px solid var(--rule); padding: 9px 0 0; margin-top: 9px; }
.insp-section h3 {
  margin: 0 0 6px;
  font-family: var(--font-sans);
  font-size: var(--t-11);
  font-weight: bold;
  color: var(--ink);
}
.insp-prose { margin: 0; font-size: var(--t-11); line-height: 1.5; color: var(--ink-soft); }

.insp-pair { display: grid; grid-template-columns: 82px 1fr; gap: 8px; padding: 3px 0; font-size: var(--t-11); }
.insp-pair dt { color: var(--ink-faint); }
.insp-pair dd { margin: 0; color: var(--ink-soft); }
.insp-pair dd.is-absent { color: var(--coral); }

.move-list { display: grid; gap: 4px; }
.move {
  border: 0;
  background: none;
  padding: 0;
  text-align: left;
  font-family: var(--font-sans);
  font-size: var(--t-11);
  line-height: 1.4;
  color: var(--ink-soft);
  cursor: pointer;
}
.move b { color: var(--accent); font-weight: bold; }
.move:hover b { text-decoration: underline; text-underline-offset: 3px; }
.move em { font-style: normal; color: var(--ink-faint); }

.name-link {
  border: 0;
  padding: 0;
  background: none;
  font: inherit;
  color: var(--ink);
  font-weight: bold;
  border-bottom: 1px solid var(--coral);
  cursor: pointer;
}
.name-link:hover { background: var(--rose); }

@media (max-width: 760px) {
  .app-top {
    flex-wrap: wrap;
    gap: var(--gap-sm);
    padding: 12px 16px;
  }
  .app-top > div:first-child { flex: 1 1 100%; }
  .app-actions { flex: 1 1 100%; }
  .app-actions .app-btn { font-size: var(--t-11); padding: 7px 9px; }
  .app-title { font-size: var(--t-21); }

  /* The sentence gets the width. The counts go underneath it, in a row. */
  .boundary { grid-template-columns: 1fr; }
  .boundary-figures, .metric-strip { flex-wrap: wrap; }
  .boundary-figures .metric, .metric-strip .metric { min-width: 0; flex: 1 1 30%; }

  .app-shell { padding: 12px 14px 28px; }
  .controls { display: block; }
  .controls > * + * { margin-top: var(--gap-sm); }
  .filters { flex-wrap: wrap; }
  .filters select { min-width: 0; width: 100%; }
  .filters label { flex: 1 1 140px; }
  .lens-tabs { flex-wrap: wrap; }
  .lens-tabs button { flex: 1 1 auto; min-width: 0; }

  .inspector { position: static; max-height: none; }
  .plot-frame, .plot-frame canvas { min-height: 340px; height: 340px; }
}


/* --------------------------------------------------------------------------
   41 — the canvas
   A diagram somebody else placed. lib/canvas.js draws it from coordinates the
   model carries; the theme owns what the boxes look like and never where they
   are, which is the opposite of the poster and the point of having both.

   Colour is read off the tokens at runtime by the engine. Nothing here sets a
   fill: an SVG rect with a fill attribute would lose to a stylesheet rule and
   silently ignore the role the model asked for.
   -------------------------------------------------------------------------- */

.canvas { display: block; width: 100%; height: auto; }
.canvas-label { font-family: var(--font-sans); }
.canvas-box rect { vector-effect: non-scaling-stroke; }
.canvas-box.is-boundary rect { vector-effect: non-scaling-stroke; }
.canvas-edge { vector-effect: non-scaling-stroke; }
.canvas-figure { pointer-events: none; }
.canvas-mark { pointer-events: none; }
