/* aibotkit-child · the landing-page section system — U6
 *
 * Hand-authored, like assets/css/chrome.css and for the same reason: these are
 * the rules a Tailwind utility cannot express. A ground that changes with a
 * modifier, a radial wash on a pseudo-element, a vertical rhythm driven by
 * adjacent-sibling margins -- none of those are a utility.
 *
 * ⛔ THE CLASS NAMES ARE LOAD-BEARING, AND NOT ONLY FOR STYLING.
 *    ops/measure/check-law.js asserts the law against the RENDERED page using
 *    these exact selectors -- `a.btn`, `.badge`, `figure.figure`, `.draw`.
 *    Its button assertion reads `document.querySelectorAll('a.btn, button.btn')`
 *    and, finding none, reports success while checking nothing. So renaming
 *    `.btn` does not break a check loudly; it switches the check OFF.
 *    That is why this file keeps the prototype's vocabulary. We rebuilt the
 *    CSS; we kept the names the guard measures.
 *
 * ⛔ EVERY --wp--custom-- NAME HERE IS WORDPRESS'S OWN KEBAB FORM.
 *    `3xl` becomes `space--3-xl`, not `space--3xl`. A wrong name makes the
 *    declaration invalid and the property falls back SILENTLY. U5 lost the
 *    footer gap to exactly this. Check every name against theme.json.
 *
 * ⚠ LOADS AFTER tw.css, deliberately -- it overrides the utilities.
 */

/* ---------------------------------------------------------------------------
   0 · The two ground aliases.
   The law names the page's two grounds by role, not by colour: the FLOOR is
   what a section stands on, the PANEL is the inset card it stands on when it
   wants to be read as a separate surface. Q1 settled which hex each is.
   --------------------------------------------------------------------------- */
:root {
	--abk-floor: var(--wp--custom--color--paper);
	--abk-panel: var(--wp--custom--color--tint);
}

/* ⛔ `border-box` INSIDE A SECTION, AND IT IS NOT TIDINESS. The prototype's
   stylesheet opens with a `*` reset that sets it; WordPress does not set it
   universally. Measured: every fixed-height box came out its padding taller --
   the 300px product well painted 348px, and the chip row 35.2px against
   27.2px, so the card trio and the whole section grew. ⚠ A height is a
   PROMISE about the painted box, and under `content-box` it is a promise
   about something the eye never sees.
   ⚠ SCOPED TO `.section`. The prototype's own `*` reset could not be ported
   whole -- it also carries `p { max-width: 68ch }`, which would truncate every
   paragraph on all 22 pages and in the editor. This takes the one declaration
   that is safe and leaves the rest where it belongs. */
.section,
.section *,
.section *::before,
.section *::after { box-sizing: border-box; }

/* ⛔ A TRACKING TIER IS A TOKEN, NOT AN OVERRIDE. Two type roles cross the
   28px display boundary as the viewport narrows, so their tracking has to
   change with it. Doing that with media-query OVERRIDES cost three separate
   defects in one day: the rule was correct and lost on source order each time
   a new section was appended below it.
   ⭐ SWITCHING THE TOKEN INSTEAD ENDS THE ARGUMENT. Every consumer follows
   automatically, nothing has to outrank anything, and a section added later
   cannot break it.
   ⚠ THE BREAKPOINTS ARE DERIVED, NOT CHOSEN -- each is the exact viewport
   where that role's own clamp crosses 28px:
       step--display-small  clamp(1.5rem, 2.0834vw, 1.875rem)  ->  28px at 1344px
       step--subsection     clamp(1.375rem, 1.6667vw, 1.5rem)  ->  never: 24px at most
   ⭐ THE 2026-09-25 LADDER (48/38, Alpesh's pick) MOVED BOTH. Until then
      display-small crossed at 933.33px and subsection at 1400px. Subsection
      now tops out at 24px, so it is the LEAD tier at every width, and the
      variable stays only so its consumers need not change. */
:root {
	--abk-track-dsm: var(--wp--custom--tracking--display);
	--abk-track-sub: var(--wp--custom--tracking--lead);
}
@media (max-width: 1343px) { :root { --abk-track-dsm: var(--wp--custom--tracking--lead); } }

/* ---------------------------------------------------------------------------
   1 · THE SECTION. Every landing-page section is an inset rounded panel.
   --------------------------------------------------------------------------- */
.section {
	/* ⛔ `clip`, never `hidden`. Both clip the same box, but `hidden` makes a
	   SCROLL CONTAINER, and a scroll container is the containing block for
	   every `position: sticky` descendant -- so a sticky child scrolls away
	   with its section and never sticks. The prototype's audit reported
	   "0 sticky elements on the page" for this reason and nobody had failed
	   to build one. The `hidden` line is the fallback for a browser with no
	   `clip`, and must stay ABOVE it. */
	overflow: hidden;
	overflow: clip;
	margin: 0 var(--wp--custom--space--panel-inset);
	border-radius: var(--wp--custom--rounded--lg);
	padding: var(--wp--custom--space--section) var(--wp--custom--width--gutter);
	background: var(--abk-panel);
	color: var(--wp--custom--color--body);
}

/* ⛔ TWO CORE RULES OWN THE SECTION'S MARGINS, AND BOTH OUTRANK A SINGLE CLASS.
   This is KD46 again, in the place it always shows up: WordPress prints its
   layout rules with `:root :where(...)`, which is (0,2,0) because `:root`
   counts and `:where()` does not. A one-class rule loses to it however late
   the file loads, because LOAD ORDER CANNOT WIN A SPECIFICITY ARGUMENT.

     1 · the flow rhythm put 19.2px above the section -- the hero started at
         95.2px where the prototype starts at 76px, the bar's own bottom edge.
     2 · the `alignfull` rule pulled the section out to the viewport with a
         -50px inline margin, so the 16px panel inset disappeared and the
         section painted 1440 wide instead of 1408.

   ⭐ So this rule is written at (0,3,0) rather than with `!important`: three
   classes state WHERE the exception applies, which an `!important` does not.
   ⚠ Only MARGIN needs this. Padding, colour, size, flex and display are
   untouched by core's layout CSS. */
:root .entry-content > .section {
	margin-block: 0;
	margin-inline: var(--wp--custom--space--panel-inset);
}

/* ⛔ AND THE LAST 19.2px SITS ON `main`, NOT ON THE SECTION. templates/
   page-landing.html marks it `.lp-main` for exactly this: core's flow rhythm
   puts a gap between the header and the main element, and a landing page's
   first section has to start at the bar's bottom edge -- 76px, the
   prototype's number. ⚠ SCOPED TO THE LANDING TEMPLATE. The same 19.2px is on
   all 22 other pages and U0's baseline has it; removing it everywhere would
   move every page for no reason anybody asked for. */
:root .wp-site-blocks > .lp-main { margin-block-start: 0; }

.section--floor    { background: var(--abk-floor); }

/* ⭐ TWO PANELS IN A ROW ARE ONE GROUND: THE CORNERS WHERE THEY MEET ARE
   SQUARE. Each panel rounds all four corners, so two tint sections with no
   gap left four white wedges at the join -- on this site 4925's two
   `section--generous` panels, and the lead-generation page's features and
   proof. Critique round 2, 2026-09-25, picked by looking. ⚠ A floor section
   is the page's own white, so its corners never show; the close paints the
   floor too. */
:root .section:not(.section--floor, .section--close):has(+ .section:not(.section--floor, .section--close)) {
	border-bottom-left-radius: 0; border-bottom-right-radius: 0;
}
:root .section:not(.section--floor, .section--close) + .section:not(.section--floor, .section--close) {
	border-top-left-radius: 0; border-top-right-radius: 0;
}
.section--tight    { padding-block: var(--wp--custom--space--section-tight); }
.section--generous { padding-block: var(--wp--custom--space--section-generous); }

/* R1 at 390: the inset halves rather than eating a fifth of the screen. The 8px
   is the law's `spacing.panel-inset-phone` (Round 4, item 21, 2026-10-19). */
@media (max-width: 480px) {
	:root { --wp--custom--space--panel-inset: var(--wp--custom--space--panel-inset-phone); }
}

/* ⛔ THE THREE SECTION PADDINGS TAKE A MOBILE VALUE, AND theme.json CANNOT
   HOLD IT. Each token is a clamp whose MINIMUM is its desktop-ish floor, so
   at 390 the viewport term has long since bottomed out and the section keeps
   its full 88px. Measured on the prototype at 390: 60px, not 88px. A clamp
   cannot express "and below 620px, less than the floor"; only a media query
   can, so the value lives here beside the rule it changes. */
@media (max-width: 620px) {
	.section             { padding-block: 44px; }
	.section--tight      { padding-block: 32px; }
	.section--generous   { padding-block: 60px; }
}

/* The measure. `.wrap` is the section's content column. */
.section .wrap {
	max-width: var(--wp--custom--width--figure);
	margin-inline: auto;
}

/* ⛔ CORE INJECTS A 24px margin-block-start ON EVERY FLOW SIBLING, and this
   section system drives its rhythm from named adjacent-sibling margins
   instead. Zero core's rhythm first, then state ours. Two classes, so it
   beats core's `:where(.is-layout-flow) > *`, which is one. */
.section .wrap > * { margin-block: 0; }

.section--centred            { text-align: center; }
.section--centred .wrap > *  { margin-inline: auto; }

/* ⛔ A SECTION THAT A LINK JUMPS TO STOPS BELOW THE FIXED BAR, not under it.
   The bar is fixed, so it reserves no space, and a plain `#id` jump puts the
   section's top edge at the viewport's top -- under the bar. 68px is the
   same measured bar (8px inset + 60px, since 2026-09-27) that chrome.css
   reserves on `.wp-site-blocks`. A modifier, not `[id]`, so no existing page
   changes. */
.section--anchor             { scroll-margin-top: 68px; }

/* ---------------------------------------------------------------------------
   2 · THE LABEL LAYER — the eyebrow and its dot.
   ⛔ The dot aligns to the FIRST LINE's optical centre, not to the element's,
      so a two-line eyebrow keeps the dot beside the first word rather than in
      the gutter between the lines. `1lh` is the element's own line box, so it
      needs no magic number and survives a change of size or leading.
   --------------------------------------------------------------------------- */
.eyebrow {
	display: inline-flex;
	align-items: flex-start;
	gap: var(--wp--custom--space--sm);
	font-size: var(--wp--custom--step--label);
	font-weight: var(--wp--custom--weight--emphasis);
	letter-spacing: var(--wp--custom--tracking--label);
	line-height: var(--wp--custom--leading--body);
	color: var(--wp--custom--color--secondary);
	text-transform: none;   /* ⛔ no ALL CAPS — PRODUCT.md refuses it */
}
.eyebrow::before {
	content: "";
	flex: none;
	width: 6px;
	height: 6px;
	border-radius: var(--wp--custom--rounded--full);
	background: var(--wp--custom--color--brand);
	margin-top: calc((1lh - 6px) / 2);
}
.eyebrow--plain::before  { display: none; }

/* ⭐ THE SECTION OPENING IS A UNIT, and this 16px is most of why the
   prototype's openings read composed. ⚠ It does NOT collapse into the next
   element's margin, because `.eyebrow` is inline-flex and margin collapsing
   is a block-box rule -- so the gap below an eyebrow really is its own 16px
   ON TOP of whatever the next element asks for. Measured: without it the
   hero's stat triple sat 15px high and the section came out 15px short.
   ⚠ Three classes, because the rhythm reset above is two. */
:root .section .eyebrow { margin-bottom: var(--wp--custom--space--md); }

/* ---------------------------------------------------------------------------
   3 · THE BADGE — the hero's pill. One of exactly TWO components the law
   lets take the pill radius; check-law.js asserts that list.
   --------------------------------------------------------------------------- */
.badge {
	display: inline-flex;
	align-items: center;
	gap: var(--wp--custom--space--sm);
	min-height: 34px;
	padding: 0 var(--wp--custom--space--md);
	border-radius: var(--wp--custom--rounded--full);
	background: var(--abk-panel);
	box-shadow: var(--wp--custom--shadow--edge);
	font-size: var(--wp--custom--step--label);
	font-weight: var(--wp--custom--weight--emphasis);
	letter-spacing: var(--wp--custom--tracking--label);
	line-height: var(--wp--custom--leading--body);
	color: var(--wp--custom--color--secondary);
}
.badge::before {
	content: "";
	flex: none;
	width: 6px;
	height: 6px;
	border-radius: var(--wp--custom--rounded--full);
	background: var(--wp--custom--color--brand);
}

/* ---------------------------------------------------------------------------
   4 · BUTTONS — R17: at least 44×44, and 8px, never a pill.
   ⭐ U28 (2026-10-01): THREE SIZES, S 44 · M 48 · L 56, and M is the default —
     every button on 4925 and 6352, the header and the footer included (Alpesh:
     "it looks small, I want one step bigger"). Label 16 / 500 at S and M.
   ⚠ The class sits on the ANCHOR, not on core's wrapper div, because that is
     the element check-law.js measures and the element the user clicks.
   --------------------------------------------------------------------------- */
.btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: var(--wp--custom--button--height-m);
	min-width: 44px;
	padding: 0 var(--wp--custom--space--lg);
	border: 0;
	border-radius: var(--wp--custom--rounded--sm);
	font-size: var(--wp--custom--step--body);
	font-weight: var(--wp--custom--weight--display);
	line-height: var(--wp--custom--leading--display);
	letter-spacing: var(--wp--custom--tracking--text);
	text-decoration: none;
	cursor: pointer;
	transition: background-color var(--wp--custom--motion--duration) var(--wp--custom--motion--easing);
}
/* ⚠ `color: #fff` AND NOT A LITERAL. check-law.js assertion reads every
   painted colour and refuses a hex that IS one of our tokens: white is
   `--paper`, so writing it by hand states the same value twice and the two
   can drift. The guard caught this one on its first run. */
/* ⭐ THE GREEN BUTTON'S FINISH (U28): an inner light line and a white sheen over
   the top half — a finish, not the visible gradient the law refuses. ⛔ Hover and
   active set `background-color` ONLY, so the sheen stays on every state. */
.btn--brand {
	background-color: var(--wp--custom--color--brand);
	background-image: var(--wp--custom--button--sheen);
	box-shadow: var(--wp--custom--button--finish);
	color: var(--wp--custom--color--paper);
}
.btn--brand:hover   { background-color: var(--wp--custom--color--brand-hover); }
.btn--brand:active  { background-color: var(--wp--custom--color--brand-active); }
.btn--outline {
	background: transparent;
	color: var(--wp--custom--color--heading);
	box-shadow: inset 0 0 0 1px var(--wp--custom--color--hairline);
}
/* ⭐ THE OUTLINE BUTTON'S HOVER IS `ink` AT 6%, THE SAME AS `banner-2` ON A LIGHT
   GROUND (Alpesh, 2026-10-19, Round 4, item 15); it had no hover. `.btn.` so it
   beats `.hero-veil .btn--outline` (0,2,0), which sets the hero's glass fill. */
.btn.btn--outline:hover { background: color-mix(in oklab, var(--wp--custom--color--ink) 6%, transparent); }
.btn .ico {
	/* ⛔ AN ICON IS NEVER THE THING THAT GIVES WAY. The button is an inline-flex
	   box, so its svg is a flex item and shrinks like any other once the label
	   runs out of room -- it was measured at ZERO width at 360px, where the
	   arrow simply disappeared from the closing CTA while every number said the
	   button fit. ⚠ THE SCREENSHOT CAUGHT IT AND THE MEASUREMENTS DID NOT:
	   width, height and overflow were all correct with the arrow already gone.
	   ⭐ `flex: none` makes the LABEL wrap instead, which is the legible half of
	   the pair to sacrifice. Nothing shrinks it above 375px, so no painted
	   button changes anywhere the icon was already visible. */
	flex: none;
	width: 15px;
	height: 15px;
	margin-left: var(--wp--custom--space--sm);
	display: block;
	fill: none;
	stroke: currentColor;
	stroke-width: 1.3333;   /* ⛔ one RENDERED stroke: 15 / 20 × 1.3333 = 1.000 (U28: the M button's icon, was --lg's) */
	stroke-linecap: round;
	stroke-linejoin: round;
}

/* The button row. */
.row {
	display: flex;
	flex-wrap: wrap;
	gap: var(--wp--custom--space--md);
}
.section--centred .row { justify-content: center; width: max-content; max-width: 100%; }

/* ---------------------------------------------------------------------------
   5 · THE STAT TRIPLE.
   ⛔ NO SIDE-STRIPE. A green rule beside each number is a side-stripe border,
      which the law refuses by name. The separation is PAID FOR WITH AIR: the
      gap goes from 24px to 64px. Three numbers at 40px with 64px between them
      read as three things without a line.
   --------------------------------------------------------------------------- */
.stats {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--wp--custom--space--lg);
	list-style: none;
	margin: 0;
	padding: 0;
}
/* ⚠ `margin-block: 0` is not tidiness. Core gives a list's items the theme's
   block gap -- measured 8px -- which stacked on top of the grid `gap` and
   made the triple 73.2px tall against the prototype's 65.2px. */
.section .stats > li { margin-block: 0; }
.stats > li { display: grid; gap: 2px; align-content: start; padding-left: 0; }
.stats strong {
	font-size: var(--wp--custom--step--subsection);
	font-weight: var(--wp--custom--weight--display);
	line-height: var(--wp--custom--leading--display);
	letter-spacing: var(--abk-track-sub);
	color: var(--wp--custom--color--heading);
}
/* The label layer (Alpesh's pick, 2026-10-05): 12px takes 450 and the label tracking. */
.stats span {
	font-size: var(--wp--custom--step--label);
	font-weight: var(--wp--custom--weight--emphasis);
	letter-spacing: var(--wp--custom--tracking--label);
	color: var(--wp--custom--color--secondary);
}

.stats--ruled { gap: var(--wp--custom--space--3-xl); }
.stats--ruled strong { font-size: var(--wp--custom--step--display-small); letter-spacing: var(--abk-track-dsm); }

@media (max-width: 520px) {
	.stats { grid-template-columns: 1fr; }
}

/* ---------------------------------------------------------------------------
   6 · THE HERO.
   ⚠ It does NOT run under the fixed bar. Measured on the prototype at both
     widths: its page wrapper carries a 76px top offset and the hero's own box
     starts exactly at the bar's bottom edge. chrome.css already applies that
     same 76px to `.wp-site-blocks`, so the default is already correct and
     `.abk-hero-under` must NOT be set on this page.
   --------------------------------------------------------------------------- */
/* ⛔ EVERY HEADING IN A SECTION BALANCES ITS LINES, and leaving this out is
   invisible in a token diff. `text-wrap: balance` asks the browser to even
   out the line lengths instead of filling each line greedily. Measured:
   without it the problem section's h2 broke as "Your site never sleeps. Your
   / team does." against the prototype's "Your site never sleeps. / Your team
   does." -- same width, same size, same tracking, a WORSE break. ⭐ Two cards
   in the same section broke differently too.
   ⚠ A computed-style diff of size, tracking and leading passes this cleanly.
   It was found by putting the two screenshots side by side. */
.section :is(h1, h2, h3, h4, h5, h6) { text-wrap: balance; }

/* ⛔ THE SECTION HEADING TAKES ITS OWN RUNG, AND theme.json CANNOT GIVE IT
   ONE. theme.json's `elements.h2` sets a single size for every h2 on the
   site, including the footer's column headings at 14px. A landing-page
   section's h2 is the SECTION rung -- 48px at 1440, 32px at 390. Measured:
   without this it rendered at 28px against the prototype's 48px.
   ⚠ `step--section` is clamp(2rem, 3.6vw, 3rem), so its MINIMUM is 32px and
   it never drops below the 28px display boundary. It needs no tracking
   breakpoint, unlike `step--subsection` and `step--display-small`. */
.section h2 {
	font-size: var(--wp--custom--step--section);
	font-weight: var(--wp--custom--weight--display);
	line-height: var(--wp--custom--leading--display);
	letter-spacing: var(--wp--custom--tracking--display);
	color: var(--wp--custom--color--heading);
}

.hero .wrap { max-width: 1180px; }

.hero h1 {
	max-width: 20ch;
	margin-inline: auto;
	font-size: var(--wp--custom--step--hero);
	font-weight: var(--wp--custom--weight--display);
	line-height: var(--wp--custom--leading--display);
	letter-spacing: var(--wp--custom--tracking--display);
	color: var(--wp--custom--color--heading);
}

/* ⛔ THE SECOND SENTENCE GETS A SIZE RUNG, NOT A COLOUR TRICK. And the rung is
   40, not 48: at 48 the hero would carry 48 beside the 40 stat numbers --
   1.20 apart, the exact collision the placement rule exists to stop, in the
   one place a visitor looks hardest. So this line and the stat numbers SHARE
   40 and separate by weight and colour: 400 and secondary here, 600 and ink
   there. */

/* The hero's own rhythm, measured off the prototype at 1440 and 390. */
.hero .wrap > .row     { margin-top: var(--wp--custom--space--xl); }
.hero .wrap > .eyebrow { margin-top: var(--wp--custom--space--md); }
.hero .wrap > .stats   { margin-top: var(--wp--custom--space--3-xl); }
/* The lead-generation hero has a lead paragraph, which 4925's does not.
   62ch is the source's own measure for it. */
.hero .wrap > .lead    { margin-top: var(--wp--custom--space--lg); max-width: 62ch; }
/* ⭐ 24px UNDER THE BADGE, ON EVERY HERO. The rhythm reset takes every margin
   and nothing gave one back here, so the badge sat ON the headline, gap 0, at
   every width on 4925 (Alpesh saw it at 390, 2026-09-26). (0,3,1) beats the
   reset's (0,2,0). */
.hero .wrap > .badge + h1 { margin-top: var(--wp--custom--space--lg); }
/* ⭐ ON A PHONE THE DOT IS INLINE WITH THE FIRST WORD. As an inline-flex item
   it stayed alone on the left while the wrapped text centred beside it
   (lead-generation hero at 390). As an inline box it leads the first line. */
@media (max-width: 620px) {
	.hero .wrap > .eyebrow { display: block; }
	.hero .wrap > .eyebrow:not(.eyebrow--plain)::before {
		display: inline-block;
		vertical-align: middle;
		margin: -2px var(--wp--custom--space--sm) 0 0;
	}
}

.hero .eyebrow { display: inline-flex; }
/* ⛔ THE TRIPLE CENTRES ITS OWN COLUMNS. ALPESH, 2026-09-23, LOOKING AT THE
   PAINTED HERO: "the stats part is not centralized."
   ⚠ IT WAS `text-align: left`, AND THE BOX WAS ALREADY CENTRED -- 1040px
     inside an 1180px wrap. That is what made it hard to see: the BOX was
     centred and the CONTENT was not. Each column is 1fr, so every number
     started at its column's left edge, and the third label is the longest --
     so the painted mass sat left of the hero's axis while the box said
     otherwise. Everything above it, badge to eyebrow, is centred.
   ⭐ THREE CANDIDATES WERE PAINTED ON THE REAL PAGE: left as it shipped, the
      columns centred, and the box pulled in to 820px while staying left. He
      picked the centred columns. */
.hero .stats   { max-width: 1040px; margin-inline: auto; text-align: center; }

/* ⛔ THE HERO'S STAT LABELS TAKE THE HEADING INK, AND NOWHERE ELSE DOES.
   ⚠ MEASURED 2026-09-22, ON THE PAINTED PAGE: the wash runs `to top`, so it is
     STRONGEST AT THE SECTION'S FLOOR -- and the floor is where the smallest
     type on the page lives. The ground sampled beside the labels is #C3E5D6,
     and --secondary #656550 on it is 4.39:1. THE FLOOR IS 4.5. It failed.
   ⛔ NO CHECKER COULD SEE IT. check-law.js grades a colour against its TOKEN
      ground, and the token ground here is --tint, where #656550 is 5.64. The
      wash is a ::before layer, so the real ground is never the token.
   ⭐ THE WASH IS NOT WEAKENED TO PAY FOR THIS. Easing its bottom stop to 26%
      buys 4.60 -- a 0.10 margin, the same margin refused on the terracotta
      gradient -- and visibly drains the band. Four candidates were painted on
      the real page and Alpesh picked this one. Heading ink measures 14.46:1.
   ⚠ THE HIERARCHY IS CARRIED BY SIZE AND WEIGHT, NOT BY COLOUR: 40px semibold
     against 12px regular. That was checked in the paint, not reasoned about.
   ⛔ SCOPED TO `.hero`. The ink-ground stats remap to --ink-muted above and
      must not move; a bare `.stats span` rule would take them too. */
.hero .stats span { color: var(--wp--custom--color--heading); }

/* ---------------------------------------------------------------------------
   7 · THE HERO VEIL -- `hero-veil`, page 4925 only (lp-hero.php). ⭐ Alpesh's
   F1, 2026-09-30, picked over five painted rounds
   (docs/landing-pages/ai-chatbot-for-wordpress/drawing-review-2026-09-28/hero-wash/):
   white paper, one green horizon veil BEHIND THE BUTTONS, and "See it work"
   in translucent white so the veil's lines do not run through its label.
   It replaces `wash-bloom` on 4925 ("I don't want the olive tint").
   ⭐ THE VEIL HANGS FROM THE BUTTON ROW, NOT FROM THE SECTION. Painted on the
     section, it scaled with the section's height, and the one-line 56px
     heading lifted the buttons off it. Here it is the row's ::before, the
     width of the screen at 16:9, with the row's middle at 55.56 % of its
     height -- where the row sat at 48px -- so it stays behind the buttons at
     every width and every heading size. The section clips it (`overflow`)
     to its own corners; z-index -1 puts it under the content inside the
     section's isolation.
   ⭐ The file is ops/assets/gen-veil-hero.js output (`--check`).
   ⚠ ON A PHONE the row stacks its two buttons, so the veil is 220vw wide:
     at 100vw the band was thinner than one button.
   --------------------------------------------------------------------------- */
.hero-veil { position: relative; isolation: isolate; overflow: clip; }
/* ⭐ THE HERO GROUND, REDONE 2026-10-03 (Alpesh, after dub.co's hero): "I want
   the hero gradient ... removed, and the way we had our earlier gradient to
   be brought back, like the rectangular section we had. I didn't want the
   olive tint there. We keep it white along with this gradient", then our own
   white lines in place of dub's grid ("A, go with the veil lines"), and the
   green first G2 ("A, go with G2", over 34 / 16 and 14 / 6), then R15 below.
   ⭐ THREE LAYERS on the white panel: the brand-green rise, and over it 22 fine WHITE lines -- the
     horizon veil's own curves (ops/assets/gen-veil-hero.js, hero-lines.svg).
     White on white is invisible, so the lines show only where the green is
     and fade out upward, the way dub's grid fades into its colour.
   ⭐ THE RAMP IS dub.co's (R15, Alpesh: "the orange travel is still at the
     top, very faint ... Can we replicate the dubs.co as closely as
     possible?"; "A"): ONE straight rise, brand 15 % at the foot, clear at
     82 % of the height. Measured from his screenshot, dub's colour starts at
     about 20 % and rises in a line; G2 (22 / 10 %, clear at 68 %) left the
     top half pure white. R15 follows dub within 1-2 levels at every height.
   ⛔ The green veil behind the buttons (F1, `.row::before`) is gone. */
.hero-veil::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	background:
		url("../img/veil/hero-lines.svg") center bottom / 100% auto no-repeat,
		linear-gradient(to top,
			color-mix(in oklab, var(--wp--custom--color--brand) 15%, transparent) 0%,
			transparent 82%);
}
.hero-veil .btn--outline {
	background: color-mix(in oklab, var(--wp--custom--color--paper) 80%, transparent);
	-webkit-backdrop-filter: blur(8px);
	backdrop-filter: blur(8px);
}

/* ⭐ THE HERO HEADLINE, A NAMED ONE-TIME EXCEPTION (Alpesh, 2026-09-30): 56px
   at 1440 on ONE line, over the ladder's `hero` 48 on two. 56 is the
   source's size, looked at beside 48, 60 and 64. ⛔ NOT A LADDER STEP: he
   chose "one time exception for this page" over raising `hero` (which would
   move §3's lines and the lead-generation hero) and over an 11th step.
   32px on a phone, as `hero`. ops/measure/check-law.js names it (an h1 with
   this class, 32–56px) and prints it on every run.
   ⚠ `tw:text-hero` carried the display tracking and leading WITH the size, so
     they are restated here from the tokens (check-law.js caught the loss). */
.hero-headline {
	font-size: clamp(2rem, 3.8889vw, 3.5rem);
	line-height: var(--wp--custom--leading--display);
	letter-spacing: var(--wp--custom--tracking--display);
}

/* ---------------------------------------------------------------------------
   7a · THE DOT FIELD -- `hero--dots`, page wordpress-lead-generation only
   (lg-hero.php). ⭐ Alpesh picked it by looking, 2026-09-26: the source's own
   hero ground (C of four that impeccable polished and composed on the real
   page), in place of 4925's wash.
   ⛔ A GROUND TREATMENT, NOT AN ELEMENT: two pseudo-layers behind the copy,
      faded by masks, never in the reading order (the Ground-Treatment Rule).
      ::before is one soft brand glow behind the headline; ::after is the
      source's 9px dot grid, masked to the two sides.
   ⭐ THE SOURCE'S NUMBERS, RE-TOKENED: its dot is rgba(12,21,32,.42) at .34
      opacity, here the ink at 42% at .34; its glow is its green at 10%, here
      the brand at 10%. The masks are the source's two 46vw panels, restated
      as percentages of the section.
   ⚠ ON A PHONE THE DOTS MOVE TO THE OUTER EDGES. With the source's masks the
     dots sat under the stat captions, and the darkest pixel under them read
     4.35 : 1. Measured on the paint after the move: every text 5.1 or more.
   ⭐ THE HEADLINE IS TWO LINES FROM 768px UP at the source's 26ch; 4925's
      20ch broke it into three. The stat captions step up from `label` to
      `body-small`: at 12px under 30px numbers they read as footnotes.
   --------------------------------------------------------------------------- */
.hero--dots { position: relative; isolation: isolate; }
.hero--dots::before,
.hero--dots::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
}
.hero--dots::before {
	background: radial-gradient(ellipse 778px 424px at 50% 40%,
		color-mix(in oklab, var(--wp--custom--color--brand) 10%, transparent) 0%,
		transparent 66%);
}
.hero--dots::after {
	opacity: .34;
	background-image: radial-gradient(circle,
		color-mix(in oklab, var(--wp--custom--color--ink) 42%, transparent) .85px,
		transparent 1px);
	background-size: 9px 9px;
	--hero-dots-mask:
		radial-gradient(ellipse 21% 48% at 12% 31%, #000 0%, rgba(0, 0, 0, .5) 42%, transparent 74%),
		radial-gradient(ellipse 17.5% 41% at 19.5% 74%, #000 0%, transparent 72%),
		radial-gradient(ellipse 20% 50% at 88% 38%, #000 0%, rgba(0, 0, 0, .5) 44%, transparent 76%),
		radial-gradient(ellipse 15.6% 36% at 80.5% 79%, #000 0%, transparent 70%);
	-webkit-mask-image: var(--hero-dots-mask);
	        mask-image: var(--hero-dots-mask);
}
@media (max-width: 720px) {
	.hero--dots::after {
		opacity: .22;
		--hero-dots-mask:
			radial-gradient(ellipse 16% 30% at 0% 22%, #000 0%, transparent 74%),
			radial-gradient(ellipse 16% 30% at 100% 48%, #000 0%, transparent 74%);
	}
}
.hero--dots .wrap > h1 { max-width: 26ch; }
/* ⭐ THE PANEL READS AS A BOX AGAIN, BY ITS EDGE, NOT BY A TINT: on the paper
   floor a white panel has no visible edge. Alpesh, 2026-09-26: "a light border
   so that make it look like a rectangular box like earlier" -- the `edge` ring,
   which the Shadow-Budget Rule leaves unlimited. And 32px of side space on a
   phone, this hero only (his pick: the rest of the page keeps the gutter). */
.hero--dots { box-shadow: var(--wp--custom--shadow--edge); }
@media (max-width: 620px) {
	.hero--dots { padding-inline: var(--wp--custom--space--xl); }
}
.hero--dots .stats span {
	/* 14px is not the label layer, so it keeps the body weight and the text tier. */
	font-size: var(--wp--custom--step--body-small);
	font-weight: var(--wp--custom--weight--body);
	letter-spacing: var(--wp--custom--tracking--text);
	margin-top: var(--wp--custom--space--xs);
}

/* ===========================================================================
   ⛔ EVERY TYPE SIZE TAKES ITS OWN TRACKING TIER, AND A CLAMP CHANGES THE SIZE
   WITHOUT CHANGING THE TIER. This block is the correction, and it is the one
   thing in this file that is easiest to get wrong.

   The three tiers, and they step ONE WAY ONLY:
       >= 28px   -0.0275em   display
       20-27px   -0.0100em   lead
       <  20px    0          text

   ⚠ THE BREAKPOINTS ARE DERIVED, NOT CHOSEN. Each role switches at the exact
   viewport where its own clamp crosses 28px:
       step--display-small  clamp(1.5rem, 2.0834vw, 1.875rem)  ->  28px at 1344px
       step--subsection     never crosses: 24px at most (the lead tier everywhere)
   (the 2026-09-25 ladder; see the variables at the top of this file)

   ⛔ AND THIS BLOCK SITS AT THE END OF THE FILE ON PURPOSE. A media query adds
   NO specificity. Put it above the rules it corrects and it loses to them on
   source order, silently, at one width only -- which is the hardest kind of
   defect to see. An override must come AFTER the rule it overrides as well as
   match its shape.

   ⭐ ops/measure/check-law.js reads the tier off the RENDERED size, so a value
   that is right at 1440 and wrong at 390 fails there and only there.
   =========================================================================== */

/* ---------------------------------------------------------------------------
   8 · THE CUSTOMER LOGO ROW — U6, section 2.
   Nine real customer marks, scrolling as one continuous wall.
   --------------------------------------------------------------------------- */
.marq {
	position: relative;
	overflow: hidden;
	/* ⛔ A CUT EDGE IS A MASK, NEVER A GRADIENT OVERLAY. An overlay has to
	   know the ground it sits on and stops being invisible the moment the
	   section's ground changes; a mask cuts the element itself. */
	mask-image: linear-gradient(to right, transparent 0%, #000 6%,
	                            #000 94%, transparent 100%);
}

.marq__label {
	text-align: center;
	/* ⛔ 48px, NOT 24. ALPESH, 2026-09-23: "I feel there should a bit more
	   spacing between the text and logos."
	   ⚠ 40px WAS PAINTED AND REFUSED -- it is not on the spacing ladder, and
	     an off-ladder value is exactly what check-tokens.sh exists to catch.
	     The legal rungs either side of 24 are 32 and 48; both were painted at
	     1440 and he took the larger. A 12px caption above a 34px wall of
	     marks needs the room to read as a caption rather than a label stuck
	     to the row. The section's own frame is 64px, so 48 still sits inside
	     it rather than competing with it. */
	margin: 0 0 var(--wp--custom--space--2-xl);
	/* ⛔ BLEED THE FRAME, NEVER THE SENTENCE. `.marq`'s mask fades its outer
	   6% each side, and the label is inside `.marq`, so a sentence as wide as
	   the row fades out at both ends. Measured 2026-09-25 at 390: the
	   lead-generation label ran 19px into each fade. This padding is the
	   mask's own 6%, so the label wraps inside the opaque band instead.
	   ⚠ Page 4925's shorter label measured 264px in a 294px band: still one line. */
	padding-inline: 6%;
	font-size: var(--wp--custom--step--label);
	font-weight: var(--wp--custom--weight--emphasis);
	letter-spacing: var(--wp--custom--tracking--label);
	line-height: var(--wp--custom--leading--body);
	color: var(--wp--custom--color--secondary);
}

.marq__track {
	display: flex;
	align-items: center;
	gap: var(--wp--custom--space--2-xl);
	width: max-content;
	margin: 0;
	padding: 0;
	list-style: none;
	animation: marq-run 44s linear infinite;
}
/* ⛔ -50% AND NOT -100%, AND THE MARKUP IS WHY. The nine marks are printed
   TWICE, so the track is exactly two identical halves. Moving it by half its
   own width lands the second nine where the first nine started, and the loop
   has no seam. Print the nine once and this jumps. */
@keyframes marq-run { to { transform: translateX(-50%); } }

/* ⛔ MANDATORY, AND IT DOES NOT SLOW THE ROW -- IT STOPS IT. A marquee that
   merely runs slower is still motion, and the request was for none. The row
   wraps into a static centred grid, which keeps all nine visible. The mask
   also goes: with nothing moving, a cut edge hides marks for no reason. */
@media (prefers-reduced-motion: reduce) {
	.marq { mask-image: none; }
	.marq__track {
		animation: none;
		width: 100%;
		flex-wrap: wrap;
		justify-content: center;
		gap: var(--wp--custom--space--xl) var(--wp--custom--space--2-xl);
	}
	/* ⛔ AND THE DUPLICATE NINE GO. They exist ONLY to make the loop seamless.
	   With nothing looping they are just repeats, so a visitor who asked for
	   no motion met every customer TWICE -- eighteen marks in a static grid,
	   which reads as a longer customer list than we have. ⚠ THE PROTOTYPE HAS
	   THIS FAULT TOO: its reduced-motion rule wraps the same eighteen. Found
	   by painting the branch rather than by reading the rule. */
	.marq__mark[aria-hidden="true"] { display: none; }
}

/* ⛔ OPTICAL WEIGHT IS NOT PIXEL HEIGHT, AND MATCHING THE HEIGHT DOES NOT
   DELIVER IT. Measured: the nine source files run from 72px to 484px wide at
   a common 80px height. Set to one height, `Select Interiors` occupied nearly
   SEVEN TIMES the width of `Emerald Yards`, and the row read as scattered
   noise rather than as a wall.
   ⭐ So every mark gets THE SAME BOX and is contained inside it, which gives
   the row one rhythm. `data-w` then trims the DENSE marks, which is the only
   part of optical weight a box cannot do. */
.marq__mark {
	display: block;
	flex: none;
	width: 132px;
	height: 34px;
	object-fit: contain;
	object-position: center;
	/* Q9 candidate A, and it is what the prototype paints. `brightness(0)`
	   AFTER `grayscale(1)`, so a mark's own colour cannot leak through at any
	   lightness. ⚠ 0.55 measured too faint to read as testimony; 0.68 holds
	   the row together without competing with the line above it. */
	filter: grayscale(1) brightness(0) opacity(0.68);
}
/* ⭐ Q9 SETTLED BY ALPESH, 2026-09-19: GREY AT REST, THEIR OWN COLOURS ON
   HOVER. Neither camp on its own -- the flat ink holds the row together as a
   wall, and the colour is there for anyone who stops on a mark, which is the
   moment it reads as a citation rather than as decoration.
   ⛔ AND THE ROW HAS TO STOP FOR THE HOVER TO BE USABLE. On a moving strip the
   mark slides out from under the cursor before the transition finishes, so
   the colour would only ever be glimpsed. Pausing is what makes the feature
   work; it is not an extra.
   ⚠ `@media (hover: hover) and (pointer: fine)` ON PURPOSE. A touch screen
   reports no hover, and without the guard a tap can leave a mark stuck in
   colour with no way to clear it. Touch keeps the flat ink, which is the
   state that was shipped. */
@media (hover: hover) and (pointer: fine) {
	.marq__mark { transition: filter var(--wp--custom--motion--duration) var(--wp--custom--motion--easing); }
	.marq:hover .marq__track { animation-play-state: paused; }
	.marq__mark:hover { filter: none; }
}
/* R26 again: if motion is refused, nothing pauses because nothing runs. The
   colour on hover stays -- it is not motion. */

.marq__mark[data-w="xs"] { transform: scale(0.94); }   /* thin serif wordmark */
.marq__mark[data-w="s"]  { transform: scale(0.86); }
.marq__mark[data-w="sm"] { transform: scale(0.90); }
.marq__mark[data-w="m"]  { transform: scale(0.82); }   /* dense script mark  */
.marq__mark[data-w="l"]  { transform: scale(0.78); }   /* solid glyph        */

@media (max-width: 720px) {
	.marq__mark { width: 104px; height: 28px; }
}

/* ---------------------------------------------------------------------------
   9 · THE PROBLEM SECTION — U6, section 3. Five components, and four of them
   are reused by later sections rather than built again.
   --------------------------------------------------------------------------- */

/* -- 9a · THE GHOST TRANSCRIPT.
   ⭐ The cheapest lift device on the page and the one no reference site can
   copy, because our content IS conversations. It is TEXT, so it costs no
   asset and no request.
   ⛔ EVERY LINE IS ALREADY ON THIS PAGE -- the three visitor rows in the
   drawing below it and the four sentences of the paragraph beside it. The
   section that carried this device the first time was removed precisely
   because it INVENTED words. */
.ghost {
	position: absolute;
	inset: 0;
	z-index: -1;
	width: auto;
	max-width: none;
	user-select: none;          /* a drag cannot select it */
	pointer-events: none;
	font-size: 13px;
	line-height: var(--wp--custom--leading--body);
	letter-spacing: var(--wp--custom--tracking--text);
	/* ⛔ 0.055 WAS TUNED FOR AN INK GROUND, where light type on black barely
	   shows. On the tint it measured 1.112 : 1 and every line was READABLE --
	   the one thing this device forbids by name. Read off the paint, halved,
	   and re-read: 1.045 : 1. */
	color: var(--wp--custom--color--ink);
	opacity: 0.026;
	padding: var(--wp--custom--space--lg);
	columns: 3;
	column-gap: var(--wp--custom--space--2-xl);
	overflow: hidden;
	/* ⛔ A TREATMENT MUST COVER WHAT IT TREATS. An earlier centre at 50% 30%
	   left the lower half of an 808px section plain. Centred, and wider. */
	mask-image: radial-gradient(150% 150% at 50% 50%, #000 45%, transparent 95%);
}
/* ⛔ ONE ROW PER TURN. Without `display: block` the spans run together and the
   field reads as a PARAGRAPH -- a thing to read rather than a texture.
   ⚠ AND NOT `white-space: nowrap`: that made each row 1408px wide, so the
   columns collapsed and only two rows survived. Measured both ways. */
.ghost span { display: block; }
@media (max-width: 760px) { .ghost { columns: 1; } }

/* ⛔ CORE'S 24px FLOW RHYTHM, ZEROED ONCE FOR EVERY CONTAINER THIS SECTION
   INTRODUCES. WordPress prints `:root :where(.is-layout-flow) > *` at (0,2,0),
   so a single-class reset loses to it however late this file loads -- KD46,
   again, and only MARGIN behaves this way. ⭐ Then the rhythm is stated
   positively below, at (0,2,1) or better, which is what makes each gap a
   decision rather than a leftover. */
:root .section > *,
:root .split > *,
:root .fgrid > *,
:root .f > *,
:root .demo__frame > *,
:root .split__copy > * { margin-block: 0; }

/* ⭐ THE SECTION OPENING IS A UNIT, and this rhythm is a large part of why the
   prototype's openings read composed rather than merely correct. */
.section h2 + p,
.section h2 + .lead { margin-top: var(--wp--custom--space--lg); }
/* ⛔ A SECTION'S HEADING PAIR NEVER TOUCHES THE CONTENT BELOW IT.
   ⚠ AND THIS RULE FIRES THROUGH A HIDDEN ELEMENT, WHICH IS WHY THE MOBILE GAP
   IS BIGGER THAN THE DESKTOP ONE. The copy ships a long and a short version of
   the same sentence and hides one by width. At 390 the LONG lead is
   `display: none`, but it is still the previous SIBLING -- so the short lead
   matches `.lead + *` and takes 48px where the long one would have taken 24.
   ⭐ Measured on the prototype at 390 and reproduced deliberately: Alpesh
   decided this section from that paint, so the paint is the specification. */
/* ⛔ `:root` HERE IS LOAD-BEARING. This rule has to outrank BOTH the rhythm
   reset above it AND each component's own default top margin -- the
   definition list asks for 32px on its own and must take 48px when it follows
   a lead. Measured: without it every one of the four term/definition sections
   came out 16px short. */
:root .section .lead + * { margin-top: var(--wp--custom--space--2-xl); }
/* ⛔ `>` THROUGHOUT, AND IT IS A RULE RATHER THAN A TIDY-UP: A CARD'S TYPE
   RULES MUST NOT REACH INSIDE A DRAWING IT HOLDS. A drawing states its own
   type. Measured twice in one section: the card's paragraph colour repainted
   the widget's white user bubble grey-on-green, and the card's 8px paragraph
   rhythm repainted the app screen's 2px caption gap, growing every card.
   ⭐ A card's own copy is its DIRECT children; anything deeper belongs to a
   drawing. */
.section .f > h3 + p { margin-top: var(--wp--custom--space--sm); }

/* -- 9b · THE SPLIT. Two columns on the wide screen, one below 900px. */
.split { display: grid; gap: var(--wp--custom--space--2-xl); align-items: center; }
@media (min-width: 900px) {
	.split            { grid-template-columns: 1fr 1fr; }
	.split--wide-left { grid-template-columns: 1.15fr 1fr; }
	/* ⭐ 40 / 60, the problem section of `wordpress-lead-generation`:
	   Alpesh, 2026-09-25 -- "more towards 60 for the animation and 40 for the
	   text, so the animation can come out properly." ⚠ The source's near-even
	   1 : 1.06 was painted and built on 2026-09-26, and he asked for 40 / 60
	   back the same day: it shrank the animation to 288px at 900. */
	.split--40-60     { grid-template-columns: 2fr 3fr; }
	.split--flip > *:first-child { order: 2; }
}
/* ⛔ SHOW THE MECHANISM BEFORE THE CLAIM, AND BELOW 900px THERE IS NO LEFT AND
   NO RIGHT -- the split stacks in DOM ORDER, and the DOM writes the argument
   first. Measured: all four split sections put the claim first at 390,
   against three of four that pass at 1440. ⭐ The phone was the worse half and
   nobody had looked. ⚠ It costs the desktop rhythm nothing, because a stacked
   column has no side to alternate. */
@media (max-width: 899px) {
	.split > figure,
	.split > .demo__frame { order: -1; }
}

.lead {
	font-size: var(--wp--custom--step--lead);
	line-height: var(--wp--custom--leading--body);
	letter-spacing: var(--wp--custom--tracking--lead);
	/* ⭐ U27 (2026-09-30): the argument is a darker olive grey, not `secondary` (D1, P-W1). */
	color: var(--wp--custom--color--secondary-strong);
	max-width: 62ch;
}

/* ⭐ BODY-LARGE -- A SPLIT COLUMN'S PARAGRAPH IS NOT A LEAD (2026-09-25).
   A lead is one or two sentences under a heading. In a split's text column
   the same `.lead` paragraph ran three and four lines as body copy, which is
   the fault Alpesh named: "using the lead font size for a body paragraph is,
   I think, causing the issue." Applied BY POSITION, so no page's markup had
   to change: 1 paragraph on the lead-generation page, 5 on 4925.
   ⛔ IT KEEPS THE LEAD COLOUR (`secondary-strong`) ON PURPOSE: 18 against the
      16px ink body is only 1.125, so the colour carries the difference (the law, bodyLarge).
   ⚠ (0,3,1): it must outrank `.lead` and stay under nothing that sets a size. */
:root .split__copy > p.lead {
	font-size: var(--wp--custom--step--body-large);
	letter-spacing: var(--wp--custom--tracking--text);
}

/* ⭐ ON A PHONE EVERY LEAD IS BODY-LARGE, 2026-09-26 (the law, `lead`).
   Alpesh, looking at the lead-generation hero at 390: the headline and the
   lead "look big on mobile, there is no breathing space". At 20px the hero
   lead ran five lines in a 334px column. ⛔ THE TRACKING STEPS WITH THE SIZE:
   18 is under 20, so it takes the TEXT tier, not the lead tier -- check-law.js
   fails the other pairing by name. Below `tab` (640px), the law's phone layout. */
@media (max-width: 639px) {
	.lead {
		font-size: var(--wp--custom--step--body-large);
		letter-spacing: var(--wp--custom--tracking--text);
	}
}

/* -- 9d · THE FIGURE AND THE DRAWING IT HOLDS.
   ⭐ THE RULE, STATED ONCE: an object takes the light ground its surface is
   NOT, and anything inside it takes the one the OBJECT is not. Written as a
   pair because this exact inversion has been got wrong three times. */
.figure {
	--fig:    var(--abk-floor);
	--fig-in: var(--abk-panel);
	border-radius: var(--wp--custom--rounded--lg);
	background: var(--fig);
	padding: var(--wp--custom--space--xl);
	overflow: hidden;                 /* ⛔ the ground clips its own bleed */
	margin: 0;
}
:is(.section--floor, .tw\:bg-paper) .figure { --fig: var(--abk-panel); --fig-in: var(--abk-floor); }

/* ⭐ THE SUPPRESSION LADDER, BOLDEST LAST, DONE AS A LAYER and never baked
   into the artwork. Exactly one thing per drawing carries `.lit`. */
.dim { opacity: 0.35; }
@media (max-width: 720px) {
	.figure { padding: var(--wp--custom--space--md); }
}

/* -- 9e · THE FEATURE GRID. One component, three densities. This is the reuse
   test: page 4558 runs SIX consecutive feature sections, and if each needed
   its own CSS the vocabulary has failed. */
/* ⚠ `:root .section .fgrid` AND NOT `.fgrid`. The rhythm reset on
   `.section .wrap > *` is (0,2,0) and it zeroed this margin -- the four cards
   sat flush against the paragraph above them, 48px too high. A positive
   rhythm rule has to outrank the reset that cleared core's. */
.fgrid { display: grid; gap: var(--wp--custom--space--md); }
:root .section .fgrid { margin-top: var(--wp--custom--space--2-xl); }
.fgrid--3 { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 899px) { .fgrid--3 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) { .fgrid--3 { grid-template-columns: 1fr; } }

/* ⭐ THREE CARDS, THEN TWO -- page `wordpress-lead-generation`, lg-features.php
   (plan KTD5, Alpesh's call). Six tracks: row 1 spans two each (one third),
   row 2 spans three each (one half). At 1440 in the 1280 wrap with the 16px
   gap that is 416px and 632px.
   ⛔ A MODIFIER, AFTER THE COMPONENT IT EXTENDS, and 4925 does not carry it.
   ⚠ THE BREAKPOINTS ARE `fgrid--3`'s: two columns at 900 and below, one at
     620 and below. Each media rule comes AFTER the base rule (gotcha 74). At
     900 the fifth card sits alone, exactly as it would in `fgrid--3`. */
.fgrid--3-2 { grid-template-columns: repeat(6, 1fr); }
.fgrid--3-2 > * { grid-column: span 2; }
/* ⭐ ROW 2 IS THE SOURCE'S BENTO (Alpesh, 2026-09-26): the brand card at TWO
   THIRDS, 848px, and the connected tools at ONE THIRD, 416px. It was two
   halves. */
.fgrid--3-2 > :nth-child(4) { grid-column: span 4; }
.fgrid--3-2 > :nth-child(5) { grid-column: span 2; }
@media (max-width: 899px) {
	.fgrid--3-2 { grid-template-columns: repeat(2, 1fr); }
	.fgrid--3-2 > *,
	.fgrid--3-2 > :nth-child(5) { grid-column: auto; }
	/* the wide card keeps a whole row, so its drawing is not halved */
	.fgrid--3-2 > :nth-child(4) { grid-column: 1 / -1; }
}
@media (max-width: 620px) { .fgrid--3-2 { grid-template-columns: 1fr; } }

/* ⛔ A CARD IS ALWAYS THE FLOOR, ON EVERY GROUND. An earlier rule read "a card
   takes the light ground its surface is NOT", which tinted every card on a
   white section. Alpesh overruled it: the tint is a SECTION-scale token and a
   card stays white. ⭐ A white card on a white section reads by its EDGE --
   `shadow--edge` is ink at 10%, which is exactly the barely-there 1px border
   he cited two references for, and it costs no shadow budget. */
.f {
	background: var(--abk-floor);
	border-radius: var(--wp--custom--rounded--md);
	padding: var(--wp--custom--space--lg);
	box-shadow: var(--wp--custom--shadow--edge);
	margin: 0;
}
/* ⛔ THIS HEADING RENDERED AT BODY SIZE CARRYING DISPLAY TRACKING AND DISPLAY
   LEADING, on eleven cards. Two faults in one line: it was the SAME SIZE as
   the paragraph under it, and it inherited the display tracking from the
   shared heading rule -- a 72px correction applied to 16px text, which
   squeezed the letters shut. ⭐ Alpesh picked the LEAD size from four painted
   candidates. It is not a new rung; the card reuses one of the nine.
   ⚠ It takes the LEAD tracking because that is 21px's tier, and the DISPLAY
   line-height because it is a heading, not a paragraph. */
.f > h3 {
	margin: 0;
	font-size: var(--wp--custom--step--lead);
	font-weight: var(--wp--custom--weight--display);
	letter-spacing: var(--wp--custom--tracking--lead);
	line-height: var(--wp--custom--leading--display);
	color: var(--wp--custom--color--heading);
}
.f > p {
	margin-top: var(--wp--custom--space--sm);
	color: var(--wp--custom--color--secondary);
	max-width: none;
}

/* ---------------------------------------------------------------------------
   10 · THE PUNCH — U6, section 4. One sentence at display size, alone on the
   floor. No card, no ground, no icon: THE TYPE IS THE SECTION.
   ⭐ This is the gear change the middle of the page was missing. Eleven
   sections read heading → lead → row of cards, and both accent grounds were
   already spent, so the variation had to come from GEOMETRY.
   --------------------------------------------------------------------------- */

/* ⭐ THE STATEMENT RULE, premium pass U25 (2026-09-30): the source's 40 x 2px
   green line above §3's two lines, picked by Alpesh on the painted map.
   ⚠ 40px AND 2px ARE NOT SPACING STEPS. They are the source's own values: a
     short stroke, like a border width, not a gap. Flagged to Alpesh with the
     build; the nearest steps are 32px or 48px wide.
   ⭐ Brand green is spent once in this section, on this line (the
     Balanced-Accent Rule). */
.statement-rule {
	display: block;
	width: 40px;
	height: 2px;
	margin: 0 auto var(--wp--custom--space--lg);
	background: var(--wp--custom--color--brand);
}
@media (min-width: 640px) {
	.statement-rule { margin-bottom: var(--wp--custom--space--xl); }
}

/* ⭐ §3'S TWO LINES, A NAMED EXCEPTION (Alpesh, 2026-09-30, U25): he picked
   the painted values over the nearest law steps, looking at both on the real
   page. 34ch keeps each line on ONE line at 1440 (24ch, the `statement`
   width, broke them 2 + 2). #9A9A92 is the source's faded first line; the
   `secondary` #656550 read as a second heading.
   ⚠ NEITHER VALUE IS A LAW STEP YET. The type law (plan U27) decides whether
     they become steps or go. Until then, only this section uses them.
   ⛔ #9A9A92 IS 2.834 : 1 ON WHITE, UNDER THE LARGE-TEXT FLOOR OF 3. Alpesh
      kept it on 2026-09-30 as a ONE-TIME exception on this line only.
      ops/measure/check-law.js names it (this class AND this colour) and
      prints it on every run; any other grey here, or this grey anywhere else,
      fails. */
.statement-line { max-width: var(--wp--custom--width--statement); }
.statement-line--faded { color: #9A9A92; }

/* ⭐ THE STATEMENT SHEEN, the law's ONE named exception to gradient text
   (Alpesh, 2026-09-30; the design-system skill, "Don't"; SECTION-MAP §3,
   option 3). A band of brand-text → brand → brand-text crosses line B ONCE,
   left to right, as the line rises through the screen, and runs back when the
   reader scrolls back. At both ends of the pass the band is off the line, so
   the line at rest is plain ink.
   ⭐ THE PASS RUNS FROM THE LINE'S ENTRY AT THE BOTTOM (cover 5%) TO JUST
     UNDER THE HEADER (cover 80%), and the band is ON the line for all of it
     (Alpesh, on the live page, 2026-09-30: first "too fast … if I am
     scrolling fast I don't even notice", then "I can only see the gradient
     when the line is centralized"). ⚠ The keyframes are NOT the source's
     150% → -50%: with a 260% image that moves the band across the line in
     the middle third only. 94% puts the band's right green edge (58% of the
     image) at the line's left edge; 6% puts its left green edge (42%) at the
     line's right edge: -1.6 × position + 2.6 × edge = 0 or 1.
   ⭐ CSS, NOT GSAP: a scroll timeline does exactly this, and 4925 loads no
     GSAP (the law refuses the 45 KB for one effect).
   ⛔ LAW GREENS ONLY. The source peaked at #22B98C, 2.50 : 1 on white; brand
      is 4.50 : 1 and brand-text 6.16 : 1, so every point of the band clears
      4.5. `color` stays ink, so a check that reads `color` still reads ink.
   ⛔ PLAIN INK when the browser has no scroll timeline, under reduced
      motion, in forced colours, and in a selection. */
@supports (animation-timeline: view()) {
	@media (prefers-reduced-motion: no-preference) and (not (forced-colors: active)) {
		.statement-line--sheen {
			background-image: linear-gradient(100deg,
				var(--wp--custom--color--ink) 0%,
				var(--wp--custom--color--ink) 30%,
				var(--wp--custom--color--brand-text) 42%,
				var(--wp--custom--color--brand) 50%,
				var(--wp--custom--color--brand-text) 58%,
				var(--wp--custom--color--ink) 70%,
				var(--wp--custom--color--ink) 100%);
			background-size: 260% 100%;
			background-position: 94% 0;
			-webkit-background-clip: text;
			background-clip: text;
			-webkit-text-fill-color: transparent;
			animation: statement-sheen linear both;
			animation-timeline: view();
			animation-range: cover 5% cover 80%;
		}
		.statement-line--sheen::selection { -webkit-text-fill-color: var(--wp--custom--color--ink); }
	}
}
@keyframes statement-sheen {
	from { background-position: 94% 0; }
	to   { background-position: 6% 0; }
}

/* ---------------------------------------------------------------------------
   11 · HOW WE HELP — U6, section 5. A trio of welled cards, each holding a
   drawing of the real product, plus a pull quote.
   ⛔ THE TWO BACKGROUND CURVE OBJECTS ARE NOT BUILT, AND THAT IS MEASURED,
      NOT AN OMISSION. The prototype carries two `<svg class="obj obj--help
      obj--cv">` here, and both compute `display: none`: the curve family is
      an unpicked experiment gated behind `data-obj="curve"`, and the
      prototype's own `<html>` sets no `data-obj` at all. Engrave is the
      default and it has no window in this section. Building inert markup
      would have looked like faithfulness and painted nothing.
   --------------------------------------------------------------------------- */

/* The icon. ONE rendered stroke width across the page: an icon drawn at a
   different size takes a compensating `stroke-width` so the painted line is
   always 1.000. Two stroke widths read as assembled. */
.ico {
	width: 20px;
	height: 20px;
	display: block;
	fill: none;
	stroke: currentColor;
	stroke-width: 1;
	stroke-linecap: round;
	stroke-linejoin: round;
}

/* ⛔ A GREEN GROUND UNDER GREY TEXT IS DECORATION, NOT MEANING. The chip's own
   text is `--secondary`, so its ground is the neutral tint. */
.chips { display: flex; flex-wrap: wrap; gap: 6px; list-style: none; padding: 0; }
/* ⚠ `margin: 0` because core gives a LIST'S ITEMS the theme's block gap, and
   the chip row is a flex row with its own `gap`. Measured: each chip came out
   8px taller and the row 8px taller with it. */
:root .chips li,
.chips li {
	margin: 0;
	/* The label layer (Alpesh's pick, 2026-10-05), not a literal 12px. */
	font-size: var(--wp--custom--step--label);
	font-weight: var(--wp--custom--weight--emphasis);
	letter-spacing: var(--wp--custom--tracking--label);
	color: var(--wp--custom--color--secondary);
	padding: 4px 8px;
	border-radius: var(--wp--custom--rounded--sm);
	background: var(--wp--custom--color--tint);
}

/* ⛔ THE STEP NUMERAL GAVE UP ITS PILL. It is neither a badge nor a pill, and
   those are the only two components the law lets take the full radius -- and
   it sits outside every drawing root, so the drawn-chrome exemption does not
   cover it either. The Three-Radii Rule holds on this page with NO exception. */
.step__n {
	display: grid;
	place-items: center;
	width: 32px;
	height: 32px;
	border-radius: var(--wp--custom--rounded--sm);
	background: var(--wp--custom--color--ink);
	color: var(--wp--custom--color--ink-heading);
	font-size: var(--wp--custom--step--label);
	font-weight: var(--wp--custom--weight--emphasis);
	line-height: var(--wp--custom--leading--display);
	margin-bottom: var(--wp--custom--space--md);
}

/* -- 11a · THE WELLED CARD, AND ITS MEDIA-FIRST VARIATION.
   ⛔ `.f--media-first` IS A VARIATION OF `.f--welled`, NOT A SECOND CARD.
      Only the ORDER changes, so a comparison isolates one attribute. */
.f--welled { padding-bottom: 0; overflow: hidden; display: flex; flex-direction: column; }
:root .f--welled > :last-child { margin-top: auto; }
:root .f--welled > .step__n {
	/* ⭐ Inside a welled card the numeral is a LABEL, not a step in a sequence:
	   small, in the accent, on no ground at all. */
	width: auto; height: auto; padding: 0; background: none;
	color: var(--wp--custom--color--brand-text);
	font-size: var(--wp--custom--step--label);
	font-weight: var(--wp--custom--weight--emphasis);
	letter-spacing: var(--wp--custom--tracking--label);
	display: block;
	margin-bottom: var(--wp--custom--space--sm);
}
/* ⭐ THE NUMERAL AS A SEQUENCE -- `step__n--seq`, page
   `wordpress-lead-generation` only (lg-steps.php). The same accent, at the
   ladder's `subsection` rung and its own tracking, so "1 · 2 · 3" reads
   before the headings. Critique round 1, 2026-09-25: Alpesh picked the
   `subsection` rung by looking (28px then), over 12px and 40px. Under the
   48/38 ladder that he approved the same day, the rung paints 24px. ⚠ (0,3,0), like the rule above, and it wins by
   coming after it. 4925's numerals do not carry the class. */
:root .f--welled > .step__n--seq {
	font-size: var(--wp--custom--step--subsection);
	font-weight: var(--wp--custom--weight--display);
	/* ⛔ THE ROLE'S TRACKING, NOT THE DISPLAY TIER BY HAND: check-law.js failed
	   the first version at 390 (22px tracking -0.0275). `--abk-track-sub` is the
	   lead tier, because `subsection` never reaches 28px under the 48/38 ladder. */
	letter-spacing: var(--abk-track-sub);
	line-height: var(--wp--custom--leading--display);
	font-variant-numeric: tabular-nums;
}
:root .f--welled > h3   { margin: 0; }
/* ⛔ `:not(.step__n)` BECAUSE THE NUMERAL IS A PARAGRAPH HERE AND A `<span>`
   in the prototype -- a block editor has no span-level block, so the label had
   to become a `<p>`. Without the exclusion it took this 8px and the whole
   card grew by it. ⭐ A structural difference forced by the editor, absorbed
   in the stylesheet rather than paid for in the paint. */
:root .f--welled > p:not(.step__n) { margin-top: var(--wp--custom--space--sm); }
/* ⭐ TEXT FIRST, WELL LAST: THE TEXT TAKES THE CARD'S OWN PADDING BELOW IT.
   The card's bottom padding is 0 so the well fuses to its foot, and so the
   last paragraph ended exactly on the well's top edge (165 = 165px at 1440).
   Critique round 2, 2026-09-25, picked by looking. ⚠ Only lg-steps.php
   builds this order; every media-first card is untouched. */
:root .f--welled:not(.f--media-first) > p:has(+ .well:last-child) { margin-bottom: var(--wp--custom--space--lg); }
/* ⚠ 16px UNDER THE CHIP ROW TOO. In the prototype that gap is the browser's
   own default bottom margin on a `<ul>`, which WordPress zeroes -- so it has
   to be stated here or the card closes up 16px tighter than the one Alpesh
   approved. */
:root .f--welled > .chips { margin-top: var(--wp--custom--space--md); margin-bottom: var(--wp--custom--space--md); }

/* ⭐ THE WELL IS THE OLIVE TINT AND THE DRAWING LIFTS OFF IT WITH A SHADOW.
   ⛔ THE ROUTE MATTERS MORE THAN THE ANSWER. The well began as the brand at
      8%, where the white product card separated at only 1.16 : 1. Five accent
      candidates were measured off the paint and terracotta won at 2.60 -- and
      it was STILL wrong, because a card-sized panel of terracotta is an
      ACCENT GROUND and the page rations those to two. ⭐ A SHADOW SEPARATES
      WITHOUT SPENDING ONE. Alpesh, 2026-09-16: "the whole background can be
      the same olive tint… for the product drawings we can add a bit of a drop
      shadow so that it lifts on the page."
   ⛔ AND THE HEIGHT IS FIXED AT 300px BY DESIGN. The wells started at
      different heights because their content differs, so the trio read as
      three unequal cards. A well is a CROP: the content fits the crop, never
      the reverse. */
.well {
	margin: var(--wp--custom--space--lg) calc(var(--wp--custom--space--lg) * -1) 0;
	padding: var(--wp--custom--space--lg);
	border-radius: 0;
	/* ⛔ --paper-raised, NOT --tint, AND ALPESH FOUND THE FAULT HIMSELF ON
	   2026-09-22: "the section background and the product drawings panel here
	   are the same tint." He was exactly right. The section is --tint #F9F9F6,
	   the card is --paper, and this well was --tint AGAIN -- so the OUTERMOST
	   and the INNERMOST surfaces were one colour and the one between them was
	   not. The well read as a hole punched back through to the page.
	   ⭐ He picked the fix off the canvas: "let's go with option 1:
	      paper-raised." #FDFDFA. Heading on it measures 19.22:1.
	   ⚠ THE THREE DRAWINGS BAKE THIS COLOUR into their mp4s, so changing it
	     here without re-exporting them would show as a rectangle. */
	background: var(--wp--custom--color--paper-raised);
	--fig:    var(--wp--custom--color--paper-raised);
	--fig-in: var(--abk-floor);
	display: grid;
	align-content: start;
	/* ⛔ THE FIXED 300px HEIGHT IS GONE, and the videos are why it can be.
	   It existed because the three wells held DIFFERENT DOM drawings and so
	   started at different heights, and the trio read as three unequal cards.
	   Three videos of one frame are equal by construction. ⚠ AND THE FIXED
	   HEIGHT WAS ITSELF A DEFECT: with overflow hidden, the drawings overflowed
	   it by 64px at 768 and by 137px of 321 at 390, so a third of card 2 was
	   sliced away on a phone. */
	min-height: 0;
	overflow: hidden;
	/* The inset hairline is what FUSES the well to the card's own edge. */
	box-shadow: inset 0 1px 0 var(--wp--custom--color--hairline), var(--wp--custom--shadow--edge);
}


.f--media-first { padding-top: 0; padding-bottom: var(--wp--custom--space--lg); }
:root .f--media-first .well {
	order: -1;
	/* ⭐ It bleeds TOP, left and right -- the mirror of the welled card's
	   bottom bleed. The card's own `overflow: hidden` clips it to the radius. */
	margin: 0 calc(var(--wp--custom--space--lg) * -1) var(--wp--custom--space--lg);
	box-shadow: inset 0 -1px 0 var(--wp--custom--color--hairline);
}
/* ⛔ NO FADE ON A MEDIA-FIRST WELL ANY MORE (2026-09-27, the 4925 drawing phase).
   The well used to cut its drawing in the middle, and a faded cut read better
   than a hard edge. The drawings are now whole, with the Breathing-Room Rule's
   32px under them, so the fade hid their last row (help-tools' calendar card).
   Only lp-help.php carries `f--media-first`. */

/* -- 11c · THE CHAT WIDGET, DRAWN. ⛔ The page runs ONE widget vocabulary:
   two treatments of one object on one page reads as assembled. */
.wgt {
	width: 100%;
	max-width: 380px;
	background: var(--abk-floor);
	border-radius: var(--wp--custom--rounded--md);
	overflow: hidden;
	box-shadow: var(--wp--custom--shadow--edge);
	font-size: 14px;
	line-height: var(--wp--custom--leading--body);
	letter-spacing: var(--wp--custom--tracking--text);
}
/* ⛔ The reference's header is a green GRADIENT; our law refuses gradients,
   and Alpesh chose white over a flat green, so the widget reads as OURS. */
.wgt__bar { display: flex; align-items: center; gap: 8px; padding: 12px var(--wp--custom--space--md); background: var(--abk-floor); color: var(--wp--custom--color--heading); box-shadow: inset 0 -1px 0 var(--wp--custom--color--hairline); font-size: var(--wp--custom--step--body); font-weight: var(--wp--custom--weight--emphasis); letter-spacing: var(--wp--custom--tracking--text); }
.wgt__log { padding: var(--wp--custom--space--md); display: grid; gap: 10px; }
.wgt__msg { padding: 9px 12px; border-radius: var(--wp--custom--rounded--md); max-width: 84%; margin: 0; }
.wgt__msg--bot  { background: var(--wp--custom--drawing--color--bubble); color: #1A1A1A; justify-self: start; border-bottom-left-radius: var(--wp--custom--rounded--sm); }
.wgt__msg--user { background: var(--wp--custom--color--brand); color: var(--wp--custom--color--paper); justify-self: end; border-bottom-right-radius: var(--wp--custom--rounded--sm); }
/* ⭐ THE PROGRESS TRACK, and it replaced a green "live" dot. The dot claimed an
   ONLINE STATE on a scripted replay -- it said "someone is here", which is not
   true of a walk-through. This says what is actually true: it is a recording,
   and it is this far in. ⚠ It sits on the bar's bottom edge so it reads as the
   header's own underline until it moves. */
.wgt__bar { position: relative; }
.wgt__prog {
	position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
	overflow: hidden;
}
.wgt__prog i {
	display: block; height: 100%; width: 100%;
	background: var(--wp--custom--color--brand);
	transform: scaleX( 0 ); transform-origin: 0 50%;
}
/* ⭐ GREY, NOT GREEN (U33 Q3, Alpesh: "A"). The demo widget is a SCREENSHOT,
   so its visitor bubbles keep the product's default brand green -- and a green
   bubble means nothing else in the widget is green but the send button. */
.wgt--play .wgt__prog i { background: var(--wp--custom--drawing--color--muted); }
/* ⛔ THE FILL IS AN ANIMATION AND THE PLAYER SETS ONLY ITS DURATION. Driving a
   width from a timer would be one setTimeout per frame for 13 seconds; one
   linear animation is the compositor's job, and its duration is the scene's
   own length so the bar cannot disagree with what is playing. */
.wgt__prog.is-running i { animation: abk-prog var( --abk-scene, 10s ) linear both; }
@keyframes abk-prog { from { transform: scaleX( 0 ); } to { transform: scaleX( 1 ); } }

/* ⭐ EVERY LINE RISES INTO PLACE, AND THE CURVE IS THE LIVE PAGE'S OWN. Alpesh:
   "they just appear directly there, and it is sort of hard to read... can we
   give it an animation where it pops up from the bottom to the top, so someone
   who is watching can follow the user question coming instead of it popping up
   directly there out of nowhere." The live page's `abkin` is
   `.32s cubic-bezier(.34,1.3,.64,1)` from `opacity:0, translateY(6px)`; the
   control point past 1 is the small overshoot that makes it read as arriving
   rather than fading. Copied rather than re-invented. */
@keyframes abk-msg-in {
	from { opacity: 0; transform: translateY( 6px ); }
	to   { opacity: 1; transform: none; }
}
.wgt--play .wgt__log > *:not( [ hidden ] ) {
	animation: abk-msg-in .32s cubic-bezier( .34, 1.3, .64, 1 ) both;
}

/* ⛔ AND `hidden` HAS TO BE RE-ASSERTED, WHICH COST A ROUND. The player hides
   a row with `el.hidden = true`, and the browser's own `[hidden]{display:none}`
   lives in the USER-AGENT sheet -- so ANY author rule that sets `display` beats
   it. Every row type below sets one: the dots are `inline-flex`, the agent
   bubble is `flex`, the lead card is `grid`. The result painted five sets of
   typing dots stacked in one window while the JavaScript believed it had hidden
   them all. ⭐ (0,2,0) here outranks the (0,1,0) row rules and costs one line. */
.wgt__log > [ hidden ] { display: none; }

/* -- 11c-i · THE PLAYED CONVERSATION. Five row types the static widget never
   needed: the scene avatar in the bar, the typing dots, a human colleague's
   message, the "Sarah joined" system row and the captured-lead card. They are
   scoped to `.wgt--play` so the page's other widgets keep the plain vocabulary
   -- the page runs ONE widget, and this is one widget with more to say. */
.wgt__av {
	flex: none; width: 26px; height: 26px; border-radius: var(--wp--custom--rounded--full);
	background: var(--wp--custom--color--brand); color: var(--wp--custom--color--paper);
	display: grid; place-items: center;
	font-size: 12px; font-weight: var(--wp--custom--weight--emphasis);
}
.wgt__who { flex: 1 1 auto; }

/* ⛔ THE DOTS ARE THE ONLY THING REDUCED MOTION REMOVES OUTRIGHT, because they
   are pure motion and carry no words. Everything else is revealed. */
.wgt__typ {
	justify-self: start; display: inline-flex; gap: 4px; align-items: center;
	padding: 11px 13px; border-radius: var(--wp--custom--rounded--md);
	border-bottom-left-radius: var(--wp--custom--rounded--sm);
	background: var(--wp--custom--drawing--color--bubble);
}
.wgt__typ i {
	width: 4px; height: 4px; border-radius: var(--wp--custom--rounded--full);
	background: var(--wp--custom--drawing--color--muted);
	animation: abk-dot 1.3s ease-in-out infinite;
}
.wgt__typ i:nth-child( 2 ) { animation-delay: .15s; }
.wgt__typ i:nth-child( 3 ) { animation-delay: .3s; }
@keyframes abk-dot {
	0%, 60%, 100% { opacity: .35; transform: translateY( 0 ); }
	30%           { opacity: 1;   transform: translateY( -2px ); }
}

/* the colleague. ⛔ Their bubble is NOT the bot's: a human taking the thread
   over is the section's whole argument, so it has to be visibly a third voice
   rather than the assistant in a different mood. */
.wgt__msg--agent {
	justify-self: start; background: var(--abk-floor);
	color: #1A1A1A;
	box-shadow: inset 0 0 0 1px var(--wp--custom--drawing--color--line);   /* the product's `outline` bubble */
	border-bottom-left-radius: var(--wp--custom--rounded--sm);
	/* ⚠ flex-START, not flex-end. Bottom-aligned, the avatar sank to the last
	   line of a two-line message and the face appeared to belong to the wrong
	   sentence. A speaker's mark belongs beside the line they START. */
	display: flex; align-items: flex-start; gap: 8px;
}
.wgt__agentav {
	flex: none; width: 20px; height: 20px; border-radius: var(--wp--custom--rounded--full);
	background: var(--wp--custom--drawing--color--chip); color: var(--wp--custom--color--paper);
	display: grid; place-items: center; font-size: 10px;
	font-weight: var(--wp--custom--weight--emphasis);
	/* ⚠ centred on the FIRST LINE BOX, not on its top edge. The line box is
	   14px x 1.55 = 21.7px tall and the disc is 20px, so it drops the 0.85px
	   difference to sit on the line's optical centre rather than its ascender. */
	margin-top: .85px;
}
/* the system row -- centred, quiet, and not a message. ⭐ The product's own
   takeover pill (TakeoverEventChip.tsx): white, one line, grey-700 text. */
.wgt__join {
	justify-self: center; margin: 2px 0; padding: 4px 11px;
	border-radius: var(--wp--custom--rounded--full);
	background: var(--abk-floor);
	box-shadow: inset 0 0 0 1px var(--wp--custom--drawing--color--line);
	color: var(--wp--custom--drawing--color--chip);
	font-size: 11.5px;
}
/* the captured lead. ⛔ It carries its own "demo state" label, because a bare
   card claiming an outcome nothing was sent for is a claim about an outcome. */
.wgt__lead {
	justify-self: start; width: 90%; display: grid; gap: 4px;
	padding: 10px 12px; border-radius: var(--wp--custom--rounded--md);
	/* ⭐ U33 Q3: no product screen shows this card, so it is drawn in the
	   product's greys -- white, one line, ink values, grey labels. */
	background: var(--abk-floor);
	box-shadow: inset 0 0 0 1px var(--wp--custom--drawing--color--line);
	color: #1A1A1A;
}
.wgt__lead p { margin: 0; font-size: 11.5px; }
.wgt__lead-t {
	font-weight: var(--wp--custom--weight--emphasis);
	display: flex; align-items: center; justify-content: space-between;
	gap: var(--wp--custom--space--sm);
}
.wgt__lead-r { color: #1A1A1A; }
.wgt__lead-r b {
	display: inline-block; min-width: 58px;
	color: var(--wp--custom--drawing--color--muted);
	font-weight: var(--wp--custom--weight--body);
}

.wgt__demotag { font-size: 11px; font-weight: var(--wp--custom--weight--body); color: var(--wp--custom--drawing--color--muted); }
.wgt__foot { display: flex; align-items: center; gap: 8px; margin: 0 var(--wp--custom--space--md) var(--wp--custom--space--md); padding: 7px 7px 7px 14px; border-radius: var(--wp--custom--rounded--full); box-shadow: inset 0 0 0 1px var(--wp--custom--drawing--color--line); color: var(--wp--custom--drawing--color--muted); }
.wgt__foot > span:first-child { flex: 1 1 auto; }
.wgt__send { flex: none; width: 32px; height: 32px; border-radius: var(--wp--custom--rounded--full); background: var(--wp--custom--color--brand); display: grid; place-items: center; }
.wgt__send .ico { width: 15px; height: 15px; color: var(--wp--custom--color--paper); stroke-width: 1.3333; }
/* ⛔ SCOPE A STROKE RECIPROCAL BY THE viewBox, NEVER BY THE PARENT. `.wgt__send`
   holds two different marks: demo-stage uses `<use href="#i-send">`, whose
   symbol is 20 units, and industry-sim inlines a 12-unit path. One reciprocal
   cannot serve both -- 1.3333 x (15/20) paints 1.000, and 1.3333 x (15/12)
   paints 1.667. Measured on the live page: one icon at 1.667 against the law's
   single rendered width. 0.8 x (15/12) = 1.000. SVC-15. */
.wgt__send .ico[viewBox="0 0 12 12"] { stroke-width: 0.8; }

/* -- 11c-ii · THE DEMO WIDGET IS A SCREENSHOT (U33, Alpesh, 2026-10-03: Q1
   "A", the font Q2 "B" -- Manrope stays, KTD28's exception). Its parts copy the
   product (ChatWidget.tsx, widget-tokens.css): a white header with the
   product's #E5E7EB line, a white avatar with a line, the #1A1A1A ink, the
   composer's grey line. ⚠ Scoped to `.wgt--play` because the simulator shares
   these base rules and keeps its own paint until its own questions. */
.wgt--play { background: var(--abk-floor); color: #1A1A1A; }
.wgt--play .wgt__bar { background: var(--abk-floor); color: #1A1A1A; box-shadow: inset 0 -1px 0 var(--wp--custom--drawing--color--line); }
.wgt--play .wgt__av { background: var(--abk-floor); color: #1A1A1A; box-shadow: inset 0 0 0 1px var(--wp--custom--drawing--color--line); }

/* -- 11d · THE PULL QUOTE. ⛔ NOT A CARD. It was a 680px white panel with a
   1px ring, so it read as a stray FOURTH card under a row of three.
   ⚠ AND IT IS NOT A DUPLICATE OF THE TESTIMONIAL BELOW. The live page carries
   this person TWICE on purpose: here with no box at all, and again in the
   testimonial grid with a rating. */

:root .section .pull__by {
	display: flex; align-items: center; justify-content: center;
	gap: var(--wp--custom--space--md);
	margin-top: var(--wp--custom--space--xl);
	text-align: initial;
}
/* ⛔ A ROUNDED SQUARE, NOT A CIRCLE. A round avatar paints the pill radius,
   which only a badge and a pill may take -- and the step numeral gave up its
   pill for that exact reason, so an exception here would undo that. */
.quote__face { width: 40px; height: 40px; flex: none; border-radius: var(--wp--custom--rounded--sm); object-fit: cover; box-shadow: var(--wp--custom--shadow--edge); }
.quote__who { display: grid; }
.quote__who b { font-weight: var(--wp--custom--weight--emphasis); color: var(--wp--custom--color--heading); }
.quote__who > span { font-size: var(--wp--custom--step--body-small); font-weight: var(--wp--custom--weight--body); letter-spacing: var(--wp--custom--tracking--text); color: var(--wp--custom--color--secondary); }

/* ---------------------------------------------------------------------------
   12 · THE TESTIMONIALS — U6, section 6. Four real customers, each quote
   fused to the person who gave it.
   ⛔ A NAMED QUOTE AND ITS OUTCOME ARE ONE PHYSICAL UNIT. The same two
      elements 200px apart read as a quote and an unsupported number.
   --------------------------------------------------------------------------- */
.quote { display: grid; }
:root :is(.section, .tw\:panel) .quote__body { padding: var(--wp--custom--space--lg); margin: 0; }
.quote__outcome {
	/* FUSED to the bottom edge -- no gap, and NO GROUND OF ITS OWN. Two
	   earlier answers were both wrong and the second more interestingly: a 5%
	   ink tint of the card's own ground is correct in theory, because it
	   darkens what is behind it rather than naming a colour -- but on a card
	   that is already a gradient it reads as a SECOND gradient, a band with
	   its own edge across the bottom third. ⭐ The photograph and the name are
	   what separate the strip; the card's own wash runs through it. */
	margin: 0;
	padding: var(--wp--custom--space--md) var(--wp--custom--space--lg);
	border-radius: 0 0 var(--wp--custom--rounded--md) var(--wp--custom--rounded--md);
	font-weight: var(--wp--custom--weight--emphasis);
	display: flex;
	align-items: center;
	gap: var(--wp--custom--space--md);
}

/* ⭐ THE RATING SHIPS, AND IT NARROWS A REFUSAL RATHER THAN BREAKING ONE.
   The Don't list read "fabricated proof -- an outcome, a figure, a logo, a
   RATING or a testimonial. Ever." It now reads "a rating THE LIVE PAGE DOES
   NOT ALREADY MAKE". The live page makes this one, on all four cards.
   ⛔ THE STAR IS A FILL, NOT A STROKE, so the one-rendered-stroke-width rule
      does not reach it -- and the warm accent is legal exactly this way: it is
      refused as TEXT or as a mark on a light ground, and permitted as a FILL. */
.quote__rating { display: flex; gap: 3px; padding: var(--wp--custom--space--lg) var(--wp--custom--space--lg) 0; }
/* ⛔ THE STAR IS OUTLINED, AND THE OUTLINE IS WHAT MAKES IT LEGAL. 2026-09-23.
   ⚠ `fill: --warm` WITH `stroke: none` BROKE THE LIT-WINDOW RULE, which refuses
     a warm stop as a MARK on a light ground by name. Measured off the paint:
     #FDC830 reads 1.53 on the lime card and 1.38 on the terracotta one --
     under even the 3:1 floor a graphical object needs.
   ⛔ AND check-law.js COULD NOT SEE IT. The colour is an SVG `fill`, not text,
      so a text-contrast assertion walks straight past 20 star marks. Same
      blind spot as gotcha 68, one layer over.
   ⭐ ALPESH KEPT THE YELLOW: "the stars color needs to be yellow". A darkened
      fill was painted at oklch(0.600 0.164 87.6) -- it clears 3.87/3.50 and
      reads as ochre, and he chose the outline instead. The bright fill stays;
      the dark boundary does the contrast work.
   ⭐ THE COLOUR IS MIXED FROM `--warm`, NOT DECLARED. A token for one use would
      grow the palette for nothing, and a literal hex here would drift from the
      stop it is derived from. 65% lands at #9E7F29: lime 3.73, terracotta
      3.38, paper 3.80 -- all measured. ⚠ 70% was tried first and fails the
      terracotta card at 2.94, which is the binding ground, not paper.
   ⭐ IT ALSO MOVES THE STAR TOWARD THE ICON SYSTEM. Every other mark on this
      page is stroked and unfilled; this was the only solid one. */
.quote__rating .star {
	width: 16px; height: 16px; flex: none;
	fill: var(--wp--custom--color--warm);
	stroke: color-mix( in oklab, var(--wp--custom--color--warm) 65%, var(--wp--custom--color--heading) );
	stroke-width: 1.2;
	stroke-linejoin: round;
	paint-order: stroke fill;
}
:root :is(.section, .tw\:panel) .quote__rating + .quote__body { padding-top: var(--wp--custom--space--sm); }

/* -- 12-i · THE REVIEW CARD -- page `wordpress-lead-generation`, lg-review.php
   (plan R15, U6). ONE customer's words on ONE centred card: the stars, a large
   quotation mark, the quote, the photo, the name, the company. It is the
   source's card, re-tokened (Alpesh's call). 4925 has no single-card quote.
   ⭐ IT REUSES FOUR 4925 PARTS: the star (`.quote__rating .star`), the photo
      (`.quote__face`), the name and company (`.quote__who`) and the centred
      attribution row (`.pull__by`). Only the card, the mark and the quote's
      type are new.
   ⭐ THE CARD LIFTS: `depth`, the law's one lift, on the paper floor. Alpesh,
      2026-09-26, beside the source: "the card is floating and standing out.
      Ours is doing nothing." He picked A (lifted card on paper) over B (the
      same card on its own tint panel) and C (no card), by looking.
      ⚠ `depth` is lighter than the source's `0 22px 50px -34px` at 28%; a
      heavier lift is a change to the Shadow-Budget Rule, not to this card.
   ⚠ 1000px, NOT `.pull`'s 900: at the `section` rung the quote broke into
      three lines at 900. At 1000 it is two, as in the source. */
.qcard {
	position: relative;
	max-width: 1000px;
	margin: 0 auto;
	padding: var(--wp--custom--space--3-xl) var(--wp--custom--space--3-xl) var(--wp--custom--space--2-xl);
	background: var(--abk-floor);
	border-radius: var(--wp--custom--rounded--md);
	box-shadow: var(--wp--custom--shadow--depth);
	text-align: center;
}
/* The mark sits BEHIND the copy: every other child is positioned too, so it
   paints later, and so on top. */
.qcard > * { position: relative; }
/* ⛔ DECORATION, `aria-hidden`. It takes `tint-brand`, the pale green wash, as
   the source paints its green at 10%: a ground colour, not a green spend.
   ⛔ A FIXED SIZE, NOT A RUNG. A decoration is not a role. Manrope's quotation
      mark paints small for its size, so 15rem makes a mark about 56px tall.
      It was 4.5rem in the hairline (critique round 2); Alpesh's pick A,
      2026-09-26, made it large and green, as the source's. */
.qcard > .qcard__mark {
	position: absolute;
	top: -1.5rem;
	left: var(--wp--custom--space--xl);
	font-size: 15rem;
	font-weight: var(--wp--custom--weight--emphasis);
	letter-spacing: var(--wp--custom--tracking--display);
	line-height: 1;
	color: var(--wp--custom--color--tint-brand);
	pointer-events: none;
	user-select: none;
}
.qcard__rating { justify-content: center; padding: 0; }
/* 20px stars, from 4925's 16: at 16 they read as a speck above a 38px quote. */
.qcard .quote__rating .star { width: 20px; height: 20px; }
/* ⭐ THE QUOTE IS AT THE `section` RUNG (the h2 size, 38px at 1440) AND THE
   BODY WEIGHT: the source's 38px. It was `display-small`, 30px, which Alpesh
   found "too small, and it is not emphasizing". On phones it steps down to
   `display-small` (24px), the size painted and picked at 390.
   ⚠ `:root .section` for the reason `.pull__body` carries it: the rhythm
   resets would take the margin. */
:root .section .qcard__body {
	margin: var(--wp--custom--space--lg) auto 0;
	padding: 0;
	font-size: var(--wp--custom--step--section);
	font-weight: var(--wp--custom--weight--body);
	letter-spacing: var(--wp--custom--tracking--display);
	line-height: var(--wp--custom--leading--display);
	color: var(--wp--custom--color--heading);
	text-wrap: balance;
}
/* ⭐ THE HIGHLIGHT IS GREEN TEXT, AND THAT IS ALPESH'S CALL FOR THIS CARD,
   2026-09-25, picked by looking at the ink and the green painted side by side.
   ⚠ It is an exception to the Balanced-Accent Rule for this card, not a
   change to the rule. brand-text on paper: 6.16:1, never `brand`, which is a
   FILL. */
.qcard .hl { color: var(--wp--custom--color--brand-text); }
/* The photo is 52px, as the source's; 4925's quote face is 40px at `sm`.
   ⛔ `rounded.md`, THE CARD'S RADIUS, NOT ROUND. The source's photo is a
      circle, and the Three-Radii Rule gives the pill radius to a badge and a
      pill only (check-law.js fails it). Alpesh picked 12px over round and
      16px, 2026-09-26, by looking. */
.qcard .pull__by { margin-top: var(--wp--custom--space--xl); gap: 14px; }
.qcard .quote__face { width: 52px; height: 52px; border-radius: var(--wp--custom--rounded--md); }
@media (max-width: 620px) {
	.qcard { padding: var(--wp--custom--space--xl) var(--wp--custom--space--lg); }
	.qcard > .qcard__mark { top: -1.25rem; left: var(--wp--custom--space--sm); font-size: 8rem; }
	:root .section .qcard__body { font-size: var(--wp--custom--step--display-small); letter-spacing: var(--abk-track-dsm); }
	.qcard .quote__face { width: 46px; height: 46px; }
}

/* -- 12a · THE DRIFT. ⭐ DEPTH FROM POSITION, NOT FROM SHADOW: four cards at
   different vertical offsets. Set every box-shadow to none and the depth
   survives, which is the test that it is real.
   ⛔ THE HORIZONTAL OVERLAP WAS REMOVED. Each card carried `margin-left:
      -120px`, which covered 104px of the card before it and ate the opening
      words of three of the four quotes -- and it made card 1 narrower than
      the others, because a negative margin makes a grid item WIDER than its
      track rather than moving it. ⭐ Occlusion reads as depth only when the
      covered thing is a SURFACE. Never over running text, a quote, a heading
      or a name. The VERTICAL drift stays: it occludes nothing. */
.drift { display: grid; gap: var(--wp--custom--space--md); }
:root :is(.section, .tw\:panel) .drift { margin-top: var(--wp--custom--space--2-xl); }
:root .drift > * { margin: 0; position: relative; }

@media (min-width: 900px) {
	.drift { grid-template-columns: repeat(4, 1fr); align-items: start; }
	:root .drift > *:nth-child(1) { margin: 0;                              z-index: 4; }
	:root .drift > *:nth-child(2) { margin: var(--wp--custom--space--2-xl) 0 0 0; z-index: 3; }
	:root .drift > *:nth-child(3) { margin: var(--wp--custom--space--lg)  0 0 0;  z-index: 2; }
	:root .drift > *:nth-child(4) { margin: var(--wp--custom--space--3-xl) 0 0 0; z-index: 1; }
}
/* ⭐ THREE CARDS -- page `wordpress-lead-generation`, lg-proof.php (plan KTD10).
   Three columns, so no track stands empty; cards 1-3 keep the offsets above.
   ⚠ AFTER the four-card rule and inside the same query (gotcha 74). The
     620px scrolled row below needs no change for three. */
@media (min-width: 900px) {
	.drift--3 { grid-template-columns: repeat(3, 1fr); }
}

/* ⛔ AT 390 THE FOUR CARDS WERE A COLUMN ~1,600px DEEP, for a device whose
   whole point at 1440 is the stagger. A scrolled row keeps all four, keeps
   the cut edge, and costs one screen instead of four.
   ⚠ THE FADE IS TRAILING-ONLY. A leading fade ate the first character of
   every row. */
@media (max-width: 620px) {
	.drift {
		display: flex;
		gap: var(--wp--custom--space--md);
		margin-right: calc(var(--wp--custom--width--gutter) * -1 - var(--wp--custom--space--panel-inset));
		overflow-x: auto;
		scrollbar-width: none;
		mask-image: linear-gradient(to right, #000 0%, #000 90%, transparent 100%);
	}
	.drift::-webkit-scrollbar { display: none; }
	:root .drift > * { flex: 0 0 82%; margin: 0; }
}

/* ⭐ EVERY PROOF CARD IS PAPER WITH THE `edge` RING (premium pass U25,
   2026-09-30). Alpesh picked it for 4925's four cards on the painted map
   (SECTION-MAP.md §8; ANALYSIS A2; P-G2): the lime and peach washes were two
   small colour marks. The lead-generation page's three cards were already
   paper (the rule below), so this changes 4925 only.
   ⚠ `background-color` is the floor and `background-image` is cleared: the
     wash was an image, and the old markup on the servers still carries it
     until the content sync. */
:root .section--proof .quote {
	background-image: none;
	background-color: var(--abk-floor);
	box-shadow: var(--wp--custom--shadow--edge);
	padding: 0;
}

/* ⭐ THE LEAD-GENERATION PAGE'S THREE CARDS (Alpesh, 2026-09-26) were the
   first proof cards on paper; since U25 every proof card is (the rule above).
   Their stars lose the dark outline, which the wash needed. ⚠ `drift--3`
   only: 4925's stars keep their outline, as painted in the pick. */
.drift--3 .quote__rating .star { stroke: none; }
/* ⭐ NO STAGGER, AND THE STARS CLOSER (Alpesh, 2026-09-26): the three cards
   align at the top, and the stars sit 2px apart. ⚠ THE GLYPH IS 12.4px OF ITS
   16px BOX, so the box's own 3.6px of air is in every gap: the overlap takes
   1.6px of it back. `drift--3` only. */
@media (min-width: 900px) {
	.drift--3 { align-items: stretch; }
	:root .drift--3 > *:nth-child(n) { margin: 0; }
	/* the extra height goes to the QUOTE's row, so the stars and the quote
	   stay at the top and the pill and the name sit at the foot */
	:root .section--proof .drift--3 > .quote { grid-template-rows: auto 1fr auto auto; }
}
.drift--3 .quote__rating { gap: 0; }
.drift--3 .quote__rating .star + .star { margin-left: -1.6px; }

/* -- 12b · THE OUTCOME TAG -- page `wordpress-lead-generation`, lg-proof.php
   (plan R18). The source's green tag, as the law's PILL: a filled
   label, `rounded.full`, `label` type. The element also carries `.pill`,
   which is how check-law.js knows the pill radius is legal here.
   ⛔ SCOPED TO `.quote__tag`, NOT `.pill`: a global `.pill` would restyle any
      page that happens to use that class.
   ⭐ GREEN TEXT IS `brand-text` on a `tint-brand` fill, Alpesh's call.
   ⚠ `justify-self: start` because `.quote` is a grid: without it the pill
     stretches to the card's full width. */
:root .section .quote__tag {
	justify-self: start;
	margin: 0 var(--wp--custom--space--lg);
	padding: 6px 12px;
	border-radius: var(--wp--custom--rounded--full);
	/* ⭐ FILLED with tint-brand, no ring (Alpesh, 2026-09-26: "the pills there
	   need to be colored"). brand-text on tint-brand: 5.73, computed. */
	background: var(--wp--custom--color--tint-brand);
	font-size: var(--wp--custom--step--label);
	font-weight: var(--wp--custom--weight--emphasis);
	letter-spacing: var(--wp--custom--tracking--label);
	line-height: var(--wp--custom--leading--body);
	color: var(--wp--custom--color--brand-text);
}

/* ---------------------------------------------------------------------------
   13 · THE FOUR TERM/DEFINITION SECTIONS — U6, section 7.
   ⭐ ONE COMPONENT, FOUR USES. Lead capture, customisation, WordPress and
   multi-channel are the same shape: a heading, a lead, a definition list, and
   a drawing that alternates sides down the page. ⛔ THAT IS THE REUSE TEST --
   if each needed its own CSS the vocabulary has failed.
   --------------------------------------------------------------------------- */

/* ⛔ THE 2px HAIRLINE ON EVERY ROW CAME OFF — thirteen of them, and a
   side-stripe border is refused however faint it is. A rule that turns on
   width and colour is a rule the next page argues past.
   ⭐ PAID FOR WITH ALIGNMENT, AND IT IS MEASURABLE: with the stripe the row
      text sat at x=98 while its own h2 sat at x=80. The list now starts on
      the same column as the heading it belongs to. ⚠ The bold lead-in was
      already marking each row; the stripe was marking it a second time. */
.dl {
	list-style: none;
	padding: 0;
	display: grid;
	gap: var(--wp--custom--space--md);
	max-width: var(--wp--custom--width--prose);
}
.section .dl { margin: var(--wp--custom--space--xl) 0 0; }
/* A call to action under a definition list, as on the lead-generation page. */
.section .dl + .row { margin-top: var(--wp--custom--space--xl); }
:root .dl > li { padding-left: 0; margin: 0; }

/* ⭐ THE TICKED LIST -- `dl--ticks`, the problem section of
   `wordpress-lead-generation`. The source's `.dashlist` marks each line; ours
   had lost the mark and read as four loose lines. Alpesh, 2026-09-25: ticks,
   "because the bullet points are what we are presenting as a solution".
   ⛔ A TILE WITH THE 8px RADIUS, NOT THE SOURCE'S CIRCLE: only a badge and a
      pill take the full radius (the Three-Radii Rule); a chip takes 8px.
   ⭐ The tick is a MASK over the brand-text token, so its colour is a token
      and never a literal in a data URI. */
.dl--ticks { gap: 0; }
:root .dl--ticks > li { position: relative; display: flex; align-items: center;
	gap: var(--wp--custom--space--sm); padding-block: 9px; }
.dl--ticks > li::before { content: ""; flex: none; width: 28px; height: 28px;
	border-radius: var(--wp--custom--rounded--sm);
	background: var(--wp--custom--color--paper);
	box-shadow: var(--wp--custom--shadow--edge); }
.dl--ticks > li::after { content: ""; position: absolute; left: 7px; top: 50%;
	width: 14px; height: 14px; transform: translateY( -50% );
	background: var(--wp--custom--color--brand-text);
	-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.4l3 3 5.8-7'/%3E%3C/svg%3E") center / contain no-repeat;
	        mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.4l3 3 5.8-7'/%3E%3C/svg%3E") center / contain no-repeat; }
/* ⭐ THE LEAD-IN IS BOUND TO ITS LIST. The paragraph above ends with a colon,
   so it belongs to the list, not to the heading: 16px, where every other
   `.lead + *` takes 48px (critique round 1: the largest gap in the column
   sat exactly between the colon and what it introduced). (0,4,0). */
:root .split__copy > .lead + .dl--ticks { margin-top: var(--wp--custom--space--md); }
.dl b    { font-weight: var(--wp--custom--weight--emphasis); letter-spacing: var(--wp--custom--tracking--text); }
.dl span { color: var(--wp--custom--color--secondary); letter-spacing: var(--wp--custom--tracking--text); }

/* ⭐ ONE VARIABLE DRIVES THE WIDGET'S BRAND, exactly as the product does.
   ⛔ An earlier version set the drawing's HEADER to the customer's colour and
      left the user bubble OUR green -- so the drawing argued the opposite of
      the section it sits in. ⚠ A customer's colour is not ours, and that is
      the point of the section. */
.wgt[data-brand] .wgt__send { background: var(--cb); }
.wgt[data-brand="violet"] { --cb: #6D28D9; }
.wgt[data-brand="blue"]   { --cb: #1D4ED8; }

/* -- 13c2 · AN EXPORTED DRAWING.
   ⭐ THE ASSET CARRIES ITS OWN SHADOW inside its alpha channel, so this rule
   adds NO radius, NO shadow and NO ground -- the same refusal `.draw` makes.
   ⛔ AND IT NEVER REFLOWS. Alpesh, 2026-09-21: "we should keep the product
   drawing fixed -- not changing for mobile and desktop version". It scales as
   ONE piece; there is no second layout to get wrong. */
/* ⛔ AND THE FIGURE'S OWN PADDING IS THE OUTER WHITE BAND Alpesh saw. 32px on
   every side of a 546x382 asset that already carries its own shadow is more
   breathing room than a bordered card would need. 20px returns 24px of width
   and 24px of height to the drawing. */
.figure--field { padding: 20px; }

/* ⭐ THE STUDIO GROUND (U30, Alpesh 2026-10-02, pick C1 of two rounds): the
   olive studio behind a TRANSPARENT drawing -- lit from above behind the
   product, fine light rings, a soft floor. ops/assets/gen-ground.js writes the
   file from theme.json (`--check`). The frame clips it, so it has no edge.
   ⛔ NEVER BEHIND A FLAT-GROUND FILM: a film bakes its ground into the mp4, and
      would show as a flat rectangle on the studio. A film takes the studio
      only when it is exported with the studio baked in (meta.json
      `"ground": "studio"`, U32) -- and then the film IS the frame's ground,
      so it fills the frame edge to edge (the rule below). */
.figure--studio {
	background: var(--fig) url("../img/ground/ground-studio-olive.svg") center / cover no-repeat;
}
/* ⭐ A STUDIO FILM FILLS ITS FRAME (U32, brand-customization first). The export
   rasterises the same studio file at the film's own size, `cover`, so the
   film's ground and the frame's are one picture; a padding band would show
   the seam. The 2x film scales up to the frame's width (580px at 1440). The
   reduced-motion still is transparent and stands on the CSS studio, so it
   takes the same size. */
.figure--studio:has( > .dvid ) { padding: 0; }
.figure--studio:has( > .dvid ) > :is( .dvid, .dvid__still ) { max-width: none; }

.dimg {
	display: block;
	width: 100%;
	max-width: var(--wp--custom--width--drawing);   /* its painted size (the law's drawing width); a raster is never upscaled */
	height: auto;
	margin-inline: auto;

	/* ⭐ THE LIFT, and it is a FILTER rather than a box-shadow ON PURPOSE.
	   `drop-shadow` follows the image's ALPHA CHANNEL, so it casts a shadow in
	   the SHAPE of the drawing instead of around its rectangle -- and it paints
	   OUTSIDE the image box, so it needs no margin inside the frame and no
	   re-export when the value is tuned.

	   ⚠ THE VALUE IS MEASURED, AND ALPESH TIGHTENED IT TWICE.
	   Round 1, he rejected four candidates: "too much drop shadow and spread.
	   That doesn't look like what Arcade has." ⛔ The fault was the DECAY, not
	   the density -- Arcade falls to a nineteenth of its strength over 32px
	   while those fell to between a half and a quarter. A soft wide cloud where
	   Arcade has a tight edge.
	   Round 2, 2026-09-21: "I would like to go with the lighter one, T2, and
	   even maybe we can make it tighter... Spread is a bad thing." ⭐ SO THIS IS
	   CRISPER THAN T2 AND DEAD BY 8px:
	     below the card edge   1px     2px     4px     8px    12px
	     T2                   16.1%   13.7%    9.0%    3.5%    1.2%
	     THIS                 20.8%   15.7%    7.1%    0.8%    0.0%
	   Candidates and curves: docs/research/prototypes/lift/ */
	filter: drop-shadow( 0 2px 3px oklch( 0.153 0.006 107.1 / 0.19 ) )
		drop-shadow( 0 1px 1px oklch( 0.153 0.006 107.1 / 0.14 ) );
}

/* ⛔ NO SHADOW ON A CUT EDGE. Alpesh, 2026-09-21: "take the leads product. We
   have the chat widget cut at the top, right? It is like it expands there, so
   at the top edge of the chat widget, we don't need the drop shadow. It should
   be at the sides and at the bottom only."
   ⭐ HE IS RIGHT AND IT IS A REAL FLAW IN WHAT I SHIPPED. `drop-shadow` follows
   the WHOLE silhouette, so an edge that runs off the frame -- an edge that is
   meant to continue -- was getting a shadow that says "this is where it ends".
   ⚠ WHICH EDGE IS CUT WAS MEASURED, not assumed: the share of each outer row or
   column of the raster that is fully opaque.
     leads-capture         TOP 83.5%   -> cut at the top
     wordpress-autosync    BOTTOM 92.7% -> cut at the bottom
     brand-customization   every edge 0.0% -> nothing cut, so no class
   `clip-path: inset()` takes NEGATIVE values, which let the shadow out on the
   sides it belongs on and cut it flat on the one it does not. */
/* ── 13c-ii · AN ANIMATED DRAWING IS A VIDEO, NOT AN IMAGE.
   ⛔ WHY. Animated WebP only compresses well when consecutive frames are
   IDENTICAL. The moment the WordPress drawing gained the film's camera, every
   frame differed: 23 encoded frames became 72 and the file went 188 KB to
   1639 KB. Lossy bought 28%, 12fps lost smoothness -- the container was wrong.
   ⭐ OUR OWN LIVE FILMS SHIP THIS CONTENT AT 253 KB and they are H.264 with the
   ground baked in (`pix_fmt=yuv420p`, measured). Ours is now 301.6 KB at
   900x634 -- 1.65x the display size, the same ratio the live film uses.
   ⚠ SO AN ANIMATED DRAWING'S PANEL AND SHADOW ARE BAKED INTO THE ASSET, agreed
   with Alpesh for videos. It gets NO `filter` here: the lift is already inside
   it, and a filter on an opaque rectangle would draw a second shadow around the
   whole panel. A STILL drawing is unchanged -- transparent, on the CSS panel,
   lifted by the filter on `.dimg`. */
.dvid {
	display: block;
	width: 100%;
	max-width: var(--wp--custom--width--drawing);   /* its painted size (the law's drawing width); never upscaled */
	height: auto;
	margin-inline: auto;
}

/* ⛔ prefers-reduced-motion IS MANDATORY, and CSS cannot stop a video
   autoplaying -- so the still is a real sibling element and the two swap.
   The video carries `preload="none"` so the swap does not cost a download. */
.dvid__still { display: none; }
@media ( prefers-reduced-motion: reduce ) {
	.dvid { display: none; }
	.dvid__still { display: block; }
}

/* ⭐ A DRAWING THAT FILLS ITS WELL. The three lp-help videos are 420x380 and
   the well is the card's full width, so they stretch to it rather than sitting
   centred in a 546px box that is wider than the card.
   ⚠ THE WELL'S SIDE PADDING IS CANCELLED FOR THEM: a drawing whose own ground
     is baked must reach the well's edges, or the well's --paper-raised shows as
     a frame around an identical --paper-raised rectangle and the seam appears
     exactly where the video's corner rounding does not match. */
/* ⛔ A VIDEO WELL IS SIZED BY ITS VIDEO, AND TWO RULES HAD TO BE BEATEN.
   Measured after the swap, not reasoned about:
   1. `:root .f--welled > :last-child { margin-top: auto }` on a COLUMN FLEX
      card made the well a stretching flex item -- 777px tall around a 377px
      video, so 400px of bare paper-raised sat under each drawing.
   2. `@media (max-width: 620px) { .well { height: 208px } }` survived the
      fixed-height deletion and CLIPPED 119px off the video at 390. ⭐ That rule
      was deleted on 2026-10-19 (Round 4, item 20): removing it changed no well
      on either page that has wells.
   ⭐ Both are answered here rather than edited above, because the DOM wells
      elsewhere on the site still want them. */
:root .f--welled > .well:has( > .dvid--well ) {
	padding: var(--wp--custom--space--lg) 0 0;
	height: auto;
	flex: 0 0 auto;
	margin-top: auto;
}
/* ⛔ NO `display` HERE, AND THAT IS [GOTCHA 66] FOR THE THIRD TIME.
   The first version said `display: block`. `.dvid__still { display: none }` is
   also (0,1,0) and sits EARLIER in this file, so the still won its width and
   lost its hiding: BOTH the video and the still painted, 376 + 376 + 24 = the
   777px well I then went looking for a flex bug to explain.
   ⭐ A rule that only needs to set a SIZE must only set a size. `.dvid` and
      `.dvid__still` own `display`, and the reduced-motion swap depends on it. */
.dvid--well { width: 100%; max-width: none; height: auto; }
@media ( max-width: 620px ) {
	:root .well:has( > .dvid--well ) { height: auto; }
}

/* -- THE ONE-PANEL CARD, `f--panel` -- page `wordpress-lead-generation`,
   lg-steps.php. ⭐ ONE PANEL: the text and the drawing on ONE tint ground,
   apart by space alone -- no second surface and no line (Alpesh, 2026-09-26,
   from Arcade's feature cards). He picked tint over paper; to go back to
   paper, the background and the ring are the only two lines to change.
   ⭐ THE BREATHING-ROOM RULE sets the numbers: 40px of text padding and
      40px from the text to the drawing, 24px below 620px.
   ⭐ THE DRAWING SPANS THE CARD'S FULL WIDTH: its frame carries its own 32px
      to each side, so the card adds none.
   ⛔ NO FILTER ON THE DRAWING. Each carries its own lift, and a drop-shadow
      on a faded (half-transparent) edge paints a grey haze. */
:root .f--panel {
	background: var(--wp--custom--color--tint);
	box-shadow: none;
	padding: var(--abk-panel-pad) var(--abk-panel-pad) 0;
	--abk-panel-pad: 40px;
}
@media (max-width: 620px) { :root .f--panel { --abk-panel-pad: var(--wp--custom--space--lg); } }
/* ⚠ (0,6,1): it must outrank the welled card's own (0,5,1) margin rule. */
:root .f--panel.f--welled:not(.f--media-first) > p:has(+ .well:last-child) { margin-bottom: 0; }
:root .f--panel > .well {
	height: auto;
	margin: var(--abk-panel-pad) calc(var(--abk-panel-pad) * -1) 0;
	padding: 0;
	background: none;
	box-shadow: none;
}
.panel-draw { display: block; width: 100%; max-width: none; height: auto; }
/* ⭐ ON A TINT SECTION THE ONE PANEL IS PAPER, the opposite ground, or a tint
   panel on a tint section would vanish, and it takes the `edge` ring: "the
   cards look completely flat" (Alpesh, 2026-09-26; he picked the ring, B's
   card, over A's `depth`). lg-features.php. */
:root .section:not(.section--floor) .f--panel { background: var(--abk-floor); box-shadow: var(--wp--custom--shadow--edge); }

/* ⛔ A MEDIA-FIRST WELL TAKES NO `margin-top: auto`, AND THE SLACK MOVES TO THE
   CHIPS. Alpesh, 2026-09-23: "we should keep the divisions of these three
   cards at the same length... the panels are not the same length."
   ⚠ HE IS RIGHT AND THE WELLS WERE ALREADY IDENTICAL -- all three measured
     400.8px tall. What differed was the well's MARGIN-TOP: 25.59px on cards 01
     and 03, 0px on card 02. So the drawings were the same size and the seam
     under them was not, which is the part a reader sees.
   ⭐ THE CAUSE IS AN auto MARGIN DOING ITS JOB IN THE WRONG DIRECTION. Two
      rules put `margin-top: auto` on the well -- `.f--welled > :last-child`
      and the video-well rule above -- and both were written for a well that
      sits at the card's FOOT, where auto correctly pushes it down. A
      media-first well is `order: -1`, so it PAINTS first while staying the
      DOM's last child, and the same auto margin banks the card's leftover
      space ABOVE the drawing instead of below the text.
   ⚠ THE LEFTOVER VARIES BY LINE COUNT: card 02's paragraph runs FOUR lines
     against three, so it had no slack to bank and its drawing started 25.59px
     higher than its neighbours'. Every future copy edit would move the seam
     again. This rule ends that class of drift, not just today's instance.
   ⭐ THE SLACK GOES TO THE CHIPS, not to nothing. With no auto margin at all
      the wells align and the chip rows still sit one line apart. Pinning the
      chips to the card's foot aligns the top seam AND the bottom row. */
/* ⛔ THE `:has()` FORM IS NOT BELT-AND-BRACES. IT IS THE ONLY ONE THAT WINS.
   ⚠ MEASURED, NOT REASONED: with the plain selector alone the misalignment
     HALVED -- 25.59px to 12.80px -- instead of going to zero, and a half-fix
     is the tell. `:has()` takes the specificity of its MOST SPECIFIC ARGUMENT,
     so `:root .f--welled > .well:has( > .dvid--well )` is (0,4,0) while
     `:root .f--media-first > .well` is only (0,3,0). The well kept its auto
     margin, the chips had just been given one too, and flexbox did exactly
     what it should: it split the free space between the two of them, 12.8px
     each. ⭐ The number named the cause -- an exact half is two claimants. */
:root .f--media-first > .well,
:root .f--media-first > .well:has( > .dvid--well ) { margin-top: 0; }
:root .f--media-first > .chips { margin-top: auto; }

.dimg--cut-top    { clip-path: inset( 0 -24px -24px -24px ); }
.dimg--cut-bottom { clip-path: inset( -24px -24px 0 -24px ); }

/* ---------------------------------------------------------------------------
   14 · SECURITY — U6, section 11. A centred opening, four cells, and one ink
   bar that carries the memorable half of the argument.
   --------------------------------------------------------------------------- */

/* ⭐ A CENTRED SECTION OPENING. Every other section on this page opens
   left-aligned over its content, so this one had no opening of its own. */
.shead { max-width: var(--wp--custom--width--focused); text-align: center; }
:root .shead > * { margin-inline: auto; }

/* ⭐ THE ICON IS BARE — no tile, no ground, no ring. A repeated affordance is
   not the page's focal action, and the page allows exactly one focal green
   action per fold.
   ⚠ `place-items: start` is not redundant. The tile this replaced declared
   `place-items: center` for its grid, and Chrome aligns the in-flow children
   of a BLOCK container too -- so the declaration kept working after `display`
   changed from grid to block, and the mark stayed centred. */
.mark {
	display: block;
	place-items: start;
	justify-items: start;
	color: var(--wp--custom--color--brand-text);
}
.mark > .ico { width: 28px; height: 28px; stroke-width: 0.7143; }  /* 28 / vb 20 = 1.000 rendered */

/* ⭐ THE MEMORABLE HALF: WHAT IT NEVER DOES. ONE tracked-out label and three
   fragments on an ink bar, each with one drawn mark at the page's single
   stroke width.
   ⛔ IT IS ONE "NEVER", NOT THREE. Rebuilding it as a bulleted list with
      "Never" repeated three times was a COPY deviation as well as a design
      one -- the live page records one label and three fragments.
   ⚠ THE MARK INHERITS ITS COLOUR AND MUST. An explicit `--ink-muted` made the
     mark DIMMER (7.91) than the words beside it (12.03), for no reason. */
/* ⛔ THE BAND WENT WHITE ON 2026-09-23, ON ALPESH'S CALL, AND EVERY ROLE IN
   IT HAD TO MOVE WITH THE GROUND. It was the neutral dark gradient carrying
   dark-ground text; it is now paper carrying brand-text green.
   ⚠ A WHITE BAND ON THE TINT SECTION IS 1.05 : 1 -- measured -- so it needs
     the explicit hairline ring below or it does not read as a band at all.
     That separation is what the dark gradient was buying. */
.nevers {
	padding: var(--wp--custom--space--xl);
	border-radius: var(--wp--custom--rounded--md);
	background: var(--wp--custom--color--paper);
	box-shadow: inset 0 0 0 1px var(--wp--custom--color--hairline);
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: var(--wp--custom--space--md) var(--wp--custom--space--xl);
	list-style: none;
}
/* ⛔ THE WORDS ARE INK AND ONLY THE MARKS CARRY COLOUR. Alpesh painted the
   all-green version, saw it, and corrected it: "build it as shown in the
   screenshot" -- black words, coloured marks.
   ⚠ A GREEN BAND WAS TRIED AND IT WAS THE WRONG SIGNAL ANYWAY. The sentence
     is a list of things the product NEVER does; green is this page's colour
     for the thing it DOES. The state colour went on the refusal marks.
   ⛔ SUPERSEDED 2026-09-27 (U11 critique): Alpesh looked at red, secondary and
     ink side by side and picked INK for the marks too -- the one state colour
     read as an error in a list of promises. lp-security.php carries
     `tw:text-heading!` on each mark; the `danger` rule below stays only for
     the old markup still on the servers until the content sync.
   ⚠ AND `--brand` WAS REFUSED ON THE NUMBER BEFORE THAT: #028858 on paper
     measures EXACTLY 4.50, the floor itself with no clearance. */
:root .nevers > li {
	display: inline-flex;
	align-items: center;
	gap: var(--wp--custom--space--sm);
	margin: 0;
	padding-left: 0;
	font-size: var(--wp--custom--step--lead);
	line-height: var(--wp--custom--leading--body);
	letter-spacing: var(--wp--custom--tracking--lead);
	color: var(--wp--custom--color--heading);          /* 19.59 : 1 on paper */
}
/* ⛔ THE LABEL LOST ITS OWN SIZE AND ITS OWN TRACKING, 2026-09-23. Alpesh:
   "the 'Never' font text looks odd... needs to be consistent with everything...
   overall, in that band, we should use a lead font size."
   ⚠ HE IS RIGHT AND THE MEASUREMENT SAYS WHY: it was 12px beside items at
     16px -- TWO rungs down, in a row meant to read as one sentence.
   ⛔ AND THE TRACKING HAD TO GO WITH THE SIZE. The label layer is the one
      place the scale tracks OUT, +0.72px. At the lead rung the tier is
      -0.21px, and ops/measure/check-law.js fails a size carrying another
      size's tier by name. ⭐ SO THE LABEL NOW SEPARATES ON WEIGHT ALONE --
      600 against 400 -- which costs no contrast and breaks no tier. */
:root .nevers > li.nevers__l {
	/* ⛔ WEIGHT IS ALL IT HAS LEFT, AND THAT IS THE POINT. It used to separate
	   three ways -- a smaller size, the label layer's OUTWARD tracking, and a
	   different ink. All three are gone: the size because Alpesh saw 12px
	   beside 16px and called it odd, the tracking because it MUST follow the
	   size, and the ink because the band's ground changed.
	   ⚠ THE TRACKING SURVIVED MY FIRST EDIT AND WAS MEASURED WRONG: 1.26px on
	     a 21px word while its neighbours ran -0.21px. Deleting a `font-size`
	     does not delete the tier that went with it, and ops/measure/check-law.js
	     fails a size carrying another size's tier by name. ⭐ The declaration
	     that must change is rarely only the one you meant to change. */
	font-weight: var(--wp--custom--weight--display);
}
/* ⭐ THE MARKS ARE INK, THE SAME INK AS THE WORDS (Alpesh, 2026-09-27, U11
   critique): `danger` read as an error in a list of promises. They were
   `danger` from 2026-09-23 to 2026-09-27; `danger` stays the one state colour
   in the law. The `tw:text-heading!` on each mark in lp-security.php paints
   the same value; this rule makes the CSS say it too (Round 4, item 14). */
.nevers .ico { flex: none; color: var(--wp--custom--color--heading); }
@media (max-width: 760px) {
	/* ⛔ Centred fragments in a column read as a poem. The label leads. */
	.nevers {
		flex-direction: column;
		align-items: flex-start;
		justify-content: flex-start;
		gap: var(--wp--custom--space--md);
		padding: var(--wp--custom--space--lg);
	}
}

/* ---------------------------------------------------------------------------
   14b · THE CLOSE — the page's last word, BUILT 2026-09-23 and MISSING UNTIL
   THEN. Alpesh found it by looking; no checker counts sections against the
   prototype, and flatten-patterns.php takes an explicit list, so a section
   nobody names simply does not exist.
   --------------------------------------------------------------------------- */
/* ⛔ THE GROUND IS THE FLOOR, NOT INK, AND THE PROTOTYPE'S MARKUP MISLEADS.
   Its section carries `section--ink`, and a rule 1,300 lines further down in
   foundation.css overrides that class's ground back to the floor:
   "C IS THE PAGE NOW, 2026-09-18 -- Alpesh: 'the content block, like option C,
   I like.' The lime is still the last word... but it stopped being a 1408x669
   full-bleed slab: 24% less area."
   ⚠ I READ THE MARKUP AND REPORTED THIS AS THE PAGE'S ONLY DARK SECTION. It
     is not dark at all. A class name is not a ground. */
.section--close { background: var(--abk-floor); padding-inline: var(--wp--custom--space--2-xl); }
.section--close > .wrap {
	position: relative;
	isolation: isolate;
	overflow: hidden;
	border-radius: var(--wp--custom--rounded--lg);
	/* ⭐ THE FOOT EQUALS THE HEAD, 2026-09-25 (critique round 2, Alpesh's
	   pick by looking). It was 2.2x the head, so the copy finished above the
	   band's old 68% clear point -- and that left 227px of empty green under
	   the microcopy against 96px above the headline. ⚠ THE BAND MOVED WITH IT:
	   see the stops below. Cutting the padding alone puts the copy in the
	   green. This rule is 4925's close too. */
	padding: var(--wp--custom--space--4-xl) var(--wp--custom--space--2-xl);
}
.section--close h2 { max-width: 26ch; }

/* ⭐ THE CLOSING BAND IS THE HERO'S OWN WASH, and the page ends where it
   began. ⛔ IT IS A `::before`, EXACTLY AS THE HERO IS -- and that is not a
   way of dodging ops/measure/check-law.js. The guard reads a ground off an
   ANCESTOR; a ::before is a sibling, so it cannot see either surface. ⚠ SO
   THIS ONE IS VERIFIED BY SAMPLING THE RENDERED PIXELS UNDER EVERY LINE, the
   same method that found the hero's stat labels failing at 4.39 this morning.
   ⛔ THE MIDDLE STOP NAMES `--brand` WHILE THE HERO'S NAMES `--brand-lift`,
      AND THE DIFFERENCE IS DELIBERATE. This section carries `.on-ink`, whose
      remap re-points `--brand-lift` to `--brand`. Copying the hero's
      declaration stop-for-stop WAS a real defect: it silently resolved to a
      different colour here. The stop is written as what it actually paints.
   ⭐ THE STOPS ARE MEASURED POSITIONS, AS THE PHONE'S ARE. With the foot equal
      to the head, measured at 1440 as fractions UP from the wrap's bottom:
      the buttons start at 0.441 on this page and 0.404 on 4925, and the
      lead-generation microcopy's middle line sits at 0.264. So the wash
      clears at 46%, just above the buttons, and holds 16% brand at 26%.
   ⛔ THE 26% STOP IS THE PHONE'S CONTRAST BUDGET: the microcopy is inside the
      band. Measured on the painted pixels at the darkest point under it:
      4.71 : 1 at 1440, 4.66 : 1 at 390. */
.section--close > .wrap::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	background: linear-gradient(to top,
		color-mix(in oklab, var(--wp--custom--color--brand)      34%, transparent)  0%,
		color-mix(in oklab, var(--wp--custom--color--brand)      16%, transparent) 26%,
		transparent 46%);
}

/* ⭐ THE DARK BRAND CLOSE -- page `wordpress-lead-generation`, lg-close.php.
   The panel stands on `g-brand-glow`, the law's dark brand ground (Alpesh,
   2026-09-26, G2 by looking), and the light wash is gone. ⛔ PAGE-ONLY: 4925's
   close keeps the wash. This section carries no `on-ink`, so its text takes
   the dark-ground ink tokens directly. */
:root .section--close-dark > .wrap { background: var(--wp--preset--gradient--g-brand-glow); }
:root .section--close-dark > .wrap::before { display: none; }
:root .section--close-dark .wrap > h2      { color: var(--wp--custom--color--ink-heading); }
/* ⚠ ALL LIGHT INK, not ink-body and ink-muted: on the glow's brightest stop,
   brand-text at the top centre, ink-body measured 3.78 and ink-muted 2.49
   (check-law.js reads that stop). The paint under the lines was 7.32 and
   6.39, but no line should depend on where the glow falls. */
:root .section--close-dark .wrap > .lead,
:root .section--close-dark .wrap > .eyebrow { color: var(--wp--custom--color--ink-heading); }
:root .section--close-dark .btn.btn--banner       { background: var(--abk-floor); color: var(--wp--custom--color--ink); }
/* ⭐ U28: the focus ring on the dark close is the green made for ink (8.64:1 beside
   the button; the inherited #007047 measured 3.09:1). The section does not carry
   `.on-ink`, so the `.on-ink :focus-visible` rule in system.css does not reach it. */
.section--close-dark :focus-visible { outline-color: var(--wp--custom--color--brand-on-ink); }
:root .section--close-dark .btn.btn--banner:hover { background: var(--wp--custom--color--tint); }
:root .section--close-dark .btn.btn--banner-2     { color: var(--wp--custom--color--ink-heading);
	box-shadow: inset 0 0 0 1px color-mix( in oklab, var(--wp--custom--color--paper) 40%, transparent ); }
:root .section--close-dark .btn.btn--banner-2:hover { background: color-mix( in oklab, var(--wp--custom--color--paper) 8%, transparent ); }

/* ⭐ THE VEIL GROUND -- the dark room (the design-system skill: the No-Ink-Ground
   Rule and the Veil Rule; Alpesh's U24 picks, law in U26, 2026-09-30). Ink with
   the veil pattern, never flat ink. The SVGs are written by ops/assets/gen-veil.js
   into assets/img/veil/ (`--check` proves them current and the veils apart).
   ⛔ HAND-WRITTEN, because a background image of a file is not a `tw:` utility
      without a free value.
   ⚠ The text floors bind on the pixels UNDER the text, veil included: the veils
     are anchored bottom right, away from the heading, and the build is measured
     by sampling the rendered ground under every line (plan U26).

   1 · A WHOLE SECTION: 4925 §5, the industry simulator, the greyish filled veil. */
:root .veil-ground {
	background: url("../img/veil/veil-fill-grey-2.svg") right bottom / cover no-repeat,
		var(--wp--custom--color--ink);
	color: var(--wp--custom--color--ink-body);
}
:root .veil-ground .lead    { color: var(--wp--custom--color--ink-body); }
/* ⛔ THE SMALL LINE TAKES THE LIGHTEST INK. The veil's lower strands pass under
   it: ink-muted measured 2.5 : 1 on the worst pixel at 1440, ink-heading 5.98
   (ops/measure/check-veil-contrast.js). Alpesh picked this, option 3, 2026-09-30. */
:root .veil-ground .eyebrow { color: var(--wp--custom--color--ink-heading); }
:root .veil-ground .eyebrow::before { background: var(--wp--custom--color--brand-on-ink); }
/* ⭐ ON A PHONE THE VEIL IS THE LIGHT WEIGHT, AND THE LEAD TAKES THE LIGHTEST INK.
   The column is narrow, so the veil's crest runs behind the lead: the full weight
   measured 1.9 : 1 there. The light veil with ink-heading text measures 6.87 : 1.
   Alpesh kept the steep "mountain" placement over four others (horizontal, two
   diagonals, centre-to-left), 2026-09-30. */
@media (max-width: 639px) {
	:root .veil-ground { background-image: url("../img/veil/veil-fill-grey-2-light.svg"); }
	:root .veil-ground .lead { color: var(--wp--custom--color--ink-heading); }
}

/* 2 · A CLOSING PANEL: 4925 §14, two LIGHT green veils (text sits on this
   ground, so light and few) over `g-ink-glow`, on ink. It rides on
   `section--close-dark` above for its text and its buttons: white primary, the
   second action outlined (a dark banner takes paper). */
:root .section--close-veil > .wrap {
	background: url("../img/veil/veil-fill-green-2-light.svg") right bottom / 100% auto no-repeat,
		var(--wp--preset--gradient--g-ink-glow),
		var(--wp--custom--color--ink);
}

/* ⛔ `.btn.` IS LOAD-BEARING IN BOTH SELECTORS. A bare `.btn--banner` is
   (0,1,0) and loses to anything that colours a link inside a remapped section,
   which in the prototype painted the label BRAND GREEN on an ink fill at
   2.44 : 1. The doubled class costs nothing and closes that. */
.btn.btn--banner        { background: var(--wp--custom--color--ink); color: var(--wp--custom--color--paper); }
.btn.btn--banner:hover  { background: var(--wp--custom--color--heading); }
.btn.btn--banner-2      { background: transparent; color: var(--wp--custom--color--ink);
	box-shadow: inset 0 0 0 1px color-mix( in oklab, var(--wp--custom--color--ink) 30%, transparent ); }
.btn.btn--banner-2:hover { background: color-mix( in oklab, var(--wp--custom--color--ink) 6%, transparent ); }

/* ⛔ AT 390 THE PANEL SPENT 192px ON PADDING -- 48px on the section and 48px
   again on the wrap -- which left a 182px TEXT COLUMN. ⚠ 182 AND NOT 198: the
   section itself is 374px wide, not 390, because the page insets it 8px a side
   before any of this. The primary
   button wanted 315.8px inside it, and `.wrap` is `overflow: hidden`, so the
   button was CLIPPED FLAT at both ends.
   ⭐ THE BUTTON WAS NEVER FULL-WIDTH. It was too wide and the panel trimmed it,
      which is why it read as an edge-to-edge bar. Alpesh, 2026-09-24: "the
      banners should not have that much margin on the left and right... the
      main primary button is touching end to end there."
   ⚠ THE BUTTON'S OWN INLINE PADDING HAS TO STEP DOWN TOO. 294px is still not
     enough for a 251.8px label inside 2 x 32px. ⛔ ONLY THE PADDING MOVES --
     the 21px lead size and the 56px tap floor are untouched, so neither the
     type ladder nor check-law.js's control floor is disturbed.
   ⚠ `padding-inline` ALONE, NOT THE SHORTHAND. The wrap's bottom padding is
     deliberately 2.2x its top so the last line finishes above the green band;
     a shorthand here would silently reset it. */
@media (max-width: 620px) {
	.section--close { padding-inline: var(--wp--custom--space--md); }
	.section--close > .wrap { padding-inline: var(--wp--custom--space--lg); }
	/* ⛔ AND THE CAP IS THE GUARD, NOT THE PADDING. The padding is a fixed
	   number, so the button's appetite does not shrink with the viewport. At
	   360px -- the commonest Android width, and BELOW the 390 floor stated in
	   theme.json -- the column is 264px and the button still wants 283.8px, so
	   the clip comes back. ⭐ Capped, the label WRAPS to a second line and the
	   button grows TALLER instead of being trimmed. A wrapped label is legible;
	   a trimmed one is the defect Alpesh reported.
	   ⛔ `min-width: 0` ON THE WRAPPER IS LOAD-BEARING, AND `max-width` ALONE
	      DOES NOTHING WITHOUT IT. Core wraps every button in a
	      `.wp-block-button` div, and that div is the FLEX ITEM. A flex item's
	      `min-width` defaults to `auto`, which means "never smaller than my
	      content" -- so the wrapper stayed 283.8px wide and the anchor's
	      `max-width: 100%` resolved against a box that had already overflowed.
	   ⚠ MEASURED, NOT REASONED: with the cap on the anchor alone the button was
	     still 283.8px and still clipped at 320, 360 and 375. */
	/* ⛔ 227px OF EMPTY GREEN SAT UNDER THE LAST LINE, and the band is why.
	   The wrap's bottom padding is 2.2x its top so the copy always finishes
	   above the gradient's clear point at 68%. ⚠ SO CUTTING THE PADDING ALONE
	     PUTS THE COPY IN THE GREEN: measured, the last line moves from 69.5%
	     to 83.5% of the wrap. ⭐ THE BAND HAS TO MOVE WITH IT. On a phone the
	     band is a thin echo of the hero rather than a slab, so its stops
	     compress and the padding can fall to one 4xl. Alpesh drew the box,
	     2026-09-24: "there is too much white space".
	   ⚠ THE STOPS ARE RE-STATED IN FULL, not patched. A gradient is one
	     property; overriding two of three stops is not possible. */
	.section--close > .wrap {
		padding-top: var(--wp--custom--space--3-xl);
		padding-bottom: var(--wp--custom--space--4-xl);
	}
	/* ⛔ THE BAND REACHES THE SECOND BUTTON, AND EVERY STOP IS A MEASURED
	   POSITION RATHER THAN A ROUND NUMBER. Alpesh, 2026-09-24: "the gradient at
	   the bottom is not right. It's just a small band. At least it should
	   stretch till the Book a Demo button."
	   ⚠ THE GRADIENT RUNS `to top`, SO 0% IS THE BOTTOM EDGE. Measured at 390,
	     as fractions UP from the wrap's bottom: "Book a 30-min demo" starts at
	     0.340 and the eyebrow's middle line sits at 0.204. So the wash clears
	     at 34% -- the button's own top -- and not before.
	   ⛔ AND THE MIDDLE STOP IS A CONTRAST BUDGET, NOT A TASTE. The eyebrow is
	      INSIDE the band now, and its ink needs the ground to stay pale enough
	      for 4.5 : 1. ⚠ MEASURED ON THE PAINTED PIXELS: at 28% brand the ground
	      is rgb(184,222,208) and the ratio is 4.08 -- a fail. The ceiling is
	      about 21% brand; 16% at the 20% stop leaves the eyebrow at roughly
	      15.5% and holds the margin.
	   ⭐ THIS IS WHY THE STOPS ARE RE-STATED IN FULL. A gradient is one
	      property, so two of three stops cannot be patched. */
	/* ⚠ 37%, NOT 34%, SINCE 2026-09-25. The lead-generation page's second
	   button starts at 0.350 and 4925's at 0.348, so a wash that clears at 34%
	   painted its visible edge ACROSS the button (critique round 2). 37% clears
	   just above both. */
	.section--close > .wrap::before {
		background: linear-gradient(to top,
			color-mix(in oklab, var(--wp--custom--color--brand) 34%, transparent)  0%,
			color-mix(in oklab, var(--wp--custom--color--brand) 16%, transparent) 20%,
			transparent 37%);
	}
	.section--close .row--cta .wp-block-button { min-width: 0; max-width: 100%; }
	/* ⛔ `white-space: normal` IS THE REST OF THE FIX, AND WITHOUT IT THE CAP
	      ONLY MOVES THE DEFECT. The button inherits `nowrap`, so a capped box
	      does not wrap the label -- it lets the label hang OUT of the box, and
	      an inline-flex centres the overhang, so it spills equally at both ends
	      and reads exactly like the clipped bar we started from.
	   ⚠ EVERY BOX MEASUREMENT SAID THIS WAS FIXED. Width, height, `scrollWidth`
	     and the overflow check were all clean while the label hung 4.8px past
	     each edge; a Range around the text node measured 228.8px inside a 224px
	     button. ⛔ THE SCREENSHOT FOUND IT, NOT THE NUMBERS -- the tenth time in
	     this work that a green measurement sat beside a wrong page. */
	.section--close .btn { padding-inline: var(--wp--custom--space--md); max-width: 100%; white-space: normal; }
}

/* ---------------------------------------------------------------------------
   15 · THE FAQ — U6, section 12. Twenty-two native disclosures, no JavaScript.
   --------------------------------------------------------------------------- */
.faq { max-width: var(--wp--custom--width--prose); }
/* ⛔ THE RULE IS ON THE BOTTOM EDGE, NOT THE TOP. With a top border on every
   item plus a bottom border on the last, the section painted 23 rules over
   1,290px -- and a hairline repeated 23 times is not a hairline, it is a
   TABLE. ⭐ 22 of 23 is not the point: a BOTTOM rule reads as "this item ENDS
   here", while a top rule reads as a table's row divider. */
.faq details { border-bottom: 1px solid var(--wp--custom--color--hairline); }
.faq summary {
	cursor: pointer;
	list-style: none;
	padding: var(--wp--custom--space--md) var(--wp--custom--space--2-xl) var(--wp--custom--space--md) 0;
	min-height: 44px;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--wp--custom--space--md);
	font-weight: var(--wp--custom--weight--emphasis);
	position: relative;
}
.faq summary::-webkit-details-marker { display: none; }
/* ⭐ A REAL DISCLOSURE ROTATES ONE MARK; the affordance was a bare text "+".
   ⛔ AND IT IS DRAWN IN CSS, NOT AS AN ICON. It must be pixel-crisp at 11px,
      and a stroked SVG at that size lands off-pixel. The FAMILY is the stroke
      width and the ink, not the medium. */
.faq summary::after {
	content: "";
	position: absolute;
	right: 6px;
	top: 50%;
	width: 11px;
	height: 11px;
	margin-top: -5px;
	background:
		linear-gradient(var(--wp--custom--color--ink) 0 0) center/100% 1px no-repeat,
		linear-gradient(var(--wp--custom--color--ink) 0 0) center/1px 100% no-repeat;
	transition: transform var(--wp--custom--motion--duration) var(--wp--custom--motion--easing);
}
.faq details[open] summary::after { transform: rotate(45deg); }
@media (prefers-reduced-motion: reduce) { .faq summary::after { transition: none; } }
:root :is(.section, .tw\:panel) .faq details > p {
	margin: 0;
	padding-bottom: var(--wp--custom--space--lg);
	color: var(--wp--custom--color--secondary);
}

/* ⛔ A QUESTION LIST IS PROSE: IT TAKES A MEASURE AND IT CENTRES. It was a
   half-width LEFT slab in a 1408px section -- 22 questions with an empty right
   half beside them.
   ⭐ TWO COLUMNS, and `break-inside: avoid` keeps an item whole, because a
      disclosure must be able to grow without re-flowing its siblings. */
.faq--centred {
	max-width: var(--wp--custom--width--frame);
	margin-inline: auto;
	column-count: 2;
	column-gap: var(--wp--custom--space--3-xl);
}
.faq--centred details { break-inside: avoid; }
/* ⚠ ONE COLUMN BELOW THE FRAME. Two columns of 22 questions at 390 would be
   two columns of unreadable ones. */
@media (max-width: 899px) { .faq--centred { column-count: 1; } }
.section--faq h2 { max-width: 24ch; }
@media (max-width: 620px) {
	/* 22 questions at 390 wrap to two lines each, so the VERTICAL padding is
	   paid 22 times. The 44px control floor is untouched.
	   ⛔ AND ONLY THE VERTICAL PADDING GOES -- THE PROTOTYPE LOSES BOTH AND IT
	   IS A MEASURED DEFECT. Its rule is `padding: 10px 0`, which also removes
	   the 48px gutter the disclosure mark sits in. Measured at 390 on the
	   prototype: FIVE of the 22 questions run under their own +/- mark, the
	   worst by 12px. ⭐ The rule's own comment says the padding is about the
	   VERTICAL cost; losing the gutter was collateral. ⚠ THE COST IS HONEST
	   AND STATED: our FAQ is 128px taller at 390 because five questions wrap
	   one line further. Text under a control is a defect; a longer column is
	   not. */
	.faq summary { padding-top: 10px; padding-bottom: 10px; }
	:root :is(.section, .tw\:panel) .faq details > p { padding-bottom: var(--wp--custom--space--md); }
}

/* -- 15a · THE RANK MATH FAQ IN 4925's LOOK -- page `wordpress-lead-generation`,
   lg-faq.php (plan R20, U11, KTD8). `rank-math/faq-block` renders
   `.rank-math-list > .rank-math-list-item > h3.rank-math-question +
   .rank-math-answer`; faq-accordion.js then moves the question into a
   `button.abk-faq-toggle` and hides the answer.
   ⛔ EVERY RULE IS UNDER `.faq--rm`. style.css styles `.abk-faq-accordion` on
      seven other pages, and they must not move. Its rules are (0,2,0) to
      (0,3,1); these are one class more, or equal and later in the cascade.
   ⭐ THE VALUES ARE 15's, rule for rule: one column at the prose measure, the
      hairline on the BOTTOM edge only, the question at the emphasis weight
      with a 48px gutter for the mark, the answer in `secondary`, and the
      drawn plus that turns 45deg when open.
   ⚠ THE PRE-SCRIPT CLASSES ARE STYLED TOO, so the list reads as the same
     component before the script runs and without it. */
:root .faq--rm .wrap > .rank-math-block { margin-top: var(--wp--custom--space--2-xl); }
.faq--rm .rank-math-list {
	max-width: var(--wp--custom--width--prose);
	margin-inline: auto;
	text-align: initial;
}
.faq--rm .rank-math-list-item {
	margin: 0;
	border-bottom: 1px solid var(--wp--custom--color--hairline);
}
.faq--rm .rank-math-list .rank-math-question {
	margin: 0;
	font-size: var(--wp--custom--step--body);
	font-weight: var(--wp--custom--weight--emphasis);
	letter-spacing: var(--wp--custom--tracking--text);
	line-height: var(--wp--custom--leading--body);
	color: var(--wp--custom--color--heading);
	text-wrap: pretty;
}
/* Before the script: the question is plain text in the h3, so the h3 takes
   the summary's padding. After it, the button does. */
.faq--rm .rank-math-list:not(.abk-faq-accordion) .rank-math-question {
	padding: var(--wp--custom--space--md) 0;
}
.faq--rm .abk-faq-accordion .abk-faq-toggle {
	position: relative;
	min-height: 44px;
	padding: var(--wp--custom--space--md) var(--wp--custom--space--2-xl) var(--wp--custom--space--md) 0;
	gap: var(--wp--custom--space--md);
	cursor: pointer;
}
/* ⭐ 4925's MARK: two 1px bars, drawn in CSS so they land on the pixel. It
   replaces style.css's bordered chevron, so the border is cleared. */
.faq--rm .abk-faq-accordion .abk-faq-toggle::after {
	content: "";
	position: absolute;
	right: 6px;
	top: 50%;
	width: 11px;
	height: 11px;
	margin-top: -5px;
	border: 0;
	background:
		linear-gradient(var(--wp--custom--color--ink) 0 0) center/100% 1px no-repeat,
		linear-gradient(var(--wp--custom--color--ink) 0 0) center/1px 100% no-repeat;
	transform: none;
	transition: transform var(--wp--custom--motion--duration) var(--wp--custom--motion--easing);
}
.faq--rm .abk-faq-accordion .abk-faq-toggle[aria-expanded="true"]::after { transform: rotate(45deg); }
/* ⚠ RESTATED: the base rule above outranks style.css's reduced-motion rule. */
@media (prefers-reduced-motion: reduce) {
	.faq--rm .abk-faq-accordion .abk-faq-toggle::after { transition: none; }
}
.faq--rm .rank-math-list .rank-math-answer {
	padding-bottom: var(--wp--custom--space--lg);
	color: var(--wp--custom--color--secondary);
}
.faq--rm .rank-math-list .rank-math-answer > * { margin: 0; }
@media (max-width: 620px) {
	/* 15's phone values: only the vertical padding goes; the gutter stays. */
	.faq--rm .abk-faq-accordion .abk-faq-toggle { padding-top: 10px; padding-bottom: 10px; }
	.faq--rm .rank-math-list .rank-math-answer { padding-bottom: var(--wp--custom--space--md); }
}

/* ---------------------------------------------------------------------------
   16 · THE LIVE BOT — U7. The one object on this page served from another
   host, and the section's whole argument is that THIS ONE IS REAL.
   ⛔ IT IS NOT A BLOCK, AND THE PLAN SAID IT WOULD BE. A block exists to hold
      STATE and this section holds none: an iframe, a fallback behind it, and
      some copy. Measured on the prototype -- three scripts, and not one of
      them touches this section. So it is a pattern.
   --------------------------------------------------------------------------- */
/* ⚠ `:is(.section, .tw\:panel)` on the frame rules below (2026-09-27): the live-bot
   section moved to the Tailwind chassis and its frame stayed a component. The
   :is() keeps the old (0,3,0)-class weight. */
.demo__frame { display: grid; place-items: center; }
:root :is(.section, .tw\:panel) .demo__frame { margin-top: var(--wp--custom--space--2-xl); }
:root :is(.section, .tw\:panel) .demo__frame > .eyebrow { margin-bottom: var(--wp--custom--space--md); }

/* ⭐ THE LIVE WIDGET STANDS ON A PAINTED RIBBON — the FIRST painted field to
   reach the theme. Twenty were generated on 2026-09-20; Alpesh kept two, and
   docs/research/prototypes/painted/README.md states that the copy into this
   directory IS the review gate. This is that copy.
   ⛔ IT IS f1-ribbon, ALL GREEN. ⚠ A DERIVED RECOLOUR -- f1's shape carrying
      r1-duo's colour -- SHIPPED FIRST AND HE REJECTED IT THE SAME DAY, after
      seeing it here: the duo's orange landed beside the headline, the one warm
      thing in a green section, pulling the eye off the live demo.
   ⭐ f1's CURVE IS WHY IT WINS, not its colour. It is the fatter of the two
      kept ribbons, so more of it survives past a 480px widget; r1-duo's
      thinner curve showed as two unrelated corners. And the record's
      "greens alone read as eco" does not hold behind a green-headed chat
      widget -- there they read as of a piece with the product.
   ⚠ A FLAT PANEL CAME FIRST AND HE REFUSED IT: tint measured 1.05:1 against
     the section's paper and did almost nothing; dark neutral worked but "feels
     dull". A field BEHIND the widget is atmosphere. A panel AROUND it is a
     box, and the widget is already a box.
   ⛔ NO TEXT LANDS ON THE RIBBON, so nothing here needs a contrast ruling. The
      caption sits above it and the hint below it, both on the section's paper.
      ⚠ If either ever moves onto the field, it stops being decoration and
        needs a measured floor like any other text. */
#livebot .demo__frame {
	padding: var(--wp--custom--space--3-xl) var(--wp--custom--space--2-xl);
	border-radius: var(--wp--custom--rounded--lg);
	background-image: url("../img/lb-ribbon.webp");
	background-size: cover;
	background-position: center;
}
/* ⭐ THE WIDGET FLOATS OVER THE FIELD: the drawing scale's `floating` shadow.
   Alpesh picked it on 2026-10-05 over the old custom cast and the page's
   `depth`. It is the one page surface that takes a drawing shadow. */
#livebot .lb-frame {
	box-shadow: var(--wp--custom--drawing--shadow--floating);
}
@media (max-width: 620px) {
	/* ⛔ THE PANEL BREAKS OUT OF THE SECTION'S GUTTER, and its own padding is
	   then the only margin the widget needs. Alpesh, 2026-09-24: "the panel is
	   already providing margin for the widget, the panel can have full width
	   with no margins."
	   ⚠ MEASURED AT 390: the widget lost 40px to the section's 20px gutter
	     BEFORE the panel's own 16px ever applied -- 374 -> 334 -> 302. Pulling
	     the panel back over the gutter returns all 40px to the chat window.
	   ⛔ THE GUTTER ITSELF STAYS. It still holds the headline and the lead above
	      this panel off the screen edge; only the PANEL escapes it, which is
	      why this is a negative margin and not a smaller gutter. */
	#livebot .demo__frame {
		padding: var(--wp--custom--space--xl) var(--wp--custom--space--md);
		margin-inline: calc( var(--wp--custom--width--gutter) * -1 );
	}
}
/* ⛔ AND THE COLUMN'S OWN TOP MARGIN GOES, OR "CENTRED" IS 24px OUT.
   Alpesh asked for the copy to be centred against the panel, and dropping
   `split--top` was only half of it: `:root .section .demo__frame` carries
   `margin-top: 48px`, which a centred grid item keeps -- so the panel's centre
   sat 24px below the copy's, MEASURED at 617.8 against 593.8. ⭐ Half of a
   margin is exactly the offset it puts into a centred pair, which is the
   number to look for.
   ⚠ ONLY ABOVE 900px. Below that the split stacks and the frame moves ABOVE
     the copy, where that margin is the gap doing its real job. */
@media (min-width: 900px) {
	:root :is(.section, .tw\:panel) .split > .demo__frame { margin-top: 0; }
}

.lb-frame {
	position: relative;
	isolation: isolate;
	width: 100%;
	max-width: 480px;
	aspect-ratio: 480 / 580;
	background: var(--abk-floor);
	border-radius: var(--wp--custom--rounded--lg);
	box-shadow: var(--wp--custom--shadow--depth);
	overflow: hidden;
	display: grid;
	place-items: center;
}
/* ⛔ BEHIND THE IFRAME, NOT INSTEAD OF IT — z-index 0 against the iframe's 1.
   If app.aibotkit.io fails, the panel still paints with this line showing
   through, so a failure reads as an empty panel rather than a hole in the
   page. A drawing of a loading state that can never finish is the thing this
   replaced. */
.lb-fallback {
	position: absolute;
	z-index: 0;
	margin: 0;
	padding: var(--wp--custom--space--lg);
	max-width: 34ch;
	text-align: center;
	font-size: var(--wp--custom--step--body-small);
	letter-spacing: var(--wp--custom--tracking--text);
	color: var(--wp--custom--color--secondary);
}
.lb-iframe { position: relative; z-index: 1; width: 100%; height: 100%; border: 0; display: block; }

/* ⛔ AN ASPECT RATIO TIES HEIGHT TO WIDTH, AND ON A PHONE THAT IS BACKWARDS.
   `.lb-frame` is 480 x 580 on a desktop, but the ratio makes the box SHORTER as
   the screen narrows: measured 302 x 365 at 390, which is 43% of an 844px
   viewport. ⭐ WIDTH IS THE SCARCE THING ON A PHONE AND HEIGHT IS THE ABUNDANT
   ONE -- and height is what carries a conversation. Alpesh, 2026-09-24: "it
   should be such that it can take a mobile browser's viewport by having some
   breathing space at the top and bottom."
   ⛔ `dvh`, NOT `vh`. `vh` measures the viewport with the browser's URL bar
      HIDDEN, so a `vh` box is taller than what a reader can actually see when
      the page opens. `dvh` is the viewport as it is right now.
   ⚠ THE `vh` LINE ABOVE IT IS A FALLBACK, NOT A DUPLICATE. A browser that does
     not know `dvh` drops the second declaration and keeps the first.
   ⚠ `aspect-ratio: auto` MUST BE STATED. Leaving the ratio in place with a
     height set makes the box compute a WIDTH from the new height. */
@media (max-width: 620px) {
	.lb-frame {
		aspect-ratio: auto;
		height: clamp(420px, 72vh, 620px);
		height: clamp(420px, 72dvh, 620px);
	}
}
/* ⛔ THE CAPTION AND THE HINT ARE ONE UNIT, AND THE WIDGET IS NOT.
   They were 32px apart while the hint sat 0px off the widget, so the line read
   as a label ON the widget rather than as the second half of the caption above
   it. ⭐ SPACING IS WHAT GROUPS TWO LINES -- 8px inside the pair, 32px to the
   object below. Alpesh drew this on 2026-09-24. */
:root :is(.section, .tw\:panel) .demo__frame .eyebrow { margin-bottom: var(--wp--custom--space--sm); }
:root :is(.section, .tw\:panel) .lb-hint {
	margin-top: 0;
	margin-bottom: var(--wp--custom--space--xl);
	font-size: var(--wp--custom--step--body-small);
	letter-spacing: var(--wp--custom--tracking--text);
	color: var(--wp--custom--color--secondary);
}

/* ⛔ `.pills`, `.pill`, `.ask` AND `.ask-lead` WERE DELETED HERE, 2026-09-23,
   in the same commit that removed their markup. Alpesh removed the four claim
   pills and the three suggested questions from the live-bot section, and those
   four selectors appeared in NO other pattern and NO other row of page 4925 --
   grepped, not assumed. ⭐ A rule whose only element is gone is dead weight
   that the next person has to read and rule out. The audit he scheduled
   REMOVES rather than adds, and this is what that looks like.
   ⚠ `.on-ink .pill` went with them; it had no live element either. */
:root .section .row--cta        { margin-top: var(--wp--custom--space--xl); }
:root .section .row--cta + .eyebrow { margin-top: var(--wp--custom--space--md); }

/* ---------------------------------------------------------------------------
   17 · THE DEMO STAGE — U7, and it is a BLOCK because it holds state.
   ⛔ THE STAGE IS WHAT MAKES THE FOUR STEPS ONE PRODUCT. As four separate
      feature sections there is no sense that step 04 is the same system as
      step 01. One window that stays put while the argument moves past it says
      so without a sentence.
   --------------------------------------------------------------------------- */
.stage { display: grid; gap: var(--wp--custom--space--2-xl); }
/* ⚠ (0,2,0), NOT (0,3,0). The stage asks for 64px on its own and must take
   48px when it follows a lead -- the same rule every other component obeys.
   At (0,3,0) it outranked that rule and sat 16px low. */
.section .stage { margin-top: var(--wp--custom--space--3-xl); }

/* ⭐ THE RAIL IS A PROGRESS BAR DISGUISED AS A MARGIN RULE: a 3px left border
   on the list, and the ACTIVE step paints its own segment of it. */
:root .stage .stage__steps {
	list-style: none;
	margin: 0;
	padding: 0;
	border-left: 3px solid var(--wp--custom--color--hairline);
}
:root .stage .stage__step {
	margin: 0;
	padding: var(--wp--custom--space--lg) 0 var(--wp--custom--space--lg) var(--wp--custom--space--xl);
	margin-left: -3px;
	border-left: 3px solid transparent;
	transition: border-color var(--wp--custom--motion--duration) ease;
}
/* ⭐ THE DIMMING, AND THE RULE IS "IT DOES NOT VANISH; IT GOES QUIET."
   ⛔ `data-on` IS NOT A BOUND ATTRIBUTE, AND THAT COST TWO FINDINGS TO REACH.
      Bound per-step, WordPress's server-side directive processing REMOVED it
      -- PHP cannot evaluate a derived per-element value -- so the page
      arrived with no lit step at all. Bound as a boolean, it renders as the
      STRING "false" rather than being removed, so every step carried it. ⭐ It
      is written server-side on step 1 and maintained by the view module,
      because ops/measure/check-law.js reads this exact attribute to tell a
      STATEFUL rail from a decorative side-stripe -- and it refused the rail
      the moment the attribute stopped distinguishing the lit step. */
/* ⭐ THE UNLIT STEPS GO QUIET, NOT DIM (U34, C3; P-D5, Alpesh "B"). The whole
   step at opacity 0.55 put its text at 2.34 : 1, under the floor. Now only the
   number and the heading of an unlit step step back (5.95 : 1); the lit step
   keeps the ink badge, the ink heading and the green rail. */
:root .stage .stage__step:not([data-on]) > h3 { color: var(--wp--custom--color--secondary); }
:root .stage .stage__step:not([data-on]) > .step__n {
	background: var(--wp--custom--color--chrome);
	color: var(--wp--custom--color--secondary);
}
/* ⚠ (0,4,0), BECAUSE THE RESET ABOVE IS (0,3,0) AND USES THE `border-left`
   SHORTHAND. The dimming worked and the RAIL DID NOT: `opacity` was set by a
   one-class rule and won, while the colour was set inside a three-class
   shorthand and lost. ⭐ check-law.js found it by reading the PAINT -- it
   refuses a side-stripe that carries no state, and a rail whose lit colour
   never paints carries none. */
:root .stage .stage__step[data-on] { border-left-color: var(--wp--custom--color--brand); }
/* ⛔ `>` ON BOTH, AND IT IS THE SAME LEAK AS THE CARD'S. Below 900px each
   transcript MOVES INSIDE its own step -- so `.stage__step p` matched every
   chat message in it and gave each one the step's own 8px rhythm. Measured at
   390: the widget came out 16px tall of nothing and the whole column with it.
   ⭐ A step's own copy is its DIRECT children; anything deeper is a drawing,
   and a drawing states its own type. Third time this rule has earned itself. */
:root .stage .stage__step > h3 {
	margin: var(--wp--custom--space--sm) 0 0;
	font-size: var(--wp--custom--step--subsection);
	font-weight: var(--wp--custom--weight--display);
	letter-spacing: var(--abk-track-sub);
	line-height: var(--wp--custom--leading--display);
	color: var(--wp--custom--color--heading);
}
:root .stage .stage__step > p { margin: var(--wp--custom--space--sm) 0 0; color: var(--wp--custom--color--secondary); }
/* ⭐ ONE EDGE (U33 Q3): the widget LIES on the prop -- one line, an almost flat shadow. */
.stage__card .wgt { box-shadow: var(--wp--custom--drawing--shadow--lying); }

/* ⛔ THE STACKED CARD NEEDS ITS OWN HEIGHT, AND IT HAD NONE. The height that
   makes this widget a WINDOW is declared inside `@media (min-width: 900px)`
   below, so on a phone the widget was sized by whatever was in it.
   ⚠ THAT WAS HARMLESS ONLY WHILE THE PHONE SHOWED EVERY LINE AT ONCE. The
     moment the player runs here, an empty log collapses the card to 146px and
     it grows line by line -- so the section gets TALLER under the reader's
     thumb, the page scrolls out from under them, and the card crosses the
     observer's band repeatedly. MEASURED: card 1 restarted four times in one
     500px scroll.
   ⭐ A FIXED BOX IS ALSO WHAT `trim()` NEEDS. The log drops its oldest line
      when a new one will not fit, and "will not fit" has no meaning in a box
      that simply grows. Alpesh, 2026-09-23: "we can fix the height of the chat
      widget relative to the viewport available so that at least 2 turns are
      always visible."
   ⚠ `dvh` FOR THE SAME REASON AS `.lb-frame`, with a `vh` fallback first. */
@media (max-width: 899px) {
	.stage__card .wgt {
		height: clamp(320px, 52vh, 460px);
		height: clamp(320px, 52dvh, 460px);
		display: flex;
		flex-direction: column;
	}
	.stage__card .wgt__log { flex: 1 1 auto; overflow: hidden; align-content: start; }
}

/* ⛔ ONLY THE ACTIVE CARD SHOWS, AND CSS DECIDES IT FROM ONE ATTRIBUTE ON THE
   ROOT. The cards carry no directives of their own precisely so the narrow
   layout may re-parent them; a bound element the runtime has hydrated should
   not be moved. */
.stage__card { display: none; }
.stage[data-active="1"] .stage__prop > .stage__card[data-step="1"],
.stage[data-active="2"] .stage__prop > .stage__card[data-step="2"],
.stage[data-active="3"] .stage__prop > .stage__card[data-step="3"],
.stage[data-active="4"] .stage__prop > .stage__card[data-step="4"],
.stage[data-flat="true"] .stage__card,
.stage__step > .stage__card { display: block; }

@media (min-width: 900px) {
	.stage { grid-template-columns: 1fr 1fr; gap: var(--wp--custom--space--4-xl); align-items: start; }
	/* ⛔ THE STEPS NEED SCROLL DISTANCE OR THE STAGE HAS NOTHING TO STAGE. */
	/* ⛔ 70vh, NOT 46vh -- Alpesh chose to lengthen the steps rather than speed
	   the conversations up. The four live scenes run to 9.8s, 9.8s, 12.0s and
	   13.0s, and at 46vh a reader scrolling normally left before Sarah arrived,
	   which is the best beat in the whole section. ⚠ It costs the section about
	   1000px of height and that was the trade he accepted. */
	:root .stage .stage__step { min-height: 70vh; display: flex; flex-direction: column; justify-content: center; }
	/* ⛔ ~~THE LAST STEP NEEDS NO DWELL~~ -- WITHDRAWN 2026-09-22, AND IT WAS
	   MEASURED WRONG. The reasoning was that a step's 46vh exists so the window
	   can be read before the NEXT step claims it, and the fourth has no next
	   step. ⚠ THE READER STILL HAS TO READ IT. Measured at 1440x900, the pinned
	   dwell per step was 560 / 480 / 400 / 80px -- the fourth got FIVE TO SEVEN
	   TIMES LESS than the others, because it was 194px tall against 414px and
	   the pin released the moment the stage ended. Alpesh: "even though the
	   fourth section is not landed, things get scrolled away completely." */

	/* ⭐ AND THE TAIL IS WHAT KEEPS THE PIN ALIVE THROUGH STEP 4. `position:
	   sticky` releases when its CONTAINING BLOCK's bottom arrives, and the
	   containing block is `.stage` -- which used to end at step 4's last line.
	   So the fourth step could never be centred with the window still pinned.
	   The padding is on the STEP LIST, not the stage, so the window's own
	   column is unaffected. ⚠ The value is tuned against the measured dwell
	   table, not chosen: see the plan record for the before and after. */
	:root .stage .stage__steps { padding-bottom: 43vh; }
	/* ⭐ THE PIN. This is the whole of the device, and it is one property. */
	.stage__prop { position: sticky; }

	/* ⭐ THE WINDOW IS NOW A PANEL, AND IT FIXED TWO THINGS AT ONCE. Measured
	   before: the widget painted #ffffff on a #ffffff section -- 1.00:1, no
	   separation at all, the same "it is jelling with the background" Alpesh
	   caught on the WordPress drawing. And the pinned box was 279 / 401 / 502 /
	   516px tall across the four steps, a 237px swing, so the one thing that
	   exists to HOLD STILL was the thing that moved most.
	   ⛔ min-height IS THE HALF THAT MATTERS. A panel that resizes per step is
	      still a moving frame; fixed, the frame holds and only the contents
	      swap, which is the whole device.
	   ⭐ THE GRADIENT IS THE TESTIMONIAL CARDS', RUN THE OTHER WAY. Theirs go
	      pale at the top to deeper at the bottom, because the stars sit at the
	      top. Alpesh asked for the reverse here, so the chat sits on the pale
	      end and the panel has a defined top edge. */
	.stage__prop {
		background-image: linear-gradient(
			to bottom,
			var(--wp--custom--color--a-terra-light),
			var(--wp--custom--color--a-terra-light-2)
		);
		border-radius: var(--wp--custom--rounded--lg);
		padding: var(--wp--custom--space--xl);
		display: flex;
		flex-direction: column;
		justify-content: center;
	}
	/* ⛔ AND THE PANEL NO LONGER CARRIES A min-height. It used to stand 576px
	   tall while step 1's card was 257px, so the window floated in 160px of
	   empty ground above and below. The WINDOW is fixed now, so the panel can
	   simply hug it and the dead bands cannot come back. */

	/* ⭐ THE WINDOW IS SIZED TO THE VIEWPORT, so at least two turns are always
	   in view -- Alpesh: "we can fix the height of the chat widget relative to
	   the view port avaible so that atleat 2 turns are always avisible".
	   ⚠ THE SUBTRACTION IS THE PINNED FURNITURE ABOVE AND BELOW IT: the fixed
	     site header, the pinned section heading, the gap under it, and the
	     panel's own padding top and bottom. `--abk-demo-head` is measured by
	     the view module, so a heading that wraps to two lines shrinks the
	     window rather than pushing it off screen.
	   ⛔ THE CLAMP IS NOT DECORATION. Without a floor, a short laptop window
	      collapses the log to nothing; without a ceiling, a tall monitor makes
	      a chat window taller than any chat window has ever been. */
	.stage__card .wgt {
		max-width: none;
		height: clamp(
			340px,
			calc( 100vh - 48px - var( --abk-demo-head, 140px ) - 24px - 64px - 24px ),
			560px
		);
		display: flex;
		flex-direction: column;
	}
	/* ⛔ overflow:hidden AND NOT auto. The log NEVER scrolls: when a new line
	   will not fit, the player drops the oldest one. A scrollbar here would be
	   a second, competing way to move through the same conversation. */
	/* ⛔ `align-content: start` IS A DESIGN RULING, NOT ONLY A MECHANICAL ONE --
	      ALPESH, 2026-09-23: "the conversation should start from top to bottom
	      always."
	   ⚠ THE AUDIT PROPOSED THE OPPOSITE AND HE REFUSED IT. Measured on beat 4:
	     the log is 446px and the beat ends 121px full, so the copy beside it
	     ("It knows when to fetch a human") sits 162px BELOW the last message.
	     `align-content: end` was painted -- it closes the gap to -71px and the
	     two halves line up -- and he looked at it and chose the white space:
	     "the white space is not much of an issue in this case."
	   ⭐ SO THE PAIRING FAULT IS KNOWN AND ACCEPTED. Do not re-open it by
	      reading the gap as a bug; a bottom-anchored log was tried and lost on
	      a reason that outranks it. A shrink-to-fit panel was painted too and
	      lost harder -- it moves the blank rather than removing it, and it
	      changes the sticky panel's height on every beat. */
	.stage__card .wgt__log {
		flex: 1 1 auto;
		overflow: hidden;
		align-content: start;
	}
}
/* Declared outside the query so the offset is one value in one place; below
   the breakpoint the prop is static and `top` is simply ignored.
   ⚠ AND IT NOW CLEARS THE PINNED HEADER. `--abk-demo-head` is written by the
     view module from the header's MEASURED height, because the heading wraps
     at some widths and a guessed constant would overlap at exactly the widths
     nobody tests. It falls back to 0, which is the pre-pin behaviour. */
.stage__prop { top: calc(48px + var(--abk-demo-head, 0px) + var(--wp--custom--space--lg)); }

/* ⭐ THE PINNED SECTION HEADER. Alpesh, 2026-09-22: "the header should also
   be there. As we scroll, only the right-side image and the left-side content,
   the timeline, should keep changing." Measured before: the heading left the
   viewport at scrollY 5186 in a section running to 7006 -- absent for 80% of
   its own scroll, so steps 2, 3 and 4 played with no title above them.
   ⛔ THE OPAQUE GROUND IS NOT DECORATION. Without it the steps scroll THROUGH
      the letters. It takes the section's own panel colour, so the header does
      not read as a second surface.
   ⚠ AND `.section` IS `overflow: clip`, NEVER `hidden`, WHICH IS WHY THIS CAN
     WORK AT ALL -- `hidden` makes a scroll container, and a scroll container
     is the containing block for every sticky descendant. Section 1 records
     that the prototype audit once reported "0 sticky elements on the page"
     for exactly this reason. */
@media (min-width: 900px) {
	#demo .demo-head {
		position: sticky;
		top: 48px;
		z-index: 3;
		/* ⛔ THE SECTION'S OWN GROUND, MIRRORING THE SECTION'S OWN RULE. It was
		   `--abk-panel`, which IS the tint token -- so the band painted #f9f9f6
		   over a #ffffff section and read as a second surface. `.section--floor`
		   paints `--abk-floor`, so the header has to follow the same fork or
		   the two can disagree again the next time a section changes ground. */
		background: var(--abk-panel);
		/* ⚠ THE TOP PADDING IS THE HEADING'S AIR WHEN IT IS STUCK, and it is
		   NOT pulled back out of the rhythm. It used to be, with a negative
		   margin -- which put the header's opaque box 8px over the eyebrow's
		   last line and made the eyebrow look deleted. Measured: eyebrow
		   238-257, header box starting 249. Letting the padding stand moves
		   the heading down 24px and the eyebrow is clear. ⭐ The cost is that
		   the gap under the eyebrow is 40px here against 16px elsewhere; the
		   gain is an eyebrow you can see. */
		padding-block: var(--wp--custom--space--lg);
	}
	/* ⛔ `#demo` AND NOT `.section--floor`, AND THAT COST A ROUND. The rule
	   above is (1,1,0) because it is scoped by an ID; a `.section--floor`
	   override is (0,2,0) and LOSES to it, so the band kept painting tint and
	   the fix appeared to do nothing. ⭐ An ID in the base rule forces an ID in
	   every override -- the argument against the ID, but the section IS
	   addressed by its anchor everywhere else on this page. */
	#demo:is(.section--floor, .tw\:bg-paper) .demo-head { background: var(--abk-floor); }

	/* ⭐ THE 48px ABOVE THE PINNED HEADING IS COVERED BY THE STEP COLUMN, NOT BY
	   THE HEADING, AND THAT COST A ROUND TOO. The site header is `position:
	   fixed`, a floating pill 64px tall inset 12px, so the strip between the
	   viewport edge and the pinned heading is live page -- the next step's
	   paragraph scrolled through it and two texts stacked.
	   ⛔ MY FIRST FIX WAS AN UPWARD BOX-SHADOW ON THE HEADING, AND IT ATE THE
	      EYEBROW. Any band tall enough to reach the viewport top when stuck
	      also reaches 48px above the heading AT REST, and the eyebrow sits 16px
	      above the heading. Measured: eyebrow 238-257, band reaching 201. It
	      looked deleted, and Alpesh read it as deleted.
	   ⭐ SO THE BAND BELONGS WHERE THE BLEED IS. The text that shows through is
	      the STEP column's, so a sticky strip at the top of the step list
	      covers it -- and it lives far below the eyebrow, where it cannot
	      reach. The negative margin keeps it out of the layout, so the stage
	      is not 48px taller for it. */
	:root .stage .stage__steps::before {
		content: '';
		display: block;
		position: sticky;
		top: 0;
		z-index: 2;
		height: 48px;
		margin: -48px 0 0 -3px;   /* -3px covers the rail's own border */
		background: var(--abk-panel);
	}
	#demo:is(.section--floor, .tw\:bg-paper) .stage__steps::before { background: var(--abk-floor); }
	/* ⛔ THE HEADER OUTRANKS THE RAIL AND THE WINDOW, and both need saying.
	   The rail is a 3px border on a list that scrolls under the header, and
	   the pinned window sits in the same stacking context. */
	.stage__prop { z-index: 1; }
}

@media (max-width: 899px) {
	/* ⛔ Below the breakpoint every step is lit, because without the pin
	   there is nothing for a dim step to be quieter THAN. */
	.stage .stage__step { opacity: 1; }
	/* ⛔ AT 390 IT UNPINS AND BECOMES THE STACK IT EXISTS TO AVOID. We accept
	   the loss rather than fake it: a sticky window in a single column has
	   nothing to stay still against. */
	.stage__prop { position: static; }
	.stage__step { opacity: 1; }
	/* ⛔ AND THE RAIL GOES WITH THE STATE. Without the pin the rail marks
	   nothing, and a decorative vertical line down a phone is a side-stripe,
	   which the law refuses by name. */
	:root .stage .stage__steps,
	:root .stage .stage__step { border-left: 0; padding-left: 0; margin-left: 0; }
	:root .stage .stage__step > .stage__card { margin-top: var(--wp--custom--space--lg); }
	.stage__prop:empty { display: none; }
}

/* ⛔ WITH THE PREFERENCE SET, NOTHING MOVES -- and "nothing moves" is only
   half the rule. "Nothing is MISSING" is the other half, which the view
   module enforces by lighting every step and showing every card. */
@media (prefers-reduced-motion: reduce) {
	:root .stage .stage__step { transition: none; }
	/* ⛔ NOTHING PLAYS, SO NOTHING RISES AND NOTHING FILLS. The whole transcript
	   is already shown by the view module; an arrival animation on a line that
	   was never absent is motion for its own sake, and a progress bar for a
	   recording that is not running is a lie. */
	/* ⚠ `:not([hidden])` HAS TO BE REPEATED, or this loses. The rule it
	   overrides is (0,3,0) -- two classes plus the attribute inside `:not` --
	   and a bare `> *` is (0,2,0). Measured: reduced motion still reported
	   `abk-msg-in 0.32s`. Equal specificity, later in the file, so it wins. */
	.wgt--play .wgt__log > *:not( [ hidden ] ) { animation: none; }
	.wgt__prog { display: none; }
}

/* ---------------------------------------------------------------------------
   18 · THE INDUSTRY SIMULATOR — U7, and the largest object on the page.
   --------------------------------------------------------------------------- */
/* ⚠ 26px, WHICH IS HALF THE CONTROL. Alpesh, 2026-09-22: "this tab swticher we
   should move up a bit so that is half sits inside and half outsite as
   earlier." The control is 52px tall, so half of it is 26 -- the block reserves
   exactly that above the panel and the other half sits on the site.
   ⭐ It still costs the frame far less than the old full row did: 26px against
      52, because only half of it is outside now. */
.chromed { position: relative; padding-top: 26px; }
:root .section .chromed { margin-top: var(--wp--custom--space--xl); }

/* ⛔ NO BROWSER BAR. Every product-depiction reference refuses one by name, so
   the chrome is a segmented pill row sitting half-outside the panel's top
   edge rather than three window dots and five fake tabs.
   ⭐ AND IT LIFTS. The edge shadow alone left it at 1.055 against the panel;
   it is FLOATING CHROME, and floating chrome is one of the shapes the shadow
   budget already counts. */
/* ⭐ AND IT MOVED TO THE LEFT AND ONTO THE EDGE. Alpesh, 2026-09-22: "at the
   top, we have this switcher tab, and that is also restricting the height of
   the chat widget from becoming bigger" -- so it stopped being a full row above
   the panel; then "we should move up a bit so that is half sits inside and half
   outsite as earlier" -- so it straddles rather than floating clear.
   ⚠ `top: 0` WITH THE BLOCK'S 26px OF PADDING IS WHAT MAKES IT HALF AND HALF:
     the control runs 0-52 and the panel starts at 26. */
.chromed__ctl {
	position: absolute;
	top: 0;
	left: var(--wp--custom--space--xl);
	z-index: 7;
	display: flex;
	gap: var(--wp--custom--space--xs);
	padding: var(--wp--custom--space--xs);
	border-radius: var(--wp--custom--rounded--full);
	background: var(--abk-panel);          /* ⛔ opaque -- translucency refused */
	box-shadow: var(--wp--custom--shadow--edge), var(--wp--custom--shadow--depth);
	max-width: calc(100% - var(--wp--custom--space--2-xl));
	overflow-x: auto;
	scrollbar-width: none;
}
/* ⛔ ON A TINTED SECTION THE CONTROL TAKES THE FLOOR, or it is the same colour
   as the panel behind it and stops reading as a separate object. */
/* ⚠ `.tw\:panel` too (2026-09-27): the Tailwind chassis is a tint panel unless
   it carries `tw:bg-paper`, and the control bar must stay the floor on it. */
:is(.section:not(.section--floor), .tw\:panel:not(.tw\:bg-paper)) .chromed__ctl { background: var(--abk-floor); }
.chromed__ctl::-webkit-scrollbar { display: none; }
@media (max-width: 720px) {
	/* At 390 the control scrolls, with a fade at both ends. */
	.chromed__ctl { mask-image: linear-gradient(to right, transparent 0, #000 5%, #000 95%, transparent 100%); }
}
/* ⛔ A TAB IS A REAL CONTROL, so the 44px floor applies to it. */
.chromed__tab {
	flex: none;
	border: 0;
	cursor: pointer;
	font: inherit;
	min-height: 44px;
	padding: 0 16px;
	border-radius: var(--wp--custom--rounded--full);
	background: transparent;
	color: var(--wp--custom--color--secondary);
	font-size: var(--wp--custom--step--label);
	font-weight: var(--wp--custom--weight--emphasis);
	line-height: var(--wp--custom--leading--body);
	letter-spacing: var(--wp--custom--tracking--text);
	white-space: nowrap;
	transition: background .16s ease, color .16s ease;
}
.chromed__tab:hover { color: var(--wp--custom--color--heading); }
/* ⭐ THE SELECTED TAB TAKES THE SAME RAMP AS THE CARD, so the switcher and the
   card read as one system rather than two dark things that happen to differ.
   ⚠ It was a flat `ink`; the ramp is a step lighter, which is why the label
     stays `ink-heading` rather than dropping to `ink-body`. */
.chromed__tab[aria-selected="true"] { background: var(--wp--preset--gradient--g-neutral); color: var(--wp--custom--color--ink-heading); }
@media (prefers-reduced-motion: reduce) { .chromed__tab { transition: none; } }

.chromed__panel {
	position: relative;
	border-radius: var(--wp--custom--rounded--lg);
	overflow: hidden;                      /* clips the simulated site */
	background: var(--abk-floor);
	box-shadow: var(--wp--custom--shadow--depth);
}

/* ⭐ THE FRAME TAKES WHAT THE VIEWPORT LEAVES. Alpesh, 2026-09-22: "can we keep
   it height relative to viewports, as the header above it takes all the
   available viewport height?" ⚠ THE SUBTRACTION IS THE FURNITURE ABOVE AND
   BELOW IT, measured on the rendered page: the fixed site header, this
   section's heading (53px) and lead (67px), the section's own padding and the
   note beneath. ⛔ THE CLAMP IS NOT DECORATION -- without a floor a short laptop
   window leaves no site to look at, and without a ceiling a tall monitor makes
   a frame nobody can take in at once. */
.sim { position: relative; overflow: hidden; height: clamp(460px, calc(100vh - 205px), 780px); background: var(--abk-floor); }
/* ⭐ 205px, NOT 316 (U33 Q6, Alpesh, 2026-10-03: "it should take the available
   viewport height after leaving breathing space"). Measured on the page: the
   fixed header ends at 72px, 24px of air, the tabs over the frame's top 26px,
   the caption under it 32 + 19px, and 32px of air at the foot -- 205px. At
   1440 x 900 the frame grew 584 -> 695px; the 780px ceiling stays (his "A"). */
/* ⚠ 26px AGAIN. The control straddles the panel's top edge, so its lower half
   lands on the site -- and every simulated site puts its own logo at top left,
   exactly where the control now sits. The site starts below it. `SITE_TOP` in
   the view module matches this. */
/* ⚠ `max-width: none` because the frame is SCALED, not fitted. It is authored
   at 1280px and transformed down; a max-width of 100% would clamp it to the
   panel first and the scale would then shrink an already-shrunk site.
   Measured at 390: 334px wide against the prototype's 1040. */
:root .sim .sim__frame { position: absolute; left: 0; top: 26px; border: 0; display: block; max-width: none; transform-origin: top left; background: var(--abk-floor); }

/* ⛔ THE CAPABILITY STRIP IS OPAQUE, NOT GLASS. Translucency is refused, and
   the reason is measured: glass only shows over something colourful, and our
   floor is plain by choice. */
/* ⭐ A CARD, NOT A BAND, AND IT LIVES ON THE SITE. It used to span the panel's
   full width along the bottom; it now sits bottom-LEFT facing the widget, so
   the site carries two cards -- the assistant, and what it does here. They are
   a pair, both change with the tab, and both survive a full-height frame,
   which a band stacked above the frame would not. */
/* ⭐ THE WRAPPER IS `display: contents` ABOVE 720px, SO NOTHING MOVES THERE.
   The strips left `.sim` in the markup on 2026-09-23; `contents` makes the
   wrapper vanish from the box tree, so each card still positions against
   `.chromed__panel` -- which is `position: relative` and the same box `.sim`
   occupied. ⛔ MEASURED AT 1440 BEFORE AND AFTER THE MOVE: identical. */
.sim__strips { display: contents; }
.sim__strip {
	position: absolute;
	left: var(--wp--custom--space--md);
	bottom: var(--wp--custom--space--md);
	width: min(330px, 46%);
	z-index: 4;
	pointer-events: none;
	display: grid;
	gap: 2px;
	padding: var(--wp--custom--space--md) 0 var(--wp--custom--space--sm);
	border-radius: var(--wp--custom--rounded--md);
	/* ⚠ THE CARD STAYS ON THE PANEL COLOUR, AND THAT WAS A ROUND TRIP. It was
	   built on `g-neutral` after he asked to see it -- "let's implement the dark
	   neutral gradient. Let me see. If I don't like it, we will switch back" --
	   and he switched back: "switch back from the capability card to the
	   previous white BG, and just keep the green bullet points."
	   ⭐ THE RAMP STAYED ON THE SELECTED TAB, which is the half he kept. The
	      two variations remain drawn on the canvas either way. */
	/* ⭐ WHITE AND FLOATING (U33 Q8, Alpesh: "A"). The card is an
	   illustration, and no object takes olive: it was `tint` with the `depth`
	   shadow. It now floats on the same edge as the widget. His green bullets
	   stay (his pick, above). */
	background: var(--abk-floor);
	box-shadow: var(--wp--custom--drawing--shadow--floating);
}

/* ⛔ NO TRACKING, AND check-law.js CAUGHT IT. I gave this kicker an uppercase
   0.12em, which is the DISPLAY tier, on text that renders at 11px -- and the
   guard reads tracking off the RENDERED SIZE, never the tag: "p.sim__strip-k
   at 11.0px tracks 0.1200, its tier is 0". ⭐ It took the size token and the
   text tracking that goes with it, so the label is on the ladder like
   everything else. */
.sim__strip-k {
	margin: 0 var(--wp--custom--space--md) 6px;
	font-size: var(--wp--custom--step--label);
	font-weight: var(--wp--custom--weight--emphasis);
	letter-spacing: var(--wp--custom--tracking--text);
	color: var(--wp--custom--color--secondary);
}
/* ⛔ MUST FOLLOW THE `display: grid` ABOVE. A class rule outranks the browser's
   own `[hidden] { display: none }`, so without this every strip paints at
   once and all five stack on top of each other. */
.sim__strip[hidden] { display: none; }
/* U33 Q8: the card's quiet text in the product's grey (#6B7280, 4.83 : 1 on
   white), not the olive `secondary`. Above 720px only -- below it the card has
   no ground of its own, and that layout was not part of the question. */
@media (min-width: 721px) {
	/* (0,2,x): `.sim__snip span` is declared later at the same weight */
	.sim__strip .sim__strip-k, .sim__strip .sim__snip span { color: var(--wp--custom--drawing--color--muted); }
}
/* ⚠ 6px, NOT 11px, and NO divider. The snips sat side by side in a row and
   carried a vertical hairline between them; stacked, that rule drew a line
   down the left of every line after the first. */
/* ⛔ `display: contents` SO THE DESKTOP PANEL IS GENUINELY UNCHANGED. The
   wrapper exists only to be the scroll container on a phone. `.sim__strip` is
   a GRID with a 2px gap, and a grid's gap applies between ITS OWN children --
   so boxing the three snips turned three grid items into one and the two gaps
   disappeared. ⚠ MEASURED AT 1440: the strip went 212.31px -> 208.31px and the
   three lines sat flush. ⭐ FOUND BY A SEPARATE VERIFIER, against a comment of
   mine that claimed the wrapper was inert at desktop. It was not. */
.sim__snips { display: contents; }
.sim__snip { padding: 6px var(--wp--custom--space--md); }
.sim__snip b {
	display: block; position: relative; padding-left: 14px;
	font-size: var(--wp--custom--step--label);
	font-weight: var(--wp--custom--weight--emphasis);
	line-height: var(--wp--custom--leading--body);
	letter-spacing: var(--wp--custom--tracking--text);
	color: var(--wp--custom--color--heading);
}
.sim__snip b::before {
	content: ""; position: absolute; left: 0; top: 0.55em;
	width: 5px; height: 5px; border-radius: var(--wp--custom--rounded--full);
	background: var(--wp--custom--color--brand);
}
.sim__snip span {
	display: block; padding-left: 14px; margin-top: 2px;
	font-size: var(--wp--custom--step--label);
	line-height: var(--wp--custom--leading--body);
	letter-spacing: var(--wp--custom--tracking--text);
	color: var(--wp--custom--color--secondary);
}
@media (max-width: 720px) {
	/* ⛔ 340px MADE THE ASSISTANT UNUSABLE, AND THE BULLETS WERE NOT THE CAUSE.
	   Alpesh, 2026-09-24: "the chat widget height has become too small. It is
	   not usable. The reason for that is these three bullet points that are
	   coming below." ⚠ MEASURED AT 390 AND THE CAUSE IS ELSEWHERE: the widget
	     is `position: absolute` INSIDE `.sim`, so it never shared space with
	     anything below it. `.sim` was 340px, the widget was capped at 220px,
	     and `.wgt__log` came out at 46px -- ONE LINE. Freeing the bullets would
	     have shortened the section and left the conversation exactly as it was.
	   ⭐ SO THE TWO NUMBERS MOVE, AND HE RE-MADE THE TRADE THEY ENCODE. The cap
	      existed so the fake shop stayed visible behind the widget -- the shop
	      IS the argument. He chose the conversation: about 80px of shop instead
	      of 120, and a log near 246px instead of 46. */
	.sim { height: 520px; }
	/* ⭐ NOW IT REALLY IS A LIST UNDER THE FRAME. The wrapper stops being
	   `contents` and becomes the block the cards sit in, AFTER `.sim` -- which
	   is what the old rule intended and could not reach from inside `.sim`. */
	.sim__strips { display: block; padding: var(--wp--custom--space--md); }
	.sim__strip { position: static; width: auto; padding: 0; background: none; box-shadow: none; pointer-events: auto; }
	/* ⭐ THE HEADING COMES BACK. It was hidden because the list had no panel of
	   its own to head; below the frame it does, and without it three bullets
	   arrive with nothing saying what they are. */
	.sim__strip-k { display: block; margin: 0 0 var(--wp--custom--space--xs); }
	.sim__snip { padding-inline: 0; }
	/* ⛔ THE THREE LINES SCROLL SIDEWAYS, AS THE TESTIMONIALS DO. Alpesh,
	   2026-09-24: "instead of showing these three bullet points vertically, we
	   can show them as scrollable cards horizontally, as we have done for the
	   testimonial section."
	   ⚠ IT IS NOT WHAT FIXES THE WIDGET, AND HE KNOWS THAT NOW -- see `.sim`
	     above. It is worth doing on its own: three stacked bullets cost a third
	     of a screen and a scrolled row costs one line of it.
	   ⭐ THE DEVICE IS `.drift`'s, NOT A NEW ONE. Same flex row, same hidden
	      scrollbar, same TRAILING-ONLY mask -- a leading fade ate the first
	      character of every row when `.drift` was built, and it would here too.
	   ⚠ THE HEADING STAYS PUT. Only the snips move, so `.sim__strip` becomes a
	     grid whose second row is the scroller; the heading is not a card. */
	.sim__strip { display: grid; gap: var(--wp--custom--space--xs); }
	.sim__snips {
		display: flex;
		/* ⚠ overrides the `display: contents` the wrapper carries by default */
		gap: var(--wp--custom--space--md);
		overflow-x: auto;
		scrollbar-width: none;
		-webkit-mask-image: linear-gradient(to right, #000 0%, #000 90%, transparent 100%);
		mask-image: linear-gradient(to right, #000 0%, #000 90%, transparent 100%);
	}
	.sim__snips::-webkit-scrollbar { display: none; }
	/* ⚠ 78% AND NOT 100%: the next card must be visibly cut, or nothing says
	   the row scrolls. `.drift` uses 82% against wider cards. */
	.sim__snips > .sim__snip { flex: 0 0 78%; }
	/* ⭐ THE OVERFLOW IS VISIBLE NOW. The strip scrolls -- 572px of tabs in a
	   286px box, measured at 390 -- and said nothing, so two of the five
	   industries were unreachable unless a reader guessed. ⛔ A mask, because
	   the law already names one: a cut edge is a mask-image, never a gradient
	   overlay painted in the ground colour. */
	.chromed__ctl {
		-webkit-mask-image: linear-gradient( to right, #000 calc(100% - 32px), transparent );
		mask-image: linear-gradient( to right, #000 calc(100% - 32px), transparent );
	}
}

/* ⭐ OUR PRODUCT, FLOATING ON THEIR SITE. It is `.wgt`, unchanged. */
/* ⚠ NO `width` OF ITS OWN. The prototype declares 336px here and it is DEAD:
   `.wgt` is defined later in that stylesheet and its `width: 100%` wins, so
   the widget paints at its 380px cap. Measured, and the paint is what Alpesh
   approved. */
/* ⭐ IT FILLS THE FRAME IT ALREADY SAT IN. Measured before this change: the
   widget was 380x322 inside a 568px frame -- 246px of slack, unused, while the
   tab row above and the band below were being blamed for the squeeze. Top and
   bottom now, so it takes the frame's height and grows with the viewport.
   ⛔ ~~AND IT OVERLAPS THE SITE'S RIGHT EDGE~~ -- WITHDRAWN, AND IT NEVER
      WORKED. I hung it 16px past the edge so it would read as being IN FRONT
      without a scrim, reported the overhang as built, and had measured only
      the BOX: getBoundingClientRect() returns the geometry whether or not the
      pixels paint. ⛔ `.chromed__panel` is `overflow: hidden`, so the overhang
      was CLIPPED -- the widget simply had its right edge cut off. Alpesh saw
      it: "the chat widget right edge is outside the frame it is getting cuttof,
      it should move in a it liitle bit of maign on right".
   ⚠ A BOX IS NOT A PAINT, and that is the third time this class of mistake has
     cost a round today.
   ⭐ SO THE LIFT IS THE SHADOW AND THE RING, and nothing else: the tight
      three-layer pair deepened one step, which is the drawing's own lift
      language rather than a new one. */
/* ⛔ IT HUGS ITS CONTENT AND IS CAPPED -- IT IS NOT STRETCHED. 2026-09-23.
   ⚠ `top` AND `bottom` TOGETHER STRETCHED IT TO THE FRAME: measured at 1440,
     400x732 holding a greeting bubble, three chips and a composer -- about
     600px of blank white in the middle of the page's most-looked-at object.
     The rule that did it was written to stop the OPPOSITE fault, a widget
     sitting in 246px of unused slack, and it overshot.
   ⭐ ALPESH ASKED FOR A MAX HEIGHT AND THE MAX HEIGHT ALONE DOES NOTHING --
      520px and 440px were both painted and NEITHER BOUND, because a stretched
      box is already shorter than any cap you would want. Releasing `top` is
      what collapses it: measured 732 -> 319, with no constant invented.
   ⭐ THEN THE CAP DOES THE JOB HE MEANT IT FOR. A conversation grows the log,
      and this stops the widget outgrowing the frame it floats on. */
.sim__widget {
	position: absolute;
	right: var(--wp--custom--space--lg);
	/* ⭐ THE WIDGET FILLS THE FRAME (U33 Q7, Alpesh: "the objective of
	   increasing the height was to increase the widget height too"). Its top
	   clears the lowest demo-site menu bar -- 104px under the frame's top,
	   measured on all five sites at 1440 and 1920 (89-94px at 1280) -- by 32px.
	   ⚠ This releases the content-sized widget below; the bubbles still do not
	   stretch (`align-content: start` on the log). Below 720px the stated 420px
	   height and `top: auto` still win. */
	top: calc( 104px + var(--wp--custom--space--xl) );
	bottom: var(--wp--custom--space--lg);
	width: min(400px, 46%);
	z-index: 6;
	display: flex;
	flex-direction: column;
	/* ⭐ ONE EDGE (U33 Q5): it FLOATS over the customer's site -- the soft
	   stack, no ring. It had a large shadow and a 1px ring. */
	box-shadow: var(--wp--custom--drawing--shadow--floating);
	color: #1A1A1A;
}
/* ⛔ `max-width: none` OR IT PAINTS AT 380. `.wgt` caps itself at 380px and is
   declared later in this file, so the width above would be ignored. */
:root .sim .sim__widget { max-width: none; }
/* the conversation takes whatever the bar, the chips and the footer leave.
   ⛔ `align-content: start` OR EVERY BUBBLE STRETCHES. `.wgt__log` is a GRID,
   and a grid's default `align-content` is `stretch` -- so with the log given a
   height of its own, one greeting filled the entire window and the user's first
   question came out as a green slab. Alpesh: "the first greeting bubble is too
   big. There is a lot more white space there." ⭐ Rows now take their content's
   height and stack from the top, which is also where a chat writes. */
.sim__widget .wgt__log { flex: 1 1 auto; overflow-y: auto; align-content: start; }
/* ⭐ THE BAR TAKES THE BRAND GREEN, AND THIS IS THE SECOND TIME HE CHOSE IT.
   U33 Q5 made it white (the product's default face). On the real page, he
   then saw it melt into four of the five sites, whose top areas are white or
   near-white: "The chat widget is not standing out against the white-ish
   backgrounds of other websites ... we should have a highlight color there.
   Like the earlier brand green" (U33 Q11, 2026-10-03: "A", the simulator
   only). The earlier rules, restored exactly; the rest of Q5 stays.
   ⭐ STILL A TRUE PRODUCT PICTURE: the product sets the header colour per bot.
   ⛔ SCOPED TO THE SIMULATOR'S WIDGET. The demo stage keeps its white bar: it
      stands on its own peach panel. This one sits on a CUSTOMER'S SITE and
      has to be unmistakably ours. White on brand green measures 4.50 : 1. */
:root .sim__widget .wgt__bar {
	background: var(--wp--custom--color--brand);
	color: var(--wp--custom--color--paper);
	box-shadow: none;
}
/* ⭐ THE ROLE IS THE NAME NOW, so it stops being the quiet half of a pair. */
:root .sim__widget .wgt__role { font-weight: var(--wp--custom--weight--emphasis); color: var(--wp--custom--color--paper); }
/* ⚠ AND THE DISC INVERTS, or a brand-green disc on a brand-green bar vanishes. */
:root .sim__widget .wgt__av { background: oklch(1 0 0 / 0.22); color: var(--wp--custom--color--paper); }
/* the initial disc, in place of the green "live" dot */
.wgt__av {
	flex: none;
	width: 28px;
	height: 28px;
	border-radius: var(--wp--custom--rounded--full);
	background: var(--wp--custom--color--brand);
	color: var(--wp--custom--color--paper);
	display: grid;
	place-items: center;
	font-size: 12px;
	font-weight: var(--wp--custom--weight--emphasis);
}
@media (max-width: 720px) {
	/* ⛔ BELOW 720 IT IS AN OVERLAY AGAIN, not a full-height column: the site
	   must stay visible behind it, because the site IS the argument. */
	/* ⛔ `height`, NOT `max-height`. A cap only stops the box GROWING, and this
	   widget is sized by its content, so raising the cap from 220 to 420 moved
	   it to 291px -- the height of the conversation so far, not the height of a
	   window. ⚠ MEASURED: the log went 46 -> 117px and stopped, because nothing
	     was asking for the rest. ⭐ A stated height makes the log's `flex: 1`
	     meaningful, and the window stops resizing as lines arrive. */
	.sim__widget { left: var(--wp--custom--space--sm); right: var(--wp--custom--space--sm); top: auto; bottom: var(--wp--custom--space--sm); width: auto; height: 420px; max-height: calc(100% - var(--wp--custom--space--md)); }
	/* ⛔ The site must stay visible behind the widget -- it IS the argument. */
	/* ⚠ NO `max-height` ANY MORE. The widget has a stated height, so `flex: 1`
	   on the log gives it exactly what the bar, the chips and the input row do
	   not use -- about 246px at 390. A second cap here would fight it. */
	/* One scrolling row, not three stacked.
	   ⛔ `flex: none` SO THE CONTROL ROW IS NEVER SQUASHED. The widget is a
	   fixed 220px column, so the browser shrinks whatever it can -- and it
	   was shrinking the CHIPS, to 15.4px tall against their natural 30.5px.
	   ⚠ The prototype has the same fault; measured there too. A conversation
	   can scroll, a control cannot be compressed to half its height. */
	.sim__widget .wgt__chips { flex: none; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; mask-image: linear-gradient(to right, #000 0, #000 88%, transparent 100%); }
	.sim__widget .wgt__chips::-webkit-scrollbar { display: none; }
	.sim__widget .wgt__chip { flex: none; }
}

/* ⛔ THE MESSAGE'S SIDE COMES FROM A CONTEXT VALUE, NOT A BOUND CLASS. A
   per-element class bind needs a derived value and PHP cannot evaluate one,
   so `data-who` carries it -- a plain context value the server resolves. The
   `--bot` / `--user` classes stay for the four drawn widgets elsewhere. */
.wgt__msg[data-who="bot"],
.wgt__msg[data-who="typing"] { background: var(--wp--custom--drawing--color--bubble); color: #1A1A1A; justify-self: start; border-bottom-left-radius: var(--wp--custom--rounded--sm); }
.wgt__msg[data-who="typing"] { color: var(--wp--custom--drawing--color--muted); }   /* the dots are currentColor */
.wgt__msg[data-who="user"] { background: var(--wp--custom--color--brand); color: var(--wp--custom--color--paper); justify-self: end; border-bottom-right-radius: var(--wp--custom--rounded--sm); }
/* The capture line is the widget's own state row rather than a message. */
.wgt__msg[data-who="state"] {
	/* U33 Q5: the product's greys -- white, one line, ink */
	background: var(--abk-floor);
	box-shadow: inset 0 0 0 1px var(--wp--custom--drawing--color--line);
	color: #1A1A1A;
	justify-self: stretch;
	max-width: none;
	border-radius: var(--wp--custom--rounded--sm);
	font-size: 12px;
	font-weight: var(--wp--custom--weight--emphasis);
	display: flex; align-items: center; justify-content: space-between; gap: var(--wp--custom--space--sm);
}
.wgt__msg span[data-w="b"] { font-weight: var(--wp--custom--weight--display); }
.wgt__msg span[data-w="i"] { font-style: italic; }
/* ⛔ THE DEMO IS LABELLED AS A DEMO, INSIDE THE DRAWING. A bare "Lead
   captured" line with nothing sent anywhere is a claim about an OUTCOME. */
.wgt__msg span[data-w="tag"] { font-size: 11px; font-weight: var(--wp--custom--weight--body); color: var(--wp--custom--drawing--color--muted); flex: none; }

/* ⛔ THE CHIPS DRIVE STATE, so they ARE controls -- unlike the drawn footer
   beneath them, which is part of the picture. */
.wgt__chips { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 var(--wp--custom--space--md) var(--wp--custom--space--md); }
/* ⛔ 44px, AND THE GUARD IS THE REASON. These chips drive state, so they ARE
   controls, and `ops/measure/check-law.js` reads every control against a 44px
   floor in both directions. At their old 5px padding they measured about 30px
   and only escaped because the guard exempts what is inside a drawing -- which
   is the wrong exemption for something you click. */
.sim__widget .wgt__chip { min-height: 44px; padding-block: 0; }
.wgt__chip {
	/* ⛔ `font-family: inherit` OR THE CHIP PAINTS IN ARIAL. A `<button>` does
	   not inherit the family, and the prototype set it through the `font:`
	   shorthand -- which I split into longhands and lost. ⚠ Measured on the
	   rendered chip: `font-family: Arial`, and the text came out 4px narrower,
	   which changed how the row wrapped. ⭐ The same class of defect as the
	   impostor font faces in U2: the size and weight were right and the
	   LETTERFORMS were not. */
	font-family: inherit;
	font-size: var(--wp--custom--step--label);
	font-weight: var(--wp--custom--weight--emphasis);
	line-height: var(--wp--custom--leading--body);
	letter-spacing: var(--wp--custom--tracking--text);
	padding: 5px 11px;
	border-radius: var(--wp--custom--rounded--full);
	border: 0;
	cursor: pointer;
	/* ⭐ THE PRODUCT'S CHOICE PILL (U33 Q5; `.aibk-chat-choice-pill`,
	   widget-tokens.css): white, a line of the brand at 35% on white, ink
	   text, the brand at 6% on hover. */
	background: var(--abk-floor);
	color: #1A1A1A;
	box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--wp--custom--color--brand) 35%, #fff);
}
.wgt__chip:hover { background: color-mix(in srgb, var(--wp--custom--color--brand) 6%, #fff); }
.wgt__chip.is-off { display: none; }

/* The three dots while it thinks. */
.wgt__msg[data-who="typing"] { display: flex; align-items: center; gap: 5px; min-height: 22px; }
.wgt__msg[data-who="typing"]::before,
.wgt__msg[data-who="typing"]::after,
.wgt__msg[data-who="typing"] > i {
	content: ""; width: 5px; height: 5px; border-radius: var(--wp--custom--rounded--full);
	background: currentColor; animation: abk-wgt-dot 1.2s infinite ease-in-out;
}
.wgt__msg[data-who="typing"]::after { animation-delay: .15s; }
@keyframes abk-wgt-dot {
	0%, 60%, 100% { opacity: .25; transform: translateY(0); }
	30%           { opacity: 1;   transform: translateY(-2px); }
}
@media (prefers-reduced-motion: reduce) {
	.wgt__msg[data-who="typing"]::before,
	.wgt__msg[data-who="typing"]::after { animation: none; opacity: .55; }
}

/* ---------------------------------------------------------------------------
   19 · THE PROBLEM STACK — aibotkit/problem-stack, page wordpress-lead-generation.
   Plan U5, KTD3. Three scripted scenarios in a swiping stack of windows,
   ported from the marketing source and re-tokened.
   --------------------------------------------------------------------------- */
/* ⛔ THIS SECTION MUST STAY BELOW SECTION 18. `.sim` is the industry
   simulator's root, and section 18 gives it a viewport-height clamp,
   `overflow: hidden` and the floor -- and a 520px height again inside a
   720px media query. `.sim--stack` resets all three, and a reset must come
   AFTER every rule it resets (gotcha 74), media queries included.
   ⭐ `.sim` IS KEPT AS THE ROOT ON PURPOSE (plan KTD3, user-approved): it is
      one of the law's six drawing roots, so the drawn Google, Instagram and
      WhatsApp chrome is under the Drawn-Chrome Rule without a seventh root.
   ⛔ CONTRAST AND OUR TOKENS STILL BIND INSIDE IT. Every text colour below
      was measured against its own ground at 4.5:1 or better. A third party's
      colour stays a literal only where it is THEIRS (Google blue, the
      Instagram ring, WhatsApp green); a grey that failed 4.5:1 was darkened,
      and anything that equals one of our tokens is the token. */
:root .sim.sim--stack { height: auto; overflow: visible; background: transparent; }
@media (max-width: 720px) {
	:root .sim.sim--stack { height: auto; }
}

/* ⛔ BELOW 900px THE DRAWING GOES FIRST, as every split's figure does
   (section 9b: "show the mechanism before the claim"), and as the source's
   own panel did (`order: -1` at 960px). The block root is a div, not a
   figure, so the 9b rule cannot reach it by itself. */
@media (max-width: 899px) {
	.split > .pstack { order: -1; }
}

.pstack { position: relative; min-width: 0; width: 100%; max-width: 500px; margin-inline: auto; }

/* ⭐ THE ANIMATION'S OWN PANEL, WHOLE AND CENTRED -- Alpesh, 2026-09-25: "I
   don't want it to be cut on the edge … I want it centered like in our
   original HTML." The pause control and the caption row sit inside it.
   ⭐ THE LIGHT TERRACOTTA GRADIENT, a-terra-light-2 to a-terra-light, since
   2026-09-26: Alpesh picked it by looking over the dark neutral and lime,
   once the features took the dark neutral. So the page parts of the panel --
   the caption, the pips, the pause button -- take INK (heading on the deeper
   stop: 15.33 by the tokens). The windows inside keep their own (the
   Drawn-Chrome Rule). */
.pstack { background: linear-gradient(150deg, var(--wp--custom--color--a-terra-light-2) 0%, var(--wp--custom--color--a-terra-light) 100%);
	border-radius: var(--wp--custom--rounded--lg);
	padding: 20px 12px 16px; }
@media (min-width: 900px) {
	.pstack { max-width: none; padding: 44px 44px 28px; }
	.pstack .sim--stack .cap, .pstack .sim--stack .pips { padding-right: 60px; }
}
/* ⭐ LARGER FROM 1408px, BY ZOOM, SO THE TEXT PAINTS LARGER AND STAYS SHARP.
   The stack is drawn at 500px in px units; `zoom: 1.3` paints it 650px wide,
   its 9px address text at about 12px (critique round 1: 7-11px was too small).
   ⛔ A FIXED 500px, NEVER A PERCENTAGE. Measured in Chromium 151: under
      `zoom` a percentage width is not enlarged, so `calc(100% / 1.3)` cancelled
      the zoom out and the window painted 503px.
   ⛔ 1408px, NOT 1280px. The column only reaches its full width at a 1408px
      viewport; at 1280 the panel's inner width was 555px and the 650px window
      ran past its edge -- the exact cut Alpesh refused. Checked at eight widths
      from 390 to 1920: the window stays inside the panel at every one. */
@media (min-width: 1408px) {
	.pstack .sim--stack .bstack { zoom: 1.3; width: 500px; max-width: none; margin: 0 auto; }
}
/* ⭐ THE CAPTION AT BODY-SMALL IN THE HEADING INK: "One question, two days too
   slow" needed more contrast and a larger size (Alpesh, 2026-09-26). On the
   light terracotta: 15.33 : 1 on the deeper stop, by the tokens.
   14px is the TEXT tracking tier, so the label tracking leaves with the size. */
.pstack .sim--stack .cap {
	color: var(--wp--custom--color--heading);
	font-size: var(--wp--custom--step--body-small);
	letter-spacing: var(--wp--custom--tracking--text);
}
.pstack .sim--stack .pip   { background: color-mix(in oklab, var(--wp--custom--color--heading) 18%, transparent); }
.pstack .sim--stack .pip i { background: var(--wp--custom--color--heading); }
.pstack .pstack__toggle {
	color: var(--wp--custom--color--heading);
	background: transparent;
	box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--wp--custom--color--heading) 25%, transparent);
}
/* ⭐ AND LARGER ON A PHONE, 1.15x, THE PANEL TAKING THE SECTION'S FULL WIDTH.
   At 390 the stack's text painted 6.5-9px and "We aim to reply within 48
   hours" 9.5px (critique round 2). Alpesh picked 1.15x over 1.3x by looking,
   2026-09-25: the scene keeps more of the page behind the form.
   ⭐ HERE THE PERCENTAGE WIDTH IS WANTED. Under `zoom` the stack's 100% is
      not enlarged, so the window stays the panel's width and draws its
      content at width / 1.15 -- 311px at 390, the width it drew before.
      The panel's 48px of bleed pays for the zoom.
   ⚠ THE BLEED IS THE SECTION'S OWN GUTTER, `clamp(20px, 5vw, 64px)`: 20px
     at 390 and 360, 31px at 619. */
@media (max-width: 619px) {
	.pstack { margin-inline: calc( var(--wp--custom--width--gutter) * -1 ); width: auto; max-width: none; padding-inline: 8px; }
	.pstack .sim--stack .bstack { zoom: 1.15; }
}

/* ⛔ THE PAUSE CONTROL IS A PAGE BUTTON, OUTSIDE THE DRAWING ROOT. `.btn`
   gives it 44px in both directions and 8px; it sits level with the caption
   row, which is 44px tall for exactly that reason. */
.pstack__toggle { position: absolute; right: 12px; bottom: 12px; padding: 0; width: 44px; }
@media (min-width: 900px) { .pstack__toggle { right: 24px; bottom: 20px; } }
.pstack__toggle[hidden] { display: none; }
.pstack__toggle .ico { stroke-width: 1.5; }
.pstack__toggle .pstack__ico--play { fill: currentColor; }
.pstack__toggle .pstack__ico--play,
.pstack__toggle[data-state="paused"] .pstack__ico--pause { display: none; }
.pstack__toggle[data-state="paused"] .pstack__ico--play { display: block; }

.sim--stack {
	--ps-ink: var(--wp--custom--color--ink);
	--ps-paper: var(--wp--custom--color--paper);
	/* ⭐ --chrome, not the panel's --tint: the bars and pills are drawn chrome
	   (Alpesh, 2026-09-26). */
	--ps-tint: var(--wp--custom--color--chrome);
	--ps-line: var(--wp--custom--color--hairline);
	--ps-quiet: var(--wp--custom--color--secondary);
	--ps-warn: var(--wp--custom--color--a-terra-dark);
	--ps-warn-bg: var(--wp--custom--color--a-terra-light-2);
	--ps-warn-line: var(--wp--custom--color--a-terra-light);
	--ps-g-grey: #5f6368;                 /* Google's own secondary text */
	--ps-g-blue: #1a73e8;
	--ps-ig-text: var(--wp--custom--color--ink);   /* Instagram's near-black is within 8 of our ink-hairline, so it is the ink */
	--ps-ig-quiet: #6b6b6b;               /* Instagram's #8e8e8e is 3.3:1; darkened */
	--ps-ig-blue: #0064e0;                /* #0095f6 carries white at 3.2:1; darkened */
	--ps-wa-text: #111b21;
	--ps-wa-quiet: #54656f;
	--ps-wa-green: #008069;
	--ps-ease: cubic-bezier(.22, 1, .36, 1);
	color: var(--ps-ink);
}
.sim--stack * { margin: 0; }
.sim--stack [hidden] { display: none; }
.sim--stack em { font-style: normal; }
.sim--stack b { font-weight: var(--wp--custom--weight--display); }
.sim--stack svg { display: block; }

/* -- 19a · THE STACK. One card in front and two behind. A finished card is
   flicked away to the left and reinserted at the back, so the same three
   elements cycle for ever without being rebuilt. */
.sim--stack .bstack { position: relative; width: 100%; max-width: 500px; margin: 0 auto; height: 376px; }
.sim--stack .bwin {
	position: absolute; left: 0; right: 0; top: 22px;
	background: var(--ps-paper);
	border-radius: var(--wp--custom--rounded--lg);
	overflow: hidden;
	box-shadow: var(--wp--custom--shadow--depth);
	transition: transform .62s var(--ps-ease), opacity .5s ease, filter .5s ease;
}
.sim--stack .bwin[data-pos="0"] { transform: none; opacity: 1; z-index: 3; filter: none; }
.sim--stack .bwin[data-pos="1"] { transform: translateY(-13px) scale(.955) rotate(-1.1deg); opacity: .62; z-index: 2; filter: saturate(.75); }
.sim--stack .bwin[data-pos="2"] { transform: translateY(-24px) scale(.912) rotate(1.4deg); opacity: .3; z-index: 1; filter: saturate(.5); }
.sim--stack .bwin.swipe {
	transition: transform .5s cubic-bezier(.4, 0, .7, .3), opacity .44s ease;
	transform: translate(-118%, 26px) rotate(-9deg) scale(.94); opacity: 0; z-index: 4;
}
.sim--stack .bwin.reset { transition: none; }

/* The browser frame is OURS, so it takes our neutrals. */
.sim--stack .bw-chrome {
	height: 36px; display: flex; align-items: center; gap: 6px; padding: 0 12px;
	background: var(--ps-tint); border-bottom: 1px solid var(--ps-line);
}
.sim--stack .bw-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--ps-line); flex: none; }
.sim--stack .bw-back { font-size: 15px; color: var(--ps-quiet); margin: 0 4px 0 6px; line-height: 1; }
.sim--stack .bw-url {
	position: relative; flex: 1; max-width: 240px; height: 19px; overflow: hidden;
	background: var(--ps-paper); border: 1px solid var(--ps-line); border-radius: 6px;
}
.sim--stack .bw-url em {
	position: absolute; left: 8px; top: 50%; transform: translateY(-50%);
	font-size: 9px; color: var(--ps-quiet); white-space: nowrap; opacity: 0; transition: opacity .15s ease;
}
/* ⛔ OUT, THEN IN -- see `.sc` below. */
.sim--stack .bw-url em.live { opacity: 1; transition: opacity .22s ease .15s; }
.sim--stack .bw-view { position: relative; height: 308px; background: var(--ps-paper); overflow: hidden; }
/* ⛔ OUT, THEN IN, NEVER A CROSS-DISSOLVE. Both layers used to fade over the
   same .4s, so for a moment the old scene's text painted through the new one:
   critique round 1, 2026-09-25, P2, Alpesh's pick. The leaving scene now goes
   in .18s, and the arriving one waits for it. A rule's `transition` applies to
   the state it moves TO, so the delay sits on `.live` only. */
.sim--stack .sc { position: absolute; inset: 0; opacity: 0; transition: opacity .18s ease; pointer-events: none; }
.sim--stack .sc.live { opacity: 1; transition: opacity .28s ease .18s; }
/* the ring marks what is being acted on */
.sim--stack .cur-ring {
	position: absolute; inset: -6px; border-radius: inherit; z-index: 11; pointer-events: none;
	border: 2px solid oklch(0.153 0.006 107.1 / 0.24);
	opacity: 0; transform: scale(.85);
	transition: opacity .32s ease, transform .45s var(--ps-ease);
}
.sim--stack .cur-ring.live { opacity: 1; transform: none; }

/* -- 19b · THE CAPTION ROW. ⛔ NO CAPITALS: the source uppercased it in CSS,
   and the law's label layer is TRACKING, never capitals. It is 44px tall so
   the pause control beside it sits level, and it keeps clear of that control
   on both sides so the centred caption stays centred. */
.sim--stack .foot {
	min-height: 44px; margin-top: var(--wp--custom--space--md); padding: 0 52px;
	display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--wp--custom--space--sm);
}
.sim--stack .cap {
	display: flex; align-items: center; gap: var(--wp--custom--space--sm);
	font-size: var(--wp--custom--step--label);
	font-weight: var(--wp--custom--weight--emphasis);
	line-height: var(--wp--custom--leading--body);
	letter-spacing: var(--wp--custom--tracking--label);
	color: var(--ps-quiet); text-align: center;
}
.sim--stack .cap i { width: 6px; height: 6px; border-radius: 50%; background: var(--wp--custom--color--a-terra); flex: none; }
.sim--stack .pips { display: flex; gap: 7px; }
.sim--stack .pip { position: relative; width: 34px; height: 3px; border-radius: 2px; background: var(--ps-line); overflow: hidden; }
/* ⭐ scaleX, NOT width: the same bar without a layout pass on every beat
   (critique round 1, the detector's layout-transition). view.js sets it. */
.sim--stack .pip i { position: absolute; inset: 0; background: var(--ps-quiet);
	transform: scaleX( 0 ); transform-origin: left center; transition: transform .3s linear; }

/* -- 19c · THE OUTCOME, the last beat of every card. It is OUR verdict on
   their story, so it takes our ink, our danger and our secondary. */
.sim--stack .sc6 {
	background: var(--ps-paper); display: flex; flex-direction: column; align-items: center;
	justify-content: center; gap: 7px; padding: 0 32px; text-align: center;
}
.sim--stack .sc6 b,
.sim--stack .sc6 em { font-size: 15px; font-weight: var(--wp--custom--weight--display); line-height: 1.3; color: var(--ps-ink); }
.sim--stack .sc6 em { color: var(--wp--custom--color--danger); }
.sim--stack .sc6 p { font-size: 9.5px; line-height: 1.6; color: var(--ps-quiet); margin-top: 5px; }

/* -- 19d · CARD 1 · a Google search, a course page and its contact form. */
.sim--stack .sc1 { padding: 12px 14px; }
.sim--stack .gs {
	display: flex; align-items: center; gap: 9px; max-width: 330px; padding: 7px 13px;
	border: 1px solid var(--ps-line); border-radius: 99px; transition: border-color .3s ease;
}
.sim--stack .gs.focus { border-color: #a8c7f0; box-shadow: 0 1px 6px rgb(26 115 232 / .12); }
.sim--stack .gs b { flex: 1; min-width: 0; overflow: hidden; font-size: 10px; font-weight: var(--wp--custom--weight--body); color: #202124; white-space: nowrap; }
.sim--stack .caret { display: inline-block; width: 1px; height: 11px; background: var(--ps-g-blue); vertical-align: -2px; margin-left: 1px; opacity: 0; }
.sim--stack .typing .caret { opacity: 1; animation: abk-ps-blink 1s step-end infinite; }
@keyframes abk-ps-blink { 50% { opacity: 0; } }
.sim--stack .gres { opacity: 0; transition: opacity .45s ease; }
.sim--stack .gres.live { opacity: 1; }
.sim--stack .gtabs {
	display: flex; gap: 14px; margin-top: 9px; padding-bottom: 6px;
	font-size: 9px; color: var(--ps-g-grey); border-bottom: 1px solid #ebedf0;
}
.sim--stack .gtabs .a { color: var(--ps-g-blue); font-weight: var(--wp--custom--weight--display); position: relative; }
.sim--stack .gtabs .a::after { content: ''; position: absolute; left: 0; right: 0; bottom: -7px; height: 2px; background: var(--ps-g-blue); border-radius: 2px; }
.sim--stack .gsp { font-size: 8.5px; color: var(--ps-g-grey); margin-top: 11px; }
.sim--stack .gres-i {
	position: relative; margin-top: 6px; padding: 8px 9px; border: 1px solid transparent; border-radius: 8px;
	transition: background .3s ease, border-color .3s ease;
}
.sim--stack .gres-i .dom { font-size: 8px; color: var(--ps-g-grey); }
.sim--stack .gres-i .ttl { display: block; font-size: 11px; font-weight: var(--wp--custom--weight--emphasis); color: #1a0dab; margin-top: 3px; }
.sim--stack .gres-i .dsc { font-size: 8.5px; line-height: 1.5; color: #4d5156; margin-top: 3px; }
.sim--stack .hot .gres-i--ad,
.sim--stack .hot .gbiz--pick { background: var(--ps-tint); border-color: #c9d0d8; }

.sim--stack [data-card="1"] .sc2 { padding: 0; }
.sim--stack .cn {
	display: flex; align-items: center; justify-content: space-between; padding: 9px 14px;
	border-bottom: 1px solid #eef1f4; background: var(--ps-paper); position: relative; z-index: 3;
}
.sim--stack .cn b { font-size: 10px; letter-spacing: var(--wp--custom--tracking--label); color: var(--ps-ink); }
.sim--stack .cn span { font-size: 8px; color: var(--ps-g-grey); }
.sim--stack .cscroll { padding: 12px 14px; transition: transform .8s var(--ps-ease); }
.sim--stack .crumb { font-size: 8px; color: var(--ps-g-grey); }
.sim--stack .ch { font-size: 15px; font-weight: var(--wp--custom--weight--display); line-height: 1.25; margin-top: 7px; color: var(--ps-ink); }
.sim--stack .cmeta { display: flex; flex-wrap: wrap; gap: 9px; margin-top: 8px; }
.sim--stack .cmeta span {
	font-size: 8px; font-weight: var(--wp--custom--weight--display); color: #3c4043;
	background: var(--ps-tint); border: 1px solid var(--ps-line); border-radius: 99px; padding: 3px 8px;
}
.sim--stack .cbuy { display: flex; align-items: center; gap: 11px; margin-top: 11px; }
.sim--stack .cprice { font-size: 19px; font-weight: var(--wp--custom--weight--display); color: var(--ps-ink); }
.sim--stack .cenrol { background: var(--ps-ink); color: var(--ps-paper); border-radius: 6px; font-size: 9px; font-weight: var(--wp--custom--weight--display); padding: 7px 13px; }
.sim--stack .csec { margin-top: 16px; padding-top: 13px; border-top: 1px solid #eef1f4; }
/* ⛔ The source uppercased this label in CSS. Tracking, never capitals. */
.sim--stack .csec .t { font-size: 9.5px; font-weight: var(--wp--custom--weight--display); letter-spacing: var(--wp--custom--tracking--label); color: var(--ps-g-grey); }
.sim--stack .cmod { display: flex; align-items: center; gap: 8px; font-size: 9px; color: #3c4043; padding: 6px 0; border-bottom: 1px solid #eef1f4; }
.sim--stack .cmod i {
	width: 15px; height: 15px; border-radius: 4px; background: #eef1f4; flex: none;
	font-size: 7px; font-style: normal; font-weight: var(--wp--custom--weight--display); color: var(--ps-g-grey);
	display: flex; align-items: center; justify-content: center;
}
.sim--stack .cfaq { margin-top: 6px; }
.sim--stack .cfaq span {
	display: flex; justify-content: space-between; gap: 8px; margin-bottom: 5px; padding: 7px 9px;
	font-size: 9px; color: #3c4043; border-radius: 6px; background: var(--ps-tint);
}
.sim--stack .cfaq b { color: var(--ps-ink); }
.sim--stack .cfaq .miss { background: var(--ps-warn-bg); border: 1px solid var(--ps-warn-line); color: var(--ps-warn); }
.sim--stack .cfaq .miss b { color: var(--ps-warn); }
.sim--stack .ccontact {
	position: relative; display: inline-block; margin-top: 11px; padding: 7px 13px;
	border: 1px solid #c9d0d8; border-radius: 6px; font-size: 9px; font-weight: var(--wp--custom--weight--display); color: var(--ps-ink);
}
.sim--stack .mdim { position: absolute; inset: 0; background: oklch(0.153 0.006 107.1 / 0.46); opacity: 0; transition: opacity .34s ease; pointer-events: none; }
.sim--stack .mdim.live { opacity: 1; }
.sim--stack .modal {
	position: absolute; left: 50%; top: 50%; width: 236px; max-width: calc(100% - 24px); padding: 15px;
	background: var(--ps-paper); border-radius: 12px; box-shadow: var(--wp--custom--shadow--depth);
	opacity: 0; transform: translate(-50%, -46%) scale(.96); pointer-events: none;
	transition: opacity .34s ease, transform .4s var(--ps-ease);
}
.sim--stack .modal.live { opacity: 1; transform: translate(-50%, -50%); }
.sim--stack .mtop { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 11px; }
.sim--stack .mtop b { font-size: 11.5px; color: var(--ps-ink); }
.sim--stack .mx { position: relative; font-size: 12px; line-height: 1; color: var(--ps-g-grey); }
.sim--stack .mf {
	display: flex; align-items: center; height: 25px; margin-bottom: 8px; padding: 0 9px;
	border-radius: 6px; background: var(--ps-tint); border: 1px solid #e5e9ed; font-size: 9px; color: var(--ps-g-grey);
}
.sim--stack .mf.area { height: 36px; align-items: flex-start; padding-top: 7px; }
/* a drawn DISABLED button: grey on grey, and still 4.5:1 */
.sim--stack .msend { display: inline-block; margin-top: 4px; padding: 7px 13px; border-radius: 6px; background: #e8ebee; color: var(--ps-g-grey); font-size: 9.5px; font-weight: var(--wp--custom--weight--display); }
.sim--stack .mwait {
	display: block; margin-top: 13px; padding: 8px 10px; text-align: center;
	font-size: 9.5px; font-weight: var(--wp--custom--weight--display);
	color: var(--ps-warn); background: var(--ps-warn-bg); border: 1px solid var(--ps-warn-line); border-radius: 7px;
	opacity: 0; transform: translateY(5px); transition: opacity .4s ease, transform .4s ease;
}
.sim--stack .mwait.live { opacity: 1; transform: none; }

/* -- 19e · CARD 2 · an Instagram reel and a direct message. */
.sim--stack [data-card="2"] .sc1 { display: flex; padding: 0; }
.sim--stack [data-card="2"] .ph {
	width: min(214px, 43%); flex: none; position: relative; overflow: hidden;
	background: linear-gradient(160deg, #e8dcd2 0%, #d6c3b4 52%, #c2a894 100%);
}
/* the subject is deliberately out of focus -- it reads as a photograph
   without pretending to be one, and nothing in it has to be identifiable */
.sim--stack [data-card="2"] .ph .fig,
.sim--stack [data-card="2"] .ph .hair { filter: blur(11px); }
.sim--stack [data-card="2"] .ph .fig { position: absolute; left: 50%; bottom: -12px; transform: translateX(-50%); width: 118px; height: 186px; border-radius: 58px 58px 0 0; background: linear-gradient(180deg, #b99a83, #a8866c); }
.sim--stack [data-card="2"] .ph .hair { position: absolute; left: 50%; bottom: 110px; transform: translateX(-50%); width: 96px; height: 88px; border-radius: 48px 48px 26px 26px; background: linear-gradient(160deg, #5b4034, #3d2a22); }
.sim--stack [data-card="2"] .ph::after { content: ''; position: absolute; inset: 0; pointer-events: none; background: radial-gradient(60% 50% at 50% 34%, rgb(255 255 255 / .22), transparent 70%); }
/* ⛔ BOTH LABELS SIT ON A SOLID CHIP. White on the photo's lightest stop is
   2.1:1; the source's translucent chip does not count as a ground. */
.sim--stack [data-card="2"] .ph .rl,
.sim--stack [data-card="2"] .ph .dur { position: absolute; z-index: 1; font-size: 7.5px; font-weight: var(--wp--custom--weight--display); color: var(--ps-paper); background: #2b1d17; border-radius: 4px; padding: 2px 6px; }
.sim--stack [data-card="2"] .ph .rl { left: 9px; top: 9px; display: flex; align-items: center; gap: 4px; }
.sim--stack [data-card="2"] .ph .dur { right: 9px; bottom: 9px; }
.sim--stack .pmeta { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.sim--stack .ig-top { display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-bottom: 1px solid #efefef; }
.sim--stack .ig-av,
.sim--stack .igl-row > i,
.sim--stack .dr-av { width: 26px; height: 26px; border-radius: 50%; flex: none; padding: 2px; background: linear-gradient(45deg, var(--wp--custom--color--warm), #ee2a7b, #6228d7); }
.sim--stack .igl-row > i,
.sim--stack .dr-av { width: 24px; height: 24px; }
.sim--stack .ig-av i,
.sim--stack .igl-row > i span,
.sim--stack .dr-av i { display: block; width: 100%; height: 100%; border-radius: 50%; background: #d8c7b8; border: 1.5px solid var(--ps-paper); }
.sim--stack .ig-who { min-width: 0; }
.sim--stack .ig-who b { display: block; font-size: 10px; color: var(--ps-ig-text); }
.sim--stack .ig-who em { display: block; font-size: 8px; color: var(--ps-ig-quiet); margin-top: 1px; }
.sim--stack .ig-msg { margin-left: auto; position: relative; flex: none; font-size: 8.5px; font-weight: var(--wp--custom--weight--display); color: var(--ps-paper); background: var(--ps-ig-blue); border-radius: 7px; padding: 5px 11px; }
.sim--stack .ig-cap { flex: 1; padding: 10px 12px; font-size: 9px; line-height: 1.6; color: var(--ps-ig-text); }
.sim--stack .ig-cap .tag { color: #00376b; }
.sim--stack .ig-acts { display: flex; align-items: center; gap: 12px; padding: 8px 12px; border-top: 1px solid #efefef; }
.sim--stack .ig-ic { position: relative; width: 19px; height: 19px; color: var(--ps-ig-text); }
.sim--stack .ig-ic svg { width: 19px; height: 19px; }
.sim--stack .heart-o { transition: opacity .2s ease; }
.sim--stack .heart-f { position: absolute; inset: 0; opacity: 0; transform: scale(.4); color: #ed4956; transition: opacity .22s ease, transform .34s cubic-bezier(.2, 1.6, .4, 1); }
.sim--stack .liked .heart-o { opacity: 0; }
.sim--stack .liked .heart-f { opacity: 1; transform: none; }
.sim--stack .ig-likes { padding: 0 12px 10px; font-size: 9px; font-weight: var(--wp--custom--weight--display); color: var(--ps-ig-text); }

.sim--stack [data-card="2"] .sc2 { display: flex; }
.sim--stack .igl { width: 126px; flex: none; border-right: 1px solid #efefef; display: flex; flex-direction: column; }
.sim--stack .igl-top { padding: 9px 10px; font-size: 9px; font-weight: var(--wp--custom--weight--display); color: var(--ps-ig-text); border-bottom: 1px solid #efefef; }
.sim--stack .igl-row { display: flex; align-items: center; gap: 7px; padding: 8px 10px; background: var(--ps-tint); }
.sim--stack .igl-row > span { min-width: 0; }
.sim--stack .igl-row b { display: block; font-size: 8.5px; color: var(--ps-ig-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sim--stack .igl-row em { display: block; font-size: 7px; color: var(--ps-ig-quiet); margin-top: 1px; }
.sim--stack .dr { flex: 1; display: flex; flex-direction: column; min-width: 0; background: var(--ps-paper); }
.sim--stack .dr-head { flex: none; display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-bottom: 1px solid #efefef; }
.sim--stack .dr-head b { font-size: 9.5px; color: var(--ps-ig-text); }
.sim--stack .dr-body { flex: 1; padding: 9px 11px; display: flex; flex-direction: column; gap: 6px; overflow: hidden; }
.sim--stack .dm-note { align-self: center; max-width: 88%; text-align: center; font-size: 7px; line-height: 1.5; color: var(--ps-ig-quiet); }
.sim--stack .dm {
	max-width: 78%; padding: 6px 10px; border-radius: 14px; font-size: 9px; line-height: 1.45;
	opacity: 0; transform: translateY(6px) scale(.97);
	transition: opacity .3s ease, transform .34s var(--ps-ease);
}
.sim--stack .dm.live { opacity: 1; transform: none; }
.sim--stack .dm--me { align-self: flex-end; background: var(--ps-ig-blue); color: var(--ps-paper); border-bottom-right-radius: 4px; }
.sim--stack .dm--them { align-self: flex-start; background: #efefef; color: var(--ps-ig-text); border-bottom-left-radius: 4px; }
.sim--stack .dm-seen { align-self: flex-end; font-size: 7px; color: var(--ps-ig-quiet); opacity: 0; transition: opacity .3s ease; }
.sim--stack .dm-auto,
.sim--stack .dm-quiet,
.sim--stack .wa-quiet {
	font-size: 7.5px; font-weight: var(--wp--custom--weight--display);
	color: var(--ps-warn); background: var(--ps-warn-bg); border: 1px solid var(--ps-warn-line);
	border-radius: 99px; padding: 4px 10px; opacity: 0; transition: opacity .45s ease;
}
.sim--stack .dm-auto { align-self: flex-start; margin-top: -2px; padding: 3px 8px; font-size: 7px; transition-duration: .35s; }
.sim--stack .dm-quiet,
.sim--stack .wa-quiet { align-self: center; margin-top: auto; }
.sim--stack .dm-seen.live,
.sim--stack .dm-auto.live,
.sim--stack .dm-quiet.live,
.sim--stack .wa-quiet.live { opacity: 1; }
.sim--stack .dr-input { flex: none; display: flex; align-items: center; gap: 8px; padding: 7px 10px; border-top: 1px solid #efefef; }
.sim--stack .dr-field {
	flex: 1; min-width: 0; padding: 6px 11px; font-size: 8px; color: var(--ps-ig-text); white-space: nowrap; overflow: hidden;
	border: 1px solid var(--ps-line); border-radius: 99px; transition: border-color .3s ease;
}
.sim--stack .dr-field.focus { border-color: #a8cdf5; }
.sim--stack .dr-field .ph2 { color: var(--ps-ig-quiet); }
.sim--stack .dr-send { position: relative; flex: none; font-size: 8.5px; font-weight: var(--wp--custom--weight--display); color: var(--ps-ig-blue); }

/* -- 19f · CARD 3 · Google Maps, a business panel and WhatsApp Web. */
.sim--stack .gcols { display: flex; gap: 14px; margin-top: 11px; }
.sim--stack .gleft { flex: 1; min-width: 0; }
.sim--stack [data-card="3"] .gsp { margin-top: 0; }
.sim--stack .gbiz { position: relative; margin-top: 6px; padding: 8px 9px; border: 1px solid #e6e9ed; border-radius: 8px; transition: background .3s ease, border-color .3s ease; }
.sim--stack .gbiz-t { font-size: 10.5px; font-weight: var(--wp--custom--weight--display); color: #202124; }
.sim--stack .gbiz-m { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; margin-top: 3px; font-size: 8px; color: var(--ps-g-grey); }
.sim--stack .st { color: #f9ab00; }
.sim--stack .op { color: #188038; font-weight: var(--wp--custom--weight--display); }
.sim--stack .gbiz-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.sim--stack .gbiz-tags span { font-size: 7.5px; font-weight: var(--wp--custom--weight--display); color: #3c4043; border: 1px solid var(--ps-line); border-radius: 99px; padding: 2px 7px; }
.sim--stack .gbiz-tags .wa { border-color: #a8e6bd; color: #0b7a45; background: #f0fdf4; }
.sim--stack .gmap { position: relative; width: min(168px, 36%); height: 150px; flex: none; border-radius: 7px; overflow: hidden; background: #eaefe9; }
.sim--stack .gmap .rd { position: absolute; background: var(--ps-paper); }
.sim--stack .gmap .rd.a { left: 0; right: 0; top: 50px; height: 6px; }
.sim--stack .gmap .rd.b { left: 0; right: 0; top: 100px; height: 5px; }
.sim--stack .gmap .rd.c { left: 58px; top: 0; bottom: 0; width: 6px; }
.sim--stack .gmap .rd.d { left: 122px; top: 0; bottom: 0; width: 4px; }
.sim--stack .gmap .wt { position: absolute; left: 0; top: 0; bottom: 0; width: 13px; background: #cfe0ea; }
.sim--stack .gmap .pin { position: absolute; width: 12px; height: 12px; border-radius: 50% 50% 50% 0; background: #ea4335; transform: rotate(-45deg); box-shadow: 0 1px 2px rgb(0 0 0 / .24); }
.sim--stack .gmap .lb { position: absolute; font-size: 6.5px; font-weight: var(--wp--custom--weight--display); color: #3c4043; background: rgb(255 255 255 / .9); border-radius: 3px; padding: 1px 4px; white-space: nowrap; }

.sim--stack [data-card="3"] .sc2 { padding: 12px 14px; display: flex; gap: 13px; }
.sim--stack .pg { width: min(172px, 36%); flex: none; display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 70px 52px; gap: 2px; border-radius: 7px; overflow: hidden; }
.sim--stack .pg div:nth-child(1) { grid-column: span 2; background: linear-gradient(150deg, #cdd7de, #b7c3cc); }
.sim--stack .pg div:nth-child(2) { background: linear-gradient(150deg, #d5dce2, #c1cad2); display: flex; align-items: center; justify-content: center; }
.sim--stack .pg div:nth-child(3) { background: linear-gradient(150deg, #c9d3da, #b4bfc9); }
/* ⛔ a SOLID chip, for the same reason as the reel labels */
.sim--stack .pg .vid span { font-size: 7px; font-weight: var(--wp--custom--weight--display); color: var(--ps-paper); background: #2b3238; border-radius: 3px; padding: 2px 5px; }
.sim--stack .pinfo { flex: 1; min-width: 0; }
/* ⛔ The source uppercased the business name in CSS. It stays as written. */
.sim--stack .pname { font-size: 12.5px; font-weight: var(--wp--custom--weight--display); line-height: 1.3; color: #202124; }
.sim--stack .prate { display: flex; align-items: center; gap: 5px; margin-top: 6px; font-size: 9.5px; color: #3c4043; }
.sim--stack .pcat { margin-top: 4px; font-size: 9px; color: var(--ps-g-grey); }
.sim--stack .ptabs {
	display: flex; gap: 11px; margin-top: 9px; padding-bottom: 7px; border-bottom: 1px solid #ebedf0;
	font-size: 8px; font-weight: var(--wp--custom--weight--display); letter-spacing: var(--wp--custom--tracking--label); color: var(--ps-g-grey);
}
.sim--stack .ptabs .a { color: var(--ps-g-blue); position: relative; }
.sim--stack .ptabs .a::after { content: ''; position: absolute; left: 0; right: 0; bottom: -8px; height: 2px; background: var(--ps-g-blue); border-radius: 2px; }
.sim--stack .pacts { display: flex; gap: 9px; margin-top: 13px; }
.sim--stack .pact { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.sim--stack .pact .ic { position: relative; width: 32px; height: 32px; border-radius: 50%; border: 1px solid var(--ps-line); display: flex; align-items: center; justify-content: center; }
.sim--stack .pact em { font-size: 7px; color: #3c4043; }
.sim--stack .pact--wa .ic { border-color: #a8e6bd; background: #f0fdf4; }
.sim--stack .pnote { margin-top: 12px; padding-top: 10px; border-top: 1px solid #eef1f4; font-size: 8.5px; line-height: 1.5; color: var(--ps-g-grey); }

.sim--stack .sc3 { display: flex; }
.sim--stack .wl { width: 132px; flex: none; display: flex; flex-direction: column; background: var(--ps-tint); border-right: 1px solid #e6e9ed; }
.sim--stack .wl-top { display: flex; align-items: center; gap: 6px; padding: 9px 10px; border-bottom: 1px solid #e6e9ed; font-size: 8.5px; font-weight: var(--wp--custom--weight--display); color: #3b4a54; }
.sim--stack .wl-top i { width: 16px; height: 16px; border-radius: 50%; background: #c8d1d8; flex: none; }
.sim--stack .wl-search { margin: 8px 9px; padding: 5px 9px; background: var(--ps-paper); border: 1px solid #e6e9ed; border-radius: 99px; font-size: 7.5px; color: var(--ps-wa-quiet); }
.sim--stack .wl-chat { display: flex; align-items: center; gap: 7px; padding: 8px 10px; background: #eef1f4; }
.sim--stack .wl-chat > span { min-width: 0; }
.sim--stack .wl-chat i,
.sim--stack .wa-av { flex: none; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: var(--ps-wa-green); color: var(--ps-paper); font-style: normal; font-weight: var(--wp--custom--weight--display); }
.sim--stack .wl-chat i { width: 24px; height: 24px; font-size: 9px; }
.sim--stack .wl-chat b { display: block; font-size: 8.5px; color: var(--ps-wa-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sim--stack .wl-chat em { display: block; margin-top: 1px; font-size: 7px; color: var(--ps-wa-quiet); }
.sim--stack .wr { flex: 1; min-width: 0; display: flex; flex-direction: column; position: relative; background: #efe6dc; }
.sim--stack .wr::before {
	content: ''; position: absolute; inset: 0; pointer-events: none; opacity: .55;
	background-image:
		radial-gradient(circle at 14% 10%, #ddd0be 0 3px, transparent 4px),
		radial-gradient(circle at 64% 18%, #ddd0be 0 2px, transparent 3px),
		radial-gradient(circle at 36% 30%, #ddd0be 0 4px, transparent 5px),
		radial-gradient(circle at 86% 38%, #ddd0be 0 3px, transparent 4px),
		radial-gradient(circle at 24% 52%, #ddd0be 0 2px, transparent 3px),
		radial-gradient(circle at 74% 62%, #ddd0be 0 4px, transparent 5px),
		radial-gradient(circle at 46% 76%, #ddd0be 0 3px, transparent 4px),
		radial-gradient(circle at 90% 84%, #ddd0be 0 2px, transparent 3px);
}
.sim--stack .wa-head { position: relative; z-index: 2; flex: none; display: flex; align-items: center; gap: 8px; padding: 7px 12px; background: #f0f2f5; border-bottom: 1px solid var(--ps-line); }
.sim--stack .wa-av { width: 26px; height: 26px; font-size: 10px; }
.sim--stack .wa-who { min-width: 0; }
.sim--stack .wa-who b { display: block; font-size: 10px; color: var(--ps-wa-text); }
.sim--stack .wa-who em { display: block; margin-top: 1px; font-size: 8px; color: var(--ps-wa-quiet); }
.sim--stack .wa-body { position: relative; z-index: 2; flex: 1; display: flex; flex-direction: column; gap: 5px; padding: 9px 11px; overflow: hidden; }
.sim--stack .wa-day { align-self: center; padding: 3px 9px; border-radius: 6px; background: var(--ps-paper); box-shadow: 0 1px 1px rgb(0 0 0 / .06); font-size: 7.5px; font-weight: var(--wp--custom--weight--display); color: #5b6a70; }
.sim--stack .wa-e2e { align-self: center; max-width: 92%; padding: 5px 8px; border-radius: 6px; background: #fdf4d3; box-shadow: 0 1px 1px rgb(0 0 0 / .05); text-align: center; font-size: 7px; line-height: 1.5; color: #7d6a3f; }
.sim--stack .wa-msg {
	align-self: flex-end; max-width: 76%; padding: 6px 8px 4px; border-radius: 8px 3px 8px 8px;
	background: #d9fdd3; box-shadow: 0 1px 1px rgb(0 0 0 / .07); font-size: 9px; line-height: 1.45; color: var(--ps-wa-text);
	opacity: 0; transform: translateY(6px) scale(.97); transition: opacity .3s ease, transform .34s var(--ps-ease);
}
.sim--stack .wa-msg.live { opacity: 1; transform: none; }
.sim--stack .wa-txt { display: block; }
.sim--stack .wa-msg .t { display: flex; align-items: center; justify-content: flex-end; gap: 3px; margin-top: 2px; font-size: 7px; color: var(--ps-wa-quiet); }
.sim--stack .tick { width: 12px; height: 7px; position: relative; display: inline-block; }
.sim--stack .tick::before,
.sim--stack .tick::after { content: ''; position: absolute; top: 1px; width: 4.5px; height: 2.5px; border-left: 1.3px solid #8696a0; border-bottom: 1.3px solid #8696a0; transform: rotate(-45deg); }
.sim--stack .tick::before { left: 0; }
.sim--stack .tick::after { left: 3.6px; opacity: 0; transition: opacity .3s ease; }
.sim--stack .tick.two::after { opacity: 1; }
.sim--stack .wa-input { position: relative; z-index: 2; flex: none; display: flex; align-items: center; gap: 7px; padding: 6px 10px; background: #f0f2f5; }
.sim--stack .wa-field { flex: 1; min-width: 0; padding: 6px 10px; border-radius: 99px; background: var(--ps-paper); font-size: 8px; color: var(--ps-wa-text); white-space: nowrap; overflow: hidden; transition: box-shadow .3s ease; }
.sim--stack .wa-field.focus { box-shadow: 0 0 0 2px rgb(0 128 105 / .28); }
.sim--stack .wa-field .ph { color: var(--ps-wa-quiet); }
.sim--stack .wa-send { position: relative; flex: none; width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: var(--ps-wa-green); color: var(--ps-paper); font-size: 10px; }

/* ⛔ REDUCED MOTION. The script never starts, and nothing may move by CSS
   either: the cards hold their places and every state change is instant. */
@media (prefers-reduced-motion: reduce) {
	.sim--stack *,
	.sim--stack *::before,
	.sim--stack *::after { transition: none; animation: none; }
}
