:root{
  --void:#000; --line:#161d1c; --line-lit:#28322f;
  --ghost:#c2ccc9; --stone:#5d6a67; --snow:#fff;
  --signal:#4fe4d7; --alarm:#ff4d3d;
  --frame:0.48;            /* the agent's share of the viewport; JS drives it */

  /* ── the measure, in cells ──
     `ch` is the advance of "0", and the prose is set in the same family at the
     same size as the field behind it — so one `ch` is exactly one cell of that
     grid, and the column can be stated in the same unit the insert layer thinks
     in. src/field/inserts.ts holds the other copy of these three numbers, and
     tests/unit/column.test.ts reads this file to hold the two to each other. */
  --measure:64ch;          /* what the column wants */
  --measure-min:52ch;      /* and what it will not go below, whatever happens */
  --corridor:16ch;         /* what it leaves between itself and the agent's box */
}
*{box-sizing:border-box}
html,body{margin:0;background:var(--void)}
body{
  color:var(--ghost);
  font-family:ui-monospace,SFMono-Regular,Menlo,"Roboto Mono",monospace;
  font-size:13px; line-height:1.5;
}
a{color:var(--ghost); text-decoration:none}
a:hover{color:var(--snow)}
:focus-visible{outline:2px solid var(--signal); outline-offset:2px}

/* ── the agent: a canvas, rendered entirely on the GPU ── */
canvas#agent{
  position:fixed; top:0; right:0; z-index:2;
  width:calc(var(--frame) * 100vw); height:100svh;
  display:block; pointer-events:none;
}
/* ── the field: real text in the DOM, the page's vocabulary ── */
pre#field{
  position:fixed; inset:0; margin:0; z-index:0;
  font-family:inherit; font-size:13px; line-height:1;
  white-space:pre; overflow:hidden; user-select:none; pointer-events:none;
  color:#12352f;
}
/* ── the fallback: the agent as bytes, shown until the GPU path takes over.
   It occupies the canvas's own box, not the whole viewport, because it IS the
   canvas's picture — captured at the `wake` keyframe, whose frame is 0.48, and
   --frame's initial value above is that same 0.48. On this path nothing ever
   drives the variable, so the plate is laid into exactly the box it was
   rendered for, and the handoff to WebGL2 moves nothing. ── */
pre#poster{
  position:fixed; top:0; right:0; margin:0; z-index:2;
  width:calc(var(--frame) * 100vw); height:100svh;
  display:flex; align-items:center; justify-content:center;
  font-family:inherit; font-size:clamp(6px,0.95vw,12px); line-height:1;
  white-space:pre; overflow:hidden; pointer-events:none; color:#37988a;
}
html[data-render="webgl2"] #poster{display:none}
/* and the converse, which is the half that was missing: once the page has
   committed to the poster, nothing is ever coming to paint the canvas, so it
   must not go on occupying the space claiming otherwise. */
html[data-render="poster"] canvas#agent{display:none}

.scrim{
  position:fixed; inset:0; z-index:1; pointer-events:none;
  background:linear-gradient(100deg, rgba(0,0,0,.96) 0%, rgba(0,0,0,.90) 34%,
             rgba(0,0,0,.62) 50%, rgba(0,0,0,.30) 68%, rgba(0,0,0,.18) 100%);
  transition:opacity .5s linear;
}
body.resolving .scrim{opacity:.22}
/* ── the inserts: the evidence track, one block at each beat that argues.
   ABOVE the scrim, and in --signal, which is the whole reason they are not part
   of #field: that layer is one colour under a 96%-opaque wash, so anything
   written into it can never read as louder than the drift. Where they stand,
   and where the corridor runs out, is decided in inserts.ts. ── */
pre#insert{
  position:fixed; inset:0; margin:0; z-index:3;
  font-family:inherit; font-size:13px; line-height:1;
  white-space:pre; overflow:hidden; user-select:none; pointer-events:none;
  color:var(--signal);
}
.scan{
  position:fixed; inset:0; z-index:9; pointer-events:none;
  background:repeating-linear-gradient(to bottom, rgba(255,255,255,.018) 0 1px, transparent 1px 3px);
  mix-blend-mode:screen;
}
@media(max-width:900px){
  /* one rule for both, so the fallback cannot drift away from the thing it
     stands in for: same box, same dimming, whichever one is on screen */
  canvas#agent, pre#poster{width:100vw; opacity:.5}
  #field{opacity:.30}
  .scrim{background:linear-gradient(180deg,
    rgba(0,0,0,.45) 0%, rgba(0,0,0,.94) 9%, rgba(0,0,0,.94) 91%, rgba(0,0,0,.45) 100%)}
  body.resolving #field{opacity:1}
}

.doc{position:relative; z-index:4}

/* ── chrome ── */
.bar{
  position:sticky; top:0; z-index:6; display:flex;
  border-bottom:1px solid var(--line-lit); background:rgba(0,0,0,.90);
  backdrop-filter:blur(4px);
}
.bar .cell{
  padding:10px 16px; border-right:1px solid var(--line-lit);
  font-size:11px; letter-spacing:.18em; text-transform:uppercase; color:var(--stone);
  display:flex; align-items:center; gap:9px; white-space:nowrap;
}
.bar .mark{color:var(--snow); letter-spacing:.26em}
.grow{flex:1; border-right:1px solid var(--line-lit)}
.dot{width:6px;height:6px;background:var(--signal);box-shadow:0 0 8px var(--signal)}
.bar a.cell:hover{color:var(--snow); background:rgba(79,228,215,.06)}
@media(max-width:900px){ .bar .opt{display:none} }

/* ── measure ──
   The other half of the composition. The agent takes 48vw through beats 1-5,
   opens to 68vw at the boundary as the wall enters, and takes the screen in the
   coda; the column is what gives it that ground. `--frame` is already written
   to the document element on every frame that changes it, so this is pure
   arithmetic on a variable the page already has — no measurement, and nothing
   for the render loop to read back.

   Three terms, in the order they bind: the space the agent leaves, less the
   corridor the insert layer stands in; never wider than the measure; never
   narrower than the floor. Past the floor the column stops yielding and lets
   the canvas box pass over it — a column that keeps giving ground eventually
   stops being one. */
.col{
  max-width:clamp(
    var(--measure-min),
    calc(100vw - var(--frame) * 100vw - var(--corridor)),
    var(--measure));
  padding:0 24px 0 40px;
}
/* The column sits against the left edge because the agent owns the right, but on
   a wide screen that left inset reads as a page pinned to its own margin. There
   is room to move it in: the gap between the prose's right edge and the insert
   layer measures 157px at 1440 and 87px at 1280.
   Scaled with the viewport and gated at 1200px, because at 1024 that gap is only
   40px and every pixel of it is the corridor the evidence track stands in.
   tests/e2e/gap.spec.ts holds the floor so this cannot quietly eat it later. */
@media(min-width:1200px){ .col{margin-left:clamp(0px, 2.5vw, 48px)} }
@media(max-width:800px){ .col{padding:0 20px} }
/* Below 900px the agent is full-bleed and the prose stands on top of it — see
   the canvas rule above. There is no corridor there to yield to, so the column
   takes the measure and the escalation is carried by the dimming instead. */
@media(max-width:900px){ .col{max-width:var(--measure)} }
.col > section{
  min-height:100svh; display:flex; flex-direction:column; justify-content:center;
  padding:96px 0 40px;
}
@media(max-width:800px){ .col > section{min-height:auto; padding:64px 0 28px} }

.kicker{font-size:10px; letter-spacing:.28em; text-transform:uppercase; color:var(--stone); margin-bottom:20px}
h1{
  margin:0; font-size:clamp(28px,4.4vw,54px); line-height:1.05;
  letter-spacing:-.035em; font-weight:500; color:var(--snow);
  text-wrap:balance; max-width:19ch;
}
h2{
  margin:0 0 8px; font-size:clamp(20px,2.6vw,28px); line-height:1.2;
  letter-spacing:-.03em; font-weight:500; color:var(--snow); text-wrap:balance;
}
p{margin:16px 0 0; font-size:13.5px; line-height:1.78}
p b{color:var(--snow); font-weight:400}
.lead{font-size:14.5px}
/* the two lines that point at the animation — the only place prose turns to look */
.points{color:var(--snow)}

.axiom{
  margin:28px 0 0; padding:14px 0 14px 18px; border-left:1px solid var(--line-lit);
  display:flex; flex-direction:column; gap:7px; font-size:13.5px;
}
.axiom span:last-child{color:var(--signal)}

/* No scroller. The panel hid 104px of itself at 1440 and 198px at 1024 — and
   what ran off the right was the `rationale` string, the one field beat 5 argues
   is a precondition rather than an option. A JSON block the reader has to drag
   sideways to finish, with nothing on screen saying so, makes that argument in
   a box that contradicts it. The long lines wrap instead, hanging under their
   own indent so the shape still reads as JSON. */
.code{margin:26px 0 0; border:1px solid var(--line-lit); background:rgba(4,7,6,.92)}
.code .head{
  padding:8px 14px; border-bottom:1px solid var(--line-lit);
  font-size:10px; letter-spacing:.2em; text-transform:uppercase; color:var(--stone);
  display:flex; justify-content:space-between; gap:16px;
}
.code pre{margin:0; padding:14px; font-size:12.5px; line-height:1.65;
          white-space:pre-wrap; overflow-wrap:break-word}
.c-key{color:#8fb6ae} .c-str{color:var(--ghost)} .c-num{color:var(--signal)}
.c-meth{color:var(--signal)} .c-cmt{color:var(--stone)}
/* The ONLY alarm-coloured thing on the page: beat 6's headline. The same two
   words appear neutrally in beat 5's JSON — stated first, shouted second. */
.c-false{color:var(--alarm)}

/* No min-width, and nothing to scroll.
   The table used to be 460px wide inside a container the measure caps at about
   437, with `overflow-x:auto` quietly hiding the difference — so "A plan with
   its rationale" arrived as "A plan with its rational" and the reader had no
   way to know a word had been taken off the end. A scroller with nothing to
   indicate it scrolls is not a fallback, it is a truncation.
   The cells wrap instead. Two lines of a readable phrase beats one line of a
   cut one, and the same rule serves the phone, where 460px never fit anyway. */
.tbl{margin:24px 0 0; border:1px solid var(--line-lit)}
table{border-collapse:collapse; width:100%; font-size:12.5px; table-layout:fixed}
th,td{text-align:left; padding:10px 14px; border-bottom:1px solid var(--line);
      overflow-wrap:break-word}
th{font-size:10px; letter-spacing:.2em; text-transform:uppercase; color:var(--stone); font-weight:400}
tr:last-child td{border-bottom:0}
td b{color:var(--snow); font-weight:400}

.versus{margin:26px 0 0; border:1px solid var(--line-lit); display:grid; grid-template-columns:1fr 1fr}
@media(max-width:700px){ .versus{grid-template-columns:1fr} }
.versus > div{padding:16px}
.versus > div:first-child{border-right:1px solid var(--line-lit)}
@media(max-width:700px){ .versus > div:first-child{border-right:0;border-bottom:1px solid var(--line-lit)} }
.versus h3{margin:0 0 10px; font-size:10px; letter-spacing:.24em; text-transform:uppercase; color:var(--stone); font-weight:400}
.versus p{margin:0; font-size:13px}
.versus .ours{color:var(--snow)}

.acts{display:flex; margin-top:36px; border:1px solid var(--line-lit); flex-wrap:wrap; max-width:max-content}
.act{
  font:inherit; font-size:12px; letter-spacing:.16em; text-transform:uppercase;
  padding:13px 24px; border:0; border-right:1px solid var(--line-lit);
  background:rgba(0,0,0,.55); color:var(--ghost); cursor:pointer; text-decoration:none;
  display:inline-flex; align-items:center;
}
.act:last-child{border-right:0}
.act.primary{background:var(--signal); color:#001a15}
.act.primary:hover{background:var(--snow); color:#001a15}
.act:not(.primary):hover{background:rgba(79,228,215,.09); color:var(--snow)}
.act[aria-disabled="true"]{color:var(--stone); cursor:default}
.act[aria-disabled="true"]:hover{background:rgba(0,0,0,.55); color:var(--stone)}

/* ── the coda: prose ends, the field resolves ── */
/* The caption's resting place is the FOOT of this section, not its head, and
   that placement is load-bearing: sticky only ever holds a box back in the
   direction it is stuck from, so a note that starts at the top and scrolls
   upward out of view is one `bottom` can do nothing for. Started at the bottom
   it is pulled UP to the foot of the scrollport for as long as the coda is in
   view, then rides the section's own bottom edge out — which lands it directly
   above the footer at the last stop. */
#coda{
  padding:0; height:150svh; position:relative;
  display:flex; flex-direction:column; justify-content:flex-end;
}
/* The caption of the plate, and therefore not standing on it. It used to sit
   at `top:52vh` — dead centre, exactly where the mark and then the wordmark
   resolve — so at the last stop the words "AG3NT · Simpl3 Inc." lay across the
   letterforms of AG3NT and neither could be read. Same chrome as the strip at
   the top of the page: a hairline, a wash, nothing pretending to be depth. */
.coda-note{
  position:sticky; bottom:0;
  border-top:1px solid var(--line-lit); background:rgba(0,0,0,.86);
  padding:13px 40px;
  font-size:11px; letter-spacing:.2em; text-transform:uppercase; color:var(--stone);
  pointer-events:none;
}
@media(max-width:800px){ .coda-note{padding:13px 20px} }
/* On a phone the caption changes ends, and for a measured reason. The footer is
   268px tall there — 40% of the screen — so at the last stop it lifts a
   bottom-stuck caption to 352px, and the resolved wordmark's ink runs to 371.
   There is no arrangement of a bottom band that clears that. Above the plate
   there is room: the mark's ink starts at 213 and the wordmark's at 293, and
   the bar is 38px deep. So the caption sits under the bar instead, and the
   hairline moves to the edge it is now the boundary of. */
@media(max-width:900px){
  #coda{justify-content:flex-start}
  .coda-note{
    bottom:auto; top:38px;
    border-top:0; border-bottom:1px solid var(--line-lit);
  }
}
.coda-note b{color:var(--signal); font-weight:400}
/* Three captions, all of them markup. The render loop moves `data-stop` and
   this decides which one is on screen — so the accent in each survives, which
   it did not when the loop wrote the line itself. */
.coda-note .cap{display:none}
.coda-note[data-stop="0"] .cap[data-stop="0"],
.coda-note[data-stop="1"] .cap[data-stop="1"],
.coda-note[data-stop="2"] .cap[data-stop="2"]{display:block}

footer{border-top:1px solid var(--line-lit); background:rgba(0,0,0,.86)}
.foot2{padding:20px 18px 34px; font-size:11px; letter-spacing:.08em; color:var(--stone);
       display:flex; gap:24px; flex-wrap:wrap; border-top:1px solid var(--line)}

.cursor{display:inline-block;width:7px;height:14px;background:var(--signal);
  vertical-align:-2px;margin-left:3px;animation:bl 1.1s steps(1) infinite}
@keyframes bl{50%{opacity:0}}

@media(prefers-reduced-motion:reduce){
  .cursor{animation:none}
  .scrim{transition:none}
  html{scroll-behavior:auto}
}
