/* ============================================================================
   AI WEBSITE BUILDER - layout templates. "Paper" edition, violet.
   ----------------------------------------------------------------------------
   Sibling of vola.css. Same language, same bones: warm chalk paper, a light
   editorial serif doing display, a grotesque doing body, handwritten
   marginalia for labels, a faint grid, hairline rules, ONE saturated accent
   ink, and a page that MODULATES - paper carries the reading, the accent
   carries the short look-at-this bands.

   What is deliberately different from vola.css, and why:

   1. THE ACCENT IS NOT THE BRAND COLOUR, AND THAT IS THE POINT.
      The product's primary is #8450C2. Measured on this paper it is 4.76:1 -
      it scrapes AA for body text and fails the moment anything is set small or
      light. This page's own subject is a system whose V3 audit says "at least
      two palettes land near or below 4.5:1"; shipping a case study about that
      failure WITH that failure would be the worst joke on the site.

      So #8450C2 stays a CONTENT colour - it appears in the owner's sheets, the
      chip matrix and on the drawn UI, where it is what the product actually
      uses. The page's structural ink is the same hue driven to ink
      weight, --violet #4B2A82, which measures 9.5:1 on paper and takes white
      at 9.5:1 when it is the band. Both roles are the brand; only one of them
      is allowed to be text.

   2. --spark IS A FILL, NEVER A TEXT COLOUR ON PAPER.
      Same separation vola.css makes between --court and --tape, and the value
      is borrowed from the product's own `accent-strong`, #FACC15. On paper it
      measures 1.36:1, so it is the marker, the active tick and the data bar,
      and it is never ink. On the violet band it is 7.0:1 and can be.

   3. THERE IS A MONOSPACE, AND VOLA HAS NONE.
      This page quotes real column arithmetic, real token names and real
      values. Set in the body grotesque, `(1296 - 2 x 24) / 3 = 416` reads as a
      sentence that went wrong. It is a system stack - ui-monospace first - so
      it costs nothing over the network.

   4. THE OWNER'S ARTWORK IS SHOWN AS MADE, AND NOTHING IS DRAWN TWICE.
      This page used to draw every miniature in CSS - hero variants, density
      states, a colourway demo - because no export was on hand. Once the owner
      supplied the real Figma boards and sheets, each drawing became a second,
      worse copy of something on the page, and they went. What is left drawn
      is the builder panel (.panel), which has no export. The boards live on a
      canvas (.boards), the sheets in a track (.kit) and a figure (.sheet).

   No build step, no dependency. Every colour is a token.
   ========================================================================= */

/* ---------------------------------------------------------------- TOKENS -- */
:root{
  /* Surfaces. Identical to vola.css - this is the site's paper, not this
     page's paper, and two case studies on two creams is how a palette drifts. */
  --paper:#F4F1E8;
  --ink:#14140F;
  --ink-2:#4C4A40;          /* body copy */
  --ink-3:#6A6759;          /* captions only, never below 13px */

  /* THE VIOLET, AND IT HAS TWO JOBS - the same split vola.css makes with its
     green. --violet is the GROUND the inverted band and the
     solid button are painted on; --violet-ink is the same violet used as TEXT.
     They are equal in the light theme and they are NOT in the dark one, which
     is the whole reason they are two tokens. */
  --violet:#4B2A82;
  --violet-hover:#3C216A;
  --violet-ink:#4B2A82;
  /* Text that sits ON the violet ground. Near-paper in BOTH themes, because
     the ground it sits on is the same deep violet in both. */
  --on-violet:#F4F1E8;
  /* ...and on the spark, which is a LIGHT fill in both themes, so its ink is
     dark in both. Inheriting --ink here is the bug vola.css paid for: the
     moment --ink flips, yellow under near-white measures 1.4:1. */
  --on-spark:#14140F;
  --spark:#FACC15;          /* FILL ONLY - never text on paper */
  --brand:#8450C2;          /* the PRODUCT's primary. Content, never structure */

  /* Surfaces that are a tint of the ink rather than a colour of their own.
     Tokenised because a 3.5%-black wash is invisible on a dark ground. */
  --pane-bg:rgba(255,255,255,.42); /* .pane, .switch */
  --violet-wash:rgba(75,42,130,.08);

  /* The slab is the page's one fixed-dark surface and it stays dark in both
     themes - inverting it would flatten the structural contrast the whole page
     is built on. Same argument vola.css makes for its green band. */
  --slab-bg:#14140F;
  --slab-ink:#F4F1E8;

  --line:rgba(20,20,15,.14);       /* hairline on paper */
  --line-2:rgba(20,20,15,.07);     /* the chalk grid on paper */
  --line-on:rgba(244,241,232,.20); /* hairline on violet */
  --grid-on:rgba(244,241,232,.06); /* the chalk grid on violet */

  /* Type. Same three families as vola.css, plus the system monospace. */
  --serif:'Newsreader',Georgia,'Times New Roman',serif;
  --sans:'Manrope',system-ui,-apple-system,'Segoe UI',sans-serif;
  --hand:'Kalam','Bradley Hand',cursive;
  --mono:ui-monospace,'SF Mono',SFMono-Regular,Menlo,Consolas,monospace;

  /* One scale, clamped, and the same one - so the two case studies set their
     headlines at the same size at the same width. */
  --fs-hero:clamp(40px,6.4vw,100px);
  --fs-statement:clamp(25px,3.3vw,50px);
  --fs-section:clamp(29px,3.6vw,58px);
  --fs-feat:clamp(23px,2.2vw,34px);
  --fs-lead:clamp(17.5px,1.45vw,21.5px);
  --fs-body:clamp(16px,1.22vw,18px);
  --fs-micro:12px;

  /* THE MEASURE. 57ch, measured off the rendered line rect rather than the
     source string - the whole derivation is in vola.css and it did not change
     because the typeface did not change. */
  --measure:57ch;

  /* Rhythm. --sec-x matches nav.css exactly, so the bar's edge and every
     section's edge are one line down the page. */
  --sec-x:clamp(20px,6vw,120px);
  --sec-y:clamp(84px,9.5vw,164px);
  --maxw:1500px;
  --r:18px;
  --r-lg:28px;
  --ease:cubic-bezier(.22,.61,.36,1);

  /* THE SHARED NAVBAR, re-tinted through its own tokens and nothing else.
     Never restyle .nav/.cta/.mnav from here - that is the moment there are two
     navbars. This page's bar is paper with violet ink. */
  --nav-bg:#F4F1E8;
  --nav-ink:#14140F;
  --nav-muted:#6A6759;
  --nav-faint:#8C8879;
  --nav-line:rgba(20,20,15,.14);
  --nav-fill:rgba(75,42,130,.06);
  --nav-edge:rgba(75,42,130,.32);
  --nav-edge-hi:rgba(75,42,130,.62);
  --nav-hair:rgba(20,20,15,.10);
  --nav-press:rgba(75,42,130,.07);
  --nav-grid:rgba(20,20,15,.045);
  --nav-eye-ring:rgba(20,20,15,.22);
  --nav-accent:#4B2A82;
  --nav-serif:var(--serif);
  --nav-sans:var(--sans);
}

/* ------------------------------------------------------------------ BASE -- */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}

body{
  margin:0;
  background:var(--paper);
  color:var(--ink);
  font-family:var(--sans);
  font-size:var(--fs-body);
  line-height:1.62;
  font-synthesis-weight:none;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}

img,video,svg{display:block;max-width:100%}
a{color:var(--violet-ink);text-underline-offset:3px;text-decoration-thickness:1px}
a:hover{text-decoration-thickness:2px}

h1,h2,h3,h4{
  font-family:var(--serif);
  font-weight:300;
  line-height:1.04;
  letter-spacing:-.022em;
  margin:0;
  color:var(--ink);
  text-wrap:balance;
}
/* A 300-weight serif is beautiful at 96px and spindly at 40px. Below 600 the
   display weight steps UP rather than the size stepping down. */
@media (max-width:600px){
  h1,h2,h3{font-weight:400;letter-spacing:-.014em}
}

p{margin:0;color:var(--ink-2);text-wrap:pretty}
p + p{margin-top:1.05em}

/* EVERY WIREFRAME ON THIS PAGE IS A <figure>, and the UA gives a figure
   `margin:1em 40px`. Unreset, that inset all nineteen of them by 40px a side
   inside their own grid cell - a 295px column rendering a 215px wireframe,
   with the caption starting 40px right of the section edge it is supposed to
   align with. Measured; it was the single biggest layout error on the first
   render and it is invisible until you check an edge against a rule. */
figure,figcaption{margin:0}

code,kbd{font-family:var(--mono);font-size:.88em;letter-spacing:-.01em}

::selection{background:var(--spark);color:var(--on-spark)}

:focus-visible{outline:2.5px solid var(--violet-ink);outline-offset:3px;border-radius:5px}
.band :focus-visible{outline-color:var(--spark)}

.skip{
  position:fixed;left:12px;top:-80px;z-index:200;
  background:var(--violet);color:var(--on-violet);
  padding:12px 18px;border-radius:10px;font-weight:700;
  text-decoration:none;transition:top .18s var(--ease);
}
.skip:focus{top:12px}

/* ------------------------------------------------------------ STRUCTURE -- */
/* ONE-SIDED RHYTHM. Nothing on paper has a background of its own, so a
   section's bottom pad and the next one's top pad are the same empty stretch
   bought twice. Only .close pays a bottom pad. */
.sec{position:relative;padding:var(--sec-y) var(--sec-x) 0}

/* The inverted band. Full-bleed by construction rather than by a negative
   margin, so it can never be the thing that creates a horizontal scrollbar. */
.band{
  background:var(--violet);color:var(--on-violet);
  padding:clamp(62px,7vw,116px) var(--sec-x);
  position:relative;overflow:hidden;
}
.band h2,.band h3,.band h4{color:var(--on-violet)}
.band p{color:rgba(244,241,232,.84)}
.band a{color:var(--spark)}

/* THE CHALK GRID. Two repeating gradients, no image. */
.grid-bg::before{
  content:'';position:absolute;inset:0;pointer-events:none;z-index:0;
  background:
    repeating-linear-gradient(90deg,var(--gc,var(--line-2)) 0 1px,transparent 1px var(--gs,84px)),
    repeating-linear-gradient(0deg, var(--gc,var(--line-2)) 0 1px,transparent 1px var(--gs,84px));
  -webkit-mask-image:radial-gradient(112% 82% at 50% 26%,#000 28%,transparent 96%);
          mask-image:radial-gradient(112% 82% at 50% 26%,#000 28%,transparent 96%);
}
.band.grid-bg::before{--gc:var(--grid-on)}
.grid-bg > *{position:relative;z-index:1}
@media (max-width:600px){.grid-bg::before{--gs:56px}}

/* --------------------------------------------------------- MARGINALIA -- */
.marg{
  display:inline-flex;flex-direction:column;gap:1px;
  font-family:var(--hand);font-weight:400;
  font-size:clamp(19px,1.45vw,23px);
  line-height:1.2;color:var(--violet-ink);letter-spacing:0;
}
.band .marg{color:var(--spark)}
.marg svg{width:100%;height:9px;overflow:visible}
.marg svg path{fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;opacity:.7}

/* THE MARKER. --spark as a fill behind ink, never as ink itself. */
mark{
  background:var(--spark);color:var(--on-spark);
  padding:.02em .16em .08em;border-radius:3px;
  -webkit-box-decoration-break:clone;box-decoration-break:clone;
}

.lead{font-size:var(--fs-lead);line-height:1.55;color:var(--ink-2);max-width:var(--measure)}

/* The small caps label that opens a sub-block inside a section. The PDF's
   eyebrow, which is 16px with 4px of tracking - a large size made to read as a
   small label. Set here at 12.5 with .16em, which is the same trick at web
   sizes where 16px tracked that hard starts to fall apart. */
.eyebrow{
  display:block;margin:0 0 14px;
  font-family:var(--sans);font-weight:700;
  font-size:var(--fs-micro);line-height:1.2;
  letter-spacing:.16em;text-transform:uppercase;
  color:var(--ink-3);
}
.band .eyebrow{color:rgba(244,241,232,.62)}

/* ------------------------------------------------------------- BUTTONS -- */
.btn{
  display:inline-flex;align-items:center;gap:10px;
  min-height:48px;padding:13px 22px;      /* >=44px touch target everywhere */
  border-radius:999px;border:1.5px solid transparent;
  font-family:var(--sans);font-weight:700;font-size:15px;letter-spacing:-.01em;
  text-decoration:none;cursor:pointer;
  transition:background .2s var(--ease),color .2s var(--ease),
             border-color .2s var(--ease),transform .2s var(--ease);
}
.btn-solid{background:var(--violet);color:var(--on-violet)}
.btn-solid:hover{background:var(--violet-hover)}
.btn-ghost{background:transparent;color:var(--ink);border-color:var(--line)}
.btn-ghost:hover{border-color:var(--ink);background:rgba(20,20,15,.04)}
.band .btn-solid{background:var(--spark);color:var(--on-spark)}
.band .btn-solid:hover{background:#FFD83C}
.band .btn-ghost{color:var(--on-violet);border-color:var(--line-on)}
.band .btn-ghost:hover{border-color:var(--on-violet);background:rgba(244,241,232,.08)}

/* Position-based, never class-based: a LEADING arrow nudges the way it points
   (left), a TRAILING one goes up and right. */
.btn svg{flex:none;transition:transform .22s var(--ease)}
.btn:hover svg:first-child{transform:translateX(-3px)}
.btn:hover svg:last-child{transform:translate(3px,-2px)}

/* ============================================================== 01 HERO -- */
.hero{
  position:relative;
  padding:calc(var(--nav-h) + clamp(46px,6vw,92px)) var(--sec-x) 0;
}
/* Three areas, and the DOM is in the same order as all three of them - so the
   visual, tab and reading orders are one order at every width. No `order`
   anywhere; the areas re-flow instead. Same rule vola.css states at length. */
.hero-in{
  max-width:var(--maxw);margin:0 auto;
  display:grid;
  grid-template-columns:minmax(0,1fr);
  grid-template-areas:"copy" "plate" "meta";
  gap:clamp(34px,4vw,64px);
  align-items:start;
}
.hero-copy{grid-area:copy;padding-top:clamp(4px,1vw,18px);text-align:center;display:flex;flex-direction:column;align-items:center}
.hero-copy .lead{margin-left:auto;margin-right:auto}
.hero h1{margin-left:auto;margin-right:auto}
.hero-plate{grid-area:plate}
.hero-meta{grid-area:meta}

.hero h1{font-size:var(--fs-hero);margin:18px 0 0;max-width:17ch}
.hero .lead{margin-top:clamp(20px,2vw,30px)}

/* THE PLATE. VOLA's is a stage for a rotating phone; this one holds a single
   still, because there is exactly one photograph on this page and it is the
   only artefact here that is a finished website rather than a diagram of one.
   It stands on the paper unframed: the render is already a rectangle with its
   own edge, and a coloured panel around it was a second frame around a frame. */
.plate-shot{
  width:100%;height:auto;border-radius:var(--r);
  /* Re-tuned off the violet. The old shadow was two near-black layers at .4
     and .8, which reads as depth against a dark ground and as a bruise on
     cream. Same geometry, the page's own ink, a third of the weight. */
  box-shadow:0 2px 8px rgba(20,20,15,.10),0 26px 54px -26px rgba(20,20,15,.30);
}

/* THE META GRID. Answers "what is this, who made it, when" in one glance. */
.meta{
  margin-top:clamp(24px,2.6vw,40px);
  display:grid;grid-template-columns:repeat(4,minmax(0,1fr));
  gap:1px;background:var(--line);
  border:1px solid var(--line);border-radius:var(--r);
  overflow:hidden;
}
.meta-cell{background:var(--paper);padding:clamp(16px,1.5vw,22px)}
.meta-cell .marg{margin-bottom:6px}
.meta-cell p{font-size:15px;line-height:1.5;color:var(--ink)}
.meta-cell p span{display:block;color:var(--ink-3)}

/* =========================================================== 02 PROBLEM -- */
/* ONE SECTION OPENING, USED EVERYWHERE. Headline left, its paragraph right.
   Three rules make the pair read as one unit rather than two things that
   happen to share a row:
   1. The label gets a row of its own across the top, so the headline and the
      paragraph START on the same line - the paragraph's first line sits level
      with the headline's first line instead of sinking to the bottom of its
      column with a hole above it. (The wrapper div is display:contents so the
      label, headline and paragraph are all items of this one grid.)
   2. The headline column is the wider one, 7:5, and the headline balances,
      so it breaks into two or three even lines, never four narrow ones.
   3. The paragraph is set at the lead size, so its column carries enough
      weight to partner a display headline instead of reading as a caption. */
.sec-head{
  max-width:var(--maxw);margin:0 auto clamp(42px,4.6vw,78px);
  display:grid;grid-template-columns:minmax(0,7fr) minmax(0,5fr);
  column-gap:clamp(36px,5.4vw,104px);
  align-items:start;
}
.sec-head > div{display:contents}
.sec-head .marg{grid-column:1/-1;justify-self:start}
.sec-head h2{grid-column:1;font-size:var(--fs-section);max-width:none;margin:18px 0 0;text-wrap:balance}
.sec-head p{grid-column:2;max-width:46ch;margin-top:18px;font-size:var(--fs-lead);line-height:1.55}
.statement .sec-head{margin-bottom:clamp(40px,4.2vw,66px)}
.statement .sec-head h2{font-size:var(--fs-statement);line-height:1.14}

/* LEDGER ROWS. A ruled label/value line: the label in the body face, the
   value in monospace. Used by the "Verified at" spec list. */
.ledger-row{
  display:flex;align-items:baseline;justify-content:space-between;gap:18px;
  padding:clamp(13px,1.2vw,17px) 0;
  border-bottom:1px solid var(--line);
}
.ledger-row:first-child,.ledger-row:nth-child(2){border-top:1px solid var(--line)}
.ledger-row dt{font-weight:600;color:var(--ink);margin:0}
.ledger-row dd{
  margin:0;font-family:var(--mono);font-size:14px;letter-spacing:-.01em;
  color:var(--ink-3);text-align:right;
}

/* THE REFRAME. One inverted slab, deliberately alone - the pivot the rest of
   the page hangs off. Not a .band: a band is a full-bleed section and this has
   to sit INSIDE the problem section, under the statement it answers. */
/* Headline left, paragraph right, both starting on the first line under the
   label - the same three rules as .sec-head, because it is the same job. */
.slab{
  max-width:var(--maxw);margin:clamp(52px,5.4vw,86px) auto 0;
  background:var(--slab-bg);color:var(--slab-ink);
  border-radius:var(--r-lg);
  padding:clamp(34px,4vw,64px) clamp(26px,3.6vw,60px);
  display:grid;grid-template-columns:minmax(0,7fr) minmax(0,5fr);
  gap:clamp(20px,2.2vw,32px) clamp(36px,5vw,96px);align-items:start;
}
.slab .eyebrow{grid-column:1/-1;color:rgba(244,241,232,.55)}
.slab h3{
  font-size:clamp(23px,2.5vw,40px);line-height:1.18;color:var(--slab-ink);
  max-width:none;margin:0;text-wrap:balance;
}
.slab h3 em{font-style:normal;color:#C7A6F0}   /* 9.0:1 on --ink */
.slab p{color:rgba(244,241,232,.72);max-width:46ch;font-size:var(--fs-lead);line-height:1.55}

/* ================================================ 04 GOVERNING DECISION -- */
/* Two facing columns with one hairline between them. The division IS the
   content here - what the model is allowed to touch, and what it is not - so
   the two panels are the same size, the same weight, and differ only in the
   colour of the rule on top. Anything that made one look more important than
   the other would be arguing the opposite of the section. */
.split{
  max-width:var(--maxw);margin:0 auto;
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(20px,2.6vw,44px);
}
.pane{
  border:1px solid var(--line);border-radius:var(--r);
  padding:clamp(24px,2.6vw,40px);
  background:var(--pane-bg);
}
.pane::before{
  content:'';display:block;height:3px;width:clamp(46px,5vw,76px);
  border-radius:2px;margin-bottom:clamp(20px,2vw,28px);
  background:var(--pane-rule,var(--line));
}
.pane--model{--pane-rule:var(--brand)}
.pane--system{--pane-rule:var(--ink)}
.pane h3{font-size:clamp(19px,1.5vw,23px);margin:0 0 18px}

/* The bulleted list used in both panes and in the varies/frozen pair. The
   marker is a real element, not a ::marker, because it has to be tintable per
   context and ::marker takes almost no properties in Safari. */
/* THE MARKER IS ABSOLUTE, NOT A GRID TRACK, and that is also a correction.
   As `li{display:grid}` with an auto column for ::before, any ELEMENT inside
   the item - a <strong> around "Generate" - became a third grid item and broke
   to its own row, so half the bullets read as two lines with a bold orphan
   under them. A grid item is a box; inline emphasis is not. Out-of-flow marker,
   normal flow text, and the emphasis stays in its sentence. */
.dots{list-style:none;margin:0;padding:0;display:grid;gap:13px}
.dots li{position:relative;padding-left:20px}
.dots li::before{
  content:'';position:absolute;left:0;top:.58em;
  width:8px;height:8px;border-radius:50%;
  background:var(--dot,var(--violet-ink));
}
.pane--model .dots li::before{background:var(--brand)}
.band .dots li::before{background:var(--spark)}
.dots strong{font-weight:700;color:var(--ink)}
.band .dots strong{color:var(--paper)}
.dots li{color:var(--ink-2)}
.band .dots li{color:rgba(244,241,232,.84)}
.pane-note{
  margin-top:clamp(20px,2vw,28px);padding-top:clamp(16px,1.6vw,22px);
  border-top:1px solid var(--line);
  font-size:14.5px;line-height:1.55;color:var(--ink-3);
}


/* ========================================== 05 THE VOCABULARY ---------- */
/* THE TEMPLATE BOARDS. The owner's ten Figma sections on a canvas that pans
   and zooms like Figma's. Everything about the canvas is aiwb.js section 4;
   what is here is its two states:

   - WITHOUT .is-live (no script, or before it runs) the world is a sideways
     row of the ten overview images at a readable height. Nothing is hidden,
     only the zoom is missing.
   - WITH .is-live the stage is a fixed-height window, the world is one big
     absolutely positioned plane in EXPORT pixels, and the script moves it with
     one transform. Boards are placed by the script from their data-w/data-h;
     none of the geometry is written here.

   THE CANVAS IS PINNED DARK IN BOTH THEMES, #1E1E1E - Figma's own canvas, and
   the ground the boards were exported on (#2B2B2B) reads against it as the
   sections they are. Same argument as the slab: inverting it would be a
   picture of a tool that does not look like that. */
.boards{
  --bv-h:min(78vh,820px);
  --cv:#1E1E1E; --cv-ink:rgba(255,255,255,.62); --cv-tool:#2C2C2C; --cv-line:rgba(255,255,255,.12);
  max-width:var(--maxw);margin:0 auto;
}
.boards-bar{display:none;margin-bottom:clamp(14px,1.4vw,20px)}
.boards.is-live .boards-bar{display:flex;align-items:center;gap:12px;min-width:0}
/* Eleven chips will not share a phone's width, so the pill scrolls rather than
   squeezing them - the base .switch rule at <=560 makes every button flex:1,
   which is right for three tabs and wrong for eleven. */
.boards-chips{max-width:100%;overflow-x:auto;scrollbar-width:none;overscroll-behavior-x:contain}
.boards-chips::-webkit-scrollbar{display:none}
.boards-chips button{flex:none;white-space:nowrap}
.boards-chips button[aria-current="true"]{background:var(--violet);color:var(--on-violet)}

.boards-stage{position:relative}
.boards-view{overflow-x:auto;padding-bottom:14px;-webkit-overflow-scrolling:touch}
.boards-world{display:flex;gap:24px;width:max-content}
.board{margin:0;flex:none}
.board img{display:block;height:min(60vh,520px);width:auto;border-radius:8px}
.board figcaption{margin-top:10px;font-family:var(--sans);font-weight:700;font-size:13px;color:var(--ink-3)}
.boards-tools,.boards-hint{display:none}

.boards.is-live .boards-stage{
  height:var(--bv-h);overflow:hidden;border-radius:var(--r);background:var(--cv);
  box-shadow:0 1px 2px rgba(20,20,15,.08),0 26px 50px -30px rgba(20,20,15,.55);
}
/* touch-action:pan-y - a vertical swipe still scrolls the PAGE, so a phone can
   never get stuck inside the canvas; a sideways swipe and a pinch come to the
   script. Full screen takes every gesture (none). */
.boards.is-live .boards-view{
  position:absolute;inset:0;overflow:hidden;padding:0;
  cursor:grab;touch-action:pan-y;user-select:none;-webkit-user-select:none;
}
.boards.is-live .boards-view:focus-visible{outline-offset:-4px;outline-color:#B49BE8}
.boards.is-live .boards-view.is-dragging{cursor:grabbing}
.boards.is-live .boards-world{position:absolute;left:0;top:0;display:block;width:auto;transform-origin:0 0}
.boards.is-live .board{position:absolute}
/* The images take no pointer events: the view is the one surface that pans,
   and an <img> would start a native drag of itself on the first mouse move. */
.boards.is-live .board img,.board .strip{
  width:100%;height:100%;border-radius:0;pointer-events:none;
  -webkit-user-drag:none;user-select:none;
}
/* The frame name, the way Figma writes it: above the frame, a constant 12px
   whatever the zoom - scaled by the inverse of the world's own scale, which
   the script keeps in --s. */
.boards.is-live .board figcaption{
  position:absolute;left:0;bottom:100%;margin:0;padding:0 0 7px;
  transform-origin:0 100%;transform:scale(calc(1 / var(--s,1)));
  white-space:nowrap;font-size:12px;font-weight:600;color:var(--cv-ink);pointer-events:none;
  /* Never wider than its own board ON SCREEN - 100% is the board's width in
     export px, and x s is what that measures after the world's scale. At a
     phone's overview the boards are 60px wide and the names would run into
     their neighbours ("TestimonialsFAQ"). */
  max-width:calc(100% * var(--s,1));overflow:hidden;text-overflow:ellipsis;
}
/* The detail strips sit over the overview, finer levels on top, and fade in
   once decoded so a strip never paints half-loaded. */
.board .strip{position:absolute;left:0;height:auto;opacity:0;transition:opacity .25s var(--ease)}
.board .strip.on{opacity:1}
.board .strip[data-k="1"]{z-index:1}
.board .strip[data-k="2"]{z-index:2}

.boards.is-live .boards-tools{
  position:absolute;right:12px;bottom:12px;z-index:5;
  display:flex;align-items:center;gap:2px;padding:4px;
  background:var(--cv-tool);border:1px solid var(--cv-line);border-radius:12px;
  box-shadow:0 8px 24px -8px rgba(0,0,0,.6);
}
.boards-tools button{
  min-width:36px;height:36px;padding:0 10px;display:grid;place-items:center;
  border:0;border-radius:8px;background:transparent;color:#F5F5F5;cursor:pointer;
  font:600 13px/1 var(--sans);
}
.boards-tools button:hover{background:rgba(255,255,255,.08)}
.boards-tools button:focus-visible{outline:2px solid #B49BE8;outline-offset:1px}
.boards-tools svg{width:14px;height:14px}
.boards-tools [data-full] .i-close,.boards.is-full [data-full] .i-open{display:none}
.boards.is-full [data-full] .i-close{display:inline}
.boards-pct{min-width:48px;text-align:center;font:600 12px/1 var(--mono);color:var(--cv-ink)}

.boards.is-live .boards-hint{
  display:block;position:absolute;left:12px;bottom:12px;z-index:5;margin:0;
  max-width:calc(100% - 260px);padding:9px 12px;border-radius:10px;
  background:rgba(44,44,44,.92);border:1px solid var(--cv-line);
  font:500 12px/1.4 var(--sans);color:var(--cv-ink);
  transition:opacity .4s var(--ease);
}
.boards-hint.is-gone{opacity:0;pointer-events:none}
.h-touch{display:none}
@media (hover:none),(pointer:coarse){.h-fine{display:none}.h-touch{display:inline}}

/* FULL SCREEN is a fixed layer over the page, not the Fullscreen API: iPhone
   Safari has no element fullscreen, and the one mode that matters most on a
   phone cannot be the one that is missing there. The page underneath is
   scroll-locked through html.boards-lock. */
.boards.is-full{
  position:fixed;inset:0;z-index:400;max-width:none;margin:0;
  display:flex;flex-direction:column;background:var(--cv);
  padding:max(12px,env(safe-area-inset-top)) 12px 0;transform:none;
}
.boards.is-full .boards-bar{margin-bottom:10px}
.boards.is-full .boards-chips{background:var(--cv-tool);border-color:var(--cv-line)}
.boards.is-full .boards-chips button{color:rgba(255,255,255,.7)}
.boards.is-full .boards-chips button:hover{color:#fff}
.boards.is-full .boards-stage{flex:1;height:auto;border-radius:12px 12px 0 0;box-shadow:none}
.boards.is-full .boards-view{touch-action:none}
html.boards-lock,html.boards-lock body{overflow:hidden}


/* THE COMPONENT SHEETS. Three exports in one sideways track - web header,
   mobile header, buttons - sized by HEIGHT, not width: the sheets are two to
   four times wider than tall, so fitting one to the column would print its
   labels at 5px.

   THE HEIGHT IS WHAT MAKES MANDATORY SNAPPING SAFE. min(72vh, 46vw) keeps the
   widest single group on any sheet (the opened submenus, 1720 source px) inside
   the space between the column edge and the window edge on a desktop, so every
   stop shows whole components. Below 1080 no legible height can promise that,
   so the snap relaxes to proximity and the track simply scrolls.

   THE SNAP POINTS ARE 1px MARKERS, NEVER THE FIGURES. A snap area wider than
   the snapport is exempt from snapping anywhere inside it (spec: any position
   where it covers the snapport is valid), so aligning the 2,400px mobile-nav
   figure itself would switch snapping off for its whole length.

   FULL-BLEED BY CANCELLING THE SECTION'S PADDING, never 100vw - a vw width
   counts a classic scrollbar and is the classic source of a sideways page
   scroll. .kit is exactly the section's padding box and --kit-in puts the bar
   and the first sheet back on the column edge; the track bleeds off the RIGHT
   edge only, which is the cue that there is more. */
.kit{
  --kit-in:var(--sec-x);
  --kit-gap:clamp(16px,1.8vw,28px);
  --kit-h:min(72vh,46vw,760px);
  margin:clamp(52px,5.4vw,88px) calc(-1 * var(--sec-x)) 0;
}
@supports (width:1cqw){
  .kit{
    container-type:inline-size;
    --kit-in:calc(var(--sec-x) + max(0px, (100cqw - 2 * var(--sec-x) - var(--maxw)) / 2));
  }
}
/* Hidden until aiwb.js wires it: a tab that cannot jump is a dead control. */
.kit-bar{
  display:none;align-items:center;justify-content:space-between;gap:16px;
  padding-inline:var(--kit-in);margin-bottom:clamp(16px,1.6vw,22px);
}
.kit.is-live .kit-bar{display:flex}
.kit-tabs button[aria-current="true"]{background:var(--violet);color:var(--on-violet)}
.kit-arrows{display:flex;gap:8px;flex:none}
.kit-arrow{
  width:44px;height:44px;padding:0;display:grid;place-items:center;cursor:pointer;
  border:1px solid var(--line);border-radius:50%;background:var(--pane-bg);color:var(--ink);
  transition:border-color .2s var(--ease),opacity .2s var(--ease);
}
.kit-arrow:hover:not(:disabled){border-color:var(--ink)}
.kit-arrow:disabled{opacity:.35;cursor:default}
.kit-arrow svg{width:15px;height:15px}

/* The bottom pad is the image's shadow, which overflow would otherwise cut
   square: offset + blur - spread = 16 + 30 - 22 = 24px, so 30 clears it. */
.kit-track{
  display:flex;gap:var(--kit-gap);
  overflow-x:auto;overflow-y:hidden;
  padding:2px 0 30px var(--kit-in);
  scroll-padding-inline:var(--kit-in);
  scroll-snap-type:x mandatory;
  overscroll-behavior-x:contain;   /* no browser back-swipe at the left end */
  -webkit-overflow-scrolling:touch;
  scrollbar-width:thin;scrollbar-color:var(--line) transparent;
}
/* End padding on a scroller is not reliably scrollable in every engine; a
   spacer is. It is --kit-in less the gap the flex row already puts before it -
   unless aiwb.js has measured that the LAST stop needs more room to reach the
   column edge, and set --kit-tail to say so. */
.kit-track::after{content:'';flex:0 0 var(--kit-tail, max(1px, calc(var(--kit-in) - var(--kit-gap))))}
/* A WINDOW, NOT A STRIP, where the stops are exact. Every stop lands a group
   on the column edge, so whatever scrolls past into the margin is the tail of
   the group before - a sliver of purple box with nothing to read in it. Clip
   the track at the column edge and the reader only ever sees whole groups.
   Only above 1080: below it the snap is proximity and the edge is arbitrary
   anyway. Negative insets on the other three sides leave the shadow and the
   focus ring alone. */
@media (min-width:1081px){
  .kit-track{clip-path:inset(-12px -12px -12px calc(var(--kit-in) - 2px))}
}
.kit-sheet{position:relative;flex:none;margin:0}
.kit-sheet::before,.kit-stop{
  content:'';position:absolute;top:0;left:0;width:1px;height:1px;
  scroll-snap-align:start;pointer-events:none;
}
/* PHONE-ONLY STOPS. On a phone even one group is wider than the screen, so the
   group stops alone would land a swipe mid-component; these sit in the finer
   white gaps (T3 | T4, white | brand submenus, white | brand states) and only
   exist where the snap is proximity - on a desktop they would halve every
   arrow step. aiwb.js reads scroll-snap-align, so it skips them there too. */
.kit-stop--m{scroll-snap-align:none}
.kit-sheet img{
  display:block;height:var(--kit-h);width:auto;max-width:none;background:#fff;
  border:1px solid rgba(20,20,15,.14);border-radius:var(--r);
  box-shadow:0 1px 2px rgba(20,20,15,.05),0 16px 30px -22px rgba(20,20,15,.4);
}

/* ========================================== 06 COMPOSITION -------------- */
/* THE RECIPE. Ten slots, one variant chosen per slot, laid out as a receipt
   rather than as a diagram - because that is what a generated page IS here: a
   list of choices, in order, from a fixed menu. It scrolls sideways inside its
   own box below 900px instead of wrapping to two rows; a recipe that wraps
   stops reading as a sequence. */
.recipe{
  max-width:var(--maxw);margin:0 auto;
  display:grid;grid-template-columns:repeat(10,minmax(0,1fr));
  gap:clamp(6px,.8vw,12px);
}
.slot{display:flex;flex-direction:column;gap:9px;text-align:center}
/* Solid tiles - a translucent one lets whatever is behind it show through -
   stepping from dark to light in slot order, so the strip reads top of the
   page to the bottom. The shades are set per slot in the markup (--sl). */
.slot i{
  display:block;height:clamp(62px,7vw,104px);border-radius:8px;
  background:var(--sl,#6A4AA3);
  border:1px solid rgba(244,241,232,.14);
}
.slot b{
  font-family:var(--sans);font-weight:700;font-size:12.5px;letter-spacing:-.01em;
  color:var(--on-violet);
}
.slot span{font-family:var(--mono);font-size:11.5px;color:var(--spark);letter-spacing:.04em}

/* THE ARITHMETIC. The one number the whole approach rests on, set in the
   display serif at section size, with its factors under it in monospace so the
   claim is checkable rather than asserted. */
.maths{
  max-width:var(--maxw);margin:clamp(52px,5.4vw,88px) auto 0;
  display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);
  gap:clamp(28px,3.6vw,76px);align-items:start;
}
.sum b{
  display:block;font-family:var(--serif);font-weight:300;
  font-size:clamp(46px,6.4vw,104px);line-height:.94;letter-spacing:-.035em;
  color:var(--spark);
}
.sum .factors{
  margin-top:clamp(16px,1.6vw,24px);
  font-family:var(--mono);font-size:14px;line-height:1.9;
  color:rgba(244,241,232,.78);letter-spacing:-.01em;
}
.sum .factors em{font-style:normal;color:var(--on-violet)}

/* The widths the kit was re-rendered at rather than assumed to scale to. */
.checked{
  border:1px solid var(--line-on);border-radius:var(--r);
  padding:clamp(22px,2.4vw,32px);
}
.checked dl{margin:0;display:grid;gap:0}
/* The ledger's top rules are drawn on rows 1 AND 2, because on paper it runs
   as two columns and both need a head. In here it is a single column, so both
   of those come back off - otherwise row two carries a stray line across the
   middle of the list. */
.checked .ledger-row{border-color:var(--line-on);border-top:0}
.checked .ledger-row:last-child{border-bottom:0}
.checked dt{color:var(--on-violet)}
.checked dd{color:var(--spark)}
.checked p{margin-top:18px;font-size:14px;line-height:1.6}

/* =============================== 07 CONTENT-SHAPE RESILIENCE ----------- */
/* THE SEGMENTED CONTROL. Born as the density switch, which is gone; the
   component-sheet tabs and the canvas chips both wear it now. */
.switch{
  display:inline-flex;gap:4px;padding:4px;
  border:1px solid var(--line);border-radius:999px;background:var(--pane-bg);
}
.switch button{
  min-height:40px;padding:0 18px;border:0;border-radius:999px;background:transparent;
  font-family:var(--sans);font-weight:700;font-size:14px;color:var(--ink-3);
  cursor:pointer;transition:background .2s var(--ease),color .2s var(--ease);
}
.switch button:hover{color:var(--ink)}
/* Touch: the tabs and the canvas tools reach 44px. */
@media (pointer:coarse){
  .switch button{min-height:44px}
  .boards-tools button{min-width:44px;height:44px}
}

/* THREE COUNTS, THREE SUMS, IN ONE BOX. The states themselves are on the
   Services board; what this fold owns is the arithmetic that resolves each
   one. Built exactly like the hero's .meta strip - a 1px gap over a
   line-coloured ground draws the dividers, each cell paints the paper - so the
   page has one kind of box, not two. Stacked, the same gap becomes the
   horizontal dividers. */
.counts{
  max-width:var(--maxw);margin:0 auto;
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:1px;background:var(--line);
  border:1px solid var(--line);border-radius:var(--r);overflow:hidden;
}
.counts > div{background:var(--paper);padding:clamp(24px,2.6vw,38px)}
.counts h3{font-size:var(--fs-feat);margin:0 0 14px;max-width:14ch}
.counts .sum-line{
  display:block;margin-top:20px;padding-top:18px;border-top:1px solid var(--line);
  font-family:var(--mono);font-size:14.5px;line-height:1.6;color:var(--violet-ink);
  letter-spacing:-.01em;
}
.counts-go{max-width:var(--maxw);margin:clamp(26px,2.8vw,40px) auto 0}

/* ========================================== 08 COLOUR & TYPE ------------ */
/* One fold, on the band: the argument as two lists, then the owner's sheet.
   It was two folds and a live colourway switch; the page comment says why.

   The lists are one .dots told apart by the marker - --spark for what a
   palette changes, paper for what it never touches. The eyebrow above each
   says the same thing in words, so the colour is never the only signal. */
.duo{
  max-width:var(--maxw);margin:0 auto;
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(30px,4vw,72px);align-items:start;
}
.band .dots--frozen li::before{background:var(--on-violet)}

/* THE SHEET. Full content width on a desktop, because it is a document with
   14px labels in it and every pixel of width is legibility. Not capped by vh
   the way the component sheets are: this one is read by scrolling DOWN it.

   #fff behind the image, not --paper: the sheet was exported on white, and
   the frame is the same white so the rounded corner cuts the artwork rather
   than a cream edge round it. */
.sheet{max-width:var(--maxw);margin:clamp(44px,4.8vw,80px) auto 0}
.sheet img{
  display:block;width:100%;height:auto;background:#fff;
  border:1px solid rgba(20,20,15,.2);border-radius:var(--r);
  box-shadow:0 1px 2px rgba(4,2,10,.3),0 26px 46px -26px rgba(4,2,10,.6);
}

/* ========================================== 10 THE BUILDER -------------- */
/* The section panel, redrawn. It is the one piece of product UI on the page
   and it is reproduced rather than screenshotted for the same reason the
   layouts are - a screenshot of this panel is mostly a picture of a plumbing
   company's copy. Drawn, it can be labelled. */
.builder{
  max-width:var(--maxw);margin:0 auto;
  display:grid;grid-template-columns:minmax(0,.78fr) minmax(0,1.22fr);
  gap:clamp(30px,4vw,80px);align-items:start;
}
.panel{
  /* A drawing of the shipped builder - the page's one drawn miniature, as no
     export of it exists - so its ink, muted ink and border are the product's
     own neutral tokens, the base colours on the owner's sheet in the colour
     band. They do not follow the site's theme because the panel being drawn
     does not. */
  --ink:#18181B;
  --ink-3:#71717A;
  --line:#E4E4E7;

  background:#fff;border:1px solid var(--line);border-radius:12px;
  overflow:hidden;max-width:400px;
  box-shadow:0 1px 2px rgba(20,20,15,.05),0 26px 48px -30px rgba(20,20,15,.5);
  font-size:13.5px;
}
.panel-top{
  display:flex;align-items:center;gap:10px;
  padding:15px 18px;border-bottom:1px solid var(--line);
  font-weight:700;color:var(--ink);
}
.panel-top svg{width:15px;height:15px;flex:none;stroke:var(--ink-3);fill:none}
.panel-tabs{display:flex;border-bottom:1px solid var(--line)}
.panel-tabs span{
  flex:1;text-align:center;padding:13px 8px;font-weight:600;color:var(--ink-3);
  border-bottom:2px solid transparent;margin-bottom:-1px;
}
.panel-tabs span.on{color:var(--brand);border-bottom-color:var(--brand)}
.panel-body{padding:18px}
.panel-lbl{
  display:block;margin:0 0 7px;
  font-weight:700;font-size:11px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-3);
}
.panel-lbl + .panel-lbl,.panel-field + .panel-lbl,.panel-acts + .panel-lbl{margin-top:18px}
.panel-field{
  border:1px solid var(--line);border-radius:7px;padding:11px 12px;
  color:var(--ink);background:#fff;
}
.panel-field.is-area{display:grid;gap:6px;padding:12px}
.panel-field.is-area i{display:block;height:6px;border-radius:3px;background:rgba(20,20,15,.12)}
.panel-field.is-area i:last-child{width:58%}
.panel-acts{display:flex;gap:8px;margin-top:11px}
.panel-btn{
  display:inline-flex;align-items:center;gap:6px;
  padding:8px 13px;border-radius:6px;font-weight:700;font-size:12.5px;
}
.panel-btn.is-fill{background:var(--brand);color:#fff}
.panel-btn.is-out{border:1px solid var(--brand);color:var(--brand)}
.panel-list{display:grid;gap:8px}
.panel-item{
  display:flex;align-items:center;gap:10px;
  border:1px solid var(--line);border-radius:7px;padding:10px 12px;color:var(--ink);
}
.panel-item .grip{color:var(--ink-3);letter-spacing:-2px;font-size:15px;flex:none}
.panel-item .tog{
  margin-left:auto;flex:none;width:28px;height:16px;border-radius:99px;
  background:var(--brand);position:relative;
}
.panel-item .tog::after{
  content:'';position:absolute;top:2px;right:2px;width:12px;height:12px;
  border-radius:50%;background:#fff;
}
.panel-item .go{color:var(--ink-3);flex:none}

/* The four named decisions. Numbered, hairline-separated, and the number is in
   monospace rather than the marginalia hand - these are clauses in a spec, and
   .steps already owns the handwritten numeral further down the page. */
.calls{list-style:none;margin:0;padding:0;border-top:1px solid var(--line)}
.calls li{
  display:grid;grid-template-columns:clamp(38px,3.4vw,54px) minmax(0,1fr);
  gap:6px clamp(14px,1.6vw,24px);
  padding:clamp(20px,2.2vw,32px) 0;border-bottom:1px solid var(--line);
}
.calls .n{
  font-family:var(--mono);font-size:13px;color:var(--violet-ink);
  padding-top:.35em;letter-spacing:.02em;
}
.calls h3{font-size:clamp(18px,1.4vw,22px);margin:0;max-width:26ch}
.calls p{grid-column:2;max-width:58ch;font-size:15.5px}
.calls code{
  background:var(--violet-wash);color:var(--violet-ink);
  padding:.08em .35em;border-radius:4px;
}

/* ========================================== 11 THE BOOKING FLOW --------- */
.flow{
  max-width:var(--maxw);margin:0 auto;
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(16px,2vw,30px);
}
/* Cards and chips on a gridded band paint the band's own violet, solid. The
   chalk grid is texture for OPEN space; left transparent, a card shows the
   grid running straight through its text. Same colour as the band, so the
   card still reads by its border, not by a second tone. */
.step-card{
  background:var(--violet);
  border:1px solid var(--line-on);border-radius:var(--r);
  padding:clamp(24px,2.6vw,38px);
  border-top:3px solid var(--spark);
}
.step-card .hd{display:flex;align-items:baseline;justify-content:space-between;gap:14px;margin-bottom:16px}
.step-card h3{font-size:clamp(20px,1.7vw,27px);margin:0;max-width:11ch}
.step-card .of{font-family:var(--mono);font-size:13px;color:var(--spark);flex:none}
.step-card p{font-size:15.5px}

.carry{
  max-width:var(--maxw);margin:clamp(40px,4vw,64px) auto 0;
  padding-top:clamp(26px,2.8vw,40px);border-top:1px solid var(--line-on);
}
.carry p{max-width:80ch}
.chips{display:flex;flex-wrap:wrap;gap:10px;margin-top:22px}
.chips span{
  display:inline-flex;align-items:center;gap:9px;
  padding:9px 15px;border-radius:999px;
  background:var(--violet);border:1px solid var(--line-on);
  font-size:13.5px;color:rgba(244,241,232,.9);
}
.chips span i{width:6px;height:6px;border-radius:50%;background:var(--spark);flex:none}
.chips span em{font-style:normal;color:var(--spark);font-family:var(--mono);font-size:12px}

.beyond{max-width:var(--maxw);margin:clamp(44px,4.4vw,72px) auto 0}

/* ========================================== 12 THE HANDOFF GATE --------- */
.gates{
  max-width:var(--maxw);margin:0 auto;
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:1px;background:var(--line);
  border:1px solid var(--line);border-radius:var(--r);overflow:hidden;
}
/* Five gates in a three-up grid leaves the last row 2 + 1 with an empty cell
   beside the Review card - a grey hole. The last card spans the two leftover
   tracks instead, so the grid always closes flush. Desktop only: below 980 the
   grid is one column and a span would open a second implicit track. */
@media (min-width:981px){
  .gate:last-child:nth-child(3n+2){grid-column:span 2}
}
.gate{background:var(--paper);padding:clamp(22px,2.4vw,34px)}
.gate .eyebrow{margin-bottom:12px}
.gate b{
  display:block;font-family:var(--serif);font-weight:300;
  font-size:clamp(34px,3.4vw,50px);line-height:1;color:var(--violet-ink);letter-spacing:-.03em;
}
.gate p{margin-top:12px;font-size:14.5px;line-height:1.55}


/* ========================================== 13 WHAT I'D CHANGE ---------- */
.steps{max-width:var(--maxw);margin:0 auto;border-top:1px solid var(--line)}
.step{
  display:grid;
  grid-template-columns:clamp(60px,6vw,104px) minmax(0,.5fr) minmax(0,1fr);
  gap:clamp(18px,3vw,56px);
  align-items:start;
  padding:clamp(26px,3vw,46px) 0;
  border-bottom:1px solid var(--line);
}
.step-n{
  font-family:var(--hand);font-weight:400;
  font-size:clamp(30px,3.2vw,50px);line-height:1;
  color:var(--violet-ink);
}
.step h3{font-size:clamp(20px,1.75vw,28px);margin:0;max-width:18ch}
.step p{max-width:52ch}

/* ============================================================== CLOSE --- */
.close{padding-bottom:clamp(56px,6vw,96px)}
.close-in{
  max-width:var(--maxw);margin:0 auto;
  display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,.9fr);
  gap:clamp(30px,4vw,80px);align-items:center;   /* two columns of different heights - centre them */
}
.close h2{font-size:var(--fs-section);max-width:20ch;margin:20px 0 0}
.close h2 em{font-style:normal;color:var(--spark)}
.close-acts{display:flex;flex-wrap:wrap;gap:12px;margin-top:clamp(16px,1.6vw,24px)}

/* THE NEXT PROJECT - the shape is assets/css/next.css, shared by all four
   case studies. This page only tints it and says where it sits. */
.close{
  --next-ink:var(--on-violet);
  --next-muted:rgba(244,241,232,.80);
  --next-line:var(--line-on);
  --next-line-hi:rgba(244,241,232,.42);
  --next-accent:var(--spark);
  --next-name-font:var(--serif);
  --next-label-font:var(--sans);
  --next-r:var(--r);
  --next-ease:var(--ease);
}
.close .next-proj{justify-self:end}
.colophon{
  max-width:var(--maxw);margin:clamp(50px,5vw,84px) auto 0;
  padding-top:clamp(20px,2vw,30px);
  border-top:1px solid var(--line-on);
  display:flex;flex-wrap:wrap;gap:10px 28px;align-items:baseline;
  font-size:13.5px;color:rgba(244,241,232,.80);
}
.colophon b{
  font-family:var(--serif);font-weight:300;font-size:22px;
  letter-spacing:.02em;color:var(--on-violet);
}

/* ============================================================== REVEALS -- */
/* Guarded by html.js, so with scripting off nothing is ever hidden. The
   failsafe in the page head covers the OTHER case - the script 404s or throws. */
html.js .rise{opacity:0;transform:translateY(18px)}
html.js .rise.in{
  opacity:1;transform:none;
  transition:opacity .7s var(--ease),transform .7s var(--ease);
}
@media (prefers-reduced-motion:reduce){
  html.js .rise{opacity:1;transform:none;transition:none}
}

/* ============================================================================
   RESPONSIVE
   ----------------------------------------------------------------------------
   Not a set of things that shrink. Five of these change what a piece of the
   page IS, because the desktop idea stops being the better idea:

   - 1180  the ten-slot recipe stops being one row. Ten tracks in a 940px
           column is a 94px slot holding the word "Testimonials".
   - 1080  the hero stops being side-by-side, and the four variants go two-up.
           A 100px headline and a 16:10 render cannot share a row below this.
   -  900  every two-column module stacks. Measured: the .split panes hold a
           six-word bullet on two lines at 880 and on four at 820.
   -  700  the variant row goes one-up and the wireframes take back the size
           two-up cost them. Below this a T2 split is 74px of photo against
           94px of text and the mirror pair stops being legible AS a pair.
   -  560  the four-up strips go two-up, and the measure hands over to the
           viewport.
   ========================================================================= */

/* ------------------------------------------------------------ <= 1180px -- */
@media (max-width:1180px){
  /* THE RECIPE SCROLLS RATHER THAN WRAPPING. A sequence that wraps to a second
     row stops reading as a sequence - the tenth slot ends up above the fifth.
     Sideways inside its own box keeps the order and keeps the page's no-
     horizontal-scroll rule, because the overflow is on the strip and not on
     the body. */
  .recipe{
    display:flex;overflow-x:auto;scroll-snap-type:x proximity;
    padding-bottom:14px;-webkit-overflow-scrolling:touch;
  }
  .slot{flex:0 0 clamp(92px,13vw,130px);scroll-snap-align:start}
}

/* ------------------------------------------------------------ <= 1080px -- */
@media (max-width:1080px){
  /* One column, and the three areas simply stack in the order they are
     written: title, then the render, then the reference material. No `order`. */
  .hero-in{gap:clamp(30px,4.6vw,50px)}
  .hero h1{max-width:18ch}
  .sec-head{grid-template-columns:minmax(0,1fr)}
  .sec-head h2,.sec-head p{grid-column:1}
  .sec-head p{max-width:var(--measure);margin-top:clamp(16px,2.6vw,26px)}
  .close-in{grid-template-columns:minmax(0,1fr);gap:clamp(16px,2.6vw,26px);align-items:start}
  .close .next-proj{justify-self:start;margin-top:clamp(12px,3vw,24px)}

  .maths{grid-template-columns:minmax(0,1fr);gap:clamp(28px,3.4vw,44px)}
  /* No legible height fits a whole group between column and window edge
     below here, so the snap stops insisting and the track just scrolls. */
  .kit{--kit-h:min(64vh,600px)}
  .kit-track{scroll-snap-type:x proximity}
  .kit-stop--m{scroll-snap-align:start}
  .builder{grid-template-columns:minmax(0,1fr);gap:clamp(30px,3.6vw,48px)}
  .panel{max-width:420px}
}

/* ------------------------------------------------------------- <= 980px -- */
@media (max-width:980px){
  .flow,.gates{grid-template-columns:minmax(0,1fr)}
  .close h2{max-width:24ch}

  /* The step's three columns become two: the number joins its own heading
     rather than sitting alone in a column 56px wide. */
  .step{
    grid-template-columns:clamp(48px,6vw,64px) minmax(0,1fr);
    gap:clamp(14px,2.4vw,26px) clamp(16px,3vw,28px);
    align-items:baseline;
  }
  .step p{grid-column:2;max-width:52ch}
}

/* ------------------------------------------------------------- <= 900px -- */
@media (max-width:900px){
  .split,.duo,.counts,.slab{grid-template-columns:minmax(0,1fr)}
  .slab p{max-width:var(--measure)}
  /* Stacked, the paragraph needs its own air back - the grid gap was buying it
     horizontally. */
  /* With one column the ledger's second-column top rule is now a stray line in
     the middle of the list. Only the true first row keeps one. */
  .ledger-row:nth-child(2){border-top:0}
  .meta{grid-template-columns:repeat(2,minmax(0,1fr))}
}

/* ------------------------------------------------------------- <= 700px -- */
@media (max-width:700px){
  /* The canvas keeps most of a phone's height - a short window onto a board
     sixteen thousand pixels tall is a letterbox. The hint drops to one line
     above the tools rather than beside them. */
  .boards{--bv-h:min(72vh,620px)}
  .boards.is-live .boards-hint{max-width:none;right:12px;bottom:64px}
}

/* ------------------------------------------------------------- <= 760px -- */
@media (max-width:760px){
  /* ON A PHONE THE COLOUR SHEET SCROLLS SIDEWAYS INSTEAD OF SHRINKING. Fitted
     to a 343px column its 14px labels render at 3px; at this height they are
     about 7. The overflow is on the figure, not the body, so the page keeps
     its no-horizontal-scroll rule - the same argument as the recipe strip.
     Full-bleed by cancelling the band's own padding; the sheet running off
     the right edge is the cue. */
  .sheet{
    display:flex;overflow-x:auto;overflow-y:hidden;
    margin-inline:calc(-1 * var(--sec-x));
    padding:2px 0 28px var(--sec-x);scroll-padding-left:var(--sec-x);
    overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch;
    scrollbar-width:thin;scrollbar-color:var(--line-on) transparent;
  }
  .sheet::after{content:'';flex:0 0 var(--sec-x)}
  /* The band's deep shadow reaches 46px down, which the scroller would cut
     square; this one reaches 24 and clears the 28px pad. */
  .sheet img{
    flex:none;width:auto;max-width:none;height:min(78vh,700px);
    box-shadow:0 1px 2px rgba(4,2,10,.3),0 16px 30px -22px rgba(4,2,10,.55);
  }
}

/* ------------------------------------------------------------- <= 560px -- */
@media (max-width:560px){
  :root{
    --sec-y:clamp(64px,14vw,92px);
    /* A 57ch measure on a 360px screen is not the constraint any more - the
       viewport is. Let it go to 100% and let padding do the work. */
    --measure:100%;
  }
  /* Three tabs and two arrows do not share 343px. Touch scrolls the track
     directly; the tabs stay, because they are what says there are three. */
  .kit-arrows{display:none}
  .meta{grid-template-columns:minmax(0,1fr);max-width:520px}

  /* Buttons go full width and stack. Two 48px pills side by side at 360px are
     two 160px pills with the label wrapping inside them. */
  .hero-cta,.close-acts{flex-direction:column;align-items:stretch}
  .hero-cta .btn,.close-acts .btn{justify-content:center;width:100%}

  .switch{display:flex;width:100%}
  .switch button{flex:1;padding:0 8px}

  .colophon{gap:8px 20px;font-size:13px}
  .slab{border-radius:var(--r)}
  .boards-chips.switch{display:inline-flex;width:auto}
  .boards-chips.switch button{flex:none;padding:0 14px}
}

/* ------------------------------------------------------------- <= 400px -- */
@media (max-width:400px){
  .step{grid-template-columns:minmax(0,1fr)}
  .step p{grid-column:1}
  .step-n{font-size:26px}
  .calls li{grid-template-columns:minmax(0,1fr);gap:8px}
  .calls p{grid-column:1}
}

/* --------------------------------------------------- SHORT AND LANDSCAPE -- */
@media (max-height:560px) and (orientation:landscape){
  :root{--sec-y:clamp(48px,8vw,72px)}
  .hero{padding-top:calc(var(--nav-h) + 28px)}
}

/* ==========================================================================
   THE DARK THEME
   Added 2026-09-11, alongside the site-wide switcher.

   THIS IS A TOKEN BLOCK AND NOTHING ELSE - the same shape vola.css lands on,
   and possible for the same reason: every colour on this page was already a
   token, so a second theme is a palette rather than a second stylesheet.

   --violet AND --spark KEEP THEIR JOBS. In the light theme the violet is the
   ink and the spark is a fill that may never be text. On a dark ground the
   deep violet is unreadable, so the ink role moves to a lifted violet at
   7.8:1, and --spark - already a bright yellow - becomes safe as text for the
   first time. It is still only ever used inside a band, where nothing moved.

   WHAT DOES NOT CHANGE is the violet band. .band, .skip
   and .btn-solid paint --violet as a GROUND in both themes and carry
   near-paper ink on it, so every rgba(244,241,232,...) inside a band is left
   exactly as it is. A dark theme that also inverted the bands would flatten
   the page's one structural contrast into a single tone.

   THREE SURFACES ARE PINNED AND DO NOT INVERT, each for a stated reason: the
   .slab (the page's one dark pivot), .boards (Figma's canvas, as Figma draws it) and
   .panel (a drawing of the shipped builder). All three carry their own tokens
   where they are declared rather than an html.dark override here.
   ========================================================================== */
html.dark{
  --paper:#12120E;               /* the cream, inverted and kept warm */
  --ink:#F4F1E8;
  --ink-2:rgba(244,241,232,.72);
  --ink-3:rgba(244,241,232,.58); /* .58, not .40 - a caption still has to read */

  /* THE GROUND DOES NOT MOVE, only the INK lifts. #B49BE8 measures 7.8:1 on
     this paper; the deep violet it replaces measures 1.2:1 and is a hole. */
  --violet-ink:#B49BE8;
  --spark:#FACC15;               /* unchanged: still a fill, now also legible */

  --line:rgba(244,241,232,.15);
  --line-2:rgba(244,241,232,.07);

  /* The two washes, re-mixed off the light ink. A 3.5% black tint on a
     near-black ground is nothing at all. */
  --pane-bg:rgba(244,241,232,.04);
  --violet-wash:rgba(180,155,232,.13);

  /* The bar follows the page. nav.css owns the bar; this only re-tints it. */
  --nav-bg:#12120E;
  --nav-ink:#F4F1E8;
  --nav-muted:rgba(244,241,232,.62);
  --nav-faint:rgba(244,241,232,.42);
  --nav-line:rgba(244,241,232,.15);
  --nav-fill:rgba(180,155,232,.10);
  --nav-edge:rgba(180,155,232,.38);
  --nav-edge-hi:rgba(180,155,232,.70);
  --nav-hair:rgba(244,241,232,.10);
  --nav-press:rgba(180,155,232,.12);
  --nav-grid:rgba(244,241,232,.05);
  --nav-eye-ring:rgba(244,241,232,.24);
  --nav-accent:#B49BE8;
}

/* --ink ALONE IS NEVER ENOUGH. `color` inherits as a COMPUTED value, so an
   element that resolved it against the light --ink keeps that literal after
   the token flips underneath it. Re-stating it on the elements that declare
   their own colour is what actually moves them. This is the failure vola.css
   documents from the Marketplace page, where two headings went to 1.00:1. */
html.dark body{color:var(--ink)}
html.dark .btn-ghost{color:var(--ink)}
html.dark .btn-ghost:hover{background:rgba(244,241,232,.06)}
/* NO html.dark RULE FOR .btn-solid, and none for .band .btn-solid. Both inks
   are theme-independent tokens now, so the base rules already cover them - and
   an html.dark selector at (0,2,1) would outrank `.band .btn-solid` at (0,2,0)
   and put the spark button's dark ink back to near-white. That exact override
   is what broke VOLA's footer button at 1.11:1. */

/* Headings, and the two places a heading's colour is restated. */
html.dark h1,html.dark h2,html.dark h3,html.dark h4{color:var(--ink)}
html.dark .dots strong{color:var(--ink)}
html.dark .meta-cell p{color:var(--ink)}


/* -------------------------------------------------------------- PRINT -- */
/* A case study is a thing people put in a PDF and send to a hiring manager.
   Violet bands eat a cartridge, and a wireframe drawn in near-white on white
   disappears entirely - so every drawn box gets a real border for print. */
@media print{
  /* A DARK PAGE PRINTED IS STILL INK ON WHITE, AND THE FIX IS THE PALETTE, NOT
     THE RULES. The theme class survives printing, and every html.dark rule
     outranks the plain-element rules in here on SPECIFICITY - which source
     order cannot undo, so restating each one would mean chasing all fourteen
     and every one added later. Resetting the tokens instead means the same
     html.dark rules resolve to black on their own. */
  html.dark{
    --paper:#fff;
    --ink:#000;
    --ink-2:#000;
    --ink-3:#333;
    --line:#999;
    --line-2:#ccc;
    --pane-bg:#fff;
    --violet-ink:#000;
    --violet-wash:#fff;
  }
  html.dark body{background:#fff}

  /* THE ONE PLACE THIS PAGE TOUCHES .nav DIRECTLY, and it is allowed because
     it does not restyle the bar - it removes it from a medium that has no
     scrolling. .nav is position:fixed; printed, it lands on top of the first
     paragraph of every page. */
  .nav,.mnav,.skip,.hero-cta,.close-acts,.switch{display:none !important}
  body{background:#fff;color:#000}
  .band{background:#fff;color:#000;border-top:1px solid #999;border-bottom:1px solid #999}
  .band h2,.band h3,.band h4,.band p,.colophon,.colophon b{color:#000}
  .sum b,.gate b{color:#000}
  .slab{background:#fff;color:#000;border:1px solid #999}
  .slab h3,.slab h3 em,.slab p{color:#000}
  .slot i{border-color:#999}
  .slot b,.slot span{color:#000}
  /* THE SHEETS PRINT WHOLE. Paper has no sideways scroll, and a print page is
     narrow enough to hit the <=760 rule, which would clip the colour sheet at
     the page edge. Every sheet goes back to full width, stacked. */
  .sheet,.kit-track{display:block;overflow:visible;margin-inline:0;padding:0}
  .sheet::after,.kit-track::after,.kit-stop,.kit-sheet::before,.kit-bar{display:none !important}
  .kit{margin-inline:0}
  .kit-sheet{margin-top:14px}
  .sheet img,.kit-sheet img{width:100%;height:auto;box-shadow:none;border-color:#999}
  .sheet,.kit-sheet{break-inside:avoid;page-break-inside:avoid}

  /* THE CANVAS PRINTS AS ITS TEN OVERVIEWS, two to a row. Paper cannot pan,
     and every inline style the script wrote - position, size, transform - has
     to be overridden, which is what the !important is for. */
  .boards-bar,.boards-tools,.boards-hint,.board .strip{display:none !important}
  .boards-stage{height:auto !important;overflow:visible !important;background:none !important;box-shadow:none !important}
  .boards-view{position:static !important;overflow:visible !important;padding:0 !important}
  .boards-world{position:static !important;transform:none !important;display:grid !important;
    grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;width:auto !important}
  .board{position:static !important;width:auto !important;height:auto !important;break-inside:avoid;page-break-inside:avoid}
  .board img{width:100% !important;height:auto !important}
  .board figcaption{position:static !important;transform:none !important;color:#000 !important;margin-top:6px}

  .gate,.pane,.step-card,.checked{break-inside:avoid;page-break-inside:avoid}
  .step,.calls li{break-inside:avoid;page-break-inside:avoid}
  .grid-bg::before{display:none}
  html.js .rise{opacity:1 !important;transform:none !important}
}

/* ------------------------------------------------- FORCED COLOURS / HC -- */
/* Windows High Contrast throws every author colour away. Everything on this
   page that carries meaning by colour ALONE is a drawn box, so every drawn box
   needs a border to survive it. */
@media (forced-colors:active){
  .meta,.gates,.panel,.pane,.step-card,.boards-stage{border:1px solid CanvasText}
  .slot i{border:1px solid CanvasText}
  .btn,.switch button,.kit-arrow,.boards-tools button{border:1px solid ButtonText}
  .kit-tabs button[aria-current="true"],.boards-chips button[aria-current="true"]{background:Highlight;color:HighlightText}
  mark{background:Highlight;color:HighlightText}
}
