/* site.css -- THE FRONT PAGE'S OWN STYLESHEET, lifted whole out of
 * index.html's inline <style> at the 3.1.010.0 migration. NOT ONE RULE WAS
 * CHANGED, and that is the point: the site language moved the page's
 * STRUCTURE and CONTENT into data, and left the styling where styling
 * belongs. A renderer that also re-implemented 1200 lines of CSS would have
 * been a rewrite pretending to be a migration, and the oracle test -- diff
 * the language-built page against what cxai.plus serves -- would have had
 * nothing to say, because everything would differ.
 *
 * The tokens at the top of it are ALSO written onto :root at boot by
 * render.js from site.tokens.json, so a token edit reaches a live page with
 * no rebuild. Both spellings agree because the JSON was lifted from here.
 */

:root{
  --paper:#ECEFF0;
  --paper-deep:#E2E7E8;
  --ink:#16181A;
  --ink-2:#4C545B;
  --hairline:#C6CDD0;
  --crimson:#CE2431;
  --title-deep:#4A2C6D; /* aubergine, HIS PICK 2026-09-02 from eight rendered
                           candidates (navy, teal, forest, aubergine, oxblood,
                           indigo, slate, umber) shown against the name the
                           heading has to out-rank. It began as the navy he asked
                           for -- *"bolder? and a different colour? dark blue?"*
                           -- and became this when he could see them side by
                           side, which is the only way this call has ever gone
                           well. It is a TOKEN rather than a literal because a
                           second heading asking for the same treatment must get
                           the SAME colour -- the palette above is the reason no
                           crimson on this page drifted, and a hex typed at the
                           one selector that needs it today is how that starts. */
  --panel:#111417;
  --panel-ink:#D8DEE2;
  --panel-dim:#6B7681;
  --green:#79C48E;
  --maxw:1080px;
  --band-h:68px;        /* his band, which IS the fixed bar now (his ruling
                           2026-08-22: "the white bar and black should be one");
                           the rail, the menu and <main> all measure from it */
  --band-inset-top:0px; /* HIS WHITE STRIP ACROSS THE TOP (2026-08-23). Paper
                           above the black, full width -- and this one is a NEW
                           decision, not a restoration: neither this page nor
                           the one before it ever had it, which is exactly why
                           three attempts to find it by comparing against the
                           live site kept landing on the left-hand corner
                           instead. Asked, then built. */
  --topbar-h:calc(var(--band-h) + var(--band-inset-top)); /* one height, two
                           names: everything that measured from the old white
                           bar keeps its token and follows -- so the strip
                           costs one line here and nothing anywhere else */
}
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
body{
  background:var(--paper);
  color:var(--ink);
  font-family:'Newsreader',serif;
  font-size:19px;
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
  padding-top:var(--topbar-h);   /* the fixed bar is out of flow; give it its room back */
}
::selection{background:var(--crimson);color:#fff}
a{color:var(--ink)}

/* left rail — matches the deliverable set convention. The items run the SAME
   system as the deck (user eyeball 2026-07-31): they drift and collide like the
   cards, and the active one wears the ARGB strip. More vertical spacing than the
   original 6px so the drift has room to move and neighbours meet at the edges,
   not in each other's text. */
/* ---- THE FIXED LAYER IS THE BAND ------------------------------------------
   His ruling 2026-08-22: "the white bar and black should be one (except for the
   overview)". The white 56px bar is deleted and its whole population -- the
   wordmark, Try it, the two preferences, the section toggle and the Get door --
   lives on the black band, whose rules are below at .plaqueband. What survives
   from the old layer's decisions: the wordmark is still the home button, the
   section links are still NOT duplicated from the rail, and below 901px the
   compact section menu still replaces the rail.
   The colour rules here are the band's: ink on paper became light on #000. */
.brand{
  font-family:'Bricolage Grotesque',sans-serif;font-weight:750;font-size:19px;
  letter-spacing:-0.02em;color:#EDF1F4;text-decoration:none;
  display:inline-flex;align-items:center;gap:1px;padding:6px 4px;border-radius:4px;
}
.brand span{color:#8B95A0;font-weight:500}
.brand:hover{color:var(--crimson)}
.brand:hover span{color:var(--crimson)}
.brand:focus-visible{outline:3px solid var(--crimson);outline-offset:3px}
.topbar-left{grid-column:1;justify-self:start;display:flex;align-items:center;gap:16px}
/* The two global preferences. Still the quiet treatment -- on black the
   neutrals lighten, nothing else changes. */
.pref{
  font-family:'Spline Sans Mono',monospace;font-size:11.5px;color:#B9C2CA;
  background:transparent;border:1px solid #3A4249;border-radius:4px;
  padding:7px 10px;cursor:pointer;line-height:1;
}
.pref:hover{color:#fff;border-color:#5A6570}
.pref:focus-visible{outline:3px solid var(--crimson);outline-offset:2px}
#stillbtn[aria-pressed="true"]{color:#fff;border-color:#5A6570;background:#14181C}
.langsel{appearance:none;padding-right:9px}
@media(max-width:520px){ #stillbtn{display:none} }   /* narrow: the rail is gone anyway */

/* A PLAIN LINE THAT IS ALSO A LINK. It keeps the paragraph's colour and flow
   -- the point is that it BE a link (copyable, middle-clickable, reachable from
   a keyboard), not that it look like one. */
.cx-textlink{display:block;color:inherit;text-decoration:none;}
.cx-textlink:hover{text-decoration:underline;}
.tryit{
  background:var(--crimson);color:#fff;text-decoration:none;
  font-family:'Bricolage Grotesque',sans-serif;font-weight:700;font-size:14px;
  padding:9px 18px;border-radius:4px;white-space:nowrap;
}
.tryit:hover{filter:brightness(1.1)}
.tryit:focus-visible{outline:3px solid var(--ink);outline-offset:3px}

/* The mobile section menu. Hidden entirely on desktop -- the rail is there. */
.navtoggle{display:none}
.navmenu{display:none}
@media(max-width:900px){
  .navtoggle{
    display:inline-block;
    font-family:'Spline Sans Mono',monospace;font-size:12px;color:#B9C2CA;
    background:transparent;border:1px solid #3A4249;border-radius:4px;
    padding:8px 12px;cursor:pointer;
  }
  .navtoggle:focus-visible{outline:3px solid var(--crimson);outline-offset:3px}
  .navmenu{
    display:block;position:fixed;left:0;right:0;top:var(--topbar-h);z-index:69;
    background:var(--paper);border-bottom:1px solid var(--hairline);
    padding:8px 20px 14px;
    transform:translateY(-8px);opacity:0;pointer-events:none;
    transition:opacity .18s ease, transform .18s ease;
  }
  .navmenu[data-open="1"]{transform:translateY(0);opacity:1;pointer-events:auto}
  .navmenu a{
    display:block;padding:11px 4px;text-decoration:none;color:var(--ink);
    font-family:'Spline Sans Mono',monospace;font-size:13.5px;
    border-bottom:1px solid var(--hairline);
  }
  .navmenu a:last-child{border-bottom:0}
  .navmenu a:focus-visible{outline:3px solid var(--crimson);outline-offset:-3px}
}
@media (prefers-reduced-motion:reduce){
  .navmenu{transition:none}
}

.rail{
  /* top:0 -- the rail owns its column all the way up now ("except for the
     overview"): the paper corner beside the band is the rail's own head, and
     the notice that slides into it shares the rail's top edge, which is the
     assumption the --tk-push arithmetic below has always made. */
  position:fixed;left:0;top:0;bottom:0;width:200px;
  border-right:1px solid var(--hairline);
  /* THE TOP PADDING IS THE NOTICE'S ROOM (v3.1.004.0). His placement ruling put
     the notice on the top row, which is the top of THIS column -- so rather than
     the notice covering the links, the links start below it. --tk-push is set by
     the notice itself when it works out its own height and returns to 28px when
     it goes away; nothing else writes it, and with no notice on the page the
     fallback is the padding this rule always had.
     WHY THIS AND NOT A z-INDEX ARGUMENT: a panel over navigation is a panel over
     navigation whichever one wins, and the rail is a fixed column with room to
     spare. This is the only arrangement in which BOTH of his rulings of that
     glance hold at once -- the notice on the top line, and no menu item clipped
     by it.
     THE DRIFT DOES NOT NEED REHOMING. The physics caches each box's offsetTop
     once, but every collision test compares two of them against each other, so a
     padding change that moves all six by the same amount leaves every comparison
     exactly as true as it was. */
  padding:var(--tk-push,28px) 22px 28px;
  /* AND IT SCROLLS RATHER THAN CLIPS (SITE-362, 2026-09-08). The rail grew from
     eleven rows to thirteen when his two new panels joined it, and at 1280x720
     -- the commonest laptop height there is -- the last row landed at 713..753
     in a 720px viewport with no way to reach it. Measured on the LAST LINK, not
     on the column: a fixed column ends at the viewport edge whether or not its
     contents do, so the container's own numbers report a clipped rail as a
     healthy one.
     THE RULING IS ALREADY HIS AND IS WRITTEN TWENTY LINES ABOVE -- "no menu item
     clipped by it". A row cut off by the fold is that defect wearing a different
     lid, so this keeps his rule rather than adding one.
     ONLY WHEN IT HAS TO: at 900px there is no scrollbar, no reflow and the same
     pixels as before. 'contain' stops a flick at the end of the rail from
     scrolling the page behind it, which is what makes a scrolling fixed column
     feel broken when it is left out. */
  overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin;
  transition:padding-top .6s cubic-bezier(.16,.84,.30,1);
  /* 11px AND NOT 17px, AND THE SIX PIXELS BOUGHT SOMETHING SPECIFIC (his ruling
     2026-09-03). The playground joined the rail as an eleventh item, which cost
     61px -- its own box plus one gap -- and pushed the workshop notice's
     requirement from a window 786px tall to one 847px tall. The notice is his
     log, and between those two numbers sit a 1366x768 laptop and every
     part-height window; it simply stopped appearing on them. Measured, per gap:
     13px needs 807, 12px needs 797, 11px needs 787 -- so this is not a round
     number chosen for looks, it is the value that puts the notice back exactly
     where it was, and it was picked off that table rather than by eye.
     Ten gaps x 6px = the 60px the eleventh item took. */
  display:flex;flex-direction:column;gap:11px;
  background:var(--paper);
  z-index:10;
}
.rail .mark{
  font-family:'Bricolage Grotesque',sans-serif;font-weight:800;font-size:22px;
  letter-spacing:-0.02em;margin-bottom:30px;
}
.rail .mark span{color:var(--crimson)}
/* EACH ITEM IS A BOX, not a bar. Same object as a deck card, rail-sized: a
   rounded rectangle that drifts, collides, jitters and ROTATES on impact. The
   physics is the deck's rAF loop with a vertical bias, because this is a
   vertical list -- cards wander in a grid, these wander down a column. */
.rail a{
  position:relative;
  display:block;
  font-family:'Spline Sans Mono',monospace;font-size:12.5px;
  color:var(--ink-2);text-decoration:none;
  padding:10px 12px;
  border-radius:9px;
  transition:color .15s;
  will-change:transform;
}
.rail a:hover{color:var(--ink)}

/* THE LEAD ITEM WEARS THE DECK'S STRIP, THIN (his ruling 2026-09-03: "it's
   bounding box is a thin argb"). Every other item on this rail is a neon tube in
   its own hue; this one is the rotating ARGB border the cards on 01/explore
   wear, at 2px against their 6px. Two reasons it is the right object and not
   just a different one:

   IT NEEDS NO ELEVENTH COLOUR. The ten tubes already cover most of the wheel --
   that was the measured cost of giving the playground a rail slot at all, since
   an eleventh hue has to crowd a neighbour. The strip takes every hue instead of
   asking for one more, so the palette closes at ten and stays closed.

   AND IT SAYS "NOT ONE OF THE TEN". The numbered items are places in a
   sequence; this one sits above the sequence and carries no number, so it should
   not read as a section that lost its tube. It reads as the thing the deck
   reads as: something you open.

   THE GRADIENT IS A BORDER WITHOUT AN EXTRA ELEMENT -- two backgrounds, the
   paper clipped to the padding box and the strip to the border box. The --argb
   property and the argb-idle keyframes are DECK.CSS'S, not copies: that file is
   loaded by every page that loads this one, and a second declaration of a
   registered property is how the two would eventually rotate at different
   speeds. */
.rail a.lead{
  border:2px solid transparent;
  background-image:
    linear-gradient(var(--paper),var(--paper)),
    conic-gradient(from var(--argb),
      #ff0040, #ff8a00, #ffd400, #35d07f, #00c2ff, #6a5cff, #d94fff, #ff0040);
  background-origin:border-box;
  background-clip:padding-box,border-box;
  animation:argb-idle 9s linear infinite;
  color:var(--ink);
  filter:saturate(.62);
  transition:filter .3s ease,color .15s;
}
.rail a.lead:hover{ filter:saturate(1) }
/* AND IT STRIKES LIKE THE OTHERS DO, in the only way a strip can: the ten tubes
   go from a failing flicker to solid when you are on their section, so this one
   goes from muted to full colour. Without this the rail sat completely dark
   while the playground was open -- ten unlit tubes and a grey strip -- because
   "which item am I on" is answered by the same class either way. The colour
   override is here because the .rail a.active rule takes var(--led), and the
   lead item has no LED to take. */
.rail a.lead.active{ filter:saturate(1); color:var(--ink) }
/* no tube on this one -- the strip IS its frame. */
.rail a.lead::before{ display:none }

/* THE NEON TUBES (user ruling 2026-07-31). Each rail item has its OWN LED
   colour. Unselected, its tube is FAILING: it flickers erratically -- irregular
   gaps, the odd double-stutter, never a metronome blink. Selected, it strikes
   and STAYS ON, solid and glowing, and the page picks up the same colour as a
   bounding box (see `section.current` below). One tube lights the menu item and
   the page together.
   The deck on 01/explore is deliberately NOT part of this: its rotating ARGB
   strip is user-approved work and stays exactly as it is. */
/* TEN ITEMS, TEN TUBES (his ask 2026-09-03: *"in the menu the predominant
   bounding box colour is red; can we contrive more colours?"*). Six were named
   and four fell through to the `--crimson` default -- and with 00/overview's
   own red that made FIVE of the ten read red, which is not a spectrum, it is a
   default with exceptions. The four that were missing (pricing, download,
   examples, source) are named below, chosen to sit in the gaps the first six
   left on the wheel rather than beside them: no two ADJACENT items share a
   neighbourhood, and the closest pair on the whole rail -- 01 amber and 09 gold
   -- are the two furthest apart in the list.
   IT IS NOT ONLY THE MENU. `section.current` picks the same `--led` up as the
   page's bounding box, so naming these four also gives four sections a frame
   colour of their own; that is the point of the tube and why the default was
   worth spending.
   The section numbers in these comments are the rail's, and two of them were
   WRONG before this edit (docs read 05, and 04/05/08/09 had no line at all).
   Corrected while the block was open: a comment that misnumbers the thing it
   labels is worse than none, because it is checked against nothing. */
.rail a{ --led:var(--crimson) }
.rail a[data-s="top"]     { --led:#FF3B30 }   /* 00 overview  — red    */
.rail a[data-s="explore"] { --led:#FF9500 }   /* 01 explore   — amber  */
.rail a[data-s="platform"]{ --led:#00C2FF }   /* 02 platform  — cyan   */
.rail a[data-s="team"]    { --led:#35D07F }   /* 03 team      — green  */
.rail a[data-s="pricing"] { --led:#2E7DFF }   /* 04 pricing   — blue, and it is
                                                 the business panel's own blue
                                                 answering from the rail */
.rail a[data-s="download"]{ --led:#7ED321 }   /* 05 download  — lime   */
.rail a[data-s="origins"] { --led:#A855FF }   /* 06 origins   — violet */
.rail a[data-s="docs"]    { --led:#FF2D92 }   /* 07 docs      — pink   */
.rail a[data-s="examples"]{ --led:#00D3B8 }   /* 08 examples  — teal   */
.rail a[data-s="source"]  { --led:#FFC400 }   /* 09 source    — gold   */
/* AND THE TWO HIS PANELS OF 2026-09-08 BROUGHT (SITE-362). Same argument as
   the four above, and the same defect caught the same way: without these both
   new rows fell through to --crimson, which would have made SEVEN of thirteen
   read red. The hues are DERIVED rather than picked -- the ten in use were
   sorted by hue and the two largest gaps taken. ⚠ THE FIRST CUT DERIVED THEM
   FROM site.tokens.json AND THAT IS THE WRONG SOURCE -- those led-* tokens are
   read by NOTHING (render.js uses `led` only to derive data-s) and four of them
   disagree with the colours this block actually ships. Recomputed from the
   COMPUTED styles: gaps 89..149 and 269..331, midpoints 119 and 300, so each
   new tube is ~30deg from its nearest neighbour -- comparable to pairs already
   here (team 149 vs examples 172 is 23deg). */
.rail a[data-s="how"]        { --led:#F05BF0 }   /* 10 how it works — magenta, hue 300 */
.rail a[data-s="contribute"] { --led:#54E851 }   /* 11 help          — green, hue 119  */

/* The tube is the BOX ITSELF -- a rounded neon outline tracing the whole item,
   the rail's echo of a card's lit edge. It lives on ::before so the FLICKER
   animates the outline's opacity and never touches the label: text that blinks
   is unreadable, a frame that blinks is neon. */
.rail a::before{
  content:'';position:absolute;inset:0;border-radius:9px;pointer-events:none;
  border:1.5px solid var(--led);
  box-shadow:0 0 7px -1px var(--led),
             inset 0 0 9px -4px var(--led);
  /* The resting state of a BROKEN tube: mostly dark, struggling to strike. */
  opacity:.18;
  animation:tube-fail 5.5s steps(1,end) infinite;
}
/* Erratic by construction: the gaps are uneven, two of them are double-stutters,
   and the long dark stretch between 40% and 70% is what stops it reading as a
   pulse. Per-item duration + negative delay (the deck's desync lesson) means no
   two tubes ever agree. */
@keyframes tube-fail{
  0%,3%   {opacity:.85}
  4%      {opacity:.10}
  6%      {opacity:.80}
  7%      {opacity:.12}
  8%,11%  {opacity:.88}
  12%     {opacity:.10}
  19%     {opacity:.10}
  20%,21% {opacity:.75}
  22%     {opacity:.10}
  23%     {opacity:.70}
  24%,39% {opacity:.12}
  40%     {opacity:.90}
  41%,69% {opacity:.08}
  70%,71% {opacity:.80}
  72%     {opacity:.10}
  73%     {opacity:.85}
  74%     {opacity:.12}
  86%     {opacity:.12}
  87%     {opacity:.65}
  88%     {opacity:.10}
  96%     {opacity:.10}
  97%,100%{opacity:.85}
}
/* NO SPACE before ::before -- `a[x] ::before` is a DESCENDANT selector and these
   links have no element children, so it would silently match nothing and every
   tube would share one duration. */
.rail a[data-s="top"]::before{ animation-duration:5.5s; animation-delay:-0.7s }
.rail a[data-s="explore"]::before{ animation-duration:7.3s; animation-delay:-2.1s }
.rail a[data-s="platform"]::before{ animation-duration:4.7s; animation-delay:-3.4s }
.rail a[data-s="team"]::before{ animation-duration:8.1s; animation-delay:-1.2s }
.rail a[data-s="origins"]::before{ animation-duration:6.3s; animation-delay:-4.8s }
.rail a[data-s="docs"]::before{ animation-duration:7.7s; animation-delay:-2.6s }

/* STRUCK. The selected tube stops failing and simply burns: no animation, full
   opacity, a wider glow, and the label takes the tube's own colour. */
.rail a.active{ color:var(--led) }
.rail a.active::before{
  animation:none;
  opacity:1;
  border-width:2px;
  box-shadow:0 0 9px -1px var(--led), 0 0 22px -4px var(--led),
             inset 0 0 12px -5px var(--led);
}

/* THE FUZZLE, rail-sized. A box that just collided goes briefly soft while the
   loop adds shake -- the deck's idea at a scale that keeps 12.5px mono legible
   (the card's 1.6px blur would erase this text). */
.rail a.fuzz::before{ filter:blur(1.1px);transition:filter .08s ease }
.rail a.fuzz{ color:var(--ink-2) }
.rail .foot{margin-top:auto;font-family:'Spline Sans Mono',monospace;font-size:11px;color:var(--ink-2)}

/* ---- HIS PLAQUE BAND -------------------------------------------------------
   His ruling, 2026-08-22: the three engraved plaques he rendered belong at the
   TOP of the front page, on a pure #000 band -- "same principle and I prefer to
   have the image". The three materials are a deliberate vocabulary, not
   eclecticism: GOLD is the standard (what built the page), WOOD is nature and
   the human hand (the donation), SAPPHIRE is transparency (the see-through
   material for the open-book subject). A future plaque picks its material by
   what it MEANS.

   #000 AND NOT var(--panel). The plaques carry their own near-black field --
   measured at (0,0,0) to (5,5,5) along their outer pixel -- so on true black
   they seam invisibly and appear to float on the band. On the panel's #111417
   they would each sit in a faintly darker rectangle, which is the one thing the
   whole crop-and-derive step exists to avoid.

   IT IS OFFSET LIKE <main>, NOT FULL-BLEED. The rail is fixed, opaque and 200px
   wide from the bar downwards, so a full-bleed band would be sliced by it and
   the black would stop in the middle of nothing. The band lives in the same
   column its content does.

   THE HEIGHT IS SPENT, AND THE BUDGET IS THE FOLD -- MEASURED, NOT CHOSEN.
   v3.1.001.0 moved the run path above the demo because at 1280x720 it sat 421px
   below the fold. With no band at all the Get CX+AI door ends at y=669 in a
   720px viewport and the three steps at 694 -- the whole run path in the first
   screen. Anything above the hero spends that back, pixel for pixel: the band
   costs +68 and the hero's own padding gives -36, so the door moves to 701.

   Measured on the STAGED page under Emulation.setDeviceMetricsOverride -- five
   variants of THIS page, one per row, each rendered and read from the DOM:

       plaque  band  hero-pad   door ends   steps end   720: door/steps   768
         64     92     100         761         786        no  / no        no /no
         56     80      72         721         746        no  / no        YES/YES
         48     68      64         701         726        YES / no        YES/YES
         44     64      56         689         714        YES / YES       YES/YES
         40     56      48         673         698        YES / YES       YES/YES

   So his plaques at full 64px cost the door on BOTH common laptop heights, and
   48px -- HIS RULING of 2026-08-22 -- is the largest that keeps it, with 19px of
   margin at 720. The hero's top padding drops from 100px to 64px in the same
   motion, and that is not a sacrifice: a black band already separates the hero
   from the bar, so a good part of that whitespace was doing the band's job
   before the band existed.

   ONE THING THE FIRST TABLE COULD NOT SAY, BECAUSE IT NEVER MEASURED IT: at 48px
   the DOOR clears a 720px fold and the THIRD QUICKSTART STEP does not -- 726
   against 720, six pixels short. Only 44px and below put the whole run path in
   the first screen at that height. Reported rather than acted on: 48 is a ruled
   value and the hero's padding is not the band's to spend.

   AND THE ROW NUMBERS REPLACE THE ONES THIS COMMENT CARRIED BEFORE
   (777/737/717/705/689), which do not reproduce. Re-measured 2026-08-22 by
   reading `.getcx` out of the DOM in five rendered variants, the column comes
   back 16px lower throughout -- and the pre-band figure the paragraph above used
   to give (716) is 47px out, not 16. Two different offsets, so this is not one
   constant error with a tidy explanation; the numbers were simply taken before
   the page had settled, and the vendored webfonts re-flow the hero when they
   land. Every YES/no VERDICT the old table reached survives the correction, which
   is why his ruling stands on it -- but a table read as measurement has to BE one,
   and the discipline that makes it one is waiting for the artifact to finish
   painting. [[feedback_measure_the_artifact_that_ships]]

   THE FIRST VERSION OF THAT TABLE WAS WRONG BY 30 PIXELS, and the reason is
   worth keeping. It was measured against a server rooted at the SOURCE
   directory rather than the stage, where `<!-- SELF-HOSTED, same as every page on this origin: everything we authored
     -- fonts included -- is served from cxai.plus, and no request leaves this
     page except to Netlify, the host that already serves you every byte of it.
     The one thing we did not author is Netlify's own visit counter, one
     injected script reporting aggregate traffic to the host -- no cookie, no
     identity, no advertiser, no one else. -->` is still an unfilled
   marker -- and an unfilled marker in <head> is not head content, so the browser
   moves it into <body> as literal text and it takes a 30px line box. Invisible
   on screen only because the fixed rail is opaque and sits on top of it. Every
   number was 30px pessimistic, which cost a whole plaque size. MEASURE THE
   ARTIFACT THAT SHIPS: the source page and the staged page are different
   documents, and only one of them is the site.

   THE SIZE IS HIS CALL AND THIS IS THE FOLD-SAFE END OF IT. The run path is the
   one finding all three independent reviewers reached, so the default cannot be
   the variant that undoes it -- but the numbers above are what he is ruling on,
   and --band-h plus one img height is the whole edit.
   ------------------------------------------------------------------------- */
/* ---- HIS LAYOUT, 2026-08-22 ------------------------------------------------
   Four marks in three places, and the arrangement is his eyeball's, not a
   measurement's: "powered by CX+AI in centre - yes ... the other 2 images move
   to the right on top of each other and next to them the last image".

   A GRID, NOT A CENTRED FLEX ROW, AND THAT IS THE WHOLE REASON FOR THE CHANGE.
   `justify-content:center` centres the ROW; the moment a cluster joins the right
   end, the row's midpoint moves and the gold plaque drifts left of centre with
   it. `1fr auto 1fr` centres the MIDDLE CELL against the band regardless of what
   the side cells hold, so his first ruling survives his third. The left cell is
   deliberately empty -- it is the counterweight that makes the centre true.

   THE STACK IS 28px AND THE BAND DID NOT GROW, which is the one number in here
   that was forced rather than chosen. Two 48px plaques stacked are 100px against
   a 68px band, and the band's height is spent against the FOLD (the table below):
   68 puts the Get CX+AI door at y=701 in a 720px viewport with 19px to spare, and
   80 puts it at 721 -- off the first screen, undoing the run-path fix all three
   reviewers independently asked for. So the stack fits inside the height that
   already exists: 28 + 4 + 28 = 60px inside a 65px content box. That is smaller
   than his renders would like, and it is also the correct HIERARCHY -- one large
   mark that says what built the page, two small ones that ask for something, and
   the logo. His glance is the judge of the size; the fold is not negotiable.

   THE FOURTH ASSET CAME AND WENT. cxailogo.png joined the cluster on the
   layout ruling and LEFT on his glance of the same day: "no cx icon - not
   needed". The derivation still produces it (retiring the asset end-to-end is
   filed in TODO); the band no longer shows it, which also retires the
   contrast(1.14) black-point workaround its ~13/255 ground needed.

   AND THE GLANCE MERGED THE BARS. His ruling on the staged result: "the white
   bar and black should be one (except for the overview)". So the white fixed
   bar is gone, the band is the fixed layer, its left cell -- the empty
   counterweight -- now holds the wordmark and Try it, and the controls joined
   the cluster. The grid's 1fr auto 1fr is what makes that free: the centre
   plaque cannot drift whatever the side cells hold. One bar in the flow
   instead of two (56px bar + 68px band -> 68px band) hands 56px back to the
   fold, so every door figure in the table above improves by that much.
   ------------------------------------------------------------------------- */
.plaqueband{
  /* THE NEON FRAME IS THE RAIL'S TUBE, STRUCK (his ruling: "The black space came
     horrible but we can save it. we make neon red around it"). Same border, same
     three-layer glow, same --led custom property carrying the hue -- the notice
     took this idiom from the rail in v3.298.0 and the band takes it from the same
     place rather than from the notice's copy of it (rule 33). RED is his word and
     #FF3B30 is the site's neon red: it is 00/overview's LED, already on this page,
     not a fourth red invented for one element.
     IT TAKES THE STRUCK STATE, NOT THE FAILING ONE, for the notice's reason -- a
     rail item flickers because it is a tube waiting to be chosen, and a band that
     holds his maker's marks is not awaiting selection.
     THE FRAME COSTS NO HEIGHT. border-box means the 1.5px edge is drawn inside
     var(--band-h), and the insets are horizontal only, so every row of the fold
     table below still reads true after this rule. That was the constraint, and it
     is why the panel is inset at the sides and flush at top and bottom. */
  /* FIXED AT THE TOP NOW, because it IS the bar (his one-bar ruling above).
     left:214 = the rail's 200 plus the gutter -- "except for the overview"
     drawn as geometry: the paper corner over the rail is the one part of the
     top the black does not take, and it is where the notice slides in. The
     top corners go square because the band is flush with the viewport edge. */
  --led:#FF3B30;
  /* HIS EYEBALL, 2026-08-23, ON THE LIVE 2.12.0: "no top white edge" and "the
     workshop sitting top-left". Those two and "the menu still not below the top
     black bar" are ONE observation from three sides, and the comment above is
     what proves it -- the paper corner existed so the notice had somewhere to
     slide into, and it is exactly the white edge he is looking at. So the black
     takes the whole top edge, the rail starts below it, and the notice no longer
     lives in a corner that no longer exists.
     THE INSETS ARE TOKENS so that is a NUMBER he can change rather than an arc:
     site.tokens.json holds band-inset-left / band-inset-right, the renderer
     writes them onto :root at boot, and putting the corner back is one edit to a
     JSON file. */
  position:fixed;top:var(--band-inset-top,0px);left:var(--band-inset-left,214px);right:var(--band-inset-right,14px);z-index:70;
  height:var(--band-h);
  background:#000;
  display:grid;
  grid-template-columns:1fr auto 1fr;
  align-items:center;
  padding:0 16px;
  border-radius:var(--band-radius,0 0 10px 10px);
  border:1.5px solid var(--led);
  box-shadow:0 0 9px -1px var(--led),
             0 0 22px -4px var(--led),
             inset 0 0 12px -5px var(--led);
}
.plaqueband .centre{grid-column:2;justify-self:center}
.plaqueband .cluster{grid-column:3;justify-self:end;display:flex;align-items:center;gap:14px}
/* THE STACK IS A COLUMN, and it is the only place on this band where two marks
   share a slot. They are the two ASKS -- the coffee and the offer of help -- so
   they belong together and below the statement, not beside it. */
.plaqueband .stack{display:flex;flex-direction:column;gap:4px}
/* Scoped to the img-bearing anchors: the band carries the bar's controls now,
   and a line-height:0 block treatment on the Try it / Get doors would crush
   their text. The plaque anchors are the ones whose first child is an <img>. */
.plaqueband .centre a,.plaqueband .stack a{display:block;line-height:0;border-radius:6px;text-decoration:none}
.plaqueband img{display:block;height:48px;width:auto}
.plaqueband .stack img{height:28px}
/* (The cX neon mark left the band on his ruling of 2026-08-22 -- "no cx icon -
   not needed" -- and its contrast(1.14) black-point rule went with it.) */
/* The lift is brightness, not scale: these are pictures of physical objects and
   a plaque that grows when you point at it stops being one. */
.plaqueband .centre a,.plaqueband .stack a{transition:filter .18s}
.plaqueband .centre a:hover,.plaqueband .stack a:hover{filter:brightness(1.12)}
.plaqueband .centre a:focus-visible,.plaqueband .stack a:focus-visible{outline:2px solid #E8CB68;outline-offset:4px}
@media (prefers-reduced-motion:reduce){.plaqueband a{transition:none}}
/* The rail makes its room instantly when the visitor has asked for no motion --
   the same rule the notice's own slide follows. */
@media (prefers-reduced-motion:reduce){.rail{transition:none}}
html.still .rail{transition:none}
/* Below the rail's own breakpoint the band goes full-bleed and the PICTURES
   STAND DOWN (his mobile ruling, 2026-08-22: "for the mobile we don't need to
   use images but a simpler theme"). What remains is the bar's working set --
   the wordmark, Try it, the language picker and the section menu -- in one
   68px row. The bytes still travel (display:none does not stop a download);
   withholding them is the mobile arc's job, filed and open. */
@media (max-width:900px){
  .plaqueband{left:0;right:0;border-radius:0;border-left:0;border-right:0;padding:0 14px}
  .plaqueband .centre,.plaqueband .stack{display:none}
}
main{margin-left:200px}
/* Below 901px the router steps aside and these are ordinary anchor jumps, so
   every target must clear the fixed bar -- without this the heading a visitor
   just tapped lands UNDERNEATH it. */
section{padding:88px 56px;border-bottom:1px solid var(--hairline);
  scroll-margin-top:calc(var(--topbar-h) + 8px)}
.wrap{max-width:var(--maxw);margin:0 auto}

/* ONE SECTION PER CLICK -- the whole site behaves like the deck (user eyeball
   2026-07-31). The rail is the driver: exactly one section is shown, the rest
   are removed from the flow, and the router swaps `.current` on a rail click.
   GATED TWICE. `html.js` -> off without scripting (all sections visible, an
   ordinary scroll -- the no-JS contract). `min-width:901px` -> off below the
   rail's own breakpoint, where the rail is hidden and there would be no way to
   drive the sections; there the page stays the scroll it already was, which is
   also exactly the mobile design that shipped. So the single-section model
   exists in precisely the window where the driver does. */
@media (min-width:901px){
  /* 100vh MINUS the fixed bar -- body already carries that much padding, so a
     full 100vh here would overflow the viewport by exactly the bar's height. */
  html.js main{display:flex;flex-direction:column;min-height:calc(100vh - var(--band-h))}
  html.js main > section{display:none}
  html.js main > section.current{display:block;flex:1 0 auto}
  html.js footer{flex:0 0 auto}

  /* THE PAGE WEARS THE SAME TUBE. The shown section is framed in the colour of
     the rail item that opened it -- one bounding box, lit solid, the page half
     of the menu's neon. `--page-led` is set on <main> by the router, so the box
     and the struck tube can never disagree about the colour: there is ONE
     value and both read it. */
  html.js main{ --page-led:#FF3B30 }
  html.js main > section.current{
    /* THE TOP LINE NEEDS ROOM TO EXIST. The first cut used `margin:0 18px 18px`,
       so the box's top edge sat flush against y=0 and the frame read as two
       verticals with no lid. A box is a box on all four sides. */
    border:2px solid var(--page-led);
    border-radius:12px;
    margin:18px;
    box-shadow:0 0 10px -1px color-mix(in srgb, var(--page-led) 60%, transparent),
               0 0 34px -6px color-mix(in srgb, var(--page-led) 40%, transparent),
               inset 0 0 22px -14px var(--page-led);
    transition:border-color .35s ease, box-shadow .35s ease;
  }
  /* The access section is the dark panel; its own border-bottom:none plus the
     frame would otherwise fight. Give it the frame and keep the panel. */
  /* The incoming section rises in, echoing the card lift. One transform + fade,
     nothing that reflows its neighbours (there are none on screen to reflow). */
/* backwards, NOT both, AND THE DIFFERENCE IS A PAGE THAT WORKS. both keeps the
   animation's final value applied for ever. That value is `transform: none` --
   which sounds harmless and is not: an ANIMATED transform computes to
   `matrix(1,0,0,1,0,0)`, and ANY transform makes the element the containing
   block for its `position: fixed` descendants. A lifted deck card is fixed and
   positions itself against the viewport, so its coordinates were being read
   against this section instead: measured at y = -1747 in an 860px window, which
   is a card that vanishes when you click it.
   `backwards` drops the forwards half only. The keyframe's end state is the
   element's natural state, so nothing changes on screen -- measured after: the
   section computes `transform: none` and the card lands at y = 128.
   The entry animation still runs, and still traps a fixed child for its 0.42s,
   which is a window in which no card can be open. */
  html.js main > section.current{animation:sectionIn .42s cubic-bezier(.22,.9,.24,1) both}
/* AND IT IS SWITCHED OFF WHILE A CARD IS LIFTED, which is the whole of the fix.
   A lifted deck card is position:fixed and places itself against the VIEWPORT --
   but any transform on an ancestor makes that ancestor the containing block for
   fixed descendants, and an ANIMATED transform computes to matrix(1,0,0,1,0,0)
   even when the keyframe says none. So the card was reading its coordinates
   against this section: measured at y = -1747 in an 860px window, which is a
   card that vanishes the moment you click it.
   THE FIRST FIX WAS backwards INSTEAD OF both, AND IT WAS WRONG IN A WAY WORTH
   RECORDING. It does remove the trailing identity transform -- measured, card at
   y = 128 -- but both fill directions carry the FROM state, and a from state of
   opacity:0 is a section nobody can see if the animation never advances. Caught
   in a THROTTLED TAB, where CSS animations do not start: the section sat at
   opacity 0 with translateY(16px) applied and play-state running. Under both
   that is equally true, so it is not a regression either way -- but a fix whose
   correctness depends on an animation actually running is not one to prefer.
   Turning the animation OFF for the duration of the zoom needs nothing to have
   run: transform resolves to none, opacity to its base 1, and the normal path is
   untouched. Measured after: card at y = 120, section transform none, opacity 1.
   It has to beat an ANIMATION rather than a declaration, which is why it removes
   the animation rather than setting transform:none -- an animated property wins
   over a normal one in the cascade whatever the selector says. */
  body.zoomed main > section.current{animation:none}
}
@keyframes sectionIn{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:none}}
.eyebrow{
  font-family:'Spline Sans Mono',monospace;font-size:12.5px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--crimson);margin-bottom:18px;
}
h1,h2{font-family:'Bricolage Grotesque',sans-serif;letter-spacing:-0.025em;line-height:1.06}
h1{font-size:clamp(40px,5.4vw,68px);font-weight:750;max-width:19ch}
h2{font-size:clamp(28px,3.4vw,40px);font-weight:700;margin-bottom:22px;max-width:24ch}
.lede{font-size:clamp(20px,2vw,24px);color:var(--ink-2);max-width:56ch;margin-top:22px;font-weight:340}

/* ---- NO LINE LEFT HOLDING ONE WORD (his ruling 2026-08-25) -----------------
   "A complete stack, written in pure C." was breaking so that "C." sat alone on
   a line of its own, and his words for it were: "a line just for C here is
   rather wasteful." He is describing a WIDOW, and a widow is not a property of
   that sentence -- it is what a fixed `max-width` in `ch` does to any headline
   whose length lands just past the measure. So this is a rule over every
   headline rather than a hand-tuned break in one of them: `balance` asks the
   engine to even out the lines instead of filling the first and dropping the
   remainder, and on this heading it gives the two halves of the sentence a line
   each -- which is the reading the comma already asked for.

   PROSE GETS `pretty`, NOT `balance`. Balance is for a few short lines and the
   engines cap it (Chrome stops at six); on a paragraph it does nothing useful.
   `pretty` is the one that exists to keep a last line from carrying a single
   word, which is the same defect at a different scale.

   OLDER ENGINES IGNORE BOTH and wrap exactly as they do today, so there is no
   fallback to write and nothing to feature-detect. */
h1, h2, .eyebrow, .subhead, .masthead { text-wrap: balance; }
.lede, .cx-prose p, .facts-note { text-wrap: pretty; }
.lede em{font-style:italic;color:var(--ink)}

/* hero terminal diptych — the signature */
/* 64px, not 100px: the plaque band above now supplies the separation from the
   fixed bar that this padding used to, and the 36px it gives back is what keeps
   the Get CX+AI door inside a 720px viewport (see .plaqueband for the table). */
.hero{padding-top:64px}
.demo-tabs{display:flex;gap:8px;margin-top:48px}
.demo-tabs .dtab{
  background:transparent;border:1px solid var(--hairline);border-radius:4px;
  color:var(--ink-2);padding:8px 18px;font-family:'Spline Sans Mono',monospace;
  font-size:12.5px;letter-spacing:.05em;text-transform:uppercase;cursor:pointer;
}
.demo-tabs .dtab.active{border-color:var(--crimson);color:var(--crimson)}
.demo{
  margin-top:14px;display:grid;grid-template-columns:1fr 1fr;gap:1px;
  background:var(--hairline);border:1px solid var(--hairline);
  border-radius:6px;overflow:hidden;
  box-shadow:0 24px 60px -30px rgba(22,24,26,.35);
}
.pane{background:var(--panel);padding:26px 28px 30px;min-height:260px}
.pane .bar{
  font-family:'Spline Sans Mono',monospace;font-size:11.5px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--panel-dim);margin-bottom:20px;
  display:flex;justify-content:space-between;
}
.pane .bar b{color:var(--panel-ink);font-weight:500}
.pane pre{
  font-family:'Spline Sans Mono',monospace;font-size:14.5px;line-height:1.75;
  color:var(--panel-ink);white-space:pre-wrap;word-break:break-word;
}
.tok-k{color:#8AB8E8}.tok-s{color:var(--green)}.tok-c{color:var(--panel-dim)}
.tok-r{color:#E88A93}
.caret::after{content:'▍';color:var(--crimson);animation:blink 1s steps(1) infinite}
@keyframes blink{50%{opacity:0}}
@media (prefers-reduced-motion:reduce){.caret::after{animation:none}}
.demo-note{
  font-family:'Spline Sans Mono',monospace;font-size:12.5px;color:var(--ink-2);
  margin-top:14px;max-width:78ch;line-height:1.75;
}
.demo-note b{color:var(--crimson);font-weight:600}

/* THE RUN PATH. Quiet furniture around one loud button: the steps are the same
   monospace the demo captions use, so they read as instructions rather than as
   a second block of prose competing with the lede. The button is the hero's
   only accent besides the fixed layer's Try it, and they never appear in the
   same band of the page. */
.runpath{
  display:flex;align-items:flex-start;gap:26px;flex-wrap:wrap;
  margin-top:26px;padding-top:22px;border-top:1px solid var(--hairline);
}
.getcx{
  background:var(--crimson);color:#fff;text-decoration:none;
  font-family:'Bricolage Grotesque',sans-serif;font-weight:700;font-size:15px;
  padding:12px 22px;border-radius:4px;white-space:nowrap;flex:0 0 auto;
}
.getcx:hover{filter:brightness(1.1)}
.getcx:focus-visible{outline:3px solid var(--ink);outline-offset:3px}
.runsteps{
  font-family:'Spline Sans Mono',monospace;font-size:12.5px;color:var(--ink-2);
  margin:0;padding-left:20px;line-height:1.9;max-width:64ch;
}
.runsteps li::marker{color:var(--ink-2)}
.runsteps code{background:var(--paper-deep);color:var(--ink);padding:1px 6px;border-radius:3px}
/* THE TERMS LINE, under the door rather than beside it (v3.1.003.0). `flex-basis
   100%` puts it on its own row of the same flex container, so it sits below BOTH
   the button and the steps without a wrapper div -- and it cannot move the door,
   which is the element the fold table beside .plaqueband is measured against. */
.runterms{
  flex:1 0 100%;margin:8px 0 0;
  font-family:'Spline Sans Mono',monospace;font-size:12px;color:var(--ink-2);
}
.runterms a{color:var(--ink-2);text-decoration:underline;text-underline-offset:3px}
.runterms a:hover,.runterms a:focus-visible{color:var(--crimson)}
/* The fixed layer's door wears the ACCENT now (his ruling 2026-08-22: "the get
   cx+ai should be the same as the image (consistency)") -- it takes .tryit
   wholesale, so the two red doors up top cannot drift apart. This class only
   keeps its mobile stand-down below. */
.getcx-quiet{display:inline-block}
@media(max-width:900px){
  /* The bar is already carrying a menu toggle, two preferences and Try it below
     901px. The hero's door is a screen away, not a page away, so the quiet one
     stands down rather than wrapping the header onto two lines. */
  .getcx-quiet{display:none}
  .runpath{gap:16px}
}

/* platform facts */
.facts{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--hairline);border:1px solid var(--hairline);margin-top:10px}
.fact{background:var(--paper);padding:26px 24px}
.fact .n{font-family:'Bricolage Grotesque',sans-serif;font-size:38px;font-weight:750;letter-spacing:-0.03em}
.fact .l{font-family:'Spline Sans Mono',monospace;font-size:12.5px;color:var(--ink-2);margin-top:4px}
.fact .sub{font-family:'Spline Sans Mono',monospace;font-size:11px;color:var(--ink-2);
  opacity:.72;margin-top:3px}

/* The feature deck reuses .card/.deck wholesale (see the markup); only its
   heading needs styling of its own. The accordion rules that used to live here
   -- .feats/.feat/.fb and a hand-drawn +/- marker -- are GONE rather than left
   unused: a second way to open an explanation is exactly what the user rejected. */
.feath{
  font-family:'Bricolage Grotesque',sans-serif;font-weight:750;font-size:26px;
  letter-spacing:-0.02em;margin:52px 0 0;
}

/* A SHORT SNIPPET INSIDE A CARD. Every snippet in the platform deck was
   COMPILED with the shipped cx.exe before it was published, and the two that
   are safe to execute were run -- code on a marketing site that does not build
   is its own kind of lie, and the first draft of the aiRule one did not: it
   passed a map where the entity is an int id, and the compiler said so. */
.snip{
  font-family:'Spline Sans Mono',monospace;font-size:13px;line-height:1.6;
  background:var(--panel);color:var(--panel-ink);
  border-radius:6px;padding:13px 15px;margin:16px 0 0;
  overflow-x:auto;white-space:pre;
}
.snip .c{color:#7C8A94}
.snip .k{color:#8FD3FF}
.snip .s{color:#F0B7A4}

/* The stdlib family grid. Thirty cells, and the counts are the linked
   reference's own -- they sum to exactly 500, which is the number on the tile
   above, so the list PROVES the headline rather than restating it. */
.famgrid{
  display:grid;grid-template-columns:repeat(3,1fr);gap:1px;
  background:var(--hairline);border:1px solid var(--hairline);margin-top:15px;
}
.famgrid div{
  background:var(--paper);padding:6px 10px;
  font-family:'Spline Sans Mono',monospace;font-size:11.5px;color:var(--ink-2);
  display:flex;justify-content:space-between;align-items:baseline;gap:8px;
}
.famgrid b{color:var(--ink);font-weight:600;font-variant-numeric:tabular-nums}
.famsum{font-family:'Spline Sans Mono',monospace;font-size:11.5px;
  color:var(--ink-2);margin-top:9px}
@media(max-width:620px){ .famgrid{grid-template-columns:repeat(2,1fr)} }

/* "For the experts". The plain answer comes first and stands alone; this is
   the technical half, fenced off so a non-specialist can stop reading at the
   line and lose nothing (user: "we don't want to scare people"). */
.expert{margin-top:18px;padding-top:14px;border-top:1px solid var(--hairline)}
.expert .lbl{
  display:block;font-family:'Spline Sans Mono',monospace;font-size:11px;
  letter-spacing:.09em;text-transform:uppercase;color:var(--crimson);
  margin-bottom:7px;
}

.facts-note{font-size:16px;color:var(--ink-2);margin-top:18px;max-width:70ch}
.facts-note + .facts-note{margin-top:12px}

/* team */
.team{display:grid;grid-template-columns:1fr 1fr;gap:44px;margin-top:8px}
.member h3{font-family:'Bricolage Grotesque',sans-serif;font-size:21px;font-weight:700}
.member .role{font-family:'Spline Sans Mono',monospace;font-size:12.5px;color:var(--crimson);margin:4px 0 10px;text-transform:uppercase;letter-spacing:.06em}
.member p{font-size:17px;color:var(--ink-2)}

/* access */
form{margin-top:34px;display:flex;gap:12px;max-width:560px;flex-wrap:wrap}
input[type=email]{
  flex:1;min-width:240px;background:#1B2025;border:1px solid #2E363D;border-radius:4px;
  padding:14px 16px;color:var(--panel-ink);font-family:'Spline Sans Mono',monospace;font-size:15px;
}
input[type=email]:focus{outline:2px solid var(--crimson);outline-offset:1px;border-color:transparent}
button[type=submit]{
  background:var(--crimson);color:#fff;border:none;border-radius:4px;
  padding:14px 26px;font-family:'Bricolage Grotesque',sans-serif;font-weight:700;font-size:16px;
  cursor:pointer;letter-spacing:.01em;
}
button[type=submit]:hover{filter:brightness(1.1)}
button:focus-visible{outline:2px solid #fff;outline-offset:2px}
.tagline{
  margin-top:70px;font-family:'Newsreader',serif;font-style:italic;
  font-size:clamp(22px,2.6vw,30px);color:var(--panel-ink);font-weight:340;
}
.tagline b{color:#fff;font-weight:500;font-style:normal}
/* ---- THE CONTRIBUTE BANNER (v3.305.0, his "a banner..notice+page for anybody
 * who want to contribute/help").
 *
 * IT IS NOT A SECTION, AND THAT IS THE WHOLE PLACEMENT DECISION. The router
 * shows one <section> at a time; a sixth one would have needed a rail tube, a
 * nav entry and a slot in the 00-05 sequence -- a heavier thing than a banner,
 * and it would have taken a place in the thirty-second read that the language
 * itself should have. Sitting outside the router, beside the footer, it is
 * visible from every section and competes with none of them.
 *
 * AND IT MUST NOT FIGHT THE NOTICE. The ticker slides, glows and carries the
 * only saturated colour on the page, because it is the thing that says the
 * project is alive. This is flat, quiet, in the footer's own palette and on the
 * opposite side of the page. Two things asking for attention at once means
 * neither gets it, and the notice was here first.
 *
 * DARK, LIKE THE FOOTER IT LEANS ON. Sections alternate paper and panel as the
 * router moves, so a band with a light ground would flash between two states no
 * visitor asked to see. Taking the footer's ground makes the bottom of the page
 * one block wherever the reader happens to be. */
.helpband{padding:22px 56px;background:var(--panel);border-top:1px solid #21262B}
.helpband .wrap{display:flex;align-items:baseline;justify-content:space-between;
  flex-wrap:wrap;gap:10px 24px;font-family:'Spline Sans Mono',monospace;
  font-size:12.5px;line-height:1.6;color:var(--panel-dim)}
.helpband p{margin:0;max-width:74ch}
.helpband b{color:var(--panel-ink);font-weight:500}
.helpband a{color:var(--panel-ink);text-decoration:none;white-space:nowrap;
  border-bottom:1px solid #2E363D;padding-bottom:2px}
.helpband a:hover,.helpband a:focus-visible{color:#fff;border-bottom-color:var(--crimson)}
footer{padding:26px 56px;background:var(--panel);border-top:1px solid #21262B}
footer .wrap{display:flex;justify-content:space-between;flex-wrap:wrap;gap:10px;
  font-family:'Spline Sans Mono',monospace;font-size:12px;color:var(--panel-dim)}
footer a{color:var(--panel-dim)}

@media(max-width:900px){
  .rail{display:none}
  main{margin-left:0}
  section{padding:64px 24px}
  .demo,.team{grid-template-columns:1fr}
  .facts{grid-template-columns:1fr 1fr}
  footer{padding:22px 24px}
  .helpband{padding:20px 24px}
}

/* ---- THE TICKER: what is being built, right now ---------------------------
 * His design (2026-08-19): "a popping notice which comes out slowly from the
 * side (left) -- which we populate on what we are working on now."
 *
 * FIXED, AND THAT IS THE HARD REQUIREMENT RATHER THAN A STYLE CHOICE. The
 * content arrives from another origin at page load, so it may arrive late, or
 * wrong, or never. `position:fixed` means the notice is outside flow in every
 * one of those cases: it cannot move a single pixel of the page whatever the
 * endpoint does, and there is no reserved box to sit empty when it does
 * nothing. It also starts `hidden`, so absence is the DEFAULT state and
 * appearing is the exception -- the opposite arrangement is how a broken fetch
 * leaves a visitor looking at an empty panel.
 *
 * Z-INDEX 40 puts it above the rail (10) and below the scrim (50) and a lifted
 * card (60). Opening a card is the visitor asking to read something; the notice
 * getting out of the way is correct.
 *
 * IT COMES OUT SLOWLY, which is a legibility decision as much as his taste: at
 * 1.1s the eye follows it instead of being startled by it, and it finishes long
 * before anyone has scrolled to the second section. */
.ticker{
  /* IT COMES OUT ON THE TOP LINE (his ruling 2026-08-22: the notice, when it
     slides in, sits on the same line as the top row). It used to sit at
     bottom:22px, which put it in the one corner of the page nothing else was
     using -- a defensible place for a footnote and the wrong one for the thing
     the crown is about. Aligned to var(--topbar-h) its top edge is the band's
     top edge, so the notice reads as the crown's left-hand end: the log slides
     out level with the marks it is a log of.
     IT STILL OVERLAYS THE RAIL, because a 210px panel coming out of the left
     edge always will -- what changes is WHICH items it crosses. At the bottom it
     cut through 05 / docs and the rail's foot; at the top it crosses the first
     three or four, and it is dismissible either way. Said plainly rather than
     claimed as a fix: this is a placement ruling, not an occlusion fix. */
  /* top:0, because the top row IS the top of the viewport now -- the band is
     the fixed bar and the notice slides out level with it, in the paper corner
     over the rail that the one-bar ruling left un-blacked. */
  position:fixed;left:0;top:0;z-index:40;
  /* WIDTH, AND WHY THIS NUMBER (revised v3.295.0 -- his "it needs to be bigger").
     MEASURED BY LOOKING, twice now. v3.294.0's first cut was 330px wide and four
     entries tall, and the screenshot showed it lying across the hero's own prose
     and the top-left of the code panel -- while every structural check passed,
     because "does it overlap the pitch" is not a question a DOM query knows how
     to ask. That cut was then pulled back to the rail's own 200px.

     Growing it has a HARD CEILING, and the ceiling was MEASURED rather than
     reasoned to -- which mattered, because the reasoning was wrong. The obvious
     argument says: the rail is 200, sections pad 56, so nothing is at risk
     before x=256. The page says otherwise. `main > section.current` draws its
     own neon frame, and that frame's left edge sits at x=218 at EVERY viewport
     from 918px to 1898px (checked at five). Text does start at 276, so a 236px
     notice covered no words -- and the screenshot showed it crossing the
     section's lit edge anyway. Covering one neon tube with another, in the very
     arc where he ruled on the neon standard, is precisely the defect worth
     catching.

     So: 210px. It clears 218 by 8px, and it still overhangs the rail's 200px
     edge, which is the read that matters -- the notice came OUT of the side, it
     is not a panel filed neatly into the furniture. The rest of "bigger" is
     spent where it is not rationed: the type, the spacing, a fourth entry when
     the window is tall enough for one, and the hover. The probe asserts the
     notice's rect against the LIVE furniture, not against this comment. */
  width:210px;
  box-sizing:border-box;
  background:var(--panel);color:var(--panel-ink);
  /* NAMED, because the close button is positioned FROM them -- see .ticker-x.
     THE TOP IS 26px RATHER THAN 14px, AND IT IS THE PRICE OF A TITLED BOX. The
     header spans the panel so that its words are centred at every width (his
     sixth ask); the close button therefore has nowhere on that line to go, and
     at 210px it has nowhere on the line even in principle -- MEASURED: the
     English title ends at x=176 and a button on that row starts at x=167, and
     German is longer. So the button sits ABOVE the box, in a strip made for it,
     and the bottom padding gives 3px of that back. */
  --tk-pad-top:26px;
  --tk-pad-right:16px;
  padding:var(--tk-pad-top) var(--tk-pad-right) 12px 18px;
  border-radius:0 12px 12px 0;

  /* ---- THE NEON TUBE (his ruling 2026-08-19: "we have a standard and the
     notice isn't up to standard; it should be in neon"). This is the RAIL's
     tube, inherited rather than reinvented: the same lit edge, the same
     three-layer glow, the same --led custom property carrying the hue.

     IT TAKES THE STRUCK STATE, NOT THE FAILING ONE -- solid, no animation, and
     that is his "it should not jitter" as well as the correct reading of the
     metaphor. A rail item flickers because it is a tube waiting to be chosen; a
     notice is not awaiting selection, it is an announcement. A flickering
     announcement is a fault light. */
  border:1.5px solid var(--led);
  border-left:0;
  box-shadow:0 0 10px -1px var(--led),
             0 0 26px -6px var(--led),
             inset 0 0 14px -6px var(--led),
             0 10px 30px rgba(0,0,0,.28);

  transform:translateX(-102%);
  /* The slide is unchanged at 1.1s. The hover expansion is a SEPARATE, faster
     curve on separate properties -- a hover that took a second to answer would
     feel broken, and a slide that took a quarter of one would be a pop. */
  transition:transform 1.1s cubic-bezier(.16,.84,.30,1),
             width .3s ease, padding .3s ease,
             box-shadow .3s ease;
}
/* THE TUBE SAYS WHAT THE CHANGE WAS, and the four hues now live in ticker.css
   because build.html renders the same rows (v3.297.0). What stays here is the
   fact that the BOX takes one: the notice announces ONE thing -- what is
   happening now -- so its tube burns in the NEWEST entry's class while each row
   keeps its own. `data-change` is set on the box in show() below. */
/* THE FALLBACK HUE, AND WHY IT IS SPELT WITH :not() (v3.297.0).
   This was a bare `--led:var(--crimson)` inside the .ticker rule above, which
   was correct for as long as the four change colours lived in this same
   stylesheet BELOW it. Moving them to ticker.css moved them ABOVE: a <link> in
   <head> precedes this inline <style>, `.ticker` and `[data-change="..."]` have
   IDENTICAL specificity (0,1,0), and at equal specificity source order decides.
   So the extraction silently repainted the notice crimson while every class,
   every attribute and every row colour stayed exactly right.
   Written as :not([data-change]) it is (0,2,0) AND it only applies when there is
   genuinely no class to apply -- which is a truer statement of what a fallback
   is than "crimson, unless something later disagrees". */
.ticker:not([data-change]){ --led:var(--crimson) }
.ticker[hidden]{display:none}
/* THE WORKSHOP ROW'S VERSION, which leads the line rather than trailing it
   (his ask 2026-09-02). Mono because a version is a number and reads as one;
   green because he asked for green, and `--green` is the palette's only one --
   a hex typed here is how a second green starts. Smaller than the sentence it
   introduces, so it labels rather than competes. NOTE it is `.tk-v`, a
   different row from `.tk-ver` above: that one is the standalone ticker page's,
   this one the front page's workshop notice, and they are rendered by different
   files. */
.cx-list .tk-v{font-family:'Spline Sans Mono',ui-monospace,monospace;font-size:.72em;
  letter-spacing:.02em;color:var(--green)}

.ticker.out{transform:translateX(0)}
/* ---- "IN THE WORKSHOP": THE ONE THING IN HERE THAT MOVES ------------------
 * His ask, 2026-08-19: "in the workshop can it be a different colour, larger
 * font, in neon colours and jittering."
 *
 * IT IS RED, IN A BOX OF ITS OWN, AND CENTRED -- his ruling of 2026-08-19,
 * three of the six things he said the notice still got wrong. It used to take
 * var(--led), the notice's own tube colour, which was a defensible mechanism
 * and the wrong answer: --led means WHAT THE CHANGE WAS, so the title wore a
 * data identity and one hue said two things.
 *
 * IT SHAKES, IT DOES NOT BLINK -- and the animation itself is no longer HERE.
 * It moved to ticker.css at v3.310.0, as the class `tk-jitter`, because the
 * archive page carries the same title and could not see keyframes declared in
 * this page's inline <style>: the title on the page named after it sat still
 * while the one on the notice trembled. That file's own comment carries the
 * reasoning about WHY it is a transform rather than a blink; what is left here
 * is the one thing that is true of a NOTICE -- that its header is the only thing
 * in it allowed to move at all. */
.ticker-h{
  /* IT IS A TITLE, AND ITS WORDS ARE GOLD (his ruling 2026-08-21). They were RED
     from v3.298.0 -- his "it's a title... Red is the obvious choice for titles"
     -- and gold is the reversal, recorded here and in ticker.css rather than
     quietly applied: the site's gold is the maker's plaque in every footer, and
     the workshop title is that same signature said in words.
     It was cyan before either, and not by choice but by inheritance: it took
     var(--led), the notice's tube, and the tube burns in the NEWEST ENTRY'S
     CLASS. So a week of new features made the header cyan, a week of fixes
     would have made it green, and the title was wearing whichever data identity
     the log happened to be carrying. --tk-title is declared in ticker.css
     beside the four change hues, precisely so that it is visibly NOT one of
     them -- which is now a MEASUREMENT, because gold sits one hue from the
     major class's yellow. */
  color:var(--tk-title);
  text-shadow:0 0 10px color-mix(in srgb, var(--tk-title) 55%, transparent);

  /* ITS OWN BOX -- his "ARGB box", which is this site's struck tube: a lit
     border with the three-layer glow, exactly as the rail draws one and as the
     notice around it draws one. The title is then an object in its own right
     rather than a line of text leaning on the panel's edge.

     AND IT IS THE TUBE THIS SITE ALREADY DRAWS, which is his ruling of
     2026-08-21: "the frame should be the argb frame we use elsewhere." Every
     other tube on this page -- the rail's `a::before`, its active state, the
     notice around this header -- takes `var(--led)`, THE ELEMENT'S OWN IDENTITY
     COLOUR. Not one of them carries a hex of its own.

     v3.310.0 briefly gave this one a name and a fixed red (`--tk-frame`), on the
     reasoning that the box was the notice's furniture while the words were the
     title. That is recorded rather than deleted because it was wrong in an
     instructive way: it made this the only struck tube on the site with a
     seventh colour nobody could trace, and it split one object into two
     identities to protect a symmetry that was never at risk -- the archive page
     has no tube at all, and what the two surfaces SHARE is the colour, which
     both already take from --tk-title. */
  border:1.5px solid var(--tk-title);
  border-radius:7px;
  box-shadow:0 0 8px -2px var(--tk-title),
             0 0 20px -8px var(--tk-title),
             inset 0 0 12px -7px var(--tk-title);
  padding:5px 8px 4px;

  /* AND IT STAYS CENTRED WHEN THE NOTICE GROWS (his sixth ask). `display:block`
     with `text-align:center` centres the WORDS inside a box that is always the
     full width of the panel -- so it is centred at rest, centred on hover, and
     centred at every width the media queries produce, without anything having
     to be re-measured. */
  display:block;text-align:center;
  margin:0 0 10px;
  font-family:'Spline Sans Mono',ui-monospace,monospace;
  font-size:13px;letter-spacing:.12em;text-transform:uppercase;

  /* THE JITTER IS THE HEADER'S ALONE. His earlier ruling stands: the NOTICE
     does not jitter, because a flickering announcement reads as a fault light.
     A struck label that trembles reads as a workshop, which is what it says.
     The motion arrives through the `tk-jitter` CLASS in the markup below --
     declared once in ticker.css and worn by this header and by the archive
     page's h1, so the two cannot tremble differently. */
  transition:font-size .3s ease,color .3s ease;
}
/* THE LIST SCROLLS, AND THAT IS WHAT DECIDES HOW MANY ARE VISIBLE (his answer
   2026-08-19: "a scroll bar would fix the issue of how many are visible").
   Everything ABOUT a row -- its type, its label, its version, its line, the date
   rule that leads it -- is in ticker.css, shared with the archive page. What is
   here is the notice's own budget: a height, and a scrollbar inside it.

   THE SCROLLBAR IS THE PANEL'S, not the operating system's. A default light
   scrollbar on a #111417 panel is a bright rectangle down one edge of the one
   element that is supposed to read as a lit tube. */
.ticker .tk-log{
  overflow-y:auto;overscroll-behavior:contain;
  scrollbar-width:thin;scrollbar-color:var(--panel-dim) transparent;
  /* TWO BUDGETS, BOTH MEASURED, AND THE SMALL ONE IS THE RESTING STATE (his
     first correction of 2026-08-19: "the window grew. Too much").

     v3.297.0 gave the list every pixel down to the last rail link, which is how
     a notice that had been four entries tall became ten on a tall screen. It
     is not the scrolling that was wrong -- that answered "how many are
     visible" and stays -- it is that the resting size became a property of the
     visitor's monitor.

     So: at rest it is FOUR ROWS, which is the height the design had at v3.294.0
     and the one size in this widget's history he did not object to; pointed at,
     it grows to the space that is actually there, because a visitor looking
     straight at it has asked for more. Both numbers are set in fitToSpace()
     from a MEASURED row height and the MEASURED distance to the furniture --
     neither is a pixel constant here.

     A NOTICE SHOWING FOUR OF TWELVE ENTRIES IS NOT SHOWING LESS: the rest are
     one scroll away, the fade says they are there, and the full log has a page
     of its own. */
  max-height:var(--tk-rest,none);
  transition:max-height .3s ease;
}
.ticker:hover .tk-log,
.ticker:focus-within .tk-log{ max-height:var(--tk-open,none) }
.ticker .tk-log::-webkit-scrollbar{width:6px}
.ticker .tk-log::-webkit-scrollbar-track{background:transparent}
.ticker .tk-log::-webkit-scrollbar-thumb{background:var(--panel-dim);border-radius:3px}
.ticker .tk-log::-webkit-scrollbar-thumb:hover{background:var(--panel-ink)}
/* AND THE FADE THAT SAYS THERE IS MORE. A clipped sentence with no affordance
   reads as a layout fault, not as a scroll region -- which is exactly how it
   looked in the first screenshot of this design. The deck already answers this
   with a `data-more` attribute set from measurement; same idea, same name, set
   in fitToSpace() and updated on scroll so the fade GOES when the last row is
   reached. A permanent fade at the bottom of a fully-scrolled list is the same
   lie in the other direction.
   The scrollbar is the other half of the answer and cannot be screenshotted
   here: the probe's Chrome runs with --hide-scrollbars. This half can be, which
   is a reason to have it rather than an excuse for it. */
.ticker .tk-log[data-more]{
  -webkit-mask-image:linear-gradient(to bottom,#000 calc(100% - 28px),transparent);
          mask-image:linear-gradient(to bottom,#000 calc(100% - 28px),transparent);
}

/* THE WAY OUT OF THE WINDOW. The notice shows a week; the archive shows
   everything. Without this link the rest of the log is unreachable from the one
   place a visitor is looking at it. */
.tk-more{
  margin:10px 0 0;font-family:'Spline Sans Mono',ui-monospace,monospace;
  font-size:10.5px;letter-spacing:.06em;transition:font-size .3s ease;
}
.tk-more a{color:var(--panel-dim);text-decoration:none;border-bottom:1px solid transparent}
.tk-more a:hover,.tk-more a:focus-visible{color:var(--panel-ink);border-bottom-color:var(--panel-ink)}

/* ---- POINTED AT: bigger fonts, dates, bold (his three asks, verbatim) -------
 * :focus-within is not decoration and not an afterthought -- without it the
 * dates are reachable by mouse and by nothing else, and the close button inside
 * the notice is already in the tab order, so a keyboard visitor lands here
 * naturally and must be shown the same thing.
 *
 * The expansion overlays the content column, and that is the intended
 * difference from the resting state: at rest the notice must cover nothing,
 * because nobody asked for it; expanded, the visitor is pointing straight at it
 * and a flyout that refuses to grow is just a smaller notice. It is transient,
 * it is reversible by moving the mouse, and it never moves the page under it --
 * position:fixed sees to that in both states. */
.ticker:hover,
.ticker:focus-within{
  width:min(340px,92vw);
  padding:16px 20px 17px 18px;
  box-shadow:0 0 12px 0 var(--led),
             0 0 34px -6px var(--led),
             inset 0 0 16px -6px var(--led),
             0 14px 40px rgba(0,0,0,.34);
}
/* The header grows with everything else and KEEPS its tube colour -- the old
   rule swapped it to the panel's ink on hover, which would now put out the one
   light he asked for at the moment the visitor is looking straight at it. */
.ticker:hover .ticker-h,
.ticker:focus-within .ticker-h{ font-size:16px }
.ticker:hover .tk-log li,
.ticker:focus-within .tk-log li{ font-size:15px }
.ticker:hover .tk-kind,
.ticker:focus-within .tk-kind{ font-size:11.5px }
.ticker:hover .tk-ver,
.ticker:focus-within .tk-ver{ font-size:11.5px }
/* BOLD, AND NOTHING ELSE. 640 rather than 700: this is light text on a dark
   panel, which already reads heavier than it measures, and 700 at 15px turns
   the line into a shout.
   IT USED TO REPAINT THE LINE WHITE as well, which was harmless while the line
   was the panel's neutral ink. Since v3.298.0 the LINE is what carries the
   change colour (his ask: "the colour should be applied to the text not
   titles"), so a white override would put out every row's light at the exact
   moment the visitor is pointing at it -- the same defect the header's hover
   rule was fixed for one arc earlier. */
.ticker:hover .tk-line,
.ticker:focus-within .tk-line{ font-weight:640 }
.ticker:hover .tk-date,
.ticker:focus-within .tk-date{ font-size:11px }
.ticker:hover .tk-more,
.ticker:focus-within .tk-more{ font-size:11.5px }

/* IT SITS IN A STRIP ABOVE THE HEADER, and getting there took three attempts and
   two screenshots (v3.298.0). The moment the header became a lit box of its own,
   both wanted the top-right corner. Insetting the box away from the button
   wrapped "IN THE WORKSHOP" onto two lines at 210px. Putting the button INSIDE
   the box looked right at 300px and was measured overlapping the last letter by
   9px at 210px -- and the German title is longer still. There is simply no room
   on that line at this width, so the button gets a line of its own.
   BOTH DEFECTS WERE FOUND BY LOOKING, and the second only after a measurement
   was taken to settle what the picture was showing: "two things overlap" is not
   a question a DOM query knows how to ask. Fourth and fifth on this surface. */
.ticker-x{
  position:absolute;top:4px;right:6px;
  background:none;border:0;padding:2px 6px;cursor:pointer;
  color:var(--panel-dim);font-size:15px;line-height:1;
}
.ticker-x:hover{color:var(--panel-ink)}
.ticker-x:focus-visible{outline:2px solid var(--led);outline-offset:2px;border-radius:4px}
/* Below 901px the rail is gone and the screen is small enough that a permanent
   panel is in the way, so the notice sits flush at the very bottom and gives up
   its rounded right corner rather than shrinking the text. The 256px ceiling
   above does not exist here -- there is no rail and no fixed content column --
   so the width is a share of the viewport instead. */
@media(max-width:900px){
  /* `top:auto` before `bottom:0`, or the box would have BOTH edges pinned and
     stretch to the full height of the viewport instead of moving to its foot. */
  .ticker{top:auto;bottom:0;width:min(300px,88vw);border-radius:0 12px 0 0}
  .ticker:hover,.ticker:focus-within{width:min(340px,94vw)}
  .ticker .tk-log li{font-size:13.5px}
}
/* ON A PHONE THERE IS NO HONEST PLACE FOR IT, SO IT DOES NOT APPEAR (his ruling
   2026-08-19, option 2 of three). Measured on the LIVE site before the ruling:
   the rule above intends a share of the viewport, but `min(300px,88vw)` resolves
   to the 300px arm on every phone -- 88vw is 343px at 390 -- so the notice was a
   FIXED 300px, i.e. 77% of a 390px screen and 83% of a 360px one, and hit-testing
   the area underneath showed it sitting on the code panel at 390 and 430 and on
   the opening paragraph at 360. The hero is the one thing a first-time mobile
   visitor is there to read.
   WHY SUPPRESS RATHER THAN SHRINK: below the rail's breakpoint there is no empty
   column, so any permanent panel is over content by construction -- shrinking it
   only makes the thing it covers smaller. Someone on a phone is skimming the
   pitch, not following the build; the ticker's audience is at a desk.
   CSS ONLY, AND THE FETCH IS LEFT ALONE ON PURPOSE. Skipping the request would
   put the same decision in two places that can drift apart (rule 33), and it
   would strand a desktop window that starts narrow and is widened. The payload
   is 806 bytes.
   The band 701-900px keeps the notice as the footnote that arm 4b governs. */
@media(max-width:700px){
  .ticker{display:none}
}

/* THE TICKER OBEYS STILLNESS TOO, and it obeys it the same way the deck does:
   the flag is the deck's, read here rather than duplicated. With motion off the
   notice does not slide -- it is simply THERE. Removing the transition and not
   the notice is the point; stillness is about movement, not about content.
   ANIMATION AS WELL AS TRANSITION, since v3.296.0: the header jitters, and a
   jitter is an animation, so a rule that only killed transitions would have let
   the one genuinely moving thing on this notice walk straight through the
   stillness toggle. The word is still there and still lit -- it just stops
   trembling, which is the same answer the slide gets. */
html.still .ticker,
html.still .ticker *{transition:none;animation:none}

/* MOTION OFF -- this page's own furniture. The deck's three rules moved to
   deck.css with the deck, so they are off on the docs door too; what is left
   here is the section swap and the rail tubes, which exist only on this page. */
@media (prefers-reduced-motion:reduce){
  /* The section swap is INSTANT under reduced motion -- no rise, no fade. The
     click still changes the section; it just does not move to get there. */
  html.js main > section.current{ animation:none }
  /* NO FLICKERING TUBES. Erratic flicker is the single most provocative thing on
     this page for a photosensitive or vestibular visitor, so under reduced
     motion every tube simply BURNS: unselected ones dim and steady, the selected
     one stays lit. Nothing is lost -- the colour coding and the page's bounding
     box, which carry the actual meaning, are untouched. */
  /* The ticker appears without travelling. Same answer as html.still above;
     stated in both places because one is the OS's answer and one is his. */
  /* EVERY transition on the notice, not only the slide: the hover
     expansion grows the box and its type, and a visitor who asked for no
     motion did not ask for a smaller amount of it. The bigger fonts, the
     bold and the dates still ARRIVE on hover -- they just arrive at once,
     which is the same answer stillness gives the slide. */
  /* ANIMATION TOO -- the jittering header. Sub-pixel or not, an involuntary
     repeating movement is exactly what a vestibular or photosensitive visitor
     turned this setting on to be rid of, and it is the only thing on this
     notice that moves by itself. */
  .ticker,.ticker *{ transition:none;animation:none }
  .rail a::before{ animation:none;opacity:.42 }
  .rail a.active::before{ animation:none;opacity:1 }
}


/* ==========================================================================
   SITE V3 -- THE THREE DOORS (arc 3.1.013.0)

   Everything above this line is index.html's own inline <style>, lifted whole
   and unedited at the migration. Everything below is NEW: shapes the live page
   never had, because the page it describes is not the live page.

   Three of them, and each exists because a WORD grew a parameter rather than
   because a definition asked for a class -- a definition still cannot name one.
     .cx-tabs / .cx-tabbar   `tabs`, which had no styling because nothing used it
     .ask                    `list show: ask`
     .cx-app                 `app`, which needed a frame once it stopped being
                             one game at the bottom of the page
   ========================================================================== */

.cx-tabbar {
  display: flex;
  gap: .4rem;
  margin: 1.6rem 0 0;
  border-bottom: 1px solid var(--hairline);
  flex-wrap: wrap;
}
.cx-tabbar button {
  font: 600 .95rem/1 var(--body);
  letter-spacing: .08em;
  background: none;
  border: 0;
  border-bottom: 3px solid transparent;
  color: var(--ink-2);
  padding: .7rem 1.1rem;
  cursor: pointer;
  margin-bottom: -1px;
}
.cx-tabbar button:hover { color: var(--ink); }
.cx-tabbar button[aria-selected="true"] {
  color: var(--ink);
  border-bottom-color: var(--crimson);
}
.cx-tabs > div > [data-cxtab] { padding-top: 1.4rem; }

/* THE HOSTED PROGRAMS. A frame, a dark ground and a maximum width, because the
   canvas inside declares its own pixel size and a visitor's window does not. */
.cx-app { margin: 1.2rem 0; }
.cx-app iframe {
  display: block;
  width: 100%;
  max-width: 100%;
  border: 1px solid var(--hairline);
  border-radius: 10px;
  /* THE FRAME TAKES THE DOOR'S GROUND, NOT BLACK. A hosted program declares its
     own canvas size, so there is always a margin around it -- and a black margin
     was right while every centrepiece was dark. The invoice became a LIGHT sheet
     in round 4 (his "make it look more like excel"), and a light sheet in a
     black surround on a pale blue door reads as three unrelated rectangles.
     `transparent` lets the door behind it show, so the margin belongs to
     whichever door the program is standing in. */
  background: transparent;
}
.cx-appstart {
  font: 600 1rem/1 var(--body);
  background: var(--black);
  color: var(--paper);
  border: 0;
  border-radius: 8px;
  padding: .9rem 1.4rem;
  cursor: pointer;
}

/* THE ASK BOARD. A question is a button that spans the row; its answer opens
   under it and arrives at the rate the definition asked for. */
.ask { margin: 1.8rem 0 0; border-top: 1px solid var(--hairline); }
.ask-row { border-bottom: 1px solid var(--hairline); }
.ask-q {
  display: block;
  width: 100%;
  text-align: left;
  font: 500 1.02rem/1.45 var(--body);
  color: var(--ink);
  background: none;
  border: 0;
  padding: .85rem 0;
  cursor: pointer;
}
.ask-q::before {
  content: "+";
  display: inline-block;
  width: 1.4em;
  color: var(--crimson);
  font-weight: 700;
}
.ask-row.open > .ask-q::before { content: "\2212"; }      /* a minus, not a hyphen */
.ask-a {
  font: 400 1rem/1.62 var(--body);
  color: var(--ink-2);
  padding: 0 0 0 1.4em;
  max-width: 62ch;
  white-space: pre-line;                                   /* the answers are paragraphs */
}
.ask-row.open > .ask-a { padding-bottom: 1.1rem; }

/* A CARET WHILE IT IS STILL ARRIVING, so a half-written answer reads as one
   being written rather than as one that stopped. */
.ask-a.streaming::after {
  content: "";
  display: inline-block;
  width: .5em;
  height: 1em;
  margin-left: .1em;
  vertical-align: -.15em;
  background: var(--crimson);
  animation: cx-caret 1s steps(2) infinite;
}
@keyframes cx-caret { 50% { opacity: 0; } }
html.still .ask-a.streaming::after { display: none; }

/* THE PINNED ROW is his skeptic Q&A, and on the hero it is the name decoder.
   It is open, it does not answer to a click, and it carries weight. */
.ask-row.pinned { border-bottom: 0; }
.ask-row.pinned > .ask-q {
  cursor: default;
  font-weight: 700;
  font-size: 1.12rem;
  color: var(--ink);
}
.ask-row.pinned > .ask-q::before { content: "\201C"; }     /* an opening quote */
.ask-row.pinned > .ask-a { padding-bottom: 1.4rem; }

/* ==========================================================================
   ROUND 4 (arc 3.1.015.0) -- HIS EYEBALL ON THE THREE DOORS
   ========================================================================== */

/* ---- ONE RESTORATION, NOT A NEW SHAPE ------------------------------------
   Every h3 the live page owns sits inside a `.face` and is styled by it. A
   `text as: sub` standing in a door's prose matched nothing and rendered in the
   body SERIF at 22.23px, beside the site's own 22px Bricolage. Nothing that
   READ either stylesheet could have found that; it took getComputedStyle on
   both pages at once. */
.subhead {
  font-family: 'Bricolage Grotesque', sans-serif;
  font-size: 22px;
  letter-spacing: -0.02em;
  line-height: 1.15;
  margin: 2.2rem 0 .4rem;
}

/* ---- 1b. THE MASTHEAD: ONE LINE, FIVE LANGUAGES -------------------------
   His ruling of 2026-08-24: *"The German wrapped to two lines. One line, all
   five languages -- smaller type, tighter tracking, whatever it takes; never a
   wrap."*

   IT WORE `title` AND THAT WAS THE BUG. The site's h1 is capped at 19ch, which
   is exactly right for the four words the live page's headline carries and
   wrong by half for a masthead of fifty -- so it was going to wrap at any size
   worth reading, and the German simply got there first.

   THE SIZE IS DERIVED, NOT CHOSEN, and both numbers behind it were measured in
   a browser rather than guessed. `cqw` is a percentage of the column the line is
   given, so the container answers "how much room"; the divisor is the LONGEST of
   the five translations rendered in this face -- French, at 23.04 times its own
   font size -- so the coefficient is 1/23.04 = 4.34%, taken to 4.2 for headroom.
   Tightening the tracking below the h1's own buys a little more of it. Change
   the copy in any language and the arm that measures this goes red before a
   visitor sees a second line; change the column and nothing needs touching at
   all, which is what the container unit is for.

   THE CEILING IS THE h1's OWN 40px, because a masthead that grows past the
   headline it introduces is a different page.

   AND THE SIZE LIVES IN THE TOKEN, NOT HERE. The definition gives this line a
   `style: size` out of site.tokens, which lands as an INLINE style and beats
   anything written in this file -- so the rule below is the fallback for a
   masthead nobody sized, and `h1-line` is the number that actually applies. The
   first cut of this had the expression here and a viewport clamp in the token,
   and the two disagreed exactly where it mattered: at 1400px both said 40px and
   the page looked right, while at 390px the token said 26px, the class was
   ignored, and 26px of a line that may never wrap made the whole document 595px
   wide inside a 390px window. Two places holding one number is the shape of that
   bug; there is one place now. */
.hero > .wrap { container-type: inline-size; }
.masthead {
  max-width: none;
  white-space: nowrap;
  letter-spacing: -0.03em;
  font-size: min(40px, 4.2cqw);
  margin: 0 0 .1rem;
  /* AND IT IS A TITLE, SO IT TAKES THE TITLE COLOUR (his ask, 2026-09-02:
     *"the CX+AI -- One language. For you & for your AI. remains black; it
     should be like the others"*). Same `--title-ink` the section titles read,
     so a hero that ever sits on a ground of its own recolours with the panel
     rather than needing a second rule. It was the only heading on the page
     still in `--ink`, which is exactly the "one of these is not like the
     others" he keeps catching. */
  color: var(--title-ink, var(--title-deep));
}

/* ---- 2. THE NAME DECODER: HIS ARTWORK, ARRIVING -------------------------
   His verdict on round 4's typographic version, verbatim: "We are degrading in
   quality now said sliding bars. This is static. I need to make it myself." He
   then made it -- DOCS/Resources/cxailines.png, five 3D bars on a black field --
   and `forge assets` cuts it into five pictures along its own quiet rows.

   THE STAGE IS GONE (round 6). It used to be a black slab, because his render's
   field is black and JPEG has no transparency to key it out with. His ruling of
   2026-08-24 retired the premise rather than the slab: *"The image needs to be
   dissected into 5 horizontal strips which fit the page (white/blue/black)"* --
   three door colours, one picture, and a black rectangle fits exactly one of
   them. So `forge assets` now derives an ALPHA CHANNEL from the picture's own
   brightness and the strips sit on whatever ground the door is wearing. No
   square, no frame, no border-radius, no background of any kind here.

   IT STILL CLIPS. `overflow: hidden` is not decoration left over from the slab:
   it is what makes a bar arrive from OFF-STAGE rather than slide across the
   page and push a horizontal scrollbar out of the body. A clip with no paint is
   invisible until something moves through it.

   THE BARS TILE IT. Each picture is the full width of the render and the five
   heights sum to its height, so at rest the five <img> elements laid flush
   reproduce his file pixel for pixel. `display:block` is load-bearing: an inline
   image sits on a text baseline and would open a four-pixel gap under every
   bar -- five hairlines of daylight across his artwork. */
.cx-decoder {
  overflow: hidden;
  margin: 1.25rem 0 .75rem;
  /* THE SIZE IS A TOKEN, WHICH MEANS IT IS HIS. Round 5 shipped 780px as base's
     taste call and the close-out said so; his answer was "much smaller. much
     smaller", twice over. It is `--decoder-width` in site.tokens.json now, so
     the next ruling is a data change and not a stylesheet edit -- and the
     picture's own resolution (FA_LINES_OUT in tests/forge.cx) is a separate
     number, because how big it looks and how many pixels draw it are two
     different questions. */
  width: min(100%, var(--decoder-width, 380px));
  line-height: 0;

  /* TWO COLUMNS, WHICH IS WHERE THE HEIGHT GOES (his ruling 2026-08-24: "we
     gain about half the height"). CX runs down the left, the connector and AI
     down the right. The saving is not one row: each column is half the width,
     so every strip draws at half the scale as well -- five stacked full-width
     bars against two half-width columns. The measured numbers are in the
     round-7 arm, which reads the rendered block rather than trusting this
     comment.

     `align-items: start` matters: without it the shorter column stretches and
     his strips would be scaled to fill it. */
  display: flex;
  align-items: start;
  gap: var(--decoder-gap, .5rem);

}

/* THE ROOM A NUDGE NEEDS COMES OUT OF THE COLUMNS, and every percentage on
   these three lines resolves against the SAME box -- the flex container's own
   content width, which is what a flex-basis percentage and a flex item's margin
   are both measured in. That is the whole reason the reach is not a padding on
   the block: a percentage PADDING resolves against the parent instead, and the
   parent here is the hero column, so asking for 2.5% of the lockup reserved
   3.3% of something else and quietly took six pixels off every mark. The
   renderer sets the two custom properties, and it sets neither on a side that
   nothing leaves. */
.dec-col {
  flex: 0 0 calc((100% - var(--decoder-gap, .5rem)
                  - var(--decoder-reach-left, 0%)
                  - var(--decoder-reach-right, 0%)) / 2);
  min-width: 0;
}
.dec-col:first-child { margin-left: var(--decoder-reach-left, 0); }

/* "TO THE RIGHT AND SLIGHTLY BELOW" -- his words, and the drop is a FRACTION of
   the column rather than a pixel count, so the lockup holds its proportions at
   every width instead of sitting right on a monitor and wrong on a phone. */
.dec-col:last-child { margin-top: var(--decoder-drop, 9%); }
/* HIS THREE NUDGES LAND HERE, one custom property per bar, set from the row's
   own `nudge`. `left` on a relatively positioned block moves the PAINT and not
   the layout, so a mark can be moved without the column it sits in changing
   width -- which is what keeps every other mark exactly where it was. */
.dec-bar { display: block; position: relative; left: var(--nudge, 0); }
.dec-art {
  display: block;
  width: 100%;
  height: auto;
  user-select: none;
}

/* THE ARRIVAL, WHICH IS THE HALF HE ASKED FOR TWICE. "we should make the line
   arrive horizontally from left, right top right, left everytime tab changes or
   displayed first time" -- four directions cycling, one per bar, restarted by
   the renderer whenever a door is opened.

   THE RESTING STATE IS THE DEFAULT AND THE ANIMATION IS THE EXCEPTION. Written
   the other way round -- bars hidden until `.arrive` lands -- a visitor who
   asked for stillness would get an empty black slab, and so would anyone whose
   browser declined the animation.

   THE TRAVEL IS A PERCENTAGE OF THE BAR, not a pixel count, so a bar comes fully
   off-stage at every width instead of half-entering on a phone and overshooting
   on a monitor. */
@keyframes decIn0 { from { opacity: .15; transform: translateX(-108%) } to { opacity: 1; transform: none } }
@keyframes decIn1 { from { opacity: .15; transform: translateX( 108%) } to { opacity: 1; transform: none } }
@keyframes decIn2 { from { opacity: .15; transform: translate( 108%, -60%) } to { opacity: 1; transform: none } }
/* KEYED ON A CLASS, NOT ON THE STYLE ATTRIBUTE. Round 4 wrote these as
   `[style*="--enter:1"]`, and Chrome serialises a custom property WITH A SPACE
   after the colon -- so the selector matched nothing and all five lines arrived
   from the left for a whole round. */
.dec-bar.arrive { animation: decIn0 var(--decoder-in, 1.15s) cubic-bezier(.2,.85,.3,1) both;
                  animation-delay: calc(var(--step, 0) * var(--decoder-stagger, 190ms)); }
.dec-bar.dec-from-right.arrive    { animation-name: decIn1; }
.dec-bar.dec-from-topright.arrive { animation-name: decIn2; }
@media (prefers-reduced-motion: reduce) { .dec-bar.arrive { animation: none; } }
html.still .dec-bar.arrive { animation: none; }

/* HIS SIGNATURE, AND THE INSTRUCTION WAS "a quiet close, not a flourish". So:
   the display face, because a name is not prose; a size UNDER the body text
   rather than over it; the muted ink; and space above it, which is what makes a
   line read as a close rather than as another sentence. No italic, no rule, no
   flourish -- the restraint IS the styling. */
.cx-sign {
  font-family: var(--display), system-ui, sans-serif;
  font-size: .95rem;
  letter-spacing: .04em;
  color: var(--ink-dim, #6b6b6b);
  margin-top: 1.6rem;
}

/* ---- THE SOURCE TAB -------------------------------------------------------
   A listing is READ, so it gets a reading column: the page's mono face, a line
   height that is not a code editor's, and a scroll of its own rather than a
   section that runs for six hundred lines. The colours are the three a reader
   actually uses -- what is a comment, what is a string, what is a number -- and
   nothing else is coloured, because a listing painted in eight hues is a
   decoration of source rather than source. */
.cx-source {
  border: 1px solid var(--rule, #d8d2c4);
  border-radius: 6px;
  background: var(--paper-2, #faf7f0);
  margin: 1rem 0;
  overflow: hidden;
}
.cx-source .src-head {
  padding: .55rem .9rem;
  border-bottom: 1px solid var(--rule, #d8d2c4);
  font-family: var(--display), system-ui, sans-serif;
  font-size: .9rem;
  letter-spacing: .02em;
}
.cx-source .src-body {
  margin: 0;
  padding: .9rem 1rem;
  max-height: 58vh;
  overflow: auto;
  font-family: var(--mono), ui-monospace, monospace;
  font-size: .78rem;
  line-height: 1.55;
  white-space: pre;
  tab-size: 3;
}
.cx-source .s-c { color: #6f8f6f; }
.cx-source .s-s { color: #9a5b2c; }
.cx-source .s-p { color: #7a6bb0; }
.cx-source .s-n { color: #38618c; }
/* HIS WISH NAMED FIVE THINGS AND THE SHEET PAINTED THREE AND A HALF: keywords
   had weight but no colour, and integers and floats shared one. Floats take a
   related-but-separate hue rather than a louder one -- they are a NEIGHBOUR of
   the integer, not a warning. The suffix gets its own because `.i`/`.f` is the
   single most CX-specific mark on the page and the thing a C reader asks about
   first. All four sit in the same muted, printed register as the three above;
   a listing is for reading, not for a light show. */
.cx-source .s-k { color: #9b2c4a; font-weight: 600; }
.cx-source .s-f { color: #2f7d6f; }
.cx-source .s-t { color: #8a6d3b; }
/* A JSON KEY IS STRUCTURE; A STRING IS DATA. 09 / source shows the site's own
   definitions through this colourer, and with both painted alike it read as a
   wall of one hue -- which is what "apply it to 09 / source too" was pointing
   at. Keys take a calm slate so the SHAPE of a definition is what the eye gets
   first and the values stay the warm colour they already had. Deliberately not
   a sixth bright hue: five is already the most a listing can carry before it
   stops being read. */
.cx-source .s-y { color: #55606a; }

/* EXPAND/COLLAPSE FOR {} SECTIONS -- the other half of the same commission.
   Every line is a block so one can be hidden; the control sits in a gutter the
   NON-folding lines also reserve, because a marker that appears and disappears
   would shift the code sideways as blocks fold. */
/* EACH LINE IS ITS OWN BLOCK and they are joined with NO newline between them:
   inside a <pre> a literal newline plus a block element breaks the line TWICE,
   which the first preview showed as a listing at double spacing. A blank source
   line then has no content at all, so it is given a height rather than
   collapsing to nothing. */
.cx-source .src-line { display: block; position: relative; padding-left: 1.15em;
  min-height: 1.55em; }
.cx-source .src-line[hidden] { display: none; }
.cx-source .src-fold {
  position: absolute; left: 0; top: 0;
  width: 1em; height: 1.55em; line-height: 1.45em;
  padding: 0; border: 0; background: none;
  font: inherit; color: var(--ink-3, #9a9384);
  cursor: pointer; text-align: center; opacity: .55;
}
.cx-source .src-fold:hover { opacity: 1; color: var(--accent, #9b2c4a); }
.cx-source .src-fold:focus-visible { outline: 1px solid var(--accent, #9b2c4a); opacity: 1; }

/* ---- 3. REAL TABS: FOLDER TRAPEZOIDS, FULL WIDTH, THREE GROUNDS ---------
   His ruling: "tabs have to be real tabs and cover the full segment and each
   page a different colour. Tabs should be folder trapezoid segments per colour
   White/Blue/Black".

   THE TRAPEZOID IS DRAWN BEHIND THE BUTTON, NEVER ON IT. clip-path on the
   button itself would clip the CLICK TARGET and slice the focus ring, so the
   shape is a ::before that fills the button's box and takes the clip; the
   button keeps its full rectangle and its outline. A browser with no clip-path
   gets a rectangle, which is a tab. */
.cx-tabs.folder > .cx-tabbar {
  display: flex;
  gap: 0;
  width: 100%;
  margin: 2rem 0 0;
  border-bottom: 0;
  flex-wrap: nowrap;
}
.cx-tabs.folder > .cx-tabbar button {
  position: relative;
  flex: 1 1 0;                     /* "cover the full segment" */
  background: none;
  border: 0;
  border-bottom: 0;
  margin: 0;
  padding: .95rem 1rem .8rem;
  font: 700 1rem/1 var(--body);
  letter-spacing: .1em;
  /* A TAB IS ITS COLOUR, AND CLICKING IT DOES NOT CHANGE THAT (his ruling
     2026-08-24: "tabs should not change colour after click").

     Round 4 dimmed an unselected label toward its own ground and desaturated its
     trapezoid, so choosing a door repainted both the tab and its neighbours --
     which is exactly the thing a real folder tab never does. The selection is
     now carried entirely by GEOMETRY: the chosen tab stands taller and joins the
     page below it, and every tab keeps the ink and the ground it had before
     anybody touched it.

     WHAT THAT COSTS, SAID OUT LOUD: the only cue is height and the seam. That is
     enough here because the doors are three strongly different colours and the
     page underneath changes with them, and `aria-selected` carries the state for
     anyone not reading pixels at all. */
  color: var(--door-ink, var(--ink));
  cursor: pointer;
  z-index: 1;
}
.cx-tabs.folder > .cx-tabbar button::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--door, var(--paper-deep));
  border-top: 1px solid var(--hairline);
  clip-path: polygon(14px 0, calc(100% - 14px) 0, 100% 100%, 0 100%);
  /* GEOMETRY, NOT COLOUR -- so the transition is on `inset` and there is no
     filter for a click to turn off. */
  transition: inset .18s ease;
}
/* A tab lifts a little under the pointer: the same language the selection
   speaks, one third as loud, and still not a repaint. */
.cx-tabs.folder > .cx-tabbar button:hover::before { inset: -3px 0 0 0; }
.cx-tabs.folder > .cx-tabbar button[aria-selected="true"]::before {
  /* The chosen tab stands taller AND runs one pixel into the page below it, so
     the two read as one sheet of paper rather than as a tab above a panel. */
  inset: -7px 0 -1px 0;
}
.cx-tabs.folder > .cx-tabbar button[aria-selected="true"] { z-index: 2; }
.cx-tabs.folder > .cx-tabbar button:focus-visible {
  outline: 2px solid var(--crimson);
  outline-offset: -3px;
}
/* THE PAGE BEHIND THE TAB WEARS THE TAB'S COLOUR -- the half of the ruling that
   is not decoration: it is how a visitor knows which door they are standing in
   without reading the label. */
.cx-tabs.folder > div > .cx-door {
  background: var(--door, var(--paper));
  color: var(--door-ink, var(--ink));
  padding: 1.8rem 1.6rem 2rem;
  border-radius: 0 0 10px 10px;
}
.cx-tabs.folder > div > .cx-door .lede,
.cx-tabs.folder > div > .cx-door p,
.cx-tabs.folder > div > .cx-door h3 { color: inherit; }
.cx-tabs.folder > div > .cx-door .subhead { color: inherit; }

/* ---- 5. THE YELLOW PAD, WRITTEN ON THE LINES -----------------------------
   "it should be a box - preferably a yellow papers with faint red lines withe
   the Q and A in it as a title. Then the demo." (2026-08-24) and then, on the
   staged result the same day: "The text in the yellow pad should be on top of
   the line."

   ONE CONSTANT, USED TWICE -- which is his ruling ("derive one from the other,
   never two constants") taken literally. `--pad-line` is the pad's line unit: it
   is the line-height of every line in the pad AND the period of the ruling
   gradient, so the words and the rules cannot drift apart at any zoom or in any
   language, because there is nothing for them to drift relative to.

   AND THE RULE SITS AT THE BASELINE, WHICH IS WHERE THE SECOND NUMBER COMES IN.
   "On top of the line" means the letters rest ON it, like handwriting; a rule at
   the bottom of a line box sits below the descenders and reads as a gap. The
   baseline's position inside a line box is (line - font)/2 + ascent, and the
   ascent is a property of the TYPEFACE rather than of this design -- so
   `--pad-ascent` is MEASURED from the shipped font (0.76em; the probe re-derives
   it from the rendered page at two zooms and fails if the font ever changes
   under it) and everything else is arithmetic.

   ON WHICH SIDE OF THE BASELINE, THOUGH -- and round 5 got that half wrong.
   His verdict on it: *"text should still be higher on the yellow and
   consistent."* A one-pixel rule has to sit on one side of a zero-width line,
   and the gradient put it on the LAST PIXEL ABOVE, which is inside the glyphs'
   own overshoot -- so it read as a strike through the feet of every word rather
   than as paper the words are standing on. It is the first pixel BELOW now. Same
   derivation, same single constant, one term moved: `--pad-base` is still the
   baseline and the rule is still nothing but that number.

   THE PRICE, SAID OUT LOUD: every line in the pad is the same SIZE **AND THE
   SAME FACE**. Both fall out of the same arithmetic, and round 5 only paid half
   of it. The baseline offset is (line - size)/2 + ascent x size: two SIZES
   sharing one rule period land 0.26 x the difference apart, and two FACES land
   (ascent_a - ascent_b) x size apart whatever the size is -- measured here at a
   round pixel between the body serif and Bricolage. So the pad's title was a
   pixel low against its own ruling for a reason no size could fix, and it is the
   pad's face now, distinguished by weight and tracking alone. A legal pad is one
   hand; that is what makes it a legal pad. */
.pad {
  background: var(--pad-paper);
  color: var(--pad-ink);
  border: 1px solid var(--pad-edge);
  border-radius: 4px;
  box-shadow: 0 6px 18px -10px rgba(0,0,0,.45);
  margin: 0 0 1.6rem;
  padding: 0;
  overflow: hidden;
}
.pad > .wrap {
  /* The baseline's offset inside one line box -- derived, never typed. */
  --pad-base: calc((var(--pad-line) - 1em) / 2 + var(--pad-ascent));
  font-size: 1rem;
  padding: var(--pad-line) 1.5rem var(--pad-line);
  /* content-box origin, so the first period starts exactly at the first line
     box's top edge -- with no second number saying where the ruling begins. */
  background-origin: content-box;
  background-image:
    repeating-linear-gradient(to bottom,
      transparent 0,
      transparent calc(var(--pad-base) + var(--pad-clear)),
      var(--pad-rule) calc(var(--pad-base) + var(--pad-clear)),
      var(--pad-rule) calc(var(--pad-base) + var(--pad-clear) + 1px),
      transparent calc(var(--pad-base) + var(--pad-clear) + 1px),
      transparent var(--pad-line));
  background-position: 0 0;
  max-width: none;
}
/* HIS ROUND-7 RULING, AND IT REVERSES HALF OF ROUND 6's. "2-3px more clearance
   between the text and the rule line, and a different font between question and
   answer." The comment above this block argued the opposite about the face and
   it argued it correctly: two faces sharing one ruling land
   (ascent_a - ascent_b) x size apart, which is why round 6 made the pad one
   hand. That is an argument about a CONSEQUENCE, not about the ruling -- and the
   consequence has a fix.

   THE FIX IS TO MEASURE THE SECOND FACE TOO. `--pad-ascent` was already a
   measurement rather than a taste (where the baseline sits inside a line box for
   the shipped typeface); the display face has its own, and the question is
   nudged by exactly the difference. `position: relative` moves the GLYPHS and
   not the LAYOUT, so the line box is untouched and every line below it is still
   on the grid -- which is the property the round-6 comment was protecting.

   AND THE CLEARANCE IS ONE CONSTANT, added to the ruling's offset rather than to
   any line's margin: the period is unchanged, so every rule in the pad moves the
   same 3px away from the words above it and nothing comes off the grid.

   EVERY LINE IN THE PAD IS ON THE GRID, so nothing here carries a margin that
   is not a whole number of line units -- a half-unit anywhere below the top
   would take every line after it off the ruling. */
.pad h2,
.pad p {
  font-size: 1rem;
  line-height: var(--pad-line);
  margin: 0;
  max-width: none;
  color: var(--pad-ink);
}
.pad .eyebrow { line-height: var(--pad-line); margin: 0; max-width: none; }
/* THE EYEBROW TAKES THE PAD'S SIZE, AND ROUND 5 HAD THIS BACKWARDS. It kept the
   site's 12.5px and wrote down the cost -- 0.26 x the size difference, 0.9px of
   grid error -- as "under the tolerance". His answer was one word: CONSISTENT.
   A line that sits nine tenths of a pixel off the ruling while the two below it
   sit on it is exactly the thing an eye finds and a tolerance forgives.

   IT WAS KEPT AT 12.5 TO SATISFY AN INSTRUMENT, AND THE INSTRUMENT WAS AIMED
   WRONG. The computed-style diff's eyebrow landmark was `main .eyebrow`, which
   on the live page finds a SECTION eyebrow and on this one finds the PAD's --
   two different pieces of furniture compared as though they were one, which is
   why "the pad eyebrow changed size" arrived reading as "the site's eyebrows
   changed size". The landmark is a section eyebrow on both pages now
   (tests/web/lang_probe.js), and the site's own eyebrows are still held to the
   live page's exactly as before. */
.pad .eyebrow {
  font-size: 1rem;
  color: var(--crimson);
  opacity: .8;
}
/* THE EYEBROW IS THE PAD'S OWN HAND -- `inherit` rather than a named family, so
   the label above the question cannot come to disagree with the answer below
   it. */
.pad .eyebrow {
  font-family: inherit;
}
/* THE QUESTION IS THE OTHER HAND (his ruling 2026-08-24). The pad is a question
   and an answer, and he wanted to see which is which without reading them: the
   question takes the display face the site's headings wear, the answer keeps the
   reading face its prose wears.
   THE NUDGE IS THE PRICE, AND IT IS PAID EXACTLY. A face with a different ascent
   puts its baseline somewhere else inside the same line box; `top` moves the
   glyphs by precisely that difference and leaves the box where it was, so the
   question sits back on the ruling and nothing under it shifts by a pixel. Both
   ascents are measurements of the shipped fonts, re-derived from the rendered
   page by the probe -- if a typeface is ever swapped, the arm says so rather
   than the pad quietly coming off its lines. */
.pad h2 {
  font-family: var(--display);
  font-weight: 800;
  letter-spacing: -.005em;
  position: relative;
  /* Both tokens are already `em`, so their difference is a LENGTH and needs no
     multiplier. Written `* 1em` first, which is a length times a length: calc
     rejects it, `top` falls back to 0, and the nudge silently did nothing on a
     page where everything else looked right. The arm two files over is what
     said so. */
  top: calc(var(--pad-ascent) - var(--pad-ascent-display));
}

/* ---- A LIFTED DOCUMENT MAY NOT WIDEN THE PAGE ----------------------------
   FOUND WHILE LOOKING AT THE DOORS ON A PHONE, and it is NOT one of his eight:
   it has been true since `document` started lifting manuals into the front page.

   A manual's code samples are `white-space: pre` and some of them are 888px
   wide. Inside a 390px viewport that is not a wide code block -- it is a
   HORIZONTALLY SCROLLING FRONT PAGE, because one element that cannot shrink
   sets the width of the whole document, and the fixed band then sizes itself to
   the same 912px. Measured: V3 scrollWidth 912 at a 390 viewport, the live page
   390 (it has no lifted document to overflow).

   THE RULE IS THE ORDINARY ONE AND IT BELONGS TO THE CONTAINER, NOT THE PAGE:
   wide content scrolls inside its own box. `min-width: 0` is the half that is
   easy to forget -- a grid or flex child will not shrink below its content
   without it, so `overflow-x: auto` alone leaves the page exactly as wide. */
/* AND ITS OWN BOX IS NEUTRALISED. The container the panel now lifts carries
   the manual's standalone-page geometry -- `max-width:900px; margin:0 auto;
   padding:1.6rem 1.25rem 4rem` -- which is right for a document that IS the
   page and wrong for one sitting in a panel that already has a width and a
   pad. Only the box; every other `.wrap` rule in the lifted sheet is the
   manual's look and is exactly what we lifted it for. The docs door carries
   the same three declarations for the same reason (`.card .body .wrap`). */
.doc-body > .wrap { max-width: none; margin: 0; padding: 0; }
.doc-body { min-width: 0; }
.doc-body pre,
.doc-body table,
.doc-body .cx-scroll {
  max-width: 100%;
  overflow-x: auto;
}
.doc-body code { overflow-wrap: anywhere; }
.doc-body pre code { overflow-wrap: normal; }

/* ---- 4. PRICING, AND THE FIRST REAL TABLE ------------------------------
   `table show: rows` HAD NO STYLING AT ALL. Not a thin ruleset -- none: no
   `.cx-table` rule existed anywhere, so the word rendered as a browser-default
   table and had done since it was built. Nothing noticed because its only
   customer until now was a four-row placeholder whose every price said "contact
   us"; his pricing ruling of 2026-08-24 put real figures on it and the shape
   became the first thing anybody looked at.

   THE PRICE COLUMN IS THE ONE THAT MUST NOT WRAP. "Contact us" breaking across
   two lines beside "€15 / user / month" breaking across three is a price list
   that reads as damage. It is the LAST column in both tables here, which is a
   property of the data rather than of this stylesheet -- so it is addressed as
   :last-child and a table that grows a column after it keeps its own shape. */
.cx-table {
  width: 100%;
  border-collapse: collapse;
  margin: 1.6rem 0 1.2rem;
  font-size: .98rem;
  line-height: 1.5;
}
.cx-table th {
  text-align: left;
  vertical-align: bottom;
  font: 600 .78rem/1.3 var(--mono);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-2);
  padding: 0 1rem .5rem 0;
  border-bottom: 1px solid var(--ink);
}
.cx-table td {
  vertical-align: top;
  padding: .85rem 1rem .85rem 0;
  border-bottom: 1px solid var(--hairline);
  color: var(--ink-2);
}
.cx-table td:first-child {
  font-weight: 650;
  color: var(--ink);
  white-space: nowrap;
  padding-right: 1.6rem;
}
.cx-table th:last-child,
.cx-table td:last-child {
  text-align: right;
  padding-right: 0;
  white-space: nowrap;
  font-family: var(--mono);
  color: var(--ink);
}
.cx-table tr:last-child td { border-bottom: 0; }

@media (max-width: 720px) {
  /* A four-column price list does not fit a phone, and squeezing it produces
     three-word columns. Each row becomes a block with its own price on the
     right of its first line, which is how a price list reads on paper too. */
  .cx-table, .cx-table tbody, .cx-table tr, .cx-table td { display: block; width: 100%; }
  .cx-table thead { display: none; }
  .cx-table tr { border-bottom: 1px solid var(--hairline); padding: .9rem 0; }
  .cx-table td { border: 0; padding: .15rem 0; }
  .cx-table td:last-child { text-align: left; font-weight: 650; }
}

/* A FLOATING PANEL WITH NO ROOM IS NOT SHOWN (round 6). His ruling of
   2026-08-24 -- "the workshop should start at the bottom but never overlay the
   menu" -- has a case where both halves cannot be satisfied at once: a window
   short enough that the rail already reaches the foot. The renderer measures
   that case and says so with this class; `display: none` rather than a clipped
   sliver, because a notice reduced to its own border is not a smaller notice,
   it is a bar sitting on the menu. */
.cx-noroom { display: none !important; }

/* ---- 6a. THE BUSINESS CONTEXT --------------------------------------------
   His ruling of 2026-08-24: pricing "renders in the BLUE/business context". The
   section takes the SAME pair the BUSINESS door wears -- `--door` and
   `--door-ink`, set on the element by the renderer out of the two tokens -- so
   the blue on this page and the blue behind that tab cannot come to disagree.
   Everything below reads those two variables and nothing names a colour.

   IT ALSO CARRIES `cx-door`, WHICH IS THE POINT. The door rules already dress
   every chip, button and prompt bar against `--door`/`--door-ink`; wearing the
   class means a control added to a door tomorrow is dressed here too, by
   construction, and nobody has to remember this section exists. */
section.business {
  background: var(--door, var(--paper));
  color: var(--door-ink, var(--ink));
  /* And the section TITLE with it. The title treatment near the bottom of this
     file paints headings in `--title-deep` -- an aubergine chosen against the
     paper ground, and unreadable on this one. A panel that brings its own ground
     says so here, in the one block that already knows both of its colours. */
  --title-ink: var(--door-ink, var(--ink));
}
section.business .eyebrow { color: var(--door-ink, var(--ink)); opacity: .72; }
section.business h2,
section.business h3,
section.business p,
section.business .lede,
section.business li,
section.business td,
section.business th { color: inherit; }
section.business .cx-table th,
section.business .cx-table td { border-color: color-mix(in srgb, var(--door) 72%, var(--door-ink)); }

/* ---- 6b. THE PRICE CARDS -------------------------------------------------
   His ruling of 2026-08-24: *"too much info and not very friendly ... same idea
   with very goods and crosses ... When we do have more info to say we add a tab
   to the panel."* The convention every AI company's pricing page uses -- name,
   one line, the figure large, one action, then what is and is not in the tier --
   worn in OUR colours and on OUR blue page. Nothing is copied but the shape.

   IT IS THE DECK'S CARD, UNDRESSED. `.card` in deck.css is a 4:3 tile with an
   animated rainbow strip for a border and a body that stays hidden until the
   tile is opened -- exactly right for a gallery of ten and exactly wrong for
   three prices, where nothing may be hidden and the three are different heights
   by nature. So every one of those four properties is named and turned off
   HERE, out loud, rather than a second card being written next to the first
   (rule 33): one card element, two looks.

   THREE COLUMNS THAT BECOME THREE BLOCKS. `auto-fit` with a floor rather than a
   media query, so the row reflows at whatever width it actually runs out of. */
.deck.deck-price {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1rem;
  margin: 1.4rem 0 .6rem;
}
.deck-price .card {
  aspect-ratio: auto;
  cursor: default;
  padding: 0;
  border: 1px solid color-mix(in srgb, var(--door, var(--paper)) 62%, var(--door-ink, var(--ink)));
  border-radius: 12px;
  background: none;
  animation: none;
  filter: none;
  box-shadow: none;
}
.deck-price .card:hover { filter: none; box-shadow: none; }
/* height:100% AND the card stretched by the grid, so three tiers with different
   amounts to say still line their doors up along one edge. `auto` let each face
   shrink to its own content and left Enterprise's button floating halfway up the
   row. */
.deck-price .card > .face {
  height: 100%;
  overflow: visible;
  border-radius: 11px;
  background: color-mix(in srgb, var(--door, var(--paper)) 88%, var(--door-ink, var(--ink)));
  color: var(--door-ink, var(--ink));
  padding: 1.3rem 1.2rem 1.1rem;
  display: flex;
  flex-direction: column;
}
.deck-price .card > .face > h3 {
  font-size: 1.35rem;
  max-width: none;
  margin: 0 0 .3rem;
  color: inherit;
}
.deck-price .kicker { margin: 0 0 1rem; color: inherit; opacity: .78; font-size: .95rem; line-height: 1.45; }
/* THE FIGURE IS THE LARGEST THING ON THE CARD, which is the one non-negotiable
   part of this convention: a price a visitor has to look for is a price they
   suspect. */
.deck-price .price {
  font: 750 2.6rem/1 var(--body);
  letter-spacing: -.02em;
  margin: 0 0 1rem;
  color: inherit;
}
.deck-price .price-sub {
  display: block;
  font: 400 .8rem/1.45 var(--body);
  letter-spacing: 0;
  opacity: .72;
  margin-top: .5rem;
}
.deck-price .body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  margin-top: 0;
  overflow: visible;
  font-size: 1rem;
  color: inherit;
}
/* THE MARK IS A ::before ON THE ROW'S CLASS, never a character in the copy --
   so a translation cannot lose it and a row cannot be a tick in one language and
   a cross in another. */
.ticks { list-style: none; margin: 0 0 1.2rem; padding: 0; flex: 1 1 auto; }
.ticks li {
  position: relative;
  padding-left: 1.6rem;
  margin: 0 0 .55rem;
  font-size: .93rem;
  line-height: 1.45;
}
.ticks li::before { position: absolute; left: 0; top: 0; font-weight: 700; }
.ticks li.yes::before { content: "\2713"; color: var(--green); }
.ticks li.no::before  { content: "\2715"; color: var(--crimson); opacity: .9; }
.ticks li.no { opacity: .6; }
/* THE ACTION SITS AT THE FOOT OF EVERY CARD REGARDLESS OF HOW LONG ITS LIST IS,
   so three cards of different heights still line their doors up. */
.deck-price .door { margin-top: auto; align-self: flex-start; }
/* SAID IN WORDS FOR A READER WHO CANNOT SEE THE MARK. A tick list read aloud as
   a flat list of features tells a screen-reader user the opposite of what the
   page shows. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---- 6c. THE DOWNLOAD ROWS ----------------------------------------------
   His R1 ruling of 2026-08-24: *"a download link per OS with an image per OS."*
   Three cards again, undressed the same way the price cards are, with the mark
   standing where a deck card's serial number would.

   THE MARKS ARE DRAWN AND THEY TAKE THE PAGE'S INK. Each file is a stroke-only
   SVG using `currentColor`, so one drawing works on paper, on a blue door and on
   a black one -- and nothing here is a vendor's logo, which is a trademark
   question rather than a design one. The platform's NAME is the card's title, in
   text, so the meaning never depends on the picture. */
.deck.deck-os {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: 1rem;
  margin: 1.4rem 0 .8rem;
}
.deck-os .card {
  aspect-ratio: auto;
  cursor: default;
  padding: 0;
  border: 1px solid var(--hairline);
  border-radius: 12px;
  background: none;
  animation: none;
  filter: none;
  box-shadow: none;
}
.deck-os .card:hover { filter: none; box-shadow: none; }
.deck-os .card > .face {
  height: 100%;
  overflow: visible;
  border-radius: 11px;
  background: var(--paper-deep);
  padding: 1.2rem 1.15rem 1.05rem;
  display: flex;
  flex-direction: column;
}
.deck-os .card-pic { width: 40px; height: 40px; display: block; margin: 0 0 .7rem; color: var(--ink); opacity: .8; }
.deck-os .card > .face > h3 { font-size: 1.15rem; max-width: none; margin: 0 0 .3rem; }
.deck-os .kicker { margin: 0 0 1rem; font-size: .92rem; line-height: 1.45; }
.deck-os .body { display: flex; flex-direction: column; flex: 1 1 auto; margin-top: 0; overflow: visible; }
.deck-os .door { margin-top: auto; align-self: flex-start; }

/* THE RAIL'S NUMBER IS ITS OWN ELEMENT NOW (round 6), because the renderer
   counts it rather than every translator typing it. It inherits everything --
   this rule exists so the number can be dimmed a shade without touching the
   word, and so an instrument can read the two apart. */
.rail-n { opacity: .75; }

/* ---- 7. THE PROMPT BAR ---------------------------------------------------
   "a standard ai prompt with edit turned off but with a listbox with the 20
   questions" (2026-08-24), and then, pointing at a picture of one: "this is how
   an AI prompt looks like; and its how ours should be too."

   THE FORM IS THE CONVENTION, THE COLOURS ARE OURS. What a visitor recognises is
   a shape -- a dark rounded field with the line inside it and a row of small
   controls along its bottom edge -- not anybody's brand. Every colour here comes
   out of site.tokens, and each door re-dresses the bar through --door-ink, so
   the same element reads correctly on white, on blue and on black.

   IT IS A CHAT NOW, AND THE FIELD REALLY UNLOCKS (round 6). His ruling: the
   answer lands "in same chat box which expands then is scrollable like a
   buffer", and "after at least one choice user can be able to type". So the bar
   holds three things stacked -- the conversation, the field, the controls -- and
   the field is a real <input> that starts read-only and greyed and becomes live
   the moment a question has been asked. Read-only rather than a div, because
   this one is going to accept typing: a box that will never take text and a box
   that does not take it YET are different promises, and only the second may look
   like a field. */
.cx-prompt { margin: 1.8rem 0 .5rem; max-width: 46rem; }
.cx-promptbar {
  background: var(--panel);
  color: var(--panel-ink);
  border: 1px solid color-mix(in srgb, var(--panel) 82%, var(--panel-ink));
  border-radius: 22px;
  padding: 1rem 1.15rem .7rem;
  cursor: default;
  box-shadow: 0 8px 26px -18px rgba(0,0,0,.7);
}
/* THE PILL STAYS DARK ON EVERY DOOR -- that is the shape he pointed at -- but it
   has to be VISIBLE on the one door that is already dark. A step toward the
   door's own ink lifts it away from a black ground and settles it a little
   deeper on a white one, which is the same one-rule-three-grounds move the
   door's other controls make. */
.cx-door .cx-promptbar {
  background: color-mix(in srgb, var(--panel) 86%, var(--door-ink, var(--panel-ink)));
  border-color: color-mix(in srgb, var(--panel) 66%, var(--door-ink, var(--panel-ink)));
}
/* ---- THE BUFFER -----------------------------------------------------------
   Empty and taking no room until the first turn lands. It GROWS with the
   conversation and then stops growing and scrolls, which is his sentence
   exactly: a box that expands, then is a buffer. The ceiling is in `rem` so it
   is a number of LINES rather than a number of pixels -- the same reasoning the
   pad's ruling follows. */
.cx-chat {
  max-height: 22rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  margin: .2rem .1rem .8rem;
  padding-right: .35rem;
  scrollbar-width: thin;
}
.cx-turn {
  font: 400 1rem/1.55 var(--body);
  white-space: pre-line;
  margin: 0 0 .55rem;
}
/* THE VISITOR'S SIDE IS THE ONE THAT LOOKS ADDRESSED. A question sits in its own
   soft chip pushed to the right; an answer is plain text at full width, which is
   the arrangement every assistant a visitor has used already makes. */
.cx-turn-q {
  color: var(--panel-ink);
  background: color-mix(in srgb, var(--panel) 78%, var(--panel-ink));
  border-radius: 14px 14px 4px 14px;
  padding: .5rem .8rem;
  margin-left: auto;
  width: fit-content;
  max-width: 85%;
  font-weight: 550;
}
.cx-turn-a { color: var(--panel-ink); opacity: .92; margin-bottom: 1.1rem; }
/* WAITING IS THREE DOTS AND NOT A WORD, which is the one piece of this box that
   needs no translation and reads the same to everyone. */
.cx-dots i {
  display: inline-block;
  width: .38em; height: .38em;
  margin-right: .28em;
  border-radius: 50%;
  background: currentColor;
  opacity: .35;
  animation: cx-dots 1.1s ease-in-out infinite;
}
.cx-dots i:nth-child(2) { animation-delay: .18s; }
.cx-dots i:nth-child(3) { animation-delay: .36s; }
@keyframes cx-dots { 0%,80%,100% { opacity: .25 } 40% { opacity: .95 } }
@media (prefers-reduced-motion: reduce) { .cx-dots i { animation: none } }
html.still .cx-dots i { animation: none; }
.cx-turn-a.streaming::after {
  content: "";
  display: inline-block;
  width: .5em; height: 1em;
  margin-left: .1em;
  vertical-align: -.15em;
  background: var(--green);
  animation: cx-caret 1s steps(1) infinite;
}
html.still .cx-turn-a.streaming::after { animation: none; }

/* ---- THE FIELD ------------------------------------------------------------
   One element in two states, and the states have to be legible at a glance:
   locked it is dim and takes no caret, live it is the door's own ink and does.
   `readonly` rather than `disabled` because a disabled input is skipped by the
   keyboard entirely -- a visitor tabbing through the page would never meet the
   thing that is about to become available to them. */
.cx-prompt-q {
  font: 400 1rem/1.5 var(--body);
  color: var(--panel-ink);
  background: transparent;
  border: 0;
  outline: 0;
  width: 100%;
  display: block;
  min-height: 1.5em;
  margin: .15rem .2rem .9rem;
  padding: 0;
}
.cx-prompt-q[readonly] { color: var(--panel-dim); cursor: default; }
.cx-prompt-q::placeholder { color: var(--panel-dim); opacity: .85; }
.cx-prompt-q:not([readonly]):focus-visible { outline: 0; }
/* THE ARROW IS NOT FURNITURE -- it appears the moment the field is live, which
   is how a visitor learns that it is. */
.cx-send {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem; height: 1.9rem;
  border-radius: 50%;
  border: 0;
  cursor: pointer;
  color: var(--panel);
  background: var(--panel-ink);
}
.cx-send[disabled] { opacity: .4; cursor: default; }
.cx-send:focus-visible { outline: 2px solid var(--crimson); outline-offset: 2px; }
.cx-promptrow {
  display: flex;
  align-items: center;
  gap: .55rem;
}
/* The mic sits at the far end of the bar, as it does in the shape he pointed
   at -- pushed there by the gap rather than by a second layout. The send arrow
   follows it, so the right-hand cluster reads left to right as voice-then-send
   whether or not the arrow is there yet. */
.cx-promptrow .cx-mic { margin-left: auto; }
/* THE TWO GLYPHS ARE FURNITURE. They are part of the shape he pointed at and
   they do nothing, so they are dimmed to well below the picker, they refuse the
   pointer, and the markup hides them from assistive technology entirely. A
   control a screen reader announces and the page cannot honour is a worse lie
   than a drawing of one. */
.cx-inert {
  color: var(--panel-dim);
  opacity: .55;
  pointer-events: none;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.cx-chip {
  width: 1.9rem;
  height: 1.9rem;
  border: 1px solid currentColor;
  border-radius: 50%;
  font: 400 1.15rem/1 var(--body);
}
.cx-mic { width: 1.9rem; height: 1.9rem; }
/* THE PICKER IS THE MODE CHIP, and that is not a costume: in the bar he pointed
   at, the chip bottom-left is what you change to change what the answer will be,
   and here that is exactly what it does. */
.cx-pick {
  /* A CHIP, NOT A FIELD. Full width it repeated the chosen question directly
     under the line that already shows it, which reads as a bug rather than as a
     control; sized to its content it is the mode chip his reference has, and the
     duplication becomes the ordinary "this is what is selected". */
  flex: 0 1 auto;
  min-width: 0;
  max-width: 24rem;
  text-overflow: ellipsis;
  font: 500 .92rem/1.3 var(--body);
  color: var(--panel-ink);
  background: color-mix(in srgb, var(--panel) 86%, var(--panel-ink));
  border: 1px solid color-mix(in srgb, var(--panel) 74%, var(--panel-ink));
  border-radius: 999px;
  padding: .45rem .9rem;
  cursor: pointer;
}
.cx-pick:focus-visible { outline: 2px solid var(--crimson); outline-offset: 2px; }
/* ---- THE RULE, WHERE A VISITOR CAN READ IT ------------------------------
   ABOVE the globe, on his ruling of the same day -- and it opens carrying the
   globe's OWN rule, so the first thing anybody reads is the text that made the
   picture they are already looking at, not a promise about a picture they might
   get. `--panel-dim` for the lead and the panel's own ink for the rule: the
   text is the exhibit, the label is not. */
.cx-rule-lead {
  margin: 0 0 .35rem;
  font-size: .82rem;
  color: var(--panel-dim);
}
/* SOMEBODY SPOKE, AND IT MUST NOT LOOK LIKE SOURCE. His report, 2026-08-31:
   "chat should look like dialogue not rules; dialogue should have its own
   frame; code has its own."

   THE BOX HAD ONE FRAME BECAUSE IT HAD ONLY EVER CARRIED ONE KIND OF ANSWER.
   Every destination before backgammon replies with a rule and nothing else, so
   a character's line arriving inside that rule was printed in the monospace
   green block built for formulas -- a person's speech, set as code. The frames
   are separated here rather than in the answer, because the two things are
   genuinely different and were always going to need saying differently.

   IT IS THE OPPOSITE OF `pre.cx-rule` ON EVERY AXIS THAT CARRIES MEANING: the
   page's own reading face rather than monospace, an open quote mark rather than
   braces, and no filled block at all -- speech in this box is the ordinary
   thing and the code is the exhibit, which is the reverse of the invoice's
   arrangement and correct for the same reason. */
.cx-said {
  margin: 0 0 .9rem;
  padding: .15rem 0 .15rem 1.05rem;
  border-left: 2px solid var(--panel-dim);
  font-size: .95rem;
  line-height: 1.6;
  font-style: italic;
}
.cx-said::before {
  content: "\201C";
  font-style: normal;
  font-size: 1.4rem;
  line-height: 0;
  vertical-align: -.15rem;
  margin-right: .15rem;
  color: var(--panel-dim);
}
.cx-said::after {
  content: "\201D";
  font-style: normal;
  font-size: 1.4rem;
  line-height: 0;
  vertical-align: -.35rem;
  margin-left: .1rem;
  color: var(--panel-dim);
}
/* A FORMULA IS THE ONE THING IN THIS BOX THAT IS NOT PROSE, and now it looks
   like it. His ruling, 2026-08-30, on the four states a turn moves through:
   "Formula (dark green background, white text) then english translation. Then
   prompt. Then new formula which amends the formula."

   IT WAS THE SAME PANEL COLOUR AS EVERYTHING ELSE, so the exhibit -- the text
   that IS the behaviour -- sat in the transcript looking like another remark. A
   reader scrolling the conversation could not find the formulas without reading
   the sentences around them, which is exactly backwards: the formulas are the
   thing and the sentences are about them.

   THE GREEN IS THE SHEET'S OWN. `#1e6b43` is the accent the invoice paints its
   own column heads and totals with (its `_json look`), so the block in the box
   and the sheet above it are visibly the same document rather than two designs
   that happen to share a page. */
pre.cx-rule {
  margin: 0 0 1rem;
  padding: .8rem 1rem;
  border-radius: 12px;
  background: #123f2a;
  color: #f2f7f3;
  border: 1px solid #1e6b43;
  font: .82rem/1.55 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  /* A rule is short and its lines are meaningful; wrapping is kinder than a
     scrollbar on a paragraph nobody expected to scroll. */
  white-space: pre-wrap;
  overflow-x: auto;
}
/* WHAT A FORMULA SAYS, DIRECTLY UNDER THE FORMULA. His first two states are one
   turn -- the code, and beneath it the same thing in words -- so this is not
   another chat bubble but a caption belonging to the block above it. Pulled up
   into the air `pre.cx-rule` reserves for the next turn, for the same reason
   `.cx-rule-more` is. */
.cx-turn-said {
  margin: -.7rem 0 .2rem;
  line-height: 1.55;
}
.cx-turn-said.waiting { min-height: 1.2rem; }

/* THE LINE UNDER AN ABRIDGED RULE. It sits with the lead rather than with the
   rule -- same dim ink, same size -- because it is our note about the exhibit
   and not part of it. Pulled up under the block it belongs to, since
   `pre.cx-rule` reserves a full line of air for the turn that follows. */
.cx-rule-more {
  margin: -.7rem 0 1rem;
  font-size: .78rem;
  color: var(--panel-dim);
}

/* `.cx-prompt-a` IS GONE WITH THE PARAGRAPH IT DRESSED. Round 5's answer landed
   BELOW the pill in a block of its own with a 4.8rem floor reserved for it --
   which is a box holding space for an answer nobody has asked for yet. The
   answer is a turn in the buffer now, and the caret rule that used to live here
   moved with it (`.cx-turn-a.streaming`, above) rather than being copied. */
/* EVERY CHIP AND BUTTON INSIDE A DOOR TAKES THE DOOR'S GROUND. Written one
   selector at a time this would drift; written once, a control added to a door
   tomorrow is dressed by construction. `color-mix` toward the ink gives a
   surface that is a step away from the ground in whichever direction the ground
   is -- lighter on the black door, darker on the white one -- so one rule
   serves three grounds instead of three rules serving one each.

   THE BLACK DOOR IS WHY THIS EXISTS. A white opponent-picker and a black-on-
   black start button were the two things his gaming tab put on a dark ground,
   and both were correct on the page they were written for. */
.cx-door .pref,
.cx-door .cx-appstart {
  background: color-mix(in srgb, var(--door, var(--paper)) 88%, var(--door-ink, var(--ink)));
  color: var(--door-ink, var(--ink));
  border-color: color-mix(in srgb, var(--door, var(--paper)) 68%, var(--door-ink, var(--ink)));
}
.cx-door .cx-appstart { border: 1px solid; }
.cx-door .cx-appstart:hover,
.cx-door .pref:hover {
  border-color: var(--crimson);
}

@media (max-width: 720px) {
  .cx-tabbar button { padding: .6rem .7rem; font-size: .86rem; }
  .cx-tabs.folder > .cx-tabbar button { padding: .7rem .4rem; font-size: .8rem; letter-spacing: .04em; }
  .cx-tabs.folder > div > .cx-door { padding: 1.2rem .9rem 1.4rem; }
  .dec-mark { font-size: 1.7rem; }
}

/* A CONTROL IN THE PAGE IS NOT A CONTROL IN THE BAND. `.pref` is sized for his
   black bar; the same word standing in a section is something a visitor is meant
   to reach for, so it gets the size of one. */
main .pref {
  font: 500 .98rem/1 var(--body);
  padding: .55rem .8rem;
  margin: .2rem 0 1.1rem;
  background: var(--paper-deep);
  color: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: 8px;
}
main .pref:hover { border-color: var(--crimson); }

/* ---- A SECTION TITLE FITS ONE LINE (his ruling 2026-08-25, R9.3) -----------
   His words: "2 lines when 1 suffices". The balance fix made a wrapped title
   PRETTY; he does not want it wrapped at all. So the rule is: a section title
   fits ONE line at its measure, and the SIZE steps down before the line breaks.

   THIS IS THE MASTHEAD'S MECHANISM, GENERALISED -- not a new one. `.masthead`
   already does exactly this a thousand lines above: `white-space: nowrap` plus a
   size expressed in `cqw` against a container, so the type shrinks to fit the
   column instead of the sentence folding. Writing a second mechanism for the
   same requirement is the shape rule 33 forbids; this is the same three
   declarations pointed at a different selector.

   WHY THE TITLES WRAPPED AT ALL, and it is not the column. Measured on the
   staged page at 1400px: the section column is 1073px wide and the two wrapped
   titles need 632px and 802px. They wrapped because `h2` carries
   `max-width: 24ch` -- a MEASURE, the right instinct for prose and the wrong one
   for a heading that is a single sentence. So `max-width` goes to none here and
   the size becomes the thing that gives.

   THE COEFFICIENT IS MEASURED, NOT CHOSEN. Every section title in all five
   languages, rendered in this face at this weight and this tracking, and the
   widest is GERMAN: "Ein kompletter Stack, in reinem C geschrieben." at 20.77
   times its own font size at 40px. 100/20.77 = 4.81% -- and 4.81 IS THE NUMBER
   THAT DOES NOT WORK, which is why the first cut of this rule is not the one
   that shipped. Rendered back at the size the expression actually chooses, that
   same line measures 20.97 times its font size: hinting and tracking do not
   scale perfectly, so a ratio taken at one size is not a ratio. At 1024px the
   4.7 that came out of the first measurement left TEN PIXELS of a 697px column,
   which is a rule that holds until somebody edits five words of German. 4.5
   comes from the ratio measured where it is used and leaves 5.6% -- the same
   headroom instinct the masthead's 4.2 comes from, arrived at the same way, and
   with the same consequence: change the copy in any language and this is what
   has to be re-measured.

   MEASURED IN THE DOM, BECAUSE THE CANVAS LIED. The first cut derived these
   ratios from `canvas.measureText` and they came out 7% short (475px predicted
   against 511px actually rendered) -- webfont metrics and tracking do not
   survive that route. The numbers above come from a real cloned h2 in its real
   place. An instrument that under-reads by 7% would have produced a coefficient
   that wraps the German line on the day somebody looks.

   AND IT STOPS AT 622px, WHICH IS THE HONEST HALF. `nowrap` with no floor makes
   a phone-width heading 15px -- measured: a 375px viewport gives a 327px column,
   and 4.5% of that is smaller than the body text it introduces. That is not a
   heading any more, and forcing one line there would be obeying the letter of
   the ruling against its point. So below 622px of column the title wraps at
   28px, exactly as it does today. 622 is not a round number and is not meant to
   be: it is 28 / 0.045, the width at which the expression yields precisely the
   size the wrapped title already uses, so the hand-off has no step in it.

   SCOPE IS `main > section > .wrap > h2` AND THAT IS DELIBERATE. Two other
   populations render as h2 and neither is a section title: `.pad h2` are the
   three door QUESTIONS (16px, inside `.pad > .wrap`), and `.doc-body h2` are the
   headings of a LIFTED MANUAL -- somebody else's document, whose headings are
   prose-length and whose sizing is not this page's decision. The child
   combinator excludes the second by structure; the ancestor excludes the first. */
/* `width: 100%` IS LOAD-BEARING AND IT COST A RED PIN TO LEARN. A `section` here
   is a GRID CONTAINER, so `.wrap` is a grid ITEM -- and `container-type:
   inline-size` implies inline-size CONTAINMENT, which removes the contents from
   the item's intrinsic size. A grid item with no intrinsic size and no explicit
   one shrink-wraps to ZERO: measured at 390px, `.wrap` went 342px -> 0px and
   centred itself, the facts band spilled out of it, and the document went 397px
   wide inside a 390px window. `Z_site_lang_browser` caught it as "the page
   scrolls sideways at 390px" and named `.plaqueband` -- which was innocent, and
   said so itself: it is `position: fixed`, so it was FOLLOWING the width rather
   than setting it. An explicit width gives containment something definite to
   contain, and the `max-width: 1080px` above it still decides the measure. */
main > section > .wrap { container-type: inline-size; width: 100%; }
@container (min-width: 622px) {
  main > section > .wrap > h2 {
    white-space: nowrap;
    max-width: none;
    font-size: min(40px, 4.5cqw);
  }
  /* ---- AND ONE TITLE IS LONGER THAN THE MECHANISM ABOVE CAN HOLD ----------
     DECLARED BY THE PANEL, NOT DETECTED. His team headline of 2026-09-02 --
     "Built by a man and his AI assistant -- the premise in the build itself" --
     needs 29.3 times its own font size at weight 700. Every other title in all
     five languages fits inside 20.97, which is where the 4.5 above comes from,
     so this one line is not a title that grew: it is 40% past the widest thing
     the coefficient was ever measured against.

     WHY THE COEFFICIENT ABOVE IS NOT RE-MEASURED, which is what the block above
     tells the next person to do. Because it is GLOBAL: 100/29.3 is 3.41, and
     `min(40px, 3.4cqw)` puts EVERY section title on the site at 35px in the
     1026px column instead of 40px. One headline would have shrunk the type of
     nine sections that were already correct, and it would have done it silently
     -- the arms measure widows and overflow, not whether the page still looks
     like his.

     HIS RULING, 2026-09-02, SHOWN TO HIM AS TWO RENDERED CANDIDATES AND NOT AS
     NUMBERS: one line. He was given the sentence wrapped on two balanced lines
     and the sentence held on one at a smaller size, and chose the second -- so
     the mechanism above is not re-measured, it is RE-POINTED at this one
     selector with its own coefficient. That keeps the other nine titles at 40px,
     which was the whole reason the global re-measure was refused.

     AND THEN HE ASKED FOR THE RANK BACK, in the same breath: *"the title we can
     make it bolder? and a different colour? dark blue?"*, and a message later
     *"I do prefer another font and another bold colour"*. Both come from the
     same place: a heading at 26px sits barely above the 22px name beneath it,
     and size was the only thing saying "heading". So FACE and COLOUR carry the
     rank that size gave up -- see the rule below the query for which, and why.

     HOW THE FACE AND THE COLOUR WERE ACTUALLY CHOSEN, because the method is the
     reusable part: three faces and eight colours were RENDERED, in place, each
     at the largest size that holds one line, and he picked two names off the
     page ("A2 and B4"). He had already answered a table of pixel measurements
     with *"I honestly have no idea what you're trying to tell me"*, which was
     fair -- his own account of how he decides is *"I'm hopeless as no idea
     what's good or bad. But I can copy ideas I like."* Asking him to originate a
     type decision costs him time to manufacture an answer; asking him to select
     one is instant and accurate. Build the candidates, put them on screen.

     THE COEFFICIENT IS MEASURED IN THE FACE IT SHIPS IN, at the size the
     expression actually chooses -- the same "a ratio taken at one size is not a
     ratio" the block above learnt the hard way, and this face makes the point
     twice over: it measures 27.90 times its font size at 24px and 28.82 at 40px.
     3.25 is taken against the WIDEST column this page can hand it (1080px, where
     the ratio is worst), not against the 813px column it was looked at in, and
     it leaves 7% there and 9% here. Change these words in any language, or move
     this heading into a wider panel, and that is what has to be re-measured --
     in the DOM, never in a canvas.

     AND IT HAS ITS OWN FLOOR AT 738px, WHICH IS THE SAME HONEST HALF THE 622
     ABOVE IS. `nowrap` with no floor is what makes a heading stop being one: at
     the bottom of the 622 band this line would come out at 20px, and the two
     things it introduces are a 22px name and 19px body text. A heading smaller
     than the name under it is not a heading, so below 738 -- which is 24/0.0325,
     the width where the one-line size stops out-ranking that 22px name -- this
     title goes back to wrapping on two balanced lines, which is what it did
     before today and is still the right answer at that measure. That is why the
     override is TWO rules and not one: the 622 band restores `normal` + balance
     (the outer rule has already said `nowrap` for every title, so saying nothing
     here would overflow the column rather than wrap), and the 738 band takes the
     one line back.

     ADDRESSED BY ID, AND THAT IS DELIBERATE. The first cut of this declared a
     `wraptitle` class on the panel, and `forge site` REFUSED it: `class` is a
     CLOSED SET (hero | plain | deck | quiet | ticker | pad | business) and a new
     value is a word added to the page language, which is his to grant -- the
     same gate the weather document stayed outside of. It should not take a
     vocabulary ruling to stop a headline being clipped, and this is not
     vocabulary: a page author gains nothing to SAY here. It is one section's
     type, so it is addressed as one section's type. If the headline moves or the
     panel is renamed, this rule stops matching and the title clips again --
     visibly, in the shot the next site round takes. */
  main > section#team > .wrap > h2 {
    white-space: normal;
    text-wrap: balance;
  }
}
@container (min-width: 738px) {
  main > section#team > .wrap > h2 {
    white-space: nowrap;
    max-width: none;
    font-size: min(40px, 3.25cqw);
  }
}
/* ---- AND IT IS EVERY SECTION TITLE, NOT ONE (his ask, 2026-09-02) ----------
   *"can we use same colour/font on all titles which have white backgrounds? as
   at the moment only 1 is like that"* -- and he is right that one heading in a
   different face and colour reads as a mistake rather than a decision. So the
   treatment he picked for the team panel is the SITE'S title treatment, and the
   `#team` block above keeps only what is genuinely local to it: the fit.

   THE FACE IS THE MASTHEAD'S, AND THAT IS HIS SECOND RULING ON IT. He first
   picked `Newsreader` -- the paragraph serif -- off a page of three faces shown
   on ONE heading. Seeing it on nine, with the masthead in the sans above them,
   he asked the question that settles it: *"Can we adopt this font used here for
   the other titles?"* So the page has ONE title face and it is the one the
   masthead already wore: Bricolage at 750 with -0.03em, copied from `.masthead`
   deliberately rather than approximated. The serif answer was not wrong; it was
   answered on too small a sample, which is what happens when a candidate is
   shown in isolation instead of in the page it has to live in.

   750 IS THE MASTHEAD'S WEIGHT, MEASURED IN THE DOM RATHER THAN READ OFF A
   RULE -- the h1 inherits 700 from the base and the hero's own styling takes it
   to 750, so "the font used here" is a number this file does not state anywhere
   else. Copying 700 would have been a heading visibly lighter than the line
   above it and no one could have said why.

   OUTSIDE THE CONTAINER QUERY on purpose, so a phone gets the same headings,
   wrapped. And the SIZE is not touched, through two changes of face now --
   because it did not need to be, and that was CHECKED rather than assumed. The
   `4.5cqw` above was measured against Bricolage 700 at -0.025em, where the
   widest line on the site (German platform) came to 20.97 times its font size;
   at 750 with the masthead's tighter -0.03em it measures 20.70, so every title
   fits with slightly MORE room than the coefficient was built for. The team
   line's own 3.25 was calibrated for the serif and re-measured here too: 28.16,
   which lands it at about 90% of its column. Had either come out wider than its
   coefficient allows, the number would have had to move -- that is fit, not
   taste. Moving it because a face left room spare would be the other thing.

   THE ONE PANEL ON A DARK GROUND KEEPS ITS OWN INK, AND IT ASKS FOR IT RATHER
   THAN OUT-RANKING ANYTHING. He scoped the ask to white backgrounds; `#pricing`
   is the blue business panel. The obvious reading is that sec.6a's
   `section.business h2 { color: inherit }` already wins -- and it does NOT:
   that is (0,1,2) against this rule's (0,1,3), so the first cut of this change
   painted a dark aubergine heading onto a dark blue panel. MEASURED, not
   reasoned: the survey that checked every section reported `#pricing` in
   rgb(74,44,109) like all the others.
   So the colour goes through `--title-ink`, which any panel may redefine for
   its own ground -- the same shape sec.6a already uses for `--door`/`--door-ink`
   ("everything below reads those two variables and nothing names a colour").
   A specificity war would have worked once; a token keeps working when the next
   panel arrives on a ground of its own.
   The dark panel does take the FACE, deliberately: one face for every title on
   the page, and the colour is what follows the ground. */
main > section > .wrap > h2 {
  font-family: 'Bricolage Grotesque', sans-serif;
  font-weight: 750;
  letter-spacing: -0.03em;
  color: var(--title-ink, var(--title-deep));
}

/* ---- THE CAPABILITY GRID (his design 2026-08-25, R9.1) ---------------------
   *"stick to the slogan and make a pretty cell grid with how cx+ai can help.
   Then we list the capabilities human and ai with the [two] next [to each other]
   so its visual."* The masthead says "One language. For you - and - for your
   AI."; this is that sentence unpacked, and the two columns read ACROSS because
   reading across IS the claim.

   BOXES, NOT A SPREADSHEET. He asked for pretty, and the site's own aesthetic is
   hairline boxes on the page ground -- so each cell is one of those, and the grid
   is a CSS grid rather than a <table>. That also settles the phone: three columns
   of prose at 375px is either a sideways scroll or unreadable type, and a grid
   restacks with one line of CSS where a table needs markup gymnastics.

   THE ROW LABEL IS A TRACK, NOT A HEADING, so the capability name sits on the
   same baseline as the two cells it introduces. `max-content` keeps that column
   exactly as wide as the longest capability name in whichever language is on --
   no measurement to maintain, and no number here to go stale when a translation
   lands (which is the same argument the section-title rule above had to make the
   hard way, with a coefficient). */
/* ONE GRID, NOT EIGHT. The first cut gave every row its own
   `grid-template-columns: max-content 1fr 1fr`, which is correct per row and
   wrong down the page: `max-content` then measures each capability name on its
   own, so the label column changed width row by row and the cells came out
   ragged. `subgrid` makes the rows share the PARENT's tracks, which is the whole
   reason the property exists -- the alternative, hard-coding a label width, is a
   number that goes stale the first time a translation lands. */
.capgrid {
  display: grid;
  grid-template-columns: max-content 1fr 1fr;
  gap: 10px;
  margin: 26px 0 8px;
}
.capgrid-head,
.caprow {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
  align-items: stretch;
}
.capgrid-head span {
  font-family: 'Spline Sans Mono', monospace;
  font-size: 12.5px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--crimson); padding: 0 2px 2px;
}
.cap {
  display: flex; align-items: center;
  font-family: 'Bricolage Grotesque', sans-serif;
  font-weight: 650; font-size: 17px; line-height: 1.2;
  letter-spacing: -0.015em; color: var(--ink);
  padding: 14px 16px;
}
.capcell {
  border: 1px solid var(--hairline); border-radius: 8px;
  padding: 14px 16px; color: var(--ink-2);
  font-size: 15.5px; line-height: 1.45;
  transition: border-color .18s ease, color .18s ease;
}
/* The AI column is the one the slogan is about, so it is the one that carries
   the accent -- a single hairline, not a fill, because a filled column would
   read as a comparison table and this is not one. */
.caprow .capcell:last-child { border-color: color-mix(in oklab, var(--crimson) 34%, var(--hairline)); }
.caprow:hover .capcell { border-color: var(--crimson); color: var(--ink); }
/* Set by the renderer on every cell and hidden here: at desktop the two heads
   above the grid already say which column is which. */
.capcol { display: none; }

@media (max-width: 720px) {
  /* One card per cell, the capability its title, and each cell says which side
     it is -- which is why the repeated column name exists at all. */
  .capgrid { grid-template-columns: 1fr; }
  .capgrid-head { display: none; }
  .caprow { grid-template-columns: subgrid; gap: 6px; }
  .cap { padding: 6px 2px 2px; font-size: 19px; }
  .capcol {
    display: block; margin-bottom: 4px;
    font-family: 'Spline Sans Mono', monospace;
    font-size: 11.5px; letter-spacing: .08em; text-transform: uppercase;
    color: var(--crimson);
  }
}

/* ---- THE DATA PANEL: A DOCUMENT PRESENTED, NOT A FILE SHOWN ---------------
   His commission, 2026-08-26: "pretty display of json/xml/csv/txt files in a
   panel. Because it's also about style. how we present the data."

   IT SHARES ITS FRAME WITH .cx-source AND NOTHING ELSE. The two sit side by
   side on section 08 -- the X-ray and the portrait of one document -- so the
   frame must be the same or the pairing reads as two unrelated panels. What
   differs is everything INSIDE: source is a listing in one face, this is keys,
   values and counts, each with a job.

   A KEY IS NOT A STRING HERE, even though json spells both with quotes. The
   text view already tells you about the characters; this view is for the
   values, so the key wears the display face and the value wears the type. */
.cx-data {
  border: 1px solid var(--rule, #d8d2c4);
  border-radius: 6px;
  background: var(--paper-2, #faf7f0);
  margin: 1rem 0;
  overflow: hidden;
}
.cx-data .dhead {
  padding: .55rem .9rem;
  border-bottom: 1px solid var(--rule, #d8d2c4);
  font-family: var(--display), system-ui, sans-serif;
  font-size: .9rem;
  letter-spacing: .02em;
}
.cx-data .dtree {
  padding: .8rem 1rem;
  max-height: 58vh;
  overflow: auto;
  font-size: .82rem;
  line-height: 1.7;
}
.cx-data .dtree.dbroken {
  font-family: var(--mono), ui-monospace, monospace;
  color: var(--crimson, #a3242b);
}

/* THE GUIDE LINE IS THE STRUCTURE. Indentation alone makes a deep document a
   guessing game about which key a row belongs to; one hairline per level
   answers it without a single extra word on the page. */
.cx-data .dkids {
  margin: .1rem 0 .1rem .42rem;
  padding-left: .78rem;
  border-left: 1px solid color-mix(in oklab, var(--rule, #d8d2c4) 70%, transparent);
}
.cx-data .drow { display: flex; gap: .5rem; align-items: baseline; }
.cx-data .dkey {
  font-family: var(--display), system-ui, sans-serif;
  font-size: .78rem;
  letter-spacing: .015em;
  color: color-mix(in oklab, currentColor 62%, transparent);
  flex: 0 0 auto;
}
.cx-data .dkey::after { content: ""; }
.cx-data .dval {
  font-family: var(--mono), ui-monospace, monospace;
  overflow-wrap: anywhere;
  min-width: 0;
}
/* The same four hues the listing uses, for the same four things, so a reader
   who looks at both views twice does not have to learn two palettes. */
.cx-data .dstr  { color: #9a5b2c; }
.cx-data .dnum  { color: #38618c; }
.cx-data .dbool { color: #7a6bb0; }
.cx-data .dnull { color: #6f8f6f; font-style: italic; }

/* A NODE IS A DISCLOSURE, so the browser owns opening, closing, remembering and
   the keyboard. The marker is drawn here only because the default triangle is a
   different shape in every engine and this panel is his eyeball's territory. */
.cx-data .dnode > summary {
  display: flex;
  gap: .5rem;
  align-items: baseline;
  cursor: pointer;
  list-style: none;
  padding: .05rem 0;
  border-radius: 3px;
}
.cx-data .dnode > summary::-webkit-details-marker { display: none; }
.cx-data .dnode > summary::before {
  content: "";
  flex: 0 0 auto;
  width: 0; height: 0;
  border-left: 4px solid color-mix(in oklab, currentColor 45%, transparent);
  border-top: 3.5px solid transparent;
  border-bottom: 3.5px solid transparent;
  transform: translateY(-1px);
  transition: transform .12s ease;
}
.cx-data .dnode[open] > summary::before { transform: translateY(-1px) rotate(90deg); }
.cx-data .dnode > summary:hover { background: color-mix(in oklab, var(--rule, #d8d2c4) 30%, transparent); }

/* `d->count`, MADE VISUAL. His ruling puts a large document at depth 1 wearing
   its counts, so the count is the thing a closed row is FOR -- it earns a chip
   rather than a parenthesis. */
.cx-data .dcount {
  font-family: var(--mono), ui-monospace, monospace;
  font-size: .68rem;
  letter-spacing: .04em;
  padding: .05rem .4rem;
  border-radius: 999px;
  border: 1px solid color-mix(in oklab, var(--rule, #d8d2c4) 85%, transparent);
  background: color-mix(in oklab, var(--paper, #fffdf7) 70%, transparent);
  color: color-mix(in oklab, currentColor 55%, transparent);
  flex: 0 0 auto;
}

/* ---- A VIEW SWITCH IS NOT A SECOND ROW OF TABS ----------------------------
   Section 08 nests a set inside a set: the outer four are DOCUMENTS, the inner
   two are VIEWS of the one you chose. Dressed alike they read as peers, which
   is exactly the thing the outer set already said. So a NESTED set that did not
   ask to be folders renders as a compact switch -- derived from the structure
   rather than declared, so no definition has to name a look for it and no new
   parameter joins `tabs` to say something the nesting already says. */
.cx-tabs .cx-tabs:not(.folder) > .cx-tabbar {
  margin: 1.1rem 0 0;
  border-bottom: 0;
  gap: 0;
  width: max-content;
  max-width: 100%;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  padding: 2px;
  background: color-mix(in oklab, var(--paper-2, #faf7f0) 80%, transparent);
}
.cx-tabs .cx-tabs:not(.folder) > .cx-tabbar button {
  font: 600 .74rem/1 var(--mono), ui-monospace, monospace;
  letter-spacing: .1em;
  text-transform: lowercase;
  padding: .42rem 1rem;
  border: 0;
  border-radius: 999px;
  margin: 0;
  color: var(--ink-2);
}
.cx-tabs .cx-tabs:not(.folder) > .cx-tabbar button[aria-selected="true"] {
  background: var(--ink);
  color: var(--paper, #fffdf7);
  border-bottom-color: transparent;
}
.cx-tabs .cx-tabs:not(.folder) > div > [data-cxtab] { padding-top: .9rem; }

/* A TRANSCRIPT IS NOT SOURCE, and the panel should not pretend otherwise. Same
   frame, same measure -- but nothing coloured, because there are no tokens to
   colour, and a touch more line to read a run rather than scan a listing. */
.cx-source .src-body.src-out {
  color: color-mix(in oklab, currentColor 82%, transparent);
  line-height: 1.62;
}

/* THE PRE-RENDERED BODY IS FOR READERS WHO GET NO RENDERER (round 10, WS4).
   `#cxsite` holds a build-time capture of this page's own markup so a crawler
   or a scripting-off visitor reads the site instead of an empty div. When
   scripting IS on, render.js removes the element entirely a moment later --
   this rule is what stops the capture being painted first and swapped in front
   of somebody. Keyed on `html.js`, which the shell sets synchronously in its
   head, so the hide happens before the first paint rather than after it. */
html.js #cxsite { display: none; }

/* THE DOCS DECK ARRIVES WITH ONE CONTROL TOO MANY (3.1.073.0).

   The docs section lifts the documentation door's own markup, and that markup
   carries the door's drift toggle -- "If the drifting boxes irritate you, click
   here to hold them still." On the door it is the only way to ask; here the top
   band already has `boxes: moving`, and deck.js's click handler matches BOTH
   (`#stillbtn, [data-still-toggle]`), so the two are one flag with two faces.

   IT IS HIDDEN RATHER THAN LEFT ALONE BECAUSE THE LIFTED ONE CANNOT TELL THE
   TRUTH. Its label is painted by the door's own inline script, which does not
   run on this page -- so it would keep offering to hold the boxes still after
   they had been stilled, while the band beside it correctly read `boxes: still`.
   A control that works but describes the wrong state is worse than no second
   control at all, and the band is the one written in this page's voice.

   NOT FIXED AT THE SOURCE, on purpose: the door needs its own words, and the
   split that lets it have them (deck.js owns the flag and names no string) is
   the same split that makes the label local and therefore unpaintable from
   here. Hiding the duplicate is the small honest answer; teaching this page to
   paint the door's sentence would be a second copy of the door's copy. */
.doc-body [data-still-toggle] { display: none; }


/* --- THE WEATHER OVERLAY ---------------------------------------------------
   His commission of 2026-08-31: site-wide effects -- rain, bubbles, petals on
   the wind -- asked for through the page's own prompt box. The canvas is the
   whole of what this layer owns on the page; everything else about it is rule
   text in lang/site.weather.json.

   THREE PROPERTIES, ALL LOAD-BEARING:

   `pointer-events: none` is the one that keeps a promise. Weather falls over
   the three demos, and a game with rain in front of it has to stay as playable
   as a game without: every click, drag and key goes straight through to what is
   underneath, because this element is never a target for any of them.

   The z-index sits ABOVE the page's content and BELOW its navigation (the band
   is 69, the rail 70). Rain across what you are reading is weather; rain across
   the controls you are reaching for is an obstacle.

   `position: fixed` means the sky does not scroll with the document, which is
   what makes it weather rather than decoration printed on the page. */
.cx-weather {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 60;
  pointer-events: none;
}

/* AND THERE IS DELIBERATELY NO RULE HIDING THIS CANVAS, which is worth a
   paragraph because the first draft had one and it was WRONG.

   It read `html.still .cx-weather { display: none; }` -- a belt to the engine's
   brace, on the reasoning that a page holding still should paint no weather.
   But `html.still` means HIS BOXES are still, and since v3.1.074.0 the weather
   has a master of its own: "turn off the jitter" sets that class and the
   weather is supposed to keep falling. The rule therefore hid the sky on the
   exact sentence his commission names. A screenshot found it; nothing else
   could, because both halves were behaving correctly and only their meeting was
   wrong.

   THE ENGINE IS THE AUTHORITY AND IS THE ONLY ONE. A stopped loop clears the
   canvas, and a cleared canvas is invisible -- so a second opinion in this file
   could only ever disagree with the first (rule 33). */
