/* ==========================================================================
   exp33 . HERO v2 . the copy layer that sits over the living intent graph
   Loaded LAST on every exp33 page. Everything hero-specific is scoped to
   .hero33; the two rules that are not (the header's demo action and the
   headline weight split) are deliberately site-wide so the change is
   consistent across index / graph / method / onboarding / demo / prove.

   WHY THIS FILE EXISTS
   The founder's verdict on hero v1: "with the headline blurred out, somebody
   should be able to look at the animation and say: that's the Proof site."
   Getting there is not only a background change. It also needs
     - a headline with a split weight instead of one uniform display line,
     - ONE interactive object, built like an instrument rather than a pill,
     - a header action that stops competing with it,
     - and a lot more black space, so the hero gets deeper, not busier.
   ========================================================================== */

/* ==========================================================================
   1. THE COPY GUARD IS A DIMMER, NOT A PLATE
   exp33.css paints a masked wash of the page's own ground colour behind the copy
   column (.hero-film::after, strength --film-scrim). Two earlier passes both got
   it wrong, in opposite directions, and this is the middle they bracket.

   PASS ONE: A PLATE. --film-scrim .42-.68 per theme, over a mask with a wide
   opaque PLATEAU (solid to 34% of the ellipse before it began to feather) and a
   further 1.10-1.45x lift for the pull-back beat. It did its contrast job
   perfectly and it is exactly why the founder could see nothing behind the hero
   copy: the ground painted at half strength over the densest part of the frame
   does not protect the graph's neighbours, it deletes them, and it deletes them
   over an area large enough to read as a hole.

   PASS TWO: NOTHING AT ALL. --film-scrim 0 and content:none, so the graph ran
   under the copy at full tier. With the envelope raised at the same time, the
   type stopped sitting forward -- the founder's "protect a bit hero text".

   WHAT IS HERE NOW: about a THIRD of pass one's strength, and no plateau. The
   mask is a single continuous feather from the centre of the copy column out to
   nothing, so there is no edge anywhere for the eye to find, and the graph is
   never knocked out -- at the deepest point of the guard it still keeps roughly
   three quarters of its luminance, and the flanks and the whole periphery keep
   all of it. It reads as the copy having its own quiet air around it.

   THE ELLIPSE IS THE MEASURED GLYPH COLUMN, NOT THE COPY BOX. Swept at
   1440x900 (craft/glyphbox.mjs), the union of every text run in the headline,
   the machine line, the subhead, the CTA row and the facts line is
       terminal  x 13.2..65.5%  y 17.0..76.2%   centre 39.3% 46.6%
       centred   x 25.6..74.4%  y 18.2..75.2%   centre 50.0% 46.7%
   of the film box -- the tube left-aligns its copy, every other theme centres
   it, so the guard has two centres and no other per-theme geometry. The radii
   are ~1.75x the union's half-extent, which puts the outermost glyphs at ~.6 of
   full guard strength and lands the last stop outside anything the copy touches.

   PLACEMENT STILL DOES MOST OF THE WORK, and none of it is revoked: the copy's
   line boxes were swept across the whole viewport matrix (craft/lines.mjs), the
   horizontal bands that no viewport puts a glyph in are y 175..194, 215..246,
   506..515, 596..622, 757..826 and 867..899 in graph user space, and every
   LABELLED CARD above the fold sits in one of them. What passes behind the
   glyphs is edges, dots and ticks. The guard is the second half of that
   argument, not a replacement for it: craft/regress.mjs check D and
   craft/contrast4.mjs measure the result at the glyph boxes at every beat.
   ========================================================================== */
.hero33{--film-scrim:.26;}
.hero33 .hero-film::after{
  content:"";
  /* one continuous feather; no solid plateau at any radius */
  -webkit-mask-image:radial-gradient(44% 52% at 50% 47%,
      rgba(0,0,0,1) 0%,rgba(0,0,0,.94) 22%,rgba(0,0,0,.72) 46%,
      rgba(0,0,0,.40) 70%,rgba(0,0,0,.14) 87%,transparent 100%);
          mask-image:radial-gradient(44% 52% at 50% 47%,
      rgba(0,0,0,1) 0%,rgba(0,0,0,.94) 22%,rgba(0,0,0,.72) 46%,
      rgba(0,0,0,.40) 70%,rgba(0,0,0,.14) 87%,transparent 100%);
}
/* the tube left-aligns the hero copy, so the guard follows the copy, not the
   frame; the radius is a touch wider because the tube's glyph column is wider */
html[data-theme="terminal"] .hero33 .hero-film::after{
  -webkit-mask-image:radial-gradient(46% 52% at 39% 47%,
      rgba(0,0,0,1) 0%,rgba(0,0,0,.94) 22%,rgba(0,0,0,.72) 46%,
      rgba(0,0,0,.40) 70%,rgba(0,0,0,.14) 87%,transparent 100%);
          mask-image:radial-gradient(46% 52% at 39% 47%,
      rgba(0,0,0,1) 0%,rgba(0,0,0,.94) 22%,rgba(0,0,0,.72) 46%,
      rgba(0,0,0,.40) 70%,rgba(0,0,0,.14) 87%,transparent 100%);
}
/* brutal keeps hard geometry: a flat wash at half strength, never a soft
   ellipse, which on that theme would read as a smudge. exp33.css states the
   rule; it is restated here only because this file loads later. */
html[data-theme="brutal"] .hero33 .hero-film::after{
  -webkit-mask-image:linear-gradient(#000,#000);mask-image:linear-gradient(#000,#000);
}
/* light grounds show a wash of their own ground sooner than a dark one does,
   and they carry less graph to knock down in the first place */
html[data-theme="paper"] .hero33,html[data-theme="riso"] .hero33,
html[data-theme="brutal"] .hero33,html[data-theme="olo"] .hero33,
html:not([data-theme]) .hero33{--film-scrim:.19;}
/* the pull-back multiplier is NOT coming back: the guard is the same strength
   at every beat, including the wide one, so no beat can be the one that hides
   the graph behind the copy */
.hero33{--film-wide:1;}

/* ==========================================================================
   2. TYPOGRAPHY: one face, two weights
   Line 1 is the subject, line 2 is the claim. Splitting the weight rather than
   the size or the colour is what gives the headline a character of its own
   without adding a second voice. The claim is forced onto its own line so the
   break is deterministic at every width.
   NO GREEN IN THE HEADLINE: green means verified, and a headline is not a
   verdict. Every theme that painted .h1-em in its accent is overridden to the
   plain ink token. The single exception, and the reason it is an exception, is
   documented in section 6.
   ========================================================================== */
.hero33 .hero-copy h1{
  font-weight:500;
  letter-spacing:-.03em;
  max-width:22ch;
  text-wrap:normal;
}
.hero33 .hero-copy h1 .h1-em{
  display:block;
  font-weight:680;
  letter-spacing:-.038em;
  color:var(--ink);
  text-shadow:none;
}
html[data-theme="night"] .hero33 .hero-copy h1 .h1-em,
html[data-theme="brutal"] .hero33 .hero-copy h1 .h1-em,
html[data-theme="olo"] .hero33 .hero-copy h1 .h1-em{color:var(--ink);text-shadow:none;}
/* acid fills its display with a violet-cyan-CHARTREUSE gradient. In the hero
   that is both a second voice and a green claim, so the hero headline drops
   back to the plain ink token. Section H2s keep the gradient. */
html[data-theme="acid"] .hero33 .hero-copy h1{
  background:none;
  -webkit-background-clip:border-box;background-clip:border-box;
  color:var(--ink);
}
/* riso prints its display in klein blue with a deliberately misregistered pink
   pass. Forcing line 2 to black ink read as a broken print, so both lines keep
   the theme's own ink and the weight split alone carries the split. */
html[data-theme="riso"] .hero33 .hero-copy h1 .h1-em{
  color:var(--signal);text-shadow:.055em .05em 0 rgba(255,61,154,.75);
}

/* the machine annotation under line 2. Brand texture, not copy: aria-hidden,
   never a sentence, and it is quieted by TRACKING and SIZE, not by dimming.
   IT WAS 9.5px AT .44 EFFECTIVE ALPHA. Founder review item 14 puts a floor of
   11-12px on metadata and states the principle behind it: "the terminal identity
   should not depend on making text difficult to read." Measured against the
   painted hero ground -- graph, film and scrim, worst pixel inside the glyph box
   -- 9.5px at var(--muted) x opacity .55 landed at 3.81:1 on the tube, under the
   4.5:1 small-text floor and the only hero element that was. The craft layer
   raises the size to 12px; the alpha is fixed here, where it is set.
   .62 of the ink token measures 6.0-7.2:1 across the eight themes, which is the
   same band the subhead and the receipt already sit in, and the line still reads
   as a machine stamp because .34em of tracking is what makes it one. */
.hero-mach{
  margin:clamp(1.1rem,1.8vw,1.5rem) auto 0;
  font-family:var(--mono);font-size:11px;font-weight:500;
  letter-spacing:.34em;text-transform:uppercase;
  color:rgba(var(--ink-rgb),.76);opacity:1;
  white-space:nowrap;
}
.hero-mach i{font-style:normal;opacity:.8;padding:0 .1em;}
html[data-theme="terminal"] .hero-mach{margin-left:0;margin-right:0;}

.hero33 .hero-sub{
  margin-top:clamp(1.2rem,2vw,1.7rem);
  /* 2026-08-27 leads-fill-the-column ruling: was clamp(1.08rem,1.35vw,1.22rem),
     which set the flagship sub SMALLER than body copy at 1280 (17.3px) on the
     widest column of the site. Steps toward the sitewide lead scale while
     staying under it — the hero budget (clamp band height) absorbs it because
     the wider measure costs a wrapped line. */
  font-size:clamp(1.14rem,1.5vw,1.3rem);
  max-width:52ch;
  /* MEASURED, not eyeballed: --ink-60 lands at 5.1:1 on the paper ground, and
     the brief asks for 7:1 minimum behind the graph. .78 of the ink token
     clears 8.7:1 on paper and 10.5:1 on the near-black grounds. */
  color:rgba(var(--ink-rgb),.78);
}
/* riso's ink is a print blue-black on cream: .78 of it lands at 6.7:1, so this
   one theme needs a heavier pass to clear the bar. */
html[data-theme="riso"] .hero33 .hero-sub{color:rgba(var(--ink-rgb),.88);}

/* ==========================================================================
   3. THE ONE INTERACTIVE OBJECT
   A Proof machine control, not a CTA pill: 2px corners, a hairline border in
   the site's own line colour, a mono label, and a small state lamp on the
   left. The lamp is var(--signal) -- an affordance, never a state -- because
   in this theme green would read as "verified" and a button is not a verdict.
   ========================================================================== */
/* CTA VISUAL POLISH (founder review, "CTA visual polish").
   The verdict was that the control "looks a little like a small toolbar button
   relative to the enormous headline", with the targets: 46-50px tall, generous
   horizontal padding, a visible focus AND hover state, 1-3px corners, no
   rounded pill, and the icon clearly separated from the label.
   Measured before: 46.00 x 268.80 px box, 11.52px label, 15.2/18.4px padding,
   13.6px label-to-icon gap. Measured after: 50.00 x 331.86 px, 12.80px label,
   21.6/25.6px padding, and the icon is fenced off by its own hairline.
   That is +8.7% height and +23% width -- the founder's "perhaps 10-15%" is a
   size instruction, not a tolerance, and the label needed the extra 1.28px more
   than the box needed the extra 4px.
   The box is deliberately NOT scaled by a font-size bump alone: the label is
   .8rem so it clears the 12px metadata floor of item 14, and the height is a
   pixel value so the control is the same object on every theme. */
.pctl{
  display:inline-flex;align-items:center;gap:1.05rem;
  min-height:50px;padding:0 1.6rem 0 1.35rem;
  appearance:none;
  background:transparent;
  border:1px solid var(--hair-strong);
  border-radius:2px;
  font-family:var(--mono);font-size:.8rem;font-weight:500;
  letter-spacing:.19em;text-transform:uppercase;
  color:var(--ink);text-decoration:none;white-space:nowrap;cursor:pointer;
  transition:border-color .16s ease,color .16s ease,background-color .16s ease,
             box-shadow .16s ease;
}
.pctl-lamp{
  position:relative;
  flex:0 0 auto;width:5px;height:5px;border-radius:50%;
  background:var(--signal);opacity:.85;
}
/* one hairline tick either side of the lamp: the control reads as wired into
   something rather than floating on the page */
.pctl-lamp::before,.pctl-lamp::after{
  content:"";position:absolute;top:50%;width:7px;height:1px;
  background:var(--hair-strong);
}
.pctl-lamp::before{right:100%;margin-right:3px;}
.pctl-lamp::after{left:100%;margin-left:3px;}
/* THE ICON IS FENCED OFF FROM THE LABEL, not merely spaced from it.
   The founder asked for the icon "clearly separated from the label". A gap
   alone does not read as separation inside a tracked mono line -- the label's
   own .19em letter-spacing is already 2.4px, so a 13.6px gap is five letter
   spaces and parses as more label. A full-height hairline in the control's own
   line colour turns the key into a second compartment: the same move the
   control's lamp ticks already make on the left. */
.pctl-key{
  position:relative;
  flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;
  width:22px;height:22px;
  border:1px solid var(--hair-strong);border-radius:1px;
  font-size:.68rem;line-height:1;letter-spacing:0;
  color:var(--ink);
  transition:border-color .16s ease,color .16s ease;
}
.pctl-key::before{
  content:"";position:absolute;top:50%;right:100%;
  margin-right:.52rem;transform:translateY(-50%);
  width:1px;height:26px;background:var(--hair-strong);opacity:.7;
}
/* HOVER IS A STATE, NOT A TINT SHIFT. Before, hover moved the border and the
   label to --signal and nothing else: on the tube, where the resting border is
   already 45% signal green, that is a change of about 4% luminance on a 1px
   line. It now also lifts the ground and drops a soft halo, so the control
   visibly ARMS. Both are painted from the theme's own signal token at low
   alpha, so no theme gains a colour it does not already own. */
.pctl:hover{
  border-color:var(--signal);color:var(--signal);
  background:rgba(var(--signal-rgb),.07);
  box-shadow:0 0 0 1px rgba(var(--signal-rgb),.16),0 0 22px -6px rgba(var(--signal-rgb),.45);
}
.pctl:hover .pctl-key{border-color:var(--signal);color:var(--signal);}
.pctl:hover .pctl-key::before{background:var(--signal);opacity:.55;}
/* focus is louder than hover, and it is drawn OUTSIDE the box so it survives
   the hover ground. 3px offset keeps it clear of the 1px border. */
.pctl:focus-visible{
  outline:2px solid var(--signal);outline-offset:3px;
  background:rgba(var(--signal-rgb),.07);
  box-shadow:0 0 24px -6px rgba(var(--signal-rgb),.5);
}
/* hard-edged themes take square corners from --radius like everything else */
html[data-theme="riso"] .pctl,
html[data-theme="brutal"] .pctl,
html[data-theme="terminal"] .pctl,
html[data-theme="verified"] .pctl{border-radius:0;}
html[data-theme="brutal"] .pctl{border-width:2px;}

/* ==========================================================================
   3b. THE PLAY VARIANT . [ > WATCH PROOF WORK ]
   Founder ruling on the hero's actions: the primary is the film, the secondary
   is the live portal, and the demo ask stays in the header and the closing
   section. "the headline creates the question How?, the founder-led video
   answers it, the live portal lets the visitor verify it, and the demo request
   comes after understanding and trust."

   WHY THE ICON MOVES TO THE LEFT HERE AND NOWHERE ELSE.
   Every other .pctl on the site trails a key on the right, because every other
   .pctl is a GO-THERE control and the mark it carries is an arrow. This one is
   a START-THIS control. Put the play mark on the right and the two hero actions
   both end in a mark, at the same size, on the same baseline -- which is the
   one failure the ruling exists to prevent: two versions of the same gesture.
   Leading play against trailing arrow is the whole hierarchy in two glyphs.

   THE MARK IS DRAWN, NOT TYPED. U+25B6 has an emoji presentation on macOS and
   iOS: as a character it renders as a flat blue-black triangle from the colour
   font on some stacks and as monochrome ink on others, so it would neither take
   the control's --ink nor turn --signal on hover, and it would not be the same
   object across the eight themes. A border triangle is currentColor by
   construction, so it inherits the resting ink, the hover signal and the focus
   state for free, and it is identical at any dpr.
   ========================================================================== */
.pctl-play{
  /* the label is four characters shorter than EXPLORE PUBLIC PROOF, so the
     padding goes UP rather than down: the founder's note was that the control
     read small against the headline, and that verdict is about the box, not
     about the string inside it. 50px height is untouched. */
  padding:0 1.9rem 0 1.55rem;
}
/* THE MARK LOSES THE KEY'S BORDER. On the trailing key the border is what makes
   an arrow read as a KEYCAP -- a thing you press to go somewhere. A play mark
   inside a bordered box inside a bordered control is three nested frames before
   the first letter, and screenshotted at 3x against the lamp's own tick marks it
   read as two icons rather than one. Compared side by side at 1440 the naked
   triangle is the founder's sketch and the boxed one is furniture: 279.58px
   boxed against 271.58px naked, same 50px height, one less frame. The fence
   hairline is what separates the mark from the label, which is what the polish
   note actually asked for -- the box was never doing that job. */
.pctl-play .pctl-key{border:0;width:14px;}
/* the fence hairline crosses to the key's RIGHT, because the key leads now.
   Same 1px, same 26px, same line colour: the icon is still a second compartment
   rather than a spaced glyph, which is what "clearly separated" asked for. */
.pctl-play .pctl-key::after{
  content:"";position:absolute;top:50%;left:100%;
  margin-left:.52rem;transform:translateY(-50%);
  width:1px;height:26px;background:var(--hair-strong);opacity:.7;
}
.pctl-play:hover .pctl-key::after{background:var(--signal);opacity:.55;}
/* ::before is the mark itself here, so every property the trailing-fence rule
   set on it is unset explicitly rather than left to cascade order. */
.pctl-play .pctl-key::before{
  content:"";
  position:static;transform:none;
  margin:0 0 0 2px;               /* a triangle's mass sits left of its box */
  width:0;height:0;
  border-style:solid;
  border-width:5.5px 0 5.5px 9px;
  border-color:transparent transparent transparent currentColor;
  background:none;opacity:1;
}
.pctl-play:hover .pctl-key::before{background:none;opacity:1;}

/* ==========================================================================
   3c. WHAT THE CONTROL SAYS WHEN THE FILM IS NOT THERE
   assets/video.js runs one same-origin HEAD at boot against whichever source
   the trigger has -- the founder's video if one is wired, the captured session
   otherwise. If that source 404s it adds
   .vid-degraded to the trigger and .vid-missing to <html> and stops
   intercepting the click, so the anchor's own href takes the visitor to the
   public proof page. This is the part that makes that honest instead of silent:
   the control stops SAYING watch. It says what it now does, the mark changes
   from a play triangle to the same arrow every other go-there control uses, and
   the secondary link -- which would otherwise be the same action twice in one
   row -- steps out.
   Both labels are in the markup, not in the script: a stylesheet swap cannot
   race, cannot reflow late and works with the script's own copy untouched.
   THE ONE THING THIS MAY NEVER BECOME is a "coming soon" state. The control has
   two truthful faces and no third.
   ========================================================================== */
.pctl [data-vid-off]{display:none;}
.pctl.vid-degraded [data-vid-on]{display:none;}
.pctl.vid-degraded [data-vid-off]{display:inline;}
.pctl-play.vid-degraded .pctl-key::before{
  content:"\2192";
  border:0;width:auto;height:auto;margin:0;
  font-size:.68rem;line-height:1;color:currentColor;
}
html.vid-missing .hero33 .cta-row .hero-act[data-vid-dup]{display:none;}

/* ==========================================================================
   3d. TERMINAL ONLY . THE PRIMARY CONTROL IS INVERSE VIDEO
   Founder ruling: "the CTA button should be much more visible and clear",
   scoped to the tube. Every rule in this section is behind
   html[data-theme="terminal"] .hero33 .cta-row, so it reaches exactly one
   control on one page and the other seven themes are byte-identical.

   WHAT WAS ACTUALLY WRONG, AND IT WAS NOT SIZE.
   The resting control was a 1px hairline in --hair-strong on a transparent
   ground. On the tube that hairline is rgba(43,255,111,.45) and the hero's
   graph draws its requirement, source, test and change nodes in the SAME
   line colour at the SAME weight on the SAME ground. Measured at 1440 the
   border ran 3.56:1 against its own ground while the node cards behind it ran
   the same treatment, so the one interactive object on the page was rendered
   in the dialect of the page's inert furniture. Squinted to 25% the control
   was indistinguishable from the five node cards below it. No amount of extra
   padding fixes that, because the problem is vocabulary, not scale.

   THE FIX IS THE TUBE'S OWN ANSWER TO "THIS ONE IS SELECTED": INVERSE VIDEO.
   The ground becomes --ink and the label becomes --paper. Those are the
   theme's own two tokens, in the roles they already have, swapped -- the same
   move themes.css already makes for ::selection. Nothing new enters the
   palette, the scanline overlay still crosses the block, so it reads as lit
   phosphor rather than as a marketing button, and it is now the only
   achromatic mass in a hero that is otherwise entirely green.

   WHY THE FILL IS NOT var(--signal), AND WHY --signal IS NOT SPLIT.
   On the tube --signal and --pass are both #2BFF6F, so a control filled with
   the signal colour would be a solid block of the verified colour sitting
   under a headline about verification. --ink has no state meaning at all: it
   is what prose is printed in. Splitting --signal for this theme was the other
   route and it is the wrong one -- on the tube --signal is not an accent, it
   is the light. It paints the headline, the prompt chevron, the block cursor,
   the selection, every hairline and the wordmark period, and 89 references to
   the literal #2BFF6F / 43,255,111 across thirteen stylesheets would not
   follow the token, so the tube would silently end up with two greens.
   The collision is real and is still open in riso and olo; it is recorded as
   a known issue rather than half-fixed here.

   MEASURED, 1440, terminal, against the same live ground both times:
     before  271.58 x 50.00, 12.80px label, border 3.56:1, ground transparent
     after   316.38 x 58.00, 15.00px label, block edge 18.3:1, ground --ink
   +16.5% wide, +16.0% tall, +17.2% on the label, and the resting contrast
   defect (1px at 3.56:1, under the 3:1 non-text floor in six other themes and
   only just over it here) stops existing because the control no longer asks a
   hairline to carry it.
   ========================================================================== */
html[data-theme="terminal"] .hero33 .cta-row .pctl{
  min-height:58px;
  padding:0 2.2rem 0 1.75rem;
  gap:1.1rem;
  background:var(--ink);
  /* transparent, not ink: a 1px ink border on an ink ground was invisible,
     and it read as border+bloom to any audit. The box keeps its 1px so the
     control's metrics do not move. */
  border:1px solid transparent;
  color:var(--paper);
  font-size:.9375rem;
  font-weight:600;
  letter-spacing:.2em;
  /* the phosphor halo the tube gives every bright object. It is the same
     construction as the h1's text-shadow, at the block's own ink. */
  box-shadow:0 0 40px -10px rgba(210,255,226,.55);
}
/* THE LAMP KEEPS A DARK APERTURE. It is the HDR carrier -- herograph.js hangs
   .pctl-hdr inside it and hdr-event.css §4 flares it in the theme's own ink on
   the verification beat. A green disc emitting onto a white ground is a 1.28:1
   event, i.e. no event at all, so the lamp is given a black bezel out of the
   control's own --paper and the flare has a ground again. The bezel is a
   box-shadow, so it paints under the injected child and cannot occlude it. */
html[data-theme="terminal"] .hero33 .cta-row .pctl-lamp{
  width:7px;height:7px;opacity:1;
  background:var(--signal);
  box-shadow:0 0 0 5px var(--paper),0 0 10px 5px rgba(43,255,111,.55);
}
/* the two wiring ticks invert with everything else: a green hairline on a
   white block is invisible, and they are structure, not decoration. */
html[data-theme="terminal"] .hero33 .cta-row .pctl-lamp::before,
html[data-theme="terminal"] .hero33 .cta-row .pctl-lamp::after{
  background:rgba(5,8,7,.45);width:5px;
}
html[data-theme="terminal"] .hero33 .cta-row .pctl-lamp::before{margin-right:6px;}
html[data-theme="terminal"] .hero33 .cta-row .pctl-lamp::after{margin-left:6px;}
/* the mark grows with the box so it still reads as PLAY and not as a caret at
   a glance: 11px x 12px of solid ink where it was 9px x 11px of thin green. */
html[data-theme="terminal"] .hero33 .cta-row .pctl-play .pctl-key{
  width:17px;color:var(--paper);
}
html[data-theme="terminal"] .hero33 .cta-row .pctl-play .pctl-key::before{
  border-width:7px 0 7px 12px;
}
html[data-theme="terminal"] .hero33 .cta-row .pctl-play .pctl-key::after{
  background:rgba(5,8,7,.42);height:30px;margin-left:.62rem;opacity:1;
}
/* HOVER IS THE PHOSPHOR AT SATURATION, NOT A RING. The ramp is achromatic and
   the theme already owns both ends of it: #D2FFE2 at rest, white at full
   drive. No hue is introduced and no ring is spent here, because the ring is
   what focus uses and the two states may not look alike. */
html[data-theme="terminal"] .hero33 .cta-row .pctl:hover{
  background:#FFFFFF;border-color:#FFFFFF;color:var(--paper);
  box-shadow:0 0 54px -8px rgba(255,255,255,.7);
}
html[data-theme="terminal"] .hero33 .cta-row .pctl:hover .pctl-key{color:var(--paper);}
html[data-theme="terminal"] .hero33 .cta-row .pctl:hover .pctl-key::after{background:rgba(5,8,7,.42);}
/* FOCUS IS LOUDER THAN HOVER AND IT IS DRAWN OUTSIDE THE BLOCK. --signal
   against the white ground would be 1.28:1; at offset 4px the ring lands on
   the hero's near-black ground instead and measures ~15:1 there. */
html[data-theme="terminal"] .hero33 .cta-row .pctl:focus-visible{
  outline:3px solid var(--signal);outline-offset:4px;
  background:#FFFFFF;border-color:#FFFFFF;
  box-shadow:0 0 54px -8px rgba(255,255,255,.7);
}
/* the phone gives up the elbows, same as section 5 does for every theme, and
   keeps the height, the fill and the mark. Measured after: no horizontal
   scroll at 320, 360, 390 or 428. */
@media (max-width:760px){
  html[data-theme="terminal"] .hero33 .cta-row .pctl{
    min-height:54px;padding:0 1.15rem 0 1rem;gap:.8rem;
    font-size:.8125rem;letter-spacing:.14em;
  }
}

.hero33 .cta-row{
  margin-top:clamp(2.1rem,3.2vw,2.7rem);
  justify-content:center;gap:1.4rem 2rem;align-items:center;
}
html[data-theme="terminal"] .hero33 .cta-row{justify-content:flex-start;}

/* THE HERO'S SECOND ACTION IS NO LONGER A SALES ACTION.
   Founder review, item 5: "do not finish with both hero actions being sales
   actions". It used to be `Request a demo ->`, which put the same ask in the
   header, in the hero and in the closing section. The demo ask keeps its two
   homes -- the header nav and the final commercial section -- and the hero's
   second action now opens the ONE public requirement object the section below
   is built on, which is also item 10's instruction that the card and the hero
   CTA deep-link to the same object.
   Box gone, same as the header's: one boxed control per screen. It is a size
   step up from before (.7rem -> .78rem) because an action label may not sit
   under the metadata floor while the metadata beside it clears it. */
.hero33 .cta-row .hero-act{
  min-height:0;padding:0;
  background:transparent;border:0;border-radius:0;
  color:rgba(var(--ink-rgb),.80);          /* 8.3:1 on paper, 10.1:1 on black */
  font-family:var(--mono);font-size:.78rem;font-weight:500;
  letter-spacing:.16em;text-transform:uppercase;
  text-decoration:none;
  border-bottom:1px solid transparent;
  transition:color .16s ease,border-color .16s ease;
}
.hero33 .cta-row .hero-act:hover,
.hero33 .cta-row .hero-act:focus-visible{
  background:transparent;color:var(--signal);border-bottom-color:var(--signal);
}

/* the facts line drops back: it is provenance, not a claim, and the hero now
   has exactly one thing to look at. Quieted by SIZE and tracking, not by colour.

   THE COLOUR IS SET IN THE HERO BECAUSE THE HERO HAS A CONTRAST FLOOR.
   The brief's four -- headline, subhead, control label, facts line -- carry a
   7:1 minimum measured at their own glyph boxes. Measured that way, the shell's
   inherited fine-print token lands at 5.16:1 on paper and 5.26:1 on verified.
   That is not the graph: with .hg-svg forced to zero opacity the same glyph
   boxes measure 5.19 and 5.28, i.e. the animation costs the facts line 0.03 of
   contrast ratio. It is the token, and it is under the bar wherever the token is
   used. Scoped to .hero33 so the fine print everywhere else on the site is
   untouched: .78 of the ink token is the same figure the subhead above already
   uses and measures, and it clears the floor on every theme. */
.hero33 .facts{
  margin-top:clamp(1.5rem,2.2vw,1.9rem);
  font-size:.7rem;letter-spacing:.02em;
  color:rgba(var(--ink-rgb),.78);
}
.hero33 .facts span{color:rgba(var(--ink-rgb),.78);}
.hero33 .facts a{color:rgba(var(--ink-rgb),.84);border-bottom-color:var(--hair-strong);}
.hero33 .facts a:hover{color:var(--ink);border-bottom-color:var(--ink);}
.hero33 .facts b{font-weight:500;color:rgba(var(--ink-rgb),.92);}

/* ==========================================================================
   4. THE HEADER'S DEMO ACTION, QUIETED
   Site-wide on purpose. The label set and the destination are untouched: only
   the chrome goes, and the arrow + caps come from CSS, so the hero has exactly
   one boxed control on it.
   ========================================================================== */
/* SIZE: founder review item 14 sets a nav floor of 12-13px. This action was
   .7rem = 11.20px, under it. .78rem = 12.48px clears it and matches the hero's
   own second action exactly, so the two quiet actions on the first screen are
   one object at two positions. */
/* HIT AREA (WCAG 2.5.8): the quieting stripped the box and with it the tap
   target -- the only above-the-fold conversion route on a phone measured
   ~12px tall. Padding buys the target back (>=44px effective height) and an
   equal negative margin cancels it out of layout, so not one rendered pixel
   moves: transparent ground, no border, and the neighbours keep their gaps
   because the margin box is unchanged. Horizontal stays small (.35rem) so
   the halo cannot cross into the menu control beside it. */
.site-header .nav > .btn-primary{
  min-height:0;padding:1rem .35rem;margin:-1rem -.35rem;
  background:transparent;border:0;border-radius:0;
  color:var(--ink);
  font-family:var(--mono);font-size:.78rem;font-weight:500;
  letter-spacing:.16em;text-transform:uppercase;
  transition:color .16s ease,opacity .16s ease;
}
.site-header .nav > .btn-primary::after{
  content:"\2192";margin-left:.55em;letter-spacing:0;opacity:.7;
}
.site-header .nav > .btn-primary:hover{background:transparent;border:0;color:var(--signal);}
html[data-theme="verified"] .site-header .nav > .btn-primary,
html[data-theme="verified"] .site-header .nav > .btn-primary:hover{background:transparent;border-color:transparent;}
html[data-theme="verified"] .site-header .nav > .btn-primary{color:var(--ink);}
html[data-theme="verified"] .site-header .nav > .btn-primary:hover{color:var(--signal);}
html[data-theme="terminal"] .site-header .nav > .btn-primary,
html[data-theme="brutal"] .site-header .nav > .btn-primary,
html[data-theme="riso"] .site-header .nav > .btn-primary{
  background:transparent;border:0;box-shadow:none;
}
/* BRUTAL: THE DEMO ACTION WAS BLACK ON BLACK, i.e. absent.
   That theme draws the header as a solid #000 bar and paints its nav links
   rgba(255,255,255,.82) to sit on it. This action is a nav link too, but the
   generic rule above hands it `color:var(--ink)`, and brutal's --ink is #000.
   Measured against the painted header: 1.00:1, the only hero-slice element on
   any of the eight themes that did not clear the floor -- and the founder's
   review requires `REQUEST DEMO →` to be in the header, so on brutal the site
   was failing a stated requirement, not just a contrast one. It takes the bar's
   own ink and the theme's own hover chartreuse. Measured after: 17.5:1 resting,
   17.6:1 on hover. Nothing else about the treatment changes. */
html[data-theme="brutal"] .site-header .nav > .btn-primary{color:rgba(255,255,255,.92);}
html[data-theme="brutal"] .site-header .nav > .btn-primary:hover{color:#E8FF2A;}
/* TERMINAL: THE CLOSING BRACKET WAS BEING EATEN.
   themes.css draws every .btn on the tube as a bracketed command -- ::before is
   "[" and ::after is "]" -- and the rule above replaced ::after with the arrow,
   so the header read `[ REQUEST A DEMO →` with no closing bracket while the
   hero's own secondary (whose arrow is in the label text, not in CSS) correctly
   read `[ REQUEST A DEMO → ]`. The arrow and the bracket now share the one
   pseudo-element the tube leaves free, in the bracket's own chrome green, so the
   pair is complete and the two actions match. One box, no extra glyph width to
   clip: verified at 1440 / 1280 / 1024 / 390. */
html[data-theme="terminal"] .site-header .nav > .btn-primary::after{
  content:"\2192\00A0]";
  margin-left:.5em;
  letter-spacing:0;
  color:rgba(43,255,111,.62);
  opacity:1;
}
html[data-theme="terminal"] .site-header .nav > .btn-primary:hover::after{color:var(--signal);}

/* ==========================================================================
   5. THE BAND
   More air. The scroll cue that used to close the band is gone; the graph's
   own hairline crossing into section 2 is the only cue the fold needs.
   ========================================================================== */
.hero33{min-height:clamp(600px,86vh,940px);}
html[data-theme="terminal"] .hero33{min-height:clamp(560px,82vh,900px);}

@media (max-width:760px){
  /* a phone hero has no free flank, so the copy is top-weighted and the band
     grows: that buys a real graph band under the copy instead of a
     whisper behind the headline. */
  .hero33{min-height:clamp(560px,88vh,880px);}
  .hero33 > .hero-main{margin-top:0;margin-bottom:auto;}
  /* Line 2 must stay on ONE line or the weight split reads as an accident
     rather than as a decision. exp33.css and theme-verified.css both set the
     hero H1 scale from an attribute selector, so the override has to match
     that specificity to win; it loads last, so a tie goes to this file. */
  html[data-theme] .hero33 .hero-copy h1,
  html:not([data-theme]) .hero33 .hero-copy h1{
    max-width:none;
    /* 9.3vw was 36.27px at 390 and BOTH lines wrapped: "REQUIREMENTS / THAT"
       over "TEST / THEMSELVES." -- four ragged lines, which is exactly the
       accident this rule exists to prevent. The two lines are 17 and 16 mono
       characters at .57em and .562em of effective advance (the tracking is
       negative), so they fit the 322px phone column at 33.2px and 35.8px
       respectively. 8.4vw = 32.76px at 390 clears both with 6px to spare and
       still fills the column. */
    font-size:clamp(1.85rem,8.4vw,2.9rem);
    line-height:1.02;
  }
  .hero33 .hero-sub{font-size:1.05rem;}
  .hero-mach{font-size:9px;letter-spacing:.22em;}
  .hero33 .cta-row{gap:1rem 1.2rem;}
  /* THE CONTROL KEEPS ITS HEIGHT AND LOSES ITS ELBOWS. The desktop box is
     312.03px wide -- the founder's "generous horizontal padding", measured --
     and at 320 that pushed the document to 341px, i.e. 21px of horizontal
     scroll, which the desktop box did not do at 268.80px. The 50px height, the
     12.8px label, the lamp, the divider and the key box are all unchanged; only
     the padding, the gap and the tracking come in, because those are the parts
     that buy air rather than legibility. Measured after: no horizontal scroll at
     320, 360, 390 or 428. */
  .pctl{
    width:auto;max-width:100%;
    gap:.8rem;padding:0 1.05rem 0 .9rem;letter-spacing:.14em;
  }
  .pctl-key::before{margin-right:.4rem;}
  /* the play variant gives up the same elbows, and its fence comes in with it */
  .pctl-play{padding:0 1.15rem 0 1rem;}
  .pctl-play .pctl-key::before{margin:0 0 0 2px;}
  .pctl-play .pctl-key::after{margin-left:.4rem;}
}

/* nothing in this layer animates; the graph driver holds its own final frame
   under reduced motion. */

/* ==========================================================================
   6. TERMINAL: the one theme where the headline stays in its own ink
   Green means verified everywhere else on the site, which is why nothing in
   the headline is green. On the tube green IS the ink -- the whole page is
   phosphor -- so forcing a pale headline there would read as a broken theme
   rather than as a rule. What DID read as broken was line 2 inheriting a
   different colour from .h1-em, and the theme's block cursor being pushed onto
   a line of its own by the forced break. Both are fixed here.
   ========================================================================== */
html[data-theme="terminal"] .hero33 .hero-copy h1 .h1-em{
  color:var(--signal);
  text-shadow:0 0 14px rgba(43,255,111,.4);
}
html[data-theme="terminal"] .hero33 .hero-copy h1::after{display:none;}
/* --------------------------------------------------------------------------
   THE BLOCK CURSOR . founder review item 4
   "Keep the block cursor. It is one of the strongest parts of the hero. But it
   visually resembles a striped loading block. Refine it: slightly narrower,
   aligned to the headline baseline, normal green while idle, brief emissive
   flash when the graph begins, smooth phosphor decay rather than a hard flash."

   GEOMETRY, measured at 1440 (h1 font-size 73.60px)
     before   36.80 x 62.55 px, pushed 7.36px BELOW the baseline by
              transform:translateY(.1em), so it overshot the cap line at the top
              and the baseline at the bottom -- a bar beside the type, not a
              caret on it. Twenty-one scanline periods tall.
     after    29.44 x 51.52 px (.40em x .70em), bottom flush with the baseline.
              vertical-align:baseline aligns an inline-block's BOTTOM MARGIN
              EDGE with the baseline, so deleting the translate is the whole
              alignment fix; .70em sits a hair over IBM Plex Mono's .698em cap
              height, so the block reads as one character cell of the line it is
              on. Seventeen scanline periods, and 39% less lit area.

   WHAT IS NOT FIXED HERE, AND WHY. The stripes themselves are the tube's own
   scanline overlay (themes.css paints body::after, position:fixed, z-index:40).
   Every element inside <main> is trapped below it: main is position:relative
   z-index:1 and .hero33 carries isolation:isolate, so no z-index reachable from
   this file can lift the cursor over the lines, and the overlay is viewport-
   fixed so no counter-gradient can stay in phase with it under scroll. The
   block is de-emphasised as a stripe carrier by being 39% smaller and by
   gaining a resting bloom, which softens its edges into light rather than into
   a hard-edged bar. Raising the overlay's z-order is a themes.css change and is
   written up in the report rather than made here.

   IDLE IS LIT, AND IT IS LIT IN HDR. The resting PQ raster was taken off this
   block once and put back by founder ruling -- "blinking cursor should use hdr
   all the time like before" -- and it is painted in s35-herocopy.css section
   4.1, one file later in the cascade, because the `background` shorthand below
   would reset it. Everything HERE stays SDR and stays load-bearing: the tile's
   SDR decode is the same #2BFF6F as the background-color under it, so if the
   raster is killed, 404s, or is clamped by the compositor, the block is exactly
   the block you see below. The HDR is headroom on top, never the shape.

   THE DECAY IS A TRANSITION, NOT A KEYFRAME. The flash is two class toggles
   (see s35-herocopy.css); the transition below is what turns the toggle OFF
   into a 620ms phosphor fall instead of the hard cut the founder called out.
   Transform decays faster than light does, so the width snaps back in 240ms
   while the bloom is still falling -- that is the order a real phosphor does it
   in, and it is why the flash does not read as a resize.
   -------------------------------------------------------------------------- */
html[data-theme="terminal"] .hero33 .hero-copy h1 .h1-em::after{
  content:"";display:inline-block;width:.40em;height:.70em;margin-left:.16em;
  /* SHORTHAND, DELIBERATELY: it resets background-image, so this rule can never
     leave a stale raster behind on a theme switch. s35-herocopy.css puts the
     resting PQ tile back on top of this colour for the terminal theme only. */
  background:var(--signal);vertical-align:baseline;
  /* the resting bloom: one soft phosphor halo in SDR, no second colour. It is
     the block's own light, and it is what keeps the caret reading as light
     rather than as a hard-edged stripe on a display with no headroom at all. */
  box-shadow:0 0 10px -1px rgba(var(--signal-rgb),.42);
  animation:t-blink 1.1s steps(1) infinite;
  transition:box-shadow .62s cubic-bezier(.22,.61,.36,1),
             transform .24s cubic-bezier(.22,.61,.36,1),
             background-color .40s ease-out;
}
@media (prefers-reduced-motion: reduce){
  html[data-theme="terminal"] .hero33 .hero-copy h1 .h1-em::after{
    animation:none;transition:none;
  }
}

/* ==========================================================================
   7. TERMINAL: THE `>` PROMPT HANGS. THE TEXT COLUMN IS FLUSH.
   --------------------------------------------------------------------------
   WHAT WAS WRONG
   themes.css draws the prompt as an IN-FLOW ::before (content:"> ") on every
   h1. In the hero that puts it inside the first line box, and section 2 of this
   file forces line 2 onto its own line with .h1-em{display:block}. So the two
   lines did not share a column: measured at 1440, line 1 began at x=213.91 and
   line 2 at x=130.00 -- an 83.91 px step, which is exactly the prompt glyph's
   own advance. Every headline line except the first started under the prompt
   instead of beside it.

   THE FIX, AND WHY IT IS NOT A PADDING ON LINE 2
   Indenting line 2 would only paper over the first line's inflation, and it
   would break again the moment the headline wrapped to three lines. The prompt
   is taken OUT OF FLOW instead:

     .hero-copy    carries the prompt column as its own padding-left, so the
                   headline, the machine stamp, the subhead, the CTA row, the
                   facts line and the cue all begin on ONE column
     h1            is the containing block (position:relative) and keeps the
                   scale that drives the column width, so there is one source
                   of truth for both
     h1::before    is absolutely positioned at right:100% -- entirely outside
                   the text column, hanging in the padding the copy block just
                   opened for it, and no longer part of any line box

   Because the pseudo-element is not sized here it still shrink-wraps to the
   glyph, which is what assets/hdr-event.css section 2 needs: its HDR raster is
   background-clip:text over that box, so the emitting pixels stay exactly the
   shape of the character. No bloom or geometry retune was required.

   THE COLUMN IS NOW THE SITE'S RAIL INDENT, NOT A CHARACTER ADVANCE.
   Founder review item 13: two deliberate rails, PROMPT/LABEL at x = A and
   CONTENT at x = A + 28-36px, used by every section on every page. The craft
   layer publishes them as --rail-indent (32px desktop / 20px phone) and moves
   every section heading onto the content rail.
   Before this change the hero was the one block on the site that did not
   answer them: measured at 1440, every section kicker and heading sat at
   x = 130.0 and the hero copy column sat at x = 190.3, because the column was
   .82em of a 73.6px headline -- a `>` ADVANCE (.6em) plus a gap (.22em) --
   which is 60.35px, not 32px. Two content rails 60px apart is exactly the
   "small alignment issue" the review names.
   So the column is the token, and the PROMPT GLYPH IS SIZED TO THE COLUMN
   instead of the column being sized to the glyph:
       font-size = (column - gap) / .6      (.6em is Plex Mono's advance)
   which at 1440 makes the `>` 40px against a 73.6px headline -- a machine
   annotation at label scale rather than a second display glyph competing with
   the line it introduces. The glyph therefore never reaches past .hero-copy
   into the page gutter at any width down to 390, which was the old column's
   job too.
   THE PROMPT NO LONGER INHERITS THE HEADLINE'S FONT-SIZE, so its vertical
   placement stops being free. It takes the headline's LINE BOX as a length
   instead, which lands the chevron's ink centre on the cap band's centre to
   within a pixel at every width -- the algebra and the measured figures are in
   the rule itself.
   ========================================================================== */
html[data-theme="terminal"] .hero33 .hero-copy{
  --t-h1:clamp(2.2rem,5.4vw,4.6rem);
  /* the gap between the prompt's advance and the headline's first glyph. It is
     a share of the column, so the prompt keeps its proportion when the craft
     layer changes the indent or the phone breakpoint halves it. */
  --t-rail:var(--rail-indent,32px);
  --t-pgap:calc(var(--t-rail) * .25);
  --t-pw:var(--t-rail);
  --t-pfs:calc((var(--t-pw) - var(--t-pgap)) / .6);
  padding-left:var(--t-pw);
}
html[data-theme="terminal"] .hero33 .hero-copy h1{
  font-size:var(--t-h1);
  position:relative;
}
html[data-theme="terminal"] .hero33 .hero-copy h1::before{
  content:">";
  position:absolute;
  top:0; right:100%;
  margin-right:var(--t-pgap);
  font-size:var(--t-pfs);
  /* THE VERTICAL ALIGNMENT IS THE MATCHED LINE BOX, AND NOTHING ELSE.
     Give the prompt the headline's first line box AS A LENGTH and both baselines
     resolve to top + L/2 + F*(asc-desc)/2, so the L term cancels and the prompt's
     baseline sits (F_h1 - F_prompt) * (asc-desc)/2 above the headline's -- 12.50px
     at 1440, measured, which for IBM Plex Mono is (asc-desc) = .744.
     That is the RIGHT amount, because a `>` is not a letter: its ink is centred
     on the math axis, .305em over its own baseline, not sitting on it. Landing
     the two baselines together would drop the chevron 11.4px below the middle of
     the cap band and read as a glyph that had slipped. Measured with the line
     boxes matched and no translate at all:
         chevron ink centre  y 237.19      cap band centre  y 236.23
     i.e. 0.96px low at 1440, and the same algebra gives 0.84px at 390 -- the L
     term cancels there too, which is why the phone override only has to restate
     the line height. Sub-pixel on both, so there is no correction to carry. */
  line-height:calc(var(--t-h1) * 1.14);
  text-shadow:none;
  pointer-events:none;
}
/* the phone keeps the same construction on its own type scale, and picks up the
   craft layer's own narrower phone indent through --rail-indent. This block is
   last in the file, so it wins the specificity tie with section 5's narrow
   headline rule. */
@media (max-width:760px){
  html[data-theme="terminal"] .hero33 .hero-copy{
    --t-h1:clamp(1.85rem,8.4vw,2.9rem);
    /* the craft layer halves the indent on the phone; the fallback has to halve
       with it, or a missing token leaves a 40px prompt beside a 36px headline */
    --t-rail:var(--rail-indent,20px);
  }
  html[data-theme="terminal"] .hero33 .hero-copy h1{
    max-width:none;
    font-size:var(--t-h1);
    line-height:1.02;
  }
  html[data-theme="terminal"] .hero33 .hero-copy h1::before{
    line-height:calc(var(--t-h1) * 1.02);
  }
}

/* --------------------------------------------------------------------------
   ... AND ON EVERY OTHER HERO ON THE SITE.
   The tube is now the default theme, so the prompt is what a first-time visitor
   sees on all six pages -- and graph.html, method.html, demo.html and prove.html
   all carry a headline that wraps. Measured at 1440 before this rule: graph and
   method stepped 88.33 px between line 1 and line 2, demo and prove 72.97 px.
   Same defect, same cause.

   Those four heroes are not .hero33 and their headline wrappers all differ
   (.hero-copy, .dm-left, .pv-left), so this variant is self-contained: the h1
   carries the prompt column as its own padding and the prompt hangs at the
   column's left edge. Every line of the headline therefore starts on one column
   and the prompt sits outside it, exactly as in the hero above, without this
   rule needing to know what the headline is wrapped in.

   box-sizing is content-box for the duration: every one of these h1s is sized by
   a `max-width` in ch, and under the site's border-box default the new padding
   would eat that measure and could force an extra wrap.
   -------------------------------------------------------------------------- */
html[data-theme="terminal"] .hero:not(.hero33) h1{
  --t-pw:.82em;
  position:relative;
  box-sizing:content-box;
  padding-left:var(--t-pw);
}
html[data-theme="terminal"] .hero:not(.hero33) h1::before{
  content:">";
  position:absolute;
  top:0; left:0;
  text-shadow:none;
  pointer-events:none;
}
