/* fonts.css */
/* inter-latin-ext-wght-normal */
@font-face {
  font-family: 'Inter Variable';
  font-style: normal;
  font-display: optional;
  font-weight: 100 900;
  src: url(fonts/inter-latin-ext-wght-normal.woff2) format('woff2-variations');
  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;
}
/* inter-latin-wght-normal */
@font-face {
  font-family: 'Inter Variable';
  font-style: normal;
  font-display: optional;
  font-weight: 100 900;
  src: url(fonts/inter-latin-wght-normal.woff2) format('woff2-variations');
  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;
}
/* archivo-latin-ext-wdth-normal */
@font-face {
  font-family: 'Archivo Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  src: url(fonts/archivo-latin-ext-wdth-normal.woff2) format('woff2-variations');
  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;
}
/* archivo-latin-wdth-normal */
@font-face {
  font-family: 'Archivo Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  src: url(fonts/archivo-latin-wdth-normal.woff2) format('woff2-variations');
  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.css */
/* Wanovix design tokens.
   Working values, not the brand: taken from William's brand handoff of
   7 October 2026 (exploration only). Dark is direction B, graphite with
   orange ("absolutely love how it goes with orange"); light is direction A,
   bone with orange, with cobalt kept structural (focus, the second data
   series). The Design Lab replaces values here and nothing else; the names
   stay. Read by the dashboard, the site and the lab. */

:root {
  color-scheme: light;

  /* Type. Inter for running text and data; Archivo (the brand handoff's
     leaning) for the display voice: titles and big figures, set a touch
     narrow through its width axis so figures stand tall in a tile. */
  --font-sans: "Inter Variable", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-display: "Archivo Variable", var(--font-sans);
  --display-stretch: 87.5%;
  --display-weight: 650;
  --display-tracking: -0.02em;
  /* The wordmark: Archivo condensed bold in caps, the handoff's variant #2. */
  --wordmark-stretch: 62%;
  --wordmark-weight: 700;
  --label-weight: 600;
  --label-tracking: 0.06em;

  /* Scale: ratio 1.2 from 16 px. On a phone nothing is below 16 px; from
     1024 px the two small steps go to 14 and 13 px for dense data. */
  --text-xs: 1rem;
  --text-sm: 1rem;
  --text-md: 1rem;
  --text-lg: 1.2rem;
  --text-xl: 1.44rem;
  --text-2xl: 1.728rem;
  --text-3xl: 2.488rem;
  --text-4xl: 3.583rem;
  --leading: 1.5;
  --leading-tight: 1.15;
  --measure: 68ch;

  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4.5rem;

  /* Tiles: square-ish modules with thin joints (William: "thin joints"). */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-pill: 999px;
  --joint: 1px;
  --tap: 44px;
  --rail: 15.5rem;
  --bar: 3.5rem;

  /* Light: bone ground, chalk tiles, graphite ink, orange spark. */
  --bg: oklch(94.9% 0.015 90.2);
  --grout: oklch(86.5% 0.02 87.5);
  --surface: oklch(97.6% 0.008 90);
  --surface-2: oklch(92.6% 0.016 88);
  --surface-3: oklch(89.5% 0.018 87);
  --line: oklch(87.7% 0.02 87.5);
  --line-strong: oklch(58% 0.012 85);
  --text: oklch(28.5% 0.004 84.6);
  --text-muted: oklch(44% 0.01 91.6);
  --accent: oklch(70.4% 0.191 44.5);
  --accent-strong: oklch(76% 0.165 52);
  --accent-soft: oklch(91% 0.05 55);
  --accent-text: oklch(48% 0.15 40);
  --on-accent: oklch(28.5% 0.004 84.6);
  --info: oklch(45% 0.18 266);
  --info-soft: oklch(91% 0.04 266);
  --focus: oklch(45% 0.19 266);

  /* States, each with its own hue and always with its word beside it. */
  --run: var(--accent-text);
  --ok: oklch(46% 0.11 150);
  --ok-soft: oklch(92% 0.045 150);
  --warn: oklch(47% 0.1 70);
  --warn-soft: oklch(92% 0.06 88);
  --stop: oklch(46% 0.17 27);
  --stop-soft: oklch(93% 0.035 27);
  --on-stop: oklch(97.6% 0.008 90);

  --shadow: 0 1px 2px oklch(28% 0.01 80 / 0.08), 0 8px 24px oklch(28% 0.01 80 / 0.08);
  --scrim: oklch(20% 0.004 84.6 / 0.55);

  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-fast: 120ms;
  --dur: 200ms;
  --motion: var(--dur) var(--ease-out);
}

@media (min-width: 1024px) {
  :root {
    --text-xs: 0.8125rem;
    --text-sm: 0.875rem;
  }
}

/* Dark: graphite ground and tiles, bone ink, orange spark. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: oklch(25.2% 0.004 84.6);
    --grout: oklch(21.5% 0.004 84.6);
    --surface: oklch(28.5% 0.004 84.6);
    --surface-2: oklch(31.5% 0.005 84.6);
    --surface-3: oklch(35% 0.005 84.6);
    --line: oklch(36% 0.005 84.6);
    --line-strong: oklch(60% 0.01 85);
    --text: oklch(94.9% 0.015 90.2);
    --text-muted: oklch(80% 0.016 88);
    --accent: oklch(73.1% 0.176 49.2);
    --accent-strong: oklch(78% 0.16 52);
    --accent-soft: oklch(34% 0.06 49);
    --accent-text: oklch(76% 0.16 52);
    --on-accent: oklch(25.2% 0.004 84.6);
    --info: oklch(74% 0.09 268);
    --info-soft: oklch(33% 0.045 268);
    --focus: oklch(84% 0.13 88);
    --ok: oklch(78% 0.13 150);
    --ok-soft: oklch(31% 0.045 150);
    --warn: oklch(85% 0.13 88);
    --warn-soft: oklch(33% 0.05 85);
    --stop: oklch(70% 0.17 27);
    --stop-soft: oklch(32% 0.07 27);
    --on-stop: oklch(21.5% 0.004 84.6);
    --shadow: 0 1px 2px oklch(0% 0 0 / 0.35), 0 12px 32px oklch(0% 0 0 / 0.35);
    --scrim: oklch(12% 0.004 84.6 / 0.7);
  }
}

/* The same values when the page asks for dark itself. */
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: oklch(25.2% 0.004 84.6);
  --grout: oklch(21.5% 0.004 84.6);
  --surface: oklch(28.5% 0.004 84.6);
  --surface-2: oklch(31.5% 0.005 84.6);
  --surface-3: oklch(35% 0.005 84.6);
  --line: oklch(36% 0.005 84.6);
  --line-strong: oklch(60% 0.01 85);
  --text: oklch(94.9% 0.015 90.2);
  --text-muted: oklch(80% 0.016 88);
  --accent: oklch(73.1% 0.176 49.2);
  --accent-strong: oklch(78% 0.16 52);
  --accent-soft: oklch(34% 0.06 49);
  --accent-text: oklch(76% 0.16 52);
  --on-accent: oklch(25.2% 0.004 84.6);
  --info: oklch(74% 0.09 268);
  --info-soft: oklch(33% 0.045 268);
  --focus: oklch(84% 0.13 88);
  --ok: oklch(78% 0.13 150);
  --ok-soft: oklch(31% 0.045 150);
  --warn: oklch(85% 0.13 88);
  --warn-soft: oklch(33% 0.05 85);
  --stop: oklch(70% 0.17 27);
  --stop-soft: oklch(32% 0.07 27);
  --on-stop: oklch(21.5% 0.004 84.6);
  --shadow: 0 1px 2px oklch(0% 0 0 / 0.35), 0 12px 32px oklch(0% 0 0 / 0.35);
  --scrim: oklch(12% 0.004 84.6 / 0.7);
}

/* White: the main colour of the public site (William, 15 October). The ink
   is graphite, orange is a fill, a line or a mark and never text; the raised
   surfaces take a trace of the orange so they read warm, not grey; the focus
   ring is the ink, as in the Design Lab. Asked for with data-ground="white"
   on a page that is light (data-theme="light"). */
:root[data-ground="white"] {
  color-scheme: light;
  --bg: oklch(100% 0 0);
  --grout: oklch(95.5% 0.02 49);
  --surface: oklch(97.5% 0.018 49);
  --surface-2: oklch(94.5% 0.025 49);
  --surface-3: oklch(92% 0.03 49);
  --line: oklch(89% 0.021 49);
  --line-strong: oklch(58% 0.012 85);
  --text: oklch(28.5% 0.004 84.6);
  --text-muted: oklch(46% 0.008 84.6);
  --accent: oklch(73.1% 0.176 49.2);
  --accent-strong: oklch(68% 0.18 46);
  --accent-soft: oklch(94% 0.04 49);
  --accent-text: oklch(52% 0.15 42);
  --on-accent: oklch(28.5% 0.004 84.6);
  --focus: oklch(28.5% 0.004 84.6);
}

@media (prefers-reduced-motion: reduce) {
  :root { --dur-fast: 0ms; --dur: 0ms; --motion: 0ms linear; }
}

/* brand.css */
/* The brand from the Design Lab (his bone and orange until he presses Final choice). Written from packages/tokens/final.json. */
:root, :root[data-ground] {
  color-scheme: light;
  --bg: #F2EEE3;
  --grout: #D6D2C8;
  --surface: #F9F6F0;
  --surface-2: #E6E2D8;
  --surface-3: #E0DCD1;
  --line: #D4D0C6;
  --line-strong: #81807E;
  --text: #2B2A28;
  --text-muted: #605F5D;
  --accent: #FD802E;
  --accent-strong: #FF9A62;
  --accent-soft: #FFE5D8;
  --accent-text: #AF4F00;
  --on-accent: #2B2A28;
  --focus: #2B2A28;
}

/* base.css */
/* Base styles every app shares; values only from @wanovix/tokens. */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--text-md);
  line-height: var(--leading);
  font-feature-settings: "cv11", "ss01";
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, p, ol, ul { margin: 0; }
ol, ul { padding: 0; list-style: none; }
a { color: inherit; }
button { font: inherit; color: inherit; }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: var(--radius-sm); }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* site.css */
/* wanovix.com: his bone, the Archivo wordmark and orange accents (William,
   15 and 16 October); orange is a fill, a line or a mark and never text. The
   homepage and the portfolio share the header, the globe language menu, the
   button and the footer from here. Values only from the tokens; the colours
   and shapes of the brand come from brand.css, which the Design Lab's Final
   choice writes (packages/tokens/final.json). */

:root { --gutter: var(--space-4); }

/* The header: the way home (on the other pages), the work, the language and
   the one button. Every target is at least --tap high. */
.top {
  /* Above the hero: the wordmark's glyph box reaches up under the header and
     would otherwise take its clicks. */
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-block-size: var(--bar);
  padding: var(--space-2) var(--gutter);
}
.top > :first-child { margin-inline-end: auto; }
/* On the homepage the wordmark below is the way home: the rest stays together
   on the right. */
.top > .top-nav:first-child { margin-inline: auto 0; }

.top-home {
  display: inline-flex;
  align-items: center;
  min-block-size: var(--tap);
  font-family: var(--font-display);
  font-stretch: var(--wordmark-stretch);
  font-weight: var(--wordmark-weight);
  font-size: var(--text-lg);
  text-transform: uppercase;
  text-decoration: none;
}

.top-nav a {
  display: inline-flex;
  align-items: center;
  min-block-size: var(--tap);
  padding-inline: var(--space-1);
  font-weight: var(--label-weight);
  text-decoration-line: underline;
  text-decoration-color: transparent;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.35em;
  transition: text-decoration-color var(--motion);
}
.top-nav a:hover { text-decoration-color: var(--accent); }
/* The current page is marked in graphite: orange on white is under 3:1. */
.top-nav a[aria-current="page"] { text-decoration-color: currentColor; }

/* The language menu: a disclosure that works without script; lang.js adds
   Escape, closing on a click elsewhere and remembering the choice. */
.lang { position: relative; }
.lang-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-block-size: var(--tap);
  min-inline-size: var(--tap);
  padding-inline: var(--space-2);
  border-radius: var(--radius-md);
  font-weight: var(--label-weight);
  list-style: none;
  cursor: pointer;
  user-select: none;
  transition: background-color var(--motion);
}
.lang-btn::-webkit-details-marker { display: none; }
.lang-btn:hover,
.lang[open] > .lang-btn { background: var(--surface-2); }
.globe {
  inline-size: 1.25rem;
  block-size: 1.25rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
}
.lang-list {
  position: absolute;
  inset-block-start: calc(100% + var(--space-1));
  inset-inline-end: 0;
  z-index: 10;
  display: grid;
  min-inline-size: 11rem;
  padding: var(--space-1);
  border: var(--joint) solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--bg);
  box-shadow: var(--shadow);
}
.lang-list a {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-block-size: var(--tap);
  padding-inline: var(--space-3);
  border-radius: var(--radius-md);
  text-decoration: none;
}
.lang-list a::before {
  content: "";
  inline-size: var(--space-4);
  block-size: var(--space-1);
  border-radius: var(--radius-pill);
  background: transparent;
}
.lang-list a:hover { background: var(--surface); }
.lang-list a[aria-current] { font-weight: var(--label-weight); }
.lang-list a[aria-current]::before { background: var(--accent); }

/* The one button: orange with a graphite label. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-block-size: var(--tap);
  padding: 0 var(--space-5);
  border-radius: var(--radius-md);
  background: var(--accent);
  color: var(--on-accent);
  font-weight: var(--label-weight);
  text-decoration: none;
  white-space: nowrap;
  transition: background-color var(--motion);
}
.btn:hover { background: var(--accent-strong); }
.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline-offset: 3px; border-radius: var(--radius-md); }
.top-cta { padding-inline: var(--space-3); }

/* A quiet link beside the button: the same height, an orange underline. */
.next {
  display: inline-flex;
  align-items: center;
  min-block-size: var(--tap);
  font-weight: var(--label-weight);
  text-decoration-line: underline;
  text-decoration-color: var(--accent);
  text-decoration-thickness: 2px;
  text-underline-offset: 0.35em;
  transition: text-decoration-thickness var(--motion);
}
.next:hover { text-decoration-thickness: var(--space-1); }

/* The wordmark: caps of Archivo condensed bold, sized by the width of its
   container (7 letters measure 3.33 em), so it spans it from 320 to 1920 px
   and, on a short screen, no more than about a third of the height; trimmed
   to its caps so the space around it is exact. An orange rule draws in under
   it once, on arrival. */
.mark {
  position: relative;
  padding-block-end: var(--space-4);
  font-family: var(--font-display);
  font-stretch: var(--wordmark-stretch);
  font-weight: var(--wordmark-weight);
  text-transform: uppercase;
  font-size: min(29.5cqi, 48dvh);
  line-height: 0.78;
  text-box: trim-both cap alphabetic;
  white-space: nowrap;
}
.mark::before {
  content: "";
  position: absolute;
  inset: auto 0 0;
  block-size: var(--space-1);
  background: var(--accent);
  transform-origin: left;
  animation: draw 900ms var(--ease-out) 150ms both;
}
@keyframes draw { from { transform: scaleX(0); } }

/* The hero: the wordmark and the statement, close under the header, so the
   first screen at every size already shows where the next part begins.
   Three arrangements to choose from (see the script in the page's head):
   a, the name first; b, the statement first; c, side by side. */
.hero {
  container-type: inline-size;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "mark" "line" "lede" "acts";
  gap: var(--space-4);
  padding: var(--space-4) var(--gutter) var(--space-6);
}
.hero .mark { grid-area: mark; margin-block-end: var(--space-2); }
.hero-line {
  grid-area: line;
  max-inline-size: 20ch;
  font-family: var(--font-display);
  font-stretch: var(--display-stretch);
  font-weight: var(--display-weight);
  letter-spacing: var(--display-tracking);
  font-size: var(--text-2xl);
  line-height: var(--leading-tight);
  text-wrap: balance;
}
/* The words that carry the promise stand on an orange stroke. */
.hero-line em {
  font-style: normal;
  background: linear-gradient(var(--accent), var(--accent)) 0 94% / 100% 0.12em no-repeat;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}
.hero-lede { grid-area: lede; max-inline-size: 46ch; color: var(--text-muted); text-wrap: pretty; }
.acts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-5);
}
.hero-acts { grid-area: acts; margin-block-start: var(--space-2); }

/* The brand motif in the hero (William, 25 October 2026): Labyrinth 4's still start frame in
   bone, orange and graphite, as the Design Lab's homepage preview shows it. The motif fills the panel,
   and every screen shows the same part of it whole, a square of 900 motif px around its centre: the
   shapes scale with the panel's shorter side, and a wider or taller panel shows the pattern going on
   around it. The name stands in the motif's clearing, 520 motif px wide, in graphite (the Lab's
   composition, frame-brand.js: S0 900, NAME 520). The frame is the Lab's own, its start frame with the
   clearing on, traced once to a vector: motif/labyrinth-4.svg, 4800 x 900 motif px around the centre.
   Its top and bottom are the panel's own edges (a panel wider than tall is 900 motif px high), and there
   the motif ends as it does around the clearing (round 5, William's note 9: "bleed out into the
   background colour"): each band ends in a round cap of its own width, nothing is cut off straight, and
   it flows into the bone ground; the sides run off the screen. A panel taller than wide (a phone) shows
   the same 900 px square whole with bone above and below. The motion stays in the Lab. The hero fills
   the first screen under the header. */
.hero { grid-template-rows: minmax(0, 1fr) auto auto auto; min-block-size: calc(100svh - var(--bar)); }
.brand-panel {
  grid-area: mark; position: relative; overflow: hidden; container-type: size;
  min-block-size: clamp(15rem, 42svh, 40rem);
  margin-inline: calc(var(--gutter) * -1); margin-block: calc(var(--space-4) * -1) var(--space-3);
  background: var(--bg);
}
.brand-motif {
  position: absolute; inset: 0;
  background: url(/motif/labyrinth-4.svg) 50% 50% / calc(min(100cqw, 100cqh) * 48 / 9) auto no-repeat;
}
/* the name: 7 capitals of the wordmark measure 3.331 em (measured in the browser), so 520 of 900 motif px */
.brand-name {
  position: absolute; inset: 0; display: grid; place-items: center; margin: 0;
  font-family: var(--font-display); font-stretch: var(--wordmark-stretch); font-weight: var(--wordmark-weight);
  font-size: calc(min(100cqw, 100cqh) * 520 / 900 / 3.331); line-height: 1;
  text-transform: uppercase; white-space: nowrap; color: var(--text); pointer-events: none;
}

:root[data-hero="b"] .hero { grid-template-areas: "line" "mark" "lede" "acts"; }
:root[data-hero="b"] .hero-line { max-inline-size: 18ch; font-size: var(--text-2xl); }
:root[data-hero="c"] .hero-line { font-size: var(--text-2xl); }

/* The motif William chose in the Design Lab, when his Final choice has one: a full-bleed panel
   at the top of the hero (motif.js draws it), the name inside it where the motif leaves room for
   it: in the river's pool, on the dots' island, on an island the moving blobs flow around, big in
   the band's solid top. A logotype is exempt from text contrast; its two colours are the motif's. */
.mark-panel { position: relative; grid-area: mark; display: grid; place-items: center; overflow: hidden;
  block-size: clamp(16rem, 40svh, 30rem); margin-inline: calc(var(--gutter) * -1); margin-block-start: calc(var(--space-4) * -1);
  background: var(--motif-ground, var(--bg)); }
.mark-panel > .motif { position: absolute; inset: 0; }
.mark-panel > .motif svg, .mark-panel > .motif canvas { display: block; inline-size: 100%; block-size: 100%; }
:root[data-motif="lava"] .mark-panel > .motif, :root[data-motif="labyrinth"] .mark-panel > .motif, :root[data-motif="marble"] .mark-panel > .motif { touch-action: pan-y; }
.mark-panel .mark { grid-area: auto; position: relative; z-index: 1; margin: 0; padding: 0; font-size: min(11cqi, 9rem); color: var(--motif-ink, var(--accent)); pointer-events: none; }
.mark-panel .mark::before { content: none; }
:root[data-motif="river"] .mark-panel .mark { color: var(--motif-ground, var(--bg)); }
:root[data-motif="dots"][data-island="open"] .mark-panel { display: block; block-size: auto; overflow: visible; background: none; }
:root[data-motif="dots"][data-island="open"] .mark-panel > .motif { position: relative; block-size: clamp(12rem, 30svh, 22rem); }
:root[data-motif="dots"][data-island="open"] .mark-panel .mark { padding: var(--space-4) var(--gutter) 0; color: var(--text); font-size: min(29.5cqi, 48dvh); }
:root[data-motif="band"] .mark-panel { block-size: clamp(22rem, 54svh, 40rem); place-items: start center; }
:root[data-motif="band"] .mark-panel .mark { block-size: var(--band-top, 50%); display: grid; place-items: center; color: var(--motif-ground, var(--bg)); font-size: min(15cqi, 12rem); }
:root[data-motif="band"][data-bandmark="graphite"] .mark-panel .mark { color: var(--text); }
@media (min-width: 1024px) {
  :root[data-hero="c"] .mark-panel { margin-inline-end: 0; block-size: auto; min-block-size: clamp(24rem, 60svh, 40rem); grid-row: 1 / span 3; }
}

/* A portrait screen (a phone, a tablet held upright): the brand panel is square, as wide as the screen,
   so its top and bottom are the motif's own round-capped edges (900 motif px high) and no empty bone stands
   above or below it; the hero is then as tall as it needs to be, and the story's start shows under it. */
@media (max-aspect-ratio: 1/1) {
  .hero { min-block-size: 0; grid-template-rows: auto auto auto auto; }
  .brand-panel { aspect-ratio: 1 / 1; min-block-size: 0; }
}

/* The sections: a large line in the display face, and the words beside it
   on a wide screen. */
.band {
  padding: var(--space-7) var(--gutter) var(--space-8);
  border-block-start: var(--joint) solid var(--line);
}
.display {
  font-family: var(--font-display);
  font-stretch: var(--display-stretch);
  font-weight: var(--display-weight);
  letter-spacing: var(--display-tracking);
  font-size: var(--text-2xl);
  line-height: var(--leading-tight);
  text-wrap: balance;
}
.points { display: grid; gap: var(--space-7); }
.point { display: grid; gap: var(--space-4); }
.point .display { max-inline-size: 18ch; }
.point p { max-inline-size: 52ch; text-wrap: pretty; }

/* The work: short mentions, each a row that leads to the portfolio. */
.work-list { list-style: none; padding: 0; border-block-end: var(--joint) solid var(--line); margin-block: var(--space-5) var(--space-5); }
.work-row {
  display: grid;
  gap: var(--space-2);
  padding-block: var(--space-5);
  border-block-start: var(--joint) solid var(--line);
  text-decoration: none;
}
.work-name {
  font-family: var(--font-display);
  font-stretch: var(--display-stretch);
  font-weight: var(--display-weight);
  letter-spacing: var(--display-tracking);
  font-size: var(--text-xl);
  line-height: var(--leading-tight);
  text-decoration: underline 2px transparent;
  text-underline-offset: 0.2em;
  transition: text-decoration-color var(--motion);
}
.work-who { color: var(--text-muted); }
.work-line { max-inline-size: 46ch; }
.work-go {
  justify-self: start;
  font-weight: var(--label-weight);
  text-decoration: underline 2px var(--accent);
  text-underline-offset: 0.35em;
}
.work-row:hover .work-name { text-decoration-color: var(--accent); }
.work-row:hover .work-go { text-decoration-thickness: var(--space-1); }
.work-row:focus-visible { outline-offset: var(--space-1); }

/* The close: one question, one sentence, the button. */
.close { display: grid; justify-items: start; gap: var(--space-5); }
.close .display { max-inline-size: 16ch; font-size: var(--text-3xl); line-height: 1.05; }
.close p { max-inline-size: 44ch; color: var(--text-muted); text-wrap: pretty; }

/* The page for an address that does not exist (404.html, nl/404.html): the
   close band's shape, a little lower on the screen, with the way back. */
.lost { display: grid; justify-items: start; align-content: center; gap: var(--space-5); min-block-size: 62vh; }
.lost .display { max-inline-size: 16ch; font-size: var(--text-3xl); line-height: 1.05; }
.lost p { max-inline-size: 44ch; color: var(--text-muted); text-wrap: pretty; }
.lost .lost-code { font-weight: var(--label-weight); letter-spacing: 0.08em; text-transform: uppercase; color: var(--text); }
.lost .lost-other a { color: var(--text); }

.foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2) var(--space-5);
  padding: var(--space-4) var(--gutter);
  border-block-start: var(--joint) solid var(--line);
  color: var(--text-muted);
}
.foot-mark {
  font-family: var(--font-display);
  font-stretch: var(--wordmark-stretch);
  font-weight: var(--wordmark-weight);
  font-size: var(--text-lg);
  text-transform: uppercase;
  color: var(--text);
}
.foot a {
  display: inline-flex;
  align-items: center;
  min-block-size: var(--tap);
  text-decoration-color: var(--accent);
  text-underline-offset: 0.35em;
}
.foot a:hover { color: var(--text); }

/* Four header items on a subpage fit a 320 phone, also with "Contacteer
   ons": below 420 the wordmark, the paddings and the gaps are a step smaller,
   and below 360 the wordmark and the gaps are smaller again and the globe
   stands alone (its language code is still read). */
@media (max-width: 359.98px) {
  .top { gap: var(--space-1); }
  .top-home { font-size: var(--text-md); }
  .lang-now {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
}

/* The homepage on a small phone: its header's two links, the globe and the button do not fit one
   line (in Dutch under 445 px, in English under 360 px), so the header leaves its button to the
   hero, which shows the same button just below it. */
@media (max-width: 444.98px) {
  :root[lang="nl"] .top > .top-nav:first-child ~ .top-cta { display: none; }
}
@media (max-width: 359.98px) {
  .top > .top-nav:first-child ~ .top-cta { display: none; }
}
/* The next version: on a subpage the Dutch header leaves its button below 465 px, where the wordmark,
   the link that stays, the globe and "Contacteer ons" no longer share a line (the portfolio's link to
   itself gives way below 535 px, in portfolio.css; every header's button goes below 360 px); the
   page's close holds the same button. */
@media (max-width: 464.98px) { :root[lang="nl"] .top > .top-home:first-child ~ .top-cta { display: none; } }
@media (max-width: 359.98px) { .top-cta { display: none; } }
/* The header's links never wrap to a second line. In English, where the button stays, below 480 px a
   subpage's link to itself gives way (it is the page the visitor is on; the wordmark leads home). */
.top-nav { display: flex; flex-wrap: nowrap; white-space: nowrap; }
@media (max-width: 479.98px) { :root:not([lang="nl"]) .top-nav a[aria-current="page"] { display: none; } }

@media (min-width: 420px) {
  .top { gap: var(--space-3); }
  .top-home { font-size: var(--text-xl); }
  .top-nav a { padding-inline: var(--space-2); }
  .lang-btn { gap: var(--space-2); padding-inline: var(--space-3); }
  .top-cta { padding-inline: var(--space-4); }
}

@media (min-width: 768px) {
  :root { --gutter: var(--space-6); }
  .hero-line { max-inline-size: 22ch; font-size: var(--text-3xl); }
  .hero-lede, .point p, .close p { font-size: var(--text-lg); }
  :root[data-hero="b"] .hero-line,
  :root[data-hero="c"] .hero-line { font-size: var(--text-3xl); }
  .display { font-size: var(--text-3xl); }
  .work-name { font-size: var(--text-2xl); }
  .close .display { font-size: var(--text-4xl); }
  .lost .display { font-size: var(--text-4xl); }
  .lost p { font-size: var(--text-lg); }
  .band { padding-block: var(--space-8); }
  .points { gap: var(--space-8); }
}

@media (min-width: 1024px) {
  .hero {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "mark mark" "line acts" "lede acts";
    grid-template-rows: minmax(0, 1fr) auto auto;
    column-gap: var(--space-7);
  }
  .hero-acts { align-self: end; margin-block-start: 0; }
  :root[data-hero="b"] .hero { grid-template-areas: "line line" "mark mark" "lede acts"; }
  :root[data-hero="c"] .hero {
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
    grid-template-rows: 1fr auto auto;
    grid-template-areas: "mark line" "mark lede" "mark acts";
    align-items: end;
  }
  :root[data-hero="c"] .hero .mark { font-size: min(16cqi, 40dvh); margin-block-end: 0; }
  :root[data-hero="c"] .hero-acts { align-self: end; }

  .point { grid-template-columns: minmax(0, 6fr) minmax(0, 5fr); column-gap: var(--space-8); align-items: start; }
  .point p { padding-block-start: var(--space-2); }
  .work-row {
    grid-template-columns: minmax(0, 5fr) minmax(0, 6fr) auto;
    grid-template-rows: auto auto;
    column-gap: var(--space-7);
    align-items: baseline;
  }
  .work-name { grid-row: 1 / span 2; }
  .work-go { grid-column: 3; grid-row: 1; }
  .work-who { grid-column: 2; grid-row: 1; }
  .work-line { grid-column: 2; grid-row: 2; }
}

@media (min-width: 1280px) {
  /* The statement is the page's h1: never smaller than a section's line. */
  .hero-line { font-size: var(--text-4xl); }
  :root[data-hero="b"] .hero-line,
  :root[data-hero="c"] .hero-line { font-size: var(--text-4xl); }
  .display { font-size: var(--text-4xl); }
  .work-name { font-size: var(--text-3xl); }
}

@media (min-width: 1440px) {
  :root { --gutter: var(--space-7); }
}

@media (prefers-reduced-motion: reduce) {
  .mark::before { animation: none; }
}


/* ======================================================================
   The next homepage (the website rounds of 18 and 19 October 2026). Every
   part below stands complete and still without script or motion: motion.js
   and the scroll timelines only take a part apart and play it back. Orange
   stays a fill, a line or a mark, and the one colour of what can be pressed;
   the ballpoint blue is an ink for drawing, never a control.
   ====================================================================== */

:root { --ink: #24439c; }

.skip {
  position: absolute; inset-inline-start: var(--gutter); inset-block-start: var(--space-2); z-index: 10;
  padding: var(--space-2) var(--space-4); border-radius: var(--radius-md);
  display: inline-flex; align-items: center; min-block-size: var(--tap);
  background: var(--text); color: var(--bg); font-weight: var(--label-weight);
  transform: translateY(-200%);
}
.skip:focus { transform: none; }

/* The header stays at the top on a wide screen and draws its edge once the page has moved. */
@media (min-width: 768px) {
  .top { position: sticky; inset-block-start: 0; background: var(--bg); }
  /* what gets focus or is jumped to lands under the header, not behind it */
  html { scroll-padding-block-start: calc(var(--bar) + 1rem); }
}

/* ---- H2: what you tell us becomes your software (round 6, 26 October 2026: William's OK of the story
   proposal; STORY-PROPOSAL.md in the OS repository). Nothing is pinned and nothing holds the scroll: the three
   titles are ordinary text that scrolls past at the page's own speed, and the picture is sticky: beside them,
   centred under the header, on a screen at least 768 px wide; on top of the screen while they scroll under it
   on a phone (both at least 600 px tall, with motion). Every change in the picture follows the scroll position
   itself (motion.js): the package comes apart, an orange story line draws itself from title to title and on a
   wide screen runs into the picture as the pen that writes the whiteboard, the marker lines straighten into
   their software, Lies's job moves to Wednesday, and the line ends in a dot at the heading. A short runway: one
   title every --step, then --tail and the picture scrolls away with the page. On a short screen and with less
   motion: the stacked version, each title with its own picture (the package, the whiteboard, the software),
   nothing sticky; with less motion everything stands still at its end. Without script: the titles and the
   software. ---- */
.tale { overflow-x: clip; --step: clamp(17rem, 35svh, 26rem); --tail: clamp(8rem, 18svh, 12rem); --pen: var(--accent); }
.tale-grid { display: grid; grid-template-columns: minmax(0, 1fr); align-content: start; }
.tale-title {
  margin: 0; font-family: var(--font-display); font-stretch: var(--display-stretch); font-weight: var(--display-weight);
  letter-spacing: var(--display-tracking); line-height: 1.04; font-size: clamp(2rem, 8.8vw, 2.25rem); text-wrap: balance; color: var(--text);
}
.tale-t1, .tale-t2 { margin-block-start: var(--space-7); }
.tale-pic, .tale-stage { margin-block-start: var(--space-5); }
/* the pictures motion.js draws (the package, the whiteboard) are there only when it draws them */
.tale-pic { display: none; }
:root[data-tale="stacked"] .tale-pic { display: block; }

.field, .tale-pic { position: relative; aspect-ratio: 358 / 330; max-inline-size: 28rem; container: field / inline-size; }
.soft {
  position: absolute; inset: 0; display: grid; gap: var(--joint);
  grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: auto minmax(0, 1fr);
  background: var(--grout); border-radius: var(--radius-lg); overflow: hidden; cursor: default;
  box-shadow: 0 24px 48px -28px color-mix(in oklch, var(--text) 40%, transparent);
  font-size: var(--text-md); line-height: 1.3;
}
.soft-bar { grid-column: 1 / -1; margin: 0; display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); min-block-size: 3rem; padding: var(--space-2) var(--space-3) var(--space-2) var(--space-4); background: var(--surface); }
.soft-name { display: inline-flex; align-items: center; gap: 0.625rem; font-family: var(--font-display); font-stretch: var(--display-stretch); font-weight: var(--display-weight); font-size: 1.125rem; }
.soft-name b { inline-size: 1.375rem; block-size: 1.375rem; border-radius: 5px; background: var(--text); }
.soft-people { display: flex; gap: 0.25rem; }
.soft-people b, .soft-who b {
  display: inline-grid; place-items: center; flex: none; inline-size: 2rem; block-size: 2rem; border-radius: 50%;
  background: var(--surface-3); color: var(--text); font-size: 1rem; font-weight: 700; letter-spacing: -0.03em;
}
.soft-side { display: none; margin: 0; }
.soft-col { display: flex; flex-direction: column; gap: 0.5rem; padding-block-end: 0.5rem; background: var(--bg); min-inline-size: 0; }
.soft-col-0, .soft-col-3 { display: none; }
.soft-day { margin: 0 0 0.125rem; display: flex; align-items: center; min-block-size: 2.5rem; padding-inline: 0.75rem; background: var(--surface); box-shadow: 0 var(--joint) 0 var(--grout); font-weight: var(--label-weight); }
.soft-card { display: grid; gap: 0.4rem; margin: 0 0.5rem; padding: 0.625rem 0.75rem; border-radius: 6px; background: var(--surface); box-shadow: 0 1px 0 var(--line); min-inline-size: 0; }
.soft-job { font-weight: var(--label-weight); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.soft-who { display: flex; align-items: center; gap: 0.45rem; color: var(--text-muted); }
.soft-urgent { border-inline-start: 3px solid var(--ink); padding-inline-start: calc(0.75rem - 3px); }
.soft-flag { display: inline-flex; align-items: center; gap: 0.3rem; color: var(--ink); font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; }
.soft-flag-mark { inline-size: 0.7em; block-size: 0.85em; flex: none; }
.soft-card.soft-more { display: none; }
/* the place the moved job left on Tuesday */
.soft-gap { visibility: hidden; }
.soft-moved { background: var(--text); color: var(--surface); box-shadow: 0 16px 30px -14px color-mix(in oklch, var(--text) 70%, transparent); }
.soft-moved .soft-who { color: color-mix(in oklch, var(--surface) 78%, transparent); }
.soft-moved .soft-who b { background: color-mix(in oklch, var(--surface) 88%, transparent); color: var(--text); }
.soft-card.soft-cover { position: absolute; inset: 0; margin: 0; border-radius: inherit; color: var(--text); }
.soft-moved .soft-cover .soft-who { color: var(--text-muted); }
.soft-moved .soft-cover .soft-who b { background: var(--surface-3); color: var(--text); }
.soft-lift { position: absolute; inset: 0; border-radius: inherit; pointer-events: none; box-shadow: 0 16px 30px -14px color-mix(in oklch, var(--text) 70%, transparent); }

@media (min-width: 768px) {
  /* the pictures: the whole column's width, a little wider than tall: two days filled to their last card */
  .tale { --field-h: clamp(19.5rem, calc(63svh - 8.5rem), 21rem); }
  .field, .tale-pic { aspect-ratio: auto; inline-size: 100%; block-size: var(--field-h); max-inline-size: none; }
  .tale-title { font-size: clamp(2.25rem, min(3.75vw, 6.2svh), 4.25rem); }
  /* the titles beside the pictures: the stacked version, a row each */
  .tale-grid { grid-template-columns: minmax(0, 0.39fr) minmax(0, 0.61fr); column-gap: clamp(var(--space-6), 3.6vw, 4.5rem); row-gap: var(--space-7); align-items: center; }
  .tale-t0, .tale-t1, .tale-t2 { grid-column: 1; margin: 0; }
  .tale-pic, .tale-stage { grid-column: 2; margin: 0; }
  .tale-t0, .tale-p0 { grid-row: 1; }
  .tale-t1, .tale-p1 { grid-row: 2; }
  .tale-t2 { grid-row: 3; }
  .tale-stage { grid-row: 3; }
  /* without the pictures (no script): the software beside all three titles */
  :root:not([data-tale]) .tale-stage { grid-row: 1 / 4; }
  /* the sticky version: the titles a --step apart, the stage beside them all, held in the middle of
     the screen under the header while they pass, until --tail after the last */
  :root[data-tale="sticky"] .tale-grid { grid-template-rows: var(--step) var(--step) var(--step) var(--tail); row-gap: 0; align-items: center; }
  :root[data-tale="sticky"] .tale-title { color: var(--text-muted); transition: color 400ms var(--ease-out); }
  :root[data-tale="sticky"] .tale-title.is-now { color: var(--text); }
  :root[data-tale="sticky"] .tale-stage {
    grid-row: 1 / 5; align-self: start; position: sticky;
    inset-block-start: calc(var(--bar) + (100svh - var(--bar) - var(--field-h)) / 2);
  }
}
/* the phone's sticky version: the picture on top of the screen (its own row, as tall as the picture: --ph,
   which motion.js measures), the titles a --step apart under it, scrolling under the picture and out of
   sight beneath it, until --tail after the last */
@media (max-width: 767.98px) {
  :root[data-tale="sticky"] .tale { --step: clamp(13rem, 32svh, 19rem); --tail: clamp(7rem, 16svh, 10rem); }
  :root[data-tale="sticky"] .tale-grid { grid-template-rows: var(--ph, 22rem) var(--step) var(--step) var(--step) var(--tail); align-items: center; }
  /* the titles in the picture's column too, under it (placed by hand, so the grid never adds a column) */
  :root[data-tale="sticky"] .tale-title { grid-column: 1; margin: 0; color: var(--text-muted); transition: color 400ms var(--ease-out); }
  :root[data-tale="sticky"] .tale-title.is-now { color: var(--text); }
  :root[data-tale="sticky"] .tale-t0 { grid-row: 2; }
  :root[data-tale="sticky"] .tale-t1 { grid-row: 3; }
  :root[data-tale="sticky"] .tale-t2 { grid-row: 4; }
  :root[data-tale="sticky"] .tale-stage {
    grid-row: 1 / 6; grid-column: 1; align-self: start; position: sticky; inset-block-start: var(--space-3); z-index: 2;
    /* its ground runs from edge to edge, so the story line and what scrolled past go under it too */
    margin: 0 calc(var(--gutter) * -1); padding: 0 var(--gutter) var(--space-3); background: var(--bg);
  }
  /* nothing that has scrolled past peeks out in the gap above the picture */
  :root[data-tale="sticky"] .tale-stage::before { content: ""; position: absolute; inset: calc(var(--space-3) * -1) 0 100%; background: var(--bg); pointer-events: none; }
  /* what scrolls under the picture fades out under its lower edge */
  :root[data-tale="sticky"] .tale-stage::after { content: ""; position: absolute; inset: 100% 0 auto; block-size: var(--space-5); background: linear-gradient(var(--bg), color-mix(in oklch, var(--bg) 0%, transparent)); pointer-events: none; }
}
/* on a phone that will get the sticky version, the picture stands first from the start, so nothing moves
   on the screen when motion.js sets it up (Lighthouse, round 6) */
@media (scripting: enabled) and (prefers-reduced-motion: no-preference) and (max-width: 767.98px) and (min-height: 600px) {
  :root:not([data-motion="reduce"]):not([data-tale]) .tale-stage { order: -1; margin-block: 0 var(--space-5); }
}
/* the story line (motion.js draws it): beside the titles in the band's margin, under the second title, into
   the picture on a wide screen; a dot where it starts and one at the heading */
:root[data-tale="sticky"] .tale-grid { position: relative; }
.tale-line { position: absolute; inset-block-start: 0; inset-inline-start: 0; overflow: visible; pointer-events: none; z-index: 1; }
.tale-line path { fill: none; stroke: var(--pen); stroke-linecap: round; stroke-linejoin: round; }
.tale-line circle { fill: var(--pen); }
/* a tall tablet: room for Wednesday's third job too, so both days run to the panel's foot */
@media (min-width: 768px) and (max-width: 1099.98px) and (min-height: 800px) {
  .tale { --field-h: 22.25rem; }
  .soft-card.soft-more { display: grid; }
}
@media (min-width: 1100px) {
  /* the width sets the panel (the whole column, the week from a laptop's 1280 px up); its height grows
     with the screen and with its width, so the week fills it on a laptop and on a 2560 monitor alike */
  .tale { --field-h: clamp(19.75rem, min(calc(64vh - var(--bar) - 4rem), calc(18vw + 9rem)), 46rem); }
}
/* a wide but short window has the stacked version (motion.js): the pictures are square there, room for two days */
@media (min-width: 1100px) and (max-height: 599.98px) {
  .field, .tale-pic { aspect-ratio: 1 / 1; inline-size: min(100%, 28rem); block-size: auto; }
}
@container field (min-width: 640px) {
  /* a field wide enough for the whole week: dense, like the real thing, and drawn larger the larger the
     field (--u is one pixel of the drawing: 1 up to a field about 1100 px wide, then growing with it) */
  .soft { --u: calc(clamp(14px, 1.27cqi, 21px) / 14); grid-template-columns: repeat(4, minmax(0, 1fr)); font-size: calc(14 * var(--u)); }
  .soft-bar { min-block-size: calc(52 * var(--u)); padding: calc(8 * var(--u)) calc(12 * var(--u)) calc(8 * var(--u)) calc(16 * var(--u)); }
  .soft-name { font-size: calc(19 * var(--u)); gap: calc(10 * var(--u)); }
  .soft-name b { inline-size: calc(22 * var(--u)); block-size: calc(22 * var(--u)); border-radius: calc(5 * var(--u)); }
  .soft-people { gap: calc(4 * var(--u)); }
  .soft-people b { inline-size: calc(28 * var(--u)); block-size: calc(28 * var(--u)); font-size: calc(11 * var(--u)); }
  .soft-who { gap: calc(7 * var(--u)); }
  .soft-who b { inline-size: calc(22 * var(--u)); block-size: calc(22 * var(--u)); font-size: calc(11 * var(--u)); }
  .soft-col-0, .soft-col-3 { display: flex; }
  .soft-card.soft-more { display: grid; }
  .soft-job { white-space: normal; text-wrap: balance; }
  .soft-day { min-block-size: calc(42 * var(--u)); padding-inline: calc(12 * var(--u)); margin-block-end: calc(2 * var(--u)); }
  .soft-card { margin-inline: calc(6 * var(--u)); padding: calc(10 * var(--u)); gap: calc(6.4 * var(--u)); border-radius: calc(6 * var(--u)); }
  .soft-urgent { padding-inline-start: calc(10 * var(--u) - 3px); }
  .soft-col { gap: calc(7 * var(--u)); padding-block-end: calc(8 * var(--u)); }
  .soft-flag { font-size: calc(12 * var(--u)); gap: calc(4.8 * var(--u)); }
}
@container field (min-width: 760px) {
  /* room for the side bar too, as wide as its longest label in either language (never cut) */
  .soft { grid-template-columns: max-content repeat(4, minmax(0, 1fr)); }
  .soft-side { display: grid; align-content: start; gap: calc(6 * var(--u)); padding: calc(14 * var(--u)) calc(8 * var(--u)); background: var(--surface); }
  .soft-side span { display: block; min-block-size: calc(32 * var(--u)); line-height: calc(32 * var(--u)); padding-inline: calc(8 * var(--u)) calc(12 * var(--u)); border-radius: calc(6 * var(--u)); font-size: calc(14 * var(--u)); white-space: nowrap; }
  .soft-side span.is-on { background: var(--surface-2); font-weight: var(--label-weight); }
  .soft-side i { display: inline-block; vertical-align: -0.05em; margin-inline-end: calc(8 * var(--u)); inline-size: calc(10 * var(--u)); block-size: calc(10 * var(--u)); border-radius: 2px; background: var(--surface-3); }
  .soft-side .is-on i { background: var(--text); }
}
/* a laptop's shorter screen caps the panel lower: the week's cards sit closer, all four of
   Thursday's jobs still whole */
@media (min-width: 1100px) and (max-height: 842.98px) {
  @container field (min-width: 640px) {
    .soft-bar { min-block-size: calc(40 * var(--u)); padding-block: calc(4 * var(--u)); }
    .soft-name { font-size: calc(17 * var(--u)); }
    .soft-people b { inline-size: calc(24 * var(--u)); block-size: calc(24 * var(--u)); font-size: calc(10 * var(--u)); }
    .soft-day { min-block-size: calc(32 * var(--u)); }
    .soft-col { gap: calc(5 * var(--u)); padding-block-end: calc(6 * var(--u)); }
    .soft-card { padding: calc(7 * var(--u)) calc(9 * var(--u)); gap: calc(4 * var(--u)); }
    .soft-urgent { padding-inline-start: calc(9 * var(--u) - 3px); }
    .soft-job { line-height: 1.25; }
    .soft-who b { inline-size: calc(18 * var(--u)); block-size: calc(18 * var(--u)); font-size: calc(9 * var(--u)); }
    .soft-side { padding-block: calc(10 * var(--u)); gap: calc(4 * var(--u)); }
    .soft-side span { min-block-size: calc(28 * var(--u)); line-height: calc(28 * var(--u)); }
  }
}

/* The scene's own layer (motion.js builds it inside the field): the package that does not fit, the
   whiteboard and its marker, the trail of the job on its way. */
.tale-fly { position: absolute; inset: 0; pointer-events: none; z-index: 2; }
.tale-fly:empty { display: none; }
.fly-pkg { position: absolute; background: var(--surface-2); border: 1.5px solid var(--line-strong); border-radius: 6px; }
.fly-part { position: absolute; inset-block-start: 0; inset-inline-start: 0; border-radius: 4px; box-sizing: border-box; will-change: transform; }
.fly-board { position: absolute; inset: 0; background: var(--surface); border: var(--joint) solid var(--line); border-radius: 6px; box-shadow: 0 20px 40px -26px color-mix(in oklch, var(--text) 45%, transparent); }
.fly-ink { position: absolute; inset-block-start: 0; inset-inline-start: 0; overflow: visible; }
.fly-ink path { fill: none; stroke: var(--pen, var(--ink)); stroke-linecap: round; stroke-linejoin: round; }
.fly-trail { position: absolute; inset-block-start: 0; inset-inline-start: 0; border-radius: 6px; background: var(--text); will-change: transform; }
/* Until the motion is set up, the software waits hidden, so nothing a visitor already sees
   disappears when it starts (motion.js marks the page data-motion-done once it is set up, or once it
   will not be). Without scripts, with less motion asked, and after four seconds whatever happens, it
   stands. The titles never wait: they are the page's text. */
@media (scripting: enabled) and (prefers-reduced-motion: no-preference) {
  :root:not([data-motion="reduce"]):not([data-motion-done]) .field {
    visibility: hidden;
    animation: wait-for-motion 0s 4s forwards;
  }
}
@keyframes wait-for-motion { to { visibility: visible; } }

/* ---- William Cooke (round 4, 25 October 2026): who is behind Wanovix, in his own words, beside his
   headshot (no label, no caption). The photo on top on a phone, beside the words from 768 px. ---- */
.who { display: grid; gap: var(--space-6); align-items: center; }
.who-photo {
  display: block; inline-size: min(100%, 22rem); block-size: auto; aspect-ratio: 3 / 4; object-fit: cover;
  border-radius: var(--radius-lg); background: var(--surface-2);
}
.who-say { display: grid; gap: var(--space-4); max-inline-size: 40rem; }
.who-say p { text-wrap: pretty; }
.who-say em { font-style: italic; }
@media (min-width: 768px) {
  .who { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); column-gap: clamp(var(--space-6), 6vw, 7rem); }
  .who-photo { inline-size: 100%; max-inline-size: 26rem; justify-self: start; }
  .who-say p { font-size: var(--text-lg); }
}

/* ---- H3: the calculator (round 3): a card per task done by hand, how often and how long, what an
   hour is worth, then the year on graphite with a bar per task. ---- */
.calc { display: grid; gap: var(--space-6); }
.calc .display { max-inline-size: 30ch; }
.calc-body { display: grid; gap: var(--space-5); }
.calc-form { display: grid; gap: var(--space-4); align-content: start; min-inline-size: 0; }
.tasks { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }
.task {
  display: grid; gap: var(--space-2); min-inline-size: 0;
  padding: var(--space-3) var(--space-3) var(--space-3) var(--space-4);
  border: var(--joint) solid var(--line); border-radius: var(--radius-lg); background: var(--surface);
}
.task-top { display: flex; align-items: center; gap: var(--space-2); min-inline-size: 0; }
.task-name {
  flex: 1; min-inline-size: 0; min-block-size: var(--tap); padding: 0.55rem var(--space-1) 0.45rem; resize: none; overflow: hidden; field-sizing: content; line-height: 1.3;
  border: 0; border-block-end: 2px dashed var(--line-strong); border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  background: transparent; color: var(--text); font: inherit; font-weight: var(--label-weight); font-size: var(--text-lg);
}
.task-name::placeholder { color: var(--text-muted); font-weight: 400; }
.task-name:hover { border-block-end-color: var(--text); }
.task-name:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; border-block-end-style: solid; }
.task-del {
  flex: none; display: grid; place-items: center; inline-size: var(--tap); block-size: var(--tap);
  border: 0; border-radius: var(--radius-md); background: transparent; color: var(--text-muted); cursor: pointer;
  transition: background-color var(--motion), color var(--motion);
}
.task-del svg, .task-add svg { inline-size: 1.25rem; block-size: 1.25rem; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
.task-del:hover { background: var(--surface-2); color: var(--text); }
.task-del:focus-visible, .task-add:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.task-nums { display: flex; flex-wrap: wrap; align-items: center; gap: 0 var(--space-3); margin: 0; color: var(--text-muted); }
/* where the two figures of a task take two lines, the dot between them goes */
@media (max-width: 479.98px) { .task-nums { gap: 0 var(--space-4); } .task-nums .calc-dot { display: none; } }
.tasks-none { margin: 0; color: var(--text-muted); }
.tasks-none[hidden] { display: none; }
.task-add-row { margin: 0; }
.task-add {
  display: inline-flex; align-items: center; gap: var(--space-2); min-block-size: var(--tap); padding: 0 var(--space-4) 0 var(--space-3);
  border: 2px dashed var(--line-strong); border-radius: var(--radius-md); background: transparent; color: var(--text);
  font: inherit; font-weight: var(--label-weight); cursor: pointer; transition: border-color var(--motion), background-color var(--motion);
}
.task-add:hover { border-color: var(--text); background: var(--surface); }
.task-add[hidden] { display: none; }
.calc-rate { margin: var(--space-2) 0 0; }
.calc-rate .calc-field { flex-wrap: wrap; gap: var(--space-1) var(--space-3); color: var(--text-muted); }
.calc-rate-q { font-weight: var(--label-weight); font-size: var(--text-lg); color: var(--text); }
.calc-rate-in { display: inline-flex; align-items: center; gap: var(--space-1); }
/* The figures: small, in a sentence, and plainly editable. */
.calc-field { display: inline-flex; align-items: center; gap: var(--space-1); min-block-size: var(--tap); cursor: text; }
.calc-field input {
  inline-size: 4.8ch; min-inline-size: 0; block-size: 2.25rem; padding: 0 var(--space-1);
  border: 0; border-block-end: 2px dashed var(--line-strong); border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  background: var(--bg); color: var(--text); font: inherit; font-weight: var(--label-weight);
  font-variant-numeric: tabular-nums; text-align: center;
  -moz-appearance: textfield; appearance: textfield;
}
.calc-field input::-webkit-outer-spin-button, .calc-field input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.calc-field input:hover { border-block-end-color: var(--text); }
.calc-field input:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; border-block-end-style: solid; }
.calc-dot { color: var(--line-strong); }
/* where the two figures no longer share a line, the dot between them goes */
@media (max-width: 359.98px) { .calc-dot { display: none; } }

/* The year, on graphite (he liked how it looks), a bar per task, the email under it. */
.calc-out {
  display: grid; gap: var(--space-3); align-content: start; min-inline-size: 0;
  padding: var(--space-5); border-radius: var(--radius-lg);
  background: var(--text); color: var(--bg);
}
.calc-big { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 var(--space-3); }
.calc-big output {
  font-family: var(--font-display); font-stretch: var(--display-stretch); font-weight: var(--display-weight);
  letter-spacing: var(--display-tracking); font-size: clamp(2.6rem, 13vw, 4.5rem); line-height: 1; font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.calc-big span { font-size: var(--text-lg); }
.calc-human { color: color-mix(in oklch, var(--bg) 82%, var(--text)); font-size: var(--text-lg); text-wrap: pretty; }
.calc-bars { list-style: none; margin: var(--space-2) 0 0; padding: 0; display: grid; gap: var(--space-3); }
.calc-bars:empty { display: none; }
.calc-bar { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--space-1) var(--space-3); align-items: baseline; }
.calc-bar-name { min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: color-mix(in oklch, var(--bg) 82%, var(--text)); }
.calc-bar-hours { font-weight: var(--label-weight); font-variant-numeric: tabular-nums; }
.calc-bar-line { grid-column: 1 / -1; display: block; block-size: 6px; border-radius: var(--radius-pill); background: color-mix(in oklch, var(--bg) 18%, var(--text)); overflow: hidden; }
.calc-bar-line b { display: block; block-size: 100%; inline-size: 0; border-radius: inherit; background: var(--bg); transition: inline-size var(--motion); }
.calc-acts { margin: var(--space-3) 0 0; }
/* on graphite the focus ring is the page's bone, so it shows */
.calc-out .btn:focus-visible { outline: 3px solid var(--bg); outline-offset: 3px; }
.calc-out .btn { white-space: normal; text-wrap: balance; text-align: center; block-size: auto; padding-block: var(--space-2); }
@media (scripting: none) {
  .calc-form, .calc-big, .calc-human[data-out], .calc-bars { display: none; }
}
@media (min-width: 1024px) {
  .calc-body { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); column-gap: var(--space-8); align-items: start; }
  .calc-out { padding: var(--space-6); }
}

/* ---- H4: what we've built. The whole card follows its link. ---- */
.build { display: grid; gap: var(--space-6); }
.cards { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-4); }
.card {
  position: relative; display: grid;
  grid-template-areas: "art" "name" "line" "go"; grid-template-rows: auto auto 1fr auto;
  gap: var(--space-3); padding: 0 var(--space-5) var(--space-4); border-radius: var(--radius-lg); overflow: hidden;
  border: var(--joint) solid var(--line); background: var(--surface); color: var(--text);
  transition: transform var(--motion), box-shadow var(--motion);
}
.card:has(.card-go:hover) { transform: translateY(-2px); box-shadow: var(--shadow); }
.card:has(.card-go:focus-visible) { outline: 3px solid var(--focus); outline-offset: 3px; }
.card-art {
  grid-area: art; position: relative; display: block;
  margin: 0 calc(var(--space-5) * -1) var(--space-2);
  aspect-ratio: 4 / 3; overflow: hidden; background: var(--surface-2); border-block-end: var(--joint) solid var(--line);
}
.card-art > img { position: absolute; inset: 0; display: block; inline-size: 100%; block-size: 100%; object-fit: cover; }
/* The photo tools: the rough job site photo and the finished one, whole, side by side. */
.card-art-pair { display: grid; grid-template-columns: 1fr 1fr; gap: var(--joint); background: var(--line); }
.pair-half { position: relative; display: block; min-inline-size: 0; overflow: hidden; }
.pair-half img { position: absolute; inset: 0; display: block; inline-size: 100%; block-size: 100%; object-fit: cover; object-position: 50% 55%; }
.pair-tag {
  position: absolute; inset-block-start: var(--space-2); inset-inline-start: var(--space-2); padding: 0.125rem var(--space-2);
  border-radius: var(--radius-pill); background: color-mix(in oklch, var(--text) 78%, transparent); color: var(--bg);
  font-size: var(--text-md); font-weight: var(--label-weight); line-height: 1.5;
}
/* The floor plan the documents tool reads, as the portfolio shows it. */
.card-art-plan { background: var(--bg); }
.card-art-plan > img { object-fit: contain; padding: var(--space-3); }
.card-name {
  grid-area: name;
  font-family: var(--font-display); font-stretch: var(--display-stretch); font-weight: var(--display-weight);
  letter-spacing: var(--display-tracking); font-size: var(--text-xl); line-height: var(--leading-tight); text-wrap: balance;
}
.card-line { grid-area: line; color: var(--text-muted); text-wrap: pretty; }
.card-go {
  grid-area: go; justify-self: start; display: inline-flex; align-items: center; min-block-size: var(--tap);
  font-weight: var(--label-weight); text-decoration: underline 2px var(--accent); text-underline-offset: 0.35em;
}
.card-go:focus-visible { outline: none; }
.card-go::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.card-go:hover { text-decoration-thickness: var(--space-1); }
/* The systems' own logos, all in one quiet tone, each in an equal cell. */
.card-art-logos { container-type: inline-size; display: grid; align-content: center; padding: var(--space-4) var(--space-5); background: var(--surface-2); }
/* (WinBooks is Exact WinBooks since Exact took it over; its own site carries Exact's mark, so Exact's logo
   stands for both.) Three to a row, the last row centred. */
.logos { --gap: clamp(1rem, 6cqi, 2.25rem); list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: clamp(0.75rem, 5cqi, 1.5rem) var(--gap); }
/* each logo in an equal cell, drawn whole inside it: a long wordmark by its width, a compact mark by its height */
.logos li { display: block; flex: 0 0 calc((100% - 2 * var(--gap)) / 3); block-size: clamp(1.5rem, 8.5cqi, 2.75rem); min-inline-size: 0; }
.logos img { display: block; inline-size: 100%; block-size: 100%; object-fit: contain; filter: grayscale(1) contrast(1.1); opacity: 0.85; mix-blend-mode: multiply; }
@media (min-width: 640px) and (max-width: 1023.98px) {
  /* a tall photo pair beside the two other cards */
  .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-5); }
  .card-photos { grid-row: span 2; grid-template-rows: 1fr auto auto auto; }
  .card-photos .card-art { aspect-ratio: auto; min-block-size: 18rem; }
}
@media (min-width: 1024px) { .cards { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-5); } }

/* ---- The before-and-after (compare.js; the homepage and the portfolio): the finished photo below,
   the job site photo above it, cut at the line. The line follows the scroll until the visitor's
   first touch (motion.js), then it is theirs: dragged, tapped or moved with the keys. ---- */
.compare {
  position: relative; overflow: hidden; isolation: isolate; container-type: inline-size;
  border-radius: var(--radius-lg); background: var(--surface-2);
  touch-action: pan-y; cursor: ew-resize; user-select: none; -webkit-user-select: none;
  --pos: 50%;
}
.compare img { display: block; inline-size: 100%; block-size: auto; pointer-events: none; }
.compare-before { position: absolute; inset: 0; clip-path: inset(0 calc(100% - var(--pos)) 0 0); }
.compare-before img { block-size: 100%; object-fit: cover; }
.compare-line {
  position: absolute; inset-block: 0; inset-inline-start: var(--pos); inline-size: 3px; translate: -50% 0;
  background: var(--bg); box-shadow: 0 0 0 1px color-mix(in oklch, var(--text) 30%, transparent);
  pointer-events: none;
}
.compare-line::after {
  content: ""; position: absolute; inset-block-start: 50%; inset-inline-start: 50%; translate: calc(-50% + clamp(calc(25px - var(--p, 50) * 1cqw), 0px, calc(100cqw - 25px - var(--p, 50) * 1cqw))) -50%;
  inline-size: var(--tap); block-size: var(--tap); border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 3px var(--bg), var(--shadow);
  /* two small arrows: the line moves both ways */
  background-image: linear-gradient(135deg, transparent 46%, var(--on-accent) 46% 54%, transparent 54%), linear-gradient(45deg, transparent 46%, var(--on-accent) 46% 54%, transparent 54%), linear-gradient(45deg, transparent 46%, var(--on-accent) 46% 54%, transparent 54%), linear-gradient(135deg, transparent 46%, var(--on-accent) 46% 54%, transparent 54%);
  background-size: 9px 9px; background-repeat: no-repeat;
  background-position: 9px 13px, 9px 22px, 26px 13px, 26px 22px;
}
.compare-tag {
  position: absolute; inset-block-start: var(--space-3); padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill); background: color-mix(in oklch, var(--text) 78%, transparent); color: var(--bg);
  font-weight: var(--label-weight); pointer-events: none;
}
.compare-tag-before { inset-inline-start: var(--space-3); }
.compare-tag-after { inset-inline-end: var(--space-3); }
.compare-range {
  position: absolute; inset: 0; inline-size: 100%; block-size: 100%; margin: 0;
  opacity: 0; pointer-events: none; appearance: none; -webkit-appearance: none; background: transparent;
}
.compare:has(.compare-range:focus-visible) { outline: 3px solid var(--focus); outline-offset: 3px; }

/* ---- H5: a job site photo in, a finished photo out: the slider, larger, alone ---- */
.proof { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-6); }
.proof-say { display: grid; gap: var(--space-4); align-content: start; justify-items: start; }
.proof-say .display { max-inline-size: 16ch; }
.proof-say p { max-inline-size: 38ch; color: var(--text-muted); text-wrap: pretty; }
.proof-main {
  display: grid; gap: var(--space-3); margin: 0;
  inline-size: min(100%, 46rem, calc((100svh - var(--bar) - var(--space-7)) * 0.75));
}
.proof-main figcaption, .pair-compare figcaption { color: var(--text-muted); max-inline-size: 52ch; text-wrap: pretty; }
.proof-all { margin-block-start: var(--space-2); }
@media (min-width: 768px) { .proof-say p { font-size: var(--text-lg); } }
@media (min-width: 1024px) {
  .proof { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); column-gap: var(--space-8); align-items: start; }
  .proof-say { position: sticky; inset-block-start: calc(var(--bar) + var(--space-7)); }
  .proof-main { justify-self: center; }
}

/* ---- H6: how we work: four promises on a line that draws itself down ---- */
.how { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-6); }
.steps { list-style: none; margin: 0; padding: 0 0 0 var(--space-6); display: grid; gap: var(--space-6); position: relative; counter-reset: step; }
.steps::before {
  content: ""; position: absolute; inset-block: var(--space-2); inset-inline-start: var(--space-2); inline-size: 3px;
  border-radius: var(--radius-pill); background: var(--accent); transform-origin: top;
}
.step { display: grid; gap: var(--space-2); counter-increment: step; position: relative; }
.step::before {
  content: counter(step); position: absolute; inset-inline-start: calc(var(--space-6) * -1 - 0.28rem); inset-block-start: 0;
  display: grid; place-items: center; inline-size: 1.75rem; block-size: 1.75rem; border-radius: 50%;
  background: var(--bg); border: 3px solid var(--accent); font-size: 1rem; font-weight: var(--label-weight); line-height: 1;
}
.step-name {
  font-family: var(--font-display); font-stretch: var(--display-stretch); font-weight: var(--display-weight);
  letter-spacing: var(--display-tracking); font-size: var(--text-xl); line-height: var(--leading-tight);
}
.step p { max-inline-size: 48ch; color: var(--text-muted); text-wrap: pretty; }
.how-steps { display: grid; gap: var(--space-6); justify-items: start; min-inline-size: 0; }
.how-act { margin: 0; padding-inline-start: var(--space-6); }
@media (min-width: 768px) { .step p { font-size: var(--text-lg); } .step-name { font-size: var(--text-2xl); } }
@media (min-width: 1024px) {
  .how { grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); column-gap: var(--space-8); align-items: start; }
}

/* ---- H7: the close: two doors of equal weight, each one line and one email ---- */
.doors-band { display: grid; gap: var(--space-7); }
.doors { display: grid; gap: var(--space-7); }
.door { display: grid; gap: var(--space-4); align-content: start; justify-items: start; }
.door + .door { padding-block-start: var(--space-7); border-block-start: var(--joint) solid var(--line-strong); }
.door-title { max-inline-size: 20ch; line-height: 1.05; }
.door p { max-inline-size: 40ch; color: var(--text-muted); text-wrap: pretty; }
.door .door-act { margin-block-start: var(--space-2); }
.doors-mail { margin: 0; }
@media (min-width: 768px) {
  .doors { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: auto auto auto; gap: 0; }
  .door { grid-row: span 3; grid-template-rows: subgrid; row-gap: var(--space-4); padding-inline-end: var(--space-7); }
  .door + .door { padding-block-start: 0; border-block-start: 0; padding-inline: var(--space-7) 0; border-inline-start: var(--joint) solid var(--line-strong); }
  .door p { font-size: var(--text-lg); }
  .door .door-act { align-self: end; }
}

/* ---- The motion switch in the footer ---- */
.motion-switch {
  display: inline-flex; align-items: center; gap: var(--space-2); min-block-size: var(--tap);
  padding: 0 var(--space-3); border: var(--joint) solid var(--line-strong); border-radius: var(--radius-pill);
  background: transparent; color: var(--text); font: inherit; cursor: pointer;
}
.motion-switch::before {
  content: ""; inline-size: 1.9rem; block-size: 1.1rem; border-radius: var(--radius-pill); flex: none;
  background: radial-gradient(circle at 0.55rem 50%, var(--bg) 0.4rem, transparent 0.42rem) var(--line-strong);
}
.motion-switch[aria-pressed="true"]::before {
  background: radial-gradient(circle at 1.35rem 50%, var(--bg) 0.4rem, transparent 0.42rem) var(--text);
}
.motion-switch:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
/* hidden until motion.js shows it: without scripts the switch could do nothing */
.motion-switch[hidden] { display: none; }

/* ---- The motif's pause button (motif.js adds it while the motif moves; WCAG 2.2.2) ---- */
.motif-pause {
  position: absolute; inset-block-end: var(--space-3); inset-inline-end: var(--space-3); z-index: 2;
  display: inline-flex; align-items: center; gap: var(--space-2); min-block-size: var(--tap); padding: 0 var(--space-4);
  border: 0; border-radius: var(--radius-pill); background: color-mix(in oklch, var(--text) 82%, transparent); color: var(--bg);
  font: inherit; font-weight: var(--label-weight); cursor: pointer;
}
.motif-pause:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

/* ---- Small arrivals, only where the browser ties animation to the scroll and nobody asked for
   less motion. Without that support every part simply stands. ---- */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    :root:not([data-motion="reduce"]) .card,
    :root:not([data-motion="reduce"]) .step {
      animation: rise linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 100%;
    }
    :root:not([data-motion="reduce"]) .steps::before {
      animation: draw-down linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 60%;
    }
    :root:not([data-motion="reduce"]) .top {
      animation: edge linear both;
      animation-timeline: scroll(root);
      animation-range: 0 8rem;
    }
  }
}
/* (no fading: text keeps its full contrast at every moment of an arrival) */
@keyframes rise { from { translate: 0 1.5rem; } }
@keyframes draw-down { from { transform: scaleY(0); } }
@keyframes edge { to { box-shadow: 0 1px 0 var(--line); } }

@media (prefers-reduced-motion: reduce) {
  .card { transition: none; }
}
:root[data-motion="reduce"] .card { transition: none; }
:root[data-motion="reduce"] .mark::before { animation: none; }

/* ---- The email panel (mail.js): every email button opens the visitor's mail app and shows this beside
   it, the address and a copy button that is an icon (a check once copied). ---- */
.mail-pop {
  position: absolute; z-index: 30; display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-1) var(--space-1) var(--space-1) var(--space-4); border: var(--joint) solid var(--line);
  border-radius: var(--radius-lg); background: var(--bg); color: var(--text); box-shadow: var(--shadow);
  font-weight: var(--label-weight); white-space: nowrap;
}
.mail-pop[hidden] { display: none; }
.mail-pop-addr { user-select: all; -webkit-user-select: all; }
.mail-pop-copy {
  display: grid; place-items: center; inline-size: var(--tap); block-size: var(--tap); flex: none;
  border: 0; border-radius: var(--radius-md); background: var(--surface-2); color: var(--text); cursor: pointer;
  transition: background-color var(--motion);
}
.mail-pop-copy:hover { background: var(--surface-3); }
.mail-pop-copy:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.mail-pop-copy svg { grid-area: 1 / 1; inline-size: 1.25rem; block-size: 1.25rem; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.mail-pop-copy .mail-pop-done { opacity: 0; }
.mail-pop.is-copied .mail-pop-copy svg:not(.mail-pop-done) { opacity: 0; }
.mail-pop.is-copied .mail-pop-done { opacity: 1; }

/* portfolio/portfolio.css */
/* The portfolio (website round 2, 19 and 20 October 2026; PLAN.md section 4, P0 to P7).
   Loaded after site.css, which gives the header, the buttons, .mark, .display, the slider
   (.compare), the close and the footer. Values from the tokens; orange is a fill, a line or a
   mark and the one colour of what can be pressed. The one colour of its own is the ballpoint
   blue of the floor plan's marks (--ink, defined once in site.css), an illustration ink and never a control. Every part
   stands complete and still without script or motion; the motion only plays it. */

:root {
  /* the header's height on a wide screen, where it stays at the top: its 44 px row and its padding */
  --head: calc(var(--tap) + var(--space-2) * 2);
  --stepbar: 3.25rem;
}
figure { margin: 0; }
/* The header on a narrow screen: the link to the page the visitor is on gives way (the wordmark leads
   home), so the header keeps one line with its button: below 520 px in English, below 535 px in Dutch,
   whose words are longer. */
@media (max-width: 519.98px) { .top-nav a[aria-current="page"] { display: none; } }
@media (max-width: 534.98px) { :root[lang="nl"] .top-nav a[aria-current="page"] { display: none; } }
figcaption { max-inline-size: 60ch; color: var(--text-muted); text-wrap: pretty; }

/* Jumps within the page glide, unless the visitor asked for less motion (the device, or the
   page's own switch, which sets data-motion on the html element). */
@media (prefers-reduced-motion: no-preference) {
  :root:not([data-motion="reduce"]) { scroll-behavior: smooth; }
}
/* What the keyboard reaches never slides under the step bar (and, from 768 px, the header that stays
   at the top): the page keeps that much room at its top. An entry or a chapter reached by a link still
   lands with its title just under the header (its margin gives the step bar's room back); a tool lands
   under the step bar. */
:root { scroll-padding-block-start: calc(var(--stepbar) + 1rem); }
.entry, .pf-reader, .pf-gal, .pf-suite { scroll-margin-block-start: calc((var(--stepbar) + 1rem) * -1); }
.pf-step { scroll-margin-block-start: 0; }
@media (min-width: 768px) {
  :root { scroll-padding-block-start: calc(var(--head) + var(--stepbar) + 1rem); }
}

/* ---------- P0: "Portfolio" at the homepage wordmark's scale, and two pieces of work ---------- */
.pf-head {
  container-type: inline-size;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-6);
  padding: var(--space-4) var(--gutter) var(--space-8);
}
/* .mark (site.css) gives the wordmark's voice: Archivo condensed bold in caps, trimmed to its caps,
   the orange rule drawn in under it. Nine letters measure 3.86 em, so this size spans the column
   edge to edge as WANOVIX does on the homepage (measured at 390 and 1440); on a short screen it keeps to about two fifths. */
.pf-head .pf-title { margin: 0; font-size: min(25.1cqi, 40dvh); }

.pf-tiles { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-7); }
.pf-tile {
  display: grid;
  gap: var(--space-2);
  color: var(--text);
  text-decoration: none;
  border-radius: var(--radius-lg);
}
.pf-tile-art {
  display: block;
  overflow: hidden;
  aspect-ratio: 4 / 3;
  margin-block-end: var(--space-3);
  border-radius: var(--radius-lg);
  background: var(--surface-2);
}
.pf-tile .card-img { display: block; inline-size: 100%; block-size: 100%; object-fit: cover; object-position: 50% 66%; }
/* Hestia's whole chain from one photo (round 3, his words: "a card split into 4, with 4 images. the
   same image but from scratch"): the job site photo, cleaned up, furnished, the light oak door. */
.pf-chain { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; gap: 2px; background: var(--bg); }
.pf-chain .card-img { min-block-size: 0; object-position: 50% 62%; }
/* Groomania: the hall with the Event Timer's own screens in their running state, on the hall's own dark. */
.pf-tile-hall { display: grid; align-content: center; background: #1d1b19; }
.pf-tile-hall .hall-art { border-radius: 0; }
.pf-tile-hall .hall-room { display: block; inline-size: 100%; block-size: auto; }
.pf-tile-name {
  justify-self: start;
  font-family: var(--font-display);
  font-stretch: var(--display-stretch);
  font-weight: var(--display-weight);
  letter-spacing: var(--display-tracking);
  font-size: var(--text-2xl);
  line-height: var(--leading-tight);
  text-decoration: underline 2px transparent;
  text-underline-offset: 0.2em;
  transition: text-decoration-color var(--motion);
}
.pf-tile-line { max-inline-size: 44ch; color: var(--text-muted); text-wrap: pretty; }
.pf-tile:hover .pf-tile-name { text-decoration-color: var(--accent); }
.pf-tile:focus-visible { outline: 3px solid var(--focus); outline-offset: var(--space-2); }

@media (min-width: 768px) {
  .pf-head { padding-block: var(--space-5) var(--space-8); gap: var(--space-7); }
  .pf-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-6); }
  .pf-tile-line { font-size: var(--text-lg); }
}
@media (prefers-reduced-motion: no-preference) {
  :root:not([data-motion="reduce"]) .pf-chain .card-img, :root:not([data-motion="reduce"]) .pf-tile-hall .hall-art { transition: scale 700ms var(--ease-out); }
  :root:not([data-motion="reduce"]) .pf-tile:hover .pf-chain .card-img, :root:not([data-motion="reduce"]) .pf-tile:hover .pf-tile-hall .hall-art { scale: 1.03; }
}

/* ---------- The entries ---------- */
.entry + .entry { border-block-start: var(--joint) solid var(--line); }

/* P1: Hestia's head: who, the outcome, one line. Then straight into the photos. */
.hestia-head {
  display: grid;
  gap: var(--space-4);
  padding: var(--space-8) var(--gutter) var(--space-7);
  border-block-start: var(--joint) solid var(--line);
}
.hestia-who { color: var(--text-muted); font-weight: var(--label-weight); }
.hestia-head .case-title { max-inline-size: none; }
/* two statements: the second, underlined, starts its own line */
.hestia-head .case-title em::before { content: "\A"; white-space: pre; }
.hestia-head .case-lede { font-size: var(--text-lg); }
@media (min-width: 768px) {
  .hestia-head { padding-block: var(--space-8); }
  .hestia-head .case-lede { font-size: var(--text-xl); }
}

/* ---------- P2: one job site photo through the three tools ---------- */
.pf-suite { padding: var(--space-7) var(--gutter) var(--space-6); background: var(--surface); }
.pf-suite-say { display: grid; gap: var(--space-3); }
.pf-suite-say .display { max-inline-size: 16ch; }
.pf-suite-say p { max-inline-size: 46ch; color: var(--text-muted); font-size: var(--text-lg); text-wrap: pretty; }

/* The step bar: the three tools in their order; it stays in view through the three steps, marks the
   one on screen (demos.js) and takes the visitor to a step when pressed (a link to it). */
.pf-stepbar {
  position: sticky;
  inset-block-start: 0;
  z-index: 3;
  margin: var(--space-6) calc(var(--gutter) * -1) 0;
  padding: 0 var(--gutter);
  background: var(--surface);
  border-block-end: var(--joint) solid var(--line);
}
/* on a phone each name takes its own width (a long Dutch word never breaks out of the bar), from 768 px three equal parts */
.pf-stepbar ol { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, auto); justify-content: space-between; column-gap: var(--space-3); }
.pf-stepbar a {
  position: relative;
  display: flex;
  align-items: center;
  min-block-size: var(--stepbar);
  line-height: 1.2;
  color: var(--text-muted);
  font-weight: var(--label-weight);
  text-decoration: none;
  transition: color var(--motion);
}
.pf-stepbar a::after {
  content: "";
  position: absolute;
  inset: auto 0 calc(var(--joint) * -1) 0;
  block-size: 3px;
  background: transparent;
  transition: background-color var(--motion);
}
.pf-stepbar a:hover { color: var(--text); }
.pf-stepbar a:hover::after { background: var(--line-strong); }
.pf-stepbar a[aria-current="step"] { color: var(--text); }
.pf-stepbar a[aria-current="step"]::after { background: var(--text); }
.pf-stepbar a:focus-visible { outline: 3px solid var(--focus); outline-offset: -3px; border-radius: var(--radius-sm); }
@media (min-width: 768px) {
  .pf-stepbar { inset-block-start: var(--head); }
  .pf-stepbar ol { grid-template-columns: repeat(3, minmax(0, 1fr)); justify-content: stretch; column-gap: var(--space-6); }
  .pf-stepbar a { font-size: var(--text-lg); }
}

.pf-step {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-5);
  padding-block: var(--space-7);
}
.pf-step + .pf-step { border-block-start: var(--joint) solid var(--line); }
.pf-step-say { display: grid; gap: var(--space-1); align-content: start; }
.pf-step-name {
  font-family: var(--font-display);
  font-stretch: var(--display-stretch);
  font-weight: var(--display-weight);
  letter-spacing: var(--display-tracking);
  font-size: var(--text-2xl);
  line-height: var(--leading-tight);
}
.pf-step-say p { max-inline-size: 40ch; color: var(--text-muted); font-size: var(--text-lg); text-wrap: pretty; }

/* The photo: as large as the screen allows, never taller than it. */
.pf-step-photo { display: grid; gap: var(--space-3); inline-size: min(100%, 34rem); }
.pf-step-photo figcaption { margin-block-start: var(--space-2); }

/* "One click": a real button in the site's square shape, standing on the photo's lower edge. It
   plays the slider from the job site photo to the finished one, and back when pressed again. */
.pf-click-row { position: relative; z-index: 1; display: flex; justify-content: center; margin-block-start: calc(var(--space-7) * -0.5 - var(--space-3)); }
.pf-click {
  min-block-size: 3.5rem;
  padding-inline: var(--space-6);
  border: 0;
  cursor: pointer;
  font-family: var(--font-display);
  font-stretch: var(--display-stretch);
  font-weight: var(--display-weight);
  font-size: var(--text-xl);
  box-shadow: 0 0 0 var(--space-1) var(--surface), var(--shadow);
}
.pf-click[hidden] { display: none; }
.pf-click:focus-visible { outline: 3px solid var(--focus); outline-offset: var(--space-2); }

/* The chat that never ends: the prompts everyone has typed, in a chat app's grey, drifting up
   behind a fade; under them the verdict. On a phone the last three bubbles. */
.pf-chat { display: grid; gap: var(--space-5); align-content: end; }
.pf-chat-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: flex-end;
  gap: var(--space-3);
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 55%);
  mask-image: linear-gradient(to bottom, transparent, #000 55%);
}
.pf-chat-list li {
  flex: none;
  max-inline-size: 88%;
  padding: var(--space-2) var(--space-4);
  border-radius: 1.15rem 1.15rem var(--radius-sm) 1.15rem;
  background: var(--surface-2);
  color: var(--text);
  line-height: 1.4;
}
.pf-chat-list li:nth-child(-n + 7) { display: none; }
.pf-chat-verdict {
  font-family: var(--font-display);
  font-stretch: var(--display-stretch);
  font-weight: var(--display-weight);
  letter-spacing: var(--display-tracking);
  font-size: var(--text-2xl);
  line-height: var(--leading-tight);
  text-wrap: balance;
}
.pf-chat-verdict em { white-space: nowrap; }
.pf-chat-verdict em, .hestia-head .case-title em {
  font-style: normal;
  background: linear-gradient(var(--accent), var(--accent)) 0 94% / 100% 0.1em no-repeat;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

/* Step 3: the same wall, three other doors, a catalogue's row. */
.pf-doors { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-3); }
.pf-door { display: grid; gap: var(--space-2); }
.pf-door img { display: block; inline-size: 100%; block-size: auto; border-radius: var(--radius-md); }
.pf-door figcaption { color: var(--text); font-weight: var(--label-weight); }

@media (min-width: 768px) {
  .pf-suite { padding-block: var(--space-8) var(--space-7); }
  .pf-suite-say p { font-size: var(--text-xl); }
  .pf-step { padding-block: var(--space-8); column-gap: var(--space-7); }
  .pf-step-name { font-size: var(--text-3xl); }
  .pf-step-site {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
    grid-template-areas: "say say" "chat photo";
    align-items: end;
  }
  .pf-step-site .pf-step-say { grid-area: say; }
  .pf-step-site .pf-chat { grid-area: chat; padding-block-end: var(--space-8); }
  .pf-step-site .pf-step-photo { grid-area: photo; justify-self: end; }
  .pf-step-show .pf-step-photo { justify-self: end; }
  /* never taller than the screen under the header and the step bar, also on a short landscape screen */
  .pf-step-photo { inline-size: min(100%, 34rem, max(12rem, calc((100svh - var(--head) - var(--stepbar) - 9rem) * 0.75))); }
  .pf-chat-list li:nth-child(-n + 7) { display: block; }
  .pf-chat-list { max-block-size: 22rem; }
  .pf-doors { gap: var(--space-5); }
  .pf-door figcaption { font-size: var(--text-lg); }
}
@media (min-width: 1024px) {
  /* the steps' words on the left, the photo on the right at the same place from step to step */
  /* (the photo's column takes the photo's own width here) */
  .pf-step-photo { inline-size: min(34rem, max(12rem, calc((100svh - var(--head) - var(--stepbar) - 9rem) * 0.75))); }
  .pf-step-site, .pf-step-show {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "say photo" "chat photo";
    grid-template-rows: auto 1fr;
    column-gap: var(--space-8);
    align-items: start;
  }
  /* step 2 has no chat: its words stand level with the middle of its photo, in view with it */
  .pf-step-show .pf-step-say { grid-area: say; grid-row: 1 / -1; align-self: center; }
  .pf-step-show .pf-step-photo { grid-area: photo; }
  /* the chat takes the height the photo gives it (size containment: its bubbles never make the step taller) */
  .pf-step-site .pf-chat { align-self: stretch; contain: size; grid-template-rows: minmax(0, 1fr) auto; padding-block: var(--space-5) var(--space-8); }
  .pf-step-site .pf-chat-list { max-block-size: none; min-block-size: 0; }
  .pf-step-site .pf-step-photo, .pf-step-show .pf-step-photo { justify-self: end; }
  .pf-chat-verdict { font-size: var(--text-3xl); max-inline-size: 14ch; }
  .pf-step-door { grid-template-columns: minmax(0, 1fr); }
}
@media (min-width: 1280px) {
  .pf-chat { padding-inline-end: var(--space-8); }
}

/* The chat drifts up as the step passes, where the browser ties animation to the scroll and nobody
   asked for less motion; anywhere else it simply stands. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) and (min-width: 768px) {
    :root:not([data-motion="reduce"]) .pf-step-site { view-timeline: --chat block; }
    :root:not([data-motion="reduce"]) .pf-chat-list li {
      animation: drift linear both;
      animation-timeline: --chat;
      animation-range: entry 10% contain 55%;
    }
  }
}
@keyframes drift { from { translate: 0 10rem; } }

/* ---------- P3: the floor plan, its ballpoint marks, and the question on the mark ---------- */
.pf-reader { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-6); padding: var(--space-8) var(--gutter); }
.pf-reader .display { max-inline-size: 18ch; }
.pf-plan { display: grid; gap: var(--space-5); justify-items: start; }
/* the drawing as large as the screen allows, the question and the CNC row in view with it */
.pf-plan-sheet { position: relative; container-type: inline-size; inline-size: min(100%, 64rem, calc((100svh - 12rem) * 1.209)); }
.pf-plan-art { position: relative; }
.pf-plan-art picture, .shot picture { display: block; }
.pf-plan-art img { display: block; inline-size: 100%; block-size: auto; border-radius: var(--radius-md); box-shadow: var(--shadow); }

/* The circled mark, in the drawing's own coordinates (plan.py prints them): it pulses once when it
   first comes into view, and on a narrow drawing carries the number its message refers to. */
.pf-plan-mark {
  position: absolute;
  inset-inline-start: 73.27%;
  inset-block-start: 38.72%;
  inline-size: 15.4%;
  aspect-ratio: 2.6;
  translate: -50% -50%;
  border-radius: 50%;
  pointer-events: none;
}
.pf-plan-num {
  display: inline-grid;
  place-items: center;
  inline-size: 1.6rem;
  block-size: 1.6rem;
  border-radius: 50%;
  background: var(--text);
  color: var(--bg);
  font-size: var(--text-md);
  font-weight: var(--label-weight);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.pf-plan-mark .pf-plan-num { position: absolute; inset-block-start: -1.2rem; inset-inline-end: -1.2rem; box-shadow: 0 0 0 2px var(--surface); }
.ask-q .pf-plan-num { margin-inline-end: var(--space-2); vertical-align: 0.1em; }

/* Below 700 px the drawing is the phone's view of the sheet (plan-*-phone.svg: the hall, D2 to D4 and the
   circled mark), so the mark and the message's pointer take that view's coordinates. */
@media (max-width: 699.98px) {
  .pf-plan-mark { inset-inline-start: 60.24%; inset-block-start: 22.74%; inline-size: 31.5%; }
  .ask::before { inset-inline-start: calc(60.24% - var(--space-2)); }
}

/* The question as a message: on a narrow drawing under it, its pointer up to the numbered mark. */
.ask {
  position: relative;
  display: grid;
  gap: var(--space-4);
  margin-block-start: var(--space-5);
  padding: var(--space-4) var(--space-5) var(--space-5);
  border: var(--joint) solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--bg);
  box-shadow: var(--shadow);
}
.ask::before {
  content: "";
  position: absolute;
  inset-block-start: calc(var(--space-2) * -1 - 1px);
  inset-inline-start: calc(73.27% - var(--space-2));
  inline-size: var(--space-4);
  block-size: var(--space-4);
  border-inline-start: var(--joint) solid var(--line);
  border-block-start: var(--joint) solid var(--line);
  background: var(--bg);
  rotate: 45deg;
}
.ask-q { font-weight: var(--label-weight); line-height: 1.4; text-wrap: pretty; }
.ask-a { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-3); }
.ask-yes, .ask-no {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-block-size: var(--tap);
  padding-inline: var(--space-4);
  border-radius: var(--radius-md);
  font: inherit;
  font-weight: var(--label-weight);
  cursor: pointer;
  transition: background-color var(--motion);
}
.ask-yes { border: var(--joint) solid var(--accent); background: var(--accent); color: var(--on-accent); }
.ask-yes:hover { background: var(--accent-strong); }
.ask-no { border: var(--joint) solid var(--line-strong); background: var(--bg); color: var(--text); }
.ask-no:hover { background: var(--surface-2); }
.ask-yes[aria-pressed="true"], .ask-no[aria-pressed="true"] { border-color: var(--text); background: var(--text); color: var(--bg); }
.ask-yes[aria-pressed="true"]::before, .ask-no[aria-pressed="true"]::before {
  content: "";
  flex: none;
  inline-size: 0.8rem;
  block-size: 0.42rem;
  margin-inline-end: var(--space-2);
  border-inline-start: 2px solid currentColor;
  border-block-end: 2px solid currentColor;
  transform: translateY(-20%) rotate(-45deg);
}
.ask-yes:focus-visible, .ask-no:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; }
.ask-said { color: var(--text-muted); text-wrap: pretty; }
.ask-said:empty { position: absolute; inline-size: 1px; block-size: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* A drawing wide enough: the message stands on the plan above the mark, its tail on it. */
@container (min-width: 700px) {
  .pf-plan-mark .pf-plan-num, .ask-q .pf-plan-num { display: none; }
  .ask {
    position: absolute;
    inset-inline-start: 73.27%;
    inset-block-end: calc(100% - 38.72% + 3.49% + var(--space-4));
    inline-size: min(25rem, 46cqi);
    margin: 0;
    translate: -72% 0;
    padding: var(--space-4) var(--space-4) var(--space-4) var(--space-5);
    border-radius: var(--radius-lg) var(--radius-lg) var(--radius-lg) var(--radius-lg);
  }
  .ask::before {
    inset-block-start: auto;
    inset-block-end: calc(var(--space-2) * -1 - 1px);
    inset-inline-start: calc(72% - var(--space-2));
    border: 0;
    border-inline-end: var(--joint) solid var(--line);
    border-block-end: var(--joint) solid var(--line);
  }
}
@container (min-width: 820px) {
  .ask { inline-size: min(27rem, 44cqi); }
  .ask-q { font-size: var(--text-lg); line-height: 1.35; }
}

/* The CNC row: its swing cell waits with "?", and turns L or R with a short highlight. */
.cnc-row, .sheet-table { border-collapse: collapse; font-variant-numeric: tabular-nums; }
.cnc-row { min-inline-size: min(100%, 20rem); background: var(--bg); }
.cnc-row caption, .sheet-table caption { caption-side: top; text-align: start; padding-block-end: var(--space-2); color: var(--text-muted); font-weight: var(--label-weight); }
.cnc-row th, .cnc-row td, .sheet-table th, .sheet-table td { padding: var(--space-2) var(--space-4); border: var(--joint) solid var(--line); text-align: start; white-space: nowrap; }
.cnc-row th, .sheet-table th { background: var(--surface-2); font-weight: var(--label-weight); }
.cnc-swing { min-inline-size: 4.5rem; font-weight: var(--label-weight); background: var(--accent-soft); }
.cnc-swing.is-set { background: var(--bg); box-shadow: inset 0 0 0 2px var(--accent); }

@media (prefers-reduced-motion: no-preference) {
  :root:not([data-motion="reduce"]) .pf-plan-mark.is-pulse::after {
    content: "";
    position: absolute;
    inset: -8%;
    border-radius: 50%;
    border: 2px solid var(--ink);
    opacity: 0;
    animation: ring 1.5s var(--ease-out) 1;
  }
  :root:not([data-motion="reduce"]) .cnc-swing.is-new { animation: flash 1.1s var(--ease-out) 1; }
}
@keyframes ring { 0% { opacity: 0.55; scale: 0.9; } 100% { opacity: 0; scale: 1.7; } }
@keyframes flash { 0% { background: var(--accent); } 100% { background: var(--bg); } }

@media (min-width: 1280px) {
  .pf-reader { grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); column-gap: var(--space-7); align-items: start; }
  .pf-reader .display { position: sticky; inset-block-start: calc(var(--head) + var(--space-6)); max-inline-size: 15ch; font-size: calc(var(--text-4xl) * 1.15); }
  .pf-plan-sheet { inline-size: min(100%, calc((100svh - 12rem) * 1.209)); }
}

/* ---------- P4: the configurator, its name and its two screens ---------- */
.shot {
  overflow: hidden;
  border: var(--joint) solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--bg);
  box-shadow: var(--shadow);
}
.shot img { display: block; inline-size: 100%; block-size: auto; }
.shot-phone {
  inline-size: 40%;
  max-inline-size: 13.5rem;
  border: var(--space-2) solid var(--text);
  border-radius: calc(var(--radius-lg) * 2.5);
}
.pf-config { display: grid; gap: var(--space-6); padding: var(--space-8) var(--gutter) var(--space-8); background: linear-gradient(transparent 0 42%, var(--surface) 42%); }
.pf-config-stage { display: grid; grid-template-columns: minmax(0, 1fr); }
.pf-config-stage .shot-phone { justify-self: end; margin: calc(var(--space-6) * -1) var(--space-3) 0 0; }
@media (min-width: 768px) {
  .shot-phone { inline-size: 28%; }
}
@media (min-width: 1024px) {
  .pf-config-stage { grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--space-5); }
  .pf-config-stage .shot-wide { grid-column: 1 / 12; grid-row: 1; }
  .pf-config-stage .shot-phone {
    grid-column: 10 / 13;
    grid-row: 1;
    align-self: end;
    justify-self: end;
    inline-size: 100%;
    max-inline-size: 16.5rem;
    margin: 0 0 calc(var(--space-8) * -1);
  }
}

/* ---------- P5: the chapters, a short title over a large screen each ---------- */
.pf-gallery { display: grid; gap: var(--space-8); padding: var(--space-8) var(--gutter); }
.pf-gal { display: grid; gap: var(--space-5); }
.pf-gal-title {
  max-inline-size: 22ch;
  font-family: var(--font-display);
  font-stretch: var(--display-stretch);
  font-weight: var(--display-weight);
  letter-spacing: var(--display-tracking);
  font-size: var(--text-2xl);
  line-height: var(--leading-tight);
  text-wrap: balance;
}
.pf-gal-pair { display: grid; gap: var(--space-4); }
@media (min-width: 768px) {
  .pf-gallery { gap: calc(var(--space-8) * 1.5); }
  .pf-gal-title { font-size: var(--text-3xl); }
}
@media (min-width: 1024px) {
  /* the option list behind, one option opened in front of it */
  .pf-gal-pair { grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--space-5); align-items: start; }
  .pf-gal-pair .shot-list { grid-column: 1 / 9; grid-row: 1; }
  /* the option opened in full: its choices, its output and the matrix of what may go with what */
  .pf-gal-pair .shot-detail {
    grid-column: 7 / 13;
    grid-row: 1;
    justify-self: end;
    inline-size: min(100%, 38rem);
    margin-block-start: 12%;
    box-shadow: 0 2px 4px color-mix(in oklch, var(--text) 10%, transparent), 0 24px 56px color-mix(in oklch, var(--text) 22%, transparent);
  }
}

/* The CNC sheet, drawn: on a phone it scrolls sideways inside its own frame. */
.sheet { display: grid; gap: var(--space-3); }
.sheet-scroll {
  overflow-x: auto;
  border-radius: var(--radius-md);
  border: var(--joint) solid var(--line);
  /* scroll shadows: a cover that moves with the table hides each shadow once that edge is reached */
  background:
    linear-gradient(to right, var(--bg) 40%, transparent) left center / 2.5rem 100% no-repeat local,
    linear-gradient(to left, var(--bg) 40%, transparent) right center / 2.5rem 100% no-repeat local,
    linear-gradient(to right, color-mix(in oklch, var(--text) 22%, transparent), transparent) left center / 1rem 100% no-repeat scroll,
    linear-gradient(to left, color-mix(in oklch, var(--text) 22%, transparent), transparent) right center / 1rem 100% no-repeat scroll,
    var(--bg);
}
.sheet-scroll:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.sheet-table { inline-size: 100%; }
.sheet-table caption { padding: var(--space-3) var(--space-4) var(--space-2); }
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    :root:not([data-motion="reduce"]) .sheet-table tbody tr {
      animation: fill-in linear both;
      animation-timeline: view();
      animation-range: entry 30% cover 45%;
    }
  }
}
@keyframes fill-in { from { background: var(--accent-soft); translate: -0.5rem 0; } }

/* P6: one footnote, the end of the Hestia entry: one small, quiet line in the entry's column (round 3: it
   stood one word to a line on a wide screen, where its old width rule left it no room). */
.pf-footnote { margin: 0; padding: 0 max(var(--gutter), calc((100% - 90rem) / 2)) var(--space-7); font-size: var(--text-sm); color: var(--text-muted); text-wrap: pretty; }

/* ======================================================================
   P7: the Event Timer's entry (Groomania), unchanged until William's own
   review (note 37): its head, its hall with the board's own screens, and
   its two chapters, on graphite.
   ====================================================================== */
.case-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-5);
  padding: var(--space-6) var(--gutter) var(--space-7);
}
.case-title {
  max-inline-size: 16ch;
  font-family: var(--font-display);
  font-stretch: var(--display-stretch);
  font-weight: var(--display-weight);
  letter-spacing: var(--display-tracking);
  font-size: var(--text-3xl);
  line-height: 1.02;
  text-wrap: balance;
}
.case-title em {
  font-style: normal;
  background: linear-gradient(var(--accent), var(--accent)) 0 94% / 100% 0.1em no-repeat;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}
.case-lede { max-inline-size: 52ch; color: var(--text-muted); text-wrap: pretty; }
.case-facts { display: grid; margin: var(--space-2) 0 0; }
.case-facts > div {
  display: grid;
  gap: var(--space-1);
  padding-block: var(--space-3);
  border-block-start: var(--joint) solid var(--line);
}
.case-facts dt { font-weight: var(--label-weight); }
.case-facts dd { margin: 0; color: var(--text-muted); }

.chapter {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-6);
  padding: var(--space-7) var(--gutter) var(--space-8);
}
.chapter-say { display: grid; gap: var(--space-4); align-content: start; }
.chapter-say .display { max-inline-size: 16ch; margin-block-end: var(--space-2); }
.chapter-say .display::before {
  content: "";
  display: block;
  inline-size: var(--space-7);
  block-size: var(--space-1);
  margin-block-end: var(--space-5);
  border-radius: var(--radius-pill);
  background: var(--accent);
}
.chapter-say p { max-inline-size: 52ch; text-wrap: pretty; }

.entry-dark {
  --dark-muted: color-mix(in oklch, var(--bg) 74%, var(--text));
  --dark-line: color-mix(in oklch, var(--bg) 22%, var(--text));
  background: var(--text);
  color: var(--bg);
  padding-block-end: var(--space-7);
}
.entry-dark .case-lede, .entry-dark .case-facts dd, .entry-dark figcaption { color: var(--dark-muted); }
.entry-dark .case-facts > div { border-block-start-color: var(--dark-line); }
.entry-dark .chapter { padding-block: var(--space-6); }
.entry-dark .chapter-say p { color: var(--dark-muted); }

.scene { display: grid; gap: var(--space-3); padding: 0 var(--gutter); }
.scene img {
  display: block;
  inline-size: 100%;
  block-size: auto;
  border-radius: var(--radius-lg);
  box-shadow: 0 var(--space-4) var(--space-7) calc(var(--space-4) * -1) color-mix(in oklch, var(--text) 70%, transparent);
}
.entry-dark .scene img { box-shadow: 0 0 0 var(--joint) var(--dark-line); }

/* The hall: the drawing, with the Event Timer's own screens laid into its frames (viewBox 1600 by 1000). */
.hall-art { position: relative; display: block; border-radius: var(--radius-lg); overflow: hidden; }
.hall-art .hall-room { display: block; inline-size: 100%; block-size: auto; }
.hall-art .hall-screen { position: absolute; display: block; overflow: hidden; border-radius: 0.3%; background: #060504; }
.hall-art .hall-centre { inset: 24.8% auto auto 30.625%; inline-size: 38.75%; block-size: 35.2%; }
.hall-art .hall-left { inset: 26.6% auto auto 12%; inline-size: 12.375%; block-size: 37.8%; }
.hall-art .hall-right { inset: 26.6% auto auto 75.625%; inline-size: 12.375%; block-size: 37.8%; }
.scene.hall { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-3); }
.scene.hall .hall-art { box-shadow: 0 0 0 var(--joint) var(--dark-line, var(--line)); }
.scene.hall .hall-art .hall-room { box-shadow: none; border-radius: 0; }
.et-states { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.et-states[hidden] { display: none; }
.et-state {
  min-block-size: var(--tap); padding: 0 var(--space-4);
  border: var(--joint) solid var(--dark-line, var(--line-strong)); border-radius: var(--radius-pill);
  background: transparent; color: inherit; font: inherit; font-weight: var(--label-weight); cursor: pointer;
}
.et-state[aria-pressed="true"] { background: var(--bg); color: var(--text); border-color: var(--bg); }
.et-state:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

@media (min-width: 768px) {
  .case-head { padding-block: var(--space-7); }
  .case-title { font-size: var(--text-4xl); }
  .case-lede, .chapter-say p { font-size: var(--text-lg); }
  .case-facts { grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: var(--space-5); }
  .chapter { padding-block: var(--space-8); }
}
@media (min-width: 1024px) {
  .case-head { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); column-gap: var(--space-8); align-items: end; }
  .case-title { grid-column: 1 / -1; max-inline-size: 18ch; font-size: calc(var(--text-4xl) * 1.45); }
  .case-facts { grid-column: 1 / -1; }
  .chapter-say { grid-template-columns: minmax(0, 6fr) minmax(0, 5fr); column-gap: var(--space-8); }
  .chapter-say .display { grid-row: span 3; }
  .chapter-say .display::before { margin-block-end: var(--space-6); }
  .chapter-say p:first-of-type { padding-block-start: var(--space-6); }
  .scene { grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--space-5); }
  .scene img { grid-column: 1 / 12; }
  .scene figcaption { grid-column: 1 / 9; }
  .scene-wide img { grid-column: 2 / 13; }
  .scene-wide figcaption { grid-column: 2 / 10; }
  .scene.hall { grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--space-5); }
  .scene.hall .hall-art { grid-column: 1 / 12; }
  .scene.hall .et-states, .scene.hall figcaption { grid-column: 1 / 10; }
  .scene-wide.hall .hall-art { grid-column: 2 / 13; }
  .scene-wide.hall figcaption { grid-column: 2 / 10; }
}

/* ---------- The Event Timer's own screens (scripts/hall.mjs writes them; round 3) ----------
   The app's markup and its styles (WillJam4/event-timer: app/globals.css, timer-card.tsx, display-board.tsx,
   event-banner.tsx), every size in container units against the screen and the card as the app lays them
   out, so a screen drawn in the hall is the app's screen at any size; the app's few pixel floors are left
   out, since at the app's own screen sizes they never apply. The class text uses the app's own text-size
   control (its +/- steps): one step smaller on the tall panels (0.72) and two on the big board (0.48), so a
   class's title and subtitle show whole with the start of the next under them (his note). Its colours
   are the app's theme and the event's labels; the times stand still at the moment chosen. */
@font-face { font-family: "Michroma"; font-style: normal; font-weight: 400; font-display: swap; src: url(/fonts/michroma-latin-400-normal.woff2) format("woff2"); }
.et-board {
  --et-ink: #060504; --et-panel: #0f0e0d; --et-fg: #ffffff; --et-gold: #c9b47a; --et-bronze: #4c3e27;
  --et-rule: linear-gradient(90deg, var(--et-bronze), var(--et-gold) 50%, var(--et-bronze));
  container-type: size; position: absolute; inset: 0; display: flex; flex-direction: column; overflow: hidden;
  background: var(--et-ink); color: var(--et-fg); font-family: "Inter Variable", Inter, system-ui, Arial, sans-serif;
  font-weight: 400; line-height: 1.5; letter-spacing: normal; text-align: start; -webkit-font-smoothing: antialiased; font-synthesis-weight: none;
}
.et-board[hidden] { display: none; }
.et-wide { --font-scale: 0.48; }
.et-panel { --font-scale: 0.72; }
.et-tnum { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }
.et-nowrap { white-space: nowrap; }
.et-board p, .et-board ul { margin: 0; padding: 0; list-style: none; }
/* the event banner: the logo, the group's name, the venue clock, a gold hairline under it */
.et-banner {
  --banner-h: 15cqh; --banner-pad: 0.6cqh; --banner-slot: calc(var(--banner-h) - 2 * var(--banner-pad) - 1px);
  flex: none; box-sizing: border-box; block-size: var(--banner-h);
  display: grid; grid-template-columns: var(--banner-slot) minmax(0, 1fr) var(--banner-slot); align-items: center; gap: 1.5cqw;
  padding: var(--banner-pad) 1.8cqw; background: var(--et-ink);
  border-block-end: 1px solid transparent; border-image: var(--et-rule) 1;
}
.et-logo { display: flex; align-items: center; inline-size: var(--banner-slot); block-size: var(--banner-slot); }
.et-logo img { display: block; inline-size: 100%; block-size: 100%; object-fit: contain; object-position: left center; }
.et-title { display: flex; min-inline-size: 0; flex-direction: column; align-items: center; justify-self: center; gap: 0.6cqh; text-align: center; }
.et-name {
  font-family: "Michroma", Arial, sans-serif; font-weight: 400; font-size: min(2.4cqh, 3.3cqw); line-height: 1.3;
  letter-spacing: 0.12em; padding-inline-start: 0.12em; text-transform: uppercase; color: var(--et-gold); text-wrap: balance; overflow-wrap: break-word;
}
.et-breakin { border: 1px solid var(--et-gold); color: var(--et-gold); background: var(--et-ink); padding: 0.35em 0.9em; font-size: 1.5cqh; font-weight: 600; line-height: 1; white-space: nowrap; }
.et-clock { display: flex; flex-direction: column; align-items: flex-end; justify-self: end; line-height: 1; }
.et-clock-label { font-family: "Michroma", Arial, sans-serif; font-size: 1.1cqh; letter-spacing: 0.2em; text-transform: uppercase; color: var(--et-gold); }
.et-clock-digits { margin-block-start: 0.5cqh; font-size: 3.2cqh; font-weight: 700; color: var(--et-fg); }
/* the group's start band, under the banner in the last hour before the start */
.et-prestart {
  flex: none; display: flex; align-items: baseline; justify-content: center; gap: 2cqw; padding: 1cqh 2.5cqw;
  background: var(--et-ink); color: var(--et-gold); border-block-end: 1px solid transparent; border-image: var(--et-rule) 1;
}
.et-prestart-label { font-family: "Michroma", Arial, sans-serif; font-size: 2.1cqh; letter-spacing: 0.2em; text-transform: uppercase; }
.et-prestart-clock { font-size: 4.6cqh; font-weight: 700; line-height: 1; letter-spacing: -0.01em; }
/* the stage: the big board's grid of cards, a panel's rows */
.et-stage { display: flex; flex: 1; min-block-size: 0; flex-direction: column; padding: 1.2cqh 2.5cqw 8.5cqh; }
.et-stage-rows { padding: 0.45cqh; }
.et-cards {
  display: grid; flex: 1; min-block-size: 0; place-content: center; gap: 1.6cqmin; overflow: hidden;
  grid-template-columns: repeat(3, minmax(0, min(66cqh, 90cqw))); grid-template-rows: repeat(2, minmax(0, min(44cqh, 60cqw)));
}
.et-rows { display: grid; flex: 1; min-block-size: 0; grid-template-columns: minmax(0, 1fr); grid-template-rows: repeat(3, minmax(0, 1fr)); gap: 0.45cqh; }
/* the full-screen break */
.et-breakpanel { display: flex; flex: 1; flex-direction: column; align-items: center; justify-content: center; background: var(--et-panel); }
.et-break-title { font-family: "Michroma", Arial, sans-serif; font-size: 13cqw; line-height: 1; letter-spacing: 0.12em; padding-inline-start: 0.12em; color: var(--et-gold); }
.et-board .et-break-clock { margin-block-start: 3cqh; border: max(1px, 0.25cqmin) solid var(--et-gold); padding: 1.2cqh 4cqw; font-size: 7cqw; font-weight: 700; line-height: 1; color: var(--et-fg); }
/* one timer: its colour down the left, the duration and the end time, the class list, the clock */
.et-card { container-type: size; position: relative; overflow: hidden; display: flex; flex-direction: column; inline-size: 100%; block-size: 100%; background: var(--et-panel); }
.et-c1 { --c: #ffff00; } .et-c2 { --c: #92d050; } .et-c3 { --c: #00b0f0; } .et-c4 { --c: #ffc000; } .et-c5 { --c: #ff0000; } .et-c6 { --c: #e383e6; }
.et-main { display: flex; flex: 1 1 auto; min-block-size: 0; }
.et-accent { flex: none; inline-size: 2.2cqw; block-size: 100%; background: var(--c); }
.et-body {
  --clock-size: min(32cqh, 22cqw); --head-size: min(4.2cqh, 2.4cqw);
  --class-title: calc(var(--clock-size) * 0.5 * var(--font-scale, 1)); --class-sub: calc(var(--class-title) * 0.85); --class-lh: calc(var(--class-title) * 1.06);
  --class-room: max(0px, calc(84cqh - var(--clock-size) - var(--head-size) * 2.6 - (1px + var(--head-size) * 0.9)));
  display: flex; flex: 1; min-inline-size: 0; flex-direction: column; justify-content: center; padding: 3cqmin;
}
.et-head { display: flex; align-items: baseline; justify-content: space-between; gap: 2cqmin; line-height: 1.5; }
.et-badge { font-size: var(--head-size); letter-spacing: 0.14em; font-weight: 600; color: var(--c); }
.et-status { font-size: calc(var(--head-size) * 0.92); letter-spacing: 0.16em; font-weight: 600; color: var(--et-fg); opacity: 0.5; }
.et-ends { margin-inline-start: auto; font-size: calc(var(--head-size) * 1.1); font-weight: 600; letter-spacing: 0.06em; white-space: nowrap; color: var(--et-gold); }
.et-list { position: relative; max-block-size: var(--class-room); overflow: hidden; margin-block: calc(var(--head-size) * 0.8); }
/* only while it scrolls, its top and bottom edges soften, as the app's do */
.et-list[data-scroll="true"] {
  --fade: calc(var(--class-lh) * 0.6);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--fade), #000 calc(100% - var(--fade)), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 var(--fade), #000 calc(100% - var(--fade)), transparent 100%);
}
.et-scroller { will-change: transform; }
.et-board .et-classes.et-dup { margin-block-start: var(--class-lh); }
.et-board .et-classes[hidden] { display: none; }
.et-class + .et-class { margin-block-start: calc(var(--class-title) * 0.34); }
.et-class-name { display: block; font-size: var(--class-title); line-height: 1.04; font-weight: 600; overflow-wrap: break-word; }
.et-class-variant { display: block; font-size: var(--class-sub); line-height: 1.06; margin-block-start: calc(var(--class-title) * 0.04); opacity: 0.7; overflow-wrap: break-word; }
.et-rule { flex: none; block-size: 1px; margin-block: calc(var(--head-size) * 0.45); background: var(--et-rule); }
.et-time { font-size: var(--clock-size); line-height: 0.95; letter-spacing: -0.025em; font-weight: 700; color: var(--c); }
/* a tall panel's row: the clock first, the classes under it */
.et-row .et-body {
  --clock-size: min(30cqh, 19cqw); --head-size: min(5cqh, 2.9cqw);
  --class-room: max(var(--class-lh), calc(100cqh - 5cqmin - var(--head-size) * 1.8 - var(--clock-size) * 0.95 - (1px + var(--head-size) * 0.9)));
  justify-content: flex-start; padding: 2.5cqmin;
}
.et-row .et-clock-block { text-align: center; }
.et-row .et-list { margin-block: 0; }
/* (the Hestia head's title keeps its own measure) */

/* ---------- One width for the entries (from about 1600 px) ----------
   The sections keep their full-width grounds; their content keeps to 90rem, centred, so the words and the
   pictures of every entry share one column. "Portfolio" itself stays edge to edge, as the wordmark does. */
.pf-tiles { inline-size: min(100%, 90rem); justify-self: center; }
.hestia-head, .pf-suite, .pf-reader, .pf-config, .pf-gallery,
.entry-dark > .case-head, .entry-dark > .scene, .entry-dark > .chapter,
.doors-band {
  padding-inline: max(var(--gutter), calc((100% - 90rem) / 2));
}
@media (min-width: 1024px) {
  /* the door row fits under the header and the step bar, its names included */
  .pf-doors { max-inline-size: min(100%, calc((100svh - var(--head) - var(--stepbar) - 8rem) * 2.25 + 3rem)); }
}
