/* --- Virginia blue, with the cardinal for an accent -------------------------

   The masthead is the Commonwealth's deep blue and keeps that colour in both
   themes: it is the one fixed surface on the page, and a header that inverted
   along with everything else would stop reading as a masthead. Below it the
   neutrals are tinted towards the same blue, so the accent used on links and
   selection reads as emphasis rather than as more chrome.

   Cardinal red is the mascot's colour and is spent in exactly one place, the
   search button. It is deliberately NOT a status colour -- --fail is a duller,
   browner red so that "this check failed" can never be mistaken for the brand,
   and nothing on a chart is ever drawn in it. */

/* --- which theme is on -----------------------------------------------------

   `data-theme` on <html>, set by app.js, and it is the ONLY thing that
   selects dark below -- there is no `@media (prefers-color-scheme: dark)` in
   this file any more. There were nine of them, and once a reader could choose
   a theme they would each have needed a second copy under the attribute: 25
   rules become 50, two lists of colours that have to agree, and the first
   time they disagree the page is half dark. So the media query is asked ONCE,
   in JavaScript, and its answer is written onto the root as an attribute.

   The cost is that this stylesheet alone renders light, and the reader's
   system preference reaches it only after a script has run. That is paid for
   in the <head> of index.html rather than here: three lines that set the
   attribute before the first paint, so nothing flashes. app.js keeps
   following the system setting live while the choice is "auto".

   `color-scheme` follows the same attribute rather than staying `light dark`,
   because the platform draws things this stylesheet cannot reach -- scrollbars
   and the <select> popup among them -- and it must be told which way to draw
   them when the reader has overruled the system. */
:root {
  color-scheme: light;

  /* Masthead. The BAR follows the theme -- see the dark block below, which
     darkens --navy and --navy-2 -- but the text and the accent on it do not:
     --on-navy and --cardinal are the same in both, because the cardinal is
     only ever painted on the bar and lightening it for dark mode dropped
     white-on-red to 3.6:1. (This comment used to say the whole masthead was
     theme-fixed, which the dark block has never agreed with.) */
  --navy: #0f2c52;
  --navy-2: #16416f;
  --on-navy: #ffffff;
  --cardinal: #c8102e;
  /* The mascot's colour, whichever mascot is flying. `--cardinal` stays as
     the name of the RED specifically -- it is referred to by name in two
     comments and is what the swatch means. Virginia gets the cardinal's red;
     Florida the gator's green. */
  --mascot-accent: var(--cardinal);
  /* What the search button spends, split from --mascot-accent so the bar and
     the button can be different colours. They had to be: once Florida painted
     the whole BAR its green, a green button on it was a button you could not
     see. Virginia is unchanged by the split -- its bar is navy and its button
     is still the cardinal, which is what --mascot-accent already resolved to.
     Every value here clears 4.5:1 against white; the two states' buttons are
     deliberately close, 5.88 and 5.93. */
  --masthead-button: var(--mascot-accent);

  --bg: #f1f5fa;
  --panel: #ffffff;
  --ink: #10202f;
  --muted: #58697c;
  --line: #dae2ed;
  --accent: #17518c;
  --accent-soft: #e6eff9;
  --money: #0f6b4b;
  --value: #855012;
  --warn: #8a6100;
  --warn-soft: #fdf5e2;
  --fail: #a12630;
  --radius: 8px;
  --shadow: 0 1px 2px rgba(16, 32, 47, 0.05);
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --navy: #0a1a2c;
  --navy-2: #102a46;
  --bg: #0b1622;
  --panel: #12202f;
  --ink: #e1e9f3;
  --muted: #8ca1b8;
  --line: #223549;
  --accent: #74aee4;
  --accent-soft: #16293d;
  --money: #56c79a;
  --value: #d8a56a;
  --warn: #d9ab4a;
  --warn-soft: #2c2617;
  --fail: #ef7b7b;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.32);
}

/* The selected state's overrides, and they go HERE rather than inside the
   :root list above -- a state block opened mid-list once already, and it
   swallowed every token below it into itself, so Virginia lost --bg, --ink,
   --accent and the rest and the page rendered unstyled. Own block, after both
   themes, and it only ever sets tokens it names.

   Florida paints the whole bar, not just one control. The green is the
   gator's, darker at the foot than the head so the gradient runs the same
   direction Virginia's navy does. The button then has to leave the green --
   a green button on a green bar is invisible -- so it takes Florida's orange,
   which is the citrus and is dark enough to hold white text at 5.93:1. */
:root[data-state="fl"] {
  --mascot-accent: #1d6b32;
  --navy: #12492a;
  --navy-2: #1d6b32;
  --masthead-button: #a34a06;
}

/* The federal level. Same shape as Florida's block above and the same reason
   for the split between bar and button: the flag's navy on the bar, the
   flag's red on the button, because a navy button on a navy bar is invisible.

   Both numbers are computed rather than picked: #3c3b6e carries white at
   10.3:1 and #b22234 at 6.6:1, against the 4.5:1 floor. The button is the
   highest-contrast of the three -- Virginia's is 5.88 and Florida's 5.93 --
   which is the flag's red being darker than either, not a different rule.

   --mascot-accent is the canton blue and not the stripe red, which is the one
   place this level differs in spirit from the other two: the flag has no
   single colour, so the accent takes the one that is a FIELD in it rather
   than the one that is a stripe. */
:root[data-state="us"] {
  --mascot-accent: #3c3b6e;
  --navy: #2b2a55;
  --navy-2: #3c3b6e;
  --masthead-button: #b22234;
}

/* Dark mode darkens Florida's bar the way it darkens Virginia's, and for the
   same reason: a masthead that stays bright is the one panel on a dark page
   still glowing. Same ratio of change as --navy takes above. The button is NOT
   darkened here, exactly as the cardinal is not -- it sits on the bar in both
   themes and dropping its luminance drops its contrast with the white on it. */
:root[data-theme="dark"][data-state="fl"] {
  --navy: #0a2b19;
  --navy-2: #0f3d24;
}
/* Same treatment, same ratio, same exception: the button is not darkened. */
:root[data-theme="dark"][data-state="us"] {
  --navy: #1a1936;
  --navy-2: #262550;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 14px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

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

/* --- header ----------------------------------------------------------------

   Two bands. The masthead carries identity -- mascot, name, search -- and the
   nav below it carries the seventeen sections, which were previously strung
   into the same row as everything else and wrapped into an unreadable block.
   Splitting them lets the sections be grouped and lets the current one be
   marked, which is what the old header could not do at all. */

header {
  /* Above the trail rail (z 20): the header is a stacking context, so its
     dropdown menus can never paint over anything the header itself sits
     under -- a menu's own z-index is trapped inside. The Browse menu opened
     BEHIND the docked trail until the whole header outranked it. */
  position: sticky; top: 0; z-index: 30;
  border-bottom: 1px solid var(--line);
}

.topbar {
  background: linear-gradient(180deg, var(--navy-2), var(--navy));
  padding: 9px 20px;
  display: flex; gap: 18px; align-items: center; flex-wrap: wrap;
}

.brand { display: flex; align-items: center; gap: 10px; min-width: 0; }
.brand:hover { text-decoration: none; }
/* 32px, the sprite's own size. Scaled to anything that is not a whole
   multiple, its one-unit rects land on fractional pixels and crispEdges
   resolves them to uneven widths -- the bird gets a lopsided beak. So this
   tracks the artwork: a 32x32 sprite is crisp at 32 and 64, and blurry at
   the 24 the flat sprite used. */
.mascot { width: 32px; height: 32px; flex: none; display: block; }

/* Pixel flags in tables. `image-rendering: pixelated` is the whole point: the
   browser's default smoothing turns a 32px pixel-art flag scaled by any amount
   into a blur, and these are drawn pixel by pixel precisely so they do not
   blur. `vertical-align` rather than a flex row on the cell, because a table
   cell that becomes a flex container stops aligning with the ones beside it. */
.flag { image-rendering: pixelated; vertical-align: middle; flex: none; }
.flag-32 { width: 24px; height: 24px; }
.flag-64 { width: 48px; height: 48px; }
.with-flag { display: inline-flex; align-items: center; gap: .4em; }
/* Two icons then the name. The creature is slightly smaller than the
   flag: a flag is a rectangle that fills its box and an animal is a
   silhouette with air around it, so matching their box sizes makes the
   animal look bigger than the flag beside it. */
.with-flag .flag + .flag { width: 22px; height: 22px; }
.brand-name {
  font-size: 15.5px; font-weight: 650; letter-spacing: -0.012em;
  color: var(--on-navy); white-space: nowrap;
}

.topbar form {
  flex: 1 1 300px; display: flex; gap: 8px;
  min-width: 200px; max-width: 640px; margin-left: auto;
}
.topbar input[type="search"] {
  border-radius: 999px;
  border-color: rgba(255, 255, 255, 0.22);
  background: rgba(255, 255, 255, 0.10);
  color: var(--on-navy);
}
.topbar input[type="search"]::placeholder { color: rgba(255, 255, 255, 0.55); }
.topbar input[type="search"]:focus {
  outline: none;
  background: rgba(255, 255, 255, 0.17);
  border-color: rgba(255, 255, 255, 0.5);
}
/* The one control on the bar painted in its own colour rather than the bar's.
   `--masthead-button`, not `--mascot-accent`: in Virginia they are the same
   red, but Florida spends the mascot's green on the whole bar and the button
   has to be something else to still be a button. */
.topbar button {
  border-radius: 999px; padding: 7px 17px; font-weight: 600;
  background: var(--masthead-button); border-color: transparent;
}

/* The state switcher. A <select> rather than pills: there are two states now
   and the list only grows, and a control that changes shape at the fourth
   entry is the nav's mistake repeated in the masthead. */
.statepick {
  appearance: none; cursor: pointer;
  border-radius: 999px; padding: 5px 28px 5px 12px;
  font-size: 13px; font-weight: 600;
  color: var(--on-navy);
  background: rgba(255, 255, 255, 0.10) no-repeat right 10px center;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0h10L5 6z' fill='%23ffffff'/%3E%3C/svg%3E");
  border: 1px solid rgba(255, 255, 255, 0.22);
}
.statepick:focus-visible { outline: 2px solid var(--on-navy); outline-offset: 1px; }
/* The open popup is drawn by the platform, not by this stylesheet, and on
   Linux Chromium it took the CLOSED control's white text with it and painted
   it on a white menu -- invisible until hovered. So the options set both of
   their own colours in absolute terms rather than inheriting either: the
   popup is not the masthead and cannot use the masthead's palette. */
.statepick option {
  color: #10202f;
  background-color: #ffffff;
}
:root[data-theme="dark"] .statepick option { color: #e1e9f3; background-color: #12202f; }

/* The theme switch, beside the jurisdiction switch and deliberately its
   sibling: both say what the reader is looking at rather than what they are
   looking for, and a lone icon among the search controls read as a third
   thing to click on the way to searching.

   THREE positions, not two, and the name is spelled out beside the icon
   because that is the only way the third is visible. "Auto" is the default
   and means the system's own setting, still followed live; a two-way toggle
   would have had a first click that silently threw that away with no way
   back, which is a preference destroyed rather than set.

   It is a `.topbar .themetoggle` rather than a `.themetoggle` because
   `.topbar button` above paints every button on this bar in the search
   button's colour, and this one is not that button. */
.topbar .themetoggle {
  display: inline-flex; align-items: center; gap: 6px;
  border-radius: 999px; padding: 5px 12px 5px 10px;
  font-size: 13px; font-weight: 600;
  color: var(--on-navy);
  background: rgba(255, 255, 255, 0.10);
  border: 1px solid rgba(255, 255, 255, 0.22);
  cursor: pointer;
}
.topbar .themetoggle:hover {
  filter: none;                       /* the bar's buttons brighten; this tints */
  background: rgba(255, 255, 255, 0.17);
  border-color: rgba(255, 255, 255, 0.4);
}
.topbar .themetoggle:focus-visible { outline: 2px solid var(--on-navy); outline-offset: 1px; }
/* Drawn in the current colour so the icon and the word cannot drift apart,
   and at the type's own size rather than a fixed pixel count. */
.themetoggle svg { width: 15px; height: 15px; display: block; flex: none; }
.themetoggle .theme-name { line-height: 1; }

/* Under 560px the masthead is already stacking the search box onto its own
   row; the word goes first so the two switches stay side by side rather than
   pushing search down again. The icon still says which mode is on, and the
   button keeps its accessible name from aria-label. */
@media (max-width: 560px) {
  .themetoggle .theme-name { display: none; }
  .topbar .themetoggle { padding: 5px 8px; }
}

/* Five items now, three of them dropdowns -- the shape the eighteen-pill
   version of this comment predicted it would need at twenty. Five afford
   readable type where eighteen bought room out of the font, and the row
   cannot wrap on anything wider than a watch. */
header nav {
  display: flex; gap: 2px; align-items: center;
  background: var(--panel); padding: 6px 12px;
  /* BELT AND BRACES FOR THE BREAKPOINT BELOW, which is a measured number and
     measured numbers go stale: the collapse fires under 850px because eleven
     items need 838, and the twelfth item nobody has added yet moves that.
     Wrapping is what gives, so an outgrown nav takes a second row instead of
     pushing the document sideways -- which is what it did, by 448px at 390
     and by 77 at 761.

     IT WAS `min-width: 0; overflow-x: auto` FOR ABOUT AN HOUR AND THAT BROKE
     THE DROPDOWNS. Setting overflow on one axis computes the other from
     `visible` to `auto`, so the nav became a clipping container and every
     .nav-menu -- absolutely positioned at top:100%, 340px tall -- was cut off
     338px short at the nav's own bottom edge. A scroll container cannot hold
     a menu that is meant to escape it, and no amount of z-index fixes that;
     only not clipping does.

     Degrading, not correct: if this ever wraps on a desktop the breakpoint
     wants re-measuring. */
  flex-wrap: wrap;
}
header nav a, details.nav-group > summary {
  padding: 5px 11px; border-radius: 999px;
  color: var(--muted); font-size: 12.5px; font-weight: 550;
  white-space: nowrap; cursor: pointer;
}
header nav a:hover, details.nav-group > summary:hover {
  background: var(--accent-soft); color: var(--accent); text-decoration: none;
}
header nav a[aria-current="page"] {
  background: var(--accent); color: var(--panel);
}
/* A group lights when the page you are on lives inside it -- the marking the
   flat row could only give its top-level links. */
details.nav-group { position: relative; }
details.nav-group.on > summary { background: var(--accent); color: var(--panel); }
details.nav-group > summary { list-style: none; }
details.nav-group > summary::-webkit-details-marker { display: none; }
details.nav-group > summary::after {
  content: ' ▾'; font-size: 9px; opacity: 0.6;
}
details.nav-group[open] > summary {
  background: var(--accent-soft); color: var(--accent);
}
.nav-menu {
  position: absolute; left: 0; top: calc(100% + 4px); z-index: 30;
  min-width: 170px; padding: 6px;
  display: flex; flex-direction: column; gap: 1px;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 10px; box-shadow: 0 4px 18px rgba(16, 32, 47, 0.16);
}
.nav-menu a { border-radius: 6px; }
/* A menu with SHELVES. Virginia's Federal group holds the same 29 items the
   United States level spreads over four dropdowns, and 29 rows in one column
   is 750px -- taller than a laptop viewport, with nowhere to scroll because
   the menu is absolutely positioned. Four columns, each headed, is the same
   shape at a third the height. The heading is a span and not a link: it is
   not a page, and markNav only looks at anchors. */
.nav-menu.nav-cols { flex-direction: row; gap: 10px; }
.nav-col { display: flex; flex-direction: column; gap: 1px; }
.nav-col .nav-head {
  padding: 4px 11px 2px; font-size: 10.5px; font-weight: 650;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted);
  opacity: 0.75; white-space: nowrap;
}

/* The toggle exists only below the breakpoint; above it, the row is the nav. */
.nav-toggle { display: none; }

/* On a phone the pills DID NOT wrap at all -- the row is flex with
   white-space:nowrap, so eleven items ran 448px past a 390px viewport and
   scrolled the whole page sideways [1800]. This rule used to read "cheaper to
   let it scroll away and be scrolled back to", which made the header static
   and left the overflow: it addressed the 224px HEIGHT and not the width.
   Wrapping is the other thing that was rejected, and rightly -- four rows of
   pills is the 224px header again.
   So it collapses: one button, and the nav opens as a stacked list. */
@media (max-width: 760px) {
  header { position: static; }
}

/* 849 AND NOT 760, measured rather than chosen: the eleven-item row needs
   838px, so a 760 breakpoint left 761-849 showing a nav that did not fit and
   overflowed the page by up to 77px. Kept separate from the rule above
   because the two are different questions -- whether the header sticks is
   about its HEIGHT on a phone, and this is about the nav's WIDTH. */
@media (max-width: 849px) {
  .nav-toggle {
    display: block; margin: 6px 12px; padding: 7px 12px;
    background: var(--panel); color: var(--ink);
    border: 1px solid var(--line); border-radius: var(--radius);
    font-size: 13px; font-weight: 550;
  }
  header nav { display: none; }
  header nav[data-open] {
    display: flex; flex-direction: column; align-items: stretch; gap: 1px;
    padding-bottom: 10px;
  }
  /* A group's menu is absolutely positioned on desktop, which inside a
     stacked column would float over the items below it. Here it is part of
     the list, indented so the nesting is still legible. */
  header nav[data-open] .nav-menu {
    position: static; min-width: 0; box-shadow: none; border: none;
    background: transparent; padding: 2px 0 2px 14px;
  }
  header nav[data-open] details.nav-group { position: static; }
  /* Stacked, the shelves stack too: four columns in a phone-width list would
     be four unreadable slivers. */
  header nav[data-open] .nav-menu.nav-cols { flex-direction: column; gap: 4px; }
  header nav[data-open] a,
  header nav[data-open] details.nav-group > summary {
    border-radius: var(--radius); padding: 9px 12px; font-size: 14px;
  }
  /* Stacked, so it must not also scroll sideways. */
  header nav[data-open] { overflow-x: visible; }
}

input[type="search"] {
  flex: 1; padding: 7px 12px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--bg); color: var(--ink); font-size: 13.5px;
}
input[type="search"]:focus { outline: 2px solid var(--accent-soft); border-color: var(--accent); }

button {
  padding: 7px 14px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--accent); color: #fff; font-size: 13px; cursor: pointer;
}
button:hover { filter: brightness(1.1); }

/* --- layout ------------------------------------------------------------- */

main { max-width: 1180px; margin: 0 auto; padding: 22px 20px 80px; }

/* --- table controls, index pages and detail pages alike ------------------
 *
 * ONE set of rules for both. There were two, `.controls` for the index pages
 * and `.dt-controls` for the tables on detail pages, and they had drifted into
 * different sizes of the same thing: the filter box was 220-340px one side and
 * 200px the other, its padding came from the global input rule one side and a
 * 5px override the other, and labels and counts sat at 12/12.5px against
 * 11.5/11.5px. Nothing chose any of that; the second set was written later and
 * never reconciled, so the same control was a different size depending on
 * which page you were on.
 *
 * The detail-page sizing won. It is the smaller of the two and reads better
 * directly above a table, which is where most of them are.
 *
 * `.topbar` search is deliberately NOT folded in here. It is masthead identity
 * on a dark band, not a table control. */

.controls, .dt-controls {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  margin-bottom: 8px;
}
.controls input[type="search"], .dt-controls input[type="search"] {
  flex: 0 1 220px; padding: 5px 9px; font-size: 12.5px;
}
.controls select, .dt-controls select {
  padding: 5px 8px; font-size: 12.5px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--bg); color: var(--ink);
}
.controls label, .dt-controls label { color: var(--muted); font-size: 11.5px; }
.controls .count, .dt-controls .count {
  color: var(--muted); font-size: 11.5px; margin-left: auto;
}
.controls button:disabled { opacity: 0.4; cursor: default; filter: none; }

/* Index pages: one bar of controls, one status line. */
.index-bar { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; margin-bottom: 10px; }
.index-bar input[type="search"] {
  flex: 1 1 280px; min-width: 220px; padding: 7px 11px; font-size: 13.5px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--bg); color: var(--ink);
}
.index-bar input[type="search"] { max-width: 420px; }
.index-bar .facet { display: flex; flex-direction: column; gap: 2px; }
.index-bar .facet label { color: var(--muted); font-size: 11.5px; }
.index-bar .facet select {
  max-width: 220px; padding: 6px 8px; font-size: 12.5px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--bg); color: var(--ink);
}
.index-flags { margin-bottom: 10px; }
.index-status {
  display: flex; gap: 14px; align-items: center; flex-wrap: wrap;
  margin: 4px 0 8px; font-size: 11.5px; color: var(--muted);
}
.index-status .count { color: var(--ink); font-weight: 600; font-size: 12.5px; }
.index-status .hint { margin: 0 0 0 auto; }
.index-pager { margin-top: 12px; }
.index-pager .count { margin-left: 0; }
.index > .note { margin-top: 14px; }

/* Sortable column headers. The arrow marks the active column and direction. */
th.sortable { cursor: pointer; user-select: none; white-space: nowrap; }
th.sortable:hover { color: var(--ink); }
th.sortable::after { content: ''; opacity: 0.35; margin-left: 4px; }
th.sortable:hover::after { content: '↕'; }
th.sorted-asc::after  { content: '↑'; opacity: 1; color: var(--accent); }
th.sorted-desc::after { content: '↓'; opacity: 1; color: var(--accent); }

/* A bar drawn behind a number, so relative size reads without a chart. */
td.barcell { position: relative; }
td.barcell .fill {
  position: absolute; right: 0; top: 3px; bottom: 3px;
  background: var(--accent-soft); border-radius: 3px; z-index: 0;
}
td.barcell span.v { position: relative; z-index: 1; }

h1 { font-size: 23px; margin: 0 0 5px; letter-spacing: -0.022em; word-break: break-word; }
h2 { font-size: 15px; margin: 0 0 10px; letter-spacing: -0.01em; }
.subtitle { color: var(--muted); margin: 0 0 20px; font-size: 13px; max-width: 80ch; }

/* The route back from a leaf page to the entity that gathers it. Set apart
   from the section notes because it is navigation, not commentary. */
.callout {
  margin: 16px 0 0; padding: 10px 14px; font-size: 13px;
  border-left: 3px solid var(--accent); background: var(--panel);
  border-radius: 0 6px 6px 0; color: var(--muted);
}
.callout a { font-weight: 600; }

section {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 10px; padding: 16px 18px; margin-bottom: 16px;
  box-shadow: var(--shadow);
}
section > h2, .sec-head > h2 { color: var(--ink); }

/* --- foldable sections ----------------------------------------------------
   The heading is the control: the whole title toggles, not a separate icon a
   pointer has to hunt for. Closed, the card keeps its head and loses its
   body -- a shut drawer, not a removed one. */
.fold-btn {
  display: inline-flex; align-items: baseline; gap: 7px; cursor: pointer;
  background: none; border: 0; padding: 0; margin: 0;
  color: inherit; font: inherit; letter-spacing: inherit; text-align: left;
}
.fold-btn:hover { color: var(--accent); }
.fold-arrow {
  flex: none; font-size: 11px; color: var(--muted);
  transform: rotate(90deg); transition: transform 0.12s ease;
  transform-origin: 40% 50%;
}
section.fold.closed .fold-arrow { transform: rotate(0deg); }
section.fold.closed > .sec-body { display: none; }
section.fold.closed .sec-head > h2 { margin-bottom: 0; }

/* A visible note that ran long: two lines, then "more". Caveats never clamp
   -- a warning behind a click is a warning nobody reads. */
p.note.clamp {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden;
}
p.note.clamp.open { -webkit-line-clamp: unset; }
button.note-more {
  background: none; border: 0; padding: 0; margin: -8px 0 12px;
  color: var(--accent); font-size: 11.5px; cursor: pointer;
}
button.note-more:hover { text-decoration: underline; filter: none; }
section > .note { color: var(--muted); font-size: 12.5px; margin: -4px 0 12px; max-width: 84ch; }

/* --- the section note, folded behind the cardinal ------------------------
 *
 * The bird sits in the section's top-right corner and the note opens beneath
 * the heading, at the left margin where a note has always started.
 *
 * That corner is why this is three rules rather than the flex row and the
 * subgrid block it replaced. A <summary> is the button and the note is the rest
 * of its <details>, so the panel cannot live anywhere but inside the
 * disclosure -- which fought every attempt to have the button beside the
 * heading AND the paragraph at the left margin. Taking the summary out of flow
 * ends the argument: the details is a plain block under the h2, so the note
 * flows exactly where it used to, and the button is positioned against the
 * head's own box.
 *
 * The PANEL is never positioned, only the button. A note absolutely positioned
 * over the section would cover the table it explains, which is the one thing
 * you have it open to read alongside. */
.sec-head { position: relative; }
/* The corner is reserved rather than shared. Without this a long heading runs
   under the bird -- "Paid to a name this warehouse already knows" reaches the
   right edge on a narrow card, and the two overlap with no layout to stop it. */
.sec-head > h2 { padding-right: 40px; }
.sec-note > summary {
  /* -4px cancels this button's own padding, so the BIRD lines up with the
     section's right edge rather than the invisible box around it -- the same
     edge the tables and controls below it end on. */
  position: absolute; top: 0; right: -4px;
  display: inline-flex; align-items: center; cursor: pointer;
  padding: 1px 4px; border-radius: var(--radius);
  list-style: none;   /* the disclosure triangle, next to a bird, reads as two
                         controls where there is one */
}
.sec-note > summary::-webkit-details-marker { display: none; }
/* 30x20, not the 24x16 this started at. The bird is a 48x32 pixel grid, and at
   24 wide every grid pixel is half a device pixel: the speech bubble lost its
   outline and the exclamation mark rendered as two grey specks -- the one part
   of the icon that says "there is something to read here". 30x20 still fits
   inside the 22.5px line box of the h2 it sits level with. */
.sec-note > summary img { display: block; width: 30px; height: 20px; opacity: 0.72; }
.sec-note > summary:hover { background: var(--accent-soft); }
.sec-note > summary:hover img, .sec-note[open] > summary img { opacity: 1; }
/* Under the heading, at the left margin, across the whole card. No `max-width`
   here, unlike `section > .note` above: behind a button the note is something
   you opened on purpose and will close again, so the shorter it is on the page
   the better, and a 84ch measure made a two-line note four lines long and left
   half the card empty beside it. The -4px pulls it up against the h2's own
   bottom margin, the same offset the visible notes use. */
.sec-note > .note {
  color: var(--muted); font-size: 12.5px; margin: -4px 0 12px;
}

/* The children give up their own margin to the gap below, so the grid carries
   the bottom one itself -- the same 16px `section` uses, because a row of
   paired sections sits in the same rhythm as a single one. Without it a pair
   butted straight into whatever followed: 16px of air above and none below,
   which reads as the next section belonging to the pair. */
.grid {
  display: grid; gap: 16px; margin-bottom: 16px;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
.grid > section { margin: 0; }

/* --- stat tiles --------------------------------------------------------- */

.tiles { display: grid; gap: 10px; grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); }
/* A fold carries its own 16px BELOW it and none above, which reads correctly
   between folds and not at all under a tile row -- the tiles and the first
   section touched, with every section after it spaced. Stated as an adjacency
   so it adds the gap only where the two actually meet. */
.tiles + .fold { margin-top: 16px; }
.tile {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 10px 12px; background: var(--bg);
}
.tile .n {
  font-size: 19px; font-weight: 640; font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em; color: var(--ink);
}
.tile .k { color: var(--muted); font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.04em; }
.tile.money .n { color: var(--money); }
.tile.value .n { color: var(--value); }

/* --- tables ------------------------------------------------------------- */

.scroll { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th, td { text-align: left; padding: 6px 10px; border-bottom: 1px solid var(--line); vertical-align: top; }
/* A tinted band rather than a heavy rule: these tables run to dozens of
   columns and a black underline across all of them read as a divider between
   two tables. */
th {
  color: var(--muted); font-weight: 600; font-size: 11px;
  text-transform: uppercase; letter-spacing: 0.05em; white-space: nowrap;
  background: var(--bg);
}
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--accent-soft); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
td.money { color: var(--money); }
td.value { color: var(--value); }
td.mono, .mono { font-family: var(--mono); font-size: 12px; }

/* --- badges ------------------------------------------------------------- */

.badge {
  display: inline-block; padding: 1px 7px; border-radius: 20px;
  font-size: 11px; border: 1px solid var(--line); color: var(--muted);
  white-space: nowrap;
}
.badge.on { background: var(--accent-soft); color: var(--accent); border-color: transparent; }
.badge.warn { background: var(--warn-soft); color: var(--warn); border-color: transparent; }
/* A badge that DOES something -- the "fly this flag" control on the flags
   page. It is a <button> rather than a clickable span so it is reachable by
   keyboard and announced as a control; these three properties are only what a
   button needs to stop looking like one, since .badge already draws the pill. */
.badge-btn { font: inherit; font-size: 11px; background: none; cursor: pointer; }
.badge-btn:hover, .badge-btn:focus-visible {
  border-color: var(--accent); color: var(--accent);
}
.badges { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 14px; }

.caveat {
  background: var(--warn-soft); color: var(--warn);
  border-radius: var(--radius); padding: 8px 12px;
  font-size: 12.5px; margin-bottom: 12px;
}

/* --- misc --------------------------------------------------------------- */

dl.facts { display: grid; grid-template-columns: auto 1fr; gap: 4px 16px; margin: 0; font-size: 13px; }
dl.facts dt { color: var(--muted); white-space: nowrap; }
dl.facts dd { margin: 0; word-break: break-word; }

.chart { width: 100%; height: 130px; display: block; }
.chart .bar { fill: var(--accent); }
/* A month that nets negative hangs below the axis (see barChart): reversals
   exceeded payments. Muted so a reversal reads as an annotation, not spend. */
.chart .bar.negative { fill: var(--muted); opacity: 0.7; }
.chart .axis { stroke: var(--line); }
.chart text { fill: var(--muted); font-size: 9px; }

.prose { font-size: 13px; color: var(--ink); max-height: 260px; overflow-y: auto; }
.prose h3 { font-size: 13px; margin: 12px 0 4px; }
.prose p { margin: 0 0 8px; color: var(--muted); }

/* --- sources page ------------------------------------------------------- */

/* A second line inside a cell: the publisher under its dataset, the host
   under the method. Small enough to read as annotation rather than content. */
.sub { color: var(--muted); font-size: 11.5px; font-weight: 400; }

/* A report from another machine that has stopped arriving. Coloured because
   the row it annotates still looks exactly like a running scraper, and the
   only thing separating "collecting" from "was collecting when we last heard"
   is this line. */
.sub.stale { color: var(--warn); }

/* An absent date, which on the sources page is a finding and not a gap: this
   publisher states no update date. Dimmed so a column of them reads as an
   answer rather than as something failing to load. */
.none { color: var(--muted); opacity: 0.65; }

/* --- the file registry's own pages -------------------------------------- */

/* The head of a source file, and a diff over two of them. Both are the same
   thing on the page -- a block of somebody else's lines, monospaced, scrolling
   in both directions -- and neither may wrap: these are CSV rows, and a
   wrapped row stops being one row. Horizontal scroll is the honest failure
   there; reflowing is not. */
.filepeek, .diff {
  font-family: var(--mono); font-size: 11.5px; line-height: 1.55;
  background: var(--bg); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 10px 12px; margin: 8px 0 0;
  max-height: 460px; overflow: auto; white-space: pre; tab-size: 8;
}
.filepeek .ln {
  display: inline-block; width: 3.2em; margin-right: 10px; text-align: right;
  color: var(--muted); opacity: 0.6; user-select: none;
}
.diff span { display: block; }
/* Colour AND a leading +/- from the markup, never colour alone: this is the
   one place on the site where the whole meaning of a line is carried by which
   side of a change it is on, and about one reader in twelve cannot use the
   colour to tell. */
.diff .add { color: var(--money); }
.diff .del { color: var(--fail); }
.diff .ctx { color: var(--muted); }
.diff .hunk {
  color: var(--accent); background: var(--accent-soft);
  margin: 0 -12px 4px; padding: 2px 12px;
}
.diff .hunk:not(:first-child) { margin-top: 10px; }

details.feed { border-bottom: 1px solid var(--line); padding: 8px 0; }
details.feed:last-child { border-bottom: 0; }
details.feed > summary {
  cursor: pointer; font-size: 13px; display: flex; gap: 8px;
  align-items: baseline; flex-wrap: wrap;
}
details.feed > summary::marker { color: var(--muted); }
details.feed > .note { margin: 8px 0; color: var(--muted); font-size: 12.5px; }
details.feed code { font-family: var(--mono); font-size: 11.5px; }

.empty { color: var(--muted); font-size: 13px; font-style: italic; }
.status-PASS { color: var(--money); }
.status-WARN { color: var(--warn); }
.status-FAIL { color: var(--fail); }
.loading { color: var(--muted); padding: 40px 0; text-align: center; }

/* The "back shortly" view the router paints when the API is down or the
   warehouse is locked (showDisruption in app.js). Quiet on purpose: it is an
   interruption the site expects, not an error for the reader to act on. */
.disruption { max-width: 34rem; margin: 48px auto; }
.disruption .note { color: var(--muted); }
.disruption-pulse { display: inline-block; width: .6rem; height: .6rem;
  margin-right: .5rem; border-radius: 50%; background: var(--accent);
  vertical-align: middle; animation: disruption-pulse 1.6s ease-in-out infinite; }
@keyframes disruption-pulse { 50% { opacity: .25; } }

/* Checkbox filter set on the browse pages. */
.flagset { display: flex; gap: 14px; flex-wrap: wrap; }
label.chk {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12.5px; color: var(--ink); cursor: pointer;
}
label.chk input { accent-color: var(--accent); margin: 0; }

/* .dt-controls is styled with .controls, above -- they are one control bar. */
tfoot td { border-top: 2px solid var(--line); border-bottom: none; }

/* Paging, under a table. The range states what slice of the FILTERED set is
   on screen; the totals row above it keeps describing the whole set, so the
   two must sit apart or they read as a contradiction. */
.pager {
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
  margin: 8px 0 0; font-size: 11.5px; color: var(--muted);
}
.pager .pager-range { font-variant-numeric: tabular-nums; }
.pager button[data-role="page-prev"], .pager button[data-role="page-next"] {
  padding: 2px 10px; font-size: 11.5px; border-radius: 20px;
  background: transparent; color: var(--muted); border: 1px solid var(--line);
}
.pager button:hover:not(:disabled) {
  filter: none; color: var(--accent); border-color: var(--accent);
}
.pager button:disabled { opacity: 0.35; cursor: default; }
/* A numeric column that is a dimension, not a measure (fiscal year, year
   built): right-aligned like a number, never summed. */
td.dim, th.dim { font-variant-numeric: tabular-nums; }

/* Grouping chips: the current grouping, in the order the levels nest. */
.chips { display: inline-flex; gap: 4px; align-items: center; flex-wrap: wrap;
         font-size: 11.5px; color: var(--muted); }
.chip {
  padding: 2px 8px; border-radius: 20px; font-size: 11.5px;
  background: var(--accent-soft); color: var(--accent);
  border: 1px solid transparent; cursor: pointer;
}
.chip { background: transparent; color: var(--muted); border-color: var(--line); }
.chip:hover { filter: none; border-color: var(--accent); color: var(--accent); }
.chip.on { background: var(--accent-soft); color: var(--accent); border-color: transparent;
           font-weight: 600; }
.chip .lvl {
  display: inline-block; min-width: 14px; height: 14px; line-height: 14px;
  border-radius: 7px; background: var(--accent); color: var(--panel);
  font-size: 9.5px; text-align: center; margin-right: 2px;
}
.chip.clear { color: var(--muted); border-style: dashed; }

/* Filters on the cases page: one panel, a labelled field per filter, built
 * from the same border, radius and type sizes as the table controls above. */
.case-filters {
  display: flex; gap: 14px 22px; align-items: flex-end; flex-wrap: wrap;
  margin: 12px 0 14px; padding: 10px 14px;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow);
}
.case-filters .cf-field { display: flex; flex-direction: column; gap: 4px; }
.case-filters .cf-label {
  color: var(--muted); font-size: 10.5px; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase;
}
.case-filters .cf-range { display: inline-flex; align-items: center; gap: 6px; }
.case-filters .cf-to { color: var(--muted); font-size: 11.5px; }
.case-filters input.cf-date, .case-filters select {
  padding: 5px 8px; font-size: 12.5px; font-family: inherit;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--bg); color: var(--ink);
}
.case-filters input.cf-date { width: 108px; font-variant-numeric: tabular-nums; }
.case-filters input.cf-date::placeholder { color: var(--muted); opacity: 0.7; }
.case-filters input.cf-date:focus, .case-filters select:focus {
  outline: 2px solid var(--accent-soft); border-color: var(--accent);
}
.case-filters .cf-charge select { min-width: 220px; max-width: 340px; }
.case-filters .cf-actions { display: inline-flex; gap: 8px; align-items: center; margin-left: auto; }
.case-filters .cf-apply { padding: 5px 16px; font-size: 12.5px; font-weight: 600; }
.case-filters .chip.clear { padding: 4px 10px; font-size: 11.5px; }

/* The calendar behind the date boxes. */
#cf-cal {
  position: absolute; z-index: 50; width: 268px; padding: 8px;
  background: var(--panel); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.25);
}
#cf-cal[hidden] { display: none; }
#cf-cal button {
  padding: 0; border: 1px solid transparent; border-radius: 6px;
  background: transparent; color: var(--ink); font-size: 12.5px; cursor: pointer;
}
#cf-cal button:hover { filter: none; background: var(--accent-soft); color: var(--accent); }
.cf-cal-head { display: flex; align-items: center; gap: 2px; margin-bottom: 6px; }
.cf-cal-head select {
  flex: 1; min-width: 84px; padding: 3px 2px; margin: 0 2px; font-size: 12.5px; font-weight: 600;
  font-family: inherit; border: 1px solid var(--line); border-radius: 6px;
  background: var(--bg); color: var(--ink);
}
.cf-cal-head select[data-pick="year"] { flex: 0 0 64px; }
.cf-cal-head select:focus { outline: 2px solid var(--accent-soft); border-color: var(--accent); }
.cf-cal-head button { width: 22px; height: 26px; color: var(--muted); }
.cf-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; text-align: center; }
.cf-cal-grid b { font-size: 10.5px; color: var(--muted); font-weight: 600; padding: 3px 0; }
.cf-cal-grid button { height: 28px; font-variant-numeric: tabular-nums; }
#cf-cal button.today { border-color: var(--line); }
#cf-cal .cf-cal-grid button.on { background: var(--accent); color: var(--panel); font-weight: 600; }
.cf-cal-foot { display: flex; justify-content: space-between; margin-top: 6px; }
.cf-cal-foot button { padding: 3px 8px; font-size: 11.5px; color: var(--accent); }


/* A pane: data beside the thing it was read from.
 *
 * The rebuild is the default view and the source page is the alternate, which
 * is the way round it has to be -- the rebuilt thing is interactive and the
 * PDF is a picture. Used first on the tax rate tables, where the rebuild is
 * only a table, but built for the artefacts that are not: the "Your Tax
 * Dollar At Work" dollar bill, an org chart of boxes and lines. */
.pane { margin: 0 0 18px; }
.pane-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
}
.pane-head h3 { margin: 0 0 6px; }
/* A segmented control, not two buttons: one of them is always on, which is
   what tells a reader they are looking at a choice rather than an action. */
.seg {
  display: inline-flex; border: 1px solid var(--line); border-radius: 20px;
  overflow: hidden; flex: 0 0 auto;
}
.seg button {
  padding: 2px 12px; font-size: 11.5px; border: 0; cursor: pointer;
  background: transparent; color: var(--muted); border-radius: 0;
}
.seg button:hover { filter: none; color: var(--accent); }
.seg button.on {
  background: var(--accent-soft); color: var(--accent); font-weight: 600;
}
.seg button + button { border-left: 1px solid var(--line); }
.source-page {
  border: 1px solid var(--line); border-radius: 4px; background: var(--panel);
}
/* The page as an image, with the row the figures were read from marked on it.
   Positioned in PERCENTAGES off the image box, because the marks are stored
   as fractions of the page and so land correctly at any width. */
.page-shot { position: relative; display: inline-block; max-width: 100%; }
.page-shot img {
  display: block; max-width: 100%; height: auto;
  border: 1px solid var(--line); border-radius: 4px;
}
.page-mark {
  position: absolute; pointer-events: none; border-radius: 2px;
  /* Outlined and tinted rather than a solid block: a filled highlight over
     dark text on a light scan is unreadable at any opacity that still reads
     as a highlight. */
  background: rgba(255, 214, 0, 0.28);
  outline: 2px solid var(--accent); outline-offset: 1px;
}
/* The page image stays white in dark mode -- it is a photograph of paper --
   so the mark is tuned against the paper, not against the page theme. */
:root[data-theme="dark"] .page-mark { background: rgba(255, 214, 0, 0.32); }
.source-line {
  overflow-x: auto; white-space: pre; font-size: 11.5px;
  background: var(--accent-soft); padding: 8px 10px; border-radius: 4px;
}

/* --- per-row citations ---------------------------------------------------
   A marker on the row itself, and the page it names opening underneath it.
   Quiet until hovered: a table where every row shouts its provenance is a
   table nobody reads the figures in. */
td.cite-col, th.cite-col { width: 1%; white-space: nowrap; }
/* In its own column it reads as a link, because that is what it does -- opens
   the page. Not a bordered button: a border down a whole column draws more
   attention than the figures beside it, and this is an affordance offered
   beside the data rather than competing with it. `citeMark` can also wrap a
   value in place, where it inherits the cell and only the underline shows. */
.cite-mark {
  font: inherit; font-size: 11.5px; color: var(--accent);
  background: none; padding: 0; border: 0;
  border-bottom: 1px dotted var(--accent); cursor: pointer;
}
.cite-mark:hover, .cite-mark:focus-visible { border-bottom-style: solid; }
.cite-mark.on {
  border-bottom-style: solid; background: var(--accent-soft);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
/* The opened page sits in the table as its own row, so it stays attached to
   the figure it explains however the table is sorted or paged. */
tr.cite-open > td { background: var(--panel); padding: 12px 14px; }
tr.cite-open .page-shot { max-width: 760px; }
.hint { color: var(--muted); font-size: 11.5px; margin: -4px 0 8px; }

/* The CSV form of the same citation. There is no page image to draw a box on
   -- a CSV has no page -- so the mark is the row itself, held in its
   neighbours so a reader can see it sits among rows of the same kind. */
.cite-rows-scroll { overflow-x: auto; max-width: 100%; }
table.cite-csv { border-collapse: collapse; font-size: 11.5px; }
table.cite-csv th, table.cite-csv td {
  padding: 3px 8px; white-space: nowrap; border-bottom: 1px solid var(--line);
  max-width: 320px; overflow: hidden; text-overflow: ellipsis;
}
table.cite-csv th { text-align: left; color: var(--muted); font-weight: 600; }
/* The equivalent of `.page-mark`. Left border rather than a full fill: the
   cited row must read as marked without its own values becoming harder to
   read than the neighbours it is being compared against. */
table.cite-csv tr.cite-hit > td {
  background: var(--accent-soft); font-weight: 600;
}
table.cite-csv tr.cite-hit > td:first-child {
  box-shadow: inset 2px 0 0 var(--accent);
}

/* Inflow/outflow diagram. */
.flow-wrap { margin: 4px 0 2px; }
/* No overflow:visible -- labels now sit inside the viewBox, and letting the
   SVG paint outside it made the wrapper scroll and clipped the right-hand
   gutter at the panel edge. */
.flow { width: 100%; min-width: 620px; height: auto; display: block; }
.flow .band { fill: var(--accent); opacity: 0.14; stroke: var(--accent);
              stroke-width: 0.75; stroke-opacity: 0.35; }
.flow .band.out { fill: var(--money); opacity: 0.16; stroke: var(--money); }
.flow .node { fill: var(--accent); }
.flow .node.out { fill: var(--money); }
.flow .node.centre { fill: var(--ink); opacity: 0.78; }
.flow .flow-label { fill: var(--ink); font-size: 11.5px; }
.flow .flow-amt { fill: var(--muted); }
.flow .flow-centre { fill: var(--panel); font-size: 11px; font-weight: 650; }
.flow .flow-centre.amt { font-size: 10px; opacity: 0.9; }
/* A hairline joining a label to the band it belongs to, needed because label
   spacing is fixed while band spacing is proportional. */
.flow .leader { stroke: var(--line); stroke-width: 1; fill: none; }

/* Hovering a row lights it and dims the rest, so a thin band can still be
   traced from its label across to the centre. */
.flow .flow-row { cursor: default; }
.flow a.flow-row { cursor: pointer; }
.flow:hover .flow-row { opacity: 0.32; transition: opacity 0.12s; }
.flow:hover .flow-row:hover { opacity: 1; }
.flow .flow-row:hover .band { opacity: 0.5; }
.flow .flow-row:hover .flow-label { font-weight: 650; }
.flow a.flow-row:hover .flow-label { fill: var(--accent); text-decoration: underline; }

/* Banded grouping: rows stay visible under a heading per group, each band
   closing with its own subtotal. */
tr.band-head td {
  background: var(--accent-soft); border-bottom: none;
  padding-top: 10px; font-size: 12.5px;
}
tr.band-head .band-label {
  color: var(--muted); font-size: 10.5px; text-transform: uppercase;
  letter-spacing: 0.04em; margin-right: 6px;
}
tr.band-head a { font-weight: 650; }
tr.band-head .band-sep { color: var(--muted); margin: 0 8px; }
/* Subtotals stay indented by level so a nested one reads as belonging to the
   band above it; the headings no longer need to, now that each carries the
   full path. */
tr.band-total.level-1 td:first-child { padding-left: 26px; }
tr.band-total.level-2 td:first-child { padding-left: 46px; }
tr.band-total td {
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  color: var(--muted); font-size: 12px;
}
tr.band-total.level-0 td { border-bottom: 2px solid var(--line); }
tr.band-total .band-count { color: var(--muted); font-weight: 400; margin-left: 6px; }
table.banded tbody tr:hover { background: none; }
table.banded tbody tr:not(.band-head):not(.band-total):hover { background: var(--accent-soft); }

/* An identifier meant to be copied: selectable as one unit, and visibly a
   value rather than prose. */
.copyable {
  background: var(--bg); border: 1px solid var(--line); border-radius: 4px;
  padding: 1px 6px; user-select: all;
}

/* External map links: buttons, not badges. */
.maplinks { display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
            margin-bottom: 16px; }
a.maplink {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 12px; border-radius: var(--radius);
  border: 1px solid var(--accent); color: var(--accent);
  font-size: 13px; font-weight: 550; line-height: 1.2;
  background: var(--panel);
}
a.maplink:hover { background: var(--accent); color: var(--panel); text-decoration: none; }
a.maplink svg { width: 14px; height: 14px; fill: currentColor; flex: none; }
a.maplink .ext { font-size: 11px; opacity: 0.75; }
.maplink-note { color: var(--muted); font-size: 12px; }

/* --- county org chart -----------------------------------------------------
   Colour on an org card says how a body is ACCOUNTABLE -- elected, judicial,
   Board-appointed, under the County Executive -- which is the one structural
   fact the printed chart carries that its text does not. It never encodes
   size, budget or importance. */
.k-elected { --k: #8a5714; }
.k-court   { --k: #634882; }
.k-board   { --k: #17518c; }
.k-exec    { --k: #0f6b4b; }
/* The Commonwealth's chart splits accountability four ways the county's does
   not need: a sovereign root, the legislative branch, and bodies independent
   of the executive entirely. `elected`, `court` and `exec` mean the same thing
   on both charts and deliberately keep the same colour -- a reader moving from
   one page to the other should not have to relearn them. */
.k-sovereign   { --k: #6b6257; }
.k-legislative { --k: #8f4a4a; }
.k-independent { --k: #2f6f78; }
:root[data-theme="dark"] .k-elected     { --k: #d2a05a; }
:root[data-theme="dark"] .k-court       { --k: #a98fcb; }
:root[data-theme="dark"] .k-board       { --k: #74aee4; }
:root[data-theme="dark"] .k-exec        { --k: #56c79a; }
:root[data-theme="dark"] .k-sovereign   { --k: #b3a893; }
:root[data-theme="dark"] .k-legislative { --k: #e09393; }
:root[data-theme="dark"] .k-independent { --k: #74c2cc; }

.org-controls { align-items: center; }
.org-grid {
  display: grid; gap: 10px; margin-bottom: 6px;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
}
.org-group {
  color: var(--muted); font-size: 11.5px; text-transform: uppercase;
  letter-spacing: 0.05em; margin: 14px 0 8px;
}
a.org-card {
  display: block; padding: 10px 12px; text-decoration: none; color: inherit;
  background: var(--panel); border: 1px solid var(--line);
  border-left: 3px solid var(--k, var(--line)); border-radius: var(--radius);
}
a.org-card:hover { border-color: var(--accent); border-left-color: var(--k, var(--accent)); }
a.org-card[hidden] { display: none; }
.org-name { font-weight: 600; font-size: 13.5px; line-height: 1.3; }
.org-head { font-size: 12.5px; margin-top: 4px; }
.org-role { color: var(--muted); font-size: 11.5px; }
.org-money {
  margin-top: 6px; font-size: 13px; font-weight: 600; color: var(--money);
  font-variant-numeric: tabular-nums;
}
.org-money .dim { font-weight: 400; font-size: 11.5px; }
.org-tags { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 7px; }
.badge.dotted { border-style: dashed; color: var(--accent); }

/* A state agency's internal chart, redrawn from the PDF's own box geometry
   (vaOrgDiagram in pages/browse.js). The SVG is in page units and sized to a
   little under print size; the wrapper scrolls rather than shrinking a
   3,000-point page into a strip of grey. Strokes are non-scaling so the
   hairlines survive the scale, and the label font is in page units so it
   scales WITH the boxes -- a fixed pixel size would spill out of every box on
   a wide page. */
.orgchart { margin: 0 0 12px; }
.orgchart figcaption {
  color: var(--muted); font-size: 11.5px; text-transform: uppercase;
  letter-spacing: 0.05em; margin: 12px 0 6px;
}
/* The canvas is the PAGE tint and the boxes are the PANEL, which is the
   opposite way round from the rest of this stylesheet and is the whole
   difference between a diagram and a wireframe: a white card on a tinted
   board separates without being outlined, so the border can drop to a hairline
   instead of doing all the work. It inverts correctly on its own in dark mode,
   where --panel (#12202f) is the LIGHTER of the two -- a card is the raised
   surface in both themes, which is why no rule here is written twice. */
.orgchart-scroll {
  overflow: auto; max-height: 80vh; background: var(--bg);
  border: 1px solid var(--line); border-radius: var(--radius);
}
.orgchart svg { display: block; }
.orgchart .link {
  fill: none; stroke: var(--muted); stroke-width: 1.1px; opacity: 0.55;
  stroke-linecap: round; stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
/* The shadow is a constant rather than a token because it does the same job in
   both themes for opposite reasons: in light it is the separation, and in dark
   it is nearly invisible and the raised surface is. Two lines of blur at 1.5
   user units, which the 1.15 scale leaves under 2px. */
.orgchart .box rect {
  fill: var(--panel); stroke: var(--line); stroke-width: 1px;
  vector-effect: non-scaling-stroke;
  filter: drop-shadow(0 0.7px 1.1px rgb(15 32 51 / 0.16));
}
/* Three weights of ONE neutral, light to dark, because what they encode is a
   magnitude -- how much of this box the drawing gave up. A leaf nothing points
   at, a box other boxes report to, and one that has those lines and none of
   its own. No colour is spent on any of them: --accent is the HOVER, and on a
   chart like Elections three boxes are legitimately top-of-what-was-drawn, so
   painting them all blue reads as "these three are selected" rather than as a
   third step of the same ramp. */
.orgchart .box.kids rect { stroke: var(--muted); }
.orgchart .box.top rect { stroke: var(--ink); stroke-width: 1.5px; }
/* A cluster box is OURS -- the agency drew no rectangle and these words were
   grouped by proximity -- so it is dashed and UNFILLED. It must not inherit
   the weights above: they measure the drawing, and this is the case where
   there is none. */
.orgchart .box.cluster rect {
  fill: none; stroke: var(--muted); stroke-width: 1px;
  stroke-dasharray: 3 2; filter: none;
}
.orgchart .box:hover rect { stroke: var(--accent); stroke-width: 1.8px; }
.orgchart .lbl {
  width: 100%; height: 100%; box-sizing: border-box; padding: 1px 3px;
  display: flex; align-items: center; justify-content: center;
  text-align: center; overflow: hidden; color: var(--ink);
  font: 7.5px/1.15 system-ui, sans-serif; overflow-wrap: break-word;
}
/* The LAID-OUT trees (core.js `orgTree`) size the box to the label instead of
   the label to the box, because there the box is ours to make bigger. So they
   set their own type here rather than inheriting the agency charts' 7.5px
   floor, which exists only because a PDF's rectangle cannot be resized. */
/* `.orgchart.laid`, not `.orgchart .laid` -- one element carries both, and the
   descendant form matched nothing while looking right. And the inner class is
   `lbl-sub` rather than `sub`, because `.sub` is already a site-wide 11.5px
   rule: inherited here it drew every officeholder's name HALF AGAIN LARGER
   than the office it belongs to. */
.orgchart.laid .lbl {
  flex-direction: column; gap: 1.5px; padding: 3px 5px;
  font: 10px/1.25 system-ui, sans-serif;
}
.orgchart.laid .lbl .lbl-sub { color: var(--muted); font-size: 8.5px; }
.orgchart .lbl-a {
  display: block; width: 100%; height: 100%;
  text-decoration: none; color: inherit;
}
.orgchart .lbl-a:hover .lbl { text-decoration: underline; }
/* Each swatch wears the diagram's own border, so the key can be checked
   against the picture rather than described to it -- --line plain, --muted for
   a box others report to, --ink for the top. */
.orgchart-key .k-box {
  display: inline-block; padding: 0 5px; border: 1px solid var(--line);
  border-radius: 3px; background: var(--panel); font-size: 11px;
}
.orgchart-key .k-cluster {
  border-style: dashed; border-color: var(--muted); background: none;
}
.orgchart-key .k-kids { border-color: var(--muted); }
.orgchart-key .k-top { border: 1.5px solid var(--ink); }
.orgchart-key .k-line {
  display: inline-block; width: 22px; border-top: 1.5px solid var(--muted);
  vertical-align: middle;
}
.orgchart-more, .orgchart-rows { margin-top: 10px; }
.orgchart-more summary,
.orgchart-rows summary { cursor: pointer; color: var(--accent); font-size: 13px; }
.chip.k-elected, .chip.k-court, .chip.k-board, .chip.k-exec,
.chip.k-sovereign, .chip.k-legislative, .chip.k-independent {
  border-left: 3px solid var(--k);
}

/* A leader block. The name and the composition of a collegial body are
   deliberately styled apart: "12 appointees" must not read as a person. */
.leader {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 12px 14px; margin-bottom: 10px; background: var(--panel);
}
.leader-who { display: flex; gap: 9px; align-items: baseline; flex-wrap: wrap;
  margin-bottom: 9px; }
.leader-name { font-size: 17px; font-weight: 600; }
.leader-body { font-size: 15px; color: var(--muted); font-style: italic; }
.leader-role { color: var(--muted); font-size: 12.5px; }
span.dim { color: var(--muted); }
a.outlink { font-size: 12.5px; }

/* Party colours. Conventional US party colours. These were a label chip only
   and this comment said so; the office timeline now encodes party as colour
   along a time axis, which is a real data encoding and the reason the chip
   hues are reused rather than a second palette invented. Two rules come with
   that: the timeline always carries a legend, because colour alone is not a
   label, and every segment carries its holder and party in a title, because
   colour alone is not accessible either. */
.badge.p-democratic  { color: #1b5e8f; border-color: #1b5e8f; }
.badge.p-republican  { color: #9c2c2c; border-color: #9c2c2c; }
.badge.p-independent { color: var(--muted); }
.badge.p-libertarian { color: #8a6100; border-color: #8a6100; }
:root[data-theme="dark"] .badge.p-democratic  { color: #6fb3e0; border-color: #6fb3e0; }
:root[data-theme="dark"] .badge.p-republican  { color: #e07a7a; border-color: #e07a7a; }
:root[data-theme="dark"] .badge.p-libertarian { color: #d9ab4a; border-color: #d9ab4a; }

/* Office timeline. One horizontal band per seat, segments proportional to the
   interval BETWEEN ELECTIONS -- which is what the data measures, and is not
   the same as time in office; the page says so rather than the CSS pretending
   otherwise. Flexbox rather than SVG because the segments carry text and want
   to wrap their labels at whatever width the reader's window is. */
.timeline { margin: 14px 0 6px; }
.timeline-band {
  display: flex; width: 100%; height: 34px; border-radius: 3px;
  overflow: hidden; border: 1px solid var(--line); background: var(--bg);
}
.tl-seg {
  min-width: 3px; position: relative; cursor: default;
  border-right: 1px solid var(--bg);
}
.tl-seg:last-child { border-right: 0; }
.tl-seg.p-democratic  { background: #1b5e8f; }
.tl-seg.p-republican  { background: #9c2c2c; }
.tl-seg.p-independent { background: #7a7a7a; }
.tl-seg.p-other       { background: #8a6100; }
.tl-seg.p-none        { background: repeating-linear-gradient(45deg,
                          var(--line) 0 4px, transparent 4px 8px); }
/* The seat is still held: no end date, so the segment is drawn open rather
   than closed at an arbitrary today. */
.tl-seg.current { box-shadow: inset -3px 0 0 var(--accent); }
.timeline-axis {
  display: flex; justify-content: space-between;
  font-size: 11.5px; color: var(--muted); margin-top: 3px;
}
.timeline-legend {
  display: flex; flex-wrap: wrap; gap: 12px; margin-top: 8px;
  font-size: 12.5px; color: var(--muted);
}
.timeline-legend span { display: inline-flex; align-items: center; gap: 5px; }
.timeline-legend i {
  width: 11px; height: 11px; border-radius: 2px; display: inline-block;
  border: 1px solid var(--line);
}
.timeline-legend i.p-democratic  { background: #1b5e8f; }
.timeline-legend i.p-republican  { background: #9c2c2c; }
.timeline-legend i.p-independent { background: #7a7a7a; }
.timeline-legend i.p-other       { background: #8a6100; }
.timeline-legend i.p-none        { background: repeating-linear-gradient(45deg,
                                     var(--line) 0 4px, transparent 4px 8px); }
:root[data-theme="dark"] .tl-seg.p-democratic, :root[data-theme="dark"] .timeline-legend i.p-democratic { background: #4a90c4; }
:root[data-theme="dark"] .tl-seg.p-republican, :root[data-theme="dark"] .timeline-legend i.p-republican { background: #c25b5b; }
:root[data-theme="dark"] .tl-seg.p-other, :root[data-theme="dark"] .timeline-legend i.p-other { background: #b8912f; }


/* Department mission, and the internal org tree read off the budget-plan
   drawings. The tree is nested <ul>; indentation carries the hierarchy. */
.mission { font-size: 14.5px; line-height: 1.6; margin: 0; max-width: 68ch; }
ul.tree, ul.tree ul { list-style: none; margin: 0; padding: 0; font-size: 13px; }
ul.tree ul { padding-left: 16px; border-left: 1px solid var(--line); margin-left: 4px; }
ul.tree li { padding: 3px 0; color: var(--muted); line-height: 1.35; }
ul.tree > li > span { font-weight: 600; color: var(--ink); }

/* Whether a parcel's school assignment was checked against FCPS, or only
   derived from the county's polygons. Deliberately understated: agreement is
   the normal case and should not look like an award. */
.validate {
  margin-top: 12px; padding: 11px 13px; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--panel); font-size: 13px;
}
.validate.agrees { border-left: 3px solid var(--money); }
.validate.differs { border-left: 3px solid var(--warn); }
.validate .dim { display: block; margin-top: 3px; line-height: 1.5; }
.validate-note { margin: 9px 0 0; font-size: 12.5px; color: var(--muted); line-height: 1.5; }
button.btn-validate {
  margin-top: 10px; padding: 7px 13px; font-size: 13px; cursor: pointer;
  background: var(--accent-soft); color: var(--accent);
  border: 1px solid transparent; border-radius: var(--radius); font-weight: 600;
}
button.btn-validate:hover { background: var(--accent); color: var(--panel); }
button.btn-validate:disabled { opacity: 0.55; cursor: default; }
table.verdicts { margin-top: 9px; border-collapse: collapse; font-size: 13px; }
table.verdicts td { padding: 3px 14px 3px 0; }
table.verdicts td:first-child { color: var(--muted); }

/* --- the map of Virginia ---------------------------------------------------

   A selector first and a picture second, so it is drawn as flat as it can be:
   no borders around the map, no shadow, no graticule, no labels. The only
   things carrying meaning are the three fills -- how deep the collection goes
   -- and the outline on whatever is selected.

   Selection is shown by fading everything else rather than by raising the
   selected shape. SVG has no z-index and the outlines are drawn in FIPS
   order, so a selected county's border would otherwise be painted over by
   whichever neighbour comes after it in the alphabet. */

.coverage {
  display: grid; gap: 24px; align-items: start;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
}
@media (max-width: 900px) { .coverage { grid-template-columns: 1fr; } }

.map-frame { position: relative; }
.va-map { display: block; width: 100%; height: auto; touch-action: pan-y; }
/* Borders are measured in screen pixels, not map units, so zooming in shows
   more coastline instead of thicker lines.

   The stroke is a translucent ink line, NOT the background colour. The service
   this geometry comes from generalises every locality independently -- in the
   source data Staunton's 20-vertex outline shares only 13 vertices with the
   hole Augusta County leaves for it -- so at high zoom neighbouring edges do
   not coincide. Drawn in the background colour those gaps were white gashes
   between counties; drawn in ink at low opacity, two edges a fraction of a
   pixel apart read as one slightly soft line, which is the truth: the border
   is known to about that precision and no better. */
.va-map .loc {
  cursor: pointer;
  stroke: var(--ink); stroke-width: 0.7; stroke-opacity: 0.28;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  transition: opacity 0.12s ease;
}
/* Four steps, light to dark, because the statewide parcel layer put a floor
   under every locality and the interesting difference moved up a level: t1 is
   the parcel map alone, t2 adds the locality's own owners or values, t3 is a
   locality collected from its own publications, t4 has its government too. */
.va-map .t1 { fill: #dfe5ee; }
.va-map .t2 { fill: #b8d0e8; }
.va-map .t3 { fill: #6f9fca; }
.va-map .t4 { fill: var(--accent); }
.va-map .t0 { fill: #eef1f6; }
:root[data-theme="dark"] .va-map .t1 { fill: #2b3a4b; }
:root[data-theme="dark"] .va-map .t2 { fill: #2f5b7e; }
:root[data-theme="dark"] .va-map .t3 { fill: #3f7cae; }
:root[data-theme="dark"] .va-map .t0 { fill: #1a2634; }
.va-map .loc:hover { stroke: var(--ink); stroke-width: 1; stroke-opacity: 1; }

/* An independent city too small to click: a dot you can see and a circle you
   can hit, which are not the same size. A dot drawn big enough to click would
   be wider than the county beside it and would read as a quantity. */
.va-map .city { cursor: pointer; transition: opacity 0.12s ease; }
.va-map .city .hit { fill: transparent; }
.va-map .city .dot {
  pointer-events: none;
  stroke: var(--ink); stroke-width: 0.8; stroke-opacity: 0.45;
  vector-effect: non-scaling-stroke;
}
/* Zoomed in far enough that the locality itself can be clicked: the marker
   has done its job and gets out of the way of the shape. */
.va-map .city.retired { display: none; }
.va-map .city:hover .dot { stroke-width: 1; stroke-opacity: 1; }
.va-map .city.on .dot { stroke-width: 1.3; stroke-opacity: 1; }

.va-map.has-sel .loc:not(.on), .va-map.has-sel .city:not(.on) { opacity: 0.3; }
.va-map .loc.on { stroke: var(--ink); stroke-width: 1.2; }

.map-legend {
  list-style: none; margin: 8px 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 4px 16px;
  color: var(--muted); font-size: 11.5px;
}
.map-legend li { display: flex; align-items: center; gap: 6px; }
.map-legend .key { width: 11px; height: 11px; border-radius: 2px; display: inline-block; }
.map-legend .key.t1 { background: #dfe5ee; }
.map-legend .key.t2 { background: #b8d0e8; }
.map-legend .key.t3 { background: #6f9fca; }
.map-legend .key.t4 { background: var(--accent); }
:root[data-theme="dark"] .map-legend .key.t1 { background: #2b3a4b; }
:root[data-theme="dark"] .map-legend .key.t2 { background: #2f5b7e; }
:root[data-theme="dark"] .map-legend .key.t3 { background: #3f7cae; }

.coverage-panel h3 {
  margin: 0 0 10px; font-size: 15px; font-weight: 600; letter-spacing: -0.01em;
}
.coverage-panel .tiles { grid-template-columns: repeat(auto-fit, minmax(118px, 1fr)); }
.coverage-panel .hint { margin: 10px 0 0; }
.coverage-panel button {
  padding: 5px 11px; font-size: 12px; font-weight: 600;
  background: none; color: var(--accent); border: 1px solid var(--line);
}
.coverage-panel button:hover { background: var(--accent-soft); filter: none; }

/* Zoom. Top-left because that corner of the projected state is empty sky,
   and because the data is in the top-right. */
.map-frame.zoomed .va-map { cursor: grab; }
.map-frame.zoomed .va-map:active { cursor: grabbing; }
.map-zoom {
  position: absolute; top: 0; left: 0; display: flex; gap: 4px;
}
.map-zoom button {
  min-width: 26px; height: 26px; padding: 0 7px;
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; line-height: 1; font-weight: 600;
  background: var(--panel); color: var(--ink);
  border: 1px solid var(--line); border-radius: 6px;
}
.map-zoom button:hover:not(:disabled) {
  filter: none; background: var(--accent-soft); color: var(--accent);
  border-color: transparent;
}
.map-zoom button:disabled { opacity: 0.35; cursor: default; }

/* Choosing a government: one line to choose with, one line about what you
   chose. This was a nested <ol> of four steps, each with a name, a sentence
   and a line of counts -- 300px above every money, funds, vendors, departments
   and org page, most of it describing governments you were not looking at. The
   nesting did say something true (a locality is a creature of the state), but
   it said it on every page load whether or not you were reading it. */
.gov-bar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin: 14px 0 8px;
}
.gov-home { font-size: 15px; font-weight: 600; }
.gov-home.on { color: var(--ink); }
/* Sized like every other control on the site, not like the name beside it --
   it is a filter box, and `.controls select` is the vocabulary. */
.gov-pick {
  font-size: 12.5px; font-weight: 400; padding: 5px 8px; max-width: 100%;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--bg); color: var(--ink);
}
.gov-summary {
  color: var(--muted); font-size: 12.5px; margin: 0 0 20px; max-width: 84ch;
}
.gov-summary strong { color: var(--ink); font-weight: 600; }
.gov-summary .gov-stats { font-variant-numeric: tabular-nums; }

/* The band-order control. The grouped column stops being a header you can
   click -- its values became the band headings -- so the direction lives on
   the heading itself. It was a 9px caret nested inside the group chip, a
   control inside a control, and the two did opposite things a few pixels
   apart: the caret reordered the bands, the chip around it removed them. */
tr.band-head .dir { opacity: .55; font-size: .85em; margin-left: 2px; }
tr.band-head a:hover .dir { opacity: 1; }
/* The index tables' drill, demoted from the band value itself to its own
   small word: the value now sorts (the same verb the detail tables chose),
   and "only" is the thing that filters down to the band. */
tr.band-head a.band-drill {
  font-size: 10.5px; color: var(--muted); font-weight: 400;
  border: 1px solid var(--line); border-radius: 20px;
  padding: 0 7px; margin-left: 6px; vertical-align: 1px;
}
tr.band-head a.band-drill:hover {
  color: var(--accent); border-color: var(--accent); text-decoration: none;
}



/* --- the system flows funnel ----------------------------------------------
   Six stages, drawn from a count taken on load. The bars share ONE scale --
   the widest stage -- so a hop that loses half its tables looks like it lost
   half its tables. Scaling each bar to its own row would draw six full-width
   bars and hide the only thing the picture is for.

   No fixed colours and no fixed widths: the bars are var(--accent) over
   var(--accent-soft), which are defined in both themes, and the grid reflows
   to one column on a phone rather than scrolling sideways. */
.funnel { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px;
          grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.funnel-step { border: 1px solid var(--line); border-radius: var(--radius);
               padding: 11px 13px; background: var(--bg); position: relative; }
/* Six columns exactly, once there is room for six. `auto-fit` alone laid out
   5 + 1 at 1280px, which reads as two unrelated rows of boxes rather than as
   one pipeline -- and the order of the hops is the only thing the picture
   asserts. Below that width it wraps freely and the arrows come off, because
   an arrow pointing at a box on the next line is worse than no arrow. */
@media (min-width: 1080px) {
  .funnel { grid-template-columns: repeat(6, 1fr); }
  .funnel-step + .funnel-step::before {
    content: "▸"; position: absolute; left: -13px; top: 10px;
    color: var(--muted); font-size: 13px; line-height: 1;
  }
}
.funnel-head { display: flex; justify-content: space-between; align-items: baseline;
               gap: 8px; }
.funnel-name { font-weight: 600; font-size: 13px; }
.funnel-n { font-size: 19px; font-weight: 640; font-variant-numeric: tabular-nums;
            letter-spacing: -0.02em; color: var(--ink); }
.funnel-bar { margin: 7px 0 8px; height: 7px; border-radius: 4px;
              background: var(--accent-soft); overflow: hidden; }
.funnel-bar span { display: block; height: 100%; background: var(--accent);
                   border-radius: 4px; }
.funnel-what { margin: 0; font-size: 11.5px; line-height: 1.5; color: var(--muted); }
/* An attrition line, and it only appears where tables actually stop. A stage
   that loses nothing prints nothing rather than "0 stop here" -- a zero in a
   column of real losses reads as a measurement that failed. */
.funnel-lost { margin: 6px 0 0; font-size: 11.5px; font-weight: 600;
               color: var(--warn); }

/* How far a table gets. Deliberately not the build's PASS/WARN/FAIL palette:
   none of these four is a failure, and `staged` in red would turn "nothing
   reads this yet" into an accusation the page explicitly refuses to make. */
/* Eight columns of one row each. Left to themselves the browser gave PUBLISHER
   three lines and PAGE about thirty pixels -- so the last hop of the path, the
   one the whole page is about, was an ellipsis. Fixed layout with declared
   widths, and the long lists break inside the cell rather than widening it. */
.flow-paths table { table-layout: fixed; }
.flow-paths th:nth-child(1), .flow-paths td:nth-child(1) { width: 18%; }
.flow-paths th:nth-child(2), .flow-paths td:nth-child(2) { width: 6%; }
.flow-paths th:nth-child(3), .flow-paths td:nth-child(3) { width: 7%; }
.flow-paths th:nth-child(4), .flow-paths td:nth-child(4) { width: 13%; }
.flow-paths th:nth-child(5), .flow-paths td:nth-child(5) { width: 13%; }
.flow-paths th:nth-child(6), .flow-paths td:nth-child(6) { width: 14%; }
.flow-paths th:nth-child(7), .flow-paths td:nth-child(7) { width: 15%; }
.flow-paths th:nth-child(8), .flow-paths td:nth-child(8) { width: 14%; }
.flow-paths td { overflow-wrap: anywhere; }

.reach { font-size: 11.5px; font-weight: 600; }
.reach-staged { color: var(--warn); }
.reach-modelled, .reach-queried { color: var(--muted); }
.reach-served { color: var(--money); }
.tile.warn .n { color: var(--warn); }

/* --- share-with-no-match bars ----------------------------------------------
   A bar and its number on one line, inside a table cell. Drawn in --warn
   rather than --fail on purpose: this measures two books failing to overlap,
   not a person failing to pay, and a red bar beside somebody's district would
   make the page say the one thing its first paragraph says it cannot. */
.nomatch { display: flex; align-items: center; gap: 8px; }
.nomatch-bar { flex: 1 1 auto; min-width: 40px; height: 7px; border-radius: 4px;
               background: var(--accent-soft); overflow: hidden; }
.nomatch-bar span { display: block; height: 100%; background: var(--warn);
                    border-radius: 4px; }
.nomatch-pct { flex: 0 0 auto; font-variant-numeric: tabular-nums;
               font-size: 12px; color: var(--muted); min-width: 42px;
               text-align: right; }
/* The same bar for the opposite fact: how much of the match an independent
   address agrees with. In --warn it read as a 90% problem. */
.nomatch.ok .nomatch-bar span { background: var(--money); }

/* --- coverage grid -------------------------------------------------------
   One cell per period. The shape carries the finding, so the cells are big
   enough to count by eye and the years stay on one line per dataset. A gap is
   drawn hollow rather than in a second colour: on a page whose whole subject
   is absence, "nothing there" should look like nothing there. */
.cov-sets { display: grid; gap: 18px; }
.cov-set { border: 1px solid var(--line); border-radius: 8px; padding: 14px 16px; }
.cov-set.has-gaps { border-left: 3px solid var(--warn, #b8860b); }
.cov-head { display: flex; justify-content: space-between; align-items: baseline;
            gap: 12px; flex-wrap: wrap; }
.cov-head h3 { margin: 0; font-size: 1rem; }
.cov-score { font-size: 0.85rem; color: var(--dim); }
.cov-set .note { margin: 6px 0 10px; }
.cov-grid { display: flex; flex-wrap: wrap; gap: 10px 12px; margin: 10px 0 8px; }
.cov-year { display: flex; flex-direction: column; gap: 3px; align-items: center; }
.cov-boxes { display: flex; gap: 2px; }
.cov-cell { width: 9px; height: 16px; border-radius: 2px;
            border: 1px solid var(--line); background: transparent; }
.cov-grid.yearly .cov-cell { width: 16px; }
.cov-cell.have { background: var(--accent, #2f6f4f); border-color: var(--accent, #2f6f4f); }
.cov-year .cov-label { font-size: 0.68rem; color: var(--dim); letter-spacing: 0.02em; }
.cov-year.none .cov-label { opacity: 0.55; }
.cov-meta { font-size: 0.78rem; color: var(--dim); margin: 6px 0 0; }
.cov-meta.tail { font-style: italic; }
@media (max-width: 640px) { .cov-cell { width: 7px; height: 14px; }
                            .cov-grid.yearly .cov-cell { width: 12px; } }

/* --- researching a property elsewhere --------------------------------------
   Easements, septic and well are not in this warehouse and these rows never
   link inward, so they are deliberately NOT styled like the fact lists above
   them: a question, the offices that answer it, and what the answer costs.
   Reuses .maplink, which is already the site's "this leaves the page" button,
   rather than inventing a second one -- a reader who has learnt that an
   outlined pill with an arrow leaves the site should not have to learn it
   twice on the same page. */
.research-row { padding: 12px 0; border-top: 1px solid var(--line); }
.research-row:first-child { border-top: 0; padding-top: 0; }
.research-row h3 { font-size: 13px; margin: 0 0 8px; }
.research-row .maplinks { margin-bottom: 6px; }
.research-note { margin: 0; font-size: 12px; color: var(--muted);
                 max-width: 72ch; line-height: 1.5; }

/* --- the trail --------------------------------------------------------------
   The session's path through the graph, as a chain: locality > parcel >
   school > person. A dedicated LEFT rail where the window affords one --
   the page shifts over rather than being covered, so the trail can stay
   open while reading. On a narrow window the same panel overlays instead,
   because a rail on a phone would cost a third of the page. The chip in
   the corner is the handle either way. Entity pages only, session-scoped
   -- see trailRecord in app.js. */
#trail-chip {
  position: fixed; left: 16px; bottom: 16px; z-index: 20;
  padding: 6px 14px; border-radius: 999px; font-size: 12px; font-weight: 600;
  background: var(--panel); color: var(--accent);
  border: 1px solid var(--line); box-shadow: var(--shadow); cursor: pointer;
}
#trail-chip:hover { border-color: var(--accent); filter: none; }
#trail {
  position: fixed; left: 16px; bottom: 56px; z-index: 20;
  width: 250px; max-height: min(480px, calc(100vh - 140px));
  overflow-y: auto; overscroll-behavior: contain;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 10px; box-shadow: 0 4px 18px rgba(16, 32, 47, 0.14);
  padding: 10px 12px;
}
/* Docked: a full-height rail, not a floating card. The body class is set by
   the same toggle that shows the panel, so open-ness has ONE source of truth
   and the media query only decides how open LOOKS. Open means open until the
   reader closes it -- the rail does not blink away between pages or when a
   chain is empty. */
@media (min-width: 992px) {
  body.trail-open #trail {
    top: 106px; bottom: 0; left: 0; max-height: none;
    border-radius: 0 10px 0 0; border-left: 0; border-bottom: 0;
    box-shadow: var(--shadow);
  }
  body.trail-open main {
    margin-left: 266px;
    max-width: min(1180px, calc(100vw - 286px));
  }
  body.trail-open #trail-chip { left: 266px; }
}

/* One chain per journey. The current chain draws expanded; earlier ones fold
   to a summary line, because a trail you finished is context, not the thing
   you are following now. */
.trail-chain { margin-bottom: 10px; }
details.trail-chain > summary {
  cursor: pointer; font-size: 11.5px; color: var(--muted);
  padding: 3px 0; list-style: none;
}
details.trail-chain > summary::before { content: '▸ '; font-size: 9px; }
details.trail-chain[open] > summary::before { content: '▾ '; }
details.trail-chain > summary:hover { color: var(--accent); }
.trail-chain-label {
  font-size: 10px; color: var(--muted); text-transform: uppercase;
  letter-spacing: 0.05em; margin: 0 0 2px;
}
.trail-empty { color: var(--muted); font-size: 12px; font-style: italic; }
/* The close control lives at the panel's own foot -- the chip is only the
   CLOSED panel's handle and hides while the rail shows. */
.trail-foot {
  margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--line);
}
.trail-foot button {
  width: 100%; padding: 4px 0; font-size: 11.5px; cursor: pointer;
  background: transparent; color: var(--muted);
  border: 1px solid var(--line); border-radius: 6px;
}
.trail-foot button:hover { color: var(--accent); border-color: var(--accent);
                           filter: none; }
.trail-head {
  display: flex; justify-content: space-between; align-items: center;
  gap: 8px; flex-wrap: wrap;
  font-size: 12.5px; margin-bottom: 8px;
}
/* Actions are pills, not bare words: "clear" destroys every chain and a
   control that destructive should not look like a label. The dashed border
   is the site's existing "this one is different" mark. */
.trail-actions { display: inline-flex; gap: 4px; }
.trail-head button {
  background: transparent; border: 1px solid var(--line);
  border-radius: 20px; padding: 2px 9px;
  color: var(--muted); font-size: 11px; cursor: pointer;
}
.trail-head button:hover {
  color: var(--accent); border-color: var(--accent); filter: none;
}
.trail-head button.danger { border-style: dashed; }
.trail-head button.danger:hover { color: var(--fail); border-color: var(--fail); }
#trail ol { list-style: none; margin: 0; padding: 0; }
#trail li {
  padding: 4px 0 4px 14px; position: relative;
  font-size: 12.5px; line-height: 1.35;
}
/* The chain drawn as a line down the left, a dot per step: the trail reads
   as one path, not a list of bookmarks. */
#trail li::before {
  content: ''; position: absolute; left: 3px; top: 0; bottom: 0;
  width: 1px; background: var(--line);
}
#trail li:first-child::before { top: 11px; }
#trail li:last-child::before { bottom: auto; height: 11px; }
#trail li::after {
  content: ''; position: absolute; left: 1px; top: 9px;
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--muted);
}
#trail li.here::after { background: var(--accent); }
#trail li.here a { font-weight: 650; }
/* Steps ahead of the position: still on the path, dimmed until a branch
   prunes them -- the forward half of the history. */
#trail li.ahead { opacity: 0.45; }
.trail-kind {
  display: inline-block; color: var(--muted); font-size: 10px;
  text-transform: uppercase; letter-spacing: 0.05em; margin-right: 4px;
}

/* --- the eviction pipeline -------------------------------------------------
   A funnel of RECORDED events: bars scale to the first stage because the
   whole point is what share of filings survives to each step. Divs, not
   SVG, so the bars follow the theme for free. */
.funnel { margin: 6px 0 14px; }
.funnel-row {
  display: grid; grid-template-columns: 170px 1fr 90px 52px;
  gap: 10px; align-items: center; padding: 3px 0; font-size: 12.5px;
}
.funnel-label { color: var(--muted); text-align: right; }
.funnel-bar {
  height: 18px; background: var(--bg); border-radius: 4px;
  border: 1px solid var(--line); overflow: hidden;
}
.funnel-fill {
  display: block; height: 100%; background: var(--accent); opacity: 0.75;
  border-radius: 3px 0 0 3px;
}
.funnel-n { text-align: right; font-variant-numeric: tabular-nums; }
.funnel-pct {
  color: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums;
}
.ev-map-block { margin-top: 14px; }
.ev-map { max-width: 720px; }
.ev-map-block input[type="range"] { width: 260px; accent-color: var(--accent); }
@media (max-width: 640px) {
  .funnel-row { grid-template-columns: 110px 1fr 70px 44px; font-size: 11.5px; }
}

/* --- collector actions ----------------------------------------------------
   The only buttons on the site that change what the machine is doing, so they
   are deliberately quiet: this is a status page first, and a row of loud
   controls would read as the point of it. Sized to sit inside a table cell
   without changing the row height. */
button.act {
  font: inherit; font-size: 11.5px; line-height: 1;
  padding: 4px 8px; margin: 0 2px 0 0;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--bg); color: var(--ink); cursor: pointer;
}
button.act:hover { border-color: var(--ink); }
button.act:disabled { opacity: 0.5; cursor: default; }

/* --- data centre pins ------------------------------------------------------

   The state outline is scaffolding here rather than the subject: nothing is
   choropleth-shaded, so the localities stay at t0 and the pins carry all the
   ink. `.dc-map .loc` drops the pointer cursor the overview map sets, because
   on this map a locality is not clickable and a cursor that says otherwise is
   a promise the page does not keep. */
.dc-map .loc { cursor: default; }
.dc-map .loc.highlighted {
  fill: rgba(79, 140, 255, 0.14) !important;
  stroke: rgba(79, 140, 255, 0.6) !important;
}
.dc-town circle { fill: var(--muted); fill-opacity: 0.85; }
.dc-town text {
  fill: var(--muted); font-weight: 600;
  paint-order: stroke; stroke: var(--panel); stroke-width: 2px;
  stroke-linejoin: round;
}
.dc-pin {
  cursor: pointer;
}
.dc-dot {
  vector-effect: non-scaling-stroke;
  transition: r 0.12s ease;
}
.dc-dot.on {
  fill: #f59e0b !important;
  fill-opacity: 0.95 !important;
  stroke: #ffeb3b !important;
  stroke-width: 2.5px !important;
  filter: drop-shadow(0 0 6px rgba(245, 158, 11, 0.8)) !important;
}
.dc-hit, .cl-hit { fill: transparent; cursor: pointer; }
.dc-key, .cl-key {
  display: inline-block; width: 11px; height: 11px; border-radius: 50%;
  vertical-align: -1px; margin-right: 5px;
}
.dc-key.current   { background: var(--accent, #3b82f6); }
.dc-key.candidate { background: #8b5cf6; }

.dc-map, .cl-map {
  cursor: grab;
  touch-action: none;
}
.dc-map:active, .cl-map:active { cursor: grabbing; }
.dc-zoom-at, .cl-zoom-at {
  display: inline-block; margin-left: 6px; font-size: 12px;
  color: var(--muted); font-variant-numeric: tabular-nums;
}
.dc-step, .cl-step {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
}
.dc-step button, .cl-step button {
  min-width: 32px; font-size: 16px; font-weight: 700; line-height: 1; padding: 3px 10px; cursor: pointer;
}

/* --- Split map + facts layout (Compact & Responsive) -------------------- */
.map-split-frame {
  position: relative;
  margin: 16px 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--panel);
}
.map-split-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  align-items: stretch;
}
@media (max-width: 880px) {
  .map-split-grid {
    grid-template-columns: 1fr;
  }
  .map-split-panel {
    border-left: none !important;
    border-top: 1px solid var(--line);
  }
}
.map-split-map-col {
  position: relative;
  display: flex;
  flex-direction: column;
  background: rgba(0, 0, 0, 0.18);
}
.map-split-svg {
  width: 100%;
  height: 290px;
  max-height: 310px;
  display: block;
}
.map-split-panel {
  padding: 14px 18px;
  background: rgba(255, 255, 255, 0.015);
  border-left: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  justify-content: center;
  max-height: 360px;
  overflow-y: auto;
}
.map-split-legend {
  padding: 6px 14px;
  background: rgba(0, 0, 0, 0.35);
  border-top: 1px solid var(--line);
  display: flex;
  gap: 14px;
  align-items: center;
  flex-wrap: wrap;
  font-size: 11.5px;
}

/* --- Fund Flow Traversal & Sankey Engine --------------------------------- */

.fund-flow-page {
  display: flex;
  flex-direction: column;
  gap: 20px;
  margin-bottom: 40px;
}

.flow-control-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 12px 18px;
}

.flow-control-group {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.flow-label {
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.flow-mode-switch {
  display: inline-flex;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 2px;
}

.flow-mode-btn {
  background: transparent;
  border: none;
  padding: 6px 14px;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--muted);
  cursor: pointer;
  border-radius: 4px;
  transition: all 0.15s ease;
}

.flow-mode-btn:hover {
  color: var(--ink);
}

.flow-mode-btn.active {
  background: var(--panel);
  color: var(--accent);
  font-weight: 600;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}

.flow-select {
  padding: 6px 12px;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--ink);
  border-radius: 6px;
  font-size: 13px;
  outline: none;
  cursor: pointer;
}

.flow-select:focus {
  border-color: var(--accent);
}

.flow-trail-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: var(--accent-soft);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 8px 14px;
  font-size: 13px;
}

.trail-crumbs {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.trail-crumb {
  background: var(--panel);
  border: 1px solid var(--line);
  padding: 3px 10px;
  border-radius: 4px;
  color: var(--accent);
  font-weight: 500;
  cursor: pointer;
  font-size: 12px;
  transition: background 0.15s ease;
}

.trail-crumb:hover {
  background: var(--line);
}

.trail-crumb.current {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
  cursor: default;
}

.trail-sep {
  color: var(--muted);
  font-size: 11px;
}

.flow-reset-btn {
  background: var(--panel);
  border: 1px solid var(--line);
  padding: 3px 10px;
  border-radius: 4px;
  font-size: 12px;
  color: var(--muted);
  cursor: pointer;
}

.flow-reset-btn:hover {
  color: var(--ink);
  border-color: var(--muted);
}

/* Canvas & SVG */
.flow-canvas-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 16px;
  position: relative;
  overflow: hidden;
  box-shadow: 0 1px 4px rgba(0,0,0,0.03);
}

.flow-canvas-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--line);
}

.flow-canvas-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}

.flow-canvas-hint {
  font-size: 12px;
  color: var(--muted);
}

.sankey-svg-wrap {
  width: 100%;
  overflow-x: auto;
}

.sankey-svg {
  display: block;
  width: 100%;
  min-width: 860px;
  height: 520px;
  user-select: none;
}

.sankey-node {
  cursor: pointer;
  transition: transform 0.15s ease, opacity 0.15s ease;
}

.sankey-node:hover rect {
  filter: brightness(1.08);
  stroke: var(--accent);
  stroke-width: 1.5px;
}

.sankey-node.selected rect {
  stroke: var(--ink);
  stroke-width: 2px;
  filter: brightness(1.12);
}

.sankey-node rect {
  rx: 4px;
  ry: 4px;
  transition: all 0.2s ease;
}

.sankey-node text {
  font-family: inherit;
  pointer-events: none;
}

.node-title {
  font-size: 11.5px;
  font-weight: 600;
  fill: var(--ink);
}

.node-sub {
  font-size: 10.5px;
  fill: #ffffff;
}

.macro-node .node-title,
.macro-node .node-child-title {
  fill: #ffffff;
}

.sankey-ribbon {
  fill-opacity: 0.35;
  stroke: none;
  cursor: pointer;
  transition: fill-opacity 0.2s ease, filter 0.2s ease;
}

.sankey-ribbon:hover {
  fill-opacity: 0.75;
  filter: drop-shadow(0 2px 4px rgba(0,0,0,0.15));
}

.sankey-ribbon.highlighted {
  fill-opacity: 0.85;
}

.sankey-ribbon.dimmed {
  fill-opacity: 0.08;
}

/* Tooltip */
.sankey-tooltip {
  /* fixed, not absolute: this sits inside .flow-canvas-card, which is a
     positioned ancestor, so an absolute tooltip placed at pageX/pageY landed
     hundreds of pixels from the cursor once the page was scrolled. */
  position: fixed;
  pointer-events: none;
  background: var(--navy);
  color: #fff;
  padding: 8px 12px;
  border-radius: 6px;
  font-size: 12px;
  line-height: 1.4;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
  z-index: 100;
  display: none;
  max-width: 280px;
}

.sankey-tooltip-title {
  font-weight: 600;
  margin-bottom: 2px;
  color: #fff;
}

.sankey-tooltip-val {
  font-size: 13px;
  color: #79c0ff;
  font-weight: 600;
}

.sankey-tooltip-note {
  font-size: 10.5px;
  color: #a5d6ff;
  margin-top: 4px;
}

/* Evidence Dossier */
.evidence-dossier {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.dossier-header {
  padding: 14px 18px;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}

.dossier-title {
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
  display: flex;
  align-items: center;
  gap: 8px;
}

.dossier-scope-badge {
  font-size: 12px;
  padding: 2px 8px;
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: 4px;
  font-weight: 500;
}

.dossier-stat-strip {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
}

.dossier-stat-item {
  font-size: 12px;
  color: var(--muted);
}

.dossier-stat-item strong {
  color: var(--ink);
  font-size: 13px;
}

.dossier-tabs {
  display: flex;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  padding: 0 14px;
  gap: 4px;
  overflow-x: auto;
}

.dossier-tab-btn {
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 10px 14px;
  font-size: 13px;
  font-weight: 500;
  color: var(--muted);
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 6px;
  transition: all 0.15s ease;
  white-space: nowrap;
}

.dossier-tab-btn:hover {
  color: var(--ink);
}

.dossier-tab-btn.active {
  color: var(--accent);
  border-bottom-color: var(--accent);
  font-weight: 600;
}

.dossier-tab-badge {
  background: var(--bg);
  border: 1px solid var(--line);
  font-size: 11px;
  padding: 1px 6px;
  border-radius: 10px;
  color: var(--muted);
}

.dossier-tab-btn.active .dossier-tab-badge {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: transparent;
}

.dossier-content {
  padding: 16px;
  min-height: 240px;
  max-height: 480px;
  overflow-y: auto;
}

.dossier-empty {
  padding: 40px 20px;
  text-align: center;
  color: var(--muted);
  font-size: 13px;
}

/* Capital River Inflow & Outflow Expansion Styling */
.flow-river-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 6px 12px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 6px;
  margin-bottom: 12px;
}

.flow-river-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.flow-river-btn {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 3px 9px;
  font-size: 11.5px;
  font-weight: 500;
  color: var(--ink);
  cursor: pointer;
  transition: all 0.15s ease;
}

.flow-river-btn:hover {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: var(--accent);
}

.river-expand-badge {
  font-size: 10px;
  font-weight: 700;
  cursor: pointer;
  padding: 2px 6px;
  border-radius: 4px;
  user-select: none;
}

.river-expand-badge:hover {
  filter: brightness(1.2);
}

.node-parent-header {
  font-size: 11px;
  font-weight: 700;
  fill: var(--ink);
}

.node-child-title {
  font-size: 11px;
  font-weight: 500;
  fill: var(--ink);
}

.node-child-sub {
  font-size: 10px;
  fill: #ffffff;
}

/* Multi-Level Traversal Transitions & Controls */
.flow-traversal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}

.flow-back-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--panel);
  border: 1px solid var(--accent);
  color: var(--accent);
  padding: 5px 12px;
  border-radius: 5px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}

.flow-back-btn:hover {
  background: var(--accent);
  color: #ffffff;
  transform: translateX(-2px);
}

.flow-level-indicator {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--muted);
}

.flow-level-pill {
  padding: 2px 8px;
  border-radius: 12px;
  font-size: 11px;
  font-weight: 700;
  background: var(--accent-soft);
  color: var(--accent);
  border: 1px solid var(--line);
}

.flow-chart-wrapper {
  position: relative;
  transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.22s ease;
}

.flow-chart-zooming-in {
  transform: scale(1.04);
  opacity: 0.25;
}

.flow-chart-zooming-out {
  transform: scale(0.96);
  opacity: 0.25;
}

.sankey-ribbon {
  cursor: pointer;
  transition: fill-opacity 0.2s ease, filter 0.2s ease;
}

.sankey-ribbon:hover {
  fill-opacity: 0.96;
  filter: drop-shadow(0 0 10px rgba(121, 192, 255, 0.65));
}

.sankey-ribbon[data-reverse-target]:hover {
  filter: drop-shadow(0 0 12px rgba(46, 213, 115, 0.85));
}

.sankey-node[data-reverse-target] {
  cursor: pointer;
  transition: transform 0.2s ease, filter 0.2s ease;
}

.sankey-node[data-reverse-target]:hover {
  transform: translateX(-4px);
  filter: drop-shadow(0 0 10px rgba(46, 213, 115, 0.6));
}

.sankey-drill-badge {
  font-size: 9.5px;
  font-weight: 700;
  fill: var(--accent);
  cursor: pointer;
}

.sankey-drill-glow {
  stroke: var(--accent);
  stroke-width: 1.5;
}

/* --- Bracket / Family Tree Horizontal Sliding Traversal --- */

.bracket-camera-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
  padding: 8px 12px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 6px;
  flex-wrap: wrap;
  gap: 8px;
}

.bracket-step-pills {
  display: flex;
  align-items: center;
  gap: 8px;
  overflow-x: auto;
}

.bracket-step-pill {
  padding: 4px 10px;
  border-radius: 14px;
  font-size: 11.5px;
  font-weight: 600;
  background: var(--bg);
  border: 1px solid var(--line);
  color: var(--ink);
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 4px;
  transition: all 0.15s ease;
  white-space: nowrap;
}

.bracket-step-pill:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.bracket-step-pill.active {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}

.bracket-nav-btn {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 4px 10px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  transition: all 0.15s ease;
}

.bracket-nav-btn:hover {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: var(--accent);
}

.bracket-tree-wrapper {
  position: relative;
  width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  padding: 18px 12px 28px 12px;
  background: var(--bg);
  border-radius: 8px;
  border: 1px solid var(--line);
  scroll-behavior: smooth;
}

.bracket-tree-track {
  display: flex;
  align-items: flex-start;
  gap: 36px;
  min-width: max-content;
  position: relative;
  padding-right: 20px;
}

.bracket-column {
  width: 350px;
  min-width: 350px;
  max-width: 350px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
  display: flex;
  flex-direction: column;
  position: relative;
  transition: transform 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease;
}

.bracket-column.active-tier {
  border-color: var(--accent);
  box-shadow: 0 6px 22px rgba(15, 107, 75, 0.16);
}

.bracket-column-header {
  padding: 12px 14px;
  border-bottom: 1px solid var(--line);
  background: var(--bg);
  border-top-left-radius: 8px;
  border-top-right-radius: 8px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.bracket-header-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.bracket-tier-badge {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--accent);
  background: var(--accent-soft);
  padding: 2px 8px;
  border-radius: 10px;
}

.bracket-column-title {
  font-size: 14.5px;
  font-weight: 700;
  color: var(--ink);
  margin-top: 2px;
}

/* Explicit Money In vs Money Out Comparison Strip */
.in-out-balance-strip {
  padding: 12px 14px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.in-out-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

.in-out-box {
  padding: 8px 10px;
  border-radius: 6px;
  display: flex;
  flex-direction: column;
}

.in-box {
  background: rgba(15, 107, 75, 0.08);
  border: 1px solid rgba(15, 107, 75, 0.22);
}

.out-box {
  background: rgba(183, 121, 31, 0.08);
  border: 1px solid rgba(183, 121, 31, 0.22);
}

.in-out-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.in-box .in-out-label {
  color: #0f6b4b;
}

.out-box .in-out-label {
  color: #b7791f;
}

.in-out-val {
  font-size: 14.5px;
  font-weight: 700;
  color: var(--ink);
  margin-top: 2px;
}

.in-out-net-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  padding: 4px 8px;
  background: var(--bg);
  border-radius: 4px;
}

.in-out-net-val {
  font-weight: 700;
  color: var(--ink);
}

/* Node List / Branches */
.bracket-section-label {
  font-size: 10.5px;
  font-weight: 700;
  color: var(--muted);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 10px 14px 4px 14px;
}

.bracket-items-list {
  padding: 4px 10px 14px 10px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 420px;
  overflow-y: auto;
}

.bracket-node-card {
  padding: 8px 10px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  cursor: pointer;
  transition: all 0.15s ease;
  position: relative;
}

.bracket-node-card:hover {
  border-color: var(--accent);
  background: var(--accent-soft);
  transform: translateX(2px);
}

.bracket-node-card.active-branch {
  background: #16416f;
  border-color: #17518c;
  color: #ffffff !important;
  box-shadow: 0 2px 8px rgba(22, 65, 111, 0.25);
}

.bracket-node-card.active-branch .bracket-node-title,
.bracket-node-card.active-branch .bracket-node-sub {
  color: #ffffff !important;
}

.bracket-node-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow: hidden;
  flex: 1;
}

.bracket-node-title {
  font-size: 12px;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.bracket-node-sub {
  font-size: 11px;
  font-weight: 700;
  color: var(--muted);
}

.bracket-node-arrow {
  font-size: 12px;
  font-weight: 700;
  color: var(--accent);
  margin-left: 8px;
  flex-shrink: 0;
}

.bracket-node-card.active-branch .bracket-node-arrow {
  color: #ffeb3b;
}

/* Bracket Lineage Indicator between columns */
.bracket-column-bridge {
  position: absolute;
  top: 50%;
  right: -36px;
  width: 36px;
  height: 2px;
  background: var(--accent);
  z-index: 2;
}

.bracket-column-bridge::after {
  content: '';
  position: absolute;
  right: 0;
  top: -3px;
  width: 7px;
  height: 7px;
  border-top: 2px solid var(--accent);
  border-right: 2px solid var(--accent);
  transform: rotate(45deg);
}

/* --- Universe Model: Orbital Constellation & Sankey Energy Streams --- */

.universe-canvas-wrap {
  position: relative;
  width: 100%;
  min-height: 580px;
  background: radial-gradient(circle at 50% 50%, #0d1b2a 0%, #060c14 100%);
  border-radius: 8px;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.12);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.35);
}

.universe-hud-bar {
  position: absolute;
  top: 12px;
  left: 14px;
  right: 14px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  pointer-events: none;
  z-index: 10;
  flex-wrap: wrap;
  gap: 8px;
}

.universe-hud-left,
.universe-hud-right {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.universe-system-pill {
  background: rgba(13, 27, 42, 0.85);
  border: 1px solid rgba(121, 192, 255, 0.4);
  color: #79c0ff;
  padding: 4px 12px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 700;
  backdrop-filter: blur(8px);
  display: flex;
  align-items: center;
  gap: 6px;
}

.universe-flux-badge {
  background: rgba(13, 27, 42, 0.85);
  border: 1px solid rgba(255, 255, 255, 0.15);
  color: #ffffff;
  padding: 4px 10px;
  border-radius: 16px;
  font-size: 11px;
  font-weight: 600;
  backdrop-filter: blur(8px);
  display: flex;
  align-items: center;
  gap: 6px;
}

.universe-warp-btn {
  background: rgba(22, 65, 111, 0.85);
  border: 1px solid rgba(121, 192, 255, 0.6);
  color: #ffffff;
  padding: 4px 12px;
  border-radius: 6px;
  font-size: 11.5px;
  font-weight: 700;
  cursor: pointer;
  backdrop-filter: blur(8px);
  transition: all 0.2s ease;
}

.universe-warp-btn:hover {
  background: #1f6feb;
  box-shadow: 0 0 14px rgba(31, 111, 235, 0.6);
  transform: translateY(-1px);
}

.universe-core-node {
  cursor: pointer;
  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.universe-core-node:hover {
  filter: drop-shadow(0 0 20px rgba(121, 192, 255, 0.8));
}

/* --- Unified Continuous Macro River Canvas --- */

.macro-viewport-container {
  position: relative;
  width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: inset 0 2px 10px rgba(0,0,0,0.12);
  scrollbar-width: thin;
  scrollbar-color: var(--accent) var(--panel);
}

.macro-viewport-container::-webkit-scrollbar {
  height: 10px;
}
.macro-viewport-container::-webkit-scrollbar-thumb {
  background: var(--accent);
  border-radius: 5px;
}

.macro-camera-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 12px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
  gap: 8px;
}

.macro-stage-pills {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.macro-stage-pill {
  background: rgba(255,255,255,0.06);
  border: 1px solid var(--line);
  color: var(--text);
  padding: 5px 12px;
  border-radius: 16px;
  font-size: 11.5px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s ease;
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

.macro-stage-pill:hover {
  background: rgba(255,255,255,0.14);
  border-color: var(--accent);
  color: #fff;
}

.macro-stage-pill.active {
  background: var(--accent);
  color: #0b1d30;
  border-color: var(--accent);
  font-weight: 700;
  box-shadow: 0 0 10px rgba(121, 192, 255, 0.4);
}

.macro-zoom-controls {
  display: flex;
  align-items: center;
  gap: 6px;
}

.macro-zoom-btn {
  background: rgba(255,255,255,0.06);
  border: 1px solid var(--line);
  color: var(--text);
  padding: 4px 10px;
  border-radius: 4px;
  font-size: 11.5px;
  cursor: pointer;
  transition: all 0.15s ease;
}

.macro-zoom-btn:hover {
  background: rgba(255,255,255,0.18);
  color: #fff;
}

.macro-river-svg {
  display: block;
  transition: transform 0.25s ease-out;
  transform-origin: top left;
}

.macro-stage-title {
  paint-order: stroke;
  stroke: var(--panel);
  stroke-width: 6px;
  stroke-linejoin: round;
}

.macro-stage-backdrop {
  fill: rgba(255, 255, 255, 0.015);
  stroke: rgba(255, 255, 255, 0.08);
  stroke-dasharray: 4, 4;
}

.macro-bridge-stream {
  cursor: pointer;
  transition: filter 0.2s ease, fill-opacity 0.2s ease;
}

.macro-bridge-stream:hover {
  filter: drop-shadow(0 2px 6px rgba(46, 213, 115, 0.4));
}

/* Refined, softened highlighting: only styles shapes, NEVER outlines text */
.macro-ribbon.macro-stream-lit {
  stroke: #38bdf8 !important;
  stroke-width: 1.5px !important;
  fill-opacity: 0.95 !important;
  opacity: 1.0 !important;
}

/* The vertical layout's pipes are strokes, so lighting one must keep its width */
.macro-ribbon.macro-vlink.macro-stream-lit {
  stroke-width: var(--w) !important;
  stroke-opacity: 1 !important;
}

.macro-vlink { cursor: pointer; }

.macro-node.macro-stream-lit > rect {
  stroke: #38bdf8 !important;
  stroke-width: 2px !important;
  opacity: 1.0 !important;
}

/* Ensure text inside nodes NEVER receives strokes or glow */
.macro-node text,
.macro-node.macro-stream-lit text,
.macro-node.macro-stream-dimmed text {
  stroke: none !important;
  stroke-width: 0 !important;
  filter: none !important;
}

.macro-stream-dimmed {
  opacity: 0.38 !important;
  transition: opacity 0.15s ease;
}

.universe-satellite-node {
  cursor: pointer;
  transition: transform 0.2s ease, filter 0.2s ease;
}

.universe-satellite-node:hover {
  transform: scale(1.03);
  filter: drop-shadow(0 0 12px rgba(255, 235, 59, 0.8));
}

.universe-satellite-node.highlighted {
  filter: drop-shadow(0 0 16px #ffeb3b);
}

.universe-ribbon {
  fill-opacity: 0.45;
  transition: fill-opacity 0.2s ease, stroke-width 0.2s ease;
}

.universe-ribbon:hover,
.universe-ribbon.highlighted {
  fill-opacity: 0.88;
  filter: drop-shadow(0 0 8px rgba(255, 255, 255, 0.5));
}





/* --- Fund flow: the locality / department chooser ------------------------ */
/* One popup for both. Fixed-position so pan and zoom on the SVG cannot
   carry it away; the list is measured (133 localities, the portal's
   departments), never typed into the page. */
.flow-picker {
  position: fixed;
  z-index: 300;
  width: 320px;
  max-height: 400px;
  display: flex;
  flex-direction: column;
  background: var(--panel, #10233f);
  border: 1px solid rgba(148, 163, 184, 0.45);
  border-radius: 10px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
  overflow: hidden;
}
.flow-picker-head {
  padding: 10px 12px 6px;
  font-size: 12px;
  font-weight: 700;
  color: var(--muted, #94a3b8);
}
.flow-picker-filter {
  margin: 0 10px 8px;
  padding: 6px 10px;
  font-size: 13px;
  color: inherit;
  background: rgba(148, 163, 184, 0.12);
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 6px;
  outline: none;
}
.flow-picker-list {
  overflow-y: auto;
  padding: 0 6px 8px;
}
.flow-picker-item {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 8px;
  font-size: 12.5px;
  border-radius: 6px;
  cursor: pointer;
}
.flow-picker-item:hover { background: rgba(56, 189, 248, 0.15); }
.flow-picker-item.current {
  background: rgba(46, 213, 115, 0.14);
  font-weight: 700;
}
.flow-picker-sub {
  color: var(--muted, #94a3b8);
  font-size: 11.5px;
  white-space: nowrap;
}
.flow-picker-empty {
  padding: 10px;
  font-size: 12px;
  color: var(--muted, #94a3b8);
}

/* --- Fund flow: reconciliation flags ------------------------------------- */
/* A red outline in the diagram says WHICH node disagrees; this panel says by
   how much, because "these do not add up" is only useful with the two
   numbers that did not. */
.flow-check-panel {
  margin: 0 0 10px;
  padding: 10px 12px;
  border-radius: 8px;
  font-size: 12.5px;
  border: 1px solid transparent;
}
.flow-check-panel.ok {
  background: rgba(46, 213, 115, 0.08);
  border-color: rgba(46, 213, 115, 0.35);
  color: var(--muted, #94a3b8);
}
.flow-check-panel.warn {
  background: rgba(251, 191, 36, 0.09);
  border-color: rgba(251, 191, 36, 0.45);
}
.flow-check-panel.error {
  background: rgba(248, 113, 113, 0.09);
  border-color: rgba(248, 113, 113, 0.55);
}
.flow-check-head {
  font-weight: 700;
  margin-bottom: 8px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.flow-check-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  font-size: 12px;
  font-weight: 800;
  flex: none;
}
.flow-check-badge.ok    { background: rgba(46, 213, 115, 0.25);  color: #2ed573; }
.flow-check-badge.warn  { background: rgba(251, 191, 36, 0.25);  color: #fbbf24; }
.flow-check-badge.error { background: rgba(248, 113, 113, 0.25); color: #f87171; }
.flow-check-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
}
.flow-check-table th {
  text-align: left;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted, #94a3b8);
  padding: 3px 8px 3px 0;
  border-bottom: 1px solid rgba(148, 163, 184, 0.25);
}
.flow-check-table td {
  padding: 4px 8px 4px 0;
  vertical-align: top;
  border-bottom: 1px solid rgba(148, 163, 184, 0.12);
}
.flow-check-table .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.flow-check-table .muted { color: var(--muted, #94a3b8); }
.flow-check-table tr.error td:first-child { border-left: 3px solid #f87171; padding-left: 7px; }
.flow-check-table tr.warn  td:first-child { border-left: 3px solid #fbbf24; padding-left: 7px; }
.flow-check-table tr.ok    td:first-child { border-left: 3px solid #2ed573; padding-left: 7px; }
.flow-check-panel { overflow-x: auto; }
.flow-check-src {
  font-size: 10.5px;
  color: var(--muted, #94a3b8);
  white-space: normal;
  min-width: 110px;
}
.flow-check-agree { margin-top: 8px; }
.flow-check-more { margin-top: 8px; font-size: 12px; }
.audit-panel > summary { cursor: pointer; list-style: none; margin-bottom: 0; }
.audit-panel[open] > summary { margin-bottom: 8px; }
.audit-panel > summary::-webkit-details-marker { display: none; }
.flow-check-agree summary { cursor: pointer; font-weight: 600; color: #2ed573; }
.flow-check-agree .flow-check-table { margin-top: 6px; }
/* a phone gets one card per comparison instead of six squeezed columns */
@media (max-width: 640px) {
  .flow-check-table thead { display: none; }
  .flow-check-table tr { display: block; padding: 6px 0 6px 9px; border-bottom: 1px solid rgba(148, 163, 184, 0.25); }
  .flow-check-table tr.error { border-left: 3px solid #f87171; }
  .flow-check-table tr.warn  { border-left: 3px solid #fbbf24; }
  .flow-check-table tr.ok    { border-left: 3px solid #2ed573; }
  .flow-check-table tr.error td:first-child,
  .flow-check-table tr.warn td:first-child,
  .flow-check-table tr.ok td:first-child { border-left: none; padding-left: 96px; }
  .flow-check-table td,
  .flow-check-table td.num,
  .flow-check-table tr td:first-child {
    display: block; position: relative; border: none; padding: 2px 0 2px 96px;
    text-align: left; white-space: normal;
  }
  .flow-check-table td:empty { display: none; }
  .flow-check-table td::before {
    content: attr(data-label); position: absolute; left: 0; top: 4px; width: 90px;
    font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted, #94a3b8);
  }
}

/* #/audit -- reuses the check table above, so a phone gets the same cards */
.audit-tabs { display: flex; gap: 6px; margin: 10px 0 14px; border-bottom: 1px solid rgba(148, 163, 184, 0.3); }
.audit-tabs a { padding: 6px 12px; border-radius: 6px 6px 0 0; text-decoration: none; color: var(--muted, #94a3b8); }
.audit-tabs a.active { color: inherit; font-weight: 700; border: 1px solid rgba(148, 163, 184, 0.3); border-bottom-color: transparent; margin-bottom: -1px; background: var(--panel, transparent); }
.audit-filters { display: flex; flex-wrap: wrap; gap: 8px 14px; margin: 6px 0 10px; }
.audit-filter { display: flex; flex-direction: column; font-size: 11px; color: var(--muted, #94a3b8); gap: 2px; }
.audit-filter select, .audit-filter input { font-size: 13px; padding: 3px 6px; max-width: 260px; }
.audit-level { display: inline-block; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; padding: 1px 6px; border-radius: 9px; margin-right: 4px; }
.audit-level.error { background: rgba(248, 113, 113, 0.2); color: #dc2626; }
.audit-level.warn  { background: rgba(251, 191, 36, 0.25); color: #b45309; }
.audit-level.ok    { background: rgba(46, 213, 115, 0.2); color: #15803d; }
.audit-candidates { white-space: normal; }
.audit-kinds tr.audit-active td { background: rgba(56, 189, 248, 0.1); }
.audit-pager { display: flex; gap: 14px; align-items: center; margin: 10px 0; }
.audit-kinds td.audit-ksum { display: none; }
@media (max-width: 640px) {
  .audit-kinds td.audit-kn { display: none; }
  .audit-kinds td.audit-ksum { display: block; }
}

/* the outlined node also pulses once so the eye finds it after a redraw */
.macro-node-bad rect { animation: flowFlagPulse 1.6s ease-out 1; }
@keyframes flowFlagPulse {
  0%   { stroke-opacity: 0.35; }
  50%  { stroke-opacity: 1; }
  100% { stroke-opacity: 1; }
}
.sankey-tooltip-flag {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid rgba(248, 113, 113, 0.4);
  color: #fca5a5;
  font-size: 11.5px;
  max-width: 340px;
  white-space: normal;
}

/* A filter that cannot reach this tab's data says so, rather than letting
   the rows read as though they were narrowed. */
.dossier-scope-note {
  margin: 0 0 10px;
  padding: 7px 10px;
  border-radius: 6px;
  font-size: 12px;
  background: rgba(251, 191, 36, 0.1);
  border: 1px solid rgba(251, 191, 36, 0.4);
  color: var(--fg, #e2e8f0);
}

/* Stage 0's bypass channel. It reads as a pipe rather than a Sankey ribbon
   on purpose: the levy does not divide on its way to the county, and a
   tapering ribbon would imply it does. */
.s0-bypass-path {
  transition: stroke-opacity 0.15s ease;
  stroke-dasharray: 1 0;
}
.s0-bypass:hover .s0-bypass-path { stroke-opacity: 0.95; }
.sankey-tooltip-sub {
  margin-top: 5px;
  color: var(--muted, #94a3b8);
  font-size: 11.5px;
  max-width: 340px;
  white-space: normal;
}

/* --- Fund flow: what a click does --------------------------------------- */
/* A segmented pair rather than two loose pills: isolate and highlight are
   two answers to one question, and a reader should see which is armed. */
.macro-mode-group {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 6px 2px 8px;
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 999px;
  background: rgba(148, 163, 184, 0.06);
}
.macro-mode-label {
  font-size: 11px;
  font-weight: 700;
  color: var(--muted, #94a3b8);
  margin-right: 2px;
}
.macro-mode-btn {
  border: 1px solid transparent;
  background: transparent;
  color: var(--muted, #94a3b8);
  font-size: 11.5px;
  font-weight: 700;
  padding: 3px 10px;
  border-radius: 999px;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
.macro-mode-btn:hover { color: var(--fg, #e2e8f0); background: rgba(148, 163, 184, 0.14); }
.macro-mode-btn.active {
  background: rgba(56, 189, 248, 0.18);
  border-color: rgba(56, 189, 248, 0.55);
  color: #7dd3fc;
}

/* --- Fund flow: where each figure comes from ----------------------------- */
.prov-note {
  margin: 0 0 10px;
  padding: 8px 11px;
  border-radius: 6px;
  font-size: 12.5px;
  background: rgba(56, 189, 248, 0.08);
  border: 1px solid rgba(56, 189, 248, 0.3);
}
.prov-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.prov-table th {
  text-align: left;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted, #94a3b8);
  padding: 4px 10px 4px 0;
  border-bottom: 1px solid rgba(148, 163, 184, 0.3);
  white-space: nowrap;
}
.prov-table td {
  padding: 7px 10px 7px 0;
  vertical-align: top;
  border-bottom: 1px solid rgba(148, 163, 184, 0.12);
}
.prov-table .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* a measured row is anchored to a document, a derived one to arithmetic --
   the left rule is the fastest way to tell which you are reading */
.prov-table tr.measured td:first-child { border-left: 3px solid #2ed573; padding-left: 8px; }
.prov-table tr.derived  td:first-child { border-left: 3px solid #fbbf24; padding-left: 8px; }
.prov-stage { color: var(--muted, #94a3b8); white-space: nowrap; font-size: 11.5px; }
.prov-kind {
  display: inline-block;
  font-size: 10px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 1px 6px;
  border-radius: 999px;
  margin-bottom: 3px;
}
.prov-kind.measured { background: rgba(46, 213, 115, 0.18); color: #2ed573; }
.prov-kind.derived  { background: rgba(251, 191, 36, 0.18); color: #fbbf24; }
.prov-pub { font-weight: 600; }
.prov-doc { font-size: 11.5px; }
.prov-table-name { font-size: 11px; color: var(--muted, #94a3b8); margin-top: 2px; }
.prov-loc { font-size: 11.5px; line-height: 1.5; }
.prov-sub { font-size: 11px; color: var(--muted, #94a3b8); margin-top: 2px; }
.prov-quote {
  margin: 5px 0 0;
  padding: 5px 7px;
  background: rgba(148, 163, 184, 0.1);
  border-radius: 4px;
  font-size: 10.5px;
  white-space: pre-wrap;
  overflow-x: auto;
  color: var(--muted, #94a3b8);
}

/* --- Fund flow: the grouped toolbar -------------------------------------- */
/* Three questions -- what moves the view, what changes what is shown, what a
   click does -- were eighteen loose pills in one row. Each group is now a
   labelled segment so the question is visible before the answers. */
.macro-tool-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.macro-tool-row + .macro-tool-row { margin-top: 7px; }
.macro-tool-group {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 3px 5px 3px 9px;
  border: 1px solid rgba(148, 163, 184, 0.3);
  border-radius: 999px;
  background: rgba(148, 163, 184, 0.05);
}
.macro-tool-label {
  font-size: 10.5px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted, #94a3b8);
  margin-right: 4px;
  white-space: nowrap;
}
.macro-tool-btn {
  border: 1px solid transparent;
  background: transparent;
  color: var(--muted, #94a3b8);
  font-size: 11.5px;
  font-weight: 700;
  padding: 3px 9px;
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.12s ease, color 0.12s ease;
}
.macro-tool-btn:hover { color: var(--fg, #e2e8f0); background: rgba(148, 163, 184, 0.16); }
.macro-tool-btn.active {
  background: rgba(56, 189, 248, 0.18);
  border-color: rgba(56, 189, 248, 0.55);
  color: #7dd3fc;
}
.macro-tool-clears { display: inline-flex; gap: 6px; }
.macro-tool-btn.clear-isolate {
  border-color: rgba(248, 113, 113, 0.6);
  color: #f87171;
}
.macro-tool-btn.clear-highlight {
  border-color: rgba(56, 189, 248, 0.6);
  color: #38bdf8;
}

/* The flags-and-mascots reference. A grid of cards rather than a table: each
   entry is two pictures, a name, a caption and sometimes a stack of
   alternates, which is not a row of cells. */
.pixel-grid { display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
.pixel-card { border: 1px solid var(--line); border-radius: var(--radius);
  padding: 10px 12px; background: var(--panel); }
.pixel-row { display: flex; align-items: center; gap: 10px; }
.pixel-row + .pixel-row { margin-top: 8px; }
.pixel-alts { margin-top: 10px; padding-top: 8px;
  border-top: 1px dashed var(--line); }
.pixel-card .note-body { margin: 8px 0 0; font-size: 12.5px;
  color: var(--muted); }

/* --- architecture diagrams (#/architecture, ticket c133) -------------------
   Every colour here is a token, never a literal, because the diagrams are
   painted in both themes and an SVG fill is the one place a hardcoded hex
   survives a theme switch without looking broken until somebody flips it. */
.arch-scroll { overflow-x: auto; margin: 6px 0 4px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); }
.arch-svg { display: block; }
.arch-svg text { font-size: 10.5px; fill: var(--ink);
  font-family: inherit; pointer-events: none; }
.arch-col { font-size: 10px; fill: var(--muted); font-weight: 600;
  letter-spacing: .04em; text-transform: uppercase; }

.arch-node rect { fill: var(--accent-soft); stroke: var(--line); }
.arch-node { cursor: default; }
.arch-node-n { fill: var(--muted); font-size: 9.5px; }
.arch-node-warn rect { stroke: var(--warn); stroke-width: 1.4; }

.arch-edge { fill: none; stroke: var(--line); stroke-width: 1.1; }
.arch-edge-warn { stroke: var(--warn); stroke-dasharray: 4 3;
  stroke-width: 1.4; }
.arch-ribbon { fill: var(--accent); opacity: .16; }
.arch-flow .arch-node rect { fill: var(--accent); stroke: none; }

/* Hover isolates one path. Opacity rather than display, so the diagram does
   not reflow under the cursor -- a box that moves as you point at it is
   unusable, which the first version was. */
.arch-focused .arch-off { opacity: .12; }
.arch-focused .arch-on.arch-edge { stroke: var(--accent); stroke-width: 2; }
.arch-focused .arch-on.arch-ribbon { opacity: .5; }
.arch-focused .arch-on rect { stroke: var(--accent); stroke-width: 1.6; }

.arch-matrix { border-collapse: collapse; font-size: 11.5px;
  /* Hug the content. Without this the row-label column absorbs every
     spare pixel of a full-width page and pushes the grid itself to the
     far right, which reads as two unrelated things rather than a
     matrix. */
  width: max-content; }
.arch-matrix th { font-weight: 600; color: var(--muted); padding: 4px 7px;
  white-space: nowrap; }
.arch-matrix tbody th { text-align: right; padding-right: 11px; }
.arch-matrix thead th { text-align: left; vertical-align: bottom; }
.arch-matrix thead th span { display: inline-block; writing-mode: vertical-rl;
  transform: rotate(180deg); font-size: 10.5px; }
.arch-matrix td { border: 1px solid var(--line); width: 30px; height: 24px;
  text-align: center; font-variant-numeric: tabular-nums; color: var(--muted); }
.arch-matrix td.arch-hit { background: color-mix(in srgb,
  var(--accent) calc(var(--w) * 55%), transparent); color: var(--ink);
  font-weight: 600; }
.arch-matrix td.arch-self { background: var(--bg); border-color: var(--bg); }

.arch-layers { list-style: none; margin: 4px 0 12px; padding: 0;
  display: grid; gap: 6px; }
.arch-layers li { display: grid; gap: 10px; align-items: baseline;
  grid-template-columns: 74px 150px 1fr; padding: 6px 10px;
  border-left: 3px solid var(--accent); background: var(--panel); }
.arch-layer-n { font-size: 17px; font-weight: 640; text-align: right;
  font-variant-numeric: tabular-nums; }
.arch-layer-t { font-weight: 600; font-size: 12.5px; }
.arch-layer-w { color: var(--muted); font-size: 11.5px; }

.arch-bars { list-style: none; margin: 4px 0 10px; padding: 0;
  display: grid; gap: 4px; }
.arch-bars li { display: grid; align-items: center; gap: 10px;
  grid-template-columns: 120px 1fr 44px 170px; font-size: 12px; }
.arch-bar { height: 8px; border-radius: 4px; background: var(--accent-soft); }
.arch-bar span { display: block; height: 100%; border-radius: 4px;
  background: var(--accent); }
.arch-bar-n { text-align: right; font-variant-numeric: tabular-nums;
  color: var(--muted); }
.arch-bar-v { color: var(--muted); font-size: 11.5px; }
.arch-bar-v.warn { color: var(--warn); font-weight: 600; }

/* Machine topology (the sixth diagram). A publisher is drawn hollow because
   it is the one kind of node here that is NOT ours -- solid boxes are
   machines this project runs on, and the difference is the whole point of a
   connection diagram. */
.arch-node-ext rect { fill: none; stroke-dasharray: 3 2; }
.arch-warn { color: var(--warn); font-weight: 600; }
.arch-legend { list-style: none; margin: 6px 0 4px; padding: 0;
  display: grid; gap: 4px; font-size: 11.5px; color: var(--muted); }
/* TWO children per row, and the second holds both the label and its sentence.
   Three grid children in a two-column grid put the description into the 22px
   swatch column, which wrapped it to one word per line and made a three-item
   legend 1,300px tall. */
.arch-legend li { display: grid; grid-template-columns: 22px 1fr;
  align-items: baseline; gap: 8px; }
.arch-legend strong { color: var(--ink); font-weight: 600; }
.arch-key { display: inline-block; height: 2px; margin-bottom: 3px;
  background: var(--line); }
.arch-key-fetch { background: var(--muted); }
.arch-key-sync { background: var(--accent); height: 3px; }
.arch-key-heartbeat { background: var(--line); }

/* Jurisdictions inside a source file (#/sources/disc/...). Its own chip class
   rather than .chip, which is a clickable FILTER control -- pointer cursor,
   hover recolour, an .on state. These are readings, not controls, and
   borrowing that class would offer an interaction that does not exist. */
.areagroup { margin-bottom: 14px; }
.areahead { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
            margin-bottom: 6px; font-size: 12.5px; }
/* Capped in HEIGHT and never in CONTENT: a GLEIF golden copy carries 237
   countries and all of them render. A list silently cut at 40 would read as
   though the file held 40. */
.areachips { display: flex; flex-wrap: wrap; gap: 4px;
             max-height: 220px; overflow-y: auto; }
.areachip { padding: 2px 8px; border-radius: 20px; font-size: 11.5px;
            border: 1px solid var(--line); color: var(--muted);
            white-space: nowrap; }
.areachip .mono { color: var(--fg); margin-right: 3px; }
.areachip .dim { margin-left: 5px; }

/* Month grid -- derived monthly spend (pages/spend-months.js). A cell is a
   subtraction of two snapshots, and the classes are the ways a subtraction
   stops being one month: negative (a restatement, shown signed), span (the
   previous snapshot is more than a month back), short (a schools rollup
   with fewer leaves reporting than the year holds). */
.month-grid td.neg { color: var(--warn); }
.month-grid td.span, .month-grid td.short { background: var(--warn-soft); }
.month-grid td.total { font-weight: 600; }
.month-grid .marks { font-size: 10px; color: var(--muted); display: flex;
                     flex-wrap: wrap; gap: 3px; justify-content: flex-end;
                     max-width: 12em; margin-left: auto; margin-top: 2px; }
.month-grid .mark { border: 1px solid var(--line); border-radius: 8px;
                    padding: 0 5px; cursor: help; white-space: nowrap; }
.month-grid td { vertical-align: top; }

/* The fund universe: three governments' funds as orbits (#/fund-universe).
   Bodies are sized in the renderer, never here -- area carries the money, so
   a CSS rule that resized a circle would silently restate a quantity. */
.orbit-grid { display: grid; gap: 18px;
              grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.orbit-card { border: 1px solid var(--line); border-radius: var(--radius);
              padding: 14px; background: var(--panel); }
.orbit-card h3 { margin: 0 0 2px; font-size: 15px; }
.orbit-total { margin: 0 0 8px; font-size: 20px; font-weight: 600;
               color: var(--money); }
.orbit-total span { font-size: 12px; font-weight: 400; color: var(--muted); }
.orbit { width: 100%; height: auto; display: block; overflow: visible; }
.orbit-scale, .orbit-count { font-size: 11px; color: var(--muted);
                             margin: 8px 0 0; }
.orbit-core { fill: var(--navy); }
.orbit-core-label { fill: var(--on-navy); font-size: 9px; font-weight: 600;
                    text-anchor: middle; }
.orbit-link { stroke: var(--line); fill: none; }
.orbit-body { fill: var(--accent); fill-opacity: 0.72;
              stroke: var(--panel); stroke-width: 0.5; }
.orbit-body.tier-vendor, .orbit-body.tier-agency { fill: var(--money); }
.orbit-body.tier-department, .orbit-body.tier-secretariat {
              fill: var(--value); }
.orbit-body.is-link { cursor: pointer; }
.orbit-body.is-link:hover { fill-opacity: 1; stroke: var(--ink);
                            stroke-width: 1; }
/* The one place the three governments are measured against each other. */
.orbit-bars { display: flex; flex-direction: column; gap: 6px; }
.orbit-bar-row { display: grid; gap: 10px; align-items: center;
                 grid-template-columns: 14em 1fr 7em 9em; }
.orbit-bar-track { background: var(--accent-soft); border-radius: 3px;
                   height: 14px; overflow: hidden; }
.orbit-bar-fill { display: block; height: 100%; background: var(--accent); }
.orbit-bar-name { font-weight: 600; }
.orbit-bar-value { text-align: right; font-variant-numeric: tabular-nums; }
.orbit-bar-unit { font-size: 11px; color: var(--muted); }
@media (max-width: 640px) {
  .orbit-bar-row { grid-template-columns: 1fr auto; }
  .orbit-bar-track, .orbit-bar-unit { grid-column: 1 / -1; }
}
/* The WebGL fund scene. The canvas sizes itself from the mount's width, so
   only the frame is styled here -- a CSS rule that resized a body would be
   restating a quantity the renderer already encoded as volume. */
.orbit3d { position: relative; width: 100%; min-height: 200px;
           border: 1px solid var(--line); border-radius: var(--radius);
           background: var(--bg); overflow: hidden; }
.orbit3d canvas { display: block; touch-action: none; }
.orbit3d-tip { position: absolute; pointer-events: none; z-index: 5;
               background: var(--panel); border: 1px solid var(--line);
               border-radius: 6px; padding: 5px 8px; font-size: 12px;
               box-shadow: var(--shadow); max-width: 22em; }
.orbit3d-tip span { color: var(--muted); font-size: 11px; }
.orbit-views { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 12px; }
.orbit3d-play { position: absolute; top: 8px; right: 8px; z-index: 6;
                font-size: 11px; padding: 3px 9px; border-radius: 12px;
                border: 1px solid var(--line); background: var(--panel);
                color: var(--ink); cursor: pointer; }
.orbit3d-play:hover { border-color: var(--accent); color: var(--accent); }
/* Filter bar for the WebGL scenes. Every control REMOVES bodies, so the
   count on the right is not decoration -- a filtered picture that does not
   say it is filtered is the same defect as a capped ring that stays quiet. */
.f3d { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 16px;
       margin: 0 0 10px; font-size: 12px; }
.f3d-group { display: flex; align-items: center; gap: 8px; }
.f3d-label { color: var(--muted); font-size: 11px; text-transform: uppercase;
             letter-spacing: 0.04em; }
.f3d-opt { display: inline-flex; align-items: center; gap: 4px;
           cursor: pointer; white-space: nowrap; }
.f3d-opt input { margin: 0; }
.f3d select { font-size: 12px; padding: 2px 4px; border: 1px solid var(--line);
              border-radius: 4px; background: var(--panel); color: var(--ink); }
.f3d-hubs { color: var(--cardinal); font-weight: 600; }
.f3d-count { margin-left: auto; color: var(--muted);
             font-variant-numeric: tabular-nums; }
/* The selected body. A click names it here rather than navigating away --
   leaving the page would lose the camera, the pause state and whatever the
   reader was half-way through comparing, for a body they may only have
   wanted to identify. */
.orbit3d-sel { position: absolute; top: 8px; left: 8px; z-index: 6;
               max-width: 22em; background: var(--panel);
               border: 1px solid var(--line); border-left: 3px solid var(--cardinal);
               border-radius: 6px; padding: 8px 10px; font-size: 12px;
               box-shadow: var(--shadow); }
.orbit3d-sel-body strong { display: block; margin-right: 14px; }
.orbit3d-sel-body span { color: var(--muted); font-size: 11px; }
.orbit3d-sel-links { color: var(--cardinal); font-size: 11px; margin-top: 4px; }
.orbit3d-sel-go { display: inline-block; margin-top: 6px; font-weight: 600; }
.orbit3d-sel-none { margin-top: 6px; color: var(--muted); font-size: 11px; }
.orbit3d-sel-x { position: absolute; top: 4px; right: 5px; border: 0;
                 background: none; color: var(--muted); cursor: pointer;
                 font-size: 13px; line-height: 1; padding: 2px 4px; }
.orbit3d-sel-x:hover { color: var(--ink); }
.orbit3d-sel-hop { margin-top: 6px; padding-top: 5px;
                   border-top: 1px solid var(--line); font-size: 11px;
                   line-height: 1.35; }
.orbit3d-sel-hop b { color: var(--ink); }
.orbit3d-sel-hop.far { color: var(--muted); }
/* Find a body by name. 6,163 of them is past what anyone can pick out by eye. */
.f3d-find { position: relative; display: inline-flex; }
.f3d-find input { font-size: 12px; padding: 3px 7px; width: 15em;
                  border: 1px solid var(--line); border-radius: 4px;
                  background: var(--panel); color: var(--ink); }
.f3d-find input:focus { outline: 2px solid var(--accent-soft);
                        border-color: var(--accent); }
.f3d-hits { position: absolute; top: 100%; left: 0; z-index: 8; margin: 2px 0 0;
            padding: 0; list-style: none; min-width: 22em; max-height: 17em;
            overflow-y: auto; background: var(--panel);
            border: 1px solid var(--line); border-radius: 5px;
            box-shadow: var(--shadow); }
.f3d-hits button { display: block; width: 100%; text-align: left; border: 0;
                   background: none; padding: 5px 9px; cursor: pointer;
                   font-size: 12px; color: var(--ink); }
.f3d-hits button:hover { background: var(--accent-soft); }
.f3d-hit-name { display: block; }
.f3d-hit-meta { display: block; font-size: 10px; color: var(--muted); }
.orbit3d-also { display: block; margin-top: 3px; color: var(--warn);
                font-size: 10px; line-height: 1.3; }

/* The parcel overlay map. No tile layer and no map library -- the shapes come
   from the API already clipped to the parcel, so this is one inline <svg>.
   max-width keeps a square drawing from filling a wide desktop column. */
.parcel-map {
  display: block;
  max-width: 620px;
  margin: 0.75rem 0;
  color: var(--ink);
}
.parcel-map path { vector-effect: non-scaling-stroke; }

/* The legend swatch, matched to the fill on the drawing. */
.swatch {
  display: inline-block;
  width: 0.85rem;
  height: 0.85rem;
  border-radius: 2px;
  vertical-align: middle;
}

/* --- parcel overlay map ------------------------------------------------- */
/* The tiles are absolutely positioned inside this box and the SVG sits on top
   of them, so the container must establish the coordinate origin and clip
   whatever hangs over the edge. */
.pm-canvas {
  position: relative;
  width: 700px;
  height: 560px;
  max-width: 100%;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--panel);
  color: var(--ink);
}
/* THE RESETS ARE LOAD-BEARING. Something upstream gives every img padding
   10px 12px and a 1px border, and with box-sizing: border-box a 257px tile
   renders 231px of photograph -- so the aerial came out as a grid of pictures
   with the page background showing between them, which looks exactly like
   tiles failing to load. The element measured 257px the whole time; it was the
   content box that was short. One pixel oversize on top of that closes what
   rounding leaves between neighbours. */
.pm-canvas .tile {
  position: absolute;
  width: 257px; height: 257px;
  padding: 0; border: 0; margin: 0;
  max-width: none; box-sizing: border-box;
}
.pm-canvas svg { position: absolute; inset: 0; }
.pm-canvas svg path { vector-effect: non-scaling-stroke; }

.pm-toolbar {
  display: flex; gap: 18px; align-items: center; flex-wrap: wrap;
  margin: 0.75rem 0 0.5rem;
}
.pm-toolbar label { display: flex; gap: 6px; align-items: center; }

/* Two columns of ticks under the map rather than one long list: with 30-odd
   layers in view a single column pushes the map off the screen. */
.pm-layers {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 2px 18px;
  margin: 0.6rem 0;
  max-width: 700px;
}
.pm-layer { display: flex; gap: 8px; align-items: center; padding: 2px 0; }
.pm-layer .pm-name { flex: 1; }
.pm-layer .pm-pct { color: var(--muted); font-variant-numeric: tabular-nums; }

/* Zoom buttons. Disabled is a real state here: it says the imagery has run
   out at z20, or that a refetch is in flight, rather than the click doing
   nothing for reasons the reader cannot see. */
.pm-zoom { display: inline-flex; gap: 4px; }
.pm-zoom button {
  width: 30px; height: 30px; line-height: 1;
  font-size: 17px; font-weight: 600;
  border: 1px solid var(--line); border-radius: 4px;
  background: var(--panel); color: var(--ink); cursor: pointer;
}
.pm-zoom button:hover:not(:disabled) { background: var(--accent-soft); }
.pm-zoom button:disabled { opacity: 0.4; cursor: default; }

/* Busy is a cue on the canvas, never a disabled button -- see property.js. */
.pm-canvas[aria-busy="true"] { opacity: 0.65; transition: opacity 120ms; }

/* Dragging shifts what is already drawn and re-requests on release -- see
   property.js. The transform is on the children rather than the container so
   the container keeps clipping them. */
.pm-canvas { cursor: grab; touch-action: none; }
.pm-canvas.pm-dragging { cursor: grabbing; }
.pm-canvas.pm-dragging > * {
  transform: translate(var(--pm-dx, 0), var(--pm-dy, 0));
}

/* ---------------------------------------------------------------------------
   The file-data viewer (web/pages/filedata.js) -- a registered file's own rows
   on its registry page. Everything here is prefixed .fd- because style.css is
   shared by every page in the app and a generic .filters or .range would land
   on somebody else's markup.
   --------------------------------------------------------------------------- */

.fd-bar { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
          margin: .5rem 0; }
.fd-bar input[type="search"] { flex: 1 1 16rem; min-width: 12rem;
          padding: .35rem .5rem; font: inherit; }
.fd-range { font-variant-numeric: tabular-nums; opacity: .8; }
.fd-size { display: inline-flex; gap: .2rem; }
.fd-bar button[disabled] { opacity: .35; cursor: default; }
.fd-pick { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem;
           margin: .35rem 0; }
.fd-pick > span { margin-right: .3rem; }
.fd-note { margin: .6rem 0; font-size: .9em; line-height: 1.5; }
.fd-sql { margin: .35rem 0; padding: .45rem .6rem; overflow-x: auto;
          white-space: pre; font-size: .85em; border-radius: 4px;
          background: rgba(127, 127, 127, .12); }
.fd-busy { opacity: .55; transition: opacity .15s; }

/* A HEADER OF 418 COLUMNS IS THE NORMAL CASE HERE, not the pathological one:
   the VCSC sentencing workbook has 418 and the Census school finance table
   184. So the cells do not wrap, the table scrolls sideways inside .scroll,
   and the row number stays put. */
.fd-table th, .fd-table td { white-space: nowrap; vertical-align: top; }
.fd-table th[data-fd-sort] { cursor: pointer; }
.fd-table th.fd-sorted { text-decoration: underline; }
.fd-table .fd-rn { text-align: right; font-variant-numeric: tabular-nums;
                   position: sticky; left: 0; background: inherit; }
.fd-filters th { padding: .15rem .25rem; }
.fd-filters input { width: 100%; min-width: 5rem; padding: .15rem .3rem;
                    font: inherit; font-size: .85em; }
.fd-long { display: inline-block; max-width: 46rem; white-space: pre-wrap; }
.fd-more { opacity: .6; font-size: .85em; }

/* The fast-copy notice on a file's data section. Deliberately not .dim: it
   says the rows came from somewhere other than the file named above it, which
   is the one thing on that page a reader must not skim past. */
.fd-sidecar { margin: .5rem 0; padding: .5rem .7rem; font-size: .9em;
              line-height: 1.5; border-radius: 4px;
              background: rgba(127, 127, 127, .10); }
.fd-sidecar .badge { margin-right: .4rem; }
.fd-sidecar button { margin-left: .3rem; }

/* One OSHA citation's own narrative, on the inspection page. Blocks rather
   than table cells: the text runs to 541 characters at the median and 132,736
   at the longest, and a cell that wide destroys every other column. */
.cite-text { border-left: 3px solid var(--accent-soft); padding: 0.1rem 0 0.1rem 0.8rem;
             margin: 0 0 1.1rem 0; }
.cite-text h3 { font-size: 0.95rem; font-weight: 600; margin: 0 0 0.35rem 0;
                display: flex; gap: 0.5rem; align-items: baseline; flex-wrap: wrap; }
.cite-text p { margin: 0; white-space: pre-wrap; }

/* SEVERITY ON AN INLINE VALUE, not on a whole column or tile.
 *
 * `td.value` colours every cell in a column and `.tile.value .n` colours a
 * tile, and until the machines table carried sensor readings that was the
 * whole need. A PER-ROW severity had no class at all -- one machine's disk
 * at 95.5% while the other five are fine -- so a span carrying one rendered
 * in ordinary ink and the alarming number looked exactly like the calm ones.
 * Measured: rgb(16,32,47), the body colour, on a cell meant to be warning. */
span.value { color: var(--value); font-weight: 600; }
span.warnv { color: var(--warn); font-weight: 600; }

/* The #/system index. Scoped names rather than `.card` / `.cards`, which this
 * sheet does not define -- the first version used those and the grid simply
 * never applied, rendering as stacked blocks that happened to look passable.
 * A class name that styles nothing is a layout nobody chose. */
.syspages { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 0.9rem; }
.syspage { border: 1px solid var(--line); border-radius: 8px; padding: 0.8rem 0.9rem; }
.syspage h3 { margin: 0 0 0.3rem 0; font-size: 1rem; }
.syspage p { margin: 0 0 0.3rem 0; }
.syspage .note { margin: 0; font-size: 0.85rem; }

/* The 3D preview on a parcel page. Sized in aspect-ratio rather than a fixed
   height so it does not reflow the page when it loads -- the box is the same
   size empty as it is drawn, which is what keeps the rest of the page still. */
.land-preview-wrap { display:block; margin:0 0 1rem; text-decoration:none;
  color:inherit; max-width:34rem; }
.land-preview[hidden] { display:none; }
.land-preview { aspect-ratio:16/9; width:100%; border-radius:6px;
  background:#1a2029; overflow:hidden; display:grid; place-items:center; }
.land-preview canvas { display:block; width:100% !important; height:100% !important; }
.land-preview-cap { display:block; font-size:.8rem; color:var(--muted, #667);
  margin-top:.3rem; }
.land-preview-wrap:hover .land-preview-cap { text-decoration:underline; }
a.land-full-btn { display:inline-block; padding:8px 15px; font-size:13.5px; font-weight:600;
  background:var(--accent-soft); color:var(--accent); border-radius:var(--radius);
  text-decoration:none; }
a.land-full-btn:hover { background:var(--accent); color:var(--panel); }

/* A footnote reference beside the number it annotates (insider trades, 871d).
   `fnref-missing` is a filing citing a footnote it did not file -- ~3% of
   price references -- and it stays visible rather than being dropped, because
   a reader who sees nothing concludes there was nothing to say. */
.fnref { color: #0b5cad; cursor: help; font-size: .72em; padding-left: .1em; }
.fnref-missing { color: #999; text-decoration: underline dotted; }

.hero-search { margin: 18px 0 8px; }
.hero-search input[type="search"] { width: min(640px, 70%); padding: 12px 14px; font-size: 16px; }
.hero-search button { padding: 12px 18px; font-size: 15px; border-radius: 8px; background: var(--accent); color: #fff; border: 0; cursor: pointer; }
.dir-grid { display: grid; gap: 10px; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
.dir-card { display: flex; flex-direction: column; gap: 4px; padding: 12px 14px; border: 1px solid var(--line, #d8dee8); border-radius: 8px; text-decoration: none; background: var(--card, #fff); }
.dir-card:hover { border-color: var(--accent); }
.dir-card span { font-size: 13px; color: var(--muted, #5b6778); }

.two-col { display: grid; gap: 1.5rem; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.two-col h3 { margin: 0 0 .4rem; font-size: 1rem; }

/* School-board document search and reader. */
.doc-hit { margin: 0 0 1.1em; }
.doc-hit .snippet { margin: .25em 0 0; white-space: pre-wrap; font-size: .92em; }
.doc-hit mark { background: #ffe066; color: #111; padding: 0 1px; }
.doc-page h3 { margin: 1.2em 0 .3em; font-size: 1em; }
.doc-text { white-space: pre-wrap; word-break: break-word; font-size: .85em;
  max-height: 32em; overflow: auto; padding: .6em .8em; border-radius: 6px;
  background: var(--panel, rgba(127,127,127,.08)); }

/* Hopewell's assessor sketches (property.js hopewellSketchSvg). */
svg.sketch { width: 100%; max-width: 720px; height: auto; display: block; margin: .5rem 0 1rem; }
svg.sketch .sk-seg { fill: color-mix(in srgb, var(--accent, #2a6fdb) 12%, transparent); stroke: currentColor; stroke-width: 1.5; }
svg.sketch .sk-out { fill: none; stroke: currentColor; stroke-width: 1.2; stroke-dasharray: 5 4; }
svg.sketch .sk-dim { font-size: 11px; fill: currentColor; opacity: .75; }
svg.sketch .sk-lbl { font-size: 12px; font-weight: 600; fill: currentColor; }

/* An index's scope sits behind the cardinal, top-right above the controls. */
.index-scope { min-height: 22px; }
.index-scope > .sec-note > .note { padding-right: 40px; margin-top: 0; }
