/* =============================================================================
   PRISM II — THE HERO AND THE DOORS. Every page opens with the hero; the home
   page alone closes its opening with the three figures.

   Loads after site_00_foundation, which owns the tokens, the type scale, .wrap,
   .sec, .shead, .eyebrow, .secnum, the button family, svg.ico and the prism's
   own strokes and ray colours. Nothing here restates any of those — this file
   positions the prism and paints the ground behind it, and that is all it does
   to the mark. Nothing here names a literal colour either: every value resolves
   through a token, so both themes and the light-hue rule are inherited rather
   than re-derived.
   ============================================================================= */

/* -----------------------------------------------------------------------------
   THE HERO

   Full-bleed without a horizontal scrollbar. The first version bled with
   margin-inline: calc(50% - 50vw). 50vw includes the scrollbar, so on any
   platform with a classic one the hero ran 8-17px wider than the viewport and
   gave the whole document a sideways scroll — on every page, since every page
   has a hero. The bleed is done with a padded wrapper instead: the section is an
   ordinary block that already spans its container, .inner carries the measure,
   and nothing in here is ever allowed to exceed 100%.

   The gradient's three stops are page-scoped custom properties, set at the foot
   of this file. Each falls back to --bg, so a page that somehow arrives without
   its body class opens on a plain ground rather than on an invalid gradient
   declaration that drops the background entirely and takes the grid with it.
   -------------------------------------------------------------------------- */
/* ONE HEIGHT ON EVERY PAGE (Jason, 2026-09-01, heights corrected 2026-09-02).

   The heroes used to be as tall as whatever they contained, and the spread was
   395px on /about to 810px on home — more than 2x, on the one component a
   visitor meets first on all seven pages. It read as seven designs rather than
   one, and the shortest page looked like a page that had lost something.

   The band is a flex container with a min-height; .inner is TOP-ALIGNED in it
   (see the comment below), so a page with less to say gets air below its
   content rather than a squat band. 656px clears the tallest hero's own
   content — 648px on /audit, a two-line headline, a lede, an ask and a mono
   line — with a little air under it, and leaves a sliver of the next section
   visible at the fold on a 900px viewport so the page reads as scrollable
   rather than as a wall.

   --hero-h is declared once here rather than left to the bare fallback, and
   is the ONLY value to change — it steps down twice below 920px. That single
   declaration is also what .prism's top reads (below): the mark's vertical
   position used to be a percentage of .inner's own rendered height, which is
   content-driven and therefore different on every page (measured spread:
   128px to 309px from the header). Anchoring it to this fixed token instead
   is what makes it — and the band itself — actually identical everywhere,
   not just documented as such. */
.hero {
  position: relative;
  overflow: hidden;
  border-bottom: 1px solid var(--line);
  display: flex;
  /* TOP-ALIGNED, NOT CENTRED (Jason, 2026-09-01, reversed twice same day — this is
     the final call). Centring was tried so a short page's CTAs wouldn't sit
     lopsided against the space above the eyebrow, but it bought that at a bigger
     cost: the eyebrow's vertical position became a function of how much each page
     had to say, so it landed in a different spot on every one of the seven pages
     instead of mirroring the home page. Top-alignment is the one rule that keeps
     the eyebrow — and the top margin above it — pixel-identical everywhere, which
     is the property that actually matters; the band's spare height falls below
     the content instead. */
  align-items: flex-start;
  --hero-h: 656px;
  min-height: var(--hero-h, 580px);
  background-image: linear-gradient(103deg,
    var(--h1, var(--bg)) 0%,
    var(--h2, var(--bg)) 48%,
    var(--h3, var(--bg)) 100%);
}

/* The measure. Padding uses --gutter rather than the mockup's flat 32, because
   site_00 already steps that token to 24 below 920 — so the breakpoint at the
   foot of this file only has to restate the vertical rhythm. */
.hero .inner {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 42px var(--gutter) 28px;
}

/* The 44px measurement grid, laid over the gradient and under the content. It is
   the same drafting language the prism is drawn in and the same --grid token the
   figures reuse, which is what stops the hero reading as decoration. */
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px);
  background-size: 44px 44px;
}

/* One mono line above the headline, and where a page has two labels they sit
   next to each other rather than at opposite ends of a hairline. The rule that
   used to span between them is gone (Jason, 2026-09-01), and the rung ordinals
   that sat on its right-hand end went with it — the ladder is what states a
   rung's number, and repeating it in the eyebrow made the page's first line a
   reference to a list the reader had not met yet. */
.eyerow { display: flex; align-items: baseline; gap: 10px; margin-bottom: 32px; flex-wrap: wrap; }
/* The separator the hairline used to be. Without it "LABS" and "LIVE AI SYSTEMS"
   sat 16px apart in the same face at the same size and read as one
   run-on label. The middot is the same separator the labels use inside
   themselves, so nothing new is introduced. */
.eyerow .mono + .mono::before { content: "·"; margin-right: 10px; color: var(--ink3); }

.hero h1 { max-width: 15ch; text-wrap: balance; }
/* HOME'S HEADLINE IS THE ONE THAT DOES NOT FIT THE SHARED RULE (2026-09-05).
   "Websites AI can read, software built for people, and systems that listen to
   both." is thirteen words against the stream pages' nine or fewer. At .t-96 in
   a 15ch measure it set in four lines at 1440 and pushed the band to 712px
   against the 656px every hero shares (hero_consistency_test). Three lines need
   the full .inner measure and a cap, and the breaks are forced in the markup so
   text-wrap is moot here.

   THE CAP IS 72px, AND IT WAS 80 UNTIL 2026-09-06, WHEN IT BROKE IN PRODUCTION
   ON THE EXACT FAILURE THE OLD COMMENT HERE NAMED AND THEN UNDER-BUDGETED FOR.
   That comment rejected 84 because "the first forced line is 1073px in a 1076px
   box, which is a different platform's font rendering away from wrapping", and
   settled on 80 — 1022px, 54px of slack, 5%. Google Fonts does not serve one
   Inter: the face live at www.cxmatters.com.au measures ~9% wider than the one
   the 1440 numbers above were taken against, so at 80px that first line is
   1116px in the same 1076px box. It wrapped, the headline set in four lines,
   and the band grew to 307px of type — which is what a very wide window shows
   and what a screenshot at the measuring machine never will, because both
   renderings are "Inter" and both report themselves loaded.

   So the cap is not "the largest size that fits", it is the largest size with
   real headroom. Measured live against the PRODUCTION face, first line: 80px →
   1116, 76 → 1060 (fits by 16px, 1.5% — still one rendering away), 74 → 1032,
   72 → 1004, i.e. 72px of slack, 6.7%. 72 also happens to be what
   .p-intelligence below already caps at, so the two long headlines now share a
   number rather than each carrying its own. Re-measure on the deployed site,
   never on localhost, if this is ever raised again. */
body.p-home .hero h1 { font-size: clamp(42px, 5.6vw, 72px); max-width: none; }
/* /customer-intelligence's headline gained a noun on 2026-09-05 (copy review
   task 22) and hit the same wall home did: eight words at .t-96 in a 15ch
   measure set in four lines at 1440 and took the band to 707px. 72px with the
   full measure sets it in three; measured at 1440: 471 / 720 / 730px, the
   longest line ending short of the prism's left edge at 756. */
body.p-intelligence .hero h1 { font-size: clamp(38px, 5vw, 72px); max-width: none; }
.hero .sub { margin: 28px 0 30px; max-width: 50ch; }

/* About is the one hero with no lede between the headline and .btns (see the
   page's own note: "a headline and no lede"), so .btns would otherwise sit
   flush against the h1 with none of the 30px gap every other hero gets from
   .sub's margin-bottom. Same value, applied directly to .btns instead. */
body.p-about .hero .btns { margin-top: 30px; }

/* -----------------------------------------------------------------------------
   THE PRISM, POSITIONED. Its strokes, its dotted construction and its three ray
   colours are site_00's and are not repeated here — this rule only says where
   the mark sits.
   -------------------------------------------------------------------------- */
.prism {
  position: absolute;
  right: var(--gutter);
  /* Not a percentage: that resolved against .inner's own rendered height,
     which is content-driven and different on every page (see the --hero-h
     comment above). calc() against the fixed token instead makes this
     identical across all seven heroes, regardless of headline length. */
  top: calc(var(--hero-h, 580px) * .52);
  transform: translateY(-50%);
  width: min(44%, 520px);
  pointer-events: none;
  z-index: 2;
}

/* -----------------------------------------------------------------------------
   THE DOORS. Three figures on the home page, each one the entrance to a stream.

   A DOOR IS PAINTED WITH THE LIGHT OF THE ROOM BEHIND IT. Each figure's drawing
   panel carries the exact gradient the hero of its destination opens with, so a
   reader has already met a page before they arrive at it and the arrival is a
   recognition rather than an introduction. The per-figure values are at the foot
   of this file, beside the hero gradients they are copied from, so the two can
   be checked against each other in one glance instead of across two screens.

   The panel is a --fig-* interface with a fallback for every property, which is
   what lets one component serve three destinations and still render correctly if
   it is ever used somewhere that sets none of them.
   -------------------------------------------------------------------------- */
.figs {
  display: grid;
  grid-template-columns: repeat(3, minmax(min(220px, 100%), 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}
.fig {
  min-width: 0;
  background: var(--bg);
  padding: 30px 28px 32px;
  display: flex;
  flex-direction: column;
}
.fig .lbl {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--fig-link, var(--ink3));
  margin-bottom: 22px;
}
/* The drawing panel: the destination's gradient and a hairline in the
   destination's own ink. IT CARRIED A 22px GRID UNTIL 2026-09-03 — the hero's
   44px drafting grid, halved because the panel is a quarter of the size. The
   halving is what killed it: at 172px tall you get eight cells of a texture too
   fine to read as graph paper and just coarse enough to interfere with a 0.9px
   line drawing, worst on figure 1.3 where the clay grid crossed the curve at the
   weight of the curve itself. The drafting language is not lost — the hero still
   carries its own grid, and the hairline and the drawings are what actually do
   the technical-drawing work here. --fig-grid went with it, from all three
   figures at the foot of this file. */
.fig .draw {
  height: 172px;
  display: grid;
  place-items: center;
  margin-bottom: 22px;
  border: 1px solid var(--fig-line, var(--line2));
  background-image:
    linear-gradient(103deg, var(--f1, var(--bg)), var(--f2, var(--bg)) 48%, var(--f3, var(--bg)));
}
.fig h3 { font-size: 19px; font-weight: 500; letter-spacing: -.014em; margin-bottom: 8px; }
.fig p { font-size: 16px; color: var(--ink2); margin: 0; }
/* margin-top:auto is what keeps the three links on one line across figures of
   different prose lengths, without the panel having to be measured. */
.fig .go {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: auto;
  padding-top: 20px;
  min-height: 44px;
  font-size: 16px;
  color: var(--fig-link, var(--cobalt));
  font-weight: 500;
}
.fig:hover { background: var(--bg2); }

svg.dr {
  width: 100%;
  height: 100%;
  stroke: var(--fig-stroke, var(--draw));
  fill: none;
  stroke-width: .9;
  stroke-linejoin: round;
  stroke-linecap: round;
}
svg.dr .hi { stroke: var(--fig-hi, var(--cobalt)); }
/* A filled point rather than a stroked one. svg.dr sets fill:none for line art, so
   the Observatory's dot field needs the fill handed back — in the drawing's own
   line colour, not currentColor, which on a card resolves to the link's ink and
   would put near-black dots inside a pale figure. */
svg.dr .dot { fill: var(--fig-stroke, var(--draw)); stroke: none; }

/* -----------------------------------------------------------------------------
   THE FRONT DESK BAND, standing alone (2026-09-02). It was the fourth child of
   .figs for a while ("the fourth way in"), but the heading above the doors says
   THREE ways in, and the doors are routes while this is an act-here surface —
   fused in, it read as a broken fourth card. Standalone it is the page's live
   demonstration. Everything it used to inherit from .fig/.figs is restated
   here: the border (was the .figs frame), the ground, the padding and its
   ≤560px step, the .lbl eyebrow metrics and the description paragraph. The
   .fig hover-lighten is deliberately NOT restated — that was a door affordance,
   and this block's interactive elements are its buttons.

   THERE IS NO .fd-orb RULE HERE, DELIBERATELY, AND ONE MUST NOT BE ADDED. The
   orb is a five-state, amplitude-driven instrument drawn entirely by
   labs_09_front_desk.css off shared/_front_desk_orb's two counter-rotating
   conic layers plus a ring — front_desk_orb_test pins its keyframes to
   transform/opacity only. This rule set supplies the CELL: size, hue, pace and
   the third gold step, via the FOUR custom properties labs_09 reads off an
   ancestor. All four are required — see the note on --fd-period below for what
   a missing one does and why it does it quietly.

   --fd-orb-size and --stage/--stage-ink carry over unchanged through every
   re-homing of this band — same values, so the orb's scale and colour don't
   silently change just because its section did. .fd-page-orb in labs_09
   mirrors the size values; change one and change the other. */
.fd-band {
  --fd-orb-size: clamp(112px, 13vw, 158px);
  /* THESE TWO ARE NOT OPTIONAL AND THEIR ABSENCE IS SILENT. Both were on
     .fd-band-stage before Prism II folded that rule into this one, which carried
     the size and the hues across and left these behind — so the band's orb sat
     dead on the homepage while the lab page's, which declares its own, moved.

     An undefined custom property inside a shorthand makes the whole declaration
     invalid AT COMPUTED-VALUE TIME: the property falls back to its initial value
     rather than the rule being dropped at parse time, so nothing warns and
     DevTools shows the authored rule intact. Without --fd-period every
     `animation: fd-drift var(--fd-period) ...` in labs_09_front_desk.css computes
     to `animation: none`, which kills all five states at once, not just idle.
     Without --fd-deep, .fd-orb-over's conic-gradient computes to
     `background-image: none` and one of the two aurora layers is never painted.

     24s matches .fd-page-orb in labs_09_front_desk.css — the two DECORATIVE
     surfaces move together, warmer than the overlay's near-still 90s, because
     both are shown before a call to invite one. --fd-deep mirrors .fd-stage's
     definition exactly: a darkness of the existing ink, never a new hue. */
  --fd-period: 24s;
  --fd-deep: color-mix(in srgb, var(--stage-ink, var(--clay)) 72%, #000);
  --stage: var(--fd-gold);
  --stage-ink: var(--fd-gold-ink);
  border: 1px solid var(--line);
  background: var(--bg);
  padding: 30px 28px 32px;
  /* Same rhythm .figs gets from .shead's margin-bottom (site_00_foundation.css)
     — it shares .sec with the doors above rather than opening its own, so this
     is the only thing separating it from the grid above it. */
  margin-top: 40px;
}
.fd-band-inner {
  display: grid;
  grid-template-columns: minmax(130px, max-content) 1fr auto;
  gap: 40px;
  align-items: center;
}
.fd-band-inner > * { min-width: 0; }
.fd-band-inner h3 { font-size: 19px; font-weight: 500; letter-spacing: -.014em; margin-bottom: 8px; max-width: 24ch; }
.fd-band-inner > div > p { font-size: 16px; color: var(--ink2); margin: 12px 0 0; max-width: 56ch; }

/* THE LINK IN THAT PARAGRAPH NEEDS AN AFFORDANCE (2026-09-04). "The live systems
   on this site" became home's only anchor to /labs, and with no rule it inherited
   the paragraph's own --ink2 with no underline: a link indistinguishable from the
   text around it, which is a link nobody clicks.

   Same treatment as `.facts a` in site_02, which is this site's established shape
   for an inline body link — the accent colour and a 3px-offset underline. The
   accent is a text partner on every page, so this can never become a light hue
   carrying type. */
.fd-band-inner > div > p a {
  color: var(--accent, var(--deep));
  text-decoration: underline;
  text-underline-offset: 3px;
}
/* The eyebrow metrics .fig .lbl used to supply. */
.fd-band .lbl {
  font-size: 12px;
  letter-spacing: .1em;
  text-transform: uppercase;
  margin-bottom: 22px;
}
/* The one mono line on the page that is not neutral: this is the warm end of
   the spectrum, and --stage-ink is the readable partner of the hue the tile is
   washed in. */
.fd-band .mono { color: var(--stage-ink); }

.fd-band-orb {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

/* The tile's third track: the call button and its note, stacked. */
.fd-band-act {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
}

/* -----------------------------------------------------------------------------
   THE EVIDENCE BAND. Three figures directly under the hero, standing behind the
   claim the hero makes. Hairlines rather than cards, for the same reason the
   ladder is a hairline list: three bordered boxes read as an advertisement, and
   the whole point of this band is that it reads as a citation.

   The figure takes the HUMAN face at display size. That is the one place on
   this page a serif appears outside a quotation, and it is deliberate: the
   audit report sets its scores in the same face, so the two artefacts agree
   typographically as well as numerically. It takes --accent, which is a text
   partner on every page it could render on, never a light hue.
   -------------------------------------------------------------------------- */
/* Bottom padding matches the sources row's own top margin (26px), so the row
   sits centred in the space below the last hairline. It used to be 44px, sized
   for a two-line closing paragraph that this band no longer has; left alone it
   would have doubled the gap below the row against the gap above it. */
.evidence { padding: 52px 0 26px; border-bottom: 1px solid var(--line); }
/* NO MEASURE ON THIS ONE. Every other h2 on the site is capped at 22ch, which
   is right for a heading that runs to a sentence or two; this heading is nine
   words and the cap broke it over two lines with a third of the row empty
   beside it. It is the page's thesis and it reads as one line. It still wraps
   below 920, where one line is not available at any size worth setting. */
.evidence h2 { max-width: none; text-wrap: balance; margin-bottom: 32px; }

.stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0 40px;
  border-top: 1px solid var(--line);
}
/* A plain block. These were flex columns with the citations pushed to the foot,
   back when each column carried its own: that was the only way to hold five
   labels level, and it bought the alignment with a dead gap above every one of
   them. The citations are one shared row now, so there is nothing to align and
   each column ends where its content ends. */
.stat { padding: 26px 0 22px; border-bottom: 1px solid var(--line); min-width: 0; }
.stat .num {
  font-family: var(--human);
  font-size: clamp(56px, 6vw, 84px);
  line-height: .95;
  letter-spacing: -.02em;
  color: var(--accent, var(--deep));
  margin: 0 0 14px;
}

.stat .what { font-size: 16px; color: var(--ink); max-width: 30ch; margin: 0 0 8px; }
.stat .how { font-size: 15px; color: var(--ink2); max-width: 32ch; margin: 0 0 18px; }
/* THE CITATION, ONCE. A row rather than a column per tile: it wraps as a whole,
   so no single label breaks across two lines the way "BrightLocal, Local
   Consumer Review Survey 2026" did, leaving its rule under the first line and a
   stub under the second. The label reads "Sources" so the row is announced as a
   citation rather than as five links somebody forgot to format. */
.srcline {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px 18px;
  /* site_00 caps every p at 64ch, which is right for prose and wrong for a row
     of five labels: it broke after the third with the rest of the row empty. */
  max-width: none;
  margin: 26px 0 0;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .04em;
  color: var(--ink3);
}
.srcline .lbl { color: var(--ink3); }
/* UNDERLINED, NOT BOTTOM-BORDERED. A border draws one rule under the whole
   inline box and ignores where the text wraps; text-decoration follows the
   text, and the offset keeps the rule off the descenders. */
.srcline a {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  color: var(--accent, var(--deep));
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: color-mix(in srgb, var(--accent, var(--deep)) 38%, transparent);
  transition: text-decoration-color .18s;
}
.srcline a:hover { text-decoration-color: var(--accent, var(--deep)); }
/* The mark rides with the text and never underlines: it is a sign, not a word. */
.srcline svg.ico { width: 11px; height: 11px; stroke-width: 2; align-self: center; }
/* -----------------------------------------------------------------------------
   PER-PAGE OPENINGS

   EACH PAGE OPENS AT ITS OWN POSITION ON THE SPECTRUM, and the accent is always
   the TEXT-weight partner of that position, never the light hue itself. The
   gradient is where a light hue belongs — it is a wash — and --accent is what
   the emphasised headline word, the current sheet item and the plain button
   hover all resolve through, so a light hue there is type at 2.3:1. That is the
   measurement the whole token split exists to prevent.

   Every stop is mixed against --bg, and every stop is then multiplied by
   --hero-k. The mixing alone was the mockup's mechanism and half of it is true:
   one set of declarations does produce pale prism light on a light ground. The
   other half is not. 55% of a LIGHT hue is light whatever it is mixed into, so
   the dark hero came out a mid-tone band — while the dark theme's inks are light,
   because they are meant for a dark ground.

   MEASURED, by sampling the rendered pixels rather than trusting an ancestor's
   declared background, which is what made this invisible for so long: the eyerow
   ran at 1.47:1, the free-audit line at 1.58:1, the lede at 2.41:1, and even the
   H1 at 4.30:1. Four failures on the first screen of every page, in the theme
   roughly half of visitors have set.

   --hero-k is the only thing that changes. The six positions on the spectrum stay
   exactly as they were written and the hue relationships between them are
   untouched; the band simply stops being bright enough to fight the type.

   The mockup scopes these with page IDs; the Rails layout sets a body class via
   content_for :body_class instead. The mapping, for checking against
   design/mock-26-09-01-prism-ii.html lines 479-510:

       mockup      this file            page
       ------      ---------            ----
       #home       body.p-home          home
       #vis        body.p-visibility    visibility
       #soft       body.p-software      software
       #ci         body.p-intelligence  customer intelligence
       #about      body.p-about         about
       #audit      body.p-audit         the free audit
   -------------------------------------------------------------------------- */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --hero-k: .28; }
}
:root[data-theme="dark"] { --hero-k: .28; }

body.p-home .hero {
  --h1: color-mix(in srgb, var(--apricot) calc(55% * var(--hero-k, 1)), var(--bg));
  --h2: color-mix(in srgb, var(--lilac) calc(40% * var(--hero-k, 1)), var(--bg));
  --h3: color-mix(in srgb, var(--ice) calc(55% * var(--hero-k, 1)), var(--bg));
}
body.p-visibility .hero {
  --h1: color-mix(in srgb, var(--cobalt) calc(15% * var(--hero-k, 1)), var(--bg));
  --h2: color-mix(in srgb, var(--cobalt) calc(24% * var(--hero-k, 1)), var(--bg));
  --h3: color-mix(in srgb, var(--ice) calc(42% * var(--hero-k, 1)), var(--bg));
}
body.p-software .hero {
  --h1: color-mix(in srgb, var(--peri) calc(28% * var(--hero-k, 1)), var(--bg));
  --h2: color-mix(in srgb, var(--lilac) calc(36% * var(--hero-k, 1)), var(--bg));
  --h3: color-mix(in srgb, var(--apricot) calc(36% * var(--hero-k, 1)), var(--bg));
}
body.p-intelligence .hero {
  /* TWO STOPS ARE LOWER THAN THE MOCKUP'S: apricot 54 rather than 62, rose 16
     rather than 26. This was the brightest ground of the six and the only one
     where --ink3 failed AA in the light theme — the eyerow's right-hand label sat
     at 4.36:1 over the rose stop, where the other five pages ran 4.60 to 5.19.
     Rose is also the hue the mockup itself says should stay rare, so a lighter
     hand here costs the least of any fix available. The other five heroes are
     exactly as written. */
  --h1: color-mix(in srgb, var(--apricot) calc(54% * var(--hero-k, 1)), var(--bg));
  --h2: color-mix(in srgb, var(--rose) calc(16% * var(--hero-k, 1)), var(--bg));
  --h3: color-mix(in srgb, var(--lilac) calc(40% * var(--hero-k, 1)), var(--bg));
}
body.p-about .hero {
  --h1: color-mix(in srgb, var(--apricot) calc(48% * var(--hero-k, 1)), var(--bg));
  --h2: color-mix(in srgb, var(--apricot) calc(24% * var(--hero-k, 1)), var(--bg));
  --h3: color-mix(in srgb, var(--sand) calc(70% * var(--hero-k, 1)), var(--bg));
}
/* THE LABS ARE PERIWINKLE — the translation, which is exactly what they do: turn
   what people said into what a business does about it. The mockup's System pane
   says so outright, and it is the hue the labs list on /customer-intelligence
   already sets every stage label in, so arriving from that list is a recognition.
   Lilac and ice carry it out to the cool end, because what the labs translate
   into is a machine reading. */
body.p-labs .hero {
  --h1: color-mix(in srgb, var(--peri) calc(30% * var(--hero-k, 1)), var(--bg));
  --h2: color-mix(in srgb, var(--lilac) calc(38% * var(--hero-k, 1)), var(--bg));
  --h3: color-mix(in srgb, var(--ice) calc(52% * var(--hero-k, 1)), var(--bg));
}
body.p-audit .hero {
  --h1: color-mix(in srgb, var(--ice) calc(64% * var(--hero-k, 1)), var(--bg));
  --h2: color-mix(in srgb, var(--apricot) calc(22% * var(--hero-k, 1)), var(--bg));
  --h3: color-mix(in srgb, var(--apricot) calc(55% * var(--hero-k, 1)), var(--bg));
}

body.p-home         { --accent: var(--deep); }
body.p-visibility   { --accent: var(--deep); }
body.p-software     { --accent: var(--plum); }
body.p-intelligence { --accent: var(--clay); }
body.p-about        { --accent: var(--clay); }
body.p-audit        { --accent: var(--deep); }
/* CLAY, THE WARM TEXT PARTNER, because a written message is the human end of the
   spectrum - the same reason /about takes it. It is a partner rather than a
   light hue, which is what colour_vocabulary_test requires of every page accent,
   and it must stay the FIRST declaration in this rule: that test's regex reads
   `--accent` immediately after the brace.

   NO --h1/--h2/--h3 TRIPLE. /contact has no hero, for the same measured reason
   /audit deleted its own - 656px of gradient before the first field puts every
   input below the fold on a laptop. A triple here would paint nothing and would
   have to be scaled by --hero-k anyway. */
body.p-contact      { --accent: var(--clay); }
body.p-labs         { --accent: var(--plum); }

/* THE PRISM READS DIFFERENTLY ON EACH PAGE. The three rays are a person, the
   translation and the machine, in that order; a page dims the readings it is not
   about, so the same mark states each page's subject without a second drawing.
   The rays are never recoloured — only their weight in the composition changes. */
body.p-visibility .prism .r1 { opacity: .22; }
body.p-intelligence .prism .r3 { opacity: .3; }
body.p-about .prism .r2,
body.p-about .prism .r3 { opacity: .28; }
/* The labs are the middle reading, so the outer two step back. */
body.p-labs .prism .r1,
body.p-labs .prism .r3 { opacity: .26; }

/* THE THREE DOORS, each on its destination's own opening gradient. Read these
   against the hero rules directly above: figure one is the visibility hero,
   figure two the software hero, figure three the customer-intelligence hero,
   with the link and label colours moved to the readable partner of each
   position — --deep, --plum, --clay — since those are type. */
body.p-home .figs .fig:nth-child(1) {
  --f1: color-mix(in srgb, var(--cobalt) calc(15% * var(--hero-k, 1)), var(--bg));
  --f2: color-mix(in srgb, var(--cobalt) calc(24% * var(--hero-k, 1)), var(--bg));
  --f3: color-mix(in srgb, var(--ice) calc(42% * var(--hero-k, 1)), var(--bg));
  --fig-line: color-mix(in srgb, var(--cobalt) calc(30% * var(--hero-k, 1)), var(--bg));
  --fig-stroke: var(--draw);
  --fig-hi: var(--cobalt);
  --fig-link: var(--deep);
}
body.p-home .figs .fig:nth-child(2) {
  --f1: color-mix(in srgb, var(--peri) calc(28% * var(--hero-k, 1)), var(--bg));
  --f2: color-mix(in srgb, var(--lilac) calc(36% * var(--hero-k, 1)), var(--bg));
  --f3: color-mix(in srgb, var(--apricot) calc(36% * var(--hero-k, 1)), var(--bg));
  --fig-line: color-mix(in srgb, var(--peri) calc(32% * var(--hero-k, 1)), var(--bg));
  --fig-stroke: var(--draw);
  --fig-hi: var(--peri);
  --fig-link: var(--plum);
}
body.p-home .figs .fig:nth-child(3) {
  /* 54 and 16, matching the customer-intelligence hero exactly — see the note
     there. A door that no longer matches its room is the rule failing silently. */
  --f1: color-mix(in srgb, var(--apricot) calc(54% * var(--hero-k, 1)), var(--bg));
  --f2: color-mix(in srgb, var(--rose) calc(16% * var(--hero-k, 1)), var(--bg));
  --f3: color-mix(in srgb, var(--lilac) calc(40% * var(--hero-k, 1)), var(--bg));
  --fig-line: color-mix(in srgb, var(--clay) calc(26% * var(--hero-k, 1)), var(--bg));
  /* THE STROKE TAKES NO --hero-k, AND THAT IS THE POINT. It was
     `color-mix(clay calc(55% * var(--hero-k)), var(--bg))` — the wash formula,
     applied to a line. --hero-k is .28 in dark, so the drawing was clay at 15.4%
     against a near-black ground and the curve, both plain dots and the baseline
     all disappeared into the plate; the ember highlight was the only thing left,
     so the figure read as a stray circle. Figures 1.1 and 1.2 never had this
     because they stroke in flat --draw. --draw-warm is that token's warm sibling
     and computes to the same colour this line used to produce in light mode. */
  --fig-stroke: var(--draw-warm);
  --fig-hi: var(--ember);
  --fig-link: var(--clay);
}


/* -----------------------------------------------------------------------------
   THE LAB HERO (2026-09-03). The band every /labs SHOW page now opens on, and the
   reason the six of them stopped looking like a different website.

   They opened on `.wrap.lp-head` — centred eyebrow, light title, paragraph, plain
   white — while /labs itself already opened on this component in periwinkle. So
   the index looked like the site and its six children did not, and a visitor
   arriving from a lab card met none of the mark, hue or copy that card showed
   them. Everything below is that component, unchanged, with three substitutions:
   the height, where the gradient stops come from, and a drawing instead of the
   prism.

   THE STAGE CLASS IS ON THE BAND, not on <body>, which is what puts --stage and
   its three companions in scope here. Nothing in this block names a hue — the
   whole point is that six pages share one rule and differ only by the token their
   own stage supplies, exactly as the lab cards do. A literal colour here is the
   failure colour_vocabulary_test exists to catch.
   -------------------------------------------------------------------------- */

/* TWO CLASSES, AND THAT IS DELIBERATE. `.hero` declares --hero-h INSIDE its own
   rule, so an ancestor cannot override it and neither can a bare class; the
   trailing media queries in this file restate `.hero { --hero-h }` twice more.
   0,2,0 beats all of them at every width, which is what makes this band the same
   height on all six pages — the requirement. */
.hero.lab-hero {
  --hero-h: 300px;

  /* THE THREE STOPS ARE THE LAB CARD'S OWN THREE STOPS, and the equality is the
     point rather than a coincidence. 16 / 24 / 42 is exactly what `.lab .dw`
     (site_06_surfaces.css) and `.lab-index-mark` (labs_03_index.css) paint their
     drawing plate with, so this band IS that plate enlarged — which is the site's
     existing "a door is painted with the light of the room behind it" rule,
     applied to the lab cards. Tuning one and not the other is silent, so a test
     holds the three numbers equal.
     --hero-k is not optional: without it a 42% wash of a light hue is still light
     on the dark ground, which is the failure documented against the heroes above. */
  --h1: color-mix(in srgb, var(--stage)    calc(16% * var(--hero-k, 1)), var(--bg));
  --h2: color-mix(in srgb, var(--stage)    calc(24% * var(--hero-k, 1)), var(--bg));
  --h3: color-mix(in srgb, var(--stage-lt) calc(42% * var(--hero-k, 1)), var(--bg));

  /* em.it and .o both read --accent, and no body.p-* rule sets one for a lab
     page. Pointing it at the stage's TEXT partner is what makes the headline's
     accent word the lab's own colour rather than site_00's --plum fallback. */
  --accent: var(--stage-ink);
}

/* 34/28 rather than the marketing 42/28. The arithmetic is tight enough to write
   down, because overflowing a fixed-height band clips in silence: 62px of padding,
   a 15px eyerow on a 22px margin, a one-line t-56 headline at ~59px, and a
   three-line lede at ~88px on a 20px margin comes to ~266 of the 300. A two-line
   headline or a fourth line of lede does not fit. */
.hero.lab-hero .inner { padding: 34px var(--gutter) 28px; }
.hero.lab-hero .eyerow { margin-bottom: 22px; }
.hero.lab-hero h1 { max-width: 20ch; }
.hero.lab-hero .sub { margin: 20px 0 0; max-width: 56ch; }

/* The stage word is the one coloured item in the row; "Labs" and the kind stay in
   --ink3, so the eyebrow reads as a breadcrumb with one emphasis rather than three
   competing labels. --stage-ink is the only one of the four stage tokens that may
   take a `color`. */
.hero.lab-hero .eyerow .mono.is-stage { color: var(--stage-ink); font-weight: 500; }

/* THE LAB INTRO AND THE STANDARD HEIGHT (2026-09-24, Jason, from the indexing audit).
   Each lab band carries a second paragraph from the catalogue (`intro:`), so the
   thin lab pages have text a crawler reads. A band that carries one is 400px on
   every lab: the intros run 36 to 48 words and the tallest measured 359px, so one
   height holds all six. It is a min-height (.hero reads --hero-h that way), so a
   longer intro later grows the band rather than being clipped. /labs/data has no
   intro and keeps the base 300px above, which colour_vocabulary_test pins.

   `text-wrap: pretty` on both paragraphs is Jason's rule from the review: no line
   may end on a single word. The 64ch measure is for the same reason; at 60ch the
   Front Desk intro left its last word alone. */
.hero.lab-hero.has-intro { --hero-h: 400px; }
.hero.lab-hero .sub { text-wrap: pretty; }
.hero.lab-hero .lab-hero-intro {
  margin: 18px 0 0;
  max-width: 64ch;
  font-size: 16px;
  line-height: 1.6;
  color: var(--ink2);
  text-wrap: pretty;
}

/* THE MARK IS THE BAND'S FIGURE. z-index 0 puts it over .hero::before's drafting
   grid and under .inner, which sits at 1. It bleeds off the right edge on purpose
   — .hero's own overflow: hidden is what crops it — so the drawing reads as the
   light the page is painted in rather than as a picture in a box. */
.lab-hero-mark {
  position: absolute;
  z-index: 0;
  top: 50%;
  right: 0;
  transform: translate(15%, -50%);
  width: min(46%, 460px);
  aspect-ratio: 200 / 150;
  opacity: .55;
  pointer-events: none;
  --fig-stroke: var(--stage-draw, var(--draw));
  --fig-hi: var(--stage);
}

/* THE STROKE MUST NOT SCALE, AND THIS IS THE WHOLE TRAP IN THIS BLOCK. svg.dr sets
   stroke-width .9 against a 200x150 viewBox, sized for the ~168px plate these marks
   were drawn for; there it renders about 0.76px. Stroke scales with the viewBox, so
   at 460px the same .9 renders about 2.1px and the drawing stops being line art —
   a different picture from the one on the card two clicks away, and one that looks
   acceptable in a screenshot and wrong in person.
   A hand-tuned smaller stroke-width would land it back at the right weight AT ONE
   WIDTH and drift the moment the box changes, which it does at every breakpoint.
   non-scaling-stroke pins the rendered weight in CSS pixels whatever the box does,
   so 1.1px here is 1.1px at every size this band is ever drawn at. */
.lab-hero-mark svg.dr {
  vector-effect: non-scaling-stroke;
  stroke-width: 1.1;
}

/* THE BAND GOES WITH THE HEAD IT REPLACED. labs_00's `.is-conversing .lp-head`
   hid the Listening Post's header the moment a question was asked, clearing the
   screen for the answer; the hero is that header now, and at 300px it is a third
   of a phone viewport of gradient above a conversation. Declared here rather than
   in labs_00 so the rule sits with the component it hides. */
/* THE DATASET'S BAND HAS NO STAGE TO PAINT IT, and that is the whole of this rule.
   The corpus is not a lab: there is no light hue to spend, so the three stops fall
   back to the page's own warm neutrals rather than to a grey invented for the
   occasion, and the drawing strokes in --draw with no accent — exactly what its
   entry on /labs already does, for the same reason.
   Declared as three --h stops rather than by handing --stage a neutral, because a
   token like --bg3 mixed at 16% against --bg is very nearly --bg: the percentages
   above assume a saturated hue, and the band would simply have vanished. */
.hero.lab-hero.is-dataset {
  --h1: var(--bg2);
  --h2: var(--bg3);
  --h3: var(--bg2);
  --stage-ink: var(--ink);
  --stage-draw: var(--draw);
  --stage: var(--draw);
}

/* THE ONE MARK THAT MUST NOT BLEED. Every other drawing ends in open strokes —
   dots, bars, rays — so running it off the edge reads as a figure continuing past
   the frame. The dataset's is a TABLE: a closed rectangle, and a closed rectangle
   cut mid-border does not read as a bleed, it reads as a rendering fault. So this
   one sits fully inside the band, at the gutter, and is sized down to clear 300px
   at its 200x150 ratio rather than being cropped to fit. */
.hero.lab-hero.is-dataset .lab-hero-mark {
  right: var(--gutter);
  transform: translateY(-50%);
  width: min(34%, 320px);
  opacity: .5;
}

.is-conversing .lab-hero { display: none; }

/* /labs/data's two live figures, in the same position for the same reason: the
   band is one shape on all seven pages, and the counts are the dataset's, not the
   band's. .dt-stats' own metrics are labs_02's and are untouched. */
/* WIDTH 100% IS LOAD-BEARING, and the reason is not obvious. `main.lp-page` is a
   flex column (labs_00) and `.wrap` centres itself with `margin: 0 auto` — an auto
   cross-axis margin beats `align-items: stretch`, so a .wrap shrinks to its own
   content instead of filling the measure. Every other .wrap on these pages holds a
   table or a composer and is wide enough that it never showed; this one holds two
   short figures and rendered as a 289px box floating in the middle of the page. */
.dt-stats-row {
  width: 100%;
  padding-block: 26px 0;
}

@media (max-width: 680px) {
  /* Below 680 the hero is content-height, not --hero-h, and the home H1's
     forced breaks (see .hero h1 above) stop helping: at 390 they left
     "systems" alone on a line. Let it wrap naturally here. Same for
     /customer-intelligence's, added 2026-09-05. */
  body.p-home .hero h1 br,
  body.p-intelligence .hero h1 br { display: none; }

  .dt-stats-row { padding-block: 18px 0; }
}


@media (max-width: 920px) {
  /* Three columns at 40px apart put each figure's supporting line under about
     28 characters here, which is narrower than the figure itself. */
  .stats { grid-template-columns: 1fr; }
  .evidence { padding: 40px 0 34px; }

  /* THE PRISM MOVES; IT DOES NOT DISAPPEAR. The first version set it to
     display:none below this width, which took the site's signature mark away
     from the majority of its traffic — the one drawing a prospect has already
     met on the free audit report, hidden from every phone. A second version
     went static under the headline at full width, which fixed the hiding but
     traded it for a new problem: a ~200px drawing before a word of explanation,
     gating the headline on every phone.

     This version keeps the mark in the SAME row as the eyebrow instead — the
     one line with visible slack next to it on every phone — as a compact
     companion glyph rather than a shrunk poster. `_prism`'s render call moves
     in the view from before `.eyerow` to its last child, so this is a sizing
     rule, not a positioning one: nesting one level deeper changes nothing
     above this breakpoint, since `.prism` is still absolutely positioned
     against `.hero .inner` there regardless of DOM depth.

     At this size the entry beam and the four measurement ticks are dropped —
     construction detail, not identity — keeping only the triangle and the
     three rays, still unmistakably three diverging lines in their three ray
     colours. The full 204x80 viewBox still includes the now-hidden beam as
     dead space on its left, so `.prism svg` is scaled and shifted to crop into
     just the triangle+rays region rather than shrinking the whole drawing
     uniformly into a smudge. */
  .eyerow { justify-content: flex-start; }

  .prism {
    position: static;
    transform: none;
    width: 84px;
    height: 36px;
    max-width: none;
    margin-top: 0;
    margin-left: auto;
    opacity: .9;
    overflow: hidden;
    flex: 0 0 auto;
    align-self: center;
  }
  .prism .pz-beam,
  .prism .pz-tick { display: none; }
  /* Solid rather than dotted at this scale: the construction-drawing dots
     (drawn 4.5 viewBox units apart) shrink to sub-pixel dashes once the mark
     is scaled down this far and all but disappear — a continuous stroke is
     what stays legible as three coloured lines rather than a faint smudge. */
  .prism .pz-body path,
  .prism .pz-fan path { stroke-dasharray: none; }
  .prism svg {
    width: 132px;
    height: auto;
    transform: translate(-47px, -8px);
  }

  /* The prism no longer adds a ~200px block above the headline, so the
     content is shorter here than it was — 640px still clears the tallest of
     the seven at this width and keeps them equal. */
  .hero { --hero-h: 640px; }
  .hero .inner { padding: 30px var(--gutter) 20px; }
  .figs { grid-template-columns: 1fr; }
  .fd-band-inner { grid-template-columns: 1fr; gap: 26px; }
}

@media (max-width: 620px) {
  /* Mirrored by .fd-page-orb in labs_09 — change one and change the other. */
  .fd-band { --fd-orb-size: 96px; }
}

@media (max-width: 560px) {
  /* BELOW 560px THE HEROES ARE NOT ALL THE SAME HEIGHT, and that is the deliberate
     choice rather than an oversight. On a 390px screen the tallest of them is
     871px on its own content — a three-line headline, a five-line lede and the
     prism now stacked under both — which is already taller than the 844px
     viewport. Holding every page to that would open /about, which has no lede and
     no buttons, on a screenful of empty band above the fold.
     Consistency is worth having where it can be seen: a visitor moves between
     pages on a desktop and notices the band changing shape, and on a phone they
     meet one page at a time and never compare. So the floor here is a floor and
     nothing more. */
  .hero { --hero-h: 460px; }
  .fig, .fd-band { padding: 24px 20px 26px; }
}

/* The labs' own breakpoint, not the site's — 680px is where
   labs_01_composer_mobile.css takes over every lab page, and it sits below this
   file's 920/620/560 sequence for that reason rather than by accident.

   THE BAND STEPS, AND IT IS STILL IDENTICAL ON ALL SIX PAGES AT ANY GIVEN WIDTH,
   which is what "the same height everywhere" is actually protecting. A rigid
   300px on a 428x926 phone is a third of the screen before the lab starts, and it
   would undo the compression labs_01 applies specifically so the Listening Post's
   empty state fits one viewport.

   The mark pulls further right and drops back as it goes: at this width the
   headline and the lede run most of the band, and a drawing at .62 behind them is
   texture competing with type rather than a figure beside it. */
@media (max-width: 680px) {
  .hero.lab-hero { --hero-h: 240px; }
  .hero.lab-hero .inner { padding: 26px var(--gutter) 22px; }
  .hero.lab-hero .eyerow { margin-bottom: 16px; }
  .hero.lab-hero .sub { margin-top: 14px; }
  /* 420px on a phone: the tallest intro measured 380px at 428px wide. */
  .hero.lab-hero.has-intro { --hero-h: 420px; }
  .hero.lab-hero .lab-hero-intro { margin-top: 14px; font-size: 15px; }

  .lab-hero-mark {
    width: min(52%, 300px);
    transform: translate(26%, -50%);
    opacity: .3;
  }
}
