/* pinda.place — just the map.
   Colours from constants/Theme.ts: Paper #e4e2d9 is the map's own land colour, so it stands in
   before the first tiles land. */

/* THE GRID, in one place. The screen side gutter is SPACING.xl (32) and the columns are separated
   by the SAME 32 — a column is never closer to its neighbour than it is to the edge of the screen.
   Three columns therefore take the width less two margins and two gutters. Both the layouts and
   the construction guides read `--col`, so they cannot describe different grids. */
:root {
  --margin: 32px;
  --gutter: 32px;
  --col: calc((100% - 128px) / 3);
}

* { box-sizing: border-box; }
html, body { margin: 0; }

/* The page has no content, but the map's pan is driven by scroll position — so the body carries
   five screens of scrollable height for the drift to run across. */
/* Mandatory snap: a scroll always settles on a page, never between two. Safe to be mandatory
   here because every page is exactly one viewport and nothing can outgrow it — if a page ever
   carries content taller than the screen, this has to drop to `proximity` or it will trap the
   reader inside that page. */
html {
  scroll-snap-type: y mandatory;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body { background: #e4e2d9; }

/* One screen. dvh so a phone's toolbars don't leave a strip of the next page showing. */
.page {
  position: relative;
  height: 100dvh;
  scroll-snap-align: start;
}

.backdrop { position: fixed; inset: 0; background: #e4e2d9; }

/* width/height, NOT just inset. mapbox-gl.css is injected AFTER this file and its
   `.mapboxgl-map { position: relative }` beats `position: absolute` on the cascade — at which
   point `inset: 0` sizes nothing and the container collapses to 0 tall. */
.backdrop__map {
  position: absolute; inset: 0; width: 100%; height: 100%;
  /* Fades in once the map has actually loaded, rather than tiles popping in over paper.
     FADE_DURATION — the app's one crossfade timing. */
  opacity: 0;
  transition: opacity 280ms ease;
}
.backdrop__map.is-live { opacity: 1; }
/* Decorative, so Reduce Motion gets it instantly rather than not at all. */
@media (prefers-reduced-motion: reduce) {
  .backdrop__map { transition: none; }
}

/* Mapbox's terms require the wordmark and the © line to stay visible. */
.mapboxgl-ctrl-bottom-left, .mapboxgl-ctrl-bottom-right { z-index: 2; pointer-events: auto; }

/* The canary + title — a 1:1 port of components/ui/WelcomeCard.tsx.
   Canary: TablerIcon size 40, colour c.primary, the app's 2px icon stroke.
   Title:  FONT_SIZE.xl (22) · FONT_FAMILY (Courier New) · weight 700 · c.text · SPACING.sm above. */
/* The message sits inside page one and scrolls away with it. Only the logo and the guides
   toggle are pinned; below page one it's bare map. */
.welcome {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.canary {
  width: 40px; height: 40px;
  fill: none;
  stroke: #FF6A06;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.welcome__title {
  margin: 8px 0 0;
  font-family: 'Courier New', Courier, monospace;
  font-weight: 700;
  /* FONT_SIZE.lg */
  font-size: 18px;
  color: #4d4d4d;
  text-align: center;
}

/* The lockup, pinned top left on the screen side gutter (SPACING.xl), clear of the notch. */
.logo {
  position: fixed;
  top: max(32px, env(safe-area-inset-top));
  left: max(32px, env(safe-area-inset-left));
  height: auto;
  z-index: 2;
}
/* The Ink, like everything else. The lockup used to sit at 95% black — a deliberate exception to
   the standard's "Ink replaces pure black everywhere" — and no longer does: there is now no black
   anywhere on the site, in the app's own sense of the word. */
.logo path { fill: #4d4d4d; }

/* ── Construction guides ─────────────────────────────────────────────────────
   The app's overlay, ported: colour = meaning (ConstructionLines.tsx).
     GREEN  #00ff00  the margins — the screen side gutter, SPACING.xl (32)
     YELLOW #ffff00  the symmetry axis — dead centre, the app's vCenter
     BLUE   #0000ff  the column edges — FOUR lines, one either side of each gutter, because
                     the gutters are as wide as the margins and a column edge is never
                     shared with its neighbour's
   Drawn over everything and pointer-events: none, so it can never intercept anything. */
.guides { position: fixed; inset: 0; z-index: 50; pointer-events: none; }
.g { position: absolute; }

.g--margin { background: #00ff00; }
.g--top    { left: 0; right: 0; top: 32px; height: 1px; }
.g--bottom { left: 0; right: 0; bottom: 32px; height: 1px; }
.g--left   { top: 0; bottom: 0; left: 32px; width: 1px; }
.g--right  { top: 0; bottom: 0; right: 32px; width: 1px; }

.g--col { top: 0; bottom: 0; width: 1px; background: #0000ff; }
.g--col-1-end   { left: calc(var(--margin) + var(--col)); }
.g--col-2-start { left: calc(var(--margin) + var(--col) + var(--gutter)); }
.g--col-2-end   { left: calc(var(--margin) + var(--col) * 2 + var(--gutter)); }
.g--col-3-start { left: calc(var(--margin) + var(--col) * 2 + var(--gutter) * 2); }

/* Both axes, the app's vCenter / hCenter. */
.g--axis { background: #ffff00; }
.g--axis-v { top: 0; bottom: 0; left: 50%; width: 1px; }
.g--axis-h { left: 0; right: 0; top: 50%; height: 1px; }

/* The welcome copy — WelcomeCard.tsx's `body`: FONT_SIZE.sm (14) · Mono · c.text · centred ·
   lineHeight 21 · SPACING.md above. `pre-line` keeps the app's blank-line paragraph breaks
   (the {'\n\n'} in the source) rather than approximating them with margins, and the measure is
   the app card's own: maxWidth 340 less its SPACING.lg padding either side. */
.welcome__body {
  /* SPACING.xl — doubled from the app card's SPACING.md, which is tight without the label
     around it. */
  margin: 32px 0 0;
  /* Hug the longest line rather than a fixed measure, so the breaks written into the copy are
     the ONLY breaks — the label grows to fit instead of re-wrapping the text. Capped at the
     space between the page margins (less the frame's own padding) so it can never push past
     the grid; below that width the longest line has to wrap after all. */
  width: max-content;
  max-width: calc(100vw - 64px - 32px);
  white-space: pre-line;
  font-family: 'Courier New', Courier, monospace;
  /* FONT_SIZE.sm, the app's own pairing of 14 with 21. */
  font-size: 14px;
  line-height: 21px;
  color: #4d4d4d;
  text-align: center;
}

/* Icon circle (Lexicon §2) — the universal round control: 25x25, 1px Ink outline, Paper fill,
   glyph centred. Shared by the guides toggle and the scroll button so they can't drift. */
.icon-circle {
  position: relative;
  width: 25px; height: 25px;
  padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: #e4e2d9;
  border: 1px solid #4d4d4d;
  border-radius: 9999px;
  color: #4d4d4d;
  cursor: pointer;
  appearance: none; -webkit-appearance: none;
  /* The one app-wide shadow — it belongs on every button, label and panel (Theme.ts). */
  box-shadow: -5px 5px 10px rgba(0, 0, 0, 0.15);
}
/* Expand the touch target to 44x44 (HIG) without growing the visible circle. */
.icon-circle::after { content: ''; position: absolute; inset: -10px; }
.icon-circle:active { transform: translate(-1px, 1px); }
.icon-circle__glyph {
  width: 14px; height: 14px;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}

/* The guides toggle — top right, mirroring the logo's gutter, above the guides so a line can
   never cover it. Off, it recedes to the Soft line: available but inactive. */
.guides-toggle {
  position: fixed;
  top: max(32px, env(safe-area-inset-top));
  right: max(32px, env(safe-area-inset-right));
  z-index: 60;
}
.guides-toggle[aria-pressed="false"] { border-color: #c4c4c4; color: #c4c4c4; }

/* The scroll button — a full MODULE (25) under the message. The app's rule is BUTTON_GAP, half a
   module, for a button hanging below a block; without the label's frame to sit against it wants
   the whole one. `.welcome` is pointer-events: none, so this claims its own clicks. */
.scroll-next { margin-top: 25px; pointer-events: auto; }

.guides.is-off { display: none; }

.page-mark {
  --mark-scale: 0.8;
  display: block;
  margin-left: max(32px, env(safe-area-inset-left));
  /* Two columns wide. The mockup is 4:3 landscape, so at two columns it is only ~0.74 as tall
     as it is wide and comfortably clears the screen at every size — no clamp needed (the square
     app icon that lived here before did need one). The min() is kept as a guard: if the art is
     ever swapped for something taller, it shrinks instead of outgrowing the page and fighting
     the mandatory scroll snap. */
  /* --mark-scale is the one number to tune: 1 = exactly two columns. */
  /* Two columns AND the gutter between them. */
  width: min(
    calc((var(--col) * 2 + var(--gutter)) * var(--mark-scale)),
    calc((100dvh - 128px) * 1.352)
  );
  height: auto;
}

/* The hero label is OFF — the content sits straight on the map. The element stays as the layout
   wrapper so the panel is one block away: put back the padding, fill, border, radius and shadow
   below and it is the app's Frame style again (PlaceLabel.tsx: RADIUS.md 12, c.outline 1 in Ink,
   SPACING.md 16 all round, LABEL_FILL paper at 0.9, the one app-wide shadow). */
.frame {
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Page three — the three aspects in a row, close together, light then dark then classic.
   All three were cropped from the same box, so they share a scale and line up exactly.
   Width is whichever is smaller: a third of the space between the margins, or what keeps the
   phones inside the page's height (they are tall, 0.557 wide per unit of height, and on a short
   laptop a third of the width would overflow the screen — and a page taller than the viewport
   fights the mandatory snap). SPACING.sm between them: close, but not touching. */
.lineup {
  display: flex;
  align-items: center;
  gap: 8px;
}
.lineup img {
  /* --lineup-scale: 1 fills the row edge to edge between the margins. */
  --lineup-scale: 0.75;
  display: block;
  width: min(
    calc((100vw - 64px - 16px) / 3 * var(--lineup-scale)),
    calc((100dvh - 64px) * 0.5571 * var(--lineup-scale))
  );
  height: auto;
}

/* Page four — the type shot on the left margin, mirroring page three's right-aligned lineup.
   The phone is narrow and tall (0.3948 wide per unit of height), so height is what runs out
   first: the width is whichever is smaller of the two-column measure and what keeps it inside
   the page, since a page taller than the viewport fights the mandatory snap.
   --shot-scale: 1 means it fills that measure. */
/* A three-column grid matching the guides exactly: the padding IS the margins, the three equal
   tracks ARE the thirds. Centring in track 1 then needs no arithmetic, and it stays right at any
   width — which a padding-left calc would not, since a percentage there resolves against a
   different box than the image's own width does.
   max-width caps the phone at the column, so "centred on the first column" means inside it. */
.type-shot {
  grid-column: 1;
  display: block;
  /* Two columns and the gutter, scaled — though `max-width` below caps it at the one column it
     is centred in. The second term is the height guard: this phone is tall and narrow, so on a
     short screen height runs out first. */
  width: min(
    calc((var(--col) * 2 + var(--gutter)) * var(--shot-scale)),
    calc((100dvh - 64px) * 0.3948 * var(--shot-scale))
  );
  max-width: 100%;
  height: auto;
}

/* An inline link in the message. Pinda Orange is the app's one accent and what it uses for an
   action; the underline is the web's own affordance, and without it an orange word on paper is
   just an orange word. Sits inside .welcome, which is pointer-events: none, so it claims its own
   clicks. */
.link {
  color: #FF6A06;
  text-decoration: underline;
  text-underline-offset: 2px;
  pointer-events: auto;
  cursor: pointer;
}
.link:active { opacity: 0.7; }

/* ── Page six · the feedback form ────────────────────────────────────────────
   Deliberately NOT the app's Create-a-place vocabulary — no panel, no pill buttons, no 52pt
   filled Button. This page speaks page one's language instead: Mono straight on the map, Ink for
   type, Soft hairlines for structure, and Orange reserved for the ONE action (the same rule that
   makes the welcome message's link the only orange thing on page one).
   Landscape by construction: the intro takes column one of the guide grid, the form columns two
   and three, so it reads as one wide band rather than a stacked card. */

.sprite { position: absolute; width: 0; height: 0; }

/* Three equal tracks with the standard gutter — they ARE the columns the blue guides draw, so the form's left
   edge lands exactly on the blue line that opens column two. The separation between the two
   halves IS the grid's gutter — as wide as the screen margin, like every other gap here.
   align-content centres the band on the page; align-items keeps the two halves top-aligned, so
   the intro's FEEDBACK line sits on the same line as the form's first label. */
.page--feedback {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  column-gap: var(--gutter);
  align-content: center;
  align-items: start;
  row-gap: 40px;
  padding-left: max(32px, env(safe-area-inset-left));
  padding-right: max(32px, env(safe-area-inset-right));
}
.say-intro { grid-column: 1; grid-row: 1; }
/* The form and its thank-you share one cell — only ever one of them is on screen.
   max-width caps the measure: two thirds of a wide desktop is far past a comfortable line. */
.say, .done { grid-column: 2 / span 2; grid-row: 1; max-width: 720px; }
/* Both carry `display: flex`, which beats the UA's `[hidden] { display: none }` on the cascade —
   without this the form would stay on screen after sending and the thank-you would sit under it. */
.say[hidden], .done[hidden] { display: none; }

/* The meta line — UI Sans, 10px, 300, uppercase. One class for the section label AND every field
   label, so a label never competes with the thing it labels. */
.meta {
  display: block;
  margin: 0;
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-weight: 300;
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #9CA3AF;
}
/* Page one's title, exactly: Mono 700 at FONT_SIZE.lg in Ink. */
.say-title {
  margin: 6px 0 0;
  font-family: 'Courier New', Courier, monospace;
  font-weight: 700;
  font-size: 18px;
  color: #4d4d4d;
}

/* The kind picker — four Mono words, not four pills. Chosen = Orange and underlined, so the
   choice reads without colour alone carrying it. */
.kinds { display: flex; flex-wrap: wrap; gap: 6px 16px; margin-top: 24px; }
.kind {
  position: relative;
  padding: 0;
  font-family: 'Courier New', Courier, monospace;
  font-weight: 500; font-size: 14px; line-height: 21px;
  color: #4d4d4d;
  background: none; border: 0;
  cursor: pointer;
  appearance: none; -webkit-appearance: none;
  transition: color 280ms ease;
}
.kind::after { content: ''; position: absolute; inset: -12px -6px; } /* 44pt touch target */
.kind[aria-pressed="true"] {
  color: #FF6A06;
  text-decoration: underline;
  text-underline-offset: 4px;
}

/* The form — label over a hairline-underlined control. You and Where share a row; the note runs
   the full measure under them. */
.say { display: flex; flex-direction: column; gap: 24px; }
.say__row { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
.f { position: relative; display: flex; flex-direction: column; }
.f__control {
  width: 100%;
  padding: 2px 0 8px;
  background: transparent;
  color: #4d4d4d;
  border: 0;
  border-bottom: 1px solid #c4c4c4;
  border-radius: 0;
  font-family: 'Courier New', Courier, monospace;
  font-weight: 500;
  font-size: 16px; /* >=16 or iOS zooms the page on focus */
  line-height: 24px;
  appearance: none; -webkit-appearance: none;
  transition: border-color 280ms ease;
}
/* The hairline is the field. Focus lights it Orange rather than adding a ring. */
.f__control:focus { outline: none; border-bottom-color: #FF6A06; }
.f__control::placeholder { color: #9CA3AF; }
select.f__control { cursor: pointer; padding-right: 22px; }
.f__chevron { position: absolute; right: 0; bottom: 10px; color: #9CA3AF; pointer-events: none; }
/* Tall enough to invite a real note, short enough that the page still fits one screen — the
   scroll snap is mandatory, so a page taller than the viewport would trap the reader. */
.f__control--note { resize: none; min-height: clamp(76px, 16vh, 132px); }
.counter {
  align-self: flex-end;
  margin-top: 6px;
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-weight: 300; font-size: 10px; letter-spacing: 0.08em;
  color: #9CA3AF;
}
.counter.is-full { color: #FF6A06; }

/* Honeypot — offscreen, never focusable by a human. */
.trap { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }

/* The one action: a Mono word and an arrow, in Orange. No fill, no border, no shadow. */
.send {
  position: relative;
  align-self: flex-end;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 0;
  font-family: 'Courier New', Courier, monospace;
  font-weight: 500; font-size: 16px; line-height: 24px;
  color: #FF6A06;
  background: none; border: 0;
  cursor: pointer;
  appearance: none; -webkit-appearance: none;
  transition: opacity 280ms ease;
}
.send::after { content: ''; position: absolute; inset: -12px -8px; } /* 44pt touch target */
.send__arrow { width: 18px; height: 18px; transition: transform 120ms ease; }
.send:active .send__arrow { transform: translateX(3px); } /* the press travels with the arrow */
.send.is-busy { opacity: 0.45; pointer-events: none; }
/* A secondary action recedes to Ink — Orange only ever marks the primary one. */
.send--quiet { align-self: flex-start; color: #4d4d4d; }

/* The error speaks in the accent, unboxed. */
.notice {
  margin: 0;
  font-family: 'Courier New', Courier, monospace;
  font-size: 12px; line-height: 18px;
  color: #FF6A06;
}

/* Sent — same column, same left edge, so nothing jumps when the form is replaced. */
.done { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; }
.done__body {
  margin: 0;
  max-width: 46ch;
  font-family: 'Courier New', Courier, monospace;
  font-size: 14px; line-height: 21px; color: #4d4d4d;
}
.say-title--done { margin: 0; }
.glyph { width: 14px; height: 14px; flex: none; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.glyph--check { width: 22px; height: 22px; color: #FF6A06; }

/* Narrow — the landscape band folds into one column. Nothing else changes. */
@media (max-width: 780px) {
  .page--feedback { grid-template-columns: 1fr; row-gap: 28px; }
  .say-intro { padding-right: 0; }
  .say, .done { grid-column: 1; grid-row: auto; max-width: none; }
  .say { gap: 20px; }
  .kinds { margin-top: 18px; }
  /* You and Where STAY side by side — stacking them would cost the height this page can't
     spare. Where takes the larger share: its longest option ("Saving a place") is wider than
     any name, and a select truncates rather than wrapping. */
  .say__row { grid-template-columns: 0.8fr 1.2fr; gap: 16px; }
}
/* A short screen as well — the note gives up the height so the page still fits one screenful
   (the scroll snap is mandatory: a taller page would trap the reader inside it). */
@media (max-height: 720px) {
  .f__control--note { min-height: 64px; }
}

/* ── The masthead · lockup + section menu ────────────────────────────────────
   The lockup alone stays `position: fixed` (the gate uses it that way, and has no menu). Where a
   page carries the menu, both go inside .masthead instead and it does the pinning — one flex row,
   so the two can't drift apart as the nav grows. .logo drops its own fixed positioning INSIDE the
   masthead rather than losing it globally, so /enter is untouched. */
.masthead {
  position: fixed;
  top: max(32px, env(safe-area-inset-top));
  left: max(32px, env(safe-area-inset-left));
  right: max(32px, env(safe-area-inset-right));
  z-index: 3; /* above the guides toggle's siblings; the guides themselves are 50 and pass through */
  display: flex;
  align-items: center;
  /* SPACING.lg between the mark and the first word. */
  gap: 24px;
  pointer-events: none; /* the row is mostly empty air — only the links take clicks */
}
.masthead .logo { position: static; top: auto; left: auto; }

/* The menu speaks the kind picker's language exactly (the feedback form's four Mono words):
   Mono 14 in Ink, and the current page in Orange with an underline — so the choice never rests on
   colour alone. No pills, no fill, no shadow: it is words, like everything else on this site. */
.nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  /* The kind picker's own spacing. */
  gap: 6px 16px;
  /* Pushed to the far side of the row: the mark holds the left gutter, the menu the right, both
     on the same line and the same margins the guides draw. */
  margin-left: auto;
  /* That corner is shared with the construction-guides toggle, which every page with a menu
     carries — so the menu steps in by the toggle's width (a Module, 25) plus SPACING.md, on EVERY
     page. Unconditional on purpose: the menu has to land in the same place on all of them, and a
     per-page modifier is how that quietly stops being true. The toggle never moves; it is the
     fixed landmark in the corner. */
  margin-right: 41px;
  pointer-events: auto;
}
.nav__item {
  position: relative;
  font-family: 'Courier New', Courier, monospace;
  font-weight: 400;
  font-size: 14px;
  line-height: 21px;
  color: #4d4d4d;
  text-decoration: none;
  transition: color 280ms ease;
}
.nav__item::after { content: ''; position: absolute; inset: -12px -6px; } /* 44pt touch target */
.nav__item:active { opacity: 0.7; }
.nav__item[aria-current="page"] {
  color: #FF6A06;
  text-decoration: underline;
  text-underline-offset: 4px;
}
/* Narrow: the mark gives up its width before the menu does — the menu is the navigation, the mark
   is decoration once you're inside. */
@media (max-width: 560px) {
  .masthead { gap: 16px; }
  .masthead .logo { width: 92px; }
}

/* ── /thecase · the presentation ─────────────────────────────────────────────
   A DOCUMENT, not a deck. The tester page is five screens that snap; this one is read top to
   bottom, often by someone skimming, so `html.doc` turns snapping off — a snap point inside a
   long section traps the reader mid-paragraph.
   The hero is one screen over the live map, exactly like page one next door. Everything after it
   sits on Paper, which is also the map's own land colour, so the document reads as the map's
   surface rising rather than a card dropped on top of it. */
/* The tester page's scrolling, exactly: MANDATORY snap, so a scroll always settles on a screen and
   never between two. No scroll-padding — it insets the snapport, which would rest every section
   132px down the screen with the tail of the previous one still showing.
   A section taller than the viewport is safe: when a snap area is bigger than the snapport the spec
   lets the viewport rest anywhere inside it, so the long tables scroll normally. */
html.doc { scroll-snap-type: y mandatory; }

.hero {
  position: relative;
  min-height: 100dvh;
  scroll-snap-align: start;
  display: grid;
  align-content: center;
  padding: 132px max(32px, env(safe-area-inset-left)) 48px;
}
.hero__block { max-width: 620px; }
/* THE TYPE RULE: a title is 18 at 700, a text block is 14 at 400, both in Mono. That is the whole
   scale — a hero does not get a bigger one for being a hero. Same as the tester page's title. */
.hero__title {
  margin: 6px 0 0;
  font-family: 'Courier New', Courier, monospace;
  font-weight: 700;
  font-size: 18px;
  line-height: 26px;
  color: #4d4d4d;
}
.hero__body {
  margin: 24px 0 0;
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-weight: 300;
  font-size: 14px;
  line-height: 21px;
  color: #4d4d4d;
}
/* The scroll affordance is a LINK, not a button — the anchor does the work the tester page needs
   three lines of script for, and `scroll-behavior: smooth` on html already honours Reduce Motion. */
/* The arrow leaves the copy block and takes the screen: centred on the vertical axis, sitting on
   the bottom margin — the two guides it should answer to. Offset with `left` rather than a
   translate, because the icon circle already spends its transform on the press (:active nudges it
   a pixel) and a centring translate there would be cancelled the moment it was tapped. Half a
   Module is 12.5, the site's signature half-measure. */
.hero .scroll-next {
  position: absolute;
  left: calc(50% - 12.5px);
  bottom: max(32px, env(safe-area-inset-bottom));
  margin: 0;
}

/* NO fill. The document sits straight on the live map, exactly like page one of the tester page —
   the map is the surface every screen of this site is drawn on, not a backdrop the content hides
   after the hero. `position: relative` only puts it in the same stacking flow as the rest; it
   comes later in the DOM than the fixed backdrop, so it paints over it without a z-index. */
.paper { position: relative; }

/* One section. The SAME three-column grid as the feedback page: the label takes column one, the
   content columns two and three, and both land on the blue column edges. Under 780px it folds to
   one column, at the same breakpoint, so the whole site changes shape at one width. */
.plate {
  position: relative;
  min-height: 100dvh;
  scroll-snap-align: start;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  column-gap: var(--gutter);
  align-content: center;
  align-items: start;
  padding: 132px max(32px, env(safe-area-inset-right)) 64px max(32px, env(safe-area-inset-left));
}
.plate__label { grid-column: 1; }
.plate__body { grid-column: 2 / span 2; max-width: 720px; }
/* A plate that needs the full measure — wide tables and art. */
.plate--wide .plate__body { max-width: none; }

/* A plate laid out like page one — the copy in the LAST column, flush to the right margin, with
   the first two columns left to the map or to a picture. For a screen that CONTINUES the hero's
   thought rather than opening a new column of the document. */
.plate--lead .plate__label { padding-right: 0; }
.plate--lead .plate__label,
.plate--lead .plate__body { grid-column: 3; max-width: none; }
.plate--lead .plate__body { margin-top: 24px; }

/* Page one and the screen that continues it, laid out identically: the guide grid's three columns,
   the block in the THIRD one so its right edge is the right margin, hanging FROM the horizontal
   axis — the yellow guide at dead centre — rather than centring on it. The top edge lands on the
   line, SPACING.md clear of it, and the copy runs down into the lower half, so the block's start
   never moves when the copy changes length. Listed together so the two screens can't drift apart:
   the guides are the datum they share. */
.hero, .plate--lead {
  grid-template-columns: repeat(3, 1fr);
  column-gap: var(--gutter);
  align-content: start;
  padding-top: calc(50dvh + 16px);
  padding-right: max(32px, env(safe-area-inset-right));
}
.hero__block { grid-column: 3; max-width: none; }

/* The phone on the first two columns, centred on the screen — page two of the tester page, moved
   here. Absolute so it can sit centred on the FULL screen while the copy beside it hangs from the
   axis; it reuses .page-mark's width, which is the same guide arithmetic (two columns, capped so a
   short screen shrinks it instead of letting it outgrow the page). */
.lead-mark {
  position: absolute;
  left: max(32px, env(safe-area-inset-left));
  top: 50%;
  transform: translateY(-50%);
  margin-left: 0;
}

/* A screen that is only a picture — no label, no copy. The art centres in the column it is given,
   the way page four of the tester page centres this same phone. */
.plate--art {
  justify-items: center;
  --shot-scale: 0.8;
}

/* The mirror of --lead: copy in the FIRST column, art flush to the right margin. Screen one puts
   the picture left and the words right; screen two swaps them, so the two read as a spread. */
.plate--flip .plate__label,
.plate--flip .plate__body { grid-column: 1; }

/* The three aspects, moved over from the tester page and kept flush right, centred on the screen.
   They share .lineup (the flex row and its SPACING.sm gap) and only take a new width: two columns
   for the three phones TOGETHER, gaps included, so they clear the column the copy sits in. The
   second term is the tester page's own guard — on a short screen height runs out first, and the
   row shrinks rather than outgrowing the page. */
.lead-lineup {
  position: absolute;
  /* Columns two and three exactly: pinned from the line that opens column two to the right margin.
     BOTH edges are given, so the row has a definite width — left to shrink-to-fit, the percentage
     its images are measured in would resolve against the space left over instead of the columns,
     and the block would come up short of the line it is supposed to start on. */
  left: calc(var(--margin) + var(--col) + var(--gutter));
  right: max(32px, env(safe-area-inset-right));
  top: 50%;
  transform: translateY(-50%);
  justify-content: flex-end;
}
.lead-lineup img {
  /* Three phones filling that span, less the row's own two gaps. The second term is the tester
     page's height guard: on a short screen they shrink rather than outgrowing the page. */
  width: min(
    calc((100% - 16px) / 3),
    calc((100dvh - 128px) * 0.5571)
  );
}

/* FONT_SIZE.lg, Mono 700 — the tester page's `.say-title`, reused verbatim rather than restyled. */
.plate__title {
  margin: 6px 0 0;
  font-family: 'Courier New', Courier, monospace;
  font-weight: 700;
  font-size: 18px;
  line-height: 26px;
  color: #4d4d4d;
}
/* Body copy. `.plate__body p` is element+class, which OUTRANKS a bare `.meta` or `.stat` on the
   same paragraph — so the two roles that are not body copy are excluded here rather than left to
   lose the cascade and render as plain Mono. They keep their own face and only take the rhythm. */
.plate__body p:not(.meta):not(.stat) {
  margin: 0 0 16px;
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-weight: 300;
  font-size: 14px;
  line-height: 21px;
  color: #4d4d4d;
}
.plate__body p.meta { margin: 0 0 16px; }
.plate__body p:last-child { margin-bottom: 0; }
/* The one emphasis: Orange, no bold. Bold is unavailable in Mono's middle weights anyway (Courier
   ships Regular and Bold only), so weight would be a lie at 500. */
.hot { color: #FF6A06; }
.quiet { color: #9CA3AF; }

/* A statement line — the number a section exists to say. Mono at FONT_SIZE.xxl, Orange, with the
   unit trailing in the meta face so the figure is never read without it. */
.stat { margin: 0 0 24px; display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px; }
.stat__num {
  font-family: 'Courier New', Courier, monospace;
  font-weight: 700;
  font-size: 28px;
  line-height: 34px;
  color: #FF6A06;
}
.stat__unit {
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-weight: 300; font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase;
  color: #9CA3AF;
}

/* Lists — a hairline between rows, nothing else. The Soft line is the whole structure. */
.rows { margin: 0; padding: 0; list-style: none; }
.rows li {
  padding: 12px 0;
  border-bottom: 1px solid #c4c4c4;
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-weight: 300;
  font-size: 14px; line-height: 21px; color: #4d4d4d;
}
.rows li:last-child { border-bottom: 0; }
.rows b { font-weight: 700; }
.rows small {
  display: block;
  margin-top: 2px;
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-weight: 300; font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase;
  color: #9CA3AF;
}

/* Tables — hairlines only, no fills, no zebra. Numbers ride in Mono so the columns align on the
   digit, which is the entire reason this site sets values in a monospace face. */
.grid { width: 100%; border-collapse: collapse; }
.grid th, .grid td {
  padding: 10px 16px 10px 0;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid #c4c4c4;
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-weight: 300;
  font-size: 14px;
  line-height: 21px;
  color: #4d4d4d;
}
.grid th {
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-weight: 300; font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase;
  color: #9CA3AF;
  border-bottom-color: #4d4d4d; /* the Ink line under the header: structure, not division */
}
.grid td:last-child, .grid th:last-child { padding-right: 0; }
/* Figures stay in Mono. Aligning on the digit is the entire reason this site owns a monospace
   face — a column of prices in a proportional one is a column of ragged prices. */
.grid .num { text-align: right; font-family: 'Courier New', Courier, monospace; font-variant-numeric: tabular-nums; }
.grid tr:last-child td { border-bottom: 0; }
.grid .total td { border-top: 1px solid #4d4d4d; border-bottom: 0; font-weight: 700; }
.grid td.hot { color: #FF6A06; }
/* A table wider than the phone scrolls inside its own box rather than pushing the page sideways. */
.scroller { overflow-x: auto; }

/* Bars. Drawn here rather than shipped as an image because the widths are the argument, and an
   inline `style` would be refused by the CSP anyway (style-src 'self'). Every width is a class,
   so the numbers live in one file with the tokens.
   Before is Ink, after is Orange, and the value is printed at the end of every bar — nobody
   should have to read a length off an axis. */
.bars { margin: 0 0 8px; padding: 0; list-style: none; }
.bar { display: grid; grid-template-columns: 1fr; gap: 4px; padding: 12px 0; }
.bar__key {
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-weight: 300; font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase;
  color: #9CA3AF;
}
.bar__line { display: flex; align-items: center; gap: 12px; }
.bar__track { flex: 1; height: 14px; border-bottom: 1px solid #c4c4c4; }
.bar__fill { display: block; height: 13px; min-width: 2px; background: #4d4d4d; }
.bar__fill--now { background: #4d4d4d; opacity: 0.55; }
.bar__fill--after { background: #FF6A06; }
/* $8.10 · ~$0.52 · $0.03, to scale against $8.10. The last two are slivers, and that IS the
   finding — they are labelled outside the bar so a sliver still reads. */
.w-810 { width: 100%; }
.w-052 { width: 6.4%; }
.w-003 { width: 0.4%; }
.bar__val {
  min-width: 72px;
  text-align: right;
  font-family: 'Courier New', Courier, monospace;
  font-weight: 700; font-size: 16px; color: #4d4d4d;
  font-variant-numeric: tabular-nums;
}
.bar__val--after { color: #FF6A06; }

/* The subscription split — one stacked bar, three segments, labelled underneath. */
.split { display: flex; height: 25px; border: 1px solid #4d4d4d; }
.split__seg { height: 100%; }
.split__seg + .split__seg { border-left: 1px solid #4d4d4d; }
.seg-commission { width: 30.1%; background: #c4c4c4; }
.seg-cost { width: 1%; background: #4d4d4d; }
.seg-kept { width: 68.9%; background: #FF6A06; }
.split-key { display: flex; flex-wrap: wrap; gap: 8px 24px; margin-top: 12px; padding: 0; list-style: none; }
.split-key li {
  display: flex; align-items: center; gap: 8px;
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-weight: 300;
  font-size: 14px; line-height: 21px; color: #4d4d4d;
}
.swatch { width: 10px; height: 10px; flex: none; border: 1px solid #4d4d4d; }
.swatch--commission { background: #c4c4c4; }
.swatch--cost { background: #4d4d4d; }
.swatch--kept { background: #FF6A06; }

/* The tier cards — three Paper panels in the app's Frame style (RADIUS.md 12, 1px Ink, the one
   app-wide shadow). The recommended tier is the only orange thing in the row. */
.tiers { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.tier {
  display: flex; flex-direction: column; gap: 8px;
  padding: 16px;
  background: #e4e2d9;
  border: 1px solid #4d4d4d;
  border-radius: 12px;
  box-shadow: -5px 5px 10px rgba(0, 0, 0, 0.15);
}
.tier__price {
  font-family: 'Courier New', Courier, monospace;
  font-weight: 700; font-size: 22px; line-height: 28px; color: #4d4d4d;
}
.tier--lead .tier__price { color: #FF6A06; }
.tier__list { margin: 0; padding: 0; list-style: none; }
.tier__list li {
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-weight: 300;
  font-size: 14px; line-height: 21px; color: #4d4d4d;
}
.tier__cost { margin-top: auto; padding-top: 12px; border-top: 1px solid #c4c4c4; }

/* The step list — done is a filled Orange tick, pending is a hollow Ink ring. Never colour alone. */
.steps { margin: 0; padding: 0; list-style: none; counter-reset: step; }
.steps li {
  position: relative;
  padding: 0 0 24px 40px;
  /* The spine, drawn by the item so it stops with the last one. */
  border-left: 1px solid #c4c4c4;
  margin-left: 12px;
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-weight: 300;
  font-size: 14px; line-height: 21px; color: #4d4d4d;
}
.steps li:last-child { border-left-color: transparent; padding-bottom: 0; }
.steps li::before {
  content: '';
  position: absolute;
  left: -6px; top: 6px;
  width: 11px; height: 11px;
  border-radius: 9999px;
  background: #e4e2d9;
  border: 1px solid #4d4d4d;
}
.steps li.is-done::before { background: #FF6A06; border-color: #FF6A06; }

/* A slot for art that does not exist yet. It is deliberately visible rather than hidden: an empty
   frame with the filename in it is a to-do the page itself carries, and it drops out the moment an
   <img> takes its place. Filenames match investor-kit/08-asset-list.md. */
.slot {
  display: grid;
  place-items: center;
  gap: 6px;
  min-height: 220px;
  padding: 24px;
  border: 1px dashed #c4c4c4;
  border-radius: 12px;
  text-align: center;
}
.slot img { display: block; width: 100%; height: auto; }

/* The source line every claim on the page answers to. */
.foot {
  scroll-snap-align: start;
  position: relative;
  padding: 32px max(32px, env(safe-area-inset-right)) 48px max(32px, env(safe-area-inset-left));
  border-top: 1px solid #c4c4c4;
  margin: 0 max(32px, env(safe-area-inset-left));
}
.foot .meta + .meta { margin-top: 6px; }

/* Narrow — the same fold as the feedback page, at the same width. */
@media (max-width: 780px) {
  .plate { grid-template-columns: 1fr; }
  /* A third of a phone screen is not a column. Everything folds to one, the block stops hanging
     from the axis (there is no room below it), and the phone goes back in the flow above the copy. */
  .hero, .plate--lead { grid-template-columns: 1fr; padding-top: 132px; }
  .hero__block,
  .plate--lead .plate__label,
  .plate--lead .plate__body { grid-column: 1; }
  .lead-mark { position: static; transform: none; width: 100%; margin: 0 0 24px; }
  .lead-lineup { position: static; transform: none; margin: 0 0 24px; }
  .lead-lineup img { width: calc((100% - 16px) / 3); }
  .plate__label { padding-right: 0; padding-bottom: 12px; }
  .plate__body { grid-column: 1; max-width: none; }
  .tiers { grid-template-columns: 1fr; }
}

/* Print — an investor will PDF this. Drop the map and the fixed masthead (a fixed element repeats
   on every printed page), and never break a section across a page. */
@media print {
  .backdrop, .masthead, .scroll-next, .guides { display: none !important; }
  .hero { min-height: 0; padding: 0 0 32px; }
  .plate { min-height: 0; padding-top: 0; padding-bottom: 32px; }
  .plate, .tier, .slot { break-inside: avoid; }
  .plate { padding-left: 0; padding-right: 0; }
  .foot { margin: 0; }
}

/* ── /enter · the gate ───────────────────────────────────────────────────────
   The only page a stranger can reach (web/functions/_middleware.js). Same language as page one
   and the feedback form — Mono on paper, hairline fields, Orange on the one action — but no map:
   map.js, the vendor bundle and the style JSON all sit behind the gate, and the point of the page
   is that nothing behind it leaks. One column, left-aligned, centred on the screen. */
.gate {
  min-height: 100dvh;
  display: grid;
  place-items: center;
  /* Clear of the lockup in its corner, which is fixed and would otherwise sit on the copy. */
  padding: 112px max(32px, env(safe-area-inset-left)) 48px;
}
.gate__block { width: 100%; max-width: 420px; }
.gate__form { display: flex; flex-direction: column; gap: 20px; margin-top: 24px; }
/* `display: flex` beats the UA's `[hidden] { display: none }`, so each state has to say it. */
.gate__form[hidden] { display: none; }
.gate__body {
  margin: 0;
  font-family: 'Courier New', Courier, monospace;
  font-size: 14px; line-height: 21px; color: #4d4d4d;
}
.gate__who { color: #FF6A06; } /* the address is the thing to check for a typo */
.gate__hint {
  margin: -4px 0 0;
  font-family: 'Courier New', Courier, monospace;
  font-size: 12px; line-height: 18px; color: #9CA3AF;
}
/* A quiet inline action — a word in a sentence, not a control. */
.linkish {
  padding: 0;
  font: inherit;
  color: #4d4d4d;
  background: none; border: 0;
  text-decoration: underline; text-underline-offset: 2px;
  cursor: pointer;
  appearance: none; -webkit-appearance: none;
}
.linkish:active { opacity: 0.7; }
/* The gate reads top-down in one column, so its action sits under the field it completes rather
   than out at the right margin the way the wide feedback form's does. */
.gate .send { align-self: flex-start; }
