/* ---------------------------------------------------------------------------
   tokens.css — the shared design system
   ---------------------------------------------------------------------------
   Derived from three things, none of them invented here:

     1. causeoferror.net  — black masthead band, heavy sans title, white paper
     2. FCA threat-model workbooks — #1F4E78 headers, #FFF7D1 row banding
     3. Tufte — a wide margin column for notes and figures, hairline rules, no
        chrome that isn't carrying data, and quiet tables

   The blue is the structural accent. The yellow is a highlight and stops
   working the moment it appears on every second element, so it is reserved for
   callouts and for tables that are genuinely workbooks.

   Per-site variation happens by overriding --mark and --mark-ink from
   sites/<name>/site.json. Nothing else should be overridden. If a site needs
   more than an accent colour to feel distinct, that's a content problem.
--------------------------------------------------------------------------- */

:root {
  /* --- ink and paper ------------------------------------------------------ */
  --ink:          #14161a;   /* body text; near-black, cool cast to sit with the blue */
  --ink-quiet:    #5b6470;   /* captions, metadata, deemphasised text */
  --ink-faint:    #8b939d;   /* axis labels — below reading weight on purpose */
  --paper:        #ffffff;
  --paper-sunk:   #f5f6f8;   /* pull quotes, aside blocks */
  --rule:         #d8dce1;
  --rule-hair:    #e6e9ed;   /* gridlines; they sit under the data, never beside it */

  /* --- masthead ----------------------------------------------------------- */
  /* Kept exactly as the original site. The black band is the one thing every
     site shares on sight, and it is the whole reason they read as a set. */
  --masthead:     #000000;
  --masthead-ink: #ffffff;

  /* --- structural accent -------------------------------------------------- */
  /* The workbook header blue, promoted from table chrome to the system accent. */
  --brief:        #1f4e78;
  --brief-lift:   #2d6ba4;   /* hover / focus */
  --brief-wash:   #eaf0f6;   /* tinted backgrounds */

  /* --- banding ------------------------------------------------------------ */
  /* Use sparingly. Workbook tables and callouts only. */
  --band:         #fff7d1;
  --band-edge:    #e8d98f;

  /* --- state -------------------------------------------------------------- */
  --warn:         #8a5a00;
  --stop:         #9b2226;

  /* --- sequential ramp ---------------------------------------------------- */
  /* One hue, five values, walked out of --brief. For anything ordered: signal
     strength, density, count. Never use the categorical set for ordered data —
     the reader will look for a ranking that isn't there. */
  --seq-1: #eaf0f6;
  --seq-2: #c3d4e6;
  --seq-3: #8aabcb;
  --seq-4: #4a7ba7;
  --seq-5: #1f4e78;

  /* --- risk ramp ---------------------------------------------------------- */
  /* Likelihood x impact fills for the threat matrix. Light fills with dark ink
     rather than saturated blocks with white ink: the cell contents are the
     data, the fill is only the ordering. */
  --risk-1:     #e8eef4;   /* low */
  --risk-2:     #fff7d1;   /* moderate */
  --risk-3:     #f3d0a3;   /* high */
  --risk-4:     #e9c0c1;   /* critical */
  --risk-4-ink: #6d181b;

  /* --- categorical series ------------------------------------------------- */
  /* Five, and five is the limit. Every one of these is already a house colour;
     none was picked to look nice next to the others. A chart that needs a
     sixth series needs to be two charts. */
  --cat-1: #1f4e78;
  --cat-2: #8a5a00;
  --cat-3: #4a7ba7;
  --cat-4: #9b2226;
  --cat-5: #5b6470;

  /* --- per-site accent (overridden per site) ------------------------------ */
  --mark:         var(--brief);
  --mark-ink:     #ffffff;

  /* --- type --------------------------------------------------------------- */
  /* System stacks only. These sites need to render in ten years without a
     network dependency on somebody else's font host. The body serif is the
     Tufte influence; the display sans is the causeoferror masthead, and it is
     what keeps the sites reading as a set. */
  --face-display: ui-sans-serif, "Helvetica Neue", Helvetica, Arial,
                  "Segoe UI", Roboto, system-ui, sans-serif;
  --face-body:    "Iowan Old Style", "Palatino Linotype", Palatino,
                  "Book Antiqua", Charter, "Bitstream Charter", Georgia,
                  "Times New Roman", serif;
  /* Monospace is a first-class role here, not a code-block afterthought.
     Callsigns, grid squares, threat IDs, timestamps, S3 keys. */
  --face-mono:    ui-monospace, "SF Mono", SFMono-Regular, Menlo,
                  "Cascadia Mono", "Roboto Mono", monospace;

  --size-masthead: clamp(2.25rem, 6vw, 4rem);
  --size-h1:       clamp(1.75rem, 3.5vw, 2.5rem);
  --size-h2:       1.5rem;
  --size-h3:       1.175rem;
  --size-body:     1.125rem;   /* serif needs the extra over the old sans 17px */
  --size-small:    0.9375rem;
  --size-note:     0.8125rem;  /* sidenotes, figure captions, axis labels */
  --size-micro:    0.75rem;    /* record strip, footer */

  --weight-display: 800;
  --weight-heading: 700;
  --weight-body:    400;

  --track-display: -0.022em;  /* heavy sans wants tightening at display size */
  --track-micro:    0.08em;   /* the record strip wants opening up */

  --leading-tight: 1.15;
  --leading-body:  1.6;

  /* --- spacing ------------------------------------------------------------ */
  --s-1: 0.25rem;
  --s-2: 0.5rem;
  --s-3: 0.75rem;
  --s-4: 1rem;
  --s-5: 1.5rem;
  --s-6: 2rem;
  --s-7: 3rem;
  --s-8: 4.5rem;

  /* --- measure and layout ------------------------------------------------- */
  --measure:      65ch;    /* body text column */
  --measure-wide: 104ch;   /* text column plus margin column */
  --margin-col:   15rem;   /* the Tufte margin: sidenotes and small figures */
  --margin-gap:   var(--s-6);
  --gutter:       var(--s-5);

  /* --- other -------------------------------------------------------------- */
  --radius: 0;          /* the original site has no rounded corners; keep it */
  --focus:  3px solid var(--brief-lift);
}

/* Respect the reader's preference. Nothing here needs to move. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---------------------------------------------------------------------------
   base.css — layout, masthead, record strip, prose, notes, tables
   Depends on tokens.css. Load tokens first.
--------------------------------------------------------------------------- */

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

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--face-body);
  font-size: var(--size-body);
  font-weight: var(--weight-body);
  line-height: var(--leading-body);
}

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

/* --- skip link ----------------------------------------------------------- */

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--masthead);
  color: var(--masthead-ink);
  padding: var(--s-3) var(--s-4);
  z-index: 10;
}
.skip:focus { left: 0; }

/* --- masthead ------------------------------------------------------------ */
/* The full-bleed black band. The one element every site shares on sight. */

.masthead {
  background: var(--masthead);
  color: var(--masthead-ink);
  padding: var(--s-7) var(--gutter);
  text-align: center;
}

.masthead__title {
  margin: 0;
  font-family: var(--face-display);
  font-size: var(--size-masthead);
  font-weight: var(--weight-display);
  letter-spacing: var(--track-display);
  line-height: var(--leading-tight);
}

.masthead__title a { color: inherit; text-decoration: none; }
.masthead__title a:hover { text-decoration: underline; text-underline-offset: 0.12em; }

.masthead__tag {
  margin: var(--s-3) 0 0;
  font-family: var(--face-mono);
  font-size: var(--size-micro);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: #b9bfc7;
}

/* --- navigation ---------------------------------------------------------- */

.nav {
  border-bottom: 1px solid var(--rule);
  padding: 0 var(--gutter);
}

.nav__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-5);
  max-width: var(--measure-wide);
  margin: 0 auto;
  padding: var(--s-3) 0;
  list-style: none;
}

.nav__list a {
  font-family: var(--face-display);
  color: var(--ink-quiet);
  text-decoration: none;
  font-size: var(--size-note);
}
.nav__list a:hover,
.nav__list a[aria-current="page"] { color: var(--mark); }

/* --- page shell ---------------------------------------------------------- */
/* The shell is wide enough to hold the text measure plus the margin column.
   Everything in .prose is set to the measure; notes and wide figures are the
   only things allowed to reach into the margin. */

.shell {
  max-width: var(--measure-wide);
  margin: 0 auto;
  padding: var(--s-7) var(--gutter) var(--s-8);
}

.prose { max-width: var(--measure); }

/* --- the signature: record strip ----------------------------------------- */
/* A monospace metadata line under the page title carrying version, date and
   status. This is the `v0.1a 22/01/22` that was buried in the old site's
   footer, promoted to a designed element — because it encodes the actual
   claim these pages make: they are versioned documents, not blog posts.
   Only use it on pages that genuinely are versioned. */

.record {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-5);
  margin: 0 0 var(--s-6);
  padding: var(--s-2) 0;
  border-top: 2px solid var(--ink);
  border-bottom: 1px solid var(--rule);
  font-family: var(--face-mono);
  font-size: var(--size-micro);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--ink-quiet);
}

.record__item { white-space: nowrap; }
.record__key   { color: var(--ink-quiet); }
.record__value { color: var(--ink); }

.record__value--live    { color: var(--brief); }
.record__value--draft   { color: var(--warn); }
.record__value--retired { color: var(--stop); }

/* --- prose --------------------------------------------------------------- */

h1, h2, h3, h4 {
  font-family: var(--face-display);
  font-weight: var(--weight-heading);
  line-height: var(--leading-tight);
  letter-spacing: -0.012em;
  margin: var(--s-7) 0 var(--s-3);
}
h1 { font-size: var(--size-h1); margin-top: 0; letter-spacing: var(--track-display); }
h2 { font-size: var(--size-h2); }
h3 { font-size: var(--size-h3); }

p, ul, ol { margin: 0 0 var(--s-4); }
li { margin-bottom: var(--s-2); }

a { color: var(--brief); text-underline-offset: 0.16em; }
a:hover { color: var(--brief-lift); }

code, kbd, samp { font-family: var(--face-mono); font-size: 0.875em; }

pre {
  background: var(--paper-sunk);
  border-left: 3px solid var(--rule);
  padding: var(--s-4);
  overflow-x: auto;
  font-size: var(--size-small);
  line-height: 1.5;
}

blockquote {
  margin: var(--s-5) 0;
  padding: var(--s-3) var(--s-5);
  border-left: 3px solid var(--mark);
  background: var(--paper-sunk);
  color: var(--ink-quiet);
}

hr { border: 0; border-top: 1px solid var(--rule); margin: var(--s-7) 0; }

img, video { max-width: 100%; height: auto; display: block; }

/* --- sidenotes and margin notes ------------------------------------------ */
/* Tufte's device: the note sits beside the sentence that earned it, so the
   reader never leaves the line to read it. No JavaScript — the narrow-screen
   behaviour is a checkbox and a sibling selector.

   Numbering is a CSS counter, so notes renumber themselves when the prose is
   edited. Nothing in the markup carries a number. */

.prose { counter-reset: sidenote; }

.sidenote,
.marginnote {
  float: right;
  clear: right;
  width: var(--margin-col);
  margin-right: calc(-1 * (var(--margin-col) + var(--margin-gap)));
  margin-bottom: var(--s-4);
  font-size: var(--size-note);
  line-height: 1.45;
  color: var(--ink-quiet);
  position: relative;
  vertical-align: baseline;
}

.sidenote > :last-child,
.marginnote > :last-child { margin-bottom: 0; }

.sidenote-number { counter-increment: sidenote; cursor: pointer; }

/* The marker in the running text, and the matching number on the note. */
.sidenote-number::after,
.sidenote::before {
  content: counter(sidenote);
  font-family: var(--face-mono);
  font-size: var(--size-micro);
  color: var(--brief);
  vertical-align: super;
  line-height: 0;
}
.sidenote::before { vertical-align: baseline; margin-right: 0.4em; }

/* The checkbox is never visible; it exists to hold the open/closed state on
   narrow screens. It must be the only thing this rule hides — the label is
   what increments the counter, and a display:none element increments
   nothing, which numbers every note zero. */
.margin-toggle { display: none; }

/* A margin note is an aside, not a citation, so it gets no marker while the
   margin column is there to put it in. */
.marginnote-marker { display: none; }

/* --- narrow screens: notes fold into the text flow ----------------------- */

@media (max-width: 76rem) {
  .sidenote,
  .marginnote { display: none; }

  .sidenote-number,
  .marginnote-marker {
    display: inline;
    color: var(--brief);
    cursor: pointer;
  }
  .marginnote-marker::after {
    content: "\2295";           /* circled plus */
    font-size: var(--size-note);
  }

  /* A checked toggle reveals its note as an indented block. */
  .margin-toggle:checked + .sidenote,
  .margin-toggle:checked + .marginnote {
    display: block;
    float: none;
    width: auto;
    max-width: 100%;
    margin: var(--s-3) 0 var(--s-4) var(--s-5);
    padding-left: var(--s-4);
    border-left: 2px solid var(--rule);
  }
}

/* --- figures ------------------------------------------------------------- */

.figure {
  margin: var(--s-6) 0;
  max-width: var(--measure);
}

/* Reaches into the margin column. Use for anything with an axis. */
.figure--wide {
  max-width: calc(var(--measure) + var(--margin-gap) + var(--margin-col));
}

/* Lives entirely in the margin column, beside the paragraph it belongs to. */
.figure--margin {
  float: right;
  clear: right;
  width: var(--margin-col);
  margin: var(--s-1) calc(-1 * (var(--margin-col) + var(--margin-gap))) var(--s-4) 0;
}

.figure__caption {
  margin: var(--s-2) 0 0;
  font-size: var(--size-note);
  line-height: 1.45;
  color: var(--ink-quiet);
}
.figure__caption b,
.figure__caption strong {
  font-family: var(--face-display);
  font-weight: var(--weight-heading);
  color: var(--ink);
}

figure { margin: var(--s-6) 0; }
figcaption {
  margin-top: var(--s-2);
  font-size: var(--size-note);
  color: var(--ink-quiet);
}

@media (max-width: 76rem) {
  .figure--wide { max-width: 100%; }
  .figure--margin { float: none; width: 100%; margin: var(--s-5) 0; }
}

/* --- callout ------------------------------------------------------------- */
/* The banding yellow. Keep it rare — it stops working if it's everywhere. */

.callout {
  background: var(--band);
  border: 1px solid var(--band-edge);
  padding: var(--s-4) var(--s-5);
  margin: var(--s-5) 0;
}
.callout > :last-child { margin-bottom: 0; }

/* --- tables -------------------------------------------------------------- */
/* Two registers, and the difference between them carries meaning.

   Default (any markdown table): quiet. Hairline row rules, no verticals, no
   fill. The numbers are the point; the grid is not.

   .workbook: the FCA threat-model house style, blue header and yellow row
   banding, so a rendered model looks the same on the web as it does in the
   xlsx. Emitted by the workbook and threatmodel shortcodes, never written by
   hand — which is what keeps the yellow rare enough to still mean something. */

.table-wrap {
  overflow-x: auto;
  margin: var(--s-6) 0;
  max-width: calc(var(--measure) + var(--margin-gap) + var(--margin-col));
}

table {
  border-collapse: collapse;
  width: 100%;
  font-size: var(--size-small);
  line-height: 1.45;
  /* Lining figures throughout. A column of counts that doesn't align on the
     digit is a column you have to read rather than scan. */
  font-variant-numeric: tabular-nums;
}

thead th {
  font-family: var(--face-display);
  font-size: var(--size-note);
  font-weight: var(--weight-heading);
  text-align: left;
  padding: 0 var(--s-4) var(--s-2) 0;
  vertical-align: bottom;
  border-bottom: 1px solid var(--ink);
}

tbody td {
  padding: var(--s-2) var(--s-4) var(--s-2) 0;
  border-bottom: 1px solid var(--rule-hair);
  vertical-align: top;
}

tbody tr:last-child td { border-bottom: 1px solid var(--ink); }

/* Numeric columns line up, which is the only way a column of numbers is
   worth setting. */
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }

/* Identifier columns get the mono face — FCA###, threat IDs, callsigns. */
td.id, th.id, .mono { font-family: var(--face-mono); white-space: nowrap; }

/* --- tables: the workbook register --------------------------------------- */

.workbook table { font-size: var(--size-note); }

.workbook thead th {
  background: var(--brief);
  color: #fff;
  padding: var(--s-3) var(--s-4);
  border-bottom: 0;
}

.workbook tbody td {
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--rule);
}
.workbook tbody tr:nth-child(odd) { background: var(--band); }
.workbook tbody tr:last-child td { border-bottom: 1px solid var(--rule); }

.workbook__title {
  font-family: var(--face-mono);
  font-size: var(--size-micro);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--ink-quiet);
  margin: 0 0 var(--s-2);
}

/* --- footer -------------------------------------------------------------- */

.foot {
  border-top: 1px solid var(--rule);
  margin-top: var(--s-8);
  padding: var(--s-5) var(--gutter);
  font-family: var(--face-mono);
  font-size: var(--size-micro);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--ink-quiet);
}
.foot__inner {
  max-width: var(--measure-wide);
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-5);
  justify-content: space-between;
}

/* --- narrow screens ------------------------------------------------------ */

@media (max-width: 40rem) {
  .masthead { padding: var(--s-6) var(--s-4); }
  .shell    { padding: var(--s-5) var(--s-4) var(--s-7); }
  .record   { font-size: 0.6875rem; }
}

/* --- print --------------------------------------------------------------- */
/* These are versioned documents. They should print like documents. */

@media print {
  .nav, .skip { display: none; }
  .masthead {
    background: none;
    color: #000;
    text-align: left;
    padding: 0 0 var(--s-4);
  }
  .masthead__tag { color: #444; }
  .shell { max-width: none; padding: 0; }
  a { color: #000; }
  .sidenote, .marginnote { color: #333; }
}

/* ---------------------------------------------------------------------------
   diagrams.css — type and rules for the inline SVG figures
   Depends on tokens.css. Emitted by theme/shortcodes.js.
   ---------------------------------------------------------------------------
   The SVG carries geometry only. Every colour and every typeface is set here,
   so a figure restyles with the tokens and a site's accent reaches the
   diagrams without any of them being regenerated.
--------------------------------------------------------------------------- */

.fig {
  display: block;
  max-width: 100%;
  height: auto;
  overflow: visible;
}

/* --- figure type --------------------------------------------------------- */
/* Labels are sans, values and identifiers are mono. Everything in a figure is
   below reading size on purpose: the marks are the content, the text is
   scaffolding, and scaffolding that competes with the data has lost. */

.fig text { font-family: var(--face-display); fill: var(--ink); }

.fig__label {
  font-size: 11px;
  fill: var(--ink-quiet);
}
.fig__label--right { text-anchor: end; }

.fig__value,
.fig__tick,
.fig__id {
  font-family: var(--face-mono);
  font-size: 10px;
  fill: var(--ink-quiet);
}
.fig__value { fill: var(--ink); }
.fig__tick  { fill: var(--ink-faint); }

.fig__axis {
  font-size: 10px;
  fill: var(--ink-faint);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
}

.fig__node {
  font-size: 11.5px;
  fill: var(--ink);
}

/* A figure that hasn't arrived. Quiet enough not to shout, obvious enough
   that nobody mistakes it for the real thing. */
.fig__placeholder {
  font-family: var(--face-mono);
  font-size: 12px;
  fill: var(--ink-quiet);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
}

.fig__flow {
  font-size: 10px;
  fill: var(--ink-quiet);
}

.fig__boundary {
  font-family: var(--face-mono);
  font-size: 9.5px;
  fill: var(--stop);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
}

/* --- sparkline ----------------------------------------------------------- */
/* Sits in a line of text, so it has to sit on the baseline like a word. */

.sparkline {
  display: inline-block;
  vertical-align: -0.3em;
  margin: 0 0.15em;
}

/* --- small multiples ----------------------------------------------------- */
/* A grid of the same graphic, one shared scale. The label is above and the
   current value beside, so the eye reads down the column of values and across
   the column of shapes without a legend in between. */

.sm {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: var(--s-4) var(--s-5);
}

.sm__cell {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "label label"
    "spark value";
  align-items: center;
  gap: 0 var(--s-3);
  border-top: 1px solid var(--rule-hair);
  padding-top: var(--s-2);
}

.sm__label {
  grid-area: label;
  font-family: var(--face-display);
  font-size: var(--size-note);
  color: var(--ink-quiet);
}

.sm__cell .sparkline {
  grid-area: spark;
  vertical-align: baseline;
  margin: 0;
}

.sm__value {
  grid-area: value;
  font-family: var(--face-mono);
  font-size: var(--size-note);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

/* --- print --------------------------------------------------------------- */

@media print {
  .fig { break-inside: avoid; }
  .sm__cell { break-inside: avoid; }
}
