/* GENERATED by @crystelis/design-system — do not edit by hand. */
:root {
  color-scheme: light dark;
  --font-sans: 'IBM Plex Sans', ui-sans-serif, system-ui, -apple-system, sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, 'Cascadia Code', monospace;
  --text-xs: 0.75rem;
  --text-sm: 0.8125rem;
  --text-base: 0.9375rem;
  --text-md: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.375rem;
  --text-2xl: 1.75rem;
  --leading-tight: 1.2;
  --leading-normal: 1.5;
  --tracking-tight: -0.01em;
  --tracking-normal: 0;
  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-10: 2.5rem;
  --space-12: 3rem;
  --space-16: 4rem;
  --radius-control: 6px;
  --radius-card: 6px;
  --radius-pill: 999px;
  --radius-focus: 4px;
  --hairline: 1px;
  --border-width: 1px;
  --z-base: 0;
  --z-sticky: 100;
  --z-dropdown: 200;
  --z-modal: 300;
  --z-toast: 400;
  --dur-fast: 80ms;
  --dur: 120ms;
  --dur-slow: 160ms;
  --ease: cubic-bezier(0.2, 0, 0, 1);
  --focus-ring-width: 2px;
  --focus-offset: 2px;
  --row-h: 48px;
  --control-h: 44px;
  --pad-y: 0.5rem;
  --pad-x: 0.75rem;
  --elevation-menu: 0 2px 8px rgba(28, 26, 22, 0.14);
  --elevation-modal: 0 12px 32px rgba(28, 26, 22, 0.22);
  --elevation-toast: 0 4px 14px rgba(28, 26, 22, 0.18);
  --elevation-raised: 0 1px 2px rgba(28, 26, 22, 0.10);
  --scroll-shadow: rgba(28, 26, 22, 0.16);
  --fg-primary: #14273F;
  --fg-secondary: #33465D;
  --fg-tertiary: #4C6078;
  --fg-muted: #53647A;
  --surface-0: #F4F8FC;
  --surface-1: #FEFEFE;
  --surface-2: #EDF2F8;
  --surface-3: #E2E9F2;
  --border-hairline: #E0E7F0;
  --border: #CFD9E6;
  --border-emphasis: #A5B4C7;
  --border-focus: #0044EC;
  --control-bg: #FEFEFE;
  --control-border: #738196;
  --control-fg: #14273F;
  --brand: #0044EC;
  --brand-hover: #003BCB;
  --brand-active: #0031A8;
  --brand-fg: #FFFFFF;
  --link: #0044D5;
  --nav-bg: #132846;
  --nav-fg: #FFFFFF;
  --nav-muted: #C4D2E5;
  --nav-hover: #203B5D;
  --nav-active: #155AEF;
  --nav-border: #41536B;
  --nav-focus: #B4CCFF;
  --selection-bg: #E7EDFF;
  --scrim: #0A182B;
  --verified: #B4862E;
  --verified-fg: #6E5210;
  --verified-bg: #F6ECCF;
  --money-default: #14273F;
  --money-negative: #97281E;
  --money-credit: #1F5793;
  --status-draft-fg: #524D45;
  --status-draft-bg: #ECEAE4;
  --status-draft-border: #D8D3C8;
  --status-submitted-fg: #1F5793;
  --status-submitted-bg: #E1ECF7;
  --status-submitted-border: #BBD3EC;
  --status-accepted-fg: #6E5210;
  --status-accepted-bg: #F6ECCF;
  --status-accepted-border: #E4D2A0;
  --status-rejected-fg: #97281E;
  --status-rejected-bg: #F7E4E1;
  --status-rejected-border: #EEC5BE;
  --status-errored-fg: #8A3D08;
  --status-errored-bg: #F7E9DC;
  --status-errored-border: #EAD0B4;
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --fg-primary: #F1F5FC;
  --fg-secondary: #CAD6E8;
  --fg-tertiary: #AABBD2;
  --fg-muted: #A3B5CD;
  --surface-0: #121D2E;
  --surface-1: #19283D;
  --surface-2: #213249;
  --surface-3: #2B3C53;
  --border-hairline: #2D4059;
  --border: #3B506B;
  --border-emphasis: #617793;
  --border-focus: #9ABBFF;
  --control-bg: #121F32;
  --control-border: #7F91AA;
  --control-fg: #F1F5FC;
  --brand: #84AAFF;
  --brand-hover: #9ABBFF;
  --brand-active: #719BF4;
  --brand-fg: #0A1B35;
  --link: #9ABBFF;
  --nav-bg: #0C182B;
  --nav-fg: #FFFFFF;
  --nav-muted: #C4D2E5;
  --nav-hover: #203B5D;
  --nav-active: #155AEF;
  --nav-border: #354861;
  --nav-focus: #B4CCFF;
  --selection-bg: #213759;
  --scrim: #030A16;
  --verified: #D9AE52;
  --verified-fg: #E7C878;
  --verified-bg: #2E2612;
  --money-default: #F1F5FC;
  --money-negative: #F0A79C;
  --money-credit: #7FB2E6;
  --status-draft-fg: #B7B1A4;
  --status-draft-bg: #2A261E;
  --status-draft-border: #3A352B;
  --status-submitted-fg: #7FB2E6;
  --status-submitted-bg: #16283C;
  --status-submitted-border: #274863;
  --status-accepted-fg: #E7C878;
  --status-accepted-bg: #2E2612;
  --status-accepted-border: #4A3D1C;
  --status-rejected-fg: #F0A79C;
  --status-rejected-bg: #3A211C;
  --status-rejected-border: #5A342C;
  --status-errored-fg: #E8B583;
  --status-errored-bg: #3A2A18;
  --status-errored-border: #5A4224;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --fg-primary: #F1F5FC;
    --fg-secondary: #CAD6E8;
    --fg-tertiary: #AABBD2;
    --fg-muted: #A3B5CD;
    --surface-0: #121D2E;
    --surface-1: #19283D;
    --surface-2: #213249;
    --surface-3: #2B3C53;
    --border-hairline: #2D4059;
    --border: #3B506B;
    --border-emphasis: #617793;
    --border-focus: #9ABBFF;
    --control-bg: #121F32;
    --control-border: #7F91AA;
    --control-fg: #F1F5FC;
    --brand: #84AAFF;
    --brand-hover: #9ABBFF;
    --brand-active: #719BF4;
    --brand-fg: #0A1B35;
    --link: #9ABBFF;
    --nav-bg: #0C182B;
    --nav-fg: #FFFFFF;
    --nav-muted: #C4D2E5;
    --nav-hover: #203B5D;
    --nav-active: #155AEF;
    --nav-border: #354861;
    --nav-focus: #B4CCFF;
    --selection-bg: #213759;
    --scrim: #030A16;
    --verified: #D9AE52;
    --verified-fg: #E7C878;
    --verified-bg: #2E2612;
    --money-default: #F1F5FC;
    --money-negative: #F0A79C;
    --money-credit: #7FB2E6;
    --status-draft-fg: #B7B1A4;
    --status-draft-bg: #2A261E;
    --status-draft-border: #3A352B;
    --status-submitted-fg: #7FB2E6;
    --status-submitted-bg: #16283C;
    --status-submitted-border: #274863;
    --status-accepted-fg: #E7C878;
    --status-accepted-bg: #2E2612;
    --status-accepted-border: #4A3D1C;
    --status-rejected-fg: #F0A79C;
    --status-rejected-bg: #3A211C;
    --status-rejected-border: #5A342C;
    --status-errored-fg: #E8B583;
    --status-errored-bg: #3A2A18;
    --status-errored-border: #5A4224;
  }
}

[data-density="compact"] {
  --row-h: 32px;
  --control-h: 30px;
  --pad-y: 0.375rem;
  --pad-x: 0.625rem;
}


/* --- fonts.css --- */
/* IBM Plex Sans/Mono — self-hosted (no CDN). Files copied to ./assets/fonts by the build. */
@font-face {
  font-family: "IBM Plex Sans"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("./assets/fonts/ibm-plex-sans-latin-400-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: "IBM Plex Sans"; font-style: normal; font-weight: 500; font-display: swap;
  src: url("./assets/fonts/ibm-plex-sans-latin-500-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+2000-206F,U+2074,U+20AC,U+2122,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: "IBM Plex Sans"; font-style: normal; font-weight: 600; font-display: swap;
  src: url("./assets/fonts/ibm-plex-sans-latin-600-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+2000-206F,U+2074,U+20AC,U+2122,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("./assets/fonts/ibm-plex-mono-latin-400-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+2000-206F,U+2074,U+20AC,U+2122,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono"; font-style: normal; font-weight: 500; font-display: swap;
  src: url("./assets/fonts/ibm-plex-mono-latin-500-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+2000-206F,U+2074,U+20AC,U+2122,U+2212,U+2215,U+FEFF,U+FFFD;
}


/* --- reset.css --- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
body { min-height: 100vh; line-height: var(--leading-normal); -webkit-font-smoothing: antialiased; }
img, picture, svg, video { display: block; max-inline-size: 100%; }
input, button, textarea, select { font: inherit; color: inherit; }
button { background: none; border: none; padding: 0; cursor: pointer; }
a { color: inherit; }
p, h1, h2, h3, h4, h5, h6 { overflow-wrap: break-word; }
:where(ul, ol)[role="list"] { list-style: none; padding: 0; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}


/* --- base.css --- */
html {
  font-family: var(--font-sans);
  font-size: 16px;
  color: var(--fg-primary);
  background: var(--surface-0);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "zero" 1; /* tabular + slashed zero (fiscal IDs) */
  accent-color: var(--brand);
}
body { font-size: var(--text-base); }
input, textarea { caret-color: var(--brand); }
html { scrollbar-color: var(--border-emphasis) var(--surface-0); }
button, a, summary, select { touch-action: manipulation; -webkit-tap-highlight-color: transparent; }

h1 { font-size: var(--text-2xl); font-weight: var(--weight-semibold); line-height: var(--leading-tight); letter-spacing: var(--tracking-tight); text-wrap: balance; }
h2 { font-size: var(--text-xl); font-weight: var(--weight-semibold); line-height: var(--leading-tight); letter-spacing: var(--tracking-tight); text-wrap: balance; }
h3 { font-size: var(--text-lg); font-weight: var(--weight-semibold); line-height: var(--leading-tight); }
h4 { font-size: var(--text-md); font-weight: var(--weight-semibold); }

/* The reset zeroes all margins, so a heading sits flush against the paragraph
   ABOVE it and detached from the content it introduces — the grouping reads
   backwards. Restore asymmetric rhythm: more space before, less after. */
:where(h2, h3, h4) { margin-block: var(--space-6) var(--space-2); }
:where(h2, h3, h4):first-child { margin-block-start: 0; }
:where(p) { margin-block: 0 var(--space-3); line-height: var(--leading-normal); text-wrap: pretty; }
:where(p):last-child { margin-block-end: 0; }
small { font-size: var(--text-sm); color: var(--fg-tertiary); }

a { color: var(--link); text-underline-offset: 0.15em; }
a:hover { text-decoration: underline; }

/* Global visible focus — never removed without replacement. */
:focus-visible {
  outline: var(--focus-ring-width) solid var(--border-focus);
  outline-offset: var(--focus-offset);
  border-radius: var(--radius-focus);
}
:focus:not(:focus-visible) { outline: none; }

/* Anchor targets clear the sticky topbar (.cr-topbar is 52px, position: sticky).
   Without this an in-page link scrolls its target UNDER the bar and the reader
   lands on the wrong heading — the one thing an in-page link exists to avoid. */
:target { scroll-margin-block-start: calc(52px + var(--space-4)); }

::selection { background: color-mix(in srgb, var(--brand) 22%, transparent); }
hr { border: none; border-block-start: var(--hairline) solid var(--border-hairline); }

/* Skip link — visible on focus. */
.skip-link {
  position: absolute; inset-inline-start: var(--space-2); inset-block-start: var(--space-2);
  padding: var(--space-2) var(--space-3); background: var(--surface-1);
  border: var(--border-width) solid var(--border-emphasis); border-radius: var(--radius-control);
  transform: translateY(-150%); transition: transform var(--dur) var(--ease); z-index: var(--z-toast);
}
.skip-link:focus { transform: translateY(0); }


/* --- utilities.css --- */
.cr-tabular { font-variant-numeric: tabular-nums; }
.cr-mono { font-family: var(--font-mono); }
.cr-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-inline-size: 0; }
.cr-clamp-2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.cr-visually-hidden {
  position: absolute !important; inline-size: 1px; block-size: 1px;
  padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.cr-stack { display: flex; flex-direction: column; gap: var(--space-3); }
.cr-row { display: flex; align-items: center; gap: var(--space-2); min-inline-size: 0; }
.cr-icon { inline-size: 1em; block-size: 1em; flex: none; }
.cr-icon-lg { inline-size: 1.25rem; block-size: 1.25rem; flex: none; }


/* --- components/button.css --- */
/* Contract:
   <button class="cr-btn cr-btn--primary" type="button">Guardar</button>
   <button class="cr-btn cr-btn--secondary"><svg class="cr-icon" aria-hidden="true"><use href="#i-plus"/></svg> Nueva factura</button>
   <button class="cr-btn cr-btn--danger">Eliminar</button>
   <button class="cr-btn cr-btn--primary" data-loading aria-busy="true">…</button>
   Permission-restricted (present but not permitted):
   <button class="cr-btn cr-btn--primary" data-restricted disabled aria-describedby="perm-hint">Transmitir</button>
   Icon-only: <button class="cr-btn cr-btn--icon" aria-label="Filtrar"><svg class="cr-icon-lg" aria-hidden="true"><use href="#i-filter"/></svg></button>
*/
.cr-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  block-size: var(--control-h); padding-inline: var(--pad-x);
  font-size: var(--text-base); font-weight: var(--weight-medium); line-height: 1;
  border-radius: var(--radius-control); border: var(--border-width) solid transparent;
  white-space: nowrap; user-select: none;
  /* Anchors styled as buttons: base.css only sets `a { color }`, so the UA default
     underline survives. A button label is never underlined. */
  text-decoration: none;
  transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease),
    color var(--dur) var(--ease), scale var(--dur-fast) var(--ease);
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
}
.cr-btn:hover { text-decoration: none; }
/* Tactile press feedback — confirms the control heard the click. */
.cr-btn:active:not(:disabled):not([data-restricted]):not([data-loading]) { scale: 0.97; }
@media (prefers-reduced-motion: reduce) { .cr-btn:active { scale: 1; } }
/* A variant-less `class="cr-btn"` would otherwise be transparent-on-transparent —
   a fully invisible control. Fall back to the secondary treatment. */
.cr-btn:not([class*="cr-btn--"]) { background: var(--surface-1); color: var(--fg-primary);
  border-color: var(--border-emphasis); }
.cr-btn:not([class*="cr-btn--"]):hover { background: var(--surface-2); }

.cr-btn--primary { background: var(--brand); color: var(--brand-fg); }
.cr-btn--primary:hover { background: var(--brand-hover); }
.cr-btn--primary:active { background: var(--brand-active); }
.cr-btn--secondary { background: var(--surface-1); color: var(--fg-primary); border-color: var(--border-emphasis); }
.cr-btn--secondary:hover { background: var(--surface-2); }
.cr-btn--ghost { background: transparent; color: var(--fg-secondary); }
.cr-btn--ghost:hover { background: var(--surface-1); color: var(--fg-primary); }
.cr-btn--danger { background: var(--surface-1); color: var(--status-rejected-fg); border-color: var(--status-rejected-border); }
.cr-btn--danger:hover { background: var(--status-rejected-bg); }
.cr-btn--icon { inline-size: var(--control-h); padding-inline: 0; color: var(--fg-secondary); position: relative; }
/* Hit area to the 40px desktop floor without growing the painted control. */
.cr-btn--icon::after { content: ""; position: absolute; inset: 50% 50% 50% 50%;
  inline-size: 40px; block-size: 40px; translate: -50% -50%; }
.cr-btn--icon:hover { background: var(--surface-1); color: var(--fg-primary); }

.cr-btn:disabled, .cr-btn[aria-disabled="true"] { opacity: 0.55; cursor: not-allowed; }
/* Permission-restricted: visually distinct from a plain disabled control (a lock, dashed edge). */
.cr-btn[data-restricted] { opacity: 1; cursor: not-allowed; color: var(--fg-muted);
  background: var(--surface-1); border-style: dashed; border-color: var(--border-emphasis); }
.cr-btn[data-restricted]::before {
  content: ""; inline-size: 1em; block-size: 1em; flex: none;
  background: currentColor; -webkit-mask: var(--cr-lock) center/contain no-repeat; mask: var(--cr-lock) center/contain no-repeat;
}
/* Mask-only SVG: painted by `background: currentColor`, so the glyph carries no baked
   color (stroke='currentColor' resolves opaque inside the isolated mask document). */
:root { --cr-lock: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Crect x='5' y='11' width='14' height='10' rx='2'/%3E%3Cpath d='M8 11V7a4 4 0 0 1 8 0v4'/%3E%3C/svg%3E"); }

/* Loading: spinner + keeps its box; enabled until the request starts (host toggles data-loading). */
.cr-btn[data-loading] { color: transparent !important; position: relative; pointer-events: none; }
.cr-btn[data-loading]::after {
  content: ""; position: absolute; inline-size: 1em; block-size: 1em;
  border: 2px solid currentColor; border-block-start-color: transparent; border-radius: 50%;
  color: var(--brand-fg); animation: cr-spin 0.6s linear infinite;
}
.cr-btn--secondary[data-loading]::after, .cr-btn--ghost[data-loading]::after { color: var(--fg-secondary); }
@keyframes cr-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .cr-btn[data-loading]::after { animation: none; border-block-start-color: currentColor; opacity: 0.6; } }


/* --- components/link.css --- */
/* Contract: <a class="cr-link" href="…">Ver factura</a>  ·  external adds an icon + rel */
.cr-link { color: var(--link); text-decoration: none; text-underline-offset: 0.15em; border-radius: 2px; }
.cr-link:hover { text-decoration: underline; }
.cr-link--external::after {
  content: ""; display: inline-block; inline-size: 0.85em; block-size: 0.85em; margin-inline-start: 0.2em;
  background: currentColor; -webkit-mask: var(--cr-ext) center/contain no-repeat; mask: var(--cr-ext) center/contain no-repeat;
}
/* Mask-only SVG: painted by `background: currentColor`; the glyph carries no baked color. */
:root { --cr-ext: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpath d='M7 17 17 7M9 7h8v8'/%3E%3C/svg%3E"); }


/* --- components/field.css --- */
/* Contract:
   <div class="cr-field" data-invalid>
     <label class="cr-field__label" for="nit">NIT <span class="cr-field__req" aria-hidden="true">*</span></label>
     <input id="nit" class="cr-input" aria-describedby="nit-hint nit-err" aria-invalid="true" required>
     <p id="nit-hint" class="cr-field__hint">Sin dígito de verificación.</p>
     <p id="nit-err" class="cr-field__error" role="alert">Ingrese un NIT válido.</p>
   </div>
*/
.cr-field { display: flex; flex-direction: column; gap: var(--space-1); }
.cr-field__label { font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--fg-secondary); }
.cr-field__req { color: var(--status-rejected-fg); margin-inline-start: 2px; }
.cr-field__hint { font-size: var(--text-sm); color: var(--fg-tertiary); }
/* Visible BY DEFAULT, hidden only inside a field that is not flagged invalid.
   The old rule was `display:none` + reveal-on-[data-invalid], which silently
   swallowed every FORM-level error — those render as a standalone
   <p class="cr-field__error" role="alert"> with no .cr-field[data-invalid]
   ancestor, so the user was told nothing when a submit failed. */
.cr-field__error { font-size: var(--text-sm); color: var(--status-rejected-fg); }
.cr-field:not([data-invalid]) .cr-field__error { display: none; }
.cr-field[data-invalid] .cr-input, .cr-field[data-invalid] .cr-select__trigger { border-color: var(--status-rejected-fg); }

/* Form layout — a form is a COLUMN of fields at a readable measure, not a wall of
   viewport-wide inputs. Without this a "Cantidad" box stretches to ~1100px in a
   wide card, which reads as broken and makes the label/control pairing hard to scan.
   Contract: <form class="cr-form">…fields…<button class="cr-btn cr-btn--primary">…</button></form> */
.cr-form { display: grid; gap: var(--space-4); max-inline-size: 40rem; }
/* Short, bounded values don't need the full measure. */
.cr-form .cr-field[data-width="short"] .cr-input { max-inline-size: 10rem; }
.cr-form .cr-field[data-width="medium"] .cr-input { max-inline-size: 18rem; }
/* The submit button sits on its own line, sized to its label. */
.cr-form > .cr-btn { justify-self: start; }

/* A <fieldset> renders a browser-default etched box with the legend notched into
   the border; inside a card that reads as a rendering artifact. Flatten it and
   reuse the field label treatment. */
.cr-field fieldset, fieldset.cr-field { border: none; padding: 0; margin: 0; min-inline-size: 0; }
fieldset.cr-field > legend { padding: 0; font-size: var(--text-sm);
  font-weight: var(--weight-medium); color: var(--fg-secondary); }
/* Checkbox groups: wrap as chips with real spacing between control and label. */
fieldset.cr-field > .cr-choice, fieldset.cr-field > label {
  display: inline-flex; align-items: center; gap: var(--space-2);
  min-block-size: 32px; margin-inline-end: var(--space-4); cursor: pointer; }


/* --- components/input.css --- */
/* Contract: <input class="cr-input" type="text" name="razon_social" autocomplete="organization" placeholder="Razón social…">
   Search: <input class="cr-input" type="search" inputmode="search" spellcheck="false" placeholder="Buscar facturas…">
   Textarea: <textarea class="cr-input" rows="3"></textarea> */
.cr-input {
  inline-size: 100%; min-inline-size: 0; block-size: var(--control-h);
  padding-inline: var(--pad-x); padding-block: 0;
  background: var(--control-bg); color: var(--control-fg);
  border: var(--border-width) solid var(--control-border); border-radius: var(--radius-control);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
textarea.cr-input { block-size: auto; padding-block: var(--pad-y); resize: vertical; line-height: var(--leading-normal); }
.cr-input::placeholder { color: var(--fg-muted); }
.cr-input:hover { border-color: var(--fg-tertiary); }
.cr-input:focus-visible { outline: none; border-color: var(--brand);
  box-shadow: 0 0 0 var(--focus-ring-width) color-mix(in srgb, var(--brand) 30%, transparent); }
.cr-input:disabled { opacity: 0.6; cursor: not-allowed; background: var(--surface-2); }
.cr-input[aria-invalid="true"] { border-color: var(--status-rejected-fg); }


/* --- components/choice.css --- */
/* Contract:
   <label class="cr-choice"><input type="checkbox" class="cr-choice__control"> Acepto</label>
   <label class="cr-choice"><input type="radio" name="env" class="cr-choice__control"> Producción</label>
   <label class="cr-switch"><input type="checkbox" class="cr-switch__control"><span class="cr-switch__track"></span> Modo compacto</label>
*/
.cr-choice { display: inline-flex; align-items: center; gap: var(--space-2); min-block-size: 24px; cursor: pointer; }
.cr-choice__control { inline-size: 18px; block-size: 18px; accent-color: var(--brand); flex: none; }
.cr-switch { display: inline-flex; align-items: center; gap: var(--space-2); min-block-size: 24px; cursor: pointer; }
.cr-switch__control { position: absolute; opacity: 0; inline-size: 1px; block-size: 1px; }
.cr-switch__track { inline-size: 34px; block-size: 20px; border-radius: var(--radius-pill);
  background: var(--surface-3); border: var(--border-width) solid var(--border-emphasis); position: relative;
  transition: background-color var(--dur) var(--ease); flex: none; }
.cr-switch__track::after { content: ""; position: absolute; inset-block-start: 2px; inset-inline-start: 2px;
  inline-size: 14px; block-size: 14px; border-radius: 50%; background: var(--surface-0);
  transition: transform var(--dur) var(--ease); }
.cr-switch__control:checked + .cr-switch__track { background: var(--brand); border-color: var(--brand); }
.cr-switch__control:checked + .cr-switch__track::after { transform: translateX(14px); }
.cr-switch__control:focus-visible + .cr-switch__track {
  outline: var(--focus-ring-width) solid var(--border-focus); outline-offset: var(--focus-offset); }


/* --- components/select.css --- */
/* Contract — baseline (works with no JS): a native <select> wrapped so its chevron can be a
   masked, token-painted affordance (a <select> can't host a ::after pseudo-element, so the
   wrapper carries it):
     <div class="cr-select"><select class="cr-select-native"><option>…</option></select></div>
   Enhanced (Alpine replaces the native when JS present): a <button class="cr-select__trigger"
   aria-haspopup="listbox">…</button> plus a token-styled listbox. The two share the visual
   language below. Only the trigger is themed today; the Alpine listbox lands later. */
.cr-select-native, .cr-select__trigger {
  inline-size: 100%; min-inline-size: 0; block-size: var(--control-h);
  padding-inline: var(--pad-x); text-align: start;
  background: var(--control-bg); color: var(--control-fg);
  border: var(--border-width) solid var(--control-border); border-radius: var(--radius-control);
  display: inline-flex; align-items: center; justify-content: space-between; gap: var(--space-2);
}
.cr-select { position: relative; display: block; inline-size: 100%; }
.cr-select-native { appearance: none; padding-inline-end: calc(var(--pad-x) * 2 + 1em); }
/* Chevron: a mask-only SVG (no baked color) painted with a semantic token, so it follows
   data-theme and any tenant theme — same mask + token technique as --cr-lock / --cr-ext. */
.cr-select::after {
  content: ""; position: absolute; inset-inline-end: var(--pad-x); inset-block-start: 50%;
  transform: translateY(-50%); inline-size: 1em; block-size: 1em; pointer-events: none;
  background: var(--fg-tertiary);
  -webkit-mask: var(--cr-chevron) center / contain no-repeat; mask: var(--cr-chevron) center / contain no-repeat;
}
.cr-select__trigger:focus-visible, .cr-select-native:focus-visible { outline: none; border-color: var(--brand);
  box-shadow: 0 0 0 var(--focus-ring-width) color-mix(in srgb, var(--brand) 30%, transparent); }
:root { --cr-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); }


/* --- components/card.css --- */
/* Contract:
   <section class="cr-card">
     <header class="cr-card__header"><h3>Resolución de numeración</h3><div class="cr-card__actions">…</div></header>
     <div class="cr-card__body">…</div>
     <footer class="cr-card__footer">…</footer>
   </section> */
.cr-card { background: var(--surface-1); border: var(--border-width) solid var(--border); border-radius: var(--radius-card); }
.cr-card__header { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-3) var(--space-4); border-block-end: var(--hairline) solid var(--border-hairline); }
/* A heading that carries a leading icon lays it out inline (see .cr-tab). */
.cr-card__header > h2, .cr-card__header > h3 { display: flex; align-items: center; gap: var(--space-2); }
.cr-card__body { padding: var(--space-4); }
.cr-card__footer { padding: var(--space-3) var(--space-4); border-block-start: var(--hairline) solid var(--border-hairline);
  display: flex; gap: var(--space-2); justify-content: flex-end; }

/* Inline disclosure (e.g. "Editar línea 3" inside a table row). The native ►
   marker is a rendering artifact next to the icon set; swap it for a chevron
   that rotates on open. */
.cr-disclosure > summary { display: flex; align-items: center; gap: var(--space-1);
  list-style: none; cursor: pointer; color: var(--link); font-size: var(--text-sm);
  min-block-size: 32px; width: fit-content; }
.cr-disclosure > summary::-webkit-details-marker { display: none; }
.cr-disclosure > summary::before {
  content: ""; inline-size: 0.85em; block-size: 0.85em; flex: none; background: currentColor;
  -webkit-mask: var(--cr-chevron) center/contain no-repeat; mask: var(--cr-chevron) center/contain no-repeat;
  transition: rotate var(--dur) var(--ease); }
.cr-disclosure[open] > summary::before { rotate: 90deg; }
.cr-disclosure > summary:hover { text-decoration: underline; }
.cr-disclosure[open] { padding-block-end: var(--space-3); }
/* Mask-only SVG: painted by `background: currentColor`. */
:root { --cr-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 18 6-6-6-6'/%3E%3C/svg%3E"); }


/* --- components/plate.css --- */
/* Contract — key/value plate (document metadata, read-only):
     <dl class="cr-plate"><dt>Cliente</dt><dd>Ferretería El Roble Ltda.</dd>…</dl>
   Totals plate (the money summary of a document):
     <dl class="cr-totals"><dt>Subtotal</dt><dd>…</dd>
       <dt class="cr-totals__grand">Total</dt><dd class="cr-totals__grand">…</dd></dl>

   A <dl> is stacked block-level by default, which reads as a broken layout for
   tabular metadata — both plates lay it back out as a two-column grid. */

.cr-plate { display: grid; grid-template-columns: minmax(6rem, max-content) 1fr; gap: 0 var(--space-4); margin: 0; }
.cr-plate > dt { color: var(--fg-tertiary); padding-block: var(--space-2); }
.cr-plate > dd { margin: 0; color: var(--fg-primary); padding-block: var(--space-2); min-inline-size: 0; }
/* Hairline between rows — the ledger rule, drawn under both cells of a pair. */
.cr-plate > dt:not(:last-of-type), .cr-plate > dd:not(:last-of-type) {
  border-block-end: var(--hairline) solid var(--border-hairline);
}

/* Totals: labels left, figures flush right so the column of money reads as a column.
   gap must stay 0 on the inline axis — a column gap punches a visible break through
   the middle of every hairline, so the rules read as two broken stubs per row.
   The separation comes from padding on the value cell instead. */
.cr-totals { display: grid; grid-template-columns: 1fr max-content; gap: 0;
  margin: 0 0 0 auto; max-inline-size: 24rem; }
.cr-totals > dt { color: var(--fg-tertiary); padding-block: var(--space-2); }
.cr-totals > dd { margin: 0; padding-block: var(--space-2); padding-inline-start: var(--space-6);
  text-align: end; font-variant-numeric: tabular-nums; }
.cr-totals > dt:not(.cr-totals__grand), .cr-totals > dd:not(.cr-totals__grand) {
  border-block-end: var(--hairline) solid var(--border-hairline);
}
/* The grand total is the focal point of the document: larger, heavier, and set
   off by the brand rule — not just a bold sibling of Subtotal. */
.cr-totals__grand { border-block-start: var(--border-width) solid var(--fg-primary);
  padding-block-start: var(--space-3) !important; font-size: var(--text-lg);
  font-weight: var(--weight-semibold); color: var(--fg-primary) !important; }
.cr-totals__grand .cr-money__val { font-weight: var(--weight-semibold); }
.cr-totals__grand .cr-money__cur { font-size: 0.75em; }

@media (max-width: 640px) {
  .cr-totals { max-inline-size: none; }
  .cr-plate { grid-template-columns: 1fr; gap: 0; }
  .cr-plate > dt { padding-block-end: 0; border-block-end: none !important; }
  .cr-plate > dd { padding-block-start: var(--space-1); }
}


/* --- components/tag.css --- */
/* Contract: <span class="cr-tag">IVA 19%</span>  ·  removable: <span class="cr-tag">Aceptada <button class="cr-tag__x" aria-label="Quitar filtro"><svg class="cr-icon" aria-hidden="true"><use href="#i-x"/></svg></button></span> */
.cr-tag { display: inline-flex; align-items: center; gap: var(--space-1);
  padding-inline: var(--space-2); padding-block: 2px; font-size: var(--text-sm);
  background: var(--surface-2); color: var(--fg-secondary);
  border: var(--border-width) solid var(--border); border-radius: var(--radius-pill); }
.cr-tag__x { display: inline-flex; color: var(--fg-tertiary); border-radius: var(--radius-pill); }
.cr-tag__x:hover { color: var(--fg-primary); }


/* --- components/badge.css --- */
/* Contract — icon + text label ALWAYS (color never alone):
   <span class="cr-badge cr-badge--draft"><svg class="cr-icon" aria-hidden="true"><use href="#i-file-text"/></svg> Borrador</span>
   <span class="cr-badge cr-badge--submitted"><svg class="cr-icon" aria-hidden="true"><use href="#i-clock"/></svg> Enviada</span>
   <span class="cr-badge cr-badge--accepted"><svg class="cr-icon" aria-hidden="true"><use href="#i-sello"/></svg> Aceptada</span>
   <span class="cr-badge cr-badge--rejected"><svg class="cr-icon" aria-hidden="true"><use href="#i-x-circle"/></svg> Rechazada</span>
   <span class="cr-badge cr-badge--errored"><svg class="cr-icon" aria-hidden="true"><use href="#i-alert-triangle"/></svg> Con error</span>
   The accepted badge is the gold sello. Errored pairs with a retry affordance in context.
   The role variant is neutral (membership/access, not a fiscal state — never a status colour):
   <span class="cr-badge cr-badge--role"><svg class="cr-icon" aria-hidden="true"><use href="#i-user"/></svg> Propietario</span> */
.cr-badge { display: inline-flex; align-items: center; gap: var(--space-1);
  padding-inline: var(--space-2); padding-block: 2px; font-size: var(--text-sm); font-weight: var(--weight-medium);
  border-radius: var(--radius-pill); border: var(--border-width) solid transparent; white-space: nowrap; }
.cr-badge .cr-icon { inline-size: 0.9em; block-size: 0.9em; }
.cr-badge--draft { color: var(--status-draft-fg); background: var(--status-draft-bg); border-color: var(--status-draft-border); }
.cr-badge--submitted { color: var(--status-submitted-fg); background: var(--status-submitted-bg); border-color: var(--status-submitted-border); }
.cr-badge--accepted { color: var(--status-accepted-fg); background: var(--status-accepted-bg); border-color: var(--status-accepted-border); }
.cr-badge--rejected { color: var(--status-rejected-fg); background: var(--status-rejected-bg); border-color: var(--status-rejected-border); }
.cr-badge--errored { color: var(--status-errored-fg); background: var(--status-errored-bg); border-color: var(--status-errored-border); }
/* Roles are membership/access, not fiscal states — a single neutral treatment,
   distinguished by label + icon rather than colour (matches the neutral cr-tag). */
.cr-badge--role { color: var(--fg-secondary); background: var(--surface-2); border-color: var(--border); }

/* Standalone verified sello (beside a validated CUFE). */
.cr-sello { display: inline-flex; align-items: center; gap: var(--space-1); color: var(--verified-fg);
  background: var(--verified-bg); border: var(--border-width) solid var(--status-accepted-border);
  border-radius: var(--radius-pill); padding-inline: var(--space-2); padding-block: 2px; font-size: var(--text-sm); }
.cr-sello .cr-icon { color: var(--verified); }


/* --- components/money.css --- */
/* Contract (numeric string formatted server-side via Intl es-CO):
   <span class="cr-money"><span class="cr-money__cur">COP</span> <span class="cr-money__val">1.250.000</span></span>
   Negative: add cr-money--negative; the value string itself carries the sign/parentheses, e.g. "(1.234,50)". */
.cr-money { font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--money-default); }
.cr-money__cur { color: var(--fg-tertiary); font-size: 0.9em; margin-inline-end: 0.25em; }
.cr-money__val { font-weight: var(--weight-medium); }
.cr-money--negative { color: var(--money-negative); }
.cr-money--credit { color: var(--money-credit); }
/* In tables, money cells align to the end and share a mono-like rhythm. */
.cr-td--money { text-align: end; font-variant-numeric: tabular-nums; }
/* A <td> carrying a money/numeric value: the CELL must align, not just its span,
   or the figures stay ragged and columns can't be compared down the page. */
/* `.cr-table thead th` (0,1,1) outranks a bare `.cr-th--money` (0,1,0), so the
   HEADER of a money column stayed left-aligned over right-aligned figures.
   Match the table's own specificity to win. */
td.cr-td--money, th.cr-th--money { text-align: end; font-variant-numeric: tabular-nums; }
.cr-table thead th.cr-th--money { text-align: end; }
.cr-table tbody td.cr-td--money { text-align: end; }
td.cr-td--money .cr-money { display: inline-flex; gap: 0.25em; justify-content: flex-end; }

/* Dates render dd/mm/yyyy in es-CO; tabular so columns of dates line up. */
.cr-date { font-variant-numeric: tabular-nums; white-space: nowrap; }


/* --- components/identifier.css --- */
/* Contract:
   <span class="cr-id" translate="no">
     <span class="cr-id__val cr-truncate">a1b2c3…very-long-CUFE</span>
     <button class="cr-id__copy" aria-label="Copiar CUFE"><svg class="cr-icon" aria-hidden="true"><use href="#i-copy"/></svg></button>
   </span> */
.cr-id { display: inline-flex; align-items: center; gap: var(--space-1); max-inline-size: 100%;
  font-family: var(--font-mono); font-size: var(--text-sm); color: var(--fg-secondary); }
.cr-id__val { min-inline-size: 0; }
.cr-id__copy { display: inline-flex; color: var(--fg-tertiary); flex: none; border-radius: 2px; }
.cr-id__copy:hover { color: var(--brand); }
/* `cr-id--full`: the value is never clipped. The default treatment ellipsizes,
   which is right in a dense table and wrong wherever the operator is comparing
   the string character by character against a fiscal portal -- a CUFE shown as
   its first few characters and a "..." cannot be compared at all, and a title
   tooltip is not a fallback on touch or for a screen reader. `anywhere` because
   a CUFE has no break opportunity of its own. */
.cr-id--full { max-inline-size: 100%; }
.cr-id--full .cr-id__val { overflow-wrap: anywhere; }


/* --- components/code.css --- */
/* Contract:
   <pre class="cr-code" role="region" tabindex="0" aria-label="XML sin firmar del documento"><code>&lt;Invoice&gt;…</code></pre>

   A fiscal artifact shown verbatim (unsigned UBL, a DIAN response body). It must
   never widen the page: the block scrolls inside itself, and `tabindex="0"` is
   required by the contract because a scrollable region has to be reachable from
   the keyboard (WCAG 2.1.1) — with an aria-label, since a focusable region
   without an accessible name is a bare stop for a screen-reader user.

   Not wrapped: an XML line break is information here, and re-wrapping a document
   the operator is comparing byte-for-byte against a portal would hide exactly
   the whitespace difference that makes a CUFE irreproducible. */
.cr-code {
  margin: 0; padding: var(--space-3);
  max-block-size: 28rem; overflow: auto; overscroll-behavior: contain;
  background: var(--surface-2); color: var(--fg-secondary);
  border: var(--border-width) solid var(--border); border-radius: var(--radius-card);
  font-family: var(--font-mono); font-size: var(--text-sm); line-height: var(--leading-normal);
  font-variant-numeric: normal; /* fiscal IDs are compared glyph by glyph, not aligned in columns */
  -webkit-overflow-scrolling: touch;
}
.cr-code code { font: inherit; color: inherit; }


/* --- components/table.css --- */
/* Contract:
   <div class="cr-table-wrap">
     <table class="cr-table">
       <caption class="cr-visually-hidden">Registro de facturas</caption>
       <thead><tr>
         <th scope="col" class="cr-th--select"><input type="checkbox" aria-label="Seleccionar todo"></th>
         <th scope="col"><button class="cr-th-sort" aria-label="Ordenar por número">Número <svg class="cr-icon" aria-hidden="true"><use href="#i-arrow-up-down"/></svg></button></th>
         <th scope="col" aria-sort="ascending">Fecha</th>
         <th scope="col" class="cr-th--num">Total</th>
         <th scope="col">Estado</th>
       </tr></thead>
       <tbody>
         <tr><td>…</td>…</tr>
         <tr aria-selected="true">…</tr>
       </tbody>
     </table>
   </div>
   Empty:   <tbody><tr><td colspan="5"><div class="cr-table-state">… (see empty-state)</div></td></tr></tbody>
   Loading: add class cr-table-wrap--loading (skeleton rows) ; Error: cr-table-state with retry. */
.cr-table-wrap { inline-size: 100%; overflow-x: auto; border: var(--border-width) solid var(--border); border-radius: var(--radius-card); }
.cr-table { inline-size: 100%; border-collapse: collapse; font-size: var(--text-base); }
.cr-table caption { text-align: start; }
.cr-table thead th { position: sticky; inset-block-start: 0; z-index: var(--z-sticky);
  background: var(--surface-2); color: var(--fg-secondary); font-weight: var(--weight-semibold);
  text-align: start; padding: var(--space-2) var(--space-3); white-space: nowrap;
  border-block-end: var(--border-width) solid var(--border-emphasis); }
.cr-table tbody td { padding-inline: var(--space-3); block-size: var(--row-h);
  border-block-start: var(--hairline) solid var(--border-hairline); color: var(--fg-primary); vertical-align: middle; }
.cr-table tbody tr:hover td { background: var(--surface-1); }
.cr-table tbody tr[aria-selected="true"] td { background: color-mix(in srgb, var(--brand) 8%, var(--surface-0)); }
.cr-table tbody tr:focus-within td { box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--brand) 40%, transparent); }
.cr-th--num, .cr-td--num { text-align: end; font-variant-numeric: tabular-nums; }
.cr-th--select, .cr-td--select { inline-size: 1%; white-space: nowrap; }
.cr-th-sort { display: inline-flex; align-items: center; gap: var(--space-1); color: inherit; font: inherit; font-weight: inherit; }
.cr-th-sort .cr-icon { color: var(--fg-tertiary); }
th[aria-sort] .cr-th-sort .cr-icon, th[aria-sort] { color: var(--fg-primary); }

/* States shown in a full-width row. */
.cr-table-state { display: flex; flex-direction: column; align-items: center; gap: var(--space-2);
  padding: var(--space-12) var(--space-4); color: var(--fg-tertiary); text-align: center; }
.cr-table-wrap--loading tbody td { color: transparent; }
.cr-table-wrap--loading tbody td > * { visibility: hidden; }
.cr-table-wrap--loading tbody td::after { content: ""; display: block; block-size: 0.8em; border-radius: 3px;
  background: linear-gradient(90deg, var(--surface-2), var(--surface-3), var(--surface-2)); background-size: 200% 100%;
  animation: cr-shimmer 1.2s ease-in-out infinite; }
@keyframes cr-shimmer { to { background-position: -200% 0; } }
@media (prefers-reduced-motion: reduce) { .cr-table-wrap--loading tbody td::after { animation: none; } }

/* Horizontal scroll affordance: overlay scrollbars mean a scrollable table looks
   identical to a complete one, so on a phone the Total/Estado columns just appear
   to be missing. These edge shadows are painted by the scroll container itself —
   `local` layers scroll away with the content, `scroll` layers stay pinned, so a
   shadow shows only on the side that still has content off-screen. No JS. */
.cr-table-wrap {
  background:
    linear-gradient(to right, var(--surface-0) 30%, transparent) 0 0 / 2.5rem 100% no-repeat local,
    linear-gradient(to left, var(--surface-0) 30%, transparent) 100% 0 / 2.5rem 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, var(--scroll-shadow), transparent) 0 0 / 0.9rem 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, var(--scroll-shadow), transparent) 100% 0 / 0.9rem 100% no-repeat scroll;
}

@media (max-width: 640px) {
  /* min() so the floor applies only to tables that genuinely need it: a wide
     6-column ledger still gets 40rem (engraved rules stay legible when scrolled),
     while a 2-column table shrinks to its content instead of being stretched to
     640px inside a ~272px card. */
  .cr-table { min-inline-size: min(40rem, max-content); }

  /* Keep the row's identifier on screen while the rest scrolls under it —
     otherwise a scrolled row is an anonymous set of numbers. */
  .cr-table thead th:first-child, .cr-table tbody td:first-child {
    position: sticky; inset-inline-start: 0; background: var(--surface-0);
    box-shadow: 1px 0 0 var(--border-hairline); }
  .cr-table thead th:first-child { z-index: calc(var(--z-sticky) + 1); background: var(--surface-2); }
  .cr-table tbody td:first-child { z-index: 1; }
  .cr-table tbody tr:hover td:first-child { background: var(--surface-1); }
}


/* --- components/timeline.css --- */
/* Contract (ordered list; aria-current marks the active step; data-state colors the terminal node):
   <ol class="cr-timeline" role="list" data-state="accepted">
     <li class="cr-timeline__step" data-done><span class="cr-timeline__node"></span>Borrador</li>
     <li class="cr-timeline__step" data-done><span class="cr-timeline__node"></span>Enviada a DIAN</li>
     <li class="cr-timeline__step" aria-current="step"><span class="cr-timeline__node"></span>Aceptada</li>
   </ol>
   role="list" is required: stripping list-style below drops list semantics in Safari/VO. */
.cr-timeline { display: flex; flex-wrap: wrap; gap: var(--space-4); padding: 0; list-style: none;
  font-size: var(--text-sm); color: var(--fg-tertiary); }
.cr-timeline__step { display: inline-flex; align-items: center; gap: var(--space-2); position: relative; }
.cr-timeline__step:not(:last-child)::after { content: ""; inline-size: var(--space-4); block-size: var(--hairline);
  background: var(--border-emphasis); margin-inline-start: var(--space-2); }
.cr-timeline__node { inline-size: 10px; block-size: 10px; border-radius: 50%;
  background: var(--surface-3); border: 2px solid var(--border-emphasis); flex: none; }
.cr-timeline__step[data-done] { color: var(--fg-secondary); }
.cr-timeline__step[data-done] .cr-timeline__node { background: var(--fg-tertiary); border-color: var(--fg-tertiary); }
.cr-timeline__step[aria-current="step"] { color: var(--fg-primary); font-weight: var(--weight-semibold); }
/* Terminal colouring by outcome. */
.cr-timeline[data-state="accepted"] [aria-current] .cr-timeline__node { background: var(--verified); border-color: var(--verified); }
.cr-timeline[data-state="rejected"] [aria-current] .cr-timeline__node { background: var(--status-rejected-fg); border-color: var(--status-rejected-fg); }
.cr-timeline[data-state="errored"] [aria-current] .cr-timeline__node { background: var(--status-errored-fg); border-color: var(--status-errored-fg); }
.cr-timeline[data-state="submitted"] [aria-current] .cr-timeline__node { background: var(--status-submitted-fg); border-color: var(--status-submitted-fg); }


/* --- components/tabs.css --- */
/* Contract — two variants, same look:
   JS-widget tabs:  <div class="cr-tabs" role="tablist">
     <button class="cr-tab" role="tab" aria-selected="true">Detalle</button>
     <button class="cr-tab" role="tab" aria-selected="false">XML</button></div>
   Navigation tabs (URL-driven, plain links — no JS):
   <nav class="cr-tabs" aria-label="…"><a class="cr-tab" href="…" aria-current="page">Módulos</a></nav> */
.cr-tabs { display: flex; gap: var(--space-1); border-block-end: var(--border-width) solid var(--border); overflow-x: auto; }
/* inline-flex: the reset makes every <svg> display:block, so an icon dropped
   into an inline <a> would take a line of its own above the label. */
.cr-tab { display: inline-flex; align-items: center; gap: var(--space-2); min-block-size: 40px;
  padding: var(--space-2) var(--space-3); font-weight: var(--weight-medium); color: var(--fg-tertiary);
  border-block-end: 2px solid transparent; margin-block-end: -1px; text-decoration: none; white-space: nowrap; }
.cr-tab:hover { color: var(--fg-primary); text-decoration: none; }
.cr-tab[aria-selected="true"], .cr-tab[aria-current="page"] { color: var(--fg-primary); border-block-end-color: var(--brand); }

/* Segmented filter — a scoped list filter (estado, periodo), NOT page navigation.
   Contract: <nav class="cr-segments" aria-label="Filtrar por estado">
     <a class="cr-segment" href="…" aria-current="page">Todas</a>
     <a class="cr-segment" href="…">Borradores</a></nav>
   Distinct from .cr-tabs, which switches the whole view. */
.cr-segments { display: inline-flex; gap: 0; padding: 2px; background: var(--surface-1);
  border: var(--border-width) solid var(--border); border-radius: var(--radius-control);
  align-self: flex-start; max-inline-size: 100%; overflow-x: auto; }
.cr-segment { padding: var(--space-1) var(--space-3); font-size: var(--text-sm);
  font-weight: var(--weight-medium); color: var(--fg-tertiary); text-decoration: none;
  border-radius: 2px; white-space: nowrap; line-height: calc(var(--control-h) - 8px);
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease); }
.cr-segment:hover { color: var(--fg-primary); text-decoration: none; }
.cr-segment[aria-current="page"] { background: var(--surface-0); color: var(--fg-primary);
  box-shadow: var(--elevation-raised); }


/* --- components/breadcrumb.css --- */
/* Contract: <nav class="cr-breadcrumb" aria-label="Ruta"><ol role="list">
     <li><a class="cr-link" href="/facturas">Facturas</a></li>
     <li aria-current="page">FE-0042</li></ol></nav>
   role="list" is required: stripping list-style below drops list semantics in Safari/VO. */
.cr-breadcrumb ol { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); list-style: none; padding: 0;
  font-size: var(--text-sm); color: var(--fg-tertiary); }
.cr-breadcrumb li:not(:last-child)::after { content: "/"; margin-inline-start: var(--space-2); color: var(--border-emphasis); }
.cr-breadcrumb [aria-current="page"] { color: var(--fg-secondary); }


/* --- components/menu.css --- */
/* Contract (works with no JS): <details class="cr-menu">
     <summary class="cr-btn cr-btn--secondary">Acciones <svg class="cr-icon" aria-hidden="true"><use href="#i-chevron-down"/></svg></summary>
     <div class="cr-menu__list" role="menu">
       <button class="cr-menu__item" role="menuitem">Ver XML</button>
       <button class="cr-menu__item cr-menu__item--danger" role="menuitem">Anular</button>
     </div></details> */
.cr-menu { position: relative; display: inline-block; }
.cr-menu > summary { list-style: none; }
.cr-menu > summary::-webkit-details-marker { display: none; }
.cr-menu__list { position: absolute; inset-block-start: calc(100% + var(--space-1)); inset-inline-end: 0; z-index: var(--z-dropdown);
  min-inline-size: 12rem; padding: var(--space-1); background: var(--surface-1);
  border: var(--border-width) solid var(--border-emphasis); border-radius: var(--radius-control); box-shadow: var(--elevation-menu); }
.cr-menu__item { display: flex; inline-size: 100%; align-items: center; gap: var(--space-2); text-align: start;
  padding: var(--space-2) var(--space-2); border-radius: var(--radius-control); color: var(--fg-primary); font-size: var(--text-base);
  min-block-size: 40px; }
.cr-menu__item:hover { background: var(--surface-2); }
.cr-menu__item--danger { color: var(--status-rejected-fg); }
.cr-menu__item:disabled, .cr-menu__item[data-restricted] { color: var(--fg-muted); cursor: not-allowed; }


/* --- components/tooltip.css --- */
/* Contract: <span class="cr-tooltip"><button aria-describedby="tt1">…</button>
     <span role="tooltip" id="tt1" class="cr-tooltip__bubble">Requiere permiso einvoice.transmit</span></span> */
.cr-tooltip { position: relative; display: inline-flex; }
.cr-tooltip__bubble { position: absolute; inset-block-end: calc(100% + var(--space-1)); inset-inline-start: 50%;
  transform: translateX(-50%); z-index: var(--z-toast); max-inline-size: 16rem; padding: var(--space-1) var(--space-2);
  background: var(--fg-primary); color: var(--surface-0); font-size: var(--text-sm); border-radius: var(--radius-control);
  box-shadow: var(--elevation-menu); opacity: 0; visibility: hidden; transition: opacity var(--dur) var(--ease); white-space: normal; }
.cr-tooltip:hover .cr-tooltip__bubble, .cr-tooltip:focus-within .cr-tooltip__bubble { opacity: 1; visibility: visible; }


/* --- components/pagination.css --- */
/* Contract: <nav class="cr-pager" aria-label="Paginación">
     <a class="cr-btn cr-btn--secondary" href="?page=1" aria-label="Anterior"><svg class="cr-icon" aria-hidden="true"><use href="#i-chevron-left"/></svg></a>
     <span class="cr-pager__info">Página 2 de 18 · 1.245 facturas</span>
     <a class="cr-btn cr-btn--secondary" href="?page=3" aria-label="Siguiente"><svg class="cr-icon" aria-hidden="true"><use href="#i-chevron-right"/></svg></a></nav> */
.cr-pager { display: flex; align-items: center; gap: var(--space-3); justify-content: flex-end; }
.cr-pager__info { font-size: var(--text-sm); color: var(--fg-tertiary); font-variant-numeric: tabular-nums; }


/* --- components/feedback.css --- */
/* Contract — inline alert: <div class="cr-alert cr-alert--errored" role="alert">
     <svg class="cr-icon-lg" aria-hidden="true"><use href="#i-alert-triangle"/></svg>
     <div><strong>No se pudo transmitir.</strong> Reintente el envío a DIAN. <button class="cr-btn cr-btn--secondary">Reintentar</button></div></div>
   Toast region: <div class="cr-toasts" aria-live="polite" aria-atomic="false"> <div class="cr-toast cr-toast--accepted">…</div> </div> */
.cr-alert { display: flex; gap: var(--space-3); padding: var(--space-3) var(--space-4);
  border: var(--border-width) solid var(--border); border-radius: var(--radius-card); background: var(--surface-1); }
.cr-alert .cr-icon-lg { flex: none; margin-block-start: 2px; }
.cr-alert--info .cr-icon-lg { color: var(--status-submitted-fg); }
.cr-alert--accepted .cr-icon-lg { color: var(--verified); }
.cr-alert--errored .cr-icon-lg { color: var(--status-errored-fg); }
.cr-alert--rejected .cr-icon-lg { color: var(--status-rejected-fg); }

.cr-toasts { position: fixed; inset-block-end: var(--space-4); inset-inline-end: var(--space-4);
  display: flex; flex-direction: column; gap: var(--space-2); z-index: var(--z-toast); max-inline-size: 22rem; }
.cr-toast { display: flex; gap: var(--space-2); padding: var(--space-3); background: var(--surface-1);
  border: var(--border-width) solid var(--border-emphasis); border-radius: var(--radius-control); }

/* Empty state — a composed "nothing here yet, do this next" panel, not a bare
   sentence. Contract:
     <div class="cr-empty"><svg class="cr-icon-lg" …/><p>Todavía no…</p>
       <a class="cr-btn cr-btn--primary" href="…">Crear la primera factura</a></div> */
.cr-empty { display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-3); padding: var(--space-16) var(--space-6); text-align: center;
  border: var(--hairline) dashed var(--border); border-radius: var(--radius-card);
  background: var(--surface-1); color: var(--fg-secondary); }
.cr-empty .cr-icon-lg { inline-size: 28px; block-size: 28px; color: var(--fg-muted); }
.cr-empty p { margin: 0; text-wrap: pretty; max-inline-size: 34ch; }

/* Status remains explicit in the message and icon, with color as reinforcement. */
.cr-toast--info .cr-icon-lg { color: var(--status-submitted-fg); }
.cr-toast--accepted .cr-icon-lg { color: var(--verified); }
.cr-toast--errored .cr-icon-lg { color: var(--status-errored-fg); }
.cr-toast .cr-icon-lg { flex: none; }


/* --- components/modal.css --- */
/* Contract (native dialog; host opens with showModal()):
   <dialog class="cr-modal" aria-labelledby="m-title">
     <form method="dialog" class="cr-modal__panel">
       <header class="cr-modal__header"><h2 id="m-title">Anular factura</h2></header>
       <div class="cr-modal__body">…</div>
       <footer class="cr-modal__footer">
         <button class="cr-btn cr-btn--ghost" value="cancel">Cancelar</button>
         <button class="cr-btn cr-btn--danger" value="confirm">Anular</button>
       </footer>
     </form>
   </dialog>
   Consequential (irreversible, NOT a delete — e.g. transmit to DIAN): add class cr-modal--consequential and use cr-btn--primary. */
.cr-modal { padding: 0; border: none; background: transparent; max-inline-size: min(32rem, 92vw); }
.cr-modal::backdrop { background: color-mix(in srgb, var(--fg-primary) 45%, transparent); }
.cr-modal__panel { background: var(--surface-1); border: var(--border-width) solid var(--border-emphasis);
  border-radius: var(--radius-card); box-shadow: var(--elevation-modal); overscroll-behavior: contain; }
.cr-modal__header { padding: var(--space-4); border-block-end: var(--hairline) solid var(--border-hairline); }
.cr-modal__body { padding: var(--space-4); color: var(--fg-secondary); }
.cr-modal__footer { padding: var(--space-3) var(--space-4); border-block-start: var(--hairline) solid var(--border-hairline);
  display: flex; gap: var(--space-2); justify-content: flex-end; }
/* Consequential actions announce the stakes with an accent edge distinct from destructive. */
.cr-modal--consequential .cr-modal__panel { border-inline-start: 3px solid var(--verified); }


/* --- components/shell.css --- */
/* Contract:
   <a class="skip-link" href="#cr-main">Saltar al contenido</a>
   <div class="cr-app">
     <header class="cr-topbar">
       <a class="cr-brand" href="/"><svg class="cr-icon-lg" aria-hidden="true"><use href="#i-sello"/></svg> Crystelis</a>
       <button class="cr-org" aria-haspopup="menu" aria-expanded="false">
         <svg class="cr-icon-lg" aria-hidden="true"><use href="#i-building-2"/></svg>
         <span class="cr-org__text"><span class="cr-org__name">Comercial Andina SAS</span>
           <span class="cr-org__nit" translate="no">NIT 900.123.456-7</span></span>
         <svg class="cr-icon" aria-hidden="true"><use href="#i-chevron-down"/></svg>
       </button>
       <div class="cr-topbar__search"><input class="cr-input" type="search" aria-label="Buscar facturas, NIT o CUFE" placeholder="Buscar facturas, NIT, CUFE…" spellcheck="false"></div>
       <div class="cr-topbar__actions">
         <button class="cr-btn cr-btn--icon" aria-label="Densidad" data-density-toggle><svg class="cr-icon-lg" aria-hidden="true"><use href="#i-rows-3"/></svg></button>
         <button class="cr-btn cr-btn--icon" aria-label="Tema" data-theme-toggle><svg class="cr-icon-lg" aria-hidden="true"><use href="#i-moon"/></svg></button>
         <button class="cr-btn cr-btn--icon" aria-label="Cuenta"><svg class="cr-icon-lg" aria-hidden="true"><use href="#i-user"/></svg></button>
       </div>
     </header>
     <div class="cr-app__body">
       <nav class="cr-sidebar" aria-label="Módulos">
         <div class="cr-sidebar__group"><p class="cr-sidebar__title">Facturación</p>
           <a class="cr-navitem" href="/facturas" aria-current="page"><svg class="cr-icon" aria-hidden="true"><use href="#i-file-text"/></svg> Facturas</a>
           <a class="cr-navitem" href="/clientes"><svg class="cr-icon" aria-hidden="true"><use href="#i-user"/></svg> Clientes</a>
         </div>
       </nav>
       <main id="cr-main" class="cr-main">
         <div class="cr-page-header">
           <nav class="cr-breadcrumb" aria-label="Ruta"><ol><li><a class="cr-link" href="/facturas">Facturas</a></li><li aria-current="page">FE-0042</li></ol></nav>
           <div class="cr-page-header__row"><h1>Facturas</h1><div class="cr-page-header__actions">…</div></div>
         </div>
         <div class="cr-page-body">…</div>
       </main>
     </div>
   </div>
*/
.cr-app { min-block-size: 100vh; display: flex; flex-direction: column; }
.cr-topbar { display: flex; align-items: center; gap: var(--space-4); padding-inline: var(--space-4);
  block-size: 52px; background: var(--surface-0); border-block-end: var(--border-width) solid var(--border);
  position: sticky; inset-block-start: 0; z-index: var(--z-sticky); }
.cr-brand { display: inline-flex; align-items: center; gap: var(--space-2); font-weight: var(--weight-semibold);
  color: var(--fg-primary); text-decoration: none; letter-spacing: var(--tracking-tight); flex: none; }
.cr-brand .cr-icon-lg { color: var(--verified); }

/* Org-switcher plate — the empresa activa is a first-class, always-legible element. */
/* min-inline-size:0 is what lets the plate shrink below its intrinsic content width.
   Without it the nowrap org name holds the whole topbar open and the page scrolls
   sideways on a phone (449px of content in a 390px viewport). */
.cr-org { display: inline-flex; align-items: center; gap: var(--space-2); padding: var(--space-1) var(--space-3);
  background: var(--surface-1); border: var(--border-width) solid var(--border-emphasis); border-radius: var(--radius-control);
  color: var(--fg-primary); max-inline-size: 22rem; min-inline-size: 0; flex-shrink: 1; }
.cr-org:hover { background: var(--surface-2); }
.cr-org__text { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.15; min-inline-size: 0; }
.cr-org__name { font-weight: var(--weight-medium); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-inline-size: 15rem; }
.cr-org__nit { font-size: var(--text-xs); color: var(--fg-tertiary); font-variant-numeric: tabular-nums; }
.cr-org .cr-icon-lg { color: var(--fg-tertiary); }

.cr-topbar__search { flex: 1; max-inline-size: 32rem; min-inline-size: 0; }
.cr-topbar__actions { display: flex; align-items: center; gap: var(--space-1); margin-inline-start: auto; flex: none; }

.cr-app__body { display: flex; flex: 1; min-block-size: 0; }
.cr-sidebar { inline-size: 15rem; flex: none; padding: var(--space-3); background: var(--surface-0);
  border-inline-end: var(--border-width) solid var(--border); overflow-y: auto; }
.cr-sidebar__group { margin-block-end: var(--space-4); }
.cr-sidebar__title { font-size: var(--text-xs); font-weight: var(--weight-semibold); text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--fg-muted); padding-inline: var(--space-2); margin-block-end: var(--space-1); }
.cr-navitem { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-2) var(--space-2);
  min-block-size: 40px;
  border-radius: var(--radius-control); color: var(--fg-secondary); text-decoration: none; font-size: var(--text-base); }
.cr-navitem:hover { background: var(--surface-1); color: var(--fg-primary); }
.cr-navitem[aria-current="page"] { background: color-mix(in srgb, var(--brand) 10%, var(--surface-0));
  color: var(--fg-primary); font-weight: var(--weight-medium); box-shadow: inset 2px 0 0 var(--brand); }
.cr-navitem .cr-icon { color: var(--fg-tertiary); }
.cr-navitem[aria-current="page"] .cr-icon { color: var(--brand); }

.cr-main { flex: 1; min-inline-size: 0; overflow-y: auto; }
.cr-page-header { padding: var(--space-4) var(--space-6); border-block-end: var(--hairline) solid var(--border-hairline); }
/* wrap: the title + its action cluster (badges + "Contabilizar") exceed a phone's
   width on every detail screen; without wrapping the primary action is clipped. */
.cr-page-header__row { display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4); margin-block-start: var(--space-2); flex-wrap: wrap; }
.cr-page-header__row > h1 { min-inline-size: 0; }
.cr-page-header__actions { display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center; }
.cr-page-body { padding: var(--space-6); display: flex; flex-direction: column; gap: var(--space-6); }

@media (max-width: 860px) {
  .cr-sidebar { position: fixed; inset-block: 52px 0; inset-inline-start: 0; z-index: var(--z-dropdown);
    transform: translateX(-100%); transition: transform var(--dur-slow) var(--ease); box-shadow: var(--elevation-menu); }
  .cr-app[data-nav-open] .cr-sidebar { transform: translateX(0); }
  .cr-org__name { max-inline-size: 8rem; }
  .cr-topbar__search { display: none; }
  /* Tighter rhythm so brand + org plate + actions fit a phone without scrolling. */
  .cr-topbar { gap: var(--space-2); padding-inline: var(--space-2); }
  .cr-page-header { padding-inline: var(--space-4); }
  .cr-page-body { padding: var(--space-4); gap: var(--space-4); }
}

/* Below ~420px the wordmark is the first thing to go — the sello still marks home. */
@media (max-width: 420px) {
  .cr-brand { font-size: 0; gap: 0; }
  .cr-brand .cr-icon-lg { font-size: var(--text-base); }
  .cr-org { padding-inline: var(--space-2); }
}
