/* ==========================================================================
   Nezaam Ltd
   Hand built. No framework, no build step, no dependencies.
   Ground: drawing paper. Two ambers, one interactive and one drawn. Radius: 0.
   ========================================================================== */

/* --- Self hosted type ----------------------------------------------------- */

@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 400 700;
  font-stretch: 100%;
  font-display: swap;
  src: url('assets/fonts/archivo-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 400 700;
  font-stretch: 100%;
  font-display: swap;
  src: url('assets/fonts/archivo-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('assets/fonts/plexmono-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('assets/fonts/plexmono-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

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

:root {
  /* Paper ground, 2026-09-16. The dark scheme is retired: the brand plates are
     pale limestone on drawing paper, and a #080c0f page under them read as a
     mistake. Two ambers, because one cannot do both jobs on a light ground:
     --signal is the interactive amber and clears 4.5:1 on the paper and under
     white; --mark is the brighter drawn amber, decoration only, never text. */
  --ground: #f4f1ea;
  --raise: #eeeade;
  --raise-2: #e7e2d3;
  --line: #ded8c8;
  --line-strong: #bdb5a1;
  --text: #17150f;
  --muted: #55503f;
  /* Darkened 2026-09-18 from #736c59, which measured 4.30:1 on --raise and
     4.49:1 on --raise-2 - under AA at the 9-11px mono sizes it is actually used
     at, in 14 places. Same hue, scaled down about 7%. Now 5.23 on --ground,
     4.91 on --raise, 4.56 on --raise-2. Computed, not eyeballed. */
  --muted-dim: #6a6352;
  --signal: #9a5209;
  --mark: #cf7309;
  --signal-ink: #fdfbf7;

  --sans: 'Archivo', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

  --page: 1340px;
  --gutter: clamp(1.25rem, 4vw, 4rem);
  --rule: 1px solid var(--line);
  --ease: cubic-bezier(0.16, 1, 0.3, 1);

  color-scheme: light;
}

/* --- Reset ---------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--text);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
}

img, svg, picture { display: block; max-width: 100%; }
a { color: inherit; }
h1, h2, h3, p, ul, ol, figure { margin: 0; }
ul { list-style: none; padding: 0; }

:focus-visible { outline: 2px solid var(--signal); outline-offset: 3px; }
::selection { background: var(--signal); color: var(--signal-ink); }

/* --- Shared --------------------------------------------------------------- */

.shell {
  width: 100%;
  max-width: var(--page);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.eyebrow {
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--signal);
  margin-bottom: 1.5rem;
}

.h2 {
  font-size: clamp(1.95rem, 3.4vw, 3rem);
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: -0.03em;
}

.h3 {
  font-size: clamp(1.125rem, 1.5vw, 1.375rem);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.015em;
}

.body {
  color: var(--muted);
  font-size: 1.0625rem;
  line-height: 1.7;
  max-width: 62ch;
}

.section { padding-block: clamp(4.5rem, 9vw, 8rem); border-top: var(--rule); }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--signal); color: var(--signal-ink);
  padding: 0.75rem 1rem; font-family: var(--mono); font-size: 0.8125rem;
}
.skip:focus { left: 0; }

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

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  white-space: nowrap;
  font-family: var(--sans); font-size: 0.9375rem; font-weight: 600; line-height: 1;
  padding: 0.9375rem 1.5rem;
  border: 1px solid transparent; border-radius: 0;
  text-decoration: none; cursor: pointer;
  transition: transform 0.15s var(--ease), background-color 0.2s var(--ease),
    border-color 0.2s var(--ease), color 0.2s var(--ease);
}
.btn:active { transform: translateY(1px); }

.btn--signal { background: var(--signal); color: var(--signal-ink); }
.btn--signal:hover { background: color-mix(in srgb, var(--signal) 86%, #000); }

.btn--ghost { background: transparent; color: var(--text); border-color: var(--muted-dim); }
.btn--ghost:hover { border-color: var(--text); }

.btn--sm { padding: 0.6875rem 1.125rem; font-size: 0.8438rem; }

.textlink {
  display: inline-flex; align-items: baseline; gap: 0.5rem;
  font-weight: 600; font-size: 0.9375rem; text-decoration: none;
  color: var(--signal);
  border-bottom: 1px solid color-mix(in srgb, var(--signal) 35%, transparent);
  padding-bottom: 2px;
  transition: border-color 0.2s var(--ease);
}
.textlink:hover { border-bottom-color: var(--signal); }

/* --- Masthead ------------------------------------------------------------- */

.masthead {
  position: sticky; top: 0; z-index: 50;
  height: 70px; display: flex; align-items: center;
  background: color-mix(in srgb, var(--ground) 86%, transparent);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  border-bottom: var(--rule);
}
.masthead__inner { display: flex; align-items: center; gap: 2rem; width: 100%; }

.wordmark { display: inline-flex; align-items: center; gap: 0.6875rem; text-decoration: none; flex: none; }
.wordmark__text { font-weight: 600; font-size: 0.9375rem; letter-spacing: 0.22em; text-transform: uppercase; }
.wordmark__mark { width: 28px; height: 12px; overflow: visible; }
.wordmark__mark line { stroke: var(--line-strong); stroke-width: 1; }
.wordmark__mark circle { fill: var(--signal); }

.nav { display: flex; align-items: center; gap: 2.25rem; margin-left: auto; }

.nav__link {
  font-size: 0.9063rem; color: var(--muted); text-decoration: none; white-space: nowrap;
  transition: color 0.2s var(--ease);
}
.nav__link:hover { color: var(--text); }
.nav__link[aria-current='page'] { color: var(--text); }

/* --- Hero -----------------------------------------------------------------
   The plate is the ground, not a band underneath the type. It was composed
   with its left 45% empty paper, so the headline sits on bare paper and needs
   no scrim to be read. The wash in from the left is insurance only, for the
   crops `cover` produces at ratios the plate was not composed for. Below
   900px the hero stops being full bleed and stacks, because a tall narrow
   crop puts the stone under the words.
   ------------------------------------------------------------------------- */

.hero {
  position: relative;
  isolation: isolate;
  min-height: min(94dvh, 880px);
  display: flex;
  align-items: center;
  overflow: hidden;
  padding-block: clamp(5rem, 12vh, 9rem) clamp(4rem, 10vh, 7rem);
  border-bottom: var(--rule);
}

.hero__media { position: absolute; inset: 0; z-index: -1; }
.hero__media picture { display: block; width: 100%; height: 100%; will-change: transform; }
.hero__media img { width: 100%; height: 100%; object-fit: cover; object-position: 82% 50%; }

.hero__media::after {
  content: '';
  position: absolute; inset: 0;
  background:
    linear-gradient(90deg,
      var(--ground) 0%,
      color-mix(in srgb, var(--ground) 92%, transparent) 32%,
      color-mix(in srgb, var(--ground) 45%, transparent) 54%,
      transparent 74%),
    linear-gradient(180deg,
      color-mix(in srgb, var(--ground) 55%, transparent) 0%,
      transparent 24%,
      transparent 80%,
      color-mix(in srgb, var(--ground) 60%, transparent) 100%);
  pointer-events: none;
}

.hero__copy { position: relative; }

.hero__title {
  font-size: clamp(2.6rem, 6.6vw, 5.5rem);
  font-weight: 600; line-height: 0.97; letter-spacing: -0.042em;
  max-width: 14ch;
}
.hero__title em { font-style: normal; color: var(--signal); white-space: nowrap; }

.hero__lede {
  margin-top: 1.75rem; max-width: 44ch;
  font-size: clamp(1.0625rem, 1.35vw, 1.25rem);
  line-height: 1.6; color: var(--muted);
}

.hero__actions { display: flex; flex-wrap: wrap; gap: 0.875rem; margin-top: 2.75rem; }

/* --- The person and the ask -----------------------------------------------
   UNREFERENCED ON THE HOME PAGE AS OF 2026-09-18, and left here deliberately.
   Both sections were built for the drawing page and both are Ibrah's own
   decisions from that morning - naming himself on the site for the first time,
   and moving the form onto the home page because the one conversion is a
   message in the inbox. The home page rolled back to the stone the same
   evening and took them with it. Nothing is wrong with either; they are
   waiting on the object being settled, and putting one back is pasting its
   section into index.html. Delete these rules only if that decision is
   actually reversed.
   ------------------------------------------------------------------------- */

/* --- The person -----------------------------------------------------------
   The site's strongest claim is that you deal with the person who builds it,
   and until now that person was anonymous on every page. This section is
   deliberately the only one that does not move.
   ------------------------------------------------------------------------- */

.person__grid { display: grid; gap: clamp(2rem, 5vw, 4rem); align-items: start; }
@media (min-width: 62rem) {
  .person__grid { grid-template-columns: minmax(0, 0.62fr) minmax(0, 1fr); }
}

.person__plate {
  position: relative; aspect-ratio: 4 / 5;
  background: var(--raise-2); border: 1px solid var(--line-strong);
  display: grid; place-items: center;
}
.person__shot { display: none; }
.person__shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.person--shot .person__plate { border: 0; background: none; }
.person--shot .person__shot { display: block; }
.person--shot .person__pending { display: none; }

/* An honest empty frame, not a fake portrait. It says what is missing rather
   than filling the hole with stock. */
.person__pending {
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--muted-dim);
}

.person__text { margin-top: 1.25rem; max-width: 56ch; color: var(--muted); font-size: 1.0625rem; line-height: 1.65; }
.person__meta {
  margin-top: 1.75rem; font-family: var(--mono); font-size: 0.75rem;
  letter-spacing: 0.06em; color: var(--muted-dim);
}

/* --- The ask --------------------------------------------------------------
   Four fields and no more. The input language is the review page's ruled line,
   not a boxed field, because every other edge on this site is a hairline.
   Nothing here animates: a form that moves is a form that feels like it might
   lose what you typed.
   ------------------------------------------------------------------------- */

.ask { max-width: 46rem; display: grid; gap: clamp(1.75rem, 3.5vw, 2.5rem); }
.ask__grid { display: grid; gap: clamp(1.75rem, 3.5vw, 2.5rem); }
@media (min-width: 48rem) {
  .ask__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ask__half { max-width: 24rem; }
}
.ask__area { resize: vertical; min-height: 5.5rem; line-height: 1.6; }
.ask__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.875rem 1.5rem; }
.ask__note {
  margin: 0; max-width: 34ch;
  font-family: var(--mono); font-size: 0.6875rem; line-height: 1.7;
  letter-spacing: 0.04em; color: var(--muted-dim);
}

/* --- Page head (inner pages) ---------------------------------------------- */

.pagehead { padding-block: clamp(4rem, 8vw, 7rem) clamp(3rem, 5vw, 4.5rem); }
.pagehead__title {
  font-size: clamp(2.4rem, 5.2vw, 4rem);
  font-weight: 600; line-height: 1.0; letter-spacing: -0.04em; max-width: 17ch;
}
.pagehead__lede { margin-top: 1.5rem; max-width: 54ch; font-size: 1.125rem; color: var(--muted); line-height: 1.65; }

/* --- Split: sticky headline left, stacked statements right ---------------- */

.split { display: grid; grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); gap: clamp(2.5rem, 6vw, 6rem); }
.split__aside { align-self: start; position: sticky; top: 110px; }
.split__note { margin-top: 1.5rem; max-width: 32ch; color: var(--muted); font-size: 1rem; }

.stack > li { padding-block: clamp(1.75rem, 3vw, 2.5rem); border-top: var(--rule); }
.stack > li:first-child { border-top: 0; padding-top: 0; }
.stack__body { margin-top: 0.625rem; color: var(--muted); max-width: 54ch; }

/* --- Method: three across on a thread ------------------------------------- */

.method { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(1.75rem, 4vw, 3.5rem); margin-top: 3.5rem; }

.method__step { position: relative; padding-top: 2.25rem; }

.method__step::before {
  content: ''; position: absolute; top: 0; left: 0; right: calc(-1 * clamp(1.75rem, 4vw, 3.5rem));
  height: 1px; background: var(--line);
}
.method__step:last-child::before { right: 0; }

.method__step::after {
  content: ''; position: absolute; top: -4px; left: 0;
  width: 9px; height: 9px; background: var(--ground);
  border: 1px solid var(--line-strong); border-radius: 50%;
}
.method__step--live::after { background: var(--signal); border-color: var(--signal); }

.method__body { margin-top: 0.75rem; color: var(--muted); font-size: 0.9688rem; line-height: 1.65; }

/* --- Work tiles ----------------------------------------------------------- */

.tiles { display: grid; gap: 1px; background: var(--line); border: var(--rule); margin-top: 3.5rem; }
.tiles--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

.tile {
  background: var(--ground); padding: clamp(1.75rem, 3vw, 2.5rem);
  display: flex; flex-direction: column; gap: 1rem; text-decoration: none;
  transition: background-color 0.25s var(--ease);
}
a.tile:hover { background: var(--raise); }

.tile__shot { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; object-position: top center; border: var(--rule); }
.tile__meta { font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted-dim); }
.tile__body { color: var(--muted); font-size: 0.9688rem; line-height: 1.65; }

/* --- Work: three pieces, each with its own drawn plate --------------------
   A table of rows was the wrong shape for this: it made three different pieces
   of work look like line items in an audit. Each one gets a plate instead, and
   each plate is a diagram of that work's own mechanism, drawn in the same
   graphite-and-amber language as the survey section. Drawn in code, never
   generated, and never a number nobody measured.

   The lines set themselves down when the card arrives, using the `is-in` class
   site.js already adds. `pathLength="100"` normalises every path, so one dash
   rule fits all of them without measuring anything.
   ------------------------------------------------------------------------- */

.works { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1px; background: var(--line); border: var(--rule); margin-top: 3.5rem; }

.work { position: relative; background: var(--ground); display: flex; flex-direction: column; transition: background 0.3s var(--ease); }
.work__plate { border-bottom: var(--rule); background: var(--raise); transition: background 0.3s var(--ease); }
.work__plate svg { width: 100%; height: auto; display: block; }
.work__body { flex: 1; display: flex; flex-direction: column; gap: 0.9rem; padding: clamp(1.5rem, 2.6vw, 2.25rem); }

/* The whole card is the link, plate included, on one tab stop. The anchor sits
   on the heading, where a screen reader announces something worth reading out,
   and a stretched ::after carries the click area across the rest of the card. */
.work__link { color: inherit; text-decoration: none; }
.work__link::after { content: ''; position: absolute; inset: 0; z-index: 2; }
.work:hover { background: var(--raise); }
.work:hover .work__plate { background: var(--raise-2); }
.work:hover .work__link { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 5px; }

/* A ring round the whole card is only right when it is showing where the click
   goes, so it is bound to :has(). Where :has() is missing both rules drop
   together and the browser's own ring on the heading stands. */
.work:has(.work__link:focus-visible) { outline: 2px solid var(--signal); outline-offset: -2px; }
.work:has(.work__link:focus-visible) .work__link { outline: none; }

.work__more {
  display: inline-flex; align-items: center; gap: 0.55rem;
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--signal);
}
.work__more i { font-style: normal; transition: transform 0.3s var(--ease); }
.work:hover .work__more i { transform: translateX(5px); }

/* The generated plate, when one exists. The drawn diagram is the default and
   stays in the markup; adding `work--shot` swaps it for the image, so the two
   can be compared without touching anything else. */
.work__shot { display: none; }
.work__shot img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; display: block; }
.work--shot .work__plate svg { display: none; }
.work--shot .work__shot { display: block; }
.work__meta { font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted-dim); }
.work__text { color: var(--muted); font-size: 0.9688rem; line-height: 1.65; }
.work__text .textlink { font-size: 0.9063rem; }
.work__status {
  margin-top: auto; padding-top: 1rem; border-top: var(--rule);
  font-family: var(--mono); font-size: 0.6875rem; line-height: 1.6;
  letter-spacing: 0.06em; color: var(--muted-dim);
}

.work__plate path { fill: none; stroke: var(--text); stroke-width: 1.4; stroke-linecap: square; opacity: 0.6; }
.work__plate .d-faint { opacity: 0.3; }
.work__plate .d-key { stroke: var(--mark); stroke-width: 1.8; opacity: 1; }
.work__plate .d-mark { stroke: var(--mark); stroke-width: 1.8; opacity: 1; }
.work__plate .d-dot { fill: var(--text); stroke: none; opacity: 0.78; }
.work__plate .d-dot--key { fill: var(--mark); opacity: 1; }

@media (prefers-reduced-motion: no-preference) {
  .js .work__plate path { stroke-dasharray: 100; stroke-dashoffset: 100; }
  .js .work__plate .d-dot { opacity: 0; }
  .js .work.is-in .work__plate path { stroke-dashoffset: 0; transition: stroke-dashoffset 1.1s var(--ease) 0.15s; }
  .js .work.is-in .work__plate .d-mark { transition: stroke-dashoffset 0.4s var(--ease) 1.05s; }
  .js .work.is-in .work__plate .d-dot { opacity: 0.78; transition: opacity 0.4s ease; }
  .js .work.is-in .work__plate .d-dot--key { opacity: 1; }
  .js .work.is-in .work__plate .d-dot:nth-of-type(1) { transition-delay: 0.55s; }
  .js .work.is-in .work__plate .d-dot:nth-of-type(2) { transition-delay: 0.7s; }
  .js .work.is-in .work__plate .d-dot:nth-of-type(3) { transition-delay: 0.85s; }
  .js .work.is-in .work__plate .d-dot:nth-of-type(4) { transition-delay: 1s; }
  .js .work.is-in .work__plate .d-dot:nth-of-type(5) { transition-delay: 1.15s; }
}

/* --- Ledger (labelled rows, grouped rather than striped) ------------------ */

.ledger { display: grid; gap: 0; margin-top: 2.5rem; max-width: 62ch; }
.ledger__row { display: grid; grid-template-columns: 13rem minmax(0, 1fr); gap: 1.5rem; padding-block: 1.125rem; }
.ledger__row + .ledger__row { border-top: var(--rule); }
.ledger__key { font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted-dim); padding-top: 0.2rem; }
.ledger__val { font-size: 1rem; }
.ledger__val a { color: var(--signal); text-decoration: none; border-bottom: 1px solid color-mix(in srgb, var(--signal) 35%, transparent); }
.ledger__val a:hover { border-bottom-color: var(--signal); }

/* On the home page the ledger is the right half of a split, so the 62ch cap
   that keeps it readable in a single column would leave it floating in a much
   wider one. The column is already narrow; the cap is not needed there. */
.ledger--wide { max-width: none; margin-top: 0; }
.ledger--wide .ledger__row { grid-template-columns: 11rem minmax(0, 1fr); }
@media (max-width: 62rem) { .ledger--wide .ledger__row { grid-template-columns: 1fr; gap: 0.25rem; } }

/* --- Closing band --------------------------------------------------------- */

.band { border-top: var(--rule); padding-block: clamp(4.5rem, 9vw, 7.5rem); }
.band__title { font-size: clamp(2rem, 4.4vw, 3.4rem); font-weight: 600; line-height: 1.04; letter-spacing: -0.035em; max-width: 18ch; }
.band__body { margin-top: 1.25rem; max-width: 48ch; color: var(--muted); font-size: 1.0625rem; }
.band__actions { display: flex; flex-wrap: wrap; gap: 0.875rem; margin-top: 2.5rem; }

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

.footer { border-top: var(--rule); padding-block: 3.5rem 3rem; }
.footer__grid { display: grid; grid-template-columns: minmax(0, 1.4fr) repeat(2, minmax(0, 1fr)); gap: 2.5rem; }
.footer__head { font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted-dim); margin-bottom: 1.125rem; }
.footer__list li + li { margin-top: 0.625rem; }
.footer__list a { font-size: 0.9375rem; color: var(--muted); text-decoration: none; }
.footer__list a:hover { color: var(--text); }
.footer__blurb { color: var(--muted); font-size: 0.9375rem; max-width: 34ch; margin-top: 1rem; }
.footer__legal { margin-top: 3.5rem; padding-top: 1.75rem; border-top: var(--rule); display: flex; flex-wrap: wrap; gap: 0.5rem 2rem; }
.footer__legal p { font-size: 0.8125rem; color: var(--muted-dim); }

/* --- Motion: one reveal, used for section entries only -------------------- */

@media (prefers-reduced-motion: no-preference) {
  .js .rise { opacity: 0; transform: translateY(18px); }
  .js .rise.is-in { opacity: 1; transform: none; transition: opacity 0.7s var(--ease), transform 0.7s var(--ease); }
  .js .rise[data-d='1'].is-in { transition-delay: 0.08s; }
  .js .rise[data-d='2'].is-in { transition-delay: 0.16s; }
  .js .rise[data-d='3'].is-in { transition-delay: 0.24s; }
}


/* --- Hero image motion ----------------------------------------------------
   Two elements so two transforms never fight: the wrapper takes the scroll
   parallax, the image takes the slow drift. Transform only, so both stay on the
   compositor and neither repaints the photograph.
   ------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  .hero__media img {
    animation: drift 32s ease-in-out infinite alternate;
    will-change: transform;
  }
  @supports (animation-timeline: view()) {
    .hero__media picture {
      animation: parallax linear both;
      animation-timeline: view();
      animation-range: cover;
    }
  }
}

@keyframes drift {
  from { transform: scale(1.06) translate3d(0, 0, 0); }
  to   { transform: scale(1.13) translate3d(-1.4%, -1%, 0); }
}
@keyframes parallax {
  from { transform: translate3d(0, -4%, 0); }
  to   { transform: translate3d(0, 4%, 0); }
}

/* --- Method: the rule draws itself in ------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  .js .method__step.is-in::before { animation: wipe 0.8s var(--ease) 0.1s forwards; }
  .js .method__step[data-d='1'].is-in::before { animation-delay: 0.22s; }
  .js .method__step[data-d='2'].is-in::before { animation-delay: 0.34s; }
  .js .method__step::before { transform: scaleX(0); transform-origin: left center; }
}

@keyframes wipe { to { transform: scaleX(1); } }

/* --- The survey -----------------------------------------------------------
   What Nezaam does, shown rather than claimed. Loose fragments hold still and
   the drawing happens over them: measuring lines reach out across the stage,
   each ending in a perpendicular tick, and only then do the fragments resolve
   into one measured solid.

   Deliberately NOT the bead section, which belongs to nazm. There, objects fly
   into line one at a time and the motion is assembly. Here nothing flies and
   the motion is the survey.

   Two plates and no more: the code draws the lines over the unmeasured plate,
   which carries none of its own, then hands off to the measured plate, whose
   lines are baked in. The code lines are the act of measuring, the plate lines
   are the record of it, so they never double up.
   ------------------------------------------------------------------------- */

.survey { position: relative; border-top: var(--rule); }
.js .survey { height: 340vh; }
.js .survey.is-still { height: auto; }

.survey__stage { position: relative; height: calc(100svh - 70px); min-height: 600px; max-height: 960px; overflow: hidden; }
.js .survey:not(.is-still) .survey__stage { position: sticky; top: 70px; max-height: none; }

.survey__field { position: absolute; inset: 0; }

.survey__plate { position: absolute; inset: 0; display: block; }
.survey__plate img { width: 100%; height: 100%; object-fit: cover; object-position: 78% 50%; }

/* The measured plate is uncovered by a sweep, not faded in. A dissolve between
   two different photographs reads as a swap; a sweep reads as something being
   done to the picture. Without scripting the plate is simply already revealed. */
.survey__plate--after {
  --sweep: 108%;
  -webkit-mask-image: linear-gradient(90deg, #000 calc(var(--sweep) - 0.6%), transparent calc(var(--sweep) + 0.6%));
  mask-image: linear-gradient(90deg, #000 calc(var(--sweep) - 0.6%), transparent calc(var(--sweep) + 0.6%));
}
.js .survey__plate--after { --sweep: 0%; will-change: mask-position; }

/* The instrument itself: one amber edge, the thing doing the measuring. */
.survey__sweep {
  position: absolute; top: -6%; left: 0; z-index: 3;
  width: 2px; height: 112%;
  background: linear-gradient(180deg, transparent 0%, var(--mark) 14%, var(--mark) 86%, transparent 100%);
  opacity: 0; pointer-events: none; will-change: transform;
}

/* One soft wash in from the left so the copy keeps its contrast whatever the
   cover crop does to the plate. */
.survey__field::after {
  content: '';
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: linear-gradient(90deg,
    var(--ground) 0%,
    color-mix(in srgb, var(--ground) 90%, transparent) 30%,
    color-mix(in srgb, var(--ground) 40%, transparent) 52%,
    transparent 70%);
}

.survey__svg { position: absolute; inset: 0; z-index: 3; width: 100%; height: 100%; overflow: visible; }
.survey__svg path {
  fill: none;
  stroke: var(--text);
  stroke-width: 1;
  stroke-linecap: square;
  opacity: 0.55;
}
.survey__svg path.is-key { stroke: var(--mark); stroke-width: 1.4; opacity: 0.9; }

/* The named step strip: Watch, Draw, Build, Nezaam's own method. Each lights as
   that part of the drawing finishes. */
.tally { display: flex; align-items: center; gap: 0.9rem; margin-top: 0.6rem; font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted-dim); }

.steps { display: flex; flex-wrap: wrap; gap: 0.5rem 1.05rem; }
.steps i {
  display: inline-flex; align-items: center; gap: 0.45rem;
  font-style: normal; color: var(--muted-dim); transition: color 0.3s ease;
}
.steps i::before {
  content: ""; width: 7px; height: 7px; flex: none;
  border: 1px solid var(--line-strong);
  transition: background-color 0.3s ease, border-color 0.3s ease;
}
.steps i.on { color: var(--text); }
.steps i.on::before { background: var(--mark); border-color: var(--mark); }

.survey__copy {
  position: relative; z-index: 4; height: 100%;
  display: flex; flex-direction: column; justify-content: center; gap: 1.15rem;
}
.survey__copy > * { max-width: min(31rem, 40vw); }
.survey__lede { color: var(--muted); font-size: clamp(1rem, 1.25vw, 1.125rem); max-width: min(38ch, 40vw); }

.survey__stage.is-portrait .survey__copy { justify-content: flex-start; padding-top: clamp(2rem, 7svh, 4rem); gap: 0.85rem; }
.survey__stage.is-portrait .survey__copy > * { max-width: 100%; }
.survey__stage.is-portrait .survey__lede { max-width: 36ch; }
.survey__stage.is-portrait .survey__plate img { object-position: 60% 62%; }
.survey__stage.is-portrait .survey__field::after {
  background: linear-gradient(180deg,
    var(--ground) 0%,
    color-mix(in srgb, var(--ground) 88%, transparent) 34%,
    color-mix(in srgb, var(--ground) 30%, transparent) 58%,
    transparent 76%);
}

/* --- The thread: nazm's object, on its own dark stage ---------------------
   This belongs to the product, not the company. nazm means to string pearls
   onto a thread, so the beads are its own, and the company site draws a survey
   instead. Five loose beads, each a step an order actually passes through, fly
   onto one amber thread as the visitor scrolls (thread.js).

   It keeps the deep ink ground on purpose. The bead is black glass with an
   amber glow, and a glow only exists against dark, so the section is a dark
   stage set into a paper page rather than a re-render. It also separates the
   product from the company at a glance, which is the point.
   ------------------------------------------------------------------------- */

.string {
  position: relative;
  --ink: #080c0f;
  --ink-line: #2c383f;
  --ink-text: #eceff1;
  --ink-muted: #93a2aa;
  --glow: #ffb454;
  background: var(--ink);
  color: var(--ink-text);
}
.js .string { height: 340vh; }
.js .string.is-still { height: auto; }

.string__stage { position: relative; height: calc(100svh - 70px); min-height: 560px; max-height: 900px; overflow: hidden; }
.js .string:not(.is-still) .string__stage { position: sticky; top: 70px; max-height: none; }

.string__field { position: absolute; inset: 0; }
.string__svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.string__svg path { fill: none; stroke-linecap: round; }
.string__halo { stroke: var(--glow); stroke-width: 8; opacity: 0.3; }
.string__core { stroke: var(--glow); stroke-width: 1.8; }
.string__spark { stroke: #fff1dc; stroke-width: 2.4; opacity: 0; }

.bead { position: absolute; left: 0; top: 0; will-change: transform; }
.bead img { width: 100%; height: auto; max-width: none; user-select: none; -webkit-user-drag: none; pointer-events: none; }
.bead__glow {
  position: absolute; left: 80%; top: 48%; width: 34%; aspect-ratio: 1; translate: -50% -50%;
  border-radius: 50%; mix-blend-mode: screen; opacity: 0; pointer-events: none;
  background: radial-gradient(circle, rgb(255 196 120 / 0.95) 0%, rgb(255 180 84 / 0.45) 38%, rgb(255 180 84 / 0) 70%);
  transition: opacity 0.5s ease;
}
.bead.is-lit .bead__glow { opacity: 0.62; animation: flash 0.7s ease-out; }
@keyframes flash { 0% { opacity: 0; } 30% { opacity: 1; } 100% { opacity: 0.62; } }

.bead-tag {
  position: absolute; left: 0; top: 0; z-index: 60; white-space: nowrap; pointer-events: none;
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink-muted); will-change: transform, opacity;
}

.string__copy {
  position: relative; z-index: 40; height: 100%;
  display: flex; flex-direction: column; justify-content: center; gap: 1.15rem;
}
.string__copy > * { max-width: min(31rem, 40vw); }
.string__copy .eyebrow { color: var(--glow); }
.string__copy .h2 { color: var(--ink-text); }
.string__lede { color: var(--ink-muted); font-size: clamp(1rem, 1.25vw, 1.125rem); max-width: min(36ch, 40vw); }

.string .tally { color: var(--ink-muted); }
.string .steps i { color: var(--ink-muted); }
.string .steps i::before { border-color: var(--ink-line); }
.string .steps i.on { color: var(--ink-text); }
.string .steps i.on::before { background: var(--glow); border-color: var(--glow); }

.string__stage.is-portrait .string__copy { justify-content: flex-start; padding-top: clamp(2rem, 7svh, 4rem); gap: 0.85rem; }
.string__stage.is-portrait .string__copy > * { max-width: 100%; }
.string__stage.is-portrait .string__lede { max-width: 34ch; }
.string__stage.is-portrait .bead-tag { font-size: 0.5625rem; letter-spacing: 0.12em; }

/* The three pinned scene systems that lived here - .scene, the client scene
   and the .parts stack - were deleted on 2026-09-18 with plate.js, the only
   file that ever built them. plate.js was loaded by no page: about 200 lines
   of CSS here could not match anything, and a dead class name is not inert,
   it is a name free to be reused by accident. `.tally` was taken that way
   once already. Recover from git if a pinned scene is wanted again.
   ------------------------------------------------------------------------- */

/* --- The waves: three gates, and which one is open today ------------------- */

.waves {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1rem, 2.5vw, 2rem);
  margin-top: clamp(2.5rem, 5vw, 3.5rem); border-top: var(--rule);
}
/* The face used to be assets/img/card.webp - the pale limestone slab that was
   retired from this page on 2026-09-17 for being a shape standing near a tax
   year rather than the thing itself. It went back in here by accident when the
   waves were written. The gate is drawn instead: the amount is the payload, so
   it is set at the same size as the company number the reader types further
   down, and the line across the column head says whether that gate has opened.
   Weight is deliberate - the audit found 1px hairlines vanish at laptop
   distance. */
.wave { position: relative; padding-top: 1.9rem; display: flex; flex-direction: column; align-items: flex-start; gap: 0.4rem; }
.wave::before { content: ''; position: absolute; left: 0; top: 0; width: 1px; height: 13px; background: var(--text); opacity: 0.4; }
.wave::after { content: ''; position: absolute; left: 0; right: 0; top: -2px; height: 3px; background: transparent; }
.wave.is-open::after { background: var(--mark); }
.wave__amount {
  font-family: var(--mono); font-weight: 500; line-height: 1;
  font-size: clamp(1.125rem, 2.2vw, 1.375rem); letter-spacing: 0.04em; color: var(--text);
}
.wave__qual { font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.1em; color: var(--muted-dim); }
.wave__date { font-family: var(--mono); font-size: 0.8125rem; letter-spacing: 0.04em; color: var(--muted); margin-top: 0.75rem; }
.wave__state { font-family: var(--mono); font-size: 0.625rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted-dim); }
.wave.is-open .wave__state { color: var(--signal); }

.cycle { margin-top: clamp(2rem, 4vw, 3rem); border-top: var(--rule); padding-top: 1.5rem; }
.cycle__row { display: grid; grid-template-columns: minmax(0, 0.34fr) minmax(0, 1fr); gap: clamp(1rem, 3vw, 2.5rem); align-items: start; margin: 0; }
.cycle__key { font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted-dim); }
.cycle__val { color: var(--muted); max-width: 62ch; }

/* --- The limits: four across, deliberately plain --------------------------- */

.limits { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: clamp(1.5rem, 3vw, 2.5rem); margin-top: clamp(2.5rem, 5vw, 3.5rem); }
.limit { border-top: 1px solid var(--line-strong); padding-top: 1.1rem; }
.limit__key { font-family: var(--mono); font-weight: 400; font-size: 0.6875rem; letter-spacing: 0.1em; color: var(--text); }
.limit__val { margin-top: 0.65rem; color: var(--muted); font-size: 0.9375rem; line-height: 1.65; }

@media (max-width: 62rem) { .limits { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 48rem) {
  .cycle__row { grid-template-columns: 1fr; gap: 0.45rem; }
  /* Stacked, the three gates need their own separator or they run together:
     the shared top rule only draws above the first one. */
  .waves { grid-template-columns: 1fr; gap: 0; }
  .wave { padding-top: 1.35rem; padding-bottom: 1.35rem; }
  .wave + .wave { border-top: var(--rule); }
}
@media (max-width: 40rem) { .limits { grid-template-columns: 1fr; } }

/* ═══ THE QUILL PAGE ═════════════════════════════════════════════════════
   Rhythm, then three demonstrations. The rhythm rules are lifted from the old
   site's own DESIGN.md, which diagnosed this exact fault on 2026-08-05:

     "a stack of identical slabs. Changing the colour of a stack of identical
      slabs produces a differently coloured stack of identical slabs."

   Its fix, applied here: sections separate by CHANGING SURFACE rather than by
   a hairline under every one; vertical padding is a rank, not a default; there
   is a real heading ladder; and the ink budget is one section.
   ══════════════════════════════════════════════════════════════════════ */

:root {
  /* Ink is the page's own text colour used as a ground, not a new hue, so the
     one dark section still belongs to the paper system. */
  --ink: #17150f;
  --ink-fg: #f4f1ea;
  --ink-mut: #a9a293;
  --ink-line: rgba(244, 241, 234, 0.16);
  --good: #1a6b33;
}

.surf-paper { background: var(--ground); }
.surf-raise { background: var(--raise); }
.surf-sunk { background: var(--raise-2); }
.surf-ink { background: var(--ink); color: var(--ink-fg); }

/* A tone change is a stronger boundary than a rule and costs no ink. If every
   section grows a border again, this has been undone. */
.section[class*="surf-"] { border-top: 0; }

/* Two ranks, about 1.3 apart: enough to read as different, small enough that
   four large sections do not add a screen and a half of scroll. */
.section--lg { padding-block: clamp(6rem, 11vw, 9.5rem); }

.h2--lead {
  font-size: clamp(2rem, 4.4vw, 3.35rem);
  line-height: 1.04; letter-spacing: -0.022em; font-weight: 700;
  max-width: 20ch; text-wrap: balance;
}

.surf-ink .eyebrow { color: var(--mark); }
.surf-ink .body { color: var(--ink-mut); }
.surf-ink .btn--ghost { color: var(--ink-fg); border-color: rgba(244, 241, 234, 0.34); }
.surf-ink .btn--ghost:hover { border-color: var(--ink-fg); }
.surf-ink ::selection { background: var(--mark); color: var(--ink); }

/* ==========================================================================
   .stage - A DARK SECTION SET INTO A PAPER PAGE
   ==========================================================================
   Added 2026-09-18. Ibrah, comparing the three product pages: nazm has a dark
   hero and practice and audit do not, and they should align.

   The answer settled in that conversation, and it governs every product page
   from here: THE COLOUR IS THE COMPANY, NOT THE PRODUCT. One amber across the
   site; no product gets its own hue. What differs per product is the STAGE -
   the one dark section the object stands on - and the object itself. A colour
   per product is a portfolio move and it only pays once each product is known
   well enough that the colour alone recalls it. Three unknown products in
   three colours means a visitor learns three brands and remembers none.

   This is the rule WEBSITE-PAGE-DESIGN-SYSTEM.md section 3 already states -
   "the page is paper, the object gets a stage, the stage is one section, not
   the site" - given a class so it stops being re-derived per page.

   ⚠ NOT .page-ink. That class is the whole-page dark scheme Ibrah rejected on
   sight: "we just decided no dark theme as that's flooded in the market and
   makes us look generic." He is right and it stays unused. .stage is one
   section. If this class ever appears on a <body>, it has been misread.

   WHY #080c0f AND NOT THE OTHER --ink. styles.css carries two: #080c0f, the
   cold stage black behind nazm's beads, and #17150f, a warm brown-black used
   as a band. Ibrah approved the first by approving nazm, so that is the
   product-page ground. The collision itself is still open - see the design
   system section 7 - and is not resolved here because renaming touches every
   page and this change is one page.

   IT IS A TOKEN FLIP, NOT A SECOND STYLESHEET. Every component inside reads
   --ground, --raise, --text, --muted and the ambers, so redefining those nine
   names carries the whole component library onto the dark ground without one
   parallel rule per component. Checked before writing: the product screen
   (.mock, 60+ rules) hardcodes no colour at all - every value is a token or a
   color-mix off one - so it crosses cleanly. That is why this is 30 lines and
   not 300.

   ⚠ THE TWO AMBERS SWAP JOBS, as they do on every dark ground here:
     on paper   --signal #9a5209 is readable, --mark #cf7309 is drawn-only
     on stage   --mark's value moves INTO --signal, because #9a5209 measures
                3.35:1 on #080c0f and fails as text
   So anything amber and legible keeps using --signal and inherits the right
   value. Never hardcode either hex inside a stage.

   EVERY PAIR COMPUTED 2026-09-18, none eyeballed. Floor 4.5:1 for text,
   3:1 for a control border. Tightest pair is the active nav item at 4.74.

     --text      #eceff1  on #080c0f  17.00    --muted  #b3c0c7  10.55
     --muted-dim #93a2aa  on #080c0f   7.47    --signal #cf7309   5.71
     --signal-ink on --signal fill      5.75    nav item .on       4.74
     --text on --raise #0e141a         16.04    --muted on raise   9.95

   The structure reads slightly STRONGER than the paper version it replaces:
   frame border 1.89 against paper's 1.81, inner rules 1.24 against 1.18.
   ========================================================================== */

.stage {
  --ground: #080c0f;
  --raise: #0e141a;
  --raise-2: #121a21;
  --line: #1e2830;
  --rule: 1px solid #1e2830;
  --line-strong: #33424b;
  --text: #eceff1;
  --muted: #b3c0c7;
  --muted-dim: #93a2aa;
  --signal: #cf7309;
  --mark: #e89434;
  --signal-ink: #070b0e;

  background: var(--ground);
  color: var(--text);
  color-scheme: dark;
}

/* A tone change is the boundary, the same rule the surf-* utilities follow.
   A cream hairline across the top of a black section is the seam showing. */
.section.stage { border-top: 0; }

/* Two inversions the flip cannot do on its own, because both mix toward a
   fixed endpoint rather than a token. */
.stage .btn--signal:hover { background: color-mix(in srgb, var(--signal) 88%, #fff); }
.stage ::selection { background: var(--signal); color: var(--signal-ink); }

/* The window's lift came from a shadow tuned for paper. On a ground this
   dark a drop shadow is invisible, and the honest separator on dark is the
   edge: --line-strong holds the frame at 1.89 against the stage. */
.stage .mock { box-shadow: none; }

/* ⚠ THE ONE THING A TOKEN FLIP CANNOT DO ON ITS OWN, found by capture and not
   by reading. The product screen builds itself from two surfaces: the frame is
   --raise and its title bar, KPI tiles and panels are --ground. On paper that
   makes the wells LIGHTER than the frame, so they come forward:

     well #f4f1ea over frame #eeeade over page #f4f1ea

   Flipped, the order inverts and the wells land on exactly the page ground -
   which on a stage is the stage itself. Every well then measured 1.000 against
   the black behind the window, and the first capture showed it: the title bar
   and the tiles read as holes cut through the window rather than surfaces in
   it, and the window stopped being an object standing on the stage.

   The fix is to give the window its own two-step scale one rung above the
   stage, so nothing inside it equals the ground behind it. Only two tokens are
   involved - the screen reads --ground and --raise and nothing else.

     stage  #080c0f      frame  #121a21  1.117 above the stage
     well   #0e141a      1.060 above the stage, 1.055 below the frame

   Text is re-checked on both new surfaces, all above the 4.5 floor:
     frame  text 15.21  muted 9.44  dim 6.68  signal 5.11
     well   text 16.04  muted 9.95  dim 7.05  signal 5.39 */
.stage .mock { --ground: #0e141a; --raise: #121a21; }

.linkbtn {
  padding: 0; border: 0; background: none; cursor: pointer;
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.08em;
  color: var(--signal);
  border-bottom: 1px solid color-mix(in srgb, var(--signal) 35%, transparent);
}
.linkbtn:hover { border-bottom-color: var(--signal); }

/* --- 1 · The record fills itself ------------------------------------------
   Companies House lookup, quill/src/lib/companies-house.ts. The demo runs on
   Nezaam's own entry on the public register, so the page shows no client it
   does not have. ------------------------------------------------------- */

.fill { display: grid; gap: clamp(2rem, 4vw, 3.5rem); margin-top: clamp(2.5rem, 5vw, 4rem); }
@media (min-width: 62rem) { .fill { grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr); align-items: start; } }

.fill__label { display: block; font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted-dim); }
.fill__field {
  margin-top: 0.6rem; display: flex; align-items: center; min-height: 3.35rem;
  padding: 0 1rem; background: #fff; border: 1px solid var(--line-strong);
  font-family: var(--mono); font-size: clamp(1.125rem, 2.2vw, 1.375rem);
  letter-spacing: 0.16em; color: var(--text);
}
.fill__caret { width: 2px; height: 1.4em; background: var(--signal); margin-left: 3px; }
.fill.is-typing .fill__caret { animation: caret 1s steps(1, end) infinite; }
@keyframes caret { 50% { opacity: 0; } }
.fill__go { margin-top: 1.1rem; }
.fill__note { margin-top: 1.2rem; color: var(--muted); font-size: 0.875rem; line-height: 1.7; max-width: 42ch; }

.fill__record { margin: 0; border-top: 1px solid var(--line-strong); }
.fill__row { display: grid; grid-template-columns: minmax(0, 0.44fr) minmax(0, 1fr); gap: 1rem; padding: 0.95rem 0; border-bottom: 1px solid var(--line); }
.fill__row dt { font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted-dim); }
.fill__row dd {
  margin: 0; font-family: var(--mono); font-size: 0.875rem; line-height: 1.55;
  color: var(--text); min-height: 1.4em;
  opacity: 0; transform: translateY(5px);
  transition: opacity 0.4s var(--ease), transform 0.4s var(--ease);
}
.fill__row dd.is-in { opacity: 1; transform: none; }
/* A waiting field draws its own blank rather than sitting empty. */
.fill__row dd:empty::after { content: ''; display: block; width: 42%; height: 1px; background: var(--line-strong); opacity: 0.7; margin-top: 0.6em; }
.fill__row dd:empty { opacity: 1; transform: none; }

/* --- 2 · The request, and the chase --------------------------------------
   quill/src/lib/chase.ts: CHASE_OFFSET_DAYS = [3, 7, 14], then it stops.
   ---------------------------------------------------------------------- */

.req { margin-top: clamp(2.5rem, 5vw, 3.5rem); max-width: 46rem; background: #fff; border: 1px solid var(--line-strong); }
.req__head { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; padding: 1.2rem 1.35rem; border-bottom: 1px solid var(--line); }
.req__kicker { font-family: var(--mono); font-size: 0.625rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted-dim); }
.req__title { margin-top: 0.35rem; font-weight: 600; }
.req__status {
  flex: none; font-family: var(--mono); font-size: 0.625rem; letter-spacing: 0.12em;
  text-transform: uppercase; padding: 0.32rem 0.55rem;
  border: 1px solid currentColor; color: var(--signal); white-space: nowrap;
  transition: color 0.3s var(--ease);
}
.req__status.is-complete { color: var(--muted); }

.req__items { list-style: none; margin: 0; padding: 0; }
.req__item {
  display: flex; align-items: center; gap: 0.85rem; width: 100%;
  padding: 0.95rem 1.35rem; border: 0; border-bottom: 1px solid var(--line);
  background: none; color: inherit; font: inherit; text-align: left; cursor: pointer;
  transition: background-color 0.2s var(--ease);
}
.req__item:hover { background: var(--ground); }
.req__item:focus-visible { outline: 2px solid var(--signal); outline-offset: -2px; }
.req__box { width: 14px; height: 14px; flex: none; border: 1px solid var(--line-strong); transition: background-color 0.25s var(--ease), border-color 0.25s var(--ease); }
.req__name { flex: 1 1 auto; font-size: 0.9375rem; }
.req__state { flex: none; font-family: var(--mono); font-size: 0.625rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted-dim); }
.req__item.is-in .req__box { background: var(--text); border-color: var(--text); }
.req__item.is-in .req__name { color: var(--muted); }
.req__item.is-in .req__state { color: var(--muted); }

.req__foot { display: flex; justify-content: space-between; align-items: center; gap: 1rem 1.5rem; flex-wrap: wrap; padding: 1rem 1.35rem; }
.req__chase { display: flex; align-items: center; gap: 0.45rem; flex-wrap: wrap; margin: 0; font-family: var(--mono); font-size: 0.625rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted-dim); }
.req__chase i { font-style: normal; padding: 0.24rem 0.45rem; border: 1px solid var(--line-strong); color: var(--line-strong); transition: all 0.3s var(--ease); }
/* Was --mark filled with #fff on top: 3.44:1 at 10px. --mark is the DRAWN amber
   and the token rule says it never carries text; --signal is the one that does,
   and --signal-ink is its ink. Using the pair holds on both grounds without a
   second declaration - on paper that is near-white on #9a5209 (5.70), inside a
   stage it flips to near-black on #cf7309 (5.75). */
.req__chase i.on { border-color: var(--signal); background: var(--signal); color: var(--signal-ink); }
.req__chase.is-done i { opacity: 0.4; }
.req__reset { margin: 0; }

/* --- 3 · What gets written down, on ink ----------------------------------- */

.written { display: grid; gap: clamp(1.25rem, 2.5vw, 2rem); margin-top: clamp(2.5rem, 5vw, 3.5rem); }
/* Equal height, not align-items:start. These two are now a PAIR - the same
   moment written twice - and two boxes of different heights read as two
   unrelated items. `start` was correct for the old right-hand card, which
   held a button and sized itself. */
@media (min-width: 58rem) { .written { grid-template-columns: 1fr 1fr; } }
/* ⚠ THIS COMPONENT WAS INVISIBLE, and had been since 2026-09-16. Found by
   capture on 2026-09-18 while rewriting the section, not by reading the CSS.

   `.wcard` was written for the dark site and never re-themed when the ground
   became paper. It was still reading the leftover --ink-* names, and on
   #f4f1ea every single value failed:

     dd      --ink-fg  #f4f1ea   1.00  the value text WAS the page ground
     dt      --ink-mut #a9a293   2.25
     note    --ink-mut #a9a293   2.25
     kicker  --mark    #cf7309   3.05  drawn-only amber used as text
     border  --ink-line rgba(244,241,234,0.16)  white at 16% on white

   So the card had no border, no background, unreadable labels and NO VALUES
   AT ALL - a column of field names floating on paper. That is what Ibrah was
   looking at when he said the section was not landing, and no amount of
   rewriting the copy would have fixed it.

   Re-themed onto the real tokens below. `dt` takes --muted rather than
   --muted-dim on purpose: --muted-dim is computed against --ground and drops
   to 4.35 on a --raise card, just under the floor. Measured on #eeeade:
     dd 15.17   dt 6.70   note 6.70   kicker 4.87   card-on-ground 1.066

   The whole component now reads tokens only, so it survives a .stage flip
   without a parallel rule - which the old hardcoded rgba never could. */
.wcard { border: var(--rule); background: var(--raise); padding: clamp(1.25rem, 2.6vw, 1.85rem); }
.wcard__kicker { margin: 0; font-family: var(--mono); font-size: 0.625rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--signal); }
.wcard__rows { margin: 1.3rem 0 0; }
.wcard__rows > div { display: grid; grid-template-columns: minmax(0, 0.4fr) minmax(0, 1fr); gap: 1rem; padding: 0.6rem 0; border-top: var(--rule); }
.wcard__rows dt { font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.04em; color: var(--muted); }
.wcard__rows dd { margin: 0; font-family: var(--mono); font-size: 0.8125rem; line-height: 1.55; color: var(--text); overflow-wrap: anywhere; }
.wcard__note { margin-top: 1.3rem; font-size: 0.875rem; line-height: 1.7; color: var(--muted); }

@media (prefers-reduced-motion: reduce) {
  .fill__row dd, .req__box, .req__item, .req__chase i, .req__status { transition: none; }
  .fill.is-typing .fill__caret { animation: none; }
}

/* --- Responsive ----------------------------------------------------------- */

@media (max-width: 1000px) {
  .nav { gap: 1.5rem; }
  .method { grid-template-columns: 1fr; gap: 0; }
  .method__step { padding-top: 1.75rem; padding-bottom: 1.75rem; }
  .method__step::before { right: 0; }
  .split { grid-template-columns: 1fr; }
  .split__aside { position: static; }
}


@media (max-width: 1000px) {
  .works { grid-template-columns: 1fr; }
}

@media (max-width: 768px) {
  /* The links used to be `display: none` here with nothing in their place, so
     below 768px the site had no navigation at all - one button and no way to
     reach Company or the work. Local owners open links on a phone, so this is
     the audience that had it worst.

     No hamburger and no overlay. A disclosure is a control that has to be
     learned and opened before it gives you anything, and there are two links.
     The header wraps to a second row instead: wordmark up top, the links and
     the button under it on their own rule. */
  .masthead { height: auto; padding-block: 0.7rem 0; }
  .masthead__inner { flex-wrap: wrap; gap: 0; }
  .nav {
    width: 100%; margin-left: 0; margin-top: 0.7rem;
    gap: 1.35rem; padding-block: 0.55rem; border-top: var(--rule);
  }
  .nav__link { font-size: 0.875rem; }
  .nav .btn { margin-left: auto; }
  .tiles--2 { grid-template-columns: 1fr; }
  .footer__grid { grid-template-columns: 1fr 1fr; gap: 2rem; }
  .footer__brand { grid-column: 1 / -1; }
  .ledger__row { grid-template-columns: 1fr; gap: 0.25rem; }
  .pagehero__media { position: relative; width: 100%; aspect-ratio: 3 / 2; z-index: 0; margin-top: -1.5rem; }
  .pagehero__media img { -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 35%); mask-image: linear-gradient(180deg, transparent 0%, #000 35%); }
}


/* .page-ink, the whole-page dark scheme, was deleted on 2026-09-18. It was on
   no page and never had been: Ibrah rejected a dark site on sight on
   2026-09-17 - "that's flooded in the market and makes us look generic" - and
   the design system kept the block only because its token-flip technique and
   its computed contrast pairs were worth reusing for a stage. `.stage` was
   written on 2026-09-18 and carries both, including the measured table, so
   the reason to hold this has been met and the duplicate is gone.
   ------------------------------------------------------------------------- */

/* The hero. Shorter than a .section so the object lands inside the first
   screen, and the type steps down because it is sharing the screen now. */
.section--hero { padding-block: clamp(2.5rem, 5vw, 4rem) clamp(4rem, 8vw, 6.5rem); }
.section--hero .pagehead__title { font-size: clamp(2.1rem, 4.6vw, 3.6rem); max-width: 20ch; }
.section--hero .pagehead__lede { margin-top: 1.125rem; max-width: 62ch; font-size: 1.0625rem; }
.section--hero .reg { margin-top: clamp(2rem, 4vw, 3rem); }



/* ==========================================================================
   PRODUCT SCREENS
   ==========================================================================
   Recovered 2026-09-17 from `nezaam-website` at commit 5d603b6 (8 July 2026),
   the site that was live while Ibrah was pitching this product. Commit
   94cc272 ("reposition to independent technical assurance") deleted the
   originals on 2 August; `QuillDemo.tsx` survived on the case-study page.

   Ibrah's call: this is the idea that works. The thing that narrates itself is
   not a diagram of the product, it is THE PRODUCT, animated, in the hero. A
   layman sees an app working. An accountant sees their own Monday screen.
   Neither needs a caption. A register of abstract marks, tried earlier the
   same day, needed one and was dropped.

   PORTED, NOT COPIED. The original is React, Next, motion/react and Tailwind
   tokens. This site has no framework and no build step, so the motion is CSS
   transitions with a --i stagger and the rows are real markup: without JS the
   screen renders complete and still, which is the opposite of how the
   register failed.

   RE-THEMED so it reads as Nezaam's own work rather than a port:
     old red-orange accent  -> --signal / --mark
     old panel + bg         -> --raise / --ground
     rounded-lg, -xl, -full -> radius 0, the site rule
     Inter-ish + old mono   -> Archivo + IBM Plex Mono

   ⚠ THE PRODUCT IS NOT NAMED HERE. It is named at app.nezaam.co.uk but stays
   unnamed on this site until the UKIPO check against Quill Pinpoint Ltd is
   run (registry risk 22). The original chrome read "Quill." and
   "app.quill, dashboard"; both are stripped at Ibrah's instruction.

   ⚠ CLAIMS STRIPPED, each one a fact problem and not a taste call:
     "LIVE AT A UK CHARTERED FIRM"  registry open risk 15 records this exact
                                    claim as unverified and confirmed
                                    inaccurate, off the internet 2026-08-04
     "Start a 7-day trial"          no trial exists
     "565+ leads a week"            unsourced
   The figures that remain are inside a screen captioned as an example.
   ========================================================================== */

.mock {
  border: 1px solid var(--line-strong);
  background: var(--raise);
  box-shadow: 0 30px 70px -40px rgba(23, 21, 15, 0.35);
}

/* --- Window chrome -------------------------------------------------------- */
.mock__bar {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.6rem 0.875rem;
  border-bottom: 1px solid var(--line);
  background: var(--ground);
}
.mock__dots { display: flex; gap: 0.375rem; }
.mock__dots i { width: 8px; height: 8px; background: var(--line-strong); }
.mock__addr {
  font-family: var(--mono); font-size: 0.625rem; letter-spacing: 0.06em;
  color: var(--muted-dim); border: 1px solid var(--line);
  padding: 0.2rem 0.5rem; background: var(--raise);
}
.mock__live {
  margin-left: auto; display: inline-flex; align-items: center; gap: 0.4rem;
  font-family: var(--mono); font-size: 0.625rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--muted-dim);
}
.mock__live i { width: 6px; height: 6px; background: var(--muted-dim); }

/* --- Frame ---------------------------------------------------------------- */
.mock__body { display: flex; min-height: 0; }

.mock__side {
  width: 148px; flex: none; display: none; flex-direction: column;
  border-right: 1px solid var(--line); padding: 0.75rem;
}
.mock__nav { display: flex; flex-direction: column; gap: 0.1rem; margin-top: 0.25rem; }
.mock__navItem {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.4rem 0.5rem; font-size: 0.7188rem; color: var(--muted);
}
.mock__navItem.on { background: color-mix(in srgb, var(--signal) 12%, transparent); color: var(--signal); font-weight: 600; }
.mock__navItem svg { width: 13px; height: 13px; flex: none; }
.mock__who {
  margin-top: auto; display: flex; align-items: center; gap: 0.5rem;
  border: 1px solid var(--line); padding: 0.4rem 0.5rem;
}
.mock__avatar {
  width: 22px; height: 22px; flex: none; display: grid; place-items: center;
  background: var(--signal); color: var(--signal-ink);
  font-size: 0.5625rem; font-weight: 700;
}
.mock__whoName { font-size: 0.6563rem; font-weight: 600; display: block; }
.mock__whoRole { font-family: var(--mono); font-size: 0.5313rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted-dim); }

.mock__main { min-width: 0; flex: 1; padding: 0.875rem; }
.mock__head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 0.5rem; }
.mock__hi { font-size: 0.9375rem; font-weight: 700; letter-spacing: -0.015em; }
.mock__date { font-family: var(--mono); font-size: 0.625rem; color: var(--muted-dim); margin-top: 0.15rem; }
.mock__cta { background: var(--signal); color: var(--signal-ink); font-size: 0.6563rem; font-weight: 600; padding: 0.35rem 0.6rem; }

/* --- Stat tiles ----------------------------------------------------------- */
.mock__kpis { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.5rem; margin-top: 0.875rem; }
.mock__kpi { border: 1px solid var(--line); padding: 0.55rem 0.625rem; background: var(--ground); }
.mock__kpiKey { font-family: var(--mono); font-size: 0.5313rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted-dim); }
.mock__kpiVal { font-size: 1.25rem; font-weight: 700; line-height: 1; letter-spacing: -0.02em; margin-top: 0.25rem; }
.mock__kpiSub { font-size: 0.5938rem; font-weight: 500; margin-top: 0.25rem; color: var(--muted-dim); }
.mock__kpiSub.hot { color: var(--signal); }

/* The bars grow from the baseline. scaleY only, so it stays on the compositor. */
.mock__bars { display: flex; align-items: flex-end; gap: 3px; height: 28px; margin-top: 0.5rem; }
.mock__bars i { flex: 1; background: color-mix(in srgb, var(--text) 15%, transparent); transform-origin: bottom; }
.mock__bars i.on { background: var(--signal); }

/* --- Panels --------------------------------------------------------------- */
.mock__panels { display: grid; gap: 0.5rem; margin-top: 0.5rem; }
.mock__panel { border: 1px solid var(--line); background: var(--ground); }
.mock__panelHead {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0.4rem 0.55rem; border-bottom: 1px solid var(--line);
}
.mock__panelTitle { font-size: 0.6875rem; font-weight: 600; }
.mock__panelTag { font-family: var(--mono); font-size: 0.5625rem; color: var(--muted-dim); }

.mock__row { display: flex; align-items: center; gap: 0.5rem; padding: 0.4rem 0.55rem; border-bottom: 1px solid var(--line); }
.mock__row:last-child { border-bottom: 0; }
.mock__pip { width: 6px; height: 6px; flex: none; }
.mock__rowMain { min-width: 0; flex: 1; }
.mock__rowTop { display: block; font-size: 0.6875rem; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mock__rowSub { display: block; font-size: 0.5938rem; color: var(--muted-dim); }
.mock__rowEnd { flex: none; font-family: var(--mono); font-size: 0.5938rem; color: var(--muted); }

.mock__feed { display: flex; gap: 0.5rem; padding: 0.4rem 0.55rem; border-bottom: 1px solid var(--line); }
.mock__feed:last-child { border-bottom: 0; }
.mock__feedDot { width: 5px; height: 5px; flex: none; margin-top: 0.3rem; background: color-mix(in srgb, var(--signal) 55%, transparent); }
.mock__feedText { font-size: 0.6563rem; line-height: 1.45; color: var(--muted); }
.mock__feedText b { font-weight: 600; color: var(--text); }
.mock__feedMeta { display: block; font-family: var(--mono); font-size: 0.5313rem; color: var(--muted-dim); margin-top: 0.1rem; }

/* --- The jobs board ------------------------------------------------------- */
.mock__board { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.5rem; padding: 0.625rem; background: var(--ground); }
.mock__col { border: 1px solid var(--line); background: var(--raise); display: flex; flex-direction: column; }
.mock__colHead { display: flex; align-items: center; justify-content: space-between; padding: 0.35rem 0.45rem; border-bottom: 1px solid var(--line); }
.mock__colTitle { font-size: 0.625rem; font-weight: 600; }
.mock__colN { font-family: var(--mono); font-size: 0.5625rem; color: var(--muted-dim); }
.mock__cards { display: flex; flex-direction: column; gap: 0.35rem; padding: 0.35rem; }
.mock__card { border: 1px solid var(--line); background: var(--ground); padding: 0.4rem; }
.mock__cardTop { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.25rem; }
.mock__cardName { font-size: 0.5938rem; font-weight: 600; line-height: 1.25; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mock__cardWho { width: 14px; height: 14px; flex: none; display: grid; place-items: center; background: var(--signal); color: var(--signal-ink); font-size: 0.4375rem; font-weight: 700; }
.mock__cardBot { display: flex; align-items: center; gap: 0.25rem; margin-top: 0.35rem; }
.mock__cardJob { font-size: 0.5rem; font-weight: 500; color: var(--muted); background: color-mix(in srgb, var(--text) 7%, transparent); padding: 0.1rem 0.25rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mock__cardDue { margin-left: auto; flex: none; font-family: var(--mono); font-size: 0.5rem; color: var(--muted-dim); }
.mock__cardDue.hot { color: var(--signal); }

/* --- The caption the original never had ----------------------------------- */
.mock__note {
  margin-top: 0.875rem; font-family: var(--mono); font-size: 0.6875rem;
  line-height: 1.7; color: var(--muted-dim); max-width: 62ch;
}

/* --- Motion ---------------------------------------------------------------
   One class turns the whole screen on. Every animated part reads --i for its
   place in the queue, so ordering is markup, not a timeline in JS. The final
   state is the DEFAULT and the offsets only apply once .anim is set by script,
   so no-JS, a frozen tab and a headless capture all get a complete screen. */

.js .mock.anim .mock__kpi,
.js .mock.anim .mock__row,
.js .mock.anim .mock__feed,
.js .mock.anim .mock__card {
  opacity: 0;
  transition: opacity 0.45s var(--ease), transform 0.45s var(--ease);
  transition-delay: calc(var(--i, 0) * 70ms);
}
.js .mock.anim .mock__kpi { transform: translateY(8px); }
.js .mock.anim .mock__row { transform: translateX(-7px); }
.js .mock.anim .mock__feed { transform: translateY(5px); }
.js .mock.anim .mock__card { transform: translateY(7px); }

.js .mock.anim.is-on .mock__kpi,
.js .mock.anim.is-on .mock__row,
.js .mock.anim.is-on .mock__feed,
.js .mock.anim.is-on .mock__card { opacity: 1; transform: none; }

.js .mock.anim .mock__bars i { transform: scaleY(0); transition: transform 0.5s var(--ease); transition-delay: calc(400ms + var(--i, 0) * 60ms); }
.js .mock.anim.is-on .mock__bars i { transform: scaleY(1); }

@media (prefers-reduced-motion: reduce) {
  .js .mock.anim .mock__kpi,
  .js .mock.anim .mock__row,
  .js .mock.anim .mock__feed,
  .js .mock.anim .mock__card { opacity: 1; transform: none; transition: none; }
  .js .mock.anim .mock__bars i { transform: scaleY(1); transition: none; }
}

/* --- The hero that holds it ----------------------------------------------- */
.lede-mock { display: grid; gap: clamp(2rem, 5vw, 3.5rem); align-items: center; }
.lede-mock__copy { min-width: 0; }
.lede-mock__title { font-size: clamp(2.1rem, 4.6vw, 3.5rem); font-weight: 700; line-height: 1.03; letter-spacing: -0.035em; max-width: 15ch; }
.lede-mock__title em { font-style: normal; color: var(--signal); }
.lede-mock__lede { margin-top: 1.375rem; max-width: 46ch; font-size: 1.0625rem; color: var(--muted); line-height: 1.65; }
.lede-mock__actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 2rem; }

@media (min-width: 900px) {
  .lede-mock { grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr); gap: clamp(2.5rem, 5vw, 4.5rem); }
  .mock__side { display: flex; }
  .mock__kpis { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .mock__panels { grid-template-columns: 1.15fr 1fr; }
}

@media (max-width: 720px) {
  /* Four columns of job cards at 375px is four columns of nothing. The board
     scrolls sideways instead, with the first column already in view. */
  .mock__board { grid-template-columns: repeat(4, minmax(140px, 1fr)); overflow-x: auto; }
}

/* --- Product screen corrections, found by capture ------------------------- */

/* Two of four labels wrapped to a second line, so the tiles sat at ragged
   heights. Short labels and no wrapping; the tiles now share a baseline. */
.mock__kpiKey { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mock__kpi { display: flex; flex-direction: column; }
.mock__kpi .mock__bars { margin-top: auto; padding-top: 0.4rem; height: 32px; }

/* The hero left ~250px of ground under it before the next section. */
.section--hero { padding-block: clamp(2.5rem, 5vw, 4rem) clamp(3rem, 6vw, 5rem); }

/* The mark in the product chrome is Nezaam's, drawn small. It was reading as
   three loose dots because the thread was too faint at this size. */
.mock__side > .wordmark__mark svg { width: 100%; height: 100%; }
.mock__side > .wordmark__mark { margin: 0.15rem 0 0.35rem 0.5rem; display: block; }

/* Columns ran to different depths because each one stopped at its last card.
   A board where the columns end raggedly reads as a broken layout, not as an
   empty column. */
.mock__board > .mock__col { align-self: stretch; }
.mock__cards { flex: 1; }

/* The owner initials were 7px inside a 14px block and rendered as an amber
   smudge. Legible, or it is decoration pretending to be data. */
.mock__cardWho { width: 17px; height: 17px; font-size: 0.5313rem; }

/* The jobs chrome reused the live indicator and inherited its uppercasing,
   so "14 jobs" shouted. It is a count, not a status. */
.mock__count { margin-left: auto; font-family: var(--mono); font-size: 0.625rem; color: var(--muted-dim); }

/* ==========================================================================
   BRINGING THE PRODUCT SCREENS ONTO THE SITE'S OWN THEME
   ==========================================================================
   Ibrah, 2026-09-17: the content is right but it still carries the old
   website's theme. He was right on both counts, and both are measurable
   rather than matters of taste.

   1 - GREEN. --good #1a6b33 appeared on exactly ONE page of six: this one.
       index, nazm, company, contact and audit are paper plus a single amber.
       Green arrived with the ported components and nothing else on the site
       speaks it. It is gone, and the meaning it carried is carried better by
       the palette that is already here:

         amber    this wants you       near deadlines, overdue, urgent
         neutral  nothing to do here   done, filed, ready, far off

       That is the same rule the register used, and it means the eye has one
       thing to find instead of three colours to decode. --good stays declared
       for now but nothing uses it; do not reach for it without deciding what
       a second accent is FOR.

   2 - MOTION. The site's own reveal is 0.7s over translateY(18px) with 0.08s
       between steps. The ported screens ran 0.45s over 5-8px with 70ms steps,
       which is motion/react's default and reads quicker and tighter than
       everything around it. Matched to the site below.

       Not matched exactly, and deliberately: .rise animates four big blocks,
       these screens animate twelve small ones. Twelve items at 0.7s and 18px
       is a hero that is still assembling itself two seconds in. Same curve,
       same direction, distance and duration brought most of the way up, and
       the step kept short so the last row still lands inside a second.
   ========================================================================== */

/* --- Colour ---------------------------------------------------------------- */

.mock__live { color: var(--muted-dim); }
.mock__live i { background: var(--muted-dim); }

.mock__kpiSub { color: var(--muted-dim); }
.mock__kpiSub.hot { color: var(--signal); }

/* Pips: the two that want attention are amber, the rest are drawn but quiet. */
.mock__pip { background: var(--line-strong); }
.mock__pip.is-near { background: var(--signal); }
.mock__pip.is-soon { background: var(--mark); }

.mock__cardDue { color: var(--muted-dim); }
.mock__cardDue.hot { color: var(--signal); }
.mock__cardDue.is-done { color: var(--muted); }

/* The chase card came from the same import and carried the same green. A
   received item is not an achievement, it is one less thing outstanding. */
/* --- Motion ---------------------------------------------------------------- */

.js .mock.anim .mock__kpi,
.js .mock.anim .mock__row,
.js .mock.anim .mock__feed,
.js .mock.anim .mock__card {
  transition: opacity 0.62s var(--ease), transform 0.62s var(--ease);
  transition-delay: calc(var(--i, 0) * 60ms);
}
.js .mock.anim .mock__kpi { transform: translateY(14px); }
.js .mock.anim .mock__row { transform: translateY(12px); }
.js .mock.anim .mock__feed { transform: translateY(12px); }
.js .mock.anim .mock__card { transform: translateY(12px); }

.js .mock.anim .mock__bars i { transition: transform 0.62s var(--ease); transition-delay: calc(460ms + var(--i, 0) * 60ms); }

/* The window chrome should arrive as one object, the way a .rise block does,
   rather than the frame sitting there while its contents fade in underneath. */
.js .mock.anim { opacity: 0; transform: translateY(18px); transition: opacity 0.7s var(--ease), transform 0.7s var(--ease); }
.js .mock.anim.is-on { opacity: 1; transform: none; }

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

/* ==========================================================================
   THE WEBSITE REVIEW PAGE - THE FINDING SHEET
   ==========================================================================
   Third build, 2026-09-18, to 06-growth/AUDIT-PAGE-REPLAN-2026-09-18.md.

   THE OBJECT IS THE ONE-PAGE FINDING WE HAND OVER, and it is the same .sheet
   from the hero to the door. It opens with three rows, all ours. It gains a
   tally, an opened entry, the rows that are deliberately never filled, and it
   ends ONE ROW SHORT - row 24, the reader's, empty.

   Why this object and no other: it is literally what a buyer pays for. Every
   earlier version of this page drew a picture ABOUT the service; this shows
   the service. The practice page learned the same lesson the hard way - check
   whether the real thing exists before inventing an object for it.

   ⚠ THE GUARD AGAINST BECOMING THE RETIRED REGISTER. That object was
   technically sound and was dropped for being a dashboard with 55 abstract
   marks and no protagonist in it. Three rules hold this apart from it and all
   three are structural, not decorative:

     1. FEW ROWS, IN ENGLISH. Findings a reader can read, never coded marks.
        If this ever needs a key, it has failed.
     2. IT HAS A PROTAGONIST AND IT IS US. Row 01 is our own failure. Do not
        reorder the sheet to put a client first.
     3. THE SHEET IS NEVER FULL. Row 24 stays empty. If a later pass fills it
        with an example, the close is gone.

   Everything below reads tokens only, so .stage carries the sheet onto ink
   without one parallel rule.
   ========================================================================== */

/* --- Kept from the build this replaces ------------------------------------
   The reader label, the section heading and the body measure were right and
   are reused by the opened entry. The rest of the .rd system went with the
   five archetypal readers.
   ------------------------------------------------------------------------- */

.rd__who {
  font-family: var(--mono); font-size: 0.6875rem; font-weight: 500;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--signal);
  margin-bottom: 1rem;
}
.rd__title {
  font-size: clamp(1.6rem, 2.9vw, 2.3rem);
  font-weight: 600; line-height: 1.08; letter-spacing: -0.028em; max-width: 19ch;
}
.rd__body { margin-top: 1.125rem; max-width: 50ch; color: var(--muted); font-size: 1rem; line-height: 1.7; }

/* --- The hero, and the sheet's first three rows ---------------------------- */

.sheet-hero { padding-block: clamp(2.5rem, 5vw, 4rem) clamp(4rem, 8vw, 6.5rem); }
.sh__lead {
  font-size: clamp(2.2rem, 5vw, 3.9rem);
  font-weight: 700; line-height: 1.02; letter-spacing: -0.038em; max-width: 16ch;
}
.sh__lede { margin-top: 1.375rem; max-width: 54ch; font-size: 1.0625rem; color: var(--muted); line-height: 1.65; }
.sh__note {
  margin-top: 1.25rem; font-family: var(--mono); font-size: 0.6875rem;
  line-height: 1.7; color: var(--muted-dim); max-width: 62ch;
}
.sh__caveat { margin-top: 1.75rem; padding-top: 1.25rem; border-top: var(--rule); }

/* --- The sheet -------------------------------------------------------------
   A document, not a table of data. Hairlines between rows and nothing else:
   no zebra, no card, no radius. It should read as something printed and
   handed over, which is what it is.
   ------------------------------------------------------------------------- */

.sheet {
  margin-top: clamp(2.25rem, 4.5vw, 3.25rem);
  border-top: 1px solid var(--line-strong);
  border-bottom: 1px solid var(--line-strong);
}

.sheet__head, .row {
  display: grid;
  grid-template-columns: 2.5rem minmax(0, 1fr);
  gap: 0.25rem 1rem;
  padding: 0.9rem 0;
  align-items: baseline;
}
.row + .row, .sheet__head + .row { border-top: 1px solid var(--line); }

.sheet__head {
  font-family: var(--mono); font-size: 0.5938rem; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--muted-dim);
  padding-block: 0.7rem;
}
/* On a narrow screen the head would label columns that are no longer beside
   each other, so it says nothing and is taken out of the accessibility tree
   with it. */
.sheet__head > :nth-child(3), .sheet__head > :nth-child(4) { display: none; }

.row__n { font-family: var(--mono); font-size: 0.6875rem; color: var(--muted-dim); }
.row__w {
  font-family: var(--mono); font-size: 0.8125rem; color: var(--text);
  overflow-wrap: anywhere;
}
.row__f { grid-column: 2; color: var(--muted); font-size: 0.9375rem; line-height: 1.6; max-width: 58ch; }
.row__f b { font-weight: 600; color: var(--text); font-family: var(--mono); font-size: 0.875rem; }
.row__d { grid-column: 2; font-family: var(--mono); font-size: 0.625rem; letter-spacing: 0.08em; color: var(--muted-dim); }

@media (min-width: 60rem) {
  .sheet__head, .row {
    grid-template-columns: 2.5rem 13rem minmax(0, 1fr) 7rem;
    gap: 1.25rem;
    align-items: baseline;
  }
  .sheet__head > :nth-child(3), .sheet__head > :nth-child(4) { display: block; }
  .row__f, .row__d { grid-column: auto; }
  .row__d { text-align: right; }
}

/* Ours. The amber is the site's two-state rule doing its job: this wants you.
   It is on the row number rather than the text, so the eye finds the three
   without the copy shouting. */
.row--own .row__n { color: var(--signal); }
.row--own .row__w { color: var(--text); }

/* --- The score -------------------------------------------------------------
   Renamed from .tally 2026-09-18: that class already exists at the top of this
   file as an inline mono counter used by index.html and nazm.html, and this
   block was silently overriding it on both. Three counts, and the third is the one that matters. Drawn as type at size
   rather than as a chart: three numbers do not need axes, and 23/23 is the
   kind of figure that should be read in one look.
   ------------------------------------------------------------------------- */

.score {
  display: grid; gap: 1px; background: var(--line); border: var(--rule);
  margin-top: clamp(2.25rem, 4.5vw, 3rem);
}
@media (min-width: 52rem) { .score { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.score__it { background: var(--ground); padding: clamp(1.4rem, 3vw, 2.1rem); }
.score__n {
  font-size: clamp(2.4rem, 5vw, 3.6rem); font-weight: 700;
  line-height: 1; letter-spacing: -0.04em;
}
.score__n span { font-size: 0.4em; font-weight: 500; color: var(--muted-dim); letter-spacing: 0; }
.score__k { margin-top: 0.75rem; color: var(--muted); font-size: 0.9375rem; line-height: 1.55; max-width: 26ch; }
.score__it--hot .score__n { color: var(--signal); }

/* --- The opened entry ------------------------------------------------------ */

.open { display: grid; gap: clamp(2rem, 4vw, 3rem); align-items: start; }
.open__copy, .open__view { min-width: 0; }
@media (min-width: 62rem) {
  /* Centred, not top-aligned: the bars are a short block beside a long one,
     and starting them both at the top left ~400px of dead ground under the
     bars on the first capture. */
  .open { grid-template-columns: minmax(0, 1fr) minmax(0, 0.92fr); gap: clamp(2.5rem, 5vw, 4.5rem); align-items: center; }
}
.open__kicker {
  font-family: var(--mono); font-size: 0.625rem; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--muted-dim); margin-bottom: 1.25rem;
}
.open__cap {
  margin-top: 1.25rem; font-family: var(--mono); font-size: 0.6875rem;
  line-height: 1.7; color: var(--muted-dim); max-width: 50ch;
}

/* --- Two real measurements, drawn to time ----------------------------------
   The width carries the ratio and the duration carries the real seconds, both
   read from --w and --s in the markup, so a number cannot be changed in the
   HTML without the drawing changing with it.

   ⚠ FINISHED IS THE DEFAULT. The collapse only applies once sheet.js adds
   .is-armed, so no-JS, a frozen tab and a headless capture all get full-length
   bars with their numbers. Under prefers-reduced-motion the script never arms
   them at all - nobody is made to sit and watch a wait they asked not to have.
   ------------------------------------------------------------------------- */

.bars { display: grid; gap: 1.75rem; }
.bar { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0.5rem 1rem; align-items: center; }
.bar__k { font-family: var(--mono); font-size: 0.6875rem; color: var(--muted); overflow-wrap: anywhere; }
.bar__v { font-family: var(--mono); font-size: 0.9375rem; color: var(--text); text-align: right; }
.bar__t {
  grid-column: 1 / -1; margin-top: 0.5rem;
  display: block; height: 14px; border: 1px solid var(--line);
  background: var(--raise);
}
.bar__t i { display: block; height: 100%; width: var(--w); background: var(--line-strong); }
.bar--hot .bar__t i { background: var(--signal); }
.bar--hot .bar__v { color: var(--signal); }

.bars.is-armed .bar__t i { width: 0; }
.bars.is-armed.is-on .bar__t i { width: var(--w); transition: width var(--s) linear; }

@media (prefers-reduced-motion: reduce) {
  .bars.is-armed .bar__t i { width: var(--w); transition: none; }
}

/* --- The rows that never get filled ----------------------------------------
   Beat 6 shows the restraint instead of claiming it. The finding column is a
   ruled line with nothing written on it, which says "this stays blank" more
   plainly than a sentence can.
   ------------------------------------------------------------------------- */

.sheet--void .row__w { color: var(--muted); }
.row--void .row__n, .row--void .row__d { color: var(--muted-dim); }
.row--void .row__f i {
  display: block; height: 1px; background: var(--line-strong);
  width: 100%; max-width: 30rem; opacity: 0.8;
}

/* --- The last row ---------------------------------------------------------- */

.last .sheet--last { margin-top: clamp(2rem, 4vw, 2.75rem); }
.row--last .row__n { color: var(--signal); }
.row--last .row__f { color: var(--muted-dim); font-family: var(--mono); font-size: 0.8125rem; }
.row__you { color: var(--muted-dim); }
.row__you.is-yours { color: var(--signal); }

.addr { margin-top: clamp(2rem, 4vw, 2.75rem); max-width: 34rem; display: grid; gap: 0.5rem; }
.addr__note {
  margin-top: 0.875rem; font-family: var(--mono); font-size: 0.6875rem;
  line-height: 1.75; color: var(--muted-dim); max-width: 54ch;
}
/* --- The turn, and its two photographs --------------------------------------
   The only image pair on the site that has to be read as ONE object: two
   frames of the same stone. So they sit in a single grid with 1px of rule
   between them and no gap wide enough to let the eye treat them as separate
   pictures - the same idiom the home page work grid uses, where a group reads
   as one thing that has been divided rather than as loose tiles.
   ------------------------------------------------------------------------- */

.pair {
  display: grid; grid-template-columns: 1fr; gap: 1px;
  background: var(--line); border: var(--rule);
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
}
@media (min-width: 46rem) { .pair { grid-template-columns: 1fr 1fr; } }

.pair__it { margin: 0; background: var(--ground); }
.pair__it img { width: 100%; height: auto; }
.pair__cap {
  padding: 0.875rem clamp(0.875rem, 2vw, 1.25rem) 1rem;
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.06em;
  color: var(--muted-dim); border-top: var(--rule);
}
.pair__note {
  margin-top: 1.5rem; max-width: 60ch;
  color: var(--muted); font-size: 1rem; line-height: 1.7;
}
.pair__note b { font-weight: 600; color: var(--text); }

/* --- Where we stop ---------------------------------------------------------
   Four limits, drawn as four rules rather than four cards. Restraint is the
   product in this section, so it gets the plainest possible treatment.
   ------------------------------------------------------------------------- */

.lims { display: grid; gap: clamp(1.5rem, 3vw, 2.5rem); margin-top: clamp(2.5rem, 5vw, 3.5rem); }
@media (min-width: 52rem) { .lims { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 82rem) { .lims { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.lim { border-top: 1px solid var(--line-strong); padding-top: 1.1rem; }
.lim__k { font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.08em; color: var(--text); line-height: 1.6; }
.lim__v { margin-top: 0.65rem; color: var(--muted); font-size: 0.9375rem; line-height: 1.65; }

/* --- The question, composed ------------------------------------------------
   Carried across from the page this replaces, unchanged apart from its
   ground. It was never the problem: it is the only thing on the page a reader
   can check about their own business tonight, without us, and it deliberately
   does not predict the answer.
   ------------------------------------------------------------------------- */
.try { display: none; margin-top: clamp(2rem, 4vw, 3rem); max-width: 46rem; }
.js .try { display: block; }
.js .try__static { display: none; }
.try__static {
  margin-top: clamp(1.75rem, 3.5vw, 2.5rem);
  font-family: var(--mono); font-size: clamp(1rem, 1.7vw, 1.25rem);
  line-height: 1.6; color: var(--text); max-width: 34ch;
}

.try__row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(1rem, 2.5vw, 2rem); }
.try__field { display: flex; flex-direction: column; gap: 0.5rem; }
.try__label {
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--muted);
}
/* A ruled line rather than a box, because every other edge on this site is a
   hairline and a rounded input would be the only soft corner on the page. */
.try__input {
  appearance: none; -webkit-appearance: none;
  background: transparent; border: 0; border-bottom: 1px solid var(--line-strong);
  border-radius: 0; padding: 0.45rem 0 0.55rem;
  font-family: var(--sans); font-size: 1.0625rem; color: var(--text);
  transition: border-color 0.25s var(--ease);
}
.try__input::placeholder { color: var(--muted-dim); opacity: 1; }
.try__input:hover { border-bottom-color: var(--muted-dim); }
.try__input:focus { outline: none; border-bottom-color: var(--signal); border-bottom-width: 2px; padding-bottom: calc(0.55rem - 1px); }
.try__input:focus-visible { outline: 2px solid var(--signal); outline-offset: 4px; }

.try__out {
  display: block; margin-top: clamp(1.75rem, 3.5vw, 2.5rem);
  font-family: var(--mono); font-size: clamp(1rem, 1.7vw, 1.25rem);
  line-height: 1.6; color: var(--text); max-width: 34ch;
}
/* The reader's own two words, landing inside the sentence as he types them. */
.try__out b { font-weight: 500; color: var(--signal); }

.try__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.875rem 1.25rem; margin-top: 1.75rem; }
.try__note { font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.04em; color: var(--muted-dim); }
#try-copy { min-width: 11.5rem; }

@media (max-width: 40rem) {
  .try__row { grid-template-columns: 1fr; gap: 1.5rem; }
}
