/* ════════════════════════════════════════════════════════════════
   BRUTAL SKIN — loaded last on every document page (not the three
   canvas art scenes, not the homepage which implements the same
   system inline). Brutalist minimalism: monospace, grayscale, hard
   edges, solid blocks, one accent. All canvases render as pixel art
   (image-rendering:pixelated; the pages set low-res buffers in JS).
   ════════════════════════════════════════════════════════════════ */

:root{
  --b-bg:#0a0a0a; --b-ink:#f2f2f2; --b-muted:#9b9b9b; --b-faint:#666666;
  --b-line2:#333333; --b-acc:#7aa2ff; --b-hover:rgba(255,255,255,.05);
  --b-panel:#050505;
  /* Ink for anything sitting ON --b-panel. The panel is near-black in
     BOTH themes, so this stays light in both; scheme.js overwrites it
     with the hue-tinted value. Never derive it from --b-ink. */
  --b-panel-ink:#f2f2f2;
  /* Solid layout blocks, derived from the scheme strip's hue/saturation
     (--h and --s, set by scheme.js). At --s:0% they collapse to grey,
     which is the dark-mode default; the light theme opens on red/yellow.
     The second block sits 40 degrees along the wheel from the first. */
  --h:0; --s:0%;
  --b-b1:hsl(var(--h) var(--s) 27%); --b-b1-ink:#f2f2f2;
  --b-b2:hsl(calc(var(--h) + 40) var(--s) 46%); --b-b2-ink:#0a0a0a;
  --b-acc:hsl(var(--h) var(--s) 72%);
  --b-mono:ui-monospace,'SFMono-Regular',Menlo,Consolas,'Courier New',monospace;
  /* legacy token names still referenced by page CSS */
  --ed-bg:var(--b-bg); --ed-ink:var(--b-ink); --ed-muted:var(--b-muted);
  --ed-faint:var(--b-faint); --ed-accent:var(--b-acc);
  --ed-line:var(--b-line2); --ed-line2:var(--b-ink);
  --ed-card:transparent; --ed-card2:var(--b-hover);
}
html[data-theme="light"]{
  --b-bg:#ffffff; --b-ink:#111111; --b-muted:#5a5a5a; --b-faint:#767676;
  --b-line2:#c9c9c9; --b-hover:rgba(0,0,0,.04);
  --b-b1:hsl(var(--h) var(--s) 45%); --b-b1-ink:#ffffff;
  --b-b2:hsl(calc(var(--h) + 40) var(--s) 54%); --b-b2-ink:#111111;
  --b-acc:hsl(var(--h) var(--s) 42%);
}

/* ── sledgehammer: the brutal ground rules ──────────────────────── */
*, *::before, *::after{
  border-radius:0 !important; box-shadow:none !important; text-shadow:none !important;
  backdrop-filter:none !important; -webkit-backdrop-filter:none !important;
  font-family:var(--b-mono) !important; font-style:normal !important;
  transition:none !important;
}
body{ background:var(--b-bg) !important; color:var(--b-ink) !important; cursor:auto !important; }

/* No element may hard-code its own text colour. Every page still carries
   the old deep-blue palette inline (near-white text), which is invisible
   on a white background in light mode. Force inheritance from body, then
   let the token rules further down re-assert the hierarchy: they are both
   !important and more specific, so they still win. */
h1,h2,h3,h4,h5,h6,p,li,ul,ol,div,span,em,strong,b,i,small,
td,th,dt,dd,figcaption,blockquote,label,summary,button{
  color:inherit !important;
}
::selection{ background:var(--b-ink); color:var(--b-bg); }
canvas{ image-rendering:pixelated; }

/* No entrance choreography. These blocks start at opacity:0 and wait for a
   scroll-reveal observer to add .visible; when that never fires the content
   is simply invisible (the CV experience list did exactly that). Brutalism
   has no fade-in anyway, so the content is just there. Deliberately hidden
   things are NOT listed here: the accordion body, tooltips, modals, the
   intro toast and the ocean password gate keep their own logic. */
.entry, .pub-wrap, .section-head, .sec-text, .pcard, .crow,
.paper-wrap, .outro-wrap, .res-card, .score-wrap{
  opacity:1 !important; translate:none !important;
}
.aurora, #bg, .vignette, #cv-dot, #cv-ring{ display:none !important; }
#prog{ background:var(--b-acc) !important; height:3px !important; }

/* headings: bold mono; accent word = thick underline, never italic */
h1,h2,h3,.hero-title,.hero-name,.sec-title,.pc-title,.pub-title,.uc-title,
.paper-title,.e-title,.crow-label,.entry-title{ font-weight:700 !important; }
h1 em,.hero-title em,.hero-name em,.uc-title em,.e-title em,.name .last{
  background:none !important; -webkit-text-fill-color:currentColor !important;
  color:inherit !important;
  text-decoration:underline; text-decoration-thickness:.07em; text-underline-offset:.14em;
}
h1,.hero-title,.hero-name,.uc-title{ text-transform:uppercase; letter-spacing:-.01em; }

/* body links */
a{ color:inherit; }
p a, li a, .sec-text a, .note a, .crow-note a, .ma-link, .outro-links a{
  color:var(--b-acc) !important; text-decoration:underline !important;
  border-bottom:none !important;
}

/* ── top bar ────────────────────────────────────────────────────── */
.tab-nav{
  position:fixed !important; top:0 !important; left:0 !important; right:0 !important;
  transform:none !important; width:100% !important; max-width:none !important;
  border:none !important; border-bottom:2px solid var(--b-ink) !important;
  background:var(--b-bg) !important;
  display:flex !important; justify-content:center !important; align-items:center !important;
  gap:1.4rem !important; padding:.9rem 1.4rem !important;
}
.tab-nav a{
  background:none !important; color:var(--b-muted) !important;
  font-size:.68rem !important; letter-spacing:.06em !important; text-transform:uppercase !important;
  padding:.2rem .1rem !important; position:relative;
}
.tab-nav a:hover{ color:var(--b-ink) !important; text-decoration:underline !important; text-decoration-thickness:2px !important; }
.tab-nav a.active{ background:var(--b-ink) !important; color:var(--b-bg) !important; padding:.2rem .45rem !important; }
.tab-nav a.active::after{ display:none !important; }
@media(max-width:760px){ .tab-nav{ gap:.9rem !important; padding:.75rem 1rem !important; } }

/* shared wordmark (left) — hidden on detail pages that carry a back pill */
.tab-nav .wordmark{
  position:absolute !important; left:clamp(1.2rem,5vw,3rem) !important;
  top:50% !important; transform:translateY(-50%) !important;
  font-size:.85rem !important; font-weight:700 !important; letter-spacing:.04em !important;
  text-transform:uppercase !important; color:var(--b-ink) !important; padding:0 !important;
  white-space:nowrap;
}
.tab-nav .wordmark .dot{ color:var(--b-b1) !important; }
.tab-nav .wordmark:hover{ text-decoration:none !important; opacity:.75; }
@media(max-width:900px){ .tab-nav .wordmark{ display:none !important; } }
body:has(.backlink) .tab-nav .wordmark{ display:none !important; }

/* back pill -> square bracket link */
.backlink{
  border:2px solid var(--b-ink) !important; background:var(--b-bg) !important;
  color:var(--b-ink) !important; font-size:.62rem !important; letter-spacing:.06em !important;
  padding:.35rem .6rem !important; transform:none !important;
}
.backlink:hover{ background:var(--b-ink) !important; color:var(--b-bg) !important; transform:none !important; }

/* corner controls */
.lang-toggle{ background:var(--b-bg) !important; border:2px solid var(--b-ink) !important; padding:0 !important; }
.lang-toggle button{
  color:var(--b-muted) !important; font-size:.62rem !important; letter-spacing:.06em !important;
  padding:.3rem .55rem !important; background:none !important;
}
.lang-toggle button.active{ background:var(--b-ink) !important; color:var(--b-bg) !important; }
.theme-toggle{ background:var(--b-bg) !important; border:2px solid var(--b-ink) !important; color:var(--b-ink) !important; }
.theme-toggle:hover{ background:var(--b-ink) !important; color:var(--b-bg) !important; transform:none !important; }

/* ── eyebrows / section heads ───────────────────────────────────── */
.hero-eyebrow,.head-eyebrow,.eyebrow{
  color:var(--b-faint) !important; letter-spacing:.06em !important;
}
.hero-eyebrow::before,.head-eyebrow::before,.eyebrow::before{
  content:'// ' !important; width:auto !important; height:auto !important;
  background:none !important; color:var(--b-b1) !important; font-weight:700;
}
.sec-num{
  background:var(--b-b1) !important; -webkit-background-clip:border-box !important;
  background-clip:border-box !important; color:var(--b-b1-ink) !important;
  padding:.12em .4em !important; font-weight:700 !important;
}
.sec-title{ color:var(--b-ink) !important; text-transform:uppercase; }
.sec-line{ background:var(--b-ink) !important; height:2px !important; }
.sec-text{ color:var(--b-muted) !important; }
.sec-text b{ color:var(--b-ink) !important; }

/* ── surfaces: de-glass everything, hard borders ────────────────── */
.glass,.card,.ecard,.pcard,.lab-card,.pub-card,.pub-wrap,.paper,.paper-wrap,
.proj-modal,.proj-modal-wrap,.formula,.alpha-box,.crow,.outro,.outro-wrap,
.readout,.entry{
  background:transparent !important; border:1px solid var(--b-line2) !important;
}
.pcard,.canvas-wrap.glass,.lc-canvas-wrap,.stage,.paper,.pub-card,.proj-modal{
  border:2px solid var(--b-ink) !important;
}
.pub-wrap,.paper-wrap,.proj-modal-wrap{ background:none !important; border:none !important; padding:0 !important; }
.ecard::before,.pcard::before,.glass::before,.pub-card::before,.ecard::after{ display:none !important; }
/* ── HOVER INVERSION ──────────────────────────────────────────────
   Every card-like surface flips to a solid block of ink on hover, and
   everything inside it flips with it. A 5% tint is the safe web default;
   the whole point of this idiom is that the surface commits.

   Contrast is preserved by construction — this is the page's own ink on
   the page's own background, just the other way round — so it cannot
   fail the audit at any hue the scheme slider reaches. */
/* :not(:has(canvas)) is doing real work here, not tidiness. A card with a
   full-bleed canvas paints that canvas ABOVE the card background, so
   inverting the surface recolours the text but cannot recolour what sits
   behind it — pale text lands on the untouched dark artwork and the card
   becomes unreadable. Excluding by structure rather than by class name
   means a canvas added to any card later is handled without anyone
   remembering this rule exists. Where :has() is unsupported the whole
   selector drops and no inversion happens, which is the safe direction. */
.ecard:hover,.lab-card:hover,a.lab-card:hover,a.crow:hover,.entry:hover,
.card:hover,.res-card:hover,.uc-pill:hover,.pcard:hover{
  transform:none !important;
}
.ecard:not(:has(canvas)):hover,
.lab-card:not(:has(canvas)):hover,
a.crow:not(:has(canvas)):hover,
.entry:not(:has(canvas)):hover,
.card:not(:has(canvas)):hover,
.res-card:not(:has(canvas)):hover,
.uc-pill:not(:has(canvas)):hover{
  background:var(--b-ink) !important; border-color:var(--b-ink) !important;
}
.ecard:not(:has(canvas)):hover *,
.lab-card:not(:has(canvas)):hover *,
a.crow:not(:has(canvas)):hover *,
.entry:not(:has(canvas)):hover *,
.card:not(:has(canvas)):hover *,
.res-card:not(:has(canvas)):hover *,
.uc-pill:not(:has(canvas)):hover *{
  color:var(--b-bg) !important; border-color:var(--b-bg) !important;
}
/* Solid blocks inside an inverted card invert too, or they vanish into it.
   Their CONTENTS then have to be put back as well: the blanket rule above
   paints every descendant in --b-bg, which on a re-inverted block is the
   same colour as the block itself. SVG icons need fill and stroke, not
   just color, because they rarely inherit currentColor. */
.entry:not(:has(canvas)):hover .badge,
a.crow:not(:has(canvas)):hover .crow-icon,
.card:not(:has(canvas)):hover .badge{
  background:var(--b-bg) !important; color:var(--b-ink) !important;
}
.entry:not(:has(canvas)):hover .badge *,
a.crow:not(:has(canvas)):hover .crow-icon *,
.card:not(:has(canvas)):hover .badge *{
  color:var(--b-ink) !important; fill:var(--b-ink) !important;
  stroke:var(--b-ink) !important;
}
/* Cards that DO carry a canvas get the brutalist emphasis a different way:
   the frame thickens to the accent. The artwork stays untouched. */
.pcard:hover,.dv-card:hover{
  border-color:var(--b-acc) !important; background:transparent !important;
}

/* ── CORNER MARKS ─────────────────────────────────────────────────
   A crop mark at the top right of every heading block, borrowed from
   the print artwork so the site and the business card share a
   vocabulary rather than merely a font. */
.section-head,.page-header,.head-row{ position:relative; }
.section-head::after,.page-header::after,.head-row::after{
  content:''; position:absolute; right:0; top:0;
  width:16px; height:16px;
  border-top:2px solid var(--b-acc); border-right:2px solid var(--b-acc);
  pointer-events:none;
}

/* ── GHOST NUMERALS ───────────────────────────────────────────────
   The index of a thing, again, enormous and hollow behind it. Driven by
   CSS counters so it cannot drift out of sync with the real number, and
   set in a stroke so it never competes with the text on top. */
.page{ counter-reset:sec fig; }
.section{ counter-increment:sec; position:relative; }
.section::before{
  content:counter(sec,decimal-leading-zero);
  position:absolute; left:-.34em; top:-.36em; z-index:0;
  font-family:var(--b-mono) !important;
  font-size:clamp(5rem,13vw,9.5rem); font-weight:700; line-height:1;
  color:transparent; -webkit-text-stroke:2px var(--b-line2);
  pointer-events:none; user-select:none;
}
.section > *{ position:relative; z-index:1; }
@supports not (-webkit-text-stroke:1px red){
  .section::before{ color:var(--b-line2); opacity:.28; }
}
/* Deliberately NOT on .pcard. Those cards are filled edge to edge by a
   pixel canvas that is already the visual — a ghost numeral behind it is
   invisible, and in front of it is graffiti on the artwork. The device
   belongs where there is empty margin to put it in. */

/* ── LINKS AS BLOCKS ──────────────────────────────────────────────
   Inline links take a solid slab of accent on hover instead of an
   underline. Padding is negative-margined back out so the slab does not
   shift the line it sits in. */
.sec-text a:hover,.entry-body a:hover,.ctx a:hover,.uc-links a:hover,
.pub-title a:hover,.ma-link:hover,.crow-mail:hover{
  background:var(--b-acc) !important; color:var(--b-bg) !important;
  padding:0 .22em !important; margin:0 -.22em !important;
  text-decoration:none !important;
}
/* canvas panels: always-dark pixel screens */
.canvas-wrap,.canvas-wrap.glass,.lc-canvas-wrap,.stage,.pc-canvas{ background:var(--b-panel) !important; }
.pcard{ background:var(--b-panel) !important; }
/* Those cards are dark screens in both themes, so their type cannot
   follow the theme. In light mode .pc-title computed to rgb(29,17,29)
   on rgb(26,4,26) — 1.07:1, invisible. Same defect class as the p1
   board using --b-panel for its chrome. */
.pcard .pc-title,.pcard .pc-kicker,.pcard .pc-desc,.pcard .pc-cta,
.pcard .pc-tag,.pcard .pc-spacer{ color:var(--b-panel-ink) !important; }
.pcard .pc-desc,.pcard .pc-kicker{ opacity:.82; }
.pcard .pc-tag{ border-color:var(--b-panel-ink) !important; opacity:.72; }
.canvas-hint{ color:var(--b-faint) !important; letter-spacing:.06em !important; }

/* chips / tags / badges */
.chip,.pc-tag,.tag{
  background:none !important; border:1px solid var(--b-line2) !important;
  color:var(--b-muted) !important; letter-spacing:.04em !important;
}
.pc-badge,.badge{
  background:var(--b-b2) !important; border:none !important; color:var(--b-b2-ink) !important;
  font-weight:700 !important; letter-spacing:.05em !important;
}
.pc-badge.grey{ background:none !important; border:1px solid var(--b-line2) !important; color:var(--b-muted) !important; font-weight:400 !important; }

/* buttons / inputs */
.btn,.mail-btn,.mail-copy,.paper a,.home-btn,button.btn{
  background:none !important; border:2px solid var(--b-ink) !important; color:var(--b-ink) !important;
  letter-spacing:.06em !important;
}
.btn:hover,.mail-btn:hover,.mail-copy:hover,.paper a:hover,.home-btn:hover{
  background:var(--b-ink) !important; color:var(--b-bg) !important; transform:none !important;
}
input[type=text],input[type=password],input[type=email]{
  border:2px solid var(--b-ink) !important; background:var(--b-bg) !important; color:var(--b-ink) !important;
}
input[type=range]{ accent-color:var(--b-acc) !important; }

/* readouts */
.readout{ border:none !important; color:var(--b-muted) !important; }
.readout strong{ color:var(--b-ink) !important; }
.ro-label,.stat-l{ color:var(--b-faint) !important; letter-spacing:.06em !important; }
.stat-v{ color:var(--b-ink) !important; }

/* formulas */
.formula{ color:var(--b-ink) !important; }
.formula .fa{ color:var(--b-acc) !important; }
.formula .near{ color:inherit !important; text-decoration:underline; }
.formula .val{ color:var(--b-acc) !important; }
.formula small{ color:var(--b-faint) !important; }

/* misc text tokens on old palettes */
.hero-sub,.head-sub,.hero-bio,.pc-desc,.crow-sub,.ctx-reasons,.e-sub,.lc-desc,.pm-desc{ color:var(--b-muted) !important; }
.hero-role,.ctx-aff,.byline,.ctx-note,.crow-note,.pub-venue,.paper-sub,.pm-sub,.entry-place{ color:var(--b-faint) !important; }
.pc-kicker,.pm-kicker{ color:var(--b-faint) !important; letter-spacing:.06em !important; }
.pc-cta,.lc-cta,.ec-cta,.crow-arr{ color:var(--b-acc) !important; }
.foot-big{
  background:none !important; -webkit-text-fill-color:currentColor !important;
  color:var(--b-ink) !important; font-weight:700 !important;
}
footer,.foot-small{ color:var(--b-faint) !important; letter-spacing:.06em !important; }
.foot-small a{ color:var(--b-muted) !important; text-decoration:underline !important; border-bottom:none !important; }

/* typography polish kept from the editorial pass */
h1,h2,h3,.hero-title,.hero-name,.sec-title,.pc-title,.pub-title,.uc-title,.head-sub{ text-wrap:balance; }
p,li,.hero-sub,.hero-bio,.hero-role,.pc-desc,.crow-sub,.ctx-reasons,.ctx-note{ text-wrap:pretty; }

/* reduced motion */
@media(prefers-reduced-motion:reduce){ *{ animation:none !important; } }


/* ════════════════════════════════════════════════════════════════
   DEVICES, ROUND TWO — every one encodes something true about the
   content. Proposed by four angles, judged twice (craft + skeptic),
   only survivors installed. See the commit for the judging notes.
   ════════════════════════════════════════════════════════════════ */

/* ── THE DATE COLUMN ──────────────────────────────────────────────
   Five roles, five dates, five different x positions — so the three
   concurrent Vienna appointments in 2025 read as a plain descending
   list of separate jobs. One fixed measure puts every start date on
   the same x and the overlap becomes legible by alignment alone.
   Fixed rem, not max-content: every .entry-row is its own grid
   container, so an intrinsic track sizes per row and aligns nothing. */
.timeline-wrap .entry-row{
  display:grid !important;
  grid-template-columns:1fr 9.2rem;
  align-items:baseline; gap:.8rem;
}
.timeline-wrap .entry-date{
  background:none !important; border:0 !important;
  border-left:1px solid var(--b-line2) !important;
  border-radius:0 !important;
  padding:0 0 0 .7rem !important;
  color:var(--b-muted) !important;
  font-variant-numeric:tabular-nums; letter-spacing:.02em;
  text-align:left; white-space:nowrap;
}
@media(max-width:600px){
  .timeline-wrap .entry-row{ grid-template-columns:1fr; gap:.3rem; }
  .timeline-wrap .entry-date{ border-left:0 !important; padding-left:0 !important; }
}

/* ── WINNER AND DIRECTION ─────────────────────────────────────────
   /p1 compares three arms on EUR per KILOGRAM: lower is better, so
   the winning bar is the SHORTEST one and the page never says it.
   /ma's winner is the highest score. Both said it in colour only —
   and at --s:0, the slider's default, the colours in question are
   near-identical greys. So: form, plus the word. */
.resrow .rbar i{
  background-color:var(--b-b2) !important;
  background-image:repeating-linear-gradient(45deg,
    var(--b-bg) 0 2px, transparent 2px 5px) !important;
}
.resrow.best .rbar i{
  background-color:var(--b-acc) !important;
  background-image:none !important;
}
/* the winning value carried down through all three tracks. No border
   on .rbar: box-sizing is border-box site-wide, so a border would
   shift i's content box out from under this rule. Unset --best parks
   it off-screen and the device degrades to solid-vs-hatch alone. */
.resrow .rbar{ position:relative; overflow:visible; }
.resrow .rbar::after{
  content:''; position:absolute; z-index:3;
  left:var(--best,-999px); top:-4px; bottom:-4px; width:2px;
  background:var(--b-acc); pointer-events:none;
}
.resrow .rl{ position:relative; }
.resrow.best .rl::before{
  content:'min \25B8 '; color:var(--b-acc) !important;
  font-weight:700; letter-spacing:.1em;
}
.resrow .rv,.resrow .rn{ font-variant-numeric:tabular-nums; }
/* /ma — the winner was a box-shadow (killed by the sledgehammer above)
   plus hard-coded gold outside the hue system. A solid block instead;
   --b-b2-ink on --b-b2 is the pairing solveBlock() actually audits. */
.sc-chip{
  background:none !important;
  border:1px solid var(--b-line2) !important;
  color:var(--b-muted) !important;
  font-variant-numeric:tabular-nums;
}
.sc-chip.winner{
  background:var(--b-b2) !important;
  border-color:var(--b-b2) !important;
  transform:none !important;
}
.sc-chip.winner,.sc-chip.winner *{ color:var(--b-b2-ink) !important; }
.sc-chip.winner::after{
  content:' max'; color:var(--b-b2-ink) !important;
  font-weight:700; letter-spacing:.1em;
}

/* ── READOUTS AS TABLES ───────────────────────────────────────────
   Eight readouts across /p1, /ba and /ma are flex rows of content-
   sized label-over-value pairs: nothing lines up, and the values are
   rewritten every frame, so the whole row twitches sideways whenever
   a number changes width. Equal max-content tracks fix the x, and a
   2px rule roofs each figure with the label that owns it.
   max-content, not 1fr: 1fr in a full-width .page stretches a three-
   field readout into three 18rem columns and scatters readings that
   belong together. --b-faint, not --b-line2: --b-line2 is the one
   token scheme.js does not solve and sits under 1.6:1 in dark. */
.readout{
  display:grid !important;
  grid-template-columns:repeat(auto-fit,minmax(8.5rem,max-content));
  justify-content:start;
  gap:.9rem 1.4rem !important;
  align-items:start !important;
}
.readout > span{ display:block; min-width:0; }
.readout .ro-label{
  display:block !important;
  border-bottom:2px solid var(--b-faint);
  padding-bottom:.28rem; margin-bottom:.32rem;
}
.readout strong{ display:block !important; font-variant-numeric:tabular-nums; }

/* ── LIVE ROUTE TALLY ─────────────────────────────────────────────
   The length of the route the reader is currently building. p1's
   script rebuilds this list on every click; a CSS counter reads the
   DOM, so it cannot fall out of step with it. The only count on the
   site that changes, which is the only reason it is here.
   "n =" rather than a word: generated content cannot follow the DE/EN
   toggle, but a numeral is the same in both. */
.stoplist{ counter-reset:tally; }
.stoplist > .stop{ counter-increment:tally; }
.stoplist:not(:empty)::after{
  content:'n = ' counter(tally,decimal-leading-zero);
  display:block; margin-top:.4rem; padding-top:.3rem;
  border-top:2px solid var(--b-faint);
  font-size:.6rem; font-weight:700; letter-spacing:.14em;
  font-variant-numeric:tabular-nums; text-align:right;
  color:var(--b-ink);
}

/* ── FIGURE NUMBERS ───────────────────────────────────────────────
   Four pages carry three to six live simulations and the prose points
   at them with no way to name one. Numbering them in the gutter is the
   apparatus a paper already has. CSS counters, for the same reason the
   ghost numerals are: the number is derived from the figures and
   cannot go stale. A .duo is one figure with two panels — which is
   already how the text treats it. */
.section > .canvas-wrap,
.section > .duo:has(.canvas-wrap){ counter-increment:fig; }
/* .glass clips its children and every .canvas-wrap carries .glass, so
   the gutter label would be sheared off at the frame. */
.canvas-wrap{ overflow:visible !important; }
@media(min-width:1080px){
  .section > .canvas-wrap::after,
  .section > .duo:has(.canvas-wrap)::after{
    content:'FIG ' counter(fig,decimal-leading-zero);
    position:absolute; right:-3.1rem; top:0;
    font-family:var(--b-mono) !important;
    font-size:.52rem; letter-spacing:.1em; line-height:1;
    color:var(--b-faint);
    font-variant-numeric:tabular-nums;
    white-space:nowrap; pointer-events:none;
  }
  /* display:block !important fights the .glass::before kill above;
     inset:auto fights cv.html's inset:0, which would otherwise draw
     this bar INSIDE all three cv canvases. */
  .section > .canvas-wrap::before,
  .section > .duo:has(.canvas-wrap)::before{
    content:''; display:block !important;
    inset:auto;
    position:absolute; right:-3.1rem; top:.58rem;
    width:2.4rem; height:2px; background:var(--b-faint);
    pointer-events:none;
  }
}
/* The tally, where both counters are final. <footer> is the LAST CHILD
   of .page on all four pages — .page::after would land beneath the
   copyright line, so this hangs off the footer instead. Set at weight,
   not at mouse-print: this is a colophon, and it should look like one. */
.page > footer::before{
  content:counter(sec,decimal-leading-zero) ' SECTIONS \00b7 '
          counter(fig,decimal-leading-zero) ' FIGURES';
  display:block; margin-bottom:2.2rem; padding-top:.7rem;
  border-top:2px solid var(--b-ink);
  font-family:var(--b-mono) !important;
  font-size:clamp(1.1rem,3.4vw,2rem); font-weight:700;
  letter-spacing:.04em; line-height:1;
  color:var(--b-ink) !important;
  font-variant-numeric:tabular-nums; text-align:right;
}

/* ── KEYBOARD CIRCUIT ─────────────────────────────────────────────
   The site had no focus styling at all — one :focus rule exists and it
   is in an out-of-scope canvas scene. /p1 alone carries five sliders
   and ten buttons that told a keyboard user nothing about where they
   were. Drawn with outline, the one visual property the sledgehammer
   at the top of this file does not flatten, and offset OUTSIDE the
   element so it lands on --b-bg even on a card inverted to solid ink.
   :focus-visible, so a mouse click does not trigger it; a browser
   without it drops the block, which is the safe direction. */
:focus-visible{
  outline:3px solid var(--b-acc) !important;
  outline-offset:2px !important;
}
input[type=range]:focus-visible,
.btn:focus-visible,.mail-btn:focus-visible,.mail-copy:focus-visible,
.home-btn:focus-visible,.pcard:focus-visible,a.crow:focus-visible{
  outline-offset:4px !important;
}
/* the strip is flush to the viewport top; an outset ring on its slider
   is clipped away, so it goes inside. */
.scheme-strip input[type=range]:focus-visible{ outline-offset:-1px !important; }
/* the skip target is a whole column: offset 6px runs the ring off-canvas
   horizontally, so it sits flush. */
[tabindex="-1"]:focus-visible{
  outline-width:2px !important; outline-offset:0 !important;
}

/* ── SPAN BARS ────────────────────────────────────────────────────
   Thirteen entries state a date range and not one states a duration.
   Drawn to scale on ONE axis shared by all three groups: the PhD is
   47 months against Vodafone's 7, and the three Vienna roles in 2025
   are visibly concurrent rather than merely consecutive. Point events
   draw a minimum stub, which is also true. Values come from spans.js. */
.timeline-wrap{
  position:relative;
  background-image:repeating-linear-gradient(90deg,
    var(--b-line2) 0 1px, transparent 1px var(--yr-step,100%));
  background-origin:content-box; background-clip:content-box;
  background-repeat:repeat-x;
  background-position:var(--yr-off,0) 0;
}
.timeline-wrap[data-span]::before{
  content:attr(data-span);
  border-bottom:2px solid var(--b-ink); padding-bottom:.22rem;
  font-size:.54rem; letter-spacing:.14em; text-transform:uppercase;
  color:var(--b-faint) !important; font-variant-numeric:tabular-nums;
}
/* Four cascade fights, all real:
   - .glass::before{display:none!important} above (every entry is glass)
   - cv.html .entry::before{top:1.4rem} -> top:auto, or top+height+bottom
     is over-constrained, bottom is dropped, and all 13 bars render
     through the title text
   - cv.html :hover scale(1.4) -> a bar that swells 40% under the cursor
     misreports its own duration */
.timeline-wrap .entry.has-span::before{
  content:'' !important; display:block !important;
  position:absolute; z-index:2;
  top:auto !important; transform:none !important;
  border:0 !important; border-radius:0 !important; box-shadow:none !important;
  left:calc(var(--t0) * 100%); bottom:0;
  width:max(4px, calc((var(--t1) - var(--t0)) * 100%));
  height:3px; background:var(--b-faint) !important;
}
.timeline-wrap .entry.has-span.is-open::before{ background:var(--b-acc) !important; }
/* the blanket inversion rule uses `*`, which does not match pseudo-
   elements, so the bar would keep --b-faint and vanish into the block. */
.timeline-wrap .entry.has-span:hover::before{ background:var(--b-bg) !important; }
/* 135 months across a ~320px column is noise, not a grid. */
@media(max-width:600px){ .timeline-wrap{ background-image:none; } }
