/* @pepperui/css 2.3.1 — generated by scripts/pepper-ui.mjs (2.4.2). Do not edit: npm run pepper-ui. */
/**
 * pepper-ui.css — @pepperui/css 2.3.1, the Mobupps design system's components as plain CSS.
 * GENERATED by `npm run build` from packages/css/src — do not edit by hand; edit the source file named
 * in each section's header. Requires @pepperui/tokens 1.5.0 (tokens.css) loaded before it.
 * Source of truth: bitbucket.org/rashidmobupps/pepper-ui — 67 parts.
 */
/* @pepperui/css — the source, in load order. build/css.js inlines these @imports into dist/pepper-ui.css.
 * Load @pepperui/tokens/css first. Order follows the queue (docs/10_COMPONENTS.md): base, Actions, Forms,
 * Feedback and status, Navigation and overlay, Content blocks, Shells and patterns, Chat. */
/* base.css — what every Pepper UI class shares. Loaded first.
 *
 * Nothing here resets the page: this file lives beside Bootstrap, CoreUI and a consumer's own
 * CSS (Paggy's app.css, the site's styles.css), so it touches only its own classes and a few
 * inherited custom properties. Every value is a token from @pepperui/tokens (tokens.css must
 * be loaded before this file).
 *
 * The class grammar (D52, 2026-09-08): `pui-` prefix + BEM. Block = the registry item's name
 * (`.pui-button`), element = `__part`, modifier = `--value` where the value is the Figma
 * variant option verbatim (`.pui-button--secondary`, `.pui-input--small`). States are the
 * browser's (:hover, :focus-visible, :active, :disabled) and, for scripted states, the ARIA
 * attribute the JavaScript already sets (`[aria-pressed="true"]`, `[aria-invalid="true"]`,
 * `[aria-current]`, `[data-state="open"]`). Booleans (`Has icon`) are the presence of the part.
 */

:root {
  /* The face. Onest is the Cyrillic companion (D39): a page or a block marked lang="ru"
     switches every Pepper UI class to it through inheritance. */
  --pui-font-family: var(--pui-font-family-poppins);

  /* The focus ring — a SOLID 2 px line in border/focus, with the site's soft halo behind it (3.5.2,
     2026-09-09). One place, so every control agrees.
     Until this unit the ring was the site's `0 0 0 3px rgba(106,102,228,.15)` (styles.css:646) alone
     — border/focus at 15 %. Composited that paints #e9e8fb, which reads **1.21 against surface/page,
     1.19 on a card and 1.12 against the primary button's fill it hugs**, where Pajamas (adopted at
     07_PRINCIPLES §Stop 2) and WCAG 1.4.11 ask 3:1 of a focus indicator against its adjacent colours,
     and WCAG 2.4.13 asks for a 2 CSS px perimeter. §Stop 1 had already named this as the one thing we
     must override in the base we chose, where it arrives as shadcn's `ring-1 ring-ring/50`; the
     library had inherited the site's version of the same mistake.
     The solid line is `border/focus` #6a66e4 — **4.53 on the page, 4.06 on a card, 4.05 on a sunken
     well, 3.72 on the dark surface, and 3.22 against border/default**, the control edge it hugs. The
     halo is the site's own colour and value, moved outside the line, so the ring still reads as the
     site's; `scripts/focus_ring.py` measures every number above and every primitive that uses it. */
  --pui-ring-color: color-mix(in srgb, var(--pui-border-focus) 15%, transparent);
  --pui-ring-error-color: color-mix(in srgb, var(--pui-border-error) 10%, transparent);
  --pui-ring: 0 0 0 2px var(--pui-border-focus), 0 0 0 5px var(--pui-ring-color);
  /* An invalid control carries `--pui-ring-error` AT REST — the site's soft red glow (styles.css:650)
     is how a field says it is wrong, and it is decoration, not a focus indicator. So it stays a halo:
     giving it a solid line would leave a focused error field with nothing new to show, which is what
     3.5.2 measured on 2026-09-09 (the error rules sit after the focus rules in every one of the six
     form files, so they won, and Tabbing into an invalid input changed 1.5 worth of pixels).
     `--pui-ring-error-focus` is what such a control takes WHEN IT HAS FOCUS: the same solid 2 px line
     as the focus ring but in border/error, so focus is visible and the error signal is not overruled
     by a violet ring. 4.58 against surface/page, 3.9 against the halo it replaces. */
  --pui-ring-error: 0 0 0 3px var(--pui-ring-error-color);
  --pui-ring-error-focus: 0 0 0 2px var(--pui-border-error), 0 0 0 5px var(--pui-ring-error-color);
  /* The same ring for a control whose own box is full-bleed in its container — a menu row, a list
     item — where an outset shadow is clipped by the container it fills. Same colour, same 2 px. */
  --pui-ring-inset: inset 0 0 0 2px var(--pui-border-focus);

  /* Ghost hover — Paggy's `rgba(0,0,0,.05)` (app.css .btn--ghost:hover) is surface/sunken in the
     library; the library's value wins so a ghost on a card reads the same as a ghost on the page. */
}

:lang(ru) {
  --pui-font-family: var(--pui-font-family-cyrillic);
}

/* An icon slot. Lucide SVGs use stroke="currentColor", so a glyph takes the text colour of
   whatever it sits in. Sizes are size/icon/* (16 · 24 · 32 · 48); 16 is the default because
   every control slot in the library is 16. */
.pui-icon {
  display: inline-block;
  flex: none;
  width: var(--pui-size-icon-16);
  height: var(--pui-size-icon-16);
  vertical-align: middle;
}
.pui-icon--24 { width: var(--pui-size-icon-24); height: var(--pui-size-icon-24); }
.pui-icon--32 { width: var(--pui-size-icon-32); height: var(--pui-size-icon-32); }
.pui-icon--48 { width: var(--pui-size-icon-48); height: var(--pui-size-icon-48); }
.pui-icon > svg {
  display: block;
  width: 100%;
  height: 100%;
  /* Lucide's own attributes, so a <use> from a sprite draws the same as an inline glyph. */
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Visually hidden, still read — for the native input inside Checkbox, Radio and Switch, and
   for an icon button's name when there is no tooltip. */
.pui-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ground.css — the ground a grey surface sits on (10_COMPONENTS.md has no row for it: it is a rule between parts, not a part).
 *
 * A grey surface is one step darker than the ground it sits on. On the white ground, surface/page, it is neutral/100 —
 * what the library has always drawn, 1.12 : 1. On the product ground, surface/card #f2f2f7, neutral/100 measured
 * 1.00 : 1 and the surface disappeared; there it is neutral/200, 1.26 : 1. "Grey surface" is everything that fills
 * surface/sunken — Avatar, Kbd, the filled Toggle, Skeleton, the muted Item, the neutral Alert, a media well, the chat
 * bubbles, every hover wash — and the neutral KPI tile. The one exception is the box of a segmented Tabs: on the product
 * ground it is WHITE, not a step darker. Tints do not move. (D106, D107.)
 *
 * The mechanism is the accent's: the tokens that follow the ground re-resolve where the ground changes.
 * tokens.css writes the two halves of each ground role in :root (--pui-ground-page-sunken, --pui-ground-card-sunken,
 * --pui-ground-page-track, --pui-ground-card-track) and re-declares the FOLLOWERS under [data-ground="page" | "card"]:
 * --pui-surface-sunken, --pui-kpi-tone-neutral-bg, --pui-tabs-track. tokens.css writes resolved values, so a scope that
 * moved only --pui-surface-sunken would leave the KPI tile behind — every follower is written in each rule below, and
 * scripts/css_rules_test.py ('the ground') holds the two lists to the [data-ground] blocks, name for name.
 *
 *   <main data-ground="card"> … the product's content area, painted surface/card by the app … </main>
 *
 * What an app sets: data-ground="card" on the element it paints surface/card as the ground of a screen — no stylesheet
 * here paints that one. What this file sets, so nobody has to remember it: every part of the package that paints a
 * ground ITSELF. A part that paints surface/card (or an accent fill) and holds other parts is a card ground; a part that
 * paints white and holds other parts is a page ground again, whatever it sits in — a white Card on a product screen, a
 * menu, a popover, the KPI tile's detail slot. A control that paints white and holds nothing grey (an input, a checkbox
 * box, a switch thumb, a slider thumb) is not a ground and is not listed; css_rules_test.py names each with its reason.
 *
 * Every selector is inside :where(), so it weighs nothing: an explicit data-ground on the same element always wins, and
 * no component rule changes who wins because this file exists. No selector matches an element the other list matches.
 */

/* ── The card ground: parts that paint surface/card, or an accent fill, and hold other parts ───────────────────────────────── */
:where(
  .pui-card:not(.pui-card--plain, .pui-card--border),
  :is(.pui-card--plain, .pui-card--border) > .pui-card__slot,
  .pui-table,
  .pui-dialog,
  .pui-alert-dialog,
  .pui-questionnaire-step,
  .pui-review-card,
  .pui-bubble--user,
  .pui-suggestions
) {
  --pui-surface-sunken: var(--pui-ground-card-sunken);
  --pui-kpi-tone-neutral-bg: var(--pui-ground-card-sunken);
  --pui-tabs-track: var(--pui-ground-card-track);
}

/* ── The page ground again: parts that paint white and hold other parts ────────────────────────────────────────────────────── */
:where(
  .pui-card--plain,
  .pui-card--border,
  .pui-card:not(.pui-card--plain, .pui-card--border, .pui-card--filled-header) > .pui-card__slot,
  .pui-topbar,
  .pui-popover,
  .pui-hover-card,
  .pui-select-menu,
  .pui-form-result__slot,
  .pui-combobox-menu,
  .pui-dropdown-menu,
  .pui-command,
  .pui-calendar,
  .pui-sheet,
  .pui-drawer,
  .pui-state-block,
  .pui-dialog__content,
  .pui-kpi-tile__detail,
  .pui-review-card__quote,
  .pui-input-group,
  .pui-alert--outline,
  .pui-composer,
  .pui-message-scroller,
  .pui-chat,
  .pui-bubble:not(.pui-bubble--user),
  .pui-suggestions__row,
  .pui-table__table > tbody > tr:hover > .pui-table__cell
) {
  --pui-surface-sunken: var(--pui-ground-page-sunken);
  --pui-kpi-tone-neutral-bg: var(--pui-ground-page-sunken);
  --pui-tabs-track: var(--pui-ground-page-track);
}

/* type-roles.css is GENERATED by build/css.js — the Typography roles as classes (request 24) */
/* type-roles.css — the Typography roles as classes (request 24, 2026-09-25). GENERATED by build/css.js from
 * @pepperui/tokens 1.5.0 — do not edit: every role the token source authors, one class each. A class sets the
 * role's five properties from its custom properties, so it follows the canvas: the web-tablet and web-mobile media blocks
 * and [data-canvas="slide"] re-resolve the variables and the class moves with them. For a page's own statement, a
 * section lead, a figure — where no component owns the text and a size would otherwise be typed (R13).
 *
 *   <p class="pui-text--headline-small">Stop analysing. Start acting.</p>
 */
.pui-text--display-large {
  font-family: var(--pui-type-display-large-font-family);
  font-size: var(--pui-type-display-large-font-size);
  font-weight: var(--pui-type-display-large-font-weight);
  line-height: var(--pui-type-display-large-line-height);
  letter-spacing: var(--pui-type-display-large-letter-spacing);
}
.pui-text--display-medium {
  font-family: var(--pui-type-display-medium-font-family);
  font-size: var(--pui-type-display-medium-font-size);
  font-weight: var(--pui-type-display-medium-font-weight);
  line-height: var(--pui-type-display-medium-line-height);
  letter-spacing: var(--pui-type-display-medium-letter-spacing);
}
.pui-text--display-small {
  font-family: var(--pui-type-display-small-font-family);
  font-size: var(--pui-type-display-small-font-size);
  font-weight: var(--pui-type-display-small-font-weight);
  line-height: var(--pui-type-display-small-line-height);
  letter-spacing: var(--pui-type-display-small-letter-spacing);
}
.pui-text--headline-large {
  font-family: var(--pui-type-headline-large-font-family);
  font-size: var(--pui-type-headline-large-font-size);
  font-weight: var(--pui-type-headline-large-font-weight);
  line-height: var(--pui-type-headline-large-line-height);
  letter-spacing: var(--pui-type-headline-large-letter-spacing);
}
.pui-text--headline-medium {
  font-family: var(--pui-type-headline-medium-font-family);
  font-size: var(--pui-type-headline-medium-font-size);
  font-weight: var(--pui-type-headline-medium-font-weight);
  line-height: var(--pui-type-headline-medium-line-height);
  letter-spacing: var(--pui-type-headline-medium-letter-spacing);
}
.pui-text--headline-small {
  font-family: var(--pui-type-headline-small-font-family);
  font-size: var(--pui-type-headline-small-font-size);
  font-weight: var(--pui-type-headline-small-font-weight);
  line-height: var(--pui-type-headline-small-line-height);
  letter-spacing: var(--pui-type-headline-small-letter-spacing);
}
.pui-text--title-large {
  font-family: var(--pui-type-title-large-font-family);
  font-size: var(--pui-type-title-large-font-size);
  font-weight: var(--pui-type-title-large-font-weight);
  line-height: var(--pui-type-title-large-line-height);
  letter-spacing: var(--pui-type-title-large-letter-spacing);
}
.pui-text--title-medium {
  font-family: var(--pui-type-title-medium-font-family);
  font-size: var(--pui-type-title-medium-font-size);
  font-weight: var(--pui-type-title-medium-font-weight);
  line-height: var(--pui-type-title-medium-line-height);
  letter-spacing: var(--pui-type-title-medium-letter-spacing);
}
.pui-text--title-small {
  font-family: var(--pui-type-title-small-font-family);
  font-size: var(--pui-type-title-small-font-size);
  font-weight: var(--pui-type-title-small-font-weight);
  line-height: var(--pui-type-title-small-line-height);
  letter-spacing: var(--pui-type-title-small-letter-spacing);
}
.pui-text--body-large {
  font-family: var(--pui-type-body-large-font-family);
  font-size: var(--pui-type-body-large-font-size);
  font-weight: var(--pui-type-body-large-font-weight);
  line-height: var(--pui-type-body-large-line-height);
  letter-spacing: var(--pui-type-body-large-letter-spacing);
}
.pui-text--body-medium {
  font-family: var(--pui-type-body-medium-font-family);
  font-size: var(--pui-type-body-medium-font-size);
  font-weight: var(--pui-type-body-medium-font-weight);
  line-height: var(--pui-type-body-medium-line-height);
  letter-spacing: var(--pui-type-body-medium-letter-spacing);
}
.pui-text--body-small {
  font-family: var(--pui-type-body-small-font-family);
  font-size: var(--pui-type-body-small-font-size);
  font-weight: var(--pui-type-body-small-font-weight);
  line-height: var(--pui-type-body-small-line-height);
  letter-spacing: var(--pui-type-body-small-letter-spacing);
}
.pui-text--body-x-small {
  font-family: var(--pui-type-body-x-small-font-family);
  font-size: var(--pui-type-body-x-small-font-size);
  font-weight: var(--pui-type-body-x-small-font-weight);
  line-height: var(--pui-type-body-x-small-line-height);
  letter-spacing: var(--pui-type-body-x-small-letter-spacing);
}
.pui-text--label-medium {
  font-family: var(--pui-type-label-medium-font-family);
  font-size: var(--pui-type-label-medium-font-size);
  font-weight: var(--pui-type-label-medium-font-weight);
  line-height: var(--pui-type-label-medium-line-height);
  letter-spacing: var(--pui-type-label-medium-letter-spacing);
}

/* Actions */
/* button.css — Button · Button - secondary · Button - ghost · Button - danger (10_COMPONENTS.md § Actions).
 *
 * Source: the site's `.btn.btn--dark` (styles.css:527–563): 36 min-height, 8 / 16 padding, radius 8, 14 Medium 500,
 * hover on the darker fill, :active scale(.99), disabled at .5. Paggy's `.btn` (app.css) is the same button and adds
 * `--red` (danger) and `--ghost`. Figma: Button 18:183 · secondary 19:92 · ghost 19:183 · danger 208:5 — three sizes
 * × five states each, Label, leading / trailing icon slots. Contract: packages/react/registry/button/registry.json.
 *
 *   <button class="pui-button">Save</button>
 *   <button class="pui-button pui-button--secondary pui-button--small">
 *     <span class="pui-icon pui-button__icon">…lucide svg…</span>Back</button>
 *   <a class="pui-button pui-button--ghost" href="…">Skip</a>
 *
 * Variant   (none) primary · --secondary · --ghost · --danger        ← the four Figma sets; the registry's `variant`
 * Size      --small 28 · (none) medium 36 · --large 44               ← size/control/* (D41); font 12 / 14 / 16
 * State     :hover · :focus-visible · :active · :disabled, [aria-disabled="true"]
 * Parts     __icon — a 16 px glyph before or after the label (Has icon leading / trailing)
 * Extra     --block — width 100 % (the site's .btn--block)
 *
 * Two things stated against the sources: the Figma pressed state paints the hover fill at .92 — the web sources press
 * with scale(.99) (styles.css:561, app.css .btn:active) and this file follows them; the ghost's rest fill is
 * surface/page in Figma and transparent here, so a ghost on a card is not a white box.
 *
 * Icon button shares every rule below (its geometry is in icon-button.css), which is why the selectors are paired.
  *
 * Rule      beside inputs or selects (the end of a filter row, a form's inline action) the button is the black one, never
 *           the outlined secondary — an outline next to outlined controls reads as one more control (D59, Fedor 2026-09-09).
*/

.pui-button,
.pui-icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--pui-space-8);
  box-sizing: border-box;
  min-height: var(--pui-size-control-medium);
  margin: 0;
  /* Figma strokes sit inside the box; a CSS border sits outside the padding. One transparent pixel on every
     button (secondary paints it) keeps all four variants the same size to the pixel. */
  padding: calc(var(--pui-space-8) - 1px) calc(var(--pui-space-16) - 1px);
  border: 1px solid transparent;
  border-radius: var(--pui-radius-8);
  background: var(--pui-action-primary-bg);
  color: var(--pui-action-primary-fg);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-500);
  line-height: var(--pui-font-line-height-normal);
  letter-spacing: var(--pui-font-letter-spacing-normal);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  -webkit-appearance: none;
  appearance: none;
  transition:
    background-color var(--pui-motion-state) var(--pui-motion-easing),
    border-color var(--pui-motion-state) var(--pui-motion-easing),
    box-shadow var(--pui-motion-state) var(--pui-motion-easing),
    transform var(--pui-duration-instant);
}
/* An <a> keeps its ink through :link / :visited — the site's lesson (styles.css:555–559). */
.pui-button:is(:link, :visited),
.pui-icon-button:is(:link, :visited) { color: var(--pui-action-primary-fg); }

.pui-button:hover,
.pui-icon-button:hover { background: var(--pui-action-primary-bg-hover); }
.pui-button:focus-visible,
.pui-icon-button:focus-visible { outline: none; box-shadow: var(--pui-ring); }
.pui-button:active,
.pui-icon-button:active { background: var(--pui-action-primary-bg-hover); transform: scale(0.99); }
.pui-button:disabled,
.pui-button[aria-disabled="true"],
.pui-icon-button:disabled,
.pui-icon-button[aria-disabled="true"] {
  background: var(--pui-action-disabled-bg);
  color: var(--pui-action-disabled-fg);
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
  pointer-events: none;
}

/* ── Secondary: outlined ─────────────────────────────────────────────────────────────────────── */
.pui-button--secondary,
.pui-icon-button--secondary {
  background: var(--pui-action-secondary-bg);
  border-color: var(--pui-action-secondary-border);
  color: var(--pui-action-secondary-fg);
}
.pui-button--secondary:is(:link, :visited),
.pui-icon-button--secondary:is(:link, :visited) { color: var(--pui-action-secondary-fg); }
.pui-button--secondary:hover,
.pui-button--secondary:active,
.pui-icon-button--secondary:hover,
.pui-icon-button--secondary:active { background: var(--pui-action-secondary-bg-hover); }
.pui-button--secondary:focus-visible,
.pui-icon-button--secondary:focus-visible { border-color: var(--pui-border-focus); background: var(--pui-action-secondary-bg); }
.pui-button--secondary:disabled,
.pui-button--secondary[aria-disabled="true"],
.pui-icon-button--secondary:disabled,
.pui-icon-button--secondary[aria-disabled="true"] {
  background: var(--pui-action-disabled-bg);
  border-color: var(--pui-border-default);
  color: var(--pui-action-disabled-fg);
}

/* ── Ghost: no chrome until hovered ──────────────────────────────────────────────────────────── */
.pui-button--ghost,
.pui-icon-button--ghost {
  background: transparent;
  color: var(--pui-action-secondary-fg);
}
.pui-button--ghost:is(:link, :visited),
.pui-icon-button--ghost:is(:link, :visited) { color: var(--pui-action-secondary-fg); }
.pui-button--ghost:hover,
.pui-button--ghost:active,
.pui-icon-button--ghost:hover,
.pui-icon-button--ghost:active { background: var(--pui-surface-sunken); }
.pui-button--ghost:focus-visible,
.pui-icon-button--ghost:focus-visible { border-color: var(--pui-border-focus); background: transparent; }
.pui-button--ghost:disabled,
.pui-button--ghost[aria-disabled="true"],
.pui-icon-button--ghost:disabled,
.pui-icon-button--ghost[aria-disabled="true"] {
  background: transparent;
  color: var(--pui-action-disabled-fg);
}

/* ── Danger: the red button (Paggy .btn--red, brand red — D35) ───────────────────────────────── */
.pui-button--danger,
.pui-icon-button--danger {
  background: var(--pui-action-danger-bg);
  color: var(--pui-action-danger-fg);
}
.pui-button--danger:is(:link, :visited),
.pui-icon-button--danger:is(:link, :visited) { color: var(--pui-action-danger-fg); }
.pui-button--danger:hover,
.pui-button--danger:active,
.pui-icon-button--danger:hover,
.pui-icon-button--danger:active { background: var(--pui-action-danger-bg-hover); }
.pui-button--danger:disabled,
.pui-button--danger[aria-disabled="true"],
.pui-icon-button--danger:disabled,
.pui-icon-button--danger[aria-disabled="true"] {
  background: var(--pui-action-disabled-bg);
  color: var(--pui-action-disabled-fg);
}

/* ── Sizes (Button's padding steps: 4/12 · 8/16 · 8/24; the icon stays 16) ──────────────────────
   The height is size/control/* (D41), so the vertical padding only has to stay under it: large keeps
   medium's 8 because Figma's 12 on a fixed 44 box would make a hugging CSS box 46 (Finding 10's overshoot). */
.pui-button--small {
  min-height: var(--pui-size-control-small);
  padding: calc(var(--pui-space-4) - 1px) calc(var(--pui-space-12) - 1px);
  font-size: var(--pui-font-size-12);
}
.pui-button--large {
  min-height: var(--pui-size-control-large);
  padding: calc(var(--pui-space-8) - 1px) calc(var(--pui-space-24) - 1px);
  font-size: var(--pui-font-size-16);
}

.pui-button__icon { flex: none; width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); }
.pui-button--block { display: flex; width: 100%; }

/* icon-button.css — Icon button · - secondary · - ghost (10_COMPONENTS.md § Actions).
 *
 * A button that is only its icon: Button's tokens on a square whose side is the control height — 28 / 36 / 44
 * (Button's small / medium / large, Input's heights) with the glyph at 16 / 16 / 24; Material keeps it its own
 * component (Type round / square), shadcn sizes it icon-sm 32 · icon 36 · icon-lg 40. Figma: 70:92 · 71:122 (secondary)
 * · 72:152 (ghost) — Shape × Size × five states, an Icon swap. Contract: registry/icon-button/registry.json.
 * The chrome (fills, states, variants) is button.css — every selector there is paired with `.pui-icon-button`.
 *
 *   <button class="pui-icon-button pui-icon-button--ghost" aria-label="Close">
 *     <span class="pui-icon">…lucide x…</span></button>
 *
 * Shape     (none) square, radius 8 · --round, radius full — for the one floating action, never the default
 * Size      --small 28 · (none) medium 36 · --large 44 (icon 24)
 * Variant   (none) primary · --secondary · --ghost · --danger
 * Rule      no label: aria-label in the markup and a Tooltip on hover — both, always.
 */

.pui-icon-button {
  flex: none;
  width: var(--pui-size-control-medium);
  height: var(--pui-size-control-medium);
  min-height: 0;
  padding: 0;
}
.pui-icon-button--round { border-radius: var(--pui-radius-full); }
.pui-icon-button--small { width: var(--pui-size-control-small); height: var(--pui-size-control-small); }
.pui-icon-button--large { width: var(--pui-size-control-large); height: var(--pui-size-control-large); }
.pui-icon-button > .pui-icon { width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); }
.pui-icon-button--large > .pui-icon { width: var(--pui-size-icon-24); height: var(--pui-size-icon-24); }

/* button-group.css — Button group (10_COMPONENTS.md § Actions).
 *
 * A row or column of buttons that belong together. Standard keeps them space/8 apart (Dialog's footer; Paggy's 10
 * and the site's 12 stated); connected joins them into one control — gap 0, inner corners 0, one shared border —
 * Bootstrap's / shadcn's button group. Figma 116:182: Variant standard / connected × Orientation × Size (the size
 * is the buttons' own). Sources: MAFO OrganisationChart.js:155, Paggy .modal .row, the site's .cookie-consent__actions,
 * shadcn button-group.tsx. Contract: registry/button-group/registry.json.
 *
 *   <div class="pui-button-group" role="group">
 *     <button class="pui-button pui-button--secondary">Export</button> …</div>
 *   <div class="pui-button-group pui-button-group--connected pui-button-group--vertical" role="group">…</div>
 *
 * Variant       (none) standard · --connected
 * Orientation   (none) horizontal · --vertical
 * Rules         two to five buttons, one size; the primary — if any — is one and last; connected is for outlined
 *               buttons (flush primaries have no seams).
 * Keyboard  role="group" is a LABELLING role, not a composite: every button keeps its own tab stop and its
 *           own Enter / Space, which is what a person expects of adjacent buttons. Nothing to write (3.5.3).
 */

.pui-button-group {
  display: inline-flex;
  align-items: center;
  gap: var(--pui-space-8);
}
.pui-button-group--vertical {
  flex-direction: column;
  align-items: stretch;
}

.pui-button-group--connected { gap: 0; }
.pui-button-group--connected > * { position: relative; }
.pui-button-group--connected > :focus-visible { z-index: 1; }
.pui-button-group--connected:not(.pui-button-group--vertical) > :not(:first-child) {
  margin-inline-start: -1px;
  border-start-start-radius: 0;
  border-end-start-radius: 0;
}
.pui-button-group--connected:not(.pui-button-group--vertical) > :not(:last-child) {
  border-start-end-radius: 0;
  border-end-end-radius: 0;
}
.pui-button-group--connected.pui-button-group--vertical > :not(:first-child) {
  margin-top: -1px;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}
.pui-button-group--connected.pui-button-group--vertical > :not(:last-child) {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}

/* toggle.css — Toggle · Toggle - filled (10_COMPONENTS.md § Actions).
 *
 * A pill that stays pressed: Paggy's library, chart and component filters (.lib-filters, .comp-modes, .chart-pills)
 * and the site's blog filters (.filter-pill). Outline = Paggy's form on Button - secondary's tokens; filled = the site's
 * (surface/sunken + text/muted, hover tint/neutral/bg). On = the site's .is-active exactly — toggle/on/bg · fg (D35).
 * Radius full: five of five sources are pills. Figma: 93:42 · 95:42 — Size small / medium × default · hover · focus ·
 * on · disabled, Label, Has icon + Icon. Contract: registry/toggle/registry.json.
 *
 *   <button class="pui-toggle" aria-pressed="true">All</button>
 *   <button class="pui-toggle pui-toggle--filled pui-toggle--small"><span class="pui-icon pui-toggle__icon">…</span>Charts</button>
 *
 * Variant   (none) outline · --filled                       ← the two Figma sets; the registry's `variant`
 * Size      --small 28 (12 px, Paggy's pills) · (none) medium 36 (14 px, the site's)
 * State     :hover · :focus-visible · on = [aria-pressed="true"] or [data-state="on"] · :disabled
 * Parts     __icon — a 16 px leading glyph
 */

.pui-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--pui-space-8);
  box-sizing: border-box;
  min-height: var(--pui-size-control-medium);
  margin: 0;
  padding: calc(var(--pui-space-8) - 1px) calc(var(--pui-space-16) - 1px);
  border: 1px solid var(--pui-action-secondary-border);
  border-radius: var(--pui-radius-full);
  background: var(--pui-action-secondary-bg);
  color: var(--pui-action-secondary-fg);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-500);
  line-height: var(--pui-font-line-height-normal);
  letter-spacing: var(--pui-font-letter-spacing-normal);
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  -webkit-appearance: none;
  appearance: none;
  transition:
    background-color var(--pui-motion-state) var(--pui-motion-easing),
    border-color var(--pui-motion-state) var(--pui-motion-easing),
    color var(--pui-motion-state) var(--pui-motion-easing),
    box-shadow var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-toggle:hover { background: var(--pui-action-secondary-bg-hover); }
.pui-toggle:focus-visible { outline: none; border-color: var(--pui-border-focus); box-shadow: var(--pui-ring); }
.pui-toggle[aria-pressed="true"],
.pui-toggle[data-state="on"] {
  background: var(--pui-toggle-on-bg);
  border-color: var(--pui-toggle-on-bg);
  color: var(--pui-toggle-on-fg);
}
.pui-toggle:disabled,
.pui-toggle[aria-disabled="true"] {
  background: var(--pui-action-disabled-bg);
  border-color: var(--pui-border-default);
  color: var(--pui-action-disabled-fg);
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}

.pui-toggle--filled {
  background: var(--pui-surface-sunken);
  border-color: transparent;
  color: var(--pui-text-muted);
}
.pui-toggle--filled:hover { background: var(--pui-tint-neutral-bg); }
.pui-toggle--filled:focus-visible { border-color: transparent; }
.pui-toggle--filled[aria-pressed="true"],
.pui-toggle--filled[data-state="on"] {
  background: var(--pui-toggle-on-bg);
  border-color: var(--pui-toggle-on-bg);
  color: var(--pui-toggle-on-fg);
}
.pui-toggle--filled:disabled,
.pui-toggle--filled[aria-disabled="true"] { border-color: transparent; }

.pui-toggle--small {
  min-height: var(--pui-size-control-small);
  padding: calc(var(--pui-space-4) - 1px) calc(var(--pui-space-12) - 1px);
  font-size: var(--pui-font-size-12);
}
.pui-toggle__icon { flex: none; width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); }

/* toggle-group.css — Toggle group (10_COMPONENTS.md § Actions).
 *
 * A row of toggles space/8 apart — Paggy's library filters (All · Covers · Sections · Content · Charts · Case
 * studies) at 8; its chart-type pills (6) and the site's blog categories (10) take space/8 here — stated. One on (type single) or any number (type multiple)
 * is behaviour; the gap is never 0 — a connected row is Button group. Figma 96:118: Variant outline / filled × Size,
 * five exposed Toggles. Contract: registry/toggle-group/registry.json.
 *
 *   <div class="pui-toggle-group" role="group" aria-label="Library filters">
 *     <button class="pui-toggle" aria-pressed="true">All</button> …</div>
 *
 * Variant / Size live on the toggles themselves (outline · --filled; --small); the group only spaces and wraps.
 * Rule      two to seven items; past seven it is a Select or a Combobox.
 * Keyboard  as Button group: role="group" takes none of its children's keys, so every toggle keeps its own
 *           tab stop and its own Space. Nothing to write (3.5.3).
 */

.pui-toggle-group {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--pui-space-8);
}

/* link.css — Link (10_COMPONENTS.md § Actions).
 *
 * A text link that stands on its own — the site's .arrow-link (global-2026.css:341): the blog's feature and hub cards,
 * the home reviews' "more", the 404 page. body/large Regular on the Typography roles (22 · 18 tablet and mobile), an
 * optional 16 glyph after the label at space/4 (Icon/arrow-right by default), text/subtle at rest and text/default on
 * hover and focus, the glyph nudged 4 px along over motion/move on the standard curve — the site's own numbers, quoted.
 * On a dark or a photographic ground (--on-dark: the gradient feature card) the ink is text/on-inverse at rest and stays
 * so on hover — the nudge is the state. The inline link inside prose is Typography's (underlined, in the ink); the
 * Navigation link is Nav link's. Request 23 from the corporate site, 2026-09-25. Figma: Link 1090:2.
 * Contract: registry/link/registry.json.
 *
 *   <a class="pui-link" href="…">Read the case study</a>
 *   <a class="pui-link pui-link--arrow" href="…">All case studies<span class="pui-icon pui-link__icon"><svg><use href="icons.svg#arrow-right"/></svg></span></a>
 *
 * Arrow     --arrow — the trailing 16 glyph (__icon), nudged on hover
 * Ground    (none) light · --on-dark — text/on-inverse on a dark or gradient ground
 */

.pui-link {
  display: inline-flex;
  align-items: center;
  gap: var(--pui-space-4);
  color: var(--pui-text-subtle);
  font-family: var(--pui-font-family);
  font-size: var(--pui-type-body-large-font-size);
  font-weight: var(--pui-type-body-large-font-weight);
  line-height: var(--pui-font-line-height-normal);
  text-decoration: none;
  cursor: pointer;
  transition: color var(--pui-motion-move) var(--pui-motion-easing);
}
.pui-link:hover,
.pui-link:focus-visible { color: var(--pui-text-default); }
.pui-link:focus-visible { outline: none; border-radius: var(--pui-radius-4); box-shadow: var(--pui-ring); }
.pui-link__icon {
  flex: none;
  width: var(--pui-size-icon-16);
  height: var(--pui-size-icon-16);
  transition: transform var(--pui-motion-move) var(--pui-motion-easing);
}
.pui-link:hover .pui-link__icon,
.pui-link:focus-visible .pui-link__icon { transform: translateX(var(--pui-space-4)); }
.pui-link--on-dark,
.pui-link--on-dark:hover,
.pui-link--on-dark:focus-visible { color: var(--pui-text-on-inverse); }
.pui-link--on-dark:focus-visible { box-shadow: 0 0 0 2px var(--pui-text-on-inverse); }
@media (prefers-reduced-motion: reduce) {
  .pui-link,
  .pui-link__icon { transition: none; }
}

/* media-control.css — Media control (10_COMPONENTS.md § Actions).
 *
 * A control that sits ON media — the site's sound pill over its muted hero videos (global-2026.css .video-sound; home,
 * MAFO, MobuppsX, i/RTB): a 40 round pill on surface/glass-dark over blur/control, a white (text/on-inverse) 14 Medium
 * label with a 20 glyph before it at space/8, padding 0 / space/16 / 0 / space/12; surface/glass-dark-strong on hover;
 * hidden until wanted — it fades in over motion/menu with a 4 px rise when the media is hovered, on keyboard focus, or
 * when the consumer's script marks the host (.is-active) after a tap — and ignores clicks while hidden, so a tap cannot
 * toggle it unseen. --icon-only (the state once sound is on: the label gone, the pill round); --compact (under 480 of
 * media width: 32 tall, a 16 glyph, a 12 label). The focus ring is 2 px text/on-inverse, outside — border/focus is for
 * white pages and disappears on a picture. The 40 and 32 heights and the 20 glyph are the pill's own, stated: the
 * control scale is 28 / 36 / 44 and the icon scale 16 / 24 / 32 / 48, and on a picture Fedor's pill is the master.
 * Request 22 from the corporate site, 2026-09-25. Figma: Media control 1090:4. Contract: registry/media-control/registry.json.
 *
 *   <div class="pui-media-host"><video …></video>
 *     <button class="pui-media-control" type="button" aria-pressed="false">
 *       <span class="pui-icon pui-media-control__icon"><svg><use href="icons.svg#volume-off"/></svg></span>
 *       <span class="pui-media-control__label">Turn on sound</span></button></div>
 *
 * State     (none) muted, label + glyph · --icon-only · the host's :hover / .is-active and the control's :focus-visible show it
 * Size      (none) 40 · --compact 32
 */

.pui-media-host { position: relative; }
.pui-media-control {
  position: absolute;
  right: var(--pui-space-16);
  bottom: var(--pui-space-16);
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--pui-space-8);
  box-sizing: border-box;
  height: 40px;
  min-width: 40px;
  margin: 0;
  padding: 0 var(--pui-space-16) 0 var(--pui-space-12);
  border: 0;
  border-radius: var(--pui-radius-full);
  background: var(--pui-surface-glass-dark);
  -webkit-backdrop-filter: blur(var(--pui-blur-control));
  backdrop-filter: blur(var(--pui-blur-control));
  color: var(--pui-text-on-inverse);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-500);
  line-height: var(--pui-font-line-height-normal);
  white-space: nowrap;
  cursor: pointer;
  opacity: 0;
  transform: translateY(var(--pui-space-4));
  pointer-events: none;
  transition: opacity var(--pui-motion-menu) var(--pui-motion-easing), transform var(--pui-motion-menu) var(--pui-motion-easing),
    background-color var(--pui-motion-move) var(--pui-motion-easing);
}
.pui-media-host:hover .pui-media-control,
.pui-media-host.is-active .pui-media-control,
.pui-media-control:focus-visible { opacity: 1; transform: none; pointer-events: auto; }
.pui-media-control:hover { background: var(--pui-surface-glass-dark-strong); }
.pui-media-control:focus-visible { outline: 2px solid var(--pui-text-on-inverse); outline-offset: 2px; }
.pui-media-control__icon { flex: none; width: 20px; height: 20px; }
.pui-media-control__label { display: block; }
.pui-media-control--icon-only { padding: 0; }
.pui-media-control--icon-only .pui-media-control__label { display: none; }
.pui-media-control--compact {
  right: var(--pui-space-8);
  bottom: var(--pui-space-8);
  gap: var(--pui-space-4);
  height: 32px;
  min-width: 32px;
  padding: 0 var(--pui-space-12) 0 var(--pui-space-8);
  font-size: var(--pui-font-size-12);
}
.pui-media-control--compact .pui-media-control__icon { width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); }
@media (prefers-reduced-motion: reduce) {
  .pui-media-control { transition: none; transform: none; }
}

/* Forms */
/* input.css — Input (10_COMPONENTS.md § Forms).
 *
 * The bare text control, quoted from the site's `.field__control` (styles.css:627–651): 36 min-height, 12 horizontal
 * padding, radius 8, 14 Regular, 1 px border, the 1/2 shadow (elevation/flat); focus = border/focus with the 3 px
 * ring at 15 %; error = border/error with the ring at 10 %; hover darkens the border to border/strong (Pajamas and
 * Material — shadcn has no hover, D17); disabled .5. Figma 33:2: three sizes × five states. G27: the site's shadcn
 * neutrals (#e5e5e5, #737373) are border/default and the caption ink here. Contract: registry/input/registry.json.
 *
 *   <input class="pui-input" type="text" aria-label="Search decks" placeholder="Search decks">   (a bare input needs a name: a <label for>, a Field, or aria-label)
 *   <input class="pui-input pui-input--small" aria-invalid="true">
 *
 * Size      --small 28 (12 px, 4 / 12) · (none) medium 36 (14 px, 8 / 12) · --large 44 (16 px, 12 / 16)
 * State     :hover · :focus · error = [aria-invalid="true"], --error, :user-invalid (a red glow at rest, and the
 *           solid error ring when it also has focus — 3.5.2) · :disabled
 * Icons     an Input has no children in HTML — a leading or trailing glyph is Input group (input-group.css),
 *           the box that holds an input with company. Label, helper and error text are Field's.
 *
 * The placeholder is the caption ink (text/caption, D43 — the site's #737373 at the step that passes 4.5 at 14 px):
 * the Figma Input binds it; its contract sentence said text/subtle and is corrected (Finding 32, closed 2026-09-08).
 */

.pui-input,
.pui-textarea {
  display: block;
  box-sizing: border-box;
  width: 100%;
  min-height: var(--pui-size-control-medium);
  margin: 0;
  padding: calc(var(--pui-space-8) - 1px) calc(var(--pui-space-12) - 1px);
  border: 1px solid var(--pui-border-default);
  border-radius: var(--pui-radius-8);
  background: var(--pui-surface-page);
  box-shadow: var(--pui-elevation-flat);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
  letter-spacing: var(--pui-font-letter-spacing-normal);
  outline: none;
  -webkit-appearance: none;
  appearance: none;
  transition:
    border-color var(--pui-motion-state) var(--pui-motion-easing),
    box-shadow var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-input::placeholder,
.pui-textarea::placeholder { color: var(--pui-text-caption); opacity: 1; }
.pui-input:hover,
.pui-textarea:hover { border-color: var(--pui-border-strong); }
.pui-input:focus,
.pui-textarea:focus { border-color: var(--pui-border-focus); box-shadow: var(--pui-ring); }
.pui-input[aria-invalid="true"],
.pui-input--error,
.pui-input:user-invalid,
.pui-textarea[aria-invalid="true"],
.pui-textarea--error,
.pui-textarea:user-invalid { border-color: var(--pui-border-error); box-shadow: var(--pui-ring-error); }
/* 3.5.2 — the error rules above come after the focus rules and win at the same specificity, so an
   invalid field showed nothing on focus. Focused, it keeps the error colour and gains the solid line. */
.pui-input[aria-invalid="true"]:focus,
.pui-input--error:focus,
.pui-input:user-invalid:focus,
.pui-textarea[aria-invalid="true"]:focus,
.pui-textarea--error:focus,
.pui-textarea:user-invalid:focus { box-shadow: var(--pui-ring-error-focus); }
.pui-input:disabled,
.pui-textarea:disabled { opacity: 0.5; cursor: not-allowed; }

.pui-input--small {
  min-height: var(--pui-size-control-small);
  padding: calc(var(--pui-space-4) - 1px) calc(var(--pui-space-12) - 1px);
  font-size: var(--pui-font-size-12);
}
.pui-input--large {
  min-height: var(--pui-size-control-large);
  padding: calc(var(--pui-space-8) - 1px) calc(var(--pui-space-16) - 1px); /* the height is size/control/large (D41) */
  font-size: var(--pui-font-size-16);
}

/* The browser's own bits inside a text input are chrome, not ours: Paggy hides the date indicator and draws a
   Lucide calendar (date-picker.css); search cancel buttons go the same way. */
.pui-input::-webkit-search-cancel-button,
.pui-input::-webkit-search-decoration { -webkit-appearance: none; appearance: none; }

/* textarea.css — Textarea (10_COMPONENTS.md § Forms).
 *
 * Input's chrome on shadcn's textarea.tsx — the site has no textarea (D17): min-height 64, padding 8 / 12, radius 8,
 * 14 Regular, the same five states. One size, no icons; the box grows with what is typed. Figma 48:59.
 * Contract: registry/textarea/registry.json. The shared rules live in input.css (every `.pui-input` selector is
 * paired with `.pui-textarea`); this file holds what differs.
 *
 *   <textarea class="pui-textarea" rows="3" aria-label="Notes" placeholder="Notes"></textarea>
 */

.pui-textarea {
  min-height: calc(var(--pui-space-64));
  padding: calc(var(--pui-space-8) - 1px) calc(var(--pui-space-12) - 1px);
  resize: vertical;
  field-sizing: content;
}

/* field.css — Field (10_COMPONENTS.md § Forms).
 *
 * The wrapper a control lives in: label above, helper or error below. Quoted from the site's `.field` (styles.css:619
 * — a column with a 4 px gap), `.field__label` (:620 — 14 Medium 500, line-height 20) and `.field__error` (:732 — 14
 * Regular, line-height 1.3, red-400 → text/error); markup from career-detail.html. Description is shadcn's
 * FieldDescription (the site has no helper text, D17). Figma 40:38: Size × default / error / disabled.
 * Contract: registry/field/registry.json.
 *
 *   <div class="pui-field">
 *     <label class="pui-field__label" for="email">Email <span class="pui-field__hint">(optional)</span></label>
 *     <input class="pui-input" id="email" type="email">
 *     <p class="pui-field__description">We reply within a day.</p>
 *     <p class="pui-field__error">Enter an email address.</p>
 *   </div>
 *
 * State     --error shows __error and puts the control in its error state · --disabled dims the texts with the
 *           control (or :has(:disabled))
 * Size      lives on the control (pui-input--small …); the label stays 14 at every size, as on the site
 * Rule      "(optional)" in the label, never an asterisk; error text says what to do, not what the label says.
 */

.pui-field {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-4);
  min-width: 0;
}
.pui-field__label {
  display: inline-flex;
  align-items: center;
  gap: var(--pui-space-4);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-500);
  line-height: var(--pui-font-line-height-normal);
}
.pui-field__hint { color: var(--pui-text-muted); font-weight: var(--pui-font-weight-400); }
.pui-field__description {
  margin: 0;
  color: var(--pui-text-muted);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
}
.pui-field__error {
  display: none;
  margin: 0;
  color: var(--pui-text-error);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-400);
  line-height: 1.3; /* the site's .field__error (styles.css:737); not a token step */
}
.pui-field--error .pui-field__error,
.pui-field:has([aria-invalid="true"]) .pui-field__error { display: block; }
.pui-field--error .pui-input,
.pui-field--error .pui-textarea,
.pui-field--error .pui-select,
.pui-field--error .pui-native-select > select,
.pui-field--error .pui-combobox,
.pui-field--error .pui-input-group { border-color: var(--pui-border-error); box-shadow: var(--pui-ring-error); }
.pui-field--error .pui-input-group:focus-within { box-shadow: var(--pui-ring-error-focus); }  /* 3.5.2 */
.pui-field--disabled .pui-field__label,
.pui-field--disabled .pui-field__description,
.pui-field:has(:disabled) .pui-field__label,
.pui-field:has(:disabled) .pui-field__description { opacity: 0.5; }

/* label.css — Label (10_COMPONENTS.md § Forms).
 *
 * The name of a control on its own — beside a checkbox or a switch, in a toolbar, in a dialog — the same text Field
 * draws as its first child: the site's `.field__label` (styles.css:620 — 14 Medium 500, line-height 20, #0a0a0a →
 * text/default by D4); shadcn label.tsx second (D17). Hint is '(optional)' in text/muted — the site's word; no
 * required mark exists. Figma 212:15: State default / disabled. Contract: registry/label/registry.json.
 *
 *   <label class="pui-label" for="name">Name <span class="pui-label__hint">(optional)</span></label>
 */

.pui-label {
  display: inline-flex;
  align-items: center;
  gap: var(--pui-space-4);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-500);
  line-height: var(--pui-font-line-height-normal);
}
.pui-label__hint { color: var(--pui-text-muted); font-weight: var(--pui-font-weight-400); }
.pui-label--disabled { opacity: 0.5; }

/* input-group.css — Input group (10_COMPONENTS.md § Forms).
 *
 * Input with company — a prefix, a suffix, a leading icon, a button — inside one box that behaves like Input. The
 * site's `.subscribe-field` (blog-article.css:730 — the shared 'white box = email input + black submit') made general:
 * gap 8, padding 4 · 4 · 4 · 12 (D47: a nested control sits at one equal inset — 4 — on top, bottom and right), white,
 * a 1 px border, radius 8, elevation/flat; the input inside is transparent and borderless; the button is the primary
 * Button one size below the box at radius 4 (D47). shadcn input-group.tsx gives the API (D17). Figma 215:182: Size ×
 * five states on the box. Contract: registry/input-group/registry.json.
 *
 *   <label class="pui-input-group">
 *     <span class="pui-icon pui-input-group__icon">…search…</span>
 *     <span class="pui-input-group__prefix">https://</span>
 *     <input class="pui-input-group__control" type="text" placeholder="Your email">
 *     <span class="pui-input-group__suffix">.com</span>
 *     <button class="pui-button pui-button--small pui-input-group__button">Subscribe</button>
 *   </label>
 *
 * Size      --small 28 (12 px, no button — no 20 px Button exists) · (none) medium 36 (button small) · --large 44
 *           (16 px, button medium)
 * State     :hover · :focus-within · error = --error or :has([aria-invalid="true"]) (+ the error ring when focused,
 *           3.5.2) · disabled = --disabled or
 *           :has(:disabled)
 * Ground    (none) light · --on-dark — the same box on surface/inverse (the footer's email field, request 33): the wash
 *           color/alpha/white-8, the line nav/item/bg-active, the ink nav/item/fg-active, the placeholder nav/section/fg,
 *           and the Button in it turned over — text/on-inverse under text/default, with the inset ring
 * Parts     __icon (16, text/muted) · __prefix / __suffix (the caption ink, the text's size) · __control · __button
 * Rule      one box, one border — never two bordered controls pretending to be one; the nested button takes
 *           radius 4 and the 4 px inset, never the text padding.
 */

.pui-input-group {
  display: flex;
  align-items: center;
  gap: var(--pui-space-8);
  box-sizing: border-box;
  width: 100%;
  min-height: var(--pui-size-control-medium);
  padding: calc(var(--pui-space-4) - 1px);
  padding-inline-start: calc(var(--pui-space-12) - 1px);
  border: 1px solid var(--pui-border-default);
  border-radius: var(--pui-radius-8);
  background: var(--pui-surface-page);
  box-shadow: var(--pui-elevation-flat);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
  cursor: text;
  transition:
    border-color var(--pui-motion-state) var(--pui-motion-easing),
    box-shadow var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-input-group:hover { border-color: var(--pui-border-strong); }
.pui-input-group:focus-within { border-color: var(--pui-border-focus); box-shadow: var(--pui-ring); }
.pui-input-group--error,
.pui-input-group:has([aria-invalid="true"]) { border-color: var(--pui-border-error); box-shadow: var(--pui-ring-error); }
.pui-input-group--error:focus-within,
.pui-input-group:has([aria-invalid="true"]):focus-within { box-shadow: var(--pui-ring-error-focus); }  /* 3.5.2 */
.pui-input-group--disabled,
.pui-input-group:has(.pui-input-group__control:disabled) { opacity: 0.5; cursor: not-allowed; }

.pui-input-group__control {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  outline: none;
  -webkit-appearance: none;
  appearance: none;
}
.pui-input-group__control::placeholder { color: var(--pui-text-caption); opacity: 1; }
.pui-input-group__icon { flex: none; width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); color: var(--pui-text-muted); }
.pui-input-group__prefix,
.pui-input-group__suffix { flex: none; color: var(--pui-text-caption); white-space: nowrap; }
.pui-input-group__suffix { padding-inline-end: var(--pui-space-8); }
.pui-input-group__button { flex: none; align-self: stretch; margin-inline-start: auto; border-radius: var(--pui-radius-4); }
.pui-input-group__button.pui-button:focus-visible { box-shadow: var(--pui-ring); }

.pui-input-group--small {
  min-height: var(--pui-size-control-small);
  padding: calc(var(--pui-space-4) - 1px) calc(var(--pui-space-12) - 1px);
  font-size: var(--pui-font-size-12);
}
.pui-input-group--small .pui-input-group__button { display: none; }
.pui-input-group--large {
  min-height: var(--pui-size-control-large);
  padding-inline-start: calc(var(--pui-space-16) - 1px);
  font-size: var(--pui-font-size-16);
}

/* ON A DARK GROUND (request 33; Fedor, 2026-09-28: 'this must be the same component … the only difference must be the
   colour'). The footer's email field is this box, not a drawing of its own: the four colours the Footer gave its field,
   and the Button turned over. Hover, focus and error are the box's own and read on surface/inverse. */
.pui-input-group--on-dark {
  border-color: var(--pui-nav-item-bg-active);
  background: var(--pui-color-alpha-white-8);
  box-shadow: none;
  color: var(--pui-nav-item-fg-active);
}
.pui-input-group--on-dark .pui-input-group__control::placeholder,
.pui-input-group--on-dark :is(.pui-input-group__prefix, .pui-input-group__suffix, .pui-input-group__icon) { color: var(--pui-nav-section-fg); }
.pui-input-group--on-dark .pui-input-group__button.pui-button { background: var(--pui-text-on-inverse); color: var(--pui-text-default); }
.pui-input-group--on-dark .pui-input-group__button.pui-button:is(:hover, :active) { background: var(--pui-action-secondary-bg-hover); }
.pui-input-group--on-dark .pui-input-group__button.pui-button:focus-visible { box-shadow: var(--pui-ring-inset); }

/* form.css — Form (10_COMPONENTS.md § Forms).
 *
 * The form itself — the kit named the parts (Field, Input, Checkbox, Button, Form result, Card) and every consumer
 * wrote the form its own way; this is the one structure, read off the site's form block (blocks/form-2026), Fedor's:
 * a column at space/24 › the title (the Card's title) › the spam trap › the Fields, one per control › the consent (a
 * Checkbox in a Field, with its message) › ONE primary Button at full width › the Form result, which takes the
 * fields' place once the form is sent. The card is part of the form ('there is no form without a card', Fedor
 * 2026-09-28): a Card in one of three finishes — --lined, --raised, --plain — at the form's inset (card.css).
 * Two parts a form needs that no control had: the FILE control (a label drawn as the Input around a hidden file
 * input, the file's name in the caption ink, a paperclip at its end) and the SENDING state of the Button
 * (aria-busy — it dims and cannot be pressed twice; the label says 'Sending…'). Request 34 from the corporate site,
 * 2026-09-28. Figma: Form. Contract: registry/form/registry.json.
 *
 *   <div class="pui-card pui-card--lined"><form class="pui-form" novalidate>
 *     <h3 class="pui-card__title">Get in touch with us to know more</h3>
 *     <input class="pui-form__trap" type="text" name="surname" tabindex="-1" autocomplete="off" aria-hidden="true">
 *     <div class="pui-field"><label class="pui-field__label" for="f-name">Name</label>
 *       <input class="pui-input" id="f-name" required><span class="pui-field__error">Please enter your name</span></div>
 *     <div class="pui-field"><span class="pui-field__label">CV</span>
 *       <label class="pui-input pui-input--file"><span class="pui-input__file-name">No file chosen</span>
 *         <span class="pui-icon pui-input__file-icon"><svg><use href="icons.svg#paperclip"/></svg></span>
 *         <input type="file"></label></div>
 *     <div class="pui-field"><label class="pui-checkbox"> …the consent… </label><span class="pui-field__error">…</span></div>
 *     <button class="pui-button pui-button--block" type="submit">Book a demo</button>
 *     <div class="pui-form-result" role="status" aria-live="polite"> … </div></form></div>
 *
 * State     .is-success on the form — the parts leave and the Form result shows in their place
 *           aria-busy="true" (with disabled) on the Button — sending
 * Parts     __trap — the spam trap, never shown · .pui-input--file with __file-name and __file-icon
 * Rule      one primary action; the order of the parts never changes; a failure is an Alert above the Button, not the result
 */

.pui-form {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-24);
  min-width: 0;
  margin: 0;
  font-family: var(--pui-font-family);
}
.pui-form__trap { display: none; }

/* the result takes the fields' place */
.pui-form > .pui-form-result { display: none; }
.pui-form.is-success > :not(.pui-form-result) {
  pointer-events: none;
  animation: pui-form-out var(--pui-motion-menu) var(--pui-motion-easing) forwards;
}
.pui-form.is-success > .pui-form-result {
  display: flex;
  animation: pui-form-result-in var(--pui-motion-menu) var(--pui-motion-easing) var(--pui-motion-state) both;
}
@keyframes pui-form-out { to { opacity: 0; display: none; } }
@keyframes pui-form-result-in { from { opacity: 0; transform: translateY(var(--pui-space-8)); } }
@media (prefers-reduced-motion: reduce) {
  .pui-form.is-success > :not(.pui-form-result) { animation: none; display: none; }
  .pui-form.is-success > .pui-form-result { animation: none; }
}

/* the file control — the Input's box as a label around the native input, which is hidden and still focusable */
.pui-input--file {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--pui-space-8);
  overflow: hidden;
  cursor: pointer;
}
.pui-input--file > input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.pui-input--file:focus-within { border-color: var(--pui-border-focus); box-shadow: var(--pui-ring); }
.pui-input__file-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  color: var(--pui-text-caption);
  white-space: nowrap;
  text-overflow: ellipsis;
}
.pui-input__file-name :is(b, strong) { font-weight: var(--pui-font-weight-600); }
.pui-input__file-icon { flex: none; width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); color: var(--pui-text-muted); }

/* sending — the Button between the press and the answer */
.pui-button[aria-busy="true"] {
  background: var(--pui-action-primary-bg);   /* it keeps its fill — a sending button is not a disabled one; set `disabled` with it so it leaves the tab order */
  color: var(--pui-action-primary-fg);
  opacity: 0.5;
  cursor: progress;
  pointer-events: none;
  transform: none;
}

/* select.css — Select · Select menu · Select item (10_COMPONENTS.md § Forms).
 *
 * One choice from a list that opens on demand. The trigger is the site file's Select & Combobox master (2334:683):
 * Input's chrome — 36, radius 8, padding 8 / 12, 14 Regular, 1 px border, elevation/flat — with a 16 px Lucide
 * chevron-down in text/muted at the right, an optional leading icon and an optional prefix ("Sort:"). shadcn's
 * SelectTrigger is the same box (h-9, px-3 py-2, chevron size-4 muted). The menu is shadcn's SelectContent (D17):
 * surface/page, border/default, radius 8, padding 4, elevation/floating, the trigger's width, opening 4 px below over
 * motion/menu; a row is SelectItem — padding 8 (36 tall, the medium trigger's height), radius 4, 14 Regular, an
 * optional 16 px icon, a check at the right when selected, hover surface/sunken, disabled .5. Figma: Select 50:2 ·
 * Select item 51:33 · Select menu 52:2. Contracts: registry/select/registry.json.
 *
 *   <button class="pui-select" type="button" aria-haspopup="listbox" aria-expanded="false">
 *     <span class="pui-select__prefix">Sort:</span>
 *     <span class="pui-select__value pui-select__placeholder">Choose…</span>
 *     <span class="pui-icon pui-select__chevron">…chevron-down…</span></button>
 *   <div class="pui-select-menu" role="listbox">
 *     <div class="pui-select-menu__label">Recent</div>
 *     <div class="pui-select-item" role="option" aria-selected="true">Newest first
 *       <span class="pui-icon pui-select-item__check">…check…</span></div>
 *     <div class="pui-select-menu__separator" role="separator"></div> …</div>
 *
 * Trigger   Size --small 28 · (none) medium 36 · --large 44; states :hover · :focus-visible / [aria-expanded="true"]
 *           · error = [aria-invalid="true"], --error · :disabled; parts __icon (leading), __prefix, __value,
 *           __placeholder (the caption ink), __chevron
 * Menu      __label (12, text/subtle) · __separator (1 px border/default, 4 above and below) · items
 * Item      selected = [aria-selected="true"] shows __check · highlighted = :hover, :focus, [data-highlighted];
 *           keyboard focus adds the inset ring on top of the shared surface (3.5.2, as Menu item)
 *           (keyboard and pointer share the surface, as shadcn) · [aria-disabled="true"] .5; parts __icon, __check
 * Placing the menu (4 px under the trigger, the trigger's width) is the code side's — this file styles the surfaces.
 * Keyboard  the trigger is the tab stop; the menu takes tabindex="-1" and so does every item (3.5.3). The script
 *           owes Up / Down, Home / End, Enter / Space to select, Escape to close, and typeahead. Combobox is the
 *           same list with the INPUT holding the tab stop and naming the active option with aria-activedescendant.
 */

.pui-select,
.pui-combobox {
  display: inline-flex;
  align-items: center;
  gap: var(--pui-space-8);
  box-sizing: border-box;
  width: 100%;
  min-height: var(--pui-size-control-medium);
  margin: 0;
  padding: calc(var(--pui-space-8) - 1px) calc(var(--pui-space-12) - 1px);
  border: 1px solid var(--pui-border-default);
  border-radius: var(--pui-radius-8);
  background: var(--pui-surface-page);
  --pui-select-fill: var(--pui-surface-page);
  box-shadow: var(--pui-elevation-flat);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
  letter-spacing: var(--pui-font-letter-spacing-normal);
  text-align: start;
  cursor: pointer;
  outline: none;
  -webkit-appearance: none;
  appearance: none;
  transition:
    border-color var(--pui-motion-state) var(--pui-motion-easing),
    box-shadow var(--pui-motion-state) var(--pui-motion-easing);
}
/* --filled: the trigger on surface/card, so a form on a white page can tell its dropdowns from its inputs — Fedor's call on the
   site's blog and careers filters, 'for visibility' (request 19, 2026-09-25). Native select carries the same option. */
.pui-select--filled { background: var(--pui-surface-card); }
.pui-select:hover,
.pui-combobox:hover { border-color: var(--pui-border-strong); }
.pui-select:focus-visible,
.pui-select[aria-expanded="true"],
.pui-combobox:focus-visible,
.pui-combobox:focus-within,
.pui-combobox:has(> [aria-expanded="true"]) { border-color: var(--pui-border-focus); box-shadow: var(--pui-ring); }
.pui-select[aria-invalid="true"],
.pui-select--error,
.pui-combobox[aria-invalid="true"],
.pui-combobox--error { border-color: var(--pui-border-error); box-shadow: var(--pui-ring-error); }
.pui-select[aria-invalid="true"]:focus-visible,
.pui-select--error:focus-visible,
.pui-combobox[aria-invalid="true"]:focus-within,
.pui-combobox--error:focus-within { box-shadow: var(--pui-ring-error-focus); }  /* 3.5.2 */
.pui-select:disabled,
.pui-select[aria-disabled="true"],
.pui-combobox:disabled,
.pui-combobox[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; pointer-events: none; }

.pui-select__value,
.pui-combobox__value { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pui-select__placeholder,
.pui-combobox__placeholder { color: var(--pui-text-caption); }
.pui-select__prefix,
.pui-combobox__prefix { flex: none; color: var(--pui-text-caption); }
.pui-select__icon,
.pui-combobox__icon { flex: none; width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); color: var(--pui-text-muted); }
.pui-select__chevron,
.pui-combobox__chevron { flex: none; width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); margin-inline-start: auto; color: var(--pui-text-muted); }

.pui-select--small,
.pui-combobox--small {
  min-height: var(--pui-size-control-small);
  padding: calc(var(--pui-space-4) - 1px) calc(var(--pui-space-12) - 1px);
  font-size: var(--pui-font-size-12);
}
.pui-select--large,
.pui-combobox--large {
  min-height: var(--pui-size-control-large);
  padding: calc(var(--pui-space-8) - 1px) calc(var(--pui-space-16) - 1px); /* the height is size/control/large (D41) */
  font-size: var(--pui-font-size-16);
}

/* ── The menu ────────────────────────────────────────────────────────────────────────────────── */
.pui-select-menu,
.pui-combobox-menu,
.pui-dropdown-menu {
  box-sizing: border-box;
  min-width: 100%;
  width: max-content;
  max-width: 100vw;
  padding: var(--pui-space-4);
  border: 1px solid var(--pui-border-default);
  border-radius: var(--pui-radius-8);
  background: var(--pui-surface-page);
  box-shadow: var(--pui-elevation-floating);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  line-height: var(--pui-font-line-height-normal);
}
.pui-select-menu[hidden],
.pui-combobox-menu[hidden],
.pui-dropdown-menu[hidden] { display: none; }
.pui-select-menu__label,
.pui-dropdown-menu__group-label {
  padding: var(--pui-space-4) var(--pui-space-8);
  color: var(--pui-text-caption);
  font-size: var(--pui-font-size-12);
  font-weight: var(--pui-font-weight-400);
}
.pui-select-menu__separator,
.pui-dropdown-menu__separator {
  height: 1px;
  margin: var(--pui-space-4) 0;
  background: var(--pui-border-default);
}

/* ── One row ─────────────────────────────────────────────────────────────────────────────────── */
.pui-select-item {
  display: flex;
  align-items: center;
  gap: var(--pui-space-8);
  box-sizing: border-box;
  width: 100%;
  margin: 0;
  padding: var(--pui-space-8);
  border: 0;
  border-radius: var(--pui-radius-4);
  background: transparent;
  color: var(--pui-text-default);
  font: inherit;
  font-weight: var(--pui-font-weight-400);   /* R13 — `font: inherit` took the page's weight; the site's rows rendered Light (request 9) */
  text-align: start;
  cursor: pointer;
  outline: none;
  user-select: none;
}
.pui-select-item:hover,
.pui-select-item:focus,
.pui-select-item:focus-visible,
.pui-select-item[data-highlighted] { background: var(--pui-surface-sunken); }
.pui-select-item:focus-visible { box-shadow: var(--pui-ring-inset); }   /* 3.5.2, as Menu item */
.pui-select-item[aria-disabled="true"],
.pui-select-item:disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; }
.pui-select-item__icon { flex: none; width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); color: var(--pui-text-muted); }
.pui-select-item__label { flex: 1 1 auto; min-width: 0; }
.pui-select-item__check {
  flex: none;
  width: var(--pui-size-icon-16);
  height: var(--pui-size-icon-16);
  margin-inline-start: auto;
  visibility: hidden;
}
.pui-select-item[aria-selected="true"] .pui-select-item__check,
.pui-select-item[data-state="checked"] .pui-select-item__check { visibility: visible; }

/* native-select.css — Native select (10_COMPONENTS.md § Forms).
 *
 * The browser's <select>, styled as far as CSS reaches: the box is Select's trigger; the list that opens is the
 * browser's. The site's admin lab writes `<select class="form-control">` (adminlab create.blade.php:52) and its careers
 * form fixes the native arrow by hand (careers create.blade.php:34–48: appearance none, padding-right 2rem, a chevron
 * inset 12 px to match the 12 px text padding) — that fix, drawn once. shadcn native-select.tsx second (D17). Never
 * on a Paggy or MAFO screen (Paggy DESIGN_SYSTEM.md:369). Figma 218:46. Contract: registry/native-select/registry.json.
 *
 *   <span class="pui-native-select"><select aria-label="Category"><option value="" disabled selected>Choose…</option>…</select></span>
 *
 * Size      --small · (none) medium · --large — on the wrapper, mirroring Select
 * State     the select's own: :hover · :focus · [aria-invalid="true"] (+ the error ring when focused, 3.5.2) · :disabled
 * The chevron is a mask on the wrapper's ::after (Lucide chevron-down) painted in text/muted — a select cannot hold
 * a child, and an SVG in a background-image cannot take a token colour; a mask can.
 */

.pui-native-select {
  position: relative;
  display: block;
  width: 100%;
  font-family: var(--pui-font-family);
}
.pui-native-select--filled > select { background-color: var(--pui-surface-card); }   /* request 19 — Select's --filled, the same box drawn twice */
.pui-native-select > select {
  display: block;
  box-sizing: border-box;
  width: 100%;
  min-height: var(--pui-size-control-medium);
  margin: 0;
  padding: calc(var(--pui-space-8) - 1px) var(--pui-space-40) calc(var(--pui-space-8) - 1px) calc(var(--pui-space-12) - 1px);
  border: 1px solid var(--pui-border-default);
  border-radius: var(--pui-radius-8);
  background: var(--pui-surface-page);
  box-shadow: var(--pui-elevation-flat);
  color: var(--pui-text-default);
  font: inherit;
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
  cursor: pointer;
  outline: none;
  -webkit-appearance: none;
  appearance: none;
  transition:
    border-color var(--pui-motion-state) var(--pui-motion-easing),
    box-shadow var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-native-select > select:hover { border-color: var(--pui-border-strong); }
.pui-native-select > select:focus { border-color: var(--pui-border-focus); box-shadow: var(--pui-ring); }
.pui-native-select > select[aria-invalid="true"],
.pui-native-select--error > select { border-color: var(--pui-border-error); box-shadow: var(--pui-ring-error); }
.pui-native-select > select[aria-invalid="true"]:focus,
.pui-native-select--error > select:focus { box-shadow: var(--pui-ring-error-focus); }  /* 3.5.2 */
.pui-native-select > select:disabled { opacity: 0.5; cursor: not-allowed; }
.pui-native-select > select:has(option[value=""]:checked) { color: var(--pui-text-caption); }
.pui-native-select::after {
  content: "";
  position: absolute;
  top: 50%;
  right: var(--pui-space-12);
  width: var(--pui-size-icon-16);
  height: var(--pui-size-icon-16);
  transform: translateY(-50%);
  background: var(--pui-text-muted);
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='m6 9 6 6 6-6'/></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='m6 9 6 6 6-6'/></svg>") center / contain no-repeat;
  pointer-events: none;
}
.pui-native-select--small > select {
  min-height: var(--pui-size-control-small);
  padding-top: calc(var(--pui-space-4) - 1px);
  padding-bottom: calc(var(--pui-space-4) - 1px);
  font-size: var(--pui-font-size-12);
}
.pui-native-select--large > select {
  min-height: var(--pui-size-control-large);
  padding: calc(var(--pui-space-8) - 1px) var(--pui-space-40) calc(var(--pui-space-8) - 1px) calc(var(--pui-space-16) - 1px);
  font-size: var(--pui-font-size-16);
}

/* combobox.css — Combobox · Combobox menu (10_COMPONENTS.md § Forms).
 *
 * A Select you can type into. The trigger is Select's (select.css pairs every selector with `.pui-combobox`) with
 * chevrons-up-down in place of chevron-down — shadcn's cue that this one is typed into; Paggy's share-modal people
 * search (app.css:1413) is the house precedent, the Cabinet's react-select what it replaces. The menu is Select
 * menu's surface with shadcn's CommandInput on top: a search row (padding 8, gap 8, the glyph 16 in text/subtle, the
 * query at 14, borderless) over a 1 px border/default line, the rows padded 4 above, an empty row ('No results.',
 * padding 8, centred). Figma: 221:9 · 222:49. Contracts: registry/combobox/registry.json.
 *
 *   <div class="pui-combobox">
 *     <input class="pui-combobox__value" role="combobox" aria-expanded="false" aria-controls="menu-id" aria-autocomplete="list" aria-label="Account" placeholder="Pick an account">
 *     (ARIA 1.2: the role, the expanded state and the name sit on the input, not on the box — axe, M2.3.5)
 *     <span class="pui-icon pui-combobox__chevron">…chevrons-up-down…</span></div>
 *   <div class="pui-combobox-menu">
 *     <div class="pui-combobox-menu__search"><span class="pui-icon">…search…</span><input aria-label="Search accounts" placeholder="Search…"></div>
 *     <div class="pui-combobox-menu__items" role="listbox"> …pui-select-item… </div>
 *     <div class="pui-combobox-menu__empty">No results.</div></div>
 *
 * Rule      one row highlighted at a time — the first visible one when the list opens (data-highlighted).
 * Keyboard  the input holds the tab stop and carries aria-expanded, aria-controls and, while open,
 *           aria-activedescendant; every option in the popup takes tabindex="-1" (3.5.3). The script owes Down to
 *           open and move, Up / Down inside, Enter to select, Escape to close then clear.
 */

.pui-combobox > input.pui-combobox__value {
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  outline: none;
  -webkit-appearance: none;
  appearance: none;
}
.pui-combobox > input.pui-combobox__value::placeholder { color: var(--pui-text-caption); opacity: 1; }

.pui-combobox-menu { overflow: hidden; }
.pui-combobox-menu__search {
  display: flex;
  align-items: center;
  gap: var(--pui-space-8);
  padding: var(--pui-space-8);
  border-bottom: 1px solid var(--pui-border-default);
  color: var(--pui-text-subtle);
}
.pui-combobox-menu__search > .pui-icon { flex: none; }
.pui-combobox-menu__search > input {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--pui-text-default);
  font: inherit;
  outline: none;
  -webkit-appearance: none;
  appearance: none;
}
.pui-combobox-menu__search > input::placeholder { color: var(--pui-text-caption); opacity: 1; }
/* 3.5.2 — the search input clears its own outline and had nothing in its place: focus was invisible.
   The ring goes on the row (as Input group's does on its wrapper) and is the inset one, because the
   menu clips (`.pui-combobox-menu { overflow: hidden }`). */
.pui-combobox-menu__search:focus-within { box-shadow: var(--pui-ring-inset); }
.pui-combobox-menu__items { padding-top: var(--pui-space-4); }
.pui-combobox-menu__empty {
  padding: var(--pui-space-8);
  color: var(--pui-text-muted);
  text-align: center;
}

/* checkbox.css — Checkbox (10_COMPONENTS.md § Forms).
 *
 * Quoted from the site's `.checkbox` (styles.css:682–730): a 16 px box, radius 4, 1 px border, white, elevation/flat;
 * checked fills the button black (action/primary/bg) with a white check; label 14 Regular, line-height 20, 8 px gap;
 * the native input stays in the DOM, visually hidden. Indeterminate is a minus (shadcn / Radix). Hover darkens the
 * border, or the fill to bg-hover when checked; focus is the ring; error is border/error — the whole box when checked
 * (the master's rule). Figma 43:2: Checked false / true / indeterminate × five states.
 * Contract: registry/checkbox/registry.json.
 *
 *   <label class="pui-checkbox">
 *     <input class="pui-checkbox__input pui-sr-only" type="checkbox">
 *     <span class="pui-checkbox__box"></span>
 *     <span class="pui-checkbox__label">I agree to the privacy policy</span></label>
 *
 * State     :hover · :focus-visible · :checked · :indeterminate · error = --error or [aria-invalid="true"] on the
 *           input · :disabled (the row dims to .5)
 * Has label the __label span is optional — in a table header the column is the label (aria-label on the input)
 * The glyphs are masks (Lucide check and minus) painted in action/primary/fg, so no hex lives in this file.
 * Keyboard  nothing to write for Radio: native inputs sharing a `name` ARE a radio group and the browser gives
 *           the roving tabindex and the arrow keys. Checkbox takes Space, also the browser's (3.5.3).
 */

.pui-checkbox,
.pui-radio {
  display: inline-flex;
  align-items: flex-start;
  gap: var(--pui-space-8);
  position: relative;
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
  cursor: pointer;
  user-select: none;
}
.pui-checkbox__box,
.pui-radio__box {
  position: relative;
  flex: none;
  box-sizing: border-box;
  width: var(--pui-size-icon-16);
  height: var(--pui-size-icon-16);
  margin-top: var(--pui-space-2);
  border: 1px solid var(--pui-border-default);
  border-radius: var(--pui-radius-4);
  background: var(--pui-surface-page);
  box-shadow: var(--pui-elevation-flat);
  transition:
    background-color var(--pui-motion-state) var(--pui-motion-easing),
    border-color var(--pui-motion-state) var(--pui-motion-easing),
    box-shadow var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-checkbox__label,
.pui-radio__label { min-width: 0; }

.pui-checkbox:hover .pui-checkbox__box,
.pui-radio:hover .pui-radio__box { border-color: var(--pui-border-strong); }
.pui-checkbox__input:focus-visible + .pui-checkbox__box,
.pui-radio__input:focus-visible + .pui-radio__box { border-color: var(--pui-border-focus); box-shadow: var(--pui-ring); }

.pui-checkbox__input:checked + .pui-checkbox__box,
.pui-checkbox__input:indeterminate + .pui-checkbox__box {
  background: var(--pui-action-primary-bg);
  border-color: var(--pui-action-primary-bg);
}
.pui-checkbox:hover .pui-checkbox__input:checked + .pui-checkbox__box,
.pui-checkbox:hover .pui-checkbox__input:indeterminate + .pui-checkbox__box {
  background: var(--pui-action-primary-bg-hover);
  border-color: var(--pui-action-primary-bg-hover);
}
.pui-checkbox__box::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--pui-action-primary-fg);
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><path d='M20 6 9 17l-5-5'/></svg>") center / 12px no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><path d='M20 6 9 17l-5-5'/></svg>") center / 12px no-repeat;
  opacity: 0;
  transition: opacity var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-checkbox__input:checked + .pui-checkbox__box::after { opacity: 1; }
.pui-checkbox__input:indeterminate + .pui-checkbox__box::after {
  opacity: 1;
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round'><path d='M5 12h14'/></svg>");
  mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round'><path d='M5 12h14'/></svg>");
}

/* Error: the border, or the whole box when checked. */
.pui-checkbox--error .pui-checkbox__box,
.pui-checkbox__input[aria-invalid="true"] + .pui-checkbox__box,
.pui-radio--error .pui-radio__box,
.pui-radio__input[aria-invalid="true"] + .pui-radio__box { border-color: var(--pui-border-error); }
.pui-checkbox--error .pui-checkbox__input:checked + .pui-checkbox__box,
.pui-checkbox__input[aria-invalid="true"]:checked + .pui-checkbox__box,
.pui-checkbox--error .pui-checkbox__input:indeterminate + .pui-checkbox__box,
.pui-checkbox__input[aria-invalid="true"]:indeterminate + .pui-checkbox__box {
  background: var(--pui-border-error);
  border-color: var(--pui-border-error);
}

.pui-checkbox:has(.pui-checkbox__input:disabled),
.pui-checkbox--disabled,
.pui-radio:has(.pui-radio__input:disabled),
.pui-radio--disabled { opacity: 0.5; cursor: not-allowed; }

/* radio.css — Radio (10_COMPONENTS.md § Forms).
 *
 * Exactly one choice from a short set. From the site file's Radio master (2134:1372): a 16 px circle, 1 px
 * border/default, white, an 8 px dot in the button black (action/primary/bg) when selected — shadcn's RadioGroupItem
 * is the same circle and dot. Label 14 Regular, 8 px gap, as the checkbox; the shared rules live in checkbox.css
 * (every `.pui-checkbox` selector is paired with `.pui-radio`). Figma 45:2: Checked × five states; error marks the
 * border, the dot stays. Contract: registry/radio-group/registry.json.
 *
 *   <fieldset class="pui-radio-group"><legend class="pui-label">Billing</legend>
 *     <label class="pui-radio"><input class="pui-radio__input pui-sr-only" type="radio" name="billing">
 *       <span class="pui-radio__box"></span><span class="pui-radio__label">Monthly</span></label> …</fieldset>
 *
 * Group     .pui-radio-group — a fieldset with a legend, items stacked at shadcn's 12 (space/12)
 */

.pui-radio__box { border-radius: var(--pui-radius-full); }
.pui-radio__box::after {
  content: "";
  position: absolute;
  inset: 50% auto auto 50%;
  width: var(--pui-space-8);
  height: var(--pui-space-8);
  border-radius: var(--pui-radius-full);
  background: var(--pui-action-primary-bg);
  transform: translate(-50%, -50%) scale(0);
  transition: transform var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-radio__input:checked + .pui-radio__box::after { transform: translate(-50%, -50%) scale(1); }
.pui-radio:hover .pui-radio__input:checked + .pui-radio__box::after { background: var(--pui-action-primary-bg-hover); }

.pui-radio-group {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-12);
  margin: 0;
  padding: 0;
  border: 0;
  min-width: 0;
}
.pui-radio-group > legend { padding: 0; margin-bottom: var(--pui-space-4); }
.pui-radio-group--horizontal { flex-direction: row; flex-wrap: wrap; gap: var(--pui-space-16); }

/* switch.css — Switch (10_COMPONENTS.md § Forms).
 *
 * A setting that takes effect at once. shadcn's switch.tsx — there is no Mobupps source; the Cabinet's CFormSwitch
 * is CoreUI stock (D17): a 32 × 18 track (shadcn's w-8 × h-[1.15rem]; Figma draws 18), radius full, a 16 px white
 * thumb that travels to the far edge; small is 24 × 14 with a 12 px thumb. On = action/primary/bg, off =
 * border/default (the input-border grey); hover darkens (border/strong off, bg-hover on); focus is the ring;
 * disabled .5. Four states by design — no pressed, no error. Figma 47:2. Contract: registry/switch/registry.json.
 *
 *   <label class="pui-switch">
 *     <input class="pui-switch__input pui-sr-only" type="checkbox" role="switch">
 *     <span class="pui-switch__track"></span>
 *     <span class="pui-switch__label">Email me a weekly report</span></label>
 *
 * Size      (none) medium 32 × 18 · --small 24 × 14
 * Button    a switch that is one <button role="switch" aria-checked> (shadcn's, Base UI's, Paggy's .tumbler — 2.4.5):
 *           the button carries __track itself and shows its state by aria-checked / data-state="checked".
 * Rule      the label names the setting; the switch shows its state — never "Enabled" / "Disabled".
 */

.pui-switch {
  display: inline-flex;
  align-items: center;
  gap: var(--pui-space-8);
  position: relative;
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
  cursor: pointer;
  user-select: none;
}
.pui-switch__track {
  --pui-switch-w: 32px;  /* shadcn w-8 */
  --pui-switch-h: 18px;  /* shadcn h-[1.15rem] = 18.4 at 16; the library draws 18 */
  --pui-switch-thumb: var(--pui-size-icon-16);
  position: relative;
  flex: none;
  box-sizing: border-box;
  width: var(--pui-switch-w);
  height: var(--pui-switch-h);
  border-radius: var(--pui-radius-full);
  background: var(--pui-border-default);
  box-shadow: var(--pui-elevation-flat);
  transition:
    background-color var(--pui-motion-state) var(--pui-motion-easing),
    box-shadow var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-switch__track::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 1px;
  width: var(--pui-switch-thumb);
  height: var(--pui-switch-thumb);
  border-radius: var(--pui-radius-full);
  background: var(--pui-surface-page);
  transform: translateY(-50%);
  transition: transform var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-switch:hover .pui-switch__track { background: var(--pui-border-strong); }
.pui-switch__input:focus-visible + .pui-switch__track { box-shadow: var(--pui-ring), inset 0 0 0 1px var(--pui-border-focus); }
.pui-switch__input:checked + .pui-switch__track { background: var(--pui-action-primary-bg); }
.pui-switch:hover .pui-switch__input:checked + .pui-switch__track { background: var(--pui-action-primary-bg-hover); }
.pui-switch__input:checked + .pui-switch__track::before {
  transform: translate(calc(var(--pui-switch-w) - var(--pui-switch-thumb) - 2px), -50%);
}
/* The label is the row's text. Its type is stated on .pui-switch, which is where D61 wants it — one
   place, not two — so the only thing this class owns is its behaviour IN the row: it takes the space
   that is left and wraps, instead of shrink-proofing itself and squeezing the track. It was in the
   markup block above, in the specimen and in the workshop with no rule anywhere behind it, which the
   documentation site's class gate found on 2026-09-10: a class a component's own markup tells you to
   write, and nothing to change when its behaviour has to change. */
.pui-switch__label { min-width: 0; }

.pui-switch:has(.pui-switch__input:disabled),
.pui-switch--disabled { opacity: 0.5; cursor: not-allowed; }

.pui-switch--small .pui-switch__track {
  --pui-switch-w: 24px;  /* shadcn w-6 */
  --pui-switch-h: 14px;  /* shadcn h-3.5 */
  --pui-switch-thumb: var(--pui-space-12);
}

/* 2.4.5 — the one-element switch: <button class="pui-switch__track" role="switch" aria-checked="…"> — the ARIA switch
   pattern (shadcn, Base UI render a button; Paggy's admin .tumbler is one). The same five states as the input form. */
button.pui-switch__track { display: inline-block; padding: 0; border: 0; cursor: pointer; outline: none; }
button.pui-switch__track:hover { background: var(--pui-border-strong); }
button.pui-switch__track:focus-visible { box-shadow: var(--pui-ring), inset 0 0 0 1px var(--pui-border-focus); }
.pui-switch__track[aria-checked="true"],
.pui-switch__track[data-state="checked"] { background: var(--pui-action-primary-bg); }
button.pui-switch__track[aria-checked="true"]:hover,
button.pui-switch__track[data-state="checked"]:hover { background: var(--pui-action-primary-bg-hover); }
.pui-switch__track[aria-checked="true"]::before,
.pui-switch__track[data-state="checked"]::before {
  transform: translate(calc(var(--pui-switch-w) - var(--pui-switch-thumb) - 2px), -50%);
}
button.pui-switch__track:disabled { opacity: 0.5; cursor: not-allowed; }

/* slider.css — Slider (10_COMPONENTS.md § Forms).
 *
 * A value or a range on a track. shadcn slider.tsx on Base UI (D17 — Paggy's only slider is the browser's
 * input[type=range] with accent-color blue, app.css:1204, which its own rule bans): a 6 px track in border/default,
 * the filled range and the thumb's border in action/primary/bg, a 16 px white thumb on elevation/flat, elevation/card
 * on hover, the ring on focus, .5 disabled; no error state. Figma 224:45: Type single / range × four states.
 * Contract: registry/slider/registry.json.
 *
 *   <input class="pui-slider" type="range" aria-label="Volume" min="0" max="100" value="50" style="--pui-slider-value: 50%">
 *
 * The native control carries Type single; the filled range is a gradient split at --pui-slider-value (the consumer
 * or its script sets it — value / (max − min)). A two-thumb range is JavaScript's (2.7.5): it lays two inputs in a
 * `.pui-slider-range` box, or draws the parts below (__track, __range, __thumb) itself.
 * Rule      the value is text beside the track (label 44 wide, the number in 600 at the right — Paggy's row), never
 *           on the thumb.
 * Keyboard  all of it is the browser's: input[type=range] takes the arrow keys by step, Home / End, and
 *           PageUp / PageDown by a larger step. A scripted range slider owes the same on its thumb (3.5.3).
 */

.pui-slider {
  --pui-slider-value: 50%;
  display: block;
  box-sizing: border-box;
  width: 100%;
  height: var(--pui-size-icon-16);
  margin: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
  outline: none;
  -webkit-appearance: none;
  appearance: none;
}
.pui-slider::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: var(--pui-radius-full);
  background: linear-gradient(to right, var(--pui-action-primary-bg) var(--pui-slider-value), var(--pui-border-default) var(--pui-slider-value));
}
.pui-slider::-moz-range-track {
  height: 6px;
  border-radius: var(--pui-radius-full);
  background: var(--pui-border-default);
}
.pui-slider::-moz-range-progress {
  height: 6px;
  border-radius: var(--pui-radius-full);
  background: var(--pui-action-primary-bg);
}
.pui-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  box-sizing: border-box;
  width: var(--pui-size-icon-16);
  height: var(--pui-size-icon-16);
  margin-top: -5px;
  border: 1px solid var(--pui-action-primary-bg);
  border-radius: var(--pui-radius-full);
  background: var(--pui-surface-page);
  box-shadow: var(--pui-elevation-flat);
  transition: box-shadow var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-slider::-moz-range-thumb {
  box-sizing: border-box;
  width: var(--pui-size-icon-16);
  height: var(--pui-size-icon-16);
  border: 1px solid var(--pui-action-primary-bg);
  border-radius: var(--pui-radius-full);
  background: var(--pui-surface-page);
  box-shadow: var(--pui-elevation-flat);
}
.pui-slider:hover::-webkit-slider-thumb { box-shadow: var(--pui-elevation-card); }
.pui-slider:hover::-moz-range-thumb { box-shadow: var(--pui-elevation-card); }
/* 3.5.2 — the input's own box is 16 px tall and the thumb fills it, so webkit clips an outset ring
   to nothing and the focused thumb measured 1.31 against its surroundings. The inset half is drawn
   inside the thumb and always shows; the outset half draws what the control's height allows. */
.pui-slider:focus-visible::-webkit-slider-thumb { box-shadow: var(--pui-ring-inset), var(--pui-ring); }
.pui-slider:focus-visible::-moz-range-thumb { box-shadow: var(--pui-ring-inset), var(--pui-ring); }
.pui-slider:disabled { opacity: 0.5; cursor: not-allowed; }

/* The drawn parts, for a scripted range slider. */
.pui-slider-range { position: relative; height: var(--pui-size-icon-16); }
.pui-slider-range__track {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 6px;
  border-radius: var(--pui-radius-full);
  background: var(--pui-border-default);
  transform: translateY(-50%);
}
.pui-slider-range__range {
  position: absolute;
  top: 0;
  bottom: 0;
  border-radius: var(--pui-radius-full);
  background: var(--pui-action-primary-bg);
}
.pui-slider-range__thumb {
  position: absolute;
  top: 50%;
  box-sizing: border-box;
  width: var(--pui-size-icon-16);
  height: var(--pui-size-icon-16);
  margin: 0;
  padding: 0;
  border: 1px solid var(--pui-action-primary-bg);
  border-radius: var(--pui-radius-full);
  background: var(--pui-surface-page);
  box-shadow: var(--pui-elevation-flat);
  transform: translate(-50%, -50%);
  cursor: grab;
  outline: none;
}
.pui-slider-range__thumb:hover { box-shadow: var(--pui-elevation-card); }
.pui-slider-range__thumb:focus-visible { box-shadow: var(--pui-ring); }
.pui-slider-range--disabled { opacity: 0.5; pointer-events: none; }

/* calendar.css — Calendar · Calendar day (10_COMPONENTS.md § Forms).
 *
 * The month grid a date is picked on: Paggy's `.pgy-cal` popup (app.css:1580–1620, datepicker.js): a white panel,
 * radius/16, elevation/card, padding 16 (Paggy's 14 — stated); the head with the month in SemiBold 14 and two 28 px
 * ghost icon buttons; the Monday-first weekday row in SemiBold 12 text/subtle; six weeks of 32 px cells at a 4 px gap
 * (7 × 32 + 6 × 4 + 2 × 16 = 280); a foot with Clear and Today as ghost buttons. A day is Paggy's `.pgy-cal__d`
 * (app.css:1598): 32 × 32, radius/8, the number in Medium 12, hover surface/sunken, outside days text/disabled, today
 * an inset 1 px border/strong ring, the selected day action/primary/bg on fg; the range middle is surface/sunken with
 * square corners so the band runs unbroken. Figma: Calendar 227:5 · Calendar day 226:49.
 * Contracts: registry/calendar/registry.json.
 *
 *   <div class="pui-calendar">
 *     <div class="pui-calendar__head"><button class="pui-icon-button pui-icon-button--ghost pui-icon-button--small">…</button>
 *       <span class="pui-calendar__month">September 2026</span> …</div>
 *     <div class="pui-calendar__weekdays"><span>Mo</span>…</div>
 *     <div class="pui-calendar__grid" role="grid"><div class="pui-calendar__row" role="row">
 *       <button class="pui-calendar-day pui-calendar-day--outside">31</button>
 *       <button class="pui-calendar-day pui-calendar-day--today">7</button>
 *       <button class="pui-calendar-day" role="gridcell" aria-selected="true">14</button> …</div> …</div>
 *   (a grid owns rows, a row owns gridcells — aria-selected is only valid on a gridcell; axe, M2.3.5)
 *     <div class="pui-calendar__foot"><button class="pui-button pui-button--ghost pui-button--small">Clear</button>…</div></div>
 *
 * Day Kind  (none) default · --today · --selected / [aria-selected="true"] · --range · --outside
 * Day State :hover · :focus-visible (the ring, on a selected day too — 3.5.2) · :disabled (.5)
 * Rules     Monday first; six rows always; no leading zero; a unicode arrow never in the head.
 * Keyboard  ONE tab stop in the grid: the selected day, else today, else the first of the month, takes
 *           tabindex="0" and every other cell tabindex="-1" — a range marks two days selected and still exposes
 *           one stop (3.5.3). The script owes the arrow keys by day and week, Home / End, PageUp / PageDown by
 *           month, Shift+PageUp / PageDown by year, Enter / Space to select.
 */

.pui-calendar {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-8);
  box-sizing: border-box;
  width: 280px; /* 7 × 32 + 6 × 4 + 2 × 16 — derived from the day and the gap */
  padding: var(--pui-space-16);
  border-radius: var(--pui-radius-16);
  background: var(--pui-surface-page);
  box-shadow: var(--pui-elevation-card);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-12);
  line-height: var(--pui-font-line-height-normal);
}
.pui-calendar__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: var(--pui-size-control-small);
}
.pui-calendar__month {
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-600);
}
.pui-calendar__weekdays,
.pui-calendar__row {
  display: grid;
  grid-template-columns: repeat(7, var(--pui-space-32));
  gap: var(--pui-space-4);
}
/* The grid is a stack of rows (role="row" → role="gridcell"), the same 4px gap between them. */
.pui-calendar__grid {
  display: grid;
  gap: var(--pui-space-4);
}
.pui-calendar__weekdays {
  padding: var(--pui-space-4) 0;
  color: var(--pui-text-caption);
  font-weight: var(--pui-font-weight-600);
  text-align: center;
}
.pui-calendar__foot {
  display: flex;
  justify-content: space-between;
  padding-top: var(--pui-space-4);
}

.pui-calendar-day {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: var(--pui-space-32);
  height: var(--pui-space-32);
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: var(--pui-radius-8);
  background: transparent;
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-12);
  font-weight: var(--pui-font-weight-500);
  line-height: 1;
  cursor: pointer;
  outline: none;
  transition: background-color var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-calendar-day:hover { background: var(--pui-surface-sunken); }
.pui-calendar-day:focus-visible { box-shadow: var(--pui-ring); }
.pui-calendar-day--today { box-shadow: inset 0 0 0 1px var(--pui-border-strong); }
.pui-calendar-day--today:focus-visible { box-shadow: inset 0 0 0 1px var(--pui-border-strong), var(--pui-ring); }
.pui-calendar-day--outside { color: var(--pui-text-disabled); }
.pui-calendar-day--range { border-radius: 0; background: var(--pui-surface-sunken); }
.pui-calendar-day--selected,
.pui-calendar-day[aria-selected="true"] {
  background: var(--pui-action-primary-bg);
  color: var(--pui-action-primary-fg);
  box-shadow: none;
}
.pui-calendar-day--selected:hover,
.pui-calendar-day[aria-selected="true"]:hover { background: var(--pui-action-primary-bg-hover); }
/* 3.5.2 — the selected day sets `box-shadow: none` to drop the today ring, and that killed the focus
   ring with it: the one day a date picker puts keyboard focus on had no indicator at all. */
.pui-calendar-day[aria-selected="true"]:focus-visible { box-shadow: var(--pui-ring); }
.pui-calendar-day:disabled { opacity: 0.5; cursor: not-allowed; }

/* date-picker.css — Date picker (10_COMPONENTS.md § Forms).
 *
 * A date chosen on a Calendar that opens under a field. Paggy's Pepper date picker (app.css:1572, datepicker.js): the
 * field stays a native date input (ISO value, dd/mm/yyyy typing) with the browser's indicator hidden and a Lucide
 * calendar glyph in its place; the Calendar opens 4 px under it; a range is two Calendars side by side under one
 * field. MAFO's react-datepicker and react-date-range are what it replaces; shadcn's Popover + Calendar is the code
 * side. Figma 227:134. Contract: registry/date-picker/registry.json.
 *
 *   <div class="pui-date-picker">
 *     <input class="pui-input pui-date-picker__field" type="date" aria-label="Start date" placeholder="dd/mm/yyyy">
 *     <span class="pui-icon pui-date-picker__icon">…calendar…</span>
 *     <div class="pui-date-picker__popup" hidden> …pui-calendar… </div></div>
 *
 * State     open = the popup without [hidden] (Has calendar); the field's own states are Input's
 * Rule      mm/dd/yyyy nowhere in the house; the browser's own popup never.
 */

.pui-date-picker {
  position: relative;
  display: inline-block;
  width: 100%;
  max-width: 280px;
}
.pui-date-picker__field { padding-inline-end: var(--pui-space-40); }
.pui-date-picker__field::-webkit-calendar-picker-indicator { display: none; -webkit-appearance: none; appearance: none; }
.pui-date-picker__field::-webkit-inner-spin-button { display: none; }
.pui-date-picker__icon {
  position: absolute;
  top: 50%;
  right: var(--pui-space-12);
  width: var(--pui-size-icon-16);
  height: var(--pui-size-icon-16);
  transform: translateY(-50%);
  color: var(--pui-text-muted);
  pointer-events: none;
}
.pui-date-picker__popup {
  position: absolute;
  top: calc(100% + var(--pui-space-4));
  left: 0;
  z-index: 30;
  display: flex;
  gap: var(--pui-space-8);
}
.pui-date-picker__popup[hidden] { display: none; }

/* questionnaire.css — Questionnaire step (10_COMPONENTS.md § Forms).
 *
 * One question of a form taken in steps: the site's lead-form card with a step line, a title, one or two fields, an
 * optional consent and Back / Next. Quoted from the site's 2026 forms: `.form-card` (echoai.css:939 — 444 wide,
 * surface/card, a 2 px border/default, radius/16, padding 32, gap 24), `.ui-form__title` (global-2026.css:911 — 22
 * Medium, line-height 1.3); the fields are Field, the consent Checkbox, the buttons Button. Figma 232:10.
 * Contract: registry/questionnaire/registry.json.
 *
 *   <form class="pui-questionnaire-step">
 *     <div class="pui-progress pui-progress--labelled">…</div>
 *     <div class="pui-questionnaire-step__heading"><h2 class="pui-questionnaire-step__title">About your company</h2>
 *       <p class="pui-questionnaire-step__description">…</p></div>
 *     <div class="pui-questionnaire-step__fields"> …pui-field… </div>
 *     <label class="pui-checkbox">…</label>
 *     <div class="pui-questionnaire-step__actions"><button class="pui-button pui-button--secondary">Back</button>
 *       <button class="pui-button">Next</button></div></form>
 *
 * Rules     Back off on the first step; 'Submit' on the last, never 'Next'; the bar moves in equal steps.
 */

.pui-questionnaire-step {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-24);
  box-sizing: border-box;
  width: 100%;
  max-width: 444px; /* the site's .form-card (echoai.css:939) */
  margin: 0;
  padding: var(--pui-space-32);
  border: 2px solid var(--pui-border-default);
  border-radius: var(--pui-radius-16);
  background: var(--pui-surface-card);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
}
.pui-questionnaire-step__heading { display: flex; flex-direction: column; gap: var(--pui-space-8); }
.pui-questionnaire-step__title {
  margin: 0;
  font-size: var(--pui-font-size-22);
  font-weight: var(--pui-font-weight-500);
  line-height: 1.3; /* the site's .ui-form__title (global-2026.css:911) */
}
.pui-questionnaire-step__description {
  margin: 0;
  color: var(--pui-text-muted);
  font-size: var(--pui-font-size-14);
  line-height: var(--pui-font-line-height-normal);
}
.pui-questionnaire-step__fields { display: flex; flex-direction: column; gap: var(--pui-space-24); }
.pui-questionnaire-step__actions { display: flex; justify-content: space-between; gap: var(--pui-space-8); }
.pui-questionnaire-step__actions > :only-child { margin-inline-start: auto; }

/* Feedback and status */
/* badge.css — Badge · Chip (10_COMPONENTS.md § Feedback and status).
 *
 * Badge is the small square label: the site's tag (styles.css:1182–1197 — padding 2 / 4, radius 4, 12 Regular) and
 * the deck's slide badge (slide.css:273–288 — 18 Medium, padding 8 / 12; `.badge--xs` 14 Regular, 4 / 8). Chip is the
 * pill: Paggy's app badge (app.css:88–96 — 14 Medium for its 13, padding 4 / 12 for its 4 / 10, radius 999 — chip/radius, 'labels
 * are pills, actions are squares'); shadcn's Badge is the same pill. Tint and ink are tint/{tone} (hue 200 on 300 for
 * blue and red, yellow-300 on yellow-600, neutral-200 on neutral-600, green-100 on green-600); dark is
 * surface/inverse on text/on-inverse. No states — a label is not interactive. Figma: Badge 56:2 · Chip 56:130.
 * Contract: registry/badge/registry.json.
 *
 *   <span class="pui-badge pui-badge--green">Done</span>
 *   <span class="pui-chip pui-chip--blue pui-chip--small"><span class="pui-icon pui-chip__icon">…</span>Beta</span>
 *
 * Tone      (none) neutral · --blue · --red · --yellow · --green · --dark    ← fixed; never follows the accent
 * Badge     --small 12 Regular 2 / 4 · (none) medium 14 Regular 4 / 8 · --large 18 Medium 8 / 12
 * Chip      --small 12 Regular 2 / 8 · (none) medium 14 Medium 4 / 12
 * Parts     __icon — a 16 px leading glyph at a space/4 gap (Q4). It takes the label's ink, because it
 *           inherits currentColor and nothing here overrides it; in Figma the same rule costs a recolour
 *           on every swapped Icon, which is why it is written in both contracts (D92, Fedor 2026-09-16).
 * Rule      the pills-vs-squares test (2.2.3) reads these two radii.
 * Rule      NO BORDER, on any tone, in any size — a label is flush. The library's one ring is not the
 *           Badge's: a KPI tile rings its DELTA with kpi/delta/ring because the delta sits on the tile's
 *           tint (kpi-tile.css, D92). css_rules_test.py fails on a border or a ring declared here.
 */

.pui-badge,
.pui-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--pui-space-4);
  box-sizing: border-box;
  padding: var(--pui-space-4) var(--pui-space-8);
  border-radius: var(--pui-radius-4);
  background: var(--pui-tint-neutral-bg);
  color: var(--pui-tint-neutral-fg);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
  letter-spacing: var(--pui-font-letter-spacing-normal);
  white-space: nowrap;
  vertical-align: middle;
}
.pui-chip {
  padding: var(--pui-space-4) var(--pui-space-12);
  border-radius: var(--pui-chip-radius);
  font-weight: var(--pui-font-weight-500);
}

.pui-badge--blue,   .pui-chip--blue   { background: var(--pui-tint-blue-bg);   color: var(--pui-tint-blue-fg); }
.pui-badge--red,    .pui-chip--red    { background: var(--pui-tint-red-bg);    color: var(--pui-tint-red-fg); }
.pui-badge--yellow, .pui-chip--yellow { background: var(--pui-tint-yellow-bg); color: var(--pui-tint-yellow-fg); }
.pui-badge--green,  .pui-chip--green  { background: var(--pui-tint-green-bg);  color: var(--pui-tint-green-fg); }
.pui-badge--dark,   .pui-chip--dark   { background: var(--pui-surface-inverse); color: var(--pui-text-on-inverse); }

.pui-badge--small { padding: var(--pui-space-2) var(--pui-space-4); font-size: var(--pui-font-size-12); }
.pui-badge--large { padding: var(--pui-space-8) var(--pui-space-12); font-size: var(--pui-font-size-18); font-weight: var(--pui-font-weight-500); }
.pui-chip--small { padding: var(--pui-space-2) var(--pui-space-8); font-size: var(--pui-font-size-12); font-weight: var(--pui-font-weight-400); }

.pui-badge__icon,
.pui-chip__icon { flex: none; width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); }

/* alert.css — Alert (10_COMPONENTS.md § Feedback and status).
 *
 * A message that stays on the page. Colour is status/{tone} bg, fg and solid (M1.1, from slide.css:287 and the site's
 * status greens and red); the box is shadcn's alert.tsx: radius 8, padding 12 / 16, a 16 px icon column, a 12 gap, a
 * Medium title over a Regular description. Soft = the status bg with icon, title and description in its fg; outline
 * = surface/page, border/default, the status solid on the icon. Neutral soft is surface/sunken. No states.
 * Figma 59:2: Tone × Style. Contract: registry/alert/registry.json. Toast - status (toast.css) is this box, floating.
 *
 *   <div class="pui-alert pui-alert--success" role="status">
 *     <span class="pui-icon pui-alert__icon">…circle-check…</span>
 *     <div class="pui-alert__body"><p class="pui-alert__title">Deck published</p>
 *       <p class="pui-alert__description">Anyone with the link can open it.</p></div>
 *     <button class="pui-icon-button pui-icon-button--ghost pui-icon-button--small pui-alert__close" aria-label="Dismiss">…x…</button></div>
 *
 * Tone      (none) neutral · --info · --success · --warning · --danger      ← the tone picks the glyph
 * Style     (none) soft · --outline
 * Parts     __icon (16) · __body · __title (14 Medium) · __description (14 Regular) · __close (a ghost icon button)
 */

.pui-alert,
.pui-toast-status {
  display: flex;
  align-items: flex-start;
  gap: var(--pui-space-12);
  box-sizing: border-box;
  width: 100%;
  padding: var(--pui-space-12) var(--pui-space-16);
  border: 1px solid transparent;
  border-radius: var(--pui-radius-8);
  background: var(--pui-surface-sunken);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  line-height: var(--pui-font-line-height-normal);
}
.pui-alert__icon,
.pui-toast-status__icon {
  flex: none;
  width: var(--pui-size-icon-16);
  height: var(--pui-size-icon-16);
  margin-top: var(--pui-space-2);
  color: var(--pui-text-muted);
}
.pui-alert__body,
.pui-toast-status__body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--pui-space-4); }
.pui-alert__title,
.pui-toast-status__title { margin: 0; font-weight: var(--pui-font-weight-500); }
.pui-alert__description,
.pui-toast-status__description { margin: 0; font-weight: var(--pui-font-weight-400); }
.pui-alert__close,
.pui-toast-status__close { flex: none; margin: calc(var(--pui-space-4) * -1) calc(var(--pui-space-8) * -1) calc(var(--pui-space-4) * -1) 0; }

/* Soft: the status ground, everything in its ink. */
.pui-alert--info    { background: var(--pui-status-info-bg);    color: var(--pui-status-info-fg); }
.pui-alert--success { background: var(--pui-status-success-bg); color: var(--pui-status-success-fg); }
.pui-alert--warning { background: var(--pui-status-warning-bg); color: var(--pui-status-warning-fg); }
.pui-alert--danger  { background: var(--pui-status-danger-bg);  color: var(--pui-status-danger-fg); }
.pui-alert--info .pui-alert__icon,
.pui-alert--success .pui-alert__icon,
.pui-alert--warning .pui-alert__icon,
.pui-alert--danger .pui-alert__icon { color: inherit; }

/* Outline: the page's surface, the solid on the glyph only — the concept rows' look; the description muted. */
.pui-alert--outline,
.pui-toast-status {
  background: var(--pui-surface-page);
  border-color: var(--pui-border-default);
  color: var(--pui-text-default);
}
.pui-alert--outline .pui-alert__description,
.pui-toast-status__description { color: var(--pui-text-muted); }
.pui-alert--outline.pui-alert--info .pui-alert__icon,    .pui-toast-status--info .pui-toast-status__icon    { color: var(--pui-status-info-solid); }
.pui-alert--outline.pui-alert--success .pui-alert__icon, .pui-toast-status--success .pui-toast-status__icon { color: var(--pui-status-success-solid); }
.pui-alert--outline.pui-alert--warning .pui-alert__icon, .pui-toast-status--warning .pui-toast-status__icon { color: var(--pui-status-warning-solid); }
.pui-alert--outline.pui-alert--danger .pui-alert__icon,  .pui-toast-status--danger .pui-toast-status__icon  { color: var(--pui-status-danger-solid); }

/* alert-dialog.css — Alert dialog (10_COMPONENTS.md § Feedback and status).
 *
 * A dialog that demands an answer: one question, Cancel and one confirming verb, over the Scrim (dialog.css); no
 * close, no click-away. Paggy's confirm (me.html:244–252, app.css:515–530 — 440 wide), MAFO's Attention modals,
 * shadcn's AlertDialog. surface/card, radius/16, padding 24, gap 16, elevation/modal; the title Medium 18, the
 * description 14 text/subtle; the footer a Button group aligned right. Figma 126:69: Size small 440 / medium 480.
 * Contract: registry/alert-dialog/registry.json.
 *
 *   <div class="pui-scrim"><div class="pui-alert-dialog" role="alertdialog" aria-modal="true" aria-labelledby="t">
 *     <div class="pui-alert-dialog__texts"><h2 class="pui-alert-dialog__title" id="t">Delete “Q3 results”?</h2>
 *       <p class="pui-alert-dialog__description">This cannot be undone.</p></div>
 *     <div class="pui-button-group pui-alert-dialog__actions">
 *       <button class="pui-button pui-button--secondary">Cancel</button>
 *       <button class="pui-button pui-button--danger">Delete</button></div></div></div>
 *
 * Size      (none) small 440 · --medium 480
 * Rule      the confirming button carries the verb, never OK; a destructive verb is Button - danger.
 * Keyboard  as Dialog: focus in on open, held while open, Escape closes and returns it to the opener (3.5.3).
 */

.pui-alert-dialog {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-16);
  box-sizing: border-box;
  width: 440px;
  max-width: calc(100vw - var(--pui-space-32));
  padding: var(--pui-space-24);
  border-radius: var(--pui-radius-16);
  background: var(--pui-surface-card);
  box-shadow: var(--pui-elevation-modal);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
}
.pui-alert-dialog--medium { width: 480px; }
.pui-alert-dialog__texts { display: flex; flex-direction: column; gap: var(--pui-space-8); }
.pui-alert-dialog__title {
  margin: 0;
  font-size: var(--pui-font-size-18);
  font-weight: var(--pui-font-weight-500);
  line-height: var(--pui-font-line-height-normal);
}
.pui-alert-dialog__description {
  margin: 0;
  color: var(--pui-text-caption);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
}
.pui-alert-dialog__actions { align-self: flex-end; }

/* toast.css — Toast (the house snackbar) · Toast - status (10_COMPONENTS.md § Feedback and status).
 *
 * Toast is Paggy's `.toast` (app.css:1001, editor.js:208 — 'Slide duplicated', 'Copied', 'Saved'): a dark bar at the
 * bottom-centre, 24 up, white text, an underlined action, a key cap for the shortcut, gone after 3.2 s (6.5 with an
 * action). surface/inverse, text/on-inverse at 14, padding space/8 / space/16, radius/8, a row at space/12,
 * elevation/floating; the action underlined in Medium 14 (Paggy's #FFD23F yellow is not carried — U12, vetoable);
 * the cap is Kbd on-inverse. Toast - status is Alert's outline box set floating — shadcn's Sonner (D17; MAFO's
 * react-toastify is what it replaces): 356 wide, elevation/floating, the close on, a small secondary Action; stacked
 * at space/12, newest at the bottom, bottom-right. Figma: Toast 241:99 · Toast - status 234:9.
 * Contract: registry/toast/registry.json. The status toast's box rules are paired in alert.css.
 *
 *   <div class="pui-toast-region"><div class="pui-toast" role="status">Slide duplicated
 *     <button class="pui-toast__action">Undo</button><kbd class="pui-kbd pui-kbd--on-inverse">⌘Z</kbd></div></div>
 *   <div class="pui-toast-region pui-toast-region--bottom-right">
 *     <div class="pui-toast-status pui-toast-status--danger" role="alert"> …alert's parts, __action, __close… </div></div>
 *
 * Region    .pui-toast-region — fixed, bottom-centre, 24 up (Paggy's); --bottom-right for the status stack
 * Rules     one line, one verb; how long it stays is the code side's, never drawn.
 */

.pui-toast-region {
  position: fixed;
  left: 50%;
  bottom: var(--pui-space-24);
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--pui-space-12);
  transform: translateX(-50%);
  pointer-events: none;
}
.pui-toast-region > * { pointer-events: auto; }
.pui-toast-region--bottom-right {
  left: auto;
  right: var(--pui-space-24);
  align-items: flex-end;
  transform: none;
}

.pui-toast {
  display: inline-flex;
  align-items: center;
  gap: var(--pui-space-12);
  box-sizing: border-box;
  max-width: calc(100vw - var(--pui-space-48));
  padding: var(--pui-space-8) var(--pui-space-16);
  border-radius: var(--pui-radius-8);
  background: var(--pui-surface-inverse);
  box-shadow: var(--pui-elevation-floating);
  color: var(--pui-text-on-inverse);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
  white-space: nowrap;
}
.pui-toast__action {
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  font-weight: var(--pui-font-weight-500);
  text-decoration: underline;
  text-underline-offset: var(--pui-space-2);
  cursor: pointer;
  outline: none;
}
.pui-toast__action:focus-visible { border-radius: var(--pui-radius-4); box-shadow: var(--pui-ring); }

.pui-toast-status {
  width: 356px; /* Sonner's width */
  max-width: calc(100vw - var(--pui-space-48));
  box-shadow: var(--pui-elevation-floating);
}
.pui-toast-status__action { flex: none; align-self: center; }

/* tooltip.css — Tooltip (10_COMPONENTS.md § Feedback and status).
 *
 * A few words on hover or focus that name the thing under the pointer. shadcn's tooltip.tsx (D17; no Mobupps
 * source): surface/inverse on text/on-inverse, 12 Regular, padding space/8 · space/12, radius/8, a 12 × 6 arrow, max
 * width 280; fade and zoom from 95 % over motion/state. Figma 67:22: Side top / bottom / left / right.
 * Contract: registry/tooltip/registry.json.
 *
 *   <div class="pui-tooltip pui-tooltip--top" role="tooltip">Copy link</div>
 *
 * Side      (none) top · --bottom · --left · --right — where the tooltip sits; the arrow points at the trigger
 * Rule      a few words; a sentence is a Popover. Placing it is the code side's; this file draws it.
 * Keyboard  it appears on FOCUS as well as hover, stays while the pointer is over it, and Escape dismisses it
 *           without moving focus — WCAG 1.4.13 Content on Hover or Focus (3.5.3).
 */

.pui-tooltip {
  --pui-tooltip-arrow: 6px;
  position: relative;
  display: inline-block;
  box-sizing: border-box;
  max-width: 280px;
  padding: var(--pui-space-8) var(--pui-space-12);
  border-radius: var(--pui-radius-8);
  background: var(--pui-surface-inverse);
  color: var(--pui-text-on-inverse);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-12);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
  animation: pui-tooltip-in var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-tooltip::after {
  content: "";
  position: absolute;
  border: var(--pui-tooltip-arrow) solid transparent;
}
.pui-tooltip--top::after,
.pui-tooltip:not([class*="pui-tooltip--"])::after {
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border-top-color: var(--pui-surface-inverse);
}
.pui-tooltip--bottom::after { bottom: 100%; left: 50%; transform: translateX(-50%); border-bottom-color: var(--pui-surface-inverse); }
.pui-tooltip--left::after { left: 100%; top: 50%; transform: translateY(-50%); border-left-color: var(--pui-surface-inverse); }
.pui-tooltip--right::after { right: 100%; top: 50%; transform: translateY(-50%); border-right-color: var(--pui-surface-inverse); }

@keyframes pui-tooltip-in {
  from { opacity: 0; transform: scale(0.95); }
  to { opacity: 1; transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .pui-tooltip { animation: none; }
}

/* popover.css — Popover · Hover card (10_COMPONENTS.md § Feedback and status).
 *
 * Popover is Paggy's background panel (app.css:1194 .bg-pop): 300 wide, surface/page, border/default, radius/16,
 * padding 16 (Paggy's 14 / 16 — stated), a title in Medium 14 over a hint in 12 text/subtle, the controls, an actions
 * row aligned right; elevation/floating. shadcn's popover.tsx on Base UI is the same surface at 288 (D17). No arrow —
 * Tooltip is the only pointed surface. Hover card is the same surface at 256 (shadcn hover-card.tsx, w-64): an
 * Avatar, a name in Medium 14, a subtitle in 12 text/subtle, a Badge, a line or two in 12 text/muted. Figma:
 * Popover 237:14 · Hover card 237:45. Contracts: registry/popover/registry.json, registry/hover-card/registry.json.
 *
 *   <div class="pui-popover" role="dialog" aria-labelledby="t">
 *     <div class="pui-popover__head"><p class="pui-popover__title">Filters</p><p class="pui-popover__hint">Applied on Apply.</p></div>
 *     <div class="pui-popover__content"> …fields… </div>
 *     <div class="pui-button-group pui-popover__footer"> …Reset · Apply (small)… </div></div>
 *   <div class="pui-hover-card"><div class="pui-hover-card__who"> …avatar, name, subtitle, badge… </div>
 *     <p class="pui-hover-card__description">14 active offers, 92 % fill rate</p></div>
 * Keyboard  the trigger holds the tab stop; focus moves in on open, Escape closes and returns it (3.5.3).
 */

.pui-popover,
.pui-hover-card {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-12);
  box-sizing: border-box;
  width: 300px;
  max-width: calc(100vw - var(--pui-space-32));
  padding: var(--pui-space-16);
  border: 1px solid var(--pui-border-default);
  border-radius: var(--pui-radius-16);
  background: var(--pui-surface-page);
  box-shadow: var(--pui-elevation-floating);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  line-height: var(--pui-font-line-height-normal);
}
.pui-popover__head { display: flex; flex-direction: column; gap: var(--pui-space-4); }
.pui-popover__title { margin: 0; font-weight: var(--pui-font-weight-500); }
.pui-popover__hint { margin: 0; color: var(--pui-text-caption); font-size: var(--pui-font-size-12); }
.pui-popover__content { display: flex; flex-direction: column; gap: var(--pui-space-12); }
.pui-popover__footer { align-self: flex-end; }

.pui-hover-card { width: 256px; }
.pui-hover-card__who { display: flex; align-items: center; gap: var(--pui-space-12); }
.pui-hover-card__texts { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--pui-space-2); }
.pui-hover-card__name { margin: 0; font-weight: var(--pui-font-weight-500); }
.pui-hover-card__subtitle { margin: 0; color: var(--pui-text-caption); font-size: var(--pui-font-size-12); }
.pui-hover-card__description { margin: 0; color: var(--pui-text-muted); font-size: var(--pui-font-size-12); }

/* spinner.css — Spinner (10_COMPONENTS.md § Feedback and status).
 *
 * The wait when the shape of what is coming is unknown. shadcn's spinner.tsx (D17; the Cabinet's CSpinner is CoreUI
 * stock): Lucide loader-circle, animate-spin, role="status", aria-label "Loading". One rotation per second, linear;
 * with prefers-reduced-motion it slows, it does not stop. Figma 61:38: Size 16 / 24 / 32 × Tone.
 * Contract: registry/spinner/registry.json.
 *
 *   <span class="pui-spinner pui-spinner--24" role="status" aria-label="Loading">…loader-circle svg…</span>
 *
 * Size      (none) 16 · --24 · --32
 * Tone      (none) muted · --default · --brand · --on-accent (inside a primary button)
 */

.pui-spinner {
  display: inline-block;
  flex: none;
  width: var(--pui-size-icon-16);
  height: var(--pui-size-icon-16);
  color: var(--pui-text-muted);
  animation: pui-spin 1s linear infinite;
}
.pui-spinner > svg { display: block; width: 100%; height: 100%; }
.pui-spinner--24 { width: var(--pui-size-icon-24); height: var(--pui-size-icon-24); }
.pui-spinner--32 { width: var(--pui-size-icon-32); height: var(--pui-size-icon-32); }
.pui-spinner--default { color: var(--pui-text-default); }
.pui-spinner--brand { color: var(--pui-text-link); }
.pui-spinner--on-accent { color: var(--pui-text-on-accent); }

@keyframes pui-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .pui-spinner { animation-duration: 3s; }
}

/* skeleton.css — Skeleton (10_COMPONENTS.md § Feedback and status).
 *
 * The wait when the shape of what is coming is known. shadcn's skeleton.tsx (D17): surface/sunken with a radius per
 * shape and a 2 s opacity pulse. It follows the ground (ground.css, D107): neutral/100 on white, neutral/200 on the bare
 * product ground. On a tinted card it is white — the slot inversion (D44: surface/slot). In use a skeleton
 * takes the exact size of what it stands in for, so the page does not jump. Figma 62:11: Shape × Size × On card.
 * Contract: registry/skeleton/registry.json.
 *
 *   <span class="pui-skeleton pui-skeleton--line" style="width: 280px"></span>
 *   <span class="pui-skeleton pui-skeleton--circle pui-skeleton--40"></span>
 *   <div class="pui-skeleton pui-skeleton--block" style="height: 120px"></div>
 *
 * Shape     (none) line, radius 4, 1 em tall · --circle, radius full · --block, radius 8
 * Size      the consumer's — width and height of the thing it stands for; --24 / --40 / --64 for a circle
 * On card   --on-card, or automatically inside a tinted .pui-card (surface/slot). In a white card (--plain, --border) it
 *           is the grey of a white ground — until 2026-09-17 it was white there too, white on white
 */

.pui-skeleton {
  display: block;
  box-sizing: border-box;
  height: 1em;
  border-radius: var(--pui-radius-4);
  background: var(--pui-surface-sunken);
  animation: pui-pulse 2s var(--pui-motion-easing) infinite;
}
.pui-skeleton--line { display: inline-block; width: 100%; vertical-align: middle; }
.pui-skeleton--circle { flex: none; width: var(--pui-space-40); height: var(--pui-space-40); border-radius: var(--pui-radius-full); }
.pui-skeleton--circle.pui-skeleton--24 { width: var(--pui-space-24); height: var(--pui-space-24); }
.pui-skeleton--circle.pui-skeleton--64 { width: var(--pui-space-64); height: var(--pui-space-64); }
.pui-skeleton--block { width: 100%; height: auto; min-height: var(--pui-space-64); border-radius: var(--pui-radius-8); }
.pui-skeleton--on-card,
.pui-card:not(.pui-card--plain, .pui-card--border) .pui-skeleton { background: var(--pui-surface-slot); }

@keyframes pui-pulse { 50% { opacity: 0.5; } }
@media (prefers-reduced-motion: reduce) {
  .pui-skeleton { animation: none; }
}

/* progress.css — Progress (10_COMPONENTS.md § Feedback and status).
 *
 * How far a task has come. shadcn progress.tsx on Base UI (D17): an 8 px track, radius full, the indicator from the
 * left — the track border/default and the indicator action/primary/bg, Slider's pair. Paggy's `.loadbar` (app.css:294)
 * stays the deck generator's own hero loader; neither MAFO nor the Cabinet draws a progress bar. An optional row above: what is happening at
 * the left, the number at the right, 12 px. No indeterminate state — that is Spinner. Figma 230:33.
 * Contract: registry/progress/registry.json.
 *
 *   <div class="pui-progress" role="progressbar" aria-label="Uploading" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100" style="--pui-progress-value: 50%">
 *     <div class="pui-progress__row"><span class="pui-progress__caption">Uploading…</span><span class="pui-progress__label">50%</span></div>
 *     <div class="pui-progress__track"><div class="pui-progress__indicator"></div></div></div>
 *
 * Value     --pui-progress-value on the block (a percentage); the indicator's width follows it
 */

.pui-progress {
  --pui-progress-value: 0%;
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-4);
  width: 100%;
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-12);
  line-height: var(--pui-font-line-height-normal);
  color: var(--pui-text-default);
}
.pui-progress__row { display: flex; justify-content: space-between; gap: var(--pui-space-8); }
.pui-progress__caption { color: var(--pui-text-muted); }
.pui-progress__label { font-weight: var(--pui-font-weight-500); }
.pui-progress__track {
  height: var(--pui-space-8);
  overflow: hidden;
  border-radius: var(--pui-radius-full);
  background: var(--pui-border-default);
}
.pui-progress__indicator {
  width: var(--pui-progress-value);
  height: 100%;
  border-radius: var(--pui-radius-full);
  background: var(--pui-action-primary-bg);
  transition: width var(--pui-motion-move) var(--pui-motion-easing);
}

/* empty.css — Empty (10_COMPONENTS.md § Feedback and status).
 *
 * What a screen says when there is nothing to show, and what to do about it. Block is shadcn's empty.tsx (D17 —
 * Paggy's empty states are one-liners, MAFO's tables say 'No data'): a 40 px surface/sunken box with a 24 px icon in
 * text/muted, a title at 18 Medium, a description at 14 text/muted centred to 320, a small Button group, padding 32;
 * --border draws a dashed border/default at radius/16 for a drop zone or an empty table body. Inline is Paggy's
 * `.share-empty` line (app.css:1430): 14 in the muted ink (the Figma binds neutral-600; 13 — stated), padding 8,
 * centred — for a menu, a list, a cell. Figma 242:1582. Contract: registry/empty/registry.json.
 *
 *   <div class="pui-empty pui-empty--border">
 *     <span class="pui-empty__media"><span class="pui-icon pui-icon--24">…folder…</span></span>
 *     <div class="pui-empty__text"><p class="pui-empty__title">No campaigns yet</p>
 *       <p class="pui-empty__description">Create one, or import from a CSV.</p></div>
 *     <div class="pui-button-group pui-empty__actions">…</div></div>
 *   <div class="pui-empty pui-empty--inline">No results.</div>
 *
 * Style     (none) block · --inline
 * Extra     --border — the dashed border
 */

.pui-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--pui-space-16);
  box-sizing: border-box;
  width: 100%;
  padding: var(--pui-space-32);
  border-radius: var(--pui-radius-16);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  text-align: center;
}
.pui-empty--border { border: 1px dashed var(--pui-border-default); }
.pui-empty__media {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--pui-space-40);
  height: var(--pui-space-40);
  border-radius: var(--pui-radius-8);
  background: var(--pui-surface-sunken);
  color: var(--pui-text-muted);
}
.pui-empty__text { display: flex; flex-direction: column; gap: var(--pui-space-4); max-width: 320px; }
.pui-empty__title {
  margin: 0;
  font-size: var(--pui-font-size-18);
  font-weight: var(--pui-font-weight-500);
  line-height: var(--pui-font-line-height-normal);
}
.pui-empty__description {
  margin: 0;
  color: var(--pui-text-muted);
  font-size: var(--pui-font-size-14);
  line-height: var(--pui-font-line-height-normal);
}
.pui-empty--inline {
  display: block;
  padding: var(--pui-space-8);
  border: 0;
  border-radius: 0;
  color: var(--pui-text-muted);
  font-size: var(--pui-font-size-14);
  line-height: var(--pui-font-line-height-normal);
}

/* kbd.css — Kbd (10_COMPONENTS.md § Feedback and status).
 *
 * A key cap in running text or beside a hint: ⌘K, Esc, Enter. Paggy's `.toast kbd` (app.css:1014 — 11 px on white at
 * .16, radius 4, padding 1 / 6) and shadcn's kbd.tsx (20 tall, min-width 20, rounded-sm, muted ground and ink, 12
 * Medium) — D17. One cap in two tones: light (surface/sunken, text/muted) for pages and menus, on-inverse
 * (color/alpha/white-16, text/on-inverse) for the dark toast and the rail. Figma 239:13. Contract: registry/kbd/registry.json.
 *
 *   <kbd class="pui-kbd">⌘K</kbd>   <span class="pui-kbd-group"><kbd class="pui-kbd">⌘</kbd><kbd class="pui-kbd">⇧</kbd><kbd class="pui-kbd">K</kbd></span>
 *
 * Tone      (none) light · --on-inverse
 * Rule      one key per cap; a shortcut is a row of caps at space/4 (.pui-kbd-group).
 */

.pui-kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  height: var(--pui-space-20);
  min-width: var(--pui-space-20);
  padding: 0 var(--pui-space-4);
  border-radius: var(--pui-radius-4);
  background: var(--pui-surface-sunken);
  color: var(--pui-text-muted);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-12);
  font-weight: var(--pui-font-weight-500);
  line-height: 1;
  vertical-align: middle;
}
.pui-kbd--on-inverse {
  background: var(--pui-color-alpha-white-16);
  color: var(--pui-text-on-inverse);
}
.pui-toast .pui-kbd { background: var(--pui-color-alpha-white-16); color: var(--pui-text-on-inverse); }
.pui-kbd-group { display: inline-flex; gap: var(--pui-space-4); vertical-align: middle; }

/* form-result.css — Form result (10_COMPONENTS.md § Feedback and status).
 *
 * What a form shows once it is sent — the site's .form-success-msg (global-2026.css), Fedor's block, kept over the
 * Alert because the Alert is one box and this is two: a head (the 24 circle-check in status/success/solid beside the
 * title, body/large Medium) over a slot holding the text (body/medium Regular) on surface/slot — white on the tinted
 * card it replaces the form in. Gap space/12 between the two, the slot's inset space/12, radius/8. Three options the
 * site needs: --on-white (the slot goes surface/sunken — a grey slot on a white card, the inversion rule), --on-dark
 * (the footer's subscribe: ink text/on-inverse, the slot a white wash at 10 %), --compact (the small subscribe pills:
 * gap space/8, the head flush, the title body/medium, the text body/small, the slot's inset 8 / 12; the check
 * stays 24 — Fedor's word, 2026-09-25, where the site's compact pill drew 20). Hidden until the
 * form succeeds — [hidden], or the consumer's own state class — and announced (role="status"). A failure is an Alert.
 * Request 7 from the corporate site, 2026-09-25. Figma: Form result 1090:3. Contract: registry/form-result/registry.json.
 *
 *   <div class="pui-form-result" role="status" aria-live="polite">
 *     <div class="pui-form-result__head"><span class="pui-icon pui-icon--24 pui-form-result__icon"><svg><use href="icons.svg#circle-check"/></svg></span>
 *       <p class="pui-form-result__title">Thank you</p></div>
 *     <div class="pui-form-result__slot"><p class="pui-form-result__text">Our team will get back to you shortly.</p></div></div>
 *
 * Ground    (none) tinted card · --on-white · --on-dark
 * Size      (none) default · --compact
 */

.pui-form-result {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-12);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
}
.pui-form-result[hidden] { display: none; }
.pui-form-result__head {
  display: flex;
  align-items: flex-start;
  gap: var(--pui-space-12);
  padding: var(--pui-space-12) var(--pui-space-12) var(--pui-space-12) 0;
}
.pui-form-result__icon { flex: none; width: var(--pui-size-icon-24); height: var(--pui-size-icon-24); margin-top: var(--pui-space-4); color: var(--pui-status-success-solid); }   /* 24 in every size — Fedor, 2026-09-25: 'make it 24 as we have in our design system' */
.pui-form-result__title {
  margin: 0;
  font-size: var(--pui-type-body-large-font-size);
  font-weight: var(--pui-font-weight-500);
  line-height: var(--pui-font-line-height-relaxed);
}
.pui-form-result__slot {
  padding: var(--pui-space-12);
  border-radius: var(--pui-radius-8);
  background: var(--pui-surface-slot);
}
.pui-form-result__text {
  margin: 0;
  font-size: var(--pui-type-body-medium-font-size);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-relaxed);
}
.pui-form-result--on-white .pui-form-result__slot { background: var(--pui-surface-sunken); }
.pui-form-result--on-dark { color: var(--pui-text-on-inverse); }
.pui-form-result--on-dark .pui-form-result__slot { background: var(--pui-color-alpha-white-10); }
.pui-form-result--compact { gap: var(--pui-space-8); }
.pui-form-result--compact .pui-form-result__head { gap: var(--pui-space-8); padding: 0; }
.pui-form-result--compact .pui-form-result__icon { margin-top: var(--pui-space-2); }
.pui-form-result--compact .pui-form-result__title { font-size: var(--pui-type-body-medium-font-size); }
.pui-form-result--compact .pui-form-result__slot { padding: var(--pui-space-8) var(--pui-space-12); }
.pui-form-result--compact .pui-form-result__text { font-size: var(--pui-type-body-small-font-size); }

/* Navigation and overlay */
/* tabs.css — Tabs list · Tabs trigger - line · Tabs trigger - segment (10_COMPONENTS.md § Navigation and overlay).
 *
 * Line is Paggy's strip: triggers 4 px apart over a 1 px border/default rule, a 2 px action/primary/bg indicator
 * hugging the open label (the concept rule), padding 8 / 12 / 0, radius 4 for the ring. Segment is the site's
 * `.tabs-bar`: triggers 2 px apart inside a box at radius 8 — the site's is surface/card with 8 px padding, drawn here on
 * surface/sunken at 4 (D47's nested inset, stated) — each at radius 4 (the nested-radius rule), the chosen one surface/inverse with text/on-inverse, padding 4 / 12. The box fills
 * tabs/track, which FOLLOWS THE GROUND (ground.css; D106, D107): neutral/100 on a white ground, and on a surface/card ground — a product screen, a tinted Card, a Table — WHITE, where
 * every other grey surface goes one step darker; neutral/100 there was 1.00 : 1 and the box disappeared. Two sizes (12 / 14
 * Medium); active · hover · focus · disabled. Same API as shadcn (Tabs / TabsList / TabsTrigger; TabsContent is not
 * a component). Figma: 82:82 · 80:62 · 81:22. Contract: registry/tabs/registry.json.
 *
 *   <div class="pui-tabs" role="tablist">
 *     <button class="pui-tabs__trigger" role="tab" aria-selected="true">Charts</button>
 *     <button class="pui-tabs__trigger" role="tab">Images</button></div>
 *   <div class="pui-tabs pui-tabs--segment" role="tablist"> …the same triggers… </div>
 *
 * Variant   (none) line · --segment
 * Size      --small (12 px) · (none) medium (14 px)
 * On card   automatic: under [data-ground="card"] and inside every part that paints surface/card (ground.css) the box is
 *           white; in a white Card it is neutral/100, as on any white ground. --on-card is the explicit switch, for a
 *           consumer who paints the product ground without setting data-ground. Segment only; line has no box to fill
 * State     active = [aria-selected="true"] or [data-state="active"] · :hover · :focus-visible · :disabled
 * Parts     __trigger · __icon (line only — a 16 px leading glyph)
 * Keyboard  ONE tab stop: the selected trigger takes tabindex="0", every other trigger tabindex="-1" (3.5.3).
 *           The script owes Left / Right to move and select, Home / End for first / last; Tab leaves for the panel.
 */

.pui-tabs {
  display: flex;
  align-items: flex-end;
  gap: var(--pui-space-4);
  box-shadow: inset 0 -1px 0 var(--pui-border-default);
  font-family: var(--pui-font-family);
}
.pui-tabs__trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--pui-space-8);
  position: relative;
  box-sizing: border-box;
  margin: 0;
  padding: var(--pui-space-8) var(--pui-space-12) 0;
  border: 0;
  border-radius: var(--pui-radius-4);
  background: transparent;
  color: var(--pui-text-muted);
  font-family: inherit;
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-500);
  line-height: var(--pui-font-line-height-normal);
  white-space: nowrap;
  cursor: pointer;
  outline: none;
  transition: color var(--pui-motion-state) var(--pui-motion-easing);
}
/* The indicator: 2 px, 2 px under the label, there on every trigger and shown by opacity so the row never jumps. */
.pui-tabs__trigger::after {
  content: "";
  display: block;
  height: var(--pui-space-2);
  margin-top: var(--pui-space-2);
  border-radius: var(--pui-radius-full);
  background: var(--pui-action-primary-bg);
  opacity: 0;
  transition: opacity var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-tabs__trigger { flex-direction: column; align-items: stretch; gap: 0; }
.pui-tabs__trigger > .pui-tabs__label { display: inline-flex; align-items: center; gap: var(--pui-space-8); }
.pui-tabs__trigger:hover { color: var(--pui-text-default); }
.pui-tabs__trigger:focus-visible { box-shadow: var(--pui-ring); }
.pui-tabs__trigger[aria-selected="true"],
.pui-tabs__trigger[data-state="active"] { color: var(--pui-text-default); }
.pui-tabs__trigger[aria-selected="true"]::after,
.pui-tabs__trigger[data-state="active"]::after { opacity: 1; }
.pui-tabs__trigger:disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; }
.pui-tabs__icon { flex: none; width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); }
.pui-tabs--small .pui-tabs__trigger { font-size: var(--pui-font-size-12); }

/* ── Segment ─────────────────────────────────────────────────────────────────────────────────── */
.pui-tabs--segment {
  display: inline-flex;
  align-items: stretch;
  gap: var(--pui-space-2);
  padding: var(--pui-space-4);
  border-radius: var(--pui-radius-8);
  background: var(--pui-tabs-track);
  box-shadow: none;
}
.pui-tabs--segment.pui-tabs--on-card { background: var(--pui-surface-slot); }
.pui-tabs--segment .pui-tabs__trigger {
  flex-direction: row;
  align-items: center;
  justify-content: center;
  padding: var(--pui-space-4) var(--pui-space-12);
  border-radius: var(--pui-radius-4);
  transition: background-color var(--pui-motion-state) var(--pui-motion-easing), color var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-tabs--segment .pui-tabs__trigger::after { display: none; }
.pui-tabs--segment .pui-tabs__trigger:hover { background: var(--pui-tint-neutral-bg); }
.pui-tabs--segment .pui-tabs__trigger[aria-selected="true"],
.pui-tabs--segment .pui-tabs__trigger[data-state="active"] {
  background: var(--pui-surface-inverse);
  color: var(--pui-text-on-inverse);
}

/* dropdown-menu.css — Dropdown menu · Menu item · Context menu (10_COMPONENTS.md § Navigation and overlay).
 *
 * A list of actions that opens from a button, a pill or a right-click. Paggy's user menu (app.css:1368 .user-menu):
 * min-width 220, surface/page, radius/16, elevation/card, padding space/8, no border; a head with the name in Medium
 * 14 and the role in 12 text/subtle over a 1 px border/default line; rows of Menu items; a separator with 4 above and
 * below. A row (app.css:1379, :836): 14 Medium text/default, radius/8, hover surface/sunken, a 16 px icon in
 * text/muted, padding space/8 / space/12, gap space/8, 36 tall; Type submenu adds a right chevron in text/subtle,
 * check the picker's check in text/default, destructive text/error. A shortcut is Kbd, light, at the right. Context
 * menu is the same menu at the pointer, no header. The surface rules are paired in select.css (`.pui-dropdown-menu`).
 * Figma: 245:16 · 244:1714. Contracts: registry/dropdown-menu/registry.json, registry/context-menu/registry.json.
 *
 *   <div class="pui-dropdown-menu" role="menu">
 *     <div class="pui-dropdown-menu__header"><p class="pui-dropdown-menu__name">Fedor</p><p class="pui-dropdown-menu__role">Owner</p></div>
 *     <div class="pui-dropdown-menu__group-label">Deck</div>
 *     <button class="pui-menu-item" role="menuitem"><span class="pui-icon pui-menu-item__icon">…</span>Duplicate
 *       <kbd class="pui-kbd pui-menu-item__shortcut">⌘D</kbd></button>
 *     <button class="pui-menu-item pui-menu-item--submenu" role="menuitem">Export<span class="pui-icon pui-menu-item__chevron">…</span></button>
 *     <div class="pui-dropdown-menu__separator" role="separator"></div>
 *     <button class="pui-menu-item pui-menu-item--destructive" role="menuitem">Delete</button></div>
 *
 * Item Type   (none) default · --submenu · --check · --destructive
 * Item State  :hover, :focus, [data-highlighted] share the surface (Paggy's), and KEYBOARD focus adds the inset ring
 *             on top of it — the shared surface alone is 1.12 against the menu and WCAG 2.4.13 asks 3:1 (3.5.2)
 *             · [aria-disabled="true"]
 * Parts       __icon · __label · __shortcut · __chevron · __check (shown on [aria-checked="true"])
 * Keyboard  NO tab stop at rest — the trigger holds it. Every item takes tabindex="-1" and the script moves
 *           focus to the first on open (3.5.3). It owes Up / Down, Home / End, Enter / Space, Escape (which closes
 *           and returns focus to the trigger), Right / Left for a submenu, and typeahead by first letter.
 */

.pui-dropdown-menu {
  min-width: 220px;
  padding: var(--pui-space-8);
  border: 0;
  border-radius: var(--pui-radius-16);
  box-shadow: var(--pui-elevation-card);
}
.pui-dropdown-menu__header {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-2);
  padding: var(--pui-space-8) var(--pui-space-12) var(--pui-space-12);
  border-bottom: 1px solid var(--pui-border-default);
}
.pui-dropdown-menu__name { margin: 0; font-weight: var(--pui-font-weight-500); }
.pui-dropdown-menu__role { margin: 0; color: var(--pui-text-caption); font-size: var(--pui-font-size-12); }
.pui-dropdown-menu__group-label { padding: var(--pui-space-8) var(--pui-space-12) var(--pui-space-4); }

.pui-menu-item {
  display: flex;
  align-items: center;
  gap: var(--pui-space-8);
  box-sizing: border-box;
  width: 100%;
  min-height: var(--pui-size-control-medium);
  margin: 0;
  padding: var(--pui-space-8) var(--pui-space-12);
  border: 0;
  border-radius: var(--pui-radius-8);
  background: transparent;
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-500);
  line-height: var(--pui-font-line-height-normal);
  text-align: start;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  outline: none;
  user-select: none;
  transition: background-color var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-menu-item:hover,
.pui-menu-item:focus,
.pui-menu-item:focus-visible,
.pui-menu-item[data-highlighted] { background: var(--pui-surface-sunken); }
/* 3.5.2 — keyboard focus is NOT the hover look here. The shared surface/sunken reads 1.12 against
   the menu's surface/page, and WCAG 2.4.13 asks 3:1 between the focused and unfocused pixels; a row
   is full-bleed in a menu that clips, so the ring is the inset one. Pointer highlight is unchanged. */
.pui-menu-item:focus-visible { box-shadow: var(--pui-ring-inset); }
.pui-menu-item[aria-disabled="true"],
.pui-menu-item:disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; }
.pui-menu-item--destructive { color: var(--pui-text-error); }
.pui-menu-item__icon { flex: none; width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); color: var(--pui-text-muted); }
.pui-menu-item--destructive .pui-menu-item__icon { color: inherit; }
.pui-menu-item__label { flex: 1 1 auto; min-width: 0; }
.pui-menu-item__shortcut,
.pui-menu-item__chevron,
.pui-menu-item__check { flex: none; margin-inline-start: auto; }
.pui-menu-item__chevron { width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); color: var(--pui-text-subtle); }
.pui-menu-item__check { width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); visibility: hidden; }
.pui-menu-item[aria-checked="true"] .pui-menu-item__check { visibility: visible; }

/* navigation-menu.css — Navigation menu · Nav link · Navigation panel (10_COMPONENTS.md § Navigation and overlay).
 *
 * The site's top bar, from its production CSS (global-2026.css:149–262): 856 wide, fixed 30 down and centred, padding
 * space/8 (the site pads 10 — global-2026.css:165 and styles.css:236 — a value off the spacing scale, rounded to its nearest step;
 * stated 2026-09-09, Finding 40 of the site pilot — decided by Fedor the same day on the Blade pilot: the scale, no consumer exception), a frosted ground — surface/glass over blur/nav (the site's rgba(232,232,232,.49) over 7.3 px, now tokens
 * D42) — radius/8, elevation/modal — shadow.xxl, 0 32 64 at 14 %, the live site's own bar shadow, chosen by Fedor on the site
 * against all four levels (request 1, 2026-09-25, D161; it supersedes D149's elevation/bar, whose −12 pull-in read half as dark on a white page); the Mobupps mark at 122 × 31, links at an 8 gap, the black CTA at the right. A link
 * is 14 Regular text/default, padding 4 / 8; Type products adds the 16 px caret (padding 8 left, 0 right — the site's list item); Type cta is the black mini button
 * (action/primary/bg, padding 4 / 8, radius/4 — the site's own shape, not Button small) inside the item's 4 / 8, carried as its margin
 * (Finding 47, 2026-09-09); the panel's links dim to
 * text/disabled on hover (styles.css:324 .nav-dropdown a:hover), and the bar's links carry the same — stated. The products panel (:208 .nav-dropdown) is surface/glass-strong over blur/panel, radius/8,
 * elevation/floating, padding space/20, a column of 16 Regular links at a 4 gap, padding 4, radius/4; it rises 4 px
 * and scales from .97 over motion/menu. Web only. Figma: 247:46 · 247:27 · 247:35.
 * Contract: registry/navigation-menu/registry.json.
 *
 *   <nav class="pui-navigation-menu"><a class="pui-navigation-menu__logo" href="/">…svg…</a>
 *     <div class="pui-navigation-menu__links">
 *       <button class="pui-nav-link pui-nav-link--products" aria-expanded="false">Products<span class="pui-icon pui-nav-link__caret">…</span></button>
 *       <a class="pui-nav-link" href="…">Case studies</a> …</div>
 *     <a class="pui-nav-link pui-nav-link--cta" href="…">Contact us</a></nav>
 *   <div class="pui-navigation-panel"><a class="pui-navigation-panel__link" href="…">MAFO</a> …</div>
 */

.pui-navigation-menu {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--pui-space-16);
  box-sizing: border-box;
  width: 856px;
  /* 100 % of what CONTAINS it, not of the viewport. The bar was capped at `calc(100vw - space/32)`,
     which is only true for the --fixed placement below, where the bar IS a child of the viewport. In
     any padded container — a documentation frame, a section, a card — it then computed a width wider
     than the room it had and overflowed by exactly that padding, raising a second scrollbar inside the
     frame's own (Fedor, 2026-09-22, looking at /components/navigation-menu/). The viewport cap belongs
     on the placement that is fixed to the viewport. */
  max-width: 100%;
  padding: var(--pui-space-8);
  border-radius: var(--pui-radius-8);
  background: var(--pui-surface-glass);
  -webkit-backdrop-filter: blur(var(--pui-blur-nav));
  backdrop-filter: blur(var(--pui-blur-nav));
  box-shadow: var(--pui-elevation-modal);   /* request 1, Fedor 2026-09-25: 'the very shadow I wanted for the top navigation' — was elevation/bar (D149), D161 */
  font-family: var(--pui-font-family);
}
.pui-navigation-menu--fixed {
  /* Fixed to the viewport, so here the viewport IS the container and the cap is the site's own. */
  max-width: calc(100vw - var(--pui-space-32));
  position: fixed;
  top: 30px; /* global-2026.css:149 .nav-bar-wrap */
  left: 50%;
  z-index: 40;
  transform: translateX(-50%);
}
.pui-navigation-menu__logo { display: inline-flex; flex: none; height: 31px; color: var(--pui-text-default); }
.pui-navigation-menu__logo > svg { height: 100%; width: auto; }
.pui-navigation-menu__logo:focus-visible { outline: none; border-radius: var(--pui-radius-4); box-shadow: var(--pui-ring); }  /* 3.5.2 */
.pui-navigation-menu__links { display: flex; align-items: center; gap: var(--pui-space-8); }

.pui-nav-link {
  display: inline-flex;
  align-items: center;
  gap: var(--pui-space-2);
  margin: 0;
  padding: var(--pui-space-4) var(--pui-space-8);
  border: 0;
  border-radius: var(--pui-radius-4);
  background: transparent;
  color: var(--pui-text-default);
  font-family: inherit;
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  outline: none;
  transition: color var(--pui-motion-state) var(--pui-motion-easing), background-color var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-nav-link:hover { color: var(--pui-text-disabled); }
.pui-nav-link:focus-visible { box-shadow: var(--pui-ring); }
.pui-nav-link--products { padding-inline-end: 0; }   /* the site's li.has-caret: 8 left, 0 right (global-2026.css:189) — was 2, Finding 47 */
.pui-nav-link__caret {
  width: var(--pui-size-icon-16);
  height: var(--pui-size-icon-16);
  transition: transform var(--pui-motion-menu) var(--pui-motion-easing);
}
.pui-nav-link[aria-expanded="true"] .pui-nav-link__caret { transform: rotate(180deg); }
.pui-nav-link--cta {
  margin: var(--pui-space-4) var(--pui-space-8);   /* the site's CTA sits inside a list item padded 4 / 8 (global-2026.css:179) and keeps its own 4 / 8 —
                                                       the link's padding is the label's, this margin is the item's; without it the bar was 10 px shorter and the
                                                       row slid 16 px right (2.4.2, Finding 47 — Fedor caught it on the pilot) */
  background: var(--pui-action-primary-bg);
  color: var(--pui-action-primary-fg);
}
.pui-nav-link--cta:hover { background: var(--pui-action-primary-bg-hover); color: var(--pui-action-primary-fg); }

/* Navigation menu - mobile — the bar below the tablet breakpoint, where the links collapse to a burger.
 * Figma: 744:135, rebuilt from the file's own Mani_nav_mob_old 733:1901 (D151). Everything the wide bar
 * declares is inherited — the frosted ground, radius/8, elevation/bar, padding space/8 — and only the
 * width and the burger are its own. The master draws the CLOSED bar only, so the opened form is the
 * Navigation panel the library already ships (D142).
 *
 *   <nav class="pui-navigation-menu pui-navigation-menu--mobile">
 *     <a class="pui-navigation-menu__logo" href="/">…mark…</a>
 *     <button class="pui-navigation-menu__burger" aria-label="Menu" aria-expanded="false">
 *       <span></span><span></span><span></span></button></nav>
 */
.pui-navigation-menu--mobile { width: 370px; }
.pui-navigation-menu__burger {
  display: flex;
  flex: none;
  flex-direction: column;
  gap: var(--pui-space-4);
  box-sizing: content-box;
  width: 32px;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--pui-text-default);
  cursor: pointer;
}
/* Three bars, 4 tall, the master's #171717 — which D4 absorbs into neutral/900, so this is the same ink
   the nav links take, through currentColor rather than a second declaration of it. */
.pui-navigation-menu__burger > span {
  display: block;
  height: 4px;
  border-radius: var(--pui-radius-full);
  background: currentColor;
}
.pui-navigation-menu__burger:focus-visible { outline: none; border-radius: var(--pui-radius-4); box-shadow: var(--pui-ring); }

.pui-navigation-panel {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-4);
  box-sizing: border-box;
  /* A dropdown HUGS its content. `min-width` alone left the panel a block-level flex box, so anywhere it
     is not absolutely positioned — a documentation frame, a section, any ordinary container — it grew to
     the full width of whatever held it: 507 px against the component's own 200 (Fedor, 2026-09-22,
     against his live site and the Figma component, both of which hug). The site's own panel hugs because
     it is positioned; the component must hug on its own. */
  width: fit-content;
  min-width: 200px;
  padding: var(--pui-space-20);
  border-radius: var(--pui-radius-8);
  background: var(--pui-surface-glass-strong);
  -webkit-backdrop-filter: blur(var(--pui-blur-panel));
  backdrop-filter: blur(var(--pui-blur-panel));
  box-shadow: var(--pui-elevation-floating);
  font-family: var(--pui-font-family);
  animation: pui-panel-in var(--pui-motion-menu) var(--pui-motion-easing-enter);
}
.pui-navigation-panel__link {
  padding: var(--pui-space-4);
  border-radius: var(--pui-radius-4);
  color: var(--pui-text-default);
  font-size: var(--pui-font-size-16);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
  text-decoration: none;
  transition: color var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-navigation-panel__link:hover { color: var(--pui-text-disabled); }
.pui-navigation-panel__link:focus-visible { outline: none; box-shadow: var(--pui-ring); }

/* The `to` frame states no opacity: the panel animates to ITS OWN value, so a consumer that keeps the panel in the DOM and
   opens it with its own opacity never sees the closed panel drawn at 1 for 250 ms on load (request 13, the site's products
   dropdown, measured .94 at 120 ms; pui-fade-in already works this way). */
@keyframes pui-panel-in {
  from { opacity: 0; transform: translateY(var(--pui-space-4)) scale(0.97); }
  to { transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .pui-navigation-panel { animation: none; }
}

/* command.css — Command (10_COMPONENTS.md § Navigation and overlay).
 *
 * The ⌘K palette. shadcn's command.tsx on cmdk (D17 — MAFO's global search is the use): Dialog's surface
 * (surface/page, radius/16, elevation/modal) at 512 over the Scrim, a 44 px search row (padding 12 / 16, the glyph in
 * text/subtle, the query at 14, an Esc Kbd), a 1 px line, then groups of Menu items under 12 px group labels, a
 * separator, an empty row ('No results found.'). At most eight rows before the list scrolls (cmdk's 300).
 * Figma 250:1499. Contract: registry/command/registry.json.
 *
 *   <div class="pui-command" role="dialog" aria-label="Command palette">
 *     <div class="pui-command__search"><span class="pui-icon">…search…</span><input placeholder="Type a command…"><kbd class="pui-kbd">Esc</kbd></div>
 *     <div class="pui-command__list">
 *       <div class="pui-dropdown-menu__group-label">Offers</div> …pui-menu-item… 
 *       <div class="pui-dropdown-menu__separator"></div>
 *       <div class="pui-empty pui-empty--inline">No results found.</div></div></div>
 * Keyboard  the search input holds the tab stop; every row takes tabindex="-1" (3.5.3). The script owes Up / Down
 *           over the rows, Enter to run, Escape to close and return focus to whatever opened the palette.
 */

.pui-command {
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  width: 512px;
  max-width: calc(100vw - var(--pui-space-32));
  overflow: hidden;
  border-radius: var(--pui-radius-16);
  background: var(--pui-surface-page);
  box-shadow: var(--pui-elevation-modal);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  line-height: var(--pui-font-line-height-normal);
}
.pui-command__search {
  display: flex;
  align-items: center;
  gap: var(--pui-space-8);
  padding: var(--pui-space-12) var(--pui-space-16);
  border-bottom: 1px solid var(--pui-border-default);
  color: var(--pui-text-subtle);
}
.pui-command__search > .pui-icon { flex: none; }
.pui-command__search > input {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--pui-text-default);
  font: inherit;
  outline: none;
  -webkit-appearance: none;
  appearance: none;
}
.pui-command__search > input::placeholder { color: var(--pui-text-caption); opacity: 1; }
.pui-command__search:focus-within { box-shadow: var(--pui-ring-inset); }   /* 3.5.2, as the combobox menu's */
.pui-command__list {
  max-height: 300px; /* cmdk's */
  overflow-y: auto;
  padding: var(--pui-space-8);
}

/* dialog.css — Dialog · Scrim (10_COMPONENTS.md § Navigation and overlay).
 *
 * A card that interrupts: one question, one decision, on a scrim over the page. Paggy's `.modal` geometry (radius 16,
 * padding 24, elevation/modal, surface/card), the site's close row and widths, shadcn's parts (Header / Footer /
 * Title / Description). Three widths: small 400 (confirms — Paggy's own confirm is 440, Alert dialog's; the Dialog
 * should follow at its next touch, queue note), medium 480 (shadcn; the site's one popup is 474 — stated), large 680 (Paggy's editor modals);
 * a header at a 16 gap with a ghost close, the content as a white slot (surface/slot, radius 8), a right-aligned
 * footer of two buttons at space/8. The Scrim is overlay/scrim over the whole viewport. Figma: 86:109 · 87:37.
 * Contract: registry/dialog/registry.json.
 *
 *   <div class="pui-scrim" data-state="open">
 *     <div class="pui-dialog pui-dialog--medium" role="dialog" aria-modal="true" aria-labelledby="t">
 *       <div class="pui-dialog__header"><div class="pui-dialog__texts"><h2 class="pui-dialog__title" id="t">Rename deck</h2>
 *         <p class="pui-dialog__description">…</p></div>
 *         <button class="pui-icon-button pui-icon-button--ghost pui-icon-button--small pui-dialog__close" aria-label="Close">…x…</button></div>
 *       <div class="pui-dialog__content"> …a Field… </div>
 *       <div class="pui-dialog__footer"><button class="pui-button pui-button--secondary">Cancel</button><button class="pui-button">Save</button></div></div></div>
 *
 * Size      (none) small 400 · --medium 480 · --large 680
 * Scrim     .pui-scrim — fixed, the viewport, centred content; [hidden] when closed
 * Keyboard  focus moves into the dialog on open and stays inside while it is open — legal under WCAG 2.1.2
 *           ONLY because Escape gets out; Escape closes and focus returns to whatever opened it (3.5.3).
 */

.pui-scrim {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--pui-space-16);
  background: var(--pui-overlay-scrim);
  animation: pui-fade-in var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-scrim[hidden] { display: none; }
.pui-scrim--static { position: static; inset: auto; min-height: 320px; }

.pui-dialog,
.pui-sheet,
.pui-drawer {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-16);
  box-sizing: border-box;
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  line-height: var(--pui-font-line-height-normal);
}
.pui-dialog {
  width: 400px;
  max-width: 100%;
  max-height: calc(100vh - var(--pui-space-32));
  padding: var(--pui-space-24);
  border-radius: var(--pui-radius-16);
  background: var(--pui-surface-card);
  box-shadow: var(--pui-elevation-modal);
}
.pui-dialog--medium { width: 480px; }
.pui-dialog--large { width: 680px; }

.pui-dialog__header,
.pui-sheet__header,
.pui-drawer__header {
  display: flex;
  align-items: flex-start;
  gap: var(--pui-space-16);
}
.pui-dialog__texts,
.pui-sheet__texts,
.pui-drawer__texts { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--pui-space-8); }
.pui-dialog__title,
.pui-sheet__title,
.pui-drawer__title {
  margin: 0;
  font-size: var(--pui-font-size-18);
  font-weight: var(--pui-font-weight-500);
  line-height: var(--pui-font-line-height-normal);
}
.pui-dialog__description,
.pui-sheet__description,
.pui-drawer__description { margin: 0; color: var(--pui-text-muted); font-weight: var(--pui-font-weight-400); }   /* R13 (request 9): the site's gate dialog note rendered Light */
.pui-dialog__close,
.pui-sheet__close,
.pui-drawer__close { flex: none; margin: calc(var(--pui-space-4) * -1) calc(var(--pui-space-4) * -1) 0 0; }
.pui-dialog__content {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: var(--pui-space-16);
  border-radius: var(--pui-radius-8);
  background: var(--pui-surface-slot);
}
.pui-dialog__footer,
.pui-sheet__footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--pui-space-8);
}

@keyframes pui-fade-in { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .pui-scrim { animation: none; }
}

/* sheet.css — Sheet · Drawer (10_COMPONENTS.md § Navigation and overlay).
 *
 * Sheet: a panel that slides in from the side and keeps the page behind it — shadcn's sheet.tsx on Base UI Dialog
 * (D17; Paggy's docked right panel app.css:435 .side is the house shape at the edge: a left border, padding 16; no MAFO or Cabinet
 * screen draws a side panel today — MAFO's filters sit inline above its tables): 384 wide (max-w-sm), the viewport's height, surface/page, a 1 px border/default
 * on the page side, elevation/modal, padding 16, gap 16; the header (title 18 Medium, description 14 text/muted, a
 * ghost close), the content, a right-aligned footer. Drawer: the bottom sheet on a phone — shadcn's drawer.tsx on vaul:
 * 390 wide, the top corners radius/16, a 100 × 8 handle in border/default, padding 8 / 16 / 16, the header without its
 * close, the content, a footer of two full-width Buttons stacked at space/8. Both share the header parts in
 * dialog.css. Figma: Sheet 251:2131 · Drawer 251:2136. Contracts: registry/sheet/registry.json, registry/drawer/registry.json.
 *
 *   <div class="pui-scrim"><div class="pui-sheet" role="dialog" aria-modal="true" aria-labelledby="t"> …__header (h2#t), __content, __footer… </div></div>
 *   <div class="pui-scrim"><div class="pui-drawer" role="dialog" aria-labelledby="t"><div class="pui-drawer__handle"></div> …__header, __content, __footer… </div></div>
 *
 * Sheet side   (none) right · --left
 * Keyboard  as Dialog: focus in on open, held while open, Escape closes and returns it to the opener (3.5.3).
 */

.pui-scrim:has(> .pui-sheet) { align-items: stretch; justify-content: flex-end; padding: 0; }
.pui-scrim:has(> .pui-sheet--left) { justify-content: flex-start; }
.pui-scrim:has(> .pui-drawer) { align-items: flex-end; justify-content: center; padding: 0; }

.pui-sheet {
  width: 384px;
  max-width: 100%;
  height: 100%;
  padding: var(--pui-space-16);
  border-left: 1px solid var(--pui-border-default);
  background: var(--pui-surface-page);
  box-shadow: var(--pui-elevation-modal);
  animation: pui-sheet-in-right var(--pui-motion-menu) var(--pui-motion-easing-enter);
}
.pui-sheet--left {
  border-left: 0;
  border-right: 1px solid var(--pui-border-default);
  animation-name: pui-sheet-in-left;
}
.pui-sheet__content,
.pui-drawer__content { flex: 1 1 auto; min-height: 0; overflow: auto; }

.pui-drawer {
  width: 390px;
  max-width: 100%;
  max-height: calc(100vh - var(--pui-space-48));
  align-items: stretch;
  padding: var(--pui-space-8) var(--pui-space-16) var(--pui-space-16);
  border-radius: var(--pui-radius-16) var(--pui-radius-16) 0 0;
  background: var(--pui-surface-page);
  box-shadow: var(--pui-elevation-modal);
  animation: pui-drawer-in var(--pui-motion-menu) var(--pui-motion-easing-enter);
}
.pui-drawer__handle {
  align-self: center;
  width: 100px;
  height: var(--pui-space-8);
  border-radius: var(--pui-radius-full);
  background: var(--pui-border-default);
}
.pui-drawer__footer { display: flex; flex-direction: column; gap: var(--pui-space-8); }
.pui-drawer__footer > .pui-button { display: flex; width: 100%; }

@keyframes pui-sheet-in-right { from { transform: translateX(100%); } }
@keyframes pui-sheet-in-left { from { transform: translateX(-100%); } }
@keyframes pui-drawer-in { from { transform: translateY(100%); } }
@media (prefers-reduced-motion: reduce) {
  .pui-sheet, .pui-drawer { animation: none; }
}

/* pagination.css — Pagination (10_COMPONENTS.md § Navigation and overlay).
 *
 * Previous, the pages, Next — the library's own buttons in a row space/4 apart: ghost for every page, secondary
 * (outlined) for the current one, an ellipsis before the last page. What react-paginate draws on MAFO's
 * search and report screens (AdvertiserSearch.js:566, AffiliateSearch.js, ReportTable.js) and what shadcn's Pagination composes from its Button. No tokens of
 * its own — its states are Button's. Figma 168:138: Size medium / small. Contract: registry/pagination/registry.json.
 *
 *   <nav class="pui-pagination" aria-label="Pages">
 *     <a class="pui-button pui-button--ghost" href="…"><span class="pui-icon pui-button__icon">…chevron-left…</span>Previous</a>
 *     <a class="pui-button pui-button--ghost" href="…">1</a>
 *     <a class="pui-button pui-button--secondary" aria-current="page" href="…">2</a> …
 *     <span class="pui-pagination__ellipsis" aria-hidden="true">…</span>
 *     <a class="pui-button pui-button--ghost" href="…">Next<span class="pui-icon pui-button__icon">…chevron-right…</span></a></nav>
 *
 * Size      on the buttons (pui-button--small for the small pager)
 * Rule      one outlined page, never two; a disabled Previous stays visible.
 */

.pui-pagination {
  display: inline-flex;
  align-items: center;
  gap: var(--pui-space-4);
  font-family: var(--pui-font-family);
}
.pui-pagination > .pui-button { min-width: var(--pui-size-control-medium); }
.pui-pagination > .pui-button--small { min-width: var(--pui-size-control-small); }
.pui-pagination__ellipsis {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--pui-size-control-medium);
  height: var(--pui-size-control-medium);
  color: var(--pui-text-muted);
  font-size: var(--pui-font-size-14);
}

/* breadcrumb.css — Breadcrumb · Breadcrumb item (10_COMPONENTS.md § Navigation and overlay).
 *
 * Where you are, as the path that got you here — Home › Optimisations › Postback control: MAFO's CBreadcrumb from the
 * route names (AppBreadcrumb.js), the concepts' Topbar, shadcn's Breadcrumb. A link in text/subtle (the Figma binds
 * the muted step) that turns to ink on hover, the current page in ink; 14 (body/x-small), padding 2, radius 4 for the
 * ring; chevron-right 16 in text/subtle between items at a space/8 gap. Figma: 181:16 · 181:15.
 * Contract: registry/breadcrumb/registry.json.
 *
 *   <nav class="pui-breadcrumb" aria-label="Breadcrumb"><ol class="pui-breadcrumb__list">
 *     <li><a class="pui-breadcrumb__item" href="/">Home</a></li>
 *     <li><span class="pui-icon pui-breadcrumb__separator">…chevron-right…</span></li>
 *     <li><span class="pui-breadcrumb__item" aria-current="page">Postback control</span></li></ol></nav>
 */

.pui-breadcrumb { font-family: var(--pui-font-family); }
.pui-breadcrumb__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--pui-space-8);
  margin: 0;
  padding: 0;
  list-style: none;
}
.pui-breadcrumb__list > li { display: inline-flex; align-items: center; }
.pui-breadcrumb__item {
  display: inline-block;
  padding: var(--pui-space-2);
  border-radius: var(--pui-radius-4);
  color: var(--pui-text-muted);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
  text-decoration: none;
  transition: color var(--pui-motion-state) var(--pui-motion-easing);
}
a.pui-breadcrumb__item:hover { color: var(--pui-text-default); }
a.pui-breadcrumb__item:focus-visible { outline: none; box-shadow: var(--pui-ring); }
.pui-breadcrumb__item[aria-current] { color: var(--pui-text-default); }
.pui-breadcrumb__separator { width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); color: var(--pui-text-subtle); }

/* stepper.css — Stepper · Step (10_COMPONENTS.md § Navigation and overlay).
 *
 * Where the user is in a flow with named steps. A step is a 24 px circle with the step's number beside its name —
 * the shape every desktop system shares (MUI's Stepper; shadcn has none): filled action/primary/bg with the number in
 * action/primary/fg when current, a check in its place when complete, border/default with the number in text/muted
 * when upcoming; the label 14 Medium in text/default, text/muted when upcoming. Steps are joined by 1 px connectors
 * (border/default; border/strong behind a completed step) at a space/12 rhythm, the row filling its column.
 * Figma: 253:2094 · 253:2088. Contract: registry/stepper/registry.json.
 *
 *   <ol class="pui-stepper">
 *     <li class="pui-step pui-step--complete"><span class="pui-step__circle"><span class="pui-icon">…check…</span></span><span class="pui-step__label">Details</span></li>
 *     <li class="pui-step pui-step--current" aria-current="step"><span class="pui-step__circle">2</span><span class="pui-step__label">Budget</span></li>
 *     <li class="pui-step"><span class="pui-step__circle">3</span><span class="pui-step__label">Creatives</span></li></ol>
 *
 * State     (none) upcoming · --current / [aria-current="step"] · --complete
 * Rule      a step is a place, not a button — Back and Next move between them.
 */

.pui-stepper {
  display: flex;
  align-items: center;
  gap: var(--pui-space-12);
  width: 100%;
  margin: 0;
  padding: 0;
  list-style: none;
  font-family: var(--pui-font-family);
}
.pui-step {
  display: inline-flex;
  align-items: center;
  gap: var(--pui-space-8);
  flex: none;
  color: var(--pui-text-muted);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-500);
  line-height: var(--pui-font-line-height-normal);
  white-space: nowrap;
}
/* The connector: every step but the last grows a line after it. */
.pui-step:not(:last-child) { flex: 1 1 auto; }
.pui-step:not(:last-child)::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  margin-inline-start: var(--pui-space-4);
  background: var(--pui-border-default);
}
.pui-step--complete:not(:last-child)::after { background: var(--pui-border-strong); }
.pui-step__circle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  box-sizing: border-box;
  width: var(--pui-space-24);
  height: var(--pui-space-24);
  border: 1px solid var(--pui-border-default);
  border-radius: var(--pui-radius-full);
  background: transparent;
  color: var(--pui-text-muted);
  font-size: var(--pui-font-size-12);
  font-weight: var(--pui-font-weight-500);
  line-height: 1;
}
.pui-step__circle > .pui-icon { width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); }
.pui-step--current,
.pui-step[aria-current="step"],
.pui-step--complete { color: var(--pui-text-default); }
.pui-step--current .pui-step__circle,
.pui-step[aria-current="step"] .pui-step__circle,
.pui-step--complete .pui-step__circle {
  border-color: var(--pui-action-primary-bg);
  background: var(--pui-action-primary-bg);
  color: var(--pui-action-primary-fg);
}

/* collapsible.css — Collapsible · Accordion · Accordion item (10_COMPONENTS.md § Navigation and overlay).
 *
 * Collapsible is one row that opens to show more — shadcn's collapsible.tsx on Base UI (D17): a trigger row, padding
 * 8 / 0, the label at 14 Medium, a chevron-down in text/muted that turns when open; no border of its own. Accordion
 * is the site's FAQ (echoai.css:1083–1146) as a component: Style site — padding 32 all round with a 32 gap, the
 * question at 18 Medium, a 24 px plus that turns 45° into a cross when open, the answer 16 Regular text/muted at
 * line-height 1.7 with padding 0 / 32 / 24, a 2 px border/default under the item; Style compact — shadcn's
 * accordion.tsx: padding 16 / 0, 14 Medium, a 16 px chevron, the answer 14 text/muted with 16 under it, a 1 px rule.
 * One open at a time is the code side's. The lines here are structural (a rule under each item) — the Separator test
 * allows them by name. Figma: 254:2128 · 254:2173 · 254:2166. Contracts: registry/collapsible/registry.json,
 * registry/accordion/registry.json.
 *
 *   <details class="pui-collapsible"><summary class="pui-collapsible__trigger">Advanced settings
 *     <span class="pui-icon pui-collapsible__chevron">…chevron-down…</span></summary>
 *     <div class="pui-collapsible__content">…</div></details>
 *   <div class="pui-accordion">
 *     <details class="pui-accordion-item" open><summary class="pui-accordion-item__question">How does billing work?
 *       <span class="pui-accordion-item__marker" aria-hidden="true"></span></summary>
 *       <div class="pui-accordion-item__answer">…</div></details> …</div>
 *   <div class="pui-accordion pui-accordion--compact"> …the same items with a chevron in __marker… </div>
 *
 * A native <details> carries the open state ([open]); a scripted one uses [data-state="open"] on the item.
 * Style     (none) site · --compact (on the accordion)
 */

.pui-collapsible { font-family: var(--pui-font-family); color: var(--pui-text-default); }
.pui-collapsible__trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--pui-space-8);
  padding: var(--pui-space-8) 0;
  border-radius: var(--pui-radius-4);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-500);
  line-height: var(--pui-font-line-height-normal);
  list-style: none;
  cursor: pointer;
  user-select: none;
  outline: none;
}
.pui-collapsible__trigger::-webkit-details-marker { display: none; }
.pui-collapsible__trigger:focus-visible { box-shadow: var(--pui-ring); }
.pui-collapsible__chevron {
  flex: none;
  width: var(--pui-size-icon-16);
  height: var(--pui-size-icon-16);
  color: var(--pui-text-muted);
  transition: transform var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-collapsible[open] .pui-collapsible__chevron,
.pui-collapsible[data-state="open"] .pui-collapsible__chevron { transform: rotate(180deg); }
.pui-collapsible__content { padding-bottom: var(--pui-space-8); font-size: var(--pui-font-size-14); line-height: var(--pui-font-line-height-normal); }

/* ── Accordion ───────────────────────────────────────────────────────────────────────────────── */
.pui-accordion { display: flex; flex-direction: column; width: 100%; font-family: var(--pui-font-family); color: var(--pui-text-default); }
.pui-accordion-item { border-bottom: 2px solid var(--pui-border-default); }
.pui-accordion-item__question {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--pui-space-32);
  padding: var(--pui-space-32);
  font-size: var(--pui-type-body-medium-font-size);   /* body/medium — 18 on the desktop it was quoted at, 16 on tablet and mobile (request 14; was the font-size/18 primitive, which no mode moved) */
  font-weight: var(--pui-font-weight-500);
  line-height: var(--pui-font-line-height-normal);
  list-style: none;
  cursor: pointer;
  user-select: none;
  outline: none;
}
.pui-accordion-item__question::-webkit-details-marker { display: none; }
.pui-accordion-item__question:focus-visible { box-shadow: var(--pui-ring); border-radius: var(--pui-radius-4); }
/* The site's plus: two 2 px bars of the ink in a 24 box, turned 45° into a cross when open. */
.pui-accordion-item__marker {
  position: relative;
  flex: none;
  width: var(--pui-space-24);
  height: var(--pui-space-24);
  transition: transform var(--pui-motion-move) var(--pui-motion-easing);
}
.pui-accordion-item__marker::before,
.pui-accordion-item__marker::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--pui-space-16);
  height: var(--pui-space-2);
  background: currentColor;
  transform: translate(-50%, -50%);
}
.pui-accordion-item__marker::after { transform: translate(-50%, -50%) rotate(90deg); }
.pui-accordion-item[open] .pui-accordion-item__marker,
.pui-accordion-item[data-state="open"] .pui-accordion-item__marker { transform: rotate(45deg); }
.pui-accordion-item__answer {
  padding: 0 var(--pui-space-32) var(--pui-space-24);
  color: var(--pui-text-muted);
  font-size: var(--pui-type-body-small-font-size);   /* body/small — 16 desktop, 14 tablet and mobile (request 14) */
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-loose);
}
.pui-accordion-item__answer > p { margin: 0; }

.pui-accordion--compact .pui-accordion-item { border-bottom-width: 1px; }
.pui-accordion--compact .pui-accordion-item__question {
  gap: var(--pui-space-16);
  padding: var(--pui-space-16) 0;
  font-size: var(--pui-font-size-14);
}
.pui-accordion--compact .pui-accordion-item__marker { width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); color: var(--pui-text-muted); }
.pui-accordion--compact .pui-accordion-item__marker::before,
.pui-accordion--compact .pui-accordion-item__marker::after { display: none; }
.pui-accordion--compact .pui-accordion-item__marker > .pui-icon { display: block; width: 100%; height: 100%; }
.pui-accordion--compact .pui-accordion-item[open] .pui-accordion-item__marker,
.pui-accordion--compact .pui-accordion-item[data-state="open"] .pui-accordion-item__marker { transform: rotate(180deg); }
.pui-accordion--compact .pui-accordion-item__answer {
  padding: 0 0 var(--pui-space-16);
  font-size: var(--pui-font-size-14);
  line-height: var(--pui-font-line-height-normal);
}

/* layout-utilities.css — Separator · Scroll area · Resizable handle · Aspect ratio (10_COMPONENTS.md § Navigation
 * and overlay, page `Layout utilities`).
 *
 * Separator: a line only where a line has to be — no decorative separators (DESIGN.md §Do's); Weight default is
 * border/default (an edge between two regions), strong is border/strong ('dividers that have to be seen'); 1 px.
 * Scroll area: shadcn's scroll-area.tsx — the viewport clips, a 10 px track at the edge holds a 6 px thumb in
 * border/default at radius full, border/strong on hover (the browser's own bar, styled — Paggy keeps the gutter).
 * Resizable handle: a 1 px border/default line in a 16 px hit area, an optional 16 × 24 grip box at radius/4 with
 * grip-vertical in text/muted; border/strong while dragged. Aspect ratio: a box that keeps its proportion —
 * surface/sunken at radius/8 with the image glyph in text/subtle, at 16:9, 4:3, 1:1. Figma: 255:2169 · 255:2183 ·
 * 255:2213 · 255:2235. Contracts: registry/separator, scroll-area, resizable, aspect-ratio.
 *
 *   <hr class="pui-separator">   <div class="pui-separator pui-separator--vertical pui-separator--strong" role="separator"></div>
 *   <div class="pui-scroll-area" tabindex="0" role="region" aria-label="…" style="height: 240px"> …content… </div>  (a scrollable region is keyboard-reachable)
 *   <div class="pui-resizable-handle pui-resizable-handle--grip" role="separator" aria-orientation="vertical"><span class="pui-resizable-handle__grip"><span class="pui-icon">…</span></span></div>
 *   <div class="pui-aspect-ratio pui-aspect-ratio--4-3"><img src="…" alt=""></div>
 */

.pui-separator {
  flex: none;
  width: 100%;
  height: 1px;
  margin: 0;
  border: 0;
  background: var(--pui-border-default);
}
.pui-separator--vertical { width: 1px; height: auto; align-self: stretch; min-height: 1em; }
.pui-separator--strong { background: var(--pui-border-strong); }

.pui-scroll-area {
  overflow: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--pui-border-default) transparent;
  scrollbar-gutter: stable;
}
/* 3.5.2 — a scroll area is a focusable region (2.3.5 made it one so a keyboard can scroll it) and
   had no indicator. An inset shadow is painted in the PADDING box, and `scrollbar-gutter: stable`
   put the right edge of the ring behind the scrollbar — 1498 px of the 1768 WCAG 2.4.13 asks for.
   An outline is painted on the border box and is never clipped, so this one control uses one. */
.pui-scroll-area:focus-visible { outline: 2px solid var(--pui-border-focus); outline-offset: -2px; }
.pui-scroll-area::-webkit-scrollbar { width: 10px; height: 10px; }
.pui-scroll-area::-webkit-scrollbar-track { background: transparent; }
.pui-scroll-area::-webkit-scrollbar-thumb {
  border: 2px solid transparent;
  border-radius: var(--pui-radius-full);
  background: var(--pui-border-default);
  background-clip: padding-box;
}
.pui-scroll-area::-webkit-scrollbar-thumb:hover { background-color: var(--pui-border-strong); }
.pui-scroll-area:hover { scrollbar-color: var(--pui-border-strong) transparent; }

.pui-resizable-handle {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--pui-space-16);
  align-self: stretch;
  cursor: col-resize;
  touch-action: none;
  user-select: none;
  outline: none;
}
.pui-resizable-handle::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 50%;
  width: 1px;
  background: var(--pui-border-default);
  transform: translateX(-50%);
  transition: background-color var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-resizable-handle:hover::before,
.pui-resizable-handle[data-state="dragging"]::before { background: var(--pui-border-strong); }
/* 3.5.2 — border/strong is 2.76 against the handle's own rest colour, so the keyboard look was
   below the 3.0 floor and identical to hover. Focus paints the seam in border/focus and widens it. */
.pui-resizable-handle:focus-visible::before { background: var(--pui-border-focus); width: var(--pui-space-4); }
.pui-resizable-handle--horizontal {
  width: auto;
  height: var(--pui-space-16);
  align-self: auto;
  cursor: row-resize;
}
.pui-resizable-handle--horizontal::before { inset: 50% 0 auto 0; width: auto; height: 1px; transform: translateY(-50%); }
.pui-resizable-handle__grip {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--pui-space-16);
  height: var(--pui-space-24);
  border-radius: var(--pui-radius-4);
  background: var(--pui-border-default);
  color: var(--pui-text-muted);
}
.pui-resizable-handle--horizontal .pui-resizable-handle__grip { width: var(--pui-space-24); height: var(--pui-space-16); }
.pui-resizable-handle__grip > .pui-icon { width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); }

.pui-aspect-ratio {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  aspect-ratio: 16 / 9;
  border-radius: var(--pui-radius-8);
  background: var(--pui-surface-sunken);
  color: var(--pui-text-subtle);
}
.pui-aspect-ratio--4-3 { aspect-ratio: 4 / 3; }
.pui-aspect-ratio--1-1 { aspect-ratio: 1 / 1; }
.pui-aspect-ratio > img,
.pui-aspect-ratio > video,
.pui-aspect-ratio > iframe { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* Content blocks */
/* card.css — Card (10_COMPONENTS.md § Content blocks).
 *
 * Fedor's card (D45) — the Deck kit's Card/Basic anatomy (8010:5150 · 8010:5277 · 8011:5691 · 8010:5518): a 64 icon
 * box with a 32 glyph, the title at body/large's size at Medium (22 web · 18 mobile · 26 slide — the site's card
 * master 2051:1569 and .ui-form__title), an optional grey subtitle band on card/band, the white slot at radius 8, the
 * accent head block of Filled header; padding space/20 and gap space/12 (Paggy's --card-pad / --card-gap, the kit's);
 * nested blocks radius/8 inside radius/16. Six finishes, each from a source: basic (Card/Basic/Main, Paggy's .card),
 * plain (the concepts' white dashboard card), border (the blog card's 1 px line), soft (the accent tint — the site's
 * stat cards), colour (the accent itself; ink flips on yellow through the Accent mode, D24), filled-header (the grey
 * card with an accent head). Slots follow the inversion rule: white on grey, grey on white. Figma 133:2.
 * Contract: registry/card/registry.json.
 *
 *   <div class="pui-card">
 *     <div class="pui-card__media"><img src="…" alt=""></div>
 *     <div class="pui-card__header"><span class="pui-card__icon"><span class="pui-icon pui-icon--32">…</span></span>
 *       <div class="pui-card__texts"><h3 class="pui-card__title">Supply-path optimisation</h3></div>
 *       <button class="pui-icon-button pui-icon-button--ghost pui-icon-button--small pui-card__action" aria-label="More">…</button></div>
 *     <div class="pui-card__band">When to scale or slow down</div>
 *     <div class="pui-card__slot">…</div>
 *     <div class="pui-button-group pui-card__footer">…</div></div>
 *   <div class="pui-card pui-card--filled-header"><div class="pui-card__head"> …the header parts… </div> …</div>
 *
 *   the card as a link (request 41) — the same parts on an <a> or a <button>, no class more:
 *   <a class="pui-card pui-card--border" href="…"><span class="pui-card__media"><img src="…" alt=""></span>
 *     <span class="pui-card__header"><span class="pui-card__texts"><span class="pui-card__logo"><img src="…" alt="Client"></span>
 *       <span class="pui-card__title">…</span><span class="pui-card__description">…</span><span class="pui-badge pui-badge--small">…</span></span>
 *       <span class="pui-icon-button pui-icon-button--small pui-icon-button--round pui-card__action">…arrow-up-right…</span></span></a>
 *
 * Style     (none) basic · --plain · --border · --soft · --colour · --filled-header
 * Lined     --lined — a 2 px border/default line on a tinted finish (basic, soft, colour); --border is the white card's 1 px
 * Raised    --raised — a tinted card with no line, lifted by elevation/card: a card on a white page (the form card, request 34)
 * Link      an <a> or a <button> that IS the card: it lifts on hover, its Action steps out, the header sits on its base
 *           line, the title and the line under it are body/medium Regular; __logo is a mark over the title (request 41)
 * Form      a card that holds a .pui-form takes the form's inset — space/32, space/24 on a phone (request 34)
 * Accent    set data-accent on the card or a parent (blue / red / yellow / multicolor) — soft, colour and
 *           filled-header follow it; yellow's ink is the token's, never a hand override
 * Sequence  a row of cards in multicolor colours its icon boxes by position, sequence/1…6 (request 5); ONE card names
 *           its step with data-sequence="1…6" (request 35). The glyph is white on every step, yellow included
 * Canvas    the title and band scale with data-canvas (D36) — 22 / 18 on the web, 26 / 20 on a slide
 */

.pui-card {
  display: flex;
  flex-direction: column;
  gap: var(--pui-layout-card-gap);
  box-sizing: border-box;
  padding: var(--pui-layout-card-padding);
  border: 1px solid transparent;
  border-radius: var(--pui-radius-16);
  background: var(--pui-surface-card);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  font-size: var(--pui-type-body-medium-font-size);
  line-height: var(--pui-font-line-height-normal);
  --pui-card-slot: var(--pui-surface-slot);
}
.pui-card--plain,
.pui-card--border { background: var(--pui-surface-page); --pui-card-slot: var(--pui-surface-card); }
.pui-card--border { border-color: var(--pui-border-default); }
/* --lined: a 2 px border/default line on a TINTED card — Fedor's Echo AI contact card, kept against the kit's lineless tints
   ('the form is mixing with the background'; request 18, 2026-09-25). Basic, soft and colour take it; the white finishes have --border. */
.pui-card--lined { border-width: 2px; border-color: var(--pui-border-default); }
/* --raised: the tinted card with no line, lifted by its shadow — the form card on a white page (Contact, the gated case
   study; request 34, 2026-09-28). Never with --border or --lined: a line and a shadow together is the anti-pattern. */
.pui-card--raised { box-shadow: var(--pui-elevation-card); }
.pui-card--soft { background: var(--pui-accent-bg); }
.pui-card--colour { background: var(--pui-accent-main); color: var(--pui-accent-on-accent); }

.pui-card__media {
  overflow: hidden;
  aspect-ratio: 16 / 9;
  border-radius: var(--pui-radius-8);
  background: var(--pui-surface-sunken);
}
.pui-card__media > img,
.pui-card__media > video { display: block; width: 100%; height: 100%; object-fit: cover; }

.pui-card__header,
.pui-card__head {
  display: flex;
  align-items: center;
  gap: var(--pui-layout-card-padding);
}
.pui-card__head {
  gap: var(--pui-space-20);
  padding: var(--pui-space-12);   /* request 11 — Fedor's Figma edit of 2026-09-25: one inset on all four sides (Head 132:117 bound to space/12; was 8 / 12) */
  border-radius: var(--pui-radius-8);
  background: var(--pui-accent-main);
  color: var(--pui-accent-on-accent);
}
.pui-card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--pui-space-64);
  height: var(--pui-space-64);
  border-radius: var(--pui-radius-8);
  background: var(--pui-accent-main);
  color: var(--pui-accent-on-accent);
}
/* THE WHITE BOX — on every ground the accent paints (the soft tint, the colour card, the filled head) the box is white and
   its glyph is accent/text: the accent itself on blue and red, the ink on yellow, where accent/main on white was
   yellow on white (request 27 — Fedor's iRTB drawing is dark; D24's flip, read from the role that already carries it).
   The soft card takes the white box since request 36 (the site's demand panels, his drawing); it drew the accent box. */
.pui-card--soft .pui-card__icon,
.pui-card--colour .pui-card__icon,
.pui-card__head .pui-card__icon { background: var(--pui-surface-page); color: var(--pui-accent-text); }
/* THE SEQUENCE (request 5, Fedor 2026-09-24): in the multicolor accent a ROW of cards reads the six-step sequence by
   position — sequence/1…6: red, yellow, blue, blue/300, grey, red/300 — so the three brand mains are not the only colours
   on a page. Set data-accent="multicolor" on the row (or on each card) and the icon boxes cycle. The glyph is WHITE on
   every step, the yellow one included — Fedor's decision, 2026-09-28, on his own files (it was the dark ink of D24 until
   2.3.0; the yellow ACCENT keeps D24's ink — this is the sequence's box alone). A single card in multicolor keeps its own accent (red). */
[data-accent="multicolor"] > .pui-card:nth-child(6n+1) .pui-card__icon,
.pui-card[data-accent="multicolor"]:nth-child(6n+1) .pui-card__icon { background: var(--pui-sequence-1); color: var(--pui-text-on-accent); }
[data-accent="multicolor"] > .pui-card:nth-child(6n+2) .pui-card__icon,
.pui-card[data-accent="multicolor"]:nth-child(6n+2) .pui-card__icon { background: var(--pui-sequence-2); color: var(--pui-text-on-accent); }
[data-accent="multicolor"] > .pui-card:nth-child(6n+3) .pui-card__icon,
.pui-card[data-accent="multicolor"]:nth-child(6n+3) .pui-card__icon { background: var(--pui-sequence-3); color: var(--pui-text-on-accent); }
[data-accent="multicolor"] > .pui-card:nth-child(6n+4) .pui-card__icon,
.pui-card[data-accent="multicolor"]:nth-child(6n+4) .pui-card__icon { background: var(--pui-sequence-4); color: var(--pui-text-on-accent); }
[data-accent="multicolor"] > .pui-card:nth-child(6n+5) .pui-card__icon,
.pui-card[data-accent="multicolor"]:nth-child(6n+5) .pui-card__icon { background: var(--pui-sequence-5); color: var(--pui-text-on-accent); }
[data-accent="multicolor"] > .pui-card:nth-child(6n+6) .pui-card__icon,
.pui-card[data-accent="multicolor"]:nth-child(6n+6) .pui-card__icon { background: var(--pui-sequence-6); color: var(--pui-text-on-accent); }
/* ONE CARD NAMES ITS STEP (request 35): the site's rows skip steps and repeat them — five chips on 1 2 3 5 6, four cards all
   on the first, a slider whose cards are each alone in their slide — so position cannot say it. data-sequence="1…6" on
   the card; it outranks the row's position and the finish's white box. */
.pui-card[data-sequence="1"] > .pui-card__header > .pui-card__icon { background: var(--pui-sequence-1); color: var(--pui-text-on-accent); }
.pui-card[data-sequence="2"] > .pui-card__header > .pui-card__icon { background: var(--pui-sequence-2); color: var(--pui-text-on-accent); }
.pui-card[data-sequence="3"] > .pui-card__header > .pui-card__icon { background: var(--pui-sequence-3); color: var(--pui-text-on-accent); }
.pui-card[data-sequence="4"] > .pui-card__header > .pui-card__icon { background: var(--pui-sequence-4); color: var(--pui-text-on-accent); }
.pui-card[data-sequence="5"] > .pui-card__header > .pui-card__icon { background: var(--pui-sequence-5); color: var(--pui-text-on-accent); }
.pui-card[data-sequence="6"] > .pui-card__header > .pui-card__icon { background: var(--pui-sequence-6); color: var(--pui-text-on-accent); }
.pui-card__texts { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--pui-space-4); }
.pui-card__title {
  margin: 0;
  font-size: var(--pui-type-body-large-font-size);
  font-weight: var(--pui-font-weight-500);
  line-height: var(--pui-font-line-height-normal);
}
.pui-card__description { margin: 0; color: var(--pui-text-muted); font-size: var(--pui-type-body-x-small-font-size); }
.pui-card--colour .pui-card__description,
.pui-card__head .pui-card__description { color: inherit; }
.pui-card__action { flex: none; align-self: flex-start; }

.pui-card__band {
  margin: 0;   /* request 37 — a part written as a <p> keeps no page margin (Bootstrap's 1rem on the site) */
  padding: var(--pui-space-12);
  border-radius: var(--pui-radius-8);
  background: var(--pui-card-band);
  color: var(--pui-text-default);
  font-size: var(--pui-type-body-medium-font-size);
  font-weight: var(--pui-font-weight-400);   /* R13 — stated, or a page's body { font-weight: 300 } reaches in (request 9, the site's legacy layout.min.css) */
  line-height: var(--pui-font-line-height-normal);
}
.pui-card__slot {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-12);
  padding: var(--pui-space-12);
  border-radius: var(--pui-radius-8);
  background: var(--pui-card-slot);
  color: var(--pui-text-default);
  font-weight: var(--pui-font-weight-400);   /* R13 (request 9) */
}
.pui-card__slot > :is(p, h1, h2, h3, h4, h5, h6) { margin: 0; }   /* request 37 — the slot's gap is the rhythm */
.pui-card--filled-header .pui-card__slot { background: transparent; padding: 0; }
.pui-card__footer { align-self: flex-start; }

/* THE FORM'S CARD (request 34; Fedor, 2026-09-28: 'there is no form without a card'). A card that holds a form takes the
   form's inset — space/32, space/24 under the web-mobile mode's 768 — where a card's own is space/20. No class: the
   form says it. Its finish is one of three: --lined (tinted, the 2 px line), --raised (tinted, lifted), --plain (white). */
.pui-card:has(> .pui-form) { padding: var(--pui-space-32); }
@media (max-width: 767px) {
  .pui-card:has(> .pui-form) { padding: var(--pui-space-24); }
}

/* THE CARD AS A LINK (request 41) — a blog post, a case study, a team member: one object, the Card as an <a> or a
   <button>, built from the Card's own parts. No component per content type (Fedor, 2026-09-28) and no class more: the
   element says it. :where() keeps each rule at the weight of its classes, so a page's one-class rule still places it.
   It lifts on hover (elevation/card, space/4 up over motion/menu), the Action steps out (3 px over 300 ms — the site's
   arrow, stated), the header sits on its base line at space/12, the title and the line under it are body/medium
   Regular in text/default, a badge stands space/8 under them, a logo is a 125 × 30 box on a 35 line (stated). */
:where(a, button).pui-card {
  justify-content: space-between;
  margin: 0;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition:
    box-shadow var(--pui-motion-menu) var(--pui-motion-easing),
    transform var(--pui-motion-menu) var(--pui-motion-easing);
}
:where(a, button).pui-card:hover { box-shadow: var(--pui-elevation-card); transform: translateY(calc(-1 * var(--pui-space-4))); }
.pui-card:focus-visible { outline: none; box-shadow: var(--pui-ring); }   /* only a card that is a link or a button takes focus */
:where(a, button).pui-card > .pui-card__header { align-items: flex-end; justify-content: space-between; gap: var(--pui-space-12); }
:where(a, button).pui-card .pui-card__texts { gap: 0; }
:where(a, button).pui-card .pui-card__title,
:where(a, button).pui-card .pui-card__description {
  color: var(--pui-text-default);
  font-size: var(--pui-type-body-medium-font-size);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
}
:where(a, button).pui-card .pui-card__texts > .pui-badge { align-self: flex-start; margin-top: var(--pui-space-8); }
:where(a, button).pui-card .pui-card__action { align-self: flex-end; transition: transform 300ms var(--pui-motion-easing-enter); }
:where(a, button).pui-card:hover .pui-card__action { transform: translate(3px, -3px); }
.pui-card__logo { display: flex; align-items: center; height: 35px; }
.pui-card__logo > img { width: auto; height: auto; max-width: 125px; max-height: 30px; object-fit: contain; object-position: left center; }
@media (prefers-reduced-motion: reduce) {
  :where(a, button).pui-card,
  :where(a, button).pui-card .pui-card__action { transition: none; }
  :where(a, button).pui-card:hover,
  :where(a, button).pui-card:hover .pui-card__action { transform: none; }
}

/* item.css — Item (10_COMPONENTS.md § Content blocks).
 *
 * A list row: media, a title, a description, actions. Paggy's share rows and template-picker rows (app.css:1432
 * .share-row, :272 .tpl-opt) on shadcn's item.tsx shape (D17): the title 14 Medium in text/default, the description 12
 * text/subtle, radius/8, hover surface/sunken, the selected row on the Accent bg with a 16 px check in accent/main;
 * Style default (no border) · outline (border/default) · muted (surface/sunken); Size default (space/16) · small
 * (space/8 / space/12 — Paggy's). Rows stack at 0 or space/4 — no separators. Figma 256:462.
 * Contract: registry/item/registry.json.
 *
 *   <div class="pui-item pui-item--selected">
 *     <span class="pui-avatar pui-avatar--blue pui-item__media">FK</span>
 *     <div class="pui-item__texts"><p class="pui-item__title">Cover — split</p><p class="pui-item__description">Used 12 times</p></div>
 *     <span class="pui-icon pui-item__check">…check…</span>
 *     <button class="pui-button pui-button--secondary pui-button--small pui-item__button">Open</button>
 *     <button class="pui-icon-button pui-icon-button--ghost pui-icon-button--small pui-item__action" aria-label="More">…</button></div>
 *
 * Style     (none) default · --outline · --muted
 * Size      (none) default · --small
 * State     :hover · :focus-visible (the hover surface plus the inset ring — the surface alone is 1.0 on a card, 3.5.2)
 *           · selected = --selected, or [aria-selected="true"] when the item is a role="option" inside a role="listbox" (aria-selected is not valid on a plain div — axe, M2.3.5)
 */

.pui-item {
  display: flex;
  align-items: center;
  gap: var(--pui-space-16);
  box-sizing: border-box;
  width: 100%;
  padding: var(--pui-space-16);
  border: 1px solid transparent;
  border-radius: var(--pui-radius-8);
  background: transparent;
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  line-height: var(--pui-font-line-height-normal);
  text-align: start;
  text-decoration: none;
  transition: background-color var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-item:hover,
.pui-item:focus-visible { background: var(--pui-surface-sunken); outline: none; }
/* 3.5.2 — surface/sunken on a card is 1.0 against the ground: the focused state was invisible there.
   The item fills its list, so the ring is the inset one. */
.pui-item:focus-visible { box-shadow: var(--pui-ring-inset); }
.pui-item[aria-selected="true"],
.pui-item--selected { background: var(--pui-accent-bg); }
.pui-item--outline { border-color: var(--pui-border-default); }
.pui-item--muted { background: var(--pui-surface-sunken); }
.pui-item--muted:hover { background: var(--pui-tint-neutral-bg); }
.pui-item--small { gap: var(--pui-space-12); padding: var(--pui-space-8) var(--pui-space-12); }

.pui-item__media { flex: none; }
.pui-item__texts { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--pui-space-2); }
.pui-item__title { margin: 0; font-weight: var(--pui-font-weight-500); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pui-item__description { margin: 0; color: var(--pui-text-caption); font-size: var(--pui-font-size-12); font-weight: var(--pui-font-weight-400); }   /* R13 (request 9) */
.pui-item__check {
  flex: none;
  width: var(--pui-size-icon-16);
  height: var(--pui-size-icon-16);
  color: var(--pui-accent-main);
  visibility: hidden;
}
.pui-item[aria-selected="true"] .pui-item__check,
.pui-item--selected .pui-item__check { visibility: visible; }
.pui-item__button,
.pui-item__action { flex: none; }

/* kpi-tile.css — KPI tile (10_COMPONENTS.md § Content blocks).
 *
 * One number that matters, its label, and how it moved — Paggy's .kpi-tile (Card/Metric/color_card), the concepts'
 * Card/Metric/color_status_input that opens every MAFO dashboard, the site's stat cards. The tint is a colour team,
 * not a status (kpi/tone/{tone}/bg — a variant axis, not the Accent mode, because one row shows every team); the delta pill
 * is the only thing that says good or bad. Padding space/20, radius/16; Value title/large (36, 600) — on a slide Paggy's KPI value is 64 / 600, display/small, and the tile's slide size is
 * an open decision (D84 e) — Label body/small (16 / 14), a white detail slot at radius/8 with a 24 icon and a body/medium line. TWO BLOCKS, pushed apart (D59,
 * Fedor 2026-09-09; the site file's Card/Metric/color_card 2092:2979 and color_status_input 3928:8574 — a vertical
 * auto-layout, SPACE_BETWEEN, itemSpacing 20): the value with its delta at the TOP, the label with its detail at the
 * BOTTOM (a 4 gap between those two), and whatever height the row gives the tile opens between them — never a stack
 * that runs top-down and leaves the bottom empty. In the detail slot the 24 glyph sits on the text block's TOP line
 * (the Icon list construction; the library's KPI tile 141:56 as Fedor fixed it 2026-09-09) — not centred on a wrapped line. FILL it in the row; three across in a narrow column, never four.
 * Figma 140:55. Contract: registry/kpi-tile/registry.json.
 *
 *   <div class="pui-kpi-tile pui-kpi-tile--blue">
 *     <div class="pui-kpi-tile__top"><span class="pui-kpi-tile__value">$150K+</span><span class="pui-badge pui-badge--green pui-badge--small">+12 %</span></div>
 *     <div class="pui-kpi-tile__bottom"><span class="pui-kpi-tile__label">Monthly revenue growth</span>
 *       <div class="pui-kpi-tile__detail"><span class="pui-icon pui-icon--24">…</span>$ 8 969 916</div></div></div>
 *
 *   a category instead of a delta — the same corner, no ring:
 *     <span class="pui-badge pui-badge--blue pui-badge--small pui-kpi-tile__tag"><span class="pui-icon pui-badge__icon">…</span>Fintech</span>
 *
 * Narrow    under 200 px of its OWN width (four across on a tablet, two on a phone — the site's 167) the tile stacks the
 *           value over its tag at space/4, steps the value to title/medium and sits 12 / 20 inside (request 42). The
 *           tile is a size container for it: it takes its width from its row and never from its content
 * Tone      (none) neutral · --blue · --red · --yellow. The neutral tone FOLLOWS THE GROUND (ground.css, D107): it is
 *           surface/sunken — neutral/100 on a white page, neutral/200 on the product ground; the three tints do not move
 * Parts     __tag — a category in the top corner instead of a delta; never rung, whatever its tone (D92)
 * Rule      only a STATUS delta is rung — --green up, --red down. A badge wearing the card's own tint
 *           (grey on grey, blue on blue) is a label, not a status, and stays flush (D92, Fedor 2026-09-16)
 */

.pui-kpi-tile {
  container: pui-kpi-tile / inline-size;   /* request 42 — the narrow form reads the tile's own width */
  display: flex;
  flex-direction: column;
  justify-content: space-between; /* D59: the top block and the bottom block, the room between them */
  gap: var(--pui-space-20);
  box-sizing: border-box;
  min-width: 0;
  padding: var(--pui-space-20);
  border-radius: var(--pui-radius-16);
  background: var(--pui-kpi-tone-neutral-bg);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
}
.pui-kpi-tile--blue { background: var(--pui-kpi-tone-blue-bg); }
.pui-kpi-tile--red { background: var(--pui-kpi-tone-red-bg); }
.pui-kpi-tile--yellow { background: var(--pui-kpi-tone-yellow-bg); }
.pui-kpi-tile__top {
  display: flex;
  align-items: center;   /* Fedor, 2026-09-25, in Figma first (140:55 Top, CENTER): 'it looks much better when the badge and numbers are center aligned' — was flex-start (request 10) */
  justify-content: space-between;
  gap: var(--pui-space-12);
}
/* THE DELTA'S RING (D92, Fedor 2026-09-16, corrected by him the same day). ONLY A STATUS DELTA WEARS IT —
   green because the number went up, red because it went down. That is the whole rule, and the tone is how
   it is read: a badge in this corner that carries the CARD'S OWN TINT (grey on the grey tile, blue on the
   blue one) is a LABEL wearing the tile's colour, not a status, and it stays flush. Fedor, on the first
   build, which rang the neutral 'flat' badge too: "we are working only with green positive and red negative
   status badges — this is a different thing from coloured badges for cards, blue tints for blue cards,
   yellow for yellow, grey for grey. This is a big difference."
   Why a status delta needs it and a label does not: the delta is the one thing on the tile that says good or
   bad, and it says it in a tint-pair sitting on another tint, where the two edges run together; the ring in
   the slot white cuts it out, the same device the white detail slot uses lower down (the inversion rule).
   It is the TILE's ring, never the Badge's — a badge anywhere else in the library is flush, and
   css_rules_test.py holds both halves. A category tag says so with __tag and is excluded whatever its tone,
   because a red tag on a red tile is a team, not a fall. box-shadow, not border: the ring is outside the box
   (Figma strokeAlign OUTSIDE), so the badge's own 2/4 padding is untouched. */
.pui-kpi-tile__top > .pui-badge--green,
.pui-kpi-tile__top > .pui-badge--red {
  box-shadow: 0 0 0 2px var(--pui-kpi-delta-ring);
}
.pui-kpi-tile__top > .pui-badge.pui-kpi-tile__tag {
  box-shadow: none;
}
.pui-kpi-tile__bottom {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-4);
  min-width: 0;
}
.pui-kpi-tile__value {
  margin: 0;   /* request 37 — written as a <p> on the site; the page's paragraph margin stays out */
  white-space: nowrap;   /* a number never breaks */
  font-size: var(--pui-type-title-large-font-size);
  font-weight: var(--pui-type-title-large-font-weight);
  line-height: var(--pui-type-title-large-line-height);
  letter-spacing: var(--pui-type-title-large-letter-spacing);
}
.pui-kpi-tile__label {
  margin: 0;   /* request 37 */
  font-size: var(--pui-type-body-small-font-size);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
}
.pui-kpi-tile__detail {
  display: flex;
  align-items: flex-start; /* the glyph on the text block's top line, as in Icon list — Fedor's fix in the library (141:56), 2026-09-09 */
  gap: var(--pui-space-12);
  padding: var(--pui-space-12);
  border-radius: var(--pui-radius-8);
  background: var(--pui-surface-slot);
  font-size: var(--pui-type-body-medium-font-size);
  line-height: var(--pui-font-line-height-normal);
}
.pui-kpi-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--pui-layout-section-gap);
}

/* THE NARROW FORM (request 42) — Fedor's tablet and phone tiles on the site, stated once for every page: below 200 px
   of the tile's own width the top row stacks (the value over its tag, space/4), the value is title/medium, and the
   tile sits 12 above and below instead of 20. A container cannot restyle itself, so the 8 that comes off the top and
   the bottom is taken by the two blocks (space/12 − space/20). */
@container pui-kpi-tile (max-width: 199px) {
  .pui-kpi-tile > .pui-kpi-tile__top { flex-direction: column; align-items: flex-start; gap: var(--pui-space-4); margin-top: calc(var(--pui-space-12) - var(--pui-space-20)); }
  .pui-kpi-tile > .pui-kpi-tile__bottom { margin-bottom: calc(var(--pui-space-12) - var(--pui-space-20)); }
  .pui-kpi-tile > .pui-kpi-tile__top > .pui-kpi-tile__value {
    font-size: var(--pui-type-title-medium-font-size);
    line-height: var(--pui-type-title-medium-line-height);
    letter-spacing: var(--pui-type-title-medium-letter-spacing);
  }
}

/* insight-block.css — Insight block (10_COMPONENTS.md § Content blocks).
 *
 * One sentence that says what the numbers mean — Paggy's .insight (the statement in the accent over a grey subline)
 * and the Deck kit's Insight (an accent bar beside a dark statement). Never wider than three quarters of the canvas
 * (Fedor 2026-08-18, locked — insight/max-width 1344 is the block's max-width, and the rules test reads it). Plain:
 * Statement title/medium in accent/text (yellow flips to ink), Subtext body/small in the muted ink; gap space/12. Bar:
 * a 12 px accent/main pill beside Statement headline/small and Subtext body/large, both text/default, padding space/12
 * above and below. Figma 145:10. Contract: registry/insight-block/registry.json.
 *
 *   <div class="pui-insight-block"><p class="pui-insight-block__statement">Supply-path optimisation cut costs 18 %.</p>
 *     <p class="pui-insight-block__subtext">MAFO · Q3 2026 · all publishers</p></div>
 *   <div class="pui-insight-block pui-insight-block--bar"> …the same two texts… </div>
 *
 * Style     (none) plain · --bar
 * Canvas    (none) the slide's sizes · --web with --bar — a block that opens a section of a web page under its heading
 *           pair: Statement title/small at Semibold, Subtext body/medium on the relaxed line (request 38)
 * Accent    the Accent mode on the instance (data-accent); one insight, one accent
 */

.pui-insight-block {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-12);
  box-sizing: border-box;
  width: 100%;
  max-width: var(--pui-insight-max-width);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
}
.pui-insight-block__statement {
  margin: 0;
  color: var(--pui-accent-text);
  font-size: var(--pui-type-title-medium-font-size);
  font-weight: var(--pui-type-title-medium-font-weight);
  line-height: var(--pui-type-title-medium-line-height);
}
.pui-insight-block__subtext {
  margin: 0;
  color: var(--pui-text-muted);
  font-size: var(--pui-type-body-small-font-size);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
}
.pui-insight-block--bar {
  flex-direction: row;
  padding: var(--pui-space-12) 0;
}
.pui-insight-block--bar::before {
  content: "";
  flex: none;
  width: var(--pui-space-12);
  border-radius: var(--pui-radius-full);
  background: var(--pui-accent-main);
}
.pui-insight-block__texts { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--pui-space-12); }
.pui-insight-block--bar .pui-insight-block__statement {
  color: var(--pui-text-default);
  font-size: var(--pui-type-headline-small-font-size);
  font-weight: var(--pui-type-headline-small-font-weight);
  line-height: var(--pui-type-headline-small-line-height);
}
.pui-insight-block--bar .pui-insight-block__subtext {
  color: var(--pui-text-default);
  font-size: var(--pui-type-body-large-font-size);
}

/* ON A WEB PAGE (request 38): the bar's 40 over 22 are a slide's. Under a section's heading pair the statement is
   title/small at Semibold and the text body/medium on the relaxed line — the site's product pages, Fedor's drawing.
   The width is the page's (636–740 there); the three-quarter cap still holds. */
.pui-insight-block--web.pui-insight-block--bar .pui-insight-block__statement {
  font-size: var(--pui-type-title-small-font-size);
  font-weight: var(--pui-font-weight-600);
  line-height: var(--pui-type-title-small-line-height);
}
.pui-insight-block--web.pui-insight-block--bar .pui-insight-block__subtext {
  font-size: var(--pui-type-body-medium-font-size);
  line-height: var(--pui-font-line-height-relaxed);
}

/* icon-list.css — Icon list (10_COMPONENTS.md § Content blocks).
 *
 * A row with a glyph and a point — Paggy's Icon list (the L2 / L2C rows), the site's .icon-list, the concepts'
 * supporting lists. The glyph is the accent's LIGHT tint (icon-list/ink — red 300, blue 300, yellow keeps its main;
 * Fedor 2026-08-10), never the accent itself; success and danger rows carry the status solids; a neutral row's glyph is
 * text/subtle — grey, the de-emphasised half of a problem / solution pair (Fedor 2026-09-25, request 12; it was ink).
 * One colour per list on a blue, red or yellow slide; on a multicolor slide the deck cycles the glyphs red, yellow,
 * blue, grey row by row (Paggy's seq_accent). Box: padding space/12, gap space/12, radius 8 (the sources' 9 — stated); the glyph sits 2 px
 * down (the site's nudge). Medium = Title body/large Medium + Text body/small; small = one line body/medium. Rows stack
 * at gap 0 — the padding is the rhythm. Figma 149:81. Contract: registry/icon-list/registry.json.
 *
 *   <ul class="pui-icon-list">
 *     <li class="pui-icon-list__row"><span class="pui-icon pui-icon--24 pui-icon-list__icon">…</span>
 *       <div class="pui-icon-list__texts"><p class="pui-icon-list__title">Verified traffic</p><p class="pui-icon-list__text">…</p></div></li>
 *     <li class="pui-icon-list__row pui-icon-list__row--success">…</li></ul>
 *
 * Size      (none) medium · --small (on the list)
 * Canvas    (none) the deck's rows, which touch · --web — a list on a web page (request 39; Fedor's values of
 *           2026-09-03): the rows stand space/12 apart; under 768 a success or danger row centres its glyph on its
 *           (often two-line) text. The title stands space/12 over its text in both
 * Emphasis  a phrase in <strong> or <b> inside a row's text is Bold 700 — a phrase, never the row (R11)
 * Tone      (none) accent · __row--success · __row--danger · __row--neutral
 * Sequence  data-accent="multicolor" on the list colours the glyphs by row, sequence/1…6 (request 5)
 */

.pui-icon-list {
  display: flex;
  flex-direction: column;
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
}
.pui-icon-list__row {
  display: flex;
  align-items: flex-start;
  gap: var(--pui-space-12);
  padding: var(--pui-space-12);
  border-radius: var(--pui-radius-8);
}
.pui-icon-list__icon {
  flex: none;
  width: var(--pui-size-icon-24);
  height: var(--pui-size-icon-24);
  margin-top: var(--pui-space-2);
  color: var(--pui-icon-list-ink);
}
.pui-icon-list__row--success .pui-icon-list__icon { color: var(--pui-status-success-solid); }
.pui-icon-list__row--danger .pui-icon-list__icon { color: var(--pui-status-danger-solid); }
.pui-icon-list__row--neutral .pui-icon-list__icon { color: var(--pui-text-subtle); }
/* THE SEQUENCE (request 5): a list in the multicolor accent colours its glyphs by row, sequence/1…6 — the site's
   six-step order (Fedor, 2026-09-24), a step past the deck's four. The status and neutral tones keep their own ink. */
.pui-icon-list[data-accent="multicolor"] > .pui-icon-list__row:nth-child(6n+1) > .pui-icon-list__icon { color: var(--pui-sequence-1); }
.pui-icon-list[data-accent="multicolor"] > .pui-icon-list__row:nth-child(6n+2) > .pui-icon-list__icon { color: var(--pui-sequence-2); }
.pui-icon-list[data-accent="multicolor"] > .pui-icon-list__row:nth-child(6n+3) > .pui-icon-list__icon { color: var(--pui-sequence-3); }
.pui-icon-list[data-accent="multicolor"] > .pui-icon-list__row:nth-child(6n+4) > .pui-icon-list__icon { color: var(--pui-sequence-4); }
.pui-icon-list[data-accent="multicolor"] > .pui-icon-list__row:nth-child(6n+5) > .pui-icon-list__icon { color: var(--pui-sequence-5); }
.pui-icon-list[data-accent="multicolor"] > .pui-icon-list__row:nth-child(6n+6) > .pui-icon-list__icon { color: var(--pui-sequence-6); }
.pui-icon-list[data-accent="multicolor"] > .pui-icon-list__row--success > .pui-icon-list__icon { color: var(--pui-status-success-solid); }
.pui-icon-list[data-accent="multicolor"] > .pui-icon-list__row--danger > .pui-icon-list__icon { color: var(--pui-status-danger-solid); }
.pui-icon-list[data-accent="multicolor"] > .pui-icon-list__row--neutral > .pui-icon-list__icon { color: var(--pui-text-subtle); }   /* request 12, Fedor 2026-09-25: the grey IS the point of the row — MAFO's Problem beside Our solution; was text/default */
.pui-icon-list__texts { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--pui-space-12); }   /* the set's own gap (Figma 149:14 Body, space/12) and the site's since 2026-09-03 — it was space/4 until 2.3.0 (request 39) */
.pui-icon-list__title {
  margin: 0;
  font-size: var(--pui-type-body-large-font-size);
  font-weight: var(--pui-font-weight-500);
  line-height: var(--pui-font-line-height-normal);
}
.pui-icon-list__text {
  margin: 0;
  font-size: var(--pui-type-body-small-font-size);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
}
.pui-icon-list__text :is(b, strong) { font-weight: var(--pui-font-weight-700); }
.pui-icon-list--small .pui-icon-list__icon { margin-top: 0; }
.pui-icon-list--small .pui-icon-list__title {
  font-size: var(--pui-type-body-medium-font-size);
  font-weight: var(--pui-font-weight-400);
}
.pui-icon-list--small .pui-icon-list__text { display: none; }

/* ON A WEB PAGE (request 39). The deck's rows touch — the padding is the rhythm; on a page the rows stand apart,
   Fedor's value on the site since 2026-09-03. */
.pui-icon-list--web { gap: var(--pui-space-12); }
@media (max-width: 767px) {
  .pui-icon-list--web > .pui-icon-list__row--success,
  .pui-icon-list--web > .pui-icon-list__row--danger { align-items: center; }
  .pui-icon-list--web > :is(.pui-icon-list__row--success, .pui-icon-list__row--danger) > .pui-icon-list__icon { margin-top: 0; }
}

/* bullet-list.css — Bullet list · Bullet list item (10_COMPONENTS.md § Content blocks).
 *
 * Points in a column — Paggy's .l17-bullets, the Deck kit's bull_list rows, the site's .bullet-list — with the one
 * rule that gives the component its reason: the rows are 8 px apart, on every slide and every layout, and no layout
 * may override it (Fedor 2026-08-12, locked; bullet-list/gap is the list's gap and the rules test reads it). A row:
 * padding space/12, gap space/12, radius 8 (the sources' 9 — stated); Style check (circle-check in ink — the check
 * stays black, Fedor 2026-07-09) or dot (Paggy's 9 px accent/main circle); Size regular (body/large) or small
 * (body/medium); the check sits 2 px down on regular and flush on small. Figma: 154:144 · 154:29.
 * Contract: registry/bullet-list/registry.json.
 *
 *   <ul class="pui-bullet-list">
 *     <li class="pui-bullet-list__item"><span class="pui-icon pui-icon--24 pui-bullet-list__glyph">…circle-check…</span>A/B testing and creative experiments</li></ul>
 *   <ul class="pui-bullet-list pui-bullet-list--dot pui-bullet-list--small"><li class="pui-bullet-list__item"><span class="pui-bullet-list__glyph"></span>…</li></ul>
 *
 * Style     (none) check · --dot
 * Size      (none) regular · --small
 * Emphasis  a phrase in <strong> or <b> is Bold 700 — a phrase, never the row (R11)
 */

.pui-bullet-list {
  display: flex;
  flex-direction: column;
  gap: var(--pui-bullet-list-gap);
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
}
.pui-bullet-list__item {
  display: flex;
  align-items: flex-start;
  gap: var(--pui-space-12);
  padding: var(--pui-space-12);
  border-radius: var(--pui-radius-8);
  font-size: var(--pui-type-body-large-font-size);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
}
.pui-bullet-list__glyph {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--pui-size-icon-24);
  height: var(--pui-size-icon-24);
  margin-top: var(--pui-space-2);
  color: var(--pui-text-default);
}
.pui-bullet-list--dot .pui-bullet-list__glyph { margin-top: var(--pui-space-2); }
.pui-bullet-list--dot .pui-bullet-list__glyph::before {
  content: "";
  width: 9px; /* Paggy's .bullets dot */
  height: 9px;
  border-radius: var(--pui-radius-full);
  background: var(--pui-accent-main);
}
.pui-bullet-list__item :is(b, strong) { font-weight: var(--pui-font-weight-700); }   /* request 39 — a phrase in Bold, the site's rows */
.pui-bullet-list--small .pui-bullet-list__item { font-size: var(--pui-type-body-medium-font-size); }
.pui-bullet-list--small .pui-bullet-list__glyph { margin-top: 0; }

/* heading-pair.css — Heading pair (10_COMPONENTS.md § Content blocks).
 *
 * A title and the line under it. Slide: Paggy's heading pair — the H1 in ink (headline/large: 66 on a slide, 65 web),
 * the H2 subtitle headline/medium at Medium on accent/heading (the accent's light tint; grey on yellow and multicolor
 * — U12 as the token). Web: the site's page head — an uppercase eyebrow (body/large, 6 % tracking, text/subtle — the build record's ink, Finding 51),
 * the title, a lead line (body/large text/muted). Gap space/16. 64 below the pair before content is the layout's.
 * Figma 157:15. Contract: registry/heading-pair/registry.json.
 *
 *   <header class="pui-heading-pair"><h1 class="pui-heading-pair__title">Who is Mobupps</h1>
 *     <p class="pui-heading-pair__subtitle">Powered by verified MAFO performance</p></header>
 *   <header class="pui-heading-pair pui-heading-pair--web"><p class="pui-heading-pair__eyebrow">About us</p> …title, subtitle… </header>
 *
 *   <header class="pui-heading-pair pui-heading-pair--section"><h2 class="pui-heading-pair__title">MAFO Demand</h2>
 *     <p class="pui-heading-pair__subtitle">Intelligence for User Acquisition &amp; Growth Teams</p></header>
 *
 * Style     (none) slide · --web (a page's head) · --section (a section of a web page: the title headline/small, the
 *           line under it title/small at Regular in accent/heading, space/8 between them — request 43)
 */

.pui-heading-pair {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-16);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
}
.pui-heading-pair__title {
  margin: 0;
  font-size: var(--pui-type-headline-large-font-size);
  font-weight: var(--pui-type-headline-large-font-weight);
  line-height: var(--pui-type-headline-large-line-height);
  letter-spacing: var(--pui-type-headline-large-letter-spacing);
}
.pui-heading-pair__subtitle {
  margin: 0;
  color: var(--pui-accent-heading);
  font-size: var(--pui-type-headline-medium-font-size);
  font-weight: var(--pui-font-weight-500);
  line-height: var(--pui-type-headline-medium-line-height);
  /* D110 (2026-09-17): on a slide headline/medium is the deck's H2_46 — 46, tracked −0.46, the same −1 % the title carries;
     on the web the role's tracking is 0, so nothing moves there. */
  letter-spacing: var(--pui-type-headline-medium-letter-spacing);
}
.pui-heading-pair__eyebrow {
  margin: 0;
  /* text/subtle — the ink the component was built to (10_COMPONENTS.md:135: "uppercase Eyebrow on body/large
     with 6% tracking in text/subtle"). It shipped text/muted; corrected in 2.4.6 (Finding 51). The site draws
     neutral/300, which is text/disabled's value and scores 2.09 on white against the 3.0 large-text floor. */
  color: var(--pui-text-subtle);
  font-size: var(--pui-type-body-large-font-size);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
  letter-spacing: var(--pui-font-letter-spacing-wide);
  text-transform: uppercase;
}
.pui-heading-pair--web .pui-heading-pair__subtitle {
  color: var(--pui-text-muted);
  font-size: var(--pui-type-body-large-font-size);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
}

/* A SECTION OF A WEB PAGE (request 43): the product pages open each section with a title at headline/small and, under
   it, a line at title/small Regular in the page's accent/heading — the slide pair one step down, space/8 apart (the
   site's own gap). The accent is the page's data-accent. */
.pui-heading-pair--section { gap: var(--pui-space-8); }
.pui-heading-pair--section .pui-heading-pair__title {
  font-size: var(--pui-type-headline-small-font-size);
  font-weight: var(--pui-type-headline-small-font-weight);
  line-height: var(--pui-type-headline-small-line-height);
  letter-spacing: var(--pui-type-headline-small-letter-spacing);
}
.pui-heading-pair--section .pui-heading-pair__subtitle {
  font-size: var(--pui-type-title-small-font-size);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-type-title-small-line-height);
  letter-spacing: var(--pui-type-title-small-letter-spacing);
}

/* typography.css — Typography, prose as one block (10_COMPONENTS.md § Content blocks).
 *
 * The site's article body (blog-article.css:150–290 .article-body) quoted block by block and bound to the Typography
 * roles (D36): h2 title/small (28 / 25 mobile), h3 body/large (22 / 18) and h4 body/medium (18 / 16) at the Medium
 * weight — the site's 500; paragraphs body/medium; the caption body/x-small (14 / 12). Line-heights the site's (1.3 /
 * 1.35 / 1.4 / 1.6); margins each block's own (40 / 32 / 24 above the headings, 12 / 12 / 8 below; 16 between
 * paragraphs; 16 around lists; 24 around the quote; 32 around the figure and the rule); markers and the quote in
 * text/muted; the quote's 3 px rule border/default; links underlined in the ink. 720 wide here; the site's article column is 856 (blog-article.css:92) — stated.
 * strong / b are Semibold 600 — inline emphasis is Fedor's contrast (request 17, 2026-09-25); R11 reads 'no Semibold for a whole
 * paragraph'. A consumer's page reset written at the same specificity (.page-x ul { margin: 0 }, .page-x a { text-decoration: none })
 * and loaded after this file beats the list and the link rules here by source order: D60's guard exempts an element that CARRIES a
 * pui- class, and this is the one component whose children are bare elements — so the guard is extended (D162): a page reset also
 * excludes `.pui-typography *`. The block does not raise its own specificity, so a page that MEANS to restyle one thing inside it
 * still can with one class.
 * Figma 259:10. Contract: registry/typography/registry.json.
 *
 *   <article class="pui-typography"><h2>…</h2><p>…</p><h3>…</h3><ul><li>…</li></ul><blockquote>…</blockquote>
 *     <figure><div class="pui-aspect-ratio">…</div><figcaption>…</figcaption></figure><hr>…</article>
 */

.pui-typography {
  max-width: 720px;
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  font-size: var(--pui-type-body-medium-font-size);
  font-weight: var(--pui-font-weight-400);
  line-height: 1.6; /* the site's paragraph (blog-article.css) */
}
.pui-typography > :first-child { margin-top: 0; }
.pui-typography > :last-child { margin-bottom: 0; }
.pui-typography h2,
.pui-typography h3,
.pui-typography h4 { font-weight: var(--pui-font-weight-500); }
.pui-typography h2 {
  margin: var(--pui-space-40) 0 var(--pui-space-12);
  font-size: var(--pui-type-title-small-font-size);
  line-height: 1.3; /* the site's h2 */
}
.pui-typography h3 {
  margin: var(--pui-space-32) 0 var(--pui-space-12);
  font-size: var(--pui-type-body-large-font-size);
  line-height: 1.35; /* the site's h3 */
}
.pui-typography h4 {
  margin: var(--pui-space-24) 0 var(--pui-space-8);
  font-size: var(--pui-type-body-medium-font-size);
  line-height: var(--pui-font-line-height-normal);
}
.pui-typography p { margin: 0 0 var(--pui-space-16); }
.pui-typography ul,
.pui-typography ol {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-4);
  margin: var(--pui-space-16) 0;
  padding-inline-start: var(--pui-space-24);
}
.pui-typography li::marker { color: var(--pui-text-muted); }
.pui-typography blockquote {
  margin: var(--pui-space-24) 0;
  padding-inline-start: var(--pui-space-16);
  border-inline-start: 3px solid var(--pui-border-default);
  color: var(--pui-text-muted);
}
.pui-typography blockquote > p:last-child { margin-bottom: 0; }
.pui-typography figure {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--pui-space-8);
  margin: var(--pui-space-32) 0;
}
.pui-typography figure > * { width: 100%; }
.pui-typography figcaption {
  color: var(--pui-text-muted);
  font-size: var(--pui-type-body-x-small-font-size);
  line-height: var(--pui-font-line-height-normal);
  text-align: center;
}
.pui-typography hr {
  height: 1px;
  margin: var(--pui-space-32) 0;
  border: 0;
  background: var(--pui-border-default);
}
.pui-typography a { color: inherit; text-decoration: underline; text-underline-offset: var(--pui-space-2); }
.pui-typography a:hover { color: var(--pui-text-link); }
.pui-typography a:focus-visible { outline: none; border-radius: var(--pui-radius-4); box-shadow: var(--pui-ring); }  /* 3.5.2 */
/* Fedor, 2026-09-25 (request 17, 25): 'the emphasis of the text is missing … the contrast is missing … keep my bold variation' —
   inline emphasis is Semibold, the site's own value. R11 forbids Semibold for a whole paragraph, not for a phrase in one. */
.pui-typography strong,
.pui-typography b { font-weight: var(--pui-font-weight-600); }

/* logo-grid.css — Logo chip · Logo grid · Carousel · Carousel arrow · Review card (10_COMPONENTS.md § Content blocks,
 * page `Logo grid & Carousel`).
 *
 * Logo chip: Paggy's Logo grid chip (slide.css:393 .comp-logogrid__chip, Fedor 2026-07-17) — 182 × 73 on
 * surface/card at radius/8, padding 20, the mark contain-scaled into the 142 × 33 box inside. Logo grid: Style chips
 * (slide.css:387 — chips wrapping at space/16) · strip (the production site's partners strip, home-2026.css:233 — bare marks 33
 * tall spaced across the content width; the prototypes' hero sets twelve marks at natural size in two rows). Carousel: the site's reviews slider (home-2026.css:370–440) — cards on a
 * track at space/24, the arrows under at space/12, the track clipped at 1320; the arrow (home-2026.css:65 .arrow-btn)
 * is 40 round, a 2 px action/primary/bg border, filled on hover, .5 disabled — the site's own shape, not Icon button.
 * Review card (home-2026.css:383): 416, surface/card, radius/16, padding space/20, gap space/12; the quote in a white
 * box at radius/8, padding space/12, 16 Regular; 'Read more' 14 Medium text/subtle underlined; the mark 33 tall.
 * Figma: 262:10 · 262:203 · 262:250 · 262:230 · 262:238. Contracts: registry/logo-grid, registry/carousel.
 *
 *   <div class="pui-logo-grid"><span class="pui-logo-chip"><img src="…" alt="MAFO"></span> …</div>
 *   <div class="pui-logo-grid pui-logo-grid--strip"><img src="…" alt="…"> …</div>
 *   <section class="pui-carousel"><div class="pui-carousel__track"><div class="pui-review-card"> …__quote, __more, __logo… </div> …</div>
 *     <div class="pui-carousel__nav"><button class="pui-carousel-arrow" aria-label="Previous" disabled>…</button><button class="pui-carousel-arrow" aria-label="Next">…</button></div></section>
 */

.pui-logo-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: 182px;
  height: 73px;
  padding: var(--pui-space-20);
  border-radius: var(--pui-radius-8);
  background: var(--pui-surface-card);
  color: var(--pui-text-muted);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-20);
  font-weight: var(--pui-font-weight-500);
}
.pui-logo-chip > img,
.pui-logo-chip > svg { display: block; max-width: 142px; max-height: 33px; width: auto; height: auto; object-fit: contain; }

.pui-logo-grid { display: flex; flex-wrap: wrap; gap: var(--pui-space-16); }
.pui-logo-grid--strip {
  flex-wrap: nowrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--pui-space-24);
  height: 33px;
}
.pui-logo-grid--strip > img,
.pui-logo-grid--strip > svg { display: block; height: 33px; width: auto; }

.pui-carousel { display: flex; flex-direction: column; gap: var(--pui-space-24); max-width: var(--pui-layout-content-width); overflow: hidden; }
.pui-carousel__track {
  display: flex;
  gap: var(--pui-space-24);
  scroll-snap-type: x mandatory;
  overflow-x: auto;
  scrollbar-width: none;
}
.pui-carousel__track::-webkit-scrollbar { display: none; }
.pui-carousel__track > * { flex: none; scroll-snap-align: start; }
.pui-carousel__nav { display: flex; gap: var(--pui-space-12); }

.pui-carousel-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: var(--pui-space-40);
  height: var(--pui-space-40);
  margin: 0;
  padding: 0;
  border: 2px solid var(--pui-action-primary-bg);
  border-radius: var(--pui-radius-full);
  background: transparent;
  color: var(--pui-text-default);
  cursor: pointer;
  outline: none;
  transition: background-color var(--pui-motion-state) var(--pui-motion-easing), color var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-carousel-arrow > .pui-icon { width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); }
.pui-carousel-arrow:hover { background: var(--pui-action-primary-bg); color: var(--pui-action-primary-fg); }
.pui-carousel-arrow:focus-visible { box-shadow: var(--pui-ring); }
.pui-carousel-arrow:disabled { opacity: 0.5; cursor: not-allowed; background: transparent; color: var(--pui-text-default); }

.pui-review-card {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-12);
  box-sizing: border-box;
  width: 416px;
  max-width: 100%;
  padding: var(--pui-space-20);
  border-radius: var(--pui-radius-16);
  background: var(--pui-surface-card);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
}
.pui-review-card__quote {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-12);
  margin: 0;
  padding: var(--pui-space-12);
  border-radius: var(--pui-radius-8);
  background: var(--pui-surface-page);
  font-size: var(--pui-font-size-16);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
}
.pui-review-card__quote > p { margin: 0; display: -webkit-box; -webkit-line-clamp: 5; -webkit-box-orient: vertical; overflow: hidden; }
.pui-review-card__more {
  align-self: flex-start;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--pui-text-caption);
  font: inherit;
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-500);
  text-decoration: underline;
  cursor: pointer;
}
.pui-review-card__more:focus-visible { outline: none; border-radius: var(--pui-radius-4); box-shadow: var(--pui-ring); }  /* 3.5.2 */
.pui-review-card__logo { display: flex; align-items: center; height: 33px; }
.pui-review-card__logo > img,
.pui-review-card__logo > svg { display: block; height: 100%; width: auto; }

/* avatar.css — Avatar · Avatar - square (10_COMPONENTS.md § Content blocks).
 *
 * A person, as a photo or two initials. Paggy's .avatar (app.css:1345–1356: 28 / 40 / 96, 600 initials, 'photo when
 * uploaded, otherwise initials on the secondary palette — visible but calm'), the concept file's Avatar/User master
 * (36 in the shell) and shadcn's avatar.tsx. Sizes 24 · 28 · 36 · 40 · 96; initials 12 / 12 / 15 / 15 / 36 SemiBold;
 * Content photo (a user glyph on surface/sunken until an image replaces it) or initials in blue / red / yellow —
 * avatar/{tone}, hue 200 on hue 600. Square (radius/8) is the chat's, matching Echo's icon chip. No states.
 * Figma: 64:108 · 65:102. Contract: registry/avatar/registry.json.
 *
 *   <span class="pui-avatar pui-avatar--blue">FK</span>
 *   <span class="pui-avatar pui-avatar--40"><img src="…" alt="Fedor"></span>
 *   <span class="pui-avatar pui-avatar--square pui-avatar--yellow pui-avatar--24">AI</span>
 *
 * Size      --24 · --28 · (none) 36 · --40 · --96
 * Content   (none) photo / glyph on sunken · --blue · --red · --yellow
 * Shape     (none) round · --square
 */

.pui-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  box-sizing: border-box;
  width: var(--pui-size-control-medium);
  height: var(--pui-size-control-medium);
  overflow: hidden;
  border-radius: var(--pui-radius-full);
  background: var(--pui-surface-sunken);
  color: var(--pui-text-muted);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-15);
  font-weight: var(--pui-font-weight-600);
  line-height: 1;
  text-transform: uppercase;
  user-select: none;
}
.pui-avatar > img { display: block; width: 100%; height: 100%; object-fit: cover; }
.pui-avatar > .pui-icon { width: 50%; height: 50%; }
.pui-avatar--square { border-radius: var(--pui-radius-8); }
.pui-avatar--blue { background: var(--pui-avatar-blue-bg); color: var(--pui-avatar-blue-fg); }
.pui-avatar--red { background: var(--pui-avatar-red-bg); color: var(--pui-avatar-red-fg); }
.pui-avatar--yellow { background: var(--pui-avatar-yellow-bg); color: var(--pui-avatar-yellow-fg); }
.pui-avatar--24 { width: var(--pui-space-24); height: var(--pui-space-24); font-size: var(--pui-font-size-12); }
.pui-avatar--28 { width: var(--pui-size-control-small); height: var(--pui-size-control-small); font-size: var(--pui-font-size-12); }
.pui-avatar--40 { width: var(--pui-space-40); height: var(--pui-space-40); }
.pui-avatar--96 { width: 96px; height: 96px; font-size: var(--pui-font-size-36); }

/* table.css — Table · Table row · Table cell · Data table (10_COMPONENTS.md § Content blocks).
 *
 * Fedor's stylised table (D45) — the deck's .comp-table (slide.css:702–737; his four Quarter-Presentation tables),
 * one anatomy in two sizes: a card on surface/card at radius/16 with elevation/card, padding space/8 (Paggy's 10 —
 * stated), rows FLUSH (D104 — below), an accent-dark header bar (table/header/bg, radius/8, painted once by the row — neutral-800
 * in multicolor), row-header pills on table/header/pill-tint, dividers on table/divider (2 px slide / 1 px product)
 * under every body cell — none under the last row (D46) — and no vertical lines; hover surface/slot (the inversion
 * rule), selected the Accent bg. A WASH FILLS THE WHOLE ROW (D104, Fedor 2026-09-17, on the Data table: "fill color
 * should fill the whole row, not … stop at some point before the line … this gap does nothing actually for us"): rows
 * sit flush — no border-spacing, none under the bar — and a body row is square, so a hover or selection wash runs from
 * the divider above to the row's own with no strip of card above it and no notch at its corners; radius/8 stays on the
 * bar and the pill. The space a row needs is inside the row: the product row is his 46 / 43 (padding space/12), the
 * slide cell's padding is space/16 for the deck's 12 — the 12 plus half its old row gap on each side, so the slide
 * pitch is kept — and a row-header cell is inset space/4 so stacked pills stay 8 apart (the product pill's own padding
 * is space/8 so it fits its row). The 2026-09-08 form spaced rows space/8 apart and rounded every body row — kept
 * here, dated. The divider is a straight line (D59, 2026-09-09: Fedor vetoed a line that curves with a rounded end,
 * on the MAFO Click Cap page): the cell keeps a transparent bottom border for its height and paints the line with
 * ::after — a square row no longer needs that, and it stays because the last-row rule (D46) and the React part's
 * divider span are built on it and no pixel moves. Product = Paggy's table.admin anatomy (body 13 Regular, pill 13 Bold, pill gap 8; header 14 Medium for
 * its 12 SemiBold and padding 12 × 16 for its 10 × 14 — stated, D45) — the web's default; slide = his numbers (header 24
 * Medium at 24 × 40 for Paggy's 28 × 40 — stated; body 18 Regular at 16 × 40 — 12 before D104 — pill 20 Bold at 12 × 40, pill gap 16). Numbers right, words left; status lives in
 * a Badge, never in a cell's colour; columns fill the width — no void at the right end (Fedor 2026-08-17). Data
 * table: a toolbar, the table, a footer with the range and Pagination at space/16 (MAFO's react-table screens).
 * Figma: 165:556 · 163:311 · 162:36 · 171:7. Contracts: registry/table, registry/data-table.
 *
 *   <div class="pui-table"><table class="pui-table__table">
 *     <thead><tr class="pui-table__row"><th class="pui-table__cell pui-table__cell--header">Publisher<span class="pui-icon pui-table__sort">…</span></th>
 *       <th class="pui-table__cell pui-table__cell--header pui-table__cell--right">Revenue</th></tr></thead>
 *     <tbody><tr class="pui-table__row"><td class="pui-table__cell pui-table__cell--row-header"><span class="pui-table__pill">Q1</span></td>
 *       <td class="pui-table__cell pui-table__cell--right">$ 8 969 916</td></tr>
 *     <tr class="pui-table__row" aria-selected="true"> … </tr></tbody></table></div>
 *   <div class="pui-data-table"><div class="pui-data-table__toolbar">…</div><div class="pui-table">…</div>
 *     <div class="pui-data-table__footer"><span class="pui-data-table__range">1–5 of 118 publishers</span><nav class="pui-pagination">…</nav></div></div>
 *
 * Size      (none) product · --slide (on .pui-table)
 * Cell      __cell (body) · __cell--header · __cell--row-header (holds a __pill) · __cell--right
 * Row       :hover · selected = [aria-selected="true"] or --selected
 * Accent    data-accent on the table or a parent drives the bar and the pills — table/header/bg and table/header/pill-tint
 *           re-resolve per mode (blue navy · red · yellow · multicolor = a neutral-800 bar on neutral-200 pills, the black
 *           one); a product dashboard's table is the neutral one (data-accent="multicolor" on .pui-table — D59)
 */

.pui-table {
  box-sizing: border-box;
  width: 100%;
  padding: var(--pui-space-8);
  border-radius: var(--pui-radius-16);
  background: var(--pui-surface-card);
  box-shadow: var(--pui-elevation-card);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  --pui-table-divider-width: 1px;
}
.pui-table__table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0; /* D104: rows sit flush — a wash fills the row, divider to divider */
  table-layout: auto;
}
.pui-table__cell {
  position: relative;
  box-sizing: border-box;
  padding: var(--pui-space-12) var(--pui-space-16);
  border: 0;
  border-bottom: var(--pui-table-divider-width) solid transparent; /* the height; the line is ::after (D59) */
  background: transparent;
  color: var(--pui-text-default);
  font-size: var(--pui-font-size-13);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
  text-align: start;
  vertical-align: middle;
}
.pui-table__cell--right { text-align: end; }
/* The divider (table/divider), straight from end to end: an absolutely placed line over the transparent border. */
.pui-table__cell::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(var(--pui-table-divider-width) * -1);
  height: var(--pui-table-divider-width);
  background: var(--pui-table-divider);
  pointer-events: none;
}
.pui-table__cell--header {
  background: var(--pui-table-header-bg);
  color: var(--pui-text-on-accent);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-500);
  border-bottom: 0;
}
.pui-table__cell--header::after { content: none; }
.pui-table__cell--header:first-child { border-start-start-radius: var(--pui-radius-8); border-end-start-radius: var(--pui-radius-8); }
.pui-table__cell--header:last-child { border-start-end-radius: var(--pui-radius-8); border-end-end-radius: var(--pui-radius-8); }
.pui-table__sort { width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); margin-inline-start: var(--pui-space-4); vertical-align: middle; }
/* D104: the pill is inset space/4 above and below, so stacked pills stay 8 apart in flush rows; its own padding is the
   cell's less the inset. The pill's cell carries no divider and the pill fills its column — the deck's macro gives the
   header column neither (macros.html:611 — `--hcol` or `--div`, never both; the cell IS the pill), and the library's
   Table cell has no Divider under a pill; until 2026-09-17 this part drew the line and let each pill hug its own text. */
.pui-table__cell--row-header { padding: var(--pui-space-4) var(--pui-space-8) var(--pui-space-4) 0; }
.pui-table__cell--row-header::after { content: none; }
.pui-table__pill {
  display: block;
  padding: var(--pui-space-8) var(--pui-space-16);
  border-radius: var(--pui-radius-8);
  background: var(--pui-table-header-pill-tint);
  color: var(--pui-text-default);
  font-size: var(--pui-font-size-13);
  font-weight: var(--pui-font-weight-700);
  line-height: var(--pui-font-line-height-normal);
}
/* D46: the last row has no divider — the card's edge is the end. */
.pui-table__table > tbody > tr:last-child > .pui-table__cell::after { display: none; }
/* Hover and selection paint the whole row, square, from the divider above to its own (D104) — no radius on a body cell. */
.pui-table__table > tbody > tr:hover > .pui-table__cell { background: var(--pui-surface-slot); }
.pui-table__table > tbody > tr[aria-selected="true"] > .pui-table__cell,
.pui-table__table > tbody > tr.pui-table__row--selected > .pui-table__cell { background: var(--pui-table-row-selected-bg); }
/* D110 (2026-09-17): the selected wash is its own token, not accent/bg. In blue, red and yellow it is the same palest
   tint; in multicolor accent/bg is red/100 and a selected row on a dashboard read pink, so there it is a neutral one
   step darker than the ground the Table paints (neutral/200 on surface/card — D107's rule). Hover stays the white slot. */

.pui-table--slide { --pui-table-divider-width: 2px; }
.pui-table--slide .pui-table__cell { padding: var(--pui-space-16) var(--pui-space-40); font-size: var(--pui-font-size-18); }
.pui-table--slide .pui-table__cell--header { padding: var(--pui-space-24) var(--pui-space-40); font-size: var(--pui-font-size-24); }
.pui-table--slide .pui-table__cell--row-header { padding: var(--pui-space-4) var(--pui-space-16) var(--pui-space-4) 0; }
.pui-table--slide .pui-table__pill { padding: var(--pui-space-12) var(--pui-space-40); font-size: var(--pui-font-size-20); }

.pui-data-table { display: flex; flex-direction: column; gap: var(--pui-space-16); font-family: var(--pui-font-family); }
.pui-data-table__toolbar,
.pui-data-table__footer { display: flex; align-items: center; justify-content: space-between; gap: var(--pui-space-16); }
.pui-data-table__toolbar > .pui-input-group,
.pui-data-table__toolbar > .pui-input { flex: 1 1 auto; max-width: 320px; }
.pui-data-table__tools { display: flex; align-items: center; gap: var(--pui-space-8); }
/* 3.3.5 — a page-size Select in the footer's tools is its content's width, not the row's: select.css makes a
   trigger fill its Field (width 100 %), and in a flex row that filled basis squeezed the range text beside it
   on to two lines. Pagination's contract asks for the Select AND the range text, so the row has to hold both. */
.pui-data-table__tools > .pui-select { width: auto; }
.pui-data-table__range { color: var(--pui-text-muted); font-size: var(--pui-font-size-14); line-height: var(--pui-font-line-height-normal); }

/* device-frame.css — Device frame (10_COMPONENTS.md § Content blocks).
 *
 * A drawn browser window around a product picture — the site's four product showcases (irtb.css:923 .browser, and the
 * same block on MAFO, MobuppsX and Echo AI), Fedor's design: the window at radius 19 with a 1.19 line in
 * illustration/chrome/line, a toolbar 26 tall in illustration/chrome/bar over a 0.59 line, the three lights (7.72 round,
 * 4.75 apart, illustration/chrome/light-red · -amber · -green) in a 40.4 column, the URL well in illustration/chrome/well
 * at radius/full with its text in illustration/chrome/text, a 14 % black shadow (elevation/modal's own numbers, scaled),
 * and the screen below — the slot for the picture, the product's page, a slideshow the consumer runs. Every number is
 * a fraction of ONE UNIT, --pui-device-unit, one design pixel of the site's 770-wide window: set the unit from the
 * container (calc(100cqw / 770) inside a container-type: inline-size host) and the window keeps its proportions at any
 * width — the site's --bu. The chrome is dark by design: a device is a drawing and its shell is not the page's
 * surface, which is why these are illustration/chrome/* and not surface/*. The laptop the product pages also draw is
 * the illustrations unit's (request 8) — this part is the window alone. Request 20 from the corporate site, 2026-09-25.
 * Figma: Device frame 1090:5. Contract: registry/device-frame/registry.json.
 *
 *   <div class="pui-device-host"><div class="pui-device-frame pui-device-frame--browser">
 *     <div class="pui-device-frame__bar"><span class="pui-device-frame__lights"><i></i><i></i><i></i></span>
 *       <span class="pui-device-frame__url">mafo.mobupps.com</span></div>
 *     <div class="pui-device-frame__screen"><img src="…" alt="…"></div></div></div>
 *
 *   with the toolbar (request 29) — the same bar, the glyphs a browser shows:
 *     <div class="pui-device-frame__bar"><span class="pui-device-frame__lights"><i></i><i></i><i></i></span>
 *       <span class="pui-device-frame__nav"> …three .pui-icon: chevron-left · chevron-right · refresh-cw… </span>
 *       <span class="pui-device-frame__url"><span class="pui-icon">…lock…</span><span class="pui-device-frame__url-text">mobupps.com/MAFO</span><span class="pui-icon">…star…</span></span>
 *       <span class="pui-device-frame__tools"> …three .pui-icon: shield · plus · menu… </span></div>
 *
 * Kind      --browser (the window; the only kind today)
 * Toolbar   __nav and __tools beside the well, a glyph at each end of __url around __url-text — seven library glyphs
 *           in illustration/chrome/text, the site's window (request 29). Without them the bar is the lights and the well
 * Unit      --pui-device-unit — one design pixel; the host sets it from its width
 */

.pui-device-host { container-type: inline-size; }
.pui-device-frame {
  --pui-device-unit: 1px;
  position: relative;
  box-sizing: border-box;
  overflow: hidden;
  border: calc(1.1883 * var(--pui-device-unit)) solid var(--pui-illustration-chrome-line);
  border-radius: calc(19.0123 * var(--pui-device-unit));
  background: var(--pui-illustration-chrome-bar);
  box-shadow: 0 calc(19.0123 * var(--pui-device-unit)) calc(38.0247 * var(--pui-device-unit)) calc(-7.1296 * var(--pui-device-unit)) var(--pui-color-alpha-black-14);
  font-family: var(--pui-font-family);
}
@container (min-width: 0) {
  .pui-device-frame { --pui-device-unit: calc(100cqw / 770); }
}
.pui-device-frame__bar {
  display: flex;
  align-items: center;
  gap: calc(8.3179 * var(--pui-device-unit));
  height: calc(26.14 * var(--pui-device-unit));
  padding: 0 calc(8.3179 * var(--pui-device-unit));
  border-bottom: calc(0.59 * var(--pui-device-unit)) solid var(--pui-illustration-chrome-line);
  background: var(--pui-illustration-chrome-bar);
}
.pui-device-frame__lights {
  display: flex;
  flex: 0 0 calc(40.4 * var(--pui-device-unit));
  gap: calc(4.7531 * var(--pui-device-unit));
}
.pui-device-frame__lights > i {
  display: block;
  width: calc(7.7238 * var(--pui-device-unit));
  height: calc(7.7238 * var(--pui-device-unit));
  border-radius: var(--pui-radius-full);
  background: var(--pui-illustration-chrome-light-red);
}
.pui-device-frame__lights > i:nth-child(2) { background: var(--pui-illustration-chrome-light-amber); }
.pui-device-frame__lights > i:nth-child(3) { background: var(--pui-illustration-chrome-light-green); }
.pui-device-frame__url {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  height: calc(16.6 * var(--pui-device-unit));
  padding: 0 calc(10 * var(--pui-device-unit));
  border-radius: var(--pui-radius-full);
  background: var(--pui-illustration-chrome-well);
  color: var(--pui-illustration-chrome-text);
  font-size: calc(9.5 * var(--pui-device-unit));
  line-height: calc(16.6 * var(--pui-device-unit));
  white-space: nowrap;
  text-overflow: ellipsis;
}
.pui-device-frame__screen {
  position: relative;
  aspect-ratio: 770 / 433;
  overflow: hidden;
  background: var(--pui-illustration-screen);
}
.pui-device-frame__screen > img,
.pui-device-frame__screen > video { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top; }

/* THE TOOLBAR (request 29; taken as an option, 2026-09-28): the site's window shows what a browser shows — back,
   forward and reload before the well, a lock and a star inside it, a shield, a plus and the menu after it. Every glyph
   is the library's, in illustration/chrome/text, 8.32 of the unit in a 13.07 box (7.13 inside the well) — the site's
   numbers; the address is set in the platform's UI face, as a browser sets it. */
.pui-device-frame__nav,
.pui-device-frame__tools {
  display: flex;
  flex: none;
  align-items: center;
  gap: calc(3.5648 * var(--pui-device-unit));
  color: var(--pui-illustration-chrome-text);
}
.pui-device-frame__nav > .pui-icon,
.pui-device-frame__tools > .pui-icon {
  box-sizing: content-box;
  width: calc(8.3179 * var(--pui-device-unit));
  height: calc(8.3179 * var(--pui-device-unit));
  padding: calc(2.376 * var(--pui-device-unit));
}
.pui-device-frame__url:has(> .pui-device-frame__url-text) {
  display: flex;
  align-items: center;
  gap: calc(4.7531 * var(--pui-device-unit));
  padding: 0 calc(5.9414 * var(--pui-device-unit));
}
.pui-device-frame__url > .pui-icon { width: calc(7.1296 * var(--pui-device-unit)); height: calc(7.1296 * var(--pui-device-unit)); }
.pui-device-frame__url-text {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  font-size: calc(7.1296 * var(--pui-device-unit));
  text-align: center;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* socials.css — Socials (10_COMPONENTS.md § Content blocks).
 *
 * The row of social marks — ONE component for every place: the footer of every page, the share row of a blog article
 * (Fedor, 2026-09-28: 'structurally, because of classes, it looks like they are different components, but actually this
 * is the one single component. The difference is only colour'). Three marks — LinkedIn, Facebook, YouTube — each a
 * round 29 box (the Footer's drawing, Figma 265:30; between size/icon/24 and /32 — stated), space/12 apart. A mark is a
 * glyph of the sprite painted by currentColor (R25): a disc in the ink with the company's sign cut out of it, so the
 * ground shows through — text/default on a light ground, text/on-inverse on a dark one. The marks are
 * icons.svg#social-linkedin · #social-facebook · #social-youtube. A link opens the company's profile (or shares the
 * page) in a new tab and says where in its aria-label. Request 40 from the corporate site, 2026-09-28.
 * Figma: Socials. Contract: registry/socials/registry.json.
 *
 *   <div class="pui-socials">
 *     <a class="pui-socials__link" href="…" target="_blank" rel="noopener" aria-label="LinkedIn">
 *       <span class="pui-icon" aria-hidden="true"><svg><use href="icons.svg#social-linkedin"/></svg></span></a> …</div>
 *   <div class="pui-socials pui-socials--on-dark"> …the same links… </div>
 *
 * Ground    (none) light · --on-dark
 */

.pui-socials {
  display: flex;
  align-items: center;
  gap: var(--pui-space-12);
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--pui-text-default);
}
.pui-socials--on-dark { color: var(--pui-text-on-inverse); }
.pui-socials__link {
  display: flex;
  flex: none;
  border-radius: var(--pui-radius-full);
  color: inherit;
  text-decoration: none;
  transition: opacity var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-socials__link:is(:link, :visited) { color: inherit; }
.pui-socials__link:focus-visible { outline: none; box-shadow: var(--pui-ring); }
.pui-socials__link > .pui-icon { width: 29px; height: 29px; }   /* the mark's box — the Footer's drawing (stated) */

/* Shells and patterns */
/* sidebar.css — Sidebar · Nav item · Nav section (10_COMPONENTS.md § Shells and patterns).
 *
 * The product's rail — a floating dark panel, not a full-height strip ('full height looks like it came from a
 * constructor; make it a panel in the space' — Fedor, Aug 2026): 240 wide with 24 above and below, right corners 16,
 * on nav/bg (neutral/800 — the concept's 1000 and Paggy's #171717 stated); padding 24 / 16, gap 24; a 36 logo block,
 * groups of links at space/4 and space/16 apart, a spacer, the signed-in person on the hover wash. A Nav item is
 * Paggy's .anav-item and the concepts' Nav_item: nav/item/fg at rest, white on the 8 % wash on hover, white on the
 * 16 % wash when active — a red or accent active pill 'exists nowhere in the design system'; padding space/8 ×
 * space/12, gap space/8, radius/8, body/x-small Regular; a 16 glyph, a count (a yellow Badge), a chevron on
 * nav/section/fg. Nav section: label/medium uppercase at the concept's +8 % tracking (the role's own is +10 % — stated), nav/section/fg. Figma: 178:27 · 177:78 · 177:79.
 * Contracts: registry/sidebar, registry/nav-item.
 *
 *   <aside class="pui-sidebar"><div class="pui-sidebar__logo"><img src="…" alt=""><span class="pui-sidebar__product">MAFO</span></div>
 *     <nav class="pui-sidebar__nav"><div class="pui-sidebar__group"><div class="pui-nav-section">Workspace</div>
 *       <a class="pui-nav-item" href="…" aria-current="page"><span class="pui-icon pui-nav-item__icon">…</span>Dashboard</a>
 *       <a class="pui-nav-item" href="…">Optimisations<span class="pui-badge pui-badge--yellow pui-badge--small pui-nav-item__count">3</span>
 *         <span class="pui-icon pui-nav-item__chevron">…</span></a></div></nav>
 *     <div class="pui-sidebar__spacer"></div>
 *     <div class="pui-sidebar__user"><span class="pui-avatar pui-avatar--blue">FK</span><div class="pui-sidebar__user-texts">…</div></div></aside>
 *
 * Nav item  :hover · active = [aria-current] or --active · :focus-visible · [aria-disabled="true"] · --nested (a group's child, 2.7.6)
 */

.pui-sidebar {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-24);
  box-sizing: border-box;
  width: 240px;
  min-height: 100%;
  padding: var(--pui-space-24) var(--pui-space-16);
  border-radius: 0 var(--pui-radius-16) var(--pui-radius-16) 0;
  background: var(--pui-nav-bg);
  color: var(--pui-nav-item-fg);
  font-family: var(--pui-font-family);
}
.pui-sidebar__logo {
  display: flex;
  align-items: center;
  gap: var(--pui-space-12);
  height: var(--pui-size-control-medium);
  padding: 0 var(--pui-space-12);
}
.pui-sidebar__logo > img,
.pui-sidebar__logo > svg { display: block; height: var(--pui-size-control-medium); width: auto; }
.pui-sidebar__product { color: var(--pui-nav-item-fg-active); font-size: var(--pui-font-size-14); font-weight: var(--pui-font-weight-500); }
.pui-sidebar__nav { display: flex; flex-direction: column; gap: var(--pui-space-16); }
.pui-sidebar__group { display: flex; flex-direction: column; gap: var(--pui-space-4); }
.pui-sidebar__spacer { flex: 1 1 auto; }
.pui-sidebar__user {
  display: flex;
  align-items: center;
  gap: var(--pui-space-12);
  padding: var(--pui-space-12);
  border-radius: var(--pui-radius-8);
  background: var(--pui-nav-item-bg-hover);
}
.pui-sidebar__user-texts { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--pui-space-2); }
.pui-sidebar__user-name { margin: 0; color: var(--pui-nav-item-fg-active); font-size: var(--pui-font-size-14); font-weight: var(--pui-font-weight-500); line-height: var(--pui-font-line-height-normal); }
.pui-sidebar__user-role { margin: 0; color: var(--pui-nav-section-fg); font-size: var(--pui-font-size-12); line-height: var(--pui-font-line-height-normal); }

.pui-nav-item {
  display: flex;
  align-items: center;
  gap: var(--pui-space-8);
  box-sizing: border-box;
  width: 100%;
  margin: 0;
  padding: var(--pui-space-8) var(--pui-space-12);
  border: 0;
  border-radius: var(--pui-radius-8);
  background: transparent;
  color: var(--pui-nav-item-fg);
  font-family: inherit;
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
  text-align: start;
  text-decoration: none;
  cursor: pointer;
  outline: none;
  transition: background-color var(--pui-motion-state) var(--pui-motion-easing), color var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-nav-item:hover { background: var(--pui-nav-item-bg-hover); color: var(--pui-nav-item-fg-active); }
.pui-nav-item:focus-visible { box-shadow: var(--pui-ring); }
.pui-nav-item[aria-current],
.pui-nav-item--active { background: var(--pui-nav-item-bg-active); color: var(--pui-nav-item-fg-active); }
.pui-nav-item[aria-disabled="true"] { opacity: 0.5; pointer-events: none; }
/* 2.7.6 — a group's child (MAFO's CNavGroup items): indented past the parent's glyph by an icon and a gap */
.pui-nav-item--nested { padding-inline-start: calc(var(--pui-space-12) + var(--pui-size-icon-16) + var(--pui-space-8)); }
.pui-nav-item__icon { flex: none; width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); }
.pui-nav-item__label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pui-nav-item__count { flex: none; margin-inline-start: auto; }
.pui-nav-item__chevron { flex: none; width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); color: var(--pui-nav-section-fg); }
.pui-nav-item__count + .pui-nav-item__chevron { margin-inline-start: 0; }
.pui-nav-item__icon + .pui-nav-item__chevron:last-child,
.pui-nav-item > .pui-nav-item__chevron:last-child { margin-inline-start: auto; }
.pui-nav-item__count ~ .pui-nav-item__chevron { margin-inline-start: 0; }

.pui-nav-section {
  padding: var(--pui-space-8) var(--pui-space-12);
  color: var(--pui-nav-section-fg);
  font-size: var(--pui-type-label-medium-font-size);
  font-weight: var(--pui-font-weight-500);
  line-height: var(--pui-font-line-height-normal);
  letter-spacing: 0.08em; /* the concept's rule (design-rules §B) */
  text-transform: uppercase;
}

/* topbar.css — Topbar (10_COMPONENTS.md § Shells and patterns).
 *
 * The product's top bar — a rounded white card, level with the rail's 24 inset, never an edge-to-edge strip (the
 * concepts' §C; Topbar/MAFO 1592 × 72): where you are on the left; search, the date range, the one action,
 * notifications and the signed-in person on the right. surface/slot on radius/16, no stroke, 72 tall, padding
 * space/24 at the sides, gap space/16; the search FILLs between the breadcrumb and the controls, never narrower than
 * 160. Paggy's blur, 86 % white and hairline are not carried. Figma 182:7. Contract: registry/topbar/registry.json.
 *
 *   <header class="pui-topbar"><nav class="pui-breadcrumb">…</nav>
 *     <label class="pui-input-group pui-topbar__search"><span class="pui-icon pui-input-group__icon">…</span><input class="pui-input-group__control" placeholder="Search"></label>
 *     <button class="pui-select pui-topbar__control">…</button>
 *     <button class="pui-button"><span class="pui-icon pui-button__icon">…plus…</span>New campaign</button>
 *     <span class="pui-chip"><span class="pui-icon pui-chip__icon">…bell…</span>3</span>
 *     <span class="pui-avatar pui-avatar--blue">FK</span></header>
 *   Without a search, put <div class="pui-topbar__spacer"></div> where it would be (2.7.6).
 */

.pui-topbar {
  display: flex;
  align-items: center;
  gap: var(--pui-space-16);
  box-sizing: border-box;
  width: 100%;
  min-height: 72px; /* the concept's Topbar/MAFO */
  padding: 0 var(--pui-space-24);
  border-radius: var(--pui-radius-16);
  background: var(--pui-surface-slot);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
}
.pui-topbar__search { flex: 1 1 auto; min-width: 160px; max-width: 320px; }
.pui-topbar__spacer { flex: 1 1 auto; } /* 2.7.6 — fills when there is no search, so the controls sit at the end (MAFO) */
.pui-topbar__control { flex: none; width: auto; min-width: 160px; }
.pui-topbar > .pui-button,
.pui-topbar > .pui-chip,
.pui-topbar > .pui-avatar { flex: none; }

/* footer.css — Footer (10_COMPONENTS.md § Shells and patterns).
 *
 * The site's dark card at the bottom of every page (global-2026.css:636 .footer-2026; S4's Footer 2116:5772 at
 * 1296 × 366): surface/inverse at radius/16, padding space/64, three columns at space/40 — Menu, Terms over
 * Subscription (three links; a heading, the social marks, the email field with its Subscribe button), the badges filling
 * the rest, centred. Headings 14 SemiBold in nav/section/fg (the site's 600), links 14 Regular in nav/item/fg
 * (neutral-300 for the site's 200 — stated), white on hover; the email field a 36-tall box at radius/8 in
 * color/alpha/white-8 with a nav/item/bg-active stroke (the site's white .05 and #404040 — stated). Web only.
 * Figma 265:10. Contract: registry/footer/registry.json.
 * 2.3.0 (request 33; Fedor, 2026-09-28): THE EMAIL FIELD IS THE INPUT GROUP on the dark ground, the small Button in it —
 * 'the same component … the only difference must be the colour' — and the social marks are the Socials row (request 40).
 * `__pill`, `__subscribe` and `__socials` are DEPRECATED: they paint as before for this minor and go in the next major.
 * 2.5.10 (2026-09-18): the field was drawn as a pill with the small Button in it and the headings at 500 from 2.2 on;
 * the design lead's built footer (the prototypes' styles.css and the site's global-2026.css) is the spec, and the
 * library's Footer already drew the box — the class keeps its 1.0 name, `__pill`.
 * Every text inside states its own size, weight and line-height (2.4.2, Finding 43): on the production site the heading is an
 * <h4> under Bootstrap's heading scale and the body is 20 px / 300 (layout.min.css) — inherited values are the consumer's.
 *
 *   <footer class="pui-footer">
 *     <div class="pui-footer__column"><p class="pui-footer__heading">Menu</p><a class="pui-footer__link" href="…">Case studies</a> …</div>
 *     <div class="pui-footer__column pui-footer__column--stack"> …Terms… <div><p class="pui-footer__heading">Subscription</p>
 *       <div class="pui-socials pui-socials--on-dark"> …three marks… </div>
 *       <div class="pui-field"><label class="pui-input-group pui-input-group--on-dark"><input class="pui-input-group__control" type="email"
 *         placeholder="Enter your email"><button class="pui-button pui-button--small pui-input-group__button">Subscribe</button></label></div></div></div>
 *     <div class="pui-footer__badges"> …img… </div></footer>
 */

.pui-footer {
  display: flex;
  align-items: flex-start;
  gap: var(--pui-space-40);
  box-sizing: border-box;
  width: 100%;
  padding: var(--pui-space-64);
  border-radius: var(--pui-radius-16);
  background: var(--pui-surface-inverse);
  color: var(--pui-nav-item-fg);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-400);        /* stated: a consumer's body may be 300 (layout.min.css) — the pill's input inherited it (Finding 43) */
  line-height: var(--pui-font-line-height-normal);
}
.pui-footer__column { display: flex; flex-direction: column; gap: var(--pui-space-4); flex: none; }
.pui-footer__column--stack { gap: var(--pui-space-32); }
.pui-footer__column--stack > div { display: flex; flex-direction: column; gap: var(--pui-space-4); }
.pui-footer__heading {
  margin: 0;
  color: var(--pui-nav-section-fg);
  font-size: var(--pui-font-size-14);            /* stated, not inherited: on a Bootstrap page an <h4> heading takes its h4 scale (2.4.2, Finding 43) */
  font-weight: var(--pui-font-weight-600);          /* the site's 600 (2.5.10) */
  line-height: var(--pui-font-line-height-loose);    /* the site's 1.7 — 24 tall (2.5.10) */
}
.pui-footer__link {
  color: var(--pui-nav-item-fg);
  font-size: var(--pui-font-size-14);            /* stated: the site's <li> carries its own size (Finding 43) */
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-relaxed);  /* the site's 1.5 — rows 21 + space/4 = its 25 pitch (2.5.10) */
  text-decoration: none;
  transition: color var(--pui-motion-state) var(--pui-motion-easing);
}
/* links in a list (the site's <ul class="footer__links">) keep the column's space/4 between rows */
.pui-footer__links { display: flex; flex-direction: column; gap: var(--pui-space-4); margin: 0; padding: 0; list-style: none; }
.pui-footer__link:hover { color: var(--pui-nav-item-fg-active); }
.pui-footer__link:focus-visible { outline: none; border-radius: var(--pui-radius-4); box-shadow: var(--pui-ring); }
/* the step above the socials row and above the email field — what __socials and __pill carried; the field is the
   Subscription column's 303 (Figma 265:28 — stated) */
.pui-footer .pui-socials,
.pui-footer .pui-field { margin-top: var(--pui-space-8); }
.pui-footer .pui-field { width: 303px; max-width: 100%; }
/* deprecated: 2.3.0 — use .pui-socials (socials.css) */
.pui-footer__socials { display: flex; gap: var(--pui-space-12); margin-top: var(--pui-space-8); }
/* deprecated: 2.3.0 — use .pui-input-group.pui-input-group--on-dark with the small Button in it (input-group.css) */
.pui-footer__pill {                               /* the email field — a box since 2.5.10; the name is 1.0's */
  display: flex;
  align-items: center;
  gap: var(--pui-space-8);
  box-sizing: border-box;
  height: var(--pui-size-control-medium);
  margin-top: var(--pui-space-8);
  padding: calc(var(--pui-space-4) - 1px) calc(var(--pui-space-8) - 1px) calc(var(--pui-space-4) - 1px) calc(var(--pui-space-12) - 1px);
  border: 1px solid var(--pui-nav-item-bg-active);
  border-radius: var(--pui-radius-8);
  background: var(--pui-color-alpha-white-8);
}
.pui-footer__pill > input {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--pui-nav-item-fg-active);
  font-family: inherit;
  font-size: var(--pui-font-size-12);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
  outline: none;
}
.pui-footer__pill > input::placeholder { color: var(--pui-nav-section-fg); opacity: 1; }
/* 3.5.2 — the input clears its outline; the ring goes on the field. border/focus reads 3.72 against
   surface/inverse, the footer's ground. */
.pui-footer__pill:focus-within { border-color: var(--pui-border-focus); box-shadow: var(--pui-ring); }
/* The field's own Subscribe button (2.5.10, Figma 265:36): 12 Regular text/default on text/on-inverse, space/4 × space/8,
   radius/4, on a 1.2 line so it is the site's 22 tall — the site's mini button, not the Button component (28 at its smallest). */
/* deprecated: 2.3.0 — the field's button is the small Button inside the Input group */
.pui-footer__subscribe {
  flex: none;
  margin: 0;
  padding: var(--pui-space-4) var(--pui-space-8);
  border: 0;
  border-radius: var(--pui-radius-4);
  background: var(--pui-text-on-inverse);
  color: var(--pui-text-default);
  font-family: inherit;
  font-size: var(--pui-font-size-12);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-tight);    /* the site's 14 px line on a 12 label — 22 tall (2.5.10) */
  white-space: nowrap;
  cursor: pointer;
}
.pui-footer__subscribe:disabled { opacity: 0.5; pointer-events: none; }
/* the inset ring: 4 px of field around a 25-tall button leaves the outer ring on the field's own dark fill, where its
   change read 2.97 against 2.4.13's 3 (2.5.10); inside the white button it is the ring every clipped control takes */
.pui-footer__subscribe:focus-visible { outline: none; box-shadow: var(--pui-ring-inset); }
.pui-footer__badges {
  display: flex;
  flex: 1 1 auto;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  align-self: stretch;
  gap: var(--pui-space-24);
}
.pui-footer__badges > img { display: block; max-height: 64px; width: auto; }

/* states.css — State block: empty, loading, error (10_COMPONENTS.md § Shells and patterns).
 *
 * What a data container shows before its data, while it comes, and when it fails — Empty, Skeleton and Alert composed
 * as one block, the trio M3.2 asks every pilot container to have. A white card (surface/page, border/default,
 * radius/16, padding 24, gap 16): table context — Empty's block with its dashed border, five Skeleton rows (a small
 * circle, a medium and a small line, a long line filling the rest), Alert in the danger outline with a Retry beside
 * it; card context (368) — Empty's inline line, three Skeleton lines, the soft danger Alert. Never a Spinner for a
 * container. Figma 270:217. Contract: registry/states/registry.json.
 *
 *   <div class="pui-state-block"> …a .pui-empty… </div>
 *   <div class="pui-state-block"><div class="pui-state-block__skeleton-row"><span class="pui-skeleton pui-skeleton--circle pui-skeleton--24"></span>
 *     <span class="pui-skeleton pui-skeleton--line" style="width: 160px"></span><span class="pui-skeleton pui-skeleton--line" style="width: 80px"></span>
 *     <span class="pui-skeleton pui-skeleton--line"></span></div> ×5 </div>
 *   <div class="pui-state-block"><div class="pui-state-block__error"><div class="pui-alert pui-alert--danger pui-alert--outline">…</div>
 *     <button class="pui-button pui-button--secondary pui-button--small">Retry</button></div></div>
 *
 * Context   (none) table · --card (368, the error stacks)
 */

.pui-state-block {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--pui-space-16);
  box-sizing: border-box;
  width: 100%;
  padding: var(--pui-space-24);
  border: 1px solid var(--pui-border-default);
  border-radius: var(--pui-radius-16);
  background: var(--pui-surface-page);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
}
.pui-state-block > .pui-empty { width: 100%; }
.pui-state-block__skeleton-row {
  display: flex;
  align-items: center;
  gap: var(--pui-space-12);
  width: 100%;
}
.pui-state-block__skeleton-row > .pui-skeleton--line { flex: 0 0 auto; }
.pui-state-block__skeleton-row > .pui-skeleton--line:last-child { flex: 1 1 auto; width: auto; }
.pui-state-block__error {
  display: flex;
  align-items: center;
  gap: var(--pui-space-12);
  width: 100%;
}
.pui-state-block__error > .pui-alert { flex: 1 1 auto; }
.pui-state-block__error > .pui-button { flex: none; }
.pui-state-block--card { max-width: 368px; }
.pui-state-block--card .pui-state-block__error { flex-direction: column; align-items: stretch; }
.pui-state-block--card .pui-state-block__error > .pui-button { align-self: flex-start; }

/* Chat — Echo AI */
/* chat.css — Chat · Message · Bubble · Suggestions · Projection · Attachment · Marker · Composer · Message scroller
 * (10_COMPONENTS.md § Chat — Echo AI).
 *
 * 3.9.1 — THE CHAT IS FEDOR'S, NOT PAGGY'S. Until 2026-09-21 these parts were Paggy's app.css re-cut, and the
 * concept he drew lived on the docs site as a preview card (`.pui-echo`, site CSS) which its own comment promised
 * to delete "when the chat row lands". This is that row. The master is `Card/Chat` node `3911:8733` of the frozen
 * site file `rP1q5erxNQKyUuxqPmPZBU` (D26: read it, never write to it), read in full 2026-09-21 — strip, messages,
 * composer. The preview card only ever showed a 286 px fragment of it; the master also carries a suggestions table
 * and a row of projection chips inside Echo's bubble, and both are built here.
 *
 * THE RULE THIS UNIT WAS BUILT UNDER (his word, 2026-09-21): *"redesign everything I redesigned… if you don't see
 * these different states in my solution, okay, use what we have for this. If I didn't redesign it, then use it."*
 * A master governs what it depicts; silence in it is the system's existing answer, still standing. So REDESIGNED
 * from the master: the shell and its branded strip, both bubble roles, the avatars, the actions row, the
 * suggestions table, the projection chips, the composer. KEPT AS THE SYSTEM ALREADY HAD THEM, untouched below:
 * Attachment, Marker, the thinking state, the feedback row and the scroller's Jump to latest.
 *
 * What the master changes about a chat: the user's turn is a QUIET surface/card bubble at the right with its time
 * inside it (the old one was a dark surface/inverse slab); Echo's is WHITE with a hairline and holds an actions
 * row; both turns carry a 36 avatar; a branded strip sits at the top; and the composer is ONE LINE with a
 * LABELLED button instead of a floating 760 card with two round icon buttons.
 *
 * Every value is a token. EIGHT STATED SUBSTITUTIONS, all of them the site file's palette and space scale meeting
 * ours, none of them a change of intent:
 *   a. the master's ink and button fill are #171717, which is not in our palette — text/default and
 *      action/primary/bg (#0b0202, neutral/900) do that job and are one step darker.
 *   a2. the master's small grey (#858080) is text/subtle's value, and text/subtle is PROMISED ONLY for the
 *      subheading job at 24 px and over (the contrast matrix, U12 / D43): at 12 px its floor is 4.5 and it
 *      measures 3.49 on the card ground and 3.89 on white. The system's small grey is text/caption, so the
 *      meta lines, the suggestions head and the projection labels take it. Likewise the master's green
 *      (#74af36) is status/success/SOLID, which the matrix exempts as a GLYPH at the 3.0 non-text floor and
 *      not as type (2.65 on white), so the budget figures and a good projection value were built on
 *      status/success/fg (6.56) — and on the rendered table Fedor changed them back to the SOLID, his master's
 *      own green: *"this green color is wrong here… I want solid green color status."* It is in the contrast
 *      matrix as an exempt figure, and it is never the only cue — the column is headed BUDGET in words.
 *      The small grey stands as text/caption: his design in the role the system promises for the job.
 *   b. the master's Input instance strokes #E5E5E5; the master uses OUR Input component, so the component's own
 *      border/default supplies it (his rule: not redesigned, so use ours). Button likewise — its 8/16, radius/8,
 *      14/20 already match the master to the pixel, so `.pui-button` is used as it stands.
 *   c. paddings off the 4-step scale go to the nearest step on it: 10 -> space/12, 5 -> space/4, 2 stays space/2.
 *   d. gaps of 6 -> space/4.
 *   e. the user bubble keeps the master's 820 — every user turn is the same width whatever its length, which
 *      is what he drew — with `max-width: 100%` so it shrinks rather than overflowing a narrow host.
 *   f. Echo's bubble is 10px 20px on the master's first instance and 12px 20px on its second; 12 is the later of
 *      the two and the one on the scale.
 *   g. the composer's upward blur (0 -1px 17.6px 6px rgba(0,0,0,.09)) is dropped: there is no upward elevation
 *      token, and the 1 px border/default top line carries the same lift off the log.
 *   h. the strip's five 35 px-blurred ellipses under a 45 % white wash are drawn as the linear-gradient they
 *      resolve to at 49 px tall — stops at the ellipse centres (11 / 17 / 45 / 71 / 92 % of 1092), the same five
 *      hues in the same order. Carried over from the verified preview rather than re-derived.
 *
 * Figma: 272:2216 · 272:2173 · 272:2250 · 272:2261 · 272:2351 · 272:2266 (the library page, re-cut in 3.9.2).
 * Contracts: registry/chat, message, bubble, suggestions, projection, attachment, marker, composer, message-scroller.
 *
 *   <div class="pui-chat">
 *     <div class="pui-chat__strip"><img class="pui-chat__logo" src="…/echo-ai-wordmark.svg" alt="Echo AI" />
 *       <span class="pui-chat__live"><span class="pui-chat__dot"></span>Live · model v4.2</span></div>
 *     <div class="pui-message-scroller"><div class="pui-message-scroller__log">
 *       <div class="pui-marker pui-marker--divider">Today</div>
 *       <div class="pui-message pui-message--user">
 *         <div class="pui-bubble pui-bubble--user"><p>…</p><p class="pui-bubble__meta">Anna · 10:42</p></div>
 *         <span class="pui-avatar pui-message__avatar"><img src="…/anna.png" alt="" /></span></div>
 *       <div class="pui-message pui-message--ai">
 *         <img class="pui-message__avatar" src="…/echo-mark.svg" alt="" />
 *         <div class="pui-bubble pui-bubble--ai"><p>…</p>
 *           <div class="pui-suggestions">…</div>
 *           <div class="pui-bubble__actions"><button class="pui-button">Apply suggestions</button>
 *             <a class="pui-bubble__link" href="#">Explain the reasoning</a>
 *             <span class="pui-bubble__meta pui-bubble__meta--end">Echo · 10:42 · 1.8 s</span></div></div></div>
 *       <div class="pui-message pui-message--ai"><img class="pui-message__avatar" src="…" alt="" />
 *         <div class="pui-bubble pui-bubble--ai pui-bubble--thinking">Thinking…</div></div></div>
 *       <button class="pui-button pui-button--secondary pui-button--small pui-message-scroller__jump">Jump to latest</button></div>
 *     <form class="pui-composer">
 *       <div class="pui-composer__files"><span class="pui-attachment">…</span></div>
 *       <div class="pui-composer__bar"><input class="pui-input" aria-label="Ask Echo" placeholder="Ask Echo about publishers, GEOs, creatives or budget…" />
 *         <button class="pui-button" type="button">Ask Echo</button></div></form></div>
 */

/* ── The shell. One card: strip, log, composer, nothing escaping the 16 radius. */
.pui-chat {
  display: flex;
  flex-direction: column;
  overflow: clip;
  box-sizing: border-box;
  width: 100%;
  border: 1px solid var(--pui-border-default);
  border-radius: var(--pui-radius-16);
  background: var(--pui-surface-page);
  font-family: var(--pui-font-family);
}

/* The strip — substitution (h). The wash is the ::before, so the lockup and the pill sit above it. */
.pui-chat__strip {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--pui-space-12);
  padding: var(--pui-space-8) var(--pui-space-24);
  background-image: linear-gradient(
    90deg,
    var(--pui-color-blue-300) 11%,
    var(--pui-color-red-400) 17%,
    var(--pui-color-yellow-400) 45%,
    var(--pui-color-red-300) 71%,
    var(--pui-color-blue-300) 92%
  );
}
.pui-chat__strip::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--pui-surface-page);
  opacity: 0.45;
}
.pui-chat__logo,
.pui-chat__live { position: relative; }
/* The EXPORTED lockup — the spark and the letterforms as one set of vectors (assets/echo/README.md), never a
   glyph beside two spans of type. Height only; the width follows, so the aspect ratio is the file's. */
.pui-chat__logo {
  display: block;
  width: auto;
  height: var(--pui-space-20);
}
.pui-chat__live {
  display: flex;
  align-items: center;
  gap: var(--pui-space-4);
  padding: var(--pui-space-4) var(--pui-space-12);
  border: 1px solid var(--pui-border-default);
  border-radius: var(--pui-radius-full);
  background: var(--pui-surface-page);
  color: var(--pui-text-default);
  font-size: var(--pui-font-size-12);
  font-weight: var(--pui-font-weight-500);
  white-space: nowrap;
}
.pui-chat__dot {
  flex: none;
  width: var(--pui-space-8);
  height: var(--pui-space-8);
  border-radius: var(--pui-radius-full);
  background: var(--pui-status-success-solid);
}

/* ── The turn. A row: avatar and bubble, 12 apart; the user's is reversed so the avatar lands at the right. */
.pui-message {
  display: flex;
  align-items: flex-start;
  gap: var(--pui-space-12);
  width: 100%;
  font-family: var(--pui-font-family);
}
.pui-message--user { justify-content: flex-end; }
.pui-message__avatar {
  flex: none;
  width: var(--pui-size-control-medium);
  height: var(--pui-size-control-medium);
  border-radius: var(--pui-radius-full);
}
/* KEPT (his rule): the feedback row is not in the master, so it stands as the system had it. The author line that
   sat here is GONE (2026-09-21): where the avatar IS the Echo mark, a name and a mark beside it say the same thing
   three times. What marks a turn as generated is the avatar. */
.pui-message__actions { display: flex; gap: var(--pui-space-2); }
.pui-message__body { display: flex; flex: 1 1 auto; flex-direction: column; gap: var(--pui-space-4); min-width: 0; }

/* ── The bubble. Two roles, and they are no longer a light one and a dark one. */
.pui-bubble {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-8);
  min-width: 0;
  padding: var(--pui-space-12) var(--pui-space-20);
  border: 1px solid transparent;
  border-radius: var(--pui-radius-16);
  background: var(--pui-surface-page);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-16);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-relaxed);
  overflow-wrap: anywhere;
}
/* Echo: white, a hairline, and it takes the width left over — the suggestions table needs it. */
.pui-bubble--ai { border-color: var(--pui-border-default); }
/* Echo's turn takes the width left over — as the row's own child, or through the body column that holds the
   feedback row under it. The Figma Message is built the same way (D113). */
.pui-message--ai > .pui-bubble--ai,
.pui-message__body > .pui-bubble--ai { flex: 1 1 auto; }
/* The user: a quiet surface, no line, tighter, and its time sits INSIDE it — substitution (e) on the width. */
.pui-bubble--user {
  gap: var(--pui-space-4);
  width: 820px;
  max-width: 100%;
  padding: var(--pui-space-8) var(--pui-space-16);
  background: var(--pui-surface-card);
}
/* KEPT: the thinking state is not in the master. Its `--user` pair is gone with the dark bubble it existed for. */
.pui-bubble--thinking { color: var(--pui-text-caption); }
.pui-bubble > p { margin: 0; }
/* Consecutive PROSE paragraphs are 8 apart. The meta line is not prose — it is the turn's own line and sits at the
   bubble's gap, which is 4 on the user's: without the :not() it took both and rendered 12, where the master's bubble
   is 820 x 86 = 8 + 48 + 4 + 18 + 8. Found by the React twin in 3.9.3, not by the computed-style probe, which read
   the flex gap and never the rendered distance. */
.pui-bubble > p + p:not(.pui-bubble__meta) { margin-top: var(--pui-space-8); }

.pui-bubble__meta {
  color: var(--pui-text-caption);
  font-size: var(--pui-font-size-12);
  font-weight: var(--pui-font-weight-400);
  line-height: var(--pui-font-line-height-normal);
}
.pui-bubble__meta--end { margin-inline-start: auto; }

/* The actions row — a primary button, the links that follow it, and the meta pushed to the far end. */
.pui-bubble__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--pui-space-16);
}
.pui-bubble__link {
  color: var(--pui-text-link);
  font-size: var(--pui-font-size-14);
  font-weight: var(--pui-font-weight-500);
  text-decoration: none;
}
.pui-bubble__link:hover { text-decoration: underline; }
.pui-bubble__link:focus-visible { outline: none; box-shadow: var(--pui-ring); border-radius: var(--pui-radius-4); }

/* ── Suggestions — Echo's ranked proposal inside its bubble: a sunken block of white rows. */
.pui-suggestions {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-8);
  padding: var(--pui-space-12);
  border-radius: var(--pui-radius-8);
  background: var(--pui-surface-card);
}
.pui-suggestions__head,
.pui-suggestions__row {
  display: flex;
  align-items: center;
  gap: var(--pui-space-12);
}
.pui-suggestions__head {
  padding: 0 var(--pui-space-12);
  color: var(--pui-text-caption);
  font-size: var(--pui-font-size-12);
  font-weight: var(--pui-font-weight-600);
  line-height: var(--pui-font-line-height-normal);
}
.pui-suggestions__row {
  padding: var(--pui-space-4) var(--pui-space-12);
  border-radius: var(--pui-radius-4);
  background: var(--pui-surface-page);
  font-size: var(--pui-font-size-12);
  line-height: var(--pui-font-line-height-normal);
}
.pui-suggestions__name {
  /* 3.9.2 — 130, not the master's 190. The master's names are real ones ("Roku CTV Direct"); ours are numbered
     (no real company stands in the sample data), and at 190 a numbered name left a dead band before the bar.
     Fixed rather than hugging, so the rows still line up; a longer name clips with an ellipsis. */
  flex: none;
  width: 130px;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--pui-text-default);
  font-weight: var(--pui-font-weight-500);
}
.pui-suggestions__head .pui-suggestions__name { font-weight: var(--pui-font-weight-600); }
/* The measure: a track that fills the row, and its number beside it. */
.pui-suggestions__bar {
  display: flex;
  flex: 1 1 auto;
  align-items: center;
  gap: var(--pui-space-8);
  min-width: 0;
}
.pui-suggestions__track {
  flex: 1 1 auto;
  height: var(--pui-space-8);
  border-radius: var(--pui-radius-full);
  background: var(--pui-surface-card);
}
.pui-suggestions__fill {
  /* 3.9.1 — `display: block` is load-bearing: as an inline span this painted NOTHING, because height does
     not apply to an inline box. The rule read correct and the bar was empty until it was rendered. */
  display: block;
  height: 100%;
  border-radius: var(--pui-radius-full);
  background: var(--pui-color-blue-400);
}
/* The master steps the hue down on the rows it is less sure of, in step with the confidence pill. */
.pui-suggestions__row--tentative .pui-suggestions__fill { background: var(--pui-color-blue-300); }
.pui-suggestions__value {
  flex: none;
  color: var(--pui-text-default);
  font-weight: var(--pui-font-weight-600);
}
/* The confidence CELL is 100 wide and right-aligned; the pill inside it hugs its number. One element doing
   both stretched the pill across the whole column. */
.pui-suggestions__confidence {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: flex-end;
  width: 100px;
}
.pui-suggestions__pill {
  display: inline-flex;
  align-items: center;
  padding: var(--pui-space-2) var(--pui-space-12);
  border-radius: var(--pui-radius-full);
  background: var(--pui-status-info-bg);
  color: var(--pui-status-info-solid);
  font-weight: var(--pui-font-weight-600);
}
.pui-suggestions__row--tentative .pui-suggestions__pill {
  background: var(--pui-status-warning-bg);
  color: var(--pui-status-warning-fg);
}
.pui-suggestions__budget {
  flex: none;
  width: 90px;
  /* Fedor, 2026-09-21, on the rendered table: *"this green color is wrong here… I want solid green color
     status."* He was shown the reading (status/success/solid is 2.65 on white as type) and chose it. */
  color: var(--pui-status-success-solid);
  font-weight: var(--pui-font-weight-600);
  text-align: end;
}
/* The head's cells are labels, not values — they take the head's own subtle ink, never the column's. */
.pui-suggestions__head .pui-suggestions__budget { color: inherit; }

/* ── Projection chips — the numbers Echo commits to, under the answer that promised them. */
.pui-projection-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--pui-space-8);
}
.pui-projection {
  display: inline-flex;
  align-items: center;
  gap: var(--pui-space-4);
  padding: var(--pui-space-4) var(--pui-space-12);
  border-radius: var(--pui-radius-full);
  background: var(--pui-surface-card);
  font-size: var(--pui-font-size-12);
  line-height: var(--pui-font-line-height-normal);
}
.pui-projection__label { color: var(--pui-text-caption); }
.pui-projection__value { color: var(--pui-text-default); font-weight: var(--pui-font-weight-600); }
.pui-projection__value--good { color: var(--pui-status-success-solid); }
.pui-projection-row .pui-bubble__meta--end { margin-inline-start: auto; }

/* ── KEPT UNCHANGED (his rule — not in the master): Attachment and Marker, exactly as the system had them. */
.pui-attachment {
  display: inline-flex;
  align-items: center;
  gap: var(--pui-space-8);
  box-sizing: border-box;
  max-width: 320px;
  padding: var(--pui-space-4) var(--pui-space-12);
  border: 1px solid var(--pui-border-default);
  border-radius: var(--pui-radius-full);
  background: var(--pui-surface-sunken);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-12);
  line-height: var(--pui-font-line-height-normal);
}
.pui-attachment:has(.pui-attachment__remove) { padding-inline-end: var(--pui-space-8); }
.pui-attachment__glyph { flex: none; width: var(--pui-size-icon-16); height: var(--pui-size-icon-16); color: var(--pui-text-muted); }
.pui-attachment__name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pui-attachment__remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--pui-space-20);
  height: var(--pui-space-20);
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: var(--pui-radius-full);
  background: var(--pui-border-default);
  color: var(--pui-text-default);
  cursor: pointer;
  outline: none;
  transition: background-color var(--pui-motion-state) var(--pui-motion-easing), color var(--pui-motion-state) var(--pui-motion-easing);
}
.pui-attachment__remove > .pui-icon { width: var(--pui-space-12); height: var(--pui-space-12); }
.pui-attachment__remove:hover { background: var(--pui-tint-red-bg); color: var(--pui-tint-red-fg); }
.pui-attachment__remove:focus-visible { box-shadow: var(--pui-ring); }

.pui-marker {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--pui-space-12);
  width: 100%;
  padding: var(--pui-space-8) 0;
  color: var(--pui-text-muted);
  font-family: var(--pui-font-family);
  font-size: var(--pui-font-size-12);
  line-height: var(--pui-font-line-height-normal);
  text-align: center;
}
.pui-marker--divider::before,
.pui-marker--divider::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--pui-border-default);
}

/* ── The composer — ONE LINE. The files row is the system's, kept above the line: with nothing attached it
      collapses and what renders is the master's single row exactly. Substitutions (b), (c), (g). */
.pui-composer {
  display: flex;
  flex-direction: column;
  gap: var(--pui-space-8);
  box-sizing: border-box;
  width: 100%;
  margin: 0;
  padding: var(--pui-space-12) var(--pui-space-24);
  border-top: 1px solid var(--pui-border-default);
  background: var(--pui-surface-page);
  color: var(--pui-text-default);
  font-family: var(--pui-font-family);
}
.pui-composer__files { display: flex; flex-wrap: wrap; gap: var(--pui-space-8); }
.pui-composer__files:empty { display: none; }
.pui-composer__bar {
  display: flex;
  align-items: center;
  gap: var(--pui-space-12);
}
.pui-composer__bar .pui-input { min-width: 0; flex: 1 1 auto; }
.pui-composer__bar .pui-button { flex: none; }

/* ── The scroller. The master's own column: 32 between turns, padded 16 / 24 / 12. */
.pui-message-scroller {
  position: relative;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  background: var(--pui-surface-page);
  scrollbar-width: thin;
  scrollbar-color: var(--pui-border-default) transparent;
}
.pui-message-scroller__log {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: var(--pui-space-32);
  /* 3.9.1 — `flex: 1 0 auto` where this was `min-height: 100%`. The log filled the scroller AND the Jump pill
     sat in flow under it, so a thread that fitted still overflowed by the pill's height and the newest message
     was cut off by that much. Growing to the space LEFT by the pill anchors the log the same way and leaves
     nothing over; a thread taller than the box still grows past it and scrolls. */
  flex: 1 0 auto;
  padding: var(--pui-space-16) var(--pui-space-24) var(--pui-space-12);
}
/* KEPT: not in the master. */
.pui-message-scroller__jump {
  position: sticky;
  bottom: var(--pui-space-16);
  align-self: center;
}
