/* Collective Sponsors.
   Warm off-white paper, near-black ink, one bronze hairline. The register is
   quiet-luxury editorial: very few typefaces, very few rules, an enormous
   amount of space, and nothing that moves unless you scroll.

   Three type roles, and no more:
     script, very large                   — the hero wordmark, once, only there
     mono, uppercase, wide-tracked, small — labels, nav, buttons, numbers
     sans, light, very large              — statements
   The script earns its place by being rare. Used twice it stops reading as a
   signature and starts reading as a wedding invitation, so it is scoped to
   `.hero h1` and must not spread.

   The hero is the one dark surface: a full-bleed photograph with white type
   over it, which is the whole reason the page opens the way it does. Every
   other section is paper. Text that sits over a photograph lives under
   `.on-image` and is scoped white there rather than being white by default.

   Committed to light. There is no dark variant, so the page looks the same
   whatever the visitor's system is set to. */

/* The webfonts are linked from each page's <head>, NOT imported here. An
   @import inside a stylesheet cannot be discovered until this file has been
   fetched and parsed, which serialises two round-trips that belong in
   parallel — it was measurably costing ~300ms before first paint. */

/* Self-hosted rather than fetched from Google, because it isn't on Google.
   Licence sits next to the file at fonts/LICENCE-thesignature.txt — free for
   personal and commercial use, which is what makes self-hosting it legitimate
   here. Served as OTF: at 24KB the file is small enough that converting to
   WOFF2 would save a few kilobytes and cost a build step this project does
   not otherwise need. */
@font-face {
  font-family: "The Signature";
  src: url("/fonts/thesignature.otf") format("opentype");
  font-weight: 400;
  font-style: normal;
  /* The word is decorative and the subheading carries the meaning, so showing
     a fallback first and swapping is better than blocking on the file. */
  font-display: swap;
}

:root {
  color-scheme: light;

  --paper: #faf9f7;
  --paper-deep: #f2efea;
  --card: #ffffff;
  --ink: #14120f;
  --dim: #6f6a63;
  --dimmer: #9a938a;
  --line: rgba(20, 18, 15, .17);
  --line-soft: rgba(20, 18, 15, .09);
  --bronze: #9a7444;
  --error: #a8443f;
  --good: #3f6f52;

  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  /* A monoline handwritten signature. The fallbacks are real scripts rather
     than a serif, so a failed webfont degrades to something in the same
     register instead of something loud and wrong. */
  --script: "The Signature", "Snell Roundhand", "Apple Chancery", "Segoe Script", cursive;

  --wide: 1320px;
  --pad: 40px;
  --nav: 72px;

  /* One rhythm value drives every section's breathing room. */
  --chapter: clamp(96px, 14vh, 180px);
}

* { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  overflow-x: hidden;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  font-weight: 300;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Long words — an email address, a pasted handle, a URL — must wrap rather
   than widen the page. */
h1, h2, h3, p, li, dd, td, span, b, a { overflow-wrap: break-word; }

a { color: inherit; }
img { max-width: 100%; display: block; }

.wrap { max-width: var(--wide); margin: 0 auto; padding: 0 var(--pad); width: 100%; }
.narrow { max-width: 860px; }

::selection { background: var(--ink); color: var(--paper); }

/* Off-screen until focused. The first thing a keyboard or screen-reader user
   hits should be a way past the nav, not the nav itself. */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .12em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 14px 22px;
}
.skip:focus { left: 0; }

/* --- The two type roles --------------------------------------------------- */

/* Small, uppercase, wide-tracked. Labels, nav, buttons, attributions, specs. */
.label {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  line-height: 1.5;
  margin: 0;
}
.label.faint { color: var(--dim); }

/* Large, light, sentence case. Statements. */
.statement {
  font-weight: 200;
  font-size: clamp(26px, 3.4vw, 40px);
  line-height: 1.18;
  letter-spacing: -0.02em;
  margin: 0;
  text-wrap: balance;
}
.statement.dim { color: var(--dim); }

/* --- Type over a photograph ----------------------------------------------- */

/* Scoped rather than global: only a section that actually sits on an image
   flips to white, so nothing else on the page has to fight it back. */
.on-image { color: #ffffff; }
.on-image .label.faint { color: rgba(255, 255, 255, .72); }
.on-image .button { border-color: #ffffff; color: #ffffff; }
.on-image .button:hover { background: #ffffff; color: var(--ink); }
.on-image .button.solid { background: #ffffff; color: var(--ink); }
.on-image .button.solid:hover { background: transparent; color: #ffffff; }
.on-image ::selection { background: #ffffff; color: var(--ink); }

/* --- Scroll reveal -------------------------------------------------------- */

/* Set by app.js once the observer is wired, so a visitor with JS disabled
   sees the finished page rather than an empty one. */
.js .reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 1s cubic-bezier(.2, .6, .2, 1), transform 1s cubic-bezier(.2, .6, .2, 1);
  will-change: opacity, transform;
}
.js .reveal.in { opacity: 1; transform: none; }
.js .reveal[data-delay="1"] { transition-delay: .12s; }
.js .reveal[data-delay="2"] { transition-delay: .24s; }
.js .reveal[data-delay="3"] { transition-delay: .36s; }

@media (prefers-reduced-motion: reduce) {
  .js .reveal { opacity: 1; transform: none; transition: none; }
}

/* --- Nav ------------------------------------------------------------------ */

/* Transparent over the hero photograph, where it is white; solid paper the
   moment the page scrolls past it. */
.site-head {
  position: fixed;
  inset: 0 0 auto;
  z-index: 40;
  height: var(--nav);
  display: flex;
  align-items: center;
  color: #ffffff;
  transition: background-color .4s ease, backdrop-filter .4s ease,
              border-color .4s ease, color .4s ease;
  border-bottom: 1px solid transparent;
}
.site-head.solid {
  background: rgba(250, 249, 247, .88);
  backdrop-filter: saturate(140%) blur(14px);
  border-bottom-color: var(--line-soft);
  color: var(--ink);
}

.site-head .wrap {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 16px;
}

.wordmark {
  grid-column: 2;
  justify-self: center;
  font-family: var(--mono);
  font-size: 14px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
}
.wordmark b { font-weight: 400; }

.site-nav {
  grid-column: 3;
  justify-self: end;
  display: flex;
  align-items: center;
  gap: 26px;
}
.nav-home { grid-column: 1; justify-self: start; }
.site-nav a, .nav-home a {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  color: inherit;
  opacity: .68;
  transition: opacity .25s ease;
}
.site-nav a:hover, .nav-home a:hover { opacity: 1; }
.site-nav a[aria-current="page"] { opacity: 1; }

/* --- Buttons -------------------------------------------------------------- */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 17px 34px;
  border: 1px solid var(--ink);
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  transition: background-color .3s ease, color .3s ease, border-color .3s ease;
}
.button:hover { background: var(--ink); color: var(--paper); }
.button:focus-visible { outline: 1px solid var(--bronze); outline-offset: 4px; }

.button.solid { background: var(--ink); color: var(--paper); }
.button.solid:hover { background: transparent; color: var(--ink); }

.button.quiet { border-color: var(--line); color: var(--dim); }
.button.quiet:hover { border-color: var(--ink); background: transparent; color: var(--ink); }

.button[disabled] { opacity: .4; cursor: default; }
.button[disabled]:hover { background: transparent; color: var(--ink); }

/* A bare text link with the rule under it — used where a button would shout. */
.textlink {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  border-bottom: 1px solid var(--line);
  padding-bottom: 4px;
  transition: border-color .3s ease;
}
.textlink:hover { border-bottom-color: var(--ink); }

/* --- Full-bleed chapters -------------------------------------------------- */

/* A chapter names the image it wants in `data-img`; app.js sets `--img` and
   `.has-img` only once that file has actually decoded. Until then the graded
   field below stands in, so a missing photograph is a finished-looking page
   rather than a broken one. */
.chapter {
  position: relative;
  isolation: isolate;
  min-height: 100svh;
  display: flex;
  align-items: flex-end;
  padding: calc(var(--nav) + var(--chapter)) 0 var(--chapter);
  overflow: hidden;
  background: #0d0d0d;
}

.chapter::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -2;
  background-color: #0d0d0d;
  background-image: var(--img, none);
  background-size: cover;
  background-position: var(--img-pos, center);
  background-repeat: no-repeat;
}

/* Scrim. The supplied photography is bright warm studio work and the type on
   it is white, so it needs a floor under it or the whole thing turns into a
   stock-photo banner. */
.chapter::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(to top, rgba(0,0,0,.92) 0%, rgba(0,0,0,.55) 38%, rgba(0,0,0,.15) 68%, rgba(0,0,0,.45) 100%),
    radial-gradient(120% 80% at 50% 100%, rgba(0,0,0,.5), transparent 70%);
}

/* No photograph yet. */
.chapter:not(.has-img)::before {
  background-image:
    radial-gradient(90% 60% at var(--tint-x, 20%) var(--tint-y, 15%), rgba(185, 155, 107, .13), transparent 60%),
    radial-gradient(70% 60% at 85% 85%, rgba(255, 255, 255, .06), transparent 65%),
    linear-gradient(var(--tint-a, 160deg), #1a1a1a 0%, #0d0d0d 55%, #070707 100%);
}

/* Film grain.
   The placement photographs come from small, already-compressed sources and
   are blown up roughly 4x on a retina screen. Nothing recovers detail that was
   never captured — but flat interpolated areas are what actually reads as
   "low quality", and a fine grain over the top replaces that flatness with
   texture the eye accepts as film. It is the same reason fashion editorial
   has looked like this for fifty years.
   Kept in CSS rather than baked into the JPEGs on purpose: baking it cost
   ~400KB per image because grain is incompressible, and it would then be
   scaled up with the photo into visible blobs. Here it is a ~1KB data URI
   that rasterises at device resolution, so it stays fine on any screen. */
.chapter-inner { width: 100%; }

.chapter .grain {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: .19;
  mix-blend-mode: overlay;
  background-repeat: repeat;
  background-size: 180px 180px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* Overlay blending needs a mid-tone to work against; where it isn't supported
   the grain would sit on top as flat grey, so fall back to a plain, lighter
   dusting rather than a haze. */
@supports not (mix-blend-mode: overlay) {
  .chapter .grain { mix-blend-mode: normal; opacity: .07; }
}

/* The content sits above the grain. Only chapter-inner needs a position here —
   the index and the scroll cue are already absolutely positioned above it. */
.chapter .chapter-inner { position: relative; z-index: 1; }

/* Index numeral in the corner of each chapter — tells you where you are in
   the sequence without a progress bar. */
.chapter-index {
  position: absolute;
  top: calc(var(--nav) + 28px);
  left: 0;
  right: 0;
  z-index: 1;
  pointer-events: none;
}
.chapter-index .wrap {
  display: flex;
  justify-content: space-between;
  gap: 20px;
  color: rgba(255, 255, 255, .6);
}

/* A chapter whose subject sits on the right of the frame. The scrim runs
   left-to-right instead of bottom-up so the type lands on the empty part of
   the backdrop rather than on the subject. */
.chapter.subject-right { --img-pos: right center; }
.chapter.subject-right::after {
  background:
    linear-gradient(to right, rgba(0,0,0,.90) 0%, rgba(0,0,0,.74) 30%, rgba(0,0,0,.30) 58%, rgba(0,0,0,.08) 100%),
    /* A band under the nav. The right of this frame is a bright tan backdrop
       and the nav is white type over it — without this the links wash out. */
    linear-gradient(to bottom, rgba(0,0,0,.55) 0%, rgba(0,0,0,.22) 12%, transparent 22%),
    linear-gradient(to top, rgba(0,0,0,.5), transparent 40%);
}

/* On a phone there is no empty left third to write into — the crop is close
   to the subject either way, so the scrim goes back to bottom-up and the type
   sits under them. */
@media (max-width: 720px) {
  /* On a phone this frame crops to about a third of the photograph's width,
     and biasing that third to the right pushes the subject's face off the
     left edge. Centre it: the type moves under her here anyway, because the
     scrim below flips to bottom-up. */
  .chapter.subject-right { --img-pos: center center; }
  .chapter.subject-right::after {
    background:
      linear-gradient(to top, rgba(0,0,0,.92) 0%, rgba(0,0,0,.68) 42%, rgba(0,0,0,.28) 72%, rgba(0,0,0,.45) 100%);
  }
}

/* --- Hero ----------------------------------------------------------------- */

.hero { align-items: center; min-height: 100svh; }
.hero .wrap > * { max-width: 620px; }

/* The wordmark, set as a signature.
   Script faces carry weight below the baseline — the descender on the "ll"
   and the flourish on the "C" both overshoot — so this needs a tight
   line-height to stop it floating and padding underneath so nothing clips.
   No letter-spacing: tracking a connected script breaks the joins between
   letters, which is the one thing making it read as handwriting. */
/* `> h1` rather than `h1`, because `.hero .wrap > *` above caps children at
   620px and would otherwise win on specificity and wrap the word. */
.hero .wrap > h1 {
  font-family: var(--script);
  font-weight: 400;
  /* Sized so the word never breaks: a script that wraps mid-word stops being
     a signature.

     These numbers are specific to this face and would be wrong for another.
     The Signature is compact — "Collective" measures 2.44x the font-size in
     width, where the previous script ran nearly 5x — so the same font-size
     rendered it at 37% of the column and looked lost. 20vw puts it at a bit
     under three fifths of the available width at every size. Re-measure if
     the face ever changes. */
  font-size: clamp(88px, 20vw, 300px);
  line-height: .95;
  letter-spacing: 0;
  margin: 8px 0 0;
  padding-bottom: .1em;
  max-width: none;
  white-space: nowrap;
}

/* The line that says what the company actually does. Deliberately quieter
   than the wordmark, and in the sans, so the two never compete. */
.hero-sub {
  font-weight: 200;
  font-size: clamp(19px, 2.5vw, 33px);
  line-height: 1.16;
  letter-spacing: -0.02em;
  margin: 14px 0 0;
  max-width: 18ch;
  text-wrap: balance;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin: 46px 0 0;
}

.scroll-cue {
  position: absolute;
  bottom: 34px;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  color: rgba(255, 255, 255, .55);
  pointer-events: none;
}
.scroll-cue span { font-family: var(--mono); font-size: 11px; letter-spacing: .2em; text-transform: uppercase; }

/* --- Placements ----------------------------------------------------------- */

/* Each placement is a full-bleed photograph you scroll into, with the mark and
   the copy sitting on it — the same treatment as the hero.

   The source files are portrait and were 730–1200px wide, which is under half
   what a full-bleed chapter needs on a large display. They are converted to
   black and white and upscaled with lanczos before being used here: the
   desaturation unifies four very different color casts (two warm studio tans,
   one hot pink, one cool grey) into one sequence, and it hides the softness
   that a 2x blow-up would otherwise make obvious. Color originals are kept
   alongside as `<name>-color.jpg` — switching back is a filename change. */
/* Focal points, one per photograph.
   These are portrait files in a landscape frame, so `cover` fills the width
   exactly and spills vertically — on a desktop window the horizontal position
   has no effect at all and only the vertical one does. A single shared value
   cannot work when the four faces sit at 15%, 23%, 39% and 49% of their
   frame's height; each below is set so the face lands around the top third of
   the viewport, clear of the copy at the bottom, without cropping the top of
   anyone's head.

   On a phone the maths inverts — the frame is narrower than the image is
   tall, so the height fits, the width spills, and only the horizontal
   position matters. Every subject is roughly centred, so the narrow rule at
   the end of this file just says `center`. */
.p-alo     { --img-pos: center 62%; }
.p-skims   { --img-pos: center 13%; }
.p-sephora { --img-pos: center 18%; }
.p-ulta    { --img-pos: center 35%; }

/* Anchored bottom-left over the photograph, with the copy held to a column
   narrow enough to stay off the subject. */
.placement .wrap > * { max-width: 640px; }

.placement .brand-name { font-size: clamp(48px, 10vw, 132px); margin: 18px 0 0; }
.placement .brand-line { max-width: 46ch; }

.brand-name {
  font-weight: 200;
  line-height: .95;
  letter-spacing: -0.045em;
  text-transform: uppercase;
  margin: 0;
}

.brand-line {
  margin: 26px 0 0;
  font-size: 17px;
  line-height: 1.65;
  color: var(--dim);
}
.on-image .brand-line { color: rgba(255, 255, 255, .88); }

/* Brand marks are supplied as full-color SVGs. Knocking them back to a flat
   ink or white keeps the page monochrome and stops four unrelated brand
   palettes fighting each other and the photography. */
.brand-logo {
  height: var(--logo-h, 34px);
  width: auto;
  /* Generous, because this is only a backstop against a very wide mark on a
     narrow screen. Set too tight it silently overrides the heights below —
     at 260px Sephora was being capped by width and never reaching its height
     at all, which is half of why the set read as undersized. */
  max-width: min(380px, 78%);
  margin: 26px 0 0;
  filter: brightness(0);
  opacity: .92;
}
.on-image .brand-logo { filter: brightness(0) invert(1); opacity: .96; }

/* Heights, not widths, and one per mark. These four wordmarks range from
   1.5:1 to nearly 6:1, so a single height would leave Alo a postage stamp
   next to a Sephora that ran the width of the column. Each is set so the
   marks carry roughly equal optical weight down the sequence. */
/* Heights per mark, because the four run from 1.5:1 to 5.8:1 and a shared
   height would leave Alo a postage stamp beside a Sephora spanning the
   column. Widths these produce at desktop: Alo 138, Ulta 203, Skims 252,
   Sephora 290 — Ulta and Alo stay narrower because their marks are two lines
   deep, so they carry their weight in height instead. */
.logo-alo { --logo-h: clamp(56px, 6.4vw, 92px); }
.logo-skims { --logo-h: clamp(34px, 3.9vw, 56px); }
.logo-ulta { --logo-h: clamp(48px, 5.6vw, 82px); }
.logo-sephora { --logo-h: clamp(30px, 3.4vw, 50px); }

/* A mark starts hidden and is revealed by app.js only once the file has
   decoded, so a missing logo shows the typeset name instead of a broken-image
   glyph — and a visitor with scripting off gets the name, never the glyph. */
.brand-logo[data-logo] { display: none; }
.brand-logo[data-logo].loaded { display: block; }
.brand-logo[data-logo].loaded ~ [data-logo-fallback] { display: none; }

/* --- Plain sections ------------------------------------------------------- */

.section { padding: var(--chapter) 0; border-top: 1px solid var(--line-soft); }
.section.flush { border-top: 0; }

.section-head { margin-bottom: 60px; }
.section-head .label { margin-bottom: 22px; }

/* auto-fit rather than a fixed count: the number of promises here has already
   changed once and will again. */
.terms {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 50px 44px;
  margin-top: 80px;
  padding-top: 44px;
  border-top: 1px solid var(--line);
}
.terms h4 { margin: 0 0 14px; }
.terms p { margin: 0; color: var(--dim); font-size: 15px; line-height: 1.65; }

/* --- Closing CTA ---------------------------------------------------------- */

.close-out { padding: calc(var(--chapter) * 1.2) 0; border-top: 1px solid var(--line-soft); text-align: center; }
.close-out h2 {
  font-weight: 200;
  font-size: clamp(34px, 5.6vw, 76px);
  line-height: 1.03;
  letter-spacing: -0.04em;
  margin: 24px 0 0;
  text-wrap: balance;
}
.close-out .hero-actions { justify-content: center; }

/* --- Footer --------------------------------------------------------------- */

.site-foot {
  border-top: 1px solid var(--line-soft);
  padding: 40px 0;
  color: var(--dimmer);
  background: var(--paper-deep);
}
.site-foot .wrap {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 36px;
  align-items: baseline;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .1em;
  text-transform: uppercase;
}
.site-foot a { text-decoration: none; transition: color .25s ease; }
.site-foot a:hover { color: var(--ink); }
.site-foot .spacer { margin-left: auto; }

/* --- Form pages ----------------------------------------------------------- */

.layout {
  display: grid;
  grid-template-columns: minmax(240px, 360px) minmax(0, 1fr);
  gap: 96px;
  max-width: var(--wide);
  margin: 0 auto;
  padding: calc(var(--nav) + 88px) var(--pad) 120px;
  align-items: start;
}

.rail { position: sticky; top: calc(var(--nav) + 48px); }
.rail h1 {
  font-weight: 200;
  font-size: clamp(34px, 4vw, 54px);
  line-height: 1.04;
  letter-spacing: -0.035em;
  margin: 22px 0 24px;
  text-wrap: balance;
}
.rail p { color: var(--dim); max-width: 34ch; font-size: 16px; }
.rail-note {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .09em;
  text-transform: uppercase;
  line-height: 1.7;
  color: var(--dimmer);
  border-top: 1px solid var(--line-soft);
  padding-top: 20px;
  margin-top: 34px;
}

.form { display: flex; flex-direction: column; gap: 0; max-width: 760px; }

.q { border-top: 1px solid var(--line-soft); padding: 52px 0 0; margin-bottom: 52px; }
.q:first-of-type { border-top: 0; padding-top: 0; }
.q > h2 {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bronze);
  margin: 0 0 10px;
}
.q > .hint { color: var(--dim); font-size: 15px; margin: 0 0 30px; max-width: 58ch; }
.q > h2 + .grid-2, .q > h2 + .field, .q > h2 + .chips, .q > h2 + .platforms { margin-top: 28px; }

.sub {
  display: block;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--dim);
  margin: 0 0 10px;
}
.sub .opt { color: var(--dimmer); }

.field { margin-bottom: 4px; }

input[type="text"], input:not([type]), input[type="email"], input[type="tel"],
input[type="url"], input[type="password"], input[type="search"], select, textarea {
  width: 100%;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: transparent;
  font-family: var(--sans);
  font-size: 16px;
  font-weight: 300;
  color: var(--ink);
  padding: 11px 2px;
  border-radius: 0;
  transition: border-color .25s ease;
}
select {
  appearance: none;
  cursor: pointer;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--dim) 50%),
    linear-gradient(135deg, var(--dim) 50%, transparent 50%);
  background-position: right 11px center, right 6px center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 26px;
}
select option { background: var(--card); color: var(--ink); }
textarea {
  border: 1px solid var(--line);
  padding: 14px;
  resize: vertical;
  min-height: 110px;
  line-height: 1.6;
  background: var(--card);
}
input:hover, select:hover, textarea:hover { border-color: rgba(20, 18, 15, .38); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--ink);
}
input::placeholder, textarea::placeholder { color: var(--dimmer); }
input:-webkit-autofill {
  -webkit-text-fill-color: var(--ink);
  -webkit-box-shadow: 0 0 0 1000px var(--paper) inset;
}

.grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 30px 40px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 30px 28px; }

/* --- Platform rows -------------------------------------------------------- */

.platform-row {
  display: grid;
  grid-template-columns: 116px minmax(0, 1fr);
  gap: 20px;
  align-items: center;
  padding: 18px 0;
  border-bottom: 1px solid var(--line-soft);
}
.platform-row:first-child { border-top: 1px solid var(--line-soft); }
.platform-row > .name {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--dim);
}
.platform-row input { padding: 6px 2px; }
.platform-row .err { grid-column: 1 / -1; margin: 0; }

/* --- Photo slots ---------------------------------------------------------- */

.shots {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
  max-width: 480px;
}

/* A label wrapping a hidden file input, so the whole tile is the picker and
   no separate button is needed. */
.shot-slot {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  aspect-ratio: 3 / 4;
  padding: 18px;
  text-align: center;
  border: 1px dashed rgba(20, 18, 15, .3);
  background: var(--card);
  cursor: pointer;
  overflow: hidden;
  transition: border-color .2s ease;
}
.shot-slot:hover { border-color: var(--ink); }
.shot-slot:focus-within { outline: 1px solid var(--bronze); outline-offset: 3px; }
.shot-slot.filled { border-style: solid; }
.shot-slot.bad { border-color: var(--error); }
.shot-slot.busy { opacity: .6; }

.shot-slot img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.shot-label {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink);
}
.shot-hint { font-size: 12px; color: var(--dimmer); line-height: 1.4; max-width: 22ch; }
.shot-status { font-family: var(--mono); font-size: 10px; letter-spacing: .08em; color: var(--dim); }

/* Once a photo is in, the caption sits over it and needs its own ground. */
.shot-slot.filled .shot-label,
.shot-slot.filled .shot-status {
  position: relative;
  z-index: 1;
  color: #fff;
  background: rgba(0, 0, 0, .55);
  padding: 3px 10px;
}
.shot-slot.filled .shot-hint { display: none; }

.shot-remove {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 2;
  width: 30px;
  height: 30px;
  border: 0;
  background: rgba(0, 0, 0, .62);
  color: #fff;
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
}
.shot-remove:hover { background: rgba(0, 0, 0, .82); }

/* --- Chips ---------------------------------------------------------------- */

.chips { display: flex; flex-wrap: wrap; gap: 10px; }
.chip { position: relative; }
.chip input { position: absolute; opacity: 0; pointer-events: none; }
.chip span {
  display: inline-block;
  padding: 10px 18px;
  border: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--dim);
  cursor: pointer;
  user-select: none;
  transition: border-color .2s ease, background-color .2s ease, color .2s ease;
}
.chip span:hover { border-color: var(--ink); color: var(--ink); }
.chip input:checked + span {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}
.chip input:focus-visible + span { outline: 1px solid var(--bronze); outline-offset: 3px; }
.chip input:disabled + span { opacity: .35; cursor: not-allowed; }
.chip input:disabled + span:hover { border-color: var(--line); color: var(--dim); }
.chip small { display: block; font-size: 10px; letter-spacing: .06em; opacity: .65; }

.check {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  margin: 18px 0;
  cursor: pointer;
  line-height: 1.55;
  font-size: 15px;
  color: var(--dim);
}
.check input {
  width: 17px;
  height: 17px;
  margin-top: 3px;
  flex: none;
  accent-color: var(--ink);
  cursor: pointer;
}

.note { font-size: 13px; color: var(--dimmer); margin: 16px 0 0; max-width: 60ch; line-height: 1.6; }

.err { color: var(--error); font-size: 13px; margin: 8px 0 0; font-family: var(--mono); letter-spacing: .02em; }
.err:empty { display: none; }

/* Honeypot. Off-screen rather than display:none — some bots skip hidden
   inputs but not positioned ones. */
.trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.actions { border-top: 1px solid var(--line); padding-top: 40px; }
.actions .button { min-width: 260px; }
.status { font-family: var(--mono); font-size: 12px; letter-spacing: .06em; color: var(--dim); margin: 20px 0 0; }
.status.bad { color: var(--error); }

/* --- Thanks --------------------------------------------------------------- */

.thanks {
  max-width: 780px;
  margin: 0 auto;
  padding: calc(var(--nav) + 130px) var(--pad) 140px;
}
.thanks h1 {
  font-weight: 200;
  font-size: clamp(40px, 6.4vw, 88px);
  line-height: 1.02;
  letter-spacing: -0.04em;
  margin: 24px 0 26px;
  text-wrap: balance;
}
.thanks .lede { font-size: 19px; color: var(--dim); margin: 0; max-width: 50ch; font-weight: 300; }
/* The pass offer sits between the confirmation and the what-happens-next
   list, because adding the card is the one thing left to do here. */
.wallet-offer {
  border: 1px solid var(--line);
  padding: 30px 30px 32px;
  margin: 48px 0 0;
  background: var(--card);
}
.wallet-offer .label { margin-bottom: 14px; }
.wallet-line { color: var(--dim); margin: 0 0 26px; max-width: 52ch; font-size: 16px; }

.thanks-next { border-top: 1px solid var(--line); margin: 66px 0 0; padding-top: 12px; }
.thanks-next dt {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--bronze);
  margin-top: 34px;
}
.thanks-next dd { margin: 10px 0 0; color: var(--dim); max-width: 62ch; }

/* --- Narrow --------------------------------------------------------------- */

@media (max-width: 1000px) {
  .terms { grid-template-columns: 1fr; gap: 36px; }
  .layout { grid-template-columns: 1fr; gap: 48px; }
  .rail { position: static; }
  .rail p { max-width: none; }
}

@media (max-width: 720px) {
  :root { --pad: 22px; --nav: 60px; --chapter: clamp(72px, 10vh, 110px); }

  body { font-size: 15px; }

  /* "Collective Sponsors" at desktop tracking plus two nav links is wider
     than a 375px screen. Left alone the header overflows, and because it is
     fixed it drags the whole document wider than the viewport rather than
     being clipped by the body's overflow-x. Everything here is about fitting
     that one row. */
  .site-head .wrap { grid-template-columns: auto 1fr; gap: 10px; }
  .wordmark { grid-column: 1; justify-self: start; font-size: 11px; letter-spacing: .16em; }
  .site-nav { grid-column: 2; gap: 14px; }
  .site-nav a { font-size: 10px; letter-spacing: .1em; }
  .nav-home { display: none; }

  .hero-actions { gap: 12px; }
  .hero-actions .button { flex: 1 1 100%; }
  .placement .wrap > * { max-width: none; }
  /* A portrait source in a portrait frame fits vertically and spills
     horizontally, so the vertical focal points above stop applying and only
     the horizontal one counts. Every subject is centred in frame. */
  .placement { --img-pos: center; }

  .grid-2, .grid-3 { grid-template-columns: 1fr; gap: 26px; }

  .platform-row { grid-template-columns: 1fr; gap: 6px; padding: 16px 0; }
  .platform-row > .name { grid-column: 1 / -1; }
  .shots { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }

  .actions .button { width: 100%; }
}
