/* =========================================================
   Fonts (self-hosted, SIL Open Font License)
   Plus Jakarta Sans for everything, IBM Plex Mono for the small print (and inside the app screens drawn on canvas)
   ========================================================= */
@font-face { font-family: "Plus Jakarta Sans"; font-style: normal; font-weight: 200 800; font-display: swap; src: url("assets/fonts/plus-jakarta-sans-latin-wght.woff2") format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Plus Jakarta Sans"; font-style: normal; font-weight: 200 800; font-display: swap; src: url("assets/fonts/plus-jakarta-sans-latin-ext-wght.woff2") format("woff2"); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1E00-1E9F, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400; font-display: swap; src: url("assets/fonts/ibm-plex-mono-latin-400-normal.woff2") format("woff2"); }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 500; font-display: swap; src: url("assets/fonts/ibm-plex-mono-latin-500-normal.woff2") format("woff2"); }

/* =========================================================
   Tokens: three surfaces from the poster (vermilion, paper and ink), and everything drawn in --fg on --bg
   ========================================================= */
:root {
  --sans: "Plus Jakarta Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, Menlo, monospace;
  --gutter: clamp(20px, 3.6vw, 60px);
  --max: 1480px;
  --top: 72px;
  --out: cubic-bezier(.16, 1, .3, 1);
  --inout: cubic-bezier(.76, 0, .24, 1);

  --red: #EE4A22; --paper: #F2EDE3; --ink: #121110; --white: #FBFAF6;
  --metro: #2C66D0;
  /* paper is the default surface (the dialogs are on it too) */
  --bg: var(--paper); --fg: var(--ink);
  --fg-2: rgb(18 17 16 / .7); --fg-3: rgb(18 17 16 / .62);
  --rule: rgb(18 17 16 / .14); --rule-2: rgb(18 17 16 / .34);
  --accent: #D7380F; --hover: rgb(18 17 16 / .05);
  color-scheme: light;
}
[data-theme="ink"] { --bg: var(--ink); --fg: var(--paper); --fg-2: rgb(242 237 227 / .72); --fg-3: rgb(242 237 227 / .54); --rule: rgb(242 237 227 / .13); --rule-2: rgb(242 237 227 / .3); --accent: var(--red); --metro: #6B9BEA; --hover: rgb(242 237 227 / .06); }
[data-theme="red"] { --bg: var(--red); --fg: var(--ink); --fg-2: rgb(18 17 16 / .82); --fg-3: rgb(18 17 16 / .74); --rule: rgb(18 17 16 / .22); --rule-2: rgb(18 17 16 / .5); --accent: var(--ink); --hover: rgb(18 17 16 / .07); }

/* =========================================================
   Base
   ========================================================= */
*, *::before, *::after { box-sizing: border-box; }
/* no rubber band past the top or the bottom */
html { -webkit-tap-highlight-color: transparent; -webkit-text-size-adjust: 100%; scroll-padding-top: var(--top); background: var(--paper); overscroll-behavior-y: none; }
body { margin: 0; background: var(--paper); color: var(--ink); font: 400 16px/1.55 var(--sans); -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; overflow-x: clip; overscroll-behavior-y: none; }
/* (but not in Safari: there, with Lenis listening, it stops a trackpad or a wheel scrolling the page at all. -apple-system-body
   is a font only Safari knows, on a Mac and an iPhone) */
@supports (font: -apple-system-body) { html { overscroll-behavior-y: auto; } }
html.lenis, html.lenis body { height: auto; }
/* Scrollbars: thin, in the ink of the part of the page beside them (the page's own takes the colour of the section under
   the top bar, set by the page's script as data-surface); the letter, the menus and the code keep theirs to their own paper */
:root { --sb: rgb(18 17 16 / .42); scrollbar-width: thin; scrollbar-color: var(--sb) transparent; }
:root[data-surface="ink"] { --sb: rgb(242 237 227 / .38); }
:root[data-surface="red"] { --sb: rgb(18 17 16 / .5); }
.short__card, .palette__list, .jump { scrollbar-width: thin; scrollbar-color: rgb(18 17 16 / .28) transparent; }
.codewin > :is(pre, [data-was="pre"]) { scrollbar-width: thin; scrollbar-color: rgb(230 227 220 / .28) transparent; }
/* (Safari: the same, the older way) */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { border: 3px solid transparent; border-radius: 99px; background: var(--sb) padding-box; }
.short__card::-webkit-scrollbar-thumb, .palette__list::-webkit-scrollbar-thumb, .jump::-webkit-scrollbar-thumb { background-color: rgb(18 17 16 / .28); }
.codewin pre::-webkit-scrollbar-thumb { background-color: rgb(230 227 220 / .28); }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-stopped { overflow: hidden; }
h1, h2, h3, p, ol, ul, dl, dd, figure { margin: 0; }
ol, ul { padding: 0; list-style: none; }
img { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 3px; }
button { font: inherit; color: inherit; }
code { font: 500 .86em var(--mono); background: var(--hover); padding: .08em .36em; border-radius: 4px; }
kbd { font: 500 11px var(--mono); padding: 2px 6px; border: 1px solid var(--rule-2); border-radius: 4px; }
::selection { background: var(--red); color: var(--ink); }
:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; border-radius: 3px; }
/* (a dialog takes focus itself as it opens, rather than on its first button: no ring on that, nothing pressed yet) */
[tabindex="-1"]:focus { outline: none; }
[hidden] { display: none !important; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
.skip { position: absolute; left: 12px; top: -60px; z-index: 900; background: var(--ink); color: var(--paper); padding: 10px 14px; border-radius: 6px; }
.skip:focus { top: 12px; }
.wrap { width: 100%; max-width: var(--max); margin-inline: auto; padding-inline: var(--gutter); }
main section, .foot { position: relative; background: var(--bg); color: var(--fg); }
@media (hover: none) { .hint-fine, .hover-only { display: none; } }

/* small print: what a section is, in mono capitals */
.kicker { font: 500 12px/1.5 var(--mono); letter-spacing: .02em; text-transform: uppercase; color: var(--fg-2); }

/* a section's running head: its number, its name, and an aside, under a rule that draws itself in */
.sh { position: relative; display: grid; grid-template-columns: 4.5em minmax(0, 1fr) auto; gap: 16px; align-items: baseline; padding-top: 14px; font: 500 12px/1.5 var(--mono); letter-spacing: .02em; text-transform: uppercase; }
.sh::before { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1.5px; background: currentColor; transform-origin: 0 50%; transition: transform 1.2s var(--inout); }
.sh__aside { color: var(--fg-2); text-align: right; }
/* (on a case study, a part's name is its heading, in the running head's small print) */
h2.sh__name { font: inherit; letter-spacing: inherit; }
.js:not(.reduced) .sh:not(.is-in)::before { transform: scaleX(0); }

/* a marker run under the words that matter, as you read them */
mark { color: inherit; background: linear-gradient(var(--red), var(--red)) 0 88% / 100% .3em no-repeat; -webkit-box-decoration-break: clone; box-decoration-break: clone; padding: 0 .03em; }
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    mark { animation: marker linear both; animation-timeline: view(); animation-range: cover 30vh cover 52vh; }
  }
}
@keyframes marker { from { background-size: 0% .3em; } }

/* SplitText's line masks clip at the line box, which cuts off g, j, p and y: give them room above and below without moving anything */
.sl-mask { padding: .1em 0 .2em; margin: 0 0 -.3em; }
/* (and all of a line's give is below it: margins between two lines collapse rather than add up, so a line that took it from
   above and below would leave the text taller once it's split than before, and the page would shift) */
.sl-mask:first-child { margin-top: -.1em; }
.sl-mask:last-child { margin-bottom: -.2em; }
/* a line of big letters that come up from below it, one after another (main.js and hero.js split them) */
.mclip { display: block; overflow: hidden; padding: .08em .1em .06em 0; margin: -.08em -.1em -.06em 0; }
.mclip .ch { display: inline-block; }
/* (lower case has descenders: room for the y and the g) */
.contact__addr .mclip { padding-bottom: .26em; margin-bottom: -.26em; }

/* the big words of a section */
.title { position: relative; isolation: isolate; font: 800 clamp(54px, 10.4vw, 172px)/.84 var(--sans); letter-spacing: -.058em; text-transform: uppercase; margin-left: -.045em; }
/* ...printed in two colours: the vermilion lands first, out of register, and comes into it as you scroll on (then it's
   under the black, and goes) */
.title[data-text]::before { content: attr(data-text); position: absolute; inset: 0; z-index: -1; color: var(--red); opacity: 0; pointer-events: none; }
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .title[data-text]::before { animation: register linear both; animation-timeline: view(); animation-range: cover 0% cover 70vh; }
  }
}
@keyframes register { from { opacity: 1; transform: translate3d(.07em, .045em, 0); } 85% { opacity: 1; transform: translate3d(.0105em, .0068em, 0); } to { opacity: 0; transform: none; } }

/* labels that roll over when you point at them (main.js doubles the words) */
.roll { display: inline-grid; overflow: hidden; padding-block: .16em; margin-block: -.16em; }
.roll > span { grid-area: 1 / 1; transition: transform .6s var(--out); }
.roll > span + span { transform: translateY(calc(100% + .32em)); }
@media (hover: hover) {
  :hover > .roll > span:first-child { transform: translateY(calc(-100% - .32em)); }
  :hover > .roll > span + span { transform: none; }
}

/* buttons: a pill, solid or outlined, in the colour of the surface it's on */
.pill { display: inline-flex; align-items: center; gap: 10px; height: 48px; padding: 0 22px; border: 1.5px solid currentColor; border-radius: 999px; background: none; color: var(--fg); font: 700 14.5px var(--sans); letter-spacing: -.005em; text-decoration: none; white-space: nowrap; cursor: pointer; transition: background-color .3s, color .35s, border-color .35s, transform .5s var(--out); }
.pill.ink { border-color: var(--fg); }
.pill--solid { background: var(--fg); border-color: var(--fg); color: var(--bg); }
.pill:hover { transform: translateY(-2px); }
.pill__note { font: 500 11px var(--mono); opacity: .7; }
/* a link with an arrow that slides */
.arrow { display: inline-flex; align-items: center; gap: 8px; padding: 0; border: 0; background: none; cursor: pointer; font: 700 14.5px var(--sans); color: var(--fg); text-decoration: none; }
.arrow::after { content: ""; width: 14px; height: 14px; flex-shrink: 0; background: currentColor; transition: transform .45s var(--out);
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'><path d='M1 7h11M7.5 2.5 12 7l-4.5 4.5' fill='none' stroke='black' stroke-width='1.7'/></svg>") center / contain no-repeat;
          mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'><path d='M1 7h11M7.5 2.5 12 7l-4.5 4.5' fill='none' stroke='black' stroke-width='1.7'/></svg>") center / contain no-repeat; }
.arrow--out::after { transform: rotate(-45deg); }
.arrow:hover::after { transform: translateX(4px); }
.arrow--out:hover::after { transform: rotate(-45deg) translateX(4px); }
/* a link that leaves the site: the same arrow, pointing out */
.ext::after { content: ""; display: inline-block; width: .78em; height: .78em; margin-left: .38em; vertical-align: -.06em; background: currentColor; transform: rotate(-45deg);
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'><path d='M1 7h11M7.5 2.5 12 7l-4.5 4.5' fill='none' stroke='black' stroke-width='1.7'/></svg>") center / contain no-repeat;
          mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'><path d='M1 7h11M7.5 2.5 12 7l-4.5 4.5' fill='none' stroke='black' stroke-width='1.7'/></svg>") center / contain no-repeat; }
.pill.ext::after { margin-left: 0; }

/* =========================================================
   Top bar: clear over the poster, then a solid band in the colour of the section under it
   ========================================================= */
.top { position: fixed; inset: 0 0 auto 0; z-index: 600; height: var(--top); display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: 20px; padding-inline: var(--gutter); color: var(--fg); transition: transform .6s var(--out), color .3s, background-color .3s; }
.top::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: var(--rule); opacity: 0; transition: opacity .3s; }
.top.is-solid { background: var(--bg); }
/* (with no script to give it a band, it stays on the poster and scrolls away with it, rather than float clear over the page) */
html:not(.js) .top { position: absolute; }
.top.is-solid::after { opacity: 1; }
.top.is-hidden { transform: translateY(-101%); }
/* how far down the page you are: a rule along the bar's foot */
.top__progress { position: absolute; left: 0; right: 0; bottom: -1px; z-index: 1; height: 2px; background: currentColor; transform: scaleX(0); transform-origin: 0 50%; opacity: 0; transition: opacity .3s; pointer-events: none; }
.top.is-solid .top__progress { opacity: 1; }
.brand { display: inline-flex; flex-direction: column; justify-self: start; text-decoration: none; font: 800 13px/1.08 var(--sans); letter-spacing: .14em; text-transform: uppercase; white-space: nowrap; }
.top__nav { display: flex; gap: 6px; }
.top__nav a { position: relative; padding: 8px 12px; font: 500 12.5px var(--mono); letter-spacing: .02em; text-transform: uppercase; text-decoration: none; }
/* the section you're in has a square by it */
.top__nav a::before { content: ""; position: absolute; left: 0; top: 50%; width: 6px; height: 6px; margin-top: -3px; background: currentColor; transform: scale(0); transition: transform .4s var(--out); }
.top__nav a.is-active::before { transform: none; }
/* (on the home page, one square, that slides from section to section: main.js) */
.top__nav { position: relative; }
.top__nav.has-mark a::before { display: none; }
.top__mark { position: absolute; left: 0; top: 50%; width: 6px; height: 6px; margin-top: -3px; background: currentColor; opacity: 0; pointer-events: none; transition: transform .7s var(--out), opacity .3s; }
.top__right { display: flex; justify-content: flex-end; align-items: center; gap: 10px; }
.top__short, .top__menu, .top__back { display: inline-flex; align-items: center; text-decoration: none; height: 36px; padding: 0 14px; border: 1.5px solid currentColor; border-radius: 999px; background: none; cursor: pointer; font: 700 13px var(--sans); white-space: nowrap; transition: background-color .25s, color .25s; }
html:not(.js) .top__short:hover, html:not(.js) .top__menu:hover { background: var(--fg); color: var(--bg); }
/* outlined buttons fill with ink from where the pointer comes in, and empty toward where it leaves (main.js says where) */
.ink { position: relative; isolation: isolate; overflow: hidden; }
.ink::before { content: ""; position: absolute; left: var(--px, 50%); top: var(--py, 50%); z-index: -1; width: 260%; aspect-ratio: 1; border-radius: 50%; background: var(--fg); transform: translate(-50%, -50%) scale(0); transition: transform .6s var(--out); pointer-events: none; }
.ink.is-ink::before { transform: translate(-50%, -50%) scale(1); }
.ink.is-ink { color: var(--bg); }
.top__menu { display: none; }
html:not(.js) .top__menu, html:not(.js) .top__short { visibility: hidden; }
/* the key for the command menu (palette.js puts it in): a magnifier and the shortcut, on a wide screen */
.top__find { display: inline-flex; align-items: center; gap: 8px; height: 36px; padding: 0 10px 0 12px; border: 1.5px solid currentColor; border-radius: 999px; background: none; color: inherit; cursor: pointer; transition: background-color .25s, color .25s; }
.top__find svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2.2; }
.top__find kbd { padding: 0; border: 0; font: 500 12px var(--mono); color: inherit; }
.top__find:hover, .top__find:focus-visible { background: var(--fg); color: var(--bg); }
@media (max-width: 900px) { .top__find { display: none; } }

/* =========================================================
   The hero: a poster in vermilion and black, and my ID badge hanging on it (hero.js sets my name's size to the room
   it has, and hangs the badge, swings it and turns it over)
   ========================================================= */
/* the hero stays put while the first page slides over it (hero.js sinks it back a little as it goes) */
.lead { position: relative; }
.hero { position: sticky; top: min(0px, calc(100svh - 620px)); z-index: 0; height: 100svh; min-height: 620px; overflow: clip; transform-origin: 50% 30%; will-change: transform; }
.intro { z-index: 1; }
/* each new sheet comes in at an angle, and lies flat as it arrives: a band in its colour along its top edge, behind its
   content, turned by the scroll itself, so it's right from the first frame (where a browser can't, the sheets come in flat) */
.intro, .nums, .about, .xp, .contact { isolation: isolate; z-index: 1; }
:is(.intro, .nums, .about, .xp, .contact) > .wrap { position: relative; z-index: 1; }
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    :is(.intro, .nums, .about, .xp, .contact)::before { content: ""; position: absolute; left: 0; right: 0; top: 0; z-index: 0; height: 180px; background: var(--bg); transform-origin: 0 0; pointer-events: none;
      animation: sheet linear both; animation-timeline: view(); animation-range: cover 0% cover 70vh; }
    /* (the first page starts flat, under the poster: it only tips as it sets off over it, and its shadow on the poster
       tips with it) */
    .intro { view-timeline: --first; }
    .intro::before, .intro__lift { animation: sheet-first linear both; animation-timeline: --first; animation-range: cover 0% cover 70vh; }
    .intro__lift { transform-origin: 0 100%; }
  }
}
@keyframes sheet { from { transform: skewY(-3.2deg); } to { transform: none; } }
@keyframes sheet-first { 0% { transform: none; animation-timing-function: cubic-bezier(.25, .46, .45, .94); } 30% { transform: skewY(-3.2deg); animation-timing-function: cubic-bezier(.45, 0, .55, 1); } 100% { transform: none; } }
/* the shadow the page casts on the poster as it comes over it (hero.js fades it in as it goes) */
.intro__lift { position: absolute; left: 0; right: 0; bottom: 100%; height: 90px; pointer-events: none; opacity: 0; background: linear-gradient(rgb(18 17 16 / 0), rgb(18 17 16 / .28)); }
.hero__in { position: relative; z-index: 1; height: 100%; }
/* as the page opens, the poster is printed: the vermilion rolls down over the paper (hero.js) */
.hero__wash { position: absolute; inset: 0; z-index: 0; background: var(--red); }
.hero-pending .hero, .hero.is-printing { background: var(--paper); }
.hero-pending .hero { animation: failsafe-bg 0s 4s forwards; }
.js-ok .hero { animation: none; }
@keyframes failsafe-bg { to { background-color: var(--red); } }
/* trim marks at the sheet's corners */
.hero__marks { inset: 6px; pointer-events: none; --l: 14px; --o: 20px;
  background:
    linear-gradient(currentColor, currentColor) left 0 top var(--o) / var(--l) 1.5px,
    linear-gradient(currentColor, currentColor) left var(--o) top 0 / 1.5px var(--l),
    linear-gradient(currentColor, currentColor) right 0 top var(--o) / var(--l) 1.5px,
    linear-gradient(currentColor, currentColor) right var(--o) top 0 / 1.5px var(--l),
    linear-gradient(currentColor, currentColor) left 0 bottom var(--o) / var(--l) 1.5px,
    linear-gradient(currentColor, currentColor) left var(--o) bottom 0 / 1.5px var(--l),
    linear-gradient(currentColor, currentColor) right 0 bottom var(--o) / var(--l) 1.5px,
    linear-gradient(currentColor, currentColor) right var(--o) bottom 0 / 1.5px var(--l);
  background-repeat: no-repeat; }
.hero__in > * { position: absolute; }

/* rules along the top and the foot, frame the poster; they draw themselves in as it opens */
.hero__facts::before, .hero__foot::before { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1.5px; background: currentColor; transform-origin: 0 50%; transition: transform 1.3s var(--inout); }
.js:not(.reduced) .hero:not(.is-in) .hero__facts::before, .js:not(.reduced) .hero:not(.is-in) .hero__foot::before { transform: scaleX(0); }

/* the facts, numbered, in a row along the top (the fourth column is where the badge hangs) */
.hero__facts { left: var(--gutter); right: var(--gutter); top: calc(var(--top) + clamp(4px, 1.6vh, 18px)); display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 24px; padding-top: 14px; }
.hero__facts li { display: grid; align-content: start; gap: 2px; }
.hero__facts b { margin-bottom: 8px; font: 500 12px var(--mono); }
.hero__facts span { font: 800 15.5px/1.2 var(--sans); letter-spacing: -.005em; text-transform: uppercase; }
.hero__facts em { font: 500 14px/1.35 var(--sans); font-style: normal; color: var(--fg-2); }

/* my name: both lines the same size, as big as the room allows (these sizes are for when hero.js can't set them) */
.hero__name { left: var(--gutter); bottom: calc(clamp(24px, 5vh, 56px) + 96px); margin: 0; font: 800 calc((100vw - 2 * var(--gutter)) * .126)/.8 var(--sans); letter-spacing: -.058em; text-transform: uppercase; }
.hero__row { display: block; width: max-content; white-space: nowrap; }
.hero__row .ch { display: inline-block; }
.hero__clip { display: block; overflow: hidden; padding: .07em .1em .03em 0; margin: -.07em -.1em -.03em 0; }
/* (no transitions in it at all, not even the near-instant ones reduced motion gives everything: hero.js sets a size and
   measures it at once, and mid-transition it would measure the old one) */
.hero__name, .hero__name * { transition: none !important; }

.hero__foot { left: var(--gutter); right: var(--gutter); bottom: clamp(24px, 5vh, 56px); display: flex; align-items: flex-end; justify-content: space-between; gap: 32px; padding-top: 18px; }
.hero__line { max-width: 19ch; font: 700 clamp(22px, 2.2vw, 34px)/1.06 var(--sans); letter-spacing: -.03em; }
.hero__cta { display: flex; flex-wrap: wrap; gap: 10px; }
/* a label for the badge, like the one by a thing in a museum (on a wide screen, under it) */
.hero__label { display: none; z-index: 2; gap: 2px; }
.hero.is-wide .hero__label { display: grid; }
.hero__label b { margin-bottom: 6px; font: 500 12px var(--mono); }
.hero__label span { font: 800 15.5px/1.2 var(--sans); letter-spacing: -.005em; text-transform: uppercase; }
.hero__label em { font: 500 14px/1.35 var(--sans); font-style: normal; color: var(--fg-2); }

/* my badge: a PVC card (54 by 85.6 mm, with its slot punched through), on a lanyard from a pin on the top rule */
.hero__strap { left: 0; top: 0; z-index: 3; pointer-events: none; }
.badge { --cw: clamp(190px, 18vw, 300px); z-index: 4; left: 0; top: 0; width: var(--cw); height: calc(var(--cw) * 1.5852); padding: 0; border: 0; background: none; color: var(--ink); text-align: left; font: 500 calc(var(--cw) / 20) var(--sans); transform-origin: 50% 0; transform-style: preserve-3d; cursor: grab; touch-action: pan-y; user-select: none; -webkit-user-select: none; -webkit-tap-highlight-color: transparent; }
:where(.badge) span, :where(.badge) i { display: block; }
.has-badge .badge { will-change: transform; }
.badge.is-held { cursor: grabbing; }
.badge:focus-visible { outline: none; }
.badge:focus-visible .badge__shade { outline: 2px solid var(--ink); outline-offset: 5px; }
/* (with no script to hang it, it hangs in the fourth column, its lanyard going up off the page) */
html:not(.has-badge) .badge { left: auto; right: calc(var(--gutter) + (100vw - 2 * var(--gutter) + 24px) / 8 - 12px - var(--cw) / 2); top: calc(var(--top) + 90px); }
html:not(.has-badge) .badge::before { content: ""; position: absolute; left: 50%; bottom: 100%; width: calc(var(--cw) * .072); height: 100px; margin-left: calc(var(--cw) * -.036); background: #1B1A17; }

/* its shadow on the poster (a box of its own, so the card's punched slot doesn't let it through) */
.badge__shade { position: absolute; inset: 0; border-radius: 1.2em; box-shadow: .5em 1.3em 2.2em -.6em rgb(70 14 0 / .42), .15em .4em .7em -.2em rgb(70 14 0 / .32); transform: translateZ(-1px); }
.badge__face { position: absolute; inset: 0; overflow: hidden; backface-visibility: hidden; -webkit-backface-visibility: hidden;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 540 856' preserveAspectRatio='none'><path fill-rule='evenodd' d='M32 0h476a32 32 0 0 1 32 32v792a32 32 0 0 1-32 32H32A32 32 0 0 1 0 824V32A32 32 0 0 1 32 0zM213 34a15 15 0 0 0 0 30h114a15 15 0 0 0 0-30z'/></svg>") 0 0 / 100% 100% no-repeat;
          mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 540 856' preserveAspectRatio='none'><path fill-rule='evenodd' d='M32 0h476a32 32 0 0 1 32 32v792a32 32 0 0 1-32 32H32A32 32 0 0 1 0 824V32A32 32 0 0 1 32 0zM213 34a15 15 0 0 0 0 30h114a15 15 0 0 0 0-30z'/></svg>") 0 0 / 100% 100% no-repeat; }
/* the light on it: a face turned away from you darkens a little (hero.js adds the shade and sets how much) */
.badge__dim { position: absolute; inset: 0; z-index: 5; background: #000; opacity: 0; pointer-events: none; will-change: opacity; }
.badge__front { padding: 0 1.4em; background: var(--white); }
.badge__top { display: flex; align-items: baseline; justify-content: space-between; margin-top: 3.2em; }
.badge__top b { font: 800 .82em var(--sans); letter-spacing: .16em; text-transform: uppercase; }
.badge__top span { font: 500 .66em var(--mono); color: rgb(18 17 16 / .6); }
.badge__pic { position: relative; margin-top: .8em; border-radius: .45em; overflow: hidden; aspect-ratio: 1 / .96; background: #D9D4C8; }
.badge__pic img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 26%; pointer-events: none; }
/* a hologram, as on a real card: it catches the light as the card turns */
.badge__holo { position: absolute; right: .6em; bottom: .6em; width: 2.1em; height: 2.1em; border-radius: 50%; opacity: .72; background: radial-gradient(circle at 35% 30%, rgb(255 255 255 / .9), transparent 42%), conic-gradient(from 0deg, #F6B9D3, #B9E3F6, #CBF6B9, #F6E7B0, #D8C2F6, #F6B9D3); box-shadow: inset 0 0 0 1px rgb(255 255 255 / .6), 0 1px 2px rgb(0 0 0 / .2); will-change: transform; }
.badge__name { margin-top: .42em; font: 800 1.86em/.9 var(--sans); letter-spacing: -.035em; text-transform: uppercase; }
.badge__row { display: flex; justify-content: space-between; gap: 1em; margin-top: 1.5em; padding-top: .9em; border-top: 1px solid rgb(18 17 16 / .14); font: 500 .66em var(--mono); text-transform: uppercase; color: rgb(18 17 16 / .66); }
.badge__role { position: absolute; left: 0; right: 0; bottom: 0; height: 2.4em; display: flex; align-items: center; padding: 0 1.7em; background: var(--red); color: var(--ink); font: 800 .82em var(--sans); letter-spacing: .14em; text-transform: uppercase; }
/* (only the face towards you is drawn: hero.js says which, as Safari doesn't always leave the far one out) */
.badge:not(.is-back) .badge__back, .badge.is-back .badge__front { visibility: hidden; }
.badge__back { transform: rotateY(180deg); display: grid; justify-items: center; align-content: center; gap: .5em; padding: 2.6em 1.4em 2.4em; text-align: center; background: #161513; color: var(--paper); }
.badge__back .badge__role { justify-content: center; letter-spacing: .04em; font: 500 .72em var(--mono); text-transform: none; }
.badge__k { font: 500 .62em var(--mono); text-transform: uppercase; letter-spacing: .04em; color: rgb(242 237 227 / .6); }
.badge__who { margin-top: .1em; font: 800 1.3em/1 var(--sans); letter-spacing: -.02em; text-transform: uppercase; }
.badge__mail { font: 500 .78em var(--sans); color: rgb(242 237 227 / .82); }
.badge__qr { width: 10.6em; margin: 1.1em 0 .4em; padding: .75em; border-radius: .5em; background: var(--white); }
.badge__qr svg { display: block; width: 100%; height: auto; fill: var(--ink); }

/* the hero waits for hero.js to bring it in (the animation is a failsafe) */
.hero-pending .hero__facts, .hero-pending .hero__name, .hero-pending .hero__foot, .hero-pending .badge, .hero-pending .hero__strap, .hero-pending .hero__label { opacity: 0; animation: failsafe 0s 4s forwards; }
.js-ok .hero__in > * { animation: none; }
@keyframes failsafe { to { opacity: 1; } }

/* =========================================================
   What I do
   ========================================================= */
.intro { padding: clamp(84px, 13vh, 150px) 0 clamp(70px, 11vh, 130px); }
.intro__big { max-width: 18em; margin-top: clamp(44px, 9vh, 110px); font: 700 clamp(32px, 4.4vw, 74px)/1.03 var(--sans); letter-spacing: -.038em; text-wrap: balance; }
.caps { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px clamp(24px, 3vw, 48px); margin-top: clamp(56px, 10vh, 120px); }
.cap { display: flex; flex-direction: column; padding-top: 16px; border-top: 1px solid var(--rule-2); }
.cap__k { display: flex; align-items: center; gap: 10px; font: 800 15.5px var(--sans); text-transform: uppercase; }
.cap__k span { display: grid; place-items: center; width: 22px; height: 22px; border: 1.5px solid currentColor; border-radius: 4px; font: 500 11px var(--mono); }
.cap__v { margin-top: 12px; max-width: 26em; font-size: 16.5px; color: var(--fg-2); text-wrap: pretty; }
/* (and what each is made of, by its icon: each one a way to its case study, its name over it when pointed at) */
.cap__apps { display: flex; flex-wrap: wrap; gap: 10px; margin-top: auto; padding-top: 20px; }
.cap__apps a { position: relative; display: block; width: 40px; height: 40px; border-radius: 10px; box-shadow: 0 0 0 1px var(--rule), 0 10px 18px -12px rgb(18 17 16 / .5); transition: transform .45s var(--out), box-shadow .45s var(--out); }
.cap__apps img { width: 100%; height: 100%; border-radius: inherit; }
.cap__apps a::after { content: attr(data-name); position: absolute; left: 50%; bottom: calc(100% + 10px); padding: 5px 8px 4px; border-radius: 5px; background: var(--ink); color: var(--paper); font: 500 11px/1 var(--mono); text-transform: uppercase; white-space: nowrap; pointer-events: none; opacity: 0; transform: translate(-50%, 6px); transition: opacity .25s, transform .35s var(--out); }
@media (hover: hover) {
  .cap__apps a:hover { transform: translateY(-4px) rotate(-3deg); box-shadow: 0 0 0 1px var(--rule), 0 16px 22px -12px rgb(18 17 16 / .55); }
  .cap__apps a:hover::after { opacity: 1; transform: translate(-50%, 0); }
}
.cap__apps a:focus-visible::after { opacity: 1; transform: translate(-50%, 0); }

/* =========================================================
   Work
   ========================================================= */
/* (it follows on from What I do on the same paper, so it starts closer under it than the other sections do; gone to by a
   link, it stops short by the difference, so its running head lands where theirs do, clear of the top bar) */
.work { padding: clamp(40px, 6vh, 70px) 0 clamp(90px, 14vh, 160px); scroll-margin-top: calc(clamp(84px, 13vh, 150px) - clamp(40px, 6vh, 70px)); }
.work__head { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 21rem); align-items: end; gap: 24px 48px; margin-top: clamp(40px, 7vh, 84px); }
.work__lede { padding-bottom: .4em; font-size: 16.5px; color: var(--fg-2); text-wrap: pretty; }

/* the four, each with a spec sheet; on a wide screen, their pictures change in a frame that stays beside them */
.show { margin-top: clamp(48px, 9vh, 110px); }
.pj { padding: clamp(36px, 6vh, 56px) 0; border-top: 1px solid var(--rule-2); }
.pj__media { position: relative; display: grid; place-items: center; aspect-ratio: 4 / 3; max-height: min(78vh, 640px); margin-bottom: 28px; padding: 6%; border-radius: 8px; overflow: hidden; cursor: pointer; container-type: size; background: radial-gradient(120% 100% at 28% 12%, color-mix(in srgb, var(--c) 64%, #15130F), color-mix(in srgb, var(--c) 20%, #0E0D0B) 78%); }
.show__panel .shot { width: 100%; }
/* (sized to the frame's height as well as its width, so a phone never stands taller than the frame) */
.pj__media .shot { width: min(100cqw, calc(100cqh * 1320 / 844)); }
.pj__media .shot[data-device="phone"] { width: min(40cqw, calc(100cqh * 600 / 1308 * .9)); }
.pj__media .calc-hint { bottom: 8px; font-size: 10.5px; }
.pj__no { font: 500 12px var(--mono); color: var(--fg-3); }
.pj__name { margin-top: 10px; font: 800 clamp(40px, 11vw, 64px)/.86 var(--sans); letter-spacing: -.055em; text-transform: uppercase; margin-left: -.03em; }
.pj__name a { display: inline-block; color: inherit; text-decoration: none; }
.pj__what { max-width: 28em; margin-top: 20px; font-size: clamp(17px, 1.3vw, 19.5px); line-height: 1.45; text-wrap: pretty; }
.spec { max-width: 32em; margin-top: 26px; border-top: 1px solid var(--rule-2); }
.spec > div { display: grid; grid-template-columns: 7rem minmax(0, 1fr); gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--rule); }
.spec dt { font: 500 12px/1.65 var(--mono); text-transform: uppercase; color: var(--fg-3); }
.spec dd { font-size: 15.5px; }
/* a list that's ruled as it comes in, a line at a time, like a ledger (main.js: the rule is drawn, then what's on it) */
.ledger > div { position: relative; border-bottom-color: transparent !important; }
.ledger > div::after { content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px; background: var(--ledger-c); transform-origin: 0 50%; transform: scaleX(var(--rule-p, 1)); }
.pj__hard { max-width: 32em; margin-top: 22px; font-size: 15.5px; color: var(--fg-2); text-wrap: pretty; }
.pj__hard b { display: block; margin-bottom: 4px; font: 500 12px var(--mono); text-transform: uppercase; color: var(--accent); }
.pj__links { display: flex; flex-wrap: wrap; gap: 12px 26px; margin-top: 26px; }
.show__stage { display: none; }

@media (min-width: 1000px) {
  .show { display: grid; grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr); gap: clamp(36px, 4.6vw, 88px); }
  .pj { min-height: 92vh; display: flex; flex-direction: column; justify-content: center; }
  .pj:last-child { min-height: 100vh; }
  /* (without the script, each project keeps its own picture: the frame can't follow along) */
  html.js .pj__media { display: none; }
  .show__stage { display: block; }
  html:not(.js) .show { display: block; }
  html:not(.js) .show__stage { display: none; }
  html:not(.js) .pj { min-height: 0; }
  .show__frame { position: sticky; top: calc(var(--top) + 3vh); height: calc(100vh - var(--top) - 6vh); border-radius: 10px; overflow: hidden; background: #15130F; cursor: pointer; isolation: isolate; }
  /* each comes up over the one before, and goes back down when you scroll back */
  .show__panel { position: absolute; inset: 0; display: grid; place-items: center; padding: 9%; transform: translate3d(0, 101%, 0); transition: transform 1.1s var(--out);
    background: radial-gradient(120% 100% at 28% 12%, color-mix(in srgb, var(--c) 64%, #15130F), color-mix(in srgb, var(--c) 20%, #0E0D0B) 78%); }
  .show__panel:nth-child(1) { z-index: 1; } .show__panel:nth-child(2) { z-index: 2; } .show__panel:nth-child(3) { z-index: 3; } .show__panel:nth-child(4) { z-index: 4; }
  /* each screen comes up tipped back, and stands up as it arrives */
  .show__panel .device { transform: perspective(1400px) translate3d(0, 16%, 0) rotateX(32deg) scale(.9); transform-origin: 50% 100%; transition: transform 1.5s var(--out); }
  .show__panel.is-on, .show__panel.is-past { transform: none; }
  .show__panel.is-on .device, .show__panel.is-past .device { transform: perspective(1400px) translate3d(0, 0, 0) rotateX(0deg) scale(1); }
  /* (the column stops growing at the page's widest, 510px or so: SecureVault, the longest, is 6.65 of these wide) */
  .pj__name { font-size: clamp(48px, 4.5vw, 74px); }
  /* a phone stands most of the frame's height (its screen is 600 by 1294) */
  .show__panel .shot[data-device="phone"] { width: calc((100vh - var(--top) - 6vh) * .8 * 600 / 1294); }
  .show__count, .show__open { position: absolute; z-index: 9; font: 500 12px var(--mono); text-transform: uppercase; color: rgb(242 237 227 / .86); }
  .show__count { left: 22px; top: 18px; display: flex; gap: .5em; }
  .show__n { display: inline-grid; height: 1.5em; overflow: hidden; }
  .show__n > span { grid-area: 1 / 1; transition: transform .8s var(--out); }
  .show__n > span + span { transform: translateY(100%); }
  .show__open { right: 22px; top: 18px; display: inline-flex; align-items: center; gap: 8px; }
  .show__open::after { content: ""; width: 8px; height: 8px; border-top: 1.5px solid currentColor; border-right: 1.5px solid currentColor; transform: translateY(1px) rotate(45deg); transition: transform .4s var(--out); }
  .show__frame:hover .show__open::after { transform: translate(3px, 1px) rotate(45deg); }
  .show__frame .device { transition-property: transform; }
}

/* everything else, as a table */
.more { margin-top: clamp(100px, 16vh, 180px); position: relative; }
.more__head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 4px 20px; padding-bottom: 16px; }
.more__head .kicker { white-space: nowrap; }
.more__note { color: var(--fg-3); font-size: 14px; }
.more__list { border-top: 1.5px solid var(--fg); }
.more__row { scroll-margin-top: 38vh; position: relative; display: grid; grid-template-columns: 52px minmax(0, 1fr) minmax(0, 1.25fr) 200px 76px; gap: 20px; align-items: center; padding: 22px 0; border-bottom: 1px solid var(--rule-2); transition: color .35s; isolation: isolate; }
/* pointed at, a row turns over to ink */
.more__row::before { content: ""; position: absolute; inset: -1px calc(var(--gutter) * -.4); z-index: -1; background: var(--ink); transform: scaleY(0); transform-origin: 50% 100%; transition: transform .45s var(--out); }
.more__row.is-going, .more__row.is-going::before, .more__row.is-going * { transition: none !important; }
.more__row.is-going::before { transform: scaleY(0) !important; }
.more__row.is-going :is(.more__name, .more__no, .more__desc, .more__kind, .more__go) { color: var(--fg) !important; }
.more__row.is-going .more__desc { color: var(--fg-2) !important; }
@media (hover: hover) {
  .more__row:not(.is-going):hover { color: var(--paper); }
  .more__row:hover::before { transform: none; }
  .more__row:hover :is(.more__no, .more__desc, .more__kind, .more__go) { color: inherit; }
}
.more__no { font: 500 12px var(--mono); color: var(--fg-3); }
.more__name { justify-self: start; text-decoration: none; font: 800 clamp(22px, 2.2vw, 34px)/1.05 var(--sans); letter-spacing: -.035em; text-transform: uppercase; color: inherit; }
.more__desc { color: var(--fg-2); font-size: 15px; transition: color .35s; }
.more__kind { font: 500 12px var(--mono); text-transform: uppercase; color: var(--fg-3); transition: color .35s; }
.more__go { justify-self: end; color: inherit; }
.more__retired { font: 500 12px var(--mono); text-transform: uppercase; color: var(--fg-3); }
.more__peek { position: fixed; left: 0; top: 0; z-index: 50; width: 300px; pointer-events: none; opacity: 0; visibility: hidden; }
.more__peek .device { box-shadow: 0 30px 70px -20px rgb(0 0 0 / .55); }
.more__peek .device--phone { width: 150px; margin: 0 auto; }
/* (earlier work: a few lines of its source, or how it fits together, in its colour) */
.more__peek.is-panel { width: 380px; }
.more__peek .cs-next__media { display: grid; width: 100%; box-shadow: 0 30px 70px -20px rgb(0 0 0 / .55); animation: none !important; }
/* (how it fits together takes the height it needs) */
.more__peek .cs-next__media--flow { aspect-ratio: auto; padding: 22px 26px; container-type: inline-size; }
/* what came before: the same table, under its own heading */
.more__sub { margin-top: clamp(56px, 9vh, 100px); padding-bottom: 16px; }
.more__list--earlier .more__name { font-size: clamp(20px, 1.9vw, 28px); }

/* app screens: a browser window (its tab strip and toolbar are drawn with it), or in a phone */
.shot { position: relative; }
.device { position: relative; border-radius: 10px; overflow: hidden; background: #1E1F24; box-shadow: 0 40px 80px -36px rgb(0 0 0 / .7), 0 0 0 1px rgb(255 255 255 / .07); }
.device img { width: 100%; height: auto; }
.device--phone { padding: 7px; border-radius: 26px; background: #121214; }
.device--phone img { border-radius: 20px; }

/* the featured apps, live (live.js): an SVG on the grid the screen was drawn on, over the picture */
.live { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.device--phone .live { inset: 7px; border-radius: 20px; overflow: hidden; }
.live svg { display: block; width: 100%; height: 100%; overflow: hidden; }

/* SecureVault, working: its calculator laid over the drawn screen (kit.js), and the vault behind it, both as in the app */
.calc { position: absolute; inset: 7px; z-index: 2; border-radius: 20px; overflow: hidden; container-type: inline-size; pointer-events: none; }
.calc__screen { position: absolute; left: 3.04%; right: 3.04%; top: 14.07%; height: 18.87%; display: flex; flex-direction: column; justify-content: flex-end; align-items: flex-end; gap: .7cqw; padding: 0 5.71cqw 0 0; background: #F4F2FA; color: #1B1B21; font-family: var(--sans); white-space: nowrap; }
.calc__expr { font: 400 6.07cqw/1 var(--sans); color: #77767D; }
.calc__val { font: 600 13.39cqw/.86 var(--sans); letter-spacing: -.013em; }
.calc__key { position: absolute; padding: 0; border: 0; border-radius: 7.86cqw; background: transparent; color: transparent; font-size: 0; cursor: pointer; pointer-events: auto; transition: background-color .3s; -webkit-tap-highlight-color: transparent; }
.calc__key.is-sci { border-radius: 4.82cqw; }
.calc__key.is-down { background: rgb(27 27 33 / .1); transition: none; }
.calc__key.is-eq.is-down { background: rgb(255 255 255 / .2); }
.vault { position: absolute; inset: 7.7% 0 0; z-index: 1; display: flex; flex-direction: column; padding-top: 3.3cqw; background: #F8F7FF; color: #1B1B21; font-family: var(--sans); clip-path: circle(0% at 86.07% 88.03%); pointer-events: auto; }
.calc:not(.is-open) .vault { pointer-events: none; }
.vault::after { content: ""; position: absolute; left: 36.96%; bottom: 3.05%; width: 26.08%; height: .9cqw; border-radius: 1cqw; background: #646368; }
.vault__bar { display: flex; justify-content: space-between; padding: 0 1.8cqw; }
.vault__back, .vault__more { position: relative; width: 11.6cqw; height: 11.6cqw; padding: 0; border: 0; border-radius: 50%; background: #EEEDF4; }
.vault__back { cursor: pointer; }
.vault__back::before { content: ""; position: absolute; left: 3.7cqw; top: 50%; width: 4.6cqw; height: .6cqw; margin-top: -.3cqw; border-radius: .3cqw; background: #1B1B21; }
.vault__back::after { content: ""; position: absolute; left: 3.9cqw; top: 50%; width: 2.6cqw; height: 2.6cqw; border: solid #1B1B21; border-width: 0 0 .6cqw .6cqw; border-radius: .2cqw; transform: translate(0, -50%) rotate(45deg); transform-origin: 0 50%; }
.vault__more::before { content: ""; position: absolute; left: 50%; top: 50%; width: 1.1cqw; height: 1.1cqw; margin: -.55cqw 0 0 -.55cqw; border-radius: 50%; background: #1B1B21; box-shadow: 0 -1.7cqw #1B1B21, 0 1.7cqw #1B1B21; }
.vault__title { margin: 4cqw 0 0 5cqw; font: 800 8cqw/1.1 var(--sans); letter-spacing: -.02em; }
.vault__sub { margin: .6cqw 0 0 5cqw; font: 500 3.4cqw/1.2 var(--sans); color: #4A5F94; }
.vault__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 4cqw; margin: 13cqw 7.8cqw 0; }
.vault__grid span { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1.6cqw; aspect-ratio: 1; padding: 2cqw; border-radius: 4.6cqw; background: #F1F0F7; box-shadow: 0 .5cqw 1.4cqw rgb(27 27 33 / .16); font: 500 3.6cqw/1.25 var(--sans); text-align: center; }
/* (a file: lines on pink, locked; a folder: on blue) */
.vault__grid i { position: relative; flex-shrink: 0; width: 15.6cqw; height: 15.6cqw; margin-bottom: 2cqw; border-radius: 3.6cqw;
  background: linear-gradient(#7B4E6E 0 0) 50% 36% / 5.6cqw .7cqw no-repeat, linear-gradient(#7B4E6E 0 0) 50% 50% / 5.6cqw .7cqw no-repeat, linear-gradient(#7B4E6E 0 0) 41% 64% / 3.8cqw .7cqw no-repeat, #FADBEF; }
.vault__grid i::after { content: ""; position: absolute; left: 50%; bottom: -2.5cqw; width: 5cqw; height: 5cqw; margin-left: -2.5cqw; border-radius: 50%; background: radial-gradient(circle at 50% 58%, #fff 0 .8cqw, transparent .9cqw), #465D92; }
.vault__grid .is-folder i { background: #D9E2FF; }
.vault__grid .is-folder i::before { content: ""; position: absolute; left: 50%; top: 50%; width: 7.6cqw; height: 5.6cqw; border-radius: .9cqw; background: #46598F; transform: translate(-50%, -40%); }
.vault__grid .is-folder i::after { left: 3.9cqw; bottom: auto; top: 4.5cqw; width: 3.4cqw; height: 2cqw; margin: 0; border-radius: .6cqw .6cqw 0 0; background: #46598F; }
.vault__grid em { padding: .7cqw 1.5cqw; border-radius: 1.4cqw; background: #E5E4EB; font: 500 2.7cqw/1 var(--sans); font-style: normal; color: #46464F; }
.vault__grid .is-folder em { width: 16cqw; height: 2.8cqw; padding: 0; background: #C5C9DA; }
.vault__fab { position: absolute; right: 3.8cqw; bottom: 10cqw; width: 13.6cqw; height: 13.6cqw; border-radius: 3.6cqw; background: linear-gradient(#2D3963 0 0) 50% 50% / 4cqw .55cqw no-repeat, linear-gradient(#2D3963 0 0) 50% 50% / .55cqw 4cqw no-repeat, #D9E2FF; box-shadow: 0 .8cqw 1.8cqw rgb(27 27 33 / .2); }
.calc-hint { position: absolute; left: 0; right: 0; bottom: 18px; z-index: 3; margin: 0; text-align: center; font: 500 11.5px var(--mono); text-transform: uppercase; color: rgb(242 237 227 / .82); pointer-events: none; }

/* =========================================================
   In numbers
   ========================================================= */
.nums { padding: clamp(84px, 13vh, 150px) 0; }
.nums__grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin-top: clamp(56px, 10vh, 120px); }
.stat { display: flex; flex-direction: column; padding: 4px clamp(16px, 1.8vw, 28px) 0; border-left: 1px solid var(--rule-2); }
.stat__num { display: flex; font: 800 clamp(60px, 6.2vw, 104px)/1.06 var(--sans); letter-spacing: -.04em; font-variant-numeric: tabular-nums lining-nums; }
/* a split-flap board: each digit on two halves of a tile, with a flap for each half that turns (main.js) */
.flaps { display: inline-flex; gap: .07em; }
.flap { position: relative; display: inline-block; width: .74em; height: 1.06em; perspective: 3em; }
.flap::after { content: ""; position: absolute; left: 0; right: 0; top: 50%; z-index: 4; height: .03em; margin-top: -.015em; background: #070706; }
.flap__half { position: absolute; left: 0; right: 0; height: 50%; overflow: hidden; color: var(--paper); backface-visibility: hidden; -webkit-backface-visibility: hidden; }
.flap__top { top: 0; border-radius: .07em .07em 0 0; transform-origin: 50% 100%; background: linear-gradient(#272521, #1D1B18); }
.flap__bot { bottom: 0; border-radius: 0 0 .07em .07em; transform-origin: 50% 0; background: linear-gradient(#1A1815, #23211D); }
.flap__half b { position: absolute; left: 0; right: 0; height: 200%; display: flex; align-items: center; justify-content: center; font-weight: 800; }
.flap__top b { top: 0; }
.flap__bot b { bottom: 0; }
.flap__rise { transform: rotateX(90deg); }
.odo { display: inline-block; height: 1em; overflow: hidden; }
.odo > span { display: flex; flex-direction: column; will-change: transform; }
.odo > span > span { height: 1em; }
.stat h3 { margin-top: clamp(22px, 3.4vh, 36px); font: 800 14.5px var(--sans); text-transform: uppercase; }
/* what each number is, drawn: my nine apps, eighteen ticks (five launches, thirteen updates), the 403 cricketers as a 31
   by 13 grid, and a sign-up form, struck out */
.fig { display: flex; align-items: flex-end; height: 78px; margin-top: 22px; }
.fig--icons { display: grid; grid-template-columns: repeat(5, 34px); gap: 6px; align-content: end; }
.fig--icons img { width: 34px; height: 34px; border-radius: 9px; }
.fig--ticks { gap: 3px; }
.fig--ticks i { width: 5px; height: 40px; background: currentColor; opacity: .5; transform-origin: 50% 100%; }
.fig--ticks i.is-launch { height: 72px; opacity: 1; background: var(--red); }
.fig--ticks span { align-self: center; margin-left: 12px; font: 500 11px/1.4 var(--mono); text-transform: uppercase; color: var(--fg-3); }
.fig--ticks b { font-weight: 500; color: var(--red); }
.fig--dots { width: 186px; height: 78px; }
html:not(.js) .fig--dots { visibility: hidden; }
.fig--form { width: 205px; height: 78px; overflow: visible; }
.fig--form .f-box, .fig--form .f-in { fill: none; stroke: var(--fg-3); stroke-width: 1.2; }
.fig--form .f-btn { fill: var(--fg-3); }
.fig--form text { font: 500 9px var(--mono); text-transform: uppercase; fill: var(--fg-3); }
.fig--form .f-x { fill: none; stroke: var(--red); stroke-width: 3.5; stroke-linecap: round; stroke-dasharray: 170; }
.stat p:not(.stat__num) { margin-top: 10px; color: var(--fg-2); font-size: 15px; text-wrap: pretty; }
.stat cite { margin-top: auto; padding-top: 24px; font: 500 11.5px var(--mono); font-style: normal; text-transform: uppercase; color: var(--fg-3); }

/* =========================================================
   About
   ========================================================= */
.about { padding: clamp(84px, 13vh, 150px) 0; }
.about__grid { display: grid; grid-template-columns: minmax(0, 4fr) minmax(0, 7fr); gap: clamp(32px, 5vw, 96px); align-items: start; margin-top: clamp(44px, 8vh, 96px); }
.about__photo { max-width: 440px; }
.about__photo img { width: 100%; aspect-ratio: 4 / 5; object-fit: cover; object-position: 48% 30%; border-radius: 6px; }
.about__big { font: 700 clamp(28px, 3.1vw, 50px)/1.08 var(--sans); letter-spacing: -.032em; text-wrap: pretty; }
.about__story { max-width: 34em; margin-top: 30px; font-size: 17.5px; line-height: 1.6; color: var(--fg-2); }
.about__facts { max-width: 40em; margin-top: 44px; border-top: 1.5px solid var(--fg); }
.about__facts div { display: grid; grid-template-columns: 7rem minmax(0, 1fr); gap: 16px; padding: 15px 0; border-bottom: 1px solid var(--rule-2); }
.about__facts dt { font: 500 12px/1.75 var(--mono); text-transform: uppercase; }
.about__facts dd { font-size: 16px; color: var(--fg-2); }
.about__facts a { color: var(--fg); }

/* how I work: four habits as tabs down the left (in the photo's column), and on the right what each one means and the
   lines of my code that show it. The panels share one cell, so changing tab never moves the page (main.js) */
.ways { margin-top: clamp(72px, 12vh, 140px); }
.ways__head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px 24px; flex-wrap: wrap; padding-bottom: 16px; border-bottom: 1.5px solid var(--fg); }
.ways__k { font: 800 clamp(26px, 2.6vw, 40px)/1 var(--sans); letter-spacing: -.04em; text-transform: uppercase; }
.ways__aside { font: 500 12px/1.5 var(--mono); text-transform: uppercase; color: var(--fg-3); }
.ways__body { display: grid; grid-template-columns: minmax(0, 4fr) minmax(0, 7fr); gap: 28px clamp(32px, 5vw, 96px); align-items: start; margin-top: clamp(8px, 1.5vh, 16px); }
.ways__tab { position: relative; display: grid; grid-template-columns: 2.6rem minmax(0, 1fr); gap: 6px 8px; width: 100%; padding: 22px 0 23px; border: 0; border-bottom: 1px solid var(--rule-2); border-radius: 0; background: none; color: var(--fg-3); font: inherit; text-align: left; cursor: pointer; transition: color .3s; }
.ways__tab::after { content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px; background: var(--red); transform: scaleX(0); transform-origin: 0 50%; transition: transform .6s var(--out); }
.ways__n { grid-row: 1 / 3; padding-top: .55em; font: 500 12px/1 var(--mono); transition: color .3s; }
.ways__t { font: 800 clamp(20px, 1.75vw, 28px)/1.12 var(--sans); letter-spacing: -.03em; text-wrap: balance; }
.ways__in { font: 500 11.5px/1.4 var(--mono); letter-spacing: .02em; text-transform: uppercase; }
.ways__tab.is-on { color: var(--fg); }
.ways__tab.is-on .ways__n { color: var(--red); }
.ways__tab.is-on::after { transform: none; }
.ways__tab:focus-visible { outline-offset: 4px; }
@media (hover: hover) { .ways__tab:hover { color: var(--fg); } }
.ways__panels { display: grid; grid-template-columns: minmax(0, 1fr); padding-top: 22px; }
.ways__panel { min-width: 0; }
.ways__h { margin-bottom: 10px; font: 800 22px/1.15 var(--sans); letter-spacing: -.025em; }
.ways__text { max-width: 38em; font-size: 17.5px; line-height: 1.55; color: var(--fg-2); text-wrap: pretty; }
.ways__panel .codewin--way { margin-top: 28px; --code: clamp(12px, .92vw, 13.5px); }
.ways__src { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 10px 24px; margin-top: 16px; }
.ways__src span { font: 500 12px/1.5 var(--mono); text-transform: uppercase; color: var(--fg-3); overflow-wrap: anywhere; }
.ways__panel:focus-visible { outline-offset: 6px; }
/* (with the script: one panel at a time, crossfading, its code settling up into place; without it, all four, one under another) */
.js .ways__h { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.js .ways__panel { grid-area: 1 / 1; opacity: 0; visibility: hidden; transition: opacity .12s, visibility 0s .12s; }
.js .ways__panel.is-on { opacity: 1; visibility: visible; transition: opacity .3s .1s; }
.js .ways__panel .codewin { transform: translateY(12px); transition: transform .7s var(--out); }
.js .ways__panel.is-on .codewin { transform: none; }
html:not(.js) .ways__tabs { display: none; }
html:not(.js) .ways__body { grid-template-columns: minmax(0, 1fr); }
html:not(.js) .ways__panels { gap: 56px; }

/* my photo, printed in: it comes up as a halftone screen whose dots grow until they meet, and then it's the photo itself,
   as it is (main.js) */
@property --dot { syntax: "<length>"; inherits: false; initial-value: 0px; }
.about__photo img.is-printing {
  -webkit-mask: radial-gradient(circle, #000 calc(var(--dot) - .8px), transparent var(--dot)) 0 0 / 9px 9px, radial-gradient(circle, #000 calc(var(--dot) - .8px), transparent var(--dot)) 4.5px 4.5px / 9px 9px;
          mask: radial-gradient(circle, #000 calc(var(--dot) - .8px), transparent var(--dot)) 0 0 / 9px 9px, radial-gradient(circle, #000 calc(var(--dot) - .8px), transparent var(--dot)) 4.5px 4.5px / 9px 9px; }

/* =========================================================
   Experience: the line so far, on Hyderabad Metro's Blue Line
   ========================================================= */
.xp { padding: clamp(84px, 13vh, 150px) 0 clamp(100px, 15vh, 180px); }
.xp .title { margin-top: clamp(40px, 7vh, 84px); margin-bottom: clamp(48px, 8vh, 96px); }
.xp__body { display: grid; grid-template-columns: minmax(0, 300px) minmax(0, 1fr); gap: clamp(32px, 4vw, 72px); align-items: start; }
/* the metro's next-station board, in amber dots: it changes as the train reaches each stop (main.js) */
.board { position: sticky; top: calc(var(--top) + 28px); padding: 16px 18px 14px; border-radius: 8px; background: #060605; box-shadow: inset 0 0 0 1px rgb(255 255 255 / .07), 0 24px 60px -24px rgb(0 0 0 / .9); }
.board__k { display: flex; justify-content: space-between; gap: 12px; font: 500 11px var(--mono); text-transform: uppercase; color: rgb(255 176 64 / .62); }
.board__name { height: 2.3em; margin: 14px 0 12px; overflow: hidden; font: 500 25px/1.15 var(--mono); text-transform: uppercase; color: #FFB040; text-shadow: 0 0 9px rgb(255 150 30 / .6);
  -webkit-mask: radial-gradient(circle at 50% 50%, #000 0 1.05px, transparent 1.35px) 0 0 / 2.8px 2.8px; mask: radial-gradient(circle at 50% 50%, #000 0 1.05px, transparent 1.35px) 0 0 / 2.8px 2.8px; }
.board__led { display: block; }
.board__sub { color: rgb(255 176 64 / .5); }
.line { --p: 0; position: relative; display: grid; grid-template-columns: 34px minmax(0, 1fr); }
.line__rail { position: relative; grid-column: 1; grid-row: 1; justify-self: start; width: 2px; margin: 36px 0 44px; background: var(--rule-2); }
.line__fill { position: absolute; inset: 0 0 auto 0; height: calc(var(--p) * 100%); background: var(--metro); }
.line__train { position: absolute; left: 50%; top: calc(var(--p) * 100%); width: 10px; height: 38px; margin: -38px 0 0 -5px; border-radius: 5px 5px 3px 3px; box-shadow: 0 0 0 3px var(--bg);
  background: linear-gradient(var(--metro) 0 31%, var(--bg) 31% 35%, var(--metro) 35% 65%, var(--bg) 65% 69%, var(--metro) 69%); }
.stops { grid-column: 2; grid-row: 1; border-top: 1.5px solid var(--fg); }
.stop { position: relative; display: grid; grid-template-columns: 180px minmax(0, 1fr) minmax(0, 1fr); gap: 12px 32px; padding: 30px 0; border-bottom: 1px solid var(--rule-2); }
.stop::before { content: ""; position: absolute; left: -38px; top: 38px; width: 10px; height: 10px; border-radius: 50%; background: var(--bg); box-shadow: inset 0 0 0 2px var(--rule-2); transition: box-shadow .4s; }
.stop.is-reached::before { box-shadow: inset 0 0 0 2px var(--metro); }
/* (the train arrives: the stop's date lights up in the line's colour, and a ring goes out from its dot) */
.stop__when { transition: color .5s; }
.stop.is-reached .stop__when { color: var(--metro); }
.stop::after { content: ""; position: absolute; left: -38px; top: 38px; width: 10px; height: 10px; border-radius: 50%; box-shadow: 0 0 0 2px var(--metro); opacity: 0; pointer-events: none; }
.stop.is-reached::after { animation: stop-ring .9s var(--out); }
@keyframes stop-ring { from { opacity: .9; transform: scale(1); } to { opacity: 0; transform: scale(3.4); } }
.stop__when { padding-top: 8px; font: 500 12.5px var(--mono); text-transform: uppercase; color: var(--fg-3); }
.stop__name { font: 800 clamp(22px, 2.2vw, 32px)/1.08 var(--sans); letter-spacing: -.03em; text-transform: uppercase; }
.stop__where { margin-top: 8px; color: var(--fg-3); font-size: 14.5px; }
.stop__what { color: var(--fg-2); font-size: 15.5px; }
.stop__stack { margin-top: 10px; font: 500 12px var(--mono); color: var(--fg-3); }
.stop__link { margin-top: 14px; }
.stop--next .stop__name { color: var(--red); }
.stop--next .stop__when { color: var(--metro); }
/* certificates: on the line's grid, under the last stop (the label where the dates are) */
.also { grid-column: 2; display: grid; grid-template-columns: 180px minmax(0, 1fr) minmax(0, 1fr); gap: 14px 32px; margin-left: 34px; padding-top: 30px; }
.also__k { padding-top: 3px; font: 500 12.5px var(--mono); text-transform: uppercase; color: var(--fg-3); }
.also__name { font: 700 18px/1.3 var(--sans); letter-spacing: -.01em; }
.also__sub { margin-top: 6px; font: 500 12px var(--mono); text-transform: uppercase; color: var(--fg-3); }

/* =========================================================
   Contact: the poster again, with my email as its name
   ========================================================= */
.contact { padding: clamp(84px, 13vh, 150px) 0 clamp(64px, 10vh, 110px); overflow-x: clip; }
.contact__q { max-width: 15em; margin-top: clamp(48px, 9vh, 110px); font: 700 clamp(30px, 3.6vw, 60px)/1.04 var(--sans); letter-spacing: -.035em; text-wrap: balance; }
.contact__mail { position: relative; display: block; width: 100%; margin-top: clamp(28px, 5vh, 60px); padding: 0; border: 0; background: none; text-align: left; cursor: pointer; color: inherit; }
.contact__addr { display: block; width: max-content; font: 800 calc((100vw - 2 * var(--gutter)) / 10.65)/.92 var(--sans); letter-spacing: -.055em; white-space: nowrap; margin-left: -.03em;
  background: linear-gradient(currentColor, currentColor) 0 100% / 0 .055em no-repeat; padding-bottom: .06em; transition: background-size .7s var(--out); }
.contact__mail:hover .contact__addr, .contact__mail:focus-visible .contact__addr { background-size: 100% .055em; }
.contact__hint { position: absolute; right: 0; top: -2em; font: 500 12px var(--mono); text-transform: uppercase; opacity: 0; transform: translateY(6px); transition: opacity .3s, transform .5s var(--out); }
.contact__mail:hover .contact__hint { opacity: 1; transform: none; }
/* copied: a rubber stamp comes down on the poster */
.stamp { position: absolute; right: 1%; bottom: calc(100% + .2em); z-index: 2; padding: .16em .45em .1em; border: .085em solid currentColor; border-radius: .12em; font: 800 clamp(26px, 3.6vw, 56px)/1 var(--sans); letter-spacing: .03em; text-transform: uppercase; color: var(--ink); opacity: 0; pointer-events: none; transform: rotate(-8deg) scale(1.7);
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.55' numOctaves='3' seed='4'/><feColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 -2.4 2.15'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
          mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.55' numOctaves='3' seed='4'/><feColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 -2.4 2.15'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>"); }
.stamp.is-on { animation: stamp 2.4s var(--out) forwards; }
.contact__mail:has(.stamp.is-on) .contact__hint { opacity: 0; }
@keyframes stamp {
  0% { opacity: 0; transform: rotate(-8deg) scale(1.7); }
  12% { opacity: .94; transform: rotate(-6deg) scale(1); }
  80% { opacity: .94; transform: rotate(-6deg) scale(1); }
  100% { opacity: 0; transform: rotate(-6deg) scale(1); }
}
.contact__row { position: relative; display: flex; justify-content: space-between; align-items: flex-end; gap: 24px 40px; margin-top: clamp(44px, 8vh, 90px); padding-top: 18px; }
.contact__row::before { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1.5px; background: currentColor; }
.contact__lede { max-width: 30em; font-size: 17px; color: var(--fg-2); text-wrap: pretty; }
.contact__cta { display: flex; flex-wrap: wrap; gap: 10px; }

/* =========================================================
   Footer
   ========================================================= */
.foot { padding: clamp(64px, 10vh, 110px) 0 calc(28px + env(safe-area-inset-bottom)); overflow-x: clip; }
.foot__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)) minmax(0, 1.4fr); gap: 40px clamp(32px, 5vw, 80px); align-items: start; }
@media (min-width: 901px) { .foot__grid:has(> .cord) { grid-template-columns: repeat(3, minmax(0, 1fr)) minmax(0, 1.4fr) 150px; } }
/* the pull cord: from a plate at the footer's top edge, a cord and a bead on it (main.js pulls it about) */
.cord { position: relative; height: 262px; margin-top: calc(-1 * clamp(64px, 10vh, 110px)); }
.cord__plate { position: absolute; left: 50%; top: 0; width: 38px; height: 9px; margin-left: -19px; border-radius: 0 0 5px 5px; background: linear-gradient(90deg, #6B675F, #EDEAE3 35%, #A39E94 60%, #5C5850); }
.cord__plate::after { content: ""; position: absolute; left: 50%; top: 2px; width: 5px; height: 5px; margin-left: -2.5px; border-radius: 50%; background: #2A2723; transition: background-color .2s, box-shadow .2s; }
.cord.is-on .cord__plate::after { background: var(--red); box-shadow: 0 0 8px var(--red); }
.cord__line { position: absolute; left: 50%; top: 7px; width: 2px; height: 120px; margin-left: -1px; background: var(--fg-2); transform-origin: 50% 0; }
.cord__bead { position: absolute; left: 50%; top: 7px; width: 22px; height: 34px; margin: 0 0 0 -11px; padding: 0; border: 0; border-radius: 11px; cursor: grab; touch-action: none; transform: translate3d(0, 120px, 0);
  background: radial-gradient(circle at 35% 30%, #FF8A65, var(--red) 45%, #A9300F); box-shadow: 0 6px 14px -4px rgb(0 0 0 / .6); }
.cord__bead:active { cursor: grabbing; }
.cord__label { position: absolute; left: 50%; top: 238px; width: max-content; transform: translateX(-50%); white-space: nowrap; font: 500 11px var(--mono); text-transform: uppercase; color: var(--fg-3); pointer-events: none; transition: opacity .25s; }
.cord.is-pulling:not(.is-on) .cord__label { opacity: 0; }
.cord.is-on .cord__label { color: var(--red); }
.foot__col { display: grid; align-content: start; gap: 4px; }
.foot__col .kicker { margin-bottom: 10px; color: var(--fg-3); }
.foot__col a { width: fit-content; font: 700 clamp(20px, 1.7vw, 26px)/1.3 var(--sans); letter-spacing: -.02em; text-decoration: none; }
.foot__colophon p:not(.kicker) { max-width: 26em; font-size: 15.5px; color: var(--fg-2); }
.foot__base { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px 32px; margin-top: clamp(64px, 11vh, 120px); padding-top: 18px; border-top: 1px solid var(--rule-2); font: 500 12px var(--mono); text-transform: uppercase; color: var(--fg-3); }
.foot__base kbd { color: var(--fg-2); }
.foot__up { text-decoration: none; color: var(--fg-2); }
.foot__up:hover { color: var(--fg); }

/* =========================================================
   Dialogs: the short version, the command menu, and the menu on a narrow screen
   ========================================================= */
dialog { color: var(--fg); }
.short::backdrop, .palette::backdrop { background: rgb(18 17 16 / .55); }
.link { display: inline; padding: 0; border: 0; background: none; cursor: pointer; font: 700 14.5px var(--sans); color: inherit; text-decoration: underline; text-decoration-color: var(--rule-2); text-underline-offset: 4px; transition: text-decoration-color .2s; }
.link:hover { text-decoration-color: currentColor; }

.short { margin: auto; padding: 0; width: min(720px, calc(100vw - 24px)); max-width: none; max-height: calc(100dvh - 24px); border: 0; background: transparent; overflow: visible; }
.short[open] { animation: rise .55s var(--out); }
@keyframes rise { from { transform: translateY(18px) scale(.98); opacity: 0; } }
.short__card { max-height: calc(100dvh - 24px); overflow-y: auto; overscroll-behavior: contain; padding: clamp(20px, 4vw, 34px); border-radius: 12px; box-shadow: 0 40px 100px rgb(0 0 0 / .3);
  /* a letter, folded in three to come to you: it keeps its creases */
  background: linear-gradient(transparent calc(100% / 3 - 1px), rgb(18 17 16 / .06) calc(100% / 3 - 1px) calc(100% / 3), rgb(255 255 255 / .6) calc(100% / 3) calc(100% / 3 + 1px), transparent calc(100% / 3 + 1px) calc(200% / 3 - 1px), rgb(18 17 16 / .06) calc(200% / 3 - 1px) calc(200% / 3), rgb(255 255 255 / .6) calc(200% / 3) calc(200% / 3 + 1px), transparent calc(200% / 3 + 1px)), var(--paper);
  /* (the creases are the paper's, so they go with it as it scrolls) */
  background-attachment: local; }
/* ...and it comes up folded, and opens out, the outer third first; closed, it folds back up and goes (main.js: copies of it
   do the folding, a third each, and then the letter itself is shown) */
.short.is-letter[open] { animation: none; }
.short[open]::backdrop { animation: backdrop-in .45s ease both; }
.short.is-closing::backdrop { animation: backdrop-out .4s .3s ease both; }
.short.is-folding > .short__card { opacity: 0; }
.short.is-closing > .short__card { pointer-events: none; }
@keyframes backdrop-in { from { opacity: 0; } }
@keyframes backdrop-out { to { opacity: 0; } }
.fold { position: absolute; inset: 0; z-index: 5; perspective: 1800px; pointer-events: none; }
.fold__shadow { position: absolute; left: 0; right: 0; border-radius: 12px; box-shadow: 0 40px 100px rgb(0 0 0 / .3); }
.fold__p { position: absolute; left: 0; right: 0; transform-style: preserve-3d; }
.fold__face { position: absolute; inset: 0; overflow: hidden; backface-visibility: hidden; -webkit-backface-visibility: hidden; }
.fold__face > .short__card { position: absolute; left: 0; width: 100%; max-height: none; box-shadow: none; }
/* (only the face towards you is drawn: main.js says which, as Safari doesn't always leave the far one out) */
.fold__p:not(.is-back) > .fold__back, .fold__p.is-back > .fold__face:not(.fold__back) { visibility: hidden; }
.fold__back { transform: rotateX(180deg); background: linear-gradient(#EAE4D7, #E2DBCC); }
.fold__p:last-child .fold__back { border-radius: 0 0 12px 12px; }
.fold__p:nth-child(3) .fold__back { border-radius: 12px 12px 0 0; }
.fold__shade { position: absolute; inset: 0; background: #2A2620; opacity: 0; }
.short__head { display: flex; justify-content: space-between; align-items: center; }
.short__top { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 18px; align-items: center; margin-top: 16px; }
.short__photo { width: 88px; height: 88px; border-radius: 8px; object-fit: cover; object-position: 48% 30%; }
.short__name { font: 800 clamp(28px, 5vw, 40px)/.95 var(--sans); letter-spacing: -.045em; text-transform: uppercase; }
.short__role { margin-top: 8px; color: var(--fg-2); }
.short__facts { margin-top: 22px; border-top: 1.5px solid var(--fg); }
.short__facts > div { display: grid; grid-template-columns: 104px minmax(0, 1fr); gap: 4px 16px; padding: 12px 0; border-bottom: 1px solid var(--rule-2); }
.short__facts dt { padding-top: .2em; font: 500 11.5px var(--mono); text-transform: uppercase; color: var(--fg-3); }
.short__facts dd { font-size: 15.5px; color: var(--fg-2); }
/* (where I've worked, as a timetable: the dates set right) */
.short__xp span { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 0 16px; }
.short__xp span + span { margin-top: 3px; }
.short__xp small { font: 500 11.5px var(--mono); text-transform: uppercase; color: var(--fg-3); }
/* (a laptop's short screen: tighter, so the whole letter, buttons too, is in view) */
@media (min-width: 700px) and (max-height: 840px) {
  .short__card { padding-block: 24px; }
  .short__top { margin-top: 10px; }
  .short__photo { width: 76px; height: 76px; }
  .short__facts { margin-top: 16px; }
  .short__facts > div { padding: 8px 0; }
  .short__facts dd { font-size: 15px; }
  .short__xp span + span { margin-top: 1px; }
  .short__actions { margin-top: 16px; }
}
.short__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 20px; margin-top: 22px; }

.palette { margin: 14vh auto auto; padding: 0; width: min(600px, calc(100vw - 24px)); max-width: none; border: 0; background: transparent; }
.palette[open] { animation: rise .35s var(--out); }
.palette__box { border-radius: 12px; background: var(--paper); box-shadow: 0 30px 80px rgb(0 0 0 / .3); overflow: hidden; }
.palette__field { display: flex; align-items: center; gap: 12px; padding: 14px 18px; border-bottom: 1px solid var(--rule-2); }
.palette__field svg { width: 20px; height: 20px; fill: none; stroke: var(--fg-3); stroke-width: 2; flex-shrink: 0; }
.palette__field input { flex: 1; min-width: 0; border: 0; outline: 0; background: none; color: var(--fg); font: 500 17px var(--sans); }
.palette__list { max-height: min(52vh, 440px); overflow-y: auto; padding: 8px; }
.palette__group { padding: 10px 12px 4px; font: 500 11px var(--mono); text-transform: uppercase; color: var(--fg-3); }
.palette__item { display: flex; justify-content: space-between; gap: 16px; padding: 10px 12px; border-radius: 6px; cursor: pointer; }
.palette__item[aria-selected="true"] { background: var(--ink); color: var(--paper); }
.palette__item[aria-selected="true"] .k { color: rgb(242 237 227 / .6); }
.palette__item .k { font: 500 12px var(--mono); color: var(--fg-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.palette__empty { padding: 16px 12px; color: var(--fg-3); }
.palette__item mark { background: none; color: inherit; text-decoration: underline; text-decoration-color: var(--red); text-decoration-thickness: 2px; text-underline-offset: .2em; }
.palette__foot { display: flex; gap: 18px; padding: 10px 18px; border-top: 1px solid var(--rule-2); font: 500 11.5px var(--mono); color: var(--fg-3); }
.palette__foot kbd { margin-right: 4px; padding: 1px 5px; }
@media (hover: none) { .palette__foot { display: none; } }

.menu::backdrop { background: none; }
.menu { margin: 0; padding: 0; width: 100vw; max-width: none; height: 100dvh; max-height: none; border: 0; background: var(--red); color: var(--ink); }
.menu[open] { animation: drop .55s var(--out); }
/* (with the script, it comes down over the page as a sheet, and goes back up: main.js) */
.menu.is-sheet[open] { animation: none; }
.menu__w { display: flex; align-items: baseline; gap: 14px; }
.menu.is-sheet .menu__nav a { overflow: clip; }
@keyframes drop { from { transform: translateY(-24px); opacity: 0; } }
.menu__in { display: flex; flex-direction: column; height: 100%; padding: 0 var(--gutter) calc(24px + env(safe-area-inset-bottom)); }
.menu__top { display: flex; justify-content: space-between; align-items: center; height: var(--top); }
.menu__top .top__menu { display: inline-block; }
.menu__nav { display: grid; margin-top: 8vh; border-top: 1.5px solid currentColor; }
.menu__nav a { display: flex; align-items: baseline; gap: 14px; padding: 14px 0; border-bottom: 1px solid rgb(18 17 16 / .25); font: 800 clamp(40px, 12vw, 64px)/.95 var(--sans); letter-spacing: -.05em; text-transform: uppercase; text-decoration: none; }
.menu__nav a { transition: opacity .2s; }
.menu__nav a:active { opacity: .55; transition: none; }
.menu__nav span:not(.menu__w) { font: 500 12px var(--mono); letter-spacing: 0; }
/* (how to reach me, at the foot of the menu: the address, the time here, and where else I am) */
.menu__reach { margin-top: auto; padding-top: 28px; }
.menu__k { display: flex; justify-content: space-between; gap: 12px; padding-bottom: 10px; border-bottom: 1px solid rgb(18 17 16 / .25); font: 500 11.5px var(--mono); text-transform: uppercase; }
.menu__mail { display: block; margin-top: 12px; font: 800 clamp(22px, 7vw, 30px)/1.05 var(--sans); letter-spacing: -.04em; text-decoration: none; overflow-wrap: anywhere; }
.menu__links { display: flex; flex-wrap: wrap; gap: 6px 20px; margin-top: 12px; }
.menu__links a { font: 500 12px var(--mono); text-transform: uppercase; text-decoration: underline; text-decoration-color: rgb(18 17 16 / .35); text-underline-offset: 4px; }
.menu__mail:active, .menu__links a:active { opacity: .55; }
.menu__foot { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 22px; }

.toast { position: fixed; left: 50%; bottom: 28px; z-index: 990; padding: 11px 18px; border-radius: 999px; background: var(--ink); color: var(--paper); font: 700 14px var(--sans); transform: translate(-50%, 180%); opacity: 0; transition: transform .45s var(--out), opacity .3s; pointer-events: none; }
.toast.is-on { transform: translate(-50%, 0); opacity: 1; }
.toast.is-quiet { opacity: 0 !important; }

/* going to a section far off: a page turn (main.js). Down, the page you're on sinks back and the next slides up over it;
   up, the page you're on slides down and away off the one under it. The top bar stays where it is */
.top { view-transition-name: top-bar; }
html.vt-cover .top { view-transition-name: none; }
html.vt-page::view-transition-group(root) { animation: none; }
html.vt-page::view-transition-image-pair(root) { isolation: auto; }
/* (the sheet that moves casts a shadow along its leading edge; the one under it sinks back, rounded, into the dark) */
html.vt-down::view-transition-old(root) { border-radius: 18px; animation: page-sink .95s cubic-bezier(.65, 0, .35, 1) both; }
html.vt-down::view-transition-new(root) { box-shadow: 0 -30px 70px -12px rgb(0 0 0 / .5); animation: page-rise .95s cubic-bezier(.65, 0, .35, 1) both; }
html.vt-up::view-transition-old(root) { z-index: 1; box-shadow: 0 -30px 70px -12px rgb(0 0 0 / .5); animation: page-drop .95s cubic-bezier(.65, 0, .35, 1) both; }
html.vt-up::view-transition-new(root) { border-radius: 18px; animation: page-lift .95s cubic-bezier(.65, 0, .35, 1) both; }
html.vt-page::view-transition { background: #070706; }
@keyframes page-sink { to { transform: scale(.86); filter: brightness(.45); } }
@keyframes page-rise { from { transform: translateY(104%); } }
@keyframes page-drop { to { transform: translateY(104%); } }
@keyframes page-lift { from { transform: scale(.86); filter: brightness(.45); } to { border-radius: 0; } }

/* going from page to page of the site (a case study and back): the browser turns the page (a cross-document view
   transition; the pages say which way in pagereveal). Into a case study, from the work or from the one before it, the
   page you're on sinks back into the dark and the case study comes up over it, while the project's panel, its screen and
   its name fly from where they were to where they are on it. Back, the case study slides away down off the page under it */
/* (the pages opt in to these at the very top of each, inline, so it's known before anything else is: build.mjs, index.html) */
html:active-view-transition-type(to-case, next, back, prev)::view-transition-group(root) { animation: none; }
html:active-view-transition-type(to-case, next, back, prev)::view-transition-image-pair(root) { isolation: auto; }
html:active-view-transition-type(to-case, next, back, prev)::view-transition { background: #070706; }
html:active-view-transition-type(to-case, next)::view-transition-old(root) { border-radius: 18px; animation: page-sink 1s cubic-bezier(.65, 0, .35, 1) both; }
html:active-view-transition-type(to-case, next)::view-transition-new(root) { box-shadow: 0 -30px 70px -12px rgb(0 0 0 / .5); animation: page-rise 1s cubic-bezier(.65, 0, .35, 1) both; }
html:active-view-transition-type(back, prev)::view-transition-old(root) { z-index: 1; box-shadow: 0 -30px 70px -12px rgb(0 0 0 / .5); animation: page-drop .95s cubic-bezier(.65, 0, .35, 1) both; }
html:active-view-transition-type(back, prev)::view-transition-new(root) { border-radius: 18px; animation: page-lift .95s cubic-bezier(.65, 0, .35, 1) both; }
/* (what flies is the same thing at both ends, so the one arriving goes the whole way, solid: the panel stretches to its new
   shape, and the screen and the name, which keep theirs, grow or shrink to their new size) */
::view-transition-group(case-media), ::view-transition-group(shot), ::view-transition-group(case-title) { animation-duration: 1s; animation-timing-function: cubic-bezier(.76, 0, .24, 1); }
::view-transition-group(case-media) { z-index: 2; overflow: clip; border-radius: 10px; }
::view-transition-group(case-title) { z-index: 3; }
::view-transition-group(shot) { z-index: 4; }
::view-transition-old(case-media), ::view-transition-old(case-title) { animation: none; opacity: 0; }
::view-transition-new(case-media), ::view-transition-new(shot), ::view-transition-new(case-title) { animation: none; mix-blend-mode: normal; }
::view-transition-new(case-media) { height: 100%; }
/* (the screen leaving stays over the one arriving for a moment, while the one arriving is drawn: they're the same picture) */
::view-transition-old(shot) { z-index: 1; mix-blend-mode: normal; animation: shot-out .14s .06s both; }
@keyframes shot-out { to { opacity: 0; } }
/* (a name the page coming doesn't take, set over two lines there, fades where it was) */
::view-transition-old(case-title):only-child { opacity: 1; mix-blend-mode: normal; animation: shot-out .35s both; }
/* (from the end of one case study, the next one's name is light on ink, and on its page it's ink on paper: it stays light
   over the dark until its page has come up under it, and then it's printed in ink) */
html:active-view-transition-type(next)::view-transition-old(case-title) { z-index: 1; opacity: 1; mix-blend-mode: normal; animation: shot-out .14s .64s both; }
::view-transition-old(root), ::view-transition-new(root) { animation-duration: .4s; }

/* =========================================================
   A case study on its own page (work/<slug>/, written by build.mjs from cases.mjs): its name as big as the page, its app
   in its own colour beside what it is, the story with an index that follows you down, and the next one at the end
   ========================================================= */
/* (its top bar is a band from the start: the name is big, and goes under it as soon as you scroll) */
.cs-page .top { background: var(--bg); }
.cs-hero { padding: calc(var(--top) + clamp(4px, 1.6vh, 18px)) 0 clamp(56px, 9vh, 110px); }
.cs-hero__in { container-type: inline-size; }
/* the name: its longest line as wide as the page (--k, from build.mjs), unless that's taller than a third of the screen */
.cs-title { margin: clamp(26px, 4.6vh, 60px) 0 0 -.045em; font: 800 min(calc(100cqw * var(--k)), calc(31svh / var(--rows) / .84))/.84 var(--sans); letter-spacing: -.058em; text-transform: uppercase; }
.cs-title__t { display: inline-block; }
/* (each line in a clip, for it to come up from below as the page opens: case.js) */
.cs-title__row { display: block; overflow: clip; padding: .06em .08em .02em 0; margin: -.06em -.08em -.02em 0; white-space: nowrap; }
.cs-title__row > span { display: inline-block; }
/* (a name set over two lines on a phone, on one on a wide screen) */
@media (min-width: 901px) {
  .cs-title--wide { font-size: min(calc(100cqw * var(--k1)), calc(31svh / .84)); }
  .cs-title--wide .cs-title__row, .cs-next__name--wide .cs-next__row { display: inline-block; }
  .cs-title--wide .cs-title__row + .cs-title__row, .cs-next__name--wide .cs-next__row + .cs-next__row { margin-left: .24em; }
  .cs-next__name.cs-next__name--wide { font-size: min(calc(100cqw * var(--k1)), 19vh); }
}
.cs-sub { margin-top: 16px; font: 500 13px var(--mono); text-transform: uppercase; color: var(--fg-2); }
.cs-hero__grid { display: grid; grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); gap: 32px clamp(28px, 4vw, 72px); align-items: start; margin-top: clamp(28px, 5vh, 56px); }
.cs-lede { max-width: 22em; font: 700 clamp(20px, 1.7vw, 27px)/1.2 var(--sans); letter-spacing: -.022em; text-wrap: pretty; }
.cs-spec { margin-top: 28px; }
.cs-spec > div { grid-template-columns: 5.5rem minmax(0, 1fr); }
.cs-links { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 28px; }
/* the app, in its own colour (as in the frame beside the work, which it flies from) */
.cs-media { position: relative; display: grid; place-items: center; aspect-ratio: 16 / 10; padding: 6%; border-radius: 10px; overflow: hidden; container-type: size;
  background: radial-gradient(120% 100% at 28% 12%, color-mix(in srgb, var(--c) 64%, #15130F), color-mix(in srgb, var(--c) 20%, #0E0D0B) 78%); }
.cs-media .shot { width: min(100cqw, calc(100cqh * 1320 / 844)); }
.cs-media .shot[data-device="phone"] { width: calc(100cqh * 600 / 1294 * .9); }
.cs-media .calc-hint { bottom: 10px; }
/* as you read on, the screen in it rises a little faster than the page */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .cs-media .shot { animation: lift linear both; animation-timeline: view(); animation-range: exit 0% exit 100%; }
  }
}
@keyframes lift { to { transform: translate3d(0, -9%, 0); } }

.cs-body { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 8fr); gap: clamp(28px, 4vw, 72px); padding-top: clamp(24px, 4vh, 56px); padding-bottom: clamp(40px, 7vh, 80px); }
/* the index: where you are in it, marked as you read, and how far through you are, along its edge (case.js) */
.cs-index { position: sticky; top: calc(var(--top) + 28px); align-self: start; }
.cs-index ol { --p: 0; position: relative; margin-top: 14px; border-top: 1.5px solid var(--fg); }
.cs-index ol::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 1.5px; background: var(--rule-2); }
.cs-index ol::after { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 1.5px; background: var(--red); transform: scaleY(var(--p)); transform-origin: 50% 0; }
.cs-index a { position: relative; display: grid; grid-template-columns: 2.6em minmax(0, 1fr); padding: 11px 0 11px 16px; border-bottom: 1px solid var(--rule-2); font: 500 12px var(--mono); letter-spacing: .02em; text-transform: uppercase; text-decoration: none; color: var(--fg-3); transition: color .3s; }
.cs-index a::before { content: ""; position: absolute; left: -2.25px; top: 50%; width: 6px; height: 6px; margin-top: -3px; background: var(--red); transform: scale(0); transition: transform .4s var(--out); }
.cs-index a.is-on { color: var(--fg); }
.cs-index a.is-on::before { transform: none; }
@media (hover: hover) { .cs-index a:hover { color: var(--fg); } }
.cs-sec { padding-bottom: clamp(64px, 11vh, 130px); scroll-margin-top: calc(var(--top) + 20px); }
.cs-sec:last-child { padding-bottom: 0; }
.cs-sec > .sh { margin-bottom: clamp(26px, 4.4vh, 44px); }
/* (no heading on these pages ends on a word by itself) */
.cs-page main h3 { text-wrap: balance; }
.cs-statement { max-width: 22em; font: 700 clamp(28px, 3.1vw, 50px)/1.08 var(--sans); letter-spacing: -.034em; text-wrap: pretty; }
.cs-prose p { max-width: 36em; font-size: clamp(17px, 1.25vw, 19px); line-height: 1.6; color: var(--fg-2); text-wrap: pretty; }
.cs-prose p + p { margin-top: 16px; }
.cs-hard, .cs-latest { max-width: 38em; margin-top: 30px; padding: 18px 22px; border-left: 3px solid var(--c); border-radius: 0 8px 8px 0; background: var(--white); font-size: 16.5px; color: var(--fg-2); text-wrap: pretty; }
.cs-hard b, .cs-latest b { display: block; margin-bottom: 6px; font: 500 12px var(--mono); text-transform: uppercase; color: var(--accent); }
.cs-latest { margin-top: 0; font-size: clamp(17px, 1.3vw, 20px); color: var(--fg); }
.cs-latest b { color: var(--fg-2); }
.cs-decisions { border-top: 1.5px solid var(--fg); }
.cs-decisions li { display: grid; grid-template-columns: 4.5em minmax(0, 1fr); gap: 16px; padding: clamp(22px, 3.6vh, 32px) 0; border-bottom: 1px solid var(--rule-2); }
.cs-decisions__n { padding-top: .55em; font: 500 12px var(--mono); color: var(--fg-3); }
.cs-decisions h3 { max-width: 24em; font: 800 clamp(20px, 1.8vw, 28px)/1.15 var(--sans); letter-spacing: -.022em; text-wrap: balance; }
.cs-decisions p { max-width: 36em; margin-top: 10px; font-size: 17px; color: var(--fg-2); text-wrap: pretty; }
.cs-trade { display: grid; grid-template-columns: 6.5rem minmax(0, 1fr); gap: 12px; margin-top: 16px !important; padding-top: 14px; border-top: 1px dashed var(--rule-2); font-size: 15.5px !important; color: var(--fg-3) !important; }
.cs-trade b { font: 500 12px/1.9 var(--mono); text-transform: uppercase; color: var(--accent); }
.cs-learned { border-top: 1.5px solid var(--fg); }
.cs-learned li { position: relative; max-width: 30em; padding: 18px 0 18px 34px; border-bottom: 1px solid var(--rule-2); font: 600 clamp(18px, 1.5vw, 23px)/1.35 var(--sans); letter-spacing: -.012em; text-wrap: pretty; }
.cs-learned li::before { content: ""; position: absolute; left: 0; top: calc(18px + .67em); width: 18px; height: 2px; background: var(--accent); }
.cs-stack { display: flex; flex-wrap: wrap; gap: 8px; }
.cs-stack span { padding: 8px 14px; border: 1.5px solid var(--rule-2); border-radius: 999px; font: 500 12.5px var(--mono); }
.cs-note { margin-top: 18px; color: var(--fg-3); }

.cs-privacy { max-width: 38em; margin-top: 26px; font-size: 15.5px; color: var(--fg-2); text-wrap: pretty; }
.cs-privacy b { display: block; margin-bottom: 4px; font: 500 12px var(--mono); text-transform: uppercase; color: var(--fg-3); }
@media (hover: hover) { .hint-touch { display: none; } }

/* How it works: the steps, and on a wide screen their screens in a frame that stays beside them, each coming up over
   the one before as you reach its step (case.js says which is on), and going back down as you scroll back */
.cs-how { padding: clamp(36px, 6vh, 72px) 0 clamp(40px, 7vh, 90px); }
.cs-how .sh { margin-bottom: clamp(18px, 3vh, 34px); }
.cs-how__steps { border-top: 1.5px solid var(--fg); }
.cs-step { display: grid; grid-template-columns: 4.5em minmax(0, 1fr); gap: 0 16px; padding: clamp(22px, 3.6vh, 32px) 0; border-bottom: 1px solid var(--rule-2); }
.cs-step > * { grid-column: 2; }
.cs-step__n { grid-column: 1; grid-row: 1 / 4; padding-top: .8em; font: 500 12px var(--mono); color: var(--fg-3); }
.cs-step h3 { font: 800 clamp(26px, 2.6vw, 44px)/1.05 var(--sans); letter-spacing: -.035em; text-wrap: balance; }
.cs-step p { max-width: 26em; margin-top: 12px; font-size: clamp(16.5px, 1.2vw, 19px); color: var(--fg-2); text-wrap: pretty; }
/* (its shape is known before it's in, so nothing moves as it loads: as wide as the column, unless that's taller than 70% of
   the screen) */
.cs-step__zoom { justify-self: start; width: min(100%, calc(min(70svh, 600px) * var(--r))); aspect-ratio: var(--r); margin-top: 22px; }
.cs-step__shot { width: 100%; height: 100%; border-radius: 10px; box-shadow: 0 0 0 1px var(--rule); }
.cs-how__stage { display: none; }
@media (min-width: 1000px) {
  .cs-how__grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: clamp(36px, 4.6vw, 88px); }
  .cs-how__grid--portrait { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); }
  html.js .cs-how__steps { border-top: 0; }
  html.js .cs-step { min-height: min(62vh, 620px); align-content: center; border-bottom: 0; transition: opacity .6s var(--out); }
  html.js .cs-step:not(.is-on) { opacity: .26; }
  html.js .cs-step__zoom { display: none; }
  /* (a step that isn't the one you're reading takes you to it: case.js) */
  html.js .cs-step:not(.is-on) { cursor: pointer; }
  html.js .cs-how__frame { cursor: zoom-in; }
  html.js .cs-how__stage { display: block; }
  .cs-how__stage { container-type: inline-size; }
  .cs-how__frame { position: sticky; top: calc(var(--top) + 3vh); height: calc(100svh - var(--top) - 6vh); border-radius: 10px; overflow: hidden; isolation: isolate; container-type: size;
    background: radial-gradient(120% 100% at 28% 12%, color-mix(in srgb, var(--c) 64%, #15130F), color-mix(in srgb, var(--c) 20%, #0E0D0B) 78%); }
  /* (landscape screens get a frame of their own shape, as tall as it needs, in the middle of the screen) */
  .cs-how__grid:not(.cs-how__grid--portrait) .cs-how__frame { --fh: min(calc(100svh - var(--top) - 6vh), 76cqw); height: var(--fh); top: calc(var(--top) + (100svh - var(--top) - var(--fh)) / 2); }
  .cs-how__img { position: absolute; left: 50%; top: 52%; width: min(88cqw, calc(80cqh * var(--r))); height: auto; aspect-ratio: var(--r); translate: -50% -50%; border-radius: 8px;
    box-shadow: 0 40px 80px -30px rgb(0 0 0 / .75), 0 0 0 1px rgb(255 255 255 / .07); clip-path: inset(100% 0 0 0 round 8px); transition: clip-path 1.05s var(--inout), transform 1.2s var(--out); }
  .cs-how__img.is-on, .cs-how__img.is-past { clip-path: inset(0 0 0 0 round 8px); }
  .cs-how__img.is-past { transform: scale(.93); }
  .cs-how__count { position: absolute; left: 22px; top: 18px; z-index: 9; display: flex; gap: .5em; font: 500 12px var(--mono); color: rgb(242 237 227 / .86); }
  .cs-how__n { display: inline-grid; height: 1.5em; overflow: hidden; }
  .cs-how__n > span { grid-area: 1 / 1; transition: transform .8s var(--out); }
  .cs-how__n > span + span { transform: translateY(100%); }
}

/* what it does: six things, in a grid */
.cs-features { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); border-top: 1.5px solid var(--fg); }
.cs-features li { padding: 20px 22px 26px 0; border-bottom: 1px solid var(--rule-2); }
.cs-features li:not(:nth-child(3n + 1)) { padding-left: 22px; border-left: 1px solid var(--rule-2); }
.cs-features span { font: 500 12px var(--mono); color: var(--fg-3); }
.cs-features h3 { margin-top: 14px; font: 700 clamp(17px, 1.3vw, 20px)/1.25 var(--sans); letter-spacing: -.012em; text-wrap: balance; }
.cs-features p { margin-top: 8px; font-size: 15.5px; color: var(--fg-2); text-wrap: pretty; }

/* under the hood: how it fits together, four parts joined up as you reach them (case.js), and what's under them */
.cs-flowd { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 34px; margin-bottom: clamp(34px, 5.4vh, 54px); }
.cs-flowd li { position: relative; padding: 16px 16px 18px; border: 1.5px solid var(--fg); border-radius: 10px; background: var(--white); }
.cs-flowd li + li::before { content: ""; position: absolute; right: 100%; top: 50%; width: 34px; height: 14px; margin-top: -7px; background: var(--fg); transform-origin: 0 50%;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 34 14'><path d='M4 7h25M24.5 2.5 29 7l-4.5 4.5' fill='none' stroke='black' stroke-width='1.6'/></svg>") center / 100% 100% no-repeat;
          mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 34 14'><path d='M4 7h25M24.5 2.5 29 7l-4.5 4.5' fill='none' stroke='black' stroke-width='1.6'/></svg>") center / 100% 100% no-repeat; }
.cs-flowd__n { font: 500 11px var(--mono); color: var(--fg-3); }
.cs-flowd b { display: block; margin-top: 10px; font: 800 16.5px/1.15 var(--sans); letter-spacing: -.012em; }
.cs-flowd b + span { display: block; margin-top: 6px; font-size: 14px; line-height: 1.4; color: var(--fg-2); }
/* ...and where each part says what it does: under its box without a script; with one, below, a part at a time (case.js) */
.cs-flowd__more { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--rule); font-size: 14px; line-height: 1.5; color: var(--fg-2); }
.cs-flowd--steps.is-stepper { margin-bottom: 22px; }
.cs-flowd--steps.is-stepper .cs-flowd__more { display: none; }
.js .cs-flowd.cs-flowd--steps.is-stepper li { cursor: pointer; transition: opacity .6s var(--out) calc(var(--i) * .16s), transform .9s var(--out) calc(var(--i) * .16s), background-color .35s, color .35s, border-color .35s; }
.cs-flowd--steps.is-stepper li:hover:not(.is-on) { border-color: var(--red); }
.cs-flowd--steps.is-stepper li:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; }
.cs-flowd--steps.is-stepper li.is-on { border-color: var(--ink); background: var(--ink); color: var(--paper); }
.cs-flowd--steps.is-stepper li.is-on .cs-flowd__n, .cs-flowd--steps.is-stepper li.is-on b + span { color: rgb(242 237 227 / .7); }
.js .cs-flowd.cs-flowd--steps.is-stepper li + li::before { transition: transform .5s var(--inout) calc(var(--i) * .16s + .1s), background-color .45s; }
.cs-flowd--steps.is-stepper li.is-reached + li.is-reached::before { background: var(--red); }
.cs-flowd__panel { display: flex; justify-content: space-between; align-items: flex-start; gap: 28px; margin-bottom: clamp(34px, 5.4vh, 54px); padding: 18px 20px; border-radius: 10px; background: var(--white); box-shadow: 0 0 0 1px var(--rule-2); }
.cs-flowd__step { font: 500 11.5px var(--mono); text-transform: uppercase; color: var(--fg-3); }
.cs-flowd__text { max-width: 44em; margin-top: 8px; font-size: 17px; line-height: 1.5; text-wrap: pretty; }
.cs-flowd__nav { display: flex; gap: 8px; flex: none; }
.cs-flowd__go { display: grid; place-items: center; width: 40px; height: 40px; border: 1.5px solid var(--fg); border-radius: 50%; background: none; color: var(--fg); cursor: pointer; transition: background-color .25s, color .25s; }
.cs-flowd__go::after { content: ""; width: 14px; height: 14px; background: currentColor;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'><path d='M1 7h11M7.5 2.5 12 7l-4.5 4.5' fill='none' stroke='black' stroke-width='1.7'/></svg>") center / contain no-repeat;
          mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'><path d='M1 7h11M7.5 2.5 12 7l-4.5 4.5' fill='none' stroke='black' stroke-width='1.7'/></svg>") center / contain no-repeat; }
.cs-flowd__go--back::after { transform: scaleX(-1); }
.cs-flowd__go:hover, .cs-flowd__go:focus-visible { background: var(--fg); color: var(--bg); }
/* In the code: a few lines from the product's own source, with what they're for beside them */
.cs-code { display: grid; gap: clamp(28px, 4vh, 44px); }
.cs-excerpt { display: grid; grid-template-columns: minmax(0, 18rem) minmax(0, 1fr); gap: 20px 40px; align-items: start; }
.cs-excerpt__note h3 { font: 800 clamp(20px, 1.6vw, 24px)/1.15 var(--sans); letter-spacing: -.025em; }
.cs-excerpt__note p { margin-top: 10px; font-size: 16px; line-height: 1.55; color: var(--fg-2); }
.cs-excerpt__note .cs-excerpt__src { margin-top: 14px; font: 500 12px/1.5 var(--mono); text-transform: uppercase; color: var(--fg-3); overflow-wrap: anywhere; }
.codewin--excerpt { --code: clamp(12.5px, .95vw, 13.5px); }
.js:not(.reduced) .cs-flowd li { opacity: 0; transform: translateY(14px); transition: opacity .6s var(--out), transform .9s var(--out); transition-delay: calc(var(--i) * .16s); }
.js:not(.reduced) .cs-flowd li + li::before { transform: scaleX(0); transition: transform .5s var(--inout); transition-delay: calc(var(--i) * .16s + .1s); }
.js:not(.reduced) .cs-flowd.is-in li { opacity: 1; transform: none; }
.js:not(.reduced) .cs-flowd.is-in li + li::before { transform: none; }
.cs-hood { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 clamp(24px, 3vw, 48px); border-top: 1.5px solid var(--fg); }
.cs-hood li { padding: 20px 0 24px; border-bottom: 1px solid var(--rule-2); }
.cs-hood h3 { font: 700 clamp(18px, 1.4vw, 21px)/1.25 var(--sans); letter-spacing: -.012em; text-wrap: balance; }
.cs-hood p { max-width: 34em; margin-top: 8px; font-size: 16px; color: var(--fg-2); text-wrap: pretty; }

/* a few real lines of source, in a window with its file name */
.codewin { margin: 0; border-radius: 10px; overflow: hidden; background: #17171B; color: #E6E3DC; text-align: left; box-shadow: 0 40px 80px -36px rgb(0 0 0 / .7), 0 0 0 1px rgb(255 255 255 / .07); }
.codewin > :is(figcaption, [data-was="figcaption"]) { display: flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px; background: #24242A; font: 500 11.5px var(--mono); color: #B9B6AE; }
.codewin > :is(figcaption, [data-was="figcaption"]) i { width: 8px; height: 8px; border-radius: 50%; background: #FF5F57; }
.codewin > :is(figcaption, [data-was="figcaption"]) i:nth-child(2) { background: #FEBC2E; }
.codewin > :is(figcaption, [data-was="figcaption"]) i:nth-child(3) { background: #28C840; }
.codewin > :is(figcaption, [data-was="figcaption"]) span { margin-left: 10px; }
.codewin > :is(pre, [data-was="pre"]) { display: block; margin: 0; padding: 18px 20px 22px; overflow-x: auto; white-space: pre; font: 400 var(--code, 13px)/1.65 var(--mono); tab-size: 4; }
.codewin code { padding: 0; border-radius: 0; background: none; font: inherit; }
.codewin .cl { display: inline-block; min-height: 1lh; }
.codewin code i { font-style: normal; }
.codewin .k { color: #FF9A6E; } .codewin .s { color: #BFDB8F; } .codewin .c { color: #8E8B84; }
.codewin--body { margin-bottom: clamp(30px, 5vh, 48px); }
.codewin--body:last-child { margin-bottom: 0; }

/* in the panel at the top, or the way on to it: the source, or the parts in a column */
.cs-media--code { aspect-ratio: auto; padding: clamp(20px, 5%, 48px); container-type: inline-size; }
.cs-media--code .codewin { width: 100%; --code: clamp(10.5px, 1.5cqw, 13.5px); }
.cs-media--flow { aspect-ratio: 16 / 11; container-type: inline-size; }
.mflow { display: grid; gap: 22px; width: min(100%, 440px); color: #F4F1EA; text-align: left; }
.mflow > * { position: relative; display: grid; grid-template-columns: 2.6em minmax(0, 1fr); gap: 2px 8px; padding: 12px 16px 13px; border: 1px solid rgb(255 255 255 / .2); border-radius: 8px; background: rgb(255 255 255 / .08); }
.mflow > * + *::before { content: ""; position: absolute; left: 50%; bottom: calc(100% + 3px); width: 1.5px; height: 16px; background: rgb(255 255 255 / .45); }
.mflow > * > :first-child { grid-row: 1 / 3; padding-top: .25em; font: 500 11px var(--mono); opacity: .6; }
.mflow > * > :nth-child(2) { display: block; font: 700 15px/1.25 var(--sans); }
.mflow > * > :nth-child(3) { display: block; font-size: 12.5px; opacity: .72; }
.cs-next__media--code, .cs-next__media--flow { padding: 6%; }
.cs-next__media--code .codewin { display: block; width: 100%; --code: clamp(9px, 1.5cqw, 12.5px); }
.cs-next__media--flow .mflow { gap: 14px; }
.cs-next__media--flow .mflow > * { padding: 8px 12px 9px; }

/* the pencil sketch, worked out in the page from my photo by the same four steps (case.js): drag across to compare */
.cs-media--demo { aspect-ratio: auto; display: block; place-items: normal; padding: clamp(20px, 5%, 48px); overflow: hidden; container-type: inline-size; }
.cs-media__note { margin-top: 16px; font: 500 12px/1.5 var(--mono); text-transform: uppercase; color: rgb(244 241 234 / .78); text-align: center; }
.sketch { position: relative; width: min(100%, 520px); margin: 0 auto; aspect-ratio: 1; border-radius: 8px; overflow: hidden; box-shadow: 0 40px 80px -30px rgb(0 0 0 / .7); --x: 50%; user-select: none; -webkit-user-select: none; }
.sketch img, .sketch__out { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.sketch__out { clip-path: inset(0 0 0 var(--x)); opacity: 0; transition: opacity .6s var(--out); }
.sketch.is-ready .sketch__out { opacity: 1; }
.sketch__line { position: absolute; top: 0; bottom: 0; left: var(--x); width: 2px; margin-left: -1px; background: #F4F1EA; opacity: 0; transition: opacity .4s; pointer-events: none; }
.sketch__line b { position: absolute; left: 50%; top: 50%; width: 40px; height: 40px; margin: -20px 0 0 -20px; border-radius: 50%; background: #F4F1EA; box-shadow: 0 6px 18px rgb(0 0 0 / .35); }
.sketch__line b::before, .sketch__line b::after { content: ""; position: absolute; top: 50%; width: 7px; height: 7px; margin-top: -3.5px; border-left: 2px solid #121110; border-bottom: 2px solid #121110; }
.sketch__line b::before { left: 9px; transform: rotate(45deg); }
.sketch__line b::after { right: 9px; transform: rotate(-135deg); }
.sketch.is-ready .sketch__line { opacity: 1; }
.sketch__k { position: absolute; top: 12px; padding: 4px 8px; border-radius: 4px; background: rgb(18 17 16 / .62); font: 500 11px var(--mono); text-transform: uppercase; color: #F4F1EA; opacity: 0; transition: opacity .4s; }
.sketch__k--a { left: 12px; } .sketch__k--b { right: 12px; }
.sketch.is-ready .sketch__k { opacity: 1; }
.sketch__range { position: absolute; inset: 0; z-index: 2; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: ew-resize; touch-action: pan-y; -webkit-appearance: none; appearance: none; }
.sketch:not(.is-ready) .sketch__range { display: none; }
.sketch:has(.sketch__range:focus-visible) { outline: 2px solid #F4F1EA; outline-offset: 4px; }

/* IdentiFlash, as a picture: its toggle turns the light on and off */
.cs-media--torch { padding: 0; }
.cs-media--torch .cs-media__note { position: absolute; left: 16px; right: 16px; bottom: 16px; margin: 0; }
.torch { position: relative; display: grid; place-items: center; min-height: clamp(400px, 58cqw, 540px); padding: 24px 0 56px; overflow: hidden; }
/* (the light comes from behind the phone, where its flash is, near the top) */
.torch__beam { position: absolute; left: 50%; top: calc(50% - 150px); width: max(130%, 900px); aspect-ratio: 1; translate: -50% -50%; border-radius: 50%; pointer-events: none; opacity: 0; transition: opacity .3s var(--out);
  background: radial-gradient(closest-side, rgb(255 249 228 / .95), rgb(255 238 190 / .5) 22%, rgb(255 228 170 / .16) 52%, transparent 78%); }
.torch.is-on .torch__beam { opacity: 1; }
.torch__phone { position: relative; display: flex; flex-direction: column; align-items: center; width: 190px; height: 340px; padding: 0 16px; border-radius: 28px; background: #0E0F12; box-shadow: 0 0 0 7px #1B1C21, 0 40px 80px -30px rgb(0 0 0 / .8); }
.torch__app { align-self: stretch; margin: 0 -16px; padding: 22px 16px 12px; border-radius: 28px 28px 0 0; background: #1F6F5C; font: 600 14px var(--sans); color: #fff; }
.torch__toggle { position: relative; margin-top: 92px; width: 96px; height: 52px; padding: 0 0 8px; border: 0; border-radius: 4px; background: #E0E0E0; box-shadow: 0 2px 4px rgb(0 0 0 / .4); font: 600 14px var(--sans); letter-spacing: .06em; color: #222; cursor: pointer; }
.torch__toggle i { position: absolute; left: 14px; right: 14px; bottom: 9px; height: 4px; border-radius: 2px; background: #9E9E9E; transition: background .2s; }
.torch.is-on .torch__toggle i { background: #26A69A; }
.torch__toggle:active { transform: translateY(1px); }
.torch__toast { position: absolute; left: 50%; bottom: 26px; translate: -50% 0; width: max-content; max-width: 170px; padding: 7px 12px; border-radius: 16px; background: rgb(60 60 64 / .95); font: 500 11.5px/1.3 var(--sans); color: #F4F1EA; text-align: center; opacity: 0; transition: opacity .3s; pointer-events: none; }
.torch__toast.is-on { opacity: 1; }

/* a bug I remember, written up as a ticket, and stamped once you've read as far as the fix (case.js) */
.cs-bug { position: relative; max-width: 44em; padding: clamp(22px, 3vw, 34px); border-radius: 10px; background: var(--white); box-shadow: 0 0 0 1px var(--rule); }
.cs-bug h3 { max-width: 15em; padding-right: 4.6em; font: 800 clamp(22px, 2vw, 30px)/1.12 var(--sans); letter-spacing: -.025em; text-wrap: balance; }
.cs-bug dl { margin-top: 20px; border-top: 1px solid var(--rule-2); }
.cs-bug dl > div { display: grid; grid-template-columns: 7rem minmax(0, 1fr); gap: 12px; padding: 14px 0; border-bottom: 1px solid var(--rule); }
.cs-bug dl > div:last-child { padding-bottom: 0; border-bottom: 0; }
.cs-bug dt { font: 500 12px/1.75 var(--mono); text-transform: uppercase; color: var(--fg-3); }
.cs-bug dd { margin: 0; font-size: 16.5px; color: var(--fg-2); text-wrap: pretty; }
.cs-bug__fix dt { color: var(--accent); }
.cs-bug__fix dd { color: var(--fg); }
.cs-bug__stamp { right: clamp(18px, 3vw, 34px); top: clamp(18px, 2.8vw, 32px); bottom: auto; font-size: clamp(20px, 2vw, 28px); color: var(--red); transform: rotate(-7deg); }
.cs-bug.is-in .cs-bug__stamp { animation: stamp-down .7s var(--out) .25s forwards; }
html:not(.js) .cs-bug__stamp, .reduced .cs-bug__stamp { opacity: .9; }
@keyframes stamp-down { 0% { opacity: 0; transform: rotate(-10deg) scale(1.7); } 45% { opacity: .92; transform: rotate(-7deg) scale(.97); } 100% { opacity: .9; transform: rotate(-7deg) scale(1); } }

/* what I tried first: struck through as you reach it */
.cs-tried { border-top: 1.5px solid var(--fg); }
.cs-tried li { padding: 20px 0 24px; border-bottom: 1px solid var(--rule-2); }
.cs-tried h3 { font: 800 clamp(20px, 1.8vw, 28px)/1.2 var(--sans); letter-spacing: -.022em; }
.cs-tried h3 span { background: linear-gradient(var(--accent), var(--accent)) 0 58% / 100% 2px no-repeat; transition: background-size 1s var(--inout) .15s; }
.js:not(.reduced) .cs-tried li:not(.is-in) h3 span { background-size: 0% 2px; }
.cs-tried p { max-width: 36em; margin-top: 10px; font-size: 17px; color: var(--fg-2); text-wrap: pretty; }

/* the timeline: a line that draws down it as you read, and each release marked as the line reaches it */
.cs-history { position: relative; padding-left: 34px; view-timeline: --hist; }
.cs-history::before, .cs-history::after { content: ""; position: absolute; left: 5px; top: .55em; bottom: 1.6em; width: 1.5px; background: var(--rule-2); }
.cs-history::after { background: var(--fg); transform-origin: 50% 0; }
.cs-history li { position: relative; display: grid; grid-template-columns: 9.5rem minmax(0, 1fr); gap: 16px; padding-bottom: clamp(26px, 4vh, 40px); }
.cs-history li:last-child { padding-bottom: 0; }
.cs-history li::before { content: ""; position: absolute; left: -34px; top: .4em; width: 12px; height: 12px; border: 1.5px solid var(--fg); border-radius: 50%; background: var(--fg); box-sizing: border-box; }
.cs-history time { font: 500 12px/1.9 var(--mono); text-transform: uppercase; color: var(--fg-3); }
.cs-history h3 { font: 800 clamp(20px, 1.8vw, 28px)/1.15 var(--sans); letter-spacing: -.022em; }
.cs-history p { max-width: 34em; margin-top: 8px; font-size: 16.5px; color: var(--fg-2); text-wrap: pretty; }
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .cs-history::after { animation: grow-y linear both; animation-timeline: --hist; animation-range: exit-crossing -55vh exit-crossing calc(100% - 55vh); }
    .cs-history li::before { animation: dot-fill linear both; animation-timeline: view(); animation-range: exit-crossing -55vh exit-crossing -53vh; }
  }
}
@keyframes grow-y { from { transform: scaleY(0); } }
@keyframes dot-fill { from { background: var(--bg); } }

/* every screen: a row as wide as the screen. On a wide screen it holds still while you scroll, and goes by sideways (the
   page's scroll drives it: no script); otherwise you swipe through it */
.cs-reel { --h: min(56svh, 540px); --gap: clamp(16px, 1.8vw, 28px); padding: clamp(64px, 11vh, 130px) 0; }
.cs-reel .sh { margin-bottom: clamp(24px, 4vh, 40px); }
.cs-reel__track { display: flex; gap: var(--gap); padding: 0 var(--gutter); overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding: 0 var(--gutter); scrollbar-width: none; overscroll-behavior-x: contain; }
.cs-reel__track::-webkit-scrollbar { display: none; }
.cs-reel__item { flex: none; width: calc(var(--h) * var(--r)); scroll-snap-align: start; }
.cs-reel__item img { width: 100%; height: auto; aspect-ratio: var(--r); border-radius: 10px; box-shadow: 0 0 0 1px rgb(255 255 255 / .08); }
.cs-reel__item p { display: flex; gap: .9em; max-width: 30em; margin-top: 14px; font-size: 14.5px; line-height: 1.4; color: var(--fg-2); }
.cs-reel__item p span { font: 500 12px/1.75 var(--mono); color: var(--fg-3); }
.cs-reel__bar { position: relative; display: block; height: 1.5px; margin-top: clamp(22px, 3.6vh, 36px); background: var(--rule-2); }
.cs-reel__bar::after { content: ""; position: absolute; inset: 0; background: var(--fg); transform-origin: 0 50%; transform: scaleX(0); }
/* (where you swipe along it, the rule under it follows the swipe) */
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .cs-reel__pin { timeline-scope: --row; }
    .cs-reel__track { scroll-timeline: --row inline; }
    .cs-reel__bar::after { animation: grow-x linear both; animation-timeline: --row; }
  }
}
@supports (animation-timeline: view()) {
  @media (min-width: 901px) and (orientation: landscape) and (prefers-reduced-motion: no-preference) {
    .cs-reel { --travel: max(0px, calc(var(--a) * var(--h) + (var(--count) - 1) * var(--gap) + 2 * var(--gutter) - 100vw)); height: calc(100svh + var(--travel)); padding: 0; view-timeline: --reel; }
    .cs-reel__pin { position: sticky; top: 0; display: flex; flex-direction: column; justify-content: center; height: 100svh; padding-top: var(--top); overflow: clip; }
    .cs-reel__track { width: max-content; overflow: visible; scroll-snap-type: none; animation: reel linear both; animation-timeline: --reel; animation-range: contain 0% contain 100%; }
    .cs-reel__bar::after { animation: grow-x linear both; animation-timeline: --reel; animation-range: contain 0% contain 100%; }
  }
}
@keyframes reel { to { transform: translate3d(calc(-1 * var(--travel)), 0, 0); } }
@keyframes grow-x { to { transform: none; } }

/* a screen that opens larger, in the viewer (case.js; without the script, it's a link to the picture) */
/* (positioned, so its words for a screen reader stay inside the row it's in, which scrolls, rather than widen the page) */
.cs-zoom { position: relative; display: block; border-radius: 10px; cursor: zoom-in; }
.cs-zoom:focus-visible { outline-offset: 4px; }

/* the viewer: the screens of a case study, one at a time, as big as the window allows. A screen grows into it from where
   it was on the page, and goes back there when you close it (a view transition: case.js) */
.viewer { position: fixed; inset: 0; width: 100%; height: 100%; max-width: none; max-height: none; margin: 0; padding: 0; border: 0; overflow: hidden; background: rgb(12 11 10 / .96); color: var(--fg); }
.viewer[open] { display: grid; grid-template-rows: minmax(0, 1fr) auto; }
.viewer::backdrop { background: none; }
html.is-locked { overflow: hidden; }
.viewer__fig { display: grid; grid-template-rows: minmax(0, 1fr) auto; place-items: center; gap: 14px; min-height: 0; padding: var(--top) var(--gutter) 0; }
.viewer__img { width: auto; height: auto; max-width: min(100%, 1500px); max-height: calc(100dvh - var(--top) - 150px); border-radius: 10px; box-shadow: 0 40px 100px -30px rgb(0 0 0 / .8), 0 0 0 1px rgb(255 255 255 / .08); touch-action: pan-y; user-select: none; -webkit-user-select: none; }
.viewer__cap { max-width: 44em; min-height: 1.4em; font-size: 15px; line-height: 1.4; color: var(--fg-2); text-align: center; text-wrap: balance; }
.viewer__close { position: absolute; top: calc((var(--top) - 36px) / 2); right: var(--gutter); z-index: 2; color: var(--fg); }
.viewer__bar { display: flex; justify-content: center; align-items: center; gap: 18px; padding: 18px var(--gutter) calc(22px + env(safe-area-inset-bottom)); }
.viewer__n { min-width: 6.5em; font: 500 12px var(--mono); text-align: center; text-transform: uppercase; color: var(--fg-2); }
.viewer__btn { display: grid; place-items: center; width: 44px; height: 44px; padding: 0; border: 1.5px solid var(--rule-2); border-radius: 50%; background: none; color: var(--fg); cursor: pointer; transition: border-color .3s, background-color .3s; }
.viewer__btn::after { content: ""; width: 14px; height: 14px; background: currentColor;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'><path d='M1 7h11M7.5 2.5 12 7l-4.5 4.5' fill='none' stroke='black' stroke-width='1.7'/></svg>") center / contain no-repeat;
          mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'><path d='M1 7h11M7.5 2.5 12 7l-4.5 4.5' fill='none' stroke='black' stroke-width='1.7'/></svg>") center / contain no-repeat; }
.viewer__prev::after { transform: rotate(180deg); }
@media (hover: hover) { .viewer__btn:hover { border-color: var(--fg); background: rgb(242 237 227 / .08); } }
.viewer__btn:disabled { opacity: .3; cursor: default; }
/* (growing out of the page, and going back into it: the same picture at both ends, so the one arriving goes the whole way) */
html.vt-viewer .top { view-transition-name: none; }
::view-transition-group(zoom) { z-index: 5; animation-duration: .6s; animation-timing-function: cubic-bezier(.76, 0, .24, 1); }
::view-transition-old(zoom) { animation: none; opacity: 0; }
::view-transition-new(zoom) { animation: none; }
::view-transition-group(vfade) { z-index: 5; animation: none; }
::view-transition-old(vfade) { animation: v-away .45s cubic-bezier(.4, 0, .2, 1) both; }
@keyframes v-away { to { transform: scale(.9); opacity: 0; } }
/* (and from one screen to the next, the next slides in over the one you're leaving, which sinks back into the dark) */
::view-transition-group(vshot) { z-index: 5; animation: none; }
::view-transition-image-pair(vshot) { isolation: auto; }
::view-transition-old(vshot), ::view-transition-new(vshot) { mix-blend-mode: normal; animation-duration: .7s; animation-timing-function: cubic-bezier(.65, 0, .35, 1); animation-fill-mode: both; }
::view-transition-new(vshot) { z-index: 1; }
html:active-view-transition-type(v-next)::view-transition-old(vshot) { animation-name: v-sink-left; }
html:active-view-transition-type(v-next)::view-transition-new(vshot) { animation-name: v-from-right; }
html:active-view-transition-type(v-prev)::view-transition-old(vshot) { animation-name: v-sink-right; }
html:active-view-transition-type(v-prev)::view-transition-new(vshot) { animation-name: v-from-left; }
@keyframes v-sink-left { to { transform: translate3d(-14%, 0, 0) scale(.88); opacity: 0; } }
@keyframes v-sink-right { to { transform: translate3d(14%, 0, 0) scale(.88); opacity: 0; } }
@keyframes v-from-right { from { transform: translate3d(calc(50vw + 50%), 0, 0); } }
@keyframes v-from-left { from { transform: translate3d(calc(-50vw - 50%), 0, 0); } }

/* the running head, on a case study: once its name has gone under the top bar, the bar says which case study this is and
   which part of it you're in, in place of the sections (on a phone, of my name); the part rolls over as you go (case.js) */
.top__mid { display: grid; grid-template-columns: minmax(0, 1fr); justify-items: center; min-width: 0; overflow: clip; }
.top__mid > * { grid-area: 1 / 1; }
.top__head { display: flex; align-items: baseline; gap: 14px; max-width: 100%; padding: 0; border: 0; background: none; color: inherit; text-align: left; white-space: nowrap; font: 500 12.5px var(--mono); letter-spacing: .02em; text-transform: uppercase; opacity: 0; transform: translate3d(0, 110%, 0); transition: transform .6s var(--out), opacity .35s; pointer-events: none; cursor: pointer; }
.top__head b { overflow: hidden; text-overflow: ellipsis; font: 800 13px/1.5 var(--sans); letter-spacing: .12em; }
.top__sec { display: inline-grid; flex: none; height: 1.5em; overflow: hidden; color: var(--fg-2); }
.top__sec > span { grid-area: 1 / 1; transition: transform .7s var(--out); }
.top__sec > span + span { transform: translateY(100%); }
.top__sec i { margin-right: .75em; font-style: normal; color: var(--fg-3); }
.top__mid .top__nav, .cs-page .brand { transition: transform .6s var(--out), opacity .35s; }
.top.is-reading .top__head { opacity: 1; transform: none; pointer-events: auto; }
/* (pressed, it lists the parts, to go to one: a sheet that comes down from the top, under the bar) */
.jump { inset: 0 0 auto; width: 100%; max-width: none; max-height: 100dvh; margin: 0; padding: 0 var(--gutter) calc(20px + env(safe-area-inset-bottom)); overflow-y: auto; border: 0; border-bottom: 1.5px solid var(--fg); background: var(--bg); color: var(--fg); box-shadow: 0 30px 80px -30px rgb(0 0 0 / .45);
  clip-path: inset(0 0 100% 0); transition: clip-path .6s var(--inout), overlay .6s allow-discrete, display .6s allow-discrete; }
.jump[open] { clip-path: inset(0 0 0 0); }
@starting-style { .jump[open] { clip-path: inset(0 0 100% 0); } }
.jump::backdrop { background: rgb(18 17 16 / 0); transition: background-color .45s, overlay .6s allow-discrete, display .6s allow-discrete; }
.jump[open]::backdrop { background: rgb(18 17 16 / .45); }
@starting-style { .jump[open]::backdrop { background: rgb(18 17 16 / 0); } }
.jump__top { display: flex; justify-content: space-between; align-items: center; height: var(--top); }
.jump__k { margin: 8px 0 12px; color: var(--fg-3); }
.jump .cs-index { position: static; display: block !important; }
.jump .cs-index ol { margin-top: 0; }
.jump .cs-index a { padding-block: 13px; font-size: 12.5px; }
.top.is-reading .top__nav { opacity: 0; transform: translate3d(0, -110%, 0); visibility: hidden; transition: transform .6s var(--out), opacity .35s, visibility 0s .6s; }
@media (max-width: 900px) {
  .top__mid, .top > .brand { grid-area: 1 / 1; }
  /* (the running head's layer lies over my name: until it shows, presses go through it to the name) */
  .top__mid { justify-items: start; pointer-events: none; }
  .top__right { grid-area: 1 / 2; }
  .top__head { flex-direction: column; align-items: flex-start; gap: 0; font-size: 11.5px; }
  .top__head b { max-width: 100%; font-size: 12px; line-height: 1.3; }
  .top.is-reading .brand { opacity: 0; transform: translate3d(0, -60%, 0); visibility: hidden; transition: transform .6s var(--out), opacity .35s, visibility 0s .6s; }
}

/* a few lines of source that run on past the window's edge fade out there, and back in at the start once you've scrolled
   them along (the scroll does it: no script; where they fit, nothing fades) */
@property --fl { syntax: "<length>"; inherits: false; initial-value: 0px; }
@property --fr { syntax: "<length>"; inherits: false; initial-value: 0px; }
@supports (animation-timeline: scroll()) {
  .codewin > :is(pre, [data-was="pre"]) { -webkit-mask: linear-gradient(90deg, transparent, #000 var(--fl), #000 calc(100% - var(--fr)), transparent); mask: linear-gradient(90deg, transparent, #000 var(--fl), #000 calc(100% - var(--fr)), transparent);
    animation: code-edge linear both; animation-timeline: scroll(self inline); }
}
@keyframes code-edge { 0% { --fl: 0px; --fr: 44px; } 8%, 92% { --fl: 44px; --fr: 44px; } 100% { --fl: 44px; --fr: 0px; } }

/* the next one: its name as big as the page, its app in its own colour, and the whole of it a way in (it flies into the
   next page's top as that page comes up) */
.cs-next { position: relative; display: block; overflow-x: clip; padding: clamp(64px, 11vh, 130px) 0 clamp(72px, 12vh, 140px); background: var(--bg); color: var(--fg); text-decoration: none; }
.cs-next__in { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: 0 clamp(28px, 4vw, 72px); align-items: end; container-type: inline-size; }
.cs-next__in > .sh { grid-column: 1 / -1; }
.cs-next__name { grid-column: 1 / -1; margin: clamp(30px, 5vh, 60px) 0 clamp(28px, 4.6vh, 52px) -.045em; font: 800 min(calc(100cqw * var(--k)), 19vh)/.84 var(--sans); letter-spacing: -.058em; text-transform: uppercase; }
.cs-next__t { display: inline-block; transition: transform .8s var(--out); }
.cs-next__row { display: block; white-space: nowrap; }
.cs-next__media { position: relative; display: grid; place-items: center; aspect-ratio: 16 / 10; padding: 6%; border-radius: 10px; overflow: hidden; container-type: size;
  background: radial-gradient(120% 100% at 28% 12%, color-mix(in srgb, var(--n) 64%, #15130F), color-mix(in srgb, var(--n) 20%, #0E0D0B) 78%); }
.cs-next__media .shot { display: block; width: min(100cqw, calc(100cqh * 1320 / 844)); transition: transform 1s var(--out); }
.cs-next__media .shot[data-device="phone"] { width: calc(100cqh * 600 / 1294 * .9); }
.cs-next__about { display: grid; gap: 24px; align-self: end; }
.cs-next__lede { max-width: 22em; font: 700 clamp(19px, 1.5vw, 24px)/1.25 var(--sans); letter-spacing: -.02em; color: var(--fg-2); text-wrap: pretty; }
.cs-next__go { justify-self: start; }
@media (hover: hover) {
  .cs-next:hover .cs-next__t { transform: translateX(.04em); }
  .cs-next:hover .cs-next__media .shot { transform: translateY(-3%) scale(1.02); }
  .cs-next:hover .arrow::after { transform: translateX(4px); }
}
/* it comes up out of its frame as it arrives, and its name comes in from the side */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .cs-next__media { animation: open-up linear both; animation-timeline: view(); animation-range: entry 0% cover 45%; }
    .cs-next__name { animation: slide-in linear both; animation-timeline: view(); animation-range: entry 0% cover 40%; }
  }
}
@keyframes open-up { from { clip-path: inset(30% 0 0 0 round 10px); } to { clip-path: inset(0 0 0 0 round 10px); } }
@keyframes slide-in { from { transform: translate3d(14%, 0, 0); } }
.cs-foot { padding: 0 0 calc(28px + env(safe-area-inset-bottom)); }
.cs-foot .foot__base { margin-top: 0; }

/* as a case study opens on its own (not turned to from another page), it's printed in like the poster (case.js) */
.cs-pending .cs-title, .cs-pending .cs-sub, .cs-pending .cs-hero__text, .cs-pending .cs-media { opacity: 0; animation: failsafe 0s 3s forwards; }
.js-ok :is(.cs-title, .cs-sub, .cs-hero__text, .cs-media) { animation: none; }

/* =========================================================
   A page that isn't here (404.html): the poster, with a rubber stamp sending it back
   ========================================================= */
.lost { display: flex; min-height: 100svh; overflow: clip; }
.lost__marks { position: absolute; }
.lost__in { position: relative; z-index: 1; display: flex; flex-direction: column; justify-content: space-between; gap: 40px; padding-top: calc(var(--top) + clamp(4px, 1.6vh, 18px)); padding-bottom: clamp(24px, 5vh, 56px); }
.lost__sheet { position: relative; align-self: start; }
.lost__big { margin-left: -.05em; font: 800 min(calc((100vw - 2 * var(--gutter)) * .5), 56svh)/.8 var(--sans); letter-spacing: -.06em; }
.lost__stamp { left: 50%; right: auto; top: 50%; bottom: auto; font-size: clamp(22px, 4.2vw, 68px); white-space: nowrap; color: var(--paper); transform: translate(-50%, -50%) rotate(-11deg); }
.js:not(.reduced) .lost__stamp { animation: lost-stamp .8s var(--out) .5s both; }
html:not(.js) .lost__stamp, .reduced .lost__stamp { opacity: .92; }
@keyframes lost-stamp { 0% { opacity: 0; transform: translate(-50%, -50%) rotate(-15deg) scale(1.8); } 45% { opacity: .94; transform: translate(-50%, -50%) rotate(-11deg) scale(.96); } 100% { opacity: .92; transform: translate(-50%, -50%) rotate(-11deg) scale(1); } }
.lost__foot { position: relative; display: flex; justify-content: space-between; align-items: flex-end; gap: 24px 40px; padding-top: 18px; }
.lost__foot::before { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1.5px; background: currentColor; }
.lost__line { max-width: 22em; font: 700 clamp(20px, 2vw, 30px)/1.12 var(--sans); letter-spacing: -.025em; text-wrap: pretty; }
.lost__cta { display: flex; flex-wrap: wrap; gap: 10px; }
@media (max-width: 900px) { .lost__foot { flex-direction: column; align-items: flex-start; } }

/* =========================================================
   The site's other pages (page.js): the résumé, the privacy policies, Water Eject. Each opens under the top bar, and on
   its own (not turned to) its title is printed in (the animation is a failsafe, if page.js doesn't come)
   ========================================================= */
.pg-pending .pg-title, .pg-pending .pg-rise { opacity: 0; animation: failsafe 0s 3s forwards; }
.js-ok :is(.pg-title, .pg-rise) { animation: none; }
.cv { padding: calc(var(--top) + clamp(18px, 4vh, 44px)) 0 clamp(64px, 10vh, 110px); }
.cv__head { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 26rem); align-items: end; gap: 24px 48px; margin-top: clamp(32px, 6vh, 72px); }
.cv__title { margin-left: -.045em; font: 800 clamp(64px, 12vw, 190px)/.84 var(--sans); letter-spacing: -.058em; text-transform: uppercase; }
.cv__line { font: 700 clamp(19px, 1.6vw, 24px)/1.25 var(--sans); letter-spacing: -.02em; text-wrap: pretty; }
.cv__cta { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 22px; }
/* the résumé itself (cv.mjs), laid out as a spec sheet: what each part is in the margin, the part beside it */
.cvdoc { margin-top: clamp(44px, 8vh, 96px); border-top: 1.5px solid var(--fg); }
.cvdoc__id { padding: clamp(22px, 3.4vh, 34px) 0; }
.cvdoc__name { font: 800 clamp(30px, 3.2vw, 48px)/1 var(--sans); letter-spacing: -.04em; text-transform: uppercase; }
.cvdoc__sub { margin-top: 10px; font: 500 12.5px var(--mono); text-transform: uppercase; color: var(--fg-3); }
.cvdoc__contact { display: flex; flex-wrap: wrap; gap: 6px 22px; margin-top: 14px; font-size: 15.5px; }
.cvdoc__contact a { color: var(--fg); text-decoration: underline; text-decoration-color: var(--rule-2); text-underline-offset: .22em; transition: text-decoration-color .2s; }
.cvdoc__contact a:hover { text-decoration-color: var(--red); }
.cvdoc__part { display: grid; grid-template-columns: 11rem minmax(0, 1fr); gap: 12px 40px; padding: clamp(22px, 3.4vh, 34px) 0; border-top: 1px solid var(--rule-2); }
.cvdoc__k { padding-top: .35em; font: 500 12px/1.65 var(--mono); letter-spacing: .02em; text-transform: uppercase; color: var(--fg-3); }
.cvdoc__lede { max-width: 38em; font: 700 clamp(18px, 1.5vw, 22px)/1.35 var(--sans); letter-spacing: -.015em; text-wrap: pretty; }
.cvdoc__skills > div { display: grid; grid-template-columns: 11rem minmax(0, 1fr); gap: 2px 16px; padding: 9px 0; border-bottom: 1px solid var(--rule); }
.cvdoc__skills > div:first-child { padding-top: 0; }
.cvdoc__skills dt { font-weight: 700; font-size: 15.5px; }
.cvdoc__skills dd { font-size: 15.5px; color: var(--fg-2); }
.cvdoc__job + .cvdoc__job { margin-top: clamp(24px, 3.4vh, 34px); }
.cvdoc__role { font: 800 clamp(20px, 1.7vw, 26px)/1.15 var(--sans); letter-spacing: -.025em; }
.cvdoc__role span { font-weight: 500; color: var(--fg-2); }
.cvdoc__role span::before { content: ", "; }
.cvdoc__meta { margin-top: 6px; font: 500 12px var(--mono); text-transform: uppercase; color: var(--fg-3); }
.cvdoc__points { margin-top: 12px; list-style: none; }
.cvdoc__points li { position: relative; max-width: 46em; margin-top: 7px; padding-left: 20px; font-size: 16px; line-height: 1.55; color: var(--fg-2); text-wrap: pretty; }
.cvdoc__points li::before { content: ""; position: absolute; left: 0; top: .78em; width: 8px; height: 1.5px; background: var(--red); }
.cvdoc__role a { color: inherit; text-decoration: underline; text-decoration-color: var(--rule-2); text-decoration-thickness: 2px; text-underline-offset: .16em; transition: text-decoration-color .2s; }
.cvdoc__role a:hover { text-decoration-color: var(--red); }
.cvdoc__earlier a { font-weight: 700; color: var(--fg); text-decoration: underline; text-decoration-color: var(--rule-2); text-underline-offset: 3px; }
.cvdoc__earlier a:hover { text-decoration-color: var(--red); }
.cvdoc__links { display: flex; flex-wrap: wrap; gap: 6px 22px; margin-top: 12px; }
.cvdoc__certs { list-style: none; }
.cvdoc__certs li { display: flex; justify-content: space-between; gap: 16px; padding: 9px 0; border-bottom: 1px solid var(--rule); font-size: 16px; }
.cvdoc__certs li:first-child { padding-top: 0; }
.cvdoc__certs span { flex: none; font: 500 12px/1.9 var(--mono); text-transform: uppercase; color: var(--fg-3); }
@media (max-width: 900px) {
  .cv__head { grid-template-columns: minmax(0, 1fr); }
  .cvdoc__part, .cvdoc__skills > div { grid-template-columns: minmax(0, 1fr); }
  .cvdoc__skills > div { gap: 0; }
}
/* printed from here, it's the résumé on its own (the PDF is the same words, set for applicant tracking systems) */
@media print {
  .top, .foot, .skip, .cv > .wrap > .sh, .cv__head { display: none !important; }
  html, body { background: #fff; }
  .cv { padding: 0; }
  .cvdoc { margin: 0; border: 0; }
  .cvdoc__part { break-inside: avoid; padding: 12px 0; opacity: 1 !important; visibility: visible !important; transform: none !important; }
  .cvdoc__points li, .cvdoc__skills dd, .cvdoc__certs li { font-size: 11pt; }
  .cvdoc__links { display: none; }
  .cvdoc__role a { text-decoration: none; }
}

/* =========================================================
   The privacy policies (privacy-policies/): an index, then each app's policy at its own address, as the stores link to
   it; and an app's own policy page (privacy/<app>/), laid out the same
   ========================================================= */
.pp__index { margin-top: clamp(40px, 7vh, 80px); border-top: 1.5px solid var(--fg); }
.pp__index ol { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: 32px; }
.pp__index li { border-bottom: 1px solid var(--rule-2); }
.pp__index a { display: flex; flex-direction: column; gap: 4px; padding: 14px 0 16px; font: 700 16px/1.3 var(--sans); letter-spacing: -.01em; text-decoration: none; transition: color .25s; }
.pp__index a span { font: 500 11px var(--mono); letter-spacing: .02em; text-transform: uppercase; color: var(--fg-3); }
.pp__app { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: 24px 48px; margin-top: clamp(56px, 9vh, 104px); padding-top: 22px; border-top: 1.5px solid var(--fg); scroll-margin-top: 24px; }
.pp__app--own { margin-top: clamp(40px, 7vh, 80px); }
.pp__top h2 { margin-top: 10px; font: 800 clamp(26px, 2.6vw, 40px)/1.04 var(--sans); letter-spacing: -.035em; text-wrap: balance; }
.pp__date { margin-top: 12px; font: 500 12px var(--mono); text-transform: uppercase; color: var(--fg-3); }
.pp__ask { margin-top: 22px; }
.pp__intro p { font-size: 17px; line-height: 1.55; text-wrap: pretty; }
.pp__intro .pp__sum { margin-top: 12px; font-weight: 700; }
.pp__list { margin-top: 28px; border-top: 1px solid var(--rule-2); }
.pp__list--doc { margin-top: 0; }
.pp__list div { display: grid; grid-template-columns: 12rem minmax(0, 1fr); gap: 16px; padding: 16px 0; border-bottom: 1px solid var(--rule-2); }
.pp__list dt { font: 500 12px/1.75 var(--mono); text-transform: uppercase; }
.pp__list dd { font-size: 16px; line-height: 1.6; color: var(--fg-2); text-wrap: pretty; overflow-wrap: anywhere; }
.pp__list dd > * + * { margin-top: 10px; }
.pp__list dd ul { padding-left: 1.15em; list-style: disc; }
.pp__list dd li + li { margin-top: 6px; }
.pp__list dd :is(strong, a) { color: var(--fg); }
@media (hover: hover) { .pp__index a:hover { color: var(--fg-2); } }
@media (min-width: 901px) { .pp__top { position: sticky; top: 96px; align-self: start; } }
@media (max-width: 900px) {
  .pp__index ol, .pp__app { grid-template-columns: minmax(0, 1fr); }
  .pp__list div { grid-template-columns: minmax(0, 1fr); gap: 6px; }
}

/* =========================================================
   Water Eject (water-eject/): the tone on an ink panel beside what it's for, then how to use it, why it works, and the
   questions people ask (they open smoothly where the browser can)
   ========================================================= */
.we__grid { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 36px clamp(32px, 5vw, 80px); align-items: end; margin-top: clamp(32px, 6vh, 72px); }
.we__title { margin-left: -.04em; font: 800 clamp(44px, 6.2vw, 100px)/.9 var(--sans); letter-spacing: -.05em; text-transform: uppercase; text-wrap: balance; }
.we__lead { max-width: 30em; margin-top: clamp(22px, 3.5vh, 36px); font: 700 clamp(18px, 1.5vw, 22px)/1.35 var(--sans); letter-spacing: -.015em; text-wrap: pretty; }
.we__player { display: grid; justify-items: center; padding: 22px clamp(18px, 2.4vw, 32px) 26px; border-radius: 14px; background: var(--bg); color: var(--fg); text-align: center; box-shadow: 0 40px 80px -40px rgb(0 0 0 / .45); }
.we__modes { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; }
.we__modes button { height: 38px; padding: 0 16px; border: 1.5px solid var(--rule-2); border-radius: 999px; background: none; font: 500 12px var(--mono); letter-spacing: .02em; text-transform: uppercase; cursor: pointer; transition: background-color .25s, color .25s, border-color .25s; }
.we__modes button[aria-pressed="true"] { border-color: var(--fg); background: var(--fg); color: var(--bg); }
@media (hover: hover) { .we__modes button:not([aria-pressed="true"]):hover { border-color: var(--fg-2); } }
.we__custom { display: none; width: min(100%, 320px); margin-top: 16px; font: 500 12px var(--mono); text-transform: uppercase; color: var(--fg-2); }
.we__custom.is-shown { display: block; }
.we__custom strong { color: var(--fg); }
.we__custom input { width: 100%; margin-top: 8px; accent-color: var(--red); }
/* the dial: a speaker seen from the front, its cone in the middle; while the tone plays the cone buzzes and rings go out */
.we__dial { position: relative; display: grid; place-items: center; width: 220px; aspect-ratio: 1; margin-top: 22px; border-radius: 50%; background: radial-gradient(circle, transparent 56%, var(--rule-2) 56.5%, transparent 57.5%), radial-gradient(circle, transparent 77%, var(--rule-2) 77.5%, transparent 78.5%); }
.we__wave { position: absolute; inset: 22%; border: 1.5px solid var(--red); border-radius: 50%; opacity: 0; }
.we__cone { display: grid; place-items: center; width: 50%; aspect-ratio: 1; border-radius: 50%; background: radial-gradient(circle at 35% 30%, #FF8A65, var(--red) 48%, #A9300F); box-shadow: 0 10px 24px -8px rgb(0 0 0 / .6), inset 0 0 0 6px rgb(0 0 0 / .08); }
.we__cone svg { width: 42%; fill: var(--paper); }
.is-playing .we__wave { animation: we-wave 1.8s cubic-bezier(.2, .6, .4, 1) infinite; }
.is-playing .we__wave:nth-child(2) { animation-delay: .6s; }
.is-playing .we__wave:nth-child(3) { animation-delay: 1.2s; }
.is-playing .we__cone { animation: we-buzz .12s linear infinite; }
.reduced .is-playing .we__wave { animation: none; opacity: .6; transform: scale(1.2); }
.reduced .is-playing .we__cone { animation: none; }
@keyframes we-wave { from { transform: scale(1); opacity: .9; } to { transform: scale(1.9); opacity: 0; } }
@keyframes we-buzz { 0%, 100% { transform: none; } 25% { transform: translate(1px, -1px); } 75% { transform: translate(-1px, 1px); } }
.we__timer { margin-top: 18px; font: 800 clamp(44px, 4.4vw, 60px)/1 var(--sans); letter-spacing: -.04em; font-variant-numeric: tabular-nums; }
.we__status { min-height: 3.1em; max-width: 24em; margin-top: 10px; font-size: 15px; color: var(--fg-2); }
.we__go { min-width: 220px; justify-content: center; margin-top: 8px; }
.we__note { max-width: 30em; margin-top: 14px; font: 500 11px/1.5 var(--mono); text-transform: uppercase; color: var(--fg-3); }
.we-more { padding: 0 0 clamp(64px, 10vh, 110px); }
.we__part { margin-top: clamp(56px, 9vh, 104px); }
.we__part > .sh { margin-bottom: clamp(22px, 3.6vh, 36px); }
.we__steps { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); border-top: 1.5px solid var(--fg); }
.we__steps li { padding: 18px 20px 24px 0; border-bottom: 1px solid var(--rule-2); }
.we__steps li + li { padding-left: 20px; border-left: 1px solid var(--rule-2); }
.we__steps span { font: 500 12px var(--mono); color: var(--fg-3); }
.we__steps p { margin-top: 12px; font-size: 16px; line-height: 1.5; color: var(--fg-2); text-wrap: pretty; }
.we__steps strong { color: var(--fg); }
.we__app { display: grid; grid-template-columns: 72px minmax(0, 1fr) auto; align-items: center; gap: 18px 28px; margin-top: clamp(48px, 8vh, 88px); padding: 24px clamp(20px, 2.4vw, 32px); border-radius: 14px; background: var(--bg); color: var(--fg); }
.we__app img { width: 72px; height: 72px; border-radius: 18px; }
.we__app h2 { font: 800 clamp(20px, 1.8vw, 26px)/1.15 var(--sans); letter-spacing: -.025em; }
.we__app p { max-width: 40em; margin-top: 6px; font-size: 15.5px; color: var(--fg-2); }
.we__prose { max-width: 46em; font-size: clamp(17px, 1.3vw, 19px); line-height: 1.6; color: var(--fg-2); text-wrap: pretty; }
.we__prose strong { color: var(--fg); }
.we__faq { border-top: 1.5px solid var(--fg); interpolate-size: allow-keywords; }
.we__faq details { border-bottom: 1px solid var(--rule-2); }
.we__faq summary { position: relative; display: block; padding: 18px 48px 18px 0; font: 700 clamp(17px, 1.4vw, 21px)/1.3 var(--sans); letter-spacing: -.015em; cursor: pointer; list-style: none; }
.we__faq summary::-webkit-details-marker { display: none; }
.we__faq summary::after { content: ""; position: absolute; right: 4px; top: 50%; width: 14px; height: 14px; margin-top: -7px; background: linear-gradient(currentColor, currentColor) 50% / 100% 1.5px no-repeat, linear-gradient(currentColor, currentColor) 50% / 1.5px 100% no-repeat; transition: transform .45s var(--out); }
.we__faq details[open] summary::after { transform: rotate(45deg); }
.we__faq details p { max-width: 46em; padding: 0 0 20px; font-size: 16px; line-height: 1.6; color: var(--fg-2); }
.we__faq details::details-content { block-size: 0; overflow: clip; transition: block-size .5s var(--out), content-visibility .5s allow-discrete; }
.we__faq details[open]::details-content { block-size: auto; }
.reduced .we__faq details::details-content { transition: none; }
.we__made { margin-top: clamp(48px, 8vh, 80px); padding-top: 16px; border-top: 1px solid var(--rule-2); font: 500 12px/1.6 var(--mono); text-transform: uppercase; color: var(--fg-3); }
.we__made a { color: var(--fg-2); }
@media (max-width: 1000px) {
  .we__grid { grid-template-columns: minmax(0, 1fr); }
  .we__steps { grid-template-columns: minmax(0, 1fr); }
  .we__steps li, .we__steps li + li { display: grid; grid-template-columns: 3em minmax(0, 1fr); padding: 16px 0; border-left: 0; }
  .we__steps p { margin-top: 0; }
}
@media (max-width: 600px) {
  .we__app { grid-template-columns: 56px minmax(0, 1fr); }
  .we__app img { width: 56px; height: 56px; border-radius: 14px; }
  .we__app .pill { grid-column: 1 / -1; justify-self: start; }
  .we__dial { width: 190px; }
  .we__modes button { padding: 0 11px; font-size: 11px; }
}

/* =========================================================
   Responsive
   ========================================================= */
@media (max-width: 1100px) {
  .top__nav a { padding: 8px 9px; }
  .more__row { grid-template-columns: 44px minmax(0, 1fr) auto 70px; }
  .more__desc { display: none; }
  .more__kind { white-space: nowrap; }
  .stop { grid-template-columns: 150px minmax(0, 1fr); }
  .stop__more { grid-column: 2; }
  .also { grid-template-columns: 150px minmax(0, 1fr); }
  .also__k { grid-row: 1 / span 2; }
  .also > div { grid-column: 2; }
  .nums__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 64px; }
}
@media (max-width: 900px) {
  :root { --top: 62px; }
  .top { grid-template-columns: minmax(0, 1fr) auto; }
  .top__nav, .top__short { display: none; }
  .top__menu { display: inline-block; }
  /* the hero on a narrow screen: two facts, my badge in the middle, my name under it, the line over the buttons */
  .hero { min-height: 600px; top: min(0px, calc(100svh - 600px)); }
  .hero__facts { grid-template-columns: 1fr 1fr; gap: 14px 18px; padding-top: 12px; }
  .hero__facts li:nth-child(3) { display: none; }
  .hero__facts span { font-size: 14px; }
  .hero__facts em { font-size: 13px; }
  .hero__name { font-size: calc((100vw - 2 * var(--gutter)) * .174); bottom: 200px; }
  .hero__foot { flex-direction: column; align-items: flex-start; gap: 18px; }
  .hero__line { font-size: clamp(24px, 6.6vw, 34px); }
  .pill { height: 46px; padding: 0 20px; }
  .badge { --cw: clamp(150px, 46vw, 230px); }
  html:not(.has-badge) .badge { display: none; }
  .intro__big { font-size: clamp(30px, 7.4vw, 50px); }
  .work__head { grid-template-columns: 1fr; }
  .more__row { grid-template-columns: 34px minmax(0, 1fr) auto; gap: 6px 14px; padding: 18px 0; }
  .more__kind { grid-column: 2; grid-row: 2; white-space: normal; }
  .more__go { grid-column: 3; grid-row: 1 / 3; }
  .more__peek { display: none; }
  .ways__body { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .ways__tab { grid-template-columns: 2.2rem minmax(0, 1fr) auto; align-items: baseline; padding: 14px 0 15px; }
  .ways__n { grid-row: auto; padding-top: 0; }
  .ways__t { font-size: 18px; }
  .ways__in { font-size: 10.5px; text-align: right; }
  .ways__panel .codewin--way { margin-top: 22px; --code: 11.5px; }
  .ways__panels { padding-top: 26px; }
  .ways__text { font-size: 16.5px; }
  .xp__body { grid-template-columns: 1fr; }
  .board { display: none; }
  .stop { grid-template-columns: 1fr; gap: 8px; }
  .stop__more { grid-column: 1; }
  .stop__when { padding-top: 0; }
  .also { grid-column: 1; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .also__k { grid-column: 1 / -1; grid-row: auto; padding-top: 0; }
  .also > div { grid-column: auto; }
  .contact__row { flex-direction: column; align-items: flex-start; }
  .contact__hint { display: none; }
  .foot__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .foot__colophon { grid-column: 1 / -1; }
  /* a case study on a phone: the name, what it is, the app, then the facts; no index */
  .cs-hero__grid { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .cs-hero__text { display: contents; }
  .cs-lede { order: 1; }
  .cs-media { order: 2; margin-top: 28px; aspect-ratio: 16 / 11; }
  .cs-media--phone { aspect-ratio: 4 / 5; }
  .cs-media--phone .shot[data-device="phone"] { width: calc(100cqh * 600 / 1294 * .92); }
  .cs-spec { order: 3; max-width: none; }
  .cs-lede { max-width: 30em; }
  .cs-links { order: 4; }
  .cs-title { font-size: min(calc(100cqw * var(--k)), calc(26svh / var(--rows) / .84)); }
  .cs-body { grid-template-columns: minmax(0, 1fr); }
  .cs-index { display: none; }
  .cs-next__in { grid-template-columns: minmax(0, 1fr); }
  .cs-next__about { margin-top: 26px; }
  .cs-decisions li { grid-template-columns: 2.6em minmax(0, 1fr); gap: 10px; }
  .cs-trade { grid-template-columns: minmax(0, 1fr); gap: 4px; }
  .cs-features { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cs-features li:not(:nth-child(3n + 1)) { padding-left: 0; border-left: 0; }
  .cs-features li:nth-child(2n) { padding-left: 18px; border-left: 1px solid var(--rule-2); }
  .cs-flowd { grid-template-columns: minmax(0, 1fr); gap: 30px; }
  .cs-excerpt { grid-template-columns: minmax(0, 1fr); }
  .cs-flowd__panel { flex-direction: column; gap: 14px; }
  .cs-flowd li + li::before { right: auto; left: 22px; top: auto; bottom: 100%; width: 14px; height: 30px; margin: 0; transform-origin: 50% 0;
    -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 30'><path d='M7 4v21M2.5 20.5 7 25l4.5-4.5' fill='none' stroke='black' stroke-width='1.6'/></svg>");
            mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 30'><path d='M7 4v21M2.5 20.5 7 25l4.5-4.5' fill='none' stroke='black' stroke-width='1.6'/></svg>"); }
  .js:not(.reduced) .cs-flowd li + li::before { transform: scaleY(0); }
  .cs-hood { grid-template-columns: minmax(0, 1fr); }
  .cs-history li { grid-template-columns: minmax(0, 1fr); gap: 4px; }
  .cs-media--flow, .cs-media--code, .cs-media--demo { aspect-ratio: auto; }
  .cs-media--flow .mflow { margin: 8px auto; }
  .top__back { height: 34px; }
  .cord { grid-column: 1 / -1; margin-top: 0; border-top: 1px solid var(--rule-2); }
}
/* (at the end of a case study on a phone or an upright tablet, the screens, the next one and the footer are all ink, so
   the gaps between them aren't doubled) */
@media (max-width: 900px), (max-width: 1100px) and (orientation: portrait) {
  .cs-reel { padding: clamp(56px, 7vh, 96px) 0 clamp(28px, 3vh, 44px); }
  .cs-next { padding: clamp(44px, 5vh, 72px) 0 clamp(52px, 6vh, 80px); }
  .cs-page .foot { padding-top: clamp(24px, 3vh, 44px); }
}
@media (min-width: 601px) and (max-width: 900px) {
  .caps { gap: 24px; }
  .cap__v { font-size: 15.5px; }
  .about__grid { grid-template-columns: minmax(0, 5fr) minmax(0, 8fr); gap: 32px; }
  .about__big { font-size: clamp(24px, 3.7vw, 30px); }
  .cs-spec { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: clamp(24px, 4vw, 40px); }
}
@media (max-width: 600px) {
  .wide-only { display: none; }
  .caps { grid-template-columns: 1fr; }
  .about__grid { grid-template-columns: 1fr; }
  .about__photo { max-width: none; }
  .about__photo img { aspect-ratio: 1 / 1; object-position: 50% 50%; }
  .cs-statement { font-size: clamp(23px, 6.4vw, 28px); line-height: 1.12; }
  /* (on a small phone, the facts' second lines go, to leave the badge more room) */
  .hero__facts em { display: none; }
  /* (and an app on a phone gets a taller picture, so the calculator's keys are big enough for a thumb) */
  .pj__media:has(.shot[data-device="phone"]) { aspect-ratio: 4 / 5; }
  .pj__media .shot[data-device="phone"] { width: min(66cqw, calc(100cqh * 600 / 1308 * .92)); }
  .hero__marks { display: none; }
  .brand { font-size: 12px; letter-spacing: .12em; }
  .sh { grid-template-columns: 3.6em minmax(0, 1fr); }
  .sh__aside { display: none; }
  .nums__grid { grid-template-columns: 1fr; row-gap: 0; }
  .stat { padding: 22px 0 30px; border-left: 0; border-top: 1px solid var(--rule-2); }
  .stat h3 { margin-top: 18px; }
  .cs-features { grid-template-columns: minmax(0, 1fr); }
  .cs-features li:nth-child(2n) { padding-left: 0; border-left: 0; }
  /* (a step's number goes over its name, so the words and its screen have the whole width) */
  .cs-step { grid-template-columns: minmax(0, 1fr); }
  .cs-step > * { grid-column: 1; }
  .cs-step__n { grid-row: auto; padding: 0 0 10px; }
  .cs-bug dl > div { grid-template-columns: minmax(0, 1fr); gap: 4px; }
  .cs-bug h3 { padding-right: 3.6em; }
  .cs-reel__item { width: min(calc(var(--h) * var(--r)), 84vw); }
  .short__facts > div { grid-template-columns: 1fr; }
  .short__xp span { display: block; }
  .short__xp span + span { margin-top: 8px; }
  .short__xp small { display: block; margin-top: 1px; }
  .line { grid-template-columns: 22px minmax(0, 1fr); }
  .stop::before { left: -26px; }
  .also { margin-left: 22px; gap: 12px 20px; }
  .also__name { font-size: 16px; }
  .foot__base { flex-direction: column; align-items: flex-start; }
  .foot__grid { grid-template-columns: 1fr 1fr; }
}
/* a phone on its side: the hero has to fit in a short screen */
@media (max-height: 520px) and (orientation: landscape) {
  .hero { min-height: 0; top: 0; }
  .hero__facts { display: none; }
  .hero__foot { flex-direction: row; align-items: center; bottom: 12px; padding-top: 12px; }
  .hero__line { font-size: 19px; }
  .hero .pill { height: 38px; padding: 0 14px; }
  .hero__name { bottom: 84px; font-size: calc((100vw - 2 * var(--gutter)) * .12); }
  .badge { --cw: clamp(100px, 30vh, 150px); }
  html:not(.has-badge) .badge { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}
