/*
 * Design five: every section, in the app's own order, with the mosaic off the
 * end of it.
 *
 * Same visual language as design three, different spine. Three argues that this
 * is a piece of software worth opening, and does it by opening with the
 * software. Five has to make a narrower claim on top of that one, which is the
 * owner's: **every control in here was written for the Sun, and they are all in
 * the same program**. That claim cannot be made in adjectives. It is made by
 * showing a tool twice.
 *
 * It used to be seven, one entry per tool the owner named first, then eight
 * once Marks was added because seven read as the whole app when the panel
 * holds twenty-one sections. Spectral Mapping made it nine, and everything
 * else sat in a roster numbered straight on from those nine, 09 to 22 sharing
 * one run with 01 to 08's own numbers, both self-numbered rather than
 * borrowed from the app.
 *
 * **Restructured again on 2026-09-12, and this time the fix reaches the
 * numbers themselves rather than just where the boundary between groups
 * falls.** The owner's complaint: the page still numbered Structure Contrast
 * 01, when the app itself labels that section 6, `stepOf()`'s own number in
 * `src/lib/sections.ts`; the mosaic sat inside that numbered run, between
 * Marks and the roster, as though it were a step rather than a room the app
 * enters and leaves; and the fourteen sections that were not one of the nine
 * deserved their own entry rather than a smaller one in a separate grid,
 * since the owner was clear every control here, not just the nine, was tuned
 * on his own frames. So there is exactly one numbering left on this page, the
 * app's own `stepOf()`, and exactly one order, the app's own `PHASES`: five
 * phase headings, Stretch through Setup, each holding its sections in the
 * order the app holds them, full entries and lighter ones threaded through
 * together rather than sorted into two separate bands. The mosaic comes after
 * all five, carries no number, and says in its own heading that it is not a
 * step. `tests/checks/siteSectionCount.ts` now asserts the numbers and the
 * order against `stepOf()` and `PHASES` directly, rather than only checking
 * that a hand-typed total agreed with itself.
 *
 * ## The two pictures, and why both are needed
 *
 * Nine of the twenty-one sections, plus the mosaic room, carry a photograph of
 * their own controls in the app and a before and after of what they did to a
 * real capture; the rest carry the panel photograph alone, since there is no
 * single wipe that proves a crop tool or a phase rail the way a dragged seam
 * proves a sharpener. The mosaic has no single frame to put on either side of
 * a seam and gets two steps of one run instead.
 *
 * The before and after alone would be a page of results, which any sharpener
 * can produce and which says nothing about who the tool was written for. The
 * panel photograph is the half that carries the claim: a section headed
 * "Scattered Light" whose readout says the sky is uneven by 8.7 levels of 255,
 * or one headed "Limb & Prominences" that reports how well constrained its
 * circle fit is, is visibly not a general purpose filter with a solar label on
 * it. Neither picture argues the point on its own.
 *
 * The two are kept honest by construction rather than by care:
 * `site/tools/render-plates.ts --write-preset` writes the exact settings that
 * made the before and after, and `site/tools/shoot-app.mjs` loads that same file
 * before photographing the panel. The numbers on the sliders are the numbers
 * that made the picture beside them.
 *
 * ## Two things the copy may not say
 *
 * Both are the owner's, and both are in `site/README.md` as well because they
 * outlive this page. **No claim that nothing else has any one of these tools**:
 * the claim is that they were written for solar work and that they are in one
 * package, which is true and checkable, where the other is neither. Design three
 * carries a heading that says four of them exist nowhere else; it is not copied
 * here. And **the stitcher is never compared to any named program**.
 *
 * ## Inherited from design three, deliberately
 *
 * The tokens, the call to action, the hero and the comparison control are
 * design three's, transcribed rather than shared. `../assets/identity.css` is
 * the file all four designs are built from and none of them owns; a second
 * shared layer between three and four would make the two impossible to change
 * independently, which is the whole point of having both. Where a rule here is
 * a copy, its comment in `../three/three.css` is the longer version.
 */

/* --- Tokens ------------------------------------------------------------------- */

body.five {
  /* Warm charcoal rather than the identity's cool `#0c0c0e`. */
  --ground: #131010;
  --surf-1: #1a1614;
  --surf-2: #221c19;

  /* The accent, sampled from the app rather than chosen: `--flare` is the
     colour of the app's own logo tile and slider thumbs, `--amber` the top of
     the Solar Gold ramp and `--ember` the bottom. */
  --amber: #ffc266;
  --flare: #f5641a;
  --ember: #a83c06;

  --paper: #f5f0e8;
  --paper-2: #e9e1d3;

  --radius: 14px;
  --radius-sm: 10px;

  background: var(--ground);
}

.five .nav a:hover,
.five .nav a:focus-visible {
  color: var(--amber);
  border-bottom-color: var(--amber);
}

/* --- Type ---------------------------------------------------------------------- */

.hero__title {
  font-size: clamp(2rem, 3.4vw, 3.25rem);
  line-height: 1.05;
  letter-spacing: -0.026em;
  font-weight: 600;
  color: var(--ink-100);
  margin: 0;
  text-wrap: balance;
}

.h2 {
  font-size: clamp(1.5rem, 2.5vw, 2.2rem);
  line-height: 1.14;
  letter-spacing: -0.019em;
  font-weight: 600;
  color: var(--ink-100);
  margin: 0.7rem 0 0.9rem;
  max-width: 24ch;
  text-wrap: balance;
}

.eyebrow {
  font-family: var(--mono);
  font-size: 0.6875rem;
  line-height: 1.4;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-44);
  font-weight: 400;
  margin: 0;
}

.eyebrow--accent {
  color: var(--amber);
}

.five .lede {
  color: var(--ink-70);
}

/* --- The call to action --------------------------------------------------------
 *
 * One of these above the fold and one at the close, and they are the same
 * control. Nothing else on the page is a filled rectangle, including in the
 * masthead.
 */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  padding: 0.9rem 1.6rem;
  border-radius: 999px;
  background: linear-gradient(180deg, #ffb14a 0%, #f5641a 62%, #e04e0c 100%);
  color: #1d0d02;
  font-family: var(--sans);
  font-weight: 600;
  font-size: 1.0625rem;
  letter-spacing: -0.006em;
  text-decoration: none;
  box-shadow:
    0 12px 34px -10px rgb(245 100 26 / 0.55),
    inset 0 1px 0 rgb(255 255 255 / 0.38);
  transition: transform 0.18s ease, box-shadow 0.18s ease, filter 0.18s ease;
}

.btn:hover,
.btn:focus-visible {
  transform: translateY(-1px);
  filter: brightness(1.06);
  box-shadow:
    0 18px 44px -12px rgb(245 100 26 / 0.7),
    inset 0 1px 0 rgb(255 255 255 / 0.45);
}

/*
 * `font-weight: 400` here is worth 14.8 kB and is not a taste decision. IBM Plex
 * Mono is self-hosted as two static faces; the button's own 600 was inherited by
 * this arrow, resolved to the 500 face, and pulled a second mono file onto the
 * first screen. Nothing else on the page asks for mono above 400.
 */
.btn__arrow {
  font-family: var(--mono);
  font-weight: 400;
  font-size: 0.9em;
  translate: 0 -0.02em;
}

.proof {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  line-height: 1.5;
  color: var(--ink-44);
  margin: 0;
  max-width: 30rem;
}

/*
 * The price, and the second, quieter call to action beside it. Added for the
 * trial launch, 23 September 2026 (`PRICING.md` 18.2). `.btn` stays exactly
 * twice on this page, hero and close, both pointed at the trial; a second
 * filled rectangle for "buy" would break the one rule the call to action
 * comment above already states, so the buy link takes the underline treatment
 * `.signup button` used for its filled form, which the buy link now replaces.
 */
.price {
  font-family: var(--mono);
  font-size: 0.9375rem;
  letter-spacing: 0.02em;
  color: var(--ink-84);
  margin: 0;
}

.hero__buttons {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  flex-wrap: wrap;
}

.btn-secondary {
  display: inline-flex;
  align-items: center;
  color: var(--ink-84);
  font-family: var(--sans);
  font-weight: 500;
  font-size: 1rem;
  text-decoration: none;
  border-bottom: 1px solid var(--ink-24);
  padding-bottom: 0.2rem;
  transition: color 0.18s ease, border-color 0.18s ease;
}

.btn-secondary:hover,
.btn-secondary:focus-visible {
  color: var(--amber);
  border-bottom-color: var(--amber);
}

/* 2026-09-25: "Buy it now" measures about 30px tall on a phone, under the
   44px a thumb can reliably hit; min-height grows the tap area without
   touching the text size or colour already set above. */
@media (max-width: 62rem) {
  .btn-secondary {
    min-height: 44px;
  }
}

/* --- The hero -------------------------------------------------------------------
 *
 * `overflow: hidden` is load-bearing rather than tidy: the glow and, on a phone,
 * the app window itself both extend past the right edge, and without it the
 * document scrolls sideways.
 */

.hero {
  position: relative;
  overflow: hidden;
  padding: clamp(1.5rem, 3vw, 3rem) var(--gutter) clamp(3rem, 5vw, 4.5rem);
}

.hero__glow {
  position: absolute;
  inset: -20% -10% auto -10%;
  height: 130%;
  pointer-events: none;
  background:
    radial-gradient(46% 42% at 68% 38%, rgb(245 100 26 / 0.24), transparent 72%),
    radial-gradient(90% 60% at 50% 0%, rgb(255 194 102 / 0.08), transparent 68%);
}

.hero__grid {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 0.86fr) minmax(0, 1.14fr);
  gap: clamp(2rem, 4vw, 4rem);
  align-items: center;
  max-width: 84rem;
  margin: 0 auto;
}

.hero__copy > * + * {
  margin-top: 1.25rem;
}

.hero__lede {
  font-size: clamp(1rem, 1.15vw, 1.1875rem);
  line-height: 1.55;
  color: var(--ink-70);
  max-width: 32rem;
  margin: 0;
  text-wrap: pretty;
}

.hero__act > * + * {
  margin-top: 1rem;
}

/* --- The app window ---------------------------------------------------------------
 *
 * `app-panel` is a 2000 by 1258 photograph of the running program, and the three
 * numbers below are measured rather than eyeballed. The app's image area sits at
 * x 24, y 216, 1353 by 931, found by walking the decoded pixels for the step
 * between the app's own background and the canvas; the percentages are that
 * rectangle over the full frame, so they hold at every page width. The shot's
 * last 30 columns are the app's own panel scrollbar, a white thumb on a grey
 * track and otherwise the brightest thing in the hero; `2000 / 1968` pushes them
 * past the clip rather than encoding a second copy of a 95 kB picture.
 */

.app {
  position: relative;
  z-index: 0;
  margin: 0;
}

/* Behind the window, not around it: a glow drawn on the window itself would ring
   its rounded corners. */
.app::before {
  content: '';
  position: absolute;
  inset: 8% -8% -14% -8%;
  z-index: -1;
  background: radial-gradient(50% 50% at 50% 50%, rgb(245 100 26 / 0.42), rgb(245 100 26 / 0) 70%);
  filter: blur(38px);
}

.app__win {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius);
  box-shadow:
    0 60px 110px -40px rgb(0 0 0 / 0.9),
    0 18px 40px -20px rgb(0 0 0 / 0.6),
    0 0 0 1px rgb(255 255 255 / 0.07);
}

.app__inner {
  position: relative;
  width: 101.626%;
}

.app__shot {
  width: 100%;
  height: auto;
}

.screen {
  position: absolute;
  left: 1.2%;
  top: 17.17%;
  width: 67.65%;
  height: 74.006%;
  display: grid;
  overflow: hidden;
}

/*
 * The grid item is the `<picture>`, not the `<img>`. Both frames are served as
 * AVIF with a WebP fallback so both are wrapped, and putting `grid-area` on the
 * image stacks the two wrappers down the page instead of on top of each other.
 */
.screen > picture {
  grid-area: 1 / 1;
  display: block;
}

/*
 * `height: 100%` overrides the identity's `height: auto`, which is load-bearing
 * everywhere else on the site. It is safe here and only here: the box is sized
 * in per cent of the shot rather than by the picture, so there is no layout jump
 * to protect against.
 */
.screen {
  background: #05070c;
}

/*
 * `contain` rather than `cover`, changed for design five. The box is the app's
 * own picture area measured off the screenshot and is about 1.45 to 1, while the
 * owner's opening pair is 2 to 1: he cropped that frame top and bottom himself
 * and asked for the same crop here and on Structure Colour. `cover` would fill
 * the box by throwing about a quarter of the width away, which is the crop he
 * chose being undone silently. `contain` keeps every pixel he kept and lets the
 * frame letterbox inside the viewport, which is what the app itself does with a
 * frame wider than its window, so the screenshot stays a picture of the program
 * rather than a composition. `.screen`'s own background is what shows in the
 * band above and below.
 */
.screen img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/*
 * The wipe. Two keyframe sets rather than one registered `@property`, because
 * the property is the tidier way to do this and is the one part of it some
 * browsers still will not interpolate. Same duration, same easing, same start,
 * so they stay in step on the document timeline.
 */
@keyframes screen-wipe {
  0%,
  8% {
    clip-path: inset(0 0 0 92%);
  }
  42%,
  58% {
    clip-path: inset(0 0 0 8%);
  }
  92%,
  100% {
    clip-path: inset(0 0 0 92%);
  }
}

@keyframes screen-seam {
  0%,
  8% {
    left: 92%;
  }
  42%,
  58% {
    left: 8%;
  }
  92%,
  100% {
    left: 92%;
  }
}

.screen__after {
  clip-path: inset(0 0 0 50%);
  animation: screen-wipe 11s ease-in-out infinite;
}

.screen__seam {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 2px;
  margin-left: -1px;
  background: linear-gradient(180deg, rgb(255 210 150 / 0.55), rgb(255 255 255 / 0.92) 50%, rgb(255 210 150 / 0.55));
  box-shadow: 0 0 14px rgb(255 140 50 / 0.75);
  animation: screen-seam 11s ease-in-out infinite;
}

/* A marker, not a handle: this seam is not dragged and a circle would invite a
   drag that does nothing. The comparisons below carry circles, and are. */
.screen__seam::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0.55rem;
  height: 0.55rem;
  translate: -50% -50%;
  rotate: 45deg;
  background: var(--amber);
  box-shadow: 0 0 12px rgb(255 160 60 / 0.9);
}

.screen__end {
  position: absolute;
  bottom: 3.5%;
  font-family: var(--mono);
  font-size: clamp(0.5rem, 0.62vw, 0.6875rem);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgb(255 255 255 / 0.86);
  background: rgb(12 9 8 / 0.62);
  border: 1px solid rgb(255 255 255 / 0.14);
  border-radius: 999px;
  padding: 0.28em 0.75em;
  backdrop-filter: blur(3px);
  white-space: nowrap;
}

.screen__end--before {
  left: 2.6%;
}

.screen__end--after {
  right: 2.6%;
}

/* 2026-09-25: the clamp above computes to 8px at 375px wide, too small to
   read; raise only the floor, on narrow screens, to 0.75rem (12px). */
@media (max-width: 62rem) {
  .screen__end {
    font-size: 0.75rem;
  }
}

.app figcaption {
  font-family: var(--mono);
  font-size: 0.72rem;
  line-height: 1.5;
  letter-spacing: 0.02em;
  color: var(--ink-44);
  padding-top: 0.9rem;
  max-width: 42rem;
}

@media (prefers-reduced-motion: reduce) {
  .screen__after,
  .screen__seam {
    animation: none;
  }

  .btn {
    transition: none;
  }

  .btn:hover,
  .btn:focus-visible {
    transform: none;
  }
}

/* --- The hero on a narrow screen ---------------------------------------------------
 *
 * `display: contents` promotes the copy's children to grid items so the app can
 * be interleaved between them: what it is, then the program, then the button,
 * and the supporting paragraph last. The window is 142.35% wide, which maps
 * about 1405 of the shot's 2000 columns onto a phone and is the difference
 * between a readable application window and a smudge. It bleeds off the right
 * edge, which hides the settings panel on a phone. 2026-09-25: bleeding off the
 * left instead was built and measured, and it kept the panel but left only
 * 27 px of the Stacked half of the before and after on screen, which is the
 * picture's whole point; so the caption was made to stop pointing at the
 * panel instead. The page still never scrolls sideways, because `.hero`
 * clips.
 */
@media (max-width: 62rem) {
  .hero__grid {
    grid-template-columns: minmax(0, 1fr);
    gap: 1.15rem;
    align-items: start;
  }

  .hero__copy {
    display: contents;
  }

  .hero__copy > .eyebrow {
    order: 1;
  }

  .hero__title {
    order: 2;
  }

  .app {
    order: 3;
    margin-top: 0.6rem;
    margin-right: calc(-1 * var(--gutter));
  }

  .app__inner {
    width: 142.35%;
  }

  .app__win {
    border-radius: var(--radius) 0 0 var(--radius);
  }

  .app figcaption {
    padding-right: var(--gutter);
  }

  .hero__act {
    order: 4;
    margin-top: 0.6rem;
  }

  .hero__lede {
    order: 5;
    max-width: none;
  }

  .hero__copy > * + * {
    margin-top: 0;
  }
}

/* --- Bands ------------------------------------------------------------------------- */

.band {
  position: relative;
  background: var(--surf-1);
  padding: clamp(3.5rem, 7vw, 6rem) var(--gutter);
}

.band--index {
  background: var(--surf-1);
}

.band--tools {
  background: var(--ground);
  padding: 0 var(--gutter) clamp(3rem, 6vw, 5rem);
}

.wrap {
  max-width: 74rem;
  margin: 0 auto;
}

/* --- The index ----------------------------------------------------------------------
 *
 * Twenty-two rows, all twenty-one sections and the mosaic, is the longest
 * single screen on the page, and it is the thing the owner explicitly asked
 * for: a list of every tool in the app's own order before the scroll that
 * proves any of them. A visitor who does not read past it still leaves with
 * an accurate map of the panel, numbered the way their own copy of the app
 * would number it.
 *
 * An `<ol>` keeps its 40 px marker indent with the markers switched off, which
 * pulls the whole block in from the gutter and misaligns it against every other
 * section on the page. Measured at 375 px on design two: 295 wide against 335.
 */

.index {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 1px;
  margin: clamp(2rem, 4vw, 2.75rem) 0 0;
  padding: 0;
  list-style: none;
  background: rgb(255 255 255 / 0.06);
  border: 1px solid rgb(255 255 255 / 0.06);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.index > li {
  list-style: none;
  background: var(--surf-2);
}

.index a {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  padding: 0.95rem 1.1rem;
  text-decoration: none;
  color: var(--ink-84);
  transition: background 0.16s ease, color 0.16s ease;
}

.index a:hover,
.index a:focus-visible {
  background: rgb(255 194 102 / 0.08);
  color: var(--ink-100);
}

/* The number, name and phase stay one baseline-aligned row; the description
   added below each entry sits under it rather than crowding onto the same
   line, which is why `.index a` switched from a row to a column. */
.index__row {
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
}

.index__num {
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  color: var(--amber);
  flex: 0 0 auto;
}

.index__name {
  font-weight: 600;
  font-size: 0.9375rem;
  letter-spacing: -0.006em;
  flex: 1 1 auto;
}

.index__where {
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-44);
  flex: 0 0 auto;
}

/* 2026-09-25: 0.625rem is 10px, too small to read at 375px wide; raise it
   on narrow screens only, the row's flexible `.index__name` absorbs the
   extra width. */
@media (max-width: 62rem) {
  .index__where {
    font-size: 0.75rem;
  }
}

/* One line of what the tool does, in the same size and colour the interface
   tiles use for their own copy, so the two lists read as one voice. */
.index__note {
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--ink-44);
}

/* --- One tool ------------------------------------------------------------------------
 *
 * Twenty-one of these, one per section, and their regularity is the argument:
 * the same shape twenty-one times reads as a catalogue of things that were
 * all built the same way, where twenty-one different treatments would read as
 * twenty-one separate sales pitches. `.tool--full` (a panel photograph and a
 * before and after) and `.tool--simple` (a panel photograph alone, lighter
 * padding, a smaller heading) are the one variation the page allows, because
 * what a single picture can prove genuinely differs between them; nothing
 * else about the shape does.
 *
 * One hairline at the top of each, which is the site's rule about rules: a
 * register line, the way a plate ledger has them, and nothing else may draw one.
 */

.tool {
  padding-top: clamp(2.5rem, 5vw, 4rem);
  margin-top: clamp(2.5rem, 5vw, 4rem);
  border-top: 1px solid rgb(255 255 255 / 0.08);
}

.tool__head {
  max-width: 46rem;
}

.tool__name {
  font-size: clamp(1.375rem, 2vw, 1.75rem);
  font-weight: 600;
  letter-spacing: -0.014em;
  line-height: 1.15;
  color: var(--ink-100);
  margin: 0.5rem 0 0.7rem;
}

.tool__copy {
  font-size: 1rem;
  line-height: 1.6;
  color: var(--ink-70);
  margin: 0;
  text-wrap: pretty;
}

/*
 * Raised from 0.75rem for design five. This is the only running prose on the
 * page a reader was asked to take in at 12 px, and it carries the explanation of
 * why the two rooms have no before and after, which is exactly the sentence a
 * visitor needs. The mono face and the quieter ink still mark it as an aside.
 */
.tool__note {
  font-family: var(--mono);
  font-size: 0.875rem;
  line-height: 1.6;
  letter-spacing: 0.02em;
  color: var(--ink-44);
  margin: 0.9rem 0 0;
  max-width: 42rem;
}

/*
 * The panel photograph on the left and the before and after on the right, in
 * that order and never the reverse.
 *
 * 23rem holds the panel plate at 368, a hair under the 373 it was photographed
 * at, so it is never enlarged; the picture beside it is a 900 px render shown at
 * about 780, which is a reduction. This page argues about fine texture and
 * nothing on it may be shown past its native size, because interpolation removes
 * the exact thing being argued about before the visitor ever sees it.
 */
.tool__pics {
  display: grid;
  grid-template-columns: minmax(0, 23rem) minmax(0, 1fr);
  gap: clamp(1.5rem, 3vw, 2.5rem);
  align-items: start;
  margin-top: clamp(1.5rem, 3vw, 2.25rem);
}

@media (max-width: 62rem) {
  .tool__pics {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* --- The panel plate -------------------------------------------------------------------
 *
 * A photograph of one section of the app's own panel, at the settings that made
 * the picture beside it.
 *
 * **The frame is a scroll box and that is deliberate.** Three of these sections
 * are between 1050 and 1310 CSS pixels tall, because they genuinely have that
 * many controls in them: Field Correction is three groups, Structure Colour is
 * four. Shrinking those to fit would make their 11 px labels unreadable, which
 * destroys the one thing the picture is here to show, and letting them run full
 * height would leave a thousand pixels of nothing beside the picture opposite.
 * So the plate scrolls, which is what the panel itself does in the app, and the
 * caption says so.
 */

.panel {
  margin: 0;
}

.panel__frame {
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: #0a0a0a;
  box-shadow: 0 0 0 1px rgb(255 255 255 / 0.07), 0 24px 50px -30px rgb(0 0 0 / 0.9);
}

.panel__frame--tall {
  max-height: 34rem;
  overflow-y: auto;
  /* A soft edge at the bottom would be prettier and would also hide a control.
     A hairline says "there is more" without covering anything. */
  border-bottom: 1px solid rgb(255 194 102 / 0.28);
  /* The app's own panel scrollbar was the brightest thing in design three's
     hero and had to be cropped out of the picture. This is the same scrollbar
     one layer up, so it gets the same treatment by other means. */
  scrollbar-width: thin;
  scrollbar-color: rgb(255 194 102 / 0.32) transparent;
}

.panel__frame--tall::-webkit-scrollbar {
  width: 8px;
}

.panel__frame--tall::-webkit-scrollbar-thumb {
  background: rgb(255 194 102 / 0.32);
  border-radius: 999px;
}

.panel__frame--tall::-webkit-scrollbar-track {
  background: transparent;
}

/* On a phone the panel plate is the full column, so a 544 px window is most of
   the screen and reads as the page having stopped. Shorter, so the page around
   it stays visible while it is being scrolled. */
@media (max-width: 62rem) {
  .panel__frame--tall {
    max-height: 26rem;
  }
}

.panel__frame img {
  width: 100%;
  display: block;
}

.panel figcaption,
.plate figcaption {
  font-family: var(--mono);
  font-size: 0.71rem;
  line-height: 1.55;
  letter-spacing: 0.02em;
  color: var(--ink-44);
  padding-top: 0.8rem;
}

.plate {
  margin: 0;
}

.plate__index {
  display: block;
  color: var(--amber);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-size: 0.625rem;
  margin-bottom: 0.35rem;
}

.plate > picture {
  display: block;
  border-radius: var(--radius-sm);
  overflow: hidden;
  box-shadow: 0 0 0 1px rgb(255 255 255 / 0.06), 0 24px 50px -30px rgb(0 0 0 / 0.9);
}

.plate img {
  width: 100%;
}

/* --- The before and after control -------------------------------------------------------
 *
 * The real control is a transparent `<input type="range">` over the whole
 * picture. That buys keyboard operation, a focus ring and a screen reader
 * announcement for nothing, where a div with pointer handlers would have bought
 * a fortnight of accessibility work and got it wrong. With JavaScript off the
 * clip sits at its CSS default and the visitor gets a static half and half,
 * which is a worse control and a true picture.
 *
 * No `line-height: 0` here even though the reflex is to add one: images are
 * already `display: block` in the reset so there is no inline gap, and the zero
 * collapses the caption on top of itself.
 */

.compare {
  position: relative;
  --pos: 50%;
}

.compare__stack {
  position: relative;
  display: grid;
  border-radius: var(--radius-sm);
  overflow: hidden;
  box-shadow: 0 0 0 1px rgb(255 255 255 / 0.06), 0 24px 50px -30px rgb(0 0 0 / 0.9);
}

.compare__stack > picture {
  grid-area: 1 / 1;
  display: block;
}

.compare__stack img {
  width: 100%;
}

.compare__after {
  clip-path: inset(0 0 0 var(--pos));
}

.compare__seam {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--pos);
  width: 1px;
  background: rgb(255 255 255 / 0.8);
  pointer-events: none;
}

.compare__seam::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 2.5rem;
  height: 2.5rem;
  translate: -50% -50%;
  border: 1px solid rgb(255 220 180 / 0.8);
  border-radius: 50%;
  background: rgb(19 16 16 / 0.32);
  backdrop-filter: blur(2px);
}

.compare__range {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: ew-resize;
  appearance: none;
  background: transparent;
}

.compare__range:focus-visible + .compare__seam::after {
  border-color: var(--amber);
  border-width: 2px;
}

/* --- Phases ---------------------------------------------------------------------------
 *
 * Five headings, one per `PHASES` entry in `src/lib/sections.ts`, in the app's
 * own order rather than the page's former argument order. The label and the
 * blurb under it are copied from that file rather than reworded, because they
 * are already good copy in the app's own voice and a second version invites the
 * two to drift. A phase heading carries no number: the number belongs to a
 * section, `stepOf()`'s own comment says so, and a phase is not one.
 */
.phase__head {
  max-width: 42rem;
  padding-top: clamp(2.5rem, 5vw, 4rem);
  margin: 0 0 0.25rem;
}

/* Tightens the gap between a phase's own intro and its first entry, whose
   hairline and `.tool` spacing would otherwise double the padding above.
   Only the first `.tool` after a phase heading is targeted; every later one
   in the same phase keeps the full `.tool` rhythm. */
.phase__head + .tool {
  margin-top: clamp(1rem, 2.5vw, 1.75rem);
}

.phase__label {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--amber);
  font-weight: 400;
  margin: 0 0 0.6rem;
}

.phase__blurb {
  font-size: 1rem;
  line-height: 1.55;
  color: var(--ink-70);
  margin: 0;
  max-width: 36rem;
}

/* --- A section with no before and after ------------------------------------------------
 *
 * Eight of the twenty-one, as of 2026-09-15 (it was thirteen before the
 * owner's own pairs promoted five more to full blocks): a photograph of the
 * section's own controls and nothing else, because there is no single wipe
 * that proves a crop tool or a phase rail the way a dragged seam proves a
 * sharpener. Lighter padding and a smaller heading than `.tool`'s default
 * keep these reading as the plainer claim they are, next to the thirteen
 * that carry a before and after.
 *
 * The article itself is the two column grid: the copy beside its photograph,
 * not stacked above a photograph sized for half the row. `.tool__head` stays
 * first in the markup, so a reader relying on document order still meets the
 * heading and the copy before the picture, the same order every full entry
 * already uses; `order: -1` on `.tool__pics` only moves where the photograph
 * is *drawn*, into the same 23rem left column the panel photograph occupies
 * in every full entry, so a photograph sits in the same place on the page
 * whether its entry is full or simple.
 */
.tool--simple {
  display: grid;
  grid-template-columns: minmax(0, 23rem) minmax(0, 1fr);
  column-gap: clamp(1.5rem, 3vw, 2.5rem);
  align-items: start;
  padding-top: clamp(1.5rem, 3vw, 2.25rem);
  margin-top: clamp(1.5rem, 3vw, 2.25rem);
}

.tool--simple .tool__pics {
  order: -1;
  margin-top: 0;
}

.tool--simple .tool__name {
  font-size: clamp(1.125rem, 1.6vw, 1.375rem);
}

.tool--simple .tool__copy {
  font-size: 0.9375rem;
}

/* A panel photograph on its own, with no before and after beside it. Same
   23rem column the full entries use for their own panel photograph, so every
   photograph on the page lines up at one width regardless of which kind of
   entry it belongs to. */
.tool__pics--solo {
  grid-template-columns: minmax(0, 23rem);
}

/* Below the width `.tool__pics` itself collapses at (62rem, the same
 * breakpoint), a side-by-side row has no room left for two columns: stack the
 * photograph under the copy instead, in document order, the same
 * head-then-picture order every full entry falls back to at this width.
 *
 * `.tool__pics--solo`'s own 23rem cap is deliberately left alone here rather
 * than widened to match: a full entry's stacked photographs earn the extra
 * width, a before and after that is the whole argument of its section, but a
 * simple entry's photograph is only ever the panel screenshot, and stretching
 * it toward the full column undid most of the page-height saving `panel__frame--tall`
 * below exists for, by roughly doubling nine of the thirteen photographs that
 * were never tall to begin with. Measured at 810px: 30755px with the stretch,
 * 27787px without, against 29331px before this article carried any of it.
 * **Stale as of 2026-09-15**: five of those thirteen simple entries were
 * promoted to full blocks (see the comment above), which leaves eight simple
 * entries, only two of them (`export`, `scale`) tall. The reasoning still
 * holds; these three pixel counts do not, and want a fresh measurement in the
 * running page rather than a guess from here.
 */
@media (max-width: 62rem) {
  .tool--simple {
    grid-template-columns: minmax(0, 1fr);
  }

  .tool--simple .tool__pics {
    order: 0;
    margin-top: clamp(1.5rem, 3vw, 2.25rem);
  }
}

/* --- Off the run ------------------------------------------------------------------------
 *
 * The mosaic stitcher is a room the app enters and leaves, not a step, so it
 * sits after every phase rather than inside one and carries no number. This
 * heading is the one on the page that is not a phase, and says so in a full
 * sentence: the section that used to sit here read its position as the whole
 * argument and was misread as a step in the sequence once already.
 *
 * Its own hairline, full width rather than clipped to the text column below
 * it: the same register-line device every `.tool` uses at its own top, marking
 * this as a new beat rather than a continuation of Setup just above it.
 */
.offrun {
  border-top: 1px solid rgb(255 255 255 / 0.08);
  padding-top: clamp(3rem, 6vw, 4.5rem);
  margin-top: clamp(2.5rem, 5vw, 4rem);
}

.offrun > * {
  max-width: 44rem;
}

.offrun > * + * {
  margin-top: 0.6rem;
}

/* The mosaic `.tool` that follows would otherwise draw a second hairline of
   its own right under the one above, `.tool`'s default rather than anything
   this entry needs: `.offrun` already opened the beat. */
.offrun + .tool {
  border-top: 0;
  padding-top: clamp(1.5rem, 3vw, 2.25rem);
  margin-top: 0;
}

/* --- The mosaic ---------------------------------------------------------------------------
 *
 * The mosaic room has no seam, because there is no single frame to put on the
 * other side of one: its "before" is ten files in a folder. Two full width
 * pictures of the same run instead, and the copy says that is what they are
 * rather than dressing them as a comparison.
 *
 * These are the only pictures on the page allowed the full 74rem wrap. They are
 * 2528 px wide and shown at about 1184, so the reduction is a little over two to
 * one and the app's own 11 px labels survive it.
 */

/*
 * `minmax(0, 1fr)` rather than the `1fr` that reads the same, and the difference
 * is the whole page scrolling sideways.
 *
 * A grid item's `min-width` resolves to `auto`, which is its min-content size,
 * so the moment the narrow-screen rule below gives the picture inside a fixed
 * 60rem width the track grows to fit it and takes the document with it. Measured
 * at a 375 px viewport: `scrollWidth` 981. `min-width: 0` on the item is the
 * other half of the same fix.
 */
.tool--wide .tool__steps {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(2rem, 4vw, 3rem);
  margin-top: clamp(1.5rem, 3vw, 2.25rem);
}

.tool--wide .tool__steps > * {
  min-width: 0;
}

.plate--room figcaption {
  max-width: 52rem;
}

.plate__hint {
  display: none;
}

/*
 * The two room pictures on a narrow screen.
 *
 * Everything else on this page shrinks honestly: a photograph of the Sun at a
 * third of its size is still a photograph of the Sun. These two are pictures of
 * *text*, and a 2528 px window shown at 343 is not a smaller version of the app,
 * it is a grey rectangle. So they get a scroll container of their own, sized
 * where the report's own headline and its numbers are still legible, and the
 * caption says the picture scrolls. The page itself still never scrolls
 * sideways, which is the rule this is obeying rather than breaking.
 */
@media (max-width: 62rem) {
  .plate--room > picture {
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: thin;
    scrollbar-color: rgb(255 194 102 / 0.32) transparent;
  }

  .plate--room > picture img {
    width: 60rem;
    max-width: none;
  }

  .plate__hint {
    display: block;
    color: var(--amber);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    font-size: 0.625rem;
    margin-top: 0.4rem;
  }
}

/* --- Marks used to be two steps here, and is a wipe as of design five -------------------
 *
 * `.tool__steps--marks` sized two stacked exports. They were steps rather than a
 * wipe because the marked one was 148 px taller, carrying its key as a strip
 * beneath the picture, so a seam would have clipped one half or jumped. The
 * owner's own export replaced both halves on 2026-09-16 and places the key as a
 * box inside the frame, so the two are 1584x1072 apiece and that block now uses
 * the ordinary `.compare` seam with no sizing of its own. The rule is gone and
 * the reasoning is kept, because a later reader would otherwise rediscover the
 * height problem rather than read that it went away.
 */

/* --- The detection plate, a second slider inside one entry ------------------------------
 *
 * Structure Contrast and Fibril Enhance each carry a second wipe beneath the
 * first, showing the app's own Show detection overlay. Two things about it are
 * not obvious from the markup.
 *
 * It is placed in the right hand column explicitly rather than by flow, because
 * `.tool__pics` is two columns and a third child would otherwise land under the
 * panel photograph at 23rem wide. Row two, column two leaves column one empty,
 * which is wanted: the panel photograph is tall already and has nothing to add
 * beside this.
 *
 * And it is deliberately shorter than the plate above it. These are photographs
 * of the app's viewport at about 1.46 to 1, against the 2 to 1 the rendered
 * pairs use, so at full height two boxes would make these two entries twice the
 * height of every other one on the page. The owner asked for them narrow for
 * that reason. The crop lives here and never in the file, because the file is
 * the evidence for what the overlay drew.
 */
.compare--detect {
  grid-column: 2;
}

/*
 * The gap under the first plate, which was wrong and looked like a stylesheet
 * fault rather than a grid one. `.tool__pics` is a two column grid with
 * `align-items: start`, so row one is as tall as its tallest cell, and that is
 * the panel photograph at 946 px against the plate's 388. The detection plate,
 * placed in row two, therefore started about 560 px below the picture it
 * belongs under, with nothing in between.
 *
 * Spanning the panel photograph across both rows makes row one the plate's own
 * height, so the second plate sits directly under the first. The row gap is cut
 * to a hair as well: these two are one exhibit, and the page's general gap is
 * sized to separate exhibits from each other.
 */
.tool__pics--detect .panel {
  grid-row: 1 / span 2;
}

.tool__pics--detect .compare--detect {
  grid-row: 2;
}

.tool__pics--detect {
  row-gap: clamp(0.6rem, 1vw, 0.9rem);
}

.compare--detect .compare__stack {
  aspect-ratio: 2.6 / 1;
}

.compare--detect .compare__stack img {
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* 2026-09-25: at one column the panel's explicit `grid-row: 1 / span 2` still
   claims row 2 of the only column, so the auto-placed panel gets pushed into
   an implicit second column instead of stacking, and the detection plate that
   is explicitly pinned to column 1 collapses to 0 width beside it. Clearing
   both rows lets the two stack in document order, which is what a single
   column means. */
@media (max-width: 62rem) {
  .compare--detect {
    grid-column: 1;
  }

  .tool__pics--detect .panel,
  .tool__pics--detect .compare--detect {
    grid-row: auto;
  }
}

/* --- Four abilities that are not sections at all ---------------------------------------
 *
 * Batch, Presets, Bypass and reset, Undo: no panel entry, no eyebrow number,
 * nothing for the twenty-one sections above to count. Restructured 2026-09-12
 * out from underneath a "plain lines" treatment that used to sit beside
 * Export and Performance, back when those two had no photograph of their own
 * and shared this lighter style for that reason; both now get the same full
 * `.tool` entry as every other section, so this group is the only thing left
 * that still needs a lighter line, and it needs one for a different reason:
 * it isn't a section at all. Own class names rather than reusing `.tool` or
 * `.index`, so `tests/checks/siteSectionCount.ts` can never read one of these
 * four as a section the page forgot a photograph for. A hairline above marks
 * it as its own closing beat after the mosaic, the same device `.tool` uses
 * for every entry above it.
 */
.notpanel__lede {
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--ink-58);
  border-top: 1px solid rgb(255 255 255 / 0.08);
  padding-top: clamp(2.5rem, 5vw, 4rem);
  margin: clamp(2.5rem, 5vw, 4rem) 0 0;
}

.notpanel {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1rem 2rem;
  margin: 0.9rem 0 0;
  padding: 0;
  list-style: none;
}

.notpanel__item--wide {
  grid-column: 1 / -1;
}

.notpanel__name {
  display: block;
  font-weight: 600;
  font-size: 0.875rem;
  color: var(--ink-84);
  margin: 0 0 0.25rem;
}

.notpanel__note {
  display: block;
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--ink-44);
  max-width: 42rem;
}

/* --- The light band ---------------------------------------------------------------------
 *
 * The phases, the mosaic and the four abilities above stay dark anyway,
 * photograph or no photograph: that whole stretch is one continuous
 * walkthrough of the panel rather than a new beat, and lighting any part of
 * it would read as a second highlight where the page wants only one. This
 * band is the one that gets the light treatment, and it can
 * afford to because it is genuinely the page's break: a solar frame's sky is
 * near-black and wants a dark ground to bleed into, and this is the first place
 * since the hero with nothing solar behind it. The ink tokens are redefined
 * rather than the rules rewritten, so every shared component inside the band
 * inverts without knowing it has.
 */

.band--light {
  background: linear-gradient(180deg, var(--paper) 0%, var(--paper-2) 100%);
  color: rgb(28 22 17 / 0.86);

  --ink: #1c1611;
  --ink-100: #14100c;
  --ink-84: rgb(28 22 17 / 0.86);
  --ink-70: rgb(28 22 17 / 0.72);
  /* 0.7 rather than 0.62: ink over paper at 0.62 measures about 4.3 to 1, which
     is under what body text wants, and 0.7 measures 5.1. */
  --ink-58: rgb(28 22 17 / 0.7);
  --ink-44: rgb(28 22 17 / 0.5);
  --ink-24: rgb(28 22 17 / 0.28);
  --ink-13: rgb(28 22 17 / 0.15);
  --ink-08: rgb(28 22 17 / 0.13);
}

/* Amber has no contrast on paper. The deep end of the same ramp measures about
   5.5 to 1 against `--paper`. */
.band--light .eyebrow--accent {
  color: var(--ember);
}

/* --- The workflow -------------------------------------------------------------------- */

.flow {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: clamp(1.5rem, 3vw, 2.5rem);
  margin: clamp(2rem, 4vw, 3rem) 0 0;
  counter-reset: flow;
  /* The same 40 px marker indent as `.index`, for the same reason. */
  padding: 0;
  list-style: none;
}

.flow > li {
  list-style: none;
  counter-increment: flow;
  border-top: 2px solid var(--ember);
  padding-top: 0.9rem;
}

.flow > li::before {
  content: '0' counter(flow);
  display: block;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.6875rem;
  letter-spacing: 0.16em;
  color: var(--ember);
  margin-bottom: 0.55rem;
}

.flow strong {
  display: block;
  font-weight: 600;
  font-size: 1.0625rem;
  color: var(--ink-100);
  margin-bottom: 0.3rem;
}

.flow p {
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--ink-58);
  margin: 0;
}

/* --- Trial and buying: one grid for the steps and the facts, 2026-09-26 -----------------
 *
 * `.flow` and `.notpanel` each ran their own `auto-fit` track count (13rem vs
 * 15rem minimums), so #trial's two lists broke into columns at different
 * widths: at 1300px the steps ran 4 columns against the notes' 3-plus-a-gap,
 * and around 790px the steps dropped to 3 (one step alone) against the notes'
 * 2. Fixed, shared column counts, scoped to #trial so `.flow` in "Where it
 * fits" and `.notpanel` in the abilities list above it keep their own auto-fit
 * behaviour untouched.
 */
#trial .flow,
#trial .notpanel {
  grid-template-columns: repeat(4, 1fr);
  column-gap: clamp(1.5rem, 3vw, 2.5rem);
}

#trial .notpanel {
  row-gap: 1rem;
  /* Same distance as the lede above the steps, so the two blocks read as
     separate rather than one long list. */
  margin-top: clamp(2rem, 4vw, 3rem);
}

/* "Buying and your key" is the longest note, so it takes the middle two
   columns; the other two sit one to a column, aligned under the steps. */
#trial .notpanel__item:nth-child(1) { grid-column: 1; }
#trial .notpanel__item:nth-child(2) { grid-column: 2 / 4; }
#trial .notpanel__item:nth-child(3) { grid-column: 4; }

/* Notes read as the same family as the steps: same heading and body size, a
   rule like the steps' but in the neutral ink rather than their ember accent,
   so buying facts stay distinct without looking like a different component. */
#trial .notpanel__item {
  border-top: 1px solid var(--ink-08);
  padding-top: 0.9rem;
}

#trial .notpanel__name {
  font-size: 1.0625rem;
  color: var(--ink-100);
}

#trial .notpanel__note {
  font-size: 0.9375rem;
  color: var(--ink-58);
}

@media (max-width: 62rem) {
  /* 2x2 rather than the 3-column middle step that left 04 alone. */
  #trial .flow,
  #trial .notpanel {
    grid-template-columns: repeat(2, 1fr);
  }

  /* The longest note takes its own full-width row; the other two share the
     row above it so no item is ever alone. */
  #trial .notpanel__item:nth-child(1) { grid-column: 1; grid-row: 1; }
  #trial .notpanel__item:nth-child(2) { grid-column: 1 / -1; grid-row: 2; }
  #trial .notpanel__item:nth-child(3) { grid-column: 2; grid-row: 1; }
}

@media (max-width: 34rem) {
  /* Phones: both lists stack to one column, same as `.flow` already did. */
  #trial .flow,
  #trial .notpanel {
    grid-template-columns: 1fr;
  }

  #trial .notpanel__item:nth-child(1),
  #trial .notpanel__item:nth-child(2),
  #trial .notpanel__item:nth-child(3) {
    grid-column: 1;
    grid-row: auto;
  }
}

/* --- The close ------------------------------------------------------------------------- */

.band--close {
  overflow: hidden;
}

.hero__glow--close {
  inset: auto -10% -40% -10%;
  height: 130%;
  background: radial-gradient(46% 60% at 26% 70%, rgb(245 100 26 / 0.17), transparent 72%);
}

.close {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}

@media (max-width: 52rem) {
  .close {
    grid-template-columns: minmax(0, 1fr);
  }
}

.close > div > * + * {
  margin-top: 0.95rem;
}

/*
 * The right column of the close band. Held the "tell me when the desktop
 * version lands" email form until 23 September 2026; it is the price and the
 * buy link now, `.price` and `.btn-secondary` above, so there is nothing left
 * to style here beyond the column's own top offset.
 */
.buy-block {
  padding-top: 0.4rem;
}

.five :focus-visible {
  outline: 1px solid var(--amber);
  outline-offset: 3px;
}

/* --- Legal pages ------------------------------------------------------------------------
 *
 * `privacy.html`, `terms.html` and `refunds.html`, new for the trial launch,
 * 23 September 2026. Plain prose rather than a new component: the identity's
 * own `p`, `.lede` and `.headline` carry the body, and this adds only the
 * small heading style a policy needs that the marketing page never did. The
 * draft banner they opened with came off 2026-09-25, on the owner's review.
 */

.legal {
  padding: clamp(2.5rem, 5vw, 4rem) var(--gutter) clamp(3.5rem, 6vw, 5rem);
}

.legal .wrap {
  max-width: 40rem;
}

.legal h2 {
  font-family: var(--sans);
  font-weight: 600;
  font-size: 1.1875rem;
  color: var(--ink-100);
  margin: 2.25rem 0 0.6rem;
}

.legal h2:first-of-type {
  margin-top: 2.5rem;
}

.legal ul,
.legal ol {
  color: var(--ink-70);
  padding-left: 1.25rem;
  max-width: var(--measure);
}

.legal li + li {
  margin-top: 0.4rem;
}

.legal .updated {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  color: var(--ink-44);
  margin: 0.6rem 0 0;
}

/* --- The contact form ---------------------------------------------------------------------
 *
 * The only form on the site apart from the trial's own consent box, so it
 * gets its own small block rather than reusing anything: nothing existing
 * styled an input, a select or a textarea. No card and no border box around
 * the form itself, matching the rest of the site; each field is just a label
 * over a plain field, the same rule `.legal` already applies to a policy
 * page. Sized for a phone: fields are 44px tall and the text is 16px, which
 * stops iOS zooming in on focus.
 */

.form {
  max-width: 32rem;
  margin-top: clamp(2rem, 4vw, 2.75rem);
}

.form__row {
  margin-top: 1.25rem;
}

.form__row:first-child {
  margin-top: 0;
}

.form label {
  display: block;
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-44);
  margin-bottom: 0.4rem;
}

.form input[type='text'],
.form input[type='email'],
.form select,
.form textarea {
  width: 100%;
  min-height: 44px;
  padding: 0.6rem 0.8rem;
  font-family: var(--sans);
  font-size: 16px;
  color: var(--ink-100);
  background: var(--surf-2);
  border: 1px solid rgb(255 255 255 / 0.14);
  border-radius: var(--radius-sm);
}

.form textarea {
  min-height: 9rem;
  resize: vertical;
}

.form input:focus-visible,
.form select:focus-visible,
.form textarea:focus-visible {
  outline: 1px solid var(--amber);
  outline-offset: 1px;
}

.form .btn {
  margin-top: 0.5rem;
  border: 0;
  cursor: pointer;
}

/* Off screen rather than display:none or visibility:hidden. Both of those
   also hide a field from some bots, which is exactly the ones the honeypot
   needs to catch; moving it out of the viewport catches a bot that fills in
   every visible field without ever seeing an empty one at all. */
.form__hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* No box around this: the site's own rule is one ink on near-black with no
   cards or boxes anywhere, so the message is set apart by colour and weight
   alone, the same way `.eyebrow--accent` already is. */
.form-error {
  margin-top: 1rem;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--amber);
  max-width: 32rem;
}

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

.five .footer {
  background: var(--ground);
  border-top: 1px solid rgb(255 255 255 / 0.06);
}

/* 2026-09-25: the header nav and this footer's legal nav share `.nav a`
   from identity.css and measure about 21px tall on a phone; min-height with
   inline-flex grows the hit area without changing the text size or colour
   identity.css already sets. */
@media (max-width: 62rem) {
  .five .nav a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}

/* The mark's glyph is the app's own logo tile, the same file the installed app
 * uses as its icon (public/icons/favicon.svg), rather than identity.css's plain
 * disc, which read as a missing image beside the name. A visitor who installs
 * the app then meets the tile they saw here. */
.mark__glyph {
  width: 1.6em;
  height: 1.6em;
  border-radius: 0;
  box-shadow: none;
  background: url("logo.svg") center / contain no-repeat;
}
