/* Postie's tokens (Postie public/style.css), unchanged. See .impeccable.md for why. */
:root {
  --ink: #111318; --muted: #6b7079; --line: #ececee; --soft: #f7f7f8; --hover: #f1f2f4; --active: #eceef1;
  --primary: #b9f081; --primary-hover: #a8e76c; --primary-edge: #a2dc68; --primary-ink: #111318; --primary-soft: #f5fcee;
  --danger: #b42318;
  --r-xs: 4px; --r-sm: 6px; --r: 8px; --r-lg: 12px; --r-xl: 14px;

  /* Five type steps for the whole page */
  /* no 200px cap: past 1540px a capped hero stretched the strip to 5:1 and cut the charm letters */
  --t-hero: max(64px, 13vw);
  --t-xl: clamp(40px, 6vw, 96px);
  --t-l: clamp(26px, 2.6vw, 40px);
  --t-m: 18px;
  --t-s: 15px;
  /* Two in-between sizes: each product's name (shared with Workie's stage clauses) and the sentence that introduces it */
  --t-name: clamp(36px, 4.6vw, 76px);
  --t-lead: clamp(18px, 1.6vw, 26px);

  /* One pair of margins, one section gap */
  --margin: clamp(16px, 2vw, 28px);
  --gap-section: clamp(120px, 11vw, 160px);

  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
}
*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
body {
  margin: 0; background: #fff; color: var(--ink);
  font: 400 var(--t-m)/1.55 Inter, system-ui, -apple-system, "Segoe UI", sans-serif;
  font-optical-sizing: auto; text-rendering: optimizeLegibility;
}
img, video { display: block; max-width: 100%; }
a { color: inherit; }
button, input { font: inherit; color: inherit; }
.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; }
:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; border-radius: var(--r-xs); }
::selection { background: var(--primary); color: var(--primary-ink); }

/* Buttons: one shape for the page, Postie's lime, 14px corners */
.btn {
  display: inline-flex; align-items: center; justify-content: center; min-height: 48px; padding: 0 22px;
  border: 0; border-radius: var(--r-xl); background: var(--primary); color: var(--primary-ink);
  font-weight: 600; font-size: var(--t-s); text-decoration: none; cursor: pointer;
  transition: transform 140ms var(--ease-out), background-color 160ms ease;
}
.btn:active { transform: scale(0.97); }
@media (hover: hover) and (pointer: fine) { .btn:hover { background: var(--primary-hover); } }

.page { padding: 0 var(--margin); }
.section { margin-top: var(--gap-section); }

/* Nav: a line over the hero, not a bar */
.nav { position: absolute; inset: 0 0 auto; z-index: 1; display: flex; justify-content: space-between; padding-top: 12px; font-size: var(--t-s); }
.nav a { display: inline-flex; align-items: center; min-height: 44px; text-decoration: none; font-weight: 500; }
.nav .mark { min-width: 44px; font-weight: 700; letter-spacing: -0.02em; }
.nav nav { display: flex; gap: 28px; }
/* The one action, at every width: a small lime block like the page buttons */
.nav .nav-join { display: inline-flex; padding: 0 16px; border-radius: var(--r-xl); background: var(--primary); color: var(--primary-ink); }
@media (hover: hover) and (pointer: fine) { .nav .nav-join:hover { background: var(--primary-hover); text-decoration: none; } }
@media (hover: hover) and (pointer: fine) { .nav a:hover { text-decoration: underline; text-underline-offset: 4px; } }

/* Hero: "Meet the [strip]ies." on the vertical centre */
.hero { display: grid; grid-template-rows: 1fr auto auto auto 1fr; min-height: 100svh; padding: 48px var(--margin); }
.hero h1 { grid-row: 2; margin: 0; }
.hero-line {
  display: flex; align-items: baseline; gap: 0.18em;
  font-size: var(--t-hero); font-weight: 700; letter-spacing: -0.045em; line-height: 0.95; white-space: nowrap;
}
/* No text inside, so its baseline is its bottom edge: height alone sets the cap line (Inter caps = 0.727em) */
.strip { position: relative; flex: 1; height: 0.727em; border-radius: var(--r-xl); overflow: hidden; background: var(--primary-soft); }
.strip video, .strip-work { width: 100%; height: 100%; object-fit: cover; }
/* The Work still waits over the video; motion.js fades it in when Post ends. Without JS it stays hidden. */
.strip-work { position: absolute; inset: 0; opacity: 0; transition: opacity 500ms var(--ease-in-out), transform 0s 500ms; }
.strip-work.is-on { opacity: 1; transform: scale(1.04); transition: opacity 500ms var(--ease-in-out), transform 4.5s linear; }
/* what the two apps are comes before the ask, so nobody is asked to join something unexplained */
.hero > p { grid-row: 3; justify-self: center; max-width: 44ch; margin: 64px 0 0; font-size: 22px; line-height: 1.45; text-align: center; }
.hero > .btn { grid-row: 4; justify-self: center; margin-top: 32px; }

/* Phones and portrait tablets: the strip drops between the two halves of the sentence at 2:1, so the
   charms read at full width instead of as a 73px sliver on a 768px tablet */
@media (max-width: 760px), (max-width: 1100px) and (orientation: portrait) {
  .hero { grid-template-rows: none; align-content: center; }
  .hero h1, .hero > .btn, .hero > p { grid-area: auto; }
  .hero-line { flex-wrap: wrap; font-size: 21vw; }
  .strip { flex-basis: 100%; height: auto; aspect-ratio: 2; }
  .hero > p { margin-top: 32px; }
  .hero > .btn { margin-top: 24px; }
}
@media (max-width: 760px) {
  .nav nav a { display: none; }
  .hero > p { font-size: 16px; }
}

/* Postie demo: the headline is an input, Postie's queue answers under it (direction §7.1).
   It sits on the soft tone step: the one place on the page you type into gets its own ground. */
/* Each product opens with its name and what it does, so the demo under it has context */
.intro { grid-column: 1 / -1; padding-bottom: 12px; }
/* The product's name is a step above its feature titles (--t-l), at the size of Workie's stage clauses */
.intro h2 { margin: 0; font-size: var(--t-name); font-weight: 700; letter-spacing: -0.04em; line-height: 1; }
.intro > p:not(.intro-try) { max-width: 44ch; margin: 16px 0 0; font-size: var(--t-lead); line-height: 1.45; text-wrap: pretty; }
.intro label, .intro .intro-try { display: block; margin: 40px 0 0; font-size: var(--t-s); font-weight: 500; }
.stages > .intro { padding: 0 var(--margin) 20px; }
@media (max-width: 760px) { .intro h2 { font-size: 9vw; } }
/* Both apps are still being built: a quiet label beside the name, on the chapter's ink at low weight */
.intro h2 { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 16px; }
.dev-tag {
  display: inline-flex; align-items: center; min-height: 32px; padding: 0 12px; border-radius: var(--r-xl);
  background: color-mix(in srgb, var(--ink) 6%, transparent); color: var(--ink);
  font-size: var(--t-s); font-weight: 500; letter-spacing: 0; line-height: 1;
} /* the stage clauses' phone size, so phones keep one size fewer */
/* Wide screens: the name on the left, what the app does beside it on the demo's own tracks, so the description
   starts on the same line as Postie's explanation under it; "Try it" stays on the left, just above the demo */
@media (min-width: 1100px) {
  .intro { display: grid; grid-template-columns: minmax(0, 572px) minmax(0, 1fr); column-gap: 64px; align-items: start; }
  .intro > p:not(.intro-try) { grid-area: 1 / 2; margin: 0; }
  .intro label, .intro .intro-try { grid-column: 1; }
}

/* Each product is a chapter on its own field: Postie on its lime tint, Workie on the blue it marks new jobs with.
   Intro, live demo, then that product's features, so nothing is shown before its product is introduced. */
.chapter.post { background: var(--primary-soft); }
.chapter.work { background: var(--wk-unread); }
/* Product interface sits in a tray one tone step darker than its chapter: no line, no shadow,
   and the outer corner is the inner 8px plus the 6px rim, so the two curves stay concentric */
.tray { padding: 6px; border-radius: var(--r-xl); background: color-mix(in srgb, var(--ink) 5%, transparent); }

.demo {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-rows: auto auto auto auto 1fr; column-gap: 48px;
  /* the section gap is padding, so the #postie link lands with the input clear of the viewport top */
  padding: var(--gap-section) var(--margin) 0;
}
.type-line, .demo > .tray, .demo .egs { grid-column: 1 / -1; }
/* Long ideas scroll inside the input; it never wraps or widens the page */
.type-line {
  /* a white field one step off the lime ground, like the waitlist's field on white: it reads as somewhere to type */
  width: min(100%, 13em); min-width: 0; margin: 0; padding: 0.04em 0.22em 0.08em; border: 0; border-radius: var(--r-xl); background: #fff; appearance: none;
  font-size: var(--t-xl); font-weight: 700; letter-spacing: -0.04em; line-height: 1.2; cursor: text;
  caret-color: var(--ink); caret-shape: block;
}
.demo > .tray { max-width: 572px; margin: 48px 0 64px; }
/* The examples are white blocks on the soft field at every width, so they read as things to press */
.egs { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 16px; }
.egs-lead { margin-right: 8px; color: var(--muted); font-size: var(--t-s); }
.eg {
  min-height: 44px; padding: 0 16px; border: 0; border-radius: var(--r-xl); background: #fff; appearance: none; text-align: left; white-space: nowrap;
  font-size: var(--t-m); font-weight: 500; cursor: pointer; transition: transform 140ms var(--ease-out);
}
.eg:active { transform: scale(0.97); }
@media (hover: hover) and (pointer: fine) { .eg:hover { text-decoration: underline; text-underline-offset: 4px; } }
.demo > p { max-width: 40ch; margin: 0; }
/* Wide screens: the explanation stands beside the answer, top-aligned with it, and the examples sit under it */
@media (min-width: 1100px) {
  /* the explanation sits right beside the queue, not at the far edge of the screen */
  .demo { grid-template-columns: minmax(0, 572px) minmax(0, 1fr); column-gap: 64px; }
  .demo > .tray { grid-column: 1; }
  .demo > p { grid-area: 4 / 2; align-self: start; margin-top: 48px; }
}

@media (max-width: 760px) {
  .demo { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; align-content: start; min-height: 0; }
  /* the default idea is 10.2em wide: 8.4vw keeps it, and the block caret after it, on one line down to 320px */
  .type-line { width: 100%; font-size: min(var(--t-xl), 8.4vw); }
  .demo > .tray { margin: 28px 0 0; }
  .demo > p { margin-top: 32px; font-size: 16px; }
}

/* Each chapter's features: one row of pictures, every frame the same height (--feat-h) with its top on one line,
   so photos and interface crops read as a set and the captions line up under them. 400px holds the band card's
   five rows, so the table is never cut mid-row. Past 1440px the row keeps filling the width and the frames grow
   in height with it (27.78vw = 400px at 1440), so every frame keeps its 1440 shape: the envelope crop keeps the
   brass plate, and the band's rows stretch to fill the taller card. */
.feats {
  --feat-h: max(400px, 27.78vw);
  display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: 32px; row-gap: 72px; align-items: start;
  padding: 96px var(--margin) var(--gap-section);
}
/* subgrid: picture, title and paragraph share the row's tracks, so a two-line title pushes every paragraph down together */
.feat { display: grid; grid-row: span 3; grid-template-rows: subgrid; row-gap: 0; align-self: stretch; }
.feat figure { height: var(--feat-h); margin: 0 0 24px; }
.feat img { display: block; width: 100%; height: 100%; object-fit: cover; border-radius: var(--r-xl); background: var(--soft); }
/* an interface crop fills its frame like a photo does; anything past the frame is cropped, as on screen */
.feat .tray, .feat .tray > [data-ui] { height: 100%; aspect-ratio: auto; }
/* a taller frame spreads the band's rows instead of leaving white under them */
.feat .frag-band { display: flex; flex-direction: column; }
.feat .band-row { flex: 1 0 auto; align-content: center; }
.feat .band-label { align-self: flex-start; }
/* past 1440 the sample post grows with its frame, so the box doesn't turn into a half-empty page */
.feat .ai-text { font-size: clamp(14px, 0.97vw, 18px); }
.feat h3 { margin: 0; font-size: var(--t-l); font-weight: 600; letter-spacing: -0.025em; line-height: 1.15; text-wrap: balance; }
.feat > p { max-width: 36ch; margin: 12px 0 0; }
/* Postie: the envelope and the AI card side by side, half and half; the landscape crop keeps the slot and the whole envelope */
.post .feat:nth-child(1) { grid-column: 1 / span 6; }
.post .feat:nth-child(2) { grid-column: 7 / span 6; }
.post .feat:nth-child(1) img { object-position: 50% 48%; }
/* Workie: the band on half the row, the two photos sharing the other half */
.work .feat:nth-child(1) { grid-column: 1 / span 6; }
.work .feat:nth-child(2) { grid-column: 7 / span 3; }
.work .feat:nth-child(3) { grid-column: 10 / span 3; }
.work .feat:nth-child(2) img { object-position: 56% 50%; } /* the clock sits right of centre in its square */
@media (max-width: 1099px) {
  /* the band takes its own row at its natural height; the photos pair up under it */
  .work .feat:nth-child(1) { grid-column: 1 / -1; }
  .work .feat:nth-child(1) figure { height: auto; }
  .work .feat:nth-child(2) { grid-column: 1 / span 6; }
  .work .feat:nth-child(3) { grid-column: 7 / span 6; }
}
@media (max-width: 760px) {
  /* Phones: one column, every picture at its own shape */
  .feats { grid-template-columns: minmax(0, 1fr); row-gap: 56px; padding: 64px var(--margin) 96px; }
  .chapter .feat:nth-child(n) { grid-column: auto; }
  .feat figure, .work .feat:nth-child(1) figure { height: auto; }
  .feat img { height: auto; aspect-ratio: 4 / 5; }
  .feat .tray > .frag-ai { aspect-ratio: auto; } /* the whole sample post shows; no frame to crop it mid-line */
}

/* How Workie works: one slider drives the clause and the Workie fragment (direction §7.3).
   stages.js sets data-stage and --pct; without it the section rests on Collect, the markup's default. */
.stages {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: auto auto auto minmax(0, 1fr);
  padding-top: var(--gap-section);
}
/* The left field runs down behind the slider, so the two fields meet on one clean edge.
   The clause takes the blueprint's own size, not --t-xl: at --t-xl the longest clause runs to three
   lines in half the width, and two lines is a house rule, which outranks the five steps. */
.stage-clause {
  grid-area: 4 / 1 / 5 / 2; display: grid; align-content: start; padding: 64px 32px 0 var(--margin);
  font-size: var(--t-name); font-weight: 700; letter-spacing: -0.04em; line-height: 1; text-wrap: balance;
}
/* All four clauses share one cell, so the field never changes height. Only the current one shows;
   visibility flips at the end of the fade, so hidden clauses neither catch the pointer nor get read out. */
.stage-clause > div { grid-area: 1 / 1; opacity: 0; visibility: hidden; transition: opacity 200ms var(--ease-out), visibility 200ms; }
.stage-clause p { margin: 0; }
/* The plain sentence under each clause says what Workie does at that stage */
.stage-clause .stage-note { max-width: 36ch; margin-top: 24px; font-size: var(--t-m); font-weight: 400; letter-spacing: 0; line-height: 1.55; text-wrap: pretty; }
.stages[data-stage=collect] [data-for=collect], .stages[data-stage=dedupe] [data-for=dedupe],
.stages[data-stage=land] [data-for=land], .stages[data-stage=apply] [data-for=apply] { opacity: 1; visibility: visible; }
.stages > .tray { grid-area: 4 / 2; justify-self: center; width: min(452px, 100% - 2 * var(--margin)); margin: 64px 0 48px; }

/* The range: 8px track, ink up to the thumb (--pct), square lime thumb */
.stages input[type=range] {
  --pct: 0%; --rest: color-mix(in srgb, var(--ink) 14%, transparent); --thumb: 32px;
  grid-area: 2 / 1 / 3 / -1; width: auto; height: var(--thumb); margin: 0 var(--margin);
  appearance: none; background: none; cursor: pointer;
}
.stages input[type=range]::-webkit-slider-runnable-track { height: 8px; background: linear-gradient(to right, var(--ink) var(--pct), var(--rest) var(--pct)); } /* taste-skill-ignore: a two-colour hard stop, the only way to paint the passed track in WebKit */
.stages input[type=range]::-moz-range-track { height: 8px; background: var(--rest); }
.stages input[type=range]::-moz-range-progress { height: 8px; background: var(--ink); }
.stages input[type=range]::-webkit-slider-thumb {
  appearance: none; box-sizing: border-box; width: var(--thumb); height: var(--thumb); margin-top: calc(4px - var(--thumb) / 2);
  border: 1px solid var(--primary-edge); border-radius: 0; background: var(--primary);
}
.stages input[type=range]::-moz-range-thumb {
  box-sizing: border-box; width: var(--thumb); height: var(--thumb);
  border: 1px solid var(--primary-edge); border-radius: 0; background: var(--primary);
}

/* Columns split 1:2:2:1 so the middle names centre on the second and third stops */
.stage-names {
  grid-area: 3 / 1 / 4 / -1; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr) minmax(0, 2fr) minmax(0, 1fr);
  margin: 4px var(--margin) 0; padding: 0; list-style: none;
  color: var(--muted); font-size: var(--t-s); white-space: nowrap;
}
.stage-names li:nth-child(2), .stage-names li:nth-child(3) { text-align: center; }
.stage-names li:last-child { text-align: right; }
.stage-names .is-current { color: var(--ink); }
/* each name is a stop you can press as well as drag to */
.stage-names button {
  min-height: 44px; padding: 0; border: 0; background: none; appearance: none; color: inherit; font: inherit; cursor: pointer;
  transition: transform 140ms var(--ease-out);
}
.stage-names button:active { transform: scale(0.97); }
@media (hover: hover) and (pointer: fine) { .stage-names button:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 4px; } }

@media (prefers-reduced-motion: reduce) { .stage-clause > div { transition: none; } }

@media (max-width: 760px) {
  .stages { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; min-height: 0; }
  .stage-clause, .stages > .tray, .stages input[type=range], .stage-names { grid-area: auto; }
  .stage-clause { padding: 32px var(--margin); font-size: 9vw; }
  .stages > .tray { margin: 32px auto; }
  .stages input[type=range] { --thumb: 40px; }
}

/* Waitlist: the section asks; the form itself opens in a dialog from here, the hero and the nav */
.join { display: grid; grid-template-columns: minmax(0, 1fr); align-items: center; gap: 64px; padding: var(--gap-section) var(--margin); }
@media (min-width: 1100px) { .join { grid-template-columns: minmax(0, 1fr) auto; } }
.join-line { max-width: 12em; margin: 0; font-size: var(--t-xl); font-weight: 700; letter-spacing: -0.04em; line-height: 1; text-wrap: balance; }
.join-sub { max-width: 40ch; margin: 24px 0 0; font-size: var(--t-lead); line-height: 1.45; text-wrap: pretty; }
.join-open { margin-top: 32px; }

/* The dialog: a white sheet on a dimmed page, the page's own 14px corners and type */
.join-dialog {
  width: min(480px, calc(100% - 2 * var(--margin))); max-height: calc(100% - 2 * var(--margin)); margin: auto; padding: 0;
  border: 0; border-radius: var(--r-xl); background: #fff; color: var(--ink); overflow: auto;
}
.join-dialog::backdrop { background: color-mix(in srgb, var(--ink) 48%, transparent); }
.join-form { position: relative; display: grid; padding: 32px; }
.join-title { margin: 0 48px 24px 0; font-size: var(--t-l); font-weight: 700; letter-spacing: -0.03em; line-height: 1.1; }
.join-close {
  position: absolute; top: 16px; right: 16px; display: grid; place-items: center; width: 44px; height: 44px; padding: 0;
  border: 0; border-radius: var(--r-xl); background: none; color: var(--ink); cursor: pointer; transition: background-color 160ms ease, transform 140ms var(--ease-out);
}
.join-close svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
.join-close:active { transform: scale(0.97); }
@media (hover: hover) and (pointer: fine) { .join-close:hover { background: var(--soft); } }
.join-label { margin-bottom: 8px; font-size: var(--t-s); font-weight: 500; }
.join-form input[type=email] {
  width: 100%; min-width: 0; height: 52px; margin: 0; padding: 0 16px; border: 0; border-radius: var(--r-xl); background: var(--soft);
  font-size: var(--t-m); appearance: none;
}
.join-form input[type=email]::placeholder { color: var(--muted); opacity: 1; }
.join-form input[type=email]:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.join-form .join-btn { margin-top: 24px; }
.join-btn[aria-busy=true] { cursor: progress; }
.join-form fieldset { display: flow-root; min-inline-size: 0; margin: 20px 0 0; padding: 0; border: 0; font-size: var(--t-s); }
.join-form legend { float: left; margin-right: 12px; padding: 0; line-height: 44px; color: var(--muted); }
/* real radios, drawn as a row of blocks: soft when open, ink when chosen */
.join-form fieldset label {
  position: relative; display: inline-flex; align-items: center; min-height: 44px; margin: 0 8px 8px 0; padding: 0 16px;
  border-radius: var(--r-xl); background: var(--soft); font-weight: 500; cursor: pointer;
  transition: background-color 160ms ease, color 160ms ease, transform 140ms var(--ease-out);
}
.join-form fieldset label:has(:checked) { background: var(--ink); color: #fff; }
.join-form fieldset label:active { transform: scale(0.97); }
.join-form fieldset label:has(:focus-visible) { outline: 2px solid var(--ink); outline-offset: 3px; }
@media (hover: hover) and (pointer: fine) { .join-form fieldset label:not(:has(:checked)):hover { background: var(--hover); } }
.join-form input[type=radio] { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.hp { position: absolute; left: -10000px; }
.join-msg { margin: 0; font-size: var(--t-m); }
.join-msg:not(:empty) { margin-top: 16px; }
.join-msg.is-error { color: var(--danger); }
.join-msg:focus { outline: none; } /* focus lands here only so keyboard users keep their place */
.join-form .note { margin: 16px 0 0; color: var(--muted); font-size: var(--t-s); }
.join-done { display: none; }
/* Saved: the fields go; the live region takes their place and Done closes the sheet */
.join-form.is-done :is(.join-title, .join-label, input[type=email], fieldset, .join-btn, .note) { display: none; }
.join-form.is-done .join-msg { margin: 0 48px 0 0; font-size: var(--t-l); font-weight: 700; letter-spacing: -0.03em; line-height: 1.15; text-wrap: balance; }
.join-form.is-done .join-done { display: inline-flex; justify-self: start; margin-top: 24px; }
@media (max-width: 760px) {
  .join-sub { font-size: var(--t-m); }
  .join-form { padding: 24px 20px; }
  /* keeps "For" and all three choices on one row down to 320px */
  .join-form legend { margin-right: 8px; }
  .join-form fieldset label { margin-right: 6px; padding: 0 12px; }
}
/* Opening: the sheet rises 8px and fades in over the dimming page */
@media (prefers-reduced-motion: no-preference) {
  .join-dialog[open] { animation: sheet 240ms var(--ease-out); }
  .join-dialog[open]::backdrop { animation: fade 240ms var(--ease-out); }
}
@keyframes sheet { from { opacity: 0; transform: translateY(8px) scale(0.98); } }
/* Without the script the dialog can't open, so the form simply sits under the section */
html:not(.js) .join-dialog { display: block; position: static; margin: 0 var(--margin) var(--gap-section); }
html:not(.js) .join-close, html:not(.js) .join-open { display: none; }

/* Two charm photographs stacked like cards on a soft panel; the soft outline on the front one cuts a clean gap. */
.charms { position: relative; display: grid; width: 400px; height: 400px; padding: 32px; border-radius: var(--r-xl); background: var(--soft); }
.charms img {
  grid-area: 1 / 1; width: 62%; height: auto; border-radius: var(--r-xl);
  transition: transform 320ms var(--ease-out), opacity 320ms var(--ease-out);
}
/* its photo's ground is #f7f6f9, the panel's own tone; 3.5% brighter makes it white like the Postie tile */
.charms .charm-work { place-self: end; outline: 10px solid var(--soft); filter: brightness(1.035); }
@media (max-width: 1099px) { .charms { width: 280px; height: 280px; padding: 24px; } .charms .charm-work { outline-width: 8px; } }

/* Footer: an invitation over the cropped wordmark, on the ink field (direction §7.5) */
.foot {
  display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: 24px;
  padding: clamp(64px, 8vw, 120px) var(--margin) 0; overflow: hidden;
  background: var(--ink); color: #fff;
}
.foot :focus-visible { outline-color: var(--primary); }
.invite {
  grid-area: 1 / 1 / 2 / 8; margin: 0; text-wrap: balance;
  font-size: var(--t-xl); font-weight: 700; letter-spacing: -0.04em; line-height: 1;
}
.foot-links { grid-area: 1 / 9 / 2 / 13; justify-self: end; width: 360px; margin: 0; padding: 0; list-style: none; }
.foot-links a { display: inline-flex; align-items: center; min-height: 44px; text-decoration: none; font-weight: 500; }
@media (hover: hover) and (pointer: fine) { .foot-links a:hover { text-decoration: underline; text-underline-offset: 4px; } }
.legal {
  grid-area: 2 / 1 / 3 / -1; display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0 24px; margin-top: clamp(96px, 12vw, 200px);
  color: color-mix(in srgb, #fff 64%, var(--ink)); font-size: var(--t-s);
}
.legal p { margin: 0; }
/* translateY crops the letters at the bottom edge; the matching negative margin (30% of the 0.8em box)
   keeps the legal row sitting right on the letters instead of 0.24em above them */
.wordmark {
  grid-area: 3 / 1 / 4 / -1; margin: calc(-0.24em + 16px) 0 0 -0.04em;
  font-size: 48vw; font-weight: 700; letter-spacing: -0.06em; line-height: 0.8; white-space: nowrap;
  transform: translateY(30%); user-select: none;
}

@media (max-width: 760px) {
  .foot { grid-template-columns: minmax(0, 1fr); }
  .invite, .foot-links, .legal, .wordmark { grid-area: auto; }
  .foot-links { display: flex; flex-wrap: wrap; gap: 0 24px; justify-self: start; width: auto; margin-top: 24px; font-size: var(--t-s); }
}

/* Motion (.impeccable.md motion budget). The shutter and the entrances wait for the .js class motion.js
   adds, so the page is complete if the script never runs. */
@media (prefers-reduced-motion: no-preference) {
  /* Signature: the strip opens like a shutter while the picture settles; motion.js adds .is-open 200ms after load */
  .js .strip { clip-path: inset(0 100% 0 0); }
  .js .strip video { transform: scale(1.12); }
  /* transitions only on .is-open, or the video would scale up from 1 while .js lands and only part-way back */
  .js .strip.is-open { clip-path: inset(0); transition: clip-path 1.1s var(--ease-in-out); }
  .js .strip.is-open video { transform: none; transition: transform 1.1s var(--ease-in-out); }
  /* The same shutter opens each feature photo as its block enters, the picture settling from 1.08 */
  .js .feat figure:has(> img) { overflow: hidden; border-radius: var(--r-xl); }
  .js .feat:not(.is-in) img { clip-path: inset(0 100% 0 0); }
  .js .feat.is-in img { animation: unveil 1s var(--ease-in-out) both; }
}
@keyframes unveil { from { clip-path: inset(0 100% 0 0); transform: scale(1.08); } to { clip-path: inset(0); transform: none; } }
/* Entrances: the hero words, then each block's parts once it enters, 70ms apart. A keyframe with backwards
   fill, not a transition, so each part keeps its own transitions and transform once it lands.
   Controls are never hidden, and the wordmark keeps its crop. */
.js [data-enter]:not(.is-in) > :not(input, .wordmark) { opacity: 0; }
.js .hero-line > :not(.strip), .js [data-enter].is-in > :not(input, .wordmark) {
  animation: rise 600ms var(--ease-out) calc(var(--i, 0) * 70ms) backwards;
}
@keyframes rise { from { opacity: 0; transform: translateY(24px); } }
@keyframes fade { from { opacity: 0; } }

/* Feedback: links give on press like the buttons do */
.nav a, .foot-links a { transition: transform 140ms var(--ease-out); }
.nav a:active, .foot-links a:active { transform: scale(0.97); }

@media (prefers-reduced-motion: reduce) {
  .js .hero-line > :not(.strip), .js [data-enter].is-in > :not(input, .wordmark) { animation-name: fade; animation-duration: 200ms; }
  .btn:active, .eg:active, .nav a:active, .foot-links a:active, .stage-names button:active, .join-form fieldset label:active, .join-close:active { transform: none; }
  .charms img { transition: none; }
  .type-line { caret-animation: manual; }
}

/* /joined and /join-error: one statement, one line, one way back */
main.page { display: grid; align-content: center; justify-items: start; gap: 24px; min-height: 100svh; }
main.page h1 { margin: 0; font-size: var(--t-xl); font-weight: 700; letter-spacing: -0.04em; line-height: 1; }
main.page p { margin: 0; }
