/* ==========================================================================
   Sponsor Roster — the produx register (12-DESIGN-PLAN-V2.md)

   One typeface + one monospace. One weight (400). Letter-spacing 0 on display
   type. Zero shadows anywhere. 2px corners. Body 17px.

   Palette settled by Ethan 2026-08-10 (V2 §9, D6). Ratios below are computed;
   every one is re-measured on RENDERED PIXELS in the verification loop, because
   a ratio read off a stylesheet is not evidence (vpp/CLAUDE.md §9).

   Seamlessness is carried by four things, not by a picture (V2 §3b):
     1. momentum scroll (Lenis, app.js)
     2. sections that bleed — no section has a background, so none has an edge
     3. one easing curve and one duration set for every movement on the page
     4. no blank or white frame at any transition
   ========================================================================== */

:root {
  /* --- colour. Ratios are against --ground. --------------------------- */
  --ground:  #08110c;            /* very dark green, near-black          */
  --text:    #edebdd;            /* warm off-white          16.00 : 1    */
  --muted:   #8e9186;            /* secondary text           5.97 : 1    */
  --line:    #6e7268;            /* control boundaries       3.90 : 1    */
  --gold:    #c9a227;            /* the one accent           7.92 : 1    */
  --on-gold: #08110c;            /* text on a gold fill      7.92 : 1    */
  --hair:    rgb(237 235 221 / 9%);  /* decorative rules only            */

  /* --- type. Weight 400 only, letter-spacing 0 on display. ------------ */
  --display: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif;
  --mono:    'DM Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;
  /* The title card only. Arial Narrow is the fallback because it is the one
     condensed face on every Mac and Windows machine — the giant word keeps
     roughly its width while Oswald is still arriving, so it does not resize
     under the reader. */
  --film:    'Oswald', 'Arial Narrow', var(--display);

  --t-hero:  clamp(2.6rem, 7vw, 6.4rem);      /* 41.6 → 102px            */
  --t-h2:    clamp(2rem, 3.4vw, 2.875rem);    /* 32 → 46px               */
  --t-h3:    clamp(1.25rem, 1.5vw, 1.5rem);   /* 20 → 24px               */
  --t-lede:  clamp(1.1875rem, 1.1vw + 1rem, 1.375rem);
  --t-body:  17px;                            /* the floor, not a choice */
  --t-mono:  13px;

  /* --- space ---------------------------------------------------------- */
  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px; --s-5: 24px;
  --s-6: 32px; --s-7: 48px; --s-8: 64px; --s-9: 96px; --s-10: 128px;

  --rail-x: clamp(18px, 3.2vw, 56px);
  --edge:   clamp(20px, 3.4vw, 60px);
  --pad-l:  calc(var(--rail-x) + clamp(22px, 2.6vw, 48px));

  --r: 2px;

  /* --- motion. ONE curve, four durations. Nothing else, anywhere. ----- */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --d-1: 160ms; --d-2: 320ms; --d-3: 620ms; --d-4: 900ms;
}

*, *::before, *::after { box-sizing: border-box; }

html {
  color-scheme: dark;
  background: var(--ground);       /* no white frame, ever — including
                                      before the stylesheet finishes */
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: transparent;         /* .ground paints it, and never scrolls */
  color: var(--text);
  font: 400 var(--t-body)/1.62 var(--display);
  font-variant-numeric: tabular-nums;
}

h1, h2, h3 {
  font-weight: 400;
  letter-spacing: 0;
  margin: 0;
  text-wrap: balance;
}
p { margin: 0; text-wrap: pretty; }
ul, ol { margin: 0; padding: 0; list-style: none; }
a { color: inherit; }
em { font-style: normal; color: var(--gold); }

:focus-visible {
  outline: 2px solid var(--gold);  /* 7.92:1 — clears the 3:1 non-text floor */
  outline-offset: 3px;
}

.mono {
  font-family: var(--mono);
  font-size: var(--t-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
.num { color: var(--gold); margin-right: 0.6em; }

.skip {
  position: fixed; left: var(--s-4); top: -100px; z-index: 60;
  background: var(--gold); color: var(--on-gold);
  padding: var(--s-3) var(--s-5); border-radius: var(--r);
  font-size: var(--t-mono); font-family: var(--mono); text-transform: uppercase;
  transition: top var(--d-1) var(--ease);
}
.skip:focus { top: var(--s-4); }

/* --- the ground -----------------------------------------------------------
   Fixed, so it never scrolls and never repaints. Every section is transparent,
   which is why no section can show an edge. This is rule 2 of seamlessness,
   done structurally instead of by matching paddings. */

.ground {
  position: fixed; inset: 0; z-index: -1;
  background:
    radial-gradient(120% 78% at 50% -12%, #0e1c14 0%, rgb(8 17 12 / 0%) 62%),
    radial-gradient(84% 56% at 104% 104%, #0c1710 0%, rgb(8 17 12 / 0%) 58%),
    var(--ground);
}

/* --- the rail -------------------------------------------------------------
   The only element continuous from the first pixel to the last. Its gold fill
   is scroll progress, written by app.js as one transform — compositor work,
   no layout, so it cannot cost a scroll frame. */

/* The .rail and .rail__fill rules that used to sit here — a 1px line fixed to
   the left edge, filling with gold as you scrolled — were removed on Ethan's
   instruction, 2026-08-10. The --rail-x variable survives on purpose: --pad-l
   is calculated from it and sets the left padding of every section. */

/* --- shared band skeleton -------------------------------------------------
   Label column left (sticky, so the section names itself the whole time it is
   on screen), body right. minmax(0,…) on both tracks: a bare 1fr is
   minmax(auto,1fr) and one long word can widen the page. */

/* The hero is deliberately NOT in this list. It is full bleed: the film and
   the giant word both run to the edges of the screen, so the shared page
   padding cannot apply to it. Its own inner block carries the padding. */
.band, .foot {
  padding-left: var(--pad-l);
  padding-right: var(--edge);
}

/* One column on a phone. A fixed 190px label track left the body column 101px
   wide at 375 — every grid inside it collapsed and the page scrolled sideways.
   The label track only exists where there is width to spend on it. */
.band, .foot {
  display: grid;
  gap: var(--s-6) clamp(24px, 3.4vw, 64px);
  padding-block: clamp(var(--s-9), 11vh, 168px);
}
@media (min-width: 1000px) {
  .band, .foot {
    grid-template-columns: minmax(0, 190px) minmax(0, 1fr);
    gap: 0 clamp(24px, 3.4vw, 64px);
  }
  .band__label { position: sticky; top: var(--s-8); align-self: start; }
}
.band__body  { min-width: 0; }
.band__h     { font-size: var(--t-h2); line-height: 1.06; max-width: 20ch; }
/* The moved headline keeps its two hand-set lines. text-wrap: balance would
   otherwise re-break them and the second line reads as one phrase. */
.band__h .line     { display: block; }
.band__h:has(.line) { text-wrap: initial; }
.band__h + .lede   { margin-top: var(--s-6); }

.lede  { font-size: var(--t-lede); line-height: 1.5; max-width: 46ch; }
.note  { font-size: var(--t-body); color: var(--muted); max-width: 52ch; }
.split { display: grid; gap: clamp(var(--s-6), 4vw, var(--s-9)); margin-top: var(--s-7); }
@media (min-width: 1000px) {
  .split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start; }
}
.band .note { margin-top: var(--s-7); }
.split .note { margin-top: 0; }
.note--right { border-top: 1px solid var(--hair); padding-top: var(--s-5); }

/* One side of a two-sided section. The gold label above each half is the same
   object in three places — one rule, three names. */
.side { display: grid; gap: var(--s-5); align-content: start; }
.side .ledger { margin-top: var(--s-2); }
.who, .does__who, .formcol__who { color: var(--gold); }

/* --- 00 · the title card --------------------------------------------------
   13-HERO-SPEC.md. One film edge to edge, the name across the bottom and
   cropped by it, one quiet line, two buttons. Nothing else is allowed on this
   screen, which is the whole point of it.

   Full bleed, so no padding on the section itself — the film has to reach all
   four edges or the screen is not covered, which was the original complaint.
   overflow: hidden is what crops the letters. */

.hero {
  position: relative;
  min-height: 100svh;
  display: grid;
  grid-template-rows: auto 1fr;
  /* minmax(0, 1fr), not the default auto: an auto column sizes to its widest
     item, and the name is deliberately wider than the screen, so the column
     grew to 1453px and the word sat 8px off-centre inside it — a gap down the
     left edge and the R shaved off on the right. */
  grid-template-columns: minmax(0, 1fr);
  overflow: hidden;
  padding: 0;
  isolation: isolate;               /* keeps .ground behind, film above it */
}

.hero__film {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  background: var(--ground) center/cover no-repeat;  /* never a white frame */
  pointer-events: none;
}

/* Dark at the top and the bottom, where the type is. Nearly clear across the
   middle, where the film is. Written for the brightest frame a reel could
   hold: at the top stop, white type still reads about 10:1 over a pure-white
   frame, so a bright reel cannot break it. */
.hero__scrim {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    linear-gradient(to bottom, rgb(8 17 12 / 82%) 0%, rgb(8 17 12 / 34%) 30%, rgb(8 17 12 / 0%) 48%),
    /* The bottom stop is the ground colour at FULL opacity, and the scrim uses
       the ground's own value throughout. Anything less leaves a visible line
       across the screen where the film stops and the page begins — the seam
       this page's whole construction exists to avoid. */
    linear-gradient(to top, var(--ground) 0%, rgb(8 17 12 / 90%) 7%,
                            rgb(8 17 12 / 48%) 26%, rgb(8 17 12 / 0%) 56%),
    rgb(8 17 12 / 16%);
}

.hero__intro {
  position: relative; z-index: 2;
  display: grid; gap: var(--s-6); justify-items: start;
  padding: clamp(28px, 6vh, 64px) var(--edge) 0 var(--pad-l);
}
/* White, not --muted: --muted is a 5.97:1 colour against our own dark ground,
   but over a film frame it can fall to about 3.3:1, which is under the floor
   for 13px text. On this screen the only safe ink is white. */
.hero__line {
  font-family: var(--mono); font-size: var(--t-mono);
  letter-spacing: 0.06em; text-transform: uppercase;
  /* Solid white, not 88% white. A translucent colour is not measurable by the
     contrast tool — it reads the declared colour and ignores the alpha, so a
     faded line would score as if it were solid and the number would be a
     flattering fiction. Quietness is carried by size here, not by fading.
     (vpp/CLAUDE.md §9: contrast read through a translucent layer is garbage.) */
  color: #fff;
  /* No measure cap. The spec asks for ONE short line and this string is a
     fixed 41 characters, so there is nothing for a cap to protect against —
     and every cap I tried broke it onto two lines on a laptop. It still wraps
     on a phone, where the screen is genuinely too narrow, which is correct. */
}
.hero__acts { display: flex; gap: var(--s-3); flex-wrap: wrap; }

/* The name, edge to edge, cropped by the bottom of the screen.
   The font-size is in vw and nowrap, which is what makes it fill the width at
   EVERY width rather than at one: the text's width and the viewport's width
   both scale by the same factor, so one number fits them all. It was measured
   on rendered pixels, not guessed — see the verification note in 13-HERO-SPEC.
   line-height below 1 drops the baseline past the bottom of the box, and
   overflow: hidden on .hero cuts it. That is the crop. */
.hero__word {
  position: relative; z-index: 2;
  align-self: end;
  font-family: var(--film);
  font-weight: 400;
  /* 15.226vw is not a guess. At this size the INK of the fourteen letters
     measures exactly one screen wide, at every screen width — text width and
     screen width both scale by the same factor, so one number fits them all.
     Re-measure it if the name or the typeface ever changes. */
  font-size: 15.226vw;
  /* The line-height IS the crop. Below 1, the baseline drops past the bottom
     of the text box and .hero overflow: hidden cuts whatever falls below the
     screen. Raised from 0.76 to 0.81 on Ethan's note, 2026-08-10: he wanted
     more of the letter bottoms showing. Measured on rendered pixels, that
     took the hidden slice from 7.1% of the capital height to 4.3% at 1440 and
     8.1% to 4.5% at 375. Do not push past ~0.83 — at 0.85 nothing is cut any
     more and the name stops being cropped by the screen at all, which is the
     one thing 13-HERO-SPEC §3 asks of it. */
  line-height: 0.81;
  letter-spacing: 0;
  text-transform: uppercase;
  text-wrap: nowrap;
  white-space: nowrap;
  color: #fff;

  /* Flush left, then pulled left again by the blank sliver the S carries in
     front of it (0.039em). That sliver is the difference between the letters
     touching the edge and the letters sitting a few pixels short of it.
     Deliberately NOT done with text-align: centre — a line wider than its box
     is centred by Firefox and Safari but pushed to the right edge by Chrome,
     so centring would put the word in a different place in each browser. */
  width: max-content;
  justify-self: start;
  text-align: left;
  margin: 0 -0.039em;
  padding: 0;
  user-select: none;
}

/* On a phone the two words stack, and each one is sized to fill the width on
   its own — so the name carries about a quarter of the screen instead of the
   thin 5% strip a single line gives at 375px. "As large as fits" (§3) means
   as large as fits THAT screen, not the laptop's ratio shrunk down.

   The two sizes differ because the two words are different lengths, and both
   have to reach both edges. Every number here is measured ink, not guessed:
   SPONSOR is 3.472 screen-widths per pixel of type, ROSTER is 2.764, and each
   carries a different blank sliver in front of its first letter. */
@media (max-width: 700px) {
  .hero__word { font-size: 0; margin: 0; }   /* 0 also eats the word space */
  .hero__word-a, .hero__word-b { display: block; line-height: 0.81; }
  .hero__word-a { font-size: 28.802vw; margin-left: -0.039em; }
  .hero__word-b { font-size: 36.180vw; margin-left: -0.063em; }
}

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

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 48px; padding: 0 var(--s-6);
  border: 1px solid var(--line); border-radius: var(--r);
  background: transparent; color: var(--text);
  font: 400 var(--t-body)/1 var(--display); letter-spacing: 0;
  text-decoration: none; cursor: pointer;
  transition: background-color var(--d-2) var(--ease),
              border-color var(--d-2) var(--ease),
              color var(--d-2) var(--ease);
}
.btn:hover  { border-color: var(--gold); color: var(--gold); }
.btn--gold  { background: var(--gold); border-color: var(--gold); color: var(--on-gold); }
.btn--gold:hover { background: var(--text); border-color: var(--text); color: var(--on-gold); }
.btn--sm { min-height: 40px; padding: 0 var(--s-4); font-size: var(--t-mono);
           font-family: var(--mono); text-transform: uppercase; }

/* The two on the title card. No gold on that screen — white on film only
   (13-HERO-SPEC §4). Gold is untouched everywhere else on the site.
   The ghost border is 72% white rather than --line: --line is a 3.90:1
   boundary against our own dark ground and would drop under the 3:1 floor
   over a bright film frame. At 72% it holds about 3.6:1 even there. */
.btn--light { background: #fff; border-color: #fff; color: #08110c; }
.btn--light:hover { background: transparent; border-color: #fff; color: #fff; }
.btn--ghost { border-color: rgb(255 255 255 / 72%); color: #fff; }
.btn--ghost:hover { background: rgb(255 255 255 / 12%); border-color: #fff; color: #fff; }

/* --- 01 · the ledger ------------------------------------------------------ */

.ledger { display: grid; }
.ledger li {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline; gap: var(--s-4);
  padding: var(--s-5) 0;
  border-top: 1px solid var(--hair);
}
.ledger li:last-child { border-bottom: 1px solid var(--hair); }
.ledger__v { font-size: var(--t-h3); line-height: 1.2; text-align: right; }

/* No nowrap on the placeholder itself. The text plus its mark is ~310px as one
   unbreakable box — wider than a 375px phone's content column. It dragged the
   whole document out to 564px and collapsed every grid inside it. Only the
   mark stays unbreakable, and the mark alone fits. */
.awaiting {
  color: var(--muted);
  border-bottom: 1px dotted var(--line);
  padding-bottom: 2px;
}
.awaiting::after {
  content: ' ' attr(data-mark);
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--gold);
  vertical-align: super; white-space: nowrap;
}

/* --- 03 · what signing up does — both sides on screen at once ------------- */

.does { display: grid; gap: clamp(var(--s-8), 5vw, var(--s-10)); margin-top: var(--s-7); }
@media (min-width: 1000px) {
  .does { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}
.does__who { margin-bottom: var(--s-5); }
.steps { display: grid; gap: var(--s-6); }
.steps li { border-top: 1px solid var(--hair); padding-top: var(--s-5); }
.steps h3 { font-size: var(--t-h3); line-height: 1.15; margin-bottom: var(--s-2); }
.steps p  { color: var(--muted); max-width: 46ch; }

/* --- the onward step ------------------------------------------------------
   The one action, repeated once in the middle of the page. Sits where the
   reader has just finished reading what signing up does. */

.onward {
  display: grid; gap: var(--s-5); justify-items: start;
  margin-top: var(--s-8);
  border-top: 1px solid var(--hair); padding-top: var(--s-6);
}
@media (min-width: 1000px) {
  .onward {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: end; gap: var(--s-8);
  }
}

/* --- 03 · forms ----------------------------------------------------------- */

.forms { display: grid; gap: clamp(var(--s-8), 5vw, var(--s-10)); margin-top: var(--s-7); }
@media (min-width: 1000px) {
  .forms { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start; }
}
.formcol__who { margin-bottom: var(--s-5);
                border-top: 1px solid var(--hair); padding-top: var(--s-5); }

.form { display: grid; gap: var(--s-6); }
.field { display: grid; gap: var(--s-2); border: 0; padding: 0; margin: 0; min-width: 0; }
.field label, .field legend {
  font-family: var(--mono); font-size: var(--t-mono); letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted); padding: 0;
}
/* --line is a boundary colour at 3.90:1 — fine for a rule, below the 4.5 floor
   for 13px text. Measured on rendered pixels at 3.83:1 and 3.90:1. */
.opt { text-transform: none; letter-spacing: 0; color: var(--muted); }

.field input[type=text],
.field input[type=email],
.field input[type=password],
.field input[type=search],
.field textarea,
.dash select {
  width: 100%; min-width: 0;
  background: transparent; color: var(--text);
  font: 400 var(--t-body)/1.5 var(--display);
  border: 0; border-bottom: 1px solid var(--line); border-radius: 0;
  padding: var(--s-3) 0; min-height: 44px;
  transition: border-color var(--d-2) var(--ease);
}
.field textarea { resize: vertical; }
.field input:focus, .field textarea:focus, .dash select:focus { border-bottom-color: var(--gold); }

/* Chrome paints autofilled fields pale blue. No shadow hack — a very long
   background-color transition keeps our own colour without adding a shadow. */
input:-webkit-autofill,
input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--text);
  transition: background-color 600000s 0s, border-color var(--d-2) var(--ease);
}

.choices { display: grid; gap: var(--s-1); margin-top: var(--s-2); }
.choice {
  display: flex; align-items: center; gap: var(--s-3);
  min-height: 44px; cursor: pointer;
  font-family: var(--display); font-size: var(--t-body); letter-spacing: 0;
  text-transform: none; color: var(--text);
}
.choice input { accent-color: var(--gold); width: 18px; height: 18px; flex: none; }

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

.form__status { font-size: var(--t-body); color: var(--muted); min-height: 1.6em; }
.form__status[data-state="error"] { color: var(--gold); }

.form__done h3 { font-size: var(--t-h3); margin-bottom: var(--s-2); }
.form__done p  { color: var(--muted); max-width: 44ch; }
.inkmark {
  display: block; width: 56px; height: 40px; margin-bottom: var(--s-4);
  fill: none; stroke: var(--gold); stroke-width: 3;
  stroke-linecap: square; stroke-linejoin: miter;
  stroke-dasharray: 92; stroke-dashoffset: 92;
}
.is-done .inkmark { stroke-dashoffset: 0; transition: stroke-dashoffset var(--d-4) var(--ease); }
.is-sending button { opacity: 0.55; pointer-events: none; }

/* --- footer --------------------------------------------------------------- */

.foot { padding-block: clamp(var(--s-8), 8vh, 120px) var(--s-9); }
.foot__body { display: grid; gap: var(--s-7); }
@media (min-width: 900px) {
  .foot__body { grid-template-columns: auto minmax(0, 1fr); gap: var(--s-10); align-items: start; }
}
.foot__links { display: grid; gap: var(--s-3); align-content: start; }
/* padding-top brings the tap area to 44px without moving the underline away
   from the text. Measured at 33px before this. */
.foot__links a { text-decoration: none; border-bottom: 1px solid var(--hair);
                 display: block; padding-top: 12px; padding-bottom: 4px;
                 transition: border-color var(--d-2) var(--ease), color var(--d-2) var(--ease); }
.foot__links a:hover { color: var(--gold); border-color: var(--gold); }
.foot__fine { display: grid; gap: var(--s-4); color: var(--muted);
              font-size: var(--t-mono); font-family: var(--mono); line-height: 1.7; }
.foot__fine p { max-width: 68ch; }
.foot__fine a { color: var(--gold); }

/* --- motion ---------------------------------------------------------------
   Every movement on the page uses --ease and one of the four durations. That
   is rule 3 of seamlessness: nothing looks borrowed from somewhere else. */

.js .reveal { opacity: 0; transform: translate3d(0, 14px, 0); }
.js .reveal.is-in {
  opacity: 1; transform: none;
  transition: opacity var(--d-3) var(--ease), transform var(--d-3) var(--ease);
}
/* The sticky section label fades WITHOUT a transform. A transformed ancestor
   becomes a containing block and breaks position:sticky underneath it — the
   label would unstick for the length of every reveal. */
.js .reveal-soft { opacity: 0; }
.js .reveal-soft.is-in { opacity: 1; transition: opacity var(--d-3) var(--ease); }

/* hero entrance, on load. The name rises last and furthest — it is the thing
   the screen is for. Nothing here moves the film or the scrim, so the picture
   is solid from the first frame and only the type arrives. */
.js .hero__line,
.js .hero__acts { opacity: 0; transform: translate3d(0, 16px, 0); }
.js .hero__word { opacity: 0; transform: translate3d(0, 34px, 0); }
.js body.is-loaded .hero__line,
.js body.is-loaded .hero__acts,
.js body.is-loaded .hero__word {
  opacity: 1; transform: none;
  transition: opacity var(--d-4) var(--ease), transform var(--d-4) var(--ease);
}
.js body.is-loaded .hero__line { transition-delay: 90ms; }
.js body.is-loaded .hero__acts { transition-delay: 200ms; }
.js body.is-loaded .hero__word { transition-delay: 300ms; }

@media (prefers-reduced-motion: reduce) {
  .js .reveal, .js .reveal.is-in,
  .js .reveal-soft, .js .reveal-soft.is-in,
  .js .hero__line, .js .hero__acts, .js .hero__word {
    opacity: 1; transform: none; transition: none;
  }
  .inkmark, .is-done .inkmark { stroke-dashoffset: 0; transition: none; }
  * { animation-duration: 0.01ms !important; }
}

/* ==========================================================================
   The private side — login, dashboard, and the no-JS confirmation page.
   Same tokens, same register, so the two halves of the product agree.
   ========================================================================== */

.plainpage { display: grid; place-items: center; min-height: 100svh; padding: var(--s-8); }
.plainpage main { max-width: 46ch; display: grid; gap: var(--s-5); }
.plainpage h1 { font-size: var(--t-h2); line-height: 1.06; }
.plainpage a { color: var(--gold); }

.signin { display: grid; place-items: center; min-height: 100svh; padding: var(--s-7) var(--s-5); }
.signin__card {
  width: min(100%, 460px); display: grid; gap: var(--s-5);
  border: 1px solid var(--hair); border-radius: var(--r); padding: clamp(var(--s-6), 5vw, var(--s-8));
}
.signin h1 { font-size: var(--t-h2); line-height: 1.06; }
.signin__sub { color: var(--muted); }
.signin__sub a { color: var(--gold); }

/* The gap has to be on the shell, not on <body>. Every section lives inside
   <main data-dashboard-shell>, so a gap on the body applied between its single
   child and nothing — the sections butted together with 0px between them and
   the headings read as jammed into the block above. */
.dash { padding: var(--s-7) var(--edge) var(--s-9); }
.dash [data-dashboard-shell] { display: grid; gap: var(--s-8); }
@media (min-width: 900px) { .dash { padding-inline: clamp(32px, 5vw, 88px); } }

.dash__bar {
  display: flex; justify-content: space-between; align-items: end;
  gap: var(--s-5); flex-wrap: wrap;
  border-bottom: 1px solid var(--hair); padding-bottom: var(--s-5);
}
.dash__bar h1 { font-size: var(--t-h2); line-height: 1.06; margin-top: var(--s-2); }
.dash__bar-actions { display: flex; gap: var(--s-5); }
.dash__link {
  background: none; border: 0; padding: 0; cursor: pointer;
  font-family: var(--mono); font-size: var(--t-mono); letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted); text-decoration: none;
  transition: color var(--d-2) var(--ease);
}
.dash__link:hover { color: var(--gold); }

.dash__counts { display: grid; gap: var(--s-6); }
@media (min-width: 760px) { .dash__counts { grid-template-columns: 1fr 1fr; } }
/* align-content: start, or the shorter column's rows stretch to match the
   taller one and its big number sits 24px lower than its neighbour. */
.count { border-top: 1px solid var(--hair); padding-top: var(--s-5); display: grid;
         gap: var(--s-3); align-content: start; }
.count .eyebrow, .count .mono { color: var(--gold); }
.count__n { font-size: clamp(3rem, 7vw, 5rem); line-height: 1; }
.count__split { display: grid; gap: var(--s-2); color: var(--muted); font-size: var(--t-body); }
.count__note { color: var(--muted); }

.dash__trend, .dash__list { display: grid; gap: var(--s-5); }
.dash__trend-head { display: flex; justify-content: space-between; align-items: baseline;
                    gap: var(--s-5); flex-wrap: wrap; }
.dash__trend h2, .dash__list h2 { font-size: var(--t-h3); }

/* Class names here are dictated by dashboard.js — it writes this markup. */
.trend { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
         gap: var(--s-3); align-items: end; }
.trend li { display: grid; gap: var(--s-2); justify-items: center; align-content: end; }
.trend__bars { display: flex; align-items: flex-end; gap: 3px; width: 100%; height: 96px;
               border-bottom: 1px solid var(--hair); }
.trend__bar { flex: 1; min-height: 2px; border-radius: var(--r) var(--r) 0 0; }
.trend__bar--talent { background: var(--gold); }
.trend__bar--brand  { background: var(--text); }
.trend__day { font-family: var(--mono); font-size: 12px; letter-spacing: 0.06em;
              text-transform: uppercase; color: var(--muted); }
.trend__n   { font-family: var(--mono); font-size: 12px; color: var(--text); }

.dash__controls { display: grid; gap: var(--s-4); }
.dash__filters { display: flex; gap: var(--s-3); flex-wrap: wrap; align-items: center; }
.dash select {
  border: 1px solid var(--line); border-radius: var(--r);
  padding: var(--s-2) var(--s-4); min-height: 44px; width: auto;
  font-size: var(--t-mono); font-family: var(--mono); text-transform: uppercase;
  color: var(--text); background: transparent;
}
.dash input[type=search] {
  border: 1px solid var(--line); border-bottom: 1px solid var(--line);
  border-radius: var(--r); padding: var(--s-2) var(--s-4);
  min-height: 44px; flex: 1 1 220px;
}
.dash__empty { color: var(--muted); border-top: 1px solid var(--hair); padding-top: var(--s-5); }

.table { width: 100%; border-collapse: collapse; font-size: var(--t-body); }
.table th, .table td {
  text-align: left; padding: var(--s-4) var(--s-4) var(--s-4) 0;
  border-bottom: 1px solid var(--hair); vertical-align: top;
}
.table th {
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted); font-weight: 400;
  border-bottom-color: var(--line);
}
.table td { overflow-wrap: anywhere; }
.table time { font-family: var(--mono); font-size: 12px; color: var(--muted); white-space: nowrap; }

.pill {
  display: inline-block; padding: 3px var(--s-3); border-radius: var(--r);
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.06em;
  text-transform: uppercase; white-space: nowrap;
}
.pill--talent { background: var(--gold); color: var(--on-gold); }
.pill--brand  { border: 1px solid var(--line); color: var(--text); }

.row__trunc { display: block; max-width: 24ch; overflow: hidden;
              text-overflow: ellipsis; white-space: nowrap; }
.row__sub   { display: block; font-family: var(--mono); font-size: 12px; color: var(--muted); }
.row__email { color: var(--text); text-decoration: none;
              border-bottom: 1px solid var(--hair); }
.row__email:hover { color: var(--gold); border-color: var(--gold); }
.row__note  { display: block; color: var(--muted); max-width: 40ch; }
.count__n.is-zero { color: var(--muted); }

/* The table becomes a stack of records on a phone — no sideways scrolling
   (SC-012). Each cell names its own column, because the header row is gone. */
@media (max-width: 760px) {
  .table, .table tbody, .table tr, .table td { display: block; width: 100%; }
  .table thead { position: absolute; left: -9999px; }
  .table tr { border-bottom: 1px solid var(--line); padding: var(--s-5) 0;
              display: grid; gap: var(--s-2); }
  .table td { border: 0; padding: 0; }
  .table td::before {
    content: attr(data-label);
    display: block; margin-bottom: 2px;
    font-family: var(--mono); font-size: 12px; letter-spacing: 0.06em;
    text-transform: uppercase; color: var(--muted);
  }
  .row__trunc { max-width: none; white-space: normal; }
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.eyebrow {
  font-family: var(--mono); font-size: var(--t-mono); letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted);
}

/* --- the drifting strip ----------------------------------------------------
   Words sliding sideways, forever, between the title card and the writing.
   Same mechanism as ethanmarnadi.pages.dev: one track holding the list twice,
   slid to exactly -50%, which is the moment the second copy sits precisely
   where the first began. The restart is therefore not a cut — it is the same
   picture, the same trick the film upstairs uses to loop.

   `overflow: hidden` is load-bearing, not tidiness: the track is wider than
   the screen, and without it the whole page would scroll sideways. */
.strip {
  border-block: 1px solid var(--hair);
  padding-block: var(--s-5);
  overflow: hidden;
  white-space: nowrap;
  /* Fades the words out into the page at both ends rather than letting them
     hit a hard edge — what makes it read as drifting past, not as a box. */
  mask-image: linear-gradient(to right, transparent, #000 7%, #000 93%, transparent);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 7%, #000 93%, transparent);
}

.strip__track {
  display: flex;
  align-items: center;
  width: max-content;
  /* 135s is not a taste value: the track now slides ~4050px (three copies) per
     cycle, so the duration sets the speed at roughly 30 pixels a second. Pick a
     shorter one and the words race past a film that barely moves. If the word
     list is ever edited, the distance changes and this has to be re-measured. */
  animation: strip-drift 135s linear infinite;
  will-change: transform;
}

/* LINEAR, and it is the one place on this site that is not --ease. An eased
   marquee speeds up and slows down every cycle, which reads as a fault.
   Continuous motion has to be constant. */
@keyframes strip-drift {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

.strip__set { display: flex; align-items: center; }

/* The site's own small-caps voice — same face, size and tracking as the
   section labels, so the strip belongs here instead of reading as an insert. */
.strip__set > span {
  font-family: var(--mono);
  font-size: var(--t-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Gold on the dot only — the same job --gold does for the section numerals,
   and the smallest amount of it that still registers. */
.strip__set > b {
  color: var(--gold);
  font-weight: 600;
  font-size: var(--t-mono);
  padding-inline: var(--s-6);
}

/* Slower and tighter on a phone: the same words cross a narrow screen far more
   often, and hurried is wrong next to a film that barely moves. */
@media (max-width: 640px) {
  .strip { padding-block: var(--s-4); }
  /* Slightly quicker than the desktop's 30px/s, because on a narrow screen a
     word is on screen far longer for the same speed and it reads as stalled. */
  .strip__track { animation-duration: 105s; }
  .strip__set > b { padding-inline: var(--s-5); }
}

/* Anyone asking for less motion gets the words standing still — the same
   promise the film and the smooth scrolling already make. Still readable. */
@media (prefers-reduced-motion: reduce) {
  .strip__track { animation: none; }
}

/* --- room to breathe on a phone --------------------------------------------
   Ethan, 2026-08-10: "make it way less cramped, space things out, right now
   it's very cluttered."

   The gaps BETWEEN sections were never the problem — those are 96px and were
   fine. The crowding is all inside the blocks, and it comes from values chosen
   on a wide screen where every heading is one line:

     .steps h3   sat 8px above its own paragraph. On a phone the heading wraps
                 to two lines, so 8px reads as the paragraph touching it. This
                 is the single biggest cause of the cluttered feeling.
     .band__h    line-height 1.06 is right for one big desktop line. The same
                 heading wraps to four lines on a phone and they nearly collide.
     .split      stacks on a phone, so its 32px column gap becomes the only
                 thing separating two whole sections of writing.

   So this block loosens what is inside a block and leaves the page rhythm and
   every typeface, size and colour exactly as they were. */
@media (max-width: 700px) {
  /* Headings: room between the lines, and room before the text they head. */
  .band__h { line-height: 1.15; }
  .band__h + .lede { margin-top: var(--s-7); }

  /* Long paragraphs on a narrow screen need a looser line to stop reading as
     a wall. Size is unchanged — only the space between lines. */
  .lede { line-height: 1.58; }
  .note { line-height: 1.7; }

  /* The three steps. The margin under h3 is the actual fix for the screen
     Ethan was looking at. */
  .steps    { gap: var(--s-8); }
  .steps li { padding-top: var(--s-6); }
  .steps h3 { margin-bottom: var(--s-4); line-height: 1.25; }

  /* Two stacked halves need to read as two things, not one long column. */
  .split { gap: var(--s-9); }
  .side  { gap: var(--s-6); }

  /* Standalone notes, and the one action, get clear air around them. */
  .band .note { margin-top: var(--s-8); }
  .note--right { padding-top: var(--s-6); }
  .onward { margin-top: var(--s-9); gap: var(--s-6); padding-top: var(--s-7); }

  /* Rows in the little tables stop touching their own rules. */
  .ledger li { padding-block: var(--s-5); }
}
