/* Layout: a title, then a grid of cards. Each card holds one stat: its name, the change chips for 2022 to 2025, and a line chart of the percent change over three comparison periods on a dark panel. The page around the cards follows the viewer's light or dark theme; the chart panel is always dark. */
:root {
  --font: "Public Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --bg: #f6f8fb;
  --card: #ffffff;
  --ink: #0d1b2a;
  --ink-2: #44556a;
  --ink-3: #5d6d80;
  --grid: #e1e7ee;
  --axis: #c3ccd7;
  --focus: #1c5cab;
  --gap: #6a5fb0;
  --gap-hatch: rgb(106 95 176 / .12);
  --cbg: #0e141b;
  --cink: #e8eef5;
  --cink-3: #8e9cac;
  --cgrid: #243241;
  --caxis: #3f5063;
  --cgap: #b3a8ee;
  --s-now: #e5484d;
  --s-a: #f0a030;
  --s-b: #3ecf8e;
  --cup: #f0616d;
  --cdown: #3dc489;
  --r-10: #6da7ec;
  --r-ff: #d5deea;
  --gutter: clamp(1rem, 4vw, 2.5rem);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0e141b; --card: #151e29; --ink: #e8eef5; --ink-2: #b4c0cd; --ink-3: #8e9cac;
    --grid: #1f2b38; --axis: #2f3d4c; --focus: #6da7ec; --gap: #b3a8ee; --gap-hatch: rgb(179 168 238 / .13);
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --bg: #0e141b; --card: #151e29; --ink: #e8eef5; --ink-2: #b4c0cd; --ink-3: #8e9cac;
  --grid: #1f2b38; --axis: #2f3d4c; --focus: #6da7ec; --gap: #b3a8ee; --gap-hatch: rgb(179 168 238 / .13);
  color-scheme: dark;
}

*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }
body {
  background: var(--bg); color: var(--ink);
  font-family: var(--font); font-size: .9375rem; line-height: 1.55;
  -webkit-text-size-adjust: 100%;
}
h1, h2, p, figure { margin: 0; }
h1, h2 { text-wrap: balance; }
.wrap { max-width: var(--wrapw, 90rem); margin-inline: auto; padding-inline: var(--gutter); }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* The controls panel is a floating card. It starts at the bottom centre of the window (lifted above the zoom bar if they
   would overlap) and can be dragged anywhere by its grip or any empty part of the card. The script sets its left and top.
   On a narrow screen it becomes one row that scrolls sideways, so it does not take over the window. */
.controls { position: fixed; left: 50%; bottom: 1rem; z-index: 30; display: flex; align-items: stretch; width: max-content; max-width: calc(100vw - 1.5rem);
  background: var(--card); border: 1px solid var(--grid); border-radius: 14px; box-shadow: 0 1px 3px rgb(0 0 0 / .06), 0 10px 30px rgb(0 0 0 / .12); cursor: grab; touch-action: none; }
:root[data-view="canvas"] .controls { max-width: calc(100vw - var(--sidew) - 1.5rem); }
.controls:not(.placed) { transform: translateX(-50%); }
.controls.placed { bottom: auto; }
.controls.dragging, .controls.dragging * { cursor: grabbing; }
.controls :is(button, input, label, output) { cursor: pointer; }
.controls output { cursor: inherit; }
/* Two rows: what you look at (view, periods, rates), then layout (cards per row, width, hidden cards). */
.cbody { min-width: 0; display: grid; gap: .5rem; padding: .6rem 1rem .6rem .15rem; }
.crow { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1.5rem; }
.grip { flex: none; width: 1.6rem; display: grid; place-items: center; padding: 0; border: 0; border-radius: 13px 0 0 13px; background: transparent; color: var(--ink-3); cursor: grab !important; touch-action: none; }
.grip:hover { color: var(--ink); background: var(--bg); }
.grip svg { width: 8px; height: 14px; fill: currentColor; }
/* In the All and Sources views the page leaves room at the end so its last lines can scroll clear of the panel. */
body { padding-bottom: calc(var(--barh, 56px) + 2rem); }
@media (max-width: 48rem) {
  .controls { width: calc(100vw - 1.5rem); }
  :root[data-view="canvas"] .controls { width: calc(100vw - var(--sidew) - 1.5rem); }
  .cbody { display: flex; gap: 1.5rem; overflow-x: auto; scrollbar-width: thin; }
  .crow { display: contents; }
  .ctl { flex: none; }
}
.cardsonly[inert] { opacity: .4; }
/* View pills: one is always on. The filled pill is the current view. */
.vw { justify-content: center; padding: .2rem .85rem; font-size: .72rem; letter-spacing: .06em; text-transform: uppercase; }
.tg.vw[aria-pressed="true"], .tg.vw[aria-pressed="true"]:hover { color: var(--bg); background: var(--ink); border-color: var(--ink); }
/* Views: All shows everything, Canvas shows only the canvas, Sources shows everything except the canvas. */
:root[data-view="canvas"] .head, :root[data-view="canvas"] #cards, :root[data-view="canvas"] #rates, :root[data-view="canvas"] .src,
:root[data-view="sources"] #canvasbox { display: none; }
/* Canvas view: the canvas fills the whole window; the controls panel floats over it. */
:root[data-view="canvas"] #canvasbox { position: fixed; z-index: 1; left: 0; right: var(--sidew); top: env(safe-area-inset-top, 0px); bottom: 0; width: auto; height: auto; margin: 0; }
:root[data-view="canvas"] .canvas { height: 100%; border-block: 0; }
:root[data-view="sources"] .src { margin-top: 2rem; }
.lim { font-size: .75rem; color: var(--ink-3); }
.ctl { display: flex; align-items: center; gap: .75rem; }
.toggles { display: flex; flex-wrap: wrap; gap: .4rem; }
.tg { display: inline-flex; align-items: center; gap: .45rem; font: inherit; font-size: .8rem; font-weight: 600; color: var(--ink-3); background: transparent; border: 1px solid var(--axis); border-radius: 999px; padding: .2rem .8rem .2rem .6rem; min-height: 2rem; cursor: pointer; }
.tg:hover { border-color: var(--ink-3); }
.tg[aria-pressed="true"] { color: var(--ink); background: var(--card); border-color: var(--ink-3); }
.tg[aria-disabled="true"] { cursor: default; }
.sw { width: .65rem; height: .65rem; border-radius: 50%; border: 2px solid var(--c); background: transparent; }
.tg[aria-pressed="true"] .sw { background: var(--c); }
.sw.now { --c: var(--s-now); } .sw.a { --c: var(--s-a); } .sw.b { --c: var(--s-b); }
.controls label, .controls .lab { font-size: .75rem; font-weight: 600; color: var(--ink-2); letter-spacing: .06em; text-transform: uppercase; }
.controls input[type="range"] { width: 8.5rem; accent-color: var(--focus); }
.controls output { min-width: 1ch; white-space: nowrap; font-size: .95rem; font-weight: 700; font-variant-numeric: tabular-nums; }
h1[contenteditable] { border-radius: 6px; outline: 1px dashed transparent; outline-offset: 6px; cursor: text; }
h1[contenteditable]:hover { outline-color: var(--axis); }
h1[contenteditable]:focus { outline: 2px solid var(--focus); outline-offset: 6px; }
.head .wrap { padding-block: clamp(1.75rem, 5vw, 3rem) 1.5rem; }
.allapps { display: inline-block; margin: 0 0 10px; font-size: 14px; color: var(--focus); text-decoration: none; }
.shead .allapps { margin: 0 0 0 auto; align-self: center; }
h1 { font-size: clamp(1.75rem, 4.6vw, 2.4rem); line-height: 1.1; font-weight: 800; letter-spacing: -.02em; }
/* Cards */
.cards { --cols: 4; display: grid; grid-template-columns: repeat(var(--cols), minmax(0, 1fr)); gap: 1rem; padding-block: .5rem 3rem; }
@media (max-width: 72rem) { .cards { --cols: 2; } }
@media (max-width: 34rem) { .cards { --cols: 1; } }
.card { min-width: 0; display: grid; gap: .9rem; align-content: start; padding: 1.1rem; background: var(--card); border: 1px solid var(--grid); border-radius: 12px; cursor: pointer; }
.sw.all { --c: var(--ink-3); }
#showall[aria-disabled="true"] { opacity: .55; }
@supports (grid-template-rows: subgrid) {
  .card { grid-row: span 3; grid-template-rows: subgrid; row-gap: .9rem; }
}
.stat { font-size: 1.05rem; font-weight: 700; line-height: 1.25; }
.chips { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .65rem; }
.delta {
  font-size: .75rem; font-weight: 600; color: var(--ink-2); white-space: nowrap; font-variant-numeric: tabular-nums;
  padding: .05rem .5rem; border: 1px solid var(--axis); border-radius: 999px;
}
.delta.rel { color: var(--ink); border-color: var(--ink-3); }
.note { font-size: .82rem; color: var(--ink-2); }
/* Chart panel: always dark */
.plot { min-width: 0; background: var(--cbg); border-radius: 10px; overflow: hidden; }
.plot svg { display: block; max-width: 100%; height: auto; }
.cbase { stroke: var(--caxis); stroke-width: 1; }
.gl { stroke: var(--cgrid); stroke-width: 1; }
.ln { fill: none; stroke-width: calc(2px * var(--k, 1)); stroke-linecap: round; stroke-linejoin: round; }
.ln.now { stroke: var(--s-now); } .ln.a { stroke: var(--s-a); } .ln.b { stroke: var(--s-b); }
.dot { stroke: var(--cbg); stroke-width: calc(2px * var(--k, 1)); }
.dot.now { fill: var(--s-now); } .dot.a { fill: var(--s-a); } .dot.b { fill: var(--s-b); }
.tl { fill: var(--cink-3); font: 500 calc(11px * var(--k, 1)) var(--font); font-variant-numeric: tabular-nums; }
.yl { fill: var(--cink-3); font: 500 calc(12px * var(--k, 1)) var(--font); }
.area { stroke: none; fill-opacity: .35; }
.area.now { fill: var(--s-now); }
.area.b { fill: var(--s-b); }
.pl { fill: var(--cink-3); font: 500 calc(10.5px * var(--k, 1)) var(--font); paint-order: stroke; stroke: var(--cbg); stroke-width: calc(3px * var(--k, 1)); stroke-linejoin: round; }
.nt { fill: var(--cink-3); font: italic 400 calc(11px * var(--k, 1)) var(--font); }
.tri.up { fill: var(--cup); }
.tri.down { fill: var(--cdown); }
.vl { fill: var(--cink); font: 600 calc(13px * var(--k, 1)) var(--font); font-variant-numeric: tabular-nums; paint-order: stroke; stroke: var(--cbg); stroke-width: calc(4px * var(--k, 1)); stroke-linejoin: round; }

/* Interest rates (switched on from the controls bar) */
.sw.rt { --c: var(--r-10); }
.ratebox { display: grid; gap: .9rem; margin: -2rem 0 1rem; padding: 1.1rem; background: var(--card); border: 1px solid var(--grid); border-radius: 12px; }
.ln.r10 { stroke: var(--r-10); } .ln.rff { stroke: var(--r-ff); }
.dot.r10 { fill: var(--r-10); } .dot.rff { fill: var(--r-ff); }
.band { stroke: none; fill-opacity: .18; }
.band.now { fill: var(--s-now); } .band.a { fill: var(--s-a); } .band.b { fill: var(--s-b); }
.bl { font: 600 calc(11px * var(--k, 1)) var(--font); }
.bl.now { fill: var(--s-now); } .bl.a { fill: var(--s-a); } .bl.b { fill: var(--s-b); }
.rn { font: 600 calc(12px * var(--k, 1)) var(--font); }
.rv { font: 600 calc(12px * var(--k, 1)) var(--font); font-variant-numeric: tabular-nums; paint-order: stroke; stroke: var(--cbg); stroke-width: calc(4px * var(--k, 1)); stroke-linejoin: round; }
.rn.r10, .rv.r10 { fill: var(--r-10); } .rn.rff, .rv.rff { fill: var(--r-ff); }

/* Flow canvas */
/* The canvas has no card around it. It runs the full width (--pagew is the window width without its scrollbar)
   and full height of the window. In the All view it sits in the page between thin rules. */
.canvasbox { display: block; width: var(--pagew, 100vw); margin: 1rem calc(50% - var(--pagew, 100vw) / 2) 3rem; }
.ratebox[hidden] + .canvasbox { margin-top: -2rem; }
.controls .tg { min-height: 1.85rem; }
.canvas { position: relative; height: 100vh; height: 100dvh; overflow: hidden; border-block: 1px solid var(--grid); background-color: var(--bg); background-image: radial-gradient(circle, var(--axis) 1px, transparent 1.4px); background-size: 24px 24px; cursor: grab; touch-action: pan-y; user-select: none; -webkit-user-select: none; }
.canvas.panning { cursor: grabbing; }
.world { position: absolute; left: 0; top: 0; transform-origin: 0 0; }
.edges { position: absolute; left: 0; top: 0; width: 1px; height: 1px; overflow: visible; pointer-events: none; }
.edge { fill: none; stroke: var(--ink-3); stroke-width: 2; }
.edge.inf { stroke-dasharray: 7 6; }
.arrow { fill: var(--ink-3); }
.edge.on { stroke: var(--focus); } .arrow.on { fill: var(--focus); }
.edge.dim, .arrow.dim { opacity: .3; }
/* Connection cards: a light grey card on the cable (#f5f5f5 in both themes, so its ink is fixed dark). The band on
   top holds the card's id on its light, CONNECTION, the source → target ids in grey, and the </> toggle (drag the card
   by the band). The light is red while the table is empty and green once it has contents; a yellow state (.warn) is
   styled and ready. The body is a small table that starts as one cell: the faded + column on the right adds a
   column, the faded + row underneath adds a row. */
.node.lnode { width: auto; min-width: 240px; background: #f5f5f5; color: #1f1f1f; border: 1px solid #d4d4d4; border-radius: 3px 3px 12px 3px;
  box-shadow: 0 1px 2px rgb(0 0 0 / .12), 0 8px 20px rgb(0 0 0 / .1); }
.lnode .nh { justify-content: space-between; gap: .6rem; padding: .3rem .35rem .3rem .4rem; background: #ebebeb; border-bottom: 1px solid #d4d4d4; border-radius: 2px 2px 0 0; }
.ltag { display: flex; align-items: center; gap: .45rem; min-width: 0; }
.lid { flex: none; min-width: 1.6rem; height: 1.6rem; padding: 0 .4rem; display: grid; place-items: center; border-radius: 999px;
  font: 700 .68rem/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.lid.empty { background: #b8322c; color: #fff; }
.lid.ok { background: #178a46; color: #fff; }
.lid.warn { background: #f5c400; color: #2b2100; }
.lkl { font-size: .72rem; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: #333; white-space: nowrap; }
.lkk { font: 600 .7rem ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: rgb(0 0 0 / .5); white-space: nowrap; }
.lev { flex: none; min-width: 2rem; height: 1.6rem; padding: 0 .35rem; border: 1px solid rgb(0 0 0 / .22); border-radius: 6px; background: transparent; color: #333;
  font: 700 .72rem ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; cursor: pointer; }
.lev:hover { background: rgb(0 0 0 / .06); }
.lev[aria-pressed="true"] { background: #333; border-color: #333; color: #f5f5f5; }
.lbody { padding: .5rem .5rem .45rem; }
.ltab { border-collapse: collapse; width: 100%; }
.ltab td { padding: 0; }
.ltab .cell { border: 1px solid rgb(0 0 0 / .16); background: #fff; }
.ltab input { display: block; width: 100%; min-width: 6.5rem; box-sizing: border-box; margin: 0; padding: .3rem .4rem; border: 0; background: transparent;
  font: 400 .78rem var(--font); color: #1f1f1f; user-select: text; -webkit-user-select: text; }
.ltab .hdr .cell { background: #fafafa; }
.ltab .hdr input { font-weight: 700; }
.ltab input::placeholder { color: rgb(0 0 0 / .35); font-weight: 400; }
.ltab input:focus { outline: 2px solid var(--focus); outline-offset: -2px; }
.ltab .addc { width: 1.4rem; vertical-align: middle; }
.ltab .addr td { padding-top: 2px; }
.addb { display: grid; place-items: center; width: 100%; min-height: 1.4rem; height: 100%; padding: 0; border: 1px dashed rgb(0 0 0 / .35); border-radius: 4px;
  background: transparent; color: #333; font: 600 .9rem/1 var(--font); cursor: pointer; opacity: .4; }
.ltab .addc .addb { margin-left: 2px; min-height: 100%; }
.addb:hover, .addb:focus-visible { opacity: 1; background: #fff; }
.ljson { margin: 0; padding: .4rem .5rem; max-width: none; border: 1px solid rgb(0 0 0 / .16); border-radius: 4px; background: #fff;
  font: 400 .72rem/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: #1f1f1f; white-space: pre-wrap; overflow-wrap: anywhere;
  user-select: text; -webkit-user-select: text; cursor: text; }
.node.lnode.sel { border-color: var(--focus); box-shadow: 0 0 0 2px var(--focus), 0 8px 20px rgb(0 0 0 / .12); }
.node.lnode.dim { opacity: .35; }
.node.lnode.sel, .node.lnode:focus-within { z-index: 3; }   /* the card being worked on sits above its neighbours */
.edge.unrev { stroke-dasharray: 2 5; }
/* Drag-to-connect: the dragged cable, and the box it would land on. */
.edge.wire { stroke: var(--focus); stroke-dasharray: 6 5; }
.port.out { cursor: crosshair; }
.port.out:hover { border-color: var(--focus); background: var(--focus); }
.canvas.wiring, .canvas.wiring * { cursor: crosshair !important; }
.node.droptarget { border-color: var(--focus); box-shadow: 0 0 0 3px var(--focus); }
.node { position: absolute; background: var(--card); border: 1px solid var(--grid); border-radius: 12px; box-shadow: 0 1px 2px rgb(0 0 0 / .08), 0 6px 18px rgb(0 0 0 / .07); cursor: default; }
.node.sel { border-color: var(--focus); box-shadow: 0 0 0 2px var(--focus), 0 6px 18px rgb(0 0 0 / .1); }
/* Every box is the same card. Only its icon light differs, like a traffic light:
   green with a white icon for a box with figures, yellow with a dark icon for a missing box (dark keeps the icon legible on yellow). */
.node:not(.unk) .ni { background: #178a46; border-color: #178a46; color: #fff; }
.node.unk .ni { background: #f5c400; border-color: #f5c400; color: #2b2100; }
/* A box with no links yet gets a red light with a white icon. The script sets .unlinked from the links, so the light
   turns green by itself once a link to or from the box is added. */
.node.unlinked .ni { background: #b8322c; border-color: #b8322c; color: #fff; }
.gapf { display: grid; gap: .15rem; }
.gapl { font-size: .68rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-3); }
.gapf p { font-size: .8rem; color: var(--ink-2); }
.edge.gap { stroke: var(--gap); } .arrow.gap { fill: var(--gap); }
.edge.gap.on { stroke: var(--focus); } .arrow.gap.on { fill: var(--focus); }
.hatchln { stroke: var(--cgap); stroke-opacity: .28; stroke-width: 1; }
.gapchip { stroke: var(--cgap); stroke-opacity: .8; stroke-width: 1; stroke-dasharray: 3 2; }
.gaptx { fill: var(--cgap); font: italic 500 calc(11px * var(--k, 1)) var(--font); paint-order: stroke; stroke: var(--cbg); stroke-width: calc(3px * var(--k, 1)); stroke-linejoin: round; }
/* The title node is plain text on the canvas, like a Figma text layer. */
.node.tnode { background: transparent; border-color: transparent; box-shadow: none; }
.node.tnode.sel { border-color: var(--focus); box-shadow: 0 0 0 2px var(--focus); }
.tnode .nh { padding: .35rem .6rem; }
.ttl { margin: 0; font-size: 2rem; font-weight: 800; letter-spacing: -.02em; line-height: 1.15; }
.nh { display: flex; align-items: center; gap: .6rem; padding: .7rem .8rem; cursor: grab; touch-action: none; }
.nh:active { cursor: grabbing; }
.ni { flex: none; min-width: 2rem; height: 2rem; padding: 0 .55rem; font: 700 .72rem/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; display: grid; place-items: center; border-radius: 999px; border: 1px solid var(--grid); background: var(--bg); color: var(--ink-2); }
.ni svg, .si svg { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; display: block; }
.ni svg { width: 1.15rem; height: 1.15rem; }
.si svg { width: .95rem; height: .95rem; }
.ntl { font-size: .95rem; font-weight: 700; line-height: 1.2; }
.nk, .stag { font-size: .68rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-3); }
.stag { display: flex; align-items: center; gap: .4rem; }
.nb { display: grid; gap: .6rem; padding: 0 .8rem .8rem; }
.big { font-size: 1.5rem; font-weight: 800; letter-spacing: -.01em; line-height: 1.1; font-variant-numeric: tabular-nums; }
.sm { font-size: .78rem; color: var(--ink-3); }
.fact { display: grid; gap: .1rem; }
.fv { font-size: 1.15rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.fl { font-size: .78rem; color: var(--ink-2); }
.nfoot { display: flex; align-items: center; justify-content: space-between; gap: .5rem; border-top: 1px solid var(--grid); padding-top: .4rem; }
.nsrc { font-size: .72rem; color: var(--ink-3); }
/* The settings gear, bottom right of every box. */
.cfg { flex: none; width: 1.6rem; height: 1.6rem; margin: -.15rem -.35rem -.3rem 0; display: grid; place-items: center; padding: 0; border: 0; border-radius: 6px; background: transparent; color: var(--ink-3); cursor: default; }
.cfg:hover { background: var(--bg); color: var(--ink-2); }
.cfg svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; display: block; }
.ln.cape { stroke: #4cc3c9; } .dot.cape { fill: #4cc3c9; }
.ln.buff { stroke: #e0a24a; } .dot.buff { fill: #e0a24a; }
/* The dots on a box's sides stay hidden until the pointer comes near the box (.near), and on the box a cable is being
   dragged from (.wsrc). Hidden dots still catch the pointer. On touch screens, a selected box shows them. */
.port { position: absolute; width: 12px; height: 12px; border-radius: 50%; background: var(--card); border: 2px solid var(--ink-3); opacity: 0; transition: opacity .12s; }
.port.in { left: -7px; } .port.out { right: -7px; }
.node.near .port, .node.wsrc .port, .node.droptarget .port { opacity: 1; }
@media (hover: none) { .node.sel .port { opacity: 1; } }

/* Computation: a fixed square with square-ish corners and a heavier edge, so it reads as a different kind of card. */
.node.cnode { width: 240px; height: 240px; display: flex; flex-direction: column; border: 1.5px solid var(--ink-3); border-radius: 4px; }
.node.cnode.sel { border-color: var(--focus); }
.cnode .nh { flex: none; padding: .55rem .7rem; border-bottom: 1px solid var(--grid); }
.ctt { min-width: 0; }
.cnode .nk { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.node.cnode.st-empty .ni, .node.cnode.st-error .ni { background: #b8322c; border-color: #b8322c; color: #fff; }
.node.cnode.st-wait .ni { background: #f5c400; border-color: #f5c400; color: #2b2100; }
.node.cnode.st-ok .ni { background: #178a46; border-color: #178a46; color: #fff; }
.cface { flex: 1; min-height: 0; display: flex; flex-direction: column; justify-content: center; gap: .35rem; padding: .4rem .75rem; overflow: hidden; }
.cval { font-size: 1.85rem; font-weight: 800; letter-spacing: -.02em; line-height: 1.05; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.st-empty .cval, .st-wait .cval { color: var(--ink-3); font-size: 1.3rem; }
.st-error .cval { color: #b8322c; font-size: 1.3rem; }
.cdet { font-size: .74rem; line-height: 1.4; color: var(--ink-2); display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
.cin { flex: none; padding: 0 .75rem .4rem; font-size: .68rem; color: var(--ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cpick { flex: none; margin: 0 .6rem .6rem; display: flex; align-items: center; justify-content: space-between; gap: .5rem; padding: .45rem .6rem;
  border: 1px solid var(--axis); border-radius: 6px; background: var(--bg); color: var(--ink); font: 700 .74rem ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  letter-spacing: .04em; cursor: pointer; }
.cpick:hover { border-color: var(--ink-3); }
.cgo { font-size: 1rem; line-height: 1; color: var(--ink-3); }
.cvhelp .q.sq { border-radius: 3px; font-style: italic; }

/* The algorithm menu: full screen, solid white in both themes, so it sets its own light tokens. */
.algdlg { --ink: #0d1b2a; --ink-2: #44556a; --ink-3: #5d6d80; --grid: #e1e7ee; --axis: #c3ccd7; --card: #fff; --bg: #f6f8fb; --focus: #1c5cab; color-scheme: light;
  position: fixed; inset: 0; width: 100%; height: 100%; max-width: none; max-height: none; margin: 0; padding: 0; border: 0; background: #fff; color: var(--ink);
  overflow: hidden; }
.algdlg[open] { display: flex; flex-direction: column; }
.algdlg::backdrop { background: #fff; }
:root:has(.algdlg[open]) { overflow: hidden; }
.algtop { flex: none; display: flex; align-items: center; gap: 1rem; padding: .6rem var(--gutter); padding-top: calc(.6rem + env(safe-area-inset-top, 0px)); border-bottom: 1px solid var(--grid); }
.algtop h2 { font-size: 1rem; font-weight: 800; letter-spacing: -.01em; }
.algnode { margin-left: auto; font: 600 .75rem ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: var(--ink-3); }
.algmain { flex: 1; min-height: 0; display: grid; grid-template-columns: minmax(15rem, 20rem) 1fr; }
.alglist { overflow-y: auto; border-right: 1px solid var(--grid); padding: .75rem; display: grid; align-content: start; gap: .25rem; }
.algitem { display: grid; gap: .15rem; text-align: left; font: inherit; padding: .6rem .75rem; border: 1px solid transparent; border-radius: 8px; background: transparent; color: var(--ink); cursor: pointer; }
.algitem:hover { background: var(--bg); }
.algitem[aria-pressed="true"] { background: var(--bg); border-color: var(--ink); }
.algname { font: 700 .8rem ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing: .04em; }
.algdesc { font-size: .78rem; line-height: 1.4; color: var(--ink-2); }
.algdetail { overflow-y: auto; padding: 1.5rem var(--gutter) calc(3rem + env(safe-area-inset-bottom, 0px)); display: grid; align-content: start; gap: 1.5rem; max-width: 60rem; }
.algh { font-size: 1.6rem; font-weight: 800; letter-spacing: -.02em; line-height: 1.1; }
.algdetail > .algp { margin-top: -1rem; }
.algp { font-size: .92rem; color: var(--ink-2); }
.algsec { display: grid; gap: .6rem; }
.algsec h3 { font-size: .68rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-3); }
.algmeta { font-size: .8rem; color: var(--ink-3); }
.algtw { overflow-x: auto; border: 1px solid var(--grid); border-radius: 8px; }
.algtab { border-collapse: collapse; width: 100%; font-size: .78rem; }
.algtab th { text-align: left; font: 700 .7rem ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: var(--ink-2); padding: .4rem .6rem; background: var(--bg); border-bottom: 1px solid var(--grid); white-space: nowrap; }
.algtab td { padding: .35rem .6rem; border-bottom: 1px solid var(--grid); white-space: nowrap; }
.algtab tr:last-child td { border-bottom: 0; }
.algtab .num { text-align: right; font-variant-numeric: tabular-nums; }
.algtab .rn { color: var(--ink-3); font-variant-numeric: tabular-nums; }
.algtab .gap { text-align: center; color: var(--ink-3); font-style: italic; }
.algform { display: flex; flex-wrap: wrap; gap: .9rem 1.25rem; }
.algf { display: grid; gap: .3rem; min-width: 10rem; }
.algf.wide { flex: 1 1 100%; }
.algfl { font-size: .72rem; font-weight: 700; color: var(--ink-2); }
.algf.wide .algfl { font: 600 .75rem ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.algin { font: inherit; font-size: .88rem; padding: .4rem .55rem; border: 1px solid var(--axis); border-radius: 6px; background: #fff; color: var(--ink); max-width: 22rem; }
.algcode { font: 400 .82rem/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; padding: .6rem .7rem; border: 1px solid var(--axis); border-radius: 6px; background: var(--bg); color: var(--ink); resize: vertical; width: 100%; }
.algin:focus, .algcode:focus { outline: 2px solid var(--focus); outline-offset: 1px; }
.algbig { font-size: 2.6rem; font-weight: 800; letter-spacing: -.02em; line-height: 1.05; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.algerr { padding: .7rem .9rem; border-radius: 8px; background: #fbeaea; color: #8c1f1a; font-size: .88rem; }
@media (max-width: 48rem) {
  .algmain { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
  .alglist { border-right: 0; border-bottom: 1px solid var(--grid); grid-auto-flow: column; grid-auto-columns: max-content; overflow-x: auto; }
  .algdesc { display: none; }
  .algdetail { padding-inline: 1rem; }
}

/* Controls panel, Canvas view: the controls that only work on the cards page step aside (they are inert there anyway),
   which makes room for zoom, SHAPES and the search box. */
:root[data-view="canvas"] .controls .cardsonly { display: none; }
.canvasonly[inert] { opacity: .4; }
.zoomset { display: inline-flex; align-items: center; gap: .05rem; padding: .1rem; border: 1px solid var(--axis); border-radius: 999px; }
.zoomset .zb { min-width: 1.75rem; height: 1.6rem; border-radius: 999px; font-size: .95rem; }
.zoomset .zb.fit { font-size: .75rem; font-weight: 700; padding: 0 .55rem; }
.zoomset .zv { min-width: 2.9rem; font-size: .78rem; }
.shapesbtn { font-size: .72rem; letter-spacing: .06em; text-transform: uppercase; padding: .2rem .8rem .2rem .55rem; color: var(--ink); }
.shapesbtn svg { width: 1rem; height: 1rem; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linejoin: round; }
.shapesbtn[aria-expanded="true"] { color: var(--bg); background: var(--ink); border-color: var(--ink); }
.shapesmenu { position: absolute; z-index: 2; width: 19rem; padding: .35rem; display: grid; gap: .15rem; background: var(--card); border: 1px solid var(--grid); border-radius: 12px;
  box-shadow: 0 2px 6px rgb(0 0 0 / .08), 0 14px 36px rgb(0 0 0 / .16); cursor: default; }
.shape { display: flex; align-items: flex-start; gap: .7rem; padding: .55rem .6rem; border: 0; border-radius: 8px; background: transparent; color: var(--ink); font: inherit; text-align: left; cursor: pointer !important; }
.shape:hover, .shape:focus-visible { background: var(--bg); }
.shapeic { flex: none; width: 2.1rem; height: 2.1rem; display: grid; place-items: center; border: 1px solid var(--axis); border-radius: 8px; background: var(--card); }
.shapeic svg { width: 1.15rem; height: 1.15rem; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.shapetx { display: grid; gap: .1rem; }
.shapenm { font-size: .85rem; font-weight: 700; }
.shapeds { font-size: .76rem; line-height: 1.35; color: var(--ink-3); }
/* Search: the panel's right-hand column. */
.csearch { flex: none; width: 15rem; display: grid; align-content: center; gap: .3rem; padding: .6rem 1rem; border-left: 1px solid var(--grid); }
.sbox { display: flex; align-items: center; gap: .45rem; padding: 0 .7rem; height: 2.1rem; border: 1px solid var(--axis); border-radius: 999px; background: var(--bg);
  text-transform: none !important; letter-spacing: 0 !important; cursor: text !important; }
.sbox:focus-within { border-color: var(--focus); box-shadow: 0 0 0 1px var(--focus); }
.sbox svg { flex: none; width: .95rem; height: .95rem; fill: none; stroke: var(--ink-3); stroke-width: 2; stroke-linecap: round; }
.sbox input { flex: 1; min-width: 0; border: 0; outline: 0; background: transparent; color: var(--ink); font: 500 .85rem var(--font); cursor: text !important; }
.sbox input::placeholder { color: var(--ink-3); }
.controls .scount { min-height: 1em; padding-left: .8rem; font-size: .72rem; font-weight: 600; color: var(--ink-3); cursor: default; }
@media (max-width: 48rem) { .csearch { width: 11rem; padding: .5rem .75rem; } }
/* Search results on the canvas: matches get a ring, everything else fades. */
.node.hit { outline: 2px dashed var(--focus); outline-offset: 5px; }
.node.miss { opacity: .22; }
/* Text cards made from SHAPES are smaller than the page title. A title being renamed takes text selection. */
.tnode.tsmall .ttl { font-size: 1.15rem; font-weight: 700; letter-spacing: -.01em; }
.node [contenteditable="true"] { user-select: text; -webkit-user-select: text; cursor: text; outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 3px; }

/* Connection cards: the list of sources the source card offers, and a Pipe switch for each. */
.mtab th { text-align: left; padding: 0 .35rem .3rem; font-size: .62rem; font-weight: 800; letter-spacing: .07em; text-transform: uppercase; color: rgb(0 0 0 / .5); }
.mtab th.mpipe { text-align: center; }
.mtab td { padding: .35rem; border-top: 1px solid rgb(0 0 0 / .1); vertical-align: middle; }
.msrc { display: grid; grid-template-columns: auto 1fr; align-items: baseline; column-gap: .45rem; row-gap: .05rem; min-width: 12.5rem; max-width: 17rem; }
.mtag { grid-row: span 2; align-self: center; font: 700 .6rem ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; text-transform: uppercase; letter-spacing: .04em;
  padding: .1rem .3rem; border-radius: 4px; border: 1px solid rgb(0 0 0 / .2); color: #333; background: #fff; }
.mtag.output { background: #1f1f1f; border-color: #1f1f1f; color: #f5f5f5; }
.mtag.raw { border-style: dashed; }
.mname { font-size: .76rem; font-weight: 600; line-height: 1.3; color: #1f1f1f; }
.mmeta { font-size: .68rem; color: rgb(0 0 0 / .5); }
.mtab tr.off .mname, .mtab tr.off .mmeta, .mtab tr.off .mtag { opacity: .45; }
.mnone { font-size: .75rem; font-style: italic; color: rgb(0 0 0 / .5); }
td.mpipe { width: 3rem; text-align: center; }
.pipe { position: relative; width: 2.1rem; height: 1.2rem; padding: 0; border: 0; border-radius: 999px; background: #c8c8c8; cursor: pointer; vertical-align: middle; transition: background .15s; }
.pipe::after { content: ""; position: absolute; top: .15rem; left: .15rem; width: .9rem; height: .9rem; border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgb(0 0 0 / .3); transition: transform .15s; }
.pipe[aria-checked="true"] { background: #178a46; }
.pipe[aria-checked="true"]::after { transform: translateX(.9rem); }
.pipe:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* JSON tab: VS Code Dark+ — dark in both themes, with a dark scrollbar, line numbers and colour per token. */
.sbody.json { display: flex; flex-direction: column; overflow: hidden; background: #1e1e1e; }
.jbar { flex: none; display: flex; align-items: center; gap: .25rem; padding: .4rem .5rem; background: #252526; border-bottom: 1px solid #3c3c3c; }
.jbtn { display: inline-flex; align-items: center; gap: .4rem; height: 1.85rem; padding: 0 .65rem; border: 1px solid transparent; border-radius: 5px; background: transparent;
  color: #cccccc; font: 500 .78rem var(--font); cursor: pointer; }
.jbtn:hover { background: #2a2d2e; border-color: #3c3c3c; }
.jbtn:focus-visible { outline: 1px solid #007fd4; outline-offset: 0; }
.jbtn svg { width: .95rem; height: .95rem; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.jbtn .ok { display: none; }
.jbtn.done { color: #89d185; } .jbtn.done .ico { display: none; } .jbtn.done .ok { display: block; stroke-width: 2; }
.jbtn.fail { color: #f48771; }
.jstat { margin-left: auto; padding-right: .3rem; font: 400 .72rem ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: #858585; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.jcode { flex: 1; min-height: 0; margin: 0; padding: .6rem 0 2rem; overflow: auto; counter-reset: jl; color: #d4d4d4; background: #1e1e1e;
  font: 400 12.5px/1.6 ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; tab-size: 2; white-space: pre;
  user-select: text; -webkit-user-select: text; scrollbar-color: #424242 #1e1e1e; scrollbar-width: thin; }
.jcode:focus-visible { outline: 1px solid #007fd4; outline-offset: -1px; }
.jcode::-webkit-scrollbar { width: 12px; height: 12px; }
.jcode::-webkit-scrollbar-track, .jcode::-webkit-scrollbar-corner { background: #1e1e1e; }
.jcode::-webkit-scrollbar-thumb { background: #424242; border: 3px solid #1e1e1e; border-radius: 6px; }
.jcode::-webkit-scrollbar-thumb:hover { background: #4f4f4f; }
.jl { display: block; padding-right: 1rem; }
.jl::before { counter-increment: jl; content: counter(jl); display: inline-block; width: 3.4em; margin-right: 1.2em; text-align: right; color: #6e7681; user-select: none; -webkit-user-select: none; }
.jl:hover { background: #2a2d2e; }
.jk { color: #9cdcfe; } .js { color: #ce9178; } .jn { color: #b5cea8; } .jw { color: #569cd6; } .jp { color: #d4d4d4; }
.jb0 { color: #ffd700; } .jb1 { color: #da70d6; } .jb2 { color: #179fff; }
.jcode ::selection { background: #264f78; }

/* Right column (Canvas view only): full height, resizable from its left edge, with a tab row on top. */
:root { --sidew: 450px; }
.side { display: none; }
:root[data-view="canvas"] .side { display: flex; flex-direction: column; position: fixed; z-index: 20; top: env(safe-area-inset-top, 0px); right: 0; bottom: 0;
  width: var(--sidew); background: var(--card); border-left: 1px solid var(--grid); }
.sresize { position: absolute; left: -5px; top: 0; bottom: 0; width: 10px; cursor: col-resize; touch-action: none; z-index: 1; }
.sresize::after { content: ""; position: absolute; left: 4px; top: 0; bottom: 0; width: 2px; background: var(--focus); opacity: 0; transition: opacity .12s; }
.sresize:hover::after, .sresize:focus-visible::after, .side.sizing .sresize::after { opacity: 1; }
.sresize:focus-visible { outline: none; }
.side.sizing, .side.sizing * { cursor: col-resize !important; user-select: none; }
.shead { flex: none; display: flex; align-items: flex-end; gap: .25rem; height: 2.9rem; padding: 0 .75rem; border-bottom: 1px solid var(--grid); }
.stab { font: inherit; font-size: .78rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-3); background: transparent;
  border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px; padding: .7rem .7rem .6rem; cursor: pointer; }
.stab:hover { color: var(--ink-2); }
.stab[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--ink); }
.sbody { flex: 1; min-height: 0; overflow: auto; }
.zb { font: inherit; font-size: 1rem; font-weight: 600; min-width: 2rem; height: 2rem; padding: 0 .4rem; border: 0; border-radius: 7px; background: transparent; color: var(--ink); cursor: pointer; }
.zb:hover { background: var(--bg); }
.zb.fit { font-size: .8rem; }
.zv { min-width: 3.1rem; text-align: center; font-size: .8rem; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--ink-2); }
/* Top right of the canvas: the navigation hint and the Help pill. The bottom is left to the zoom bar and the controls panel. */
.cvtop { position: absolute; top: .75rem; right: .75rem; z-index: 5; display: flex; align-items: center; gap: .9rem; pointer-events: none; }
.cvhint { font-size: .72rem; color: var(--ink-3); }
@media (max-width: 34rem) { .cvhint { display: none; } }

/* The title's width handles: a dot on each side, shown on hover or when the title is selected, kept the same size on screen at any zoom (--inv is 1 / zoom). */
.rh { position: absolute; top: 50%; width: calc(10px * var(--inv, 1)); height: calc(10px * var(--inv, 1)); margin-top: calc(-5px * var(--inv, 1));
  border-radius: 50%; background: #fff; border: calc(1.5px * var(--inv, 1)) solid var(--focus); cursor: ew-resize; touch-action: none; opacity: 0; }
.rh.l { left: calc(-5px * var(--inv, 1)); } .rh.r { right: calc(-5px * var(--inv, 1)); }
.rh::before { content: ""; position: absolute; inset: calc(-8px * var(--inv, 1)); }
.tnode:hover .rh, .tnode.sel .rh, .tnode.sizing .rh { opacity: 1; }
.tnode:hover { border-color: var(--axis); }
.tnode.sizing { border-color: var(--focus); }

/* Help: a pill on the canvas that opens a full-screen page explaining the canvas. */
.cvhelp { pointer-events: auto; background: var(--card); color: var(--ink); border-color: var(--axis); box-shadow: 0 2px 8px rgb(0 0 0 / .1); }
.cvhelp:hover { border-color: var(--ink-3); }
.cvhelp .q { width: 1.1rem; height: 1.1rem; display: grid; place-items: center; border-radius: 50%; border: 1.5px solid currentColor; font-size: .68rem; font-weight: 800; line-height: 1; }
.helpdlg { position: fixed; inset: 0; width: 100%; height: 100%; max-width: none; max-height: none; margin: 0; padding: 0; border: 0; background: var(--bg); color: var(--ink); overflow-y: auto; }
.helpdlg::backdrop { background: var(--bg); }
:root:has(.helpdlg[open]) { overflow: hidden; }
.helptop { position: sticky; top: 0; z-index: 1; background: var(--bg); border-bottom: 1px solid var(--grid); padding-top: env(safe-area-inset-top, 0px); }
.helptop .wrap { display: flex; align-items: center; padding-block: .6rem; }
.back { font-size: .85rem; color: var(--ink); }
.back svg { width: .95rem; height: .95rem; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.helpbody { max-width: 44rem; padding-block: clamp(1.5rem, 5vw, 3rem) 4rem; padding-bottom: calc(4rem + env(safe-area-inset-bottom, 0px)); }
.helpbody h2 { font-size: clamp(1.5rem, 4vw, 2rem); font-weight: 800; letter-spacing: -.02em; line-height: 1.15; margin-bottom: 1.5rem; }
.helpbody h3 { font-size: .72rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-3); margin: 2rem 0 .75rem; }
.legend { list-style: none; margin: 0; padding: 0; display: grid; gap: .9rem; }
.legend li { display: grid; grid-template-columns: 4.5rem 1fr; gap: 1rem; align-items: center; }
.legend p { font-size: .95rem; color: var(--ink-2); }
.legend strong { color: var(--ink); }
.lgs { display: block; width: 4.5rem; height: 1.6rem; overflow: visible; }
.lgs .edge { stroke-width: 2; }
.lglight { justify-self: center; width: 2rem; height: 2rem; border-radius: 50%; }
.lglight.go { background: #178a46; } .lglight.gap { background: #f5c400; } .lglight.stop { background: #b8322c; }
.howto { margin: 0; padding-left: 1.2rem; display: grid; gap: .5rem; font-size: .95rem; color: var(--ink-2); }
.helpbody kbd { font: 600 .8rem var(--font); padding: .05rem .4rem; border: 1px solid var(--axis); border-bottom-width: 2px; border-radius: 5px; background: var(--card); color: var(--ink); }
.src h3 { font-size: 1rem; font-weight: 800; margin: 1.75rem 0 .6rem; }
#links { min-width: 44rem; }

.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* Sources and calculations */
.src { padding-bottom: 4rem; }
.src h2 { font-size: 1.25rem; font-weight: 800; letter-spacing: -.01em; margin-bottom: .9rem; }
.src a { color: var(--focus); text-underline-offset: 2px; }
.srclist { margin: 0 0 1.25rem; padding-left: 1.4rem; display: grid; gap: .45rem; font-size: .85rem; color: var(--ink-2); }
.srclist li { padding-left: .25rem; }
.srclist, .foot { overflow-wrap: anywhere; }
.tablewrap { overflow-x: auto; background: var(--card); border: 1px solid var(--grid); border-radius: 12px; }
.ctable { border-collapse: collapse; width: 100%; min-width: 66rem; font-size: .8rem; line-height: 1.45; }
.ctable th { text-align: left; font-size: .68rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-3); padding: .7rem .85rem; border-bottom: 1px solid var(--axis); white-space: nowrap; }
.ctable td { padding: .65rem .85rem; vertical-align: top; border-bottom: 1px solid var(--grid); }
.ctable tr:last-child td { border-bottom: 0; }
.ctable tr.grp td { border-top: 1px solid var(--axis); }
.ctable tbody tr:first-child td { border-top: 0; }
.ctable .num { font-variant-numeric: tabular-nums; }
.ctable .out { font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ctable .sub { display: block; font-weight: 400; color: var(--ink-3); font-size: .74rem; }
.ctable .na { color: var(--gap); }
.foot { margin: 1rem 0 0; padding-left: 1.2rem; display: grid; gap: .35rem; font-size: .78rem; color: var(--ink-3); }
