/* helix-2.com: the software I've built, one project after another.

   System, so later changes stay consistent:
   - The page is one sheet of stock on a desk: --paper for the sheet, --desk around it. Black ink,
     heavy 3px rules between parts, 1px hairlines inside them. Square corners everywhere; only the
     project tabs and the phone frames (devices) are rounded.
   - Colour belongs to the projects. Each project has one tab colour (.c1 to .c8) used for its tab,
     its bullets and its selected thumbnail; --cN-text is the same hue dark or light enough to read
     as text (the project's number, links). Beyond that, project 1's blue is the interaction colour
     (focus ring, caret, button hover), marigold is the text selection, --danger marks form errors
     and --live is the footer's dot.
   - Archivo for headings and labels (wide for the title and project names, narrow for numbers),
     Source Serif 4 for reading, Courier Prime only for data: the email address, server facts,
     character counts and status codes.
   - The project tabs are the signature: a tab per project on the sheet's right edge (a sticky strip
     at the top on a phone). The tab for the project in view slides out and shows its name; that's
     the page's one moving part.
   - Light is dark ink on a light sheet; dark reverses it, light ink on a dark sheet, with the same
     layout and tab colours. The visitor's system setting picks one until they press the theme
     button (js/theme.js then sets data-theme on <html>).
   - z-index: the phone tab strip is 10, the skip link 20. */

/* ── Fonts ──────────────────────────────────────────────────
   Self-hosted from Fontsource (SIL OFL 1.1, licences in /fonts), so visitors' browsers never
   contact a third-party font service. Archivo's file carries both its width and weight axes. */
@font-face {
    font-family: 'Archivo';
    font-style: normal;
    font-weight: 100 900;
    font-stretch: 62% 125%;
    font-display: swap;
    src: url('/fonts/archivo-latin-wdth-normal.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: 'Source Serif 4';
    font-style: normal;
    font-weight: 200 900;
    font-display: swap;
    src: url('/fonts/source-serif-4-latin-wght-normal.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: 'Source Serif 4';
    font-style: italic;
    font-weight: 200 900;
    font-display: swap;
    src: url('/fonts/source-serif-4-latin-wght-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;
}
@font-face {
    font-family: 'Courier Prime';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('/fonts/courier-prime-latin-400-normal.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;
}

/* ── Tokens ─────────────────────────────────────────────── */
:root {
    --desk: #D9DBD6;
    --paper: #F6F7F4;
    --card: #FFFFFF;      /* Screenshot mounts and the contact form: a whiter stock than the sheet. */
    --shade: #E6E8E3;     /* Note boxes. */
    --ink: #121314;
    --muted: #4F5357;
    --hair: #C9CCC8;
    --danger: #B4183E;
    --live: #0B8576;
    --device: #121314;

    /* Tab stock. Text on tabs is white, except on the marigold one. 7 and 8 are spares for the
       next programs, already checked: white on the tab and the text colour on the sheet are both
       at least 4.5:1 in light and dark. */
    --c1: #2246B5; --c2: #E8A400; --c3: #0B8576; --c4: #B5306A; --c5: #4C5661; --c6: #5F6E12; --c7: #6B3FA0; --c8: #A8461A;
    /* The same hues as text on the sheet (at least 3:1 for the big numerals, 4.5:1 for links). */
    --c1-text: #2246B5; --c2-text: #8A5A00; --c3-text: #08705F; --c4-text: #B5306A; --c5-text: #4C5661; --c6-text: #55630F; --c7-text: #6B3FA0; --c8-text: #A8461A;

    --shadow: 0 2px 4px rgb(18 19 20 / .08), 0 18px 34px -18px rgb(18 19 20 / .35);
    --ease: cubic-bezier(.16, 1, .3, 1);
    --head: 'Archivo', system-ui, sans-serif;
    --body: 'Source Serif 4', Georgia, serif;
    --data: 'Courier Prime', ui-monospace, Consolas, monospace;
    --sheet: 1440px;
    --rail: 96px;
    --gutter: clamp(16px, 4.5vw, 72px);
    --col: 200px;          /* The project number's column. */
    color-scheme: light;
}

/* Reverse print. Repeated for the two ways in: the system setting (unless the visitor picked
   light), and the visitor picking dark. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --desk: #08090A; --paper: #141516; --card: #1B1D1F; --shade: #202326;
        --ink: #E9EAE6; --muted: #A9ADAA; --hair: #34373A; --danger: #FF7D97; --live: #2FC2AE; --device: #000;
        --c1: #3557CC; --c2: #E8A400; --c3: #0B8576; --c4: #B5306A; --c5: #57626E; --c6: #66771A; --c7: #7A50B8; --c8: #B5501F;
        --c1-text: #8AA4FF; --c2-text: #F2B52B; --c3-text: #3CCBB6; --c4-text: #F27AAA; --c5-text: #AEB8C3; --c6-text: #B9CB5A; --c7-text: #C3A6F2; --c8-text: #F2925F;
        --shadow: 0 2px 4px rgb(0 0 0 / .4), 0 18px 34px -18px rgb(0 0 0 / .8);
        color-scheme: dark;
    }
}
:root[data-theme="dark"] {
    --desk: #08090A; --paper: #141516; --card: #1B1D1F; --shade: #202326;
    --ink: #E9EAE6; --muted: #A9ADAA; --hair: #34373A; --danger: #FF7D97; --live: #2FC2AE; --device: #000;
    --c1: #3557CC; --c2: #E8A400; --c3: #0B8576; --c4: #B5306A; --c5: #57626E; --c6: #66771A; --c7: #7A50B8; --c8: #B5501F;
    --c1-text: #8AA4FF; --c2-text: #F2B52B; --c3-text: #3CCBB6; --c4-text: #F27AAA; --c5-text: #AEB8C3; --c6-text: #B9CB5A; --c7-text: #C3A6F2; --c8-text: #F2925F;
    --shadow: 0 2px 4px rgb(0 0 0 / .4), 0 18px 34px -18px rgb(0 0 0 / .8);
    color-scheme: dark;
}

/* A project's colour, set once on its section and read by everything inside. */
.c1 { --c: var(--c1); --c-text: var(--c1-text); }
.c2 { --c: var(--c2); --c-text: var(--c2-text); }
.c3 { --c: var(--c3); --c-text: var(--c3-text); }
.c4 { --c: var(--c4); --c-text: var(--c4-text); }
.c5 { --c: var(--c5); --c-text: var(--c5-text); }
.c6 { --c: var(--c6); --c-text: var(--c6-text); }
.c7 { --c: var(--c7); --c-text: var(--c7-text); }
.c8 { --c: var(--c8); --c-text: var(--c8-text); }

/* ── Base ───────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }

html {
    scroll-behavior: smooth;
    scroll-padding-top: 24px;
    -webkit-text-size-adjust: 100%;
    scrollbar-color: var(--muted) var(--desk);
}

body {
    margin: 0;
    min-height: 100dvh;
    background: var(--desk);
    color: var(--ink);
    font: 400 17px/1.6 var(--body);
    -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration-thickness: 1.5px; text-underline-offset: 3px; }
h1, h2, h3 { text-wrap: balance; }
p { text-wrap: pretty; }
h1:focus { outline: none; }
:focus-visible { outline: 3px solid var(--c1-text); outline-offset: 3px; }
::selection { background: var(--c2); color: #121314; }
code, .mono { font-family: var(--data); }

.skip {
    position: absolute; left: 16px; top: 12px; z-index: 20;
    padding: 10px 16px; background: var(--ink); color: var(--paper);
    font: 700 14px/1 var(--head); text-decoration: none;
    transform: translateY(-200%);
}
.skip:focus { transform: none; }

/* ── The sheet ──────────────────────────────────────────── */
/* The right-hand margin is the thumb index's: the home page puts its tabs there, other pages leave
   it blank. */
.sheet {
    position: relative;
    width: min(100%, var(--sheet)); min-height: 100dvh; margin-inline: auto;
    padding: 0 calc(var(--rail) + 8px) 0 var(--gutter);
    background: var(--paper);
    display: flex; flex-direction: column;
    overflow-x: clip; /* The tabs' resting offset tucks them under the sheet's edge. Clip, not hidden, so they still stick. */
}
.sheet > main { flex: 1; }

/* Running head */
.running {
    display: flex; align-items: center; gap: 8px 20px; flex-wrap: wrap;
    padding-top: 12px; border-bottom: 3px solid var(--ink);
    font: 600 13px/1 var(--head); letter-spacing: .06em; text-transform: uppercase;
}
.wordmark { display: inline-flex; align-items: center; min-height: 44px; text-decoration: none; font-weight: 800; }
.running-nav { display: flex; gap: 4px; margin-left: auto; }
.running-nav a { display: inline-flex; align-items: center; min-height: 44px; padding: 0 10px; text-decoration: none; }
.running-nav a:hover { text-decoration: underline; }
.running-nav a[aria-current] { text-decoration: underline 3px; text-underline-offset: 6px; }

/* The theme button: a half-filled circle, turned half a turn in dark. It only works with
   js/theme.js, which marks <html> with data-theme-js, so without script it stays hidden. */
.theme-toggle {
    display: none; place-items: center; flex: none;
    width: 44px; height: 44px; padding: 0; cursor: pointer;
    background: transparent; color: var(--ink); border: 1.5px solid var(--ink); border-radius: 0;
    transition: background-color .2s;
}
:root[data-theme-js] .theme-toggle { display: grid; }
.theme-toggle:hover { background: var(--shade); }
.theme-toggle::before {
    content: ""; width: 16px; height: 16px; border-radius: 50%;
    border: 1.5px solid currentColor;
    background: linear-gradient(90deg, currentColor 50%, transparent 50%);
    transition: transform .4s var(--ease);
}
.theme-toggle[aria-pressed="true"]::before { transform: rotate(180deg); }

/* ── Thumb index ────────────────────────────────────────── */
/* In the sheet's right margin, running its full height; the tabs stick as the page scrolls. */
.rail { position: absolute; top: 0; right: 0; bottom: 0; width: var(--rail); }
/* Tabs show their number; the one for the project in view slides out and shows its name too. The
   list is capped at the screen's height and the number-only tabs share what's left (64px each at
   most, 40px at least), so more programs fit without the bottom tabs falling off a laptop screen.
   On a short screen the open tab keeps to its number as well. The names stay readable to screen
   readers either way. */
.tabs {
    position: sticky; top: 0; display: flex; flex-direction: column; gap: 6px;
    max-height: 100dvh; padding: 96px 0 24px 32px; margin: 0; list-style: none;
}
.tabs li { display: flex; flex: 0 1 64px; min-height: 40px; }
.tabs li:has([aria-current]) { flex: 0 0 auto; }
.tab {
    display: flex; flex-direction: column-reverse; align-items: center; justify-content: center; gap: 10px;
    width: 100%; padding: 8px 0; border-radius: 6px 0 0 6px;
    background: var(--c); color: #fff; text-decoration: none;
    font: 700 13px/1 var(--head); font-stretch: 85%; letter-spacing: .04em; text-transform: uppercase;
    transform: translateX(10px);
    transition: transform .4s var(--ease), box-shadow .4s var(--ease);
}
.tab.c2 { color: #121314; }
.tab span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.tab[aria-current] span { position: static; width: auto; height: auto; overflow: visible; clip-path: none; writing-mode: vertical-rl; transform: rotate(180deg); }
.tab b { font-size: 20px; font-weight: 800; }
.tab:hover { transform: translateX(2px); }
.tab[aria-current] { padding: 14px 0; transform: translateX(-14px); box-shadow: -2px 3px 10px -4px rgb(0 0 0 / .45); }
@media (max-height: 700px) and (min-width: 761px) {
    .tab[aria-current] { padding: 8px 0; }
    .tab[aria-current] span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); writing-mode: horizontal-tb; transform: none; }
}
.tab:focus-visible { outline-offset: 2px; }

/* ── Opening ──────────────────────────────────────────── */
/* A line about me across the top, then a short introduction beside the project list. Each list row carries its project's thumbnail, so every project shows before the first
   scroll. */
.title { padding: 36px 0 88px; }
.title h1 {
    max-width: 15em; margin: 0 0 40px; text-wrap: balance;
    font: 900 clamp(38px, 5.3vw, 76px)/.95 var(--head); font-stretch: 125%; letter-spacing: -.035em;
}
.title-row { display: grid; grid-template-columns: minmax(0, 360px) minmax(0, 1fr); gap: 56px; align-items: start; }
.lede { margin: 0; font-size: 16.5px; line-height: 1.55; }
.more { display: inline-flex; align-items: center; min-height: 44px; margin-top: 6px; font: 700 14px/1.2 var(--head); }
.toc { margin: 0; padding: 0; list-style: none; border-top: 1.5px solid var(--ink); }
.toc a {
    display: grid; grid-template-columns: 40px 128px minmax(0, 200px) minmax(0, 1fr); align-items: center; gap: 0 20px;
    padding: 10px 0; border-bottom: 1px solid var(--hair); text-decoration: none;
}
.toc-n { font: 900 32px/1 var(--head); font-stretch: 62%; color: var(--c-text); }
.toc-thumb { width: 128px; aspect-ratio: 16 / 10; object-fit: cover; object-position: top left; border: 1.5px solid var(--ink); }
.toc-name { font: 800 19px/1.15 var(--head); font-stretch: 112%; }
.toc-what { font-size: 16px; line-height: 1.35; }
.toc a:hover .toc-name { text-decoration: underline; text-decoration-color: var(--c); }
.toc a:hover .toc-thumb { outline: 3px solid var(--c); outline-offset: 2px; }

/* ── Figures ────────────────────────────────────────────── */
.fig { margin: 0; }
.frame { position: relative; border: 1.5px solid var(--ink); background: var(--card); }
.frame > a { display: block; }
.frame img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; object-position: top left; }
.fig figcaption { margin-top: 12px; font-size: 15px; line-height: 1.45; }

/* Callouts: numbered markers drawn in an SVG laid over the screenshot (positions are attributes,
   so the content security policy's ban on inline styles doesn't touch them), keyed to a list
   under the figure. Each marker scales about its own centre, larger on narrow screens. */
.callout-layer { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.callout-layer g { transform-box: fill-box; transform-origin: center; }
.callout-layer circle { fill: #121314; stroke: #fff; stroke-width: 6; }
.callout-layer text { fill: #fff; font: 800 30px/1 var(--head); text-anchor: middle; dominant-baseline: central; }
.callouts {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px 28px;
    margin: 14px 0 0; padding: 14px 0 0; list-style: none; border-top: 1px solid var(--hair);
    font-size: 15px; line-height: 1.45; counter-reset: callout;
}
.callouts li { display: grid; grid-template-columns: 24px minmax(0, 1fr); gap: 8px; counter-increment: callout; }
.callouts li::before { content: counter(callout); font: 800 14px/1.45 var(--head); }
[hidden] { display: none !important; }

/* Figure strip: the project's other screenshots. Plain links to the full images; site.js swaps
   them into the figure above instead. */
.strip {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 14px;
    margin: 22px 0 0; padding: 0; list-style: none;
}
.strip a { display: block; text-decoration: none; font: 800 12px/1.35 var(--head); letter-spacing: .02em; }
.strip img {
    width: 100%; aspect-ratio: 16 / 10; object-fit: cover; object-position: top left;
    border: 1.5px solid var(--ink); outline: 4px solid transparent; outline-offset: 2px;
    transition: outline-color .2s;
}
.strip a:hover img { outline-color: var(--hair); }
.strip a[aria-current] img { outline-color: var(--c); }
.strip span { display: block; margin-top: 8px; }

.phone { margin: 0; }
.handset { padding: 7px; background: var(--device); border-radius: 26px; box-shadow: 0 0 0 1px var(--hair), var(--shadow); }
.handset img { width: 100%; aspect-ratio: 9 / 19; object-fit: cover; object-position: top; border-radius: 20px; }
.phone figcaption { margin-top: 12px; font-size: 15px; line-height: 1.45; }

.note {
    display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 14px; max-width: 46em;
    margin: 28px 0 0; padding: 12px 16px; background: var(--shade);
    border-block: 1.5px solid var(--ink); font-size: 15px; line-height: 1.5;
}
.note b { font: 900 13px/1.6 var(--head); letter-spacing: .08em; text-transform: uppercase; }

/* ── Projects ───────────────────────────────────────────── */
.project { display: grid; grid-template-columns: var(--col) minmax(0, 1fr); column-gap: 56px; padding-bottom: 96px; border-top: 3px solid var(--ink); }
.pj-num { padding-top: 22px; font: 900 168px/.8 var(--head); font-stretch: 62%; color: var(--c-text); }
.pj-body { padding-top: 26px; min-width: 0; }
.pj-body :is(h2, h3) { margin: 0; font: 900 clamp(38px, 4.1vw, 58px)/.95 var(--head); font-stretch: 112%; letter-spacing: -.02em; }
.truth { margin: 14px 0 0; max-width: 34em; font-size: clamp(19px, 1.6vw, 22px); line-height: 1.4; }
.spec { display: flex; flex-wrap: wrap; width: fit-content; max-width: 100%; margin: 26px 0 0; border: 1.5px solid var(--ink); }
.spec div { padding: 10px 18px; border-right: 1px solid var(--hair); }
.spec div:last-child { border-right: 0; }
.spec dt { font: 700 11px/1 var(--head); letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.spec dd { margin: 6px 0 0; font: 600 16px/1.25 var(--head); overflow-wrap: anywhere; }
.spec a, .pj-body p a { text-decoration-color: var(--c); }
.spec a { display: inline-block; padding-block: 11px; margin-block: -11px; }

.pj-lead { margin-top: 44px; }
.pj-grid { display: grid; grid-template-columns: minmax(0, 1fr) 280px; gap: 48px; margin-top: 44px; align-items: start; }
.points { margin: 0; padding: 0; list-style: none; max-width: 38em; }
.points li { position: relative; padding: 0 0 14px 22px; }
.points li::before { content: ""; position: absolute; left: 0; top: .6em; width: 8px; height: 8px; background: var(--c); }

/* Smaller projects: the screenshot on one side, the text and strip on the other, under their own
   heading. */
.brief .pj-num { font-size: 120px; }
.brief .pj-grid { grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); }
.brief .pj-grid p { margin: 0; max-width: 34em; }
.brief .strip { grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* The "Smaller projects" heading: a part of its own, in the project body's column, with the first
   smaller project starting right under it. */
.part { display: grid; grid-template-columns: var(--col) minmax(0, 1fr); column-gap: 56px; padding: 26px 0 30px; border-top: 3px solid var(--ink); }
.part h2 { grid-column: 2; margin: 0; font: 900 clamp(38px, 4.1vw, 58px)/.95 var(--head); font-stretch: 112%; letter-spacing: -.02em; }
.brief .pj-body h3 { font-size: clamp(30px, 3.2vw, 46px); line-height: 1; }
.part + .project { border-top: 1px solid var(--hair); }

/* ── The homelab ────────────────────────────────────────── */
/* Laid out like How it started (below), with the host's spec table and a ruled list of its guests
   under the text. It isn't a project, so nothing in it takes a project colour. */
.lab-body { grid-column: 2; min-width: 0; }
/* One heading per machine, smaller than the section's own. */
.lab-body h3 { margin: 40px 0 0; font: 800 clamp(22px, 2vw, 28px)/1.1 var(--head); font-stretch: 112%; letter-spacing: -.01em; }
.lab-body h3 + .spec { margin-top: 16px; }
.lab-text { max-width: 38em; margin: 12px 0 0; }
.lab-text + .spec { margin-top: 20px; }
.guests { width: 100%; max-width: 46em; margin-top: 36px; border-collapse: collapse; }
.guests caption { padding-bottom: 18px; text-align: left; font: 700 11px/1 var(--head); letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.guests th, .guests td { padding: 11px 20px 11px 0; text-align: left; vertical-align: baseline; border-bottom: 1px solid var(--hair); }
.guests :is(th, td):last-child { padding-right: 0; }
.guests thead th { padding-block: 0 8px; font: 700 11px/1 var(--head); letter-spacing: .08em; text-transform: uppercase; color: var(--muted); border-bottom: 1.5px solid var(--ink); }
.guests tbody th { font: 600 16px/1.25 var(--head); white-space: nowrap; }
.guests tbody tr:last-child > * { border-bottom: 1.5px solid var(--ink); }
.g-kind { white-space: nowrap; }
.g-size { font: 400 14px/1.45 var(--data); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ── How it started ─────────────────────────────────────── */
.about {
    display: grid; grid-template-columns: var(--col) minmax(0, 1fr); column-gap: 56px;
    padding: 26px 0 96px; border-top: 3px solid var(--ink);
}
.about h2, .contact h2 { margin: 0; font: 900 clamp(34px, 3.2vw, 46px)/1 var(--head); font-stretch: 112%; letter-spacing: -.02em; }
.about h2, .about .prose { grid-column: 2; }
.prose { max-width: 38em; }
.prose p { margin: 16px 0 0; }
.degree { margin: 18px 0 0; padding-top: 12px; border-top: 1px solid var(--hair); font-size: 15px; line-height: 1.45; }
.degree b { display: block; font: 800 13px/1.6 var(--head); letter-spacing: .06em; text-transform: uppercase; }

/* ── Contact ─────────────────────────────────────────── */
.contact { display: grid; grid-template-columns: var(--col) minmax(0, 1fr); column-gap: 56px; padding: 26px 0 96px; border-top: 3px solid var(--ink); }
.contact-inner { grid-column: 2; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr); gap: 56px; align-items: start; }
.contact-intro p { margin: 14px 0 0; max-width: 34em; }
.contact-or { font-size: 15px; color: var(--muted); }

.mail { display: inline-flex; align-items: stretch; max-width: 100%; margin-top: 10px; border: 1.5px solid var(--ink); }
.mail code { padding: 11px 14px; font-size: 15px; line-height: 1.3; user-select: all; overflow-wrap: anywhere; }
.mail button {
    flex: none; min-height: 44px; padding: 0 16px; border: 0; border-left: 1.5px solid var(--ink); border-radius: 0;
    background: var(--ink); color: var(--paper); cursor: pointer;
    font: 700 13px/1 var(--head); letter-spacing: .06em; text-transform: uppercase;
}
.mail button:hover { background: var(--c1); color: #fff; }

.card { padding: 24px 30px 30px; background: var(--card); border: 1.5px solid var(--ink); box-shadow: var(--shadow); }
.form { display: grid; gap: 20px; }
.row2 { display: grid; gap: 20px; }
@media (min-width: 560px) { .row2 { grid-template-columns: 1fr 1fr; gap: 24px; } }
.field { display: grid; gap: 6px; align-content: start; }
.field label { font: 700 12px/1.3 var(--head); letter-spacing: .08em; text-transform: uppercase; }
.field label span { font-weight: 400; letter-spacing: 0; text-transform: none; color: var(--muted); }
.field input, .field textarea {
    width: 100%; padding: 8px 0; color: var(--ink);
    background: transparent; border: 0; border-bottom: 1.5px solid var(--ink); border-radius: 0;
    font: 400 17px/1.45 var(--body); caret-color: var(--c1-text);
    transition: border-color .2s, box-shadow .2s;
}
.field textarea { min-height: 150px; resize: vertical; }
.field input::placeholder, .field textarea::placeholder { color: var(--muted); font-style: italic; opacity: 1; }
.field input:focus, .field textarea:focus { outline: none; border-bottom-color: var(--c1-text); box-shadow: 0 1.5px 0 var(--c1-text); }
.field .invalid, .field .invalid:focus { border-bottom-color: var(--danger); box-shadow: 0 1.5px 0 var(--danger); }
.validation-message { margin: 0; font-size: 15px; line-height: 1.45; color: var(--danger); }
.field-hint { margin: 0; font: 400 13px/1.3 var(--data); color: var(--muted); font-variant-numeric: tabular-nums; }
.field-hint.near { color: var(--ink); }
.hp { position: absolute; left: -10000px; width: 1px; height: 1px; overflow: hidden; }
.form-error { margin: 0; padding: 12px 14px; border-block: 1.5px solid var(--danger); color: var(--ink); background: var(--shade); }
.form-foot { display: flex; align-items: center; flex-wrap: wrap; gap: 12px 18px; }
.form-note { font-size: 15px; color: var(--muted); overflow-wrap: anywhere; }
.form-privacy { margin: 0; padding-top: 16px; border-top: 1px solid var(--hair); font-size: 14px; line-height: 1.55; color: var(--muted); }
.form-privacy a { color: var(--ink); overflow-wrap: anywhere; }

.btn {
    display: inline-flex; align-items: center; justify-content: center; white-space: nowrap;
    min-height: 48px; padding: 0 26px; border: 0; border-radius: 0; cursor: pointer;
    background: var(--ink); color: var(--paper); text-decoration: none;
    font: 800 15px/1 var(--head); letter-spacing: .04em; text-transform: uppercase;
    transition: background-color .2s, color .2s;
}
.btn:hover { background: var(--c1); color: #fff; }
.btn:active { transform: translateY(1px); }
.btn[aria-disabled="true"] { cursor: progress; opacity: .7; }

.form-done { padding: 26px 30px 30px; background: var(--card); border: 1.5px solid var(--ink); box-shadow: var(--shadow); overflow-wrap: anywhere; }
.form-done:focus { outline: none; }
.form-done h3 { margin: 0 0 8px; padding-bottom: 10px; border-bottom: 3px solid var(--ink); font: 900 28px/1.1 var(--head); font-stretch: 112%; }
.form-done p { margin: 0; }
.form-done .form-again { margin-top: 18px; }
.form-again a { display: inline-block; padding-block: 11px; margin-block: -11px; font: 700 15px/1.3 var(--head); }

/* ── Footer ───────────────────────────────────────────── */
.foot {
    display: grid; grid-template-columns: var(--col) minmax(0, 1fr) auto; column-gap: 56px; row-gap: 8px;
    padding: 22px 0 40px; border-top: 1.5px solid var(--ink); font-size: 14px; line-height: 1.5; color: var(--muted);
}
.foot h2 { margin: 0; font: 800 12px/1.6 var(--head); letter-spacing: .08em; text-transform: uppercase; color: var(--ink); }
.foot p { margin: 0; }
.facts { display: flex; align-items: baseline; gap: 10px; margin-top: 6px !important; font: 400 13px/1.45 var(--data); color: var(--ink); font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.facts::before { content: ""; flex: none; align-self: flex-start; margin-top: .45em; width: 8px; height: 8px; border-radius: 50%; background: var(--live); box-shadow: 0 0 0 3px color-mix(in srgb, var(--live) 25%, transparent); }
.foot ul { margin: 0; padding: 0; list-style: none; text-align: right; }
.foot li a { display: inline-flex; align-items: center; min-height: 32px; color: var(--ink); overflow-wrap: anywhere; }

/* ── Simple pages (404, error) ─────────────────────────── */
.plain { display: grid; grid-template-columns: var(--col) minmax(0, 1fr); column-gap: 56px; padding: 56px 0 120px; }
.plain > * { grid-column: 2; max-width: 34em; }
.plain h1 { margin: 0 0 16px; font: 900 clamp(34px, 4.6vw, 56px)/1 var(--head); font-stretch: 112%; letter-spacing: -.02em; }
.plain p { margin: 14px 0 0; font-size: 18px; }
.plain .plain-status { margin-top: 32px; padding-top: 12px; border-top: 1px solid var(--hair); font-size: 13px; color: var(--muted); overflow-wrap: anywhere; }

/* ── Narrower sheets ────────────────────────────────────── */
@media (max-width: 1180px) {
    :root { --col: 110px; }
    .title-row { grid-template-columns: minmax(0, 1fr); gap: 36px; }
    .title-intro { max-width: 560px; }
    .project, .part, .contact, .about, .plain, .foot { column-gap: 32px; }
    .pj-num { font-size: 112px; }
    .brief .pj-num { font-size: 96px; }
    .pj-grid, .brief .pj-grid { grid-template-columns: minmax(0, 1fr); }
    .pj-grid .phone { max-width: 240px; }
    .contact-inner { grid-template-columns: minmax(0, 1fr); gap: 32px; }
    .foot { grid-template-columns: var(--col) minmax(0, 1fr); }
    .foot ul { grid-column: 2; text-align: left; }
}

/* A phone: the thumb index becomes a sticky strip of tabs across the top. */
@media (max-width: 760px) {
    html { scroll-padding-top: 60px; }
    body { font-size: 16px; }
    .sheet { padding: 0 16px; }
    .running { gap: 0 8px; }
    /* Two links are all that fit beside the wordmark. */
    .running-nav :is(a[href="/#projects"], a[href="/#homelab"], a[href="/#embedded"]) { display: none; }
    .rail { position: sticky; top: 0; bottom: auto; z-index: 10; width: auto; margin: 0 -16px; background: var(--paper); }
    /* The tabs share the width, never narrower than a 44px tap target; past that the strip scrolls
       sideways (site.js keeps the open tab in view). */
    .tabs {
        position: static; display: grid; grid-auto-flow: column; grid-auto-columns: minmax(44px, 1fr); gap: 3px;
        max-height: none; padding: 0 0 4px; overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none;
    }
    .tabs::-webkit-scrollbar { display: none; }
    .tabs li, .tabs li:has([aria-current]) { flex: none; min-height: 0; }
    .tab, .tab[aria-current] { min-height: 44px; padding: 0; border-radius: 0 0 5px 5px; transform: translateY(-4px); }
    .tab[aria-current] span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); writing-mode: horizontal-tb; transform: none; }
    .tab b { font-size: 16px; }
    .tab:hover { transform: translateY(-2px); }
    .tab[aria-current] { transform: translateY(3px); box-shadow: 0 3px 8px -3px rgb(0 0 0 / .45); }
    .title { padding: 26px 0 56px; }
    .title h1 { margin-bottom: 22px; }
    .lede { font-size: 15.5px; line-height: 1.5; }
    .toc a { grid-template-columns: 28px 96px minmax(0, 1fr); grid-template-areas: "n thumb name" "n thumb what"; gap: 2px 14px; }
    .toc-n { grid-area: n; font-size: 26px; }
    .toc-thumb { grid-area: thumb; width: 96px; }
    .toc-name { grid-area: name; align-self: end; font-size: 17px; }
    .toc-what { grid-area: what; align-self: start; font-size: 14px; line-height: 1.3; }
    .callouts { grid-template-columns: minmax(0, 1fr); }
    .callout-layer g { transform: scale(1.4); }
    .project, .contact, .about, .plain, .foot { display: block; padding-bottom: 64px; }
    .part { display: block; padding: 22px 0 18px; }
    .pj-num, .brief .pj-num { padding-top: 18px; font-size: 80px; }
    .pj-body { padding-top: 6px; }
    .spec { flex-direction: column; width: 100%; }
    .spec div { border-right: 0; border-bottom: 1px solid var(--hair); }
    .spec div:last-child { border-bottom: 0; }
    .pj-lead, .pj-grid { margin-top: 32px; }
    .pj-grid { gap: 32px; }
    .pj-grid .phone { max-width: 210px; }
    /* One swipeable row, so the thumbnails stay close under the figure they swap. The padding
       leaves room for the selection outline inside the scroller. */
    .strip, .brief .strip {
        grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: 42%;
        overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x mandatory;
        padding: 6px 6px 10px; margin-inline: -6px; scrollbar-width: thin;
    }
    .strip li { scroll-snap-align: start; scroll-margin-inline: 6px; }
    .about { padding-top: 22px; }
    /* Each guest becomes a block: its name and size on one line, the kind and what it's for below.
       The column headings are hidden but still read out. */
    .guests thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
    .guests tbody tr { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 12px; padding: 11px 0; border-bottom: 1px solid var(--hair); }
    .guests tbody tr:first-child { border-top: 1.5px solid var(--ink); }
    .guests tbody tr:last-child { border-bottom: 1.5px solid var(--ink); }
    .guests th, .guests td, .guests tbody tr:last-child > * { padding: 0; border: 0; }
    .guests .g-kind { grid-row: 2; font-size: 14px; color: var(--muted); }
    .guests td:last-child { grid-column: 1 / -1; margin-top: 4px; }
    .contact { padding-top: 22px; }
    .contact-inner { margin-top: 0; }
    .card, .form-done { padding: 20px 16px 22px; }
    .plain { padding-top: 40px; }
    .foot { padding-bottom: 32px; }
    .foot ul { margin-top: 10px; }
}

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    .tab, .strip img, .btn, .theme-toggle, .theme-toggle::before, .field input, .field textarea { transition: none; }
}
