@charset "UTF-8";
:root {
  --neutral-0: oklch(100% 0 0deg);
  --neutral-50: oklch(98.8% 0.002 260deg);
  --neutral-100: oklch(96.5% 0.003 260deg);
  --neutral-200: oklch(92% 0.004 260deg);
  --neutral-300: oklch(86% 0.005 260deg);
  --neutral-400: oklch(72% 0.008 260deg);
  --neutral-450: oklch(63% 0.009 260deg);
  --neutral-500: oklch(58% 0.01 260deg);
  --neutral-550: oklch(52% 0.011 260deg);
  --neutral-600: oklch(45% 0.012 260deg);
  --neutral-700: oklch(32% 0.013 260deg);
  --neutral-800: oklch(22% 0.012 260deg);
  --neutral-900: oklch(14% 0.01 260deg);
  --neutral-1000: oklch(8% 0.008 260deg);
  --success-50: oklch(97% 0.04 155deg);
  --success-100: oklch(92% 0.08 155deg);
  --success-500: oklch(65% 0.17 155deg);
  --success-700: oklch(48% 0.17 155deg);
  --success-900: oklch(28% 0.1 155deg);
  --warning-50: oklch(97% 0.04 85deg);
  --warning-100: oklch(92% 0.08 85deg);
  --warning-500: oklch(78% 0.16 85deg);
  --warning-700: oklch(48% 0.14 85deg);
  --warning-900: oklch(28% 0.08 85deg);
  --danger-50: oklch(97% 0.04 25deg);
  --danger-100: oklch(92% 0.08 25deg);
  --danger-500: oklch(58% 0.22 25deg);
  --danger-700: oklch(48% 0.2 25deg);
  --danger-900: oklch(28% 0.12 25deg);
  --info-50: oklch(97% 0.04 245deg);
  --info-100: oklch(92% 0.08 245deg);
  --info-500: oklch(65% 0.15 245deg);
  --info-700: oklch(48% 0.15 245deg);
  --info-900: oklch(28% 0.1 245deg);
  --success-ink: var(--success-700);
  --warning-ink: var(--warning-700);
  --danger-ink: var(--danger-700);
  --info-ink: var(--info-700);
  --fill-danger: var(--danger-500);
  --fill-danger-hover: oklch(48% 0.2 25deg);
  --text-on-danger: var(--neutral-0);
  --fill-success: var(--success-500);
  --text-on-success: var(--neutral-1000);
  --fill-warning: var(--warning-500);
  --text-on-warning: var(--neutral-1000);
  --fill-info: var(--info-500);
  --text-on-info: var(--neutral-1000);
  --accent-admin-50: oklch(97% 0.02 295deg);
  --accent-admin-100: oklch(93% 0.05 295deg);
  --accent-admin-200: oklch(86% 0.1 295deg);
  --accent-admin-400: oklch(72% 0.204 295deg);
  --accent-admin-500: oklch(58% 0.24 295deg);
  --accent-admin-600: oklch(54% 0.24 295deg);
  --accent-admin-700: oklch(46% 0.221 295deg);
  --accent-admin-900: oklch(28% 0.156 295deg);
  --accent-partner-50: oklch(97% 0.02 65deg);
  --accent-partner-100: oklch(93% 0.05 65deg);
  --accent-partner-200: oklch(86% 0.1 65deg);
  --accent-partner-400: oklch(66% 0.145 65deg);
  --accent-partner-500: oklch(56% 0.17 65deg);
  --accent-partner-600: oklch(48% 0.17 65deg);
  --accent-partner-700: oklch(40% 0.156 65deg);
  --accent-partner-900: oklch(28% 0.111 65deg);
  --accent-identity-50: oklch(97% 0.02 220deg);
  --accent-identity-100: oklch(93% 0.05 220deg);
  --accent-identity-200: oklch(86% 0.1 220deg);
  --accent-identity-400: oklch(63% 0.111 220deg);
  --accent-identity-500: oklch(53% 0.13 220deg);
  --accent-identity-600: oklch(45% 0.13 220deg);
  --accent-identity-700: oklch(37% 0.12 220deg);
  --accent-identity-900: oklch(28% 0.085 220deg);
  --accent-invoice-50: oklch(97% 0.02 160deg);
  --accent-invoice-100: oklch(93% 0.05 160deg);
  --accent-invoice-200: oklch(86% 0.1 160deg);
  --accent-invoice-400: oklch(62.5% 0.128 160deg);
  --accent-invoice-500: oklch(52.5% 0.15 160deg);
  --accent-invoice-600: oklch(44.5% 0.15 160deg);
  --accent-invoice-700: oklch(36.5% 0.138 160deg);
  --accent-invoice-900: oklch(28% 0.098 160deg);
  --accent-ninefive-50: oklch(97% 0.02 340deg);
  --accent-ninefive-100: oklch(93% 0.05 340deg);
  --accent-ninefive-200: oklch(86% 0.1 340deg);
  --accent-ninefive-400: oklch(68% 0.221 340deg);
  --accent-ninefive-500: oklch(58% 0.26 340deg);
  --accent-ninefive-600: oklch(50% 0.26 340deg);
  --accent-ninefive-700: oklch(42% 0.239 340deg);
  --accent-ninefive-900: oklch(28% 0.169 340deg);
  --gradient-border-subtle: linear-gradient(
    135deg,
    var(--accent-400),
    var(--accent-600)
  );
  --gradient-border-vivid: linear-gradient(
    135deg,
    var(--accent-admin-500),
    var(--accent-ninefive-500),
    var(--accent-identity-500)
  );
  --glass-surface: color-mix(in oklch, var(--surface-raised) 64%, transparent);
  --glass-blur: blur(20px) saturate(180%) brightness(1.06);
  --glass-border: 1px solid color-mix(in oklch, white 28%, transparent);
  --glass-shadow:
    0 16px 40px -12px color-mix(in oklch, var(--neutral-1000) 30%, transparent),
    0 2px 8px -4px color-mix(in oklch, var(--neutral-1000) 16%, transparent),
    inset 0 1px 0 0 color-mix(in oklch, white 65%, transparent),
    inset 0 0 0 1px color-mix(in oklch, white 8%, transparent);
  --glass-sheen: linear-gradient(150deg,
    color-mix(in oklch, white 38%, transparent) 0%,
    color-mix(in oklch, white 6%, transparent) 24%,
    transparent 48%);
  --overlay-scrim: color-mix(in oklch, var(--neutral-1000) 12%, transparent);
  --overlay-scrim-strong: color-mix(in oklch, var(--neutral-1000) 38%, transparent);
  --overlay-blur: blur(8px) saturate(120%);
  --glow-accent:
    0 0 0 3px color-mix(in oklch, var(--accent-500) 35%, transparent),
    0 0 24px color-mix(in oklch, var(--accent-500) 25%, transparent);
}

:root {
  --font-display: "Instrument Serif", ui-serif, Georgia, serif;
  --font-ui: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --text-3xs: 0.625rem; /* 10px */
  --text-2xs: 0.6875rem; /* 11px */
  --text-xs: 0.75rem; /* 12px */
  --text-sm: 0.8125rem; /* 13px */
  --text-base: 0.9375rem; /* 15px — default body */
  --text-md: 1rem; /* 16px */
  --text-lg: 1.125rem; /* 18px */
  --text-xl: 1.375rem; /* 22px */
  --text-2xl: 1.75rem; /* 28px */
  --text-3xl: 2.25rem; /* 36px */
  --text-4xl: 2.875rem; /* 46px */
  --text-5xl: 3.75rem; /* 60px */
  --text-6xl: 4.75rem; /* 76px */
  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;
  --leading-none: 1;
  --leading-tight: 1.15;
  --leading-snug: 1.3;
  --leading-normal: 1.5;
  --leading-relaxed: 1.65;
  --tracking-tight: -0.02em;
  --tracking-snug: -0.01em;
  --tracking-normal: 0;
  --tracking-wide: 0.02em;
  --tracking-widest: 0.08em;
  --icon-size-xs: 0.75rem; /* 12px */
  --icon-size-sm: 1rem; /* 16px */
  --icon-size-md: 1.25rem; /* 20px — default */
  --icon-size-lg: 1.5rem; /* 24px */
  --icon-size-xl: 2rem; /* 32px */
  --icon-stroke: 1.75;
}

:root {
  --type-display-xl-font: var(--font-display);
  --type-display-xl-size: var(--text-6xl);
  --type-display-xl-weight: var(--weight-regular);
  --type-display-xl-leading: var(--leading-none);
  --type-display-xl-tracking: var(--tracking-tight);
  --type-display-xl-transform: none;
  --type-display-xl-variant-numeric: normal;
  --type-display-lg-font: var(--font-display);
  --type-display-lg-size: var(--text-5xl);
  --type-display-lg-weight: var(--weight-regular);
  --type-display-lg-leading: var(--leading-tight);
  --type-display-lg-tracking: var(--tracking-tight);
  --type-display-lg-transform: none;
  --type-display-lg-variant-numeric: normal;
  --type-display-md-font: var(--font-display);
  --type-display-md-size: var(--text-4xl);
  --type-display-md-weight: var(--weight-regular);
  --type-display-md-leading: var(--leading-tight);
  --type-display-md-tracking: var(--tracking-snug);
  --type-display-md-transform: none;
  --type-display-md-variant-numeric: normal;
  --type-heading-xl-font: var(--font-ui);
  --type-heading-xl-size: var(--text-3xl);
  --type-heading-xl-weight: var(--weight-semibold);
  --type-heading-xl-leading: var(--leading-tight);
  --type-heading-xl-tracking: var(--tracking-snug);
  --type-heading-xl-transform: none;
  --type-heading-xl-variant-numeric: normal;
  --type-heading-lg-font: var(--font-ui);
  --type-heading-lg-size: var(--text-2xl);
  --type-heading-lg-weight: var(--weight-semibold);
  --type-heading-lg-leading: var(--leading-tight);
  --type-heading-lg-tracking: var(--tracking-snug);
  --type-heading-lg-transform: none;
  --type-heading-lg-variant-numeric: normal;
  --type-heading-md-font: var(--font-ui);
  --type-heading-md-size: var(--text-xl);
  --type-heading-md-weight: var(--weight-semibold);
  --type-heading-md-leading: var(--leading-snug);
  --type-heading-md-tracking: var(--tracking-normal);
  --type-heading-md-transform: none;
  --type-heading-md-variant-numeric: normal;
  --type-heading-sm-font: var(--font-ui);
  --type-heading-sm-size: var(--text-lg);
  --type-heading-sm-weight: var(--weight-semibold);
  --type-heading-sm-leading: var(--leading-snug);
  --type-heading-sm-tracking: var(--tracking-normal);
  --type-heading-sm-transform: none;
  --type-heading-sm-variant-numeric: normal;
  --type-body-lg-font: var(--font-ui);
  --type-body-lg-size: var(--text-md);
  --type-body-lg-weight: var(--weight-regular);
  --type-body-lg-leading: var(--leading-normal);
  --type-body-lg-tracking: var(--tracking-normal);
  --type-body-lg-transform: none;
  --type-body-lg-variant-numeric: normal;
  --type-body-font: var(--font-ui);
  --type-body-size: var(--text-base);
  --type-body-weight: var(--weight-regular);
  --type-body-leading: var(--leading-normal);
  --type-body-tracking: var(--tracking-normal);
  --type-body-transform: none;
  --type-body-variant-numeric: normal;
  --type-body-sm-font: var(--font-ui);
  --type-body-sm-size: var(--text-sm);
  --type-body-sm-weight: var(--weight-regular);
  --type-body-sm-leading: var(--leading-normal);
  --type-body-sm-tracking: var(--tracking-normal);
  --type-body-sm-transform: none;
  --type-body-sm-variant-numeric: normal;
  --type-ui-font: var(--font-ui);
  --type-ui-size: var(--text-sm);
  --type-ui-weight: var(--weight-medium);
  --type-ui-leading: var(--leading-snug);
  --type-ui-tracking: var(--tracking-normal);
  --type-ui-transform: none;
  --type-ui-variant-numeric: normal;
  --type-ui-sm-font: var(--font-ui);
  --type-ui-sm-size: var(--text-xs);
  --type-ui-sm-weight: var(--weight-medium);
  --type-ui-sm-leading: var(--leading-snug);
  --type-ui-sm-tracking: var(--tracking-normal);
  --type-ui-sm-transform: none;
  --type-ui-sm-variant-numeric: normal;
  --type-label-font: var(--font-ui);
  --type-label-size: var(--text-xs);
  --type-label-weight: var(--weight-medium);
  --type-label-leading: var(--leading-snug);
  --type-label-tracking: var(--tracking-wide);
  --type-label-transform: uppercase;
  --type-label-variant-numeric: normal;
  --type-label-sm-font: var(--font-ui);
  --type-label-sm-size: var(--text-3xs);
  --type-label-sm-weight: var(--weight-semibold);
  --type-label-sm-leading: var(--leading-none);
  --type-label-sm-tracking: var(--tracking-widest);
  --type-label-sm-transform: uppercase;
  --type-label-sm-variant-numeric: normal;
  --type-mono-font: var(--font-mono);
  --type-mono-size: var(--text-sm);
  --type-mono-weight: var(--weight-regular);
  --type-mono-leading: var(--leading-snug);
  --type-mono-tracking: var(--tracking-normal);
  --type-mono-transform: none;
  --type-mono-variant-numeric: tabular-nums;
  --type-mono-sm-font: var(--font-mono);
  --type-mono-sm-size: var(--text-xs);
  --type-mono-sm-weight: var(--weight-regular);
  --type-mono-sm-leading: var(--leading-snug);
  --type-mono-sm-tracking: var(--tracking-normal);
  --type-mono-sm-transform: none;
  --type-mono-sm-variant-numeric: tabular-nums;
}

:root {
  --type-heading-xl-size: clamp(var(--text-2xl), 5vw, var(--text-3xl));
  --type-heading-lg-size: clamp(var(--text-xl), 4vw, var(--text-2xl));
}

:root,
[data-density=comfortable] {
  --type-body-size: var(--text-base);
  --type-body-sm-size: var(--text-sm);
  --type-ui-size: var(--text-sm);
  --type-body-leading: var(--leading-normal);
}

[data-density=compact] {
  --type-body-size: var(--text-sm);
  --type-body-sm-size: var(--text-xs);
  --type-ui-size: var(--text-xs);
  --type-body-leading: var(--leading-snug);
}

:root {
  --space-0: 0;
  --space-px: 1px;
  --space-0-5: 0.125rem;
  --space-1: 0.25rem;
  --space-1-5: 0.375rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.25rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-10: 2.5rem;
  --space-12: 3rem;
  --space-16: 4rem;
  --space-20: 5rem;
  --space-24: 6rem;
}

:root,
[data-density=comfortable] {
  /* Inset — padding within components */
  --space-inset-xs: var(--space-2); /* 8px  */
  --space-inset-sm: var(--space-3); /* 12px */
  --space-inset: var(--space-4); /* 16px — default */
  --space-inset-lg: var(--space-5); /* 20px */
  --space-inset-xl: var(--space-6); /* 24px */
  /* --- The 2xs rung (stack + inline) -----------------------------------
     2px is the most-written hardcoded number in the estate: 25 `gap: 2px`
     across the platform's app templates, 25 more inside this design system's
     own src/, and 49 further hardcoded Astra declarations whose value contains
     a 2px. That is not eyeballing — it is ONE shared idiom, the two-line
     name-over-detail stack inside a dense row, written identically everywhere,
     which is the signature of a real shape.

     Yet the raw `--space-0-5` (2px) was referenced in exactly two places in
     the whole system, both inside the [data-density="compact"] block below,
     which no page in the estate can currently reach. So 2px had no semantic
     name at all, and every sweep faced a false choice: double all ~50 gaps to
     `stack-xs` (4px) and change how dense rows look, or re-commit to the raw
     scale and keep spelling rhythm as measurement. This rung removes the
     choice. It is the bottom of the ladder, not a new step on the raw scale —
     the raw scale already had 2px and is not changed.

     REJECTED here, deliberately; do not reintroduce either:
       · a 6px semantic rung. When this was written `--space-1-5` (6px) was
         referenced 0 times in Astra. The W6 sweep since gave it ~10 uses, and
         they STRENGTHEN the rejection rather than reopening it: every one is
         RAW measurement — astra-badge's sm end-caps, the scrub-bubble chips in
         astra-audio/-slider/-video, astra-calendar's event bar, the nav row
         inset. Not one is rhythm between siblings, which is the only thing a
         semantic rung would be for. 6px is a control-geometry number, and it
         already has the right home.
       · re-basing `--space-inline-xs` from 4px to 6px. The ~9 near-6px values
         that suggest it (.3rem/.35rem/.4rem) are all one thing, the
         checkbox/radio-to-label gap — and astra-checkbox and astra-radio
         already own that gap at `--space-inline-sm` (8px). Re-basing would
         silently move astra-badge's internal gap, which is correctly 4px.
     -------------------------------------------------------------------- */
  /* Stack — vertical rhythm between siblings */
  --space-stack-2xs: var(--space-0-5); /* 2px — see the note above */
  --space-stack-xs: var(--space-1); /* 4px  */
  --space-stack-sm: var(--space-2); /* 8px  */
  --space-stack: var(--space-3); /* 12px — default */
  --space-stack-lg: var(--space-4); /* 16px */
  --space-stack-xl: var(--space-6); /* 24px */
  --space-stack-2xl: var(--space-8); /* 32px */
  /* Inline — horizontal rhythm between siblings */
  --space-inline-2xs: var(--space-0-5); /* 2px — see the note above */
  --space-inline-xs: var(--space-1); /* 4px  */
  --space-inline-sm: var(--space-2); /* 8px  */
  --space-inline: var(--space-3); /* 12px — default */
  --space-inline-lg: var(--space-4); /* 16px */
  /* Layout — major page-level rhythm */
  --space-layout-sm: var(--space-6); /* 24px */
  --space-layout: var(--space-8); /* 32px — default */
  --space-layout-lg: var(--space-12); /* 48px */
  --space-layout-xl: var(--space-16); /* 64px */
  /* Gutter — page edge padding. The three RUNGS; `--gutter` itself is
     derived from them by width in section 4 below. */
  --gutter-sm: var(--space-4); /* 16px — phone  */
  --gutter-md: var(--space-6); /* 24px — tablet / narrow desktop */
  --gutter-lg: var(--space-8); /* 32px — wide desktop */
}

[data-density=compact] {
  /* Inset */
  --space-inset-xs: var(--space-1); /* 8  → 4  */
  --space-inset-sm: var(--space-2); /* 12 → 8  */
  --space-inset: var(--space-3); /* 16 → 12 */
  --space-inset-lg: var(--space-4); /* 20 → 16 */
  --space-inset-xl: var(--space-5); /* 24 → 20 */
  /* Stack — 2xs steps to the 1px rung rather than colliding with xs, which
     is itself 2px here; the ladder stays strictly ordered in both densities
     and 2xs is never the wider of the two. */
  --space-stack-2xs: var(--space-px); /* 2  → 1  */
  --space-stack-xs: var(--space-0-5); /* 4  → 2  */
  --space-stack-sm: var(--space-1); /* 8  → 4  */
  --space-stack: var(--space-2); /* 12 → 8  */
  --space-stack-lg: var(--space-3); /* 16 → 12 */
  --space-stack-xl: var(--space-4); /* 24 → 16 */
  --space-stack-2xl: var(--space-6); /* 32 → 24 */
  /* Inline */
  --space-inline-2xs: var(--space-px); /* 2  → 1  */
  --space-inline-xs: var(--space-0-5); /* 4  → 2  */
  --space-inline-sm: var(--space-1); /* 8  → 4  */
  --space-inline: var(--space-2); /* 12 → 8  */
  --space-inline-lg: var(--space-3); /* 16 → 12 */
  /* Layout */
  --space-layout-sm: var(--space-4); /* 24 → 16 */
  --space-layout: var(--space-6); /* 32 → 24 */
  --space-layout-lg: var(--space-8); /* 48 → 32 */
  --space-layout-xl: var(--space-12); /* 64 → 48 */
  /* Gutter — the rungs move; the derived `--gutter` follows them for free */
  --gutter-sm: var(--space-3); /* 16 → 12 */
  --gutter-md: var(--space-4); /* 24 → 16 */
  --gutter-lg: var(--space-6); /* 32 → 24 */
}

:root,
[data-density] {
  --gutter: var(--gutter-sm);
}

@media (min-width: 768px) {
  :root,
  [data-density] {
    --gutter: var(--gutter-md);
  }
}
@media (min-width: 1280px) {
  :root,
  [data-density] {
    --gutter: var(--gutter-lg);
  }
}
:root {
  --radius-none: 0;
  --radius-xs: 0.125rem;
  --radius-sm: 0.25rem;
  --radius-md: 0.5rem;
  --radius-lg: 0.75rem;
  --radius-xl: 1rem;
  --radius-2xl: 1.25rem;
  --radius-3xl: 1.75rem;
  --radius-full: 9999px;
  --radius-control: var(--radius-md); /* buttons, inputs, selects   */
  --radius-card: var(--radius-xl); /* default cards              */
  --radius-surface: var(--radius-2xl); /* modals, large panels       */
  --radius-pill: var(--radius-full); /* badges, avatars, capsules  */
}

:root {
  --elevation-0: none;
  --elevation-1: 0 1px 2px rgba(0, 0, 0, 0.04);
  --elevation-2: 0 1px 2px rgba(0, 0, 0, 0.06),
                 0 2px 8px -2px rgba(0, 0, 0, 0.08);
  --elevation-3: 0 2px 4px rgba(0, 0, 0, 0.06),
                 0 8px 24px -4px rgba(0, 0, 0, 0.12);
  --elevation-4: 0 4px 8px rgba(0, 0, 0, 0.08),
                 0 16px 48px -8px rgba(0, 0, 0, 0.16);
  --elevation-5: 0 8px 16px rgba(0, 0, 0, 0.10),
                 0 32px 80px -12px rgba(0, 0, 0, 0.24);
  --elevation-pop:
    inset 0 1px 0 rgba(255, 255, 255, 0.10),
    inset 0 -1px 0 rgba(0, 0, 0, 0.20),
    0 1px 2px rgba(0, 0, 0, 0.08),
    0 4px 12px -2px rgba(0, 0, 0, 0.12);
  --elevation-pop-hover:
    inset 0 1px 0 rgba(255, 255, 255, 0.15),
    inset 0 -1px 0 rgba(0, 0, 0, 0.25),
    0 2px 4px rgba(0, 0, 0, 0.10),
    0 8px 24px -4px rgba(0, 0, 0, 0.18);
  --elevation-inner-sm: inset 0 1px 2px rgba(0, 0, 0, 0.04);
  --elevation-inner: inset 0 2px 4px rgba(0, 0, 0, 0.06);
}

:root[data-theme=dark] {
  --elevation-1: 0 1px 2px rgba(0, 0, 0, 0.30);
  --elevation-2: 0 1px 2px rgba(0, 0, 0, 0.40),
                 0 2px 8px -2px rgba(0, 0, 0, 0.50);
  --elevation-3: 0 2px 4px rgba(0, 0, 0, 0.40),
                 0 8px 24px -4px rgba(0, 0, 0, 0.60);
  --elevation-4: 0 4px 8px rgba(0, 0, 0, 0.50),
                 0 16px 48px -8px rgba(0, 0, 0, 0.70);
  --elevation-5: 0 8px 16px rgba(0, 0, 0, 0.60),
                 0 32px 80px -12px rgba(0, 0, 0, 0.80);
  --elevation-pop:
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    inset 0 -1px 0 rgba(0, 0, 0, 0.40),
    0 1px 2px rgba(0, 0, 0, 0.30),
    0 4px 12px -2px rgba(0, 0, 0, 0.40);
  --elevation-pop-hover:
    inset 0 1px 0 rgba(255, 255, 255, 0.12),
    inset 0 -1px 0 rgba(0, 0, 0, 0.50),
    0 2px 4px rgba(0, 0, 0, 0.40),
    0 8px 24px -4px rgba(0, 0, 0, 0.50);
  --elevation-inner-sm: inset 0 1px 2px rgba(0, 0, 0, 0.30);
  --elevation-inner: inset 0 2px 4px rgba(0, 0, 0, 0.40);
}

:root {
  --motion-instant: 0ms;
  --motion-fast: 100ms;
  --motion-default: 150ms;
  --motion-moderate: 220ms;
  --motion-slow: 380ms;
  --motion-deliberate: 600ms;
  --ease-linear: cubic-bezier(0, 0, 1, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in: cubic-bezier(0.7, 0, 0.84, 0);
  --ease-in-out: cubic-bezier(0.76, 0, 0.24, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --ease-anticipate: cubic-bezier(0.68, -0.4, 0.32, 1.4);
  --motion-hover: var(--motion-fast) var(--ease-out);
  --motion-focus: var(--motion-fast) var(--ease-out);
  --motion-press: var(--motion-instant);
  --motion-enter: var(--motion-moderate) var(--ease-spring);
  --motion-exit: var(--motion-default) var(--ease-in);
  --motion-transform: var(--motion-default) var(--ease-out);
  --motion-theme: var(--motion-slow) var(--ease-in-out);
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --motion-fast: 0ms;
    --motion-default: 0ms;
    --motion-moderate: 0ms;
    --motion-slow: 0ms;
    --motion-deliberate: 0ms;
  }
}
:root {
  --z-base: 0;
  --z-raised: 1;
  --z-sticky: 100;
  --z-dropdown: 200;
  --z-overlay: 300;
  --z-drawer: 400;
  --z-modal: 500;
  --z-popover: 600;
  --z-toast: 700;
  --z-tooltip: 800;
  --z-debug: 9999;
}

:root {
  --breakpoint-sm: 480px;
  --breakpoint-md: 768px;
  --breakpoint-lg: 1024px;
  --breakpoint-xl: 1280px;
  --breakpoint-2xl: 1536px;
}

:root,
:root[data-theme=light] {
  color-scheme: light;
  --surface-page: var(--neutral-50);
  --surface-raised: var(--neutral-0);
  --surface-sunken: var(--neutral-100);
  --surface-overlay: var(--neutral-0);
  --surface-selected: color-mix(in srgb, var(--accent-500) 8%, var(--surface-sunken));
  --border-selected: var(--accent-500);
  --border-subtle: var(--neutral-200);
  --border-default: var(--neutral-300);
  --border-strong: var(--neutral-400);
  --text-primary: var(--neutral-800);
  --text-secondary: var(--neutral-600);
  --text-tertiary: var(--neutral-550);
  --text-placeholder: var(--neutral-550);
  --text-disabled: var(--neutral-400);
  --text-inverse: var(--neutral-50);
  --scrollbar-thumb: color-mix(in oklch, var(--accent-500) 50%, transparent);
  --scrollbar-thumb-hover: color-mix(in oklch, var(--accent-600) 80%, transparent);
}

:root[data-theme=dark] {
  color-scheme: dark;
  --surface-page: var(--neutral-900);
  --surface-raised: var(--neutral-800);
  --surface-sunken: color-mix(in oklch, var(--neutral-800) 25%, var(--neutral-900));
  --surface-overlay: var(--neutral-800);
  --surface-selected: color-mix(in srgb, var(--accent-500) 16%,
                        color-mix(in srgb, var(--neutral-700) 55%, var(--surface-raised)));
  --border-selected: var(--accent-500);
  --border-subtle: color-mix(in oklch, white 8%, transparent);
  --border-default: color-mix(in oklch, white 14%, transparent);
  --border-strong: color-mix(in oklch, white 26%, transparent);
  --text-primary: var(--neutral-100);
  --text-secondary: var(--neutral-400);
  --text-tertiary: var(--neutral-450);
  --text-placeholder: var(--neutral-450);
  --text-disabled: var(--neutral-600);
  --text-inverse: var(--neutral-900);
  --scrollbar-thumb: color-mix(in oklch, var(--accent-400) 60%, transparent);
  --scrollbar-thumb-hover: color-mix(in oklch, var(--accent-400) 90%, transparent);
  --success-50: color-mix(in srgb, var(--success-500) 18%, var(--surface-raised));
  --success-100: color-mix(in srgb, var(--success-500) 28%, var(--surface-raised));
  --success-700: oklch(78% 0.15 155);
  --warning-50: color-mix(in srgb, var(--warning-500) 18%, var(--surface-raised));
  --warning-100: color-mix(in srgb, var(--warning-500) 28%, var(--surface-raised));
  --warning-700: oklch(80% 0.14 85);
  --danger-50: color-mix(in srgb, var(--danger-500) 18%, var(--surface-raised));
  --danger-100: color-mix(in srgb, var(--danger-500) 28%, var(--surface-raised));
  --danger-700: oklch(78% 0.16 25);
  --info-50: color-mix(in srgb, var(--info-500) 18%, var(--surface-raised));
  --info-100: color-mix(in srgb, var(--info-500) 28%, var(--surface-raised));
  --info-700: oklch(78% 0.13 245);
}

:where(:root), [data-group=admin] {
  --text-on-accent: var(--neutral-0);
  --accent-50: var(--accent-admin-50);
  --accent-100: var(--accent-admin-100);
  --accent-200: var(--accent-admin-200);
  --accent-400: var(--accent-admin-400);
  --accent-500: var(--accent-admin-500);
  --accent-600: var(--accent-admin-600);
  --accent-700: var(--accent-admin-700);
  --accent-900: var(--accent-admin-900);
  --fill-accent: var(--accent-admin-500);
  --fill-accent-shade: color-mix(in oklch, var(--accent-admin-500) 50%, var(--accent-admin-600));
  --fill-accent-hover: var(--accent-admin-600);
  --fill-accent-active: var(--accent-admin-700);
  --accent-ink-on-light: var(--accent-admin-600);
  --accent-ink-on-dark: var(--accent-admin-400);
  --accent-ink: var(--accent-ink-on-light);
  --accent-ink-on-tint: color-mix(in oklch, var(--accent-ink) 80%, var(--text-primary));
}

:where(:root[data-theme=dark]), :root[data-theme=dark] [data-group=admin], :root[data-theme=dark][data-group=admin] {
  --accent-ink: var(--accent-ink-on-dark);
}

[data-group=partner] {
  --text-on-accent: var(--neutral-0);
  --accent-50: var(--accent-partner-50);
  --accent-100: var(--accent-partner-100);
  --accent-200: var(--accent-partner-200);
  --accent-400: var(--accent-partner-400);
  --accent-500: var(--accent-partner-500);
  --accent-600: var(--accent-partner-600);
  --accent-700: var(--accent-partner-700);
  --accent-900: var(--accent-partner-900);
  --fill-accent: var(--accent-partner-500);
  --fill-accent-shade: color-mix(in oklch, var(--accent-partner-500) 50%, var(--accent-partner-600));
  --fill-accent-hover: var(--accent-partner-600);
  --fill-accent-active: var(--accent-partner-700);
  --accent-ink-on-light: var(--accent-partner-600);
  --accent-ink-on-dark: var(--accent-partner-400);
  --accent-ink: var(--accent-ink-on-light);
  --accent-ink-on-tint: color-mix(in oklch, var(--accent-ink) 80%, var(--text-primary));
}

:root[data-theme=dark] [data-group=partner], :root[data-theme=dark][data-group=partner] {
  --accent-ink: var(--accent-ink-on-dark);
}

[data-group=identity] {
  --text-on-accent: var(--neutral-0);
  --accent-50: var(--accent-identity-50);
  --accent-100: var(--accent-identity-100);
  --accent-200: var(--accent-identity-200);
  --accent-400: var(--accent-identity-400);
  --accent-500: var(--accent-identity-500);
  --accent-600: var(--accent-identity-600);
  --accent-700: var(--accent-identity-700);
  --accent-900: var(--accent-identity-900);
  --fill-accent: var(--accent-identity-500);
  --fill-accent-shade: color-mix(in oklch, var(--accent-identity-500) 50%, var(--accent-identity-600));
  --fill-accent-hover: var(--accent-identity-600);
  --fill-accent-active: var(--accent-identity-700);
  --accent-ink-on-light: var(--accent-identity-600);
  --accent-ink-on-dark: var(--accent-identity-400);
  --accent-ink: var(--accent-ink-on-light);
  --accent-ink-on-tint: color-mix(in oklch, var(--accent-ink) 80%, var(--text-primary));
}

:root[data-theme=dark] [data-group=identity], :root[data-theme=dark][data-group=identity] {
  --accent-ink: var(--accent-ink-on-dark);
}

[data-group=invoice] {
  --text-on-accent: var(--neutral-0);
  --accent-50: var(--accent-invoice-50);
  --accent-100: var(--accent-invoice-100);
  --accent-200: var(--accent-invoice-200);
  --accent-400: var(--accent-invoice-400);
  --accent-500: var(--accent-invoice-500);
  --accent-600: var(--accent-invoice-600);
  --accent-700: var(--accent-invoice-700);
  --accent-900: var(--accent-invoice-900);
  --fill-accent: var(--accent-invoice-500);
  --fill-accent-shade: color-mix(in oklch, var(--accent-invoice-500) 50%, var(--accent-invoice-600));
  --fill-accent-hover: var(--accent-invoice-600);
  --fill-accent-active: var(--accent-invoice-700);
  --accent-ink-on-light: var(--accent-invoice-600);
  --accent-ink-on-dark: var(--accent-invoice-400);
  --accent-ink: var(--accent-ink-on-light);
  --accent-ink-on-tint: color-mix(in oklch, var(--accent-ink) 80%, var(--text-primary));
}

:root[data-theme=dark] [data-group=invoice], :root[data-theme=dark][data-group=invoice] {
  --accent-ink: var(--accent-ink-on-dark);
}

[data-group=ninefive] {
  --text-on-accent: var(--neutral-0);
  --accent-50: var(--accent-ninefive-50);
  --accent-100: var(--accent-ninefive-100);
  --accent-200: var(--accent-ninefive-200);
  --accent-400: var(--accent-ninefive-400);
  --accent-500: var(--accent-ninefive-500);
  --accent-600: var(--accent-ninefive-600);
  --accent-700: var(--accent-ninefive-700);
  --accent-900: var(--accent-ninefive-900);
  --fill-accent: var(--accent-ninefive-500);
  --fill-accent-shade: color-mix(in oklch, var(--accent-ninefive-500) 50%, var(--accent-ninefive-600));
  --fill-accent-hover: var(--accent-ninefive-600);
  --fill-accent-active: var(--accent-ninefive-700);
  --accent-ink-on-light: var(--accent-ninefive-600);
  --accent-ink-on-dark: var(--accent-ninefive-400);
  --accent-ink: var(--accent-ink-on-light);
  --accent-ink-on-tint: color-mix(in oklch, var(--accent-ink) 80%, var(--text-primary));
}

:root[data-theme=dark] [data-group=ninefive], :root[data-theme=dark][data-group=ninefive] {
  --accent-ink: var(--accent-ink-on-dark);
}

@font-face {
  font-family: "Instrument Serif";
  src: url("fonts/instrument-serif-regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Instrument Serif";
  src: url("fonts/instrument-serif-italic.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("fonts/inter-regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("fonts/inter-medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("fonts/inter-semibold.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("fonts/inter-bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("fonts/inter-italic.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("fonts/jetbrains-mono-regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("fonts/jetbrains-mono-medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
* {
  scrollbar-width: thin;
  scrollbar-color: transparent transparent;
  transition: scrollbar-color var(--motion-hover);
}

*:hover,
*:focus-within {
  scrollbar-color: var(--scrollbar-thumb) transparent;
}

*::-webkit-scrollbar {
  -webkit-appearance: none;
  appearance: none;
  width: 6px;
  height: 6px;
}

*::-webkit-scrollbar-track {
  background: transparent;
}

*::-webkit-scrollbar-thumb {
  background: transparent;
  border-radius: var(--radius-full);
  transition: background-color var(--motion-hover);
}

*:hover::-webkit-scrollbar-thumb,
*:focus-within::-webkit-scrollbar-thumb {
  background: var(--scrollbar-thumb);
}

*::-webkit-scrollbar-thumb:hover {
  background: var(--scrollbar-thumb-hover);
}

*::-webkit-scrollbar-corner {
  background: transparent;
}

astra-input,
astra-select,
astra-textarea,
astra-combobox,
astra-number-input,
astra-tag-input,
astra-otp-input,
astra-file-upload,
astra-media-picker {
  min-width: 0;
  max-width: 100%;
}

astra-button {
  max-width: 100%;
}

img,
video {
  max-width: 100%;
}

a {
  color: var(--accent-ink);
  font-weight: var(--weight-medium);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

a:focus-visible {
  outline: 2px solid var(--accent-500);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}

[hidden] {
  display: none !important;
}

html {
  font-size: 100%;
  font-family: var(--font-ui);
  font-feature-settings: "cv11", "ss01", "cv02", "cv03", "cv04";
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

body {
  font-family: var(--type-body-font);
  font-size: var(--type-body-size);
  font-weight: var(--type-body-weight);
  line-height: var(--type-body-leading);
  letter-spacing: var(--type-body-tracking);
  text-transform: var(--type-body-transform);
  font-variant-numeric: var(--type-body-variant-numeric);
  color: var(--text-primary);
  background: var(--surface-page);
  margin: 0;
}

h1, h2, h3, h4, h5, h6 {
  margin: 0;
  color: var(--text-primary);
}

h1 {
  font-family: var(--type-heading-xl-font);
  font-size: var(--type-heading-xl-size);
  font-weight: var(--type-heading-xl-weight);
  line-height: var(--type-heading-xl-leading);
  letter-spacing: var(--type-heading-xl-tracking);
  text-transform: var(--type-heading-xl-transform);
  font-variant-numeric: var(--type-heading-xl-variant-numeric);
}

h2 {
  font-family: var(--type-heading-lg-font);
  font-size: var(--type-heading-lg-size);
  font-weight: var(--type-heading-lg-weight);
  line-height: var(--type-heading-lg-leading);
  letter-spacing: var(--type-heading-lg-tracking);
  text-transform: var(--type-heading-lg-transform);
  font-variant-numeric: var(--type-heading-lg-variant-numeric);
}

h3 {
  font-family: var(--type-heading-md-font);
  font-size: var(--type-heading-md-size);
  font-weight: var(--type-heading-md-weight);
  line-height: var(--type-heading-md-leading);
  letter-spacing: var(--type-heading-md-tracking);
  text-transform: var(--type-heading-md-transform);
  font-variant-numeric: var(--type-heading-md-variant-numeric);
}

h4 {
  font-family: var(--type-heading-sm-font);
  font-size: var(--type-heading-sm-size);
  font-weight: var(--type-heading-sm-weight);
  line-height: var(--type-heading-sm-leading);
  letter-spacing: var(--type-heading-sm-tracking);
  text-transform: var(--type-heading-sm-transform);
  font-variant-numeric: var(--type-heading-sm-variant-numeric);
}

h5 {
  font-family: var(--type-body-lg-font);
  font-size: var(--type-body-lg-size);
  font-weight: var(--type-body-lg-weight);
  line-height: var(--type-body-lg-leading);
  letter-spacing: var(--type-body-lg-tracking);
  text-transform: var(--type-body-lg-transform);
  font-variant-numeric: var(--type-body-lg-variant-numeric);
  font-weight: var(--weight-semibold);
}

h6 {
  font-family: var(--type-ui-font);
  font-size: var(--type-ui-size);
  font-weight: var(--type-ui-weight);
  line-height: var(--type-ui-leading);
  letter-spacing: var(--type-ui-tracking);
  text-transform: var(--type-ui-transform);
  font-variant-numeric: var(--type-ui-variant-numeric);
}

p {
  font-family: var(--type-body-font);
  font-size: var(--type-body-size);
  font-weight: var(--type-body-weight);
  line-height: var(--type-body-leading);
  letter-spacing: var(--type-body-tracking);
  text-transform: var(--type-body-transform);
  font-variant-numeric: var(--type-body-variant-numeric);
  margin: 0;
}

code, pre, kbd, samp {
  font-family: var(--type-mono-font);
  font-size: var(--type-mono-size);
  font-weight: var(--type-mono-weight);
  line-height: var(--type-mono-leading);
  letter-spacing: var(--type-mono-tracking);
  text-transform: var(--type-mono-transform);
  font-variant-numeric: var(--type-mono-variant-numeric);
}

em, i {
  font-style: italic;
}

strong, b {
  font-weight: var(--weight-semibold);
}

small {
  font-family: var(--type-body-sm-font);
  font-size: var(--type-body-sm-size);
  font-weight: var(--type-body-sm-weight);
  line-height: var(--type-body-sm-leading);
  letter-spacing: var(--type-body-sm-tracking);
  text-transform: var(--type-body-sm-transform);
  font-variant-numeric: var(--type-body-sm-variant-numeric);
  color: var(--text-secondary);
}

::selection {
  background: var(--accent-200);
  color: var(--text-primary);
}

.print-only {
  display: none;
}

@media print {
  :root,
  :root[data-theme=dark],
  :root[data-theme=light] {
    color-scheme: light;
    --surface-page: var(--neutral-0);
    --surface-raised: var(--neutral-0);
    --surface-sunken: var(--neutral-100);
    --surface-overlay: var(--neutral-0);
    --border-subtle: var(--neutral-200);
    --border-default: var(--neutral-300);
    --border-strong: var(--neutral-400);
    --text-primary: var(--neutral-900);
    --text-secondary: var(--neutral-700);
    --text-tertiary: var(--neutral-600);
    --text-placeholder: var(--neutral-500);
    --success-50: oklch(97% 0.04 155);
    --success-100: oklch(92% 0.08 155);
    --success-700: oklch(48% 0.17 155);
    --warning-50: oklch(97% 0.04 85);
    --warning-100: oklch(92% 0.08 85);
    --warning-700: oklch(48% 0.14 85);
    --danger-50: oklch(97% 0.04 25);
    --danger-100: oklch(92% 0.08 25);
    --danger-700: oklch(48% 0.20 25);
    --info-50: oklch(97% 0.04 245);
    --info-100: oklch(92% 0.08 245);
    --info-700: oklch(48% 0.15 245);
    --accent-ink: var(--accent-ink-on-light);
  }
  html, body {
    background: #fff !important;
    color: var(--neutral-900) !important;
    margin: 0 !important;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  *, *::before, *::after {
    box-shadow: none !important;
    text-shadow: none !important;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  .no-print,
  .screen-only,
  [data-print=hide] {
    display: none !important;
  }
  .print-only {
    display: revert !important;
  }
  h1, h2, h3, h4 {
    break-after: avoid;
  }
  img, svg {
    max-width: 100% !important;
  }
  .page-break-before {
    break-before: page;
  }
  .page-break-after {
    break-after: page;
  }
  .print-avoid-break {
    break-inside: avoid;
  }
}
@page {
  margin: 18mm;
}
.p-0 {
  padding: var(--space-0);
}

.px-0 {
  padding-inline: var(--space-0);
}

.py-0 {
  padding-block: var(--space-0);
}

.pt-0 {
  padding-block-start: var(--space-0);
}

.pr-0 {
  padding-inline-end: var(--space-0);
}

.pb-0 {
  padding-block-end: var(--space-0);
}

.pl-0 {
  padding-inline-start: var(--space-0);
}

.m-0 {
  margin: var(--space-0);
}

.mx-0 {
  margin-inline: var(--space-0);
}

.my-0 {
  margin-block: var(--space-0);
}

.mt-0 {
  margin-block-start: var(--space-0);
}

.mr-0 {
  margin-inline-end: var(--space-0);
}

.mb-0 {
  margin-block-end: var(--space-0);
}

.ml-0 {
  margin-inline-start: var(--space-0);
}

.gap-0 {
  gap: var(--space-0);
}

.gap-x-0 {
  column-gap: var(--space-0);
}

.gap-y-0 {
  row-gap: var(--space-0);
}

.p-px {
  padding: var(--space-px);
}

.px-px {
  padding-inline: var(--space-px);
}

.py-px {
  padding-block: var(--space-px);
}

.pt-px {
  padding-block-start: var(--space-px);
}

.pr-px {
  padding-inline-end: var(--space-px);
}

.pb-px {
  padding-block-end: var(--space-px);
}

.pl-px {
  padding-inline-start: var(--space-px);
}

.m-px {
  margin: var(--space-px);
}

.mx-px {
  margin-inline: var(--space-px);
}

.my-px {
  margin-block: var(--space-px);
}

.mt-px {
  margin-block-start: var(--space-px);
}

.mr-px {
  margin-inline-end: var(--space-px);
}

.mb-px {
  margin-block-end: var(--space-px);
}

.ml-px {
  margin-inline-start: var(--space-px);
}

.gap-px {
  gap: var(--space-px);
}

.gap-x-px {
  column-gap: var(--space-px);
}

.gap-y-px {
  row-gap: var(--space-px);
}

.p-0-5 {
  padding: var(--space-0-5);
}

.px-0-5 {
  padding-inline: var(--space-0-5);
}

.py-0-5 {
  padding-block: var(--space-0-5);
}

.pt-0-5 {
  padding-block-start: var(--space-0-5);
}

.pr-0-5 {
  padding-inline-end: var(--space-0-5);
}

.pb-0-5 {
  padding-block-end: var(--space-0-5);
}

.pl-0-5 {
  padding-inline-start: var(--space-0-5);
}

.m-0-5 {
  margin: var(--space-0-5);
}

.mx-0-5 {
  margin-inline: var(--space-0-5);
}

.my-0-5 {
  margin-block: var(--space-0-5);
}

.mt-0-5 {
  margin-block-start: var(--space-0-5);
}

.mr-0-5 {
  margin-inline-end: var(--space-0-5);
}

.mb-0-5 {
  margin-block-end: var(--space-0-5);
}

.ml-0-5 {
  margin-inline-start: var(--space-0-5);
}

.gap-0-5 {
  gap: var(--space-0-5);
}

.gap-x-0-5 {
  column-gap: var(--space-0-5);
}

.gap-y-0-5 {
  row-gap: var(--space-0-5);
}

.p-1 {
  padding: var(--space-1);
}

.px-1 {
  padding-inline: var(--space-1);
}

.py-1 {
  padding-block: var(--space-1);
}

.pt-1 {
  padding-block-start: var(--space-1);
}

.pr-1 {
  padding-inline-end: var(--space-1);
}

.pb-1 {
  padding-block-end: var(--space-1);
}

.pl-1 {
  padding-inline-start: var(--space-1);
}

.m-1 {
  margin: var(--space-1);
}

.mx-1 {
  margin-inline: var(--space-1);
}

.my-1 {
  margin-block: var(--space-1);
}

.mt-1 {
  margin-block-start: var(--space-1);
}

.mr-1 {
  margin-inline-end: var(--space-1);
}

.mb-1 {
  margin-block-end: var(--space-1);
}

.ml-1 {
  margin-inline-start: var(--space-1);
}

.gap-1 {
  gap: var(--space-1);
}

.gap-x-1 {
  column-gap: var(--space-1);
}

.gap-y-1 {
  row-gap: var(--space-1);
}

.p-1-5 {
  padding: var(--space-1-5);
}

.px-1-5 {
  padding-inline: var(--space-1-5);
}

.py-1-5 {
  padding-block: var(--space-1-5);
}

.pt-1-5 {
  padding-block-start: var(--space-1-5);
}

.pr-1-5 {
  padding-inline-end: var(--space-1-5);
}

.pb-1-5 {
  padding-block-end: var(--space-1-5);
}

.pl-1-5 {
  padding-inline-start: var(--space-1-5);
}

.m-1-5 {
  margin: var(--space-1-5);
}

.mx-1-5 {
  margin-inline: var(--space-1-5);
}

.my-1-5 {
  margin-block: var(--space-1-5);
}

.mt-1-5 {
  margin-block-start: var(--space-1-5);
}

.mr-1-5 {
  margin-inline-end: var(--space-1-5);
}

.mb-1-5 {
  margin-block-end: var(--space-1-5);
}

.ml-1-5 {
  margin-inline-start: var(--space-1-5);
}

.gap-1-5 {
  gap: var(--space-1-5);
}

.gap-x-1-5 {
  column-gap: var(--space-1-5);
}

.gap-y-1-5 {
  row-gap: var(--space-1-5);
}

.p-2 {
  padding: var(--space-2);
}

.px-2 {
  padding-inline: var(--space-2);
}

.py-2 {
  padding-block: var(--space-2);
}

.pt-2 {
  padding-block-start: var(--space-2);
}

.pr-2 {
  padding-inline-end: var(--space-2);
}

.pb-2 {
  padding-block-end: var(--space-2);
}

.pl-2 {
  padding-inline-start: var(--space-2);
}

.m-2 {
  margin: var(--space-2);
}

.mx-2 {
  margin-inline: var(--space-2);
}

.my-2 {
  margin-block: var(--space-2);
}

.mt-2 {
  margin-block-start: var(--space-2);
}

.mr-2 {
  margin-inline-end: var(--space-2);
}

.mb-2 {
  margin-block-end: var(--space-2);
}

.ml-2 {
  margin-inline-start: var(--space-2);
}

.gap-2 {
  gap: var(--space-2);
}

.gap-x-2 {
  column-gap: var(--space-2);
}

.gap-y-2 {
  row-gap: var(--space-2);
}

.p-3 {
  padding: var(--space-3);
}

.px-3 {
  padding-inline: var(--space-3);
}

.py-3 {
  padding-block: var(--space-3);
}

.pt-3 {
  padding-block-start: var(--space-3);
}

.pr-3 {
  padding-inline-end: var(--space-3);
}

.pb-3 {
  padding-block-end: var(--space-3);
}

.pl-3 {
  padding-inline-start: var(--space-3);
}

.m-3 {
  margin: var(--space-3);
}

.mx-3 {
  margin-inline: var(--space-3);
}

.my-3 {
  margin-block: var(--space-3);
}

.mt-3 {
  margin-block-start: var(--space-3);
}

.mr-3 {
  margin-inline-end: var(--space-3);
}

.mb-3 {
  margin-block-end: var(--space-3);
}

.ml-3 {
  margin-inline-start: var(--space-3);
}

.gap-3 {
  gap: var(--space-3);
}

.gap-x-3 {
  column-gap: var(--space-3);
}

.gap-y-3 {
  row-gap: var(--space-3);
}

.p-4 {
  padding: var(--space-4);
}

.px-4 {
  padding-inline: var(--space-4);
}

.py-4 {
  padding-block: var(--space-4);
}

.pt-4 {
  padding-block-start: var(--space-4);
}

.pr-4 {
  padding-inline-end: var(--space-4);
}

.pb-4 {
  padding-block-end: var(--space-4);
}

.pl-4 {
  padding-inline-start: var(--space-4);
}

.m-4 {
  margin: var(--space-4);
}

.mx-4 {
  margin-inline: var(--space-4);
}

.my-4 {
  margin-block: var(--space-4);
}

.mt-4 {
  margin-block-start: var(--space-4);
}

.mr-4 {
  margin-inline-end: var(--space-4);
}

.mb-4 {
  margin-block-end: var(--space-4);
}

.ml-4 {
  margin-inline-start: var(--space-4);
}

.gap-4 {
  gap: var(--space-4);
}

.gap-x-4 {
  column-gap: var(--space-4);
}

.gap-y-4 {
  row-gap: var(--space-4);
}

.p-5 {
  padding: var(--space-5);
}

.px-5 {
  padding-inline: var(--space-5);
}

.py-5 {
  padding-block: var(--space-5);
}

.pt-5 {
  padding-block-start: var(--space-5);
}

.pr-5 {
  padding-inline-end: var(--space-5);
}

.pb-5 {
  padding-block-end: var(--space-5);
}

.pl-5 {
  padding-inline-start: var(--space-5);
}

.m-5 {
  margin: var(--space-5);
}

.mx-5 {
  margin-inline: var(--space-5);
}

.my-5 {
  margin-block: var(--space-5);
}

.mt-5 {
  margin-block-start: var(--space-5);
}

.mr-5 {
  margin-inline-end: var(--space-5);
}

.mb-5 {
  margin-block-end: var(--space-5);
}

.ml-5 {
  margin-inline-start: var(--space-5);
}

.gap-5 {
  gap: var(--space-5);
}

.gap-x-5 {
  column-gap: var(--space-5);
}

.gap-y-5 {
  row-gap: var(--space-5);
}

.p-6 {
  padding: var(--space-6);
}

.px-6 {
  padding-inline: var(--space-6);
}

.py-6 {
  padding-block: var(--space-6);
}

.pt-6 {
  padding-block-start: var(--space-6);
}

.pr-6 {
  padding-inline-end: var(--space-6);
}

.pb-6 {
  padding-block-end: var(--space-6);
}

.pl-6 {
  padding-inline-start: var(--space-6);
}

.m-6 {
  margin: var(--space-6);
}

.mx-6 {
  margin-inline: var(--space-6);
}

.my-6 {
  margin-block: var(--space-6);
}

.mt-6 {
  margin-block-start: var(--space-6);
}

.mr-6 {
  margin-inline-end: var(--space-6);
}

.mb-6 {
  margin-block-end: var(--space-6);
}

.ml-6 {
  margin-inline-start: var(--space-6);
}

.gap-6 {
  gap: var(--space-6);
}

.gap-x-6 {
  column-gap: var(--space-6);
}

.gap-y-6 {
  row-gap: var(--space-6);
}

.p-8 {
  padding: var(--space-8);
}

.px-8 {
  padding-inline: var(--space-8);
}

.py-8 {
  padding-block: var(--space-8);
}

.pt-8 {
  padding-block-start: var(--space-8);
}

.pr-8 {
  padding-inline-end: var(--space-8);
}

.pb-8 {
  padding-block-end: var(--space-8);
}

.pl-8 {
  padding-inline-start: var(--space-8);
}

.m-8 {
  margin: var(--space-8);
}

.mx-8 {
  margin-inline: var(--space-8);
}

.my-8 {
  margin-block: var(--space-8);
}

.mt-8 {
  margin-block-start: var(--space-8);
}

.mr-8 {
  margin-inline-end: var(--space-8);
}

.mb-8 {
  margin-block-end: var(--space-8);
}

.ml-8 {
  margin-inline-start: var(--space-8);
}

.gap-8 {
  gap: var(--space-8);
}

.gap-x-8 {
  column-gap: var(--space-8);
}

.gap-y-8 {
  row-gap: var(--space-8);
}

.p-10 {
  padding: var(--space-10);
}

.px-10 {
  padding-inline: var(--space-10);
}

.py-10 {
  padding-block: var(--space-10);
}

.pt-10 {
  padding-block-start: var(--space-10);
}

.pr-10 {
  padding-inline-end: var(--space-10);
}

.pb-10 {
  padding-block-end: var(--space-10);
}

.pl-10 {
  padding-inline-start: var(--space-10);
}

.m-10 {
  margin: var(--space-10);
}

.mx-10 {
  margin-inline: var(--space-10);
}

.my-10 {
  margin-block: var(--space-10);
}

.mt-10 {
  margin-block-start: var(--space-10);
}

.mr-10 {
  margin-inline-end: var(--space-10);
}

.mb-10 {
  margin-block-end: var(--space-10);
}

.ml-10 {
  margin-inline-start: var(--space-10);
}

.gap-10 {
  gap: var(--space-10);
}

.gap-x-10 {
  column-gap: var(--space-10);
}

.gap-y-10 {
  row-gap: var(--space-10);
}

.p-12 {
  padding: var(--space-12);
}

.px-12 {
  padding-inline: var(--space-12);
}

.py-12 {
  padding-block: var(--space-12);
}

.pt-12 {
  padding-block-start: var(--space-12);
}

.pr-12 {
  padding-inline-end: var(--space-12);
}

.pb-12 {
  padding-block-end: var(--space-12);
}

.pl-12 {
  padding-inline-start: var(--space-12);
}

.m-12 {
  margin: var(--space-12);
}

.mx-12 {
  margin-inline: var(--space-12);
}

.my-12 {
  margin-block: var(--space-12);
}

.mt-12 {
  margin-block-start: var(--space-12);
}

.mr-12 {
  margin-inline-end: var(--space-12);
}

.mb-12 {
  margin-block-end: var(--space-12);
}

.ml-12 {
  margin-inline-start: var(--space-12);
}

.gap-12 {
  gap: var(--space-12);
}

.gap-x-12 {
  column-gap: var(--space-12);
}

.gap-y-12 {
  row-gap: var(--space-12);
}

.p-16 {
  padding: var(--space-16);
}

.px-16 {
  padding-inline: var(--space-16);
}

.py-16 {
  padding-block: var(--space-16);
}

.pt-16 {
  padding-block-start: var(--space-16);
}

.pr-16 {
  padding-inline-end: var(--space-16);
}

.pb-16 {
  padding-block-end: var(--space-16);
}

.pl-16 {
  padding-inline-start: var(--space-16);
}

.m-16 {
  margin: var(--space-16);
}

.mx-16 {
  margin-inline: var(--space-16);
}

.my-16 {
  margin-block: var(--space-16);
}

.mt-16 {
  margin-block-start: var(--space-16);
}

.mr-16 {
  margin-inline-end: var(--space-16);
}

.mb-16 {
  margin-block-end: var(--space-16);
}

.ml-16 {
  margin-inline-start: var(--space-16);
}

.gap-16 {
  gap: var(--space-16);
}

.gap-x-16 {
  column-gap: var(--space-16);
}

.gap-y-16 {
  row-gap: var(--space-16);
}

.p-20 {
  padding: var(--space-20);
}

.px-20 {
  padding-inline: var(--space-20);
}

.py-20 {
  padding-block: var(--space-20);
}

.pt-20 {
  padding-block-start: var(--space-20);
}

.pr-20 {
  padding-inline-end: var(--space-20);
}

.pb-20 {
  padding-block-end: var(--space-20);
}

.pl-20 {
  padding-inline-start: var(--space-20);
}

.m-20 {
  margin: var(--space-20);
}

.mx-20 {
  margin-inline: var(--space-20);
}

.my-20 {
  margin-block: var(--space-20);
}

.mt-20 {
  margin-block-start: var(--space-20);
}

.mr-20 {
  margin-inline-end: var(--space-20);
}

.mb-20 {
  margin-block-end: var(--space-20);
}

.ml-20 {
  margin-inline-start: var(--space-20);
}

.gap-20 {
  gap: var(--space-20);
}

.gap-x-20 {
  column-gap: var(--space-20);
}

.gap-y-20 {
  row-gap: var(--space-20);
}

.p-24 {
  padding: var(--space-24);
}

.px-24 {
  padding-inline: var(--space-24);
}

.py-24 {
  padding-block: var(--space-24);
}

.pt-24 {
  padding-block-start: var(--space-24);
}

.pr-24 {
  padding-inline-end: var(--space-24);
}

.pb-24 {
  padding-block-end: var(--space-24);
}

.pl-24 {
  padding-inline-start: var(--space-24);
}

.m-24 {
  margin: var(--space-24);
}

.mx-24 {
  margin-inline: var(--space-24);
}

.my-24 {
  margin-block: var(--space-24);
}

.mt-24 {
  margin-block-start: var(--space-24);
}

.mr-24 {
  margin-inline-end: var(--space-24);
}

.mb-24 {
  margin-block-end: var(--space-24);
}

.ml-24 {
  margin-inline-start: var(--space-24);
}

.gap-24 {
  gap: var(--space-24);
}

.gap-x-24 {
  column-gap: var(--space-24);
}

.gap-y-24 {
  row-gap: var(--space-24);
}

.m-auto {
  margin: auto;
}

.mx-auto {
  margin-inline: auto;
}

.my-auto {
  margin-block: auto;
}

@media (hover: none) and (pointer: coarse) {
  .shortcut-hint {
    display: none !important;
  }
}
/* ─── SCREEN-READER-ONLY TEXT (`.visually-hidden`) ───────────────────────────
   THE RECIPE IS LOAD-BEARING AND EVERY DECLARATION IN IT IS DOING A JOB.
   This is not a spacing rule that happens to contain a margin. It is a single
   indivisible clip: the box is shrunk to 1px, `overflow` and `clip`/`clip-path`
   crush what is inside it to nothing, and `margin: -1px` pulls that 1px box
   back over its own edge so it cannot contribute a visible pixel of layout.

   `margin: -1px` and `padding: 0` are therefore CLIP GEOMETRY, not rhythm.
   Substituting a spacing token for either one moves the element OUT of its own
   clip rect and paints hidden screen-reader text on the page — a failure that
   shows up as garbage text in front of a customer and that no visual test in
   this estate would catch. A sweep, a linter or a future author must leave
   these two values exactly as they are.

   WHAT ACTUALLY GUARDS THAT, precisely: `tools/ci/check-css-shapes.php` in the
   platform holds all three spellings of this recipe in its `[promoted]` section,
   which is a HARD GATE — a template that re-declares the set fails the build and
   is told to adopt the class. That is protection against the recipe being
   RE-HAND-ROLLED. Nothing anywhere protects the values in THIS file from being
   edited, and no automated sweeper exists that would tokenise them; the
   protection there is this comment and nothing else. Do not read the guard as
   permission to stop being careful here.

   DOCUMENT-SCOPED, AND THAT IS A REAL LIMIT. A class only reaches nodes in the
   document tree. Anything inside a shadow root — a component's internals, and
   also anything the page hands to a component that renders into its own shadow
   root, such as an `astra-table` cell — is untouched by this rule and renders
   FULLY VISIBLE if you give it the class and assume it worked. That single
   constraint is why ~15 Astra components each carry their own copy of the
   recipe rather than using this one, and why `affiliate-payouts.php` reads the
   declarations back out of this rule at runtime and applies them inline. Check
   `el.getRootNode() === document` before trusting the class.

   Both clip spellings ship on purpose. `clip` is the legacy form and is still
   what several engines honour most reliably inside a `position: absolute`
   subtree; `clip-path: inset(50%)` is the modern one and is unaffected by
   `clip` being deprecated. Carrying both is how the recipe stays correct
   whichever one a given engine ignores — and it is what the estate's own
   copies were already split between, six writing `clip` and two `clip-path`.

   NOT focusable-safe by design. A skip link that must APPEAR when tabbed to
   needs the `:not(:focus)` variant, which is a different behaviour and would
   silently un-hide the live regions that make up every current use in the
   estate. If a skip link ever lands, give it its own class rather than
   loosening this one.

   Use it for: `role="status"` live regions announcing a copy-to-clipboard
   receipt, a `<label>` naming a control whose purpose is already visually
   obvious, a heading that structures a page for a screen reader without
   appearing in the design.
   ─────────────────────────────────────────────────────────────────────────── */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ─── ONE AUTHOR PER GAP (`.page-stack`) ────────────────────────────────────
   THE SPACING RULE THIS ESTATE KEPT BREAKING, written down as a class.
   `.page-header` carries `margin-block-end: var(--space-6)` because most pages
   stack it in normal block flow, where a margin is the only way to say "leave
   room under me". But a page whose own root is a flex or grid column ALSO owns
   a `gap` — and then two things are describing the same piece of empty space
   and the answer is their sum, which is never a step of the scale. The payouts
   console is the worked example: `.payouts { gap: var(--space-5) }` (20px) plus
   the header's own 24px put 44px between the heading block and the queue, and
   the owner read it as exactly what it is — "too much padding" (2026-09-04) —
   with no single number anywhere in the source to change.

   A page that wants the stack to own the rhythm says so with this class, and
   the header stops carrying a second opinion. There is deliberately no variant
   for a different gap: a page needing a different rhythm re-points
   `--page-stack-gap` on its own element (see the properties below), and the
   header still contributes nothing.

   UPDATED 2026-09-06: the class used to say "set `gap` on your own element".
   Do not — the section rung is DERIVED from `--page-stack-gap`, so a bare
   `gap` override changes the rhythm without changing the derivation and the
   space above a heading stops being the number that is declared. Re-point the
   property instead; it moves both. */
.page-stack {
  /* THE THREE NUMBERS, PUBLISHED AS PROPERTIES so a page can re-tune its own
     rhythm without writing a margin on anything. Override THESE, never `gap`:
     the section rung is derived from the base one below, and setting `gap`
     directly leaves the derivation reading a value the stack is no longer
     using.

     WHY THESE LADDERS. The base gap is vertical rhythm between siblings
     (`stack`); the section rung is page-level section rhythm (`layout`), which
     is the ladder whose whole job is "how far apart are two sections of a
     page"; the heading rung is again sibling rhythm, one step down, because a
     heading and the thing it titles are siblings that belong together. */
  --page-stack-gap: var(--space-stack-xl); /* 24px — between siblings */
  --page-stack-section-gap: var(--space-layout); /* 32px — above a heading  */
  --page-stack-heading-gap: var(--space-stack); /* 12px — below a heading  */
  display: flex;
  flex-direction: column;
  gap: var(--page-stack-gap);
  min-width: 0;
}

/* ─── THE SAME FAULT ONE LEVEL DOWN: THE STACKED HEADING ────────────────────
   `.page-stack` ended the double gap between a page HEADER and the page. It did
   not end it between a page and its own section headings, and the moment the
   class was rolled out to 86 templates that is where the fault reappeared —
   because a stack's `gap` and a heading's `margin-block-start` are two
   different properties describing one piece of empty space, so the answer is
   their sum and no single number in any file is it.

   MEASURED, /payouts/attention at 390pt: 56px between the page subtitle and the
   first h2 — 8px of the tabs strip's own bottom margin, plus the stack's 24px
   gap, plus the template's own 24px heading margin. Three authors, one gap, and
   the owner's word for the result was that the sections look adrift.

   THE RULE, and it is the whole of it:

     INSIDE A .page-stack, THE STACK DECIDES EVERY VERTICAL GAP. A direct child
     contributes no block margin of its own. A SECTION HEADING (h2-h6) gets
     --page-stack-section-gap above it and --page-stack-heading-gap below it;
     everything else gets --page-stack-gap. To change any of them, change the
     property — do not write a margin.

     Two exemptions, both narrow and both argued below: a bare `h1` (a page
     title, whose rhythm belongs to .page-header) and `.visually-hidden` (whose
     negative margin is clip geometry, not spacing).

   WHY THE CALC, WHICH IS THE PART TO READ BEFORE EDITING. A flex `gap` cannot
   be varied per child, so the heading's own margin is the only way to say "more
   air here". That margin ADDS to the gap — which is the fault — so what is
   written is the DIFFERENCE, and what is DECLARED is the total. Read
   `--page-stack-section-gap: 32px` and the space above a heading is 32px; there
   is one number and it is the true one. The negative below is the same
   arithmetic in the other direction and is safe for the same reason the estate's
   other negative-margin pairings are not: both halves are derived by calc from
   --page-stack-gap in one rule, in one file, so re-tuning the gap moves them
   both and they cannot be edited apart.

   WHY THE CLASS IS DOUBLED. `.attention h2` is (0,1,1) and lives in a page's
   own <style>, which is parsed after core.css — so a normal `.page-stack > h2`
   would tie on specificity and lose on source order, and the rule above would
   be true everywhere except the pages that actually have the fault. (0,2,1)
   wins that comparison, which is what makes the rule a rule rather than a
   default. A page that genuinely wants different spacing has the three
   properties; it does not need to out-specify the system to get it.

   FIRST AND LAST CHILD ARE ZEROED. A heading opening a stack must not push the
   page down by a gap that has nothing above it, and a heading closing one must
   not pull the stack's bottom edge up by the negative.

   `.visually-hidden` IS EXEMPT FROM THE RESET, AND THAT IS THE ONE LINE HERE
   THAT WOULD HAVE BROKEN PRODUCTION SILENTLY. Its `margin: -1px` is not
   rhythm — it is CLIP GEOMETRY, half of an indivisible recipe that pulls a 1px
   box back over its own edge (utilities/_utilities.scss carries the whole
   argument). This reset out-specifies that rule at (0,2,0) against (0,1,0), so
   without the exemption every screen-reader-only span that happens to be a
   direct child of a page stack loses the block half of its clip offset. It is
   already the case on one page in the estate — `portal/shop-order-detail`
   opens its stack with one — and the failure mode is hidden text painting on a
   customer's screen, which no test here would see. `:not(:where(…))` because
   `:where()` contributes ZERO specificity: the exemption must not quietly make
   this rule harder to override than the line above claims it is.

   H1 IS NOT IN THE HEADING SET, DELIBERATELY. A bare `h1` in a stack is a page
   TITLE, and the space under a page title is `.page-header`'s decision (24px,
   the same as the stack's own gap) — not a section break. Treating it as one
   pulled `portal/ads-admin`'s title 12px closer to its content, which is a
   restyle of a page that has no fault, to make a rule tidier. Section headings
   are h2 and below. */
.page-stack.page-stack > *:not(:where(.visually-hidden)) {
  margin-block: 0;
}

.page-stack.page-stack > :is(h2, h3, h4, h5, h6) {
  margin-block-start: calc(var(--page-stack-section-gap) - var(--page-stack-gap));
  margin-block-end: calc(var(--page-stack-heading-gap) - var(--page-stack-gap));
}

.page-stack.page-stack > :is(h2, h3, h4, h5, h6):first-child {
  margin-block-start: 0;
}

.page-stack.page-stack > :is(h2, h3, h4, h5, h6):last-child {
  margin-block-end: 0;
}

.page-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-4);
  margin-block-end: var(--space-6);
  /* Do NOT wrap by default. Actions stay anchored to the right at all
     tablet+ widths; the primary section shrinks to make room and the
     title truncates with an ellipsis if necessary. Mobile gets a
     dedicated rule below that restructures the header. */
}

.page-header__primary {
  flex: 1;
  min-width: 0;
}

/* ─── THE SUBTITLE UNDER THE TITLE ──────────────────────────────────────────
   `.page-header__subtitle` was MARKUP WITH NO RULE BEHIND IT. This file has
   documented it in the pattern's own example since the pattern was extracted
   (see the markup sketch above), four payouts templates write it, and nothing
   anywhere in Astra ever styled it — so it inherited `p { margin: 0 }` from
   base/_typography.scss and rendered at body size in `--text-primary`, flush
   against the heading. Measured on the rendered payouts console: a 0px gap at
   390pt AND at 1280px, at 15px, at the h1's own colour. On a phone, where the
   h1 wraps to two lines, that is a subtitle indistinguishable from a third line
   of the title — which is exactly what the owner reported (2026-09-05: "the 'JW
   GROUP LIMITED · one company's books at a time' text also seems too close to
   the text above it").

   THE VALUES ARE NOT INVENTED — THEY ARE THE ESTATE'S, ADOPTED. Sixteen
   templates across two portals independently wrote the same subtitle rule under
   sixteen local names (`.overview__scope`, `.rail__scope`, `.setup__scope`,
   `.content-item__sub`, `.guide__lede`, `.prog__scope`, …), fourteen of them
   byte for byte: `margin: var(--space-1) 0 0; color: var(--text-secondary);
   font-size: var(--text-sm)`. That is a decision the estate has already taken
   and re-taken; it simply had no home. This is the home, and the numbers are
   theirs, so a template adopting the DS class swaps one rule for none and moves
   nothing on screen.

   SPACE COMES FROM THE STACK LADDER, NOT THE RAW SCALE (docs/SPACING-WORKLIST
   §0.3): this is a VERTICAL gap between two siblings, so `--space-stack-xs`.
   It resolves to the same 4px `--space-1` those sixteen copies hardcode, and it
   halves under `[data-density="compact"]` where a hardcoded 4 would not.

   FOUR PIXELS ONLY BECAUSE THE TYPE IS DOING THE REST OF THE WORK. The gap was
   never the whole complaint — a 15px near-white line 0px under a 30px near-white
   line reads as one block whatever the margin is. Dropping to `--text-sm` and
   `--text-secondary` is what makes it a subtitle; the 4px is what stops the two
   line boxes touching. Measured together at 390pt they separate cleanly, and at
   1280px, where the h1 is one line, the header does not loosen — the block grows
   by 4px and the title's own `margin-block-end` is untouched. A larger step was
   tried and is wrong on desktop: `--space-stack-sm` puts 8px between a one-line
   title and its subtitle and the pair stops reading as one unit.

   Scoped to `.page-header__subtitle` and NOT to `.page-header__primary > h1 + p`,
   deliberately. The generic selector would out-specify all sixteen of those
   local class rules (0,1,1 beats 0,1,0) and silently restyle sixteen pages that
   are not broken — a DS rule reaching past its own class to overrule templates
   is how a design system loses trust. Those pages adopt this class when someone
   is looking at them; until then they keep their own copy and nothing moves. */
.page-header__subtitle {
  margin-block-start: var(--space-stack-xs);
  font-size: var(--text-sm);
  color: var(--text-secondary);
}

.page-header__actions {
  display: flex;
  gap: var(--space-2);
  /* THE ZONE HOLDS STATE, NOT ONLY ACTIONS. Now that a record's current state
     reads as an astra-badge here (and the transitions that change it live in
     the `⋯`), the zone has a child that is NOT 40px tall. The default
     `align-items: stretch` inflated that badge to the height of the tallest
     button beside it — a 40px pill with 12px of text in it — and every page
     that wanted a badge in the header had to reach into the zone and undo the
     stretch itself. Centre the cross axis once, here: buttons are all one
     height so it changes nothing for them, and a wrapped zone centres each
     line rather than the block.
     The ≤768px collapse below is keyed on `astra-button`, so the badge stays
     visible on a phone — which is correct: an operator must be able to see
     what state a record is in without opening a menu. */
  align-items: center;
  /* The zone is ONE flex item and it never shrinks — a shrunken zone is a
     clipped control, and the platform rule is that an ACTIONABLE CONTROL MUST
     NEVER CLIP (the title is what gives way; it has `min-width: 0` above and
     re-flows). `max-width` is the escape hatch for the pathological zone that
     is wider than the header itself: it then wraps INSIDE the zone rather than
     overflowing the page. */
  flex-shrink: 0;
  flex-wrap: wrap;
  max-width: 100%;
}

/* Same guarantee one level down: nothing in the zone shrinks. A squeezed
   astra-button clips its label instead of getting narrower gracefully. The
   cluster is `display: contents`, so ITS children are flex items of the zone
   too and need the rule as well. */
.page-header__actions > *,
.page-header__cluster > * {
  flex-shrink: 0;
}

/* ─── A HEADING THAT CARRIES A COUNT (`.count-title`) ───────────────────────
   Nineteen NineFive templates declared this rule, seventeen of them byte for
   byte, and a nine-line comment explaining WHY had been copy-pasted alongside
   it into ten of them. The idiom was understood and written down; it simply had
   no home, so it propagated by clipboard. This is that home, and this is the
   one copy of the reasoning:

   THE COUNT BELONGS INSIDE THE HEADING, IN ITS INLINE FLOW. "Shop orders 128"
   is one fact, not a title with a statistic parked beside it, and a screen
   reader must read it as one phrase — which is why the badge is a CHILD of the
   h1 and carries its own `aria-label` spelling the noun out ("128 orders"),
   rather than sitting in the actions zone or in a stats card. Putting it in the
   flow also means the pair truncates and wraps as a unit at narrow widths
   instead of the number being orphaned onto its own line.

   `align-items: center` is doing the optical work: a badge is a short pill and
   a heading is a tall line box, so aligning their boxes' tops leaves the pill
   floating near the ascenders. Centring puts it on the heading's optical
   middle. `flex-wrap: wrap` is what stops a long title plus its badge from
   forcing the whole console wider than the viewport on a phone.

   NOT named `.page-header__count`, deliberately. Three of the twenty-two uses
   are an h2 or h3 titling a section INSIDE a card, not the page's own h1, and a
   `__` name would have claimed an ownership that does not hold — it would also
   have meant editing markup in twenty-two files to rename a class that is
   already correct. The rule lives here because the page header is where it
   usually appears; it is scoped to nothing so it keeps working anywhere a
   heading counts something. */
.count-title {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}

/* An actions GROUP that needs its own element (state-machine host, form
   wrapper). Transparent to layout at BOTH breakpoints: a real flex item would
   introduce a nested row with its own gap, so the group's buttons would space
   differently from a bare child of the zone. */
.page-header__cluster {
  display: contents;
}

/* ─── THE SPLIT PRIMARY: the joined shape ───────────────────────────────────
   Width-independent half of `.page-cta-split` (see the long comment beside
   `$split-pinned` at the top of this file). The POSITIONING halves live in the
   two media blocks below, because a split is pinned exactly where and when its
   primary would have been. */
.page-cta-split {
  display: inline-flex;
  align-items: center;
}

/* THE WRAPPER IS ONLY AS AVAILABLE AS ITS PRIMARY. `hidden` goes on the button
   (the AVAILABILITY CONTRACT), and the corner scrim / bar strip / reserved zone
   all disappear with it — so the caret must go too, or a lone chevron floats in
   the corner over nothing, opening a menu of alternatives to an action that is
   not on offer. Written as the negation of the promotion gate so the two can
   never disagree. */
.page-cta-split:not(:has(> astra-button[variant=primary]:not([hidden]))) {
  display: none;
}

/* ONE CONTROL WITH A SEAM, not two buttons that happen to touch: the inner
   corners collapse and the END half's own (already-present, transparent) 1px
   border becomes the hairline between the halves. Logical radii + a logical
   border edge, so the shape flips correctly in RTL with no second rule.

   BY POSITION, NOT BY ROLE. These used to say "the direct-child primary
   flattens its end corners, the dropdown trigger flattens its start corners",
   which silently required the labelled action to be child #1 and the caret to
   be child #2 — i.e. it hard-coded one arrangement of the halves at the same
   time as the file hard-coded one verb for them. Start/end says the same thing
   about the SHAPE while leaving both free. */
.page-cta-split > astra-button:first-child::part(button), .page-cta-split > astra-dropdown:first-child > astra-button[slot=trigger]::part(button) {
  border-start-end-radius: 0;
  border-end-end-radius: 0;
}

.page-cta-split > astra-button:last-child::part(button), .page-cta-split > astra-dropdown:last-child > astra-button[slot=trigger]::part(button) {
  border-start-start-radius: 0;
  border-end-start-radius: 0;
  border-inline-start-color: color-mix(in oklch, var(--text-on-accent) 30%, transparent);
}

/* The primary's hover lifts it 1px (`transform: translateY(-1px)`), which on a
   joined control tears the seam open and slides the hairline out of true. Both
   halves keep every other hover affordance — the gradient still brightens, the
   elevation still grows — they just do not move. */
.page-cta-split astra-button::part(button):hover {
  transform: none;
}

/* ─── MIRROR ITEMS ──────────────────────────────────────────────────────────
   A `[data-mirror]` menu item is the ≤768px copy of a control that is VISIBLE
   in the zone at this width — so at this width it must not render, or the
   action reads twice (once as a button, once in the ⋯). The ≤768px half of the
   pair is in the mobile block. */
astra-menu-item[data-mirror] {
  display: none;
}

/* …and when they are ALL that a ⋯ holds, the trigger goes too — that decision
   is astra-dropdown's, from what actually renders (see "WITHDRAWAL" there).
   CSS cannot make it: `:has()` can spot a menu whose every item is a mirror,
   but not one emptied by a page's own `hidden`, a permission gate or a state
   machine, and those are the same dead control. */
/* …and the divider that fenced the mirror group off goes with it, or a menu
   whose mirrors are all hidden opens with a stray rule across its top. Keyed on
   the divider FOLLOWING a mirror: in the canonical order (mirrors, then plain
   items, then divider, then the destructive one) a divider only ever sits
   directly after a mirror when the group above it just emptied out.

   `astra-dropdown`'s divider hygiene now catches this case too, and every case
   this selector cannot see — including a menu resized while CLOSED, which the
   component re-judges at every width change rather than at the next open. This
   rule STAYS for what remains: the two react on different clocks. The media
   query flips synchronously the instant the viewport crosses 768px; the JS pass
   is coalesced into the next animation frame. Without this rule that gap is one
   painted frame of stray rule across the top of an open menu. */
astra-menu-item[data-mirror] + astra-menu-divider {
  display: none;
}

/* Card chrome for the `.page-header--sticky` band, at BOTH breakpoints: mobile
   drops the pinning (see the mobile block) but keeps the grouping the card
   creates — title + status chips + facts read as one operator object. The
   pinning itself lives in the ≥769px block at the bottom of this file. */
.page-header--sticky {
  padding: var(--space-4);
  background: var(--surface-raised);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-card);
  box-shadow: var(--elevation-1);
}

@media (max-width: 768px) {
  /* Vertical real estate the sticky CTA bar reserves at the bottom of the
     viewport: 3rem button + space-3 above + space-3 below + safe-area
     inset. Defined ONLY when the page actually renders a promoted CTA, so
     pages without one keep the shells' 12px fallback breathing room. */
  :root:has(.page-header__actions > astra-button[variant=primary]:not([hidden]), .page-header__cluster > astra-button[variant=primary]:not([hidden]), astra-button.page-cta[variant=primary]:not([hidden]), astra-button.page-cta--pinned[variant=primary]:not([hidden])) {
    --page-action-bar-zone: calc(3rem + var(--space-3) + var(--space-3) + env(safe-area-inset-bottom, 0px));
  }
  /* ─── THE ACTION ZONE at ≤768px ───────────────────────────────────────────
     ONE LAYOUT MODEL AT EVERY WIDTH. The header stays the flex row it is on
     desktop; mobile only adds (a) permission to wrap and (b) a floor under the
     title. Title + secondaries + ⋯ therefore share the heading row here exactly
     as they do at 1280px, which is the whole point: an operator does not learn
     a second layout on a phone.

     THIS USED TO BE A GRID, and that is the bug this replaces. `.page-header`
     became `grid-template-areas: "primary dropdown"`, `.page-header__actions`
     dissolved with `display: contents`, and an area was assigned to exactly ONE
     child — `astra-dropdown`. Every other child (a ghost secondary, a
     state-machine button) had no area, so it AUTO-PLACED onto a new implicit
     row: measured on a 375px portal page, two ghosts landed BELOW the header at
     y=232 and squeezed the h1 from 319px to 194px. Three templates shipped
     three different local workarounds for that one bug (invoice-edit,
     shop-order-detail, shop-return-detail) — the cost of a placement rule that
     only knows about one kind of child.

     Placing the whole ZONE (`grid-area` on `.page-header__actions` instead of
     on the dropdown) would have fixed the symptom. Dropping the grid fixes the
     CLASS: with the zone as a single flex item there is nothing left to place,
     so a cluster wrapper, a tooltip host around a button, or tomorrow's second
     dropdown cannot fall out of it. Flex also buys the two behaviours the grid
     could not express here:
       · the zone wraps AS A WHOLE — it may drop under the title, but it can
         never interleave with it or split its own controls across rows;
       · `min-width` on `.page-header__primary` is a REAL floor. `minmax(0, 1fr)`
         asks the title to collapse toward zero before anything else gives; a
         floor makes the ZONE yield the row instead, which is the correct
         priority when the title is what identifies the record.
     (Container queries were the third option: they would need a named container
     on every shell plus a fallback for pages rendered outside one, to arrive at
     these same two declarations. Not worth it.) */
  .page-header {
    flex-wrap: wrap;
    gap: var(--space-3);
    margin-block-end: var(--space-5);
  }
  .page-header__primary {
    /* TITLE FLOOR — under ~12rem a heading stops being readable, so that is the
       width at which the zone gives up the row rather than the title giving up
       characters. `min()` keeps it honest inside a container narrower than the
       floor: never wider than 100%, so the floor itself can never cause an
       overflow. */
    min-width: min(100%, 12rem);
  }
  /* COLLAPSE TO THE ⋯ ALONE.
     The narrow-width answer for a zone that HAS an overflow menu: hide the
     visible secondaries, let the ⋯ carry them, and the heading row is title +
     one 40px trigger.

     Gated on the menu actually containing the mobile copies
     (`astra-menu-item[data-mirror]` — see "MIRROR ITEMS" above), which makes
     the rule fail-safe in the only direction that matters: a zone whose
     secondaries are not mirrored keeps them (they merely wrap under the title,
     as one block), so no page can lose an action by being rebuilt on this file
     before its ⋯ is written.

     The promoted primary is excluded: it is already `position: fixed` in the
     thumb bar, not in this row. `[slot="trigger"]` excludes the ⋯ itself — the
     one control the collapse exists to keep. */
  .page-header__actions:has(astra-dropdown astra-menu-item[data-mirror]) astra-button:not([slot=trigger]):not([variant=primary]):not(.page-cta):not(.page-cta--pinned) {
    display: none;
  }
  /* The other half of the pair (default is `display: none`, above). */
  astra-menu-item[data-mirror] {
    display: block;
  }
  astra-menu-item[data-mirror] + astra-menu-divider {
    display: block;
  }
  /* Sticky primary CTA at the viewport bottom.

     THE INLINE INSET IS THE PAGE GUTTER, the block inset is the shell frame's
     own 12px. Both used to be a flat `var(--space-3)`, and that was right while
     the shell frame was also a flat 12px: the bar sat exactly on the frame's
     edge and read as floating a clean 16px proud of the content inside it.
     Then `--gutter` was wired (tokens/_spacing.scss section 4) and the frame
     collapsed to 0 below 480px, so on a 390px phone the content came to 16px
     from the screen while this bar stayed at 12px — a 4px near-miss, the bar
     overhanging every card on the page by half a hairline, which reads as a
     mistake rather than as a float. `var(--gutter)` puts it back on a line the
     eye can find: one gutter from the screen edge, the same dial the page
     itself reads, so it is flush with the content where the frame is gone and
     floats proud of it where the frame is there. It follows [data-density] for
     free, and the fallback is only for a page that loads this sheet without the
     tokens — `--gutter` is defined on :root at every width.

     `position: fixed`, not absolute: fixed pins to the viewport whatever the
     header is doing, so the bar survived the header being a grid (whose areas
     act as containing blocks for absolutely-positioned children) and survives
     it being a flex row now. It is also why the promoted primary is excluded
     from the collapse above — it is not in that row at all. */
  /* THE PRIMARY LIFTS FOR A DOCKED BAR RATHER THAN DISAPPEARING UNDER ONE.
     `--astra-bottom-dock` is the shared bottom-edge claim (see
     components/_shared/bottom-dock.js): astra-table's selection bar publishes
     its measured height there while it is docked at ≤768px, because it pins to
     this exact corner at this exact z-index and used to land straight on top of
     the CTA — the owner photographed "Start a payout" gone the instant a row
     was ticked (2026-09-04). Unset on every page that has no docked bar, so the
     fallback of 0px is the normal case and this is a no-op there. */
  .page-header__actions > astra-button[variant=primary], .page-header__cluster > astra-button[variant=primary], astra-button.page-cta[variant=primary], astra-button.page-cta--pinned[variant=primary] {
    position: fixed;
    top: auto;
    inset-inline: var(--gutter, var(--space-3));
    /* THE SAFE-AREA INSET IS INSIDE THE DOCK, SO IT MUST NOT BE ADDED TWICE.
       astra-table's docked bar pads its own bottom by
       `calc(var(--space-3) + env(safe-area-inset-bottom, 0px))`, and what it
       publishes as --astra-bottom-dock is getBoundingClientRect().height — the
       whole box, inset included. `max(--space-3, inset) + dock` therefore added
       a SECOND home-indicator height on top of a bar that already cleared it:
       on a 34px-indicator phone the visual gap between this button and the top
       of the bar became 34px instead of 12px. Subtracting the dock inside the
       max is what makes the expression right in both states: with no dock it is
       still `max(12px, inset)`, and with a dock the inset term goes negative and
       the max settles on the 12px gap this button is supposed to have.
       (env() reads 0 in headless Chromium, so this is arithmetic rather than a
       screenshot — noted honestly rather than claimed as measured.) */
    bottom: calc(max(var(--space-3), env(safe-area-inset-bottom, 0px) - var(--astra-bottom-dock, 0px)) + var(--astra-bottom-dock, 0px));
    display: block;
    width: auto;
    z-index: var(--z-overlay);
  }
  /* `display: block` above would defeat the UA's `[hidden] { display:
     none }` (lower specificity) — restore it so hidden CTAs (multi-step
     flows toggling buttons) stay hidden. */
  .page-header__actions > astra-button[variant=primary][hidden],
  .page-header__cluster > astra-button[variant=primary][hidden],
  astra-button.page-cta[variant=primary][hidden],
  astra-button.page-cta--pinned[variant=primary][hidden] {
    display: none;
  }
  .page-header__actions > astra-button[variant=primary]::part(button),
  .page-header__cluster > astra-button[variant=primary]::part(button),
  astra-button.page-cta[variant=primary]::part(button),
  astra-button.page-cta--pinned[variant=primary]::part(button) {
    width: 100%;
    height: 3rem;
    box-shadow: var(--elevation-4);
  }
  /* THE SPLIT PRIMARY IS THE BAR. The wrapper takes the fixed positioning the
     rule above just gave its primary, so the caret rides the bar instead of
     being left behind in the heading row; the primary goes back to being an
     ordinary flex item and eats the width the caret does not need. Gated on the
     wrapper actually holding a PROMOTED primary — an un-promoted split is an
     in-flow control and must stay in the flow. */
  .page-cta-split:has(> astra-button.page-cta[variant=primary]:not([hidden])), .page-cta-split:has(> astra-button.page-cta--pinned[variant=primary]:not([hidden])) {
    position: fixed;
    top: auto;
    inset-inline: var(--gutter, var(--space-3));
    /* The split rides the dock exactly as the single primary above does, and
       sits on the same gutter — the two are the same bar with a different
       number of halves in it, so these insets have to stay identical. */
    /* Same bottom as the single primary above, safe-area arithmetic included —
       see the note there. The split IS that bar with a second half in it, and
       the two expressions have to stay identical or the caret rides at a
       different height from the button it belongs to. */
    bottom: calc(max(var(--space-3), env(safe-area-inset-bottom, 0px) - var(--astra-bottom-dock, 0px)) + var(--astra-bottom-dock, 0px));
    display: flex;
    z-index: var(--z-overlay);
  }
  .page-cta-split > astra-button.page-cta[variant=primary],
  .page-cta-split > astra-button.page-cta--pinned[variant=primary] {
    position: static;
    inset: auto;
    z-index: auto;
    flex: 1 1 auto;
    min-width: 0;
  }
  /* The caret is a fixed 3rem square beside a primary that flexes — the label
     half gives up the width, never the tap target. Same height and elevation as
     the half it is joined to. */
  .page-cta-split > astra-dropdown > astra-button[slot=trigger]::part(button) {
    width: 3rem;
    height: 3rem;
    padding: 0;
    box-shadow: var(--elevation-4);
  }
  /* Backdrop-blur fade strip behind the CTA — iOS Mail / Notes idiom.
     Sits at the viewport bottom, fades from transparent at the top to the
     page surface at the bottom, and blurs whatever scrolls underneath.

     Painted from the SHELL's main slot pseudo (::part(main)::after), i.e.
     a flat-tree ANCESTOR of the promoted button:
       - not the button's own pseudo — the button is a stacking context
         (fixed + z-index), so a pseudo-child would cover its gradient +
         label;
       - not body::after — Chromium paints a fixed body pseudo OVER
         slotted shadow-DOM content regardless of z-index (verified: a
         z-299 body::after covers the z-300 promoted astra-button inside
         astra-shell-admin), so the strip would wash out the CTA.
     A pseudo INSIDE the shell shadow tree layers correctly under the
     button in every shell. body::after remains only as the fallback for
     shell-less pages (plain DOM — z-index behaves normally there).
     mask-image gates the strip's pixels by alpha so the blur dissolves
     toward the top instead of cutting off. */
  astra-shell-admin:has(.page-header__actions > astra-button[variant=primary]:not([hidden]), .page-header__cluster > astra-button[variant=primary]:not([hidden]), astra-button.page-cta[variant=primary]:not([hidden]), astra-button.page-cta--pinned[variant=primary]:not([hidden]))::part(main)::after,
  astra-shell-partner:has(.page-header__actions > astra-button[variant=primary]:not([hidden]), .page-header__cluster > astra-button[variant=primary]:not([hidden]), astra-button.page-cta[variant=primary]:not([hidden]), astra-button.page-cta--pinned[variant=primary]:not([hidden]))::part(main)::after,
  astra-shell-ninefive:has(.page-header__actions > astra-button[variant=primary]:not([hidden]), .page-header__cluster > astra-button[variant=primary]:not([hidden]), astra-button.page-cta[variant=primary]:not([hidden]), astra-button.page-cta--pinned[variant=primary]:not([hidden]))::part(main)::after,
  astra-shell-invoice:has(.page-header__actions > astra-button[variant=primary]:not([hidden]), .page-header__cluster > astra-button[variant=primary]:not([hidden]), astra-button.page-cta[variant=primary]:not([hidden]), astra-button.page-cta--pinned[variant=primary]:not([hidden]))::part(main)::after,
  astra-shell-identity:has(.page-header__actions > astra-button[variant=primary]:not([hidden]), .page-header__cluster > astra-button[variant=primary]:not([hidden]), astra-button.page-cta[variant=primary]:not([hidden]), astra-button.page-cta--pinned[variant=primary]:not([hidden]))::part(main)::after,
  body:not(:has(astra-shell-admin, astra-shell-partner, astra-shell-ninefive, astra-shell-invoice, astra-shell-identity)):has(astra-button.page-cta[variant=primary]:not([hidden]))::after {
    content: "";
    position: fixed;
    inset-inline: 0;
    /* THE STRIP RIDES THE DOCK WITH THE BUTTON IT IS BEHIND. Its whole job is
       to keep the CTA off bare content — without it the button is a solid pill
       laid across whatever it happens to be over. When a docked selection bar
       lifts the CTA (see --astra-bottom-dock beside the promoted primary), a
       strip still pinned to bottom:0 is behind the BAR instead, and the button
       is bare again — measured on the payouts queue at 390pt, the lifted CTA
       cut straight across the card the operator had just ticked. */
    bottom: var(--astra-bottom-dock, 0px);
    height: calc(var(--page-action-bar-zone) + var(--space-8));
    background: linear-gradient(to top, var(--surface-page) 0%, color-mix(in oklch, var(--surface-page) 70%, transparent) 60%, transparent 100%);
    backdrop-filter: blur(16px) saturate(180%);
    -webkit-backdrop-filter: blur(16px) saturate(180%);
    mask-image: linear-gradient(to top, rgb(0, 0, 0) 0%, rgb(0, 0, 0) 45%, rgba(0, 0, 0, 0) 100%);
    -webkit-mask-image: linear-gradient(to top, rgb(0, 0, 0) 0%, rgb(0, 0, 0) 45%, rgba(0, 0, 0, 0) 100%);
    z-index: calc(var(--z-overlay) - 1);
    pointer-events: none;
  }
  /* Reserve room under the sticky CTA so the last content row remains
     scrollable into view. Applied at the shell's main slot via
     ::part(main) — gated on a promoted CTA actually existing inside that
     shell so CTA-less pages keep their normal padding. */
  astra-shell-admin:has(.page-header__actions > astra-button[variant=primary], .page-header__cluster > astra-button[variant=primary], astra-button.page-cta[variant=primary], astra-button.page-cta--pinned[variant=primary])::part(main),
  astra-shell-partner:has(.page-header__actions > astra-button[variant=primary], .page-header__cluster > astra-button[variant=primary], astra-button.page-cta[variant=primary], astra-button.page-cta--pinned[variant=primary])::part(main),
  astra-shell-ninefive:has(.page-header__actions > astra-button[variant=primary], .page-header__cluster > astra-button[variant=primary], astra-button.page-cta[variant=primary], astra-button.page-cta--pinned[variant=primary])::part(main),
  astra-shell-invoice:has(.page-header__actions > astra-button[variant=primary], .page-header__cluster > astra-button[variant=primary], astra-button.page-cta[variant=primary], astra-button.page-cta--pinned[variant=primary])::part(main),
  astra-shell-identity:has(.page-header__actions > astra-button[variant=primary], .page-header__cluster > astra-button[variant=primary], astra-button.page-cta[variant=primary], astra-button.page-cta--pinned[variant=primary])::part(main) {
    padding-block-end: calc(var(--page-action-bar-zone, var(--space-3)) + var(--space-3));
  }
  /* Targeted layout wrappers (no-op when nested inside a shell, useful for
     pages rendered outside one) + a body-level reserve for shell-less
     pages that opt a CTA in. */
  .detail-layout,
  .settings-layout,
  .dashboard-grid {
    padding-block-end: calc(var(--page-action-bar-zone, var(--space-3)) + var(--space-3));
  }
  body:not(:has(astra-shell-admin, astra-shell-partner, astra-shell-ninefive, astra-shell-invoice, astra-shell-identity)):has(astra-button.page-cta[variant=primary]:not([hidden])) {
    padding-block-end: calc(var(--page-action-bar-zone, var(--space-3)) + var(--space-3));
  }
  /* Bottom-positioned toast regions ride above the action bar so toasts
     and the CTA never overlap (top-positioned regions are unaffected). */
  :root:has(.page-header__actions > astra-button[variant=primary]:not([hidden]), .page-header__cluster > astra-button[variant=primary]:not([hidden]), astra-button.page-cta[variant=primary]:not([hidden]), astra-button.page-cta--pinned[variant=primary]:not([hidden])) astra-toast-region[position^=bottom] {
    /* …and above a docked selection bar too, which the CTA is now sitting on
       top of (see --astra-bottom-dock beside the promoted primary). */
    bottom: calc(var(--page-action-bar-zone) + var(--astra-bottom-dock, 0px) + var(--space-2));
  }
  /* `.page-header--sticky` does NOT stick on mobile. The thumb-zone bar is the
     mobile answer, and a sticky (= stacking-context-forming) ancestor would trap
     the DS-promoted `position: fixed` CTA inside it, painting it UNDER the action
     bar's blur strip (z-overlay − 1). Static + z-index:auto lets it escape to the
     viewport. The card chrome stays — only the pinning goes. */
  .page-header--sticky {
    position: static;
    z-index: auto;
    padding: var(--space-3);
    border-radius: var(--radius-md);
  }
}
@media (min-width: 769px) {
  /* PIN FLUSH TO THE SCROLLPORT, and no glass anywhere.
     ────────────────────────────────────────────────────────────────────────
     A browser insets a sticky element's constraint rect by the SCROLL
     CONTAINER'S PADDING, so a bare `top: 0` inside the shell's `.sh__main`
     (padding-block-start: space-8) pins the band 32px BELOW the scrollport's
     visible top edge. That leaves a live 32px strip in which scrolled content
     shows ABOVE the supposedly-pinned band.

     This used to be papered over with a `::before` glass panel, and that fix
     was worse than the bug: it painted UNCONDITIONALLY — including at
     scrollTop 0 with nothing beneath it — and it was only as wide as the band
     (the content box) while the scrollport is the content box PLUS the inline
     padding, so it rendered as a 948×32 pale blurred rectangle with hard
     left/right edges that washed the shell's radial accent gradient out at
     rest. Measured on portal shop-order-detail: bbox (289,84)–(1235,173).

     Cancel the inset instead: subtract the container's own top padding, which
     the sidebar shells publish as `--sh-main-pad-block-start`. The band then
     pins flush to the scrollport's visible top edge, so no strip can exist and
     none needs glassing — the card's opaque `--surface-raised` fill and its
     elevation-1 shadow cut the content cleanly on their own.

     At rest nothing moves: sticky only offsets once the element WOULD cross the
     threshold, and at scrollTop 0 the band sits a full padding below it.
     Fallback 0px == today's `top: 0` for any non-shell scroll container with no
     top padding. Mobile keeps `position: static` (see the ≤768px block). */
  .page-header--sticky {
    position: sticky;
    top: calc(-1 * var(--sh-main-pad-block-start, 0px));
    z-index: var(--z-raised);
  }
  /* The floating primary. `position: fixed` (not sticky) because the button
     sits deep inside a card — a sticky box can only travel within its own
     containing block, which is the card, i.e. exactly the scroll range that
     already failed. Bottom-END, so it never collides with the shells' sidebar
     (start) or with the default top-right toast region. */
  astra-button.page-cta--pinned[variant=primary] {
    position: fixed;
    top: auto;
    inset-block-end: var(--space-5);
    inset-inline-end: var(--space-5);
    inset-inline-start: auto;
    z-index: var(--z-overlay);
  }
  /* `[hidden]` must still win (multi-step flows toggle the CTA). */
  astra-button.page-cta--pinned[variant=primary][hidden] {
    display: none;
  }
  astra-button.page-cta--pinned[variant=primary]::part(button) {
    height: 3rem;
    padding-inline: var(--space-6);
    box-shadow: var(--elevation-4);
  }
  /* THE SPLIT PRIMARY, pinned. Same corner, same insets, same z-index as the
     lone pill above — the wrapper simply wears them instead of the button, so
     the caret comes with it. The scrim underneath needs no new selector: it
     gates on `$pinned-visible`, i.e. on the BUTTON, which is still there,
     still classed and still not `hidden`.

     Both declarations below out-specify the `astra-button.page-cta--pinned[variant="primary"]` rules they correct
     (one extra class each) AND sit after them, so neither depends on the other
     mechanism alone. */
  .page-cta-split:has(> astra-button.page-cta--pinned[variant=primary]:not([hidden])) {
    position: fixed;
    top: auto;
    inset-block-end: var(--space-5);
    inset-inline-end: var(--space-5);
    inset-inline-start: auto;
    z-index: var(--z-overlay);
  }
  .page-cta-split > astra-button.page-cta--pinned[variant=primary] {
    position: static;
    inset: auto;
    z-index: auto;
  }
  /* The caret matches the pill it is joined to: 3rem tall, square, same
     elevation. It keeps the pill's `padding-inline` off itself — an icon-only
     button that inherited space-6 either side would be 88px wide. */
  .page-cta-split > astra-dropdown > astra-button[slot=trigger]::part(button) {
    width: 3rem;
    height: 3rem;
    padding: 0;
    box-shadow: var(--elevation-4);
  }
  /* ---- The corner scrim ---------------------------------------------------
     A pill floating over arbitrary scrolling content is only legible if
     something separates it from that content. Mobile solves this with a
     full-width blurred fade strip; a full-width strip is the wrong SHAPE for a
     corner pill — it draws a band across a page that has no band.

     So: the same trick, radially. A quarter-disc of blur anchored in the
     bottom-end corner, and a `mask-image` whose alpha falls to 0 well before
     the box ends, so the blur DISSOLVES outward instead of stopping on an
     edge. Both layers use the same `115% 115%` radial geometry, so the tint
     and the blur fade together — mismatch them and the tint outlives the blur
     as a visible ring.

     Painted from `::part(main)::after`, the same flat-tree ancestor the mobile
     strip uses, for the same two reasons documented there: the button is its
     own stacking context (fixed + z-index) so its own pseudo would cover its
     label, and a fixed `body::after` paints OVER slotted shadow-DOM content in
     Chromium regardless of z-index.

     `pointer-events: none` is non-negotiable — this thing covers ~416x256px of
     real form controls (the product editor's Width/Weight inputs sit under it)
     and would eat every click in that corner.

     No doubling with mobile: that strip lives in the ≤768px block, this in the
     ≥769px block. Exactly one of the two can ever paint. */
  astra-shell-admin:has(astra-button.page-cta--pinned[variant=primary]:not([hidden]))::part(main)::after,
  astra-shell-partner:has(astra-button.page-cta--pinned[variant=primary]:not([hidden]))::part(main)::after,
  astra-shell-ninefive:has(astra-button.page-cta--pinned[variant=primary]:not([hidden]))::part(main)::after,
  astra-shell-invoice:has(astra-button.page-cta--pinned[variant=primary]:not([hidden]))::part(main)::after,
  astra-shell-identity:has(astra-button.page-cta--pinned[variant=primary]:not([hidden]))::part(main)::after,
  body:not(:has(astra-shell-admin, astra-shell-partner, astra-shell-ninefive, astra-shell-invoice, astra-shell-identity)):has(astra-button.page-cta--pinned[variant=primary]:not([hidden]))::after {
    content: "";
    position: fixed;
    inset-block-start: auto;
    inset-inline-start: auto;
    inset-block-end: 0;
    inset-inline-end: 0;
    inline-size: min(26rem, 38vw);
    block-size: min(16rem, 34vh);
    background: radial-gradient(115% 115% at 100% 100%, color-mix(in oklch, var(--surface-page) 88%, transparent) 0%, color-mix(in oklch, var(--surface-page) 62%, transparent) 42%, transparent 74%);
    backdrop-filter: blur(18px) saturate(180%);
    -webkit-backdrop-filter: blur(18px) saturate(180%);
    mask-image: radial-gradient(115% 115% at 100% 100%, rgb(0, 0, 0) 0%, rgb(0, 0, 0) 34%, rgba(0, 0, 0, 0) 76%);
    -webkit-mask-image: radial-gradient(115% 115% at 100% 100%, rgb(0, 0, 0) 0%, rgb(0, 0, 0) 34%, rgba(0, 0, 0, 0) 76%);
    z-index: calc(var(--z-overlay) - 1);
    pointer-events: none;
  }
  /* `inset-inline-end` is logical but `at 100% 100%` is physical, so in RTL the
     anchor moves to the start edge and the blur would sit on the wrong side of
     its own box. Flip both gradients' origin with it. */
  astra-shell-admin:dir(rtl):has(astra-button.page-cta--pinned[variant=primary]:not([hidden]))::part(main)::after,
  astra-shell-partner:dir(rtl):has(astra-button.page-cta--pinned[variant=primary]:not([hidden]))::part(main)::after,
  astra-shell-ninefive:dir(rtl):has(astra-button.page-cta--pinned[variant=primary]:not([hidden]))::part(main)::after,
  astra-shell-invoice:dir(rtl):has(astra-button.page-cta--pinned[variant=primary]:not([hidden]))::part(main)::after,
  astra-shell-identity:dir(rtl):has(astra-button.page-cta--pinned[variant=primary]:not([hidden]))::part(main)::after,
  body:dir(rtl):not(:has(astra-shell-admin, astra-shell-partner, astra-shell-ninefive, astra-shell-invoice, astra-shell-identity)):has(astra-button.page-cta--pinned[variant=primary]:not([hidden]))::after {
    background: radial-gradient(115% 115% at 0% 100%, color-mix(in oklch, var(--surface-page) 88%, transparent) 0%, color-mix(in oklch, var(--surface-page) 62%, transparent) 42%, transparent 74%);
    mask-image: radial-gradient(115% 115% at 0% 100%, rgb(0, 0, 0) 0%, rgb(0, 0, 0) 34%, rgba(0, 0, 0, 0) 76%);
    -webkit-mask-image: radial-gradient(115% 115% at 0% 100%, rgb(0, 0, 0) 0%, rgb(0, 0, 0) 34%, rgba(0, 0, 0, 0) 76%);
  }
  /* Reserve the pill's zone so the last content row can still scroll clear of
     it, and lift bottom toasts above it — the desktop halves of the two mobile
     rules, gated the same way on the CTA actually existing. */
  :root:has(astra-button.page-cta--pinned[variant=primary]:not([hidden])) {
    --page-action-bar-zone: calc(3rem + var(--space-5) + var(--space-5));
  }
  astra-shell-admin:has(astra-button.page-cta--pinned[variant=primary])::part(main),
  astra-shell-partner:has(astra-button.page-cta--pinned[variant=primary])::part(main),
  astra-shell-ninefive:has(astra-button.page-cta--pinned[variant=primary])::part(main),
  astra-shell-invoice:has(astra-button.page-cta--pinned[variant=primary])::part(main),
  astra-shell-identity:has(astra-button.page-cta--pinned[variant=primary])::part(main) {
    padding-block-end: calc(var(--page-action-bar-zone, var(--space-3)) + var(--space-3));
  }
  :root:has(astra-button.page-cta--pinned[variant=primary]:not([hidden])) astra-toast-region[position^=bottom] {
    bottom: calc(var(--page-action-bar-zone) + var(--space-2));
  }
}
/* Transparency is a preference, not a given. Keep the separation (the pill
   still needs to read against content) but drop the compositor-expensive blur
   and lean on an opaque-at-the-corner tint instead. Same radial falloff, so
   there is still no hard edge. */
@media (min-width: 769px) and (prefers-reduced-transparency: reduce) {
  astra-shell-admin:has(astra-button.page-cta--pinned[variant=primary]:not([hidden]))::part(main)::after,
  astra-shell-partner:has(astra-button.page-cta--pinned[variant=primary]:not([hidden]))::part(main)::after,
  astra-shell-ninefive:has(astra-button.page-cta--pinned[variant=primary]:not([hidden]))::part(main)::after,
  astra-shell-invoice:has(astra-button.page-cta--pinned[variant=primary]:not([hidden]))::part(main)::after,
  astra-shell-identity:has(astra-button.page-cta--pinned[variant=primary]:not([hidden]))::part(main)::after,
  body:not(:has(astra-shell-admin, astra-shell-partner, astra-shell-ninefive, astra-shell-invoice, astra-shell-identity)):has(astra-button.page-cta--pinned[variant=primary]:not([hidden]))::after {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background: radial-gradient(115% 115% at 100% 100%, var(--surface-page) 0%, color-mix(in oklch, var(--surface-page) 80%, transparent) 42%, transparent 74%);
  }
}
.brand-mark {
  color: var(--accent-ink);
}

.action-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* Destructive at the start, confirming at the end, whatever the gap between
   them turns out to be. The split is what stops a mis-click, not the label. */
.action-row--split {
  justify-content: space-between;
}

/* Trailing cluster — the default reading for "these are this surface's
   actions" when nothing destructive is present. */
.action-row--end {
  justify-content: flex-end;
}

/* A cluster fenced off from the content above it (the settings "danger zone"
   idiom the five `.danger-row` copies were all reaching for). */
.action-row--fenced {
  padding-block-start: var(--space-4);
  margin-block-start: var(--space-4);
  border-block-start: 1px solid var(--border-subtle);
}

/* Below the thumb-zone breakpoint a wrapped cluster reads as a stack of
   equal-width blocks rather than a ragged row; the DS-promoted primary has
   already left the flow to the bottom bar by then, so what remains here is the
   secondary/destructive tail. */
@media (max-width: 480px) {
  .action-row > astra-button {
    flex: 1 1 auto;
  }
}
/* ─── THE LEADING-ICON NUDGE, AS ONE NAMED VALUE ────────────────────────────
   A status glyph is a square box; the sentence beside it is a line box with
   ascenders, descenders and half-leading. Align their TOPS and the glyph reads
   as sitting slightly high, because the text's own cap height starts a couple
   of pixels down inside its line box. This value is the correction, and it is
   OPTICAL ALIGNMENT TO A BASELINE, not rhythm between two things.

   That distinction is why it is published here as its own property and NOT as
   a spacing token. A spacing token is a statement about how far apart two
   objects should sit, it is re-pointed by density, and it would carry this
   number into every sweep and every ratchet that touches rhythm. This number
   is a property of the type, and it moves only if the icon size or the line
   height moves.

   It is published rather than inlined because the estate proved it has to be:
   sixteen templates re-derived this same correction by hand and landed on five
   different magic numbers (2px, .125rem, .15rem, .1em, .15em) — a spread of
   about 0.4px that nobody can see and that no two authors will ever guess the
   same way twice. A leading icon anywhere in the estate references this, so
   there is one number to re-tune and nothing to grep for.
   ─────────────────────────────────────────────────────────────────────────── */
:root {
  --status-icon-nudge: 2px;
}

.status-surface {
  --status-accent: var(--accent-500);
  --status-ink: var(--accent-ink);
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  flex-wrap: wrap;
  background: radial-gradient(150px 130px at 1.5rem 1.3rem, color-mix(in oklch, var(--status-accent) 26%, transparent) 0%, color-mix(in oklch, var(--status-accent) 9%, transparent) 30%, transparent 68%), var(--status-surface-base, var(--surface-raised));
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--elevation-2);
  padding: var(--space-3) var(--space-4);
  box-sizing: border-box;
}

.status-surface--default {
  --status-accent: var(--accent-500);
  --status-ink: var(--accent-ink);
}

.status-surface--success {
  --status-accent: var(--success-500);
  --status-ink: var(--success-ink);
}

.status-surface--warning {
  --status-accent: var(--warning-500);
  --status-ink: var(--warning-ink);
}

.status-surface--danger {
  --status-accent: var(--danger-500);
  --status-ink: var(--danger-ink);
}

.status-surface--info {
  --status-accent: var(--info-500);
  --status-ink: var(--info-ink);
}

.status-surface--quiet {
  background: color-mix(in srgb, var(--status-accent) 8%, var(--status-surface-base, var(--surface-raised)));
  border-color: color-mix(in srgb, var(--status-accent) 24%, var(--border-subtle));
  box-shadow: none;
}

.status-surface__icon {
  flex-shrink: 0;
  margin-block-start: var(--status-icon-nudge);
  display: inline-flex;
  color: var(--status-ink);
}

.status-surface__content {
  flex: 1 1 14rem;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.status-surface__title {
  margin: 0;
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
  line-height: var(--leading-snug);
}

.status-surface__message {
  margin: 0;
  font-size: var(--text-xs);
  color: var(--text-secondary);
  line-height: var(--leading-normal);
}

.status-surface__message:empty {
  display: none;
}

.status-surface__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-shrink: 0;
  flex-wrap: wrap;
  margin-inline-start: auto;
}

/* ─── INLINE — the smallest usable status surface ───────────────────────────
   THE VARIANT THAT EXISTS BECAUSE THE COMPONENT'S ENTRY PRICE WAS TOO HIGH.

   `.status-surface` is adopted in 43 templates, so nobody was unaware of it.
   Seven more places hand-rolled a tinted notice anyway, and the audit found
   why: the canonical form asks for a six-element DOM — `__icon`, `__content`,
   `__title`, `__message`, `__actions` — and what these authors had was ONE
   SENTENCE and an icon. There is no title to write, no secondary message and
   no button, so three quarters of that scaffolding would have been empty
   wrappers. Faced with writing five nested elements or eight lines of CSS,
   they wrote the CSS, and the estate grew a second status idiom with five
   different tints and five different icon nudges.

   The lesson, worth more than the variant: WHEN A COMPONENT IS BEING
   OVERRIDDEN FROM OUTSIDE, CHECK ITS SMALLEST USABLE FORM BEFORE ADDING A
   SPACING PROP. The overrides here were never about the spacing values; they
   were about the DOM.

   WHEN NOT TO REACH FOR THIS, because the survey that found these seven found
   sixteen and the other nine are a DIFFERENT SHAPE. The survey looked for the
   leading-icon nudge, and a nudge is the symptom, not the thing: nine of its
   hits (`.usage__row` ×4, `.checklist li`, `.terms li`, `.bill__notice`,
   `.reports__howpaid-bank`, `.px__preview-promise`) are icon-and-text ROWS, and
   converting them would have put a tinted, bordered, elevated notice around a
   checklist item.

   The line is NOT "does it already have padding and a radius" — four of those
   nine have both, plus a border and a surface fill. It is what the block SAYS:

     · A NOTICE is TONAL and it is ONE SENTENCE. It carries a state — success,
       warning, danger, info — the tone is load-bearing, and the whole block is
       a single statement about the thing beside it. That is this variant.
     · A ROW is NEUTRAL and it is a RECORD. It has columns, often a trailing
       action, and its icon is a type marker rather than a state. Tinting it
       would claim an alarm the author never raised.

   If you cannot name the TONE without inventing one, it is a row.

   So this variant changes the CONTRACT, not the look. Write:

     <p class="status-surface status-surface--inline status-surface--warning" role="status">
       <astra-icon name="alert-circle"></astra-icon>
       <span>We have paid out more than this quarter earned.</span>
     </p>

   A direct `astra-icon` child is treated as the leading glyph — it gets the
   ink colour, it does not shrink and it gets the nudge — so no `__icon`
   wrapper is needed. Everything else beside it is the sentence. `__icon` and
   the full scaffolding still work if a notice later grows a title and actions;
   this is a subset, not a fork.

   WHAT IS DELIBERATELY NOT CHANGED: the padding. The tint is a radial gradient
   whose origin is calibrated to a glyph sitting at `padding-inline:
   var(--space-4)`, and the comment on that gradient says not to re-tune the
   padding without re-tuning the origin. A "compact" inline notice that moved
   the padding would slide the glow off its own icon. An inline notice is
   already shorter than a titled one because it holds one line instead of
   three; it does not also need to be tighter.

   WHAT IS DELIBERATELY DIFFERENT FROM THE HAND-ROLLS IT REPLACES: they set
   `color: var(--warning-ink)` on the whole block, tinting the body text to
   match the glyph. The canonical language does not: `--status-ink` is scoped
   to the ICON, where it only has to clear WCAG's 3:1 floor for non-text UI,
   and the sentence stays `--text-primary`, which is the only value in the
   system proven to clear 4.5:1 against a tinted surface in BOTH themes. Body
   text in an ink colour was the riskier of the two guesses.
   ─────────────────────────────────────────────────────────────────────────── */
.status-surface--inline {
  margin: 0;
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
  color: var(--text-primary);
  flex-wrap: nowrap;
  gap: var(--space-inline-sm);
}

.status-surface--inline > astra-icon {
  flex: none;
  margin-block-start: var(--status-icon-nudge);
  color: var(--status-ink);
}

.status-surface--inline > :not(astra-icon):not(.status-surface__icon) {
  min-width: 0;
  overflow-wrap: anywhere;
  margin: 0;
}

.form-msg {
  margin: 0;
  font-size: var(--text-sm);
  overflow-wrap: anywhere;
}

.form-msg:empty {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  pointer-events: none;
}

.form-msg--error {
  color: var(--danger-ink);
}

.form-msg--success {
  color: var(--success-ink);
}

.form-msg--muted {
  color: var(--text-secondary);
}

:root {
  /* HOW LONG THE HOLD LASTS BEFORE THE PAGE GIVES UP AND SHOWS EVERYTHING.
     Not 3s-because-it-was-3s: measured. On the estate's own pages the module
     graph resolves in ~600ms on a warm desktop connection and 15.2s on Slow 4G,
     so no single number both "never fires on a good load" and "fires quickly on
     a broken one". Any number at all is only safe because the revealed state is
     now a labelled, correctly-laid-out page rather than a text dump — see the
     header. Measured at 4s, the reveal landed 7.4s into a 15.5s load and the
     operator lost the "this is loading" reading of the skeleton, getting a page
     that looked finished and empty for the eight seconds that followed. 8s keeps
     the skeleton through the first half of a genuinely slow load, never fires at
     all on a warm one, and still puts a page whose script is never coming in
     front of a reader inside eight seconds. */
  --astra-pre-upgrade-reveal: 8s;
  /* THE SECOND TIMER, AND WHY THERE ARE TWO. Putting a control's label back
     inside a box that was already reserved moves nothing (measured: 0.0001 of
     layout shift). Putting the SHELL CHROME back does — the nav is in normal
     flow, so revealing it pushes the page down 268px, which on a slow-but-working
     load would be a large shift for nothing, since the shell was going to arrive
     a few seconds later and place it properly anyway. So the chrome and the
     hidden tab panels — everything whose reveal costs the reader a jump — waits
     on a second, deliberately pessimistic timer.
     20s is chosen against the measurement rather than by feel: the slowest
     upgrade on the estate's own pages was 16,409ms with core.css applying at
     ~2,700ms, so anything under ~13.7s fires on a load that was going to succeed.
     Waiting is nearly free here: the page's own content is readable throughout,
     and only inert chrome and the non-first tab panels are behind this timer. */
  --astra-pre-upgrade-chrome-reveal: 20s;
  /* The plate the held controls paint. --surface-sunken is one step down from
     the page in light (neutral-100 on neutral-50) and one step up in dark
     (a well over neutral-900), so the placeholder reads as a recess in BOTH
     themes and can never come out page-on-page. */
  --astra-pre-upgrade-plate: var(--surface-sunken);
}

@keyframes astra-pre-upgrade-reveal {
  to {
    color: inherit;
    background: none;
  }
}
@keyframes astra-pre-upgrade-restore {
  to {
    max-height: none;
    content-visibility: visible;
    color: inherit;
    background: none;
  }
}
@keyframes astra-pre-upgrade-retire {
  to {
    opacity: 0;
  }
}
astra-shell-admin:not(:defined), astra-shell-ninefive:not(:defined), astra-shell-partner:not(:defined), astra-shell-identity:not(:defined), astra-shell-invoice:not(:defined), astra-shell-auth:not(:defined) {
  display: block;
  position: relative;
  box-sizing: border-box;
  min-height: 100dvh;
  background: var(--surface-page);
  color: var(--text-primary);
}

astra-shell-admin:not(:defined), astra-shell-ninefive:not(:defined), astra-shell-partner:not(:defined), astra-shell-identity:not(:defined) {
  /* 12 frame + 60 topbar + 12 gap, then the page gutter .sh__main applies. */
  padding: calc(84px + var(--gutter)) calc(12px + var(--gutter));
}

/* The receipt. Measured at 1280px: frame 16px, topbar 16,16 1248x70, main at
   y=102, and the document itself centred at .sh__main's 44rem — a customer-facing
   page whose own sheet is narrower than the window, so centring it here is what
   stops the invoice sliding sideways when the shell arrives. */
astra-shell-invoice:not(:defined) {
  padding: 102px 16px;
}

/* THE PLACEHOLDER AND THE SETTLED PAGE HAVE TO AGREE ON WIDTH, and 44rem is only the
   shell's DEFAULT — `.sh__main` is what a page overrides when its document is wider.
   The public invoice viewer does exactly that (52rem, because it carries a line-items
   table) and the not-found page goes the other way (32rem), and this rule outranks both:
   `astra-shell-invoice:not(:defined) > :not([slot])` is (0,2,1) against a bare class's
   (0,1,0). Measured on one throttled load at 1280px, the bill sat at left=288 w=704 for
   fifteen seconds and then jumped to left=224 w=832 when the shell upgraded — a 128px
   widening and a 64px slide, on the page a customer reads a bill on.
   AND CLS DOES NOT SEE IT. A custom-element upgrade re-slots the light DOM, so the
   layout-instability API scored 0.0001 across that reflow: the instrument that would
   normally catch this is blind to it, which is why the width is published as a property
   the page can state once instead of a number repeated in two places. */
astra-shell-invoice:not(:defined) > :not([slot]) {
  display: block;
  max-width: var(--astra-pre-upgrade-max, 44rem);
  margin-inline: auto;
}

/* THE AUTH SHELL IS NOT A FRAME AND MUST NOT BE GIVEN ONE. It is a two-column
   split — a brand panel beside the form, both full-bleed, no topbar card and no
   rail (measured: the form panel is x=640..1280, y=0..800). A 60px plate across
   the top of a login page is chrome the page does not have, so it is excluded
   from the reservation and from the plates below.
   It gets a reservation of its own instead, because a login page is where every
   session in the estate starts and it loads the same 2.7MB module graph as any
   other page. The numbers are the auth layout's own: two equal columns
   (`minmax(0,1fr) minmax(0,1fr)`), the form column centred both ways at
   `.sh__form-inner`'s 24rem, and the split collapsing at 767.98px — which puts
   the form at x=768 w=384 on a 1280px window, exactly where the shell puts it. */
astra-shell-auth:not(:defined) {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-content: center;
  padding: var(--space-10) var(--space-8);
}

astra-shell-auth:not(:defined) > :not([slot]) {
  width: 100%;
  max-width: 24rem;
  margin-inline: auto;
}

@media (min-width: 768px) {
  astra-shell-auth:not(:defined) {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
  astra-shell-auth:not(:defined) > :not([slot]) {
    grid-column: 2;
  }
}
/* THE STACKED SHELLS PUT A NAV STRIP BETWEEN THE TOPBAR AND THE PAGE, and only
   when they were given one. astra-shell-partner's grid is `60px auto 1fr` — the
   `auto` row is the strip, which renders solely when the top-nav slot has
   content (`:host([data-has-nav])`, stamped by the shell on slotchange). The
   same shape carries `tabs` on astra-shell-identity.
   `:has()` reads exactly the condition the shell reads. Measured at 1280px:
   astra-shell-partner without a strip puts main at y=84, with one at y=142 — a
   46px card plus the frame's own 12px gap. Without this rule the payouts and
   dashboard pages travelled 58px on upgrade; with it they do not. */
astra-shell-partner:not(:defined):has(> [slot=top-nav]),
astra-shell-identity:not(:defined):has(> [slot=tabs]) {
  padding-block-start: calc(142px + var(--gutter));
}

/* The two sidebar shells also hold the rail column open. Below 1024px the rail
   becomes a drawer (`@media (max-width: 1023.98px)` in sidebar-shell-base) and
   the reservation goes with it. */
@media (min-width: 1024px) {
  astra-shell-admin:not(:defined),
  astra-shell-ninefive:not(:defined) {
    padding-inline-start: calc(256px + var(--gutter));
  }
}
/* The frame the operator recognises. Two neutral plates on the reserved space,
   so the pre-upgrade page reads as this application loading rather than as a
   document with strange margins. Drawn on the shell's own pseudo-elements: they
   cost no markup, they cannot be reached by a page, and they vanish the instant
   the element upgrades because the selector stops matching. They also retire on
   the failsafe — a permanent grey rail with no nav in it would be a lie. */
astra-shell-admin:not(:defined)::before, astra-shell-ninefive:not(:defined)::before, astra-shell-partner:not(:defined)::before, astra-shell-identity:not(:defined)::before,
astra-shell-admin:not(:defined)::after, astra-shell-ninefive:not(:defined)::after, astra-shell-partner:not(:defined)::after, astra-shell-identity:not(:defined)::after {
  content: "";
  position: absolute;
  background: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-surface);
  /* Retires on the CHROME timer, not the label one: the plates stand in for the
     chrome, so taking them away while the chrome is still held would leave a
     band of nothing where the topbar and rail should be. */
  animation: astra-pre-upgrade-retire 0s var(--astra-pre-upgrade-chrome-reveal) forwards;
}

astra-shell-admin:not(:defined)::before, astra-shell-ninefive:not(:defined)::before, astra-shell-partner:not(:defined)::before, astra-shell-identity:not(:defined)::before {
  inset: 12px 12px auto 12px;
  height: 60px;
}

astra-shell-admin:not(:defined)::after, astra-shell-ninefive:not(:defined)::after, astra-shell-partner:not(:defined)::after, astra-shell-identity:not(:defined)::after {
  /* Rail plate — OFF by default, and `content: none` rather than `width: 0`.
     A zero-width plate is not invisible: its 1px border still paints, which put
     a 2px hairline down the left edge of every stacked shell and of the sidebar
     shells under 1024px, where the rail is a drawer. The two shells that have a
     rail turn it back on below. */
  content: none;
  inset: 84px auto 12px 12px;
}

@media (min-width: 1024px) {
  astra-shell-admin:not(:defined)::after,
  astra-shell-ninefive:not(:defined)::after {
    content: "";
    width: 232px;
  }
}
/* THESE ARE THE ONE GROUP THAT IS NEVER PUT BACK, and that is deliberate rather
   than an oversight. A modal's body, the command palette's field, a dropdown's
   menu items, an <astra-option>, an <astra-table-column> — none of them is
   content an operator is missing. They are UI that has no place on the page until
   something opens it, or a declaration with nothing to read at all; dumping them
   into the middle of the document on a failed load reproduces exactly the text
   soup this file exists to remove. `display: none` with no failsafe is therefore
   the right treatment, and it is safe here because nothing in this list holds a
   sentence the page was trying to tell anyone. */
astra-modal:not(:defined), astra-command-palette:not(:defined), astra-toast-region:not(:defined), astra-toast:not(:defined), astra-tooltip:not(:defined), astra-table-column:not(:defined), astra-option:not(:defined), astra-option-group:not(:defined), astra-menu-item:not(:defined), astra-menu-divider:not(:defined), astra-nav-divider:not(:defined) {
  display: none;
}

astra-button:not(:defined), astra-badge:not(:defined), astra-avatar:not(:defined), astra-switch:not(:defined), astra-checkbox:not(:defined), astra-radio:not(:defined), astra-loading:not(:defined), astra-notification-bell:not(:defined), astra-theme-toggle:not(:defined), astra-workspace-switcher:not(:defined), astra-progress-ring:not(:defined), astra-dropdown:not(:defined), astra-user-menu:not(:defined),
astra-input:not(:defined), astra-textarea:not(:defined), astra-select:not(:defined), astra-combobox:not(:defined), astra-number-input:not(:defined), astra-date-picker:not(:defined), astra-date-range-picker:not(:defined), astra-time-picker:not(:defined), astra-otp-input:not(:defined), astra-tag-input:not(:defined), astra-slider:not(:defined), astra-color-picker:not(:defined), astra-file-upload:not(:defined), astra-media-picker:not(:defined), astra-content-picker:not(:defined), astra-progress-bar:not(:defined),
astra-table:not(:defined), astra-chart:not(:defined), astra-calendar:not(:defined), astra-kanban:not(:defined), astra-chat:not(:defined), astra-audio:not(:defined), astra-audio-trimmer:not(:defined), astra-video:not(:defined), astra-rich-text:not(:defined), astra-batch-progress:not(:defined), astra-batch-review:not(:defined), astra-empty-state:not(:defined), astra-pagination:not(:defined), astra-stats-card:not(:defined), astra-skeleton:not(:defined), astra-notification-item:not(:defined) {
  box-sizing: border-box;
  color: transparent;
  background: var(--astra-pre-upgrade-plate);
  border-radius: var(--radius-control);
  animation: astra-pre-upgrade-reveal 0s var(--astra-pre-upgrade-reveal) forwards;
}

astra-button:not(:defined), astra-badge:not(:defined), astra-avatar:not(:defined), astra-switch:not(:defined), astra-checkbox:not(:defined), astra-radio:not(:defined), astra-loading:not(:defined), astra-notification-bell:not(:defined), astra-theme-toggle:not(:defined), astra-workspace-switcher:not(:defined), astra-progress-ring:not(:defined), astra-dropdown:not(:defined), astra-user-menu:not(:defined) {
  display: inline-block;
  vertical-align: middle;
  min-height: 2.75rem;
  min-width: 2.75rem;
}

/* The widgets are here too, and they were not at first — a reservation is only a
   reservation if the box can take it. `min-height` does not apply to a
   non-replaced INLINE box, and an un-upgraded custom element is inline by
   default, so `astra-empty-state { min-height: 12rem }` did nothing at all on the
   invoice not-found page: 0px where 192px was asked for. It looked correct
   everywhere else purely because `main.page-stack` is a flex container and flex
   blockifies its items. */
astra-input:not(:defined), astra-textarea:not(:defined), astra-select:not(:defined), astra-combobox:not(:defined), astra-number-input:not(:defined), astra-date-picker:not(:defined), astra-date-range-picker:not(:defined), astra-time-picker:not(:defined), astra-otp-input:not(:defined), astra-tag-input:not(:defined), astra-slider:not(:defined), astra-color-picker:not(:defined), astra-file-upload:not(:defined), astra-media-picker:not(:defined), astra-content-picker:not(:defined), astra-progress-bar:not(:defined),
astra-table:not(:defined), astra-chart:not(:defined), astra-calendar:not(:defined), astra-kanban:not(:defined), astra-chat:not(:defined), astra-audio:not(:defined), astra-audio-trimmer:not(:defined), astra-video:not(:defined), astra-rich-text:not(:defined), astra-batch-progress:not(:defined), astra-batch-review:not(:defined), astra-empty-state:not(:defined), astra-pagination:not(:defined), astra-stats-card:not(:defined), astra-skeleton:not(:defined), astra-notification-item:not(:defined) {
  display: block;
  min-height: 2.75rem;
}

/* Sized controls reserve their own height. --control-height is set INSIDE each
   component (astra-input .control--sm etc.), so the ladder is repeated here for
   the one moment the component cannot answer for itself: xs 1.75 / sm 2.25 /
   md 2.75 / lg 3.25rem. A field carrying a `label` also reserves the label row
   above it (--text-sm line + --space-1 gap), which is what stops a labelled form
   from shifting every row down on upgrade. */
astra-button:not(:defined)[size=xs], astra-badge:not(:defined)[size=xs], astra-avatar:not(:defined)[size=xs], astra-switch:not(:defined)[size=xs], astra-checkbox:not(:defined)[size=xs], astra-radio:not(:defined)[size=xs], astra-loading:not(:defined)[size=xs], astra-notification-bell:not(:defined)[size=xs], astra-theme-toggle:not(:defined)[size=xs], astra-workspace-switcher:not(:defined)[size=xs], astra-progress-ring:not(:defined)[size=xs], astra-dropdown:not(:defined)[size=xs], astra-user-menu:not(:defined)[size=xs],
astra-input:not(:defined)[size=xs], astra-textarea:not(:defined)[size=xs], astra-select:not(:defined)[size=xs], astra-combobox:not(:defined)[size=xs], astra-number-input:not(:defined)[size=xs], astra-date-picker:not(:defined)[size=xs], astra-date-range-picker:not(:defined)[size=xs], astra-time-picker:not(:defined)[size=xs], astra-otp-input:not(:defined)[size=xs], astra-tag-input:not(:defined)[size=xs], astra-slider:not(:defined)[size=xs], astra-color-picker:not(:defined)[size=xs], astra-file-upload:not(:defined)[size=xs], astra-media-picker:not(:defined)[size=xs], astra-content-picker:not(:defined)[size=xs], astra-progress-bar:not(:defined)[size=xs] {
  min-height: 1.75rem;
}

astra-button:not(:defined)[size=sm], astra-badge:not(:defined)[size=sm], astra-avatar:not(:defined)[size=sm], astra-switch:not(:defined)[size=sm], astra-checkbox:not(:defined)[size=sm], astra-radio:not(:defined)[size=sm], astra-loading:not(:defined)[size=sm], astra-notification-bell:not(:defined)[size=sm], astra-theme-toggle:not(:defined)[size=sm], astra-workspace-switcher:not(:defined)[size=sm], astra-progress-ring:not(:defined)[size=sm], astra-dropdown:not(:defined)[size=sm], astra-user-menu:not(:defined)[size=sm],
astra-input:not(:defined)[size=sm], astra-textarea:not(:defined)[size=sm], astra-select:not(:defined)[size=sm], astra-combobox:not(:defined)[size=sm], astra-number-input:not(:defined)[size=sm], astra-date-picker:not(:defined)[size=sm], astra-date-range-picker:not(:defined)[size=sm], astra-time-picker:not(:defined)[size=sm], astra-otp-input:not(:defined)[size=sm], astra-tag-input:not(:defined)[size=sm], astra-slider:not(:defined)[size=sm], astra-color-picker:not(:defined)[size=sm], astra-file-upload:not(:defined)[size=sm], astra-media-picker:not(:defined)[size=sm], astra-content-picker:not(:defined)[size=sm], astra-progress-bar:not(:defined)[size=sm] {
  min-height: 2.25rem;
}

astra-button:not(:defined)[size=lg], astra-badge:not(:defined)[size=lg], astra-avatar:not(:defined)[size=lg], astra-switch:not(:defined)[size=lg], astra-checkbox:not(:defined)[size=lg], astra-radio:not(:defined)[size=lg], astra-loading:not(:defined)[size=lg], astra-notification-bell:not(:defined)[size=lg], astra-theme-toggle:not(:defined)[size=lg], astra-workspace-switcher:not(:defined)[size=lg], astra-progress-ring:not(:defined)[size=lg], astra-dropdown:not(:defined)[size=lg], astra-user-menu:not(:defined)[size=lg],
astra-input:not(:defined)[size=lg], astra-textarea:not(:defined)[size=lg], astra-select:not(:defined)[size=lg], astra-combobox:not(:defined)[size=lg], astra-number-input:not(:defined)[size=lg], astra-date-picker:not(:defined)[size=lg], astra-date-range-picker:not(:defined)[size=lg], astra-time-picker:not(:defined)[size=lg], astra-otp-input:not(:defined)[size=lg], astra-tag-input:not(:defined)[size=lg], astra-slider:not(:defined)[size=lg], astra-color-picker:not(:defined)[size=lg], astra-file-upload:not(:defined)[size=lg], astra-media-picker:not(:defined)[size=lg], astra-content-picker:not(:defined)[size=lg], astra-progress-bar:not(:defined)[size=lg] {
  min-height: 3.25rem;
}

astra-input:not(:defined)[label], astra-textarea:not(:defined)[label], astra-select:not(:defined)[label], astra-combobox:not(:defined)[label], astra-number-input:not(:defined)[label], astra-date-picker:not(:defined)[label], astra-date-range-picker:not(:defined)[label], astra-time-picker:not(:defined)[label], astra-otp-input:not(:defined)[label], astra-tag-input:not(:defined)[label], astra-slider:not(:defined)[label], astra-color-picker:not(:defined)[label], astra-file-upload:not(:defined)[label], astra-media-picker:not(:defined)[label], astra-content-picker:not(:defined)[label], astra-progress-bar:not(:defined)[label] {
  min-height: calc(4rem + var(--space-1));
}

/* Round things stay round. */
astra-badge:not(:defined),
astra-avatar:not(:defined),
astra-switch:not(:defined),
astra-progress-ring:not(:defined),
astra-loading:not(:defined) {
  border-radius: var(--radius-pill);
}

astra-badge:not(:defined) {
  min-height: 1.75rem;
  min-width: 1.75rem;
}

astra-avatar:not(:defined),
astra-progress-ring:not(:defined),
astra-loading:not(:defined),
astra-notification-bell:not(:defined),
astra-theme-toggle:not(:defined) {
  min-height: 2.25rem;
  min-width: 2.25rem;
}

/* Checkbox and radio are a control the size of a glyph followed by a label the
   page needs to read. The plate is the control; the label keeps its ink. */
astra-checkbox:not(:defined),
astra-radio:not(:defined) {
  background: none;
  color: inherit;
  min-height: 1.5rem;
  min-width: 0;
}

/* An icon is a glyph-sized hole in a line of text. It paints nothing — a plate
   here would put grey chips through every notice and menu label — but it holds
   its box, off the SAME --icon-size-* tokens astra-icon's own :host reads, so
   there is one set of numbers rather than an approximation of one.
   THIS IS THE EARLIEST-SETTLING RESERVATION ON THE PAGE and it earns its keep:
   astra-icon is imported inline in every template's <head> (to hand it the
   sprite URL) so it upgrades on its own, ~30ms after first paint, hundreds of
   times before components.js finishes. Measured on portal-shop-product-edit,
   guessing 1.25em here instead of the token moved a whole card column 450px at
   3,449ms — 0.140 of layout shift from one wrong length. */
astra-icon:not(:defined) {
  display: inline-block;
  line-height: 0;
  vertical-align: middle;
  inline-size: var(--icon-size-md);
  block-size: var(--icon-size-md);
}

astra-icon:not(:defined)[size=xs] {
  inline-size: var(--icon-size-xs);
  block-size: var(--icon-size-xs);
}

astra-icon:not(:defined)[size=sm] {
  inline-size: var(--icon-size-sm);
  block-size: var(--icon-size-sm);
}

astra-icon:not(:defined)[size=lg] {
  inline-size: var(--icon-size-lg);
  block-size: var(--icon-size-lg);
}

astra-icon:not(:defined)[size=xl] {
  inline-size: var(--icon-size-xl);
  block-size: var(--icon-size-xl);
}

/* A table's rows arrive from script on most pages, so its light DOM is empty and
   its height pre-upgrade is zero while its height after is hundreds of pixels.
   16rem is roughly a header plus five rows — enough that the page below it does
   not travel, not so much that a three-row table leaves a hole. */
astra-table:not(:defined),
astra-chart:not(:defined),
astra-kanban:not(:defined),
astra-chat:not(:defined),
astra-calendar:not(:defined),
astra-batch-review:not(:defined),
astra-media-picker:not(:defined),
astra-content-picker:not(:defined),
astra-rich-text:not(:defined),
astra-file-upload:not(:defined) {
  min-height: 16rem;
  border-radius: var(--radius-card);
}

/* Server-rendered rows are the exception: those pages put the real data in the
   light DOM, so there is nothing to reserve and everything to show. */
astra-table:not(:defined):has(astra-table-row) {
  min-height: 0;
  background: none;
  color: inherit;
}

astra-empty-state:not(:defined) {
  min-height: 12rem;
  border-radius: var(--radius-card);
}

astra-pagination:not(:defined),
astra-stats-card:not(:defined),
astra-progress-bar:not(:defined),
astra-notification-item:not(:defined),
astra-batch-progress:not(:defined) {
  min-height: 3rem;
}

astra-audio:not(:defined),
astra-audio-trimmer:not(:defined),
astra-video:not(:defined) {
  min-height: 6rem;
  border-radius: var(--radius-card);
}

astra-card:not(:defined), astra-accordion:not(:defined), astra-tabs:not(:defined), astra-tab-panel:not(:defined), astra-timeline:not(:defined), astra-stepper:not(:defined), astra-breadcrumbs:not(:defined), astra-radio-group:not(:defined), astra-table-row:not(:defined) {
  display: block;
}

/* The one shape worth painting early. A card is the estate's commonest container
   and an unpainted one is a paragraph floating on the page background; giving it
   its own surface is the difference between "the page is loading" and "the page
   is broken". Matched to astra-card's own resting treatment, not invented. */
/* --space-inset, NOT --space-inset-base. The ladder's default rung has no suffix
   (xs / sm / <none> / lg / xl in tokens/_spacing.scss), and the first draft here
   wrote `--space-inset-base`, which does not exist: the declaration was invalid at
   computed-value time and every held card had ZERO padding. It still looked
   plausible in a screenshot, which is exactly why it is worth writing down.
   The values track astra-card's own `.card__body` rungs, and matching them is not
   cosmetic: the checkout pages mount Stripe Elements inside a card, and Stripe
   measures the box it is given. A card whose inner width changes when it upgrades
   would resize the field under a mounted iframe (docs/SPACING-WORKLIST.md §6.1). */
astra-card:not(:defined) {
  box-sizing: border-box;
  background: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-card);
  padding: var(--space-inset);
}

astra-card:not(:defined)[padding=none],
astra-card:not(:defined)[variant=flush] {
  padding: 0;
}

astra-card:not(:defined)[padding=sm] {
  padding: var(--space-inset-sm);
}

astra-card:not(:defined)[padding=lg] {
  padding: var(--space-inset-xl);
}

/* A tabbed page renders EVERY panel into the document; the upgrade then collapses
   it to one. On royalties/reports that is four panels' worth of page that vanishes
   — the single largest layout shift in the estate. Showing only the first panel
   pre-upgrade is not always the right panel (a page can open on another), but it
   is always the right AMOUNT of page, and the correct one arrives a moment later. */
astra-tab-panel:not(:defined) ~ astra-tab-panel:not(:defined) {
  min-height: 0;
  max-height: 0;
  overflow: hidden;
  content-visibility: hidden;
  contain-intrinsic-size: 0;
  animation: astra-pre-upgrade-restore 0s var(--astra-pre-upgrade-chrome-reveal) forwards;
}

/* Slotted card furniture reads in source order pre-upgrade, which is header,
   body, footer — already the right order. Nothing to do but keep it block. */
astra-card:not(:defined) > [slot] {
  display: block;
}

/* THE CHROME IS NOT WHERE IT LOOKS LIKE IT IS. Everything carrying a `slot`
   attribute is destined for the topbar or the rail; in the document it sits
   ahead of the page as a run of inline text and a 32px logo SVG. It is also
   INERT — an <astra-nav-item> is not an <a>, so revealing it early offers an
   operator navigation that cannot navigate. Held until the shell can place it,
   then restored by the failsafe so a page whose script never arrives still
   shows what its chrome said. Held to zero height rather than to `display: none`
   for exactly that reason — see the restore keyframe. */
astra-shell-admin:not(:defined) > [slot], astra-shell-ninefive:not(:defined) > [slot], astra-shell-partner:not(:defined) > [slot], astra-shell-identity:not(:defined) > [slot], astra-shell-invoice:not(:defined) > [slot], astra-shell-auth:not(:defined) > [slot] {
  /* !important, for the same reason base/_base.scss uses it on [hidden]: this
     rule has to beat markup it does not own. NineFive's topbar brand is written
     `<span slot="logo" style="display:inline-flex; …">`, and an inline style beats
     any author rule without it — so the span stayed inline-level, and although its
     own height was 0 the LINE BOX it sat on was not: every ninefive page started
     23px lower than it finished. Only `display` needs the escape; everything below
     is uncontested. */
  display: block !important;
  /* min-height: 0 IS LOAD-BEARING, not tidiness. The used height of a box is
     max(min-height, min(max-height, height)) — MIN WINS OVER MAX — so a slotted
     <astra-input> carrying the 2.75rem control reservation from section 3 stayed
     44px tall through `max-height: 0` and the four topbar controls stacked up
     into 168px of grey plates above the page. Measured, not reasoned: the shell
     children read 44/44/36/44px with this line missing. */
  min-height: 0;
  min-width: 0;
  max-height: 0;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  overflow: hidden;
  content-visibility: hidden;
  contain-intrinsic-size: 0;
  animation: astra-pre-upgrade-restore 0s var(--astra-pre-upgrade-chrome-reveal) forwards;
}
