/* ─────────────────────────────────────────────────────────────
   ERAWAN — ลงรักประดับมุก
   Ground: black lacquer. Mother-of-pearl is line and mark. Thai-tea
   orange is the one thing allowed to be a filled surface, and it marks
   motion only — the button you press, the thing that just went live.
   Two radials and a faint grid lift the ground off flat black.
   ───────────────────────────────────────────────────────────── */
/* Trirong sets every heading in both languages, not only Thai. It is a Thai
   family with a Latin companion drawn for it, so an English headline and its
   Thai translation are the same voice rather than two typefaces taking turns —
   which is what Newsreader-for-English did, and it showed on /th/. */
:root{
  --serif:Trirong,Georgia,serif;
  --sans:Karla,system-ui,sans-serif;
  --mono:'IBM Plex Mono',monospace;
}
/* ประดับมุก — mother-of-pearl inlaid in black lacquer. Shell is the only
   material here that shifts colour with the angle, so the inlay rules do too. */
body{
  --lacquer:#0B0E0D;   --lacquer-deep:#070908;  --panel:#111716;  --lift:#16201E;
  --gold:#94CFC1;      --gold-lit:#D9EEE8;      --gold-dim:#41665E;
  --tea:#F2A65A;       --tea-lit:#FFD19C;
  --ivory:#EEF5F2;     --text:#91A19C;          --quiet:#65736F;   --rule:#24302D;
  /* ── glass (2026-08-22, the Harness-demo look) ──
     The demo panel is no longer an opaque surface but a pane: nacre-tinted
     translucency over whatever the page put behind it, a bright hairline
     rim, and colour arriving as glow rather than fill. Dark values here;
     the light study re-declares them in site-light.css. */
  --glass:rgb(148 207 193 / .05);   --glass-2:rgb(148 207 193 / .035);
  --glass-edge:rgb(148 207 193 / .17);

  /* ── the support chat, themed by the page it sits on (2026-09-07) ──
     Ordo's widget reads these ten off whatever it inherits from, so they are
     declared here beside the palette rather than mirrored into a second list:
     the shadow host is a child of `body`, custom properties inherit through a
     shadow boundary, and `all:initial` on that host excludes them. One
     declaration therefore serves both themes — `var(--tea)` resolves at use
     time, so the panel re-themes when somebody presses the light/dark toggle
     with no JavaScript on either side.

     `--ordo-accent-text` is the exception and is a literal. Every other token
     points at one of ours, but the text sitting *on* the accent cannot: on
     paper `--lacquer` is #F5F2ED, which is **1.96:1** on tea and unreadable —
     which is exactly why `html[data-theme="light"] .btn` overrides its colour
     twenty lines from here. The accent is orange in both themes and orange
     always wants dark type, so this one is fixed at the dark lacquer: 9.6:1
     against the dark accent and 7.5:1 against the light one.

     The font is written out rather than `var(--sans)` because Karla has no
     Thai glyphs. Latin gets Karla, Thai falls through to whatever the reader
     has, which is what the widget did before this. */
  --ordo-accent:      var(--tea);
  --ordo-accent-text: #0B0E0D;
  --ordo-surface:     var(--panel);
  --ordo-text:        var(--ivory);
  --ordo-rule:        var(--rule);
  --ordo-font:        Karla, "Noto Sans Thai", system-ui, sans-serif;
  --ordo-radius:      var(--r-surface);
  /* The mark in the launcher and the panel header. The widget paints a box
     masked by this file and filled with `currentColor`, so what travels is a
     URL and not our geometry — Ordo never holds a copy to drift from, which is
     the same reason `social-network/render.mjs` compares path data instead of
     trusting one. `icon.svg` is the published mark already: same four paths as
     `design/erawan-mark.svg`, differing only in a literal fill, and a mask
     reads alpha, so the fill is irrelevant here. Reusing it means the favicon
     and the chat mark cannot disagree.
     **Absolute, alone among our asset URLs.** Everything else in this
     directory is relative so a page renders off disk (see CLAUDE.md); this one
     is consumed inside the widget's shadow root, whose base URL is not this
     stylesheet's, and the widget only exists when its remote script has
     loaded — which never happens under file://. */
  --ordo-mark:        url("https://erawan.cloud/icon.svg");
  --ordo-chat:        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M12 3C6.9 3 3 6.4 3 10.6c0 2.4 1.3 4.6 3.4 6l-.9 3.2c-.1.4.3.7.6.5l3.6-2c.7.1 1.5.2 2.3.2 5.1 0 9-3.4 9-7.6S17.1 3 12 3Z'/%3E%3C/svg%3E");
}

/* ── the support chat's launcher: our mark, a chat glyph, no words ──
   (2026-09-07) Ordo puts `part` on the launcher and on its three children, so
   the composition is ours to make while the behaviour stays theirs. This is
   the only place we reach inside the widget, and it reaches exactly as far as
   the parts allow: `::part()` cannot be descended past, which was measured
   rather than assumed — `::part(launcher) span` does nothing.

   **The label is clipped, never `display:none`.** The button carries no
   `aria-label`; its accessible name is that text and nothing else, so removing
   it from the box leaves a screen reader announcing "button" with no idea what
   it opens. Clipped, it takes no space (the pill measures 82×44 either way)
   and stays in the accessibility tree.

   **44px stays 44px.** The obvious padding for a two-icon pill gives 38, which
   is under every tap-target floor and smaller than what it replaced.

   The pill keeps `--ordo-accent`. It is one of the two tea-orange things this
   screen is allowed and, by the same rule, one of the two it must have — a
   white launcher would take one away, which is a palette decision and not a
   side effect of a layout change. */
[data-ordo]::part(launcher){ border-radius:999px; padding:12px; gap:0 }
/* `display:none` is right here and wrong two rules down: the mark is an
   empty span with a masked background — no text, nothing in the
   accessibility tree to lose. The label is the button's whole accessible
   name, which is why that one is clipped instead. */
[data-ordo]::part(mark){ display:none }
[data-ordo]::part(label){
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap;
}
/* A speech bubble, masked like the mark so it takes `currentColor` and needs
   no second colour token. Inline because it is nine words of path data and a
   file would be one more thing to keep in step with the stylesheet. */
[data-ordo]::part(launcher)::after{
  content:""; width:20px; height:20px; flex:0 0 auto;
  background-color:currentColor;
  -webkit-mask:var(--ordo-chat) center/contain no-repeat;
          mask:var(--ordo-chat) center/contain no-repeat;
}
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;overflow-x:clip}
body{margin:0;background:var(--lacquer);color:var(--text);font-family:var(--sans);
     font-size:16px;line-height:1.7;overflow-x:hidden;
     transition:background-color .3s,color .3s}
/* Fixed rather than scrolled: the ground is the room the page sits in, and a
   gradient that scrolls away leaves the lower half looking like a different
   site. Both layers are behind everything and take no pointer events. */
body::before{content:"";position:fixed;z-index:-2;inset:0;background:
  radial-gradient(70% 58% at 88% 5%,rgb(41 98 84 / .28),transparent 72%),
  radial-gradient(45% 42% at 5% 37%,rgb(242 166 90 / .08),transparent 74%),
  linear-gradient(180deg,#101815 0,var(--lacquer) 28rem,var(--lacquer-deep) 100%)}
body::after{content:"";position:fixed;z-index:-1;inset:0;pointer-events:none;opacity:.28;
  background-image:linear-gradient(rgb(148 207 193 / .035) 1px,transparent 1px),
                   linear-gradient(90deg,rgb(148 207 193 / .035) 1px,transparent 1px);
  background-size:44px 44px;mask-image:linear-gradient(to bottom,black,transparent 64%)}
::selection{background:var(--tea);color:var(--lacquer)}
svg{max-width:100%}
button{font:inherit;cursor:pointer}
a{color:inherit}
:focus-visible{outline:2px solid var(--tea-lit);outline-offset:4px}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}

.wrap{max-width:1120px;margin:0 auto;padding:0 24px}
.narrow{max-width:720px;margin-left:auto;margin-right:auto}

/* type */
h1,h2,h3{color:var(--ivory);font-family:var(--serif);font-weight:300;margin:0}
/* `balance` matters more here than it usually does: Trirong's Latin runs wider
   than Newsreader's, so a headline that used to sit on two lines now finds a
   third and leaves one word on it. */
h1{font-size:clamp(34px,5.6vw,62px);line-height:1.14;letter-spacing:-.015em;text-wrap:balance}
h2{font-size:clamp(26px,3.6vw,38px);line-height:1.22;letter-spacing:-.01em;text-wrap:balance}
h3{font-size:20px;line-height:1.35;color:var(--gold-lit)}
p{margin:0}
/* Tea as *text* rather than as a fill: the emphasised half of a headline is
   the half that names what Erawan does, and it should carry the brand's one
   warm colour. Full tea rather than the light tint: at a heading's weight —
   300 in Latin, and Thai's thin strokes thinner still — the tint washed out to
   cream and read as a slightly different white. 9.6:1 on lacquer. */
em{font-style:italic;color:var(--tea)}

.label{font-family:var(--mono);font-size:11.5px;letter-spacing:.22em;text-transform:uppercase;
       color:var(--gold);margin:0}

/* ประดับมุก — the inlay shifts green→blue→rose the way shell does.
   Only this finish gets it; the others are flat leaf and pigment. */
/* inlay rule — stops short of the edge, the way shell inlay does */
.inlay,
section.ruled::before{height:1px;margin:0;
  background:linear-gradient(90deg,transparent,#41665E 10%,#94CFC1 32%,
             #7FA9C8 52%,#B69BB0 72%,#41665E 90%,transparent)}
/* A separator belongs to the section it announces, not to the gap before it.
   As its own element it stays on the page when that section is hidden — which
   is what two rails waiting on a fetch did: two rules under nothing. As
   ::before it is drawn only when its owner is, so the page has exactly as many
   separators as it has sections to separate. Full width, edge to edge: it used
   to match `.wrap`'s content box, which put it out of step with the one other
   divider on the page — the `.do` band's own border-block, which has always run
   the whole way. The gradient fades to transparent at both ends, so it still
   reads as inlay rather than as a hard line drawn across the screen. */
section.ruled{position:relative}
section.ruled::before{content:"";position:absolute;top:0;left:0;width:100%}
.drop{width:1px;height:56px;margin:0 auto;
  background:linear-gradient(#94cfc100,#94CFC1 60%,#B69BB0)}
.hero-figure{position:relative;width:fit-content;margin:0 auto 38px}
.hero-figure::before{content:"";position:absolute;inset:-22% -14%;border-radius:50%;
  background:radial-gradient(58% 58% at 42% 34%,#94CFC133,#7FA9C826 42%,#B69BB01f 62%,transparent 74%);
  filter:blur(18px)}


.lockup{display:inline-flex;align-items:center;gap:.45em;font-family:var(--serif);
        font-size:19px;color:var(--ivory);text-decoration:none;letter-spacing:.01em}
/* Sized in em off the name beside it, so the mark and the word stay one lockup
   at whatever size the lockup is set to. 1.8 rather than 1.35: the mark is a
   silhouette with an ear sweep and legs cut out of it, and below ~30px those
   cuts close up and it reads as a blob. */
.logo{width:1.8em;height:1.8em;flex:0 0 auto;color:var(--gold);
      transform:translateY(-.11em)}

/* buttons */
/* The one filled surface on the page. Tea marks the thing you press; every
   other accent stays a line, so a single orange rectangle is unmissable. */
.btn{display:inline-block;border:1px solid var(--tea);background:var(--tea);color:var(--lacquer);
     text-decoration:none;padding:14px 38px;font-weight:700;font-size:15px;
     letter-spacing:.03em;border-radius:var(--r-control);
     transition:transform .18s,background .18s,border-color .18s,color .18s}
.btn:hover{background:var(--tea-lit);border-color:var(--tea-lit);transform:translateY(-2px)}
.btn-quiet{background:none;border-color:var(--rule);color:var(--ivory);font-weight:500}
.btn-quiet:hover{background:rgb(148 207 193 / .06);border-color:var(--gold-dim);
                 color:var(--gold-lit);transform:none}

/* header */
/* Room on both sides. It had none underneath at all, so the nav sat on the
   hero rather than above it — on a wide screen that reads as one block. */
/* The category, as a heading over the list of agents it is about. Ivory like
   every other heading: the h1's `em` is the one orange thing on this screen,
   and a second would start being a colour scheme rather than an accent
   (Decisions §10).

   It began beside the wordmark at label size, where a reader scanning a header
   skips it — the whole reason it exists is that people could not tell what the
   product was, and a line nobody reads cannot fix that. It replaces the
   "Works with" label that used to sit here: two stacked headings for one list
   was one more than the list needed, and this one says what that one said. */
.works-tag{display:inline-flex;align-items:center;justify-content:center;
  margin:0 auto 24px;padding:6px 18px;
  background:var(--glass-2);border:1px solid var(--glass-edge);
  border-radius:999px;
  font-family:var(--sans);font-size:12.5px;font-weight:500;
  letter-spacing:.03em;color:var(--gold-lit);
  box-shadow:0 1px 2px rgb(0 0 0 / .12)}
body[data-lang="th"] .works-tag{font-family:Trirong,serif;font-size:14px;letter-spacing:0}

.top{display:flex;align-items:center;justify-content:space-between;gap:16px;
     padding:30px 0 26px;flex-wrap:wrap}
/* margin-left:auto rather than relying on space-between: the bar has four
   children now, and the links, the language toggle and the button belong
   together at the right end with the mark alone on the left. */
.top nav{display:flex;flex-wrap:wrap;gap:12px 22px;align-items:center;font-size:14.5px;
         margin-left:auto}
.top nav a{text-decoration:none;color:var(--text)}
.top nav a:hover{color:var(--gold-lit)}
/* ── language & theme toggles ──
   Twin control buttons sharing exact dimensions, typography, borders and motion. */
.lang,.theme{display:inline-flex;align-items:center;justify-content:center;
  width:36px;height:30px;background:var(--glass-2);border:1px solid var(--rule);
  border-radius:var(--r-control);color:var(--text);padding:0;
  font-family:var(--mono);font-size:12px;font-weight:600;
  box-shadow:0 1px 3px rgb(0 0 0 / .04);
  transition:color .18s,border-color .18s,background .18s;cursor:pointer}
.lang:hover,.theme:hover{border-color:var(--gold);color:var(--gold-lit);background:var(--lift)}
.theme svg{width:16px;height:16px;fill:none;stroke:currentColor;
  stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.icon-sun{display:none}
html[data-theme="dark"] .icon-sun{display:block;color:var(--gold-lit)}
html[data-theme="dark"] .icon-moon{display:none}

/* the menu button — a phone control, absent from the markup's flow above 640px */
.burger{display:none;align-items:center;justify-content:center;width:40px;height:34px;
        background:none;border:1px solid var(--rule);border-radius:var(--r-control);color:var(--text);
        padding:0;cursor:pointer}
.burger:hover{border-color:var(--gold-dim);color:var(--gold-lit)}
.burger .bars{position:relative;display:block;width:17px;height:1px;background:currentColor}
.burger .bars::before,.burger .bars::after{content:"";position:absolute;left:0;
        width:17px;height:1px;background:currentColor;transition:transform .18s}
.burger .bars::before{top:-5px}
.burger .bars::after{top:5px}
/* Open: the middle bar goes, the outer two cross. The state lives on .top so
   the same attribute drives the button and the panel. */
.top[data-open="true"] .bars{background:transparent}
.top[data-open="true"] .bars::before{transform:translateY(5px) rotate(45deg)}
.top[data-open="true"] .bars::after{transform:translateY(-5px) rotate(-45deg)}

/* Screen-reader-only, and it must stay 1px rather than display:none — a hidden
   element is not announced, so the button would be read as unlabelled. */
.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
         clip-path:inset(50%);white-space:nowrap;border:0}

/* Below this the four links do not fit beside the mark and the toggle, so they
   fold into the button. The toggle stays on the bar; only the links move.
   Everything here hangs off .has-menu, which site.js adds. With no JavaScript
   the class never arrives, the links are never hidden, and the bar keeps the
   wrapping row it had — rather than a menu nothing can open. */
@media(max-width:640px){
  .has-menu .burger{display:inline-flex;order:2}
  .has-menu .lang{order:1;margin-left:auto}
  .has-menu .theme{order:1}
  .has-menu .top nav{order:3;flex-basis:100%;margin:12px 0 0;padding-top:2px;
           display:none;flex-direction:column;align-items:stretch;gap:0;
           border-top:1px solid var(--rule)}
  .has-menu .top[data-open="true"] nav{display:flex}
  .has-menu .top nav a{padding:13px 2px;font-size:16px}
  .has-menu .top nav a+a{border-top:1px solid var(--rule)}
}

/* hero */
.hero{text-align:center;padding:40px 0 78px}
.hero .drop{margin-bottom:26px;height:44px}
.hero-glyph{width:min(248px,50vw);height:auto;color:var(--gold);margin:0;display:block;position:relative;
            filter:drop-shadow(0 18px 40px rgb(0 0 0 / .52));
            animation:settle 1.2s cubic-bezier(.22,1,.32,1) both}
@keyframes settle{from{opacity:0;transform:translateY(20px) scale(.965)}}
.hero h1{max-width:20ch;margin:0 auto 22px}
.hero-sub{font-size:17.5px;line-height:1.78;max-width:50ch;margin:0 auto 36px}
.hero-paste{max-width:34rem;margin:0 auto 22px}
.hero-paste-label{font-size:13.5px;color:var(--quiet);margin-bottom:9px}
.hero-paste .paste{justify-content:center;white-space:normal}
.hero-actions{display:flex;gap:12px;justify-content:center;flex-wrap:wrap}
.hero-fine{margin-top:20px;font-size:13.5px;color:var(--quiet)}

/* triad */
.triad{display:grid;grid-template-columns:1fr;border-top:1px solid var(--rule)}
/* Side by side, the three cells are rows of one grid: a heading that wraps to
   two lines in one of them must not start its neighbours' text on a different
   line. The grid has no row gap, so the heading's own margin still sets the
   space under it. */
@media(min-width:760px){
  .triad{grid-template-columns:repeat(3,1fr)}
  .triad div{display:grid;grid-template-rows:subgrid;grid-row:span 2}
}
.triad div{padding:28px 24px;border-bottom:1px solid var(--rule)}
@media(min-width:760px){.triad div+div{border-left:1px solid var(--rule)}}
.triad h3{margin-bottom:9px}
.triad p{font-size:15px;line-height:1.72}

/* generic section */
section{padding:76px 0}
/* ── the page speaks from the centre (2026-08-22) ──
   The landing's own hero, the demo and the connect band always did; the
   sections below them were laid out from the left, which read as a second
   page under the first. What keeps its own left edge is what a reader
   scans rather than reads: a command in a card, a code line, the chat's
   bubbles — centring a terminal line is how a page becomes unreadable. */
.sec-head{text-align:center;margin-left:auto;margin-right:auto;margin-bottom:38px}
.sec-head h2{text-align:center}
.sec-head p{margin-left:auto;margin-right:auto;text-align:center}
/* Not `.uses li` any more (2026-08-29). A card here holds four things — a
   mark and a title, a description, the sentence somebody says, and where it is
   written down — and centring made every one of them start at a different x.
   The block above says what keeps its left edge is what a reader scans rather
   than reads; a card with four rows is scanned. The section's own heading
   stays centred. */
.triad div{text-align:center}
.plan{text-align:center}
.foot{justify-content:center;text-align:center;align-items:center}
.foot-brand{justify-content:center}
.foot nav{justify-content:center}
/* Scanned, not read — these keep their left edge at every width. */
.do-pair,.do-pair .paste{text-align:left}
.sec-head .label{margin-bottom:14px}
.sec-head p{margin-top:14px;font-size:16.5px;line-height:1.78;max-width:54ch}
/* ── phone: the page speaks from the middle ──
   One column, so there is no second column for a left edge to line up with,
   and a heading pinned left above a centred band (the paste box, the footer)
   reads as two different pages stacked. What stays left is anything a reader
   scans rather than reads: the cards' own lists, the chat, code to copy —
   centring a list of features is how a phone page becomes unreadable. */
@media(max-width:640px){
  .sec-head,.hero-copy,.gift .wrap,.ways-foot{text-align:center}
  .sec-head p,.hero-sub{margin-left:auto;margin-right:auto}
  .works-rail{margin-left:auto;margin-right:auto}
  /* Bubbles keep their own sides and their text keeps its own edge. */
  .said,.way{text-align:left}
}

/* the loop / transcript */
.loop{display:grid;gap:34px;grid-template-columns:1fr;align-items:center}
@media(min-width:900px){.loop{grid-template-columns:.95fr 1.05fr;gap:52px}}
/* A conversation, drawn as one. It was a two-column table — a speaker column,
   then the words, ruled between rows — which reads as a transcript of a chat
   rather than as a chat. Everything a person recognises without being told is
   in the shape: sides, bubbles, and a tail corner on the side each one came
   from. Nothing here is a new colour; the two grounds are the nacre and plain
   white at the opacities already used for surfaces. */
/* Tone, not a frame. The square panel it had — a 1px rule and a flat ground —
   read as a widget dropped onto the page beside a left column of bare type.
   What separates it now is the palette's own ladder, three steps and no line
   anywhere: page --lacquer, panel --panel, bubble --lift. The panel fades out
   downward so it has no bottom edge to be square about, and the only light on
   it is a one-pixel inset highlight along the top, the way a lacquer surface
   catches it. */
.script{border:0;padding:18px;border-radius:var(--r-surface);
  background:linear-gradient(155deg,#1B2925,#0C1210);
  box-shadow:inset 0 1px 0 rgb(255 255 255 / .05);
  display:flex;flex-direction:column;gap:9px}
.line{display:flex;flex-direction:column;gap:2px;max-width:84%;padding:0;border:0}
.line[data-who="you"]{align-self:flex-end;align-items:flex-end}
.line[data-who="ai"]{align-self:flex-start;align-items:flex-start}
/* The speaker labels are off the screen, not out of the page. Which side a
   bubble sits on says who is talking, and it says it faster than a word does —
   but only to somebody looking at it. Read aloud, the same five sentences with
   no attribution are one person talking to themselves, so the words stay for a
   screen reader and are taken out of the picture. */
.who{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
     clip-path:inset(50%);white-space:nowrap;border:0}
/* Tighter leading than the page's, and tighter than Thai body text: a message
   is one or two lines inside a bubble, not a paragraph, and at 1.72 the five
   of them stood three times taller than the headline they sit beside. */
.said{font-size:14.5px;line-height:1.5;color:var(--ivory);
      padding:8px 13px;border-radius:var(--r-bubble);border:1px solid var(--rule)}
.said.dim{color:var(--text)}
/* The square corner is the tail: bottom-right on the side that sends, and
   bottom-left on the side that answers. */
/* The third step of the ladder. The answering side is --lift with no border at
   all — a shade above the panel is enough to lift it, and a line around it
   would put back the drawn edges this is getting rid of. The asking side is the
   nacre over that same step, which is what makes it read as the warmer of the
   two without being a different hue from anything else on the page. */
.line[data-who="you"] .said{background:rgb(148 207 193 / .1);border-color:#375B54;
  border-bottom-right-radius:3px}
.line[data-who="ai"] .said{background:var(--lift);border-color:transparent;
  border-bottom-left-radius:3px}
.tick{color:var(--gold);margin-right:7px}
/* The live link is the moment the product delivers, so it is tea like the
   button — the two orange things on the page are "press" and "it worked". */
.url{display:inline-block;margin-top:4px;font-family:var(--mono);font-size:13.5px;
     color:var(--tea-lit);border-bottom:1px solid #76512D;text-decoration:none}
.steps{list-style:none;margin:0;padding:0;counter-reset:s}
.steps li{counter-increment:s;padding:16px 0 16px 46px;position:relative;
          border-bottom:1px solid var(--rule);font-size:15.5px;line-height:1.7}
.steps li:last-child{border-bottom:0}
.steps li::before{content:counter(s,upper-roman);position:absolute;left:0;top:16px;
                  font-family:var(--serif);font-size:15px;color:var(--gold);letter-spacing:.06em}
.steps b{color:var(--ivory);font-weight:400;font-family:var(--serif);font-size:17px;
         display:block;margin-bottom:3px}

/* price */
.price{display:grid;gap:1px;background:var(--rule);border:1px solid var(--rule);margin-top:34px;
       grid-template-columns:1fr}
@media(min-width:760px){.price{grid-template-columns:1fr 1fr}}
.price > div{background:var(--lacquer);padding:30px 26px}
.price .label{margin-bottom:16px}
.price .amt{font-family:var(--serif);font-size:38px;line-height:1;color:var(--ivory);
            font-weight:200;margin-bottom:6px;letter-spacing:-.02em}
.price .amt small{font-size:.42em;color:var(--text);letter-spacing:0}
.price h3{font-size:17px;color:var(--gold-lit);margin-bottom:10px}
.price p{font-size:14.5px;line-height:1.72}

/* the pasteable line, used by all three tabs of the hero band */
.paste{display:flex;gap:10px;align-items:center;background:var(--lacquer-deep);
       border:1px solid var(--rule);border-radius:var(--r-surface);padding:12px 14px;font-family:var(--mono);
       font-size:12.5px;color:var(--gold-lit);overflow-x:auto;white-space:nowrap}
.paste button{margin-left:auto;flex:0 0 auto;background:none;border:1px solid var(--rule);
              color:var(--text);padding:5px 11px;font-family:var(--mono);font-size:11px;border-radius:var(--r-control);
              transition:background .18s,border-color .18s,color .18s}
.paste button:hover{border-color:var(--gold-dim);color:var(--gold-lit)}
/* Several lines in one answer (install, then sign in) need to be told apart. */

/* absence */
.never{list-style:none;margin:0;padding:0;columns:2;column-gap:34px;max-width:640px}
@media(max-width:560px){.never{columns:1}}
.never li{padding:10px 0 10px 24px;position:relative;font-size:16px;
          break-inside:avoid;color:var(--text)}
.never li::before{content:"";position:absolute;left:2px;top:19px;width:9px;height:1px;
                  background:var(--gold-dim)}

/* footer */
footer{border-top:1px solid var(--rule);padding:34px 0 46px;margin-top:20px}
/* Two columns: the mark and what it says on the left, the links on the right.
   Top-aligned rather than centred — the brand column is two lines tall and the
   nav one, so centring floats the links against nothing. */
.foot{display:flex;justify-content:space-between;gap:20px 40px;flex-wrap:wrap;align-items:flex-start;
      font-size:13.5px;color:var(--quiet)}
/* One row: the mark, the name, and the sentence the name starts. It wraps only
   where there is genuinely no room for it, which is under ~330px.
   Centred, not baseline: the lockup is itself a flex box whose first item is
   the SVG, so the baseline it offers the row is the mark's bottom edge rather
   than the name's baseline — which dropped the sentence 6px below the name it
   continues. Aligning the two boxes is the fix; the lockup's own centring of
   mark against name is left alone. */
.foot-brand{display:flex;flex-wrap:wrap;align-items:center;gap:4px 10px}
/* Seven links: one row beside the brand on a wide screen, several rows under it
   on a phone. Left-aligned rather than right, which is invisible while the row
   is content-sized and is what makes the wrapped rows line up with the mark
   above them instead of ragging against the far edge. */
.foot nav{display:flex;flex-wrap:wrap;gap:10px 20px}
.foot a{text-decoration:none}
.foot a:hover{color:var(--gold-lit)}
/* On a phone the two columns are stacked, and a stack of three ragged-left
   blocks under a centred-nothing page end reads as a column that lost its
   second half. Centred, it reads as the end of the page. Same 640 as the menu
   button — one breakpoint on this page, not two that nearly agree. */
@media(max-width:640px){
  .foot{justify-content:center;text-align:center}
  .foot-brand{justify-content:center}
  .foot nav{justify-content:center}
}

/* ── Thai: body text also moves to Trirong, released leading ──
   Headings need no rule here: --serif is Trirong in both languages now. What
   is left is the Thai script's own needs — taller line boxes for the vowel and
   tone marks that sit above and below, and no negative tracking. */
body[data-lang="th"]{font-family:Trirong,serif;line-height:1.9}
/* **`text-wrap:balance` is turned off for Thai, and it is not a preference.**
   Balance evens the line lengths by choosing its own break points, and Thai has
   no spaces for it to choose from — so it breaks inside a word, and worse,
   inside a cluster: measured on academy.erawan.cloud on 2026-09-18 it split
   `ทำเอเจนต์` as `ทำเอเจน` / `ต์`, stranding the thanthakhat on the next line.
   Left to wrap, Chrome uses its Thai dictionary and breaks at `ให้` / `แล้ว`,
   which is a real word boundary. (`word-break:keep-all` was measured too and
   changes nothing for Thai, while risking overflow on a narrow screen.) */
body[data-lang="th"] h1{font-weight:300;font-size:clamp(28px,4.8vw,52px);line-height:1.5;letter-spacing:0;
  text-wrap:wrap}
body[data-lang="th"] h2{font-weight:400;line-height:1.48;letter-spacing:0;text-wrap:wrap}
body[data-lang="th"] h3{font-weight:500;letter-spacing:0}
body[data-lang="th"] p,body[data-lang="th"] li{line-height:1.92}
body[data-lang="th"] em{font-style:normal;font-weight:600}
body[data-lang="th"] .label{font-family:Trirong,serif;letter-spacing:.06em;
                            text-transform:none;font-size:13.5px;font-weight:500}
/* No Thai rule for .who any more: it is not drawn in either language. */
/* Thai body leading is 1.92 for paragraphs; a bubble is not one, and the marks
   above and below the line still need more room than Latin does. */
body[data-lang="th"] .said{line-height:1.7}

/* Generated pages: prose, on the same ground as the landing. */
.doc{max-width:46rem;margin:0 auto;padding:clamp(3rem,7vw,5.5rem) 1.25rem 4rem}
.doc h1{font-size:clamp(1.9rem,4vw,2.6rem);line-height:1.15;margin:0 0 .6rem}
.doc .updated{font-family:var(--mono,monospace);font-size:.78rem;opacity:.55;letter-spacing:.02em}
.doc .intro{font-size:1.06rem;line-height:1.75;margin:1.6rem 0 0;opacity:.86}
.doc h2{font-size:1.12rem;line-height:1.4;margin:2.6rem 0 .7rem;letter-spacing:.01em}
.doc p{line-height:1.8;margin:0 0 1rem;opacity:.82}
.doc ul{margin:0 0 1rem;padding:0;list-style:none}
.doc li{position:relative;padding-left:1.15rem;line-height:1.8;margin-bottom:.6rem;opacity:.82}
.doc li::before{content:"";position:absolute;left:0;top:.85em;width:.45rem;height:1px;background:currentColor;opacity:.5}
.doc .cards{display:grid;gap:0;margin:2rem 0 0}
.doc .cards div{padding:1.1rem 0;border-top:1px solid rgba(255,255,255,.1)}
.doc .cards dt{font-weight:500;margin:0 0 .35rem}
.doc .cards dd{margin:0;opacity:.75;line-height:1.7}
.doc table{width:100%;border-collapse:collapse;margin:1.5rem 0;font-size:.85rem}
.doc td{padding:.55rem .6rem;border-bottom:1px solid rgba(255,255,255,.08);opacity:.8;vertical-align:top}
.doc td:first-child{font-family:var(--mono,monospace);opacity:.55;white-space:nowrap}
.doc .back{display:inline-flex;gap:.5rem;align-items:center;margin-bottom:2.5rem;font-size:.9rem;opacity:.7}
.doc .langs{float:right;font-size:.85rem;opacity:.7}
/* Side by side, a pair is two columns of one grid: `subgrid` over the term
   row and the definition row, so a term that wraps to two lines in one
   language does not start its neighbour's definition on a different line. */
@media (min-width:640px){
  .doc .cards{grid-template-columns:1fr 1fr;column-gap:2.5rem}
  .doc .cards div{display:grid;grid-template-rows:subgrid;grid-row:span 2}
}

/* ══ index1 ══════════════════════════════════════════════════════════════ */

/* ── hero: one focused conversation at the centre ──────────────────────
   The landing is asking someone to watch an agent work. A side-by-side proof
   made the chat look like supporting evidence; centred below the claim, it is
   the task itself and gets an unbroken reading measure. */
.hero{display:flex;flex-direction:column;align-items:center;gap:34px;text-align:center;padding:58px 0 44px}
/* A grid item's automatic minimum is its min-content, and the works rail holds
   two full copies of the mark list side by side — about 940px of them. Without
   this the column inherited that floor and the single-column layout below
   980px became an 850px column inside a 342px phone: the sub-headline was cut
   off mid-word and half the conversation sat outside the screen, hidden only
   by `overflow-x:hidden` on the body. */
.hero-copy{width:min(100%,720px);min-width:0}
.hero .drop{display:none}
/* Pulled up by its own half-leading. Both columns start on the same grid line,
   but the panel's top is an edge and the headline's is the top of a line box —
   so the letters began 5-8px below it, measured off the rendered pixels rather
   than off the boxes, which agree. In em so it holds at every clamp step. */
/* 19ch, not 16. "Your AI builds the site." is twenty-four characters and at
   sixteen it broke into four lines where the old "Your AI builds it." took
   two. The measure exists to stop a headline running the full width of a
   desktop, and three more characters do not undo that — Thai has its own
   measure below and is unaffected either way. */
.hero h1{font-size:clamp(34px,3.9vw,54px);max-width:19ch;margin:-.13em auto 22px;
  animation:arrive .8s both}
.hero-sub{font-size:17px;line-height:1.76;max-width:44ch;margin:0 auto 30px;
  animation:arrive .8s .14s both}
/* Thai has no spaces to break at and its glyphs run wider, so the Latin
   measure puts this headline on three lines where English takes two. */
body[data-lang="th"] .hero h1{font-size:clamp(30px,3.5vw,48px);line-height:1.4;max-width:22ch}
/* The conversation, moved up from #loop — the whole product in four lines,
   and it was sitting at 1,400px. On the right it is what the headline's claim
   is being checked against, which is why the two columns are centred on each
   other rather than aligned at the top. */
/* The first line of the conversation is character-for-character what the box
   on the left holds. Same face, so the eye connects the thing being copied to
   the thing that made it happen; not tea, because the two orange marks on this
   screen are already spoken for — the copy button and the live URL. */
.said .go{font-family:var(--mono);font-size:.88em;color:var(--gold)}

/* ── works with: the agents this already answers ──
   A claim, not a picker. The seven-tile picker that used to be at #connect
   asked a reader to choose their tool before anything had happened; this asks
   nothing and answers the only question the headline raises — "does it work
   with the one I have".
   Marks are <img> rather than a CSS mask on purpose: a mask cannot load its
   SVG from file://, and every asset path on this page is relative precisely so
   the page renders when opened off disk. Grey and dimmed so eight other
   people's brands do not outshout ours, true colour on hover — which also
   avoids tinting somebody's trademark a colour they did not choose. */
.works{margin:44px auto 0;width:min(100%,860px)}
.works-rail{display:flex;justify-content:center;width:100%;margin:0 auto}
.works-track{display:flex;flex-wrap:wrap;justify-content:center;align-items:center;
  gap:12px 22px;list-style:none;margin:0;padding:0}
.works-track li{display:inline-flex;align-items:center;gap:7px;
  cursor:default;transition:transform .18s}
.works-track li span{white-space:nowrap;font-family:var(--sans);font-size:12.5px;font-weight:500;
  color:var(--quiet);white-space:nowrap;transition:color .18s}
body[data-lang="th"] .works-track li span{font-family:Trirong,serif;font-size:13.5px}
.works-track li:hover span{color:var(--gold-lit)}
.works-track img{width:18px;height:18px;object-fit:contain;display:block;
  filter:brightness(0) invert(1);opacity:.55;
  transition:opacity .18s,filter .18s,transform .18s}
.works-track li:hover img{filter:none;opacity:1;transform:scale(1.15)}
.hero-script{margin:0;animation:arrive .8s .22s both}
@keyframes arrive{from{opacity:0;transform:translateY(18px)}}
/* The messages land one after another, the way they would if somebody were
   typing them. Fixed delays rather than a loop: there are five, they never
   change, and this costs no JavaScript. The reduced-motion rule at the top of
   this file turns the whole thing off. */
.hero-script .line{animation:arrive .5s both}
.hero-script .line:nth-child(1){animation-delay:.45s}
.hero-script .line:nth-child(2){animation-delay:.85s}
.hero-script .line:nth-child(3){animation-delay:1.25s}
.hero-script .line:nth-child(4){animation-delay:1.65s}
.hero-script .line:nth-child(5){animation-delay:2.05s}

/* ── the one thing to do ──
   Out of the hero and into a band that runs edge to edge, because inside the
   hero it was a third item competing with a headline and a transcript, and it
   is the only thing on this page a reader can actually act on. A rule above
   and below and a darker ground make it a stop rather than a paragraph. */
.do{border-block:1px solid var(--rule);background:rgb(7 9 8 / .62);
  padding:clamp(48px,7vw,84px) 0;text-align:center}
/* ── the band's three tabs ──
   Line and text only, never a fill. This band already spends the page's one
   filled accent on the copy button, and a tea tab beside it would be the third
   orange thing on screen — which is the point at which an accent stops being
   one (PRODUCT.md brand). Selected is ivory over a pearl rule; the rest are
   quiet text.
   The tabs replaced a paste line here and a two-card #connect section most of
   a page below it: the same question answered in two places a reader could not
   see at once. */
/* Smaller than the page's other h2s on purpose. Those head sections a reader
   is being introduced to; this one labels a control they are about to use, and
   at 38px it competed with the headline one screen above it. */
.do-title{font-size:clamp(23px,2.7vw,31px);margin:0 0 clamp(20px,2.6vw,28px)}
/* The rule under the tabs is the width of the boxes beneath it (46rem), not of
   `.wrap` — a full-width line with three words clustered in the middle of it
   read as a section divider that happened to have tabs near it. */
.do-tabs{display:flex;justify-content:center;flex-wrap:wrap;
  max-width:46rem;margin:0 auto clamp(24px,3vw,34px);
  border-bottom:1px solid var(--rule)}
.do-tabs button{background:none;border:0;border-bottom:2px solid transparent;
  margin-bottom:-1px;color:var(--quiet);font-family:var(--mono);font-size:12px;
  letter-spacing:.14em;text-transform:uppercase;padding:12px clamp(14px,2.4vw,24px);
  transition:color .18s,border-color .18s}
.do-tabs button:hover{color:var(--gold-lit)}
.do-tabs button[aria-selected="true"]{color:var(--ivory);border-bottom-color:var(--gold)}
/* Trirong at Karla's tracking is unreadable, the same substitution `.label`
   already makes. */
body[data-lang="th"] .do-tabs button{font-family:Trirong,serif;letter-spacing:.04em;
  text-transform:none;font-size:15px}
.do-panel[hidden]{display:none}
/* One per row, at every width. Side by side they were two ~360px columns, and
   neither command fits in that: `curl -fsSL https://erawan.cloud/install | sh`
   broke onto two lines and the PowerShell one onto three, so the pair of them
   was five ragged lines of what is actually two single commands. Stacked, each
   gets the full 46rem and sits on one line. The label above each is what tells
   them apart, which is the job the columns were doing badly. */
.do-pair{display:grid;gap:18px;grid-template-columns:1fr;
  max-width:46rem;margin:0 auto;text-align:left}
/* A grid item's automatic minimum is its min-content, which here is the longest
   unbreakable run — a whole URL. Without this the row is sized by that rather
   than by the column, which is how `.hero-copy` once put an 850px box inside a
   342px phone. */
.do-pair > div{min-width:0}
/* The instruction, not a caption: it is the sentence that tells someone what to
   do with the box under it, so it is sized to be read first. */
.do-label{font-size:clamp(15px,1.7vw,19px);color:var(--ivory);margin:0 0 20px;
  line-height:1.5}
/* Display size, and only on the first tab — the rules under `.do-pair` and
   `#panel-mcp` above put the other two back to the base component. This line is
   a sentence somebody says to their agent, so it is set to be read; those are
   commands being quoted, and a quoted command that has been blown up to 18px
   reads as the page shouting a URL. */
.do .paste{max-width:46rem;margin:0 auto;justify-content:center;white-space:normal;
  padding:clamp(18px,2.4vw,24px) clamp(18px,2.4vw,26px);
  font-size:clamp(14.5px,1.7vw,18px);gap:16px;border-color:var(--gold-dim)}
.do .paste span{color:var(--ivory)}
/* margin-left:0 undoes the base rule's auto, which pins the button to the far
   edge. That is right on the other two tabs, where the line is a command being
   quoted and the button is a utility beside it — and those put the auto back;
   here the line and the button are one thing in the middle of a band, and the
   auto margin left a hand's width of nothing between them. */
.do .paste button{margin-left:0;background:var(--tea);border-color:var(--tea);
  color:var(--lacquer);font-weight:700;font-size:13px;padding:11px 22px;letter-spacing:.04em}
.do .paste button:hover{background:var(--tea-lit);border-color:var(--tea-lit);color:var(--lacquer)}

/* The other two tabs put the base component back, and this block sits AFTER
   the three rules above rather than before them — it did not, for one commit,
   and lost on source order at equal specificity: the CLI tab rendered two
   tea-filled copy buttons, which is the accent used twice in one panel and a
   third time in the hero above it.
   A command is quoted, not spoken. Base mono size, its own left edge, and it
   **wraps at spaces rather than anywhere** — `overflow-wrap:anywhere` broke
   `https://erawan.cloud/install` across three lines mid-host, which is a URL
   nobody can read and the copy button exists so nobody has to. */
.do .do-pair .paste,.do #panel-mcp .paste{font-size:12.5px;justify-content:flex-start;
  padding:12px 14px;white-space:normal;overflow-x:visible;flex-wrap:wrap}
/* The button drops to its own line rather than off the end of the box. A URL
   is one unbreakable word ~240px wide, and inside a 320px screen that leaves
   the copy button nothing: it sat outside the border it belongs to, which is
   the one control in there. `min-width:0` lets the line shrink to its wrap
   point instead of holding the row open at its longest word. */
.do .do-pair .paste span,.do #panel-mcp .paste span{min-width:0;flex:1 1 auto}
.do .do-pair .paste button,.do #panel-mcp .paste button{margin-left:auto;
  background:none;border-color:var(--rule);color:var(--text);font-weight:400;
  font-size:11px;padding:5px 11px;letter-spacing:0}
.do .do-pair .paste button:hover,.do #panel-mcp .paste button:hover{background:none;
  border-color:var(--gold-dim);color:var(--gold-lit)}
#panel-mcp .paste{max-width:36rem;margin:0 auto}
/* What the tab IS, in one line, above the marks. A tab called "MCP" tells
   somebody who already knows and nobody else; this is the sentence that makes
   the word mean something before they have to decide whether it is theirs. */
.do-what{max-width:56ch;margin:0 auto;text-align:center;font-size:15px;
  line-height:1.7;color:var(--text)}
.do-what code{font-family:var(--mono);font-size:.88em;color:var(--gold-lit)}
body[data-lang="th"] .do-what{font-family:Trirong,serif;font-size:15.5px;line-height:1.85}

/* The marks say who a tab is for faster than a list of names would. The same
   files the works rail uses — self-hosted, because the privacy page says
   nothing on this site reaches a third party — at a size that reads as a row
   of badges rather than a second logo wall. */
.do-logos{display:flex;flex-wrap:wrap;justify-content:center;align-items:center;
  gap:8px 10px;margin:16px 0 20px;padding:0;list-style:none}
.do-logos li{display:inline-flex;align-items:center;gap:7px;
  padding:5px 11px;border:1px solid var(--rule);border-radius:99px;
  background:var(--panel)}
.do-logos img{width:18px;height:18px;flex:0 0 auto;opacity:.85}
.do-logos span{font-family:var(--sans);font-size:12.5px;font-weight:500;
  color:var(--text);white-space:nowrap}
body[data-lang="th"] .do-logos span{font-family:var(--sans)}
/* **Three of these marks are drawn white**, for the lacquer they were chosen
   against — openai, windsurf and github-copilot. On paper they are invisible,
   and `naturalWidth` is not zero, so nothing but looking at the page says so.
   Named one by one rather than `filter:brightness(0)` on all of them, which is
   what the works rail does and which also flattens the marks that carry their
   own colour: Claude Code's orange and Gemini's violet are how those two are
   recognised. */
html[data-theme="light"] .do-logos img[src$="openai.svg"],
html[data-theme="light"] .do-logos img[src$="windsurf.svg"],
html[data-theme="light"] .do-logos img[src$="github-copilot.svg"]{
  filter:brightness(0);opacity:.72}

/* `.do-steps` was here: the three numbered claims under the paste line — the
   agent reads the playbook, the gate checks for keys, you get a link. It went
   with the paste tab on 2026-09-06, because both halves described a flow that
   only a tool with a shell could follow. */
/* `.do-fine` was here: first a link down to #connect, then — once the tabs
   absorbed that section — a line saying an agent that can run commands picks
   its own way in. Both are gone. The first pointed at a section that no longer
   exists, and the second explained the tabs to a reader who was looking at
   them. */

/* ── what you get: the metaphor heads it, the triad answers it ── */
/* No bottom border: whichever section comes next draws the separator, and this
   band drawing one too put two rules 76px apart. */
.gift{padding:76px 0 70px;text-align:center}
.gift .hero-glyph{width:min(200px,38vw);margin:0 auto 30px}
.gift h2{max-width:20ch;margin:0 auto 14px}
.gift > .wrap > p{max-width:48ch;margin:0 auto 44px;font-size:16.5px;line-height:1.78}
/* One object, not three claims and a stray row. The hairline grid is the same
   trick the pricing table uses: a 1px gap over a rule-coloured ground, so the
   dividers are the background showing through and no cell owns a border. That
   is what lets the carried list sit inside the same frame — it is the fourth
   row of the elephant's load, and floating it underneath read as an unrelated
   list of tags. */
.load{display:grid;gap:1px;border:1px solid var(--rule);background:var(--rule);
  text-align:left}
.gift .triad{display:grid;gap:1px;grid-template-columns:1fr;background:var(--rule);
  border:0;text-align:left}
@media(min-width:760px){.gift .triad{grid-template-columns:repeat(3,1fr)}}
.gift .triad > div{background:var(--lacquer-deep);border:0;padding:32px 28px}
.gift .triad h3{margin-bottom:8px}
.gift .triad p{font-size:14.5px;line-height:1.7}

/* The list that was the `absence` section, turned the right way round. As
   "None of this is your job" it was a second sermon at the foot of the page,
   competing with the elephant that had already made the same promise; as a row
   of nouns it is the promise's evidence, in six lines instead of a screen.
   Nouns, not tasks — a reader is being told what is handled, not reminded of
   eight chores they were dreading. */
/* Centred, with the triad above it left-aligned: the three cells are read one
   at a time and want a left edge to start from, while this row is one object
   and hangs off the section's own centre line — the elephant, the headline and
   its sentence. */
.carry-row{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:16px;padding:28px 28px;background:var(--lacquer-deep);text-align:center}
.carry-label{margin:0;font-family:var(--mono);font-size:11px;
  letter-spacing:.18em;text-transform:uppercase;color:var(--gold-dim);text-align:center}
.carry{display:flex;flex-wrap:wrap;justify-content:center;gap:10px;margin:0;padding:0;list-style:none}
.carry li{padding:10px 15px;border:1px solid var(--rule);border-radius:var(--r-control);color:var(--gold-lit);
  font-size:13px;transition:border-color .14s}
.carry li:hover{border-color:var(--gold-dim)}
body[data-lang="th"] .carry-label{font-family:Trirong,serif;letter-spacing:.04em;
  text-transform:none;font-size:14px}

/* ── what else it does ──
   Six cards, one per thing people ask for after the first deploy works, each
   the short version of a `cookbooks/` entry that was walked on production.

   The command is `<code>` and not a `.paste` box, which is the whole design
   decision here: a paste box is a control, and this page has exactly one thing
   it asks a reader to press. Six copy buttons down here would make the band at
   the top the seventh, and the band is the only one that matters. Pearl, never
   tea — the accent marks the button you press and the URL that just went live. */
.uses{display:grid;gap:14px;margin:30px 0 0;padding:0;list-style:none;
  grid-template-columns:repeat(auto-fill,minmax(268px,1fr))}
/* **Four cards, four columns.** `auto-fill` gave three and left the fourth
   alone on a second row, which reads as an afterthought rather than as the
   fourth of four — and this list is a set: everything between "the code is
   finished" and "a customer can open it". Two-up before there is room for
   four, one-up on a phone. The six-card list below keeps `auto-fill`, which
   is right for a number that is not four. */
@media(min-width:1040px){.uses-4{grid-template-columns:repeat(4,1fr)}}
@media(min-width:560px) and (max-width:1039px){.uses-4{grid-template-columns:repeat(2,1fr)}}
/* **Each card is `subgrid` over the list's own rows, not a column of its
   own.** In a flex column the quotation ends up at `card bottom − link −
   its own height`, so a two-line quotation drew its box higher than a
   one-line one and the six boxes stepped up and down across the band. As
   rows of one grid, the quotation row is as tall as the tallest quotation
   and every box in a row of cards starts and ends on the same line.
   The gap between the four rows is the list's own `row-gap`, which is why
   the children below carry no margins: one number, and the space between
   two cards is the same 14px as the space inside one. */
.uses li{display:grid;grid-template-rows:subgrid;padding:22px 20px;
  text-align:left;
  background:var(--panel);border:1px solid var(--rule);
  border-radius:var(--r-surface);transition:border-color .22s,background .22s}
.uses li:hover{border-color:var(--gold-dim)}
/* **A mark beside the title, on its line.** Six cards of nothing but sentences
   read as a wall, and the cheapest thing that is not decoration is a line mark
   the eye can land on before it starts reading. Beside the title rather than
   above it, so the card gains an anchor and not a centimetre: the band is the
   same height it was. Line and colourless — `--gold-dim`, never a fill — which
   is the palette's own rule that the pearl is line and mark and tea is the only
   fill. */
/* The mark is a SIBLING of the heading, not a child: the language toggle
   rewrites an element's innerHTML from its `data-th`/`data-en`, so anything
   nested inside the heading is deleted the first time somebody switches. */
/* The six-card list is head / sentence / quotation / link; the four-card one
   is head / sentence. Both span exactly what they hold — a card spanning more
   rows than it has children leaves an empty track at the bottom. */
.uses:not(.uses-4) > li{grid-row:span 4}
.uses-4 > li{grid-row:span 2}
.uses-head{display:flex;align-items:center;gap:10px;margin:0}
.uses-head .mk{flex:0 0 auto;width:26px;height:26px;color:var(--gold-dim)}
.uses h3{margin:0;font-size:17px;color:var(--gold-lit)}
.uses p{margin:0;font-size:14px;line-height:1.7;color:var(--text)}
/* **The sentence somebody says, not the command they type** (2026-08-29). Each
   of these cards used to end in a mono box — `erawan addon add myapp postgres`
   and five more — and six terminal lines in a row is the loudest thing in the
   section: a reader met "you will need a shell" before they met a single
   heading. PRODUCT.md §45 says this page speaks to somebody who wants a
   website and does not write code, and the band immediately below this one
   says in as many words that the agent does the work. So the card now shows
   what you ask for. The CLI is not hidden — it is one tab down and in the
   docs link on every card, which is where the reader who wants it is already
   going.
   Set in the reading font on purpose: a monospace quotation would be the
   terminal again in a different costume — and it wraps rather than scrolling,
   because a sentence that runs off the edge of a card is not a sentence
   anybody can read. Centred inside its box, with the docs link under it: the
   two rows a reader *reads* keep the card's left edge, and the two that are
   objects on it sit in the middle.
   **In a box, not against a rule** (2026-08-29). The old mono box was the one
   thing giving each card a shape, and losing it left four rows of text with a
   hairline down the side of one of them. This is the same block, one step up
   the elevation ladder and in the reading font: structure without the
   terminal. */
.uses .say{font-size:14.5px;line-height:1.6;color:var(--ivory);text-align:center;
  background:var(--lift);border:1px solid var(--rule);border-radius:10px;
  padding:11px 13px;margin:0}
body[data-lang="th"] .uses .say{font-family:Trirong,serif;line-height:1.75}
/* The only thing that leaves the card's left edge. Centred rather than pushed
   to the right: it is the card's last line and its own object, and six links
   sitting on the right edge of six cards drew a second column down the band
   that nothing else in the card lined up with. */
.uses .docs-link{justify-self:center;align-self:start;font-size:12.5px;color:var(--gold-dim);text-decoration:underline;
  text-decoration-color:var(--rule);text-underline-offset:3px;
  transition:color .18s,text-decoration-color .18s}
.uses .docs-link:hover,.uses .docs-link:focus-visible{color:var(--gold-lit);
  text-decoration-color:var(--gold-dim)}
body[data-lang="th"] .uses h3{font-family:Trirong,serif}
body[data-lang="th"] .uses .docs-link{font-family:Trirong,serif;font-size:13.5px}

/* ── three plans ── */
.plans{display:grid;gap:1px;background:var(--rule);border:1px solid var(--rule);
  margin-top:34px;grid-template-columns:1fr}
/* Two up before four: at 860px four columns leave each plan ~200px, which wraps
   "10 always-on services + unlimited static sites" onto four lines and makes the
   cheapest plan look as heavy as the dearest. */
@media(min-width:700px){.plans{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1040px){.plans{grid-template-columns:repeat(4,1fr)}}
/* `--panel`, not `--lacquer`: on the ground these were four columns of a table
   separated by the grid's 1px hairlines, while every other card on the page
   had a body. Same surface as `.uses li` and `.demo`, so a card means one
   thing wherever a reader meets one. */
.plan{position:relative;display:flex;flex-direction:column;background:var(--panel);padding:30px 26px;
  transition:background .22s}
/* The recommended plan is lit rather than merely listed. It is Pro since
   2026-08-17 — the tier where the database stops being shared — and the class
   moves with that decision rather than staying on a position in the row. */
.plan.pick{background:linear-gradient(155deg,#1B2925,#0C1210)}
.plan.pick::before{content:"";position:absolute;inset:0;border:1px solid var(--tea);
  pointer-events:none}
/* The plan name is a badge rather than a line of caps: four cards read as
   four objects, and the tier is the first thing to find in one. Neutral on
   purpose — `--tea` is capped at two per screen and the lit card already
   spends one on its border, so the badge is the elevation ladder
   (`--lift` over `--panel`) with the label's own gold type.
   `justify-self`/`align-self` because a grid item is blockified: without
   them the pill stretches the full width of the card. `text-indent` matches
   the tracking, which otherwise hangs as empty space after the last letter
   and leaves the word sitting left of centre. */
.plan .label{margin-bottom:18px;justify-self:center;align-self:center;
  padding:5px 13px;border:1px solid var(--rule);border-radius:99px;
  background:var(--lift);text-indent:.22em}
body[data-lang="th"] .plan .label{text-indent:.06em;padding:3px 13px}
.plan .amt{font-family:var(--serif);font-size:36px;line-height:1;color:var(--ivory);
  font-weight:200;letter-spacing:-.02em;margin-bottom:20px}
.plan .amt small{font-size:.4em;color:var(--text);letter-spacing:0}
/* **The yearly price is a price, not a feature.** It spent its life as the
   last bullet of each list, where somebody comparing tiers reads it as one
   more thing the plan includes rather than as the other way to buy it —
   and every plan has had one since §63. Inside `.amt` rather than beside it,
   because the four cards are `subgrid` over exactly four rows (label, price,
   sentence, list) and a fifth element in flow puts one card's rows out of
   step with the other three. It also keeps the JSON-LD check honest: that
   reads the first `฿` after `class="amt"`, which stays the monthly number
   Stripe actually charges. */
.plan .yr{display:block;font-family:var(--sans);font-weight:400;font-size:13.5px;
  letter-spacing:0;color:var(--text);margin-top:12px}
/* What the price returns to, under the price. Quiet on purpose: the offer is
   the loud half and it is already wearing the only badge on this screen. */
.plan .was{display:block;font-family:var(--sans);font-weight:400;font-size:12.5px;
  letter-spacing:0;color:var(--quiet);margin-top:6px}
/* **The launch badge, and it is this screen's second `--tea` and its last.**
   The lit Pro card spends the first on its border; the cap is two per screen
   and this was counted before it was added, not after. Absolutely positioned
   for the same reason `.yr` lives inside `.amt` — the cards are a subgrid and
   this must not become a row. */
/* **The strike is the whole discount now.** There was a `−50%` pill beside
   the plan's label and it is gone: the price band's job is a number somebody
   reads once, and two objects saying the same thing in one card is one of
   them repeating the other. Without the strike this line read as a future
   price rather than as this one being lower, which is the only part that was
   ever load-bearing. */
.plan .was s{color:var(--quiet);text-decoration-thickness:1px}
/* **`min-height` belongs to the sentence, not to every `<p>` in the card.**
   The label and the price are `<p>` too, so `3.4em` applied to all three — and
   on the price, where `em` is 36px, that reserved 122px for one line and left
   about eighty of dead space under every number. The sentence keeps it,
   because that is what makes the rule below it land on the same line in all
   four cards. */
.plan > p:not(.label):not(.amt){font-size:14.5px;line-height:1.72;min-height:3.4em;
  margin-bottom:22px}
.plan ul{list-style:none;margin:0;padding:20px 0 0;border-top:1px solid var(--rule);
  font-size:14px;line-height:1.7}
.plan li{position:relative;padding:5px 0 5px 20px}
.plan li::before{content:"—";position:absolute;left:0;color:var(--gold-dim)}
/* **Beside each other, the four cards are one grid rather than four.** Each
   card is `subgrid` over the same four rows — label, price, sentence, list —
   so every row is as tall as the tallest card's version of it, and the rule
   above the list lands on one line across all four. Bottom-sticking the list
   in a flex column could not do that: with equal card heights and unequal
   lists, `margin-top:auto` puts the rule at `bottom - list height`, which is
   a different line in the card holding one more bullet.
   `row-gap:0`, because `.plans` draws its hairlines with the gap over a
   `--rule` background and a row gap here would rule *inside* every card. In
   the two-column range the line between the two rows of cards is drawn by the
   lower pair's own `border-top` instead; both of them carry it, so their
   tracks stay in step with each other. */
@media(min-width:700px){
  .plans{row-gap:0}
  .plan{display:grid;grid-template-rows:subgrid;grid-row:span 4}
  .plan:nth-child(n+3){border-top:1px solid var(--rule)}
}
@media(min-width:1040px){.plan:nth-child(n+3){border-top:0}}
@media(max-width:980px){
  .hero{grid-template-columns:1fr;gap:36px;padding:46px 0 64px}
  .hero h1{max-width:19ch}.hero-sub{max-width:56ch}
}
@media(max-width:700px){
  .hero{padding:38px 0 54px}
  .gift{padding-top:56px}
  .plan > p{min-height:0}
}

/* ── the label over a pasteable line ──
   Left from the two-card #connect section that the band's tabs replaced; the
   CLI tab still names which line is for which operating system. `.ways`,
   `.way`, `.way-line` and `.ways-foot` went with that section. */
.paste-label{margin:0 0 6px;font-family:var(--mono);font-size:10.5px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--quiet)}
.paste + .paste-label{margin-top:14px}
body[data-lang="th"] .paste-label{font-family:Trirong,serif;letter-spacing:.04em;
  text-transform:none;font-size:12.5px}


/* The page's corners live in two numbers, so rounding the whole site is a
   value change here (2026-08-22: 1/0 → 12/8 — the ground the Harness-style
   redesign settled on; glass panes with square edges read unfinished). The
   numbers stay two: surface for panels and cards, control for buttons,
   fields and chips. Dots stay circles; a dot is a shape, not a corner. */
:root{--r-surface:12px;--r-control:8px}
/* The gap-over-rule grids do not read the tokens — their cells are square —
   so they are rounded as containers and clipped, which also mitres the
   hairline dividers into the corner. */
.plans,.load{border-radius:var(--r-surface);overflow:hidden}

/* The one exception, and it is named rather than hidden inside a rule
   (2026-08-17): **message bubbles are round.** Everything else on the page —
   panels, buttons, fields, the demo's own frame — still reads the two numbers
   above and stays square. A bubble is the shape people have been shown a chat
   in for fifteen years, and the console's `/lab` already draws its own that
   way; making the landing's the odd square pair meant the same product had two
   pictures of one conversation. Third value, same discipline: anything that
   rounds a message reads THIS, so undoing it is one number here. The tail
   corner stays tight (3px) on the side the message came from. */
:root{--r-bubble:10px}
/* ── the fourth number, and it belongs to glass (2026-08-22) ──
   A pane reads as glass partly by its radius: square frosted edges read as
   an unfinished surface, not a pane. Everything in the demo panel family —
   the frame, the mode cards, the field, the send button, the rail cards —
   reads THIS, so the panel is one rounded object against the page's square
   discipline, the same way the bubble already is. One number to undo. */
:root{--r-glass:14px}

/* ── the elevation ladder, put to work (2026-08-17) ──
   The palette always had four surfaces and the page used two. Measured in L*,
   which is the metric that means anything this far down — WCAG ratios compress
   to 1.0x between near-blacks and say every pair is identical when the eye can
   tell them apart perfectly well:

     lacquer-deep  2.3   lacquer  3.7   panel  7.1   lift  11.3

   So lacquer→panel is ΔL* 3.4 and panel→lift is 4.2, both visible steps. But
   `--lift` appeared exactly ONCE in this stylesheet and `--panel` four times,
   while `--rule` appeared 43 times: every card, box and panel on the page was
   the same surface with the same hairline around it, and the only thing
   separating any two objects was a 1px border at L* 18.7. That is the "nothing
   cuts through" complaint, and no amount of accent colour fixes it — the
   accent marks one or two things per screen by design, and everything else
   still has to have shape.

   Two rules, both using tokens that already existed:

   1. **A surface that responds, responds by rising.** Every hoverable card and
      outline button goes panel→lift, so the thing under the cursor is lit
      rather than merely outlined. Colour-only hover states are invisible to
      anyone who is not looking directly at the border.
   2. **A card is one thing everywhere.** `.plan` sat on `--lacquer`, the
      ground, while `.uses li` and `.demo` sat on `--panel`. Two
      different answers to "what is a card" on one page is why the price
      section read as a table and the rest read as objects. */
.uses li:hover,
.plan:hover,
.paste button:hover,
.demo-chip:hover{background:var(--lift)}
/* The band's copy button is a filled tea control and the two quiet ones beside
   it are deliberately flat — neither takes the lift, or the accent stops being
   the loudest thing in that band. */
.do .paste button:hover,
.do .do-pair .paste button:hover,
.do #panel-mcp .paste button:hover{background:none}
.do .paste button:hover{background:var(--tea-lit)}

/* ══════════ the agent demo in the hero ══════════
   Moved out of web/index-with-hermes.html when the panel became the
   landing page itself. The panel's height is the grid row's, not its
   own: the hero is two columns and a panel that sets its own height
   leaves the copy beside it floating against a taller neighbour. */
/* --- the demo panel: takes the hero slot the scripted transcript had ---
   Fixed height with only the message list scrolling, so the input never moves
   under the reader's cursor between turns. A chat whose composer walks down
   the page as the conversation grows is the shape of a transcript, not of a
   conversation. */
/* The width belongs here rather than only in the ≥900px block, where it used to
   live alone. `.hero` is a centred flex column, so a child with no width is
   sized by its content — and below 900px this panel measured 425px inside a
   375px phone, hanging off the right of the screen with nothing to show for it
   but `body{overflow-x:hidden}` swallowing the evidence. `.hero-copy` had its
   own `width:min(100%,720px)` for exactly this reason; the panel did not. */
/* ── the panel is a pane of glass, not a surface (2026-08-22) ──
   The Harness-demo look: nacre-tinted translucency over the page's own
   ground, backdrop blur so whatever sits behind arrives softened, a bright
   hairline rim on the top edge the way glass catches light, and colour as
   glow in the shadow rather than as a fill. Nothing opaque sits between
   the visitor and the page behind the panel — which is also why the blur
   is on the frame only: blurring every surface inside it would cost real
   compositing for no reading, and the inner surfaces are translucent
   instead. */
.demo{background:var(--glass);border:1px solid var(--glass-edge);
  border-radius:var(--r-glass);
  backdrop-filter:blur(18px) saturate(1.35);-webkit-backdrop-filter:blur(18px) saturate(1.35);
  box-shadow:0 24px 70px rgb(0 0 0 / .5),
             0 0 90px 24px rgb(41 98 84 / .22),
             0 0 46px 6px rgb(242 166 90 / .1),
             inset 0 1px 0 rgb(255 255 255 / .07);
  padding:0;overflow:hidden;display:flex;flex-direction:column;width:min(100%,1040px);
  /* Grid rows otherwise inherit the transcript's max-content height: each Pi
     paragraph makes the hero taller and pushes the composer down. The panel is
     a viewport; its body, not the page, owns the scroll. */
  /* Raised 2026-08-29. At 530px the conversation got 311 of them and the
     composer 150, so the thing being demonstrated had four lines to happen in
     and the box you type into took a third of the panel. The composer is the
     size the reference is and stays there; what was short is the room. */
  height:clamp(470px,70svh,680px);min-height:0;max-height:680px}
/* `.demo-head` — the "Pi · a sandbox of your own" strip — was removed
   (2026-08-22): the panel's first row is now its content, and who is
   answering is what the transcript says. `.demo-badge` went with it. */

/* `.hero` centres its text and that reaches in here. A bubble picks its side
   with `align-self`; the text inside it keeps its own left edge, or a wrapped
   Thai message ends on a centred orphan line and a pasted stack trace is
   unreadable. The 640px block above carries this correction for `.said` — the
   scripted transcript's class — and the live panel's classes never had it, so
   it was wrong in both widths. Set on the container rather than per class:
   every row in here is scanned, not read. */
.demo-body{flex:1;padding:18px;display:flex;flex-direction:column;gap:12px;
  overflow-y:auto;overscroll-behavior:contain;scrollbar-gutter:stable;min-height:0;
  text-align:left}

/* Connection and refusal state belongs to the platform, outside the agent
   transcript: an error is literal system text, never styled as something Pi
   said. Waiting is no longer drawn here — it is the typing bubble at the tail
   of the conversation, where the answer will appear. */
.demo-system[hidden]{display:none}
.demo-system{padding:8px 18px;border-bottom:1px solid var(--rule);
  background:var(--lacquer-deep);font-family:var(--mono);font-size:11.5px;
  line-height:1.5;color:var(--tea-lit);text-align:left}

/* --- the composer: present from the first frame, never moves ---
   The hero of the panel (2026-08-22, the Harness-demo shape): the input is
   what a visitor is being invited to type into, so it is taller and larger
   than a utility field, not the compact strip it was. */
/* **One column, and the composer sits in it** (2026-08-29). The panel is
   about 1000px on a desktop and the composer was taking every pixel of it: a
   12:1 letterbox with the placeholder in one corner and the send button 900px
   away in the other, which is not a shape any chat this visitor already uses
   has. The transcript had the same fault more quietly — `.demo-msg` is 92% of
   the panel, so a line of text ran ~950px, well past the ~75 characters a
   reader gets through without losing their place on the way back.
   The foot keeps its full-bleed ground and top rule, because the line under a
   conversation spans the pane in every chat there is; only what sits inside it
   is capped. */
/* Positioned so the expand button lands in the conversation's corner. It has
   to be declared HERE, above `.demo-full`: the panel carries both classes and
   the two rules have the same specificity, so `position:relative` written
   after `position:fixed` silently cancels full screen. */
.demo-chat{--chat-col:760px;position:relative}
/* **No ground of its own, and no rule above it** (2026-08-29). The foot was a
   darker band with a hairline on top, which made the composer read as a dock
   bolted under the conversation rather than as the place that conversation is
   typed. There is one room: the transcript and the composer share a ground and
   the same 760px column, and what separates the composer from the messages is
   its own box — which it has, and which is the only edge a chat needs. The
   body ends where this begins (they are flex siblings), so nothing scrolls
   under the composer and no scrim is owed. */
.demo-foot{padding:12px 14px 14px;display:flex;flex-direction:column;gap:8px}
.demo-foot>*{width:100%;max-width:var(--chat-col);margin-inline:auto}
/* The shelf is the pane's full width, not the conversation's column: that
   column is a reading measure and this is a row of pictures to scan. */
.demo-foot>.demo-shelf{max-width:none}
.demo-shelf[hidden]{display:none}

/* ── the two rows over the composer ────────────────────────────────────────
   The six demos are chips directly above the composer rather than in the empty
   state, because that element is hidden the moment a conversation exists and
   they vanished the first time somebody used one. */
.demo-starters{display:flex;flex-wrap:wrap;gap:8px;justify-content:center}
.demo-starters[hidden]{display:none}
.demo-shelf-line{display:flex;justify-content:center}
.demo-shelf-line[hidden]{display:none}
.demo-chip-icon{width:14px;height:14px;flex:0 0 auto}

/* **Louder than the chips beside it, and it is the only one that is allowed
   to be.** Every other chip in that row sends a message; this one opens the
   pictures, so it is a different kind of thing and reads as one — filled
   rather than outlined, one step up the elevation ladder, with a caret that
   turns when it is open. It is not tea: the accent is capped at two per screen
   and the send button already holds one. */
.demo-chip-shelf{background:var(--lift);border-color:var(--gold-dim);
  color:var(--gold-lit);font-weight:500;padding-inline:15px;
  display:inline-flex;align-items:center;gap:7px}
.demo-chip-shelf::after{content:"";width:6px;height:6px;flex:0 0 auto;
  border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;
  transform:rotate(45deg) translate(-1px,-1px);transition:transform .2s}
.demo-chip-shelf[aria-expanded="true"]::after{
  transform:rotate(-135deg) translate(-1px,-1px)}
.demo-chip-shelf:hover{background:var(--panel);border-color:var(--gold);
  color:var(--gold-lit)}

/* **It comes out from under the button and pushes everything up.** The shelf
   sits immediately below the control that opens it, and `.demo-foot` is
   anchored to the bottom of the pane with the transcript above taking the
   slack — so the height this row gains is height the button, the chips and the
   conversation are pushed up by. The composer does not move at all.

   Two transitions doing two jobs: the grid row from 0fr to 1fr makes the
   space, arriving at the content's own height without anybody measuring it,
   and the row inside travels its last 14px upward, so the cards read as coming
   out from under the button rather than fading in where they already were.
   `hidden` cannot be transitioned, so `site.js` takes it off a frame before
   this class arrives and puts it back when the close finishes: the attribute
   stays the truth for a screen reader and the class is what the eye follows. */
.demo-shelf{display:grid;grid-template-rows:0fr;
  transition:grid-template-rows .28s cubic-bezier(.32,.72,0,1),margin-top .28s}
.demo-shelf>*{overflow:hidden;min-height:0;
  transform:translateY(14px);opacity:0;
  transition:transform .28s cubic-bezier(.32,.72,0,1),opacity .2s ease}
.demo-shelf.is-open{grid-template-rows:1fr;margin-top:8px}
.demo-shelf.is-open>*{transform:translateY(0);opacity:1}
@media (prefers-reduced-motion:reduce){
  .demo-shelf,.demo-shelf>*{transition:none}
  .demo-shelf>*{transform:none}
}

.demo-body{width:100%;max-width:var(--chat-col);margin-inline:auto}
/* **One box, and the controls live inside it** (2026-08-29). The composer was
   a row — field, then a round button beside it — which on a phone left the
   field about two thirds of the line and put the two things a person does
   with a message on different objects. Now the text owns the full width and
   the actions sit under it on their own line: attach on the left, send on the
   right, which is the shape every chat this reader already uses. */
.demo-row{display:flex;flex-direction:column;gap:14px;
  background:rgb(11 14 13 / .75);
  border:1px solid var(--glass-edge);border-radius:18px;padding:14px 18px;
  transition:border-color .16s,box-shadow .16s}
/* Focus is a change of edge, not an announcement. A 3px ring around a box
   that is already the loudest object in the pane says something has gone
   wrong; the field is focused on load, so that ring was the panel's resting
   state. */
.demo-row:focus-within{border-color:var(--gold-dim);
  box-shadow:0 1px 3px rgb(0 0 0 / .18)}
.demo-acts{display:flex;align-items:center;justify-content:space-between}
/* It has a surface but no fill: a bare glyph on the composer's own ground was
   not reading as a control at all — nothing said it could be pressed until the
   pointer was already on it. Still never a filled circle, because two filled
   circles in one box is two instructions and only one of them is the one we
   are asking for. */
.demo-attach{width:34px;height:34px;display:grid;place-items:center;
  border:1px solid var(--glass-edge);background:rgb(255 255 255 / .05);
  color:var(--quiet);border-radius:999px;cursor:pointer;
  transition:color .16s,background .16s,border-color .16s}
.demo-attach:hover{color:var(--ivory);background:rgb(255 255 255 / .1);
  border-color:var(--gold-dim)}
.demo-attach:disabled{opacity:.45;cursor:default}
.demo-attach svg{width:17px;height:17px}
/* What the pod said about a file, where the file was offered. Only refusals
   reach it now — a file that went up is in the message that carried it, and
   the bubble is the receipt. */
.demo-upload{margin:0 0 6px;font-size:11.5px;color:var(--quiet)}
/* ── attached, not yet sent ──────────────────────────────────────────────
   Chips at the top of the composer, above the text: a picked file is part of
   the message being written, so it lives in the box that message is written
   in and leaves it when the message does. `site.js` has the reasoning; what
   is here is that a chip must read as removable — the × is a real button with
   its own hit area, not a hover affordance, because on a phone there is no
   hover and a file you cannot take back out is a file you have to reload the
   page to be rid of. */
.demo-files{display:flex;flex-wrap:wrap;gap:8px}
.demo-files[hidden]{display:none}
.demo-file{display:inline-flex;align-items:center;gap:8px;min-width:0;
  max-width:100%;padding:5px 6px 5px 5px;
  background:var(--lift);border:1px solid var(--glass-edge);
  border-radius:12px}
.demo-file img{width:30px;height:30px;flex:0 0 auto;border-radius:8px;
  object-fit:cover;display:block}
.demo-file-glyph{width:30px;height:30px;flex:0 0 auto;display:grid;
  place-items:center;border-radius:8px;background:var(--panel);
  color:var(--gold-dim);font-size:12px}
.demo-file-name{min-width:0;max-width:180px;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;
  font-family:var(--sans);font-size:12px;color:var(--text)}
.demo-file-x{flex:0 0 auto;width:22px;height:22px;display:grid;place-items:center;
  border:0;background:none;color:var(--quiet);border-radius:999px;
  font-size:15px;line-height:1;cursor:pointer;transition:color .16s,background .16s}
.demo-file-x:hover{color:var(--ivory);background:rgb(255 255 255 / .1)}
/* In the transcript. The bubble is the visitor's own line, so a photograph in
   it is shown at a size somebody can actually see rather than as a 30px chip:
   what they attached is half of what they said. */
.demo-you-files{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:6px}
.demo-you-file{display:inline-flex;max-width:100%}
.demo-you-file img{max-width:220px;max-height:170px;width:auto;height:auto;
  border-radius:10px;display:block}
.demo-you-text{display:block;white-space:pre-wrap}
/* The whole panel is the drop target: somebody dragging a photo aims at the
   conversation, not at a 32px paperclip. */
.demo-chat.is-over{outline:2px solid var(--tea);outline-offset:-2px}
/* The field is only text. Its box moved to `.demo-row` when the controls came
   inside it — a box inside a box was two focus rings for one thing to type
   into — so nothing here paints: no background, no border, no padding. It was
   left as three rules declaring a surface and two more taking it back, which
   read as if the field still had one. */
/* No `flex:1`. It is a leftover from when this row ran horizontally and the
   flexible axis was the width; in a column it makes flex-basis 0 on the
   HEIGHT, so the inline height site.js writes on every keystroke is discarded
   and the field stays one line tall while the text scrolls inside it. */
.demo-input{min-width:0;width:100%;box-sizing:border-box;
  background:none;border:0;padding:0;border-radius:0;resize:none;
  font-family:var(--sans);font-size:16.5px;line-height:1.5;color:var(--ivory);
  /* One line plus air, not two. The empty field is the resting size and the
     panel is only ~490px tall on a laptop; it grows on the first wrap anyway,
     which is when the room is actually wanted. The padding and the 14px above
     the controls are what make it read as roomy, so those stay. */
  min-height:32px;max-height:170px}
.demo-input:focus{outline:none;box-shadow:none}
.demo-input::placeholder{color:var(--quiet)}
.demo-input:disabled{opacity:.5}
.demo-send{flex:0 0 auto;width:34px;height:34px;border-radius:999px;
  border:1px solid var(--tea);background:var(--tea);color:var(--lacquer);
  box-shadow:0 2px 8px rgb(242 166 90 / .25);
  cursor:pointer;transition:.16s;
  display:flex;align-items:center;justify-content:center}
.demo-send span{font-size:17px;line-height:1;display:block}
.demo-send:hover:not(:disabled){background:var(--tea-lit);border-color:var(--tea-lit);box-shadow:0 4px 12px rgb(242 166 90 / .35)}
.demo-send:disabled{background:none;border-color:var(--rule);color:var(--quiet);
  box-shadow:none;cursor:default}
/* The scrolling rail of follow-up chips that used to live between the shelf
   and the composer is gone (2026-09-02) — `site.js` says why. `.demo-chip` is
   still the starters' and the shelf toggle's shape. */
.demo-chip{flex:0 0 auto}
.demo-chip{background:var(--glass-2);border:1px solid var(--glass-edge);
  border-radius:var(--r-glass);white-space:nowrap;
  padding:5px 11px;font-family:var(--sans);font-size:12px;color:var(--text);
  cursor:pointer;transition:.16s}
.demo-chip:hover{border-color:var(--gold-dim);color:var(--gold-lit)}
/* **The second row of chips, and the spark is the difference.** These two
   leave for the console's lab instead of filling the composer — both need an
   account, one for `--access owner` and one to fulfil secrets — so the mark is
   the only thing on the row saying "this one goes somewhere else". Gold rather
   than tea: the accent is capped at two per screen (§10) and the send button
   already holds one, which is the same reason `.demo-chip-shelf` is gold. */
.demo-apps{margin-top:2px}
.demo-chip-app{display:inline-flex;align-items:center;gap:6px;
  text-decoration:none;border-color:var(--gold-dim);color:var(--gold-lit)}
.demo-chip-app:hover{background:var(--lift);border-color:var(--gold)}
.demo-chip-icon{width:12px;height:12px;flex:0 0 auto;opacity:.9}
/* ── the plan a card needs, said before the click ──────────────────────────
   Three names, the price list's own. Not an accent: sixteen of these in one
   rail would spend the two-`--tea`-per-screen budget sixteen times over (§10),
   so they are set in the ladder's own greys and separated by weight rather
   than by colour — `free` is the one that gets any lift, because it is the
   answer most readers are looking for. */
.plan-badge{display:inline-block;vertical-align:middle;margin-right:6px;
  padding:1px 6px;border-radius:999px;border:1px solid var(--rule);
  font-family:var(--mono);font-size:9px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--quiet);background:var(--panel);
  font-weight:400;position:relative;top:-1px}
.plan-badge.plan-free{color:var(--gold);border-color:var(--gold-dim)}
.demo-chip-app .plan-badge{margin-right:0;margin-left:2px}
/* ── the panels behind the tabs ──
   A panel is the glass pane's content column: system line, transcript or
   rail, composer. The tab strip sits above them in the DOM for the
   accessibility tree. */
.demo-panel{display:flex;flex-direction:column;min-height:0;flex:1}
.demo-panel[hidden]{display:none}

/* ── the chat's empty state (§33) ──
   What the pane shows before the first word: an invitation in the
   platform's voice, and runnable starters centred where the conversation
   will be, the shape every AI chat's first screen settled on. */
/* It sits just above the composer rather than in the middle of the pane: the
   invitation and the box you answer it in are one act, and a taller pane
   (2026-08-29) turned a centred invitation into a line floating in a hole.
   The emptiness above is then headroom for the conversation, which fills from
   the top. Pushed down with `margin-top:auto` on the first child and NOT with
   `justify-content:flex-end`, which makes overflowing content unreachable
   above the scroll box. */
.demo-empty{flex:1;min-height:0;overflow-y:auto;padding:24px 20px 10px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:15px;text-align:center}
.demo-empty[hidden]{display:none}
.demo-empty-label{margin:0;font-family:var(--mono);font-size:11.5px;
  letter-spacing:.06em;color:var(--quiet)}
body[data-lang="th"] .demo-empty-label{font-family:Trirong,serif;
  letter-spacing:0;font-size:13.5px}
.demo-suggest{display:flex;gap:8px;flex-wrap:wrap;justify-content:center;max-width:580px}

/* ── the six demos, as cards ───────────────────────────────────────────────
   Where the three prompt chips were. The card IS a chip — pressing it sends a
   prompt through the same path a typed one takes — so it lives in the empty
   state with the chips it replaces rather than in a rail somewhere else. The
   picture is the label, and it is a picture of a page this prompt has actually
   built. */
/* **One row, scrolled — at every width, not only on a phone.** Six cards
   wrapped is two rows of 226px inside a 619px pane, and the second row is cut
   in half by the composer. A row that scrolls holds the pane's height fixed
   whatever the list grows to, and the half card at the edge is what says there
   are more. Capped to the conversation's own column so it lines up with the
   composer under it. */
.demo-cards{display:flex;gap:12px;flex-wrap:nowrap;justify-content:flex-start;
  /* The full width of the pane, not the conversation's column: this is a shelf
     to scan, and the reading measure that governs a paragraph has nothing to
     say about a row of pictures. Six 168px cards very nearly fit a desktop
     pane, so the sliver of the last one is what says it scrolls. */
  width:100%;overflow-x:auto;
  /* The bleed matches `.demo-foot`'s own 14px padding exactly, so the first
     card starts on the pane's margin and the last can be scrolled clear of it
     — a card touching the pane's edge reads as a rendering fault. */
  scroll-snap-type:x mandatory;padding:0 14px 2px;margin-inline:-14px;
  scrollbar-width:none}
.demo-cards::-webkit-scrollbar{display:none}
.demo-card{flex:0 0 auto;width:176px;scroll-snap-align:start;
  display:flex;flex-direction:column;
  background:var(--panel);border:1px solid var(--rule);
  border-radius:12px;overflow:hidden;text-align:left;
  transition:border-color .18s,background .18s,transform .18s}
.demo-card:hover{border-color:var(--gold-dim);background:var(--lift);
  transform:translateY(-2px)}
/* **The address bar is the advertisement.** It says "your app gets a name of
   its own" without a sentence of copy, and it is drawn in CSS rather than
   photographed: a headless browser cannot screenshot its own chrome. */
.demo-card-chrome{display:flex;align-items:center;gap:6px;padding:6px 8px;
  background:var(--lift);border-bottom:1px solid var(--rule)}
.demo-card-dots{display:flex;gap:3px;flex:0 0 auto}
.demo-card-dots i{width:5px;height:5px;border-radius:50%;background:var(--rule)}
.demo-card-addr{flex:1;min-width:0;font-family:var(--mono);font-size:8.5px;
  color:var(--quiet);background:var(--panel);border-radius:5px;padding:2px 6px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:center}
/* 860×540 is the size these are taken at, so the card shows the top of the
   page at its own aspect rather than squashing it. */
.demo-card img{display:block;width:100%;aspect-ratio:860/540;object-fit:cover;
  object-position:top center;background:var(--lift)}
/* **The whole card is the button.** It carried a name and two action lines —
   three rows of furniture under a picture, six times over, in a pane that was
   already tight. The picture and the name are the card now; pressing anywhere
   fills the composer, and the live address is a mark in the corner of the
   image that appears when the pointer is on it. */
.demo-card{position:relative;cursor:pointer}
.demo-card-body{padding:8px 11px 10px}
.demo-card-name{font-size:13px;line-height:1.3;color:var(--ivory);font-weight:500}
body[data-lang="th"] .demo-card-name{font-family:Trirong,serif;font-size:14px}
/* The button covers the card, under the corner link so that one still wins. */
.demo-card-make{position:absolute;inset:0;z-index:1;background:none;border:0;
  padding:0;cursor:pointer;font-size:0;color:transparent}
.demo-card-make:focus-visible{outline:2px solid var(--gold);outline-offset:-3px}
.demo-card-acts{display:contents}
.demo-card-see{position:absolute;z-index:2;top:36px;right:8px;
  font-family:var(--mono);font-size:10px;text-decoration:none;
  color:#F6EEDF;background:rgb(0 0 0 / .55);padding:2px 7px;border-radius:5px;
  opacity:0;transition:opacity .16s}
.demo-card:hover .demo-card-see,
.demo-card-see:focus-visible{opacity:1}

/* On a phone they lie down and scroll sideways. Stacked, three 200px cards are
   602px tall inside a 591px pane: the third is below the fold of a pane that is
   itself the fold. One row, fixed height, and the half card at the edge is what
   says there are more — the idiom `.demo-rail` already uses in the gated tabs
   rather than a second way of doing the one thing this page does twice. */
/* On a phone the first card starts at the pane's own margin rather than
   against the edge, and the last one can be scrolled clear of it. */
@media (max-width:640px){
  .demo-cards{gap:10px}
  .demo-card{width:158px}
}

.hero-sep{width:min(100%,760px);height:1px;margin:32px auto 0;
  background:linear-gradient(90deg,transparent,var(--rule),transparent)}

.demo-intro{margin:28px auto 14px;text-align:center;
  font-family:var(--sans);font-size:14.5px;font-weight:500;color:var(--quiet)}
body[data-lang="th"] .demo-intro{font-family:Trirong,serif;font-size:15.5px}

/* ── the three kinds of work, integrated as top viewport tabs (§33) ── */
.demo-kinds{display:grid;grid-template-columns:repeat(3,1fr);
  width:100%;margin:0;padding:0;
  border-bottom:1px solid var(--glass-edge);
  background:rgb(7 9 8 / .6)}
/* The divider is a shadow, not a border (2026-08-29). A border is inside the
   button's box, so `::after{left:0;right:0}` spanned 345px of a 346px tab —
   and the last tab has no border, so the underline changed length depending
   on which tab you were on. A shadow occupies no space, so every tab is the
   same and the indicator is the width of the thing it indicates. */
.demo-kinds button{display:flex;flex-direction:column;gap:4px;
  align-items:center;text-align:center;padding:10px 8px;
  background:transparent;border:0;box-shadow:1px 0 0 0 var(--glass-edge);
  border-radius:0;cursor:pointer;position:relative;
  transition:background .18s,color .18s}
.demo-kinds button:last-child{box-shadow:none}
.demo-kinds button:hover{background:var(--glass-2)}
.demo-kinds button[aria-selected="true"]{background:var(--lift)}
/* `bottom:-1px` so it covers the strip's own hairline rather than stacking on
   top of it: the tab you are on should join the pane below it, and three
   edges in 3px was reading as a rendering fault. */
.demo-kinds button[aria-selected="true"]::after{
  content:"";position:absolute;left:0;right:0;bottom:-1px;height:2px;
  background:var(--tea-lit);
}

/* Its own line-height, because `body[data-lang="th"]` sets 1.9 for prose and
   that reached in here: a two-word label was getting a 27.55px line box around
   14.5px type, which is what made the strip look loosely set and stand 6px
   taller in Thai than in English for no reason anybody chose. */
.mode-title{font-family:var(--sans);font-size:13.5px;line-height:1.25;
  font-weight:600;color:var(--text);transition:color .18s}
body[data-lang="th"] .mode-title{font-family:Trirong,serif;font-size:14.5px;font-weight:600}
.demo-kinds button:hover .mode-title,
.demo-kinds button[aria-selected="true"] .mode-title{color:var(--gold-lit)}

.mode-what{font-family:var(--sans);font-size:11px;line-height:1.35;color:var(--quiet);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;
  transition:color .18s}
body[data-lang="th"] .mode-what{font-family:Trirong,serif;font-size:12px}

/* The two gated tabs used to carry an orange subtitle and the selected one a
   teal 400 — so the loudest thing in the strip was always a tab you were not
   on, and the active one was the quietest of the three in both colour and
   weight. Colour now says what it is for: every subtitle is quiet, and the tab
   you are on is the one that lifts. The single accent in this strip is the
   underline, which is the one thing that moves. */
.demo-kinds button:hover .mode-what,
.demo-kinds button[aria-selected="true"] .mode-what{
  color:var(--gold-lit);
}

@media(max-width:640px){
  .demo-kinds{grid-template-columns:repeat(3,1fr)}
  .demo-kinds button{padding:8px 4px;gap:3px}
  .mode-title{font-size:12.5px}
  body[data-lang="th"] .mode-title{font-size:13px}
  .mode-what{font-size:9.5px}
  body[data-lang="th"] .mode-what{font-size:10px}
}

/* Tab switching hides the chat without ending it — the elements stay in the
   document so a streaming run keeps streaming behind the cards. Flex beats
   the hidden attribute's UA display:none, so it is restated here. */
.demo-body[hidden],.demo-foot[hidden],.demo-browse[hidden]{display:none}

/* The gated kinds' content: Sample apps and Open source as two-row horizontal scroll grid */
.demo-browse{flex:1;min-height:0;padding:18px 20px;
  display:flex;flex-direction:column;gap:10px;text-align:left;justify-content:center}
.demo-note{margin:0;font-family:var(--mono);font-size:11px;
  line-height:1.4;color:var(--quiet)}
body[data-lang="th"] .demo-note{font-family:Trirong,serif;font-size:12.5px}
.demo-rail{flex:1;min-height:0;display:grid;grid-template-rows:repeat(2,1fr);
  grid-auto-flow:column;grid-auto-columns:min(320px,78vw);gap:10px;
  overflow-x:auto;scroll-snap-type:x mandatory;padding-bottom:2px;
  scrollbar-width:none}
.demo-rail::-webkit-scrollbar{display:none}
/* **Back to two rows on a track (2026-09-05), with the affordance it was
   missing.** This was a wrapping grid from 2026-09-02, and the note here said
   why: `.demo-rail` was written for a pane of fixed height, and reused in a
   section it put the cards on a track running past the viewport *with nothing
   saying so* — on a phone, a column sliced down the middle.
   That objection was right about the affordance and not about the shape. Eight
   cards wrapping is three rows and most of a screen for one group; two rows on
   a track is a fixed height whatever the list holds. So the shape comes back
   and the two things that made it wrong here are fixed rather than argued
   with: the scrollbar is visible, and a phone gets one row, so no card is ever
   sliced down the middle. */
#carry-rail{display:grid;grid-auto-flow:column;
  grid-template-rows:1fr;grid-auto-columns:min(320px,80vw);
  gap:12px;overflow-x:auto;scroll-snap-type:x mandatory;
  /* Shown, not hidden. A track with no scrollbar is a track people do not
     know they can move, which is the whole of the 2026-09-02 objection. */
  scrollbar-width:thin;padding-bottom:10px;
  /* A flex/grid child defaults to `min-width:auto` and would size itself to
     the track instead of scrolling inside it — the page would scroll
     sideways, which this landing must never do. */
  min-width:0;max-width:100%}
#carry-rail::-webkit-scrollbar{height:8px}
#carry-rail::-webkit-scrollbar-thumb{background:var(--rule);border-radius:99px}
@media(min-width:640px){#carry-rail{grid-template-rows:repeat(2,auto)}}
/* And the accent budget (§10, two `--tea` per screen) survives a rail showing
   two rows at a time; it does not survive a section showing every card at
   once, where each card's orange "Start session →" becomes a column of
   identical orange lines. The heading's `<em>` carries the accent here. */
#carry-rail .demo-go{color:var(--quiet)}
#carry-rail .demo-open:hover .demo-go{color:var(--tea-lit)}
.demo-open{display:flex;flex-direction:column;gap:4px;
  padding:12px 16px;scroll-snap-align:start;
  background:var(--glass-2);border:1px solid var(--glass-edge);
  border-radius:var(--r-glass);
  text-decoration:none;transition:border-color .18s,background .18s}
.demo-open:hover{border-color:var(--gold-dim);background:var(--lift)}
.demo-open.is-starting{border-color:var(--tea);background:rgba(230, 115, 0, 0.12);pointer-events:none}
.demo-open h3{margin:0;font-size:14.5px;font-weight:500;color:var(--gold-lit)}
body[data-lang="th"] .demo-open h3{font-family:Trirong,serif;font-size:15.5px}
.demo-open p{margin:0;font-size:12px;line-height:1.45;color:var(--text);flex:1}
.demo-go{margin-top:4px;font-family:var(--mono);font-size:10.5px;color:var(--tea);
  display:inline-flex;align-items:center;gap:4px;transition:color .18s}
.demo-open:hover .demo-go{color:var(--tea-lit)}
.demo-open.is-starting .demo-go{color:var(--tea-lit);font-weight:600}
body[data-lang="th"] .demo-go{font-family:Trirong,serif;font-size:12px}

/* --- running state: the transcript, which is a chat ---
   Pi's turn is a bubble behind the mark on the left; the visitor's is the
   opposite corner. It was a `·` in a 15px gutter followed by a paragraph on
   the panel's own ground — a log, which is the shape of something being
   reported to you. Nothing about the wait, the streaming or the words changed
   to fix that; the shape did. The console's `/lab` panel carries the same one
   (`console/src/components/lab/lab-agent.tsx`) — one product, one picture. */
/* `baseline`, not `flex-start`, and this is the whole alignment fix
   (2026-08-17). Both children are text, so the thing that must line up is
   their baselines — and a flex item's baseline is its first line's, which for
   the bubble is the first line of Pi's answer regardless of its padding or
   language. The version this replaces aligned two boxes and then carried a
   measured `margin-top` per language to make the ink agree; every change to
   the type would have invalidated the number, and Thai already needed its own. */
/* Every number below is read off the console's own panel, not chosen here
   (2026-08-17): gap 8, bubble 8/12, type 14/20, the tool line 11 at 28 of
   indent. The two surfaces are one product and the landing had been drifting
   half a pixel at a time — 9px gaps, 14.5px type, a 22.5px line box. */
.demo-msg{display:flex;gap:8px;align-items:baseline;align-self:flex-start;
  max-width:92%;animation:demo-in .4s both}
@keyframes demo-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.demo-msg,.demo-you{animation:none}}
/* Pi's name, set as text. Not the Erawan mark — that is the host's, and it is
   the console's transcript that rightly carries it, since a console session
   belongs to the account watching it.
   **No box.** A 22px chip centring a glyph that has neither ascender nor
   descender put the ink 1.33px low and 0.38px left of its own centre —
   measured with `measureText`, not eyeballed — and every fix for that is a
   constant that the next change to the type invalidates. Text beside text has
   a real answer: the shared baseline, above. */
.demo-avatar{flex:0 0 auto;width:20px;text-align:right;
  font-family:var(--mono);font-size:11px;line-height:1;letter-spacing:.02em;
  color:var(--gold-dim)}
/* Exactly the treatment `.line[data-who="ai"] .said` already uses further down
   the page, down to the tail corner: one step up the ladder from `.demo`'s own
   `--panel`, and NO border, because a shade above the panel is enough to lift
   it and a line around it puts back the drawn edge that idiom removed. The
   scripted illustration and the live panel are the same conversation; they had
   no business being two pictures of it. */
.demo-bubble{min-width:0;background:var(--lift);border:1px solid transparent;
  border-radius:var(--r-bubble);border-bottom-left-radius:2px;
  /* On the bubble, not only on `.demo-text`: everything inside sizes off
     this, and `.demo-markdown code` is `.86em`. Inheriting the page's 16px
     made inline code in a Pi answer a point larger here than in the
     console, which is the kind of drift this whole pass is removing. */
  font-size:14px;line-height:20px;padding:8px 12px}
.demo-text{color:var(--ivory);min-width:0;font-size:14px;line-height:20px;
  white-space:pre-wrap;overflow-wrap:anywhere}

/* Waiting, in the place the answer will land. Content-free on purpose: this is
   the one thing in the transcript that is neither the visitor's words nor Pi's,
   and it is allowed there precisely because it has none. */
.demo-typing{display:flex;gap:9px;align-items:center;padding:14px 12px}
.demo-dots{display:flex;gap:5px;align-items:center}
/* Which step, and how long it has been on it. The words here are ALWAYS the
   platform's — a fixed set of stage strings — and they are mono at the quiet
   colour so they cannot be read as a sentence Pi wrote. A first turn schedules
   a pod, boots a sandbox and starts Pi before there is one character to
   stream; unlabelled dots for that long read as a broken page. */
.demo-stage{font-family:var(--mono);font-size:11px;line-height:1.4;
  color:var(--quiet);letter-spacing:.01em}
.demo-stage:empty{display:none}

/* The model's reasoning (2026-08-17). Quiet, indented to the same 28px as the
   activity lines, and never a bubble: a bubble is something Pi said to the
   reader, and this is not addressed to anybody. Open while it streams and
   folded once the answer starts — the same shape the console's panel uses.
   Capped in height, because a minute of reasoning would otherwise push the
   answer it is leading to off the screen. */
.demo-think{padding-left:28px;font-size:12.5px;line-height:1.55;
  color:var(--quiet);animation:demo-in .4s both}
.demo-think summary{display:flex;align-items:center;gap:7px;cursor:pointer;
  list-style:none;font-family:var(--mono);font-size:11px;letter-spacing:.02em;
  color:var(--gold-dim)}
.demo-think summary::-webkit-details-marker{display:none}
.demo-think summary .demo-spark{transform:none}
.demo-think[open] summary .demo-spark{animation:demo-pulse 1.6s infinite}
@keyframes demo-pulse{0%,100%{opacity:.45}50%{opacity:1}}
@media (prefers-reduced-motion:reduce){.demo-think[open] summary .demo-spark{animation:none}}
.demo-think-text{margin-top:5px;padding-left:9px;border-left:1px solid var(--rule);
  max-height:132px;overflow-y:auto;white-space:pre-wrap;overflow-wrap:anywhere}
/* Trirong for the label, for the reason the stage caption already carries:
   IBM Plex Mono has no Thai glyphs, so "กำลังคิด" was being drawn in whatever
   the system fell back to, at a size chosen for Latin. */
body[data-lang="th"] .demo-think{line-height:1.7}
body[data-lang="th"] .demo-think summary{font-family:Trirong,serif;font-size:12.5px;
  letter-spacing:0}

/* What the agent is touching: the tool's name and the path, the two fields the
   anonymous surface is sent (`config.TOOL_NAMES`). Indented 31px — the avatar's
   22 plus the row's 9 — so it lines up with the words in the bubbles above and
   reads as a margin note rather than as a third speaker. */
.demo-tool{display:flex;gap:8px;align-items:baseline;padding-left:28px;
  font-family:var(--mono);font-size:11px;line-height:1.5;min-width:0;
  animation:demo-in .4s both}
/* Sparkles, the same lucide shape the console's activity line uses. `baseline`
   above aligns text; an svg has none, so it is pushed onto the line by its own
   translate rather than by switching the row back to a box alignment that the
   two text spans would then lose. */
.demo-spark{flex:0 0 auto;width:12px;height:12px;color:var(--gold-dim);
  transform:translateY(2.8px)}
.demo-tool-name{color:var(--gold-dim);flex:0 0 auto}
.demo-tool-path{color:var(--quiet);overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;min-width:0}
body[data-lang="th"] .demo-tool{font-family:var(--mono)}
.demo-typing i{width:5px;height:5px;border-radius:50%;background:var(--gold-dim);
  animation:demo-typing 1.1s infinite}
.demo-typing i:nth-child(2){animation-delay:.16s}
.demo-typing i:nth-child(3){animation-delay:.32s}
@keyframes demo-typing{0%,60%,100%{opacity:.25;transform:translateY(0)}
  30%{opacity:1;transform:translateY(-2px)}}
@media (prefers-reduced-motion:reduce){.demo-typing i{animation:none;opacity:.6}}
.demo-markdown{white-space:normal}
.demo-markdown > :first-child{margin-top:0}
.demo-markdown > :last-child{margin-bottom:0}
.demo-markdown p{margin:0 0 10px}
.demo-markdown h1,.demo-markdown h2,.demo-markdown h3{font-family:var(--serif);color:var(--gold-lit);
  line-height:1.25;margin:15px 0 7px}
.demo-markdown h1{font-size:1.35em}.demo-markdown h2{font-size:1.18em}.demo-markdown h3{font-size:1.05em}
.demo-markdown ul,.demo-markdown ol{margin:4px 0 11px;padding-left:1.35em}
.demo-markdown li+li{margin-top:3px}
.demo-markdown a{color:var(--tea-lit);text-decoration-color:rgb(242 166 90 / .45);text-underline-offset:2px}
.demo-markdown a:hover{color:var(--tea)}
.demo-markdown code{font-family:var(--mono);font-size:.86em;color:var(--gold-dim);
  background:var(--lacquer-deep);border:1px solid var(--rule);border-radius:4px;padding:1px 4px}
.demo-markdown pre{margin:7px 0 11px;padding:10px 12px;overflow-x:auto;background:var(--lacquer-deep);
  border:1px solid var(--rule);border-radius:var(--r-control)}
.demo-markdown pre code{padding:0;border:0;background:none;white-space:pre}
/* There is no rule for a command here on purpose: this panel is sent none.
   `config.TOOL_EVENTS` is off for the anonymous surface, so a `bash` line
   never reaches the page to need styling — PRODUCT.md §20. */
/* Thai, which is where the alignment actually goes wrong. `body[data-lang="th"]
   p` sets 1.92 and reaches inside these bubbles — an element selector beats an
   inherited value — so the same two-line message stands a third taller in Thai
   than in English, and the avatar it is supposed to line up with drifts off the
   first line. `.said` has carried this exact correction since the scripted
   chat; the live panels get it too, and the chip's offset follows the line box
   it centres on (9px + half of 14.5×1.7 − 11 ≈ 10). The stage caption leaves
   the mono for Trirong here for a duller reason: IBM Plex Mono has no Thai
   glyphs at all, so it was being drawn in whatever the system fell back to. */
body[data-lang="th"] .demo-text,
body[data-lang="th"] .demo-you,
body[data-lang="th"] .demo-markdown p,
body[data-lang="th"] .demo-markdown li{line-height:20px}
/* No Thai override for the avatar any more: baseline alignment is language
   independent, which was the point of moving to it. */
body[data-lang="th"] .demo-stage{font-family:Trirong,serif;font-size:11.5px;
  letter-spacing:0}

/* The visitor's own line: the opposite corner, the opposite nub, and a tint
   rather than a step of the ladder, so which of the two people is speaking is
   answered before a word is read. */
.demo-you{align-self:flex-end;max-width:85%;background:rgb(148 207 193 / .1);
  border:1px solid rgb(148 207 193 / .3);border-radius:var(--r-bubble);
  border-bottom-right-radius:2px;padding:8px 12px;
  font-size:14px;line-height:20px;color:var(--ivory);animation:demo-in .4s both}

/* What the session produced, restated at the bottom of the transcript.
   
   It sits on the glass rim rather than a bubble because it is the platform
   speaking, not Pi — the same reason `.demo-system` and the stage row are not
   bubbles. The address is `.url`, which is the page's existing idiom for "the
   URL that just went live" and one of the two tea things a screen is allowed;
   the keep link is deliberately NOT tea, because a third orange thing on the
   same screen is a colour scheme and the contrast disappears (2026-08-09).
   
   Full width and self-start-free: this is a conclusion, not a turn, and
   indenting it to the left column would file it as something somebody said. */
.demo-result{align-self:stretch;margin-top:4px;padding:12px 14px;
  border:1px solid var(--rule);border-radius:12px;background:var(--lift);
  display:flex;flex-direction:column;gap:8px;animation:demo-in .4s both}
@media (prefers-reduced-motion:reduce){.demo-result{animation:none}}
/* `--lift` and `--rule`, not the glass tokens the panel around it uses. On
   paper `--glass` is white at .55 over a white panel and `--glass-edge` is
   white at .8 — measured, the block simply disappeared and its two rows read
   as loose text. The elevation ladder is defined in both themes and is what
   shape is supposed to come from here anyway (2026-08-17). */
/* `align-items:flex-start` so each link's rule stops at the end of the text.
   `.url` is inline-block, which in a flex column stretches to the full row and
   drew a tea line all the way across the panel — an underline the width of the
   container reads as a divider, not as a link. */
.demo-result-row{display:flex;flex-direction:column;align-items:flex-start;gap:2px}
.demo-result-label{font-size:11px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--quiet)}
/* The address inherits `.url` for its colour and rule; only the wrapping is
   new, because an app name plus the apex overflows a 320px panel. */
.demo-result .url{margin-top:0;word-break:break-all}
.demo-result-keep{font-family:var(--mono);font-size:13px;color:var(--ivory);
  text-decoration:none;border-bottom:1px solid var(--rule);word-break:break-all}
.demo-result-keep:hover{border-bottom-color:var(--gold-dim)}
/* Making the link (PRODUCT.md §52). An outline, never a filled button: the
   accent is capped at two per screen and on this panel they are already the
   address and the composer's own button. Once it has been pressed the row
   shows the link instead, so this is only ever on screen before there is
   anything to show. */
.demo-share{font:inherit;font-size:13px;color:var(--ivory);background:none;
  border:1px solid var(--rule);border-radius:8px;padding:4px 10px;cursor:pointer}
.demo-share:hover:not(:disabled){border-color:var(--gold-dim);
  background:rgb(148 207 193 / .06)}
.demo-share:disabled{opacity:.6;cursor:default}

/* `.demo-code`, `.demo-finding` and `.demo-again` were deleted with this pass
   (2026-08-17): they styled the scripted transcript that the real session
   replaced, and nothing has emitted those classes since. */



/* 1040px (2026-08-22, was 880), wider than `.hero-copy`'s 720 and
   deliberately not the same number. The copy above is prose holding a 44ch
   reading measure; the panel is a conversation with code, URLs and a file
   list in it, and at 720 every one of those wrapped twice. Two kinds of
   content that happened to share a number. Still short of `.wrap`'s 1072: a
   panel that reaches the page edges stops reading as a window onto something
   and becomes the page. The panel grew when it became the page's whole offer
   (§33): composer, mode cards and a rail of cards all live inside it now,
   and at 880 the transcript was the leftover. */
@media (min-width:900px){
  .hero{align-items:center;gap:38px}
  .demo{align-self:center}
}


/* ══════════ LIGHT THEME — the default ground since 2026-08-22 (§34) ══════════
   Paper over lacquer, chosen with the toggle in the header; every override
   is prefixed so the dark theme below stays the single source for the
   materials. On paper the two accents swap direction: tea as a FILL goes
   bright with dark ink (the dark theme's own trick, ~7.7:1), tea as TEXT
   goes deep; gold-dim darkens because it is small type on white here. The
   glass pane turns to milk — translucency over a light ground loses text
   faster than over a dark one.══════════════════════════════════════════ */
html[data-theme="light"] body{
  --lacquer:#F5F2ED;   --lacquer-deep:#FBF9F5;  --panel:#FFFFFF;  --lift:#EDF2EF;
  --gold:#2E7A6A;      --gold-lit:#14544A;      --gold-dim:#4F7A6E;
  --tea:#EE9D4F;       --tea-lit:#A35F0E;
  --ivory:#1A211F;     --text:#3C4643;          --quiet:#5F6B67;   --rule:#DCD7CE;
  /* White glass on paper: the pane is milkier than the dark theme's, because
     translucency over a light ground loses text faster than over a dark one. */
  --glass:rgb(255 255 255 / .55);   --glass-2:rgb(255 255 255 / .45);
  --glass-edge:rgb(255 255 255 / .8);
}
/* the room: paper radials instead of lacquer ones */
html[data-theme="light"] body::before{background:
  radial-gradient(70% 58% at 88% 5%,rgb(41 98 84 / .07),transparent 72%),
  radial-gradient(45% 42% at 5% 37%,rgb(242 166 90 / .07),transparent 74%),
  linear-gradient(180deg,#FFFFFE 0,var(--lacquer) 28rem,#EDE9E2 100%)}
/* the grid needs darker ink to be visible at all on paper */
html[data-theme="light"] body::after{opacity:.5;background-image:
  linear-gradient(rgb(46 122 106 / .06) 1px,transparent 1px),
  linear-gradient(90deg,rgb(46 122 106 / .06) 1px,transparent 1px)}
html[data-theme="light"] ::selection{background:var(--tea);color:var(--ivory)}
/* inlay rule — the shell gradient, in its darker steps */
html[data-theme="light"] .inlay,
html[data-theme="light"] section.ruled::before{background:linear-gradient(90deg,transparent,#7FA79D 10%,#2E7A6A 32%,
             #4F7D9C 52%,#8A6E80 72%,#7FA79D 90%,transparent)}
/* ── tea controls: bright fill, dark ink ──
   site.css paints every filled control `background:var(--tea);
   color:var(--lacquer)` — in the dark theme that is near-black ink
   on a light orange. Here --lacquer is cream, which on tea is
   2:1, and forcing white was 3.5:1. Same answer as the dark
   theme, re-derived: the fill brightens and the ink is --ivory. */
html[data-theme="light"] .btn,
html[data-theme="light"] .lang button[aria-pressed="true"],
html[data-theme="light"] .do .paste button,
html[data-theme="light"] .demo-send{color:var(--ivory)}
/* Hover darkens the fill rather than lighting it — on paper a
   control that lightens reads as switching off. */
html[data-theme="light"] .btn:hover,
html[data-theme="light"] .do .paste button:hover,
html[data-theme="light"] .demo-send:hover:not(:disabled){background:#D9832B;border-color:#D9832B;color:var(--ivory)}
/* Tea as TEXT, not fill: the emphasised half of a headline, links
   in answers, the demo's system line. #A35F0E is 4.3:1 on cream
   and 5:1 on white. */
html[data-theme="light"] em{color:var(--tea-lit)}
/* AI Agent Logo Line in light theme */
html[data-theme="light"] .works-track img{
  filter:brightness(0);opacity:.5;
}
html[data-theme="light"] .works-track li:hover img{
  filter:none;opacity:1;
}
html[data-theme="light"] .works-track li span{
  color:var(--quiet);
}
html[data-theme="light"] .works-track li:hover span{
  color:var(--gold-lit);
}
/* the band's recessed ground */
html[data-theme="light"] .do{background:rgb(46 74 64 / .04)}
/* the lit plan */
html[data-theme="light"] .plan.pick{background:linear-gradient(155deg,#FFFFFE,#F2EEE7)}
/* the elephant floats on paper, not on night */
html[data-theme="light"] .hero-glyph{filter:drop-shadow(0 18px 40px rgb(21 46 40 / .18))}
/* cards that derive a colour from the app's name: L* 88, not 28 */
html[data-theme="light"] .theme,
html[data-theme="light"] .lang{
  background:#FFFFFF;
  border-color:rgb(46 122 106 / .22);
  color:var(--ivory);
  box-shadow:0 1px 4px rgb(21 46 40 / .06);
}
html[data-theme="light"] .theme:hover,
html[data-theme="light"] .lang:hover{
  border-color:var(--gold);
  color:var(--gold-lit);
}
/* ── the pane, in white glass ──
   The dark theme's coloured glows become soft green/tea haloes at a third
   the strength, and the top rim highlight brightens — glass on paper reads
   by its edge, not by its shadow. The inner strips are milk, not dark. */
html[data-theme="light"] .demo{
  background:rgb(255 255 255 / .7);
  border-color:rgb(46 122 106 / .22);
  box-shadow:0 24px 60px rgb(21 46 40 / .12),
             0 0 80px 20px rgb(41 98 84 / .08),
             inset 0 1px 0 rgb(255 255 255 / .95);
}
html[data-theme="light"] .demo-kinds{
  background:#EAE5DC;
  border-bottom:1px solid rgb(46 122 106 / .22);
}
html[data-theme="light"] .demo-kinds button{
  box-shadow:1px 0 0 0 rgb(46 122 106 / .18);
  color:var(--quiet);
}
html[data-theme="light"] .demo-kinds button:last-child{box-shadow:none}
html[data-theme="light"] .demo-kinds button:hover{
  background:rgb(255 255 255 / .5);
}
html[data-theme="light"] .demo-kinds button[aria-selected="true"]{
  background:#FFFFFF;
}
html[data-theme="light"] .demo-kinds button[aria-selected="true"] .mode-title{
  color:var(--gold-lit);
}
html[data-theme="light"] .demo-kinds button[aria-selected="true"] .mode-what{
  color:var(--gold-lit);
}
/* The surface moved to `.demo-row` when the controls came inside the box
   (2026-08-29), so the paper override has to move with it — left on the
   textarea it painted a white field inside a lacquer-dark container, on a
   paper page. The field itself is only text now, in both themes. */
html[data-theme="light"] .demo-row{
  background:#FFFFFF;
  border-color:rgb(21 46 40 / .14);
  box-shadow:0 1px 3px rgb(21 46 40 / .05);
}
html[data-theme="light"] .demo-row:focus-within{
  border-color:rgb(21 46 40 / .28);
  box-shadow:0 2px 8px rgb(21 46 40 / .07);
}
html[data-theme="light"] .demo-input{color:var(--ivory)}
/* Both of these are painted for lacquer — a white-on-white .05 fill and a
   `--glass-edge` that is white at .8 leave the clip with no surface at all on
   paper, and the expand button keeps a near-black chip. */
html[data-theme="light"] .demo-attach{
  background:#F6F4F0;
  border-color:rgb(21 46 40 / .12);
}
html[data-theme="light"] .demo-attach:hover{
  background:rgb(21 46 40 / .06);
  border-color:rgb(21 46 40 / .22);
}
/* The chip is the same trap one step further in: `--lift` on paper is #EDF2EF
   and `--glass-edge` is white, so a file chip inside a white composer would be
   a pale shape with no edge at all. It needs a real hairline, like every other
   surface that sits on the composer's own white. */
html[data-theme="light"] .demo-file{
  background:#F6F4F0;
  border-color:rgb(21 46 40 / .12);
}
html[data-theme="light"] .demo-file-glyph{background:#FFFFFF}
html[data-theme="light"] .demo-file-x:hover{background:rgb(21 46 40 / .07)}
html[data-theme="light"] .demo-expand{
  background:rgb(255 255 255 / .92);
  border-color:rgb(21 46 40 / .14);
  box-shadow:0 1px 3px rgb(21 46 40 / .06);
}
html[data-theme="light"] .demo-chip{
  background:#FFFFFF;
  border-color:rgb(46 122 106 / .18);
  box-shadow:0 1px 3px rgb(21 46 40 / .04);
}
html[data-theme="light"] .demo-chip:hover{
  border-color:var(--gold);
  color:var(--gold-lit);
}
html[data-theme="light"] .demo-open{
  background:#FFFFFF;
  border-color:rgb(46 122 106 / .2);
  box-shadow:0 2px 8px rgb(21 46 40 / .05);
}
html[data-theme="light"] .demo-open:hover{
  background:#FFFFFF;
  border-color:var(--gold);
  box-shadow:0 6px 16px rgb(21 46 40 / .08);
}
html[data-theme="light"] .demo-tag{
  background:rgb(224 112 48 / .1);
  color:#C25418;
  border-color:rgb(224 112 48 / .3);
}


/* ── the panel, full screen ──────────────────────────────────────────────
   A conversation and the thing it is building do not fit in a card on a
   phone, which is where most people meet this. The button is in the panel's
   own corner rather than the page's: it belongs to the thing it expands.   */
.demo-full{position:fixed;inset:0;z-index:80;border-radius:0;max-width:none;
  height:100dvh;display:flex;flex-direction:column;
  /* Opaque, because the panel is a pane of glass in the card and glass over
     the whole viewport is the page reading through the conversation. */
  background:var(--lacquer)}
.demo-full .demo-body{flex:1;max-height:none}
/* **Full screen widens the column** (2026-09-02). `--chat-col` is 760px because
   that is a reading measure inside a ~1000px card; held at 760 across a 1440px
   viewport it becomes a strip down the middle of a black screen, which is not
   what somebody presses this button for. `min()` rather than a flat number so
   the room is what decides, and only above 1100px: below that the viewport is
   already narrower than 760 and the column is the pane. The bubbles are 92% of
   it, so a Pi answer runs long here — that is the trade this button is, and it
   is why the card keeps the measure. */
@media (min-width:1100px){.demo-full{--chat-col:min(1080px,86vw)}}
/* **The shell's `backdrop-filter` is a containing block** (2026-08-29), for
   fixed descendants as well as absolute ones — which broke both halves of
   this control at once. The button resolved `top/right` against `.demo` and
   sat in the tab strip's corner instead of the conversation's; and
   `.demo-full`'s `position:fixed;inset:0` resolved there too, so full screen
   was a panel that stayed inside the card and hung 950px out of the bottom of
   it. `.demo-chat` is now positioned, which fixes the first, and the shell
   drops its filter while a panel is full screen, which fixes the second. The
   filter is invisible then anyway: the panel covers it. */
.demo.is-full{backdrop-filter:none;-webkit-backdrop-filter:none}
.demo-expand{position:absolute;top:10px;right:10px;z-index:3;
  width:32px;height:32px;display:grid;place-items:center;
  border:1px solid var(--glass-edge);background:rgb(7 9 8 / .55);color:var(--quiet);
  border-radius:10px;cursor:pointer;transition:color .16s,border-color .16s}
.demo-expand:hover{color:var(--ivory);border-color:var(--gold)}
.demo-expand svg{width:15px;height:15px}

/* ══ academy — /academy/ and /th/academy/ (2026-09-02, renamed 2026-09-06) ══
   Long-form pages meant to be *shared*: a link pasted into Facebook lands
   here, so unlike the legal pages under `.doc` these follow the landing —
   paper by default, lacquer on the toggle (§34). That is the whole reason
   they do not reuse `.doc`, which hard-codes `rgba(255,255,255,…)` hairlines
   and only ever renders on lacquer; a reader who chose paper on the landing
   and then opened a post would watch the site change colour under them.

   No `site.js` here. It is 83KB of landing behaviour — tabs, the lab panel,
   the demo — and a post has none of those; the two things these pages do need
   (remember the theme, copy one line) are a dozen lines inline. Language is a
   real URL, `/academy/x/` ⇄ `/th/academy/x/`, not a toggle: a Thai lesson has to be
   shareable and indexable on its own address.

   Accent budget is the landing's (§10, two `--tea` things per screen, and at
   least one): an `<em>` in the title, an `<em>` in about half the headings,
   and the button at the end. Shape comes from the elevation ladder — a card
   is `--panel` and lifts to `--lift` — never from more orange. */
.post{max-width:44rem;margin:0 auto;padding:clamp(2rem,5vw,3rem) 24px clamp(4rem,8vw,6rem)}
.post-head{margin:0 0 clamp(2.2rem,5vw,3rem)}
.post .kicker{margin:0 0 1rem;font-family:var(--mono);font-size:10.5px;
  letter-spacing:.16em;text-transform:uppercase;color:var(--quiet)}
.post h1{font-size:clamp(2rem,5.4vw,3.1rem);line-height:1.14;margin:0;
  text-wrap:balance;letter-spacing:-.01em}
.post .lede{margin:1.2rem 0 0;font-size:clamp(1.02rem,2.2vw,1.16rem);line-height:1.72;
  color:var(--ivory);opacity:.88;text-wrap:pretty}
.post .byline{margin:1.6rem 0 0;font-family:var(--mono);font-size:11.5px;
  letter-spacing:.06em;color:var(--quiet)}
/* A lesson may need a table — DNS records are three columns and prose about
   them is worse. NOT `.doc table`, which is the legal pages': those are
   lacquer-only and hard-code `rgba(255,255,255,.08)` hairlines, so on a post,
   which follows the landing's ground and defaults to paper, they would be
   invisible. `--rule` is the token that moves with the theme.
   Scrolls inside itself rather than widening the page — the same rule the
   docs site records: a long value must not make the whole article scroll
   sideways on a phone. */
.post .rows{overflow-x:auto;margin:1.5rem 0}
.post table{width:100%;border-collapse:collapse;font-size:.88rem}
.post th{text-align:left;font-family:var(--mono);font-size:10.5px;letter-spacing:.08em;
  text-transform:uppercase;opacity:.55;font-weight:500;padding:0 .7rem .5rem 0;
  border-bottom:1px solid var(--rule);white-space:nowrap}
.post td{padding:.6rem .7rem .6rem 0;border-bottom:1px solid var(--rule);vertical-align:top}
.post td:first-child,.post td:last-child{font-family:var(--mono);font-size:.82rem}
.post td:first-child{opacity:.6;white-space:nowrap}

.post h2{font-size:clamp(1.32rem,3vw,1.62rem);line-height:1.3;margin:3.2rem 0 .9rem;
  text-wrap:balance}
.post h3{font-size:1.05rem;line-height:1.45;margin:2rem 0 .5rem;color:var(--gold-lit);
  font-family:var(--sans);font-weight:500}
.post p{margin:0 0 1.1rem;font-size:1.02rem;line-height:1.85}
.post strong{color:var(--ivory);font-weight:500}
/* Underline links in FLOWING TEXT only. Scoped to `p` and `li` rather than to
   `.post a`, because the index's cards and the footer line are links too — as
   `.post a` this rule underlined every word of a card, title and date
   included, and no `text-decoration:none` on a single-class selector could
   outrank it. */
.post p a:not(.btn),.post li a:not(.btn){color:var(--gold-lit);text-decoration:underline;
  text-decoration-color:var(--gold-dim);text-underline-offset:3px}
.post p a:not(.btn):hover,.post li a:not(.btn):hover{color:var(--tea)}
.post ul,.post ol{margin:0 0 1.2rem;padding:0;list-style:none;counter-reset:step}
.post li{position:relative;padding-left:1.5rem;line-height:1.8;margin-bottom:.65rem}
.post ul li::before{content:"";position:absolute;left:0;top:.86em;width:.6rem;height:1px;
  background:var(--gold-dim)}
.post ol li{counter-increment:step;padding-left:2rem}
.post ol li::before{content:counter(step);position:absolute;left:0;top:.1em;
  font-family:var(--mono);font-size:11px;color:var(--gold);opacity:.8}
/* An aside the eye can skip: a real surface off the ground, per the ladder. */
.post .note{margin:1.8rem 0;padding:1.1rem 1.25rem;background:var(--panel);
  border:1px solid var(--rule);border-radius:var(--r-surface)}
.post .note p:last-child{margin-bottom:0}
.post .note p{font-size:.96rem}
.post .paste{margin:1.2rem 0}
.post figure{margin:2rem 0}
.post figure img{width:100%;border:1px solid var(--rule);border-radius:var(--r-surface);display:block}
.post figcaption{margin-top:.6rem;font-size:.85rem;color:var(--quiet)}
/* The plan strip: four cards, the same object the landing's `.plan` is, at the
   size a paragraph can carry. */
.post .tiers{display:grid;gap:12px;margin:1.6rem 0 1.2rem;grid-template-columns:1fr}
@media(min-width:620px){.post .tiers{grid-template-columns:repeat(2,1fr)}}
@media(min-width:900px){.post .tiers{grid-template-columns:repeat(4,1fr)}}
/* Name, price and sentence are three rows of the list's own grid, so four
   tiers whose names or prices wrap differently still line up. `row-gap:0`
   because the list's 12px is the space between two cards, not between the
   lines inside one — a subgrid may override the gutter it inherits, and the
   elements' own margins set the spacing here. */
.post .tiers div{padding:1rem 1.05rem;background:var(--panel);border:1px solid var(--rule);
  border-radius:var(--r-surface)}
@media(min-width:620px){
  .post .tiers div{display:grid;grid-template-rows:subgrid;grid-row:span 3;row-gap:0}
}
.post .tiers b{display:block;font-weight:500;color:var(--ivory);font-size:.95rem}
.post .tiers .amt{display:block;margin:.15rem 0 .5rem;font-family:var(--mono);
  font-size:.86rem;color:var(--gold)}
.post .tiers span:last-child{display:block;font-size:.88rem;line-height:1.6}
/* The end of a post is the one place it asks for something. */
.post .cta{margin:3.4rem 0 0;padding:1.8rem 0 0;border-top:1px solid var(--rule);text-align:center}
.post .cta p{margin:0 0 1.2rem}
/* `.post-foot`, not `.foot`: the landing's footer already owns that name and
   it is `display:flex;justify-content:space-between`, which turned this line
   of links into four columns spread across the column. A class that is only
   ever used inside `.post` still shares the one global stylesheet. */
.post-foot{margin:3rem 0 0;padding-top:1.4rem;border-top:1px solid var(--rule);
  font-size:.85rem;color:var(--quiet)}
.post p.post-foot a{color:inherit;text-decoration:none}
.post-foot a:hover{color:var(--gold-lit)}

/* The index. One row per post, and a row is a card that lifts — the ladder
   again, because a list of links with nothing but a hairline between them is
   the flat page §10 was written about. */
.posts{display:grid;gap:14px;margin:2.4rem 0 0}
.posts a{display:block;padding:1.3rem 1.4rem;background:var(--panel);
  border:1px solid var(--rule);border-radius:var(--r-surface);text-decoration:none;
  transition:background .2s,border-color .2s,transform .2s}
.posts a:hover{background:var(--lift);border-color:var(--gold-dim);transform:translateY(-2px)}
.posts h2{margin:0;font-size:1.25rem;line-height:1.35}
.posts p{margin:.5rem 0 0;font-size:.96rem;line-height:1.7}
.posts .when{display:block;margin:.8rem 0 0;font-family:var(--mono);font-size:10.5px;
  letter-spacing:.14em;text-transform:uppercase;color:var(--quiet)}
/* The archive page only (academy/index.html, not the picker cards a getting-
   started post reuses `.posts` for): wider than the 44rem article column,
   since a list of links has no reading-line-length to respect, and one row
   per post — title left, date right — rather than a card, because at seven
   entries the blurb under each title was more scroll than signal. */
.post-archive{max-width:56rem}
.post-archive .posts{display:block;gap:0;margin:1.6rem 0 0;border-top:1px solid var(--rule)}
.post-archive .posts a{display:flex;align-items:baseline;justify-content:space-between;
  gap:1.5rem;padding:1.05rem 4px;background:none;border:none;border-bottom:1px solid var(--rule);
  border-radius:0;transform:none}
.post-archive .posts a:hover{background:none;border-color:var(--rule)}
.post-archive .posts a:hover h2{color:var(--gold-lit)}
.post-archive .posts h2{font-size:1.02rem;line-height:1.4;font-weight:400;
  transition:color .2s}
.post-archive .posts p{display:none}
.post-archive .posts .when{margin:0;flex-shrink:0;white-space:nowrap}

/* --- the academy ---
   A course page, not an article and not an archive. `.post` still gives it
   type, measure and colour, so the prose rules have to be taken back off one
   at a time at the bottom of this block — `.post ol li::before` prints its own
   counter, `.post li` indents by a bullet gutter, and `.post li a:not(.btn)`
   underlines links in running text, which here drew a line under whole
   paragraphs. Found by reading computed style; two earlier attempts lost on
   specificity and looked like they had worked. */
/* **No article padding.** `.post` is the reading column and pads
   48/24/96 to sit a paragraph in the middle of a page; this is a landing for a
   course, and that top padding pushed the hero down as if it were the opening
   of an essay. The 24px stays as the horizontal gutter — it is the same one
   `.wrap` uses and the only thing between the cards and the edge of a phone.
   The width is `.wrap`'s too, so the hero's first letter sits under the
   wordmark: at 44rem the header started at 104px and the cards at 184, which
   reads as two pages stacked rather than one. */
.academy{max-width:1120px;padding:0 24px}
.academy-hero{padding:0 0 2.4rem;border-bottom:1px solid var(--rule)}
.academy-hero > .label{margin:0 0 .9rem}
.academy h1{font-size:clamp(1.9rem,4.2vw,2.7rem);line-height:1.2;margin:0;max-width:22ch}
.academy .lead{font-size:1.05rem;line-height:1.75;margin:1rem 0 0;max-width:48ch}
.academy-go{display:flex;flex-wrap:wrap;gap:10px;margin:1.7rem 0 0}
/* How much of it exists, said on the page rather than discovered by clicking.
   A course that shows seventeen cards and delivers seven has to say which is
   which before somebody counts. */
.academy-count{margin:1.1rem 0 0;font-family:var(--mono);font-size:11px;
  letter-spacing:.06em;color:var(--quiet)}

.track{margin:3rem 0 0}
/* The first one follows the hero rule, which is already a separator. */
.track:first-of-type{margin-top:2rem}
.track-head{display:flex;align-items:flex-start;gap:.9rem}
.track-head .mk{flex:0 0 auto;width:26px;height:26px;color:var(--gold-dim);margin-top:.15rem}
.track-head h2{margin:0;font-size:1.35rem;line-height:1.3}
.track-head p{margin:.35rem 0 0;font-size:.97rem;line-height:1.7;max-width:52ch}

/* Cards, because a course is a set of things you choose between and a list is
   a set of things you do in order. `subgrid` so the meta line, the title and
   the sentence sit on the same three lines across a row whatever the titles
   do — the defect this repo spent a day removing everywhere else. */
.cards{list-style:none;display:grid;gap:12px;margin:1.4rem 0 0;padding:0;
  grid-template-columns:repeat(auto-fill,minmax(248px,1fr))}
.post .cards > li{margin:0;padding:0;position:static;display:grid;
  grid-template-rows:subgrid;grid-row:span 3}
.post .cards > li::before{content:none;display:none}
.post .cards > li > a,.post .cards > li > span{display:grid;grid-template-rows:subgrid;
  grid-row:span 3;gap:0;height:100%;padding:1rem 1.05rem;background:var(--panel);
  border:1px solid var(--rule);border-radius:var(--r-surface);
  text-decoration:none;color:inherit;transition:background .2s,border-color .2s}
.post .cards > li > a:hover{background:var(--lift);border-color:var(--gold-dim)}
.card-meta{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--gold-dim)}
.cards h3{margin:.5rem 0 0;font-size:1.02rem;line-height:1.4;color:var(--gold-lit)}
.cards p{margin:.4rem 0 0;font-size:.9rem;line-height:1.6;color:var(--text)}

/* Not written yet, and saying so plainly. Quieter but not hidden: the shape of
   the course is the useful part, and a card that pretends to be a link when it
   opens nothing is worse than one that says it is not ready. */
.cards li.soon > span{background:none;border-style:dashed;cursor:default}
.cards li.soon .card-meta{color:var(--quiet)}
.cards li.soon h3{color:var(--text)}
.cards li.soon p{color:var(--quiet)}

/* Taking the article rules back off — see the note at the top of this block. */
.post .cards li a:not(.btn),.post .cards li a:not(.btn):hover{text-decoration:none}
/* "Put this in your MCP settings" is the step people stop at, so the tab says
   where those settings are. Quiet and under the box: somebody who already
   knows has copied the address and gone. */
.do-help{margin:10px 0 0;font-size:12.5px;line-height:1.6;color:var(--quiet);text-align:center}
.do-help a{color:var(--gold-dim);text-decoration:underline;
  text-decoration-color:var(--rule);text-underline-offset:3px}
.do-help a:hover{color:var(--gold-lit)}
body[data-lang="th"] .do-help{font-family:Trirong,serif;font-size:13.5px}

/* The one place on this page that asks for something back. A band rather than
   a card, at the end rather than in the hero: somebody who has just read what
   the course covers is the person worth asking, and somebody who has read
   nothing yet is not. `--lift` over the page's own ground so it separates
   without a second accent — the tea is already spent on the hero button, and
   two per screen is the whole budget (§10). */
.consult{margin:3.4rem 0 0;padding:1.9rem 1.7rem;background:var(--lift);
  border:1px solid var(--rule);border-radius:var(--r-surface)}
.consult h2{margin:0;font-size:1.3rem;line-height:1.3}
.consult p{margin:.55rem 0 0;font-size:.99rem;line-height:1.72;max-width:52ch}
.consult-go{display:flex;flex-wrap:wrap;gap:10px;margin:1.3rem 0 0}
body[data-lang="th"] .consult h2{font-family:Trirong,serif}

body[data-lang="th"] .academy h1,
body[data-lang="th"] .track-head h2,
body[data-lang="th"] .cards h3{font-family:Trirong,serif}
}
/* Inline code in prose. Nacre rather than tea: a filename is not motion, and
   the accent is capped at two things per screen (§10). */
.post code{font-family:var(--mono);font-size:.88em;color:var(--gold-lit);
  background:var(--panel);border:1px solid var(--rule);border-radius:var(--r-control);
  padding:.1em .38em;white-space:nowrap}
