/* Hallmark · genre: n/a (bespoke) · macrostructure: Narrative Workflow (pitch) · Catalogue (work) · Notice (utility)
 * design-system: design.md · designed-as-app
 * theme: custom · vibe: "heavy glass, terminal edge, white and charcoal"
 * paper: oklch(98.6% 0.006 45) · accent: oklch(67% 0.131 39)
 * display: Newsreader 700 · body: Switzer · outlier: JetBrains Mono (role: machine data)
 * hero: H2 split diptych · knobs: ratio=7/5, right=refusal column, divider=hairline
 * steps: F4 step sequence · knobs: numbering=1.0/2.0/3.0, layout=vertical stack, connector=line
 * nav: N9 edge-aligned minimal (sticky glass) · footer: Ft2 inline single line · enrichment: none
 * axes: light / roman-serif / warm (clay ~39°) · studied: no
 * contrast: pass (40-41) · slop: pass (42-45) · mobile: pass (34, 49, 50-57)
 * honest: pass (46) · chrome: pass (47) · tokens: pass (48) · icons: pass (30)
 * pre-emit critique: P5 H5 E4 S5 R5 V5
 *
 * The public pitch. No invented metrics, no proof bar, no stock photography —
 * the four-stage offer carries the page, and the only images are real
 * screenshots of sites we actually shipped.
 *
 * Requires /assets/tokens.css to be linked BEFORE this file.
 */

/* ---------------------------------------------------------------- reset */

*, *::before, *::after { box-sizing: border-box; }
html, body { overflow-x: clip; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--color-paper);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: var(--wt-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

.wrap {
  width: 100%;
  max-width: 72rem;
  margin-inline: auto;
  padding-inline: clamp(var(--space-md), 4vw, var(--space-xl));
}

:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
  border-radius: inherit;
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------------------------------------------------------------- type */

h1, h2, h3 {
  margin: 0 0 var(--space-sm);
  font-family: var(--font-display);
  font-weight: var(--wt-display);
  font-style: normal;                    /* roman headings, always */
  line-height: var(--lh-display);
  letter-spacing: var(--tr-display);
  overflow-wrap: anywhere;
  min-width: 0;
  text-wrap: balance;
}
h1 { font-size: var(--text-display); }
h2 { font-size: var(--text-xl); line-height: var(--lh-tight); }
h3 { font-size: var(--text-md); line-height: var(--lh-tight); }

p { margin: 0 0 var(--space-md); max-width: var(--measure); }

.lede {
  font-size: var(--text-md);
  line-height: 1.5;
  color: var(--color-ink-2);
  max-width: 54ch;
  text-wrap: pretty;
}

.muted { color: var(--color-muted); }
small, .small { font-size: var(--text-sm); }
.mono {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
}

a {
  color: var(--color-accent-deep);
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: color-mix(in oklch, var(--color-accent-deep) 38%, transparent);
  transition: text-decoration-thickness var(--dur-short) var(--ease-out),
              text-decoration-color var(--dur-short) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  a:hover {
    text-decoration-thickness: 2px;
    text-decoration-color: var(--color-accent-deep);
  }
}

/* ---------------------------------------------------------------- N9 nav
   Wordmark hard-left, one CTA hard-right, nothing between. The absence is the
   design. Sticky over scrolling content, so glass earns its place here. */

.site-head {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky-nav);
  background: var(--color-paper);
  border-bottom: 1px solid var(--color-rule-2);
}
@supports (backdrop-filter: blur(1px)) {
  .site-head {
    background: var(--glass-paper);
    backdrop-filter: var(--glass-blur);
    -webkit-backdrop-filter: var(--glass-blur);
    box-shadow: inset 0 1px 0 var(--glass-edge);
  }
}
.site-head__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  min-height: var(--topbar-h);
  padding-block: var(--space-xs);
}
.logo {
  font-family: var(--font-display);
  font-weight: var(--wt-display);
  font-size: var(--text-md);
  letter-spacing: var(--tr-display);
  color: var(--color-ink);
  text-decoration: none;
  white-space: nowrap;
}
.logo::after { content: "."; color: var(--color-accent); }
.logo:hover { text-decoration: none; }

/* ---------------------------------------------------------------- buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  min-height: var(--control-h);
  padding: 0 var(--space-lg);
  border: 1px solid var(--color-accent);
  border-radius: var(--radius-md);
  background: var(--color-accent);
  color: var(--color-accent-ink);
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: var(--wt-medium);
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur-short) var(--ease-out),
              border-color var(--dur-short) var(--ease-out),
              color var(--dur-short) var(--ease-out),
              transform var(--dur-micro) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .btn:hover {
    background: var(--color-accent-hover);
    border-color: var(--color-accent-hover);
    color: var(--color-accent-hover-ink);
    text-decoration: none;
  }
}
.btn:active { transform: translateY(1px); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: 0.55; cursor: not-allowed; transform: none; }

.btn--quiet {
  background: transparent;
  border-color: var(--color-edge);
  color: var(--color-ink);
}
@media (hover: hover) and (pointer: fine) {
  .btn--quiet:hover {
    background: var(--color-paper-3);
    border-color: var(--color-ink-2);
    color: var(--color-ink);
  }
}
.btn--sm { min-height: var(--control-h); padding-inline: var(--space-md); font-size: var(--text-sm); }
.btn-block { width: 100%; }

/* Typographic link CTA — a word, an arrow, a 1px underline. */
.link-cta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  font-weight: var(--wt-medium);
  white-space: nowrap;
}

button.primary { /* shared with the bare shell (admin sign-in) */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--control-h);
  padding: 0 var(--space-lg);
  border: 1px solid var(--color-accent);
  border-radius: var(--radius-md);
  background: var(--color-accent);
  color: var(--color-accent-ink);
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: var(--wt-medium);
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--dur-short) var(--ease-out),
              transform var(--dur-micro) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  button.primary:hover {
    background: var(--color-accent-hover);
    border-color: var(--color-accent-hover);
    color: var(--color-accent-hover-ink);
  }
}
button.primary:active { transform: translateY(1px); }
button.primary:disabled { opacity: 0.55; cursor: not-allowed; transform: none; }

/* ---------------------------------------------------------------- sections
   Rhythm is deliberately uneven — heavier bottom than top, and the hero and
   the closing section break from the default. */

.section {
  padding-block: var(--space-xl) var(--space-2xl);
  border-top: 1px solid var(--color-rule-2);
}
/* Anchored sections must clear the sticky header. */
.section[id], .hero[id] { scroll-margin-top: calc(var(--topbar-h) + var(--space-md)); }
.mt-lg { margin-top: var(--space-lg); }
.section:first-of-type { border-top: 0; }
.section__title { margin-bottom: var(--space-lg); }
.section--close { padding-block: var(--space-2xl) var(--space-3xl); }

/* ---------------------------------------------------------------- H2 hero
   7/5 diptych: statement left, the offer's terms right. Left-biased, never
   centred; bottom padding is 1.6x the top so the hero sits into the page. */

.hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-xl);
  padding-block: var(--space-xl) var(--space-3xl);
}
.hero__lede { margin-bottom: var(--space-lg); }
.hero__actions { display: flex; align-items: center; gap: var(--space-md); flex-wrap: wrap; }

/* The right column is a refusal list — what this is not. No invented metrics,
   because none were supplied. */
.terms {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--color-rule);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: 1.4;
}
.terms > div {
  display: flex;
  gap: var(--space-sm);
  padding-block: var(--space-sm);
  border-bottom: 1px solid var(--color-rule-2);
}
/* The section rule below already closes the list — a second hairline here
   reads as a double rule. */
.terms > div:last-child { border-bottom: 0; }
.terms dt {
  flex: 0 0 4.5rem;
  color: var(--color-accent-deep);
  font-variant-numeric: tabular-nums;
}
.terms dd { margin: 0; color: var(--color-ink-2); min-width: 0; }

/* A page whose hero is only a title + lede doesn't need the full diptych
   breathing room — the rhythm would read as an accidental gap. */
.hero--title { padding-block: var(--space-xl) var(--space-2xl); }

@media (min-width: 60rem) {
  .hero {
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
    align-items: start;
    gap: var(--space-2xl);
  }
  .hero--title { grid-template-columns: minmax(0, 1fr); }
  .terms { margin-top: var(--space-md); }
}

/* ---------------------------------------------------------------- F4 stages
   Genuinely ordinal — you cannot see the site before we build it. The number
   stacks ABOVE its heading in one column; never number-left / heading-right. */

.stages { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; }
.stage {
  padding: var(--space-lg) 0 var(--space-lg) var(--space-lg);
  border-inline-start: 1px solid var(--color-rule);
  position: relative;
}
.stage::before {
  content: "";
  position: absolute;
  inset-inline-start: -3px;
  top: calc(var(--space-lg) + 0.4em);
  width: 5px;
  height: 5px;
  border-radius: var(--radius-pill);
  background: var(--color-accent);
}
.stage__n {
  display: block;
  margin-bottom: var(--space-2xs);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  color: var(--color-accent-deep);
}
.stage__t { margin-bottom: var(--space-2xs); font-size: var(--text-md); }
.stage p { margin: 0; color: var(--color-ink-2); }

@media (min-width: 60rem) {
  .stages { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--space-2xl); }
}

/* ---------------------------------------------------------------- Catalogue
   Real screenshots only. A hairline figure frame and a mono caption — no
   re-drawn browser chrome, no fake device frame. */

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(20rem, 100%), 1fr));
  gap: var(--space-lg);
}
.card { margin: 0; min-width: 0; }
.card img {
  display: block;
  width: 100%;
  /* The markup carries width/height attributes for layout stability. The height one is a used
     value, which beat aspect-ratio and stretched every card to 800px — auto hands the frame
     back to the ratio. */
  height: auto;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  object-position: top center;
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-md);
  background: var(--color-paper-2);
}
.card .meta {
  display: flex;
  justify-content: space-between;
  gap: var(--space-sm);
  padding-top: var(--space-xs);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
}
.card .meta strong { font-weight: var(--wt-medium); }
.card .meta span { color: var(--color-muted); text-align: right; min-width: 0; }

/* ---------------------------------------------------------------- forms */

label.f,
.field > span {
  display: block;
  margin-bottom: var(--space-2xs);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  font-weight: var(--wt-medium);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--color-muted);
}
label.f { margin-top: var(--space-md); }

input, textarea, select {
  width: 100%;
  min-height: var(--control-h);
  padding: var(--space-xs) var(--space-sm);
  border: 1px solid var(--color-edge);   /* 1px in every state */
  border-radius: var(--radius-md);
  outline: 2px solid transparent;
  outline-offset: 2px;
  background: var(--color-paper);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: 1.4;
  transition: background-color var(--dur-short) var(--ease-out),
              border-color var(--dur-short) var(--ease-out);
}
input::placeholder, textarea::placeholder { color: var(--color-muted); }
@media (hover: hover) and (pointer: fine) {
  input:hover, textarea:hover, select:hover { background: var(--color-paper-2); }
}
input:focus-visible, textarea:focus-visible, select:focus-visible {
  outline-color: var(--color-focus);
  border-color: var(--color-ink-2);
}
input[aria-invalid="true"], textarea[aria-invalid="true"] { border-color: var(--color-danger); }
input:disabled, textarea:disabled { opacity: 0.55; cursor: not-allowed; background: var(--color-paper-2); }
textarea { min-height: 7rem; resize: vertical; line-height: var(--lh-body); }

form.intake {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
  gap: var(--space-md);
  max-width: 46rem;
}
.field { display: block; min-width: 0; }
form.intake .field > span { margin-top: 0; }
form.intake .full, form.intake textarea { grid-column: 1 / -1; }
form.intake .actions {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: var(--space-md);
  flex-wrap: wrap;
}
form.intake .actions .small { color: var(--color-muted); }

.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* Intake: the three-way brief toggle, and the progressive disclosure it drives.
 * Radios, no JS — the form posts identically with scripting off, and :has() only decides
 * which optional blocks are on screen. Where :has() is unsupported every field shows, which
 * is exactly the form as it was before the toggle existed: degraded, never broken. */
.modes { border: 0; margin: 0 0 var(--space-xs); padding: 0; min-inline-size: 0; }
.modes legend {
  padding: 0;
  margin-bottom: var(--space-xs);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  font-weight: var(--wt-medium);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--color-muted);
}
.modes__row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr));
  gap: var(--space-sm);
}
/* The whole card is the control — the radio is present for keyboard and forms, never drawn.
 * Selection reads as a highlighted card, not a dot. */
.mode {
  position: relative;
  display: grid;
  row-gap: var(--space-3xs);
  align-content: start;
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--color-rule);   /* 1px in every state */
  border-radius: var(--radius-md);
  background: var(--color-paper);
  cursor: pointer;
  transition: background-color var(--dur-short) var(--ease-out),
              border-color var(--dur-short) var(--ease-out),
              box-shadow var(--dur-short) var(--ease-out);
}
.mode input[type="radio"] {
  position: absolute;
  width: 1px;
  height: 1px;
  min-height: 0;
  padding: 0;
  margin: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
}
.mode__t {
  font-weight: var(--wt-medium);
  color: var(--color-ink);
  transition: color var(--dur-short) var(--ease-out);
}
.mode__d { font-size: var(--text-sm); line-height: 1.4; color: var(--color-muted); }
@media (hover: hover) and (pointer: fine) {
  .mode:hover { background: var(--color-paper-2); border-color: var(--color-edge); }
}
.mode:has(input:checked) {
  border-color: var(--color-accent);
  background: var(--color-paper-2);
  box-shadow: inset 3px 0 0 var(--color-accent);   /* the selected edge, in place of a dot */
}
.mode:has(input:checked) .mode__t { color: var(--color-accent-deep); }
/* Keyboard arrows move the radio; the ring has to appear on the card the eye is reading. */
.mode:has(input:focus-visible) { outline: 2px solid var(--color-focus); outline-offset: 2px; }

/* The optional half of the brief, folded away behind one line. A <details> so it works with
 * scripting off, and so the closed form is eight fields instead of twenty-five. */
.more {
  grid-column: 1 / -1;
  padding-top: var(--space-md);
  border-top: 1px solid var(--color-rule-2);
}
.more > summary {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: var(--control-h);
  list-style: none;                       /* the ::before marker replaces the disclosure triangle */
  cursor: pointer;
  font-weight: var(--wt-medium);
  color: var(--color-ink);
}
.more > summary::-webkit-details-marker { display: none; }
.more > summary::before {
  content: "+";
  width: 1ch;
  font-family: var(--font-mono);
  color: var(--color-accent-deep);
}
.more[open] > summary::before { content: "\2212"; }   /* minus, not a hyphen */
.more > summary:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
@media (hover: hover) and (pointer: fine) {
  .more > summary:hover { color: var(--color-accent-deep); }
}
.more > p { margin: 0 0 var(--space-md); color: var(--color-muted); }
.more__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
  gap: var(--space-md);
  margin-top: var(--space-md);
}

/* .m marks a mode-gated block; .m-existing / .m-new / .m-pick say which modes keep it. */
.intake:has(#mode-existing:checked) .m:not(.m-existing),
.intake:has(#mode-new:checked)      .m:not(.m-new),
.intake:has(#mode-pick:checked)     .m:not(.m-pick) { display: none; }

/* Intake: page picker. Checkboxes keep their intrinsic size, the row wraps on phones. */
fieldset.field { border: 0; margin: 0; padding: 0; min-inline-size: 0; }
.checks {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-md);
}
.check {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  min-height: var(--control-h);
  cursor: pointer;
}
.check input[type="checkbox"] {
  width: 1.05rem;
  height: 1.05rem;
  min-height: 0;
  flex: none;
  padding: 0;
  accent-color: var(--color-accent);
}
.check > span {
  font-size: var(--text-base);
  color: var(--color-ink);
  white-space: nowrap;
}
form.intake p.small { color: var(--color-muted); margin: 0; }

/* Helper / error slot holds its height so validation never pushes the page. */
.help, .field-error {
  display: block;
  min-height: 1lh;
  margin-top: var(--space-2xs);
  font-size: var(--text-sm);
  color: var(--color-muted);
}
.field-error { color: var(--color-danger); }
.field-error:not(:empty)::before { content: "\26A0\FE0E  "; }

.flash {
  display: flex;
  gap: var(--space-xs);
  padding: var(--space-sm) var(--space-md);
  margin-bottom: var(--space-md);
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-md);
  background: var(--color-paper-2);
  font-size: var(--text-sm);
}
.flash::before { font-family: var(--font-mono); flex: 0 0 auto; }
.flash.ok { border-color: var(--color-edge); }
.flash.ok::before { content: "\2713"; }
.flash.error { border-color: var(--color-danger); color: var(--color-danger); }
.flash.error::before { content: "\26A0\FE0E"; }

/* ---------------------------------------------------------------- Notice
   One column, one statement, one link. Used by the prospect gate, /thanks,
   404, 500, unsubscribed, and the admin sign-in. No card, no shadow. */

.notice {
  max-width: 46ch;
  padding-block: var(--space-2xl) var(--space-3xl);
}
.notice h1 { font-size: var(--text-display-s); }
.notice__body { color: var(--color-ink-2); font-size: var(--text-md); line-height: 1.5; }
.notice__meta {
  margin-top: var(--space-md);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--color-muted);
}
.notice__actions { margin-top: var(--space-lg); display: flex; gap: var(--space-md); flex-wrap: wrap; }

/* The bare shell centres a single notice or form in the viewport. Used by the
   prospect gate and the admin sign-in. No card, no shadow — a rule and a
   wordmark are the whole frame. */
.bare {
  display: grid;
  align-content: center;
  min-height: 100dvh;
  padding: var(--space-lg);
}
.bare__inner {
  width: min(28rem, 100%);
  margin-inline: auto;
}
.bare__brand {
  display: block;
  margin-bottom: var(--space-lg);
  padding-bottom: var(--space-sm);
  border-bottom: 1px solid var(--color-accent);
  font-family: var(--font-display);
  font-weight: var(--wt-display);
  font-size: var(--text-base);
  letter-spacing: var(--tr-display);
  color: var(--color-ink);
  text-decoration: none;
}
.bare__brand::after { content: "."; color: var(--color-accent); }
.bare__inner .notice { padding-block: 0; max-width: none; }
/* Display type in a 28rem column tops out well below the page scale. */
.bare__inner h1 { font-size: var(--text-xl); }

/* ---------------------------------------------------------------- Ft2 footer
   One line: wordmark, contact, index link. Hairline above. No columns. */

.site-foot {
  border-top: 1px solid var(--color-rule);
  padding-block: var(--space-lg) var(--space-xl);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--color-muted);
}
.site-foot__inner {
  display: flex;
  align-items: baseline;
  gap: var(--space-md);
  flex-wrap: wrap;
}
.site-foot__inner strong {
  font-family: var(--font-display);
  font-weight: var(--wt-display);
  font-size: var(--text-base);
  color: var(--color-ink);
}
.site-foot a { white-space: nowrap; }

/* ---------------------------------------------------------------- mobile */

@media (max-width: 40rem) {
  .terms dt { flex-basis: 3.75rem; }
  .card .meta { flex-direction: column; gap: var(--space-3xs); }
  .card .meta span { text-align: left; }
  .hero { padding-block: var(--space-lg) var(--space-2xl); }
}

/* ---------------------------------------------------------------- Work · previews
   Same rule as the catalogue above: real screenshots, a hairline frame, a mono caption. What is
   different here is that the screenshot is the *whole page* — up to 12,000px of it — shown in a
   fixed window the visitor scrolls. A fold shot could not answer what these pages are like: the
   fold of beautyextensionhaus.com is a country splash, and every photograph on it is below that.

   Device is a real switch, not a media query: the window narrows to the shape of the device and
   loads the capture taken at that width, so "tablet" shows the tablet layout on any screen. */

.works {
  display: grid;
  gap: var(--space-3xl);
}
.work { min-width: 0; }
.work__head { margin-bottom: var(--space-md); }
.work__title {
  margin: 0;
  font-size: var(--text-xl);
  line-height: var(--lh-tight);
  letter-spacing: var(--tr-display);
}
.work__meta {
  margin: var(--space-2xs) 0 0;
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--color-muted);
}

.preview { margin: 0; }

.preview__bar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  justify-content: space-between;
  margin-bottom: var(--space-xs);
}
/* No script, no controls — and no empty rail where they would have been. */
.preview__bar:empty { display: none; }

.seg {
  display: inline-flex;
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-md);
  background: var(--color-paper);
  overflow: hidden;
}
.seg__btn {
  padding: var(--space-2xs) var(--space-sm);
  border: 0;
  border-right: 1px solid var(--color-rule);
  background: transparent;
  color: var(--color-muted);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  line-height: 1.9;
  cursor: pointer;
  transition: background-color var(--dur-micro) var(--ease-out),
              color var(--dur-micro) var(--ease-out);
}
.seg__btn:last-child { border-right: 0; }
@media (hover: hover) {
  .seg__btn:hover { background: var(--color-paper-2); color: var(--color-ink); }
}
.seg__btn[aria-pressed="true"] {
  background: var(--color-ink);
  color: var(--color-paper);
}
.seg__btn:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: -2px;
}

/* The stage is the fixed window everything happens inside: a capture that scrolls, or the live
   site in an iframe laid out at its device width and scaled down to fit. */
.preview__stage {
  position: relative;
  height: clamp(22rem, 62vh, 34rem);
  margin-inline: auto;
  overflow: hidden;
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-md);
  background: var(--color-paper-2);
  transition: max-width var(--dur-long) var(--ease-out);
  /* A rounded, clipped box around a GPU-composited child is where the hover artifacts came
     from: the corner mask has to be re-applied to the scaled iframe layer every time anything
     inside it repaints, and moving a pointer across an embedded site repaints it constantly.
     contain:paint gives the stage its own paint boundary so that work stops at its edge, and
     isolation keeps it out of the page's stacking context. */
  contain: paint;
  isolation: isolate;
}
.preview__frame {
  position: absolute;
  top: 0;
  left: 0;
  border: 0;
  /* Width, height and scale are set by preview.js from the device and the stage's own size —
     the transform is what makes 1440px of desktop layout legible in a 600px frame. */
  transform-origin: top left;
  background: var(--color-paper);
  /*
   * Deaf to the pointer until preview.js arms it — see the dwell handler there. A pointer that
   * never reaches the embedded document cannot trigger its hover states, so a fast pass across
   * the page costs nothing to render.
   *
   * No will-change here. Promoting the frame to its own layer was an attempt at the same problem
   * from the wrong end: the scaling is done with zoom now, so the child rasterises at the size it
   * is drawn at and there is no scaled surface to keep in step. Forcing an extra layer on top of
   * that only costs memory.
   */
  pointer-events: none;
}
.preview__frame.is-armed { pointer-events: auto; }
/* Hidden, but still loaded and still the same document — see preview.js. */
.preview__frame.is-hidden,
.preview__frame.is-hidden.is-armed {
  visibility: hidden;
  pointer-events: none;
}
.preview__stage .preview__win {
  position: absolute;
  inset: 0;
  height: auto;
  border: 0;
  border-radius: 0;
}

.preview__win {
  height: clamp(22rem, 62vh, 34rem);
  margin-inline: auto;
  overflow-y: auto;
  /* The window scrolls; the page must not start scrolling with it when it hits the end. */
  overscroll-behavior: contain;
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-md);
  background: var(--color-paper-2);
  transition: max-width var(--dur-long) var(--ease-out);
}
.preview__win:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}
.preview__img {
  display: block;
  width: 100%;
  height: auto;
}

/* The frame takes the shape of the device it is showing.
   Only for captures: a live iframe keeps the full stage and is scaled inside it instead, so the
   site lays out at the real device width rather than at whatever the frame happens to be. */
.preview[data-device="desktop"] .preview__win { max-width: 100%; }
.preview[data-device="tablet"][data-mode="shot"] .preview__stage { max-width: 44rem; }
.preview[data-device="phone"][data-mode="shot"] .preview__stage { max-width: 21rem; }
.preview[data-mode="live"] .preview__stage { max-width: 100%; }

.preview__cap {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-md);
  padding-top: var(--space-xs);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--color-muted);
}
.preview__note { color: var(--color-ink-2); }
.preview__hint {
  margin-left: auto;
  color: var(--color-muted);
  opacity: 0.85;
}
.preview__hint::before {
  content: "↕";
  margin-right: var(--space-2xs);
  font-family: var(--font-body);
}
@media (max-width: 40rem) {
  .preview__hint { margin-left: 0; }
}

.work__still { margin: 0; }
.work__still img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  object-position: top center;
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-md);
  background: var(--color-paper-2);
}

.work__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-sm) var(--space-lg);
  padding-top: var(--space-md);
}
.work__blurb {
  flex: 1 1 28rem;
  max-width: var(--measure);
  margin: 0;
  color: var(--color-ink-2);
}

@media (max-width: 40rem) {
  .works { gap: var(--space-2xl); }
  .preview__bar { gap: var(--space-xs); }
  .preview__win, .preview__stage { height: clamp(20rem, 58vh, 26rem); }
  /* A 21rem frame inside a phone screen is a letterbox. Give it the width it has. */
  .preview[data-device="phone"][data-mode="shot"] .preview__stage,
  .preview[data-device="tablet"][data-mode="shot"] .preview__stage { max-width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .preview__win, .seg__btn { transition: none; }
}

/* ------------------------------------------------- recent-work reels (home) */

/* The whole card is the link to the live site. No caption: the reel is the description,
   and the anchor's aria-label carries the name for anyone not seeing it. */
.card--reel {
  display: block;
  position: relative;
  min-width: 0;
  border-radius: var(--radius-md);
}

/* One frame shared by the reel and its poster, so swapping between them never shifts layout.
   The reel is authored 16:10, so `cover` crops nothing — it is here for the fallback <img>,
   whose capture is taller. Transform and opacity only: `layout-transition` in the impeccable
   detector rejects animating box metrics, and they would reflow every frame regardless. */
.card--reel > video,
.card--reel > img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  object-position: top center;
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-md);
  background: var(--color-paper-2);
  transition: opacity var(--dur-md, 200ms) linear;
}

.card--reel:hover > video,
.card--reel:hover > img,
.card--reel:focus-visible > video,
.card--reel:focus-visible > img {
  opacity: 0.92;
}

.card--reel:focus-visible {
  outline: 2px solid var(--color-ink);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .card--reel > video,
  .card--reel > img {
    transition: none;
  }
}
