/* =====================================================================================================
   Counterpart · the stylesheet for counterpartdrops.com
   =====================================================================================================

   CONTENTS
     1. Fonts          the three typefaces, served from /fonts (nothing is requested from Google)
     2. Settings       sizes, spacing and timing used everywhere
     3. Base           the defaults every element starts from
     4. Layout         page width, sections, section headings
     5. Text styles    labels, prices
     6. Buttons        primary (Fern), secondary (outlined), text links
     7. Header         the bar across the top, and the phone menu
     8. Hero           the first screen of the main page: always a product, or the newest few in turn
     9. Open now       with several open, every open drop as a grid of cards
    10. The idea       the C-mark completes itself while the captions change
    11. About          what we make and why us; and the sealed next drop
    12. Sign-up        the email band, on dark
    13. Footer
    14. Drop page      pictures, buy box, price, picker, film, features, specification, questions, buy bar
    15. Small print    the legal pages
    16. Preview only   the preview bar and the yellow "not decided yet" markers
    17. Motion         intro states, fade-ins, and the rules smooth scrolling needs
    18. Themes         photographs with a copy for each theme, and the switch between themes
    19. Page morphs    a drop's photograph glides from the main page into the drop page
    20. Tones          one background for the whole page, blending from section to section

   COLOURS ARE NOT SET IN THIS FILE. Each page writes its own palette into its <head> as CSS variables:
   --ground, --ground-2, --ink, --muted, --rule, --dark, --on-dark, --muted-on-dark, --fern, --on-fern,
   --focus (see lib/palette.mjs). That is how a drop page gets its own colours without its own stylesheet.
   Every page carries two palettes, light and dark, and data-theme on <html> says which one is showing
   (section 18). Anything written with the variables works in both themes without a rule of its own.

   Fern (--fern) is the only accent, and only ever a FILL: the main button and the piece in the C-mark.
   One exception, Jack's (12 November 2026): None open's drawings draw the missing piece in a Fern line.
   Never use it for text. Fern text fails contrast on every background we use (BRAND.md).

   CLASS NAMES follow one pattern:  .block  .block__part  .block--variant
     .card             a drop card on the main page
     .card__title      the title inside it
     .button--primary  the Fern version of a button
   ===================================================================================================== */


/* 1. Fonts ----------------------------------------------------------------------------------------- */
@font-face { font-family: 'Jost'; src: url('fonts/jost-var.woff2') format('woff2'); font-weight: 100 900; font-display: swap; }
@font-face { font-family: 'Hanken Grotesk'; src: url('fonts/hanken-grotesk-var.woff2') format('woff2'); font-weight: 100 900; font-display: swap; }
@font-face { font-family: 'Spline Sans Mono'; src: url('fonts/spline-sans-mono.woff2') format('woff2'); font-weight: 400; font-display: swap; }
/* The SemiBold, for the words on the green buttons (Jack, 12 November 2026). Fontsource 5.3.0's latin subset,
   OFL 1.1, as published. */
@font-face { font-family: 'Spline Sans Mono'; src: url('fonts/spline-sans-mono-600.woff2') format('woff2'); font-weight: 600; font-display: swap; }


/* 2. Settings -------------------------------------------------------------------------------------- */
:root {
  --display: 'Jost', 'Futura', 'Century Gothic', system-ui, sans-serif;   /* headlines, wordmark */
  --body: 'Hanken Grotesk', system-ui, -apple-system, 'Segoe UI', sans-serif;   /* reading text */
  --mono: 'Spline Sans Mono', ui-monospace, Menlo, monospace;            /* labels, the "drawing" layer */

  --max: 1440px;                        /* the widest the content gets (1600px on a 5K display, below) */
  --gutter: clamp(20px, 4.5vw, 72px);   /* space at the left and right edges */
  --section: clamp(96px, 12vw, 168px);  /* space above and below each section */
  --header: 72px;                       /* height of the top bar */

  --text-label: 0.8125rem;              /* 13px: the smallest text anywhere on the site */
  --text-body: 1.0625rem;               /* 17px */
  --text-lede: clamp(1.125rem, 1.4vw, 1.3125rem);
  --text-h3: clamp(1.375rem, 1.8vw, 1.625rem);
  --text-h2: clamp(2.25rem, 4.6vw, 4.25rem);
  --text-h1: clamp(2.85rem, 6.8vw, 7rem);    /* sized so "the drop forgot." stays on one line */

  --pill: 999px;                        /* fully rounded ends: buttons, tags */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}
/* A 5K display: above 2200px wide the column grows from 1440 to 1600px on every page, so a drop's pictures
   take about a third of the window rather than a quarter, and the header stays put from page to page (Jack,
   5 November 2026). Nothing changes below 2200px. */
@media (min-width: 2200px) { :root { --max: 1600px; } }
/* A phone's top bar is a little shorter: on a screen that small every line of it counts (Jack, 13 November 2026:
   the phone version "way too spaced out"). */
@media (max-width: 860px) { :root { --header: 60px; } }


/* 3. Base ------------------------------------------------------------------------------------------ */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; scroll-padding-top: calc(var(--header) + 16px); }
body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font: 400 var(--text-body)/1.6 var(--body);
  -webkit-font-smoothing: antialiased;
}
img, svg { display: block; max-width: 100%; }
img { height: auto; }
a { color: inherit; text-underline-offset: 0.2em; text-decoration-thickness: 1px; }
h1, h2, h3, p, ul, ol, figure, fieldset { margin: 0; }
ul, ol { padding: 0; list-style: none; }
h1, h2, h3 { font-family: var(--display); font-weight: 300; line-height: 1.05; letter-spacing: -0.015em; text-wrap: balance; }
p, li { text-wrap: pretty; }
button, input { font: inherit; color: inherit; }
::selection { background: var(--ink); color: var(--ground); }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
[tabindex="-1"]:focus { outline: none; }   /* a section that a link moved focus to, not a control */
.skip-link { position: absolute; left: -9999px; top: 12px; z-index: 100; background: var(--ink); color: var(--ground); padding: 10px 16px; }
.skip-link:focus { left: 12px; }
.mark { display: block; }
.visually-hidden { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }


/* 4. Layout ---------------------------------------------------------------------------------------- */
.wrap { width: 100%; max-width: var(--max); margin-inline: auto; padding-inline: var(--gutter); }
.section { padding-block: var(--section); }
.section--alt { background: var(--ground-2); }
.section__head { display: grid; gap: 20px; max-width: 760px; margin-bottom: clamp(48px, 6vw, 88px); }
.section__title { font-size: var(--text-h2); }
.section__lede { font-size: var(--text-lede); line-height: 1.55; color: var(--muted); max-width: 52ch; }


/* 5. Text styles ----------------------------------------------------------------------------------- */
/* The label: small, spaced capitals in the mono face. The "working drawing" layer of the brand. */
.label { font-family: var(--mono); font-size: var(--text-label); font-weight: 400; line-height: 1.5; letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted); }
.price { font-family: var(--display); font-weight: 400; font-size: 1.5rem; letter-spacing: -0.01em; }


/* 6. Buttons --------------------------------------------------------------------------------------- */
.button {
  display: inline-flex; align-items: center; justify-content: center; gap: 14px;
  min-height: 56px; padding: 0 30px;
  border: 1px solid transparent; border-radius: var(--pill);
  font-family: var(--mono); font-size: var(--text-label); letter-spacing: 0.14em; text-transform: uppercase;
  text-decoration: none; white-space: nowrap; cursor: pointer;
  transition: background-color 0.25s var(--ease), color 0.25s var(--ease), border-color 0.25s var(--ease);
}
.button__arrow { width: 18px; height: 18px; flex: none; transition: transform 0.3s var(--ease); }
.button:hover .button__arrow { transform: translateX(4px); }
/* Dark words on the green: the mono's SemiBold at a size up from the labels, a little less spaced, with the
   site's own smoothing (Jack, 9 November 2026: the regular weight read too thin; 12 November: the Medium still
   did, then the SemiBold a size bigger with the Mac's heavier smoothing read too bold). */
.button--primary { background: var(--fern); color: var(--on-fern); font-size: 0.875rem; font-weight: 600; letter-spacing: 0.12em; }
.button--primary:hover { background: var(--fern-hover); }   /* the green, one step darker (tokens.json derivedSurfaces: fernHover, fernLiftedHover) */
.button--secondary { border-color: currentColor; background: transparent; }
.button--secondary:hover { background: var(--ink); border-color: var(--ink); color: var(--ground); }
.button--text { min-height: 0; padding: 0 0 6px; gap: 10px; border-radius: 0; border-bottom: 1px solid currentColor; }
.button--small { min-height: 42px; padding: 0 20px; }
.button--block { width: 100%; min-height: 60px; white-space: normal; text-align: center; }


/* 7. Header ---------------------------------------------------------------------------------------- */
.header { position: sticky; top: 0; z-index: 50; border-bottom: 1px solid transparent; transition: background-color 0.3s var(--ease), border-color 0.3s var(--ease); }
/* Nearly solid, so nothing bright shows through under the links. No blur behind it: at 95% it would
   barely show, it flashed over the photograph while the colour faded in, and it is costly to draw on a
   large screen. */
.header.is-scrolled, .header[data-open] {
  background: color-mix(in srgb, var(--tone) 95%, transparent);
  border-bottom-color: var(--rule);
}
.header__inner { height: var(--header); display: flex; align-items: center; gap: 32px; }
/* Without JavaScript nothing tells the header the page has scrolled, so it keeps a background throughout. */
html:not(.js) .header { background: var(--ground); border-bottom-color: var(--rule); }
.brand { display: inline-flex; align-items: center; gap: 12px; color: var(--ink); text-decoration: none; }
.brand .mark { width: 30px; height: 30px; flex: none; }
/* The wordmark: Jost, capitals, weight 400, +0.16em (BRAND.md, locked). */
.brand__word { font-family: var(--display); font-size: 1.3rem; font-weight: 400; line-height: 1; letter-spacing: 0.16em; text-transform: uppercase; }
.brand--large .mark { width: 40px; height: 40px; }
.brand--large .brand__word { font-size: 1.6rem; }
.header__nav { display: flex; gap: 36px; margin-left: auto; }
.header__nav a { position: relative; padding: 8px 0; font-family: var(--mono); font-size: var(--text-label); letter-spacing: 0.16em; text-transform: uppercase; text-decoration: none; }
.header__nav a::after { content: ""; position: absolute; left: 0; right: 0; bottom: 2px; height: 1px; background: currentColor; transform: scaleX(0); transform-origin: left; transition: transform 0.3s var(--ease); }
.header__nav a:hover::after { transform: scaleX(1); }
.header__end { display: flex; align-items: center; gap: 12px; }
/* The theme switch: a half-filled circle that turns over when the theme changes. It needs JavaScript, so
   without it there is no switch, and the site shows its default theme. On phones it moves into the menu. */
.theme-toggle {
  display: inline-grid; place-items: center; width: 42px; height: 42px; flex: none;
  padding: 0; border: 0; border-radius: 50%; background: transparent; color: inherit; cursor: pointer;
  transition: background-color 0.25s var(--ease);
}
.theme-toggle:hover { background: color-mix(in srgb, currentColor 10%, transparent); }
.theme-toggle svg { width: 22px; height: 22px; transition: transform 0.6s var(--ease); }
:root[data-theme="dark"] .theme-toggle svg { transform: rotate(180deg); }
@media (prefers-color-scheme: dark) { :root:not([data-theme]) .theme-toggle svg { transform: rotate(180deg); } }
html:not(.js) .theme-toggle, .theme-toggle--menu { display: none; }
.header__menu { display: none; min-height: 42px; padding: 0 18px; border: 1px solid currentColor; border-radius: var(--pill); background: transparent; font-family: var(--mono); font-size: var(--text-label); letter-spacing: 0.14em; text-transform: uppercase; cursor: pointer; }
/* The header's one button, again as the menu's last item (templates/layout.mjs header). Only on phones and
   tablets, where the bar has no room for it; above 860px it is in the bar itself. */
.header__nav-cta { display: none; }

@media (max-width: 860px) {
  .header__cta { display: none; }
  .header__nav .header__nav-cta { display: block; }
  .header__end { margin-left: auto; }   /* the Menu button sits at the right edge */
  /* With JavaScript, the links fold away behind a Menu button. Without it, they stay visible. */
  .js .header__menu { display: inline-flex; align-items: center; }
  .js .header__nav { display: none; position: absolute; top: var(--header); left: 0; right: 0; flex-direction: column; gap: 0; padding: 8px var(--gutter) 24px; background: var(--tone); border-bottom: 1px solid var(--rule); }
  .js .header[data-open] .header__nav { display: flex; }
  .js .header__nav a { padding: 18px 0; border-bottom: 1px solid var(--rule); }
  /* In the open menu it is outlined, as it is in the bar, under the links. */
  .js .header__nav .header__nav-cta { display: flex; align-items: center; justify-content: center; min-height: 48px; margin-top: 22px; padding: 0 20px;
    border: 1px solid currentColor; border-radius: var(--pill); letter-spacing: 0.14em; transition: background-color 0.25s var(--ease), color 0.25s var(--ease), border-color 0.25s var(--ease); }
  .js .header__nav .header__nav-cta::after { content: none; }
  .js .header__nav .header__nav-cta:hover { background: var(--ink); border-color: var(--ink); color: var(--ground); }
  html:not(.js) .header__nav { gap: 18px; }
  /* Without JavaScript the links stay in the bar, where a third one does not fit a phone's width. */
  @media (max-width: 640px) { html:not(.js) .header__nav .header__nav-cta { display: none; } }
  /* The header bar has no room for the theme switch on a phone, so it sits at the foot of the menu. */
  .header__end .theme-toggle { display: none; }
  .js .theme-toggle--menu {
    display: flex; justify-content: space-between; align-items: center; width: 100%; height: auto; padding: 14px 0;
    border-radius: 0; font-family: var(--mono); font-size: var(--text-label); letter-spacing: 0.16em; text-transform: uppercase;
  }
  .js .theme-toggle--menu:hover { background: transparent; }
}
/* The smallest phones (320px): a slightly smaller wordmark so it and the Menu button fit on one line. */
@media (max-width: 400px) {
  .header__inner { gap: 16px; }
  .brand { gap: 10px; }
  .brand .mark { width: 26px; height: 26px; }
  .brand__word { font-size: 1.12rem; }
  .header__menu { padding: 0 14px; }
}


/* 8. Hero ------------------------------------------------------------------------------------------ */
/* The main page opens on a full-width photograph, composed like Max's mockup: the product on the right,
   the headline on the open wall to its left. The hero slides up under the header, which stays
   transparent until you scroll. */
/* --preview-bar is the height of the preview's page bar (assets/site.js measures it), so the first screen
   still ends at the foot of the window in the preview. The real site has no bar, so it is 0 there.
   The first screen is the whole window at every size, a 5K display's included (Jack, 5 November 2026:
   it had stopped at 980px, two thirds of a 2560 by 1440 window). The scroll cue sits at its foot. */
.hero { position: relative; isolation: isolate; display: flex; flex-direction: column; min-height: calc(100svh - var(--preview-bar, 0px)); margin-top: calc(var(--header) * -1); padding-top: var(--header); overflow: hidden; }
.hero__photo { position: absolute; inset: 0; z-index: -1; transform-origin: 70% 60%; }
.hero__photo picture, .hero__photo img, .hero__media picture, .hero__media img { display: block; width: 100%; height: 100%; }
.hero__photo img { object-fit: cover; object-position: var(--focus, 68% 50%); transform-origin: var(--focus, 50% 50%); }   /* --focus: the picture's own (parts.mjs focusStyle); its slow push closes in on it */
/* The slideshow stacks one photograph per drop in the same place; how they take turns is below. */
.hero__media-stack .hero__media { position: absolute; inset: 0; }
/* How the first screen moves (Jack, 21 Oct 2026: "flick, with a slow push aswell"). The camera pulls back as
   the page opens (motion.js intro). With several open, the next drop slides in from the side over the
   last, which drifts away and dims, and its lines slide in after it; the photograph on show keeps
   drifting slowly closer for as long as it shows. With one open, its photograph breathes slowly in and out. */
.hero__media-stack .hero__media { opacity: 1; transform: translateX(100%); transition: transform 0.95s cubic-bezier(0.65, 0, 0.25, 1); }
[data-direction="prev"] .hero__media-stack .hero__media { transform: translateX(-100%); }
.hero__media-stack .hero__media.is-active { z-index: 2; transform: none; }
.hero__media-stack .hero__media.is-leaving { z-index: 1; transform: translateX(-28%); }
[data-direction="prev"] .hero__media-stack .hero__media.is-leaving { transform: translateX(28%); }
.hero__media-stack .hero__media:not(.is-active):not(.is-leaving) { transition: none; }
/* Each drop's photograph sits on the page's own ground, and the one going dims under a shade as the next
   covers it (a shade, not a filter, so it stays cheap to draw while it moves). */
.hero__media-stack .hero__media { background: var(--ground); }
.hero__media-stack .hero__media::after { content: ""; position: absolute; inset: 0; z-index: 1; background: #000; opacity: 0; transition: opacity 0.95s var(--ease); pointer-events: none; }
.hero__media-stack .hero__media.is-leaving::after { opacity: 0.55; }
.hero__media-stack .hero__media:not(.is-leaving)::after { transition: none; }
.hero__media-stack .hero__media.is-active img, .hero__media-stack .hero__media.is-leaving img { animation: hero-push 9s linear both; }
.hero--drop:not(.hero--slider) .hero__photo img { animation: hero-breathe 16s ease-in-out infinite alternate; }
@keyframes hero-push { from { transform: scale(1.02); } to { transform: scale(1.12) translateX(-1.5%); } }
@keyframes hero-breathe { from { transform: scale(1); } to { transform: scale(1.06) translateX(-1%); } }
/* The words go as soon as their drop starts to leave, and the next drop's words arrive only once its
   photograph has crossed under them (about half way through the flick), so no drop's words ever sit on
   another drop's photograph. The lines arrive one after another. */
.hero--slider .hero__slide-copy > * { transition: opacity 0.5s var(--ease) 0.5s, transform 0.75s var(--ease) 0.5s; }
.hero--slider .hero__slide-copy:not(.is-active) > * { opacity: 0; transform: translateX(56px); transition-duration: 0.25s, 0.4s; transition-delay: 0s; }
.hero--slider .hero__slide-copy.is-leaving > * { transform: translateX(-40px); }
.hero--slider .hero__slide-copy.is-active > :nth-child(2) { transition-delay: 0.57s; }
.hero--slider .hero__slide-copy.is-active > :nth-child(3) { transition-delay: 0.64s; }
.hero--slider .hero__slide-copy.is-active > :nth-child(4) { transition-delay: 0.71s; }
.hero--slider .hero__slide-copy.is-active > :nth-child(5) { transition-delay: 0.78s; }
.hero--slider .hero__slide-copy.is-active > :nth-child(6) { transition-delay: 0.85s; }
/* A drop with no photograph yet (only ever a preview placeholder), and the "nothing open" page, show a faded C-mark. */
.hero__media--mark { display: grid; place-items: center; background: var(--ground); color: color-mix(in srgb, var(--ink) 10%, var(--ground)); }
.hero__media--mark .mark { width: min(40vw, 520px); height: auto; margin-left: 32%; }
.hero--none .hero__media--mark { position: absolute; inset: 0; z-index: -1; }
.hero__inner { position: relative; z-index: 1; flex: 1; display: flex; align-items: center; padding-block: clamp(32px, 6vh, 80px) clamp(104px, 13vh, 148px); }
/* A shade along the top of the first screen, under the header, so the header reads over any photograph,
   however bright the top of it is (a white frame, a lit product). The header has no background of its
   own until the page scrolls (section 7). */
.hero::before { content: ""; position: absolute; z-index: 0; left: 0; right: 0; top: 0; height: calc(var(--header) + 110px); pointer-events: none;
  background: linear-gradient(to bottom, color-mix(in srgb, var(--tone) 86%, transparent) 0%, color-mix(in srgb, var(--tone) 55%, transparent) 50%, transparent 100%); }
/* The first screen fades into the page at its foot, wherever the photograph has drifted to as you
   scroll away, so it runs on into the next section instead of stopping at an edge. --tone is the page's
   colour of the moment (section 20). */
.hero::after { content: ""; position: absolute; z-index: 0; left: 0; right: 0; bottom: 0; height: 30%; pointer-events: none; background: linear-gradient(to bottom, transparent, var(--tone)); }
/* On larger screens the photograph has no mask of its own (section 20), so this shade does all the
   fading. It eases in towards the foot, the curve the old mask and this shade made together, so the
   photograph is all but gone before the edge rather than meeting a line there. */
@media (min-width: 861px) {
  .hero::after { background: linear-gradient(to bottom, transparent, color-mix(in srgb, var(--tone) 26%, transparent) 26%, color-mix(in srgb, var(--tone) 65%, transparent) 49%, color-mix(in srgb, var(--tone) 84%, transparent) 66%, color-mix(in srgb, var(--tone) 96%, transparent) 83%, var(--tone)); }
}
/* With motion, the idea starts under that fade, so its heading arrives as the photograph melts away. The
   same after the grid of open drops: the idea's stage centres its words, so its empty top half would
   otherwise sit under the grid's own space. */
.motion .hero + .idea, .motion .shop + .idea { margin-top: -16svh; }
.hero__content { display: grid; gap: 28px; max-width: min(720px, 50%); }
.hero__title { font-size: var(--text-h1); line-height: 0.94; letter-spacing: -0.03em; }
/* Each line sits in its own mask, so the intro can raise it into view. */
.hero__title .line { display: block; overflow: hidden; padding-bottom: 0.1em; margin-bottom: -0.1em; }
.hero__title .line__inner { display: inline-block; }
.hero__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 20px 32px; margin-top: 6px; }
.hero__foot { position: absolute; z-index: 1; left: 0; right: 0; bottom: 0; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; padding-block: 20px 6px; }
.hero__foot > :first-child { justify-self: start; }
.hero__foot > :last-child { justify-self: end; text-align: right; }
.hero__foot .label { color: var(--ink); }
/* The cue to scroll: a small, thin V centred at the foot of the screen (Max's mockup), bobbing gently. */
.hero__scroll { display: grid; place-items: center; width: 96px; height: 60px; color: var(--ink); }
.hero__scroll svg { width: 44px; height: 26px; animation: nudge 2.4s var(--ease) infinite; }
.hero__foot--cue { grid-template-columns: 1fr; }
.hero__foot--cue > .hero__scroll { justify-self: center; }
@keyframes nudge { 0%, 100% { transform: translateY(-2px); opacity: 0.7; } 50% { transform: translateY(8px); opacity: 1; } }

/* The first screen is always a product: under the tagline, the drop it shows, set off by a hairline. */
.hero__drop { display: grid; justify-items: start; gap: 12px; padding-top: 26px; border-top: 1px solid color-mix(in srgb, var(--ink) 22%, transparent); max-width: 460px; }
.hero__lockup { color: var(--ink); }
/* The lockup, then its status on a line of its own (templates/home.mjs dropBlock). */
.hero__lockup-line, .hero__lockup-status { display: block; }
.hero__name { font-size: var(--text-h2); font-weight: 300; }
.hero__gap { max-width: 34ch; font-family: var(--display); font-size: var(--text-h3); font-weight: 300; line-height: 1.3; color: var(--muted); }
.hero__meta { display: flex; align-items: baseline; gap: 22px; padding-top: 4px; }
/* The time left is the first screen's one counter-accent element: Halfway on the dark site (lib/palette.mjs
   counter; both founders, 5 November 2026). */
.hero__meta [data-countdown], .hero__meta .label { color: var(--counter); }
/* Under the price, what the piece comes without (content/site.mjs drop.notIncluded). In the ink colour,
   not the muted one, so it is read with the price, not missed as small print. */
.hero__excludes { margin-top: -4px; color: var(--ink); }
.hero--drop .hero__content { gap: 34px; }
.hero--drop .hero__actions { margin-top: 10px; }
.hero--drop .hero__title { font-size: clamp(2.6rem, 5.6vw, 6rem); }
.hero--sealed .signup__form--compact { width: min(100%, 460px); }
/* A soft shade behind the words, so they read over any drop's photograph, not only one shot with open
   wall on the left. Phones have their own, at the top (below). */
@media (min-width: 861px) {
  .hero--drop .hero__photo::after { content: ""; position: absolute; inset: 0; pointer-events: none;
    background: linear-gradient(90deg, color-mix(in srgb, var(--ground) 82%, transparent) 0%, color-mix(in srgb, var(--ground) 48%, transparent) 36%, transparent 60%); }
}

/* The slideshow's words: every slide's parts are stacked in the same place and only the active ones show. */
.hero__stack { display: grid; }
.hero__stack > * { grid-area: 1 / 1; opacity: 0; visibility: hidden; transition: opacity 0.25s var(--ease), visibility 0s linear 0.25s; }
.hero__stack > .is-active { opacity: 1; visibility: visible; transition: opacity 0.5s var(--ease) 0.45s, visibility 0s; }
.hero__counter { display: flex; align-items: center; gap: 14px; }
.hero__counter .label { display: inline-flex; align-items: center; gap: 12px; }
.hero__counter-rule { position: relative; width: 56px; height: 1px; overflow: hidden; background: var(--rule); }
.hero__counter-rule::after { content: ""; position: absolute; inset: 0; background: var(--ink); transform: scaleX(0); transform-origin: left; }
.hero--slider.is-playing .hero__counter-rule::after { animation: slide-progress var(--slide-time, 7s) linear forwards; }
@keyframes slide-progress { to { transform: scaleX(1); } }
.hero__step { display: grid; place-items: center; width: 40px; height: 40px; border: 1px solid var(--rule); border-radius: 50%; background: transparent; cursor: pointer; transition: border-color 0.2s; }
.hero__step:hover { border-color: var(--ink); }
.hero__step svg { width: 18px; height: 18px; }
[data-slider-prev] svg { transform: rotate(90deg); }
[data-slider-next] svg { transform: rotate(-90deg); }

/* On a phone the photograph fills the screen with the product low in the frame, and the words sit on
   the wall above it. A soft wash at the top keeps them readable over the leaf shadows. */
@media (max-width: 860px) {
  .hero { min-height: calc(100svh - var(--preview-bar, 0px)); }
  /* the photograph fills the lower part of the screen; its top edge fades into the page behind the words */
  .hero__photo { top: 30%; }
  /* The first screen with a drop on it: the words, then the photograph under them in the page's flow,
     filling whatever height is left, so the words never sit on the product. */
  .hero--drop .hero__inner { flex: 0 0 auto; padding-block: 22px 18px; }
  .hero--drop .hero__photo { position: relative; inset: auto; top: auto; order: 2; flex: 1 1 0; min-height: 40svh; }
  .hero--drop .hero__photo picture, .hero--drop .hero__photo img, .hero--drop .hero__media { position: absolute; inset: 0; }
  .hero--drop .hero__photo::after { height: 26%; }
  .hero--drop .hero__photo img { object-position: var(--focus-phone, 50% 78%); transform-origin: var(--focus-phone, 50% 50%); }
  .tones .hero--drop .hero__photo { -webkit-mask-image: linear-gradient(to bottom, #000 90%, transparent); mask-image: linear-gradient(to bottom, #000 90%, transparent); }
  .hero--drop .hero__title { font-size: clamp(2.3rem, 10vw, 3.2rem); }
  .hero__drop { gap: 8px; padding-top: 18px; }
  .hero__name { font-size: 2rem; }
  .hero__photo img { object-position: var(--focus-phone, 50% 100%); transform-origin: var(--focus-phone, 50% 50%); }
  .hero__photo::after { content: ""; position: absolute; inset: 0 0 auto 0; height: 38%; background: linear-gradient(to bottom, var(--tone), transparent); }
  .hero__inner { align-items: flex-start; padding-block: 28px 112px; }
  .hero__content { max-width: none; gap: 20px; }
  /* The labels share the top row; the V has the row under them to itself, in the middle. */
  .hero__foot { grid-template-columns: 1fr auto; row-gap: 2px; padding-block: 14px 6px; }
  .hero__foot .label { white-space: nowrap; }
  .hero__foot > .hero__scroll { grid-row: 2; grid-column: 1 / -1; justify-self: center; }
  .hero__media--mark .mark { width: 70vw; margin: 48% 0 0; }
}


/* 9. Open now -------------------------------------------------------------------------------------- */
/* Every open drop, when more than one is open: a grid of cards, closing soonest first. */
.shop { padding-bottom: clamp(48px, 6vw, 88px); }
.shop__head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 6px 16px; margin-bottom: 32px; padding-bottom: 16px; border-bottom: 1px solid var(--rule); }
.shop__head > * { white-space: nowrap; }   /* on a narrow phone the note moves under the title, whole */
.shop__title { color: var(--ink); }
/* As many columns as there are drops, up to four, so two open are two good-sized cards rather than two
   cards beside two empty places. --cols is written by the template. */
.shop__grid { --cols: 4; display: grid; grid-template-columns: repeat(var(--cols), minmax(0, 1fr)); gap: clamp(28px, 3vw, 48px) clamp(20px, 2.5vw, 40px);
  max-width: calc(var(--cols) * 400px + (var(--cols) - 1) * 40px); }
@media (max-width: 1100px) { .shop__grid { grid-template-columns: repeat(min(var(--cols), 3), minmax(0, 1fr)); } }
/* Two open fill the row (Jack, 9 November 2026): two large cards across the whole column, their pictures
   landscape so a card still fits the window. */
.shop__grid--pair { max-width: none; }
.shop__grid--pair .card__media { aspect-ratio: 4 / 3; }
.shop__grid--pair .card__title { font-size: var(--text-h2); font-weight: 300; }
@media (max-width: 640px) { .shop__grid.shop__grid--pair { grid-template-columns: 1fr; } .shop__grid.shop__grid--pair .card__title { font-size: 1.6rem; } }   /* one above the other on a phone (outranks the two-up phone rule below) */
@media (max-width: 640px) { .shop__grid { grid-template-columns: 1fr 1fr; gap: 32px 18px; } .shop__grid .card__title { font-size: 1.1rem; } .shop__grid .card__meta { flex-direction: column; gap: 4px; } }
@media (max-width: 380px) { .shop__grid .card__meta .label { letter-spacing: 0.06em; } }   /* "Closes in 4 days" on one line */
/* The same cards at the foot of a drop's page ("Also open", templates/drop.mjs): smaller, a way on rather
   than a second shop window, and closer to the questions above them. */
.also { padding-top: 0; }
.also .shop__head, .also .shop__grid { max-width: calc(var(--cols) * 300px + (var(--cols) - 1) * 32px); }
.also .shop__head a { padding-block: 8px; margin-block: -8px; }   /* a target big enough to tap, in the same place */

/* A drop card. */
.card { display: flex; flex-direction: column; color: var(--ink); text-decoration: none; }
.card__media { position: relative; aspect-ratio: 4 / 5; overflow: hidden; background: var(--ground-2); }
/* The hover zoom is the scale property, not transform, so the cards' arrival (motion.js grids()) can move
   the photograph's transform without the two fighting. */
.card__media img { width: 100%; height: 100%; object-fit: cover; object-position: var(--focus, 50% 50%); transform-origin: var(--focus, 50% 50%); transition: scale 1.4s var(--ease); }
.card:hover .card__media img { scale: 1.04; }
.card__placeholder { position: absolute; inset: 0; display: grid; place-items: center; color: var(--ink); }
.card__placeholder .mark { width: 30%; }
.card__tag { position: absolute; top: 16px; left: 16px; padding: 6px 12px; border-radius: var(--pill); background: var(--ground); color: var(--ink); }
.card__body { display: flex; flex-direction: column; flex: 1; gap: 10px; padding-top: 22px; }
/* The lockup breaks after "for" (templates/home.mjs card) and always takes the same room, so the titles of
   a row line up however long each release's name is (Jack, 5 November 2026): two lines, three on phones,
   where a long name wraps once more in the narrow column. */
.card__release { min-height: calc(2 * 1lh); }
@media (max-width: 640px) { .card__release { min-height: calc(3 * 1lh); } }
.card__title { font-size: var(--text-h3); font-weight: 400; }
.card__meta { display: flex; align-items: baseline; justify-content: space-between; margin-top: auto; padding-top: 18px; border-top: 1px solid var(--rule); }


/* 10. The idea ------------------------------------------------------------------------------------- */
/* The C-mark tells the story. Without motion: the three captions stacked beside the finished mark.
   With motion: the section pins, the captions take turns in one place, and the mark draws itself. */
.idea { background: var(--ground-2); }
.idea__pin { display: flex; align-items: center; min-height: 100svh; padding-block: var(--section); }
.idea__grid { display: grid; grid-template-columns: 1fr; gap: clamp(36px, 6vw, 96px); align-items: center; }
@media (min-width: 900px) { .idea__grid { grid-template-columns: 1fr 1fr; } }
.idea__text { display: grid; gap: 24px; }
.idea__steps { display: grid; gap: 36px; margin-top: 12px; }
.idea__step { display: grid; gap: 10px; max-width: 440px; }
.idea__title { font-size: var(--text-h3); font-weight: 400; }
.idea__body { color: var(--muted); }
.idea__dots { display: none; gap: 8px; }
.idea__dots span { width: 28px; height: 2px; background: var(--rule); transition: background-color 0.4s var(--ease); }
.idea__dots span.is-active { background: var(--ink); }
.idea__text > .button { justify-self: start; margin-top: 8px; }
.idea__art { display: grid; place-items: center; color: var(--ink); }
.idea__mark { width: min(100%, 460px); height: auto; overflow: visible; }
/* A 5K display: the scene grows with the wider column, so the C and its words fill the window as they do on
   a laptop rather than sitting small in the middle of it (Jack, 9 November 2026). */
@media (min-width: 2200px) {
  .idea .section__title { font-size: 5.25rem; }
  .idea__title { font-size: 2rem; }
  .idea__step { max-width: 560px; }
  .idea__mark { width: min(100%, 640px); }
}
/* Two working-drawing layers under the mark: a faint outline of the whole C, which the ring traces over,
   and a dashed outline of the missing piece, shown while the gap is waiting for it (motion.js). */
.idea__ghost { opacity: 0.1; }
.idea__slot { opacity: 0; }
.motion .idea__pin { padding-block: calc(var(--header) + 24px) 48px; }
/* About us: the stage is only as tall as its words and mark, so the idea sits as close to what comes
   before and after it as any other section (Jack, 24 Oct 2026: "way too spaced out"). motion.js pins it
   by its middle. */
.motion .idea[data-pin="short"] .idea__pin { min-height: 0; padding-block: 48px; }
.motion .hero + .idea[data-pin="short"] { margin-top: 0; }   /* no empty top half to hide under the fade */
.motion .idea__steps { grid-template-areas: "step"; }
.motion .idea__step { grid-area: step; opacity: 0; transform: translateY(14px); transition: opacity 0.5s var(--ease), transform 0.5s var(--ease); }
.motion .idea__step.is-active { opacity: 1; transform: none; }
.motion .idea__dots { display: flex; }
@media (max-width: 899px) {
  .idea__art { order: -1; }
  .idea__mark { width: min(56vw, 260px); }
}


/* 11. About: what we make, and the sealed next drop ------------------------------------------------ */
/* About: what we make (a first screen like the main page's: the work, and the words on its open wall),
   the idea in full, the pictures, what a buyer can count on, and the drops open now. */
/* Without it, and with it (templates/home.mjs compare): the piece's view laid over the release's, cut
   at --split. A pointer drags the picture itself; the slider under it is for the keyboard (site.js). */
.compare { margin: 0; }
.compare__frame { position: relative; aspect-ratio: 16 / 9; overflow: hidden; background: var(--ground-2); touch-action: pan-y; cursor: ew-resize; user-select: none; -webkit-user-select: none; }
.compare img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: var(--focus, 50% 40%); -webkit-user-drag: none; }
.compare__with { position: absolute; inset: 0; clip-path: inset(0 0 0 var(--split)); }
.compare__line { position: absolute; top: 0; bottom: 0; left: var(--split); width: 2px; margin-left: -1px; background: #E9E5DD; pointer-events: none; }   /* Greige: the handle sits on photographs, so it keeps the brand values in either theme */
/* The handle follows the cut but stays whole inside the picture at either end. */
.compare__knob { position: absolute; top: 50%; left: clamp(31px, var(--split), calc(100% - 31px)); display: grid; place-items: center; width: 54px; height: 54px; margin: -27px 0 0 -27px; border-radius: 50%; background: #E9E5DD; color: #24211D; box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35); pointer-events: none; }
.compare__knob svg { width: 22px; height: 22px; }
.compare__tag { position: absolute; top: 18px; padding: 7px 12px; border-radius: var(--pill); background: color-mix(in srgb, #24211D 78%, transparent); color: #E9E5DD; font: 400 var(--text-label)/1 var(--mono); letter-spacing: 0.14em; text-transform: uppercase; pointer-events: none; }
.compare__tag--without { left: 18px; }
.compare__tag--with { right: 18px; }
/* The slider is there for the keyboard and screen readers; pointers drag the picture itself (site.js). */
.compare__range { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; pointer-events: none; }
.compare__frame:has(.compare__range:focus-visible) { outline: 2px solid var(--focus); outline-offset: 4px; }
.compare__caption { padding-top: 16px; color: var(--muted); }
@media (max-width: 860px) {
  .compare__frame { aspect-ratio: 4 / 5; }
  .compare img { object-position: var(--focus, 45% 40%); }
  .compare__tag--with { top: auto; bottom: 18px; }   /* the two labels would meet across a phone's width */
}

/* Attention: how we find what is missing, beside a wall of what collectors actually wrote. */
/* The before-and-after and Attention read as one thought (what a piece adds, and how we find it), so the
   space between them is about half a usual section break (Jack, 28 Oct 2026: "feels too big"). */
.compare-band { padding-bottom: clamp(32px, 4vw, 64px); }
.attention { padding-top: clamp(64px, 8vw, 120px); }
.attention__grid { display: grid; gap: clamp(32px, 5vw, 80px); align-items: center; }
.attention__text { display: grid; gap: 16px; }
.attention__title { font-size: var(--text-h2); font-weight: 300; }
.attention__body { max-width: 42ch; color: var(--muted); }
.attention__wall { display: grid; gap: 14px; margin: 0; padding: 0; list-style: none; }
.reply { display: grid; gap: 10px; padding: 18px 20px; border: 1px solid var(--rule); border-radius: 14px; background: var(--ground-2); }
.reply__about, .reply__where { color: var(--muted); }
.reply__where { font-size: 0.85rem; }
.reply__quote { margin: 0; font-size: 1.08rem; line-height: 1.45; }
@media (min-width: 860px) {
  .attention__grid { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
  .attention__wall { grid-template-columns: 1fr 1fr; }
  .reply:nth-child(2n) { position: relative; top: 28px; } /* not a transform: the arrival rise owns that */
}

/* In use: a piece in someone's hands, the full width of the page, fading into it top and bottom. No words
   over it (Jack, 5 November 2026). */
.in-use { position: relative; }
.in-use__photo { position: relative; height: min(80svh, 780px); overflow: hidden; }
.in-use__photo img { width: 100%; height: 100%; object-fit: cover; object-position: var(--focus, 50% 55%); }
.in-use__photo::before, .in-use__photo::after { content: ""; position: absolute; left: 0; right: 0; z-index: 1; height: 24%; pointer-events: none; }
.in-use__photo::before { top: 0; background: linear-gradient(var(--tone), transparent); }
.in-use__photo::after { bottom: 0; background: linear-gradient(transparent, var(--tone)); }

.promise__points { display: grid; grid-template-columns: 1fr; column-gap: clamp(28px, 4vw, 56px); }
.promise__point { display: grid; align-content: start; gap: 12px; padding-block: 8px 36px; }
.promise__title { font-size: var(--text-h3); font-weight: 400; }
.promise__body { max-width: 40ch; color: var(--muted); }
.promise__answer { display: inline-block; }   /* a two-line title breaks after its question */
.promise__more { color: var(--muted); }
.promise__more a { color: var(--ink); }
@media (min-width: 860px) { .promise__points { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.about-end { padding-top: clamp(24px, 4vw, 56px); }


/* The next drop: a full-width photograph of a covered piece under one light, with the countdown over
   its dark left half (on phones, over its foot). The photograph is a little taller than the section so
   motion.js can drift it for depth. */
.next { position: relative; display: flex; align-items: center; min-height: clamp(560px, 92vh, 980px); overflow: hidden; background: var(--ground); }
.next__photo { position: absolute; left: 0; right: 0; top: -8%; height: 116%; }
.next__photo picture, .next__photo img { display: block; width: 100%; height: 100%; }
.next__photo img { object-fit: cover; object-position: var(--focus, 68% 50%); }
.next__shade { position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(90deg, color-mix(in srgb, var(--tone) 85%, transparent) 0%, color-mix(in srgb, var(--tone) 40%, transparent) 40%, transparent 65%); }
.next__text { position: relative; z-index: 1; display: grid; justify-items: start; gap: 18px; padding-block: var(--section); }
.next__title { font-size: clamp(3rem, 7vw, 6.5rem); }
.next__body { max-width: 30ch; font-size: var(--text-lede); line-height: 1.55; color: var(--muted); }
.next__count { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 14px; margin-block: 10px 8px; }
.next__clock { font-family: var(--mono); font-size: clamp(1.2rem, 1.9vw, 1.6rem); letter-spacing: 0.04em; font-variant-numeric: tabular-nums; color: var(--counter-on-dark); }   /* the countdown in Halfway: the section's one counter-accent element */
@media (max-width: 860px) {
  .next { align-items: flex-end; }
  .next__photo img { object-position: var(--focus-phone, 50% 50%); }
  .next__shade { background: linear-gradient(0deg, color-mix(in srgb, var(--tone) 95%, transparent) 0%, color-mix(in srgb, var(--tone) 72%, transparent) 42%, transparent 72%); }
  .next__text { padding-block: 40% 56px; }
}


/* One open: what follows the first screen (templates/home.mjs solo; Jack, 9 November 2026): the
   before-and-after, the piece up close, one real comment, the idea told with the drop's own pictures, a
   last call, and the next drop with the list in one band. */
/* The before-and-after on the main page keeps a full section's space below it (on About us it sits close
   to Attention). */
.compare-band--main { padding-bottom: var(--section); }

/* The idea told with the drop's own pictures: the release, the piece on its own, then both, in a 4:5 frame,
   with the C-mark small in its corner drawing itself as the captions change. Without motion: the last
   picture, the two together, beside the finished mark. */
.idea__photos { position: relative; width: min(100%, 520px); aspect-ratio: 4 / 5; overflow: hidden; background: var(--ground); }
.idea__photo { position: absolute; inset: 0; opacity: 0; }
.idea__photo[data-idea-photo="2"] { opacity: 1; }
.motion .idea__photo { opacity: 0; transform: scale(1.04); transition: opacity 0.7s var(--ease), transform 1.4s var(--ease); }
.motion .idea__photo.is-active { opacity: 1; transform: none; }
.idea__photo picture, .idea__photo img { display: block; width: 100%; height: 100%; }
.idea__photo img { object-fit: cover; object-position: var(--focus, 50% 40%); }
.idea__badge { position: absolute; left: 18px; bottom: 18px; z-index: 1; display: grid; place-items: center; width: 84px; height: 84px; border-radius: 50%; background: color-mix(in srgb, var(--ground-2) 86%, transparent); color: var(--ink); }
.idea__photos .idea__mark { width: 56px; }
@media (min-width: 2200px) { .idea__photos { width: min(100%, 640px); } }
@media (max-width: 899px) {
  .idea__photos { width: min(70vw, 300px, calc(38svh * 0.8)); }
  .idea__badge { left: 12px; bottom: 12px; width: 60px; height: 60px; }
  .idea__photos .idea__mark { width: 40px; }
}

/* The piece up close, for a drop without a film: its feature pictures take the screen in turn, under the
   film's captions (motion.js scenes.stills). Without motion: the first picture, the captions listed. */
.stills__frame { position: absolute; inset: 0; opacity: 0; }
.stills__frame:first-child { opacity: 1; }
.motion .stills__frame { opacity: 0; transition: opacity 0.8s var(--ease); }
.motion .stills__frame.is-active { opacity: 1; }
.stills__frame picture, .stills__frame img { display: block; width: 100%; height: 100%; }
.stills__frame img { object-fit: cover; object-position: var(--focus, 50% 50%); }

/* Why we made it: one real comment, large, with where it was said. */
.why__inner { display: grid; justify-items: start; gap: 28px; }
.why__quote { margin: 0; max-width: 24ch; font-family: var(--display); font-size: clamp(2rem, 4.4vw, 4rem); font-weight: 300; line-height: 1.12; letter-spacing: -0.015em; }
.why__where { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 14px; color: var(--muted); }

/* The last call: the piece once more, the day it closes (in Halfway) and the green button. */
.last-call__grid { display: grid; gap: clamp(32px, 5vw, 80px); align-items: center; }
@media (min-width: 860px) { .last-call__grid { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); } }
.last-call__media { aspect-ratio: 4 / 5; overflow: hidden; background: var(--ground-2); }
.last-call__media picture, .last-call__media img { display: block; width: 100%; height: 100%; }
.last-call__media img { object-fit: cover; object-position: var(--focus, 50% 40%); }
.last-call__text { display: grid; justify-items: start; gap: 18px; }
.last-call__title { font-size: var(--text-h2); font-weight: 300; }
.last-call__when { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 14px; margin-top: 4px; }
.last-call__day { font-family: var(--display); font-size: var(--text-h3); color: var(--counter); }
.last-call__price { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 16px; }
.last-call .hero__actions { margin-top: 10px; }

/* The next drop and the list in one band: the sealed photograph, the list's words, the countdown, the form. */
.next__title--list { font-size: var(--text-h2); }
.next--list .signup__form--compact { width: min(100%, 520px); }
/* On a phone the words and the form need the whole width, so the photograph sits above them, its foot
   fading into the band, instead of under them. */
/* The band is one screen: the photograph takes whatever height the words leave, framed from the top so the cloth
   always has room above it, and fades into the band itself at both ends. The band's own fade is off here, so it
   never dims the form, and nothing cuts into the top of the cloth (Jack, 13 November 2026, on a Galaxy Z Flip:
   "the sealed imagery is cut off on the top"). */
@media (max-width: 860px) {
  .next--list { flex-direction: column; align-items: stretch; min-height: calc(100svh - var(--header)); }
  .tones .next--list { -webkit-mask-image: none; mask-image: none; }
  .next--list .next__photo { position: relative; top: auto; flex: 1 1 auto; height: auto; min-height: min(38svh, 96vw); max-height: 124vw;
    -webkit-mask-image: linear-gradient(to bottom, transparent, #000 9%, #000 58%, transparent 97%); mask-image: linear-gradient(to bottom, transparent, #000 9%, #000 58%, transparent 97%); }
  .next--list .next__photo picture, .next--list .next__photo img { position: absolute; inset: 0; }
  .next--list .next__photo img { object-position: 50% 32%; }
  .next--list .next__shade { display: none; }
  .next--list .next__text { margin-top: -76px; padding-block: 0 36px; gap: 14px; }
  .next--list .next__count { margin-block: 4px 2px; }
}


/* 12. Sign-up --------------------------------------------------------------------------------------- */
.signup { padding-block: var(--section); background: var(--dark); color: var(--on-dark); }
.signup .label { color: var(--muted-on-dark); }
.signup__inner { display: grid; gap: 48px; align-items: end; }
@media (min-width: 900px) { .signup__inner { grid-template-columns: 1.1fr 0.9fr; gap: 96px; } }
.signup__title { margin-top: 18px; font-size: var(--text-h2); }
.signup__body { max-width: 46ch; margin-top: 20px; font-size: var(--text-lede); color: var(--muted-on-dark); }
.signup__form { display: grid; gap: 12px; }
.signup__row { display: flex; gap: 8px; padding: 6px; border: 1px solid color-mix(in srgb, var(--on-dark) 32%, transparent); border-radius: var(--pill); transition: border-color 0.2s; }
.signup__row:focus-within { border-color: var(--on-dark); }
/* A clear ring round the whole row while the address is being typed, like the buttons' (WCAG 2.2, 2.4.13).
   Only for the field: the button inside draws its own ring when it has focus. */
.signup__row:has(input:focus-visible) { outline: 2px solid var(--focus); outline-offset: 3px; }
.signup__row input { flex: 1; min-width: 0; padding: 0 18px; border: 0; background: transparent; color: var(--on-dark); font-size: 1rem; }
.signup__row input::placeholder { color: var(--muted-on-dark); opacity: 1; }
.signup__row input:focus { outline: none; }
.signup__row .button { min-height: 50px; }
.signup__fine, .signup__message { font-size: 0.9rem; color: var(--muted-on-dark); }
.signup__message:empty { display: none; }
@media (max-width: 520px) {
  .signup__row { flex-direction: column; padding: 8px; border-radius: 28px; }
  .signup__row input { min-height: 48px; }
}
/* The compact form inside the main page hero, on the light ground. */
.signup__form--compact { max-width: 520px; margin-top: 8px; }
.signup__form--compact .signup__row { border-color: var(--ink); }
.signup__form--compact .signup__row input { color: var(--ink); }
.signup__form--compact .signup__row input::placeholder,
.signup__form--compact .signup__fine,
.signup__form--compact .signup__message { color: var(--muted); }


/* 13. Footer --------------------------------------------------------------------------------------- */
.footer { padding-block: 80px 40px; background: var(--dark); color: var(--on-dark); border-top: 1px solid color-mix(in srgb, var(--on-dark) 14%, transparent); }
.footer .label { color: var(--muted-on-dark); }
.footer .brand { color: var(--on-dark); }
.footer__top { display: grid; grid-template-columns: 1fr; gap: 48px; }
@media (min-width: 900px) { .footer__top { grid-template-columns: 2fr 1fr 1fr 1fr; } }
.footer__tagline { margin-top: 20px; font-family: var(--display); font-size: 1.5rem; font-weight: 300; color: var(--muted-on-dark); }
.footer__col ul { display: grid; gap: 12px; margin-top: 18px; }
.footer__col a { text-decoration: none; }
.footer__col a:hover { text-decoration: underline; }
.footer__legal { display: grid; gap: 10px; margin-top: 72px; padding-top: 28px; border-top: 1px solid color-mix(in srgb, var(--on-dark) 16%, transparent); font-size: 0.9rem; color: var(--muted-on-dark); }
/* On a phone the columns sit two to a row, contact across the width (its address needs it), and the gaps close
   up: stacked one under another the footer took a screen of its own (Jack, 13 November 2026). */
@media (max-width: 899px) {
  .footer { padding-block: 52px 32px; }
  .footer__top { grid-template-columns: 1fr 1fr; gap: 32px 20px; }
  .footer__brand, .footer__col:last-child { grid-column: 1 / -1; }
  .footer__col ul { gap: 10px; margin-top: 12px; }
  .footer__legal { margin-top: 40px; padding-top: 22px; }
}


/* 14. Drop page ------------------------------------------------------------------------------------ */
.product { padding-block: clamp(16px, 3vw, 40px) var(--section); }
.product__grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: clamp(28px, 4vw, 72px); }
@media (min-width: 980px) { .product__grid { grid-template-columns: minmax(0, 7fr) minmax(360px, 5fr); align-items: start; } }

/* Pictures: stacked on a computer, swiped on a phone. */
/* The pictures. Without JavaScript they are listed one under another. With it (.is-ready, assets/site.js
   gallery()) there is one main picture with a row of thumbnails under it: choosing a thumbnail crossfades
   its picture in, settling from a slight zoom. The last thumbnail of a drop with a model is 3D. */
.gallery { position: relative; min-width: 0; display: grid; gap: 14px; align-content: start; }
.gallery__main { position: relative; display: grid; gap: 12px; }
.gallery__slide { position: relative; overflow: hidden; background: var(--ground-2); }
.gallery__slide img { width: 100%; aspect-ratio: 4 / 5; object-fit: cover; object-position: var(--focus, 50% 40%); transform-origin: var(--focus, 50% 50%); }
.gallery__thumbs, .gallery__step, .gallery__count, .gallery__slide--3d { display: none; }
.gallery.is-ready .gallery__main { display: block; aspect-ratio: 4 / 5; overflow: hidden; background: var(--ground-2); }
.gallery.is-ready .gallery__slide { position: absolute; inset: 0; opacity: 0; visibility: hidden; transition: opacity 0.6s var(--ease), visibility 0s linear 0.6s; }
.gallery.is-ready .gallery__slide.is-active { opacity: 1; visibility: visible; transition: opacity 0.6s var(--ease); }
.gallery.is-ready .gallery__slide img { height: 100%; aspect-ratio: auto; transform: scale(1.06); transition: transform 1.4s var(--ease); }
.gallery.is-ready .gallery__slide.is-active img { transform: none; }
.gallery.is-settling .gallery__slide, .gallery.is-settling .gallery__slide img { transition: none; }   /* the first frames of that layout (site.js gallery) */
/* A clip in the gallery (templates/drop.mjs clip): framed like the pictures, its thumbnail marked to play. */
.gallery__video { display: block; width: 100%; aspect-ratio: 4 / 5; object-fit: cover; object-position: var(--focus, 50% 50%); background: var(--ground-2); }
.gallery.is-ready .gallery__video { height: 100%; aspect-ratio: auto; }
.gallery__play { position: absolute; inset: 0; display: grid; place-items: center; color: var(--ink); pointer-events: none; }
.gallery__play svg { width: 26px; height: 26px; padding: 5px; border-radius: 50%; background: color-mix(in srgb, var(--ground) 70%, transparent); }
.gallery.is-ready .gallery__slide--3d { display: block; background: #0f0d0b; }
.gallery.is-ready .gallery__thumbs { display: grid; grid-template-columns: repeat(auto-fill, minmax(68px, 1fr)); gap: 10px; }
.gallery__thumb { position: relative; aspect-ratio: 1; padding: 0; overflow: hidden; border: 1px solid transparent; background: var(--ground-2); cursor: pointer; opacity: 0.55;
  transition: opacity 0.3s var(--ease), border-color 0.3s var(--ease); }
.gallery__thumb img { width: 100%; height: 100%; object-fit: cover; object-position: var(--focus, 50% 50%); transition: transform 0.6s var(--ease); }
.gallery__thumb:hover { opacity: 0.9; }
.gallery__thumb:hover img { transform: scale(1.06); }
.gallery__thumb[aria-selected="true"] { opacity: 1; border-color: var(--ink); }
.gallery__thumb--3d { display: grid; place-items: center; background: #0f0d0b; color: var(--ink); font-family: var(--mono); font-size: var(--text-label); letter-spacing: 0.16em; }
.gallery.is-ready .gallery__step { position: absolute; top: 50%; z-index: 2; display: grid; place-items: center; width: 44px; height: 44px; margin-top: -22px; padding: 0;
  border: 0; border-radius: 50%; background: color-mix(in srgb, var(--ground) 72%, transparent); color: var(--ink); cursor: pointer; opacity: 0; transition: opacity 0.3s var(--ease); }
.gallery__step svg { width: 18px; height: 18px; }
.gallery__step--prev { left: 14px; }
.gallery__step--next { right: 14px; }
.gallery.is-ready .gallery__main:hover .gallery__step, .gallery.is-ready .gallery__step:focus-visible { opacity: 1; }
.gallery.is-ready .gallery__count { display: block; position: absolute; z-index: 2; right: 14px; top: 14px; padding: 6px 12px; border-radius: var(--pill); background: var(--ground); color: var(--ink); }
/* The 3D view inside the main picture (assets/viewer/viewer.js): its switch at the foot, a note at the top. */
.gallery__slide--3d .fit__stage { position: absolute; inset: 0; }
.fit__canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; cursor: grab; }
.fit__canvas:active { cursor: grabbing; }
.fit__loading { position: absolute; inset: 0; display: grid; place-items: center; margin: 0; color: var(--muted); }
.fit__caption { position: absolute; z-index: 1; left: 14px; right: 14px; top: 56px; margin: 0; color: var(--muted); }   /* under the picture count */
.fit__switch { position: absolute; z-index: 1; left: 14px; bottom: 14px; display: inline-flex; gap: 6px; visibility: hidden; }
.gallery__slide--3d.is-live .fit__switch { visibility: visible; }
.gallery__slide--3d.is-live .fit__loading { display: none; }
.fit__switch button { min-height: 40px; padding: 0 16px; border: 1px solid color-mix(in srgb, var(--ink) 30%, transparent); border-radius: var(--pill); background: color-mix(in srgb, #0f0d0b 70%, transparent); color: var(--ink);
  font-family: var(--mono); font-size: var(--text-label); letter-spacing: 0.14em; text-transform: uppercase; cursor: pointer;
  transition: background-color 0.25s var(--ease), color 0.25s var(--ease), border-color 0.25s var(--ease); }
.fit__switch button[aria-pressed="true"] { border-color: var(--ink); background: var(--ink); color: var(--ground); }
@media (max-width: 979px) {
  .gallery { margin-inline: calc(var(--gutter) * -1); }
  /* On a tablet the 4:5 picture at full width would fill the first screen on its own, so it is held to about
     two thirds of the window's height: the frame keeps its shape and its picture's focus, centred on the
     page's ground, and the drop's lockup and name come up under it (Jack, 5 November 2026), clear of the
     buy bar at 768 by 1024 (64svh; at 68svh the bar covered the name). A phone's picture is already
     shorter than that, so it stays the full width. */
  .gallery.is-ready .gallery__main { width: min(100%, 64svh * 4 / 5); margin-inline: auto; }
  .gallery.is-ready .gallery__thumbs { display: flex; gap: 8px; padding-inline: var(--gutter); overflow-x: auto; scrollbar-width: none; }
  .gallery.is-ready .gallery__thumbs::-webkit-scrollbar { display: none; }
  .gallery__thumb { flex: 0 0 60px; }
  .gallery.is-ready .gallery__step { display: none; }
  /* The 3D view's note on a phone: in the reading face, so it takes two lines rather than four. */
  .fit__caption { font-family: var(--body); font-size: var(--text-label); line-height: 1.4; letter-spacing: 0; text-transform: none; }
}

/* The buy box stays in view while the pictures scroll past it. */
.buybox { display: grid; gap: 22px; }
/* If the box is taller than the screen, it sticks by its bottom edge instead, so every line of it can
   still be read. assets/site.js measures it into --buybox-height. */
@media (min-width: 980px) {
  .buybox { position: sticky; top: min(calc(var(--header) + 24px), calc(100vh - var(--buybox-height, 0px) - 24px)); }
}
.buybox__lockup { color: var(--ink); }
.buybox__title { font-size: clamp(2.75rem, 5vw, 4.5rem); line-height: 1; }
.buybox__gap { max-width: 30ch; font-family: var(--display); font-size: clamp(1.25rem, 1.8vw, 1.5rem); font-weight: 300; line-height: 1.35; }
.buybox__lede { max-width: 46ch; color: var(--muted); }
.buybox__status { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 20px; padding-top: 6px; }
.status { display: inline-flex; align-items: center; gap: 10px; font-family: var(--mono); font-size: var(--text-label); letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink); }
/* The live dot and the time left beside it are the buy box's one counter-accent element: Halfway on the dark
   site. A drop that is not made keeps a plain, still dot. */
.status::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--counter); animation: pulse 2.4s var(--ease) infinite; }
.status--off::before { background: var(--muted); animation: none; }
.buybox__status [data-countdown] { color: var(--counter); }
@keyframes pulse { 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--counter) 35%, transparent); } 70%, 100% { box-shadow: 0 0 0 8px transparent; } }

/* The price: one total, split in two underneath. */
.price-block { display: grid; gap: 16px; padding-block: 22px; border-block: 1px solid var(--rule); }
.price-block__total { font-family: var(--display); font-size: clamp(2.75rem, 4.5vw, 3.5rem); font-weight: 300; line-height: 1; letter-spacing: -0.02em; }
.price-block__excludes { margin-block: -6px 4px; color: var(--ink); }   /* as on the first screen (.hero__excludes) */
.price-block__bar { height: 4px; overflow: hidden; border-radius: var(--pill); background: var(--ground-2); }
.price-block__bar span { display: block; height: 100%; border-radius: inherit; background: var(--ink); }
.price-block__legend { display: flex; justify-content: space-between; gap: 16px; margin-top: 10px; font-size: 0.95rem; }
.price-block__legend strong { font-weight: 600; }
.note { padding: 16px 18px; background: var(--ground-2); font-size: 0.98rem; }

/* Choosing your phone. */
.buy { display: grid; gap: 16px; }
.picker { display: grid; gap: 12px; padding: 0; border: 0; }
.picker legend { margin-bottom: 12px; padding: 0; }
.picker__options { display: flex; flex-wrap: wrap; gap: 8px; }
.picker__options input { position: absolute; opacity: 0; pointer-events: none; }
.picker__options label { display: inline-flex; align-items: center; min-height: 46px; padding: 0 20px; border: 1px solid var(--rule); border-radius: var(--pill); font-size: 0.95rem; cursor: pointer; transition: border-color 0.2s, background-color 0.2s, color 0.2s; }
.picker__options label:hover { border-color: var(--ink); }
.picker__options input:checked + label { border-color: var(--ink); background: var(--ink); color: var(--ground); }
.picker__options input:focus-visible + label { outline: 2px solid var(--focus); outline-offset: 3px; }   /* the ring takes --focus: the lifted green on Charcoal (6.41:1), Fern on Greige */
.picker__help summary { width: fit-content; list-style: none; font-size: 0.92rem; color: var(--muted); text-decoration: underline; text-underline-offset: 0.2em; cursor: pointer; }
.picker__help summary::-webkit-details-marker { display: none; }
.picker__help p { margin-top: 8px; font-size: 0.92rem; color: var(--muted); }
.buy__message { min-height: 1.3em; font-size: 0.92rem; color: var(--muted); }
.buy__message:empty { display: none; }

/* Short guarantees under the button, each with a tick. */
.trust { display: grid; gap: 10px; }
.trust li { display: flex; align-items: center; gap: 12px; font-size: 0.95rem; }
.trust li::before {
  content: ""; flex: none; width: 18px; height: 18px; background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M4 10.5l4 4 8-9' fill='none' stroke='black' stroke-width='1.6'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M4 10.5l4 4 8-9' fill='none' stroke='black' stroke-width='1.6'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* What happens to your money, said plainly beside the button (website addendum §3). */
.before { display: grid; gap: 12px; padding: 20px 22px; border: 1px solid var(--rule); }
.before ul { display: grid; gap: 10px; }
.before li { position: relative; padding-left: 18px; font-size: 0.93rem; color: var(--muted); }
.before li::before { content: ""; position: absolute; left: 0; top: 0.75em; width: 7px; height: 1px; background: var(--muted); }
.before__more { font-size: 0.93rem; color: var(--muted); }
.before__more a, .before li a { color: var(--ink); }

/* Why we made it (templates/drop.mjs proof): two or three cited comments under the money box, set as About
   us's replies are (section 11), a little smaller to suit the buy box. */
.proof { display: grid; gap: 12px; padding-top: 4px; }
.proof__list { display: grid; gap: 10px; }
.proof .reply { gap: 8px; padding: 16px 18px; }
.proof .reply__quote { font-size: 1rem; }
/* Not made (12 November 2026): the verdict, then the list for anyone who still wants the piece. */
.verdict { display: grid; gap: 14px; padding-top: 22px; border-top: 1px solid var(--rule); }
.verdict__title { font-family: var(--display); font-size: clamp(1.6rem, 2.4vw, 2.1rem); font-weight: 300; line-height: 1.15; letter-spacing: -0.01em; }
.verdict__body { max-width: 46ch; color: var(--muted); }
.verdict__facts { display: grid; gap: 8px; }
.verdict__facts li { position: relative; padding-left: 18px; font-size: 0.93rem; color: var(--muted); }
.verdict__facts li::before { content: ""; position: absolute; left: 0; top: 0.75em; width: 7px; height: 1px; background: var(--muted); }
.verdict__facts a { color: var(--ink); }
.again { display: grid; gap: 12px; padding: 20px 22px; border: 1px solid var(--rule); }
.again__body { max-width: 46ch; color: var(--muted); }



/* Fold-out sections: delivery, fit, refunds, and the questions further down. */
.accordion { border-top: 1px solid var(--rule); }
.accordion details { border-bottom: 1px solid var(--rule); }
.accordion summary { position: relative; padding: 18px 40px 18px 0; list-style: none; font-family: var(--mono); font-size: var(--text-label); letter-spacing: 0.14em; text-transform: uppercase; cursor: pointer; }
.accordion summary::-webkit-details-marker { display: none; }
.accordion summary::after {
  content: ""; position: absolute; right: 4px; top: 50%; width: 12px; height: 12px; margin-top: -6px;
  background: linear-gradient(currentColor, currentColor) center / 12px 1px no-repeat, linear-gradient(currentColor, currentColor) center / 1px 12px no-repeat;
}
.accordion details[open] > summary::after { background: linear-gradient(currentColor, currentColor) center / 12px 1px no-repeat; }
.accordion details > p { max-width: 62ch; padding-bottom: 20px; font-size: 0.95rem; color: var(--muted); }
.accordion details > p a { color: var(--ink); }
.accordion--large summary { padding-block: 24px; font-family: var(--display); font-size: clamp(1.15rem, 1.6vw, 1.375rem); font-weight: 400; letter-spacing: 0; text-transform: none; }
.accordion--large details > p { font-size: 1.02rem; }

/* Features: one claim per row, picture and words alternating sides. */
.features { display: grid; gap: clamp(64px, 9vw, 140px); }
.feature { display: grid; grid-template-columns: 1fr; align-items: center; gap: clamp(28px, 4vw, 72px); }
.feature__media { aspect-ratio: 4 / 5; overflow: hidden; background: var(--ground-2); }
.feature__media img { width: 100%; height: 100%; object-fit: cover; object-position: var(--focus, 50% 50%); transform-origin: var(--focus, 50% 50%); }   /* motion.js depth zooms towards the subject */
.feature__text { display: grid; gap: 18px; max-width: 460px; }
.feature__title { font-size: clamp(2rem, 3.4vw, 3rem); }
.feature__body { font-size: var(--text-lede); line-height: 1.6; color: var(--muted); }
@media (min-width: 860px) {
  .feature { grid-template-columns: 1fr 1fr; }
  .feature:nth-child(even) .feature__media { order: 2; }
  /* The words sit against their own picture whichever side it is on, so a row reads as one thing. */
  .feature:nth-child(even):not(.feature--wide) .feature__text { justify-self: end; }
}
/* On a phone each row stacks, picture then words: the words sit close under their own picture and well
   clear of the next one, so they never read as introducing it. */
@media (max-width: 859px) {
  .features { gap: 104px; }
  .feature { gap: 20px; }
}
/* A wide row (site.json feature "wide": true): for a landscape photograph or render. The picture runs the
   width of the page at 16:9 (or the feature's own "ratio"), never taller than about three quarters of the
   screen, so its words still show under it; the picture's focus steers any crop. */
.feature--wide { grid-template-columns: 1fr; align-items: start; }
.feature--wide .feature__media { width: 100%; aspect-ratio: var(--ratio, 16 / 9); max-height: 76svh; order: 0; }
.feature--wide .feature__text { max-width: none; }
@media (min-width: 860px) {
  .feature.feature--wide { grid-template-columns: 1fr; }
  .feature.feature--wide .feature__media { order: 0; }
  .feature--wide .feature__text { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); column-gap: clamp(28px, 4vw, 72px); align-items: start; }
  .feature--wide .feature__text > .label { grid-column: 1 / -1; }
  .feature--wide .feature__body { max-width: 52ch; }
}

/* The film. The section pins and the drop's film plays frame by frame as you scroll (motion.js), while
   the captions take turns. Without motion it is a still of the film's last frame with the captions listed. */
.film__pin { position: relative; min-height: 100svh; overflow: hidden; background: var(--ground-2); }
.film__stage { position: absolute; inset: 0; }
.film__poster, .film__canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.film__poster { object-fit: cover; }
.film__canvas { opacity: 0; transition: opacity 0.4s var(--ease); }
.film__canvas.is-ready { opacity: 1; }
.film__shade { position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(90deg, color-mix(in srgb, var(--tone) 92%, transparent) 0%, color-mix(in srgb, var(--tone) 62%, transparent) 30%, transparent 56%); }
.film__text { position: relative; z-index: 1; display: flex; flex-direction: column; justify-content: center; gap: 28px; min-height: 100svh; padding-block: 96px; }
.film__label { color: var(--ink); }
.film__steps { display: grid; gap: 32px; max-width: 400px; }
.film__step { display: grid; gap: 10px; }
.film__title { font-size: clamp(2rem, 3.4vw, 3rem); }
.film__body { font-size: var(--text-lede); line-height: 1.55; color: var(--muted); }
.film__progress { display: none; width: 160px; height: 2px; overflow: hidden; background: var(--rule); }
.film__progress span { display: block; height: 100%; background: var(--ink); transform: scaleX(0); transform-origin: left; }
.motion .film__pin { height: 100svh; min-height: 0; }
.motion .film__poster { opacity: 0; }
.motion .film__text { height: 100%; min-height: 0; padding-block: 0; }
.motion .film__steps { grid-template-areas: "step"; }
.motion .film__step { grid-area: step; opacity: 0; transform: translateY(16px); transition: opacity 0.5s var(--ease), transform 0.5s var(--ease); }
.motion .film__step.is-active { opacity: 1; transform: none; }
.motion .film__progress { display: block; }
@media (max-width: 860px) {
  .film__shade { background: linear-gradient(0deg, color-mix(in srgb, var(--tone) 95%, transparent) 0%, color-mix(in srgb, var(--tone) 75%, transparent) 40%, transparent 66%); }
  .film__text { justify-content: flex-end; }
  .motion .film__text { padding-bottom: 56px; }
  .film__title { font-size: 1.9rem; }
}

/* Specification and questions: heading on the left, content on the right. */
.spec, .faq { display: grid; gap: 40px; }
@media (min-width: 980px) {
  .spec, .faq { grid-template-columns: 5fr 7fr; }
  .spec .section__head, .faq .section__head { margin: 0; align-self: start; }
}
/* The specification as a technical drawing: a frame ruled in hairlines, crop marks off its corners and a
   scale along its top. Each row's rule is a background line, under the label (--draw-a) then under the
   value (--draw-b), so motion.js can draw it from left to right. Without motion they are simply 1: drawn. */
.spec__sheet { position: relative; padding: clamp(18px, 2.4vw, 36px); }
.spec__edge { position: absolute; background: color-mix(in srgb, var(--ink) 34%, transparent); }
.spec__edge--top { top: 0; left: 0; right: 0; height: 1px; transform-origin: 0 50%; }
.spec__edge--right { top: 0; right: 0; bottom: 0; width: 1px; transform-origin: 50% 0; }
.spec__edge--bottom { left: 0; right: 0; bottom: 0; height: 1px; transform-origin: 100% 50%; }
.spec__edge--left { top: 0; bottom: 0; left: 0; width: 1px; transform-origin: 50% 100%; }
.spec__crop { position: absolute; width: 14px; height: 14px; border: 0 solid var(--muted); }
.spec__crop--tl { top: -9px; left: -9px; border-width: 1px 0 0 1px; }
.spec__crop--tr { top: -9px; right: -9px; border-width: 1px 1px 0 0; }
.spec__crop--bl { bottom: -9px; left: -9px; border-width: 0 0 1px 1px; }
.spec__crop--br { bottom: -9px; right: -9px; border-width: 0 1px 1px 0; }
.spec__ruler { display: block; height: 10px; margin-bottom: 20px;
  background:
    repeating-linear-gradient(90deg, var(--muted) 0 1px, transparent 1px 120px) left bottom / 100% 10px no-repeat,
    repeating-linear-gradient(90deg, color-mix(in srgb, var(--ink) 28%, transparent) 0 1px, transparent 1px 24px) left bottom / 100% 5px no-repeat; }
.spec__table { width: 100%; border-collapse: collapse; }
.spec__table th, .spec__table td { --rule-line: linear-gradient(var(--rule), var(--rule)); background: var(--rule-line) left top / calc(var(--line, 1) * 100%) 1px no-repeat; }
.spec__table tr:last-child th, .spec__table tr:last-child td { background: var(--rule-line) left top / calc(var(--line, 1) * 100%) 1px no-repeat, var(--rule-line) left bottom / calc(var(--line, 1) * 100%) 1px no-repeat; }
.spec__table th { --line: var(--draw-a, 1); width: 34%; padding: 18px 24px 18px 0; text-align: left; vertical-align: top; font-weight: 400; }
.spec__table td { --line: var(--draw-b, 1); padding: 16px 0; font-size: 1.02rem; }

/* On phones: a slim bar with the price and the button, once the main button has scrolled away. */
.sticky-buy {
  position: fixed; left: 12px; right: 12px; bottom: 12px; z-index: 40;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 8px 8px 8px 22px; border: 1px solid var(--rule); border-radius: var(--pill);
  background: var(--ground); box-shadow: 0 12px 32px rgba(0, 0, 0, 0.12);
  transform: translateY(150%); transition: transform 0.4s var(--ease);
}
.sticky-buy.is-visible { transform: none; }
.sticky-buy__text { display: grid; min-width: 0; line-height: 1.3; }
.sticky-buy__name { font-family: var(--display); font-size: 1.05rem; }
.sticky-buy .button { flex: none; }
/* On a phone the button's words (Reserve: pay, today's amount, now) take most of the bar, so the bar keeps
   the full price beside them and leaves the name out: the page above has just said it. */
@media (max-width: 520px) {
  .sticky-buy { gap: 12px; padding-left: 20px; }
  .sticky-buy__name { display: none; }
  .sticky-buy__text .label { font: 400 1.25rem/1 var(--display); letter-spacing: -0.01em; text-transform: none; color: var(--ink); }
  .sticky-buy .button { padding: 0 18px; }
}
@media (max-width: 360px) {
  .sticky-buy { gap: 10px; padding-left: 16px; }
  .sticky-buy__text .label { font-size: 1.1rem; }
  .sticky-buy .button { padding: 0 14px; font-size: var(--text-label); letter-spacing: 0.08em; }   /* the label size again, so the price beside it fits */
}
@media (min-width: 980px) { .sticky-buy { display: none; } }


/* 15. Small print ---------------------------------------------------------------------------------- */
.legal { padding-block: clamp(48px, 8vw, 112px) var(--section); }
.legal__inner { max-width: 860px; }
.legal__title { margin: 16px 0 32px; font-size: var(--text-h2); }
.legal h2 { margin: 48px 0 14px; font-size: 1.5rem; font-weight: 400; scroll-margin-top: calc(var(--header) + 32px); }
.legal ul { display: grid; gap: 10px; padding-left: 20px; list-style: disc; }
.legal p, .legal li { max-width: 68ch; }
.legal p + p, .legal p + ul, .legal ul + p { margin-top: 14px; }
.draft-banner { padding: 18px 22px; border: 1px solid var(--ink); background: var(--ground-2); }


/* 16. Preview only --------------------------------------------------------------------------------- */
/* None of this appears on the real site: the build only adds the bar to preview pages, and the production
   build refuses to publish while anything is still marked "not decided yet". */
.preview-bar { position: relative; z-index: 60; display: flex; flex-wrap: wrap; align-items: center; gap: 6px 16px; padding: 10px var(--gutter); background: #111; color: #EDEDED; font: 400 12px/1.4 var(--mono); letter-spacing: 0.06em; }
.preview-bar a { display: inline-flex; align-items: center; padding: 4px 8px; border: 1px solid #444; border-radius: 2px; color: #EDEDED; text-decoration: none; white-space: nowrap; }
.preview-bar a[aria-current] { background: #EDEDED; color: #111; }
.preview-bar strong { margin-right: 6px; color: #FFF; font-weight: 500; }
.preview-bar__note { color: #BDBDBD; }
/* The checklist's ticks (templates/review.mjs): a checked page's button, and marking the page in view. */
.preview-bar__tick { width: 11px; height: 11px; margin-left: 6px; vertical-align: -1px; }
.preview-bar__mark { display: inline-flex; align-items: center; gap: 6px; padding: 4px 8px; border: 1px solid #444; border-radius: 2px; background: none; color: #EDEDED; font: inherit; letter-spacing: inherit; cursor: pointer; }
.preview-bar__mark .preview-bar__tick { margin: 0; }
.preview-bar__mark[aria-pressed="true"] { border-color: #9DB5A3; background: #9DB5A3; color: #111; }
.todo { padding: 0 5px; border-radius: 3px; background: #FFF3B0; color: #2A2400; -webkit-box-decoration-break: clone; box-decoration-break: clone; }
/* The blank drop (templates/blank.mjs): each picture's place as a frame of its shape, saying what goes in it. */
.slot { display: grid; align-content: center; gap: 10px; width: 100%; height: 100%; padding: 9%; border: 1px dashed color-mix(in srgb, var(--ink) 34%, transparent);
  background: repeating-linear-gradient(135deg, transparent 0 16px, color-mix(in srgb, var(--ink) 5%, transparent) 16px 17px), var(--ground-2); color: var(--ink); text-align: left; }
.slot__title { font: 400 var(--text-label)/1.3 var(--mono); letter-spacing: 0.14em; text-transform: uppercase; }
.slot__line { max-width: 42ch; font-size: 0.95rem; line-height: 1.45; color: var(--muted); }
.gallery__slide > .slot { aspect-ratio: 4 / 5; }
.gallery.is-ready .gallery__slide > .slot { aspect-ratio: auto; }
.gallery__thumb .slot { padding: 6px; place-content: center; }
.gallery__thumb .slot__title { font-size: 0.55rem; letter-spacing: 0.08em; text-align: center; }
.gallery__thumb .slot__line { display: none; }
.card__media .slot__line:nth-child(n+3) { display: none; }
.brief__grid { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 3fr); gap: clamp(20px, 3vw, 40px); align-items: start; }
.brief__item { position: relative; margin: 0; }
.brief__item--wide { aspect-ratio: 16 / 9; }
.brief__item--card { aspect-ratio: 4 / 5; }
.brief__item--wide .slot { padding-left: 52%; }
.brief__words { position: absolute; left: 6%; top: 50%; translate: 0 -50%; width: 38%; padding: 18px; border: 1px dashed color-mix(in srgb, var(--ink) 34%, transparent); font: 400 var(--text-label)/1.3 var(--mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); }
.brief__note { margin-top: 28px; max-width: 70ch; color: var(--muted); }
.brief__subhead { margin-top: clamp(48px, 7vw, 96px); }
.brief__grid--about { grid-template-columns: 1fr 1fr; }
.brief__grid--about .brief__item--wide .slot { padding-left: 9%; }
@media (max-width: 860px) { .brief__grid, .brief__grid--about { grid-template-columns: 1fr; } .brief__item--wide { aspect-ratio: auto; min-height: 420px; } .brief__item--wide .slot { padding: 9% 9% 9% 9%; padding-top: 140px; } .brief__words { top: 24px; translate: none; width: auto; right: 6%; } }


/* 17. Motion --------------------------------------------------------------------------------------- */
/* Sections rise gently into place as they scroll into view. Only when JavaScript is running (so nothing
   is ever stuck invisible), and never for people who have asked their device for less motion. */
/* With motion on, the intro's pieces wait out of sight until motion.js plays them in. */
.motion [data-intro] { visibility: hidden; }
.motion .hero__title .line__inner { transform: translateY(110%); }
/* The first screen's photograph starts close in and tipped, where the camera's pull-back begins
   (motion.js intro), so the first frame the page draws is already that and not the level photograph
   jumping in a moment later. These are exactly the pictures intro() moves: the one photograph, or the
   slideshow's first drop. They and the photograph's frame (pushed in as you scroll) stay layers of their
   own from the start, so the browser never rebuilds them part-way, as the pull-back ends or on the first
   scroll. Both showed as a stutter on the one-open page (Jack, 22 Oct 2026). */
.motion [data-hero-photo]:not(.hero__media-stack) picture,
.motion .hero__media-stack .hero__media[data-slide="0"] picture { transform: scale(1.28) rotate(1.2deg); will-change: transform; }
.motion [data-hero-photo] { will-change: transform; }
/* A photograph with an opening point (images.hero.open, written on its holder by parts.mjs openData) starts
   close on that point instead, on a computer: the camera starts on the Joker light's light and pulls back
   to the whole wall (Jack, 28 Oct 2026). The stylesheet finds the point in the frame the way object-fit:
   cover places the photograph (its drawn size, then its focus) and magnifies the photograph around it,
   so the point stays where it rests, on the product's side of the screen, and the photograph never passes
   behind the words. It only comes down far enough to clear the header (a fifth of the way down), and never
   so far that an edge of the photograph shows, tilt included (the 2.2: the tilt's reach, in the frame's
   own units). motion.js pulls back from whatever this shows, so the first frame and the move always
   agree, whatever the screen's shape. */
@media (min-width: 861px) {
  .hero__photo { container-type: size; }
  .motion [data-hero-photo][data-open]:not(.hero__media-stack) picture,
  .motion .hero__media-stack .hero__media[data-slide="0"][data-open] picture {
    --open-s: 1.9;
    --open-dw: max(100cqw, 100cqh * var(--open-ar));
    --open-dh: max(100cqh, 100cqw / var(--open-ar));
    --open-l: calc((100cqw - var(--open-dw)) * var(--open-fx));
    --open-t: calc((100cqh - var(--open-dh)) * var(--open-fy));
    --open-ox: calc(var(--open-l) + var(--open-x) * var(--open-dw));
    --open-oy: calc(var(--open-t) + var(--open-y) * var(--open-dh));
    --open-cx: clamp(100cqw - var(--open-s) * (var(--open-l) + var(--open-dw) - var(--open-ox)) + 2.2cqh, var(--open-ox), var(--open-s) * (var(--open-ox) - var(--open-l)) - 2.2cqh);
    --open-cy: clamp(100cqh - var(--open-s) * (var(--open-t) + var(--open-dh) - var(--open-oy)) + 2.2cqw, max(var(--open-oy), 20cqh), var(--open-s) * (var(--open-oy) - var(--open-t)) - 2.2cqw);
    transform-origin: var(--open-ox) var(--open-oy);
    transform: translate(calc(var(--open-cx) - var(--open-ox)), calc(var(--open-cy) - var(--open-oy))) rotate(1.2deg) scale(var(--open-s));
  }
}
/* The moving cloth (assets/cloth.js): its canvas over the photograph, the picture under it made transparent
   (never removed, so its description is still read), and the slow push the still photograph has
   (hero-breathe), carried over to the canvas. */
.cloth-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; pointer-events: none; }
.is-cloth picture { opacity: 0; }
.hero--drop:not(.hero--slider) .is-cloth .cloth-canvas { animation: hero-breathe 16s ease-in-out infinite alternate; }
/* Lenis, the smooth scrolling, needs these few rules (folded in from its own stylesheet). */
html.lenis, html.lenis body { height: auto; }
html.lenis { scroll-behavior: auto; }
.lenis:not(.lenis-autoToggle).lenis-stopped { overflow: clip; }
.lenis [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-smooth iframe { pointer-events: none; }
.js [data-reveal] { opacity: 0; transform: translateY(24px); transition: opacity 0.9s var(--ease), transform 0.9s var(--ease); }
.js [data-reveal].is-visible { opacity: 1; transform: none; }
/* With motion, motion.js moves the parts of each data-reveal block itself, so the block stays put. */
.motion [data-reveal] { opacity: 1; transform: none; transition: none; }
/* Headings whose words rise out of their own masks (motion.js rise). Each word is a small box of its
   own so it can move. */
.rise { display: inline-block; overflow: hidden; vertical-align: top; padding-bottom: 0.14em; margin-bottom: -0.14em; }
.rise__inner { display: inline-block; }
/* A button that leans towards the pointer catches it a little beyond its edge, so it does not slip away. */
.is-magnetic { position: relative; }
.is-magnetic::before { content: ""; position: absolute; inset: -12px; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js [data-reveal] { opacity: 1; transform: none; transition: none; }
  .hero__scroll svg, .status::before { animation: none; }
  .hero__media-stack .hero__media, .hero__stack > * { transition: none; }
  .hero__media-stack .hero__media, .hero__media-stack .hero__media picture, .hero__media-stack .hero__media::after, .hero__photo img, .hero--slider .hero__slide-copy > * { animation: none !important; transition: none !important; }
  .theme-toggle svg { transition: none; }
}


/* 18. Themes --------------------------------------------------------------------------------------- */
/* Every page carries a light and a dark palette (lib/palette.mjs). data-theme on <html> picks one: the
   site's default, written by the build (content/site.mjs theme), or the visitor's own choice with the
   header switch (assets/site.js theme). With no data-theme at all, the device's setting decides.

   A photograph with a copy for each theme (templates/parts.mjs themed) is in the page twice, and only the
   copy for the current theme is shown. These rules only ever hide, so each copy keeps its own layout. */
:root[data-theme="light"] .themed--dark,
:root[data-theme="dark"] .themed--light { display: none; }
@media (prefers-color-scheme: light) { :root:not([data-theme]) .themed--dark { display: none; } }
@media (prefers-color-scheme: dark) { :root:not([data-theme]) .themed--light { display: none; } }

/* Switching theme: the new colours open out in a circle from the switch (assets/site.js runs it with the
   View Transitions API). While it runs, the browser's own crossfade is off so only the circle moves. */
:root.theme-switching::view-transition-old(root), :root.theme-switching::view-transition-new(root) { animation: none; mix-blend-mode: normal; }


/* 19. Page morphs ---------------------------------------------------------------------------------- */
/* Clicking through to a drop, its photograph glides from where it was on the main page into the first
   picture on the drop page, while the rest of the page crossfades (cross-document View Transitions).
   assets/site.js names the photo you clicked from "drop-photo"; the drop page names its first picture
   the same, and the browser animates between them. Browsers without View Transitions, and anyone who
   asks for less motion, simply load the page. The <head> script skips the glide when arriving by Back,
   Forward, a reload or a link to a section, because those pages settle their scroll position after
   loading and the glide would aim at where things were before that. */
@view-transition { navigation: auto; }
@media (prefers-reduced-motion: reduce) { @view-transition { navigation: none; } }
.gallery__slide:first-child img { view-transition-name: drop-photo; }
::view-transition-group(drop-photo) { animation-duration: 0.75s; animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1); }
::view-transition-old(root), ::view-transition-new(root) { animation-duration: 0.4s; }


/* 20. Tones ---------------------------------------------------------------------------------------- */
/* One background for the whole page (assets/site.js tones). Each section names the colour it sits on with
   data-tone (ground, panel or deep). With the script running, the sections give up their own backgrounds
   and the page behind them blends from one section's colour to the next as you scroll, so the page reads
   as one journey rather than a stack of blocks. --tone is the colour of the moment: the header and the
   shades over photographs use it, so they blend with the page. The full-width photographs fade into it at
   their top and bottom edges. The sign-up band keeps its own colour: it opens out from a card (motion.js).
   Without the script, every section keeps its own background. */
:root { --tone: var(--ground); }
.tones body { background: var(--tone); }
.tones [data-tone]:not(.signup), .tones .film__pin { background-color: transparent; }
/* A plain section that tones() lifts to the panel colour turns its cards (and anything else drawn on the
   panel colour) to the ground colour, so they still stand out: Charcoal cards on Charcoal Panel. */
.tones [data-shade="panel"] { --ground-2: var(--ground); }
.tones .next { -webkit-mask-image: linear-gradient(to bottom, transparent, #000 16%, #000 84%, transparent); mask-image: linear-gradient(to bottom, transparent, #000 16%, #000 84%, transparent); }
/* The first screen's photograph is masked on phones only. On a larger screen, a mask on that photograph
   (which zooms as you scroll, and drifts slowly inside) made Chrome stall for about a quarter of a second
   on the first scroll, so the photograph jumped (Jack, 21 Oct 2026). There the shade at the foot of the
   first screen does the fading instead (section 8). */
@media (max-width: 860px) {
  .tones .hero__photo { -webkit-mask-image: linear-gradient(to bottom, #000 78%, transparent); mask-image: linear-gradient(to bottom, #000 78%, transparent); }
}
.tones .film__stage { -webkit-mask-image: linear-gradient(to bottom, transparent, #000 12%, #000 88%, transparent); mask-image: linear-gradient(to bottom, transparent, #000 12%, #000 88%, transparent); }


/* 21. Before the first drop: the new None open on trial (12 November 2026; templates/teaser.mjs) */
/* A drawing written into the page (the standard): a release in the page's ink and the piece it is missing in
   green, traced by Blender from one model. With motion the lines draw themselves in, the release first. */
.drawing { display: block; width: 100%; height: auto; overflow: visible; }
.drawing path { fill: none; stroke-linecap: round; stroke-linejoin: round; }
.drawing .release path { stroke: var(--ink); }   /* line widths: on each drawing's groups, set by its crop */
.drawing .piece path { stroke: var(--fern); }
.motion .standard .drawing path { stroke-dasharray: 1; stroke-dashoffset: 1; }
.motion .standard .drawing { will-change: transform; }   /* its own layer: drawing in repaints it alone */

/* b: the wall. Three rows of tiles drift across the first screen, the middle row the other way (motion.js), each
   tile a release drawn as an image with its piece named under it. The tiles are sized so all three rows sit whole
   between the header and the foot of the screen, tilted a little; eight a row and none twice, so the open side of
   the screen never shows one twice. */
.wall { --gap: clamp(12px, 1.3vw, 24px); --card: clamp(150px, min(17vw, calc(((100svh - var(--header) - 40px) / 1.06 - 108px - 4.7vw) / 3)), 460px); }
.wall__rows { position: absolute; inset: var(--header) 0 0; z-index: -1; display: grid; align-content: center; gap: var(--gap); transform: rotate(-2.5deg) scale(1.06); }
.motion .wall__rows { will-change: opacity; }
.wall__row { overflow: hidden; }
.wall__track { display: flex; gap: var(--gap); width: max-content; }
.motion .wall__track { will-change: transform; }
.wall__card { flex: none; display: grid; gap: 8px; width: var(--card); margin: 0; padding: 14px 14px 12px; border: 1px solid var(--rule); border-radius: 16px; background: color-mix(in srgb, var(--ground-2) 80%, transparent); }
.wall__img { display: block; width: 100%; height: auto; }
.wall__caption { color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Nothing on the wall paints the page's colour over the light behind it (the photo opening's shades are off here),
   so the light runs on unbroken into the sections after it: the tiles fade out under the words with a mask. */
.wall::before, .wall::after { content: none; display: none; }
.wall__rows { -webkit-mask-image: linear-gradient(90deg, rgba(0, 0, 0, 0.06) 0%, rgba(0, 0, 0, 0.16) 26%, rgba(0, 0, 0, 0.7) 54%, #000 74%); mask-image: linear-gradient(90deg, rgba(0, 0, 0, 0.06) 0%, rgba(0, 0, 0, 0.16) 26%, rgba(0, 0, 0, 0.7) 54%, #000 74%); }
.motion .hero.wall + .idea { margin-top: 0; }   /* the wall ends whole: nothing slides up over its last row */
.page-teaser { --section: clamp(80px, 9.5vw, 136px); }   /* a little tighter between sections (Jack, 12 November 2026) */
/* Phones: tighter again (Jack, 13 November 2026), and the idea's stage only as tall as its mark and words, pinned by
   its middle (motion.js, data-pin="phone"), so no empty half-screen comes before or after it. */
@media (max-width: 860px) {
  .page-teaser { --section: 56px; }
  .motion .idea[data-pin="phone"] .idea__pin { min-height: 0; padding-block: 32px; }
  .idea[data-pin="phone"] .idea__grid { gap: 28px; }
  .idea[data-pin="phone"] .idea__mark { width: min(50vw, 220px); }
}
.page-teaser .finale { min-height: clamp(520px, 84vh, 900px); }
/* The C-mark settles over the wall once the headline lands (motion.js); without motion it is there from the start,
   over a quieter wall. A soft pool of the page's colour behind it keeps it clear of the drawings. */
.wall__mark { position: absolute; inset: var(--header) 0 0 46%; z-index: 0; display: grid; place-items: center; pointer-events: none; }
.wall__mark::before { content: ""; position: absolute; width: min(80svh, 46vw); aspect-ratio: 1; border-radius: 50%; background: radial-gradient(closest-side, color-mix(in srgb, var(--tone) 58%, transparent), color-mix(in srgb, var(--tone) 22%, transparent) 62%, transparent); }
.wall__logo { position: relative; width: min(46svh, 27vw); height: auto; color: var(--ink); overflow: visible; }
.wall__rows { opacity: 0.55; }
.motion .wall__rows { opacity: 1; }
.motion .wall__mark, .motion .wall__row { opacity: 0; }   /* motion.js brings them in */
.wall__content { gap: 26px; max-width: min(760px, 54%); }
.wall__title { line-height: 1.14; }
.wall__title .line { white-space: nowrap; }   /* the tagline's two lines, never three */
.wall__line { padding-bottom: 0.32em; margin-bottom: -0.32em; }   /* room in the mask for the underline, no more */
/* The piece's word, the one that flips: underlined in green, one unbroken fine line just under the descenders
   (so it never runs through a p or leaves a stub beside one), ending above the tallest letters of the line below
   (Jack, 12 November 2026: the "d" of "drop" ran into it). Descenders end 0.23em under the baseline, the line
   0.305em, and the line below begins 0.36em down. */
.wall__piece { display: inline-block; text-decoration-line: underline; text-decoration-color: var(--fern); text-decoration-thickness: 0.045em; text-underline-offset: 0.26em; text-decoration-skip-ink: none; }
.wall__sub { max-width: 36ch; font-family: var(--display); font-size: var(--text-h3); font-weight: 300; line-height: 1.3; color: var(--muted); }
.wall__join { display: grid; gap: 14px; }
.wall__cue { color: var(--ink); font-size: var(--text-lede); }
.wall .signup__form--compact { width: min(100%, 460px); }
/* On a phone the rows take a band at the top of the screen, small and without their captions, and the words sit
   under them. The band is sized to the screen and the tiles to the band, so the whole first screen, the form
   included, fits a phone's window; no scroll cue, the form is the last thing on it (Jack, 13 November 2026). */
@media (max-width: 860px) {
  .wall { --band: clamp(212px, 33svh, 380px); --gap: 10px; --card: calc((var(--band) - 2 * var(--gap) - 18px) / 3.24); }
  .wall__rows { bottom: auto; height: var(--band); transform: rotate(-2.5deg) scale(1.08); }
  .wall__card { padding: 9px; border-radius: 12px; }
  .wall__caption { display: none; }
  .wall__rows { -webkit-mask-image: linear-gradient(to bottom, #000 80%, transparent); mask-image: linear-gradient(to bottom, #000 80%, transparent); }
  .wall__mark { inset: var(--header) 0 auto; height: var(--band); }
  .wall__mark::before { width: min(calc(var(--band) * 1.2), 84vw); }
  .wall__logo { width: min(calc(var(--band) * 0.54), 42vw); }
  .wall .hero__inner { align-items: flex-start; padding-block: calc(var(--band) + 16px) 28px; }
  .wall__content { max-width: none; gap: 16px; }
  .wall__sub { font-size: 1.125rem; }
  .wall__join { gap: 12px; }
  .wall .hero__foot { display: none; }
  /* The line above each form says what it is for, so on a phone its "Email" label is for screen readers only. */
  .page-teaser .signup__form--compact > .label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
}
/* A tablet held upright has room to spare under the form: the words sit in the middle of it, not at its top. */
@media (min-width: 600px) and (max-width: 860px) { .wall__content { margin-block: auto; } }
/* A short phone window (a small Android, a phone held sideways): two rows, not three, so the form still fits. */
@media (max-width: 860px) and (max-height: 700px) {
  .wall { --band: clamp(150px, 25svh, 300px); --card: calc((var(--band) - var(--gap) - 18px) / 2.16); }
  .wall__row:nth-child(3) { display: none; }
  .wall__content { gap: 12px; }
}

/* The standard: one large drawing in a panel beside what every piece has to be, three points under hairlines. */
.standard__grid { display: grid; grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); align-items: center; gap: clamp(32px, 6vw, 112px); }
.standard__art { padding: clamp(18px, 3vw, 48px); border: 1px solid var(--rule); border-radius: 20px; background: color-mix(in srgb, var(--ground-2) 80%, transparent); }
.standard__text { display: grid; gap: clamp(28px, 3.4vw, 48px); }
.standard__title { max-width: 15ch; }
.standard__list { display: grid; gap: clamp(18px, 2.2vw, 30px); margin: 0; padding: 0; list-style: none; }
.standard__point { display: grid; gap: 6px; padding-top: clamp(14px, 1.8vw, 22px); border-top: 1px solid var(--rule); }
.standard__name { font-size: var(--text-h3); font-weight: 400; }
.standard__body { max-width: 46ch; color: var(--muted); }
/* On a phone the panel takes the drawing's own shape: the drawing is wide, so its square's empty top and bottom are
   left out (negative margins, a share of the width), and the words follow closer. */
@media (max-width: 860px) {
  .standard__grid { grid-template-columns: 1fr; gap: 28px; }
  .standard__art { width: min(100%, 440px); padding: 12px 18px; }
  .standard__art .drawing { margin-block: -14%; }
  .standard__text { gap: 24px; }
  .standard__list { gap: 16px; }
}

/* The sealed drop and the list in one band, as the page's last section: the heading is the drop's own. */
.finale .next__title { font-size: clamp(3rem, 7vw, 6.5rem); }

/* The colour that travels down the page (Jack, 12 November 2026: "much more discreet than
   scapegoatpresents.com"). One fixed layer behind every section: three soft lights in the house colours,
   Halfway, the lifted green and a warm bone, faint enough only to warm or cool the charcoal. Each drifts slowly
   on its own; motion.js glow() moves them to each section's arrangement. Without motion they rest here. */
.glow { position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; contain: strict; }
.glow__light { position: absolute; left: 0; top: 0; width: 92vmax; height: 92vmax; margin: -46vmax 0 0 -46vmax; will-change: transform, opacity; }
.glow__light::before { content: ""; position: absolute; inset: 0; border-radius: 50%; animation: glow-drift var(--drift, 64s) ease-in-out infinite alternate; }
/* Discreet, but enough to see it change as you scroll (Jack, 12 November 2026: first "barely any movement on the
   light, but have the colour change ever so slightly", then "too inactive"): each light drifts slowly on its own,
   and between sections the colours trade strength and the lights move a little. */
.glow__light--gold::before { --drift: 64s; background: radial-gradient(closest-side, rgba(237, 178, 55, 0.2), rgba(237, 178, 55, 0.065) 55%, rgba(237, 178, 55, 0)); }
.glow__light--green::before { --drift: 78s; animation-delay: -24s; background: radial-gradient(closest-side, rgba(120, 176, 141, 0.19), rgba(120, 176, 141, 0.06) 55%, rgba(120, 176, 141, 0)); }
.glow__light--bone::before { --drift: 90s; animation-delay: -46s; background: radial-gradient(closest-side, rgba(233, 227, 218, 0.1), rgba(233, 227, 218, 0.032) 55%, rgba(233, 227, 218, 0)); }
.glow__light--gold { transform: translate3d(88vw, 10vh, 0); }
.glow__light--green { transform: translate3d(8vw, 96vh, 0); }
.glow__light--bone { transform: translate3d(58vw, 62vh, 0) scale(0.8); }
@keyframes glow-drift { from { transform: translate3d(-2.5%, -2%, 0) scale(1); } to { transform: translate3d(2.5%, 3%, 0) scale(1.06); } }
@media (prefers-reduced-motion: reduce) { .glow__light::before { animation: none; } }

/* The page for an address that does not exist: one line and the way home, in the middle of the screen. */
.notfound { display: flex; align-items: center; min-height: calc(78svh - var(--header)); }
.notfound__inner { display: grid; justify-items: start; gap: 22px; padding-block: var(--section); }
.notfound__title { font-size: var(--text-h1); line-height: 0.94; letter-spacing: -0.03em; }
.notfound__body { max-width: 34ch; color: var(--muted); font-size: var(--text-lede); }

/* The preview's strip for flipping between versions of a page on trial (build.mjs). */
.trial-strip { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; padding: 10px var(--gutter); background: #EDB237; color: #111; font: 400 12px/1.4 var(--mono); letter-spacing: 0.06em; }
.trial-strip a { padding: 4px 8px; border: 1px solid #111; border-radius: 2px; color: #111; text-decoration: none; white-space: nowrap; }
.trial-strip a[aria-current] { background: #111; color: #EDB237; }
.trial-strip strong { margin-right: 6px; font-weight: 600; }
