/* "the swap": by day, sci-fi headings over a casual body; at night the two
   faces trade jobs and the headings and margin notes turn neon.
   Mode lives on <html data-mode="day|night">, set inline before paint. */

/* Self-hosted: the latin and latin-ext files from @fontsource-variable/recursive@5.3.0, @fontsource-variable/oxanium@5.3.0
   and @fontsource/instrument-serif@5.3.0 (Google's builds, all of Recursive's axes), under their Google family names. */
@font-face {
  font-family: "Recursive"; font-style: oblique 0deg 15deg; font-weight: 300 1000; font-display: swap;
  src: url(/fonts/recursive-latin-ext.woff2) format("woff2");
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family: "Recursive"; font-style: oblique 0deg 15deg; font-weight: 300 1000; font-display: swap;
  src: url(/fonts/recursive-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: "Oxanium"; font-style: normal; font-weight: 200 800; font-display: swap;
  src: url(/fonts/oxanium-latin-ext.woff2) format("woff2");
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family: "Oxanium"; font-style: normal; font-weight: 200 800; font-display: swap;
  src: url(/fonts/oxanium-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: "Instrument Serif"; font-style: italic; font-weight: 400; font-display: swap;
  src: url(/fonts/instrument-serif-latin-ext-italic.woff2) format("woff2");
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family: "Instrument Serif"; font-style: italic; font-weight: 400; font-display: swap;
  src: url(/fonts/instrument-serif-latin-italic.woff2) format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

:root {
  --bg: #F6F7F4;
  --fg: #16181A;
  --mute: #6A6F72;
  --rule: #D7DAD4;
  --pen: #2F4FC4;
  --eo: #00785A;
  --head: #16181A;
  --head-glow: none;
  --pen-glow: none;
  --body-glow: none;

  --font-scifi: "Oxanium", system-ui, sans-serif;
  --font-casual: "Recursive", system-ui, sans-serif;
  --font-note: "Instrument Serif", Georgia, serif;

  --measure: 36rem;
  color-scheme: light;
}
:root[data-mode="night"] {
  --bg: #0A0B12;
  --fg: #C9D3E6;
  --mute: #7C86A0;
  --rule: #23263A;
  --pen: #4FF0FF;
  --eo: #3FD9A5;
  --head: #FFB547;
  --head-glow: 0 0 1px #FFFFFF55, 0 0 6px #FFB547AA, 0 0 18px #FFB54766;
  --pen-glow: 0 0 6px #4FF0FF88;
  --body-glow: 0 0 6px #7FA8FF2E;
  color-scheme: dark;
}
@media (prefers-contrast: more) {
  :root[data-mode="night"] { --head-glow: none; --pen-glow: none; --body-glow: none; }
}

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-casual);
  font-variation-settings: "CASL" 1;
  font-size: 1.0625rem;
  line-height: 1.65;
  transition: background-color 0.4s ease, color 0.4s ease;
}
:root[data-mode="night"] body {
  font-family: var(--font-scifi);
  font-variation-settings: normal;
  font-size: 1.03rem;
  letter-spacing: 0.01em;
  text-shadow: var(--body-glow);
}

/* Heading face: anything with .hd, plus real headings. */
h1, h2, h3, .hd {
  font-family: var(--font-scifi);
  font-variation-settings: normal;
  font-weight: 600;
  letter-spacing: 0.03em;
}
:root[data-mode="night"] :is(h1, h2, h3, .hd) {
  font-family: var(--font-casual);
  font-variation-settings: "CASL" 1, "slnt" -12;
  font-weight: 880;
  letter-spacing: -0.01em;
}
h1, h2, h3 {
  color: var(--head);
  text-shadow: var(--head-glow);
  line-height: 1.15;
  text-wrap: balance;
  margin: 0;
}
h1 { font-size: clamp(1.9rem, 6vw, 2.5rem); transition: letter-spacing 0.3s ease; }
:root[data-mode="day"] h1:hover { letter-spacing: 0.09em; }
:root[data-mode="night"] h1:hover { animation: flicker 0.7s steps(1) 1; }
@keyframes flicker { 8% { opacity: 0.35; } 11% { opacity: 1; } 15% { opacity: 0.55; } 18%, 100% { opacity: 1; } }

a { color: inherit; }
/* Strikethroughs are pen marks: a slightly rising pen-colour line, drawn as a gradient
   (text-decoration can't slant). Padding + matching negative margin lets the stroke
   overhang the word without moving it. clone gives each wrapped line its own stroke. */
del, s {
  text-decoration: none;
  padding: 0 0.2em;
  margin: 0 -0.2em;
  background: linear-gradient(to bottom right, transparent calc(50% - 0.8px), var(--pen) calc(50% - 0.8px), var(--pen) calc(50% + 0.8px), transparent calc(50% + 0.8px)) center 60% / 100% 0.35em no-repeat;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}
a:focus-visible, button:focus-visible, input:focus-visible { outline: 2px solid var(--pen); outline-offset: 2px; }
/* js/nav.js focuses main after a page swap; the whole column needs no ring */
main:focus { outline: none; }

.skip { position: absolute; left: 1rem; top: -4rem; padding: 0.4rem 0.8rem; background: var(--fg); color: var(--bg); }
.skip:focus { top: 1rem; }

/* ---------- page frame ---------- */
.page {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 2rem max(16px, env(safe-area-inset-right, 0px)) 4rem max(16px, env(safe-area-inset-left, 0px));
  display: flex;
  flex-direction: column;
  gap: 2.5rem;
}
/* Posts get a wider frame so long reads scroll less; max-width keeps it inside small viewports. */
.page:has(.post) { max-width: 46rem; }
main { display: flex; flex-direction: column; gap: 2.5rem; }

.site-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem 1.2rem;
  padding-bottom: 0.8rem;
  border-bottom: 1px dashed var(--rule);
  font-size: 0.85rem;
}
.brand { text-decoration: none; color: var(--head); text-shadow: var(--head-glow); }

/* The dark toggle: a moon at night, a sun by day, inked and lit like the nav text.
   drop-shadow because text-shadow doesn't reach SVG strokes; the glows are none by day. */
.mode {
  appearance: none;
  -webkit-appearance: none;
  display: inline-grid;
  place-items: center;
  margin: 0;
  padding: 0.3rem;
  border: 0;
  border-radius: 999px;
  background: none;
  color: inherit;
  cursor: pointer;
  line-height: 0;
  filter: drop-shadow(var(--body-glow));
}
.mode svg { width: 1.15rem; height: 1.15rem; }
.mode-sun { display: none; }
:root[data-mode="day"] .mode-sun { display: block; }
:root[data-mode="day"] .mode-moon { display: none; }
.mode:hover { color: var(--pen); filter: drop-shadow(var(--pen-glow)); }
.nav a:hover { color: var(--pen); text-decoration-color: var(--pen); }
.nav a[aria-current] { text-decoration-color: var(--pen); text-decoration-thickness: 2px; text-shadow: var(--pen-glow); }

.site-foot { font-size: 0.75rem; color: var(--mute); border-top: 1px dashed var(--rule); padding-top: 0.8rem; }

/* ---------- home ---------- */
.intro { display: flex; flex-direction: column; gap: 1rem; }
.intro p { margin: 0; }

/* ---------- resume ---------- */
.resume { display: flex; flex-direction: column; gap: 1rem; }
.resume .label { margin: 0; }
.resume h2 { font-size: 1.4rem; }
.resume h3 { font-size: 1.05rem; }
.role { display: flex; flex-direction: column; gap: 0.4rem; border-left: 1px dashed var(--rule); padding-left: 1rem; }
.role ul { margin: 0; padding-left: 1.3rem; display: flex; flex-direction: column; gap: 0.35rem; }
.skills { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.2rem 0; }
.skills li:not(:last-child)::after { content: "·"; color: var(--mute); margin: 0 0.6rem; }
.label { font-size: 0.72rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--mute); text-shadow: none; margin-bottom: 0.8rem; }
.posts { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.6rem; }
.posts li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 0.9rem; }
.posts time { color: var(--mute); font-size: 0.8rem; font-variant-numeric: tabular-nums; }
.posts a { text-decoration: underline; text-decoration-color: var(--rule); text-underline-offset: 3px; }
.posts a:hover { text-decoration-color: var(--pen); }

/* ---------- margin notes: <span class="note"> or [text]{.note} in markdown ---------- */
.note {
  font-family: var(--font-note);
  font-style: italic;
  font-size: 1.15em;
  color: var(--pen);
  text-shadow: var(--pen-glow);
  display: inline-block;
  transform: rotate(-2deg);
  margin-left: 0.5rem;
  letter-spacing: 0;
}
.circle, .intro a, .prose a { border: 1.5px solid var(--pen); border-radius: 50% 45% 55% 48%; padding: 0 0.3rem; margin: 0 -0.3rem; }
:root[data-mode="night"] :is(.circle, .intro a, .prose a) { box-shadow: 0 0 8px #4FF0FF66; }

/* ---------- posts ---------- */
.post { display: flex; flex-direction: column; gap: 2rem; }
.post-head { display: flex; flex-direction: column; gap: 0.5rem; }
.post-note { border-left: 2px solid var(--rule); padding: 0.6rem 0 0.6rem 1rem; color: var(--mute); }
.meta { margin: 0; color: var(--mute); font-size: 0.8rem; font-variant-numeric: tabular-nums; }

.prose > * { margin: 0; }
.prose > * + * { margin-top: 1.3rem; }
.prose h2 { font-size: 1.4rem; margin-top: 2.6rem; }
.prose h3 { font-size: 1.15rem; margin-top: 2rem; }
.intro a, .prose a { color: var(--pen); text-decoration: none; }
.prose ul, .prose ol { padding-left: 1.3rem; }
.prose blockquote { border-left: 2px solid var(--rule); padding-left: 1rem; color: var(--mute); }
.prose hr { border: 0; border-top: 1px dashed var(--rule); margin-block: 2.5rem; }
.prose img { max-width: 100%; height: auto; }

/* Pull quote: <figure class="quote"><blockquote>…</blockquote><figcaption>…</figcaption></figure>.
   Set in the swapped face (sci-fi by day, casual at night) so quoted voices read as "not mine". */
.prose > .quote { position: relative; margin-block: 3rem; padding: 1.8rem 1.5rem 1.3rem 2rem; border: 1px solid var(--rule); border-left: 3px solid var(--pen); }
.quote::before {
  content: "\201C";
  position: absolute; left: 0.9rem; top: -1.25rem;
  padding: 0 0.3rem;
  background: var(--bg);
  /* Short line box so its background only masks the border, not the first line. */
  font-family: var(--font-note); font-size: 3.8rem; line-height: 0.75; height: 2.2rem;
  color: var(--head); text-shadow: var(--head-glow);
}
.quote blockquote { margin: 0; border: 0; padding: 0; color: var(--fg); font-family: var(--font-scifi); font-variation-settings: normal; letter-spacing: 0.02em; line-height: 1.7; text-wrap: pretty; }
:root[data-mode="night"] .quote blockquote { font-family: var(--font-casual); font-variation-settings: "CASL" 1, "slnt" -8; letter-spacing: 0; }
.quote figcaption { margin-top: 1.1rem; text-align: right; font-size: 0.72rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--mute); text-shadow: none; }
.quote cite { font-style: normal; color: var(--pen); }
@media (max-width: 32rem) { .prose > .quote { padding: 1.5rem 1rem 1.1rem 1.2rem; } }

/* Code is always technical, whatever the mode. */
code, pre {
  font-family: var(--font-casual), ui-monospace, Menlo, monospace;
  font-variation-settings: "MONO" 1, "CASL" 0;
  font-size: 0.88em;
  text-shadow: none;
}
:not(pre) > code { background: var(--rule); padding: 0.1em 0.35em; border-radius: 3px; }
pre { overflow-x: auto; padding: 1rem; border: 1px solid var(--rule); border-radius: 4px; line-height: 1.55; }

/* ---------- figures: raw <figure class="fig"> bar charts in posts. Bars are labelled, so colour never carries meaning alone. ---------- */
:root {
  --fig-a: #B04220; --fig-a-lt: #D9A18B;
  --fig-b: #00996B; --fig-b-lt: #7FCCB5;
  --fig-c: #3B4FBF;
  --fig-track: #E6E8E3; --fig-good: #00785A; --fig-bad: #A8341F;
}
:root[data-mode="night"] {
  --fig-a: #FF8A5C; --fig-a-lt: #8A4A35;
  --fig-b: #3FD9A5; --fig-b-lt: #1F6B55;
  --fig-c: #7F95FF;
  --fig-track: #161A28; --fig-good: #3FD9A5; --fig-bad: #FF7A66;
}
.prose .fig { margin: 2.75rem 0; display: flex; flex-direction: column; gap: 0.9rem; text-shadow: none; }
.fig :is(.fig-num, .fig-sub, .fig-key, .fig-k, .fig-v, .fig-group, .fig-facet-h, .fig-stat b, .fig-status dt) {
  font-family: var(--font-casual), ui-monospace, Menlo, monospace;
  font-variation-settings: "MONO" 1, "CASL" 0;
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
}
.fig-head { display: flex; flex-direction: column; gap: 0.3rem; border-bottom: 1px solid var(--rule); padding-bottom: 0.6rem; }
.fig-num, .fig-group { letter-spacing: 0.1em; text-transform: uppercase; color: var(--mute); }
.fig-title { font-family: var(--font-scifi); font-size: 1.05rem; font-weight: 500; line-height: 1.25; color: var(--head); }
:root[data-mode="night"] .fig-title { font-family: var(--font-casual); }
.fig-sub { line-height: 1.6; color: var(--mute); }
.fig-legend { display: flex; flex-wrap: wrap; gap: 0.3rem 1.1rem; }
.fig-key { display: inline-flex; align-items: center; gap: 0.4rem; color: var(--mute); }
.fig-key::before { content: ""; width: 0.7rem; height: 0.7rem; background: var(--c, var(--fig-a)); flex: none; }
.fig-rows { display: flex; flex-direction: column; gap: 0.18rem; }
.fig-row { display: grid; grid-template-columns: var(--fig-label, 6.5rem) 1fr auto; align-items: center; gap: 0.55rem; }
.fig-k { color: var(--mute); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fig-v { text-align: right; min-width: 4.2rem; }
.fig-row--best .fig-v { font-weight: 600; }
.fig-track { position: relative; height: 0.85rem; background: var(--fig-track); min-width: 0; }
.fig-bar { position: absolute; inset: 0 auto 0 0; width: var(--w, 0%); background: var(--c, var(--fig-a)); }
.fig-bar--ghost { background: var(--fig-a-lt); }
.fig-group { margin-top: 0.7rem; }
.fig-facets { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: 1rem 1.4rem; }
.fig-facet { min-width: 0; }
.fig-facet-h { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; border-bottom: 1px solid var(--rule); padding-bottom: 0.25rem; margin-bottom: 0.35rem; }
.fig-facet-h span { color: var(--mute); white-space: nowrap; }
.fig-stats { display: flex; flex-wrap: wrap; gap: 0.9rem 1.8rem; border-top: 1px solid var(--rule); padding-top: 0.8rem; }
.fig-stat { display: flex; flex-direction: column; gap: 0.1rem; }
.fig .fig-stat b { font-size: 1rem; font-weight: 600; }
.fig-stat span { font-size: 0.78rem; color: var(--mute); line-height: 1.35; }
.fig-status { display: grid; grid-template-columns: auto 1fr; gap: 0.35rem 0.7rem; align-items: baseline; margin: 0; }
.fig-status dt { white-space: nowrap; }
.fig-status dd { margin: 0; font-size: 0.85rem; color: var(--mute); line-height: 1.45; }
.fig-ok { color: var(--fig-good); font-weight: 600; }
.fig-no { color: var(--fig-bad); font-weight: 600; }
.fig-note { font-size: 0.85rem; line-height: 1.55; color: var(--mute); border-left: 2px solid var(--rule); padding-left: 0.8rem; }
.fig-note b { color: var(--fg); font-weight: 600; }
@media (max-width: 32rem) {
  /* Stack rather than truncate: label and value share a line, bar below. */
  .fig-row { grid-template-columns: 1fr auto; grid-template-areas: "k v" "t t"; row-gap: 0.15rem; }
  .fig-k { grid-area: k; white-space: normal; }
  .fig-v { grid-area: v; }
  .fig-track { grid-area: t; }
  .fig-facets { grid-template-columns: 1fr; }
}

/* ---------- things: a printout the pen annotates on hover, focus or tap ---------- */
.print { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; font-size: 0.92rem; }
.row { position: relative; display: grid; grid-template-columns: 3.2rem auto 1fr auto; align-items: baseline; gap: 0 0.6rem; padding: 0.45rem 0; cursor: pointer; }
.row .yr, .row .tg { color: var(--mute); font-size: 0.82rem; font-variant-numeric: tabular-nums; }
.row .dots { border-bottom: 2px dotted var(--rule); align-self: center; translate: 0 0.25rem; }
.row a.nm { color: var(--pen); text-decoration: none; }
.nm { position: relative; padding: 0 0.15rem; }
.sub, .ann, .play { grid-column: 2 / -1; }
.sub { color: var(--mute); font-size: 0.85rem; }
.sub:empty { display: none; }
.sub span + span::before { content: " · "; }
.ring, .arrow { position: absolute; overflow: visible; pointer-events: none; fill: none; stroke: var(--pen); stroke-width: 1.6; stroke-linecap: round; filter: drop-shadow(var(--pen-glow)); }
.ring { inset: -0.45rem -0.7rem; width: calc(100% + 1.4rem); height: calc(100% + 0.9rem); }
.ring ellipse, .arrow path { stroke-dasharray: 1; stroke-dashoffset: 1; }
/* Undrawn strokes still leave a round-cap dot, so the ring hides until active. */
.ring { opacity: 0; }
:is(.row:hover, .row:focus-within, .row.on) .ring { opacity: 1; }
/* Hidden notes stay readable to screen readers. */
.ann { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.ann .arrow { left: 0.2rem; top: -0.1rem; width: 2rem; height: 1.6rem; }
:is(.row:hover, .row:focus-within, .row.on) .ann {
  position: relative; width: auto; height: auto; overflow: visible; clip-path: none;
  display: flex; padding: 0.5rem 0 0.2rem 2.4rem; animation: fade 0.25s ease;
}
:is(.row:hover, .row:focus-within, .row.on) :is(.ring ellipse, .arrow path) { animation: draw 0.55s ease forwards; }
:is(.row:hover, .row:focus-within, .row.on) .arrow path + path { animation-delay: 0.35s; animation-duration: 0.2s; }
@keyframes draw { to { stroke-dashoffset: 0; } }
@keyframes fade { from { opacity: 0; } }
/* Margin notes only once the viewport has room beside the 36rem column. */
@media (min-width: 72rem) {
  :is(.row:hover, .row:focus-within, .row.on) .ann { position: absolute; left: calc(100% + 1.2rem); top: 0.1rem; width: 15rem; padding: 0.1rem 0 0 2.4rem; }
  .ann .arrow { left: 0; top: 0.2rem; transform: scaleY(-1); }
}
.play { margin-top: 0.3rem; }
.play summary { font-size: 0.8rem; color: var(--pen); cursor: pointer; width: fit-content; }
.play elm-island { margin-top: 0.6rem; }

/* ---------- tooltips: <span class="tip"><span class="tip-tg">…</span><span class="tip-note" role="tooltip">arrow + .note</span></span>,
   open on hover, focus or tap (js/tips.js), shut by Escape ---------- */
.tip { position: relative; display: inline-block; }
.tip-tg { cursor: help; }
.tip-tg:focus-visible { outline: 2px solid var(--pen); outline-offset: 2px; }
.tip-eo .tip-tg { color: var(--eo); }
/* Hidden notes stay readable to screen readers, so aria-describedby still works. */
.tip-note { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
/* Opens to the right of the trigger on its own line, so it covers no title; js/tips.js flips it to hang
   below-left (.tip-flip) when there's no room on the right, so it doesn't push the page sideways. */
:is(.tip:hover, .tip:focus-within, .tip.on):not(.shut) .tip-note {
  top: 0; left: 100%; right: auto; z-index: 1;
  width: max-content; max-width: 15rem; height: auto; overflow: visible; clip-path: none;
  display: flex; padding: 0 0.4rem 0 2.4rem; background: var(--bg); animation: fade 0.25s ease;
}
.tip-note .arrow { left: 0.1rem; top: 0; width: 2rem; height: 1.6rem; transform: rotate(-45deg); }
:is(.tip:hover, .tip:focus-within, .tip.on):not(.shut).tip-flip .tip-note { top: 100%; left: auto; right: 0; padding: 0.5rem 2.4rem 0.2rem 0.4rem; }
.tip-flip .tip-note .arrow { left: auto; right: 0.2rem; top: -0.1rem; transform: scaleX(-1); }
.tip-note .note { margin-left: 0; white-space: nowrap; }
:is(.tip:hover, .tip:focus-within, .tip.on):not(.shut) .tip-note .arrow path { animation: draw 0.55s ease forwards; }
:is(.tip:hover, .tip:focus-within, .tip.on):not(.shut) .tip-note .arrow path + path { animation-delay: 0.35s; animation-duration: 0.2s; }

/* ---------- elm islands ---------- */
elm-island { display: block; }
.weather { display: flex; flex-direction: column; gap: 0.8rem; }
.weather img { max-width: 100%; height: auto; }
.weather button { font: inherit; color: var(--pen); background: none; border: 1px solid var(--rule); border-radius: 999px; padding: 0.1rem 0.65rem; cursor: pointer; width: fit-content; }
.weather button:hover { border-color: var(--pen); }
:root[data-mode="night"] .weather button:hover { box-shadow: 0 0 6px #4FF0FF55; }
.weather a { color: inherit; }
/* OSV chart (elm/Osv.elm): pen-coloured bars over ruled ticks. Its text is in viewBox units, sized to stay
   about 11px at phone width. */
.osv { display: flex; flex-direction: column; gap: 0.8rem; }
.osv-filters { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.osv button, .deck-bar button { font: inherit; color: var(--pen); background: none; border: 1px solid var(--rule); border-radius: 999px; padding: 0.1rem 0.65rem; cursor: pointer; }
.osv button:hover, .deck-bar button:hover { border-color: var(--pen); }
:root[data-mode="night"] :is(.osv, .deck-bar) button:hover { box-shadow: 0 0 6px #4FF0FF55; }
.osv button[aria-pressed="true"] { background: var(--pen); color: var(--bg); border-color: var(--pen); }
.osv-pick { margin: 0; padding: 0.6rem 0.8rem; border: 1px solid var(--rule); border-radius: 4px; display: grid; grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr)); gap: 0.2rem 1rem; font-size: 0.85rem; }
.osv-pick :is(legend, .meta) { grid-column: 1 / -1; }
.osv-pick legend { padding: 0 0.3rem; color: var(--mute); }
.osv-pick input { accent-color: var(--pen); }
.osv-chart { width: 100%; height: auto; overflow: visible; text-shadow: none; }
.osv-chart text { font-size: 19px; font-variant-numeric: tabular-nums; }
.osv-bar { fill: var(--pen); }
:root[data-mode="night"] .osv-bar { filter: drop-shadow(var(--pen-glow)); }
.osv-grid { stroke: var(--rule); stroke-width: 1; }
.osv-axis { stroke: var(--mute); stroke-width: 1.5; }
.osv-tick, .osv-year { fill: var(--mute); }
.osv-scale { fill: var(--fg); }
.osv a { color: inherit; }

/* ---------- talks: a deck's slides, stacked and all readable until js/deck.js marks it data-live and shows one
   at a time under the Deck island's bar (elm/Deck.elm) ---------- */
.page:has(.deck) { max-width: 46rem; }
.deck { display: flex; flex-direction: column; gap: 1.5rem; }
/* Opaque, so the night skyline stays behind the slide. */
.slide { container-type: inline-size; background: var(--bg); border: 1px solid var(--rule); border-radius: 4px; }
/* Every scaffold wraps its content in .slide-body. Its text is sized to the slide's width (its headings, tables
   and code in em), so in fullscreen all of it grows with the slide. */
.slide-body { font-size: max(0.95rem, 2.4cqi); padding: 1.6em 1.8em; display: flex; flex-direction: column; gap: 0.9em; }
.slide-body > * { margin: 0; }
.slide h1 { font-size: 2.2em; }
.slide h2 { font-size: 1.6em; }
.slide h3 { font-size: 1.2em; }
.slide pre { font-size: 0.8em; }
.slide table { font-size: 0.85em; border-collapse: collapse; }
.slide :is(th, td) { text-align: left; vertical-align: top; padding: 0.35em 0.8em 0.35em 0; border-bottom: 1px solid var(--rule); }
.slide th { color: var(--mute); font-weight: 600; }
.slide a { color: var(--pen); }
.deck[data-live] > .slide:not([data-current]) { display: none; }
.deck:not([data-live]) > elm-island[module="Deck"] { display: none; }
.deck-bar { display: flex; align-items: center; gap: 0.6rem; font-size: 0.85rem; transition: opacity 0.4s ease; }
/* Faded, not hidden, so Tab still reaches it, and any focus inside brings it back. */
.deck[data-idle] .deck-bar:not(:focus-within) { opacity: 0; }
.deck-bar button { line-height: 1.4; }
.deck-bar button[aria-disabled="true"] { opacity: 0.45; cursor: default; }
.deck-fs { display: inline-grid; place-items: center; }
.deck-fs svg { width: 1em; height: 1.4em; }
.deck[data-nofs] .deck-fs { display: none; }
.deck-count { min-width: 4.5em; text-align: center; color: var(--mute); font-variant-numeric: tabular-nums; }
.deck-progress { flex: 1; height: 2px; background: var(--rule); }
.deck-progress-fill { height: 100%; background: var(--pen); box-shadow: var(--pen-glow); transition: width 0.3s ease; }
/* Fullscreen: the slide takes the whole width (and so scales up) and the bar stays below it. */
.deck:fullscreen { background: var(--bg); padding: 2vh 2vw; gap: 1rem; overflow: auto; }
.deck:fullscreen > .slide { flex: 1 0 auto; }
/* Skyline: pinned behind the page, night only. Don't give html a background or it covers this. */
.skyline {
  position: fixed; inset: auto 0 0 0; width: 100%; height: 42vh; z-index: -1; pointer-events: none;
  opacity: 0.85; animation: fade 2s ease;
  background: linear-gradient(to top, #4FF0FF26, #7FA8FF10 45%, transparent 85%);
}
:root:not([data-mode="night"]) :is(.skyline, .stars) { display: none; }
.stars { position: fixed; inset: 0 0 auto 0; width: 100%; height: 60vh; z-index: -1; pointer-events: none; fill: var(--fg); animation: fade 3s ease; }
.stars .bright { filter: drop-shadow(0 0 2px #C9D3E6CC); }
.stars .moon { fill: #E8EEF8; }
.stars .crater { fill: #AEB9CF; opacity: 0.35; }
.stars .twinkle { animation: twinkle 4s ease-in-out infinite; }
@keyframes twinkle { 50% { opacity: 0.1; } }
/* On articles the sky clears behind the reading column rather than squeezing like the city: the same
   50rem gap Skyline.elm leaves (column plus margin), from the same width the city opens at. */
@media (min-width: 55rem) {
  :root:has(.post) .stars { mask-image: linear-gradient(to right, #000 calc(50% - 27.5rem), transparent calc(50% - 25rem), transparent calc(50% + 25rem), #000 calc(50% + 27.5rem)); }
}
/* On any page, while the page's text overlaps the city (js/city.js sets data-city), the city parts around
   the reading column in perspective; Skyline.elm gives every shape its opened position as --open
   (a matrix in viewBox units, from the viewBox origin). */
.skyline :is(rect, line, circle) { transform-origin: 0px 0px; transition: transform 1.8s cubic-bezier(0.2, 0.7, 0.2, 1) 0.4s; }
:root[data-city="open"] .skyline :is(rect, line, circle) { transform: var(--open, none); }
.skyline-far { fill: #161B33; opacity: 0.7; }
.skyline-near { fill: #0F111C; }
.skyline-lit { fill: var(--fg); filter: drop-shadow(0 0 2px #C9D3E666); }
.skyline-lit .amber { fill: var(--head); }
.skyline .sign { stroke: var(--head); stroke-width: 4; stroke-dasharray: 4 1.5; filter: drop-shadow(0 0 2px var(--head)) drop-shadow(0 0 6px var(--head)); animation: buzz 9s steps(1) infinite; }
.skyline .sign.cyan { stroke: var(--pen); filter: drop-shadow(0 0 2px var(--pen)) drop-shadow(0 0 6px var(--pen)); }
.skyline .antenna line { stroke: #1E2238; stroke-width: 0.8; }
.skyline .antenna circle { fill: #FF3B3B; filter: drop-shadow(0 0 2px #FF3B3B); animation: blink 2.4s steps(1) infinite; }
@keyframes buzz { 0%, 100% { opacity: 1; } 91% { opacity: 0.3; } 92% { opacity: 1; } 94% { opacity: 0.5; } 95% { opacity: 1; } }
@keyframes blink { 50% { opacity: 0.15; } }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; }
  /* No drawing animation: marks appear whole. */
  .ring ellipse, .arrow path { stroke-dashoffset: 0; }
}
