/* jonnyo.com
   Sections are colour-coded like the four twisted pairs in a Cat6 cable.
   Headings and code: Martian Mono. Reading text: Atkinson Hyperlegible Next. */

@font-face {
  font-family: "Martian Mono";
  src: url("../fonts/martian-mono.53a4a47c6334.woff2") format("woff2-variations");
  font-weight: 100 800; font-stretch: 75% 112.5%; font-display: swap;
}
@font-face {
  font-family: "Atkinson Next";
  src: url("../fonts/atkinson-next.825fabef695f.woff2") format("woff2-variations");
  font-weight: 200 800; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Atkinson Next";
  src: url("../fonts/atkinson-next-italic.740db8084ae9.woff2") format("woff2-variations");
  font-weight: 200 800; font-style: italic; font-display: swap;
}

/* The home page deck uses the Nocturne design system's faces, self-hosted the
   same way. Licences in static/fonts/LICENSE-google-fonts.txt. */
@font-face { font-family: "Inter"; src: url("../fonts/inter.260c81a4759b.woff2") format("woff2"); font-weight: 100 900; font-display: swap; }
@font-face { font-family: "IBM Plex Mono"; src: url("../fonts/ibm-plex-mono-500.b1c8a895f5fd.woff2") format("woff2"); font-weight: 500; font-display: swap; }
@font-face { font-family: "Doto"; src: url("../fonts/doto-800.eff107c8d7d5.woff2") format("woff2"); font-weight: 800; font-display: swap; }
@font-face { font-family: "Saira Stencil One"; src: url("../fonts/saira-stencil-one.1071c187a8db.woff2") format("woff2"); font-weight: 400; font-display: swap; }
@font-face { font-family: "Instrument Serif"; src: url("../fonts/instrument-serif-italic.bc891bdd08ad.woff2") format("woff2"); font-weight: 400; font-style: italic; font-display: swap; }

/* ---- Tokens ------------------------------------------------------------ */
:root {
  --paper: #eef1f4;
  --surface: #ffffff;
  --ink: #17202b;
  --muted: #56626f;
  --rule: #c8d0d8;
  --rule-strong: #95a1ad;
  --blue: #1d5fb4;
  --orange: #bf5610;
  --green: #2a7a4b;
  --brown: #7a4e2a;
  --amber: #a86a0c;
  --link: #1d5fb4;
  --pair-white: #ffffff;
  --code-bg: #1b2430;
  --code-bar: #243141;
  --code-ink: #dce3ea;
  --led: #2fbf62;

  --mono: "Martian Mono", ui-monospace, "Cascadia Mono", Menlo, Consolas, monospace;
  --sans: "Atkinson Next", "Atkinson Hyperlegible", system-ui, -apple-system, "Segoe UI", sans-serif;
  --measure: 68ch;
  --pad: clamp(1rem, 4vw, 2.5rem);
  --accent: var(--blue);
  color-scheme: light;
}
[data-theme="dark"] {
  --paper: #12181f; --surface: #1a222c; --ink: #e2e8ee; --muted: #9aa7b4;
  --rule: #2a3542; --rule-strong: #4a5869;
  --blue: #5d9deb; --orange: #ea8b45; --green: #56b884; --brown: #c08a5a; --amber: #e0a43c;
  --link: #7db2f2; --pair-white: #d0d6dc;
  --code-bg: #0c1117; --code-bar: #161e28;
  color-scheme: dark;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #12181f; --surface: #1a222c; --ink: #e2e8ee; --muted: #9aa7b4;
    --rule: #2a3542; --rule-strong: #4a5869;
    --blue: #5d9deb; --orange: #ea8b45; --green: #56b884; --brown: #c08a5a; --amber: #e0a43c;
    --link: #7db2f2; --pair-white: #d0d6dc;
    --code-bg: #0c1117; --code-bar: #161e28;
    color-scheme: dark;
  }
}
[data-accent="blue"]   { --accent: var(--blue); }
[data-accent="orange"] { --accent: var(--orange); }
[data-accent="green"]  { --accent: var(--green); }
[data-accent="brown"]  { --accent: var(--brown); }

/* ---- Base -------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-padding-top: 1.5rem; }
body {
  margin: 0; background: var(--paper); color: var(--ink);
  font: 400 1.125rem/1.6 var(--sans);
  text-rendering: optimizeLegibility;
  display: flex; flex-direction: column; min-height: 100vh;
}
main { flex: 1; }
img, svg, video { max-width: 100%; height: auto; }
a { color: var(--link); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { text-decoration-thickness: 2px; }
:focus-visible { outline: 2px solid var(--link); outline-offset: 3px; border-radius: 2px; }
::selection { background: color-mix(in srgb, var(--accent) 30%, transparent); }

h1, h2 { font-family: var(--mono); font-stretch: 87.5%; letter-spacing: -0.01em; line-height: 1.2; text-wrap: balance; }
h1 { font-size: clamp(1.85rem, 1.3rem + 2.2vw, 2.65rem); font-weight: 600; margin: 0 0 1rem; }
h2 { font-size: 1.35rem; font-weight: 600; margin: 2.5rem 0 .75rem; }
h3 { font-size: 1.15rem; font-weight: 700; margin: 1.75rem 0 .5rem; line-height: 1.35; }

.wrap { width: 100%; max-width: 74rem; margin-inline: auto; padding-inline: var(--pad); }
.wrap.narrow { max-width: 46rem; }
.visually-hidden { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.skip { position: absolute; left: .5rem; top: -3rem; background: var(--ink); color: var(--paper); padding: .5rem .8rem; z-index: 10; }
.skip:focus { top: .5rem; }
.lede { font-size: 1.2rem; color: var(--muted); max-width: 60ch; margin: 0 0 1rem; }
.lede p { margin: 0; }
.empty, .filter-note { color: var(--muted); }

/* A twisted pair: solid conductor above, striped conductor below. */
.pair {
  display: inline-block; flex: none; width: 1.4rem; height: 7px; vertical-align: middle;
  background:
    linear-gradient(var(--accent), var(--accent)) top / 100% 3px no-repeat,
    repeating-linear-gradient(90deg, var(--accent) 0 5px, var(--pair-white) 5px 9px) bottom / 100% 3px no-repeat;
  box-shadow: 0 0 0 .5px color-mix(in srgb, var(--ink) 15%, transparent);
  border-radius: 1px;
}
.pair--wide { display: block; width: 100%; height: 9px;
  background:
    linear-gradient(var(--accent), var(--accent)) top / 100% 4px no-repeat,
    repeating-linear-gradient(90deg, var(--accent) 0 18px, var(--pair-white) 18px 30px) bottom / 100% 4px no-repeat;
}

/* ---- Buttons ----------------------------------------------------------- */
.button {
  display: inline-block; padding: .6rem 1.05rem; border: 1.5px solid var(--ink); border-radius: 3px;
  background: var(--ink); color: var(--paper); font-weight: 700; font-size: 1rem; text-decoration: none; line-height: 1.3;
}
.button:hover { background: transparent; color: var(--ink); }
.button--quiet { background: transparent; color: var(--ink); border-color: var(--rule-strong); }
.button--quiet:hover { border-color: var(--ink); }
.status { display: flex; align-items: center; gap: .55rem; color: var(--muted); font-size: 1rem; margin: 1rem 0 0; }
.status__led { width: .6rem; height: .6rem; border-radius: 50%; background: var(--led); box-shadow: 0 0 0 3px color-mix(in srgb, var(--led) 25%, transparent); }

/* ---- Masthead ---------------------------------------------------------- */
.masthead { border-bottom: 1px solid var(--rule); background: var(--paper); }
.masthead__inner { max-width: 74rem; margin-inline: auto; padding: .9rem var(--pad); display: flex; align-items: center; justify-content: space-between; gap: 1rem 2rem; flex-wrap: wrap; }
.mark { display: flex; align-items: center; gap: .7rem; color: var(--ink); text-decoration: none; }
.mark__logo { height: 2.5rem; width: auto; display: block; }
.mark__logo--dark { display: none; }
[data-theme="dark"] .mark__logo--light { display: none; }
[data-theme="dark"] .mark__logo--dark { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .mark__logo--light { display: none; }
  :root:not([data-theme="light"]) .mark__logo--dark { display: block; }
}
.mark__text { font-family: var(--mono); font-weight: 600; font-size: 1.05rem; letter-spacing: -0.02em; }
.mark__pins { width: 1.9rem; height: auto; }
.pin--white { fill: var(--pair-white); stroke: var(--rule-strong); stroke-width: .6; }
.pin--orange { fill: var(--orange); } .pin--green { fill: var(--green); }
.pin--blue { fill: var(--blue); } .pin--brown { fill: var(--brown); }
.nav { display: flex; align-items: center; gap: .25rem 1.4rem; flex-wrap: wrap; }
.nav__link { display: inline-flex; align-items: center; gap: .5rem; color: var(--ink); text-decoration: none; font-size: 1rem; padding: .35rem 0; border-bottom: 2px solid transparent; background: none; border-top: 0; border-inline: 0; cursor: pointer; font-family: inherit; }
.nav__link:hover { border-bottom-color: var(--rule-strong); }
.nav__link[aria-current="page"] { border-bottom-color: var(--accent); font-weight: 700; }
.nav__icon { color: var(--muted); padding-inline: .15rem; }
.nav__icon:hover { color: var(--ink); }

/* ---- Home: the card deck ----------------------------------------------- */
/* Follows the "Jonnyo.com homepage redesign" project made in Claude Design,
   on its Nocturne design system. The deck runs on that system's dark palette
   whichever theme the rest of the site is in: the photos, the glow and the
   gradients are all built for a dark ground. Site tokens are re-pointed here
   too, so shared pieces (.status, focus rings) come along. */
body.is-home {
  --n-accent: #9184d9; --n-accent-200: #e7e5fe; --n-accent-300: #d2cefd;
  --n-n300: #cfd3e5; --n-n400: #b2b6ca; --n-n700: #595d6c; --n-n900: #292b31;
  --n-card: 360px; --n-gap: 16.8px; --n-pad: 22.4px; --n-rail-pad: 48px; --n-title-line: 45%;
  --n-shadow-sm: 0 0 0 1px #3f424d;
  --n-shadow-md: 0 0 0 1px #595d6c, 0 6px 18px rgba(0, 0, 0, .55);

  --paper: #161826; --surface: #232532; --ink: #e9e9ed; --muted: #b2b6ca;
  --rule: #3f424d; --rule-strong: #595d6c; --link: #d2cefd; --accent: #9184d9;
  color-scheme: dark;
  font-family: "Inter", var(--sans);
  overflow-x: hidden;
}
body.is-home .site-footer { display: none; }
body.is-home main { display: flex; flex-direction: column; min-height: 0; }
body.is-home::before {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background: radial-gradient(120% 80% at 0% 0%, var(--n-n900) 0%, var(--paper) 60%);
}

.deck { flex: 1; min-height: 0; display: flex; flex-direction: column; justify-content: center; gap: var(--n-pad); padding-block: var(--n-pad); }
.deck__rail {
  display: flex; gap: var(--n-gap); overflow-x: auto; overflow-y: hidden;
  padding: 11.2px var(--n-rail-pad) 16.8px;
  scroll-snap-type: x mandatory; scroll-padding-inline: var(--n-rail-pad); overscroll-behavior-x: contain;
  scrollbar-color: var(--n-n700) transparent;
}
.deck__rail::-webkit-scrollbar { height: 6px; }
.deck__rail::-webkit-scrollbar-thumb { background: var(--n-n700); border-radius: 3px; }
.deck__rail::-webkit-scrollbar-track, .deck__rail::-webkit-scrollbar-corner { background: transparent; }
.deck__rail:focus-visible { outline-offset: -2px; }

.dcard {
  flex: 0 0 var(--n-card); height: min(640px, 78vh); scroll-snap-align: start;
  position: relative; overflow: hidden; border-radius: 14px;
  background: var(--paper); box-shadow: var(--n-shadow-sm);
  display: flex; flex-direction: column; justify-content: space-between;
}
.dcard--intro { background: linear-gradient(180deg, var(--surface) 0%, var(--paper) 100%); }
/* Flat, to match the ground the About photo's own fade was baked against. */
.dcard--about { background: #161825; }
.dcard--intro { padding: var(--n-pad) var(--n-pad) calc(var(--n-pad) * 1.5); }
/* Two rows, so every headline bottoms out on the same line however big its
   face is and however many lines it runs to. What follows hugs the card's
   bottom on a section card, and the headline on the About card. */
.dcard__inner { position: relative; flex: 1; min-height: 0; display: grid; grid-template-rows: var(--n-title-line) minmax(0, 1fr); padding: var(--n-pad); }
.dcard__title { align-self: end; }
.dcard__inner .dcard__foot { align-self: end; max-height: 100%; }
.dcard__inner .dcard__mid { align-self: start; padding-top: var(--n-gap); }
.dcard__mid { display: flex; flex-direction: column; gap: var(--n-gap); min-height: 0; overflow-y: auto; scrollbar-width: thin; }
.deck__end { flex: 0 0 1px; }
.dcard__top { flex: none; }

/* The photo sits behind the top of the card. The files are supplied already
   cropped to the slot and already shaded, so nothing is applied on top of
   them here: no greyscale, no fade, no re-crop. */
.dcard__photo { position: absolute; inset: 0 0 30% 0; }
.dcard__photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.dcard__photo--portrait { inset: 0 0 25% 0; }
.dcard__credit { position: absolute; top: 8px; left: 8px; font-size: 10px; line-height: 1.3; color: var(--n-n400); opacity: .75; }

/* Card one: the wordmark, with the o as a lit ring. */
.brand { margin: 0; line-height: 0; }
.brand__logo { display: block; width: 100%; max-width: 280px; height: auto; }
.dcard__hair { display: block; width: 120px; height: 1px; background: linear-gradient(90deg, var(--n-accent) 0%, transparent 100%); }
.dcard__lede { font-size: 15px; line-height: 1.55; color: var(--n-n300); max-width: 26ch; text-wrap: pretty; }
.dcard__lede p { margin: 0 0 .6rem; }
.dcard__lede p:last-child { margin-bottom: 0; }
.dcard__cue { margin: 0; display: flex; align-items: center; gap: 8.4px; font-size: 13px; color: var(--n-n400); }
.dcard__cue-line { width: 32px; height: 1px; background: linear-gradient(90deg, var(--n-n400), transparent); }

/* Section cards. Each section's display face is keyed to its cable-pair
   colour: blue is a terminal, orange a dot matrix, green a stencil, brown a
   serif. A section on any other accent keeps the site's own heading face. */
.dcard__title { margin: 0; line-height: 1.15; color: var(--ink); font-family: inherit; }
.dcard__title a { color: inherit; text-decoration: none; }
.dcard__title a:hover { color: var(--n-accent-200); }
.dcard__sigil, .dcard__caret { display: none; }
[data-accent="blue"] .dcard__title { font-family: "IBM Plex Mono", var(--mono); font-weight: 500; font-size: 34px; letter-spacing: -.01em; }
[data-accent="blue"] .dcard__sigil { display: inline; color: var(--n-accent); }
[data-accent="blue"] .dcard__caret { display: inline-block; width: .55em; height: .95em; margin-left: .12em; vertical-align: -.12em; background: var(--n-accent); }
[data-accent="orange"] .dcard__title { font-family: "Doto", var(--mono); font-weight: 800; font-size: 34px; line-height: 1; letter-spacing: .02em; text-transform: uppercase; text-shadow: 0 0 12px color-mix(in oklch, var(--n-accent) 60%, transparent); }
[data-accent="green"] .dcard__title { font-family: "Saira Stencil One", var(--sans); font-size: 39px; line-height: 1; letter-spacing: .01em; text-transform: uppercase; overflow-wrap: anywhere; }
[data-accent="brown"] .dcard__title { font-family: "Instrument Serif", Georgia, serif; font-style: italic; font-weight: 400; font-size: 60px; line-height: 1; letter-spacing: -.01em; }
.dcard__title--about { font-weight: 500; font-size: 34px; letter-spacing: -.02em; }

.dcard__foot { display: flex; flex-direction: column; gap: 11.2px; min-height: 0; overflow-y: auto; scrollbar-width: thin; }
.dcard__blurb { margin: 0; font-size: 14px; line-height: 1.55; color: var(--n-n300); text-wrap: pretty; }
.dcard__empty { margin: 0; font-size: 14px; color: var(--n-n400); }
.dcard__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.dcard__list li { background: linear-gradient(90deg, var(--n-n700) 0%, transparent 100%) top left / 100% 1px no-repeat; }
.dcard__list a { display: block; padding: 8.4px 0; font-size: 14px; line-height: 1.4; color: var(--ink); text-decoration: none; text-wrap: pretty; }
.dcard__list a:hover { color: var(--n-accent-200); }
.dcard__more { align-self: flex-start; font-size: 13px; color: var(--n-accent-300); text-decoration: none; }
.dcard__more:hover { color: var(--n-accent-200); }
.dcard__actions { display: flex; flex-wrap: wrap; gap: 8.4px; margin: 0; }

/* Buttons, from the design system. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px; cursor: pointer;
  font: 500 13px/1.2 "Inter", var(--sans); color: var(--ink); text-decoration: none;
  background: transparent; border: 1px solid transparent; padding: 4.8px 8.4px; border-radius: 8px;
}
.btn--primary { color: var(--n-accent); border-color: var(--n-accent); }
.btn--primary:hover { background: color-mix(in srgb, var(--n-accent) 12%, transparent); }
.btn--ghost { color: var(--n-accent); padding-inline: 2.8px; }
.btn--ghost:hover { background: color-mix(in srgb, var(--n-accent) 10%, transparent); }
.btn--icon { width: 34px; height: 34px; padding: 0; font-size: 18px; }
.btn:disabled { opacity: .45; cursor: not-allowed; }

.deck__pager { display: flex; align-items: center; flex-wrap: wrap; gap: 8.4px; padding: 0 var(--n-rail-pad) var(--n-pad); min-height: 34px; }
.deck__fine { margin: 0 0 0 8.4px; font-size: 12px; color: var(--n-n400); white-space: nowrap; }
.deck__links { list-style: none; margin: 0 0 0 auto; padding: 0; display: flex; flex-wrap: wrap; gap: 11.2px; font-size: 12px; }
.deck__links a { color: var(--n-n400); text-decoration: none; }
.deck__links a:hover { color: var(--n-accent-200); }

@media (max-width: 30rem) {
  body.is-home { --n-card: min(310px, 84vw); --n-rail-pad: 20px; }
}
@media (prefers-reduced-motion: no-preference) {
  .deck__rail { scroll-behavior: smooth; }
  .dcard--section { transition: box-shadow .2s ease, transform .2s ease; }
  .dcard--section:hover { box-shadow: var(--n-shadow-md); transform: translateY(-2px); }
}

/* ---- Article rows ------------------------------------------------------ */
.rows { list-style: none; margin: 0; padding: 0; }
.row { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: .25rem 1.5rem; padding: .95rem 0; border-bottom: 1px solid var(--rule); }
.row__link { flex: 1 1 22rem; color: var(--ink); text-decoration: none; display: flex; flex-direction: column; gap: .25rem; }
.row__title { font-weight: 700; font-size: 1.08rem; line-height: 1.35; }
.row__link:hover .row__title { text-decoration: underline; text-decoration-color: var(--accent); text-decoration-thickness: 2px; text-underline-offset: 4px; }
.row__summary { color: var(--muted); font-size: 1rem; max-width: 62ch; }
.row__meta { display: flex; gap: .9rem; align-items: baseline; color: var(--muted); font-size: .9rem; white-space: nowrap; }
.rows--roomy .row { padding: 1.3rem 0; }
.kind { font-family: var(--mono); font-stretch: 87.5%; font-size: .72rem; padding: .08rem .45rem; border: 1px solid var(--rule-strong); border-radius: 2px; color: var(--muted); }
.kind--howto { border-color: var(--accent); color: var(--ink); }

/* ---- Section page ------------------------------------------------------ */
.section__head { padding-block: clamp(2.5rem, 6vw, 4rem) 1rem; }
.section__head h1 { margin-top: 1.25rem; }
.filters { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1.25rem; padding: .75rem 0; border-block: 1px solid var(--rule); margin-bottom: .5rem; font-size: 1rem; }
.filters a { color: var(--muted); text-decoration: none; }
.filters a:hover { color: var(--ink); }
.filters a[aria-current] { color: var(--ink); font-weight: 700; text-decoration: underline; text-decoration-color: var(--accent); text-decoration-thickness: 2px; text-underline-offset: 5px; }
.filters__tags { margin-left: auto; }
.filters__tags summary { cursor: pointer; color: var(--muted); }
.filters__tags ul { list-style: none; padding: 0; margin: .5rem 0 0; display: flex; flex-wrap: wrap; gap: .4rem 1rem; }
.pager { display: flex; gap: 1.5rem; align-items: center; justify-content: center; padding: 2rem 0; color: var(--muted); }

/* ---- Article ----------------------------------------------------------- */
.article__head { padding-block: clamp(2.5rem, 6vw, 4rem) 1.25rem; max-width: 52rem; }
.crumb { display: flex; align-items: center; gap: .6rem; margin: 0 0 1rem; font-size: 1rem; }
.crumb a { color: var(--muted); text-decoration: none; }
.crumb a:hover { color: var(--ink); text-decoration: underline; }
.article__meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: .4rem 1.1rem; color: var(--muted); font-size: .95rem; margin: 0; }

/* The spec plate: like the rating label on the back of a device. */
.spec {
  max-width: 52rem; margin: .5rem 0 2rem; padding: 1rem 1.25rem; border: 1px solid var(--rule-strong); border-radius: 4px;
  background:
    radial-gradient(circle at 10px 10px, var(--rule-strong) 2px, transparent 2.5px),
    radial-gradient(circle at calc(100% - 10px) 10px, var(--rule-strong) 2px, transparent 2.5px),
    radial-gradient(circle at 10px calc(100% - 10px), var(--rule-strong) 2px, transparent 2.5px),
    radial-gradient(circle at calc(100% - 10px) calc(100% - 10px), var(--rule-strong) 2px, transparent 2.5px),
    var(--surface);
}
.spec dl { display: flex; flex-wrap: wrap; gap: .9rem 2.5rem; margin: 0; padding-inline: .5rem; }
.spec dl > div { flex: 0 1 auto; }
.spec dl > .spec__wide { flex: 1 1 14rem; }
.spec dt { font-family: var(--mono); font-stretch: 87.5%; font-size: .72rem; color: var(--muted); margin-bottom: .15rem; }
.spec dd { margin: 0; font-weight: 700; display: flex; align-items: center; gap: .5rem; }
.meter { display: inline-flex; gap: 2px; }
.meter i { width: 5px; height: 12px; background: var(--rule); border-radius: 1px; }
.meter--beginner i:nth-child(-n+1), .meter--intermediate i:nth-child(-n+2), .meter--advanced i { background: var(--accent); }

.hero-image { margin: 0 0 2.5rem; }
.hero-image img { width: 100%; border-radius: 4px; display: block; }

.article__body { display: grid; gap: 0 3.5rem; }
@media (min-width: 70rem) {
  .article__body { grid-template-columns: minmax(0, 1fr) 15rem; }
  .article__body .toc { grid-column: 2; grid-row: 1; position: sticky; top: 1.5rem; align-self: start; border: 0; padding: 0; margin: 0; }
  .article__body .prose { grid-column: 1; grid-row: 1; }
}
.toc { border: 1px solid var(--rule); border-radius: 4px; padding: 1rem 1.25rem; margin-bottom: 2rem; font-size: .95rem; }
.toc__title { font-family: var(--mono); font-stretch: 87.5%; font-size: .72rem; color: var(--muted); margin: 0 0 .5rem; }
.toc ol { list-style: none; margin: 0; padding: 0; border-left: 2px solid var(--rule); }
.toc li a { display: block; padding: .3rem 0 .3rem .9rem; margin-left: -2px; border-left: 2px solid transparent; color: var(--muted); text-decoration: none; line-height: 1.35; }
.toc li a:hover { color: var(--ink); border-left-color: var(--accent); }

.article__foot { max-width: 52rem; padding-top: 2.5rem; margin-top: 3rem; border-top: 1px solid var(--rule); }
.related__title { font-size: 1.1rem; margin-top: 2rem; }
.tags, .skills ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .45rem; }
.tags a, .skills li { display: inline-block; font-size: .92rem; padding: .15rem .6rem; border: 1px solid var(--rule-strong); border-radius: 3px; color: var(--ink); text-decoration: none; background: var(--surface); }
.tags a:hover { border-color: var(--accent); }

/* ---- Prose ------------------------------------------------------------- */
.prose > :where(p, ul, ol:not(.steps), h2, h3, blockquote, .callout, .pullquote, .download, .figure:not(.figure--wide)),
.prose .rich-text > * { max-width: var(--measure); }
.prose p { margin: 0 0 1.15rem; }
.prose ul, .prose ol { padding-left: 1.4rem; margin: 0 0 1.25rem; }
.prose li { margin-bottom: .35rem; }
.prose li::marker { color: var(--muted); }
.prose blockquote { margin: 1.5rem 0; padding-left: 1.1rem; border-left: 3px solid var(--rule-strong); color: var(--muted); }
.prose :not(pre) > code {
  font-family: var(--mono); font-stretch: 87.5%; font-size: .82em; padding: .1em .35em;
  background: var(--surface); border: 1px solid var(--rule); border-radius: 3px; overflow-wrap: anywhere;
}
.prose > :first-child { margin-top: 0; }
.anchored { position: relative; }
.anchor { margin-left: .5rem; color: var(--rule-strong); text-decoration: none; opacity: 0; font-weight: 400; }
.anchored:hover .anchor, .anchor:focus { opacity: 1; }

/* Code blocks */
.code { margin: 1.75rem 0; background: var(--code-bg); color: var(--code-ink); border-radius: 6px; overflow: hidden; border: 1px solid color-mix(in srgb, var(--code-bg) 80%, var(--rule-strong)); }
.code__bar { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: .45rem .5rem .45rem 1rem; background: var(--code-bar); font-family: var(--mono); font-stretch: 87.5%; font-size: .72rem; color: #9fb0c0; }
.code__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.code__copy { font: inherit; color: #c9d4de; background: transparent; border: 1px solid #3a4a5c; border-radius: 3px; padding: .2rem .6rem; cursor: pointer; }
.code__copy:hover { border-color: #7d93a8; color: #fff; }
.code pre { margin: 0; padding: 1rem 1.15rem 1.1rem; overflow-x: auto; font-family: var(--mono); font-stretch: 87.5%; font-size: .8rem; line-height: 1.65; tab-size: 4; }
.hl .c, .hl .ch, .hl .cm, .hl .c1, .hl .cs, .hl .cp, .hl .cpf { color: #7f8e9c; }
.hl .k, .hl .kc, .hl .kd, .hl .kn, .hl .kp, .hl .kr, .hl .kt, .hl .nt, .hl .ow { color: #7fb4f0; }
.hl .s, .hl .sa, .hl .sb, .hl .sc, .hl .dl, .hl .sd, .hl .s2, .hl .se, .hl .sh, .hl .si, .hl .sx, .hl .sr, .hl .s1, .hl .ss { color: #a3d6a0; }
.hl .m, .hl .mb, .hl .mf, .hl .mh, .hl .mi, .hl .il, .hl .mo, .hl .kc { color: #f0ae74; }
.hl .nf, .hl .fm, .hl .na, .hl .nc, .hl .nd { color: #f0d68a; }
.hl .nb, .hl .bp, .hl .nn { color: #c9a8f2; }
.hl .nv, .hl .vc, .hl .vg, .hl .vi, .hl .vm { color: #f2b8a0; }
.hl .o, .hl .p { color: #b4c0cc; }
.hl .gp { color: #7fb4f0; font-weight: 700; user-select: none; }
.hl .go { color: #a9b5c1; }
.hl .err { color: inherit; background: none; }

/* Callouts */
.callout { --c: var(--blue); margin: 1.75rem 0; padding: .9rem 1.15rem; border-left: 4px solid var(--c); background: color-mix(in srgb, var(--c) 7%, var(--surface)); border-radius: 0 4px 4px 0; }
.callout--tip { --c: var(--green); }
.callout--warning { --c: var(--amber); }
.callout__label { font-weight: 700; margin: 0 0 .2rem; color: var(--c); font-size: 1rem; }
.callout__body > :last-child, .callout__body p:last-child { margin-bottom: 0; }

/* Steps: numbered because they happen in order, joined by a cable run. */
.steps { list-style: none; padding: 0; margin: 2rem 0; counter-reset: step; }
.step { counter-increment: step; position: relative; padding: 0 0 1.5rem 3.25rem; margin: 0; }
.step::before {
  content: counter(step); position: absolute; left: 0; top: .05rem; width: 2.1rem; height: 2.1rem;
  display: grid; place-items: center; font-family: var(--mono); font-weight: 600; font-size: .9rem;
  border: 2px solid var(--accent); border-radius: 4px; background: var(--paper); z-index: 1;
}
.step::after { content: ""; position: absolute; left: calc(1.05rem - 1px); top: 2.2rem; bottom: .1rem; width: 2px; background: color-mix(in srgb, var(--accent) 45%, var(--rule)); }
.step:last-child::after { display: none; }
.step__title { margin: .2rem 0 .6rem; font-size: 1.15rem; max-width: var(--measure); }
.step__body > * { max-width: var(--measure); }
.step__body .code { max-width: none; }
.step__body p { margin: 0 0 .9rem; }
.step__body img { border-radius: 4px; }

/* Figures, quotes, downloads, tables */
.figure { margin: 2rem 0; }
.figure img { display: block; width: 100%; border-radius: 4px; }
.figure figcaption { font-size: .92rem; color: var(--muted); margin-top: .5rem; }
/* Photos set into the text. Widths are in ch so they stay in proportion to the
   measure, leaving a readable column of text beside them. Headings and the
   wider blocks clear the float rather than squeezing in next to it. */
.figure--left, .figure--right { width: var(--fig-w, 22ch); max-width: 40%; margin-block: .35rem 1rem; }
.figure--medium { --fig-w: 30ch; }
.figure--left { float: left; margin-right: 1.75rem; }
.figure--right { float: right; margin-left: 1.75rem; }
.prose h2, .prose h3, .prose .code, .prose .steps, .prose .table-scroll { clear: both; }
.prose::after { content: ""; display: block; clear: both; }
@media (max-width: 34rem) {
  /* No room for a column beside a photo, so they go back to being ordinary figures. */
  .figure--left, .figure--right { float: none; width: auto; max-width: 100%; margin-block: 2rem; }
}
.pullquote { margin: 2.25rem 0; padding-left: 1.25rem; border-left: 4px solid var(--accent); }
.pullquote blockquote { margin: 0; }
.pullquote p { font-family: var(--mono); font-stretch: 87.5%; font-size: 1.2rem; line-height: 1.45; margin: 0; }
.pullquote figcaption { color: var(--muted); margin-top: .6rem; font-size: 1rem; }
.download a { display: inline-flex; flex-direction: column; gap: .1rem; padding: .7rem 1rem; border: 1px solid var(--rule-strong); border-radius: 4px; text-decoration: none; background: var(--surface); }
.download a:hover { border-color: var(--accent); }
.download__label { font-weight: 700; }
.download__meta { color: var(--muted); font-size: .88rem; }
.table-scroll { overflow-x: auto; margin: 1.75rem 0; border: 1px solid var(--rule); border-radius: 4px; }
.table-scroll table { border-collapse: collapse; width: 100%; font-size: .96rem; font-variant-numeric: tabular-nums; }
.table-scroll caption { text-align: left; padding: .6rem .9rem; color: var(--muted); caption-side: top; }
.table-scroll th, .table-scroll td { padding: .55rem .9rem; border-bottom: 1px solid var(--rule); text-align: left; vertical-align: top; }
.table-scroll thead th { background: var(--surface); font-weight: 700; }
.table-scroll tr:last-child td, .table-scroll tr:last-child th { border-bottom: 0; }
.embed, .responsive-object { margin: 2rem 0; max-width: 52rem; }
.responsive-object { position: relative; }
.responsive-object iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; border-radius: 4px; }

/* ---- About ------------------------------------------------------------- */
.about__head { display: grid; gap: 1.5rem 2.5rem; padding-block: clamp(2.5rem, 6vw, 4rem) 2rem; align-items: start; max-width: 58rem; }
@media (min-width: 40rem) { .about__head:has(.about__portrait) { grid-template-columns: 10rem 1fr; } }
.about__portrait { width: 10rem; height: 10rem; object-fit: cover; border-radius: 4px; }
.about__links { display: flex; flex-wrap: wrap; gap: .75rem; margin: 1.5rem 0 0; }
.timeline { list-style: none; margin: 1.25rem 0 0; padding: 0 0 0 1.5rem; border-left: 2px solid var(--rule); max-width: var(--measure); }
.timeline li { position: relative; padding-bottom: 1.75rem; }
.timeline li::before { content: ""; position: absolute; left: calc(-1.5rem - 6px); top: .55rem; width: 10px; height: 10px; border-radius: 2px; background: var(--paper); border: 2px solid var(--blue); }
.timeline li:first-child::before { background: var(--blue); }
.timeline__when { font-family: var(--mono); font-stretch: 87.5%; font-size: .75rem; color: var(--muted); margin: 0 0 .2rem; }
.timeline h3 { margin: 0 0 .4rem; }
.timeline__org { display: block; font-weight: 400; color: var(--muted); }
.skills dl { display: grid; gap: 1.25rem; margin: 1.25rem 0 0; max-width: 58rem; }
@media (min-width: 44rem) { .skills dl { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); } }
.skills dt { font-weight: 700; margin-bottom: .5rem; }
.skills dd { margin: 0; }

/* ---- Search and errors ------------------------------------------------- */
.narrow h1 { margin-top: clamp(2.5rem, 6vw, 4rem); }
.searchbar { display: flex; gap: .5rem; margin: 1rem 0 1.5rem; }
.searchbar input { flex: 1; min-width: 0; font: inherit; font-size: 1.05rem; padding: .6rem .8rem; border: 1.5px solid var(--rule-strong); border-radius: 3px; background: var(--surface); color: var(--ink); }
.searchbar input:focus { border-color: var(--link); outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--link) 25%, transparent); }
.searchbar button { font: inherit; font-weight: 700; font-size: 1rem; padding: .6rem 1.05rem; border: 1.5px solid var(--ink); border-radius: 3px; background: var(--ink); color: var(--paper); cursor: pointer; }
.error-page .code-num { font-family: var(--mono); font-stretch: 75%; font-size: clamp(4rem, 14vw, 7rem); font-weight: 300; line-height: 1; margin: clamp(2.5rem, 8vw, 5rem) 0 .5rem; color: var(--rule-strong); }
.error-page h1 { margin-top: 0; }

/* ---- Footer ------------------------------------------------------------ */
.site-footer { margin-top: 5rem; border-top: 1px solid var(--rule); color: var(--muted); font-size: .95rem; }
.site-footer__inner { max-width: 74rem; margin-inline: auto; padding: 2rem var(--pad) 2.25rem; }
.site-footer__fine { margin: 0; font-size: .88rem; }

@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }
@media print {
  .masthead, .site-footer, .toc, .code__copy, .filters { display: none; }
  body { background: #fff; color: #000; }
  .code { border: 1px solid #999; }
}

/* ---- Overrides for lists that aren't prose lists ------------------------ */
.prose .skills ul, .prose .tags { padding-left: 0; margin: 0; }
.prose .skills li { margin: 0; }
.prose .timeline { padding-left: 1.5rem; }

@media (max-width: 40rem) {
  .masthead__inner { padding-block: .75rem .25rem; }
  .nav { width: 100%; flex-wrap: nowrap; gap: .8rem; font-size: .95rem; overflow-x: auto; scrollbar-width: none; }
  .nav__icon:first-of-type { margin-left: auto; }
  .nav .pair { width: 1rem; }
  .nav__link { font-size: .95rem; }
  .nav__link { white-space: nowrap; flex: none; }
  .nav__icon svg { width: 18px; height: 18px; flex: none; }
}

/* ======================================================================
   Micro-interactions
   ====================================================================== */

/* Signal pulse: a bright packet travels along a cable pair on hover. */
.pair { position: relative; overflow: hidden; }
.pair::after {
  content: ""; position: absolute; inset: 0; width: 45%;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.95) 45%, rgba(255,255,255,.95) 55%, transparent);
  transform: translateX(-120%); pointer-events: none;
}
[data-theme="dark"] .pair::after { background: linear-gradient(90deg, transparent, rgba(255,255,255,.85) 50%, transparent); }
@media (prefers-reduced-motion: no-preference) {
  .nav__link:hover .pair::after,
  .nav__link:focus-visible .pair::after,
  .crumb:hover .pair::after,
  .card:hover .pair::after,
  .section__head:hover .pair::after {
    animation: signal .7s cubic-bezier(.4,0,.2,1) 1 both;
  }
  .pair--wide::after { width: 18%; }
  .card:hover .pair--wide::after,
  .section__head:hover .pair--wide::after { animation-duration: 1.1s; }
}
@keyframes signal { from { transform: translateX(-120%); } to { transform: translateX(600%); } }

/* Links: underline thickens and takes the section colour. */
.prose a, .row__link .row__title, .deck__links a {
  transition: text-decoration-color .15s ease, text-decoration-thickness .15s ease;
}
.prose a:hover { text-decoration-color: var(--accent); text-decoration-thickness: 2px; }

/* Buttons press in slightly. */
.button, .searchbar button { transition: background-color .15s, color .15s, border-color .15s, transform .1s; }
.button:active, .searchbar button:active { transform: translateY(1px); }

/* Copy button morphs into a checkmark. */
.code__copy { display: inline-flex; align-items: center; gap: .4rem; transition: border-color .15s, color .15s, background-color .2s; }
.code__icons { position: relative; width: 14px; height: 14px; display: inline-block; }
.code__icons svg { position: absolute; inset: 0; transition: opacity .18s ease, transform .25s cubic-bezier(.3,1.6,.5,1); }
.code__icons .icon-check { opacity: 0; transform: scale(.4) rotate(-20deg); }
.code__copy.is-copied { border-color: #4fb37a; color: #bff0cf; background: rgba(79,179,122,.12); }
.code__copy.is-copied .icon-copy { opacity: 0; transform: scale(.4); }
.code__copy.is-copied .icon-check { opacity: 1; transform: none; }

/* Heading anchors confirm the copied link. */
.anchor { transition: opacity .15s, color .15s; }
.anchor.is-copied { opacity: 1; color: var(--green); }
.anchor.is-copied::after {
  content: "Link copied"; margin-left: .5rem; font-family: var(--sans); font-size: .8rem; font-weight: 400;
  color: var(--muted); vertical-align: middle;
}

/* Keyboard focus in article lists (j / k). */
.row { position: relative; }
.row:has(.row__link:focus-visible)::before {
  content: ""; position: absolute; left: calc(-1 * min(var(--pad), 1rem)); top: .6rem; bottom: .6rem; width: 3px;
  background: var(--accent); border-radius: 2px;
}
.row__link:focus-visible { outline: none; }
.row__link:focus-visible .row__title { text-decoration: underline; text-decoration-color: var(--accent); text-decoration-thickness: 2px; text-underline-offset: 4px; }

/* Theme toggle: the half-moon turns as the theme changes. */
.theme-toggle svg { transition: transform .45s cubic-bezier(.2,.7,.2,1); }
[data-theme="dark"] .theme-toggle svg { transform: rotate(180deg); }
::view-transition-old(root), ::view-transition-new(root) { animation: none; mix-blend-mode: normal; }

/* Shortcuts dialog. */
kbd {
  font-family: var(--mono); font-stretch: 87.5%; font-size: .75rem; padding: .1rem .4rem; min-width: 1.5rem;
  display: inline-block; text-align: center; border: 1px solid var(--rule-strong); border-bottom-width: 2px;
  border-radius: 3px; background: var(--surface); color: var(--ink);
}
.kbd-hint { display: none; }
@media (hover: hover) and (pointer: fine) { .kbd-hint { display: inline; } }
.shortcuts {
  border: 1px solid var(--rule-strong); border-radius: 6px; padding: 1.5rem 1.75rem; width: min(26rem, calc(100vw - 2rem));
  background: var(--paper); color: var(--ink);
}
.shortcuts::backdrop { background: rgba(10, 15, 20, .45); }
.shortcuts h2 { margin: 0 0 1rem; font-size: 1.1rem; }
.shortcuts dl { margin: 0 0 1.25rem; display: grid; gap: .6rem; }
.shortcuts dl > div { display: grid; grid-template-columns: 5.5rem 1fr; align-items: center; gap: 1rem; }
.shortcuts dt { display: flex; gap: .3rem; }
.shortcuts dd { margin: 0; font-size: 1rem; }
.shortcuts form { margin: 0; }
@media (prefers-reduced-motion: no-preference) {
  .shortcuts[open] { animation: rise .2s cubic-bezier(.2,.7,.2,1); }
  @keyframes rise { from { opacity: 0; transform: translateY(6px) scale(.98); } }
}
