/* European Sovereignty Monitor: the shared system
   ============================================================================
   One stylesheet, three surfaces: the newsletter, the stack map at /map/ and the ledger at
   /ledger/, all on sovereigntymonitor.eu since the datasets moved onto Ghost (2026-08-14;
   their data followed in theme 1.9.48). It ships inside the Ghost theme and is served from

       https://sovereigntymonitor.eu/assets/css/system.css

   The dataset pages load their own stylesheet after it. One
   file, one edit point, and a change reaches all three surfaces the moment the theme is
   uploaded. If sovereigntymonitor.eu is unreachable the datasets render unstyled: that is a
   real failure mode, accepted because it is obvious, where a stale copy in each repo would be
   silent.

   What this file owns: tokens, base type, the shell, the header, the footer, buttons, section
   rules and the cell grid. It does NOT own @font-face; see the note below the tokens.

   What it does not own, and must not: anything inside a map canvas or a ledger table. It
   defines no class those interiors use, so a tier colour and the chrome accent cannot end up
   fighting by accident.

   The marker colours are also the map's tier scale. They were separately chosen and landed in
   near-identical hues, because they encode the same three semantic steps: European-owned is
   towards autonomy, operated in Europe is both directions, dependency is towards dependence.
   They are one scale here.
   ============================================================================ */

:root {
  --paper: #f2f2f3;   --ink: #1d1f20;    --ink-head: #15171a; --ink-body: #2c2c2c;
  --muted: #5d5d60;   --faint: #7a7a7d;  --quiet: #98989b;
  /* F3 (2026-09-02): --faint (3.82:1 on paper) and --quiet (2.57:1) are rule and mark colours
     only; every label that carries a word is --muted (5.87:1 light, 6.8:1 dark) at 11.5px or more. */
  --hair: #d4d4d7;    --hair-soft: #e0e0e3; --edge: #b7b7ba;
  --steel: #5980a6;   --steel-deep: #416180; --steel-dark: #1d2d3d;
  --up: #1a7f37;      --down: #cf222e;   --both: #bc4c00;   --none: #5f6b76;
  --up-bg: #eaf3ee;   --down-bg: #f9e9e7; --both-bg: #f7efe4; --none-bg: #ededef;
  --zero: #1d2d3d;
  /* --none was #7a7a7d, byte-identical to --faint, so "not disclosed" rendered in exactly
     the caption grey sitting under it and read correctly only because a dashed border and
     italic type were carrying it. Restruck as a cool slate: separated from the neutral
     caption grey by hue as well as value, and 4.87:1 on --paper, where --faint is 3.82:1.
     It has to clear AA because --none carries text, not just borders: .amt.undisclosed,
     .pchip, .vchip, .scard .v and .bignum all take it.

     --zero is new. A sourced EUR 0 was inheriting --down, but --down on this ledger means
     a non-EU recipient, and a confirmed zero is not a flow in either direction: it is a
     positively established absence of one. Borrowing --up instead would have swapped one
     false reading for another, because green here means the money landed with an EU
     recipient. So the confirmed zero sits off the axis entirely, in ink. */
  --head: "Barlow Condensed", "Arial Narrow", Arial, sans-serif;
  --body: "Barlow", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  --measure: 68ch;    --gutter: 44px;    --shell: 1160px;
  --head-clear: 90px;
  /* 2026-08-31, design C1: one hue per tracked area, chosen so none reads as a direction colour
     (green, red, orange, slate keep their meanings). Chips, column heads, tinted fields. */
  --area-policy-and-law: #6d5670;             --area-policy-and-law-tint: #f3eff3;   /* toned down 2026-08-31, Reidar */
  --area-compute-and-infrastructure: #4d6478; --area-compute-and-infrastructure-tint: #eef1f4;
  --area-defence-and-space: #3f6f6b;          --area-defence-and-space-tint: #ecf2f1;
  --area-industry-investment-and-rd: #7a6250; --area-industry-investment-and-rd-tint: #f2eee9;
  /* Fill tokens: the deep hue for chips and column heads under white text. Defined once, never
     overridden in the dark theme, where the text hues above brighten and white on them would fail. */
  --area-policy-and-law-fill: #6d5670; --area-compute-and-infrastructure-fill: #4d6478;
  --area-defence-and-space-fill: #3f6f6b; --area-industry-investment-and-rd-fill: #7a6250;   /* sticky header (66px bar + 1px rule) plus breathing room.
                           One number for two jobs: what an in-page anchor must clear,
                           and where the issue rail sticks. They were the same figure
                           written twice; now they cannot drift. */
}

[data-theme="dark"] {
  --paper: #14171a;   --ink: #f2f2f3;    --ink-head: #f2f2f3; --ink-body: #dcdcdf;
  --muted: #98989b;   --faint: #b7b7ba;  --quiet: #7a7a7d;
  --hair: #33363a;    --hair-soft: #26292c; --edge: #424244;
  --steel: #5980a6;   --steel-deep: #94bce3;
  --up: #3fb950;      --down: #f85149;   --both: #d29922;   --none: #93a1af;
  --up-bg: #1f2b24;   --down-bg: #2e1d1a; --both-bg: #2c2517; --none-bg: #242730;
  --zero: #dcdcdf;
  --area-policy-and-law: #c7b3c6;             --area-policy-and-law-tint: #241f25;
  --area-compute-and-infrastructure: #a9bfd1; --area-compute-and-infrastructure-tint: #1c2329;
  --area-defence-and-space: #8fbfba;          --area-defence-and-space-tint: #1a2726;
  --area-industry-investment-and-rd: #cbb39e; --area-industry-investment-and-rd-tint: #27221d;
  /* Dark goes cooler and slightly dimmer than --faint (#b7b7ba) rather than brighter.
     Brighter was tried first and is wrong here: --none is a 6px left border on every
     undisclosed node, and a near-white rule would have made "unknown" shout louder than
     the EU and non-EU nodes either side of it. 6.82:1 on --paper and 5.96:1 on the
     dataset panel ground (#20232a), so the text uses still clear AA. */
}

/* ---------- no @font-face here, deliberately ----------
   Fonts are always fetched in CORS mode. Ghost(Pro) serves woff2 with no
   Access-Control-Allow-Origin header and that is not configurable, so any @font-face declared
   in this file would work on sovereigntymonitor.eu and be refused on both dataset domains. The
   failure would be invisible to anyone testing on this domain and would cost the datasets
   exactly the thing a reader notices.

   So the family NAMES are shared, in --head and --body above, and each surface declares the
   four faces against its own local copy of the files. Three four-line blocks against one, and
   the duplication is forced rather than chosen. If the faces are ever replaced, all three
   blocks change; see datasets/CHROME.md step 2.

   A stylesheet loaded through <link> is not CORS-restricted, which is why everything else in
   this file crosses the boundary fine. */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body { margin: 0; background: var(--paper); color: var(--ink-body);
       font: 400 17px/1.62 var(--body); text-wrap: pretty; }
img { max-width: 100%; height: auto; }
::selection { background: rgba(89,128,166,.24); }

a { color: var(--steel-deep); text-decoration: underline; text-underline-offset: 2px; }
a:hover { color: var(--ink); }
:focus-visible { outline: 2px solid var(--steel); outline-offset: 2px; }

h1, h2, h3, h4 { font-family: var(--head); color: var(--ink-head); margin: 0; }

/* ---------- the shell ----------
   The one measure everything centres to. Full-bleed rules go on the outer element, contents on
   the shell, so a border can span the viewport while the type it belongs to stays aligned with
   every other left edge on the page. */
.shell { max-width: var(--shell); margin: 0 auto; padding: 0 var(--gutter); }

/* ---------- header ----------
   Identical on all three surfaces: brand, four nav links in one order, appearance toggle,
   Subscribe. The current surface is marked with a rule under its label, the same device the
   section titles use. That is what makes crossing a domain boundary invisible.

   The language switcher is newsletter-only and lives in screen.css: neither dataset is
   translated, and a switcher that lands you on an untranslated page is worse than none. */
.site-head { border-bottom: 1px solid var(--ink); position: sticky; top: 0;
             background: var(--paper); z-index: 10; }
/* The header overlays the page, so an anchor jump lands the target underneath it: every
   {{#get}}-free contents link put the section heading exactly 67px out of sight, which
   is the whole heading. Measured on the live issue, not estimated. scroll-padding-top on
   the scroll container fixes every anchor at once, including find-in-page and focus,
   where per-heading scroll-margin would have to be repeated for each new element. */
html { scroll-padding-top: var(--head-clear); }
.head-shell { display: flex; align-items: center; justify-content: space-between; gap: 32px;
              height: 66px; }
.brand { display: flex; align-items: center; gap: 12px; text-decoration: none; }
.brand-name { font: 700 17px/1 var(--head); letter-spacing: .13em; text-transform: uppercase; color: var(--ink); }
.mark { width: 22px; height: 22px; border: 1.5px solid var(--ink); flex: none;
        display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }
.mark i { display: block; background: var(--steel); }
.mark i:first-child { grid-area: 1 / 1; }
.mark i:last-child { grid-area: 2 / 2; }
.site-nav { display: flex; gap: 26px; margin-left: auto; }

/* Locale navigation. site-header.hbs renders the English nav and one generated nav per published
   locale; exactly one is shown here. The switch is a body class because that partial is included
   from default.hbs, which has no post context, so {{#has}} is blind in it and silently takes the
   wrong branch. Ghost writes internal tags into the body class, and a body class cannot lie.
   Source order matters: .site-nav-loc has the same specificity as .site-nav and must come after
   it to win. The pair of rules per locale is generated-nav-shaped, so adding Italian is two more
   lines here and a registry entry, not a new mechanism. */
/* Two signals, deliberately. body.tag-hash-es is server-rendered from the internal tag and is the
   authority wherever it exists. html[data-loc] is set from the path before first paint by the
   inline script in default.hbs, and covers the surfaces Ghost cannot tag at all: the /es/
   collection index has no post, so it has no tag and no body class. Without the second signal the
   Spanish home served the English header. The negative rules key on data-loc rather than on the
   body class because data-loc is present on every page, and a :not() on a class that is simply
   absent matches everything. */
.site-nav-loc { display: none; }
body.tag-hash-es .site-nav-en,
html[data-loc="es"] .site-nav-en { display: none; }
body.tag-hash-es .site-nav-es,
html[data-loc="es"] .site-nav-es { display: flex; }

/* The footer, same two signals. The English one stays Ghost-driven and editable; every other
   locale is generated beside it. Note the print rule further down already hides .site-foot
   entirely, so both are correctly absent on paper without a second rule here. */
/* Visually hidden, available to a screen reader. The theme had no such helper, which is why the
   English-only marker below could not carry its explanation in text until now. */
.vh { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden;
      clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }

/* "EN": this destination exists only in English. The map and the ledger have no Spanish version,
   and the Method's own rule is to declare a limitation rather than let a reader discover it on
   click. The visible mark is two letters; the sentence a screen reader gets is Spanish, and the
   title attribute gives a pointing device the same sentence. Never rendered on English surfaces,
   where it would state the obvious. */
.lang-note { font: 600 11.5px/1 var(--body); letter-spacing: .1em; color: var(--muted);   /* F3 (2026-09-02): 11.5px, --muted */
             border: 1px solid var(--hair); border-radius: 2px; padding: 2px 4px;
             margin-left: 6px; vertical-align: 1px; white-space: nowrap; }

.site-foot-loc { display: none; }
body.tag-hash-es .site-foot-en,
html[data-loc="es"] .site-foot-en { display: none; }
body.tag-hash-es .site-foot-es,
html[data-loc="es"] .site-foot-es { display: block; }

/* The area page's related links, same two signals again, but written as HIDE rules only.
   .area-foot is display:flex in screen.css, which loads AFTER this file, so a bare
   `.area-foot-loc { display: none }` here would be the same specificity and lose to it, and the
   inactive block would stay visible. Every rule below therefore outranks a single class, and the
   block that is meant to show simply inherits flex from screen.css rather than restating it. */
html:not([data-loc="es"]) .area-foot-es { display: none; }
body.tag-hash-es .area-foot-en,
html[data-loc="es"] .area-foot-en { display: none; }

/* The language selector button shows the current language. Same body-class mechanism, and English
   is the fall-through so a page that carries no locale tag reads EN rather than blank. The
   current locale is also struck from its own menu, because offering the page you are already on
   is a dead option that costs a click to discover. */
.lang-select { margin-right: 4px; }

/* Flags. Every flag gets the SAME 22x11 viewport while keeping its own official ratio inside it
   (2:1 for the union flag, 3:2 for Spain). SVG's default preserveAspectRatio letterboxes the
   narrower one, so nothing is cropped and nothing is stretched. The fixed viewport also stops the
   button changing width between EN and ES, which was moving the theme toggle and the Subscribe
   button sideways whenever the reader switched language. The hairline is drawn inside each SVG
   rather than as a box-shadow here, so it traces the flag and not the empty letterbox beside it.

   Alignment is flex rather than vertical-align: the flag is taller than the cap height of the
   12.5px label next to it, and optical centring by eye through a magic vertical-align offset is
   what made the first attempt look off. */
.flag { width: 22px; height: 11px; display: block; flex: none; }

.lang-toggle { display: inline-flex; align-items: center; gap: 6px; }
.lang-menu .lang-opt { align-items: center; gap: 8px; }

/* The appearance toggle and Subscribe, one copy per locale from partials/head-tools.hbs. Same two
   signals as everything else in this header: the body class Ghost writes from a post's internal
   tag, and the data-loc attribute default.hbs stamps on <html> before paint. Untagged and
   unstamped falls through to English, which is the correct default rather than a bar with no
   Subscribe button on it.

   flex, not inline: this span replaces two children of .head-tools, so it has to keep laying them
   out along the same axis with the same 16px gap or the toggle and the button close up together
   and read as one control. */
.head-tools-loc { display: none; }
html:not([data-loc="es"]) .head-tools-en { display: flex; align-items: center; gap: 16px; }
body.tag-hash-es .head-tools-en,
html[data-loc="es"] .head-tools-en { display: none; }
body.tag-hash-es .head-tools-es,
html[data-loc="es"] .head-tools-es { display: flex; align-items: center; gap: 16px; }

.lang-cur { display: none; }
html:not([data-loc="es"]) .lang-cur-en { display: inline-flex; align-items: center; gap: 6px; }
body.tag-hash-es .lang-cur-en,
html[data-loc="es"] .lang-cur-en { display: none; }
body.tag-hash-es .lang-cur-es,
html[data-loc="es"] .lang-cur-es { display: inline-flex; align-items: center; gap: 6px; }
/* Written as body:not(...) rather than a bare .lang-opt-en on purpose. `.nav-menu a` later in this
   file is (0,1,1) and beat a bare (0,1,0) class, so the English option stayed visible on English
   pages and the selector offered the reader the page they were already on. Specificity, not source
   order, is what decides here. */
html:not([data-loc="es"]) .lang-opt-en { display: none; }
body.tag-hash-es .lang-opt-en,
html[data-loc="es"] .lang-opt-en { display: flex; }
body.tag-hash-es .lang-opt-es,
html[data-loc="es"] .lang-opt-es { display: none; }
html:not([data-loc="es"]) .lang-opt-es { display: flex; }
.nav-link { font: 500 12.5px/1 var(--body); letter-spacing: .1em; text-transform: uppercase;
            color: var(--ink); text-decoration: none; }
.nav-link:hover { color: var(--steel-deep); }
.nav-link[aria-current="page"], .nav-link.is-current { font-weight: 600;
            border-bottom: 2px solid var(--ink); padding-bottom: 3px; }
/* ---------- the Resources disclosure ----------
   A button, not a hover menu: hover cannot be operated by touch or by keyboard, and this same
   markup ships to the map and the ledger where there is no framework to fall back on. Closed is
   the default and `hidden` is on the element, so with JS unavailable the button simply does
   nothing rather than leaving an open menu lying across the page. */
.nav-group { position: relative; display: inline-flex; }
/* No font or colour here on purpose. The button also carries .nav-link, which is defined above
   at the same specificity, so declaring font/color here would win on source order and resolve to
   the NAV's inherited values rather than the link's: the item rendered at 17px/400 beside its
   12.5px/500 siblings, and because .nav-link tracks in em the wider tracking made it read as a
   different typeface. A class beats the UA's element-level button rule, so .nav-link supplies
   both and this rule only undoes what a button brings with it. */
.nav-toggle { background: none; border: 0; padding: 0; margin: 0;
              cursor: pointer; display: inline-flex; align-items: center; gap: 6px; }
.nav-caret { width: 0; height: 0; border-left: 3.5px solid transparent; border-right: 3.5px solid transparent;
             border-top: 4px solid currentColor; transition: transform .12s ease; }
.nav-toggle[aria-expanded="true"] .nav-caret { transform: rotate(180deg); }
.nav-menu { position: absolute; top: calc(100% + 13px); left: -14px; z-index: 20;
            display: flex; flex-direction: column; min-width: 224px;
            background: var(--paper); border: 1px solid var(--ink);
            box-shadow: 0 10px 28px rgba(0,0,0,.10); padding: 6px 0; }
.nav-menu[hidden] { display: none; }
.nav-menu a { display: block; padding: 9px 16px; text-decoration: none; color: var(--ink-body);
              font: 500 14px/1.3 var(--body); white-space: nowrap; }
.nav-menu a:hover, .nav-menu a:focus-visible { background: var(--hair-soft); color: var(--ink); }
/* The area numbers ride along in the menu the way they do in the page kickers, set back a
   step so the name still reads first. */
.nav-menu b { font: 700 11.5px/1 var(--head); color: var(--muted); margin-right: 7px;
              letter-spacing: .06em; }   /* F3 (2026-09-02): 11.5px, --muted */
.nav-menu a:hover b, .nav-menu a:focus-visible b { color: var(--ink); }
/* Issue titles are headlines, not labels, so the Issues menu needs a ceiling. Everything
   else in these menus is short enough that the max-width never engages. */
.nav-menu-wide a { max-width: 380px; overflow: hidden; text-overflow: ellipsis; }
.nav-menu-all { border-top: 1px solid var(--hair-soft); margin-top: 5px; padding-top: 11px !important;
                font-weight: 600 !important; }

.head-tools { display: flex; align-items: center; gap: 16px; }

.theme-toggle { width: 26px; height: 26px; padding: 0; border: 1px solid var(--edge);
                background: none; display: flex; cursor: pointer; }
.half { flex: 1; } .half-light { background: var(--paper); } .half-dark { background: var(--ink); }
.theme-toggle:hover { border-color: var(--steel); }

/* ---------- buttons ---------- */
.btn { display: inline-block; font: 600 11.5px/1 var(--body); letter-spacing: .11em;
       text-transform: uppercase; padding: 11px 16px; text-decoration: none;
       border: 1px solid transparent; cursor: pointer; }
.btn-primary { background: var(--steel-deep); border-color: var(--steel-deep); color: #f2f2f3; }  /* 2026-08-31: --steel was 3.7:1 under an 11.5px label */
.btn-primary:hover { background: var(--steel-dark); border-color: var(--steel-dark); color: #f2f2f3; }
.btn-secondary { color: var(--steel-deep); border-color: var(--edge); background: none; }
.btn-secondary:hover { border-color: var(--steel); color: var(--ink); }
.btn-block { display: block; text-align: center; }

/* ---------- section rules and small type ---------- */
.kicker { font: 600 12px/1 var(--body); letter-spacing: .18em; text-transform: uppercase;
          color: var(--steel-deep); margin: 0; }
.section-title { font: 700 20px/1 var(--head); letter-spacing: .16em; text-transform: uppercase;
                 color: var(--ink); border-top: 2px solid var(--ink); padding-top: 14px;
                 margin: 0 0 22px; }
.quiet { font-size: 13.5px; line-height: 1.55; color: var(--muted); }  /* 2026-08-31: --faint is 3.8:1 on paper */

/* ---------- the cell grid ----------
   Equal cells separated by a hairline, which is the layout both datasets already reach for.
   A cell that links is the whole hit target. */
.cells { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px; background: var(--hair);
         border: 1px solid var(--hair); }
.cells-2 { grid-template-columns: 1fr 1fr; }
.cells-3 { grid-template-columns: repeat(3, 1fr); }
/* A cell that takes the whole row. Unused since 2026-08-20, when the dataset section went to a
   single row of thirds on Reidar's call (the timeline had held a full-width band because its
   frame is panoramic and the map's type goes small below half width; the thumbnails accept
   that and let the title, host and scale carry the reading). Kept for reuse. */
.cell-wide { grid-column: 1 / -1; }
.cell { background: var(--paper); padding: 20px 18px; }
.cell h3 { font: 600 21px/1.15 var(--head); margin: 9px 0 8px; }
.cell p { font-size: 14px; line-height: 1.5; color: var(--muted); margin: 0; }
.cell-no { font: 700 13px/1 var(--head) !important; letter-spacing: .16em; color: var(--muted); }   /* F3 (2026-09-02): --muted */
.cell-host { font: 500 11.5px/1 var(--body) !important; letter-spacing: .14em;
             text-transform: uppercase; color: var(--muted) !important; }   /* F3 (2026-09-02): 11.5px, --muted */

.cell-link { display: flex; flex-direction: column; text-decoration: none; color: inherit; }
.cell-link p:last-of-type { flex: 1; }
.cell-link:hover { background: var(--hair-soft); }
.cell-link:hover h3 { color: var(--steel-deep); }
.cell-foot { display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
             margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--hair-soft); }
.cell-scale { font: 500 11.5px/1 var(--body); letter-spacing: .1em; text-transform: uppercase;
              color: var(--muted); }   /* F3 (2026-09-02): 11.5px, --muted */
.cell-go { font: 600 11.5px/1 var(--body); letter-spacing: .11em; text-transform: uppercase;
           color: var(--steel-deep); margin-left: auto; }

/* ---------- an area cell takes its colour from its own tally ----------
   The four tracked areas do not agree with each other, and that disagreement is the most
   interesting thing the publication can put on a front page: defence and space closed +5 in the
   same month policy and law closed -1, out of the same public purse. The colour states it.

   The rule is 3px on the top edge and the net figure, and nothing else. A filled cell was tried
   on paper and rejected: four saturated blocks would make the section shout, and this
   publication does not shout. Colour on a rule and a numeral is the same restraint the marker
   scale uses everywhere else.

   The default border is --paper, not transparent and not omitted. Omitted, the cells would jump
   3px the moment the tally resolved; transparent, the grid's hairline background would show
   through as a grey band across all four. --paper is the cell's own ground, so the space is
   reserved and invisible until there is something true to put in it. */
.cell[data-area-cell] { border-top: 3px solid var(--paper); }
.cell[data-dir="up"]   { border-top-color: var(--up); }
.cell[data-dir="down"] { border-top-color: var(--down); }
.cell[data-dir="flat"] { border-top-color: var(--none); }

.cell-tally { font: 500 11.5px/1 var(--body); letter-spacing: .06em; color: var(--muted); }   /* F3 (2026-09-02): 11.5px, --muted */
.cell-tally b { font: 700 15px/1 var(--head); margin-right: 4px; color: var(--none);
                font-variant-numeric: tabular-nums; }
.cell[data-dir="up"] .cell-tally b   { color: var(--up); }
.cell[data-dir="down"] .cell-tally b { color: var(--down); }

/* ---------- the dataset previews ----------
   The only pictures this publication owns outright. They are generated from the live map and
   ledger by scripts/render-dataset-previews.js and ship inside the theme, so they cannot drift
   from the data the way a screenshot saved once would.

   Full bleed to the cell edges, against the cell's own padding, because a preview with a margin
   reads as an illustration and a preview that meets the edge reads as a window into the thing.

   The box is 7:4 and both previews are captured TALLER than that on purpose. The two frames have
   different native shapes (the map board is 618x400, the ledger rail 994x588) and the box has to
   be shorter than the shorter of the two once scaled, or one cell shows a strip of empty ground
   under its picture and the two cells stop being level. Overflowing by a margin and fading the
   overflow is the safe direction to be wrong in: a crop reads as a window, a gap reads as a
   broken image.

   The theme swap keys on [data-theme] alone, with no prefers-color-scheme query, because that is
   how the whole site works: theme.js resolves the preference and always stamps the attribute, so
   a media query here would be a second source of truth that disagrees the moment a reader uses
   the toggle. */
.cell-shot { padding-top: 0; }
.shot { display: block; position: relative; overflow: hidden;
        margin: 0 -18px 15px; aspect-ratio: 7 / 4;
        background: var(--hair-soft); border-bottom: 1px solid var(--hair); }
.shot img { display: block; width: 100%; height: auto; }
.shot::after { content: ""; position: absolute; right: 0; bottom: 0; left: 0; height: 40px;
               background: linear-gradient(to bottom, transparent, var(--paper)); }
.cell-link:hover .shot::after { background: linear-gradient(to bottom, transparent, var(--hair-soft)); }
.shot-dark { display: none; }
html[data-theme="dark"] .shot-lite { display: none; }
html[data-theme="dark"] .shot-dark { display: block; }

/* ---------- the marker and tier scale ----------
   Shape first, colour second: the glyphs carry the reading on their own, which is what lets
   them survive email, a client that inverts everything, and a reader who cannot separate red
   from green. */
.mk-up { color: var(--up); } .mk-down { color: var(--down); }
.mk-both { color: var(--both); } .mk-none { color: var(--none); }
.mk-i { font-style: normal; }
.mk-up-bg { background: var(--up); } .mk-down-bg { background: var(--down); }
.mk-both-bg { background: var(--both); } .mk-none-bg { background: var(--none); }
.mk-up-fill { background: var(--up-bg); } .mk-down-fill { background: var(--down-bg); }
.mk-both-fill { background: var(--both-bg); } .mk-none-fill { background: var(--none-bg); }

/* ---------- footer ---------- */
.site-foot { margin-top: 60px; }
.foot-shell { display: flex; justify-content: space-between; align-items: flex-start; gap: 40px;
              border-top: 1px solid var(--ink); padding-top: 26px; padding-bottom: 34px; }
.foot-note { font-size: 13px; line-height: 1.6; color: var(--muted); max-width: 46ch; margin: 0; }  /* 2026-08-31: 3.8:1 before */
.foot-nav { display: flex; flex-wrap: wrap; gap: 30px; }
.foot-nav a { font: 500 12px/1 var(--body); letter-spacing: .1em; text-transform: uppercase;
              color: var(--ink); text-decoration: none; }
.foot-nav a:hover { color: var(--steel-deep); }

/* ---------- narrow ---------- */
@media (max-width: 900px) {
  :root { --gutter: 20px; }
  .site-head { position: static; }
  /* floating a panel over a header that no longer overlays anything just hides the rest
     of the nav; let it open in flow instead. */
  .nav-group { display: block; }
  .nav-menu { position: static; border: 0; box-shadow: none; min-width: 0; padding: 4px 0 4px 14px; }
  html { scroll-padding-top: 0; }   /* nothing overlays the page here, so no offset is owed */
  /* F2 (2026-09-02): the header was 220px of chrome in four rows on a phone. The brand takes the
     slack on row one and the tools follow it (order 2, before the nav's 3), so the two share the
     row; the nav is baseline-aligned so the disclosure buttons sit level with the plain links,
     with a tighter gap so five labels fit two rows at most. */
  .head-shell { height: auto; padding-top: 12px; padding-bottom: 12px; flex-wrap: wrap; gap: 12px 16px; }
  .brand { flex: 1 1 auto; min-width: 0; }
  .brand-name { letter-spacing: .08em; line-height: 1.05; }   /* F2: tighter tracking so the wordmark does not push the tools off the row on a 360px phone */
  .head-tools { order: 2; }
  /* 2026-08-31: the nav wraps instead of scrolling; an overflow strip hid About behind an OS
     scrollbar on phones. */
  .site-nav { order: 3; width: 100%; margin-left: 0; flex-wrap: wrap; align-items: baseline; gap: 12px 20px; }
  .cells, .cells-2, .cells-3 { grid-template-columns: 1fr; }
  .foot-shell { flex-direction: column; gap: 20px; }
}

@media print {
  .site-head, .site-foot, .theme-toggle { display: none; }
  body { background: #fff; color: #000; }
}

/* ---------- 2026-08-31, after the first Impeccable critique ---------- */
/* The skip link: the first focusable thing on every page, visible only when focused. */
.skip-link { position: absolute; left: 12px; top: -60px; z-index: 100; padding: 12px 16px;
             background: var(--ink); color: var(--paper); font: 600 12.5px/1 var(--body);
             letter-spacing: .1em; text-transform: uppercase; text-decoration: none; }
.skip-link:focus { top: 12px; outline: 2px solid var(--steel); outline-offset: 2px; }
/* Tally labels were 9px and 10.5px; 11px was the floor for text that carries a label.
   F3 (2026-09-02): the floor is 11.5px. */
.tally-net-label { font-size: 11.5px; }
.tally-col span { font-size: 11.5px; }
.tally-total { color: var(--muted); }
/* Phones: 44px targets on the buttons that matter. */
@media (max-width: 600px) {
  .btn { display: inline-flex; align-items: center; min-height: 44px; padding-top: 0; padding-bottom: 0; }
  .head-tools .btn { min-height: 40px; padding-left: 12px; padding-right: 12px; }   /* F2 (2026-09-02): narrower so the tools fit beside the brand */
  /* F2 (2026-09-02): measured at 390px, a one-line 17px wordmark (284px with the mark) plus the
     tools (144px) is 444px in a 350px shell, so the tools always dropped to a second row and the
     header stayed at 178px. Under 600px the wordmark sets two lines at 15px beside the mark, the
     brand takes what the tools leave, and the header is one brand row plus the nav. */
  .brand { flex: 1 1 0; }
  .brand-name { font-size: 15px; letter-spacing: .06em; }
}
/* Dark theme: --steel-deep brightens to #94bce3 there, which put near-white button text at 1.8:1.
   The primary button keeps its light-theme fill in both themes. */
[data-theme="dark"] .btn-primary { background: #416180; border-color: #416180; color: #f2f2f3; }
[data-theme="dark"] .btn-primary:hover { background: #4b6f93; border-color: #4b6f93; }
