/* stablibfx.com - red and black, like the plugin itself: a black chassis
   where the only colour comes from light. The red is stage light (the home
   hero's beams) and lit LEDs, and each effect keeps the colour of its
   module in the plugin (Source/StabLibFxColours.h). The buttons, the red
   word in the home headline, the step numbers and links are red too.

   Bricolage Grotesque (variable, self-hosted) for everything, condensed and
   in capitals for page titles; Chakra Petch only for the wordmark, as on the
   plugin. Text is left-aligned throughout.

   Motion, like a lighting desk running cues:
   - One opening, the first time the site is opened in a tab: a line of red
     light in the dark, which opens onto the home page's stage (.cue).
   - The home page's sections are sheets that stack like modules in the
     plugin's chain: each one slides up over the last, which dims and drops
     back as it's covered (.stack, site.js).
   - Everything else that moves is tied to the scroll or answers a click:
     the plugin turns to face you, five plugins fold into one slot. Never a fade-in on its own timer.
   - Going to another page, the new one rises over the old (view
     transitions), the same move as the sections.
   None of it runs for people whose system asks for reduced motion, and a
   browser without scroll-linked animation sees every part in its final
   place. */

@font-face { font-family: "Chakra Petch"; font-weight: 700; font-display: swap; src: url("/fonts/ChakraPetch-Bold.woff2") format("woff2"); }
/* One variable file: weight 200-800, width 75-100%, optical size 12-96. */
@font-face { font-family: "Bricolage Grotesque"; font-weight: 200 800; font-stretch: 75% 100%; font-display: swap; src: url("/fonts/BricolageGrotesque.woff2") format("woff2"); }

:root {
    --black: #000;
    --chassis: #141312;    /* the plugin's module body */
    --chassis-2: #1b1a18;  /* its knob boxes */
    --rim: #302e2b;        /* their borders */
    --rim-lit: #56524b;    /* a raised panel's lit top edge */
    --text: #f4f3f0;
    --text-2: #a8a5a0;
    --text-3: #8a8782;
    --red: #e5383b;
    --red-rgb: 229, 56, 59;
    /* White text on --red is 4.2:1; on this it's 4.9:1 (WCAG AA). */
    --red-button: #d42f33;
    --red-button-hover: #e03a3e;
    --link: #ff8a80;
    --beam-pale-rgb: 255, 200, 190;
    --wide: 1200px;
    /* The page's side margin; the home hero lines up with it. */
    --edge: clamp(16px, 4vw, 48px);
    /* Vertical rhythm, one set of steps for the whole site: a section's
       padding, the gap under its heading, and the gap between blocks in it. */
    --section-y: 112px;
    --head-gap: 56px;
    --block-gap: 96px;
    /* A sheet's top corners, like the plugin's module cards at page scale. */
    --sheet-radius: 28px;
    /* The opening's line of light and the shutters opening from it. */
    --ease-cue: cubic-bezier(0.7, 0, 0.2, 1);
    --body: "Bricolage Grotesque", system-ui, -apple-system, "Segoe UI", sans-serif;
    --wordmark: "Chakra Petch", system-ui, sans-serif;
    color-scheme: dark;
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
    margin: 0;
    background: var(--black);
    color: var(--text);
    font: 400 17px/1.6 var(--body);
    -webkit-font-smoothing: antialiased;
    overflow-x: hidden;
}
img, video { max-width: 100%; height: auto; display: block; }
/* Anything the page hides stays hidden, whatever its own display is: the
   sign-up form after it's sent, a price note that doesn't apply. */
[hidden] { display: none !important; }
/* Read out by screen readers, not shown: the sign-up box's "Email" label. */
.visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
a { color: var(--link); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--text); }
:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; border-radius: 4px; }

.wrap { width: min(var(--wide), 100% - 2 * var(--edge)); margin-inline: auto; }
/* Reading width, on the same left edge as everything else. */
.narrow { width: min(var(--wide), 100% - 2 * var(--edge)); margin-inline: auto; }
.narrow > * { max-width: 720px; }

/* ---- type ---- */
h1, h2, h3 { margin: 0 0 16px; color: var(--text); text-wrap: balance; }
/* Page titles: condensed capitals, like the home headline. */
h1 { font-weight: 800; font-variation-settings: "wdth" 75; text-transform: uppercase; font-size: clamp(44px, 6vw, 78px); line-height: 0.9; letter-spacing: -0.01em; }
h2 { font-weight: 700; font-variation-settings: "wdth" 85; font-size: clamp(30px, 3.8vw, 46px); line-height: 1.04; letter-spacing: -0.02em; }
h3 { font-weight: 600; font-size: 19px; line-height: 1.3; }
p { margin: 0 0 16px; text-wrap: pretty; }
.lede { font-size: clamp(18px, 2vw, 21px); line-height: 1.5; color: var(--text-2); max-width: 34em; }
.muted { color: var(--text-2); }
.small { font-size: 14px; color: var(--text-3); }

/* ---- header ---- */
.site-header { position: relative; z-index: 10; }
/* Over the stage light, on the pages that have it. */
body:has(.hero-home, .hero-split) .site-header { position: absolute; inset: 0 0 auto; }
.site-header .wrap { display: flex; flex-wrap: wrap; align-items: center; gap: 0 28px; min-height: 76px; }
/* "StabLib FX" with a real space, so search engines read the name; shown
   in capitals with the gap closed, like the plugin's own wordmark. */
.logo { font: 700 19px/1 var(--wordmark); letter-spacing: 0.22em; text-transform: uppercase; color: var(--text); text-decoration: none; white-space: nowrap; }
.logo span { color: var(--red); margin-left: -0.19em; }
.nav { display: flex; gap: 28px; margin-left: auto; align-items: center; }
.nav a:not(.button) { color: var(--text-2); text-decoration: none; font-size: 15px; font-weight: 500; white-space: nowrap; }
.nav a:not(.button):hover, .nav a[aria-current="page"] { color: var(--text); }
/* On a phone the links get a row of their own, under the logo and button. */
@media (max-width: 720px) {
    .site-header .wrap { padding-top: 14px; }
    .nav-cta { margin-left: auto; }
    .nav { order: 3; flex-basis: 100%; margin-left: 0; gap: 22px; min-height: 48px; }
}
/* Five links don't fit one phone-width row: Features, only a jump down the
   home page, makes way for the rest. */
@media (max-width: 440px) {
    .nav a[href="/#features"] { display: none; }
}
@media (max-width: 359px) {
    .nav { gap: 14px; }
    .nav a:not(.button) { font-size: 14px; }
}

/* ---- buttons ----
   Like the plugin's buttons, not a web page's: no fade. Pointing at one
   lights it straight away, as an LED switches on, and pressing it pushes
   it in. The red stays the same red, so white on it stays 4.9:1. */
.button { display: inline-flex; align-items: center; justify-content: center; min-height: 48px; padding: 0 24px; border-radius: 999px; font: 600 16px/1.2 var(--body); text-decoration: none; border: 1px solid transparent; cursor: pointer; }
.button.primary { background: var(--red-button); color: #fff; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14); }
.button.primary:hover { color: #fff; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3), 0 0 0 1px rgba(var(--red-rgb), 0.5), 0 8px 28px -6px rgba(var(--red-rgb), 0.75); }
.button.ghost { border-color: var(--rim-lit); color: var(--text); background: transparent; }
.button.ghost:hover { border-color: var(--text); color: var(--text); background: rgba(255, 255, 255, 0.04); }
.button:active { transform: translateY(1px); box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.35); }
.button:disabled { opacity: 0.6; cursor: default; transform: none; }
.nav-cta.button { min-height: 40px; padding: 0 18px; font-size: 15px; }
.text-link { display: inline-flex; align-items: center; min-height: 48px; font: 500 16px/1.2 var(--body); color: var(--text); text-decoration: underline; text-decoration-color: rgba(var(--red-rgb), 0.85); text-underline-offset: 6px; }
.text-link:hover { color: var(--text); text-decoration-color: var(--text); }
.actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin: 0 0 20px; }

/* ---- sections ---- */
section { padding: var(--section-y) 0; position: relative; }
@media (max-width: 640px) { :root { --section-y: 80px; --head-gap: 40px; --block-gap: 64px; --sheet-radius: 20px; } }
.section-head { max-width: 720px; margin-bottom: var(--head-gap); }

/* ---- the home page's sheets ----
   Each section after the hero is a sheet with rounded top corners and a lit
   top edge, like a module card in the plugin. On their own they read as one
   section after another. With site.js they stack (.stacking): a sheet that
   reaches the bottom of the screen stays there while the next slides up
   over it, and it dims and drops back as it's covered (site.js sets
   --covered, 0 to 1, and --h, its height). */
.sheet { background: var(--black); border-radius: var(--sheet-radius) var(--sheet-radius) 0 0; box-shadow: inset 0 1px 0 var(--rim-lit), inset 0 24px 48px -32px rgba(255, 255, 255, 0.05); }
@media (prefers-reduced-motion: no-preference) {
    .stacking .stack > * { position: sticky; top: min(0px, calc(100svh - var(--h, 100svh))); transform-origin: 50% max(50%, calc(100% - 50svh)); transform: scale(calc(1 - var(--covered, 0) * 0.05)); }
    .stacking .stack > *::before { content: ""; position: absolute; inset: 0; z-index: 20; border-radius: inherit; background: #000; opacity: calc(var(--covered, 0) * 0.72); pointer-events: none; }
}
/* Heading on the left, what it says on the right. */
.split { display: grid; grid-template-columns: 5fr 7fr; gap: 24px 64px; align-items: start; }
@media (max-width: 860px) { .split { grid-template-columns: 1fr; } }

/* ---- inner pages' opening ---- */
.hero { padding: 72px 0 56px; }
.hero .lede { margin-bottom: 32px; }

/* ---- home hero: a narrow headline, and the plugin as a product shot ----
   At desktop widths the copy sits in a 380px column on the left and the
   screenshot, turned slightly in perspective, runs off the right and bottom
   edges. Below 1024px they stack; below 640px the shot is flat. Behind it
   all, stage light: three blurred beams from above the top-right corner, a
   pool where they land, film grain and a vignette (.hero-bg). */
.hero-home { position: relative; overflow: hidden; isolation: isolate; min-height: 860px; padding: 168px 0 0; --gutter: max(var(--edge), (100% - var(--wide)) / 2); }
.hero-copy { position: relative; z-index: 2; width: 380px; margin-left: var(--gutter); }
.hero-title .accent { color: var(--red); }
.hero-title { margin: 0 0 26px; text-wrap: balance; font-weight: 800; font-variation-settings: "wdth" 75; text-transform: uppercase; font-size: clamp(44px, 6.2vw, 78px); line-height: 0.88; letter-spacing: -0.01em; color: var(--text); }
.hero-lede { margin: 0 0 34px; max-width: 380px; font: 400 18px/1.5 var(--body); color: var(--text-2); }
.hero-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 28px; }
.hero-footnote { position: absolute; z-index: 2; left: var(--gutter); bottom: 32px; margin: 0; font-size: 14px; color: var(--text-3); }

/* Stage light, behind everything in the hero. Decorative only. */
.hero-bg { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
.hero-bg__beam { position: absolute; top: -33%; height: 167%; transform-origin: top center; will-change: transform; }
/* The main red beam. */
.hero-bg__beam--1 { left: 70.8%; width: 15.3%; transform: rotate(28deg); background: linear-gradient(180deg, rgba(var(--red-rgb), 0.5) 0%, rgba(var(--red-rgb), 0.08) 60%, rgba(var(--red-rgb), 0) 100%); filter: blur(40px); }
/* A thin pale highlight. */
.hero-bg__beam--2 { left: 86.1%; width: 8.3%; transform: rotate(22deg); background: linear-gradient(180deg, rgba(var(--beam-pale-rgb), 0.28) 0%, rgba(var(--red-rgb), 0.06) 55%, rgba(var(--red-rgb), 0) 100%); filter: blur(30px); }
/* A wide soft fill. */
.hero-bg__beam--3 { left: 95.8%; width: 20.8%; transform: rotate(34deg); background: linear-gradient(180deg, rgba(var(--red-rgb), 0.35) 0%, rgba(var(--red-rgb), 0.05) 60%, rgba(var(--red-rgb), 0) 100%); filter: blur(50px); }
/* Where the beams land, under the plugin: the hero's one glow. */
.hero-bg__pool { position: absolute; left: 34.7%; top: 71%; width: 76%; height: 47%; border-radius: 50%; background: rgba(var(--red-rgb), 0.1); filter: blur(90px); }
.hero-bg__vignette { position: absolute; inset: 0; box-shadow: inset 0 0 220px 40px rgba(0, 0, 0, 0.75); }
/* The plugin runs off the bottom into the dark, not into a hard edge. */
.hero-home::after { content: ""; position: absolute; z-index: 1; left: 0; right: 0; bottom: 0; height: 180px; pointer-events: none; background: linear-gradient(180deg, rgba(0, 0, 0, 0), #000); }
/* The lights come up once, beam by beam like a lighting cue, then sway
   very slowly, a few degrees over 14 to 22 seconds. After the opening
   (.cue) they wait for the dark to open. */
@media (prefers-reduced-motion: no-preference) {
    .hero-bg__beam--1 { animation: lights-up 1.4s ease-out calc(var(--cue-wait) + 0.15s) both, beam-sway-1 14s ease-in-out infinite alternate; }
    .hero-bg__beam--2 { animation: lights-up 1.4s ease-out calc(var(--cue-wait) + 0.45s) both, beam-sway-2 18s ease-in-out infinite alternate; }
    .hero-bg__beam--3 { animation: lights-up 1.4s ease-out calc(var(--cue-wait) + 0.75s) both, beam-sway-3 22s ease-in-out infinite alternate; }
    .hero-bg__pool { animation: lights-up 2s ease-out calc(var(--cue-wait) + 0.9s) both; }
}
:root { --cue-wait: 0s; }

/* ---- the opening ----
   The first page of a visit (HEAD_SCRIPT in build-website.py marks the
   rest "returning"), on the home page only: the screen is black, a line of
   red light draws across the middle like an LED strip powering on, and the
   dark opens up and down from that line onto the stage. 1.3 seconds, and
   nothing waits on it: the page is underneath, and clicks go through. Then
   the headline rises word by word and the plugin swings into place. */
.cue { display: none; }
@media (prefers-reduced-motion: no-preference) {
    html:not(.returning) .cue { display: block; position: fixed; inset: 0; z-index: 100; pointer-events: none; animation: cue-done 0s linear 1.35s forwards; }
    html:not(.returning) body:has(.cue) { --cue-wait: 0.75s; }
    .cue::before, .cue::after { content: ""; position: absolute; left: 0; right: 0; height: 50.5%; background: #000; animation: 0.8s var(--ease-cue) 0.55s forwards; }
    .cue::before { top: 0; animation-name: shutter-up; }
    .cue::after { bottom: 0; animation-name: shutter-down; }
    .cue i { position: absolute; z-index: 1; left: 50%; top: 50%; width: min(360px, 60vw); height: 2px; margin: -1px 0 0 calc(min(360px, 60vw) / -2); border-radius: 2px; background: #ff5a4f; box-shadow: 0 0 10px 1px rgba(var(--red-rgb), 0.9), 0 0 60px 10px rgba(var(--red-rgb), 0.35); transform: scaleX(0); animation: cue-line 1.35s var(--ease-cue) forwards; }
    /* The headline, a word at a time, out of the dark below each word... */
    html:not(.returning) .hero-title .w > span { animation: word-up 0.9s cubic-bezier(0.2, 0.75, 0.15, 1) calc(var(--cue-wait) + 0.1s + var(--n) * 0.06s) both; }
    .hero-title .w:nth-child(2) { --n: 1; } .hero-title .w:nth-child(3) { --n: 2; } .hero-title .w:nth-child(4) { --n: 3; }
    .hero-title .w:nth-child(5) { --n: 4; } .hero-title .w:nth-child(6) { --n: 5; } .hero-title .w:nth-child(7) { --n: 6; }
    /* ...the words under it once it's there, and the plugin swinging round
       into its place on the stage. */
    html:not(.returning) .hero-lede, html:not(.returning) .hero-actions, html:not(.returning) .hero-footnote { animation: lights-up 0.8s ease-out calc(var(--cue-wait) + 0.55s) both; }
    html:not(.returning) .hero-shot { animation: shot-in 1.5s cubic-bezier(0.2, 0.75, 0.1, 1) calc(var(--cue-wait) + 0.15s) both; }
}
.hero-title .w { --n: 0; display: inline-block; clip-path: inset(-0.4em -0.3em 0 -0.3em); }
.hero-title .w > span { display: inline-block; }
@keyframes cue-line { 0% { transform: scaleX(0); } 38% { transform: scaleX(1); opacity: 1; } 100% { transform: scaleX(5); opacity: 0; } }
@keyframes shutter-up { to { transform: translateY(-100%); } }
@keyframes shutter-down { to { transform: translateY(100%); } }
@keyframes cue-done { to { visibility: hidden; } }
@keyframes word-up { from { transform: translateY(105%); } }
@keyframes shot-in { from { opacity: 0; transform: perspective(2600px) translateX(14%) rotateY(-28deg) rotateX(6deg); } }
@keyframes lights-up { from { opacity: 0; } }
@keyframes beam-sway-1 { from { transform: rotate(26deg); } to { transform: rotate(30deg); } }
@keyframes beam-sway-2 { from { transform: rotate(21deg); } to { transform: rotate(24deg); } }
@keyframes beam-sway-3 { from { transform: rotate(32deg); } to { transform: rotate(35deg); } }
.hero-shot { position: absolute; z-index: 1; left: calc(var(--gutter) + 410px); top: 150px; width: 1060px; transform-origin: left top; transform: perspective(2600px) rotateY(-12deg) rotateX(3deg); }
/* The bezel: dark anodised metal, its top edge catching the light and its
   right edge lit red by the beams. */
.hero-frame { position: relative; padding: 10px; border-radius: 18px; background: linear-gradient(180deg, #33282a 0%, #171112 38%, #0c0909 100%); box-shadow: inset 0 1px 0 rgba(255, 225, 215, 0.24), inset -1px 0 0 rgba(var(--red-rgb), 0.4), inset 0 -1px 0 rgba(0, 0, 0, 0.6), 0 0 0 1px #0a0707, -50px 80px 100px rgba(0, 0, 0, 0.9); }
/* A thin line of the red along the bezel's bottom edge. */
.hero-frame::after { content: ""; position: absolute; left: 16px; right: 16px; bottom: 0; height: 2px; border-radius: 2px; background: var(--red); opacity: 0.55; }
.hero-screen { position: relative; border-radius: 10px; overflow: hidden; box-shadow: 0 0 0 1px #2c2523; }
.hero-screen picture { display: block; }
.hero-screen img, .hero-screen video { display: block; width: 100%; height: auto; border-radius: 10px; background: #000; }
/* Light on the screen, so it sits in the scene rather than on top of it:
   the beams' red falling across it from the top right... */
.hero-screen::before { content: ""; position: absolute; inset: 0; z-index: 1; border-radius: 10px; pointer-events: none; mix-blend-mode: screen; background: radial-gradient(120% 90% at 100% -10%, rgba(var(--red-rgb), 0.22), rgba(var(--red-rgb), 0.06) 45%, transparent 70%); }
/* ...then the glass's reflection, and the shadow it falls into towards
   the bottom and the left, away from the light. */
.hero-screen::after { content: ""; position: absolute; inset: 0; z-index: 2; border-radius: 10px; pointer-events: none; background: linear-gradient(115deg, rgba(255, 240, 235, 0.07) 0%, rgba(255, 240, 235, 0.02) 28%, transparent 28.4%), linear-gradient(180deg, transparent 45%, rgba(0, 0, 0, 0.55) 100%), linear-gradient(90deg, rgba(0, 0, 0, 0.25) 0%, transparent 30%); }

@media (max-width: 1024px) {
    .hero-home { min-height: 0; padding: 140px 0 0; }
    .hero-copy { width: auto; max-width: 520px; margin-inline: var(--gutter); }
    .hero-footnote { position: static; margin: 26px var(--gutter) 0; }
    .hero-shot { position: relative; left: auto; top: auto; width: 112%; margin: 52px 0 0 var(--gutter); transform: perspective(2600px) rotateY(-6deg) rotateX(3deg); }
}
@media (max-width: 640px) {
    .hero-home { padding: 150px 0 8px; }
    /* Beams start further in and are softer, so the text stays readable. */
    .hero-bg__beam--1 { left: 55%; width: 30%; filter: blur(28px); opacity: 0.8; }
    .hero-bg__beam--2 { left: 75%; width: 16%; filter: blur(22px); opacity: 0.8; }
    .hero-bg__beam--3 { left: 88%; width: 36%; filter: blur(34px); opacity: 0.7; }
    .hero-bg__pool { left: 0; width: 100%; top: 65%; }
    .hero-bg__vignette { box-shadow: inset 0 0 120px 20px rgba(0, 0, 0, 0.75); }
    .hero-home::after { height: 90px; }
    .hero-shot { width: auto; margin: 40px var(--gutter) 0; transform: none; }
}

/* ---- the coming-soon page's opening: the sign-up beside the plugin,
   under the home hero's stage light ---- */
.hero-split { position: relative; overflow: hidden; isolation: isolate; padding: 168px 0 112px; }
.hero-split-grid { position: relative; z-index: 2; display: grid; grid-template-columns: 5fr 6fr; gap: 56px 64px; align-items: center; }
.hero-split .lede { margin-bottom: 8px; }
.hero-split-shot { transform: perspective(2600px) rotateY(-10deg) rotateX(2deg); transform-origin: left center; }
@media (max-width: 860px) {
    .hero-split { padding: 140px 0 80px; }
    .hero-split-grid { grid-template-columns: 1fr; }
    .hero-split-shot { transform: none; }
}
@media (max-width: 640px) { .hero-split { padding-top: 150px; } }

/* ---- one plugin, not five: a mixer channel's insert slots ----
   Two strips side by side: five plugins on a vocal, and the same five
   effects inside one StabLib FX slot, with the other slots left free. */
.swap-grid { display: grid; grid-template-columns: 6fr 5fr; gap: 48px 80px; align-items: center; }
.inserts { margin: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 16px 24px; max-width: 480px; }
.inserts figcaption { grid-column: 1 / -1; margin-top: 8px; }
.strip { padding: 14px; border-radius: 12px; background: var(--chassis); border: 1px solid var(--rim); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06); }
.strip-name { margin: 0 0 12px; font-size: 14px; color: var(--text-2); }
.slots { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.slots li { display: flex; align-items: center; gap: 10px; height: 38px; padding: 0 12px; border-radius: 6px; background: var(--chassis-2); border: 1px solid var(--rim); font-size: 14px; font-weight: 500; color: var(--text); white-space: nowrap; }
.slots li::before { content: ""; flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--c, var(--text-2)); box-shadow: 0 0 8px var(--c, transparent); }
.slots li.free { color: var(--text-3); font-weight: 400; }
.slots li.free::before { background: var(--rim); box-shadow: none; }
.slots li.lit { --c: var(--red); border-color: rgba(var(--red-rgb), 0.55); background: linear-gradient(180deg, #2a1514, #1d1110); }
/* The five effects inside the one slot. */
.slots .leds { display: flex; gap: 4px; margin-left: auto; }
.slots .leds i { width: 5px; height: 5px; border-radius: 50%; background: var(--c); }
/* The five plugins that fold into it, laid exactly over its slots; only
   shown where the scroll can drive them (further down). */
.strip-one { display: grid; }
.strip-one > .slots { grid-area: 2 / 1; }
.slots-fold { display: none; }

.steps { list-style: none; counter-reset: step; margin: var(--block-gap) 0 0; padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: 40px; }
.steps li { counter-increment: step; }
.steps li::before { content: counter(step); display: block; margin-bottom: 14px; font-weight: 800; font-variation-settings: "wdth" 75; font-size: 44px; line-height: 1; color: var(--red); }
.steps p { color: var(--text-2); margin: 0; }
@media (max-width: 860px) {
    .swap-grid { grid-template-columns: 1fr; }
    .steps { grid-template-columns: 1fr; gap: 32px; }
}

/* ---- the effects, by type, in the plugin's own categories and colours ----
   Each is a radio button: picking one lights it up like a slot in the
   plugin's effect list, and shows its preview (module and description)
   beside the list. site.js moves it level with the effect just picked, so
   it opens where people are looking, and on a phone opens it under the row
   the effect was picked from. Without the script it sits at the top of its
   column, or above the list on a phone. */
#features { overflow: clip; }

/* The second row takes any extra height, so a preview moved down beside the
   list never pushes the list itself down. */
.effects-layout { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); grid-template-rows: auto 1fr; grid-template-areas: "head preview" "list preview"; gap: 0 clamp(32px, 5vw, 72px); align-items: start; }
.effects-layout > .section-head { grid-area: head; }
.effects-layout > .chain { grid-area: list; }
.effects-layout > .fx-previews { grid-area: preview; }
/* Two columns of categories where there's room, one where there isn't. */
.chain { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 32px 24px; }
.group { padding-top: 16px; border-top: 1px solid var(--rim); }
.group h3 { margin: 0 0 8px; font-size: 14px; font-weight: 500; color: var(--text-3); }
.group ul { list-style: none; margin: 0 -10px; padding: 0; }
.group li { position: relative; }
.group input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.group label { display: flex; align-items: center; gap: 12px; padding: 6px 10px; border-radius: 8px; font-size: 18px; font-weight: 500; color: #d9d6d1; cursor: pointer; transition: background-color 0.15s ease, color 0.15s ease; }
.group label::before { content: ""; flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--c); opacity: 0.7; }
.group label:hover { background: var(--chassis); color: var(--text); }
.group input:checked + label { color: #fff; background: var(--chassis); background: color-mix(in srgb, var(--c) 14%, var(--chassis)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c) 50%, transparent); }
.group input:checked + label::before { opacity: 1; box-shadow: 0 0 10px var(--c); }
.group input:focus-visible + label { outline: 2px solid var(--red); outline-offset: 2px; }

/* Only the picked effect's preview shows. The ids come from effects.json. */
.fx-preview { display: none; margin: 0; position: relative; isolation: isolate; }
/* The picked module sits in a pool of its own colour, as if its LED lit
   the stage. */
.fx-preview::before { content: ""; position: absolute; z-index: -1; left: -12%; top: -8%; width: 110%; height: 75%; border-radius: 50%; background: var(--c); opacity: 0.1; filter: blur(70px); pointer-events: none; }
.effects-layout:has(#fx-gate:checked) #fxp-gate,
.effects-layout:has(#fx-shaper:checked) #fxp-shaper,
.effects-layout:has(#fx-eq:checked) #fxp-eq,
.effects-layout:has(#fx-pitch:checked) #fxp-pitch,
.effects-layout:has(#fx-distortion:checked) #fxp-distortion,
.effects-layout:has(#fx-bitcrusher:checked) #fxp-bitcrusher,
.effects-layout:has(#fx-doubler:checked) #fxp-doubler,
.effects-layout:has(#fx-chorus:checked) #fxp-chorus,
.effects-layout:has(#fx-flanger:checked) #fxp-flanger,
.effects-layout:has(#fx-tremolo:checked) #fxp-tremolo,
.effects-layout:has(#fx-autopan:checked) #fxp-autopan,
.effects-layout:has(#fx-delay:checked) #fxp-delay,
.effects-layout:has(#fx-reverb:checked) #fxp-reverb,
.effects-layout:has(#fx-reverse:checked) #fxp-reverse { display: block; }
/* A browser that can't do that shows the first one, and the list stays a list. */
@supports not selector(:has(*)) { #fxp-pitch { display: block; } }
.fx-preview img { display: block; width: 100%; max-width: 520px; height: auto; -webkit-mask-image: linear-gradient(90deg, #000 78%, transparent), linear-gradient(180deg, #000 80%, transparent); -webkit-mask-composite: source-in; mask-image: linear-gradient(90deg, #000 78%, transparent), linear-gradient(180deg, #000 80%, transparent); mask-composite: intersect; }
.fx-preview figcaption { margin-top: 8px; max-width: 34em; }
.fx-preview h3 { display: flex; align-items: center; gap: 12px; font-size: 24px; letter-spacing: -0.01em; }
.fx-preview h3::before { content: ""; width: 10px; height: 10px; border-radius: 50%; background: var(--c); box-shadow: 0 0 12px var(--c); }
.fx-preview p { color: var(--text-2); }
.fx-preview .small strong { color: var(--text-2); font-weight: 600; }
/* Changing effect: the new one fades in, so it's clear what changed. */
@media (prefers-reduced-motion: no-preference) {
    .fx-preview { animation: fx-in 0.25s ease-out; }
}
@keyframes fx-in { from { opacity: 0; } }
@media (min-width: 700px) and (prefers-reduced-motion: no-preference) {
    .effects-layout > .fx-previews { transition: margin-top 0.3s ease-out; }
}
/* The browser's scroll anchoring would scroll the page to make up for the
   preview moving, undoing the point of moving it. Nothing else on the home
   page changes size after it loads, so it isn't needed there. */
html:has(.effects-layout) { overflow-anchor: none; }
@media (max-width: 699px) {
    .effects-layout { grid-template-columns: 1fr; grid-template-areas: "head" "preview" "list"; }
    .effects-layout > .fx-previews { margin-bottom: 40px; }
    /* Opened under the row it was picked from (site.js). */
    .chain > .fx-previews { grid-column: 1 / -1; margin: -8px 0 8px; }
}

/* ---- the plugin tour: point at, tap or pick a part of the plugin, and a
   spotlight picks it out (the rest dims, like a stage) while its words
   appear beside it, or under the picture on narrow screens. site.js adds
   .tour-live; without it, the words are listed under the picture. ---- */
.tour-stage { position: relative; overflow: hidden; border-radius: 12px; border: 1px solid var(--rim); box-shadow: 0 40px 100px rgba(0, 0, 0, 0.6); }
.tour-stage picture, .tour-stage img { display: block; width: 100%; height: auto; }
.tour-hotspot { position: absolute; z-index: 2; border-radius: 8px; cursor: pointer; }
/* A small "+" on each part, in dark glass, so the plugin itself stays the
   colourful thing. */
.tour-live .tour-hotspot::after { content: ""; position: absolute; right: -9px; top: -9px; width: 20px; height: 20px; border-radius: 50%; background: linear-gradient(var(--text), var(--text)) center / 8px 1.5px no-repeat, linear-gradient(var(--text), var(--text)) center / 1.5px 8px no-repeat, rgba(12, 11, 10, 0.88); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.3), 0 2px 10px rgba(0, 0, 0, 0.5); transition: opacity 0.2s ease, transform 0.2s ease; }
.tour-live .tour-hotspot:hover::after { transform: scale(1.15); }
.tour-active .tour-hotspot::after { opacity: 0.45; }
.tour-active .tour-hotspot.is-current::after { opacity: 0; }
/* The spotlight: everything else dims, and the part gets a soft lit rim. */
.tour-spot { position: absolute; z-index: 1; border-radius: 8px; opacity: 0; pointer-events: none; box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0.65), 0 0 28px rgba(255, 255, 255, 0.14), 0 0 0 200vmax rgba(0, 0, 0, 0.62); }
.tour-active .tour-spot { opacity: 1; }
.tour-callout { position: absolute; z-index: 3; left: 0; top: 0; width: 320px; padding: 16px 20px; border-radius: 12px; background: rgba(12, 11, 10, 0.96); border: 1px solid var(--rim-lit); box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6); opacity: 0; pointer-events: none; }
.tour-active .tour-callout { opacity: 1; }
.tour-callout h3, .tour-panel h3 { margin: 0 0 6px; font-size: 18px; }
.tour-callout p, .tour-panel p { margin: 0; font-size: 15px; color: var(--text-2); }
.tour-panel { display: none; margin-top: 20px; }
/* Room for the longest words, once there are some, so the list doesn't jump. */
.tour-active .tour-panel { min-height: 176px; }
.tour-list { display: none; list-style: none; margin: 24px -10px 0; padding: 0; flex-wrap: wrap; gap: 4px; }
.tour-live .tour-list { display: flex; }
.tour-list button { padding: 6px 10px; border: 0; border-radius: 8px; background: none; font: 500 15px/1.4 var(--body); color: var(--text-2); cursor: pointer; }
.tour-list button:hover { background: var(--chassis); color: var(--text); }
.tour-list button[aria-pressed="true"] { color: #fff; background: var(--chassis-2); box-shadow: inset 0 0 0 1px var(--rim-lit); }
.tour-list button:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }
.tour-words { margin: 32px 0 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 24px 40px; }
.tour-words dt { font-weight: 600; }
.tour-words dd { margin: 4px 0 0; color: var(--text-2); }
.tour-live .tour-words { display: none; }
@media (prefers-reduced-motion: no-preference) {
    .tour-spot { transition: left 0.3s ease, top 0.3s ease, width 0.3s ease, height 0.3s ease, opacity 0.2s ease; }
    .tour-callout { transition: transform 0.3s ease, opacity 0.2s ease; }
}
/* Narrow screens: the words go under the picture instead of over it. */
@media (max-width: 899px) {
    .tour-callout { display: none; }
    .tour-live .tour-panel { display: block; }
    /* The list straight under the picture, the words under the list, so
       the words appearing never moves what someone is about to tap. */
    .tour-live { display: flex; flex-direction: column; }
    .tour-live .tour-list { order: 1; margin-top: 16px; }
    .tour-live .tour-panel { order: 2; }
    .tour-live .tour-hotspot::after { width: 14px; height: 14px; right: -6px; top: -6px; background-size: 6px 1.5px, 1.5px 6px, auto; }
}

/* ---- tied to the scroll ----
   Only in browsers that can run an animation off the scroll position, and
   not for reduced motion; everywhere else each part simply sits in its
   final place. */
@supports (animation-timeline: view()) {
    /* The sheets these sit in stay stuck to the screen until the end of the
       page, and a browser counts all of that time in a scroll timeline's
       "cover" and "exit" ranges. So every range here is measured from when
       the thing has come fully into view (contain 0%), in pixels of scroll. */
    @media (prefers-reduced-motion: no-preference) {
        /* The tour's plugin comes up at the hero's angle and turns to face
           you as it rises into view, so it reads as the same window
           stepping forward. */
        .tour-stage { transform-origin: 20% 60%; animation: face-you linear both; animation-timeline: view(); animation-range: entry 0% entry 95%; }

        /* Five plugins fold into one: the second strip starts as a copy of
           the first, and as it rises to the middle of the screen the five
           slide up into the top slot, which lights up as StabLib FX, its
           five LEDs coming on one by one, and leaves four slots free. */
        .strip-one { view-timeline: --fold block; }
        .slots-fold { display: grid; }
        .slots-fold li { --i: 0; animation: fold-up linear both; animation-timeline: --fold; animation-range: contain 80px contain 340px; }
        .slots-fold li:nth-child(2) { --i: 1; } .slots-fold li:nth-child(3) { --i: 2; } .slots-fold li:nth-child(4) { --i: 3; } .slots-fold li:nth-child(5) { --i: 4; }
        .strip-one .slots .lit { animation: lights-up linear both; animation-timeline: --fold; animation-range: contain 260px contain 360px; }
        .strip-one .leds i { animation: led-on linear both; animation-timeline: --fold; }
        .strip-one .leds i:nth-child(1) { animation-range: contain 340px contain 370px; }
        .strip-one .leds i:nth-child(2) { animation-range: contain 370px contain 400px; }
        .strip-one .leds i:nth-child(3) { animation-range: contain 400px contain 430px; }
        .strip-one .leds i:nth-child(4) { animation-range: contain 430px contain 460px; }
        .strip-one .leds i:nth-child(5) { animation-range: contain 460px contain 490px; }

    }
}
@keyframes face-you { from { transform: perspective(2600px) rotateY(-14deg) rotateX(5deg) scale(0.9); } }
/* Each folding slot travels up to the top one (44px a slot: 38px and the
   6px gap), going out as it gets there. */
@keyframes fold-up { 70% { opacity: 1; } to { transform: translateY(calc(var(--i) * -44px)); opacity: 0; } }
@keyframes led-on { from { opacity: 0.15; box-shadow: none; } to { box-shadow: 0 0 6px var(--c); } }

.pair { display: grid; grid-template-columns: 1fr 1fr; gap: 32px 40px; margin: 40px 0; }
.pair p { margin: 0; color: var(--text-2); }
@media (max-width: 600px) { .pair { grid-template-columns: 1fr; } }
/* A short list beside its heading, one item to a row (the coming-soon
   page's "What you get for signing up"). */
.perks { margin: 0; }
.perks div { padding: 20px 0; border-top: 1px solid var(--rim); }
.perks div:last-child { border-bottom: 1px solid var(--rim); }
.perks dt { font-weight: 600; font-size: 19px; }
.perks dd { margin: 4px 0 0; color: var(--text-2); max-width: 34em; }

/* ---- the last section: the price, and the list ---- */
.closing { border-top: 1px solid var(--rim); }
.split-even { grid-template-columns: 1fr 1fr; gap: 56px 80px; }
@media (max-width: 860px) { .split-even { grid-template-columns: 1fr; } }

/* ---- quotes and demos (shown once there are some) ---- */
.grid { display: grid; gap: 40px; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.quote { margin: 0; }
.quote blockquote { margin: 0 0 18px; font-size: 19px; line-height: 1.5; color: var(--text); }
.quote .who { display: flex; align-items: center; gap: 12px; }
.quote .who img { width: 44px; height: 44px; border-radius: 50%; object-fit: cover; }
.quote .who strong { display: block; font-weight: 600; }
.quote .who span { font-size: 14px; color: var(--text-3); }

/* ---- before and after ----
   One panel per demo: its waveform, a play button, and a switch between the
   take without and with StabLib FX. site.js plays both in lockstep, so the
   switch is instant and keeps the place. Until it runs (or if it can't), the
   plain audio players in .ab-plain are shown instead. The waveform is drawn
   twice, dim and lit; the lit copy is cut off at the playhead (--p). */
.ab-list { display: grid; gap: 20px; max-width: 880px; }
.ab { --p: 0%; padding: 24px 28px 28px; border-radius: 14px; background: var(--chassis); border: 1px solid var(--rim); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06); }
.ab-head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; margin-bottom: 16px; }
.ab-head h3 { margin: 0; font-size: 22px; }
.ab-time { font-size: 14px; color: var(--text-3); font-variant-numeric: tabular-nums; }
.ab-blurb { margin: -8px 0 16px; color: var(--text-2); }
.ab-plain { display: grid; gap: 6px; }
.ab-plain audio { width: 100%; margin-bottom: 10px; }
.ab-plain .label { font-size: 14px; color: var(--text-3); }
.ab-ui[hidden], .ab-plain[hidden] { display: none; }
.ab-wave { position: relative; height: 76px; border-radius: 8px; background: var(--chassis-2); border: 1px solid var(--rim); overflow: hidden; }
.ab-bars { position: absolute; inset: 0; width: 100%; height: 100%; }
.ab-bars path { fill: currentColor; }
.ab-bars .fx, .ab[data-version="fx"] .ab-bars .dry { display: none; }
.ab[data-version="fx"] .ab-bars .fx { display: inline; }
.ab-bars.dim { color: var(--rim-lit); }
.ab-bars.lit { color: var(--text-2); clip-path: inset(0 calc(100% - var(--p)) 0 0); }
.ab[data-version="fx"] .ab-bars.lit { color: var(--red); }
.ab-wave::after { content: ""; position: absolute; top: 0; bottom: 0; left: var(--p); width: 2px; margin-left: -1px; background: var(--text); pointer-events: none; }
/* The slider over the waveform is the seek bar: invisible, but it's what
   a tap, a drag or the arrow keys move. */
.ab-seek { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.ab-wave:has(.ab-seek:focus-visible) { outline: 2px solid var(--red); outline-offset: 2px; }
.ab-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; margin-top: 18px; }
.ab-play { position: relative; flex: none; width: 56px; height: 56px; padding: 0; border-radius: 50%; border: 1px solid transparent; background: var(--red-button); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14); cursor: pointer; }
.ab-play:hover { box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3), 0 0 0 1px rgba(var(--red-rgb), 0.5), 0 8px 28px -6px rgba(var(--red-rgb), 0.75); }
.ab-play:active { transform: translateY(1px); box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.35); }
.ab-play::before { content: ""; position: absolute; left: 50%; top: 50%; transform: translate(-35%, -50%); border-style: solid; border-width: 10px 0 10px 16px; border-color: transparent transparent transparent #fff; }
.ab[data-playing] .ab-play::before { width: 5px; height: 20px; border: 0; background: #fff; box-shadow: 10px 0 0 #fff; transform: translate(-7.5px, -50%); }
.ab[data-loading] .ab-play { opacity: 0.6; cursor: progress; }
.ab-switch { display: inline-flex; flex: 1 1 280px; max-width: 420px; padding: 4px; border-radius: 999px; background: var(--chassis-2); border: 1px solid var(--rim); }
.ab-v { flex: 1; min-height: 44px; padding: 0 18px; border: 0; border-radius: 999px; background: transparent; color: var(--text-2); font: 600 15px/1.2 var(--body); cursor: pointer; }
.ab-v:hover { color: var(--text); }
.ab-v[aria-pressed="true"] { color: #fff; background: #2d2b28; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); }
.ab-v[data-version="fx"][aria-pressed="true"] { background: var(--red-button); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14), 0 0 18px -4px rgba(var(--red-rgb), 0.7); }
@media (max-width: 640px) { .ab { padding: 20px 18px 22px; } .ab-switch { max-width: none; flex-basis: 220px; } .ab-v { padding: 0 10px; font-size: 14px; white-space: nowrap; } }

/* ---- pricing ---- */
.plans { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 24px; max-width: 800px; }
.plan { display: flex; flex-direction: column; padding: 32px; border-radius: 14px; background: var(--chassis); border: 1px solid var(--rim); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06); }
/* The yearly plan is the one with the red button and the saving; the panels
   themselves are the same, with no coloured border picking one out. */
.plans-title { font-size: 24px; margin-bottom: 20px; }
.plan h3 { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin: 0 0 20px; font-size: 24px; letter-spacing: -0.01em; }
.saving { font-size: 15px; font-weight: 600; letter-spacing: 0; color: #ff8078; }
.price { margin: 0 0 6px; font-weight: 800; font-variation-settings: "wdth" 75; font-size: 64px; line-height: 1; }
.price small { font-weight: 400; font-variation-settings: "wdth" 100; font-size: 17px; color: var(--text-2); }
.plan .button { margin-top: auto; width: 100%; }
.plan .muted { margin: 0 0 28px; }
/* Studio: the same panel, laid out across, under the two personal plans. */
.plan.studio { display: grid; grid-template-columns: 1fr 1fr; gap: 16px 40px; max-width: 800px; margin-top: 24px; }
.plan.studio h3 { margin-bottom: 8px; }
.plan.studio .muted { margin: 0; }
.plan.studio .actions { margin: 16px 0 12px; }
.plan.studio .button { width: auto; margin-top: 0; }
.plan.studio .small { margin: 0; }
.studio-prices { margin: 0; font-weight: 800; font-variation-settings: "wdth" 75; font-size: 34px; line-height: 1.1; }
.studio-prices small { font-weight: 400; font-variation-settings: "wdth" 100; font-size: 16px; color: var(--text-2); }
.studio-prices .saving { margin-left: 4px; font-variation-settings: "wdth" 100; }
@media (max-width: 700px) { .plan.studio { grid-template-columns: 1fr; } }
.plan-includes { margin-top: var(--block-gap); max-width: 720px; }
.ticks { list-style: none; padding: 0; margin: 24px 0; }
.ticks li { padding: 10px 0 10px 32px; position: relative; border-bottom: 1px solid var(--rim); }
.ticks li::before { content: ""; position: absolute; left: 4px; top: 18px; width: 12px; height: 7px; border-left: 2px solid var(--red); border-bottom: 2px solid var(--red); transform: rotate(-45deg); }

/* ---- faq ---- */
details { border-bottom: 1px solid var(--rim); padding: 18px 0; }
summary { cursor: pointer; font-weight: 600; font-size: 18px; list-style: none; display: flex; justify-content: space-between; gap: 16px; }
summary::-webkit-details-marker { display: none; }
summary::after { content: "+"; color: var(--red); font-weight: 400; font-size: 24px; line-height: 1; }
details[open] summary::after { content: "\2212"; }
details p { color: var(--text-2); margin: 12px 0 0; }

/* ---- forms ---- */
.signup { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 24px; }
.signup input[type="email"] { flex: 1 1 240px; max-width: 360px; min-height: 48px; padding: 0 20px; border-radius: 999px; border: 1px solid var(--rim-lit); background: var(--chassis); color: var(--text); font: 400 16px var(--body); }
.signup input[type="email"]::placeholder { color: var(--text-3); }
.signup input[type="email"]:focus { border-color: var(--text-2); outline: 2px solid var(--red); outline-offset: 2px; }
.signup-status { margin-top: 12px; }
.signup-status.signup-error { color: #ff8078; }

/* ---- the preset library (/presets, presets.js) ----
   Each preset is a strip like a module in the plugin's chain: its effects
   as lit chips in their own module colours, in order. */
.library-tabs { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 20px; }
.library-tabs [role="tab"] { min-height: 44px; padding: 0 20px; border-radius: 999px; border: 1px solid var(--rim); background: var(--chassis); color: var(--text-2); font: 600 16px/1 var(--body); cursor: pointer; }
.library-tabs [role="tab"]:hover { color: var(--text); border-color: var(--rim-lit); }
.library-tabs [role="tab"][aria-selected="true"] { color: var(--text); border-color: var(--red); box-shadow: inset 0 0 0 1px rgba(var(--red-rgb), 0.5); }
.library-tabs [role="tab"] span { color: var(--text-3); font-weight: 500; margin-left: 4px; }
.library-share { margin-left: auto; min-height: 44px; }
.library-tools { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
.library-tools input, .library-tools select, .share-box input[type="text"], .share-box input[type="email"], .share-box input[type="password"], .share-box select {
    min-height: 44px; padding: 0 16px; border-radius: 12px; border: 1px solid var(--rim-lit); background: var(--chassis); color: var(--text); font: 400 16px var(--body); }
.library-tools input { flex: 1 1 260px; }
.library-tools select { flex: 0 1 200px; }
.library-tools :focus, .share-box :focus-visible, .preset button:focus-visible, .preset a:focus-visible, .library-tabs :focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }
.library-summary { margin: 0 0 12px; font-size: 15px; }
.preset-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.preset { display: flex; gap: 16px 24px; align-items: center; padding: 16px 20px; border-radius: 16px; background: var(--chassis); border: 1px solid var(--rim); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04); }
.preset-main { flex: 1 1 auto; min-width: 0; }
.preset-meta { margin: 0 0 2px; font-size: 12px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--red); }
.preset-name { margin: 0; font-size: 19px; overflow-wrap: anywhere; }
.preset-credit { margin: 2px 0 0; font-size: 14px; overflow-wrap: anywhere; }
.preset-chain { list-style: none; margin: 10px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.preset-chain li { font-size: 11px; font-weight: 700; letter-spacing: 0.08em; padding: 3px 8px 3px 18px; border-radius: 6px; background: var(--chassis-2); border: 1px solid var(--rim); color: var(--text-2); position: relative; }
.preset-chain li::before { content: ""; position: absolute; left: 7px; top: 50%; width: 6px; height: 6px; margin-top: -3px; border-radius: 50%; background: var(--c); box-shadow: 0 0 6px var(--c); }
.preset-side { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }
.thumbs { display: flex; gap: 4px; }
.thumb { display: inline-flex; align-items: center; gap: 6px; min-height: 40px; min-width: 56px; padding: 0 10px; border-radius: 999px; border: 1px solid var(--rim); background: transparent; color: var(--text-2); font: 600 14px var(--body); cursor: pointer; }
.thumb svg { width: 16px; height: 16px; fill: currentColor; }
.thumb:hover { color: var(--text); border-color: var(--rim-lit); }
.thumb.up[aria-pressed="true"] { color: #fff; border-color: var(--red); background: rgba(var(--red-rgb), 0.18); }
.thumb.down[aria-pressed="true"] { color: var(--text); border-color: var(--text-2); background: rgba(255, 255, 255, 0.06); }
.preset-download.button { min-height: 40px; padding: 0 18px; font-size: 15px; }
.preset-report { min-height: 40px; padding: 0 6px; border: 0; background: none; color: var(--text-3); font: 500 13px var(--body); text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
.preset-report:hover { color: var(--text); }
.preset-included { white-space: nowrap; padding: 0 8px; }
.library-more { margin-top: 20px; }
@media (max-width: 720px) {
    .preset { flex-direction: column; align-items: stretch; }
    .preset-side { flex-wrap: wrap; }
    .library-share { margin-left: 0; }
}
.library-share-section { border-top: 1px solid var(--rim); }
.library-steps { grid-template-columns: 1fr; gap: 20px; margin: 32px 0 40px; }
.library-steps li { display: grid; grid-template-columns: 44px 1fr; gap: 0 12px; color: var(--text-2); }
.library-steps li::before { grid-row: span 2; font-size: 32px; margin: 0; }
.library-steps strong { color: var(--text); }
.share-box { padding: 28px; border-radius: 20px; background: var(--chassis); border: 1px solid var(--rim); margin-bottom: 48px; }
.share-box form { display: grid; gap: 8px; }
.share-box label { font-size: 15px; font-weight: 600; margin-top: 8px; }
.share-box input[type="file"] { font: 400 15px var(--body); color: var(--text-2); }
.share-box .actions { margin: 16px 0 0; }
.share-box h3 { margin-bottom: 4px; }
.share-box p.small { margin: 0; }
.share-status { margin: 8px 0 0; min-height: 1.4em; }
.share-status.share-error { color: #ff8078; }
.share-signout { min-height: 48px; border: 0; background: none; cursor: pointer; }
.share-results { list-style: none; margin: 8px 0 0; padding: 0; display: grid; gap: 6px; }
.share-result { padding: 10px 14px; border-radius: 10px; background: var(--chassis-2); border: 1px solid var(--rim); font-size: 15px; overflow-wrap: anywhere; }
.share-result.rejected { border-color: rgba(var(--red-rgb), 0.6); }
.share-result.live { border-color: #3c8a4a; }
.share-result p { margin: 4px 0 0; }
.share-mine { margin-top: 28px; padding-top: 20px; border-top: 1px solid var(--rim); }
.report-dialog { max-width: min(440px, calc(100% - 32px)); padding: 28px; border-radius: 20px; border: 1px solid var(--rim-lit); background: var(--chassis); color: var(--text); }
.report-dialog::backdrop { background: rgba(0, 0, 0, 0.7); }
.report-dialog form { display: grid; gap: 10px; }
.report-dialog label { display: flex; gap: 10px; align-items: center; min-height: 32px; }
.report-dialog .actions { margin: 12px 0 0; }
.admin-row { display: grid; gap: 6px; padding: 14px 18px; border-radius: 14px; background: var(--chassis); border: 1px solid var(--rim); }

/* ---- /buy: account, plan, payment (buy.js) ---- */
.buy-steps { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 20px; }
.buy-step h2 { display: flex; align-items: center; gap: 14px; font-size: 26px; margin-bottom: 16px; }
.buy-num { display: inline-grid; place-items: center; width: 36px; height: 36px; border-radius: 50%; background: var(--red-button); color: #fff; font-size: 18px; flex: 0 0 auto; }
.buy-step.done .buy-num { background: #2f7d43; }
.buy-tabs { margin-bottom: 8px; }
.buy-trial-hint { margin: 4px 0 12px; font-size: 15px; color: var(--text-2); }
.buy-trial-hint .text-link { min-height: 0; font-size: 15px; border: 0; background: none; padding: 0; cursor: pointer; }
.buy-check { display: flex; gap: 10px; align-items: center; font-weight: 400 !important; }
.buy-signed-in p { margin: 0 0 4px; }
.buy-plans { border: 0; margin: 0 0 12px; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); gap: 12px; }
.buy-plan { position: relative; display: grid; gap: 4px; padding: 18px 20px 18px 52px; border-radius: 14px; border: 1px solid var(--rim-lit); background: var(--chassis-2); cursor: pointer; margin: 0 !important; }
.buy-plan input { position: absolute; left: 20px; top: 22px; width: 18px; height: 18px; accent-color: var(--red); }
.buy-plan:has(input:checked) { border-color: var(--red); box-shadow: inset 0 0 0 1px rgba(var(--red-rgb), 0.6); }
.buy-plan:has(input:focus-visible) { outline: 2px solid var(--red); outline-offset: 2px; }
.buy-plan-name { display: flex; gap: 10px; align-items: baseline; justify-content: space-between; font-size: 18px; font-weight: 700; }
.buy-plan-price { font-size: 28px; font-weight: 800; font-variation-settings: "wdth" 80; }
.buy-plan-price small { font-size: 15px; font-weight: 400; color: var(--text-2); }
.buy-plan-note { font-size: 14px; font-weight: 400; color: var(--text-2); }
.buy-done h2 { font-size: 32px; }
.buy-done-steps { padding-left: 20px; color: var(--text-2); }
.buy-done-steps li { margin-bottom: 6px; }
@media (max-width: 480px) { .share-box { padding: 20px 16px; } .buy-plan { padding-left: 46px; } .buy-plan input { left: 16px; } }

/* ---- legal and other reading pages ---- */
.prose { padding: 72px 0 96px; }
.prose h1 { font-size: clamp(38px, 5vw, 60px); margin-bottom: 32px; }
.prose h2 { font-size: 26px; margin-top: 48px; }
.prose p, .prose li { color: #e2dfda; }
.prose table { width: 100%; border-collapse: collapse; font-size: 15px; margin: 20px 0; display: block; overflow-x: auto; }
.prose th, .prose td { border: 1px solid var(--rim); padding: 10px; text-align: left; vertical-align: top; }
.prose th { background: var(--chassis); }
.prose code { font: 0.9em ui-monospace, Menlo, monospace; background: var(--chassis); padding: 2px 5px; border-radius: 4px; }

/* ---- footer ---- */
.site-footer { border-top: 1px solid var(--rim); padding: 40px 0 56px; color: var(--text-3); font-size: 14px; }
.site-footer .wrap { display: flex; flex-wrap: wrap; gap: 20px 40px; justify-content: space-between; }
.site-footer p { margin: 0; max-width: 40em; }
.site-footer nav { display: flex; flex-wrap: wrap; gap: 8px 20px; }
.site-footer .social { flex-basis: 100%; }
.site-footer a { color: var(--text-2); text-decoration: none; }
.site-footer a:hover { color: var(--text); }

/* ---- from one page to the next ----
   The page being left drops back into the dark, and the new one rises over
   it from the bottom of the screen, its top corners rounded like a sheet
   on the home page, then squaring off as it fills the screen. The header
   stays where it is throughout. Browsers without view transitions simply
   change page. */
@media (prefers-reduced-motion: no-preference) {
    @view-transition { navigation: auto; }
}
.site-header { view-transition-name: site-header; }
::view-transition-old(root) { animation: page-back 0.75s var(--ease-cue) both; }
::view-transition-new(root) { border-radius: var(--sheet-radius) var(--sheet-radius) 0 0; box-shadow: 0 -1px 0 var(--rim-lit), 0 -30px 60px rgba(0, 0, 0, 0.6); animation: page-over 0.75s var(--ease-cue) both; }
::view-transition-group(site-header) { animation-duration: 0.75s; animation-timing-function: var(--ease-cue); }
@keyframes page-back { to { transform: scale(0.94); opacity: 0.25; } }
@keyframes page-over { from { transform: translateY(100vh); } 80% { border-radius: var(--sheet-radius) var(--sheet-radius) 0 0; } to { transform: none; border-radius: 0; } }

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation: none !important; transition: none !important; }
}
