/* Open Sans, a photo hero and photo footer on the home page,
   plain grey pages with a dark grey footer elsewhere. */

/* The fonts, hosted here (SIL Open Font License 1.1), the latin and latin-ext subsets from Google Fonts.
   Open Sans is a variable font, every weight from 300 to 800 in one file, and is only fetched where
   San Francisco isn't there. The wordmark's Baumans is preloaded in each page's head, and blocks rather than
   swaps so the wordmark never shows in a fallback font. */
@font-face {
    font-family: "Baumans";
    font-style: normal;
    font-weight: 400;
    font-display: block;
    src: url("assets/fonts/baumans-latin.woff2") format("woff2");
}
@font-face {
    font-family: "Open Sans";
    font-style: normal;
    font-weight: 300 800;
    font-display: swap;
    src: url("assets/fonts/open-sans-latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: "Open Sans";
    font-style: normal;
    font-weight: 300 800;
    font-display: swap;
    src: url("assets/fonts/open-sans-latin-ext.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* The app's fonts for its window in the hero, cut down from strobe-tuner's assets/fonts with fonttools'
   pyftsubset --flavor=woff2: Inter Medium to the letters and digits, the app's tabular Inter as it is,
   Phosphor to the four icons (U+E0A0, U+E326, U+E9C8, U+E432) and Noto Sans Mono to ♯ and ♭ */
@font-face { font-family: "Strobie Inter"; font-weight: 500; font-display: block; src: url("assets/fonts/inter-medium-app.woff2") format("woff2"); }
@font-face { font-family: "Strobie Inter Tabular"; font-weight: 400; font-display: block; src: url("assets/fonts/inter-regular-tabular.woff2") format("woff2"); }
@font-face { font-family: "Strobie Inter Tabular"; font-weight: 500; font-display: block; src: url("assets/fonts/inter-medium-tabular.woff2") format("woff2"); }
@font-face { font-family: "Strobie Phosphor"; font-display: block; src: url("assets/fonts/phosphor-app.woff2") format("woff2"); }
@font-face { font-family: "Strobie Noto Sans Mono"; font-weight: 500; font-display: block; src: url("assets/fonts/noto-sans-mono-signs.woff2") format("woff2"); }

:root {
    --ink: #1f2024;
    --heading: #15141b;    /* text_color_dark */
    /* The brand accent, from strobe-tuner's src/app/gui.odin: leave one line uncommented */
    --brand: #82e2ff;    /* accent_color: the app's cyan */
    /* Links on the grey page: the brand colour darkened until it reads as text */
    --accent: color-mix(in srgb, var(--brand) 35%, #000);
    /* Film grain for the home photo: grey fractal noise, laid over it in overlay mode */
    --grain-coarse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    /* The home hero and footer photo: Pixabay 4725871 by aquiwillprobando (Pixabay licence) */
    --photo: url("assets/pedalboard-2560.webp");
    /* The photo at 24 × 16 px, stretched under it as a blurred stand-in while it loads.
       Made again after a new photo: ffmpeg -i photo.webp -vf scale=24:-1:flags=area p.png,
       then cwebp -q 80 -sharp_yuv p.png -o p.webp, base64 p.webp */
    --photo-blur: url("data:image/webp;base64,UklGRvYAAABXRUJQVlA4IOoAAAAQBQCdASoYABAAPm0skUWkIqGYBABABsSgCdMoR3DhQCI3GA0Jr+x8l2AZo5JDd0YAAP779KztJOlEOMoONZYlo6Q2NuzDxAZdKautiHIdiwehjvQ1bEnj4uQ4rfOG/1tA+mJ9qkUbdqaPu+/rGqUcpfyH4MiUlSziChKgMDrvYh+/2NKgOgjG8D8sEwIB3fsYl8w88Gr4NPh8x6QMvIIJt/t/7dyuP75rDTnJ3+H3mLRMu/62gF6FEx18EmdVPL1WFu9X1mxH325LjbzW7RIPN/4l/K+dpAN3xMxn9vcTVEv4nXp8P81XoAA=");
    /* The page sits on top of the photo and casts a soft shadow onto it, up into the hero
       and down into the footer. Deeper: raise the alpha or the 1.5rem. */
    --paper-shadow-up: linear-gradient(to top, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.25) 0.75rem, rgba(0, 0, 0, 0) 2.5rem);
    --paper-shadow-down: linear-gradient(rgba(0, 0, 0, 0.35), rgba(0, 0, 0, 0) 1.5rem);
    /* Warms the photo towards the stone page, laid over it in soft-light mode.
       Stronger: raise the alpha. Neutral: uncomment the transparent --warm line instead. */
    /*--warm: linear-gradient(transparent, transparent);*/
    /*--warm: linear-gradient(rgba(214, 140, 70, 0.6), rgba(214, 140, 70, 0.6));*/
    --warm: linear-gradient(rgba(214, 140, 70, 0.35), rgba(214, 140, 70, 0.35));
    --night: #1d1d1d;
    /* The top bar on pages without a hero, and the hairline between it and the page */
    --bar: #15161a;
    --bar-edge: transparent;
    --line: rgba(0, 0, 0, 0.18);
    /* The hairline rules of the features grid and the specifications, a cool blue-grey */
    --rule: rgba(30, 40, 60, 0.2);
    /* The page behind the text */
    /*--paper: #bdbdbd;*/    /* text_color_light: heavy, the links lose contrast */
    /*--paper: #fbfbfb;*/    /* text_color_white: barely off white */
    /* Leave one line uncommented */
    /*--paper: #ced4da;*/    /* cool blue-grey */
    --paper: #e3e4e7;        /* a light tint of strobe_bg_color */
    /*--paper: #d5d6da;*/    /* a mid tint of strobe_bg_color */
    /*--paper: #e0e0e0;*/    /* neutral */
    /*--paper: #e6e2dc;*/    /* warm stone, like the pedalboard's wood */
    /*--paper: #d4dcdc;*/    /* the photo's teal-grey, pale */
    /* The icons, numbers and index numbers: the app's cyan deepened so it reads on the light page */
    --icon: #2a9fc9;
    /* The small uppercase labels of the specifications, the features' titles are in --heading */
    --label: #2b2d38;
    color-scheme: light;
}
/* Dark theme: the page in the app's background (strobe_bg_color) with light type and the cyan at full
   strength, like the app. Back to the light page: comment out this block. */
:root {
    --paper: #15161a;
    --ink: rgba(251, 251, 251, 0.72);
    --heading: #fbfbfb;    /* text_color_white */
    --accent: var(--brand);
    --icon: var(--brand);
    --label: rgba(251, 251, 251, 0.6);
    --line: rgba(255, 255, 255, 0.16);
    --rule: rgba(255, 255, 255, 0.13);
    /* The top bar and footer a shade darker than the page in the same hue, each ruled off from it */
    --bar: #0d0e11;
    --night: var(--bar);
    --bar-edge: rgba(255, 255, 255, 0.09);
    color-scheme: dark;
}

* { box-sizing: border-box; }

/* The canvas is dark so a rubber-band overscroll shows the hero's and the footer's dark rather than
   the page grey */
html { -webkit-text-size-adjust: 100%; background: #15161a; }

body {
    margin: 0;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    background: var(--paper);
    color: var(--ink);
    /* San Francisco on Apple devices, -apple-system in Safari and BlinkMacSystemFont in Chrome */
    font-family: -apple-system, BlinkMacSystemFont, "Open Sans", system-ui, sans-serif;
    font-size: 1rem;
    /* Medium where the font has the weight (San Francisco), regular elsewhere */
    font-weight: 500;
    line-height: 1.6;
    text-rendering: optimizeLegibility;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

header, main {
    width: 100%;
    max-width: 760px;
    margin: 0 auto;
    padding: 0 16px;
}
main { flex: 1; padding-bottom: 4rem; }
/* Pages without a hero start straight on the page grey */
.topbar + main { padding-top: 3.5rem; }

h1, h2, h3 { color: var(--heading); line-height: 1.25; font-weight: 500; margin: 2.25rem 0 0.75rem; }
h1 { font-size: 2rem; font-weight: 600; letter-spacing: -0.01em; margin-top: 0; }
h2 { font-size: 1.7rem; }
h3 { font-size: 1.15rem; font-weight: 600; }

p, ul { margin: 0 0 1rem; }
ul { padding-left: 1.25rem; }
li { margin: 0.4rem 0; }
li > ul { margin: 0.3rem 0 0; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 0.2em; }

/* Features: a grid of six columns, cards spanning 2, 3, 4 or 6 of them, ruled like a panel.
   The grid and its heading reach past the text column to 1000px, centred on it, and narrow with
   the window (48px rather than 32px off the viewport leaves room for a scrollbar). */
.features, h2:has(+ .features) {
    --wide: min(1000px, 100vw - 48px);
    width: var(--wide);
    margin-left: calc((100% - var(--wide)) / 2);
}
.features {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    counter-reset: feature;
    /* One ruled panel: the grid's colour shows through 1px gaps between the cards as hairline rules.
       Every row has to add up to six columns, an empty cell would show as a block of the rule colour. */
    gap: 1px;
    border: 1px solid var(--rule);
    background: var(--rule);
}
.features section {
    display: flex;
    flex-direction: column;
    padding: 1.25rem 1.4rem 1.35rem;
    background: var(--paper);
}
.features .span-2 { grid-column: span 2; }
.features .span-3 { grid-column: span 3; }
.features .span-4 { grid-column: span 4; }
.features .span-6 { grid-column: span 6; }

/* The icon sits in a ruled square, in the accent colour */
.features .icon {
    flex: none;
    box-sizing: content-box;
    width: 24px;
    height: 24px;
    padding: 8px;
    border: 1px solid var(--rule);
    border-radius: 3px;
    fill: var(--icon);
    color: var(--icon);   /* the drawn display icons use lines in currentColor */
}
/* Small uppercase titles like the labels engraved on a front panel, each with its index number */
.features h3 {
    margin: 0.75rem 0 0.5rem;
    color: var(--heading);
    font-size: 0.8rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.1em;
}
.features h3::before {
    counter-increment: feature;
    content: counter(feature, decimal-leading-zero);
    margin-right: 0.75em;
    color: var(--icon);
    font-family: ui-monospace, Menlo, monospace;
    font-weight: 500;
    letter-spacing: 0;
}
.features h3:first-child { margin-top: 0; }
.features p { margin: 0; font-size: 0.95rem; }

/* side: a larger icon beside the title and text */
.features .side { display: grid; grid-template-columns: auto 1fr; column-gap: 1rem; align-content: center; }
.features .side .icon { grid-row: span 2; align-self: center; width: 32px; height: 32px; padding: 12px; }
.features .side h3 { margin-top: 0; }

/* stat: a number in place of the icon, in monospace with even-width digits like a readout */
.features .num {
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
    font-variant-numeric: tabular-nums;
    font-size: 2rem;
    font-weight: 500;
    line-height: 1.1;
    letter-spacing: -0.03em;
    color: var(--icon);
}
.features .num small { font-size: 0.5em; letter-spacing: 0; text-transform: uppercase; opacity: 0.75; }
.features .num.strings { font-size: 1.4rem; letter-spacing: 0.1em; }
/* The tunings rolling (displays.js): each string and the name in a window a line tall, clipped above and
   below only, the new text sliding in over the old */
.features .num .roll { display: inline-block; position: relative; clip-path: inset(0 -50em 0 -0.3em); }
.features .num .roll > span { display: inline-block; white-space: nowrap; }
.features .num .roll > .next { position: absolute; left: 0; top: 0; }

/* bare: a larger icon on its own, no tile */
.features .bare .icon { width: 56px; height: 56px; padding: 0; margin: -4px 0 -4px -2px; border: none; }

/* end: no tile, a large faint icon pushed to the bottom right */
.features .end .icon { margin: auto 0 -0.4rem auto; padding: 0.75rem 0 0; width: 56px; height: 56px; border: none; opacity: 0.35; }
/* end and bare together: at the bottom, at full strength */
.features .end.bare .icon { opacity: 1; }

/* legend: the displays side by side, each a drawn icon over its name and a few words */
.features .legend {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 1rem;
    margin: 0.75rem 0 0;
    padding: 0;
    list-style: none;
    font-size: 0.85rem;
    line-height: 1.4;
    text-align: center;
}
.features .legend li { margin: 0; }
/* Lines 10 units wide on the 256 grid, about 2px at this size, lighter than Phosphor's 16 */
.features .legend .icon { margin: 0 auto 0.5rem; width: 48px; height: 48px; padding: 0; border: none; stroke-width: 10; }
.features .legend b { display: block; margin-bottom: 0.15rem; color: var(--heading); font-size: 0.9rem; font-weight: 600; }

/* The colorways, each a disc of a lit and a dark stripe */
.swatches { display: flex; gap: 8px; }
.swatches i {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: linear-gradient(90deg, var(--lit) 50%, var(--dark) 50%);
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12);
}

@media (max-width: 600px) {
    .features, h2:has(+ .features) { width: auto; margin-left: 0; }
    .features { grid-template-columns: 1fr; }
    .features [class*="span-"] { grid-column: auto; }
    .features .legend { grid-template-columns: repeat(3, 1fr); }
}

/* Specifications: a datasheet, hairline rows, the values in monospace */
.specs { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.specs tr { border-top: 1px solid var(--rule); }
.specs tr:last-child { border-bottom: 1px solid var(--rule); }
.specs th, .specs td { padding: 0.55rem 0; text-align: left; vertical-align: baseline; }
.specs th {
    width: 11rem;
    padding-right: 1rem;
    color: var(--label);
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.1em;
}
.specs td { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-variant-numeric: tabular-nums; font-size: 0.85rem; font-weight: 500; }
@media (max-width: 600px) {
    .specs th { width: 8rem; }
}

figure { margin: 2rem 0; text-align: center; }
img { max-width: 100%; height: auto; }

code { font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.85em; }
pre {
    border: 1px solid var(--line);
    border-radius: 7px;
    padding: 1rem;
    overflow-x: auto;
    font-size: 0.8rem;
    line-height: 1.5;
}

/* Hero: full bleed over the photo, soft white type */
header {
    max-width: none;
    color: rgba(255, 255, 255, 0.85);
}
header p { max-width: 34rem; margin-left: auto; margin-right: auto; font-size: 1.1rem; }
header a { color: #fff; text-decoration: underline; text-decoration-color: rgba(255, 255, 255, 0.4); text-underline-offset: 0.2em; }
header a:hover { text-decoration-color: #fff; }
/* The ways to the app under the intro, on their own lines and aligned like it. Apple's badges at their 40 px,
   10 px apart, a quarter of their height as Apple's clear space asks, commented out in index.html until the
   app is on the App Store. The web build under them as a plain link. */
header .try-cta { display: block; margin-top: 1.75rem; }
header a.store-badge { display: inline-block; margin: 0 10px 10px 0; vertical-align: middle; }
header a.store-badge img { display: block; height: 40px; width: auto; }
header .try-online { display: block; margin-top: 0.25rem; }
/* Home hero and footer: one black-and-white photo, fixed to the window, so the page scrolls over it
   and the footer shows its lower part. Other pages keep the plain grey footer.
   Pick the photo with --photo at the top.
   Each has four layers behind its content, from the bottom: the photo (::before), a veil that darkens
   it under the type and carries the page's shadow onto it (.veil in index.html), the blur (.blur) and
   the grain (::after). The veil and the blur belong to the page; the photo and the grain are one print,
   and the parallax below moves them together. */
header.photo, header.photo ~ footer { --attach: fixed; position: relative; isolation: isolate; }
/* The page lies on top of the photo, so the screenshot's shadow stops at the hero's edge, and the
   print slid by the parallax stops at the hero's and the footer's */
header.photo, header.photo ~ footer { overflow: clip; }
header.photo::before, header.photo ~ footer::before, header.photo::after, header.photo ~ footer::after { content: ""; }
header.photo::before, header.photo ~ footer::before, header.photo > .veil, header.photo ~ footer > .veil,
header.photo::after, header.photo ~ footer::after {
    position: absolute;
    inset: 0;
    z-index: -1;
    max-width: none;    /* the footer's children are a column */
    margin: 0;
    pointer-events: none;
}
/* The photo sits on its own layer behind the content. The grey layer in "color" mode makes it
   black and white; a filter would do the same but stops Chrome honouring the fixed background.
   It is zoomed past cover (3:2, so 210vh wide is 140vh tall) and pushed right and down towards
   the pedalboard, 80% down, written out as an offset from the top so it frames the same whether
   fixed to the window or laid on the hero by the parallax. Under it the blurred stand-in, placed
   the same, gives the soft-light tint something to blend with while the photo loads, alone the
   orange shows brown. */
header.photo::before, header.photo ~ footer::before {
    --place: 85% calc(0.8 * (100vh - max(93.33vw, 140vh))) / max(140vw, 210vh) auto;
    background:
        var(--warm),
        var(--photo) var(--place) no-repeat var(--attach),
        var(--photo-blur) var(--place) no-repeat var(--attach) #15161a;
    background-blend-mode: soft-light, normal, normal;
}
/* The photo is a light grey floor, so a light dark layer keeps the white type readable */
header.photo > .veil {
    background:
        var(--paper-shadow-up),
        linear-gradient(rgba(10, 14, 18, 0.6), rgba(10, 14, 18, 0.15) 22rem);
}
/* The footer shows the photo 80% down too, written as an offset from the bottom, the footer's
   bottom when the parallax lays it there */
header.photo ~ footer::before { --place: left 85% bottom calc(0.2 * (100vh - max(93.33vw, 140vh))) / max(140vw, 210vh) auto; }
/* The footer darkens the photo so the small print stays readable */
header.photo ~ footer > .veil {
    background:
        var(--paper-shadow-down),
        linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7));
}
/* Blurs the photo in a band behind the footer's 728px column, feathered like the hero's: fading
   out over --fade on both sides, ending --past beyond the column. Kept inside the window, and on
   narrow windows the fades shrink to under a third of it each. Under the grain, like the hero's. */
header.photo ~ footer > .blur {
    --fade: 320px;
    --past: 200px;
    --edge: min(var(--fade), 30%);
    position: absolute;
    inset: 0 max(0px, 50% - 364px - var(--past));
    z-index: -1;
    max-width: none;
    margin: 0;
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 var(--edge), #000 calc(100% - var(--edge)), transparent);
    mask-image: linear-gradient(90deg, transparent, #000 var(--edge), #000 calc(100% - var(--edge)), transparent);
}
/* Coarse grain over the photo, like a pushed black-and-white print.
   Grittier: raise the opacity. Finer: raise baseFrequency in --grain-coarse. */
header.photo::after, header.photo ~ footer::after {
    /* Fixed like the photo, so the grain stays on it while the page scrolls */
    background: var(--grain-coarse) 0 0 / 300px var(--attach);
    mix-blend-mode: overlay;
    opacity: 0.6;
}

/* Split hero (add class "split" to the header): the photo full width, the wordmark and then the
   intro on the left, the screenshot on the right, the two centred together. Stacks on narrow screens.
   The photo runs on below the screenshot, as far as the padding above. It's padding on the header
   rather than a margin on the screenshot, so the type centres on the window. */
header.photo.split {
    --gap: 8rem;
    --window-scale: 0.74;    /* the app's window, 361 px across */
    display: grid;
    grid-template-columns: minmax(0, 24rem) auto;
    justify-content: center;
    column-gap: var(--gap);
    padding: 5rem 16px 2.5rem;
    text-align: left;
}
header.split > h1, header.split > p { grid-column: 1; }
header.split > h1 { grid-row: 1; align-self: end; margin: 0 0 1rem; }
/* The intro at body size, dimmer and set a little apart, so the wordmark leads. Its link stays white. */
header.split > p { grid-row: 2; align-self: start; margin: 1rem 0 0; font-size: 1rem; color: rgba(255, 255, 255, 0.7); }
header.split figure { grid-column: 2; grid-row: 1 / span 2; margin: 0; }
/* The veil only darkens the left, behind the type. The photo covers the window (3:2, so it's
   max(100vw, 150vh) wide and max(66.67vw, 100vh) tall) and is pulled up by 22% of its height, so
   the hero starts just above the pedalboard. More floor below it: raise the 0.22. */
header.photo.split::before { --place: 75% calc(-0.22 * max(66.67vw, 100vh)) / max(100vw, 150vh) auto; }
header.split > .veil {
    background:
        var(--paper-shadow-up),
        linear-gradient(90deg, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.35) 55%, rgba(0, 0, 0, 0) 80%);
}
/* Blurs the photo from the left edge to around the window's right edge, the centred group's
   edge (half of the text column, the gap and the window), and fades out over --fade,
   ending --past beyond that edge, so the pedalboard on the right stays sharp. The fade starts
   behind the window, so its edge is already soft. It sits between the photo and the grain, so the
   grain stays sharp. Stronger: raise the blur. Softer edge: raise --fade. */
header.split > .blur {
    --fade: 320px;
    --past: 200px;
    position: absolute;
    inset: 0 calc(50% - (24rem + var(--gap) + 488px * var(--window-scale)) / 2 - var(--past)) 0 0;
    z-index: -1;
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
    -webkit-mask-image: linear-gradient(to left, transparent, #000 var(--fade));
    mask-image: linear-gradient(to left, transparent, #000 var(--fade));
}
/* Parallax: the photo scrolls at half the page's speed, so it stands some way behind it. The print
   is laid on the hero and the footer rather than fixed to the window, and slid as each passes
   through the window: down the hero as it leaves at the top, up the footer as it comes in at the
   bottom, by half of the hero's or the footer's height over the way. It stays inside: the uncovered
   strip grows at half the rate the hero scrolls out of the window, and the same at the footer's foot.
   At rest, the page at its top or its bottom, the photo is framed as it is fixed.
   Where the browser runs an animation by the scroll position (Chrome 115, Safari 26, so iPhones too,
   where a fixed background doesn't work) the animations below slide it, elsewhere (Firefox)
   parallax.js does by setting --out and --in and the class parallax on the page. With reduced
   motion, or without either, the photo stays fixed.
   A slower photo, further back: raise the 50% in the keyframes and parallax.js's rules; at 100% it
   stands still. */
@media (prefers-reduced-motion: no-preference) {
    @supports (animation-timeline: view()) {
        header.photo, header.photo ~ footer { --attach: scroll; view-timeline-name: --window; }
        header.photo::before, header.photo::after, header.photo ~ footer::before, header.photo ~ footer::after {
            animation: parallax-out linear both;
            animation-timeline: --window;
            animation-range: exit;
        }
        header.photo ~ footer::before, header.photo ~ footer::after { animation-name: parallax-in; animation-range: entry; }
    }
}
@keyframes parallax-out { to { translate: 0 50%; } }
@keyframes parallax-in { from { translate: 0 -50%; } }
.parallax header.photo, .parallax header.photo ~ footer { --attach: scroll; }
.parallax header.photo::before, .parallax header.photo::after { translate: 0 calc(var(--out, 0) * 50%); }
.parallax header.photo ~ footer::before, .parallax header.photo ~ footer::after { translate: 0 calc((var(--in, 1) - 1) * 50%); }/* Stacked, the screenshot is under the type, so the whole hero is blurred. The photo covers the window
   when fixed and the hero itself when laid on it, so a tall hero on a short screen stays covered. */
@media (max-width: 760px) {
    header.split > .blur { right: 0; -webkit-mask-image: none; mask-image: none; }
    header.photo.split { grid-template-columns: 1fr; padding: 3.5rem 0 0; text-align: center; }
    header.split > h1, header.split > p { justify-self: center; width: auto; margin: 0 16px 1rem; }
    header.split > h1 { margin-top: 0; }
    header.split figure { grid-column: 1; grid-row: auto; justify-self: center; margin: 1.5rem 0 1.75rem; }
    @media (max-width: 400px) { header.photo.split { --window-scale: 0.64; } }
    header.photo.split::before { --place: 75% center / cover; }
    header.split > .veil {
        background:
            var(--paper-shadow-up),
            linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.2) 18rem);
    }
}

/* Top bar: the app's background (strobe_bg_color in strobe-tuner's src/app/gui.odin),
   edge to edge, the wordmark on the left in line with the content */
.topbar {
    background: var(--bar);
    border-bottom: 1px solid var(--bar-edge);
    padding: 0.85rem max(16px, calc((100% - 728px) / 2));
}
.topbar h1 { margin: 0; font-size: inherit; line-height: inherit; }
/* The wordmark: Baumans, rounded and geometric, lowercase, with a soft glow */
.wordmark {
    display: inline-block;
    /* White (text_color_white) with a halo like a lit lamp. The halo is wide and faint, so the
       letters stay crisp and the glow falls on the background around them. It only sits on dark,
       the top bar and the hero. Stronger glow: raise the alphas. */
    color: #fbfbfb;
    text-shadow: 0 0 0.5em rgba(255, 255, 255, 0.35), 0 0 1.2em rgba(255, 255, 255, 0.3), 0 0 2.5em rgba(255, 255, 255, 0.2);
    font-family: "Baumans", -apple-system, "Open Sans", sans-serif;
    font-weight: 400;
    font-size: 2.6rem;
    line-height: 1;
    letter-spacing: 0;
    text-transform: lowercase;
    text-decoration: none;
}
.wordmark:hover { text-decoration: none; }
/* In the home hero: larger, with a stronger and wider glow. Stronger glow: raise the alphas. */
header .wordmark {
    display: block;
    font-size: clamp(2.8rem, 8vw, 4.2rem);
    line-height: 1.1;
    text-shadow: 0 0 0.4em rgba(255, 255, 255, 0.5), 0 0 1em rgba(255, 255, 255, 0.4), 0 0 2.2em rgba(255, 255, 255, 0.3), 0 0 4em rgba(255, 255, 255, 0.15);
}

header + main { padding-top: 1.5rem; }
header + main > h2:first-child { margin-top: 0; }
/* The app's window in the hero (index.html), built in the app's points as strobe-tuner's src/app/layout.odin
   places things in the desktop window, 488 × 620 under a 28 point title bar, and scaled down to
   --window-scale. Text is sized like the app's: its sizes are the font's ascent to descent, Inter's 1.21 em
   and Noto Sans Mono's 1.362, so each is the size over that with the size as the line height, and placed by
   the top of that line. Colours from src/app/gui.odin. strobe.js draws the strobe and moves the readout. */
header .window {
    position: relative;
    width: calc(488px * var(--window-scale));
    height: calc(648px * var(--window-scale));
    overflow: hidden;
    border-radius: inherit;
    text-align: left;
}
.window .app {
    --lit: #ff6767;    /* the gauge's tick, the colorway's lit stripes */
    position: absolute;
    top: 0;
    left: 0;
    width: 488px;
    height: 648px;
    transform: scale(var(--window-scale));
    transform-origin: 0 0;
    background: #15161a;
    color: #bdbdbd;
    user-select: none;
}
/* A plain macOS title bar, 28 points: the close, minimize and zoom buttons, 12 point circles 20 apart with a
   darker rim, and the title centred */
.app .titlebar {
    position: absolute;
    inset: 0 0 auto;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #fdfdfd;
    border-bottom: 1px solid rgba(0, 0, 0, 0.6);
}
.app .lights { position: absolute; left: 13px; top: 8px; display: flex; gap: 8px; }
.app .lights i { width: 12px; height: 12px; border-radius: 50%; background: var(--fill); box-shadow: inset 0 0 0 0.5px var(--rim); }
.app .lights i:nth-child(1) { --fill: #ff5f57; --rim: #e0443e; }
.app .lights i:nth-child(2) { --fill: #febc2e; --rim: #dea123; }
.app .lights i:nth-child(3) { --fill: #28c840; --rim: #1aab29; }
.app .titlebar b { color: rgba(0, 0, 0, 0.8); font-size: 13px; font-weight: 600; line-height: 1; }
.app .screen { position: absolute; left: 0; top: 28px; width: 488px; height: 620px; }
.app .screen > * { position: absolute; white-space: nowrap; }

/* The strobe, the top half, with the inner shadow setting it in */
.app .strobe { inset: 0 0 auto; height: 310px; background: #15161a; }
.app .strobe canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.app .strobe::after { content: ""; position: absolute; inset: 0; box-shadow: inset 0 0 16px rgba(0, 0, 0, 0.6); pointer-events: none; }
.app .partial { position: absolute; right: 12px; color: #82e2ff; font: 500 calc(16px / 1.21) / 16px "Strobie Inter Tabular", sans-serif; }

.app .label { color: #bdbdbd; font: 500 calc(14px / 1.21) / 14px "Strobie Inter", sans-serif; letter-spacing: 1px; }
.app .label.muted { color: #7d7e8f; }
.app .icon { color: #9a9baa; font: 16px / 16px "Strobie Phosphor"; }
.app .icon.large { font-size: 24px; line-height: 24px; }
/* The input level, a rounded track filled from the left */
.app .level { left: 412px; top: 327px; width: 60px; height: 6px; border-radius: 3px; background: linear-gradient(90deg, #82e2ff 50%, #2d2e35 50%); }
/* The readout: Hz right aligned to the left of the gutter, cents to the right of it with the sign before them */
.app .value { top: 369.5px; display: flex; gap: 2px; color: #fbfbfb; font: 400 calc(31.2px / 1.21) / 31.2px "Strobie Inter Tabular", sans-serif; }
/* The ruler: the note in the middle with its octave, a neighbour each side with its sharp or flat */
.app .note { left: 239.6px; top: 393.4px; transform: translateX(-50%); color: #fbfbfb; font: 500 calc(114.4px / 1.21) / 114.4px "Strobie Inter", sans-serif; }
.app .octave { position: absolute; left: 100%; top: 70.5px; font: 500 calc(33.8px / 1.21) / 33.8px "Strobie Inter", sans-serif; }
.app .neighbour { top: 416.8px; transform: translateX(-50%); color: #7d7e8f; font: 500 calc(67.6px / 1.21) / 67.6px "Strobie Inter", sans-serif; }
.app .sharp { position: absolute; left: 100%; top: 6.8px; font: 500 calc(26px / 1.362) / 26px "Strobie Noto Sans Mono", monospace; }
.app .flat { position: absolute; left: calc(100% - 3.4px); top: -3.4px; font: 500 calc(39px / 1.362) / 39px "Strobie Noto Sans Mono", monospace; }
/* The gauge under the note, ticks 16.9 apart, the middle one and every fifth long, the middle one in the colorway */
.app .gauge { left: 244px; top: 504.7px; width: 0; height: 20px; }
.app .gauge i { position: absolute; left: calc(var(--at) * 16.9px - 0.5px); top: 4px; width: 1px; height: 12px; background: #7d7e8f; }
.app .gauge i:nth-child(5n + 1) { top: 0; height: 20px; }
.app .gauge i:nth-child(6) { left: -1px; width: 2px; background: var(--lit); }
/* Locked, in pill_violet */
.app .lock { left: 244px; top: 558.5px; transform: translate(-50%, -50%); padding: 5px 10px; border-radius: 12px; background: #a277ff; color: #15141b; }

/* Under the window, the colorway picker: the swatches as buttons that light the strobe, the pressed one ringed */
header .colorways { display: flex; justify-content: center; gap: 12px; margin-top: 22px; }
header .colorways button {
    width: 24px;
    height: 24px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: linear-gradient(90deg, var(--lit) 50%, var(--dark) 50%);
    cursor: pointer;
}
header .colorways button[aria-pressed="true"] { outline: 1.5px solid #fff; outline-offset: 2px; }
header .colorways button:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
/* The bezel holds the shadows, a tight one and a long soft one that lift the window
   off the photo, and, in ::after, a thin glossy rim like an iOS icon's:
   bright along the top edge, fading down the sides and catching a little light at the bottom.
   The rim is a gradient border cut out with a mask so only the 1.5px ring shows. */
header .bezel {
    position: relative;
    display: inline-block;
    vertical-align: top;
    max-width: 100%;
    border-radius: 10px;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.45), 0 24px 40px rgba(0, 0, 0, 0.6);
}
header .bezel::after,
.try .bezel::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    border: 1.5px solid transparent;
    background: linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.3) 30%, rgba(255, 255, 255, 0.18) 70%, rgba(255, 255, 255, 0.4)) border-box;
    -webkit-mask: linear-gradient(#000 0 0) padding-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) padding-box exclude, linear-gradient(#000 0 0);
    pointer-events: none;
}

/* The app itself on the try page (try/index.html), the desktop window's size, 488 × 620 points, in the
   hero's bezel with its glossy rim. On a phone it fills the screen under the top bar, edge to edge, and
   lays itself out for it, no rim: .try-screen is one screen tall then, the footer follows. */
.try-screen { flex: 1; display: flex; flex-direction: column; }
.try .bezel {
    position: relative;
    display: block;
    width: 488px;
    max-width: 100%;
    height: 620px;
    margin: 0 auto;
    overflow: hidden;
    border-radius: 10px;
    background: #15161a;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.45), 0 24px 40px rgba(0, 0, 0, 0.6);
}
.try iframe { display: block; width: 100%; height: 100%; border: 0; }
@media (max-width: 600px) {
    .try-screen { flex: none; height: 100svh; }
    main.try { display: flex; padding: 0; }
    .try figure { flex: 1; margin: 0; }
    .try .bezel { width: 100%; height: 100%; border-radius: 0; box-shadow: none; }
    .try .bezel::after { display: none; }
}

/* Footer: dark grey, centred, soft white text. On the home page the photo shows through it. */
footer {
    background: var(--night);
    color: rgba(255, 255, 255, 0.75);
    text-align: center;
    padding: 3.5rem 16px;
    font-size: 0.85rem;
    line-height: 1.65;
}
.topbar ~ footer { border-top: 1px solid var(--bar-edge); }
footer > * { max-width: 728px; margin-left: auto; margin-right: auto; }
footer a { color: #fff; text-decoration: none; border-bottom: 1px solid rgba(255, 255, 255, 0.25); }
footer a:hover { text-decoration: none; border-bottom-color: #fff; }
footer nav ul { list-style: none; padding: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: 0.5rem 1.5rem; font-size: 0.95rem; margin-bottom: 1.75rem; }
footer nav li { margin: 0; }
footer nav a { border-bottom: none; }
footer nav a:hover { color: var(--brand); }
footer p:last-child { margin-bottom: 0; }
