/* Entry point. The layer order below is the single source of truth for
   priority — later layers beat earlier ones regardless of specificity.
   Add a stylesheet by adding one @import line, in its layer's group. */

@layer reset, base, layouts, components, pages, utilities;

/* Tokens carry no layer: they only declare custom properties. */
/* Tier 1 — primitives. Raw values, named for what they ARE.
   Nothing here has an opinion about where it is used. Components must never
   reference these directly; they go through tokens/semantic.css.

   PRD 016: repainted to the Piña Digital visual system
   (prds/016-portal-redesign/docs/01-token-mapping.md has the full
   value-by-value mapping and rationale). Primary action moved from green to
   purple; green is kept, repurposed for the success status hue only —
   the source design's own --ok is green, so the name still fits its value. */

:root {
  /* ── Warm neutrals ─────────────────────────────────────────────────── */
  --p-paper-000: #FFFFFF;
  --p-paper-050: #F6F3ED;
  --p-paper-100: #FBFAF7;
  --p-paper-150: #F6F3ED;
  --p-paper-200: #EFEAE2;
  --p-paper-250: #FFFFFF;
  --p-paper-300: #E7E1D8;
  --p-paper-400: #E7E1D8;
  --p-paper-500: #D5CDC1;
  --p-paper-600: #D5CDC1;
  --p-paper-700: #B7AF9E;

  /* ── Ink ───────────────────────────────────────────────────────────── */
  --p-ink-900: #1B1725;
  --p-ink-700: #57506B;
  --p-ink-500: #8B839E;
  --p-ink-400: #A79FB5;

  /* ── Purple — actions, active state, links (was green) ───────────────── */
  --p-purple-800: #5316B8;
  --p-purple-700: #6A1FE0;
  --p-purple-tint: #F2EBFF;
  --p-purple-tint-2: #E9DBFF;

  /* ── Green — success only ─────────────────────────────────────────── */
  --p-green-700: #0F7A55;
  --p-green-150: #E3F3EB;

  /* ── Status hues ───────────────────────────────────────────────────── */
  --p-amber-700: #A96300;
  --p-amber-100: #FBEFDA;
  --p-red-700: #C4104A;
  --p-red-100: #FCE6ED;
  --p-pink-700: #E01050;
  --p-pink-100: #FCE3EC;
  --p-violet-700: #5B4A9E;
  --p-violet-100: #ECE7F8;

  /* ── Brand accents — reserved for currency/decorative use only.
        Not wired to any semantic role by this PRD (see decision #1). ──── */
  --p-brand-amber: #FCB301;
  --p-brand-orange: #FC5001;
  --p-brand-cyan: #01A8FB;
  --p-brand-teal: #01BCA4;

  /* ── Type ──────────────────────────────────────────────────────────── */
  --p-family-sans: "Figtree", system-ui, -apple-system, sans-serif;
  --p-family-display: "Fredoka", "Figtree", system-ui, sans-serif;
  --p-family-mono: "IBM Plex Mono", ui-monospace, "Cascadia Mono", monospace;

  --p-size-10: 10px;
  --p-size-105: 10.5px;
  --p-size-11: 11px;
  --p-size-115: 11.5px;
  --p-size-12: 12px;
  --p-size-125: 12.5px;
  --p-size-13: 13px;
  --p-size-135: 13.5px;
  --p-size-14: 14px;
  --p-size-145: 14.5px;
  --p-size-25: clamp(22px, 20px + 1vw, 28px);
  --p-size-27: clamp(24px, 21px + 1.3vw, 32px);

  --p-weight-regular: 400;
  --p-weight-medium: 500;
  --p-weight-semibold: 600;

  --p-leading-snug: 1.15;
  --p-leading-tight: 1.2;
  --p-leading-body: 1.55;

  --p-track-title: -0.02em;
  --p-track-figure: -0.028em;
  --p-track-brand: -0.01em;
  --p-track-mono: 0.02em;
  --p-track-code: 0.04em;
  --p-track-eyebrow: 0.1em;
  --p-track-label: 0.13em;
  --p-track-group: 0.14em;

  /* ── Spacing rhythm ────────────────────────────────────────────────── */
  --p-space-1: 1px;
  --p-space-2: 2px;
  --p-space-4: 4px;
  --p-space-6: 6px;
  --p-space-8: 8px;
  --p-space-10: 10px;
  --p-space-12: 12px;
  --p-space-14: 14px;
  --p-space-16: 16px;
  --p-space-18: 18px;
  --p-space-20: 20px;
  --p-space-26: 26px;
  --p-space-32: 32px;
  --p-space-72: 72px;

  /* ── Radius ────────────────────────────────────────────────────────── */
  --p-radius-3: 3px;
  --p-radius-4: 5px;
  --p-radius-5: 7px;
  --p-radius-6: 10px;
  --p-radius-7: 12px;
  --p-radius-9: 16px;
  --p-radius-pill: 999px;
  --p-radius-circle: 50%;

  /* ── Elevation — two tiers: resting surfaces, and floating/overlay
        surfaces. Overturns the prior "no elevation" rule (decision #5). ── */
  --p-shadow-thumb: 0 1px 2px rgb(27 23 37 / 8%), 0 8px 20px -14px rgb(27 23 37 / 20%);
  --p-shadow-popover: 0 2px 6px rgb(27 23 37 / 8%), 0 18px 36px -20px rgb(27 23 37 / 28%);

  /* ── Control and frame sizes ───────────────────────────────────────── */
  --p-control-26: 26px;
  --p-control-28: 28px;
  --p-control-32: 32px;
  --p-control-34: 34px;
  --p-control-38: 38px;
  --p-topbar-h: 56px;
  --p-sidebar-w: 248px;
  --p-sidebar-w-collapsed: 74px;
  --p-content-max: 1420px;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --p-paper-000: #1D1826;
    --p-paper-050: #251F31;
    --p-paper-100: #141019;
    --p-paper-150: #251F31;
    --p-paper-200: #312A44;
    --p-paper-250: #1D1826;
    --p-paper-300: #312A44;
    --p-paper-400: #312A44;
    --p-paper-500: #443A5C;
    --p-paper-600: #443A5C;
    --p-paper-700: #6E6482;

    --p-ink-900: #F4F0FA;
    --p-ink-700: #ADA3C0;
    --p-ink-500: #8079A0;
    --p-ink-400: #6B6488;

    --p-purple-800: #C6ABFF;
    --p-purple-700: #A87CFF;
    --p-purple-tint: #2A1C46;
    --p-purple-tint-2: #382857;

    --p-green-700: #3FBF8B;
    --p-green-150: #122C22;

    --p-amber-700: #E0A02A;
    --p-amber-100: #33260E;
    --p-red-700: #F2547E;
    --p-red-100: #39131F;
    --p-pink-700: #FF6B9B;
    --p-pink-100: #3A1226;
    --p-violet-700: #B0A2E8;
    --p-violet-100: #2A2447;

    --p-shadow-thumb: 0 1px 2px rgb(0 0 0 / 30%), 0 8px 20px -14px rgb(0 0 0 / 55%);
    --p-shadow-popover: 0 2px 6px rgb(0 0 0 / 35%), 0 18px 36px -20px rgb(0 0 0 / 65%);
  }
}

:root[data-theme="dark"] {
  --p-paper-000: #1D1826;
  --p-paper-050: #251F31;
  --p-paper-100: #141019;
  --p-paper-150: #251F31;
  --p-paper-200: #312A44;
  --p-paper-250: #1D1826;
  --p-paper-300: #312A44;
  --p-paper-400: #312A44;
  --p-paper-500: #443A5C;
  --p-paper-600: #443A5C;
  --p-paper-700: #6E6482;

  --p-ink-900: #F4F0FA;
  --p-ink-700: #ADA3C0;
  --p-ink-500: #8079A0;
  --p-ink-400: #6B6488;

  --p-purple-800: #C6ABFF;
  --p-purple-700: #A87CFF;
  --p-purple-tint: #2A1C46;
  --p-purple-tint-2: #382857;

  --p-green-700: #3FBF8B;
  --p-green-150: #122C22;

  --p-amber-700: #E0A02A;
  --p-amber-100: #33260E;
  --p-red-700: #F2547E;
  --p-red-100: #39131F;
  --p-pink-700: #FF6B9B;
  --p-pink-100: #3A1226;
  --p-violet-700: #B0A2E8;
  --p-violet-100: #2A2447;

  --p-shadow-thumb: 0 1px 2px rgb(0 0 0 / 30%), 0 8px 20px -14px rgb(0 0 0 / 55%);
  --p-shadow-popover: 0 2px 6px rgb(0 0 0 / 35%), 0 18px 36px -20px rgb(0 0 0 / 65%);
}

/* Tier 2 — semantic. Named for what they are FOR. The only tier a component
   file may reference. Restyling the app happens here.

   PRD 016: dark mode is handled entirely at the primitive layer
   (tokens/primitives.css) — every --p-* value already swaps under
   `@media (prefers-color-scheme: dark)` (guarded by
   `:root:not([data-theme="light"])`) and `:root[data-theme="dark"]`. Since
   every token below is a `var(--p-*)` reference and never a literal, that
   single point of variation is sufficient — this file needs no dark block
   of its own. (The scheme originally sketched here — duplicating semantic
   values across three states — is unnecessary once the layer beneath it
   already resolves per-theme; kept as one indirection, not two.) */

:root {
  /* ── Surfaces ──────────────────────────────────────────────────────── */
  --s-ground: var(--p-paper-100);          /* app background */
  --s-surface: var(--p-paper-000);         /* cards, table body, top bar */
  --s-surface-sunken: var(--p-paper-050);  /* table/card headers, row hover */
  --s-surface-nav: var(--p-paper-250);     /* sidebar */
  --s-surface-inset: var(--p-paper-150);   /* input rest, ghost hover, nav-item hover */

  /* ── Lines ─────────────────────────────────────────────────────────── */
  --s-border: var(--p-paper-400);          /* card and section borders */
  --s-border-strong: var(--p-paper-600);   /* interactive: buttons, inputs */
  --s-border-subtle: var(--p-paper-300);   /* row separators */
  --s-border-faint: var(--p-paper-200);    /* field separators inside cards */

  /* ── Text ──────────────────────────────────────────────────────────── */
  --s-text: var(--p-ink-900);
  --s-text-muted: var(--p-ink-700);
  --s-text-soft: var(--p-ink-500);
  --s-text-faint: var(--p-ink-400);
  --s-mark-faint: var(--p-paper-700);      /* low-priority dots, disabled marks */

  /* ── Action ────────────────────────────────────────────────────────── */
  --s-primary: var(--p-purple-700);
  --s-primary-hover: var(--p-purple-800);
  --s-primary-tint: var(--p-purple-tint);    /* active nav background */
  --s-primary-tint-2: var(--p-purple-tint-2); /* avatar background */
  --s-on-primary: var(--p-paper-000);
  --s-focus: var(--p-purple-700);
  --s-selection: var(--p-purple-tint);

  /* ── Status — background/text pairs. The label always carries the
        meaning; colour never carries it alone. ───────────────────────── */
  --s-success: var(--p-green-700);
  --s-success-bg: var(--p-green-150);
  --s-warning: var(--p-amber-700);
  --s-warning-bg: var(--p-amber-100);
  --s-danger: var(--p-red-700);
  --s-danger-bg: var(--p-red-100);
  --s-accent: var(--p-pink-700);
  --s-accent-bg: var(--p-pink-100);
  --s-info: var(--p-violet-700);
  --s-info-bg: var(--p-violet-100);
  --s-neutral: var(--p-ink-700);
  --s-neutral-bg: var(--p-paper-300);
  --s-presented: var(--p-purple-700);
  --s-presented-bg: var(--p-purple-tint);

  --s-scroll-thumb: var(--p-paper-500);

  /* ── Type roles ────────────────────────────────────────────────────── */
  --s-font: var(--p-family-sans);
  --s-font-display: var(--p-family-display); /* h1/h2 only — see base/elements.css */
  --s-font-mono: var(--p-family-mono);

  --s-text-body: var(--p-size-14);
  --s-text-row: var(--p-size-135);
  --s-text-meta: var(--p-size-12);
  --s-text-micro: var(--p-size-115);
  --s-text-eyebrow: var(--p-size-105);
  --s-text-title: var(--p-size-25);
  --s-text-figure: var(--p-size-27);
  --s-text-card-title: var(--p-size-14);

  /* ── Spacing roles ─────────────────────────────────────────────────── */
  --s-space-2xs: var(--p-space-4);
  --s-space-xs: var(--p-space-8);
  --s-space-sm: var(--p-space-12);
  --s-space-md: var(--p-space-16);
  --s-space-lg: var(--p-space-20);
  --s-space-xl: var(--p-space-32);

  --s-card-pad-x: var(--p-space-18);
  --s-card-pad-y: var(--p-space-14);
  --s-content-pad-x: var(--p-space-32);
  --s-content-pad-y: var(--p-space-26);

  /* ── Radius roles ──────────────────────────────────────────────────── */
  --s-radius-badge: var(--p-radius-pill);
  --s-radius-small: var(--p-radius-5);
  --s-radius-control: var(--p-radius-6);
  --s-radius-card: var(--p-radius-7);
  --s-radius-track: var(--p-radius-5);
  --s-radius-pill: var(--p-radius-pill);
  --s-radius-bar: var(--p-radius-3);
  --s-radius-avatar: var(--p-radius-circle);

  /* ── Elevation — two tiers now (decision #5): resting surfaces, and
        floating/overlay surfaces. ─────────────────────────────────────── */
  --s-shadow-raised: var(--p-shadow-thumb);
  --s-shadow-popover: var(--p-shadow-popover);

  /* ── Density. One token drives row height across every list. ───────── */
  --s-row-pad: var(--p-space-12);
}

:root[data-density="compacta"] {
  --s-row-pad: 7px;
}


/* PRD 016: the sans body face moved to Figtree and the new Fredoka display
   face, both loaded from Google Fonts via <link> tags in
   templates/layouts/base.html — see app/frontend.py's DEFAULT_CSP for the
   documented, scoped style-src/font-src exception this required. IBM Plex
   Mono stays self-hosted (no mono face in the new design; unaffected).

   URLs are absolute on purpose: the production bundler inlines this file into
   css/main.bundle.css without rewriting url() references, so a relative path
   would resolve from the wrong directory there. Fonts are served unversioned
   (no ?v=) and revalidate via ETag — they change ~never. */

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/ibmplexmono-400.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/static/fonts/ibmplexmono-500.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/static/fonts/ibmplexmono-600.woff2") format("woff2");
}

@layer reset {
  *,
  *::before,
  *::after { box-sizing: border-box; }

  html { -webkit-text-size-adjust: 100%; }

  body,
  h1, h2, h3, h4, p, figure, blockquote, dl, dd { margin: 0; }

  ul[role="list"],
  ol[role="list"] { list-style: none; margin: 0; padding: 0; }

  img, picture, svg, video, canvas { display: block; max-inline-size: 100%; }

  input, button, textarea, select {
    font: inherit;
    color: inherit;
  }

  button { cursor: pointer; }

  [hidden] { display: none !important; }

  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
      animation-duration: 0.01ms !important;
      transition-duration: 0.01ms !important;
      scroll-behavior: auto !important;
    }
  }
}

/* Bare-element defaults. Anything that needs a class belongs in components/. */

@layer base {
  body {
    background: var(--s-ground);
    color: var(--s-text);
    font-family: var(--s-font);
    font-size: var(--s-text-body);
    line-height: var(--p-leading-body);
    -webkit-font-smoothing: antialiased;
    text-wrap: pretty;
  }

  /* Display face on headings only (spec: "Solo en titulares... nunca en
     cifras/texto") — h3 stays in the body face, since card/section titles
     read as UI chrome, not a display headline. */
  h1, h2 {
    font-family: var(--s-font-display);
    font-weight: var(--p-weight-semibold);
    line-height: var(--p-leading-tight);
    text-wrap: balance;
  }
  h3 {
    font-weight: var(--p-weight-semibold);
    line-height: var(--p-leading-tight);
    text-wrap: balance;
  }

  h1 { font-size: var(--s-text-title); letter-spacing: var(--p-track-title); }
  h2 { font-size: var(--s-text-card-title); }

  a {
    color: var(--s-primary);
    text-decoration: none;
  }

  a:hover { color: var(--s-primary-hover); text-decoration: underline; }

  code, kbd, samp, pre, .mono {
    font-family: var(--s-font-mono);
    letter-spacing: var(--p-track-mono);
  }

  /* The prototype signals focus with border colour alone, which is not enough
     for keyboard users. Every focusable element gets a real ring here. */
  :focus-visible {
    outline: 2px solid var(--s-focus);
    outline-offset: 2px;
  }

  input, select, textarea {
    background: var(--s-surface);
    border: 1px solid var(--s-border-strong);
    border-radius: var(--s-radius-control);
    padding: 0 11px;
    block-size: var(--p-control-34);
    inline-size: 100%;
    font-size: var(--p-size-13);
  }

  textarea { block-size: auto; padding: var(--p-space-8) var(--p-space-10); }

  input:focus, select:focus, textarea:focus { border-color: var(--s-primary); }

  input[type="checkbox"] {
    block-size: 14px;
    inline-size: 14px;
    padding: 0;
    accent-color: var(--s-primary);
  }

  ::selection { background: var(--s-selection); }

  ::-webkit-scrollbar { inline-size: 11px; block-size: 11px; }
  ::-webkit-scrollbar-thumb {
    background: var(--s-scroll-thumb);
    border: 3px solid var(--s-ground);
    border-radius: var(--p-radius-9);
  }
  ::-webkit-scrollbar-track { background: transparent; }
}


/* The frame: a fixed top bar over a sidebar + main grid, each scrolling
   independently so the top bar never scrolls away.

   PRD 016: the sidebar now collapses to a 74px icon rail
   (`.app-shell.is-collapsed`, toggled + persisted by
   static/js/modules/sidebar.js) and, below 860px, becomes a slide-in
   drawer with a scrim — replacing the old "just hide it" placeholder. */

@layer layouts {
  .app-shell {
    /* dvh, not vh: on mobile browsers 100vh is the *large* viewport, measured
       with the URL bar retracted, so a shell sized in vh outgrows the visible
       area and .app-shell__main's scroll box runs off the bottom.
       pages/public.css already uses dvh; the shell never got it (PRD 017 #43). */
    block-size: 100vh;
    block-size: 100dvh;
    display: flex;
    flex-direction: column;
    /* Backstop, so the next control added to the top bar degrades into
       clipping rather than reintroducing a sideways-scrolling document.
       `clip`, not `hidden`: hidden establishes a scroll container, which would
       silently break position:sticky for any descendant. The fixed-position
       drawer is unaffected either way — its containing block is the viewport,
       not this box. */
    overflow-x: clip;
  }

  .app-shell__topbar {
    flex: none;
    block-size: var(--p-topbar-h);
    display: flex;
    align-items: center;
    gap: var(--p-space-14);
    padding: 0 var(--p-space-18) 0 var(--p-space-20);
    background: color-mix(in srgb, var(--s-ground) 88%, transparent);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-block-end: 1px solid var(--s-border);
    position: relative;
    z-index: 30;
  }

  .app-shell__body {
    display: grid;
    grid-template-columns: var(--p-sidebar-w) minmax(0, 1fr);
    flex: 1;
    min-block-size: 0;
    transition: grid-template-columns 0.16s ease;
  }

  .app-shell.is-collapsed .app-shell__body {
    grid-template-columns: var(--p-sidebar-w-collapsed) minmax(0, 1fr);
  }

  .app-shell__sidebar {
    background: var(--s-surface-nav);
    border-inline-end: 1px solid var(--s-border);
    padding: var(--p-space-14) var(--p-space-12) var(--p-space-26);
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    overflow-x: hidden;
  }

  .app-shell__main {
    overflow-y: auto;
    padding: var(--s-content-pad-y) var(--s-content-pad-x) var(--p-space-72);
  }

  .app-shell__content {
    max-inline-size: var(--p-content-max);
    margin-inline: auto;
  }

  .topbar-user {
    display: flex;
    align-items: center;
    gap: var(--p-space-10);
  }

  .topbar-user__logout { display: contents; }

  /* The compact "copy my referral link" control (PRD 016), styled after
     the source design's pill-shaped `.linkbtn`. */
  .topbar-invite {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding: 3px 3px 3px 11px;
    background: var(--s-surface);
    border: 1px solid var(--s-border-strong);
    border-radius: var(--s-radius-pill);
    box-shadow: var(--s-shadow-raised);
  }
  .topbar-invite__code {
    font-size: var(--s-text-meta);
    color: var(--s-text-muted);
    max-inline-size: 12ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .topbar-invite__icon {
    display: grid;
    place-items: center;
    inline-size: var(--p-control-26);
    block-size: var(--p-control-26);
    flex: none;
    border-radius: var(--s-radius-avatar);
    border: none;
    background: none;
    color: var(--s-text-soft);
    cursor: pointer;
  }
  .topbar-invite__icon:hover { background: var(--s-surface-inset); color: var(--s-text); }

  /* The copy control carries its label, so unlike the WhatsApp icon beside it
     this one sizes to content rather than to a fixed square. A bare icon told
     nobody what the control does — least of all the collaborators it exists
     for (invite-button-label). */
  .topbar-invite__btn {
    display: inline-flex;
    align-items: center;
    gap: var(--p-space-6);
    flex: none;
    block-size: var(--p-control-26);
    padding-inline: var(--p-space-8);
    border: none;
    border-radius: var(--s-radius-pill);
    background: none;
    color: var(--s-text-soft);
    cursor: pointer;
    font: inherit;
  }
  .topbar-invite__btn:hover { background: var(--s-surface-inset); color: var(--s-text); }

  /* `copy-link.js` toggles `is-copied` on whatever carries
     `data-copy-trigger` — the button, not the icon. A rule left on the icon
     would look right and silently never fire. */
  .topbar-invite__btn.is-copied { color: var(--s-success); }

  .topbar-invite__label {
    font-size: var(--s-text-meta);
    font-weight: var(--p-weight-semibold);
    white-space: nowrap;
  }

  @media (max-width: 620px) {
    .topbar-invite__code { display: none; }
    /* 38px of horizontal padding is a desktop luxury at this width. */
    .app-shell__topbar { padding-inline: var(--p-space-10); }
  }

  /* Below 620px the control is two icon buttons and nothing else — the same
     breakpoint that hides the code, because the label and the code are both
     the desktop detail. The width that buys back is what lets the WhatsApp
     share and the avatar stay on a phone, both of which earlier revisions
     dropped and neither of which needs to go. */
  @media (max-width: 620px) {
    /* Visually hidden, never `display: none`: the button is icon-only here and
       removing its only text would leave it with no accessible name. */
    .topbar-invite__label {
      position: absolute;
      inline-size: 1px;
      block-size: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
    }
    /* With the label gone the button collapses to the same square as the
       WhatsApp icon beside it, so the two read as a matched pair. */
    .topbar-invite__btn {
      inline-size: var(--p-control-26);
      padding-inline: 0;
      gap: 0;
    }
  }

  @media (max-width: 520px) {

    /* Below 520px the mark carries the brand on its own in the top bar.
       Scoped to the top bar deliberately: `.brand__name` appears in twelve
       templates, and an unscoped rule also blanked the wordmark on the login
       screen, the client portal, the public lead form and all five signing
       pages — none of which have a crowding problem.

       Visually hidden rather than `display: none`, because `.brand` is a link
       whose only text node is this span (`_topbar.html`, with `alt=""` on the
       mark and no aria-label). Removing it from the accessibility tree leaves
       the link with an empty accessible name — a WCAG 2.4.4 / 4.1.2 failure.
       Same recipe as `.u-visually-hidden`. */
    .app-shell__topbar .brand__name {
      position: absolute;
      inline-size: 1px;
      block-size: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
    }
  }

  .theme-toggle {
    display: grid;
    place-items: center;
    inline-size: var(--p-control-32);
    block-size: var(--p-control-32);
    flex: none;
    border-radius: var(--s-radius-avatar);
    border: 1px solid transparent;
    background: transparent;
    color: var(--s-text-soft);
  }
  .theme-toggle:hover { background: var(--s-surface-inset); color: var(--s-text); }
  .theme-toggle svg { grid-area: 1 / 1; }
  .theme-toggle__moon { display: none; }
  /* Dark mode shows the moon icon (what you're currently in), light mode
     shows the sun — same convention most theme toggles use. */
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .theme-toggle__sun { display: none; }
    :root:not([data-theme="light"]) .theme-toggle__moon { display: block; }
  }
  :root[data-theme="dark"] .theme-toggle__sun { display: none; }
  :root[data-theme="dark"] .theme-toggle__moon { display: block; }

  /* ── Collapse-to-rail (desktop ≥ 860px) ─────────────────────────────── */
  .sidebar-collapse {
    align-self: flex-end;
    display: grid;
    place-items: center;
    inline-size: 28px;
    block-size: 28px;
    margin-block-end: var(--p-space-8);
    border-radius: var(--s-radius-control);
    border: 1px solid transparent;
    background: transparent;
    color: var(--s-text-faint);
  }
  .sidebar-collapse:hover { background: var(--s-surface-inset); color: var(--s-text); }
  .sidebar-collapse svg { transition: transform 0.16s ease; }
  .app-shell.is-collapsed .sidebar-collapse { align-self: center; }
  .app-shell.is-collapsed .sidebar-collapse svg { transform: rotate(180deg); }

  .app-shell.is-collapsed .nav-group__label,
  .app-shell.is-collapsed .lbl,
  .app-shell.is-collapsed .nav-badge {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .app-shell.is-collapsed .nav-item {
    justify-content: center;
    padding-inline: 0;
  }

  /* ── Mobile drawer (< 860px) ─────────────────────────────────────────── */
  .burger { display: none; }

  .scrim {
    display: none;
    position: fixed;
    inset: 0;
    /* Pure black, not a ground-tinted value: at 45% it must read as a dim
       wash in both themes, and a dark tint over an already-dark ground
       (dark mode) would be nearly invisible. */
    background: rgb(0 0 0 / 45%);
    z-index: 55;
  }
  .scrim.is-open { display: block; }

  @media (max-width: 860px) {
    .app-shell__body,
    .app-shell.is-collapsed .app-shell__body {
      grid-template-columns: minmax(0, 1fr);
    }

    .app-shell__sidebar {
      position: fixed;
      /* A fixed element is laid out against the initial containing block, so
         `100dvh` on .app-shell cannot reach it — an earlier draft of PRD 017
         claimed one token fixed both symptoms and was wrong (caveat #61).
         `inset-block` alone resolves against the *large* viewport on mobile,
         putting the drawer's last nav item under the browser chrome. */
      inset-block: var(--p-topbar-h) auto;
      block-size: calc(100vh - var(--p-topbar-h));
      block-size: calc(100dvh - var(--p-topbar-h));
      inset-inline-start: 0;
      inline-size: 270px;
      z-index: 60;
      transform: translateX(-100%);
      transition: transform 0.2s ease;
      box-shadow: var(--s-shadow-popover);
    }
    .app-shell__sidebar.is-open { transform: none; }

    /* The rail-collapse visuals never apply on mobile — the drawer is
       either fully open or fully hidden, never a narrow rail. */
    .app-shell.is-collapsed .nav-group__label,
    .app-shell.is-collapsed .lbl,
    .app-shell.is-collapsed .nav-badge {
      position: static;
      inline-size: auto;
      block-size: auto;
      padding: revert;
      margin: revert;
      overflow: visible;
      clip-path: none;
      white-space: normal;
    }
    .app-shell.is-collapsed .nav-item { justify-content: flex-start; padding-inline: var(--p-space-10); }
    .sidebar-collapse { display: none; }

    .burger {
      display: grid;
      place-items: center;
      inline-size: 36px;
      block-size: 36px;
      flex: none;
      border-radius: var(--s-radius-control);
      border: 1px solid var(--s-border-strong);
      background: var(--s-surface);
      color: var(--s-text);
    }
    .burger:hover { background: var(--s-surface-inset); }
  }
}

/* Eyebrow → title → subtitle, with actions pushed right.
   The eyebrow states the module group, so the user always knows the lens. */

@layer layouts {
  .page-head {
    display: flex;
    align-items: flex-start;
    gap: var(--s-space-md);
    flex-wrap: wrap;
    margin-block-end: var(--p-space-20);
  }

  .page-head__eyebrow {
    font-size: var(--s-text-eyebrow);
    font-weight: var(--p-weight-semibold);
    letter-spacing: var(--p-track-group);
    text-transform: uppercase;
    color: var(--s-text-faint);
    margin-block-end: var(--p-space-6);
  }

  .page-head__subtitle {
    font-size: var(--s-text-row);
    color: var(--s-text-muted);
    max-inline-size: 70ch;
    margin-block-start: var(--p-space-6);
  }

  .page-head__actions {
    margin-inline-start: auto;
    display: flex;
    gap: var(--s-space-xs);
    flex-wrap: wrap;
  }
}


@layer components {
  .avatar {
    --avatar-size: 30px;

    display: grid;
    place-items: center;
    inline-size: var(--avatar-size);
    block-size: var(--avatar-size);
    flex: none;
    /* The brand's diagonal-split two-tone motif (design spec §3.1), the one
       place in this app it earns its keep: an "initials mark" is exactly
       what the motif was designed for. White text needs the saturated
       halves this implies, not the soft tint the rest of the app uses. */
    background: linear-gradient(135deg, var(--s-primary) 0 49.5%, var(--p-brand-cyan) 50.5% 100%);
    color: var(--s-on-primary);
    border-radius: var(--s-radius-avatar);
    font-size: var(--p-size-12);
    font-weight: var(--p-weight-semibold);
  }

  .avatar--sm { --avatar-size: 22px; font-size: var(--p-size-10); background: var(--s-neutral-bg); color: var(--s-text-muted); }
}

/* Status travels as a background/text pair plus a written label. Never colour
   alone — the label is what carries the meaning. */

@layer components {
  .badge {
    --badge-bg: var(--s-neutral-bg);
    --badge-fg: var(--s-neutral);

    display: inline-block;
    padding: var(--p-space-2) var(--p-space-8);
    background: var(--badge-bg);
    color: var(--badge-fg);
    /* A tint of the badge's own text colour, not a new token per status —
       one rule produces a border matching every variant automatically. */
    border: 1px solid color-mix(in srgb, var(--badge-fg) 30%, transparent);
    border-radius: var(--s-radius-badge);
    font-size: var(--s-text-micro);
    font-weight: var(--p-weight-semibold);
    white-space: nowrap;
  }

  .badge--success   { --badge-bg: var(--s-success-bg);   --badge-fg: var(--s-success); }
  .badge--warning   { --badge-bg: var(--s-warning-bg);   --badge-fg: var(--s-warning); }
  .badge--danger    { --badge-bg: var(--s-danger-bg);    --badge-fg: var(--s-danger); }
  .badge--info      { --badge-bg: var(--s-info-bg);      --badge-fg: var(--s-info); }
  .badge--presented { --badge-bg: var(--s-presented-bg); --badge-fg: var(--s-presented); }
  .badge--muted     { --badge-bg: var(--s-neutral-bg);   --badge-fg: var(--s-text-faint); }

  /* Sidebar alert count: white on brand pink, pill-shaped, solid — no
     border, the saturated fill already reads as a distinct mark. */
  .badge--alert {
    --badge-bg: var(--s-accent);
    --badge-fg: var(--s-on-primary);
    padding: var(--p-space-1) var(--p-space-6);
    border-color: transparent;
    border-radius: var(--s-radius-pill);
    font-family: var(--s-font-mono);
    font-size: var(--p-size-10);
  }
}

@layer components {
  .brand {
    display: flex;
    align-items: center;
    gap: var(--p-space-10);
    inline-size: 228px;
    flex: none;
    color: inherit;
    text-decoration: none;
  }

  .brand:hover { color: inherit; text-decoration: none; }

  /* The real mark (PRD 016): a transparent-background pineapple, taller
     than it is wide. Natural aspect ratio, not a cropped square — same
     treatment the source design gives it (width fixed, height auto). */
  .brand__mark {
    inline-size: 22px;
    block-size: auto;
    flex: none;
    display: block;
  }

  .brand__names { display: flex; flex-direction: column; line-height: var(--p-leading-snug); }

  .brand__name {
    font-family: var(--s-font-display);
    font-weight: var(--p-weight-semibold);
    letter-spacing: var(--p-track-brand);
  }

  .brand__tagline {
    font-size: var(--p-size-11);
    color: var(--s-text-soft);
  }

  /* PRD 017 #42. The 228px above is an alignment device, not a size: plus the
     top bar's 20px left padding it equals --p-sidebar-w (248px), so the
     brand's right edge lines up with the sidebar's. Below 860px the sidebar
     is a fixed-position drawer and there is nothing left to line up with —
     while the fixed, non-shrinkable width is 41% of a top bar that overflows
     a phone by ~194px. */
  @media (max-width: 860px) {
    .brand { inline-size: auto; }
  }
}

/* Tier-3 tokens on the block, so a variant is a token override rather than a
   second set of rules. Labels never wrap inside a fixed-height button. */

@layer components {
  .btn {
    --btn-h: var(--p-control-34);
    --btn-pad: 0 var(--p-space-14);
    --btn-bg: var(--s-surface);
    --btn-fg: var(--s-text);
    --btn-border: var(--s-border-strong);
    --btn-bg-hover: var(--s-surface-inset);
    --btn-border-hover: var(--s-border-strong);

    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--p-space-6);
    block-size: var(--btn-h);
    padding: var(--btn-pad);
    background: var(--btn-bg);
    color: var(--btn-fg);
    border: 1px solid var(--btn-border);
    border-radius: var(--s-radius-pill);
    font-size: var(--p-size-13);
    font-weight: var(--p-weight-medium);
    white-space: nowrap;
    flex: none;
  }

  .btn:hover {
    background: var(--btn-bg-hover);
    border-color: var(--btn-border-hover);
  }

  .btn[disabled] {
    opacity: 0.5;
    cursor: not-allowed;
  }

  .btn--primary {
    --btn-pad: 0 var(--p-space-16);
    --btn-bg: var(--s-primary);
    --btn-fg: var(--s-on-primary);
    --btn-border: var(--s-primary);
    --btn-bg-hover: var(--s-primary-hover);
    --btn-border-hover: var(--s-primary-hover);
  }

  .btn--danger {
    --btn-fg: var(--s-danger);
    --btn-bg-hover: var(--s-danger-bg);
  }

  /* Toolbar and card-header sizes. Height is a token, so nothing else moves. */
  .btn--toolbar { --btn-h: var(--p-control-32); --btn-pad: 0 var(--p-space-12); }

  .btn--small {
    --btn-h: var(--p-control-28);
    --btn-pad: 0 var(--p-space-10);
    --btn-fg: var(--s-text-muted);
    font-size: var(--p-size-125);
  }

  .btn--block { inline-size: 100%; }

  .btn.is-copied { --btn-fg: var(--s-success); }
}

/* Cards separate with a 1px border plus a soft raised shadow (PRD 016
   decision #5 — overturns this file's original "no elevation" rule). */

@layer components {
  .card {
    background: var(--s-surface);
    border: 1px solid var(--s-border);
    border-radius: var(--s-radius-card);
    box-shadow: var(--s-shadow-raised);
    overflow: hidden;
  }

  .card__header {
    display: flex;
    align-items: center;
    gap: var(--p-space-10);
    padding: var(--p-space-12) var(--s-card-pad-x);
    background: var(--s-surface-sunken);
    border-block-end: 1px solid var(--s-border);
  }

  .card__title {
    font-size: var(--s-text-card-title);
    font-weight: var(--p-weight-semibold);
  }

  .card__meta {
    font-family: var(--s-font-mono);
    font-size: var(--s-text-micro);
    color: var(--s-text-faint);
  }

  .card__actions { margin-inline-start: auto; display: flex; gap: var(--p-space-8); }

  .card__body { padding: var(--s-card-pad-y) var(--s-card-pad-x); }

  /* Row lists inside a card. --s-row-pad is the density token: switching it
     on :root re-spaces every list in the app at once. */
  .card__row {
    display: flex;
    align-items: center;
    gap: var(--p-space-14);
    padding: var(--s-row-pad) var(--s-card-pad-x);
    border-block-end: 1px solid var(--s-border-subtle);
  }

  .card__row:last-child { border-block-end: 0; }
  .card__row:hover { background: var(--s-surface-sunken); }

  .card--flush .card__body { padding: 0; }

  /* A card that is itself a link (Home's quick-link tiles). */
  .home-tile {
    display: block;
    color: inherit;
    text-decoration: none;
    transition: border-color 0.12s ease;
  }
  .home-tile:hover {
    border-color: var(--s-primary);
    color: inherit;
    text-decoration: none;
  }
  .home-tile h2 { margin-block-end: var(--p-space-4); }
  .home-tile p { margin: 0; color: var(--s-text-muted); font-size: var(--s-text-row); }
}

/* Document slots — the mockup's docgrid section, transcribed. */

@layer components {
  .docgrid {
    display: grid;
    gap: var(--p-space-14);
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  }

  .doc {
    display: flex;
    flex-direction: column;
    gap: var(--p-space-8);
    padding: var(--p-space-14);
    background: var(--s-surface);
    border: 1px solid var(--s-border);
    border-radius: var(--s-radius-card);
    box-shadow: var(--s-shadow-raised);
  }

  .doc--filled { background: var(--s-surface-sunken); }

  .doc__head { display: flex; align-items: baseline; gap: var(--p-space-8); }
  .doc__name { font-size: var(--s-text-row); font-weight: var(--p-weight-medium); }
  .doc__head .badge { margin-inline-start: auto; flex: none; }

  .doc__where {
    font-size: var(--s-text-meta);
    color: var(--s-text-faint);
    line-height: 1.4;
  }

  .doc__file {
    display: flex;
    align-items: baseline;
    gap: var(--p-space-8);
    min-inline-size: 0;
    font-size: var(--s-text-meta);
    color: var(--s-text-muted);
  }
  .doc__filename { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .doc__size {
    margin-inline-start: auto;
    flex: none;
    font-family: var(--s-font-mono);
    color: var(--s-text-faint);
  }

  /* The native control, exactly as the browser ships it — dashed box, its
     own button, its own text. It is deliberately *not* hidden behind a
     styled label: doing that removes drag-and-drop, which a file input gives
     for free and which people use. */
  .doc input[type="file"] {
    inline-size: 100%;
    block-size: auto;
    padding: 7px 9px;
    font-size: var(--s-text-meta);
    background: var(--s-surface);
    border: 1px dashed var(--s-border-strong);
    border-radius: var(--s-radius-control);
  }
  .doc input[type="file"]::file-selector-button {
    margin-inline-end: 9px;
    padding: 3px 9px;
    font-size: var(--s-text-meta);
    background: var(--s-surface);
    color: var(--s-text);
    border: 1px solid var(--s-border-strong);
    border-radius: var(--s-radius-small);
    cursor: pointer;
  }
  .doc input[type="file"]::file-selector-button:hover { background: var(--s-surface-inset); }
  .doc input[type="file"]:disabled { cursor: not-allowed; opacity: 0.55; }

  /* What the slot already holds, stated above the control. The browser will
     still say "no file selected" inside the input — it always does — so this
     line is what stops that from reading as "your document is gone". */
  .doc__stored {
    display: flex;
    align-items: baseline;
    gap: var(--p-space-8);
    min-inline-size: 0;
    padding: 5px 9px;
    font-size: var(--s-text-meta);
    background: var(--s-surface);
    border: 1px solid var(--s-border);
    border-radius: var(--s-radius-control);
  }
  .doc__stored-label {
    flex: none;
    font-weight: var(--p-weight-medium);
    color: var(--s-text);
  }
  .doc__stored-name {
    min-inline-size: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-family: var(--s-font-mono);
    color: var(--s-text-muted);
  }

  .doc__hint {
    font-size: var(--s-text-meta);
    color: var(--s-text-faint);
    line-height: 1.4;
  }

  .doc__date {
    display: flex;
    align-items: center;
    gap: var(--p-space-8);
    font-size: var(--s-text-meta);
    color: var(--s-text-muted);
  }
  .doc__date input { block-size: 28px; inline-size: auto; flex: none; font-size: var(--s-text-meta); }

  /* PRD 014 — the guidance disclosure. Collapsed by default; the native
     <details> marker is left alone rather than hidden, same reasoning as
     the file input above: the browser's own affordance over a reinvented
     one. No elevation — this system has exactly one shadow token and a
     disclosure that expands inline never needed it. */
  .doc__guide {
    border-top: 1px solid var(--s-border-faint);
    padding-top: var(--p-space-8);
  }
  .doc__guide-toggle {
    font-size: var(--s-text-meta);
    font-weight: var(--p-weight-medium);
    color: var(--s-primary);
    cursor: pointer;
  }
  .doc__guide-toggle:hover { color: var(--s-primary-hover); }
  .doc__guide-body {
    display: flex;
    flex-direction: column;
    gap: var(--p-space-8);
    margin-top: var(--p-space-8);
  }
  .doc__guide-link { font-size: var(--s-text-meta); }
  .doc__guide-link a { color: var(--s-primary); }
  .doc__guide-diagram {
    display: block;
    border: 1px solid var(--s-border);
    border-radius: var(--s-radius-control);
    background: var(--s-surface-inset);
  }
  .doc__guide-checklist {
    margin: 0;
    padding-inline-start: var(--p-space-16);
    font-size: var(--s-text-meta);
    color: var(--s-text-muted);
    line-height: 1.4;
    display: flex;
    flex-direction: column;
    gap: var(--p-space-4);
  }
  .doc__guide-faq { display: flex; flex-direction: column; gap: var(--p-space-4); }
  .doc__faq-item { font-size: var(--s-text-meta); }
  .doc__faq-item summary { font-weight: var(--p-weight-medium); color: var(--s-text); cursor: pointer; }
  .doc__faq-item p { margin: var(--p-space-4) 0 0; color: var(--s-text-muted); line-height: 1.4; }

  /* PRD 014 — client-side preview + retake, rendered by doc-enhance.js.
     Markup only exists once the module inserts it; no template branch. */
  .doc__preview {
    display: block;
    max-inline-size: 160px;
    max-block-size: 160px;
    object-fit: cover;
    border: 1px solid var(--s-border);
    border-radius: var(--s-radius-control);
  }
  .doc__retake {
    align-self: flex-start;
    font-size: var(--s-text-meta);
    font-weight: var(--p-weight-medium);
    color: var(--s-primary);
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
  }
  .doc__retake:hover { color: var(--s-primary-hover); }

  /* No breakpoints existed on this section before PRD 014. */
  @media (max-width: 480px) {
    .docgrid { grid-template-columns: 1fr; }
    .doc__guide-diagram { max-inline-size: 96px; }
    .doc__preview { max-inline-size: 120px; max-block-size: 120px; }
  }
}

/* Form fields — label, requirement markers, help text and validation
   messages. Meaning always travels in the text prefix, never colour alone. */

@layer components {
  .grid { display: grid; gap: var(--p-space-14); }
  .grid--2 { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
  .grid--3 { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }

  .field {
    display: flex;
    flex-direction: column;
    gap: 5px;
    min-inline-size: 0;
  }

  .field > label {
    display: flex;
    gap: var(--p-space-6);
    align-items: baseline;
    font-size: var(--p-size-125);
    font-weight: var(--p-weight-medium);
    color: var(--s-text-muted);
  }

  .field__req { color: var(--s-danger); font-weight: var(--p-weight-semibold); }

  .field__opt {
    font-size: var(--p-size-105);
    font-weight: var(--p-weight-semibold);
    color: var(--s-text-faint);
    text-transform: uppercase;
    letter-spacing: var(--p-track-eyebrow);
  }

  .field__help { font-size: var(--s-text-meta); color: var(--s-text-faint); }

  .field.is-invalid input { border-color: var(--s-danger); }

  .msg {
    display: flex;
    gap: var(--p-space-6);
    align-items: flex-start;
    font-size: var(--p-size-125);
    line-height: 1.4;
  }
  .msg::before {
    font-family: var(--s-font-mono);
    font-weight: var(--p-weight-semibold);
    flex: none;
  }
  .msg--ok { color: var(--s-success); }
  .msg--ok::before { content: "OK"; }
  .msg--bad { color: var(--s-danger); }
  .msg--bad::before { content: "!!"; }
  .msg--warn { color: var(--s-warning); }
  .msg--warn::before { content: "/!"; }
}

  /* The country prefix and the number read as one control (phone-validation).
     `align-items: stretch` so the select matches the input's height without
     either being told what that height is. */
  .phone-group {
    display: flex;
    align-items: stretch;
    gap: var(--p-space-6);
  }
  .phone-group__prefix {
    flex: none;
    inline-size: auto;
    font-family: var(--s-font-mono);
  }
  .phone-group__number { flex: 1 1 auto; min-inline-size: 0; }

/* Numbered form blocks (the mockup's block/block-head/block-num), the
   entity segmented toggle, conditional visibility and the sticky footer.

   Conditional visibility is CSS state, not JS: the entity radios and the
   same-as-fiscal checkbox drive :has() rules, so the no-JS form is fully
   functional before any module loads. */

@layer components {
  .block { margin-block-end: var(--p-space-14); overflow: visible; }

  .block-head {
    display: flex;
    align-items: center;
    gap: var(--p-space-12);
    padding: var(--p-space-12) var(--s-card-pad-x);
    background: var(--s-surface-sunken);
    border-block-end: 1px solid var(--s-border);
    border-radius: var(--s-radius-card) var(--s-radius-card) 0 0;
  }

  .block-num {
    inline-size: 22px;
    block-size: 22px;
    flex: none;
    display: grid;
    place-items: center;
    background: var(--s-primary);
    color: var(--s-on-primary);
    border-radius: var(--s-radius-badge);
    font-family: var(--s-font-mono);
    font-size: var(--p-size-11);
    font-weight: var(--p-weight-semibold);
  }

  .block-head .block-hint {
    margin-inline-start: auto;
    font-size: var(--s-text-meta);
    color: var(--s-text-faint);
  }

  .block-body {
    padding: var(--p-space-16) var(--s-card-pad-x);
    display: flex;
    flex-direction: column;
    gap: var(--p-space-16);
  }

  /* ── Entity segmented toggle (radios visually hidden, labels styled) ── */
  .segbar {
    display: inline-flex;
    gap: 2px;
    padding: 3px;
    background: var(--s-neutral-bg);
    border-radius: var(--s-radius-track);
  }

  .segbar input { position: absolute; opacity: 0; pointer-events: none; }

  .segbar label {
    block-size: 28px;
    display: inline-flex;
    align-items: center;
    padding: 0 var(--p-space-12);
    border-radius: var(--s-radius-small);
    color: var(--s-text-muted);
    font-size: var(--p-size-13);
    cursor: pointer;
  }

  #et-autonomo:checked ~ .segbar label[for="et-autonomo"],
  #et-sociedad:checked ~ .segbar label[for="et-sociedad"],
  .segbar input:checked + label {
    background: var(--s-surface);
    color: var(--s-text);
    font-weight: var(--p-weight-semibold);
    box-shadow: var(--s-shadow-raised);
  }

  .segbar input:focus-visible + label {
    outline: 2px solid var(--s-focus);
    outline-offset: 2px;
  }

  /* ── Conditional sections, driven from form state ───────────────────── */
  form:has(#et-sociedad:checked) .only-autonomo { display: none; }
  form:has(#et-autonomo:checked) .only-sociedad { display: none; }
  form:has(#et-sociedad:checked) .only-autonomo-inline { display: none; }
  form:has(#et-autonomo:checked) .only-sociedad-inline { display: none; }

  /* ── Sticky footer ──────────────────────────────────────────────────── */
  .formfoot {
    position: sticky;
    bottom: 0;
    margin-block-start: var(--p-space-16);
    display: flex;
    gap: var(--p-space-10);
    align-items: center;
    flex-wrap: wrap;
    padding: var(--p-space-12) var(--s-card-pad-x);
    background: var(--s-surface);
    border: 1px solid var(--s-border);
    border-radius: var(--s-radius-card);
    box-shadow: var(--s-shadow-raised);
  }

  .formfoot__status { margin-inline-end: auto; }
}

/* The login screen: centred card, brand above, no app chrome. */

@layer components {
  .loginpage {
    /* dvh for the same reason the app shell uses it: on mobile 100vh is the
       large viewport, so the "centred" card sits low and the page scrolls
       vertically for no reason (PRD 017 #43). No shell here, so this is the
       only rule the login screen needs. */
    min-block-size: 100vh;
    min-block-size: 100dvh;
    display: grid;
    place-items: center;
    padding: var(--p-space-26);
    background: var(--s-ground);
  }

  .loginbox { inline-size: 100%; max-inline-size: 390px; }

  .loginbox__head {
    display: flex;
    align-items: center;
    gap: var(--p-space-10);
    margin-block-end: var(--p-space-16);
  }

  .loginbox .card { padding: var(--p-space-26); }

  .loginbox__title { font-size: 21px; margin-block-end: 3px; }

  .loginbox__sub {
    margin-block-end: var(--p-space-20);
    color: var(--s-text-muted);
    font-size: var(--s-text-row);
  }

  .loginbox__fields {
    display: flex;
    flex-direction: column;
    gap: var(--p-space-14);
  }
}

/* Sidebar navigation. The active item is marked twice — tinted background and
   a 2px inset rail — so it survives being read without colour. */

@layer components {
  .nav-group { margin-block-end: var(--p-space-14); }
  .nav-group--top { margin-block-end: var(--p-space-16); }

  /* The markup doesn't carry role="list" (reset.css's list-style reset is
     scoped to that), so it falls back to default browser bullets — a
     pre-existing gap only visible now that nav items carry icons. */
  .nav-group ul { list-style: none; margin: 0; padding: 0; }

  .nav-group__label {
    padding: 0 var(--p-space-10) var(--p-space-6);
    font-size: var(--p-size-10);
    font-weight: var(--p-weight-semibold);
    letter-spacing: var(--p-track-group);
    text-transform: uppercase;
    color: var(--s-text-faint);
  }

  .nav-item {
    display: flex;
    align-items: center;
    gap: var(--p-space-8);
    inline-size: 100%;
    padding: 7px var(--p-space-10);
    margin-block-end: var(--p-space-1);
    border-radius: var(--s-radius-control);
    font-size: var(--s-text-row);
    color: var(--s-text-muted);
    text-decoration: none;
  }

  /* Was a hardcoded #E7E4DA — the one place in this layer that bypassed the
     token system (PRD 016 decision #14). --s-surface-inset is the same
     "ghost hover" role every other hover-without-a-dedicated-token uses. */
  .nav-item:hover {
    background: var(--s-surface-inset);
    color: var(--s-text);
    text-decoration: none;
  }

  .nav-item.is-active {
    background: var(--s-primary-tint);
    color: var(--s-primary);
    font-weight: var(--p-weight-semibold);
    box-shadow: inset 2px 0 0 var(--s-primary);
  }

  .nav-item__ic {
    inline-size: 16px;
    block-size: 16px;
    flex: none;
    opacity: 0.85;
  }

  .nav-item__count {
    margin-inline-start: auto;
    font-family: var(--s-font-mono);
    font-size: var(--p-size-11);
    color: var(--s-text-faint);
  }

  .nav-item .badge--alert,
  .nav-item .nav-badge { margin-inline-start: auto; }

  .nav-footer {
    margin-block-start: auto;
    padding-block-start: var(--p-space-12);
    border-block-start: 1px solid var(--s-border);
    font-size: var(--s-text-micro);
    color: var(--s-text-faint);
  }

  .nav-footer p { margin: 0; }

  .app-shell.is-collapsed .nav-footer { display: none; }
}

/* The admin notification feed: severity filter bar, rows, pager.

   The sidebar count itself needs no rules here — `.badge--alert` and
   `.nav-item .badge--alert` were already authored in badge.css and nav.css
   and had no consumer until this feature existed. */

@layer components {
  /* ── the severity filter ─────────────────────────────────────────── */
  .filterbar {
    display: flex;
    gap: var(--p-space-4);
    flex-wrap: wrap;
  }

  .filterbar__item {
    padding: var(--p-space-4) var(--p-space-10);
    border-radius: var(--s-radius-pill);
    font-size: var(--s-text-row);
    color: var(--s-text-muted);
    text-decoration: none;
  }

  .filterbar__item:hover {
    background: var(--s-surface-inset);
    color: var(--s-text);
    text-decoration: none;
  }

  /* Marked twice — tint plus weight — so the active filter survives being
     read without colour, same rule the nav follows. */
  .filterbar__item.is-active {
    background: var(--s-primary-tint);
    color: var(--s-primary);
    font-weight: var(--p-weight-semibold);
  }

  /* ── rows ────────────────────────────────────────────────────────── */
  /* An acknowledged row recedes rather than disappearing: the feed is a
     record you can walk into and read, not an inbox that empties. */
  .notif.is-acknowledged {
    color: var(--s-text-faint);
  }

  .notif.is-acknowledged .notif__message {
    color: var(--s-text-soft);
  }

  .notif__when {
    white-space: nowrap;
    color: var(--s-text-muted);
    font-size: var(--s-text-micro);
  }

  .notif__message {
    /* The longest column by far — a rubric check sentence or a provider
       failure reason. Everything else stays on one line beside it. */
    inline-size: 100%;
  }

  .notif__state {
    white-space: nowrap;
    text-align: end;
  }

  /* The sidebar badge's own wrapper. Empty when nothing is unacknowledged,
     and an empty inline element must not open a gap in the nav item. */
  .nav-badge:empty {
    display: none;
  }

  /* ── pagination ──────────────────────────────────────────────────── */
  .pager {
    display: flex;
    gap: var(--p-space-8);
    align-items: center;
    justify-content: flex-end;
    margin-block-start: var(--p-space-14);
  }

  .pager__position {
    font-size: var(--s-text-micro);
    color: var(--s-text-faint);
  }
}

/* Data tables in a card frame, per the design handoff: sunken header row,
   1px separators, hover wash. Row density rides the --s-row-pad token. */

@layer components {
  .tablewrap {
    overflow-x: auto;
    background: var(--s-surface);
    border: 1px solid var(--s-border);
    border-radius: var(--s-radius-card);
    box-shadow: var(--s-shadow-raised);
  }

  .tablewrap table {
    border-collapse: collapse;
    inline-size: 100%;
    min-inline-size: 700px;
    font-size: var(--s-text-row);
  }

  .tablewrap th {
    padding: 9px var(--p-space-14);
    text-align: start;
    white-space: nowrap;
    background: var(--s-surface-sunken);
    border-block-end: 1px solid var(--s-border);
    font-size: var(--s-text-eyebrow);
    font-weight: var(--p-weight-semibold);
    letter-spacing: var(--p-track-eyebrow);
    text-transform: uppercase;
    color: var(--s-text-soft);
  }

  .tablewrap td {
    padding: var(--s-row-pad) var(--p-space-14);
    border-block-end: 1px solid var(--s-border-subtle);
    vertical-align: top;
  }

  .tablewrap tbody tr:last-child td { border-block-end: none; }
  .tablewrap tbody tr:hover { background: var(--s-surface-sunken); }

  .rowlink {
    border: none;
    background: none;
    padding: 0;
    text-align: start;
    color: var(--s-primary);
    font-weight: var(--p-weight-medium);
    font-size: var(--s-text-row);
    text-decoration: none;
  }
  .rowlink:hover { text-decoration: underline; }

  .table-empty {
    padding: 48px var(--p-space-20);
    text-align: center;
    color: var(--s-text-faint);
    font-size: var(--s-text-row);
  }

  .table-actions {
    display: flex;
    gap: var(--p-space-6);
    justify-content: flex-end;
  }
  .table-actions form { display: contents; }

  .list-attribution { margin-block-start: var(--p-space-10); font-size: var(--s-text-micro); }
  .list-attribution a { color: inherit; }
}

/* List toolbars: search + filters above a table card. */

@layer components {
  .toolbar {
    display: flex;
    gap: var(--p-space-8);
    align-items: center;
    flex-wrap: wrap;
    margin-block-end: var(--p-space-14);
  }

  .toolbar input[type="search"] {
    max-inline-size: 240px;
    block-size: var(--p-control-32);
  }

  .toolbar__check {
    display: flex;
    align-items: center;
    gap: var(--p-space-8);
    font-size: var(--s-text-row);
    color: var(--s-text-muted);
  }
}

/* Typeahead dropdown — shared by the IAE selector now and the address
   typeahead in phase 4, so both fields behave and look identically. */

@layer components {
  .ta { position: relative; }

  .ta-list {
    position: absolute;
    z-index: 20;
    inset-inline: 0;
    top: calc(100% + 3px);
    max-block-size: 330px;
    overflow-y: auto;
    background: var(--s-surface);
    border: 1px solid var(--s-border-strong);
    border-radius: var(--s-radius-control);
    box-shadow: var(--s-shadow-popover);
  }

  .ta-item {
    padding: var(--p-space-8) 11px;
    border-block-end: 1px solid var(--s-border-subtle);
    font-size: var(--s-text-row);
    cursor: pointer;
  }
  .ta-item:last-child { border-block-end: none; }
  .ta-item:hover,
  .ta-item.is-active { background: var(--s-primary-tint); }

  .ta-item mark {
    background: var(--s-warning-bg);
    color: inherit;
    font-weight: var(--p-weight-semibold);
    border-radius: 1px;
    padding: 0 1px;
  }

  .ta-code {
    font-family: var(--s-font-mono);
    font-weight: var(--p-weight-semibold);
    color: var(--s-primary);
    letter-spacing: var(--p-track-code);
    margin-inline-end: 7px;
  }

  .ta-badge {
    font-size: var(--p-size-10);
    font-weight: var(--p-weight-semibold);
    letter-spacing: var(--p-track-eyebrow);
    text-transform: uppercase;
    background: var(--s-neutral-bg);
    color: var(--s-text-muted);
    border-radius: var(--s-radius-badge);
    padding: 1px 5px;
    margin-inline-end: 7px;
  }

  .ta-via {
    margin-block-start: 2px;
    padding-inline-start: 7px;
    border-inline-start: 2px solid var(--s-border-strong);
    font-size: var(--s-text-meta);
    color: var(--s-text-faint);
  }

  .ta-foot {
    padding: var(--p-space-6) 11px;
    background: var(--s-surface-sunken);
    border-block-start: 1px solid var(--s-border-subtle);
    font-size: var(--p-size-11);
    color: var(--s-text-faint);
  }
}


/* The public submission form: its own page shell, no app chrome.

   Reuses every component the backoffice already has — card, field, block,
   docgrid, formfoot — so a restyle of those reaches this page for free. What
   lives here is only what is genuinely particular to a page with no sidebar
   and no logged-in user. */

@layer pages {
  .publicpage {
    min-block-size: 100dvh;
    background: var(--s-surface-sunken);
  }

  .publicpage__head {
    display: flex;
    align-items: center;
    padding: var(--s-space-md) var(--s-space-lg);
    background: var(--s-surface);
    border-block-end: 1px solid var(--s-border);
  }

  .publicpage__body {
    max-inline-size: 46rem;
    margin-inline: auto;
    padding: var(--s-space-lg) var(--s-space-md) var(--s-space-xl);
  }

  .publicpage--narrow .publicpage__body {
    max-inline-size: 32rem;
  }

  /* The honeypot. Off-screen rather than `display: none` or
     `visibility: hidden`: both of those are the first thing a form-filling
     bot checks for, and moving the field out of the viewport costs it a
     little more work to notice. The template also carries tabindex="-1", so
     someone browsing without this stylesheet is not trapped by it either. */
  .hp {
    position: absolute;
    inset-inline-start: -9999px;
    inline-size: 1px;
    block-size: 1px;
    overflow: hidden;
  }
}

/* The client portal. Shares the public page shell; what is here is the
   read-only data list and the document grid's settled state. */

@layer pages {
  .publicpage__logout {
    margin-inline-start: auto;
  }

  .portal__owner {
    font-weight: var(--p-weight-semibold);
    margin-block-end: var(--s-space-2xs);
  }

  .portal__data {
    display: grid;
    gap: var(--s-space-sm);
    margin: 0;
  }

  .portal__data dt {
    font-size: var(--s-text-micro);
    color: var(--s-text-faint);
    margin-block-end: var(--s-space-2xs);
  }

  .portal__data dd {
    margin: 0;
    color: var(--s-text);
  }

  .portal__readonly-note {
    margin-block-start: var(--s-space-md);
    font-size: var(--s-text-micro);
  }

  /* A document that has already passed: shown, but not offered for
     replacement, so it should not compete for attention with the ones that
     still need something. */
  .doc--settled {
    opacity: 0.72;
  }
}


/* Highest layer: single-purpose overrides that beat any component rule without
   !important. Keep this file short — a utility used once should have been a
   component rule. */

@layer utilities {
  /* htmx's indicator classes, self-hosted (includeIndicatorStyles is off
     because the injected <style> violates our CSP). */
  .htmx-indicator { opacity: 0; }
  .htmx-request .htmx-indicator,
  .htmx-request.htmx-indicator { opacity: 1; }

  .u-visually-hidden {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .u-mono { font-family: var(--s-font-mono); letter-spacing: var(--p-track-mono); }
  .u-nums { font-variant-numeric: tabular-nums; }
  .u-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .u-push-right { margin-inline-start: auto; }
  .u-text-soft { color: var(--s-text-soft); }
  .u-text-faint { color: var(--s-text-faint); }
}

