/* --- tokens.css --- */
/* ============================================================
   SEMÁFORO — portable design tokens (ds-portable, 2026-08-26)
   The shipped "technical dopamine" system, exported flat at :root
   (no page-class scope needed): a six-step technical gray ladder
   does the depth (NO drop shadows), the primary action is INK, and
   five saturated accents each own a JOB. Doctrine:
     - Vivid fills carry INK on top (white only on deep tones).
     - No pastel tints: a surface is fully saturated or technical gray.
     - Secondary buttons are FILLED gray, never outline-only.
     - Borders: rest B1 · hover B3 · focus = 2px ink ring.
     - Inputs REST on the page gray (--color-field), wake white on focus.
   Two levels:
     1. Primitives: raw palette and scales. Never used by components.
     2. Semantic: what components consume. Retheme HERE only.
   Breakpoints (custom properties don't work in media queries):
     480px (sm) · 768px (md) · 1024px (lg) · 1280px (xl)
   ============================================================ */

:root {
  /* ---------- 1. PRIMITIVES ---------- */

  /* Accents with JOBS (rotation by section, never at random):
     naranja = CTA/energía (spend it where money or the main ask lives)
     amarillo = destacar/ofertas/"Nuevo"/bandas de aviso
     verde = éxito/positivo/canal de venta
     azul = información/enlaces/estados neutrales-positivos
     bermellón = destructivo (vivo, no el rojo de librería)
     lila = "editado"/marcas de trabajo en curso */
  --accent-orange: #FF5000;
  --accent-yellow: #FFCC00;
  --accent-green:  #37AA65;
  --accent-blue:   #508EDC;
  --accent-verm:   #EC5011;
  --accent-lilac:  #B680D2;

  /* Technical grays (pure, no warm cast) */
  --neutral-0:   #FFFFFF;
  --neutral-50:  #F8F8F8;
  --neutral-100: #F4F4F4;
  --neutral-200: #ECECEC;
  --neutral-300: #DDDDDD;
  --neutral-400: #B3B3B3;
  --neutral-500: #6E6E6E;
  --neutral-600: #555555;
  --neutral-700: #3C3C3C;
  --neutral-800: #2E2E2E;
  --neutral-900: #282828;
  --neutral-950: #111111;

  /* Green ramp (success / selling channel) */
  --green-50:  #E4F3EA;
  --green-100: #C9E7D4;
  --green-200: #9AD4B2;
  --green-500: #37AA65;
  --green-600: #2E9155;
  --green-700: #27793F;
  --green-800: #1F6434;

  /* Red ramp (danger = vivid vermilion, not library red) */
  --red-50:  #FDE9E0;
  --red-100: #FAD4C3;
  --red-600: #EC5011;
  --red-700: #C9430E;

  /* Amber ramp (warning / offers = the yellow family) */
  --amber-50:  #FFF6CC;
  --amber-100: #FFF3C4;
  --amber-500: #FFCC00;
  --amber-800: #7A5A00;

  /* Purple ramp (edited / bulk-changed marks) */
  --purple-50:  #F7F0FB;
  --purple-100: #EFE2F7;
  --purple-200: #E2CCF0;
  --purple-300: #D0ACE4;
  --purple-400: #C094DB;
  --purple-600: #B680D2;
  --purple-700: #7A4E96;
  --purple-800: #5F3A77;

  /* Typography */
  --font-sans: "Geist", system-ui, -apple-system, "Segoe UI", sans-serif;
  /* El kit consumia --font-mono con fallback y nunca lo definia, y varios
     componentes escribian `ui-monospace, monospace` a mano. Se define. */
  --font-mono: "Geist Mono", ui-monospace, "SF Mono", Consolas, monospace;
  /* Una sola familia por ahora. Cuando un evento suba la suya (fase 6), lo
     que cambia es esta ficha, no los componentes. */
  --font-display: var(--font-sans);

  --text-xs:  0.75rem;    /* 12px */
  --text-sm:  0.875rem;   /* 14px */
  --text-md:  1rem;       /* 16px — base */
  --text-lg:  1.125rem;   /* 18px */
  --text-xl:  1.25rem;    /* 20px */
  --text-2xl: 1.5rem;     /* 24px */
  --text-3xl: 1.875rem;   /* 30px */
  --text-4xl: 2.25rem;    /* 36px */
  --text-5xl: 3rem;       /* 48px */

  --leading-tight:   1.15; /* large headings */
  --leading-snug:    1.3;  /* small headings, labels */
  --leading-normal:  1.5;  /* body */
  --leading-relaxed: 1.65; /* long paragraphs */

  --weight-regular:  400;
  --weight-medium:   500;
  --weight-semibold: 600;
  --weight-bold:     700;

  --tracking-tight: -0.02em; /* headings ≥ 2xl */
  --tracking-snug:  -0.01em; /* small headings */

  /* Spacing — see fundamentos.css
     The 4px scale that used to be declared here lived in two places: this file
     said `--space-5: 1.25rem` (20px) and fundamentos.css redeclared it as 24px,
     which is what the browser drew. Reading this file told you something false.
     The whole scale now has ONE home, in fundamentos.css, next to the --e*
     steps it maps onto. Do not re-add it here. */

  /* Radii — official scale: 4, 6, 8, 12, 16, 20, 24, 32. */
  --radius-xs:   4px;
  --radius-sm:   6px;
  --radius-md:   8px;
  --radius-lg:   12px;
  --radius-xl:   16px;
  --radius-2xl:  20px;
  --radius-3xl:  24px;
  --radius-4xl:  32px;
  --radius-full: 9999px;

  /* LA ESCALERA, de afuera hacia adentro: 20 el contenedor, 16 el material,
     12 el bloque de adentro, 8 lo que se toca. Cada nivel redondea MENOS que el
     que lo contiene, que es la unica regla que hace que esto se vea coherente:
     un hijo con el mismo radio que su caja -o mas- se lee como si se saliera.
     Lo pidio Leon asi: "el redondeado mas gordo deberia ser el de la caja que
     contiene cada seccion, y luego internamente un redondeo mas apropiado".

     Radius ROLES: chrome consumes these, not the raw scale.
     Standalone cards / section containers / floating panels = card;
     media thumbnails >= 6rem = thumb; anything nested INSIDE a card -link
     rows, controls, menus, plates- = inner; buttons are pills, see button.css.

     Raised one step on 2026-10-16, at Leon's request: "creo que podriamos hacer
     mas redondeado dentro del ds los contenedores mas grandes, y hacer un poco
     mas redondeados los normales que hay internamente, para que se sienta mas
     amigable". The RAW SCALE was NOT touched, on purpose: it is the shared
     4px-grid language of the kit, and the note right above this one is about
     what happens when this file stops telling the truth about a scale. What
     moved are the roles, which is what roles are for. */
  --radius-card:    var(--radius-2xl);
  --radius-thumb:   var(--radius-xl);
  --radius-inner:   var(--radius-lg);
  /* control: lo que se toca -botones, campos, selects, items de menu-. Es la
     HOJA del arbol: siempre vive adentro de algo, asi que es el escalon mas
     chico de los cuatro. La pildora se probo el 2026-10-16 y se retiro el mismo
     dia: "me di cuenta que el redondeado total no fue una buena idea". El
     motivo es geometrico y no de gusto -un boton dentro de una fila de 12 no
     puede redondear 9999- y es lo que rompia la escalera. */
  --radius-control: var(--radius-md);
  /* micro: anything about 36px or smaller -glyph boxes, avatars, icon-only hit
     areas, tick marks-. It does NOT follow `inner`: a 32px square at 12px of
     radius reads as a blob, and at 20 it is a circle. This is the step that
     stays put when the rest of the scale goes friendlier. */
  --radius-micro:   var(--radius-md);

  /* Shadows: OFF by doctrine — depth comes from the gray ladder and
     borders. The tokens stay so components keep compiling; re-enable per
     project if you must (you probably must not). */
  --shadow-xs: none;
  --shadow-sm: none;
  --shadow-md: none;
  --shadow-lg: none;

  /* Layers */
  --z-dropdown: 100;
  --z-sticky:   200;
  --z-modal:    400;
  --z-toast:    500;

  /* Motion */
  --duration-fast:   120ms;
  --duration-base:   200ms;
  --duration-slow:   320ms;
  --ease-out: cubic-bezier(0.2, 0, 0, 1);

  /* ---------- 2. SEMANTIC (components consume ONLY these) ---------- */

  /* The gray ladder: L0 page · L1 card · L2 sunken tile · B1/B2/B3 borders */
  --color-bg:            var(--neutral-100);
  --color-surface:       var(--neutral-0);
  --color-surface-muted: var(--neutral-200);
  --color-border:        var(--neutral-300);
  --color-border-strong: #C7C7C7;
  --color-border-hover:  var(--neutral-400);
  --color-border-accent: var(--neutral-400);
  /* Modal/lightbox veil */
  --color-backdrop:      rgba(40, 40, 40, 0.55);

  --color-text:        var(--neutral-900);
  --color-text-muted:  var(--neutral-500);
  --color-text-subtle: #A9A9A9;

  /* Primary action: poster INK */
  --color-action:       var(--neutral-900);
  --color-action-hover: var(--neutral-950);
  --color-on-action:    var(--neutral-0);

  /* La misma accion, en superficie: lo que esta PUESTO, activo o encima.
     --------------------------------------------------------------------------
     Existe porque `--color-accent-soft` no sirve para eso y se estaba usando
     igual. El acento es el rol SEMANTICO de "salio bien": es verde en los
     veintisiete temas del catalogo, a proposito. Asi que pintar con el una zona
     activa, un chip elegido o un boton puesto deja esos elementos verdes sea
     cual sea el tema elegido -que es, literalmente, el sintoma que se reporto:
     "hay pantallas que no parecen estar tomando el tema"-.

     `--color-action` si es la ficha propia de cada tema -blanco en grafito,
     violeta en dracula, cian en neon- y es la que tiene que pintar lo que
     responde al tema. Esto es su version en superficie.

     Se deriva con `color-mix` y no se escribe tema por tema: asi un tema nuevo
     la trae sin tener que acordarse, y nunca puede discrepar de su accion. */
  --color-action-soft:  color-mix(in oklab, var(--color-action) 14%, var(--color-surface));

  /* Success / selling-channel green. Vivid green fills take INK on top;
     the strong/hover steps are deep enough for white if a component
     insists (buttons use strong). */
  --color-accent:        var(--green-500);
  --color-accent-strong: var(--green-700);
  --color-accent-hover:  var(--green-800);
  --color-accent-soft:   var(--green-50);
  --color-on-accent:     var(--neutral-0);

  /* Danger: vivid vermilion with ink on top */
  --color-danger:       var(--red-600);
  --color-danger-hover: #D64509;
  --color-danger-soft:  var(--red-50);
  --color-on-danger:    var(--neutral-900);

  /* Marketing CTA orange: spend it ONLY where the main ask lives */
  --color-cta:       var(--accent-orange);
  --color-cta-hover: #D64300;
  --color-on-cta:    var(--neutral-900);

  /* Info / links blue (white ink on top — it is the one deep accent) */
  --color-info: var(--accent-blue);

  /* Highlight yellow: offers, "new", plan bands. Solid chips take ink
     directly (deep = the yellow itself). */
  --color-highlight:        var(--amber-500);
  --color-highlight-soft:   var(--amber-100);
  --color-highlight-border: #EFD24D;
  --color-highlight-text:   var(--amber-800);
  --color-highlight-deep:   var(--amber-500);
  --color-on-highlight:     var(--neutral-900);

  --color-warning:      var(--amber-500);
  --color-warning-soft: var(--amber-50);
  --color-warning-text: var(--amber-800);

  --color-focus: var(--neutral-900);

  /* Inputs REST on the page gray so they read layered inside white cards
     (they wake to --color-surface + an ink ring on focus). */
  --color-field: var(--neutral-100);
}

/* --- base.css --- */
/* ============================================================
   base — reset, tipografia y @font-face. Necesita tokens.css.

   Portado de ds-portable-semaforo. Se tomaron el reset y la
   jerarquia tipografica; se descarto la maquinaria de otro
   producto (cc-boot, cc-scroll-lock, cc-admin), que dependia de
   un i18n y de un flujo de sesion que aca no existen.

   Las fuentes son LOCALES, no de Google Fonts. En un salon con
   mal internet un host externo mas es una cosa mas que puede
   fallar delante de 150 personas.
   ============================================================ */

@font-face {
  font-family: "Geist";
  src: url("fuentes/Geist-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* El kit trae el archivo pero nunca lo declara: los componentes que
   piden mono caen a la del sistema. Se declara aca. */
@font-face {
  font-family: "Geist Mono";
  src: url("fuentes/GeistMono-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
/* ---------- Reset ---------- */

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

* {
  margin: 0;
}

/* The hidden attribute must win over any component display value
   (e.g. .field { display: flex }), so conditional fields stay hidden. */
[hidden] {
  display: none !important;
}

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

body {
  font-family: var(--font-sans);
  font-size: var(--text-md);
  line-height: var(--leading-normal);
  color: var(--color-text);
  background: var(--color-bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img,
picture,
video,
svg {
  display: block;
  max-width: 100%;
}

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

/* ---------- Type hierarchy ---------- */

h1,
h2,
h3,
h4 {
  font-weight: var(--weight-semibold);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-snug);
  text-wrap: balance;
}

h1 {
  font-size: var(--text-4xl);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
}

h2 {
  font-size: var(--text-2xl);
  letter-spacing: var(--tracking-tight);
}

h3 {
  font-size: var(--text-xl);
}

h4 {
  font-size: var(--text-md);
}

p {
  max-width: 65ch;
}

small {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

a {
  color: inherit;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

/* La seleccion de texto, con la ficha del tema y no con el verde del acento.
   Mas fuerte que `--color-action-soft` porque abajo hay texto que tiene que
   seguir leyendose. */
::selection {
  background: color-mix(in oklab, var(--color-action) 30%, var(--color-surface));
}

/* ---------- Focus & interaction ---------- */

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

button {
  cursor: pointer;
}

button:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}

/* Full-screen dialogs lock the page behind them (any viewport). */
html.cc-scroll-lock,
html.cc-scroll-lock body { overflow: hidden; }

/* ---------- La barra de scroll ----------
   UNA SOLA VEZ, PARA TODO EL PRODUCTO.

   Cuatro componentes se habian escrito la suya -el cajon, la galeria, el
   selector de temas y la hoja del Studio-, cada uno con su ancho y su color, y
   el resto se quedaba con la del sistema operativo: en un tema oscuro, una
   barra gris clara de Windows al costado de una modal. La barra es parte de la
   superficie, asi que va en el reset y no en cada caja que la necesita.

   Se pinta con `--color-border-strong`, que es la ficha mas visible del tema
   que no compite con nada: la barra tiene que encontrarse cuando se la busca y
   desaparecer cuando no.

   `background-clip: content-box` con un borde transparente es lo que deja la
   barra flaca adentro de un canal ancho: el dedo y el puntero apuntan a 10px y
   lo que se ve son 4. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--color-border-strong) transparent;
}

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-corner { background: transparent; }

::-webkit-scrollbar-thumb {
  background: var(--color-border-strong);
  border: 3px solid transparent;
  border-radius: var(--radius-full);
  background-clip: content-box;
}

::-webkit-scrollbar-thumb:hover {
  background: var(--color-border-hover);
  background-clip: content-box;
}

/* ---------- Accessibility ---------- */

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

/* ---------- Skeleton loading placeholder ---------- */
/* A moving sheen instead of a spinner. Use on a block sized by the caller:
   <div class="skeleton" style="height: 1rem"></div> */
.skeleton {
  position: relative;
  overflow: hidden;
  background: var(--color-surface-muted);
  border-radius: var(--radius-sm);
}
.skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.7), transparent);
  animation: skeleton-shimmer 1.4s ease-in-out infinite;
}
@keyframes skeleton-shimmer { 100% { transform: translateX(100%); } }

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

/* --- fuentes/tipografias.css --- */
/* ============================================================
   tipografias.css — GENERADO por tools/fuentes.py

   No se edita a mano: se corre el script y se commitea el
   resultado. Las fuentes de origen estan en docs/fonts/.

   Una entrada por FAMILIA y los pesos adentro, como en
   cualquier programa de diseno: quien ajusta elige "Tiempos
   Text", no "Tiempos Text Semibold". Recortadas al alfabeto
   latino: lo que se escribe en la pantalla de un salon son
   nombres y fechas.

   `font-display: swap` a proposito: el texto se ve desde el
   primer cuadro con la fuente de respaldo y cambia cuando la
   propia llega. Una pantalla proyectada en blanco mientras baja
   una fuente es exactamente lo que no puede pasar.
   ============================================================ */

@font-face {
  font-family: "Sharp Grotesk";
  src: url("/ds/fuentes/sharp-grotesk-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Sharp Grotesk";
  src: url("/ds/fuentes/sharp-grotesk-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Sztos";
  src: url("/ds/fuentes/sztos-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Sztos";
  src: url("/ds/fuentes/sztos-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Tiempos Text";
  src: url("/ds/fuentes/tiempos-text-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Tiempos Text";
  src: url("/ds/fuentes/tiempos-text-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Spectral SC";
  src: url("/ds/fuentes/spectral-sc-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Spectral SC";
  src: url("/ds/fuentes/spectral-sc-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Sedan SC";
  src: url("/ds/fuentes/sedan-sc-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "IM Fell English";
  src: url("/ds/fuentes/im-fell-english-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "IM Fell English SC";
  src: url("/ds/fuentes/im-fell-english-sc-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Fell Type Premium";
  src: url("/ds/fuentes/fell-type-premium-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Stoke";
  src: url("/ds/fuentes/stoke-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Leckerli One";
  src: url("/ds/fuentes/leckerli-one-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Beth Ellen";
  src: url("/ds/fuentes/beth-ellen-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Borel";
  src: url("/ds/fuentes/borel-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "BM Hanna";
  src: url("/ds/fuentes/bm-hanna-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* --- componentes/marca.css --- */
/* ============================================================
   marca.css — el logo de portals

   Una forma, dos recortes: la marca entera (simbolo y palabra) y el simbolo
   solo. Los dos archivos viven en `/ds/marca/` y son el original del diseno,
   con el color cambiado a `currentColor`.

   SE PINTA COMO MASCARA, no como imagen.

   Un `<img>` no puede heredar el color: el SVG decidiria el suyo, y el panel
   tiene VEINTIOCHO temas -varios claros-. El logo vino en blanco, asi que en
   un tema claro seria un hueco. Como mascara, el archivo aporta la FORMA y la
   ficha del tema aporta el color, que es la misma division que ya usa el
   calendario del campo de fecha.

   Lo que se pierde: el degradado del original. Una mascara es alfa, asi que
   dentro de la silueta hay un solo color. Es el precio de que el logo se vea
   en los veintiocho temas, y en veinte pixeles de alto el degradado no se
   distinguia de todos modos.

   El tamano lo pone quien lo usa, con `block-size`. El ancho sale solo de la
   relacion del archivo.
   ============================================================ */

.marca {
  display: block;
  flex: none;
  block-size: 1.25rem;
  /* Las dos medidas del `viewBox` de `portals.svg`. Escrita como division y no
     resuelta a 4.56: asi se ve de donde sale si el archivo cambia. */
  aspect-ratio: 2820.71 / 618.38;
  background: currentColor;
  -webkit-mask-image: url(/ds/marca/portals.svg);
  mask-image: url(/ds/marca/portals.svg);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: left center;
  mask-position: left center;
  -webkit-mask-size: contain;
  mask-size: contain;
}

/* El simbolo solo. Donde la palabra no entra o ya esta escrita al lado. */
.marca--simbolo {
  aspect-ratio: 760.29 / 901.69;
  -webkit-mask-image: url(/ds/marca/simbolo.svg);
  mask-image: url(/ds/marca/simbolo.svg);
}

/* --- componentes/button.css --- */
/* Button — variants: primary (neutral action), accent (WhatsApp/positive),
   secondary, ghost, danger. Sizes: sm, md (default), lg. */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: 0 var(--space-4);
  height: 2.5rem; /* 40px */
  border: 1px solid transparent;
  /*
   * EL ESCALON DE CONTROL, que es el mas chico de la escalera.
   *
   * La pildora se probo el 2026-10-16 y se retiro el mismo dia: "me di cuenta
   * que el redondeado total no fue una buena idea". El motivo no es de gusto, es
   * geometrico: un boton vive SIEMPRE adentro de algo -una fila de 12, una
   * tarjeta de 20- y un hijo que redondea mas que su caja se lee como si se
   * saliera de ella. Por eso el boton es la hoja del arbol y toma el escalon
   * mas chico.
   *
   * Y los tres tamanios lo comparten: `sm` y `lg` fijaban 6 y 12 por su cuenta,
   * asi que un boton chico al lado de uno grande eran dos componentes.
   */
  border-radius: var(--radius-control);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--duration-fast) var(--ease-out),
    border-color var(--duration-fast) var(--ease-out),
    color var(--duration-fast) var(--ease-out),
    transform var(--duration-fast) var(--ease-out);
}

/* Press: a tiny dip confirms the tap. */
.btn:active { transform: scale(0.97); }
.btn:disabled { transform: none; }

.btn svg {
  width: 1rem;
  height: 1rem;
  flex-shrink: 0;
}

.btn--primary {
  background: var(--color-action);
  color: var(--color-on-action);
}

.btn--primary:hover {
  background: var(--color-action-hover);
}

.btn--accent {
  background: var(--color-accent-strong);
  color: var(--color-on-accent);
}

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

.btn--secondary {
  background: var(--color-surface);
  border-color: var(--color-border-strong);
  color: var(--color-text);
}

.btn--secondary:hover {
  border-color: var(--color-border-accent);
  background: var(--color-surface-muted);
}

/*
 * NEUTRAL: una accion que no es la principal, pero que se ve como un boton.
 *
 * `secondary` es `--color-surface` con borde, y eso funciona sobre el fondo de
 * la pagina -el boton queda un escalon por encima-. Deja de funcionar ADENTRO
 * DE UNA TARJETA, porque la tarjeta ya es `--color-surface`: el relleno del
 * boton iguala al de su contenedor y lo unico que queda es el borde. Leon lo
 * reporto exactamente asi: "solo tienen borde pero no fill... al hacerlo
 * demasiado invisible, ahora los botones de acciones se mezclan mucho con el
 * resto de cosas".
 *
 * Esta variante sube UN escalon mas: `--color-surface-muted`, que cada tema
 * define en su propia escala de tres -fondo, superficie, superficie apagada- asi
 * que el relleno es sutil y distinto del de la tarjeta en los dieciocho temas
 * sin escribir un color en ninguna parte.
 *
 * El hover no puede volver a `surface` -seria ir hacia atras- asi que se mezcla
 * hacia el color del texto: en un tema oscuro aclara y en uno claro oscurece,
 * que es la misma mecanica que usa `--color-action-hover`.
 */
/*
 * SIN BORDE, Y ESA ES LA REGLA QUE ORDENA TODO ESTO.
 *
 * Un borde es una AFORDANCIA cuando no hay nada mas que diga "esto se toca". Si
 * el elemento ya tiene relleno, el borde es decoracion — y encima de un relleno
 * sutil, decoracion que grita. Leon lo reporto sobre cuatro cosas a la vez
 * -copiar el link, copiar el de los invitados, descargar el QR, las fotos del
 * evento- y las cuatro son este boton.
 *
 * Asi que el relleno se queda y el borde se va. El `border: 1px solid
 * transparent` de la base no se saca: mantiene la medida, asi que un neutral al
 * lado de un `secondary` no baja un pixel.
 *
 * La otra mitad de la regla esta en los checkboxes, que NO tienen relleno: ahi
 * el borde es lo unico que dice que se puede tocar, y por eso ellos van al
 * escalon FUERTE. La misma decision mueve los dos en direcciones opuestas.
 */
.btn--neutral {
  background: var(--color-surface-muted);
  border-color: transparent;
  color: var(--color-text);
}

.btn--neutral:hover {
  background: color-mix(in srgb, var(--color-surface-muted) 88%, var(--color-text));
}

.btn--ghost {
  background: transparent;
  color: var(--color-text-muted);
}

.btn--ghost:hover {
  background: var(--color-surface-muted);
  color: var(--color-text);
}

.btn--danger {
  background: var(--color-danger);
  color: var(--color-on-danger, var(--color-on-action));
}

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

/*
 * LOS TAMANIOS NO TOCAN EL RADIO, y antes lo tocaban.
 *
 * `sm` lo bajaba a 6 y `lg` lo subia a 12, asi que al poner la pildora en la
 * base los botones chicos -que son la mayoria de los del panel- se habrian
 * quedado casi cuadrados y nadie habria visto el cambio donde mas se usa. Con
 * `--radius-full` no hace falta ningun ajuste por tamanio: la pildora sale de la
 * altura, sea 32, 40 o 48.
 */
.btn--sm {
  height: 2rem;
  padding: 0 var(--space-3);
  font-size: var(--text-xs);
}

.btn--lg {
  height: 3rem;
  padding: 0 var(--space-6);
  font-size: var(--text-md);
}

.btn--block {
  width: 100%;
}

/* --- componentes/field.css --- */
/* Form field — label + control + hint/error.
   Structure:
   <div class="field">
     <label class="field__label" for="x">…</label>
     <input class="field__control" id="x">
     <p class="field__hint">…</p>   |   <p class="field__error">…</p>
   </div> */

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.field__label {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  line-height: var(--leading-snug);
}

.field__label small {
  font-weight: var(--weight-regular);
}

.field__control {
  width: 100%;
  height: 2.5rem;
  padding: 0 var(--space-3);
  /* Themed checkouts fill with --color-field (the page bg) so inputs read
     against their surface card; unthemed keeps the surface fill. */
  background: var(--color-field, var(--color-surface));
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-control);
  font-size: var(--text-sm);
  transition: border-color var(--duration-fast) var(--ease-out);
}

/*
 * EN UN TELEFONO, LA LETRA DE UN CAMPO NUNCA MIDE MENOS DE 16px.
 *
 * iOS Safari hace ZOOM al enfocar un campo cuya letra mide menos de 16: la
 * pagina se agranda sola, se corre de lugar, y al salir del campo se queda
 * agrandada. Se reporto creando un espacio nuevo, pero pasa en TODOS los
 * campos del producto, porque todos usan esta clase. El peor es el del
 * invitado escribiendo su dedicatoria en la fiesta.
 *
 * `--text-sm` son 14. La hoja del invitado ya habia intentado esquivarlo
 * subiendo la raiz a 17 "para que iOS no haga zoom" —esta escrito en su
 * comentario— pero no alcanza: `--text-sm` es 0.875rem, o sea 14,9 sobre esa
 * raiz, y sigue abajo de 16. Un comentario que dice lo que el codigo no hace.
 *
 * `pointer: coarse` y no un ancho: lo que importa es que se toque con el dedo,
 * no cuantos pixeles mide la ventana. Una tablet de 1024 tambien hace zoom.
 *
 * Con `max()` para que siga siendo un piso: el dia que la escala suba, el
 * campo sube con ella en vez de quedar clavado en 16.
 */
@media (pointer: coarse) {
  .field__control { font-size: max(16px, var(--text-sm)); }
}

.field__control::placeholder {
  color: var(--color-text-subtle);
}

.field__control:hover {
  border-color: var(--color-border-hover);
}

/* Focus: border goes ink, crisp inset edge, no floating outline ring. */
.field__control:focus-visible {
  outline: none;
  border-color: var(--color-action);
  box-shadow: inset 0 0 0 1px var(--color-action);
  background: var(--color-surface); /* rests gray (--color-field), wakes white */
}

textarea.field__control {
  height: auto;
  min-height: 5.5rem;
  padding: var(--space-3);
  resize: vertical;
  line-height: var(--leading-normal);
}

/* --- el campo de fecha ----------------------------------------------------
   Las piezas que `capasDeFecha()` necesita; ver ds/controles.js para el porque
   de cada una. Viven en el kit y no en el panel porque las usan los dos campos
   de fecha del producto. */

/*
 * LAS MEDIDAS, por variable.
 *
 * Las capas se corren sobre el control por margen negativo, asi que necesitan
 * saber cuanto mide el control, cuanto hueco pone el envoltorio entre sus hijos,
 * y cuanto relleno tiene el control a los costados. Los dos campos de fecha del
 * producto no coinciden en ninguna de las tres -el del kit mide 2,5rem y el del
 * formulario de crear evento 48px- asi que cada envoltorio declara las suyas.
 */
.field {
  --alto-fecha: 2.5rem;
  --aire-fecha: var(--space-2);
  --pad-fecha: var(--space-3);
}

/*
 * EL CONTROL, QUE EN iOS SE DESBORDABA.
 *
 * Un `input[type=date]` en Safari de telefono trae su propio ancho intrinseco y
 * NO obedece a `width: 100%` mientras conserve su apariencia nativa: el campo
 * salia mas ancho que el dialogo y llegaba hasta el borde de la pantalla.
 * `appearance: none` es lo que lo apaga, y de paso es lo que saca las
 * decoraciones nativas que cada navegador le agrega por su cuenta.
 *
 * El borde, el fondo y la altura los pone `field__control` -o la clase del
 * formulario que lo use-, asi que apagar la apariencia nativa no deja el campo
 * sin forma.
 */
.entrada-con-calendario {
  -webkit-appearance: none;
  appearance: none;
  inline-size: 100%;
  max-inline-size: 100%;
  min-inline-size: 0;
  /* Lugar para el icono: sin esto la fecha pasa por abajo del calendario. */
  padding-inline-end: calc(var(--pad-fecha) * 2 + 1.25rem);
}

/*
 * El indicador nativo se esconde, PERO NO SE SACA.
 *
 * Con `display: none` se va tambien su area de toque, y en Chrome de escritorio
 * esa area es la unica forma de abrir el calendario con el mouse: hacer clic en
 * el texto solo deja escribir. Invisible sigue abriendo, y encima va el nuestro.
 */
.entrada-con-calendario::-webkit-calendar-picker-indicator {
  opacity: 0;
  inline-size: 1.25rem;
  block-size: 1.25rem;
  margin: 0;
  padding: 0;
  cursor: pointer;
}

/*
 * Y CUANDO EL CALENDARIO LO ABRE EL JS, el blanco nativo se apaga.
 *
 * La clase la pone `capasDeFecha` solo si el navegador tiene `showPicker()`
 * (ver controles.js): con los dos caminos vivos, un clic sobre el indicador
 * abriria el calendario del navegador y ademas llamaria a `showPicker`. Se
 * apaga el blanco y no el dibujo -sigue siendo `opacity: 0` y sigue ocupando su
 * lugar- asi que la geometria del campo no se mueve.
 */
.entrada-sin-indicador::-webkit-calendar-picker-indicator {
  pointer-events: none;
}

/*
 * El calendario del set, encima del nativo.
 *
 * Por mascara y no por imagen de fondo para que tome `currentColor`: pintado
 * seria un color fijo contra veintiocho temas.
 *
 * Se corre para arriba por MARGEN NEGATIVO -el alto del control mas el hueco que
 * el envoltorio pone entre sus hijos- en vez de posicionarse absoluto. Asi no
 * depende de que el envoltorio sea un contexto de posicionamiento ni de cuantos
 * renglones mida su etiqueta.
 *
 * `align-self: flex-end` lo manda al borde de adentro: el envoltorio es una
 * columna de flex, asi que el eje cruzado es el horizontal.
 */
.fecha-icono {
  display: block;
  flex: none;
  align-self: flex-end;
  inline-size: 1.25rem;
  block-size: var(--alto-fecha);
  margin-block-start: calc(-1 * var(--alto-fecha) - var(--aire-fecha));
  margin-inline-end: var(--pad-fecha);
  background: currentColor;
  color: var(--color-text-subtle);
  -webkit-mask: url(/ds/iconos/n-calendar.svg) center / 1.25rem no-repeat;
  mask: url(/ds/iconos/n-calendar.svg) center / 1.25rem no-repeat;
  /* Que el toque la atraviese y llegue al indicador nativo que esta debajo. */
  pointer-events: none;
}

/*
 * El texto de cuando no hay fecha.
 *
 * Mismo margen negativo que el icono: el icono ocupa exactamente la banda del
 * control, asi que lo que sigue arranca donde arrancaba antes.
 */
.fecha-vacia { display: none; }

.fecha-vacia.is-visible {
  display: flex;
  align-items: center;
  block-size: var(--alto-fecha);
  margin-block-start: calc(-1 * var(--alto-fecha) - var(--aire-fecha));
  padding-inline: var(--pad-fecha);
  font-size: var(--text-sm);
  color: var(--color-text-subtle);
  pointer-events: none;
}

select.field__control {
  appearance: none;
  padding-right: var(--space-8);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' stroke='%2378716C' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4 6 4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--space-3) center;
}

.field__hint {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

.field__error {
  font-size: var(--text-xs);
  color: var(--color-danger);
}

.field--invalid .field__control {
  border-color: var(--color-danger);
}

/* Error takes precedence: hover and focus keep the danger color, they never
   override it with ink. Focus just intensifies the red with a crisp inset edge. */
.field--invalid .field__control:hover {
  border-color: var(--color-danger);
}

.field--invalid .field__control:focus-visible {
  border-color: var(--color-danger);
  box-shadow: inset 0 0 0 1px var(--color-danger);
}

/* Fixed prefix (e.g. compra.chat/ + username) */
.field__group {
  display: flex;
  align-items: stretch;
}

.field__group .field__prefix {
  display: flex;
  align-items: center;
  padding: 0 var(--space-3);
  background: var(--color-surface-muted);
  border: 1px solid var(--color-border-strong);
  border-right: 0;
  border-radius: var(--radius-control) 0 0 var(--radius-control);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  white-space: nowrap;
}

.field__group .field__control {
  border-radius: 0 var(--radius-control) var(--radius-control) 0;
}

/*
 * UN GRUPO DE CONTROLES, no un control.
 *
 * Las chips de tipo de evento pasaban por `campo()`, que le pone al contenido la
 * clase `field__control`: alto fijo de 2,5rem, borde y relleno de entrada de
 * texto. Las chips miden 44px y vienen en dos filas, asi que quedaban metidas
 * adentro de una caja de 40 y la segunda fila se desbordaba sobre el campo de
 * abajo.
 *
 * Esto es un `fieldset`, que el navegador trae con borde, margen y relleno
 * propios de los anos noventa: se apagan los tres. Lo unico que queda es la
 * columna de etiqueta mas contenido, igual que un campo.
 */
.field--grupo {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-inline-size: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

/*
 * Y la etiqueta es un `legend`, que por omision flota sobre el borde del
 * fieldset y no participa del flujo. Sin esto queda pegada a las chips por
 * arriba y el `gap` no la toca.
 */
.field--grupo > .field__label {
  display: block;
  float: none;
  padding: 0;
  margin-block-end: var(--space-2);
}

/* --- componentes/card.css --- */
/* Card — generic container surface for panels and lists. */

.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-xs);
}

.card__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--color-border);
}

.card__title {
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
}

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

.card__footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-top: 1px solid var(--color-border);
  background: var(--color-surface-muted);
  border-radius: 0 0 var(--radius-card) var(--radius-card);
}

.card--interactive {
  transition: border-color var(--duration-fast) var(--ease-out),
    box-shadow var(--duration-fast) var(--ease-out);
}

.card--interactive:hover {
  border-color: var(--color-border-strong);
  box-shadow: var(--shadow-sm);
}

/* --- componentes/badge.css --- */
/* Badge — statuses and short labels (plan, offer, draft…). */

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 2px var(--space-2);
  border-radius: var(--radius-full);
  border: 1px solid transparent;
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  line-height: var(--leading-snug);
  white-space: nowrap;
}

.badge--neutral {
  background: var(--color-surface-muted);
  border-color: var(--color-border);
  color: var(--color-text-muted);
}

/* Aca vivian las tres versiones PASTEL de `--accent`, `--warning` y
   `--danger`, y a las tres las redeclaraba entera la regla solida de mas abajo:
   estaban escritas y no se aplicaba ni una de sus propiedades.

   El motivo de que la solida sea la base esta en su propio comentario, abajo.
   Lo que quedaba aca era el intento anterior. */


/* Solido y no pastel: una superficie esta saturada del todo o es gris tecnico.
   El kit tenia esta version detras de `.cc-admin` y la pastel como base; aca
   la solida es la base, que es la que aguanta los 27 temas oscuros. */
.badge--accent {
  background: var(--color-accent);
  border-color: transparent;
  color: var(--color-on-accent);
}
.badge--warning {
  background: var(--color-highlight-deep);
  border-color: transparent;
  color: var(--color-on-highlight);
}
.badge--danger {
  background: var(--color-danger);
  border-color: transparent;
  color: var(--color-on-danger);
}

/* --- componentes/switch.css --- */
/* Switch — enable/disable optional modules and sections.
   <label class="switch">
     <input type="checkbox" role="switch">
     <span class="switch__track" aria-hidden="true"></span>
     <span class="switch__label">…</span>
   </label> */

.switch {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  cursor: pointer;
  user-select: none;
}

.switch input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

/* DEFAULT = the rounded, non-growing model (user pick 2026-08-22: the old
   squared/stretching knob was retired site-wide; .switch--m3 stays as an
   inert alias so existing markup needs no edits). On-state wears the brand
   action color. */
.switch__track {
  position: relative;
  width: 2.875rem;
  height: 1.625rem;
  flex-shrink: 0;
  background: var(--color-border-strong);
  border-radius: var(--radius-full);
  transition: background var(--duration-base) var(--ease-out);
}

.switch__track::after {
  content: "";
  position: absolute;
  top: 4px;
  left: 4px;
  width: 1.125rem;
  height: 1.125rem;
  background: var(--color-surface);
  border-radius: var(--radius-full);
  box-shadow: var(--shadow-xs);
  transition: translate var(--duration-base) var(--ease-out);
}

.switch input:checked + .switch__track {
  background: var(--color-action);
}

.switch input:checked + .switch__track::after {
  translate: 1.25rem 0;
}

.switch input:focus-visible + .switch__track {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

.switch__label {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
}

/* --- componentes/table.css --- */
/* Table — panel lists (inventory, catalogs).
   Wrap in .table-wrap for horizontal scroll on small screens. */

.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card);
  background: var(--color-surface);
}

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}

.table th {
  padding: var(--space-3) var(--space-4);
  text-align: left;
  font-weight: var(--weight-semibold);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  background: var(--color-surface-muted);
  border-bottom: 1px solid var(--color-border);
  white-space: nowrap;
}

.table td {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
  vertical-align: middle;
}

.table tbody tr:last-child td {
  border-bottom: 0;
}

.table tbody tr:hover {
  background: var(--neutral-50);
}

.table__num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.table th.table__num {
  text-align: right;
}

/* --- componentes/menu.css --- */
/* Dropdown menu — for the topnav avatar and contextual actions.
   Structure:
   <div class="menu">
     <button …trigger…>
     <div class="menu__list" role="menu">
       <div class="menu__header">…</div>
       <a class="menu__item">…</a>
       <button class="menu__item menu__item--danger">…</button>
     </div>
   </div>
   JS: toggles .is-open on .menu; closes on outside click and Escape. */

.menu {
  position: relative;
}

.menu__list {
  position: absolute;
  right: 0;
  top: calc(100% + var(--space-2));
  min-width: 13rem;
  padding: var(--space-2);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-md);
  z-index: var(--z-dropdown);
  transform-origin: top;
  display: none;
}

.menu.is-open .menu__list {
  display: block;
  animation: menu-in var(--duration-fast) var(--ease-out);
}

/*
 * HACIA ARRIBA, para un menu que vive pegado al borde de abajo.
 *
 * La barra de seleccion de la galeria flota sobre el borde inferior: un
 * desplegable que caiga hacia abajo cae fuera de la ventana y no hay scroll que
 * lo alcance, porque el contenedor es `fixed`.
 *
 * Es una variante y no un calculo: donde abre un menu lo sabe quien lo pone, y
 * medir el espacio disponible en tiempo real seria un observador y una
 * reposicion por cada scroll para resolver un caso que ya se conoce.
 */
.menu__list--arriba {
  top: auto;
  bottom: calc(100% + var(--space-2));
  transform-origin: bottom;
}

.menu.is-open .menu__list--arriba { animation-name: menu-in-arriba; }

@keyframes menu-in-arriba {
  from {
    opacity: 0;
    translate: 0 6px;
    scale: 0.98;
  }
}

@keyframes menu-in {
  from {
    opacity: 0;
    translate: 0 -6px;
    scale: 0.98;
  }
}

.menu__header {
  padding: var(--space-2) var(--space-3) var(--space-3);
  margin-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-border);
}

.menu__header b {
  display: block;
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
}

.menu__header span {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

/*
 * Una raya entre grupos de un menu.
 *
 * La usa el menu del avatar del panel desde que existe, y no estaba definida en
 * ninguna hoja: eran dos `<div>` vacios y el menu salia sin ninguna division
 * entre el espacio, los ajustes y cerrar sesion. Los margenes negativos la
 * estiran hasta el borde del panel, por encima de su relleno: una raya que no
 * llega a los bordes se lee como un error de alineacion.
 */
.menu__separador {
  block-size: 1px;
  margin: var(--space-2) calc(-1 * var(--space-2));
  background: var(--color-border);
}

.menu__item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 0;
  border-radius: var(--radius-control); /* container (16) minus its padding (8) */
  background: none;
  font-size: var(--text-sm);
  color: var(--color-text);
  text-decoration: none;
  text-align: left;
  transition: background var(--duration-fast) var(--ease-out);
}

.menu__item:hover {
  background: var(--color-surface-muted);
}

.menu__item[aria-current="page"] {
  background: var(--color-surface-muted);
  color: var(--color-text);
  font-weight: var(--weight-medium);
}

/*
 * El tilde de "esta elegida", y vive ACA.
 *
 * Estaba en `client/panel/eventos.css`, que el Studio no carga: por eso los
 * desplegables del Studio mostraban el tilde en TODAS las opciones. No es un
 * estilo de la agenda, es del menu, y los dos lo usan.
 *
 * Es la misma falla que las mascaras de los iconos: un estilo que vive en una
 * hoja que la superficie no pide. No falla, no avisa; simplemente la regla
 * nunca llega.
 *
 * Ocupa lugar SIEMPRE: si apareciera solo en la elegida, las filas se correrian
 * de ancho al cambiar de opcion.
 */
.menu__tilde {
  display: grid;
  place-items: center;
  color: var(--color-action);
  visibility: hidden;
  flex: none;
}

.menu__item[aria-checked="true"] .menu__tilde { visibility: visible; }
.menu__item[aria-checked="true"] { background: var(--color-surface-muted); }

.menu__item svg,
.menu__item .ic {
  width: 1rem;
  height: 1rem;
  color: var(--color-text-muted);
  flex-shrink: 0;
}

.menu__item--danger,
.menu__item--danger svg,
.menu__item--danger .ic {
  color: var(--color-danger);
}

.menu__item--danger:hover {
  background: var(--color-danger-soft);
}

/* --- componentes/modal.css --- */
/* Modal — built on native <dialog> (focus, Escape and backdrop for free).
   <dialog class="modal">
     <div class="modal__header">…<button class="modal__close">…</div>
     <div class="modal__body">…</div>
     <div class="modal__footer">…</div>
   </dialog>
   JS: ui/js/modal.js */

.modal {
  width: min(34rem, calc(100vw - var(--space-8)));
  max-height: calc(100dvh - var(--space-16));
  /* Native <dialog> centers a modal via margin:auto; base.css resets all
     margins to 0, so we restore it here or the dialog pins to the top-left. */
  margin: auto;
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-xl);
  background: var(--color-surface);
  box-shadow: var(--shadow-lg);
  color: inherit;
}

.modal--wide {
  width: min(52rem, calc(100vw - var(--space-8)));
}

.modal::backdrop {
  background: rgba(12, 10, 9, 0.45);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

/* Mobile: modals become bottom sheets (matches the store-picker pattern).

   `--teclado` es cuanto del fondo de la ventana tapa el teclado del telefono.
   La publica `modal.js` leyendo `visualViewport`, y vale 0 cuando no hay
   ninguno. En Android no hace falta -el teclado achica la ventana y el cajon
   sube solo- pero en iOS la ventana mide lo mismo y el cajon queda ABAJO del
   teclado, con el campo que se esta escribiendo fuera de la pantalla.

   Dos cosas con el mismo numero: el cajon se levanta hasta el borde del
   teclado, y se acorta para que su contenido siga entrando en lo que queda a
   la vista. Sin lo segundo, un dialogo alto se levanta entero y se le va la
   cabecera por arriba. */
@media (max-width: 40rem) {
  .modal {
    width: 100%;
    max-width: none;
    margin: auto 0 var(--teclado, 0px);
    max-height: min(92dvh, calc(100dvh - var(--teclado, 0px) - var(--space-8)));
    border-radius: var(--radius-xl) var(--radius-xl) 0 0;
    border-bottom: 0;
  }
  .modal[open] { animation: sheet-in var(--duration-base) var(--ease-out); }
}

@keyframes sheet-in {
  from { translate: 0 100%; }
}

.modal[open] {
  animation: modal-in var(--duration-base) var(--ease-out);
}

@keyframes modal-in {
  from {
    opacity: 0;
    translate: 0 var(--space-2);
  }
}

/*
 * LA SALIDA, que no existia.
 *
 * `modal.js` agrega `is-cerrando`, espera a que terminen las animaciones y
 * recien ahi cierra el dialogo —"esperar a que termine la animacion de salida
 * antes de sacar el dialogo de la vista", dice su comentario—. Ninguna hoja
 * definia esa clase, asi que no habia ninguna animacion que esperar: el
 * dialogo desaparecia de golpe y toda esa maquinaria era decorativa.
 *
 * Sale como entro, al reves: el cajon se va para abajo y la caja se desvanece.
 */
.modal.is-cerrando { animation: modal-out var(--duration-base) var(--ease-out) forwards; }

@keyframes modal-out {
  to {
    opacity: 0;
    translate: 0 var(--space-2);
  }
}

@media (max-width: 40rem) {
  .modal.is-cerrando { animation: sheet-out var(--duration-base) var(--ease-out) forwards; }
}

@keyframes sheet-out {
  to { translate: 0 100%; }
}

.modal__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--color-border);
}

.modal__title {
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
}

.modal__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-text-muted);
}

.modal__close:hover {
  background: var(--color-surface-muted);
  color: var(--color-text);
}

.modal__body {
  padding: var(--space-5);
  overflow-y: auto;
}

.modal__footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-top: 1px solid var(--color-border);
}

/* --- componentes/toast.css --- */
/* Toast — aviso que no bloquea. Comportamiento: componentes/toast.js
   (el kit lo referenciaba y no lo traia; esta escrito aca).

   Portado de ds-portable-semaforo, con una decision resuelta: el kit traia dos
   aspectos, uno oscuro con sombra para vidrieras y otro solido sin sombra bajo
   `.cc-admin`. Queda el solido, que es el que respeta la doctrina de que la
   profundidad la dan la escalera de grises y los bordes, no un drop shadow. */

.toast-region {
  position: fixed;
  bottom: var(--space-6);
  right: var(--space-6);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  max-width: min(22rem, calc(100vw - var(--space-8)));
}

/* En un telefono va abajo y a lo ancho: la esquina inferior derecha de una
   pantalla de 6 pulgadas es justo donde esta el pulgar. */
@media (max-width: 30rem) {
  .toast-region {
    left: var(--space-3);
    right: var(--space-3);
    bottom: var(--space-3);
    max-width: none;
  }
}

.toast {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--color-accent);
  color: var(--color-on-accent);
  /* Borde de tinta: toda superficie pintada lleva su filo. */
  border: 1.5px solid var(--neutral-900);
  border-radius: var(--radius-lg);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  animation: toast-in var(--duration-base) var(--ease-out);
}

@keyframes toast-in {
  from {
    opacity: 0;
    translate: 0 var(--space-2);
  }
}

.toast--leaving {
  opacity: 0;
  transition: opacity var(--duration-base) var(--ease-out);
}

/* El icono ES el svg. Va en una pastilla apenas marcada: la forma del icono
   carga el estado, no un color fuerte. */
.toast__icon {
  flex-shrink: 0;
  box-sizing: border-box;
  width: 1.25rem;
  height: 1.25rem;
  padding: 0.22rem;
  border-radius: var(--radius-full);
  background: rgba(0, 0, 0, 0.14);
  color: inherit;
}

.toast--error {
  background: var(--color-danger);
  color: var(--color-on-danger);
}

/* --- componentes/accordion.css --- */
/* ============================================================
   Accordion — two versions kept on purpose:
     A) Native <details>/<summary> — semantic, works with no JS, instant.
     B) Animated (.accordion--anim) — nav.js toggles .is-open for a smooth
        height transition. Same look; pick per context.
   ============================================================ */

.accordion {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-control);
  overflow: hidden;
  background: var(--color-surface);
}
.accordion + .accordion { margin-top: var(--space-2); }

.accordion__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-3) var(--space-4);
  background: none;
  border: 0;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  text-align: left;
  color: var(--color-text);
  cursor: pointer;
  list-style: none; /* hides the default <summary> triangle */
}
.accordion__head::-webkit-details-marker { display: none; }
.accordion__head::marker { content: ""; }
.accordion__head:hover { background: var(--color-surface-muted); }
.accordion__head:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }

.accordion__chevron {
  width: 1rem;
  height: 1rem;
  flex: none;
  color: var(--color-text-muted);
  transition: rotate var(--duration-base) var(--ease-out);
}

.accordion__body p,
.accordion__inner p {
  padding: 0 var(--space-4) var(--space-3);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  line-height: var(--leading-normal);
}

/* ---------- A) Native <details> ---------- */
details.accordion[open] .accordion__chevron { rotate: 180deg; }

/* ---------- B) Animated ---------- */
.accordion--anim .accordion__body {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--duration-base) var(--ease-out);
}
.accordion--anim.is-open .accordion__body { grid-template-rows: 1fr; }
.accordion--anim .accordion__inner { overflow: hidden; }
.accordion--anim.is-open .accordion__chevron { rotate: 180deg; }

/* --- componentes/icon.css --- */
/* Iconos — SVG monocromo dibujado con una mascara CSS, para que el glifo tome
   `currentColor` y se adapte solo: tinta sobre superficie clara, blanco sobre
   un boton oscuro, rojo dentro de una accion destructiva.

   Portado de ds-portable-semaforo. El set original traia ~75 iconos de
   comercio (carrito, mercadopago, tienda, pedidos); aca quedan los treinta que
   este producto usa, con nombres nuestros. Agregar uno es copiar el SVG a
   `iconos/` y sumar una linea.

   LAS RUTAS SON ABSOLUTAS, y no es un detalle. Esta hoja se sirve DOS veces:
   suelta en /ds/componentes/icon.css, y concatenada dentro de /ds/bundle.css,
   que es como la carga el panel. Una `url(../iconos/x.svg)` resuelve contra la
   hoja que la contiene, asi que dentro del bundle apuntaba a /iconos/x.svg y
   daba 404. Una mascara que no resuelve NO falla: enmascara el elemento contra
   nada, es decir, lo vuelve invisible. Los treinta y cinco iconos de este set
   estuvieron invisibles en el panel sin que nada lo dijera.

   Uso: <i class="ic ic--foto" aria-hidden="true"></i>        (16px)
        <i class="ic ic--24 ic--foto" aria-hidden="true"></i> (24px)

   Siempre `aria-hidden`: es decoracion. Si el icono es la unica etiqueta de un
   control, el control lleva `aria-label`. */

.ic {
  display: inline-block;
  width: 1rem;
  height: 1rem;
  flex: none;
  vertical-align: -0.15em;
  background-color: currentColor;
  -webkit-mask: var(--ic) center / contain no-repeat;
  mask: var(--ic) center / contain no-repeat;
}

.ic--24 { width: 1.5rem; height: 1.5rem; }

/* --- acciones --- */
.ic--check     { --ic: url(/ds/iconos/n-check.svg); }
.ic--cerrar    { --ic: url(/ds/iconos/n-close.svg); }
.ic--copiar    { --ic: url(/ds/iconos/n-copy.svg); }
.ic--borrar    { --ic: url(/ds/iconos/n-delete.svg); }
.ic--editar    { --ic: url(/ds/iconos/n-edit.svg); }
.ic--nuevo     { --ic: url(/ds/iconos/n-new.svg); }
/* Crear un evento. Es el unico icono de este set que no es generico: dice que
   lo que se crea es un EVENTO, que es la accion principal de la agenda. */
.ic--nuevo-evento { --ic: url(/ds/iconos/n-add-event.svg); }
.ic--mas       { --ic: url(/ds/iconos/n-plus.svg); }
.ic--buscar    { --ic: url(/ds/iconos/n-search.svg); }
.ic--descargar { --ic: url(/ds/iconos/n-export.svg); }
.ic--escribir  { --ic: url(/ds/iconos/n-write.svg); }
.ic--cambiar   { --ic: url(/ds/iconos/n-switch.svg); }

/* --- objetos del producto --- */
.ic--foto      { --ic: url(/ds/iconos/n-add-photo.svg); }
.ic--video     { --ic: url(/ds/iconos/n-video.svg); }
.ic--qr        { --ic: url(/ds/iconos/qr.svg); }
.ic--link      { --ic: url(/ds/iconos/link.svg); }
.ic--evento    { --ic: url(/ds/iconos/celebrate.svg); }
.ic--tema      { --ic: url(/ds/iconos/n-tema.svg); }
.ic--grilla    { --ic: url(/ds/iconos/n-grid.svg); }
.ic--lista     { --ic: url(/ds/iconos/n-list.svg); }
.ic--vistazo   { --ic: url(/ds/iconos/n-vistazo.svg); }

/* --- las tres acciones del invitado ---
   Van al doble de tamano dentro de `.ds-accion__icono`, asi que el trazo tiene
   que aguantar 24px sin verse liviano: son glifos macizos, no de contorno. */
.ic--camara    { --ic: url(/ds/iconos/n-camara.svg); }
.ic--galeria   { --ic: url(/ds/iconos/n-galeria.svg); }
.ic--mensaje   { --ic: url(/ds/iconos/n-mensaje.svg); }

/* --- personas y cuenta --- */
.ic--persona   { --ic: url(/ds/iconos/user.svg); }
.ic--cuenta    { --ic: url(/ds/iconos/n-cuenta.svg); }
.ic--equipo    { --ic: url(/ds/iconos/n-team.svg); }
.ic--invitar   { --ic: url(/ds/iconos/n-add-team.svg); }
.ic--salir     { --ic: url(/ds/iconos/n-signout.svg); }
.ic--mail      { --ic: url(/ds/iconos/mail.svg); }

/* --- estado --- */
.ic--ojo       { --ic: url(/ds/iconos/n-eye.svg); }
.ic--ajustes   { --ic: url(/ds/iconos/n-settings.svg); }
.ic--ayuda     { --ic: url(/ds/iconos/n-question.svg); }
.ic--ok        { --ic: url(/ds/iconos/check-circle.svg); }
.ic--sonrisa   { --ic: url(/ds/iconos/n-smile.svg); }

/* --- componentes/esqueleto.css --- */
/* ============================================================
   esqueleto.css — el relleno que espera al contenido

   Reemplaza al preloader. Una marca que respira dice "espera";
   un esqueleto dice ademas QUE viene y DONDE, asi que la
   pagina no salta al llegar.

   Aca va solo la pieza: las formas las arma cada pantalla,
   porque un esqueleto que no se parece a lo que viene es un
   adorno. El relato esta en docs/ESTADO.md.
   ============================================================ */

/* Invisible los primeros 180ms -retardo de la animacion sobre un reposo en
   cero-: una respuesta de 60ms no dibuja nada, y un esqueleto que aparece y se
   va en dos cuadros es un destello. Sin temporizador en JS. */
.esq {
  background: var(--color-surface-muted);
  border-radius: var(--radius-control);
  opacity: 0;
  animation: esq-latir 1.6s ease-in-out 180ms infinite;
}

/* El latido va en la OPACIDAD: mover un `background-position` repinta la pieza
   entera en cada cuadro, y puede haber veinte esperando al servidor. */
@keyframes esq-latir {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}

/* `opacity: 1` a mano: el reposo es invisible, asi que apagar la animacion sin
   esto dejaria la pantalla vacia. */
@media (prefers-reduced-motion: reduce) {
  .esq { opacity: 1; animation: none; }
}

.esq--linea { block-size: 0.75rem; border-radius: var(--radius-micro); }
/* El ultimo renglon de un parrafo sale corto, como uno de verdad. */
.esq--linea:last-child { inline-size: 62%; }
.esq--titulo { block-size: 1.5rem; inline-size: min(22ch, 70%); border-radius: var(--radius-micro); }
.esq--foto { aspect-ratio: 1; border-radius: var(--radius-thumb); }
.esq--control { block-size: 2.5rem; }
.esq--caja { border-radius: var(--radius-card); }

/* `aria-busy` va en esta raiz: un lector anuncia "ocupado" una vez en lugar de
   leer veinte cajas; las piezas van `aria-hidden`. */
.esqueleto { display: grid; gap: var(--e24); }

/* --- componentes/ds-select.css --- */
/* ============================================================
   select.css — SEL / HOV
   A custom dropdown that enhances a native <select> (progressive
   enhancement — the real <select> stays in the DOM as the source of
   truth and form value). Styled trigger with a rotating chevron, a
   panel of options, and a check on the selected one. Used e.g. for the
   catalog-editor "Hover effect" field.

   Portable: depends only on the design tokens in tokens.css. Behaviour
   comes from select.js, which wraps every <select data-ds-select>.
   ============================================================ */

.ds-select { position: relative; }
.ds-select__native { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }

.ds-select__btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  width: 100%;
  height: 2.5rem;
  padding: 0 var(--space-3);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-control);
  background: var(--color-surface);
  font: inherit;
  font-size: var(--text-sm);
  color: var(--color-text);
  cursor: pointer;
}
.ds-select__btn:hover { border-color: var(--color-border-accent); }
.ds-select__btn svg { color: var(--color-text-muted); transition: rotate var(--duration-fast) var(--ease-out); }
.ds-select.is-open .ds-select__btn { border-color: var(--color-action); box-shadow: inset 0 0 0 1px var(--color-action); }
.ds-select.is-open .ds-select__btn svg { rotate: 180deg; }

.ds-select__list {
  position: absolute;
  top: calc(100% + var(--space-1));
  left: 0;
  right: 0;
  z-index: var(--z-dropdown);
  padding: var(--space-1);
  border: 1px solid var(--color-border-strong); /* shadows off: the border separates */
  border-radius: var(--radius-control);
  background: var(--color-surface);
  display: none;
  max-height: 15rem;
  overflow-y: auto;
}
.ds-select.is-open .ds-select__list { display: block; }

.ds-select__opt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  cursor: pointer;
}
.ds-select__opt:hover { background: var(--color-surface-muted); }
/* La regla de `body.storefront` -el hover con el lavado del acento- se fue: en
   este producto no hay ninguna pantalla con esa clase, asi que era una regla
   que ningun codigo podia activar. */
.ds-select__opt[aria-selected="true"] { font-weight: var(--weight-medium); }
/* Selected check — same glyph as the DDL-01 dropdowns (masked so it themes)

   Con la ficha de ACCION. Decia "masked so it themes" y no se temaba: el
   acento es el verde de "salio bien" en los veintisiete temas, asi que el
   tilde de la opcion elegida salia verde en todos -en los desplegables de la
   modal de datos y en los del Studio-. */
.ds-select__opt[aria-selected="true"]::after {
  content: "";
  width: 0.9rem;
  height: 0.9rem;
  flex: none;
  background-color: var(--color-action);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 12 4.5 4.5L19 7'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 12 4.5 4.5L19 7'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* --- componentes/ds-segmented.css --- */
/* ============================================================
   segmented.css — COL (and any mutually-exclusive preset row)
   Segmented control backed by real radio inputs (accessible). A sliding
   thumb glides to the checked option; without JS the per-option
   background is the fallback. Used for grid columns (2/3/4), etc.

   Portable: depends only on the design tokens in tokens.css. The sliding
   thumb comes from segmented.js (optional — the control works without it).
   Markup:
     <fieldset class="ds-seg" role="radiogroup">
       <label class="ds-seg__option">
         <input type="radio" name="cols" class="ds-visually-hidden" checked><span>2</span>
       </label> …
     </fieldset>
   ============================================================ */

.ds-seg {
  position: relative;
  display: inline-flex;
  margin: 0;
  padding: var(--space-1);
  gap: var(--space-1);
  background: var(--color-surface-muted);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-control);
}

.ds-seg__thumb {
  position: absolute;
  z-index: 0;
  background: var(--color-surface);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-xs);
  transition: left var(--duration-base) var(--ease-out), top var(--duration-base) var(--ease-out),
    width var(--duration-base) var(--ease-out), height var(--duration-base) var(--ease-out);
}
.ds-seg.has-thumb .ds-seg__option:has(input:checked) { background: none; box-shadow: none; }

.ds-seg__option {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-width: 2.25rem;
  height: 2rem;
  padding: 0 var(--space-3);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--color-text-muted);
  cursor: pointer;
  user-select: none;
  transition: color var(--duration-fast) var(--ease-out), background var(--duration-fast) var(--ease-out);
}
.ds-seg__option:hover { color: var(--color-text); }
.ds-seg__option:has(input:checked) { background: var(--color-surface); color: var(--color-text); box-shadow: var(--shadow-xs); }
.ds-seg__option:has(input:focus-visible) { outline: 2px solid var(--color-focus); outline-offset: 1px; }

/* visually-hidden helper (in case the host page has none) */
.ds-visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* --- componentes/ds-radius-slider.css --- */
/* ============================================================
   radius-slider.css — RAD
   Corner-radius control: a live preview square + a custom range (thin
   filled track, crisp thumb, no native chrome) with stepped ticks + a
   px readout. Snaps through the design-system radius scale.

   Portable: depends only on the design tokens in tokens.css. The live
   sync (fill %, preview radius, readout) comes from radius-slider.js.
   Markup: see design-system.html RAD-01.
   ============================================================ */

.ds-rslider { display: flex; align-items: center; gap: var(--space-4); }

.ds-rslider__square {
  width: 2.75rem;
  height: 2.75rem;
  flex-shrink: 0;
  background: var(--color-surface-muted);
  border: 1.5px solid var(--color-border-strong);
  transition: border-radius var(--duration-fast) var(--ease-out);
}

.ds-rslider__val { min-width: 2.75rem; text-align: right; font-size: var(--text-sm); font-variant-numeric: tabular-nums; color: var(--color-text-muted); }

/* custom range: JS sets --fill: <percent> */
.ds-rslider__range {
  -webkit-appearance: none;
  appearance: none;
  flex: 1;
  height: 4px;
  border-radius: var(--radius-full);
  /* Fichas semanticas y no primitivas: `--neutral-900` es tinta sobre el tema
     claro y queda invisible sobre los oscuros, que son veintisiete de los
     veintiocho.

     La parte llena usa la ficha de ACCION. Decia "el acento del evento, que es
     lo que el control esta ajustando", y era un comentario que no describia lo
     que pasaba: el acento de marca lo pone `aplicarMarca`, que solo corre en la
     pantalla del invitado. En el Studio -el unico lugar donde vive este
     control- `--color-accent` es el verde del kit, asi que la barra salia verde
     con cualquier tema. */
  background:
    linear-gradient(var(--color-action) 0 0) 0 / var(--fill, 50%) 100% no-repeat,
    var(--color-surface-muted);
  cursor: pointer;
}
.ds-rslider__range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  border-radius: var(--radius-full);
  background: var(--color-surface);
  border: 2px solid var(--color-action);
}
.ds-rslider__range::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border-radius: var(--radius-full);
  background: var(--color-surface);
  border: 2px solid var(--color-action);
}
.ds-rslider__range:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 4px; }

.ds-rslider__stepwrap { flex: 1; max-width: 13rem; }
.ds-rslider__stepwrap .ds-rslider__range { max-width: 100%; }
.ds-rslider__ticks { display: flex; justify-content: space-between; margin-top: 6px; padding: 0 9px; }
.ds-rslider__ticks span { width: 1px; height: 5px; background: var(--color-border); }

/* Sin vista previa: para medidas que un cuadradito no sabe mostrar, como el
   ancho de un marco. El numero de la derecha alcanza. */
.ds-rslider--sin-previa .ds-rslider__square { display: none; }

/* En una fila de ajustes el deslizador ocupa lo que sobra y el valor queda
   alineado a la derecha con los de arriba y abajo. */
.ds-rslider--fila { width: 100%; }
.ds-rslider--fila .ds-rslider__stepwrap { max-width: none; }

/* --- componentes/ds-theme-picker.css --- */
/* ============================================================
   theme-picker.css — THM
   A palette picker: a wrapping row of two-tone 24px tiles (base + accent)
   as radio options with a selected ring. Custom (saved) themes get
   edit/delete affordances on hover; a dashed "create your own" tile ends
   the row.

   Portable: depends only on the design tokens in tokens.css. Selection is
   pure CSS (radio :checked). theme-picker.js only wires the delete action.
   Markup: see design-system.html THM-01.
   ============================================================ */

/* Fixed grid (2026-08-26): flex-wrap sized each tile to its NAME, so the
   gaps looked ragged. Every cell now gets the same track; long names clip. */
.ds-theme { display: grid; grid-template-columns: repeat(auto-fill, minmax(4.75rem, 1fr)); gap: var(--space-3); }
/* Aca habia un .ds-theme__mark que la regla de mas abajo redeclaraba
   entera: estaba escrito y no se aplicaba ni una de sus
   propiedades. */

.ds-theme__name { display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: center; }
.ds-theme__wrap { position: relative; }

.ds-theme__opt { position: relative; display: block; cursor: pointer; }
.ds-theme__opt input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.ds-theme__mark { display: flex; flex-direction: column; align-items: center; gap: var(--space-2); }
.ds-theme__tile {
  width: 24px;
  height: 24px;
  border-radius: var(--radius-sm);
  border: 1px solid rgba(0, 0, 0, 0.12);
  overflow: hidden;
  display: flex;
  transition: box-shadow var(--duration-fast) var(--ease-out);
}
.ds-theme__tile span { flex: 1; }
.ds-theme__opt input:checked + .ds-theme__mark .ds-theme__tile,
.ds-theme__opt input:focus-visible + .ds-theme__mark .ds-theme__tile {
  box-shadow: 0 0 0 2px var(--color-surface), 0 0 0 4px var(--neutral-900);
}
.ds-theme__name { font-size: 10px; color: var(--color-text-muted); }
.ds-theme__opt input:checked + .ds-theme__mark .ds-theme__name { color: var(--color-text); font-weight: var(--weight-medium); }

/* custom (saved) themes: edit / delete on hover */
.ds-theme__acts { position: absolute; top: -7px; right: -7px; display: flex; gap: 3px; opacity: 0; transition: opacity var(--duration-fast) var(--ease-out); }
.ds-theme__wrap--custom:hover .ds-theme__acts,
.ds-theme__wrap--custom:focus-within .ds-theme__acts { opacity: 1; }
.ds-theme__act {
  display: grid;
  place-items: center;
  width: 17px;
  height: 17px;
  padding: 0;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-full);
  background: var(--color-surface);
  color: var(--color-text-muted);
  cursor: pointer;
}
.ds-theme__act svg { width: 9px; height: 9px; }
.ds-theme__act:hover { color: var(--color-text); border-color: var(--neutral-400); }
.ds-theme__act--del:hover { color: var(--color-danger); border-color: var(--color-danger); }

/* "create your own" tile */
.ds-theme__new { display: flex; flex-direction: column; align-items: center; gap: var(--space-2); text-decoration: none; }
.ds-theme__new-tile {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-sm);
  border: 1.5px dashed var(--color-border-strong);
  color: var(--color-text-subtle);
  transition: border-color var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out);
}
/* Con la ficha del tema: el acento es verde en los veintisiete. */
.ds-theme__new:hover .ds-theme__new-tile { border-color: var(--color-action); color: var(--color-text); }
.ds-theme__new .ds-theme__name { color: var(--color-text-muted); }
.ds-theme__new:hover .ds-theme__name { color: var(--color-text); }

/* --- tira horizontal ---
   Veintiocho temas en la grilla del kit son cinco filas y media pantalla de
   panel. En una tira que se scrollea entran todos en el alto de uno, y elegir
   un tema es mirar colores, no leer una lista. */

.ds-temas {
  display: flex;
  gap: var(--space-2);
  overflow-x: auto;
  /* SIN ESTO LA TIRA ENSANCHA A SU PADRE.

     `overflow-x: auto` promete que el desborde scrollea, y no alcanza: un hijo
     flex o grid tiene un minimo AUTOMATICO igual a su contenido, asi que no
     puede encogerse por debajo de las veintiocho pastillas -unos 1100 px- y lo
     que cede es el padre. Puesta en la columna de ajustes, ensancho la pantalla
     entera. Reportado: "se desbordo totalmente y ensancho la pantalla".

     Es la misma familia que el `minmax(0, 1fr)` del visor de la pared de
     fotos: una pista o un hijo que tiene que poder encogerse necesita decirlo. */
  min-width: 0;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximate;
  padding: var(--space-1) 0 var(--space-2);
  /* Con mouse se arrastra: la rueda no hace scroll horizontal sin Shift, y
     nadie descubre eso. Lo agrega ds-temas.js; el cursor lo anticipa. */
  cursor: grab;
  /* La barra la pinta `base.css`. Aca tiene que estar -no se esconde- porque es
     la unica senal de que hay mas temas a la derecha. */
}

.ds-temas.is-arrastrando { cursor: grabbing; scroll-snap-type: none; }
/* Mientras se arrastra, el click no tiene que elegir un tema por accidente. */
.ds-temas.is-arrastrando .ds-theme__opt { pointer-events: none; }

.ds-temas .ds-theme__wrap {
  flex: 0 0 auto;
  scroll-snap-align: center;
  width: 2.5rem;
}

/* Chicas: son una muestra de color, no una tarjeta. A 2rem entran once en el
   ancho del panel, que es lo que hace que la tira se lea como una paleta. */
.ds-temas .ds-theme__tile {
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-sm);
}

.ds-temas .ds-theme__name { font-size: 9px; }

/* --- componentes/ds-section-nav.css --- */
/* ============================================================
   section-nav.css — SEC
   The editor's right-column "section summary": a titled column of pill
   links, each with an optional count/status meta. In the real editors it
   scroll-spies the form sections and jumps on click; the active pill fills
   with ink.

   Portable: depends only on the design tokens in tokens.css. Behaviour
   (scroll-spy / jump, or click-to-activate) comes from section-nav.js.
   Markup: see design-system.html SEC-01.
   ============================================================ */

.ds-snav { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-1); }
.ds-snav__title { padding: 0 var(--space-2) var(--space-1); font-size: var(--text-xs); font-weight: var(--weight-medium); color: var(--color-text-muted); }
.ds-snav__list { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; }
.ds-snav__pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 2rem;
  padding: 0 var(--space-3);
  border: 0;
  border-radius: var(--radius-full);
  background: none;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--color-text-muted);
  text-decoration: none;
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out);
}
.ds-snav__pill:hover { background: var(--color-surface-muted); color: var(--color-text); }
.ds-snav__pill.is-active { background: var(--neutral-900); color: var(--neutral-0); }
.ds-snav__name { white-space: nowrap; }
.ds-snav__meta { font-size: var(--text-xs); color: var(--color-text-subtle); font-variant-numeric: tabular-nums; }
.ds-snav__meta:empty { display: none; }
.ds-snav__pill.is-active .ds-snav__meta { color: rgba(255, 255, 255, 0.7); }

/* --- componentes/ds-chips.css --- */
/* ============================================================
   ds-chips.css — seleccion MULTIPLE

   Componente nuevo, escrito con las reglas del kit. No estaba: el kit resuelve
   la eleccion excluyente (`ds-seg`) y la lista larga (`ds-select`), pero no
   "marca todas las que quieras", que es la pregunta central de este producto.
   La pregunta util no es "que layout quiero" sino "entre cuales quiero que
   varie": ver dos alternando es lo unico que dice si la variedad funciona con
   el material real del evento.

   Debajo hay casillas de verdad. Sin JS el control ya funciona y ya se ve
   marcado, porque la seleccion la pinta `:has(input:checked)`.

   Portable: depende solo de las fichas de tokens.css.

   Markup:
     <div class="ds-chips" role="group" aria-label="Layouts">
       <label class="ds-chips__chip">
         <input type="checkbox" class="ds-visually-hidden" checked>
         <span class="ds-chips__label">Collage</span>
         <small class="ds-chips__nota">2-4</small>
       </label>
     </div>
   ============================================================ */

.ds-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.ds-chips__chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 2rem;
  padding: 0 var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text-muted);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  cursor: pointer;
  user-select: none;
  transition: border-color var(--duration-fast) var(--ease-out),
              color var(--duration-fast) var(--ease-out),
              background var(--duration-fast) var(--ease-out);
}

.ds-chips__chip:hover { border-color: var(--color-border-hover); color: var(--color-text); }
.ds-chips__chip:active { transform: scale(0.97); }
.ds-chips__chip:has(input:focus-visible) { outline: 2px solid var(--color-focus); outline-offset: 2px; }

/* Marcado: superficie pintada con su filo de tinta, como el resto de las
   superficies pintadas del sistema.

   Con la ficha de ACCION y no la de acento. La cabecera de `temas.css` ya lo
   dice -"buttons, bands, chips and toasts wear the THEME, not the Semaforo
   accents"- y el chip era el que no lo hacia: el acento es verde en los
   veintisiete temas, asi que el chip elegido salia verde en todos. */
.ds-chips__chip:has(input:checked) {
  background: var(--color-action-soft);
  border-color: var(--color-border-accent);
  color: var(--color-text);
}

/* Sin punto ni casilla dibujada: la superficie pintada del chip marcado ya
   dice cual esta puesto, y un circulo por chip come ancho en una fila de
   siete. Que se pueda marcar mas de uno se aprende marcando el segundo. */

.ds-chips__nota {
  color: var(--color-text-subtle);
  font-size: var(--text-xs);
  font-weight: var(--weight-regular);
  font-variant-numeric: tabular-nums;
}

/* Compacto: para filas de muchas opciones cortas, como las relaciones de
   aspecto. */
.ds-chips--compacto .ds-chips__chip {
  height: 1.75rem;
  padding: 0 var(--space-2);
  gap: var(--space-1);
  font-size: var(--text-xs);
}

/* --- componentes/ds-layout.css --- */
/* ============================================================
   ds-layout.css — piezas de composicion que el kit no traia

   Tres cosas que aparecen en cada pantalla de este producto y que no valia la
   pena repetir en cada hoja: la fila de etiqueta y control, el contenido de un
   grupo plegable, y la barra de acciones.

   Portable: depende solo de las fichas de tokens.css.
   ============================================================ */

/* Etiqueta a la izquierda, control a la derecha. Debajo de 26rem se apila:
   en el panel de un telefono, un select de 12rem al lado de una etiqueta de
   10rem no deja leer ninguno de los dos. */
.ds-fila {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: 2.75rem;
}

.ds-fila__label {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  /* Se encoge antes que el control: una etiqueta cortada se sigue entendiendo,
     un desplegable cortado no se puede usar. */
  flex: 0 1 auto;
  min-width: 0;
}

/*
 * El control de una fila NO ocupa el 100%.
 *
 * `.field__control` del kit es `width: 100%`, pensado para un campo apilado
 * bajo su etiqueta. Metido en una fila que ademas tiene la etiqueta al lado,
 * el 100% se suma al ancho de la etiqueta y el total se pasa del panel: el
 * desplegable quedaba debajo de la barra de scroll, los textos de ayuda se
 * metian por atras, y el panel entero ganaba una barra horizontal que no
 * deberia existir.
 */
.ds-fila > :not(.ds-fila__label) {
  flex: 0 1 auto;
  min-width: 0;
  max-width: 62%;
  width: auto;
}

/* Los que si quieren todo el ancho que sobra lo piden. */
.ds-fila > .ds-rslider { flex: 1 1 auto; max-width: none; }

/*
 * Un desplegable en una fila de ajuste mide SIEMPRE lo mismo.
 *
 * La regla de arriba da ancho por contenido, asi que seis desplegables en la
 * misma columna median seis cosas distintas: el de alineacion -cuya opcion mas
 * larga es "Centrado"- quedaba angosto, y el de tipografia -"Sharp Grotesk
 * Semibold"- se estiraba. Leidos uno debajo del otro, el borde derecho era una
 * escalera.
 *
 * Las dos reglas cubren los dos casos que `selector()` puede devolver: el
 * envoltorio del componente del kit, y el <select> crudo si el JS del kit no
 * cargo. Sin la segunda, un desplegable sin montar se veria distinto de los
 * otros cinco, que es literalmente "distintos dropdowns".
 */
.ds-fila > .ds-select,
.ds-fila > select.field__control {
  flex: 0 0 auto;
  width: 15rem;
  max-width: 62%;
}

/* Varias acciones a la derecha de una fila: una etiqueta larga (el nombre de
   un miembro, un link) y dos o tres botones chicos. Se envuelve porque en un
   telefono tres botones al lado de un nombre no entran, y cortarlos deja la
   accion inalcanzable. */
.ds-fila__acciones {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  justify-content: flex-end;
}

@media (max-width: 26rem) {
  .ds-fila { flex-direction: column; align-items: stretch; gap: var(--space-2); }
  .ds-fila__label { font-size: var(--text-sm); }

  /* Apilada, el ancho fijo pelearia con el `stretch` y dejaria el desplegable
     corto contra una etiqueta de ancho completo. Acá el ancho lo da la fila. */
  .ds-fila > .ds-select,
  .ds-fila > select.field__control { width: 100%; max-width: none; }
}

/* --- jerarquia de un panel de ajustes ---
   Dos niveles y no mas. Con tres, el tercero deja de leerse como nivel y
   empieza a leerse como ruido.

     BLOQUE   separa lo que se guarda de lo que no. Es la unica distincion que
              el operador necesita tener siempre presente.
     SECCION  agrupa controles que se tocan juntos.
*/

.ds-bloque {
  display: grid;
  /* El aire ENTRE secciones es el que hace legible la jerarquia. Apretadas, un
     panel de doce controles se lee como una lista de doce cosas iguales. */
  gap: var(--space-8);
  /* Y el aire ANTES del bloque tiene que ser mayor que el de adentro, o el
     corte no se lee como corte. */
  margin-top: var(--space-16);
}
.ds-bloque:first-child { margin-top: 0; }

.ds-bloque__cabeza {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--color-border-strong);
  /* Pegada a su primera seccion: la cabeza pertenece a lo que viene abajo, no
     flota entre dos bloques. */
  margin-bottom: calc(var(--space-4) * -1);
}

.ds-bloque__titulo {
  margin: 0;
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-snug);
}

.ds-bloque__nota {
  font-size: var(--text-xs);
  color: var(--color-text-subtle);
  text-align: right;
  flex: 1;
}

.ds-seccion { display: grid; gap: var(--space-3); }

.ds-seccion__titulo {
  margin: 0;
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--color-text);
  letter-spacing: var(--tracking-snug);
}

/* La nota se pega a su titulo, no al control de abajo: explica el titulo. */
.ds-seccion__nota {
  margin: calc(var(--space-2) * -1) 0 0;
  font-size: var(--text-xs);
  color: var(--color-text-subtle);
  line-height: var(--leading-snug);
  max-width: 34ch;
}

/* Un titulo seguido de su nota y despues los controles: el salto al primer
   control es mayor que el del titulo a la nota. */
.ds-seccion__nota + * { margin-top: var(--space-1); }

/* Las filas de una seccion respiran entre si sin necesitar separadores: en un
   panel angosto, una linea cada tres controles se lee como una reja. */
.ds-seccion .ds-fila + .ds-fila { margin-top: calc(var(--space-1) * -1); }

.ds-barra { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.ds-barra .btn { flex: 1 1 auto; }

/* Un boton que esta "puesto": el modo activo de una herramienta, no una
   accion que se dispara. Con la ficha del tema: "puesto" no es "salio bien", y
   el acento es verde en los veintisiete temas. */
.btn.is-on {
  background: var(--color-action-soft);
  border-color: var(--color-border-accent);
  color: var(--color-text);
}

/* Etiqueta de un grupo de opciones dentro de una seccion. No es un `<label>`
   de un control: rotula una fila de chips, que no tiene un solo control al que
   apuntar. */
.ds-etiqueta {
  margin: var(--space-2) 0 0;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.ds-etiqueta:first-child { margin-top: 0; }

/* Muestras de una paleta derivada: permite juzgar una armonia sin aplicarla y
   esperar al proximo slide. */
.ds-paleta { display: flex; gap: var(--space-1); }
.ds-paleta i {
  flex: 1;
  height: 1.5rem;
  border-radius: var(--radius-xs);
  border: 1px solid var(--color-border);
}

/* --- barra de guardado ---
   Fija abajo del panel y solo cuando hay algo pendiente. Una barra siempre
   presente se vuelve parte del fondo y deja de avisar nada. */

.lab-guardar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  background: var(--color-surface);
  border-top: 1px solid var(--color-border-strong);
  animation: lab-guardar-entra var(--duration-base) var(--ease-out);
}

@keyframes lab-guardar-entra { from { transform: translateY(100%); } }

.lab-guardar__cuenta {
  flex: 1;
  min-width: 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.lab-guardar .btn { flex: none; }

/* Con la barra puesta, el cuerpo del panel deja lugar para no quedar tapado
   en la ultima seccion. */
body.hay-cambios .lab-panel-cuerpo { padding-bottom: var(--space-20); }

/* --- componentes/ds-drawer.css --- */
/* ============================================================
   ds-drawer.css — panel lateral en escritorio, cajon en el telefono

   Componente nuevo, escrito con las reglas del kit. El mismo markup y el mismo
   contenido; lo que cambia es de donde entra y cuanto ocupa.

   La razon es concreta: en la consola, el panel de la derecha ocupa 27.5rem.
   En un telefono eso es la pantalla entera, y la vista previa —que es la razon
   de ser de esa pagina— queda tapada. Ajustar sin ver lo que se ajusta no
   sirve. Desde abajo, el cajon deja el cuadro a la vista y se puede subir para
   trabajar y bajar para mirar.

   Tres alturas y no dos: cerrado, asomado —lo justo para tocar un control sin
   perder el cuadro de vista— y completo.

   Portable: depende solo de las fichas de tokens.css. El arrastre lo agrega
   ds-drawer.js y es opcional: sin el, el tirador alterna entre asomado y
   completo con un toque.
   ============================================================ */

.ds-drawer {
  position: fixed;
  z-index: 300;
  display: flex;
  flex-direction: column;
  background: color-mix(in oklab, var(--color-bg) 96%, transparent);
  backdrop-filter: blur(20px);
}

.ds-drawer[hidden] { display: none; }

.ds-drawer__cab {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-5) var(--space-4);
  border-bottom: 1px solid var(--color-border);
  flex: none;
}

.ds-drawer__cuerpo {
  flex: 1;
  overflow-y: auto;
  /* Nada se desborda a los lados. Si algo lo hace, es un bug del contenido y
     no algo que arreglar con una barra horizontal. */
  overflow-x: hidden;
  overscroll-behavior: contain;
  /* El hueco de la barra vertical se reserva SIEMPRE, haya o no scroll. Sin
     esto el contenido se corre unos pixeles cuando aparece, y los controles
     de la derecha quedan medio tapados. */
  scrollbar-gutter: stable;
  padding: var(--space-6) var(--space-5) var(--space-16);
  -webkit-overflow-scrolling: touch;
}

/* La barra de scroll la pinta `base.css` para todo el producto. Esta era la
   primera de cuatro copias, cada una con su propio ancho y su propio color. */

/* El tirador de arrastre. Solo existe en el telefono: con mouse no se arrastra
   un panel, se lo abre y se lo cierra. */
.ds-drawer__agarre { display: none; }

/* --- escritorio: panel a la derecha ------------------------------------- */

@media (min-width: 48rem) {
  .ds-drawer {
    right: 0;
    top: 0;
    bottom: 0;
    width: min(27.5rem, 100vw);
    border-left: 1px solid var(--color-border);
    animation: ds-drawer-lateral var(--duration-slow) cubic-bezier(0.16, 1, 0.3, 1);
  }
}

@keyframes ds-drawer-lateral {
  from { transform: translateX(100%); }
}

/* --- telefono: cajon desde abajo ----------------------------------------- */

@media (max-width: 47.99rem) {
  .ds-drawer {
    left: 0;
    right: 0;
    bottom: 0;
    /* El alto lo fija `--alto`, que escribe el JS al arrastrar. El valor por
       defecto es el asomado. */
    height: var(--alto, 52dvh);
    max-height: 92dvh;
    border-top: 1px solid var(--color-border-strong);
    border-radius: var(--radius-2xl) var(--radius-2xl) 0 0;
    /* Sin transicion mientras el dedo esta apoyado: la agrega el JS al soltar,
       para que el cajon acompane al dedo y despues encaje solo. */
    animation: ds-drawer-abajo var(--duration-base) var(--ease-out);
  }

  .ds-drawer.is-animado {
    transition: height var(--duration-base) var(--ease-out);
  }

  .ds-drawer__agarre {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
    /* Alto generoso: es el objetivo tactil que se busca sin mirar. */
    height: 1.75rem;
    padding-top: var(--space-2);
    cursor: grab;
    touch-action: none;
    background: none;
    border: 0;
    width: 100%;
  }

  .ds-drawer__agarre::before {
    content: "";
    width: 2.5rem;
    height: 4px;
    border-radius: var(--radius-full);
    background: var(--color-border-hover);
  }

  .ds-drawer__agarre:active { cursor: grabbing; }
  .ds-drawer__agarre:active::before { background: var(--color-text-muted); }

  .ds-drawer__cab {
    padding: var(--space-2) var(--space-4) var(--space-3);
  }

  .ds-drawer__cuerpo {
    padding: var(--space-5) var(--space-4) var(--space-20);
  }
}

@keyframes ds-drawer-abajo {
  from { transform: translateY(100%); }
}

/* --- componentes/ds-tactil.css --- */
/* ============================================================
   ds-tactil.css — componentes para una mano, de pie, con poca luz

   Componentes nuevos, escritos con las reglas del kit. El kit esta escalado
   para escritorio: un `.btn` mide 2.5rem, que en un mouse sobra y en un pulgar
   a media luz despues de un par de copas es un objetivo chico.

   Son tres, y los tres los usan la pagina del invitado y —cuando llegue— la de
   moderacion, que se opera igual: de pie, con una mano, sin mirar mucho.

     .ds-accion    la fila alta de accion principal
     .ds-progreso  una fila de estado con icono, nombre y avance
     .ds-miniaturas la grilla de lo propio, para ver y borrar

   Portable: depende solo de las fichas de tokens.css.
   ============================================================ */

/* --- accion grande -----------------------------------------------------------
   Icono en pastilla, titulo, y una linea de detalle opcional. 4.6rem son casi
   80px: muy por encima del minimo tactil, y suficiente para apretar sin mirar.

   Markup:
     <button class="ds-accion ds-accion--principal">
       <span class="ds-accion__icono"><svg/></span>
       <span class="ds-accion__texto">
         <span class="ds-accion__titulo">Sacar foto</span>
         <small class="ds-accion__nota">Se abre la camara</small>
       </span>
     </button>
*/

.ds-accion {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  width: 100%;
  min-height: 4.6rem;
  padding: var(--space-4) var(--space-5);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card);
  background: var(--color-surface);
  color: var(--color-text);
  text-align: left;
  cursor: pointer;
  transition: transform var(--duration-fast) var(--ease-out),
              border-color var(--duration-base) var(--ease-out),
              background var(--duration-base) var(--ease-out);
}

/* El hundido al tocar es la unica confirmacion que da un telefono: no hay
   hover, y sin esto no se sabe si el toque entro. */
.ds-accion:active { transform: scale(0.98); }
.ds-accion:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }

/* La accion que queremos que pase. Una sola por pantalla. */
.ds-accion--principal {
  background: var(--color-accent-soft);
  border-color: var(--color-border-accent);
}

.ds-accion__icono {
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  flex: none;
  border-radius: var(--radius-full);
  background: var(--color-surface-muted);
  color: var(--color-accent-strong);
}

.ds-accion--principal .ds-accion__icono {
  background: var(--color-accent);
  color: var(--color-on-accent);
}

/* El glifo va a 1.5rem venga como `svg` inline o como `.ic` enmascarado. Los
   dos toman `currentColor`, que es lo que hace que el mismo icono sirva sobre
   el acento y sobre la superficie apagada. */
.ds-accion__icono svg { width: 1.5rem; height: 1.5rem; fill: currentColor; }
.ds-accion__icono .ic { width: 1.5rem; height: 1.5rem; vertical-align: 0; }

.ds-accion__texto { display: grid; gap: 2px; min-width: 0; }

.ds-accion__titulo {
  font-size: var(--text-lg);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-snug);
}

.ds-accion__nota {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

/* --- fila de estado ----------------------------------------------------------
   Una subida en curso: pastilla de icono, nombre, estado, y avance a la
   derecha. Los estados son `is-ok`, `is-error`, `is-espera`.
*/

.ds-progreso {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-lg);
  background: var(--color-surface-muted);
}

.ds-progreso__icono {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  flex: none;
  border-radius: var(--radius-full);
  background: var(--color-surface);
  color: var(--color-text-muted);
  font-size: var(--text-md);
  line-height: 1;
}

.ds-progreso__cuerpo { display: grid; gap: 2px; min-width: 0; flex: 1; }

.ds-progreso__nombre {
  font-size: var(--text-sm);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ds-progreso__estado { font-size: var(--text-xs); color: var(--color-text-muted); }

.ds-progreso__pct {
  font-variant-numeric: tabular-nums;
  color: var(--color-accent-strong);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
}

.ds-progreso.is-ok .ds-progreso__icono {
  background: var(--color-accent-soft);
  color: var(--color-accent-strong);
}
.ds-progreso.is-ok .ds-progreso__estado { color: var(--color-accent-strong); }

.ds-progreso.is-error .ds-progreso__icono {
  background: var(--color-danger-soft);
  color: var(--color-danger);
}
.ds-progreso.is-error .ds-progreso__estado { color: var(--color-danger); }

/* "Esperando senal" late despacio: comunica que sigue vivo sin la ansiedad de
   un spinner rapido. El brief lo pide explicito — nunca un spinner infinito. */
.ds-progreso.is-espera .ds-progreso__icono { animation: ds-latir 2s ease-in-out infinite; }

@keyframes ds-latir {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}

/* --- grilla de miniaturas ----------------------------------------------------
   Lo propio, para mirarlo y borrarlo. Se llena sola: sin media queries, la
   grilla decide cuantas columnas entran.
*/

.ds-miniaturas {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
  gap: var(--space-2);
}

.ds-miniatura {
  position: relative;
  margin: 0;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius-thumb);
  overflow: hidden;
  background: var(--color-surface-muted);
  border: 1px solid var(--color-border);
}

.ds-miniatura img { width: 100%; height: 100%; object-fit: cover; }

/* Cuando la pieza es un mensaje no hay imagen: se muestra el texto. */
.ds-miniatura__texto {
  padding: var(--space-3);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  overflow: hidden;
  line-height: var(--leading-snug);
}

.ds-miniatura__estado {
  position: absolute;
  inset: auto 0 0 0;
  padding: var(--space-1) var(--space-2);
  /* Velo neutro y no una ficha del tema: debajo hay una foto que no
     controlamos y puede ser de cualquier color. */
  background: rgba(0, 0, 0, 0.72);
  font-size: var(--text-xs);
  color: rgba(255, 255, 255, 0.85);
}

.ds-miniatura__borrar {
  position: absolute;
  top: var(--space-1);
  right: var(--space-1);
  display: grid;
  place-items: center;
  /* 2.5rem son 40px: el minimo que se acierta con el pulgar sobre una
     miniatura de 8.5rem sin borrar la de al lado. */
  width: 2.5rem;
  height: 2.5rem;
  border: none;
  border-radius: var(--radius-full);
  background: rgba(0, 0, 0, 0.6);
  color: rgba(255, 255, 255, 0.92);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-out);
}

.ds-miniatura__borrar:hover { background: var(--color-danger); }
.ds-miniatura__borrar:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
.ds-miniatura__borrar .ic { width: 1rem; height: 1rem; }

/* --- la cabecera de la pagina del invitado -----------------------------------
   Marca del evento arriba, centrada. Es lo primero que se ve al escanear, y
   tiene que decir "estas en la fiesta de fulano" antes que "esta es una
   herramienta para subir fotos". */

.ds-bienvenida {
  display: grid;
  justify-items: center;
  gap: var(--space-3);
  text-align: center;
  padding: var(--space-6) 0 var(--space-2);
}

/* La foto de los anfitriones. Grande: es la cara de la fiesta. */
.ds-bienvenida__foto {
  width: 5.5rem;
  height: 5.5rem;
  border-radius: var(--radius-full);
  object-fit: cover;
  border: 2px solid var(--color-border-accent);
}

.ds-bienvenida__titulo {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  text-wrap: balance;
}

.ds-bienvenida__sub {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--text-md);
  line-height: var(--leading-snug);
  text-wrap: balance;
  max-width: 26ch;
}

/* --- el sello de portals -----------------------------------------------------
   Abajo del todo y discreto. El evento manda arriba; esto dice de que producto
   es sin competir con la fiesta. Es lo que hace que alguien que vio la pagina
   en un casamiento sepa que pedir para el suyo. */

.ds-sello {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-6) 0 var(--space-2);
  color: var(--color-text-subtle);
  font-size: var(--text-xs);
  text-decoration: none;
}

.ds-sello strong {
  color: var(--color-text-muted);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-snug);
}

.ds-sello__punto {
  width: 5px;
  height: 5px;
  border-radius: var(--radius-full);
  background: var(--color-accent);
  flex: none;
}

/* --- el perfil del invitado --------------------------------------------------
   La foto y el nombre con los que van a salir sus fotos en la pantalla. */

.ds-perfil {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

.ds-perfil__foto {
  position: relative;
  width: 4.5rem;
  height: 4.5rem;
  flex: none;
  border-radius: var(--radius-full);
  overflow: hidden;
  background: var(--color-surface-muted);
  border: 1px solid var(--color-border-strong);
  cursor: pointer;
  display: grid;
  place-items: center;
  padding: 0;
}

.ds-perfil__foto img { width: 100%; height: 100%; object-fit: cover; }

.ds-perfil__foto .ic {
  width: 1.5rem;
  height: 1.5rem;
  color: var(--color-text-muted);
}

.ds-perfil__cuerpo { display: grid; gap: var(--space-1); min-width: 0; flex: 1; }

.ds-perfil__nota { font-size: var(--text-xs); color: var(--color-text-subtle); }

/* --- componentes/ds-gallery.css --- */
/* ============================================================
   gallery.css — GAL
   Horizontal image carousel (work-strip): drag-to-scroll with snap,
   a thin scrollbar, and tiles that open the lightbox. Tile width is a
   local var (--ds-gallery-w); crop via the thumb aspect-ratio.

   Portable: depends on the design tokens in tokens.css. Behaviour +
   lightbox wiring come from gallery.js (+ lightbox.js).
   Markup:
     <div class="ds-gallery">
       <button class="ds-gallery__item" type="button">
         <div class="ds-gallery__thumb"><img src="…" alt="…"></div>
       </button>
     </div>
   ============================================================ */

.ds-gallery {
  --ds-gallery-w: 16rem;
  display: flex;
  gap: var(--space-3);
  margin: 0 -2px;
  padding-bottom: var(--space-2);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  cursor: grab;
  /* La barra la pinta `base.css`, igual que en el resto del producto. */
  -webkit-overflow-scrolling: touch;
}
.ds-gallery.is-dragging { cursor: grabbing; scroll-snap-type: none; user-select: none; }
.ds-gallery.is-snapping { scroll-snap-type: none; }

.ds-gallery__item {
  flex: none;
  width: var(--ds-gallery-w);
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  scroll-snap-align: start;
  -webkit-user-drag: none;
}
.ds-gallery__thumb {
  width: 100%;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--color-surface-muted);
}
.ds-gallery__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; -webkit-user-drag: none; }

/* --- componentes/ds-lightbox.css --- */
/* ============================================================
   lightbox.css — LBX
   Full-screen media overlay: a blurred scrim, an inset "canvas" that
   fits the image, a fixed close button and a bottom counter. Behaviour
   (click-halves, swipe, keyboard, video embeds) lives in lightbox.js.

   Portable: depends on the design tokens in tokens.css + lightbox.js.
   The scrim/text sit above a dark backdrop, so foreground text is
   neutral-0 regardless of theme.
   ============================================================ */

.ds-lightbox { position: fixed; inset: 0; z-index: var(--z-modal); }
.ds-lightbox[hidden] { display: none; }

.ds-lightbox__backdrop {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--neutral-950) 72%, transparent);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
}

.ds-lightbox__inner { position: fixed; inset: var(--space-6); display: grid; place-items: center; }
@media (min-width: 64rem) { .ds-lightbox__inner { inset: var(--space-16); } }

.ds-lightbox__media {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
}
.ds-lightbox__media picture { max-width: 100%; max-height: 100%; display: flex; align-items: center; justify-content: center; }
.ds-lightbox__media img {
  max-width: 100%;
  max-height: calc(100vh - 48px - 90px);
  object-fit: contain;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}
@media (min-width: 64rem) { .ds-lightbox__media img { max-height: calc(100vh - 128px - 90px); } }

.ds-lightbox__caption {
  width: min(900px, 100%);
  padding: 0 var(--space-2);
  text-align: center;
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  color: var(--neutral-0);
  user-select: none;
}

.ds-lightbox__video { width: 100%; height: auto; max-width: 1200px; aspect-ratio: 16 / 9; }
.ds-lightbox__video iframe { width: 100%; height: 100%; border: 0; display: block; }

.ds-lightbox__close {
  position: fixed;
  top: var(--space-8);
  right: var(--space-8);
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--neutral-0);
  cursor: pointer;
  display: grid;
  place-items: center;
  z-index: 2;
}
.ds-lightbox__close svg { width: 1.5rem; height: 1.5rem; display: block; }

.ds-lightbox__counter {
  position: fixed;
  left: 50%;
  bottom: var(--space-8);
  transform: translateX(-50%);
  font-size: var(--text-sm);
  line-height: 1;
  letter-spacing: 0.08em;
  color: var(--neutral-0);
  font-variant-numeric: tabular-nums;
  user-select: none;
  z-index: 2;
}
.ds-lightbox__error { color: var(--neutral-300); font-size: var(--text-sm); text-align: center; padding: var(--space-6); }

/* --- componentes/ds-aspect-ratio.css --- */
/* ============================================================
   aspect-ratio.css — ASP
   A segmented-style picker of aspect ratios: each option is a scaled
   rectangle silhouette (drawn with a border, sized inline per ratio)
   above its label, inside a soft inset track. Selected option lifts to
   a white chip.

   Portable: depends only on the design tokens in tokens.css. Selection
   is pure CSS (radio :checked). No JS — silhouettes are sized inline.
   Markup: see design-system.html ASP-01.
   ============================================================ */

.ds-ratios {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  padding: var(--space-1);
  background: var(--color-surface-muted);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  width: fit-content;
  max-width: 100%;
}
.ds-ratios__opt { position: relative; cursor: pointer; }
.ds-ratios__opt input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.ds-ratios__box {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
  width: 2.75rem;
  padding: var(--space-2) 0;
  border-radius: var(--radius-md);
  color: var(--color-text-muted);
  transition: background var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out);
}
.ds-ratios__opt input:checked + .ds-ratios__box { background: var(--color-surface); color: var(--color-text); box-shadow: var(--shadow-xs); }
.ds-ratios__opt input:focus-visible + .ds-ratios__box { outline: 2px solid var(--neutral-900); outline-offset: 1px; }
.ds-ratios__sil { display: grid; place-items: center; height: 1.6rem; }
.ds-ratios__sil span { border: 1.5px solid currentColor; border-radius: 2px; /* width/height set inline per ratio */ }
.ds-ratios__label { font-size: 10px; font-weight: var(--weight-medium); font-variant-numeric: tabular-nums; }

/* --- componentes/ds-collapsible-items.css --- */
/* ============================================================
   collapsible-items.css — CIT
   The in-card collapsible order summary used on checkout: a toggle line
   (count + total, with a chevron) sitting inside the same card as the
   form, revealing the order lines below when expanded. Not a floating
   dropdown — it expands in place.

   Portable: depends only on the design tokens in tokens.css. The
   expand/collapse comes from collapsible-items.js.
   Markup:
     <div class="ds-collapse">
       <button class="ds-collapse__toggle" aria-expanded="false" aria-controls="oc">
         <span>3 items · $84,990</span><svg>chevron</svg>
       </button>
       <div class="ds-collapse__body" id="oc" hidden> … </div>
     </div>
   ============================================================ */

.ds-collapse {
  padding: var(--space-5);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-2xl);
  background: var(--color-surface);
  box-shadow: var(--shadow-sm);
}
.ds-collapse__toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  width: 100%;
  padding: 0 0 var(--space-4);
  border: 0;
  border-bottom: 1px solid var(--color-border);
  background: none;
  cursor: pointer;
  font: inherit;
  font-size: var(--text-sm);
  color: var(--color-text);
}
.ds-collapse__toggle b { font-variant-numeric: tabular-nums; }
.ds-collapse__toggle svg { width: 1rem; height: 1rem; flex-shrink: 0; color: var(--color-text-muted); transition: rotate var(--duration-fast) var(--ease-out); }
.ds-collapse__toggle[aria-expanded="true"] svg { rotate: 180deg; }
.ds-collapse__body { padding-top: var(--space-3); }
.ds-collapse__body[hidden] { display: none; }

/* --- temas.css --- */
/* ============================================================
   themes-dark.css — dark themes for the Semáforo design system (v2).
   Each block overrides ONLY the semantic tokens under
   html[data-ds-theme="<key>"]; components never change. v2: every theme
   owns its SIGNATURE action color — buttons, bands, chips and toasts
   wear the THEME, not the Semáforo accents (Grafito stays the literal
   Semáforo inversion by design). Groups: Sistema · Editores (Dracula,
   Nord, Catppuccin, Tokyo Night, Gruvbox, One Dark, GitHub, Solarized)
   · Eventos (Terciopelo, Sereno, Jardín, Quince, Neón, Vino, Aurora —
   for the events/projector app). Apply: <html data-ds-theme="quince">,
   or copy a block's body into :root to hard-adopt one.
   ============================================================ */

/* ---- Grafito · Semáforo dark ---- */
html[data-ds-theme="grafito"] {
  color-scheme: dark;
  --color-bg: #111111;
  --color-surface: #1A1A1A;
  --color-surface-muted: #222222;
  --color-border: #2E2E2E;
  --color-border-strong: #3A3A3A;
  --color-border-hover: #4A4A4A;
  --color-border-accent: #4A4A4A;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #F4F4F4;
  --color-text-muted: #A9A9A9;
  --color-text-subtle: #6E6E6E;
  /* signature action: the theme's own color, not the system's */
  --color-action: #F4F4F4;
  --color-action-hover: color-mix(in srgb, #F4F4F4 85%, #1A1A1A);
  --color-on-action: #111111;
  --color-accent: #3FBF74;
  --color-accent-strong: #3FBF74;
  --color-accent-hover: color-mix(in srgb, #3FBF74 85%, #1A1A1A);
  --color-accent-soft: color-mix(in srgb, #3FBF74 16%, #1A1A1A);
  --color-on-accent: #111111;
  --color-danger: #F0592A;
  --color-danger-hover: color-mix(in srgb, #F0592A 85%, #1A1A1A);
  --color-danger-soft: color-mix(in srgb, #F0592A 16%, #1A1A1A);
  --color-on-danger: #111111;
  --color-cta: #FF5000;
  --color-cta-hover: color-mix(in srgb, #FF5000 85%, #1A1A1A);
  --color-on-cta: #111111;
  --color-info: #6AA2E8;
  --color-highlight: #FFCC00;
  --color-highlight-soft: color-mix(in srgb, #FFCC00 16%, #1A1A1A);
  --color-highlight-border: color-mix(in srgb, #FFCC00 45%, #1A1A1A);
  --color-highlight-text: color-mix(in srgb, #FFCC00 80%, #F4F4F4);
  --color-highlight-deep: #FFCC00;
  --color-on-highlight: #111111;
  --color-warning: #FFCC00;
  --color-warning-soft: color-mix(in srgb, #FFCC00 14%, #1A1A1A);
  --color-warning-text: color-mix(in srgb, #FFCC00 80%, #F4F4F4);
  --color-focus: #F4F4F4;
  --color-field: #111111;
  --purple-400: #C79BE0;
  --purple-600: #C79BE0;
  --purple-700: color-mix(in srgb, #C79BE0 80%, #F4F4F4);
  --purple-50: color-mix(in srgb, #C79BE0 14%, #1A1A1A);
  --purple-100: color-mix(in srgb, #C79BE0 20%, #1A1A1A);
}

/* ---- Consola · CRT ---- */
html[data-ds-theme="consola"] {
  color-scheme: dark;
  --color-bg: #0A0F0A;
  --color-surface: #101710;
  --color-surface-muted: #162016;
  --color-border: #1E2C1E;
  --color-border-strong: #2A3D2A;
  --color-border-hover: #3A523A;
  --color-border-accent: #3A523A;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #C8F5C8;
  --color-text-muted: #7FBF7F;
  --color-text-subtle: #4E7A4E;
  /* signature action: the theme's own color, not the system's */
  --color-action: #33FF66;
  --color-action-hover: color-mix(in srgb, #33FF66 85%, #101710);
  --color-on-action: #06120A;
  --color-accent: #33FF66;
  --color-accent-strong: #33FF66;
  --color-accent-hover: color-mix(in srgb, #33FF66 85%, #101710);
  --color-accent-soft: color-mix(in srgb, #33FF66 16%, #101710);
  --color-on-accent: #06120A;
  --color-danger: #FF5F56;
  --color-danger-hover: color-mix(in srgb, #FF5F56 85%, #101710);
  --color-danger-soft: color-mix(in srgb, #FF5F56 16%, #101710);
  --color-on-danger: #06120A;
  --color-cta: #33FF66;
  --color-cta-hover: color-mix(in srgb, #33FF66 85%, #101710);
  --color-on-cta: #06120A;
  --color-info: #5FD7FF;
  --color-highlight: #FFB000;
  --color-highlight-soft: color-mix(in srgb, #FFB000 16%, #101710);
  --color-highlight-border: color-mix(in srgb, #FFB000 45%, #101710);
  --color-highlight-text: color-mix(in srgb, #FFB000 80%, #C8F5C8);
  --color-highlight-deep: #FFB000;
  --color-on-highlight: #06120A;
  --color-warning: #FFB000;
  --color-warning-soft: color-mix(in srgb, #FFB000 14%, #101710);
  --color-warning-text: color-mix(in srgb, #FFB000 80%, #C8F5C8);
  --color-focus: #C8F5C8;
  --color-field: #0A0F0A;
  --purple-400: #B58FE0;
  --purple-600: #B58FE0;
  --purple-700: color-mix(in srgb, #B58FE0 80%, #C8F5C8);
  --purple-50: color-mix(in srgb, #B58FE0 14%, #101710);
  --purple-100: color-mix(in srgb, #B58FE0 20%, #101710);
}

/* ---- Drácula ---- */
html[data-ds-theme="dracula"] {
  color-scheme: dark;
  --color-bg: #1E1F29;
  --color-surface: #282A36;
  --color-surface-muted: #343746;
  --color-border: #3C3F51;
  --color-border-strong: #44475A;
  --color-border-hover: #5A5E78;
  --color-border-accent: #5A5E78;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #F8F8F2;
  --color-text-muted: #9DA3C8;
  --color-text-subtle: #6272A4;
  /* signature action: the theme's own color, not the system's */
  --color-action: #BD93F9;
  --color-action-hover: color-mix(in srgb, #BD93F9 85%, #282A36);
  --color-on-action: #1E1F29;
  --color-accent: #50FA7B;
  --color-accent-strong: #50FA7B;
  --color-accent-hover: color-mix(in srgb, #50FA7B 85%, #282A36);
  --color-accent-soft: color-mix(in srgb, #50FA7B 16%, #282A36);
  --color-on-accent: #1E1F29;
  --color-danger: #FF5555;
  --color-danger-hover: color-mix(in srgb, #FF5555 85%, #282A36);
  --color-danger-soft: color-mix(in srgb, #FF5555 16%, #282A36);
  --color-on-danger: #1E1F29;
  --color-cta: #FF79C6;
  --color-cta-hover: color-mix(in srgb, #FF79C6 85%, #282A36);
  --color-on-cta: #1E1F29;
  --color-info: #8BE9FD;
  --color-highlight: #F1FA8C;
  --color-highlight-soft: color-mix(in srgb, #F1FA8C 16%, #282A36);
  --color-highlight-border: color-mix(in srgb, #F1FA8C 45%, #282A36);
  --color-highlight-text: color-mix(in srgb, #F1FA8C 80%, #F8F8F2);
  --color-highlight-deep: #F1FA8C;
  --color-on-highlight: #1E1F29;
  --color-warning: #F1FA8C;
  --color-warning-soft: color-mix(in srgb, #F1FA8C 14%, #282A36);
  --color-warning-text: color-mix(in srgb, #F1FA8C 80%, #F8F8F2);
  --color-focus: #F8F8F2;
  --color-field: #1E1F29;
  --purple-400: #BD93F9;
  --purple-600: #BD93F9;
  --purple-700: color-mix(in srgb, #BD93F9 80%, #F8F8F2);
  --purple-50: color-mix(in srgb, #BD93F9 14%, #282A36);
  --purple-100: color-mix(in srgb, #BD93F9 20%, #282A36);
}

/* ---- Nord ---- */
html[data-ds-theme="nord"] {
  color-scheme: dark;
  --color-bg: #242933;
  --color-surface: #2E3440;
  --color-surface-muted: #3B4252;
  --color-border: #434C5E;
  --color-border-strong: #4C566A;
  --color-border-hover: #616E88;
  --color-border-accent: #616E88;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #ECEFF4;
  --color-text-muted: #AEB8CC;
  --color-text-subtle: #7B88A1;
  /* signature action: the theme's own color, not the system's */
  --color-action: #88C0D0;
  --color-action-hover: color-mix(in srgb, #88C0D0 85%, #2E3440);
  --color-on-action: #242933;
  --color-accent: #A3BE8C;
  --color-accent-strong: #A3BE8C;
  --color-accent-hover: color-mix(in srgb, #A3BE8C 85%, #2E3440);
  --color-accent-soft: color-mix(in srgb, #A3BE8C 16%, #2E3440);
  --color-on-accent: #242933;
  --color-danger: #BF616A;
  --color-danger-hover: color-mix(in srgb, #BF616A 85%, #2E3440);
  --color-danger-soft: color-mix(in srgb, #BF616A 16%, #2E3440);
  --color-on-danger: #ECEFF4;
  --color-cta: #D08770;
  --color-cta-hover: color-mix(in srgb, #D08770 85%, #2E3440);
  --color-on-cta: #242933;
  --color-info: #81A1C1;
  --color-highlight: #EBCB8B;
  --color-highlight-soft: color-mix(in srgb, #EBCB8B 16%, #2E3440);
  --color-highlight-border: color-mix(in srgb, #EBCB8B 45%, #2E3440);
  --color-highlight-text: color-mix(in srgb, #EBCB8B 80%, #ECEFF4);
  --color-highlight-deep: #EBCB8B;
  --color-on-highlight: #242933;
  --color-warning: #EBCB8B;
  --color-warning-soft: color-mix(in srgb, #EBCB8B 14%, #2E3440);
  --color-warning-text: color-mix(in srgb, #EBCB8B 80%, #ECEFF4);
  --color-focus: #ECEFF4;
  --color-field: #242933;
  --purple-400: #B48EAD;
  --purple-600: #B48EAD;
  --purple-700: color-mix(in srgb, #B48EAD 80%, #ECEFF4);
  --purple-50: color-mix(in srgb, #B48EAD 14%, #2E3440);
  --purple-100: color-mix(in srgb, #B48EAD 20%, #2E3440);
}

/* ---- Catppuccin Mocha ---- */
html[data-ds-theme="catppuccin"] {
  color-scheme: dark;
  --color-bg: #181825;
  --color-surface: #1E1E2E;
  --color-surface-muted: #2A2A3C;
  --color-border: #313244;
  --color-border-strong: #45475A;
  --color-border-hover: #585B70;
  --color-border-accent: #585B70;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #CDD6F4;
  --color-text-muted: #A6ADC8;
  --color-text-subtle: #6C7086;
  /* signature action: the theme's own color, not the system's */
  --color-action: #CBA6F7;
  --color-action-hover: color-mix(in srgb, #CBA6F7 85%, #1E1E2E);
  --color-on-action: #181825;
  --color-accent: #A6E3A1;
  --color-accent-strong: #A6E3A1;
  --color-accent-hover: color-mix(in srgb, #A6E3A1 85%, #1E1E2E);
  --color-accent-soft: color-mix(in srgb, #A6E3A1 16%, #1E1E2E);
  --color-on-accent: #181825;
  --color-danger: #F38BA8;
  --color-danger-hover: color-mix(in srgb, #F38BA8 85%, #1E1E2E);
  --color-danger-soft: color-mix(in srgb, #F38BA8 16%, #1E1E2E);
  --color-on-danger: #181825;
  --color-cta: #FAB387;
  --color-cta-hover: color-mix(in srgb, #FAB387 85%, #1E1E2E);
  --color-on-cta: #181825;
  --color-info: #89B4FA;
  --color-highlight: #F9E2AF;
  --color-highlight-soft: color-mix(in srgb, #F9E2AF 16%, #1E1E2E);
  --color-highlight-border: color-mix(in srgb, #F9E2AF 45%, #1E1E2E);
  --color-highlight-text: color-mix(in srgb, #F9E2AF 80%, #CDD6F4);
  --color-highlight-deep: #F9E2AF;
  --color-on-highlight: #181825;
  --color-warning: #F9E2AF;
  --color-warning-soft: color-mix(in srgb, #F9E2AF 14%, #1E1E2E);
  --color-warning-text: color-mix(in srgb, #F9E2AF 80%, #CDD6F4);
  --color-focus: #CDD6F4;
  --color-field: #181825;
  --purple-400: #CBA6F7;
  --purple-600: #CBA6F7;
  --purple-700: color-mix(in srgb, #CBA6F7 80%, #CDD6F4);
  --purple-50: color-mix(in srgb, #CBA6F7 14%, #1E1E2E);
  --purple-100: color-mix(in srgb, #CBA6F7 20%, #1E1E2E);
}

/* ---- Tokyo Night ---- */
html[data-ds-theme="tokyo"] {
  color-scheme: dark;
  --color-bg: #16161E;
  --color-surface: #1A1B26;
  --color-surface-muted: #24283B;
  --color-border: #2F3549;
  --color-border-strong: #3B4261;
  --color-border-hover: #545C7E;
  --color-border-accent: #545C7E;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #C0CAF5;
  --color-text-muted: #9AA5CE;
  --color-text-subtle: #565F89;
  /* signature action: the theme's own color, not the system's */
  --color-action: #7AA2F7;
  --color-action-hover: color-mix(in srgb, #7AA2F7 85%, #1A1B26);
  --color-on-action: #16161E;
  --color-accent: #9ECE6A;
  --color-accent-strong: #9ECE6A;
  --color-accent-hover: color-mix(in srgb, #9ECE6A 85%, #1A1B26);
  --color-accent-soft: color-mix(in srgb, #9ECE6A 16%, #1A1B26);
  --color-on-accent: #16161E;
  --color-danger: #F7768E;
  --color-danger-hover: color-mix(in srgb, #F7768E 85%, #1A1B26);
  --color-danger-soft: color-mix(in srgb, #F7768E 16%, #1A1B26);
  --color-on-danger: #16161E;
  --color-cta: #FF9E64;
  --color-cta-hover: color-mix(in srgb, #FF9E64 85%, #1A1B26);
  --color-on-cta: #16161E;
  --color-info: #7AA2F7;
  --color-highlight: #E0AF68;
  --color-highlight-soft: color-mix(in srgb, #E0AF68 16%, #1A1B26);
  --color-highlight-border: color-mix(in srgb, #E0AF68 45%, #1A1B26);
  --color-highlight-text: color-mix(in srgb, #E0AF68 80%, #C0CAF5);
  --color-highlight-deep: #E0AF68;
  --color-on-highlight: #16161E;
  --color-warning: #E0AF68;
  --color-warning-soft: color-mix(in srgb, #E0AF68 14%, #1A1B26);
  --color-warning-text: color-mix(in srgb, #E0AF68 80%, #C0CAF5);
  --color-focus: #C0CAF5;
  --color-field: #16161E;
  --purple-400: #BB9AF7;
  --purple-600: #BB9AF7;
  --purple-700: color-mix(in srgb, #BB9AF7 80%, #C0CAF5);
  --purple-50: color-mix(in srgb, #BB9AF7 14%, #1A1B26);
  --purple-100: color-mix(in srgb, #BB9AF7 20%, #1A1B26);
}

/* ---- Gruvbox ---- */
html[data-ds-theme="gruvbox"] {
  color-scheme: dark;
  --color-bg: #1D2021;
  --color-surface: #282828;
  --color-surface-muted: #32302F;
  --color-border: #3C3836;
  --color-border-strong: #504945;
  --color-border-hover: #665C54;
  --color-border-accent: #665C54;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #EBDBB2;
  --color-text-muted: #BDAE93;
  --color-text-subtle: #928374;
  /* signature action: the theme's own color, not the system's */
  --color-action: #FABD2F;
  --color-action-hover: color-mix(in srgb, #FABD2F 85%, #282828);
  --color-on-action: #1D2021;
  --color-accent: #B8BB26;
  --color-accent-strong: #B8BB26;
  --color-accent-hover: color-mix(in srgb, #B8BB26 85%, #282828);
  --color-accent-soft: color-mix(in srgb, #B8BB26 16%, #282828);
  --color-on-accent: #1D2021;
  --color-danger: #FB4934;
  --color-danger-hover: color-mix(in srgb, #FB4934 85%, #282828);
  --color-danger-soft: color-mix(in srgb, #FB4934 16%, #282828);
  --color-on-danger: #1D2021;
  --color-cta: #FE8019;
  --color-cta-hover: color-mix(in srgb, #FE8019 85%, #282828);
  --color-on-cta: #1D2021;
  --color-info: #83A598;
  --color-highlight: #FABD2F;
  --color-highlight-soft: color-mix(in srgb, #FABD2F 16%, #282828);
  --color-highlight-border: color-mix(in srgb, #FABD2F 45%, #282828);
  --color-highlight-text: color-mix(in srgb, #FABD2F 80%, #EBDBB2);
  --color-highlight-deep: #FABD2F;
  --color-on-highlight: #1D2021;
  --color-warning: #FABD2F;
  --color-warning-soft: color-mix(in srgb, #FABD2F 14%, #282828);
  --color-warning-text: color-mix(in srgb, #FABD2F 80%, #EBDBB2);
  --color-focus: #EBDBB2;
  --color-field: #1D2021;
  --purple-400: #D3869B;
  --purple-600: #D3869B;
  --purple-700: color-mix(in srgb, #D3869B 80%, #EBDBB2);
  --purple-50: color-mix(in srgb, #D3869B 14%, #282828);
  --purple-100: color-mix(in srgb, #D3869B 20%, #282828);
}

/* ---- One Dark ---- */
html[data-ds-theme="onedark"] {
  color-scheme: dark;
  --color-bg: #21252B;
  --color-surface: #282C34;
  --color-surface-muted: #2F343E;
  --color-border: #3A3F4B;
  --color-border-strong: #4B5263;
  --color-border-hover: #5C6370;
  --color-border-accent: #5C6370;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #ABB2BF;
  --color-text-muted: #828997;
  --color-text-subtle: #5C6370;
  /* signature action: the theme's own color, not the system's */
  --color-action: #61AFEF;
  --color-action-hover: color-mix(in srgb, #61AFEF 85%, #282C34);
  --color-on-action: #21252B;
  --color-accent: #98C379;
  --color-accent-strong: #98C379;
  --color-accent-hover: color-mix(in srgb, #98C379 85%, #282C34);
  --color-accent-soft: color-mix(in srgb, #98C379 16%, #282C34);
  --color-on-accent: #21252B;
  --color-danger: #E06C75;
  --color-danger-hover: color-mix(in srgb, #E06C75 85%, #282C34);
  --color-danger-soft: color-mix(in srgb, #E06C75 16%, #282C34);
  --color-on-danger: #21252B;
  --color-cta: #D19A66;
  --color-cta-hover: color-mix(in srgb, #D19A66 85%, #282C34);
  --color-on-cta: #21252B;
  --color-info: #61AFEF;
  --color-highlight: #E5C07B;
  --color-highlight-soft: color-mix(in srgb, #E5C07B 16%, #282C34);
  --color-highlight-border: color-mix(in srgb, #E5C07B 45%, #282C34);
  --color-highlight-text: color-mix(in srgb, #E5C07B 80%, #ABB2BF);
  --color-highlight-deep: #E5C07B;
  --color-on-highlight: #21252B;
  --color-warning: #E5C07B;
  --color-warning-soft: color-mix(in srgb, #E5C07B 14%, #282C34);
  --color-warning-text: color-mix(in srgb, #E5C07B 80%, #ABB2BF);
  --color-focus: #ABB2BF;
  --color-field: #21252B;
  --purple-400: #C678DD;
  --purple-600: #C678DD;
  --purple-700: color-mix(in srgb, #C678DD 80%, #ABB2BF);
  --purple-50: color-mix(in srgb, #C678DD 14%, #282C34);
  --purple-100: color-mix(in srgb, #C678DD 20%, #282C34);
}

/* ---- GitHub Dark ---- */
html[data-ds-theme="github"] {
  color-scheme: dark;
  --color-bg: #0D1117;
  --color-surface: #161B22;
  --color-surface-muted: #21262D;
  --color-border: #30363D;
  --color-border-strong: #3D444D;
  --color-border-hover: #4D565F;
  --color-border-accent: #4D565F;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #E6EDF3;
  --color-text-muted: #9198A1;
  --color-text-subtle: #656C76;
  /* signature action: the theme's own color, not the system's */
  --color-action: #3FB950;
  --color-action-hover: color-mix(in srgb, #3FB950 85%, #161B22);
  --color-on-action: #0D1117;
  --color-accent: #3FB950;
  --color-accent-strong: #3FB950;
  --color-accent-hover: color-mix(in srgb, #3FB950 85%, #161B22);
  --color-accent-soft: color-mix(in srgb, #3FB950 16%, #161B22);
  --color-on-accent: #0D1117;
  --color-danger: #F85149;
  --color-danger-hover: color-mix(in srgb, #F85149 85%, #161B22);
  --color-danger-soft: color-mix(in srgb, #F85149 16%, #161B22);
  --color-on-danger: #0D1117;
  --color-cta: #DB6D28;
  --color-cta-hover: color-mix(in srgb, #DB6D28 85%, #161B22);
  --color-on-cta: #0D1117;
  --color-info: #58A6FF;
  --color-highlight: #D29922;
  --color-highlight-soft: color-mix(in srgb, #D29922 16%, #161B22);
  --color-highlight-border: color-mix(in srgb, #D29922 45%, #161B22);
  --color-highlight-text: color-mix(in srgb, #D29922 80%, #E6EDF3);
  --color-highlight-deep: #D29922;
  --color-on-highlight: #0D1117;
  --color-warning: #D29922;
  --color-warning-soft: color-mix(in srgb, #D29922 14%, #161B22);
  --color-warning-text: color-mix(in srgb, #D29922 80%, #E6EDF3);
  --color-focus: #E6EDF3;
  --color-field: #0D1117;
  --purple-400: #BC8CFF;
  --purple-600: #BC8CFF;
  --purple-700: color-mix(in srgb, #BC8CFF 80%, #E6EDF3);
  --purple-50: color-mix(in srgb, #BC8CFF 14%, #161B22);
  --purple-100: color-mix(in srgb, #BC8CFF 20%, #161B22);
}

/* ---- Solarized Dark ---- */
html[data-ds-theme="solarized"] {
  color-scheme: dark;
  --color-bg: #00212B;
  --color-surface: #002B36;
  --color-surface-muted: #073642;
  --color-border: #0E4250;
  --color-border-strong: #586E75;
  --color-border-hover: #657B83;
  --color-border-accent: #657B83;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #EEE8D5;
  --color-text-muted: #93A1A1;
  --color-text-subtle: #657B83;
  /* signature action: the theme's own color, not the system's */
  --color-action: #268BD2;
  --color-action-hover: color-mix(in srgb, #268BD2 85%, #002B36);
  --color-on-action: #FDF6E3;
  --color-accent: #859900;
  --color-accent-strong: #859900;
  --color-accent-hover: color-mix(in srgb, #859900 85%, #002B36);
  --color-accent-soft: color-mix(in srgb, #859900 16%, #002B36);
  --color-on-accent: #FDF6E3;
  --color-danger: #DC322F;
  --color-danger-hover: color-mix(in srgb, #DC322F 85%, #002B36);
  --color-danger-soft: color-mix(in srgb, #DC322F 16%, #002B36);
  --color-on-danger: #FDF6E3;
  --color-cta: #CB4B16;
  --color-cta-hover: color-mix(in srgb, #CB4B16 85%, #002B36);
  --color-on-cta: #FDF6E3;
  --color-info: #2AA198;
  --color-highlight: #B58900;
  --color-highlight-soft: color-mix(in srgb, #B58900 16%, #002B36);
  --color-highlight-border: color-mix(in srgb, #B58900 45%, #002B36);
  --color-highlight-text: color-mix(in srgb, #B58900 80%, #EEE8D5);
  --color-highlight-deep: #B58900;
  --color-on-highlight: #FDF6E3;
  --color-warning: #B58900;
  --color-warning-soft: color-mix(in srgb, #B58900 14%, #002B36);
  --color-warning-text: color-mix(in srgb, #B58900 80%, #EEE8D5);
  --color-focus: #EEE8D5;
  --color-field: #00212B;
  --purple-400: #6C71C4;
  --purple-600: #6C71C4;
  --purple-700: color-mix(in srgb, #6C71C4 80%, #EEE8D5);
  --purple-50: color-mix(in srgb, #6C71C4 14%, #002B36);
  --purple-100: color-mix(in srgb, #6C71C4 20%, #002B36);
}

/* ---- Terciopelo · boda champagne ---- */
html[data-ds-theme="terciopelo"] {
  color-scheme: dark;
  --color-bg: #120E0B;
  --color-surface: #1A1511;
  --color-surface-muted: #241D16;
  --color-border: #322A20;
  --color-border-strong: #453A2C;
  --color-border-hover: #5C4E3A;
  --color-border-accent: #5C4E3A;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #F3EBDC;
  --color-text-muted: #C4B49A;
  --color-text-subtle: #8A7A60;
  /* signature action: the theme's own color, not the system's */
  --color-action: #D4B573;
  --color-action-hover: color-mix(in srgb, #D4B573 85%, #1A1511);
  --color-on-action: #120E0B;
  --color-accent: #A8C5A0;
  --color-accent-strong: #A8C5A0;
  --color-accent-hover: color-mix(in srgb, #A8C5A0 85%, #1A1511);
  --color-accent-soft: color-mix(in srgb, #A8C5A0 16%, #1A1511);
  --color-on-accent: #120E0B;
  --color-danger: #D98A7E;
  --color-danger-hover: color-mix(in srgb, #D98A7E 85%, #1A1511);
  --color-danger-soft: color-mix(in srgb, #D98A7E 16%, #1A1511);
  --color-on-danger: #120E0B;
  --color-cta: #D4B573;
  --color-cta-hover: color-mix(in srgb, #D4B573 85%, #1A1511);
  --color-on-cta: #120E0B;
  --color-info: #9DB8E8;
  --color-highlight: #E8CF9E;
  --color-highlight-soft: color-mix(in srgb, #E8CF9E 16%, #1A1511);
  --color-highlight-border: color-mix(in srgb, #E8CF9E 45%, #1A1511);
  --color-highlight-text: color-mix(in srgb, #E8CF9E 80%, #F3EBDC);
  --color-highlight-deep: #E8CF9E;
  --color-on-highlight: #120E0B;
  --color-warning: #E8CF9E;
  --color-warning-soft: color-mix(in srgb, #E8CF9E 14%, #1A1511);
  --color-warning-text: color-mix(in srgb, #E8CF9E 80%, #F3EBDC);
  --color-focus: #F3EBDC;
  --color-field: #120E0B;
  --purple-400: #C9A9C8;
  --purple-600: #C9A9C8;
  --purple-700: color-mix(in srgb, #C9A9C8 80%, #F3EBDC);
  --purple-50: color-mix(in srgb, #C9A9C8 14%, #1A1511);
  --purple-100: color-mix(in srgb, #C9A9C8 20%, #1A1511);
}

/* ---- Sereno · boda medianoche ---- */
html[data-ds-theme="sereno"] {
  color-scheme: dark;
  --color-bg: #0A0F1E;
  --color-surface: #101830;
  --color-surface-muted: #182342;
  --color-border: #223052;
  --color-border-strong: #2E3F69;
  --color-border-hover: #42568A;
  --color-border-accent: #42568A;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #E8EDF7;
  --color-text-muted: #A9B6D3;
  --color-text-subtle: #6C7CA3;
  /* signature action: the theme's own color, not the system's */
  --color-action: #9DB8E8;
  --color-action-hover: color-mix(in srgb, #9DB8E8 85%, #101830);
  --color-on-action: #0A0F1E;
  --color-accent: #8FC7A8;
  --color-accent-strong: #8FC7A8;
  --color-accent-hover: color-mix(in srgb, #8FC7A8 85%, #101830);
  --color-accent-soft: color-mix(in srgb, #8FC7A8 16%, #101830);
  --color-on-accent: #0A0F1E;
  --color-danger: #E08585;
  --color-danger-hover: color-mix(in srgb, #E08585 85%, #101830);
  --color-danger-soft: color-mix(in srgb, #E08585 16%, #101830);
  --color-on-danger: #0A0F1E;
  --color-cta: #9DB8E8;
  --color-cta-hover: color-mix(in srgb, #9DB8E8 85%, #101830);
  --color-on-cta: #0A0F1E;
  --color-info: #9DB8E8;
  --color-highlight: #D9C08A;
  --color-highlight-soft: color-mix(in srgb, #D9C08A 16%, #101830);
  --color-highlight-border: color-mix(in srgb, #D9C08A 45%, #101830);
  --color-highlight-text: color-mix(in srgb, #D9C08A 80%, #E8EDF7);
  --color-highlight-deep: #D9C08A;
  --color-on-highlight: #0A0F1E;
  --color-warning: #D9C08A;
  --color-warning-soft: color-mix(in srgb, #D9C08A 14%, #101830);
  --color-warning-text: color-mix(in srgb, #D9C08A 80%, #E8EDF7);
  --color-focus: #E8EDF7;
  --color-field: #0A0F1E;
  --purple-400: #B3A6E3;
  --purple-600: #B3A6E3;
  --purple-700: color-mix(in srgb, #B3A6E3 80%, #E8EDF7);
  --purple-50: color-mix(in srgb, #B3A6E3 14%, #101830);
  --purple-100: color-mix(in srgb, #B3A6E3 20%, #101830);
}

/* ---- Jardín nocturno · boda boho ---- */
html[data-ds-theme="jardin"] {
  color-scheme: dark;
  --color-bg: #0D120E;
  --color-surface: #141B15;
  --color-surface-muted: #1C261E;
  --color-border: #2A382C;
  --color-border-strong: #3A4C3D;
  --color-border-hover: #526853;
  --color-border-accent: #526853;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #EFEEE2;
  --color-text-muted: #B7BEA8;
  --color-text-subtle: #7E8871;
  /* signature action: the theme's own color, not the system's */
  --color-action: #A8C5A0;
  --color-action-hover: color-mix(in srgb, #A8C5A0 85%, #141B15);
  --color-on-action: #0D120E;
  --color-accent: #A8C5A0;
  --color-accent-strong: #A8C5A0;
  --color-accent-hover: color-mix(in srgb, #A8C5A0 85%, #141B15);
  --color-accent-soft: color-mix(in srgb, #A8C5A0 16%, #141B15);
  --color-on-accent: #0D120E;
  --color-danger: #D98A7E;
  --color-danger-hover: color-mix(in srgb, #D98A7E 85%, #141B15);
  --color-danger-soft: color-mix(in srgb, #D98A7E 16%, #141B15);
  --color-on-danger: #0D120E;
  --color-cta: #A8C5A0;
  --color-cta-hover: color-mix(in srgb, #A8C5A0 85%, #141B15);
  --color-on-cta: #0D120E;
  --color-info: #8FB8C9;
  --color-highlight: #E3C889;
  --color-highlight-soft: color-mix(in srgb, #E3C889 16%, #141B15);
  --color-highlight-border: color-mix(in srgb, #E3C889 45%, #141B15);
  --color-highlight-text: color-mix(in srgb, #E3C889 80%, #EFEEE2);
  --color-highlight-deep: #E3C889;
  --color-on-highlight: #0D120E;
  --color-warning: #E3C889;
  --color-warning-soft: color-mix(in srgb, #E3C889 14%, #141B15);
  --color-warning-text: color-mix(in srgb, #E3C889 80%, #EFEEE2);
  --color-focus: #EFEEE2;
  --color-field: #0D120E;
  --purple-400: #B8A9C9;
  --purple-600: #B8A9C9;
  --purple-700: color-mix(in srgb, #B8A9C9 80%, #EFEEE2);
  --purple-50: color-mix(in srgb, #B8A9C9 14%, #141B15);
  --purple-100: color-mix(in srgb, #B8A9C9 20%, #141B15);
}

/* ---- Quince · rosa y oro ---- */
html[data-ds-theme="quince"] {
  color-scheme: dark;
  --color-bg: #170F16;
  --color-surface: #201621;
  --color-surface-muted: #2C1E2E;
  --color-border: #3C2A3E;
  --color-border-strong: #523A54;
  --color-border-hover: #6E4E70;
  --color-border-accent: #6E4E70;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #F7EBF2;
  --color-text-muted: #CFAEC4;
  --color-text-subtle: #96718B;
  /* signature action: the theme's own color, not the system's */
  --color-action: #F27BA6;
  --color-action-hover: color-mix(in srgb, #F27BA6 85%, #201621);
  --color-on-action: #170F16;
  --color-accent: #A8C9A8;
  --color-accent-strong: #A8C9A8;
  --color-accent-hover: color-mix(in srgb, #A8C9A8 85%, #201621);
  --color-accent-soft: color-mix(in srgb, #A8C9A8 16%, #201621);
  --color-on-accent: #170F16;
  --color-danger: #E85C5C;
  --color-danger-hover: color-mix(in srgb, #E85C5C 85%, #201621);
  --color-danger-soft: color-mix(in srgb, #E85C5C 16%, #201621);
  --color-on-danger: #170F16;
  --color-cta: #F27BA6;
  --color-cta-hover: color-mix(in srgb, #F27BA6 85%, #201621);
  --color-on-cta: #170F16;
  --color-info: #A9B8E8;
  --color-highlight: #E8B4A0;
  --color-highlight-soft: color-mix(in srgb, #E8B4A0 16%, #201621);
  --color-highlight-border: color-mix(in srgb, #E8B4A0 45%, #201621);
  --color-highlight-text: color-mix(in srgb, #E8B4A0 80%, #F7EBF2);
  --color-highlight-deep: #E8B4A0;
  --color-on-highlight: #170F16;
  --color-warning: #E8B4A0;
  --color-warning-soft: color-mix(in srgb, #E8B4A0 14%, #201621);
  --color-warning-text: color-mix(in srgb, #E8B4A0 80%, #F7EBF2);
  --color-focus: #F7EBF2;
  --color-field: #170F16;
  --purple-400: #C9A0E8;
  --purple-600: #C9A0E8;
  --purple-700: color-mix(in srgb, #C9A0E8 80%, #F7EBF2);
  --purple-50: color-mix(in srgb, #C9A0E8 14%, #201621);
  --purple-100: color-mix(in srgb, #C9A0E8 20%, #201621);
}

/* ---- Neón · fiesta ---- */
html[data-ds-theme="neon"] {
  color-scheme: dark;
  --color-bg: #0B0913;
  --color-surface: #131020;
  --color-surface-muted: #1C1730;
  --color-border: #2A2347;
  --color-border-strong: #3A3060;
  --color-border-hover: #524482;
  --color-border-accent: #524482;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #F2EFFF;
  --color-text-muted: #B5ACD9;
  --color-text-subtle: #7C71A8;
  /* signature action: the theme's own color, not the system's */
  --color-action: #FF4FD8;
  --color-action-hover: color-mix(in srgb, #FF4FD8 85%, #131020);
  --color-on-action: #0B0913;
  --color-accent: #3EFFB2;
  --color-accent-strong: #3EFFB2;
  --color-accent-hover: color-mix(in srgb, #3EFFB2 85%, #131020);
  --color-accent-soft: color-mix(in srgb, #3EFFB2 16%, #131020);
  --color-on-accent: #0B0913;
  --color-danger: #FF5F56;
  --color-danger-hover: color-mix(in srgb, #FF5F56 85%, #131020);
  --color-danger-soft: color-mix(in srgb, #FF5F56 16%, #131020);
  --color-on-danger: #0B0913;
  --color-cta: #3EE8FF;
  --color-cta-hover: color-mix(in srgb, #3EE8FF 85%, #131020);
  --color-on-cta: #0B0913;
  --color-info: #3EE8FF;
  --color-highlight: #FFE14F;
  --color-highlight-soft: color-mix(in srgb, #FFE14F 16%, #131020);
  --color-highlight-border: color-mix(in srgb, #FFE14F 45%, #131020);
  --color-highlight-text: color-mix(in srgb, #FFE14F 80%, #F2EFFF);
  --color-highlight-deep: #FFE14F;
  --color-on-highlight: #0B0913;
  --color-warning: #FFE14F;
  --color-warning-soft: color-mix(in srgb, #FFE14F 14%, #131020);
  --color-warning-text: color-mix(in srgb, #FFE14F 80%, #F2EFFF);
  --color-focus: #F2EFFF;
  --color-field: #0B0913;
  --purple-400: #9D8CFF;
  --purple-600: #9D8CFF;
  --purple-700: color-mix(in srgb, #9D8CFF 80%, #F2EFFF);
  --purple-50: color-mix(in srgb, #9D8CFF 14%, #131020);
  --purple-100: color-mix(in srgb, #9D8CFF 20%, #131020);
}

/* ---- Vino · cena borgoña ---- */
html[data-ds-theme="vino"] {
  color-scheme: dark;
  --color-bg: #150C0F;
  --color-surface: #1E1216;
  --color-surface-muted: #2A1A1F;
  --color-border: #3A252C;
  --color-border-strong: #50333C;
  --color-border-hover: #6B4550;
  --color-border-accent: #6B4550;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #F5E9E6;
  --color-text-muted: #CBA9A6;
  --color-text-subtle: #91706E;
  /* signature action: the theme's own color, not the system's */
  --color-action: #CE6E85;
  --color-action-hover: color-mix(in srgb, #CE6E85 85%, #1E1216);
  --color-on-action: #150C0F;
  --color-accent: #A0BFA0;
  --color-accent-strong: #A0BFA0;
  --color-accent-hover: color-mix(in srgb, #A0BFA0 85%, #1E1216);
  --color-accent-soft: color-mix(in srgb, #A0BFA0 16%, #1E1216);
  --color-on-accent: #150C0F;
  --color-danger: #E06060;
  --color-danger-hover: color-mix(in srgb, #E06060 85%, #1E1216);
  --color-danger-soft: color-mix(in srgb, #E06060 16%, #1E1216);
  --color-on-danger: #150C0F;
  --color-cta: #CE6E85;
  --color-cta-hover: color-mix(in srgb, #CE6E85 85%, #1E1216);
  --color-on-cta: #150C0F;
  --color-info: #A0A8D9;
  --color-highlight: #D9AE6E;
  --color-highlight-soft: color-mix(in srgb, #D9AE6E 16%, #1E1216);
  --color-highlight-border: color-mix(in srgb, #D9AE6E 45%, #1E1216);
  --color-highlight-text: color-mix(in srgb, #D9AE6E 80%, #F5E9E6);
  --color-highlight-deep: #D9AE6E;
  --color-on-highlight: #150C0F;
  --color-warning: #D9AE6E;
  --color-warning-soft: color-mix(in srgb, #D9AE6E 14%, #1E1216);
  --color-warning-text: color-mix(in srgb, #D9AE6E 80%, #F5E9E6);
  --color-focus: #F5E9E6;
  --color-field: #150C0F;
  --purple-400: #C293C2;
  --purple-600: #C293C2;
  --purple-700: color-mix(in srgb, #C293C2 80%, #F5E9E6);
  --purple-50: color-mix(in srgb, #C293C2 14%, #1E1216);
  --purple-100: color-mix(in srgb, #C293C2 20%, #1E1216);
}

/* ---- Aurora · celestial ---- */
html[data-ds-theme="aurora"] {
  color-scheme: dark;
  --color-bg: #070B18;
  --color-surface: #0E1428;
  --color-surface-muted: #151E3A;
  --color-border: #20294D;
  --color-border-strong: #2C3866;
  --color-border-hover: #404E88;
  --color-border-accent: #404E88;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #EAF0FA;
  --color-text-muted: #A6B4D6;
  --color-text-subtle: #6A79A3;
  /* signature action: the theme's own color, not the system's */
  --color-action: #7FE8C1;
  --color-action-hover: color-mix(in srgb, #7FE8C1 85%, #0E1428);
  --color-on-action: #070B18;
  --color-accent: #7FE8C1;
  --color-accent-strong: #7FE8C1;
  --color-accent-hover: color-mix(in srgb, #7FE8C1 85%, #0E1428);
  --color-accent-soft: color-mix(in srgb, #7FE8C1 16%, #0E1428);
  --color-on-accent: #070B18;
  --color-danger: #FF7A8A;
  --color-danger-hover: color-mix(in srgb, #FF7A8A 85%, #0E1428);
  --color-danger-soft: color-mix(in srgb, #FF7A8A 16%, #0E1428);
  --color-on-danger: #070B18;
  --color-cta: #7FE8C1;
  --color-cta-hover: color-mix(in srgb, #7FE8C1 85%, #0E1428);
  --color-on-cta: #070B18;
  --color-info: #8CB8FF;
  --color-highlight: #FFD98A;
  --color-highlight-soft: color-mix(in srgb, #FFD98A 16%, #0E1428);
  --color-highlight-border: color-mix(in srgb, #FFD98A 45%, #0E1428);
  --color-highlight-text: color-mix(in srgb, #FFD98A 80%, #EAF0FA);
  --color-highlight-deep: #FFD98A;
  --color-on-highlight: #070B18;
  --color-warning: #FFD98A;
  --color-warning-soft: color-mix(in srgb, #FFD98A 14%, #0E1428);
  --color-warning-text: color-mix(in srgb, #FFD98A 80%, #EAF0FA);
  --color-focus: #EAF0FA;
  --color-field: #070B18;
  --purple-400: #9D8CFF;
  --purple-600: #9D8CFF;
  --purple-700: color-mix(in srgb, #9D8CFF 80%, #EAF0FA);
  --purple-50: color-mix(in srgb, #9D8CFF 14%, #0E1428);
  --purple-100: color-mix(in srgb, #9D8CFF 20%, #0E1428);
}

/* ---- Confeti · cumpleaños ---- */
html[data-ds-theme="confeti"] {
  color-scheme: dark;
  --color-bg: #141216;
  --color-surface: #1C1A20;
  --color-surface-muted: #26232B;
  --color-border: #34303C;
  --color-border-strong: #453F4E;
  --color-border-hover: #5C5468;
  --color-border-accent: #5C5468;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #F5F2F7;
  --color-text-muted: #B3ACBE;
  --color-text-subtle: #7A7286;
  /* signature action: the theme's own color, not the system's */
  --color-action: #FF6B6B;
  --color-action-hover: color-mix(in srgb, #FF6B6B 85%, #1C1A20);
  --color-on-action: #141216;
  --color-accent: #6BCB77;
  --color-accent-strong: #6BCB77;
  --color-accent-hover: color-mix(in srgb, #6BCB77 85%, #1C1A20);
  --color-accent-soft: color-mix(in srgb, #6BCB77 16%, #1C1A20);
  --color-on-accent: #141216;
  --color-danger: #E84040;
  --color-danger-hover: color-mix(in srgb, #E84040 85%, #1C1A20);
  --color-danger-soft: color-mix(in srgb, #E84040 16%, #1C1A20);
  --color-on-danger: #F5F2F7;
  --color-cta: #4ECDC4;
  --color-cta-hover: color-mix(in srgb, #4ECDC4 85%, #1C1A20);
  --color-on-cta: #141216;
  --color-info: #4D96FF;
  --color-highlight: #FFD93D;
  --color-highlight-soft: color-mix(in srgb, #FFD93D 16%, #1C1A20);
  --color-highlight-border: color-mix(in srgb, #FFD93D 45%, #1C1A20);
  --color-highlight-text: color-mix(in srgb, #FFD93D 80%, #F5F2F7);
  --color-highlight-deep: #FFD93D;
  --color-on-highlight: #141216;
  --color-warning: #FFD93D;
  --color-warning-soft: color-mix(in srgb, #FFD93D 14%, #1C1A20);
  --color-warning-text: color-mix(in srgb, #FFD93D 80%, #F5F2F7);
  --color-focus: #F5F2F7;
  --color-field: #141216;
  --purple-400: #B39DDB;
  --purple-600: #B39DDB;
  --purple-700: color-mix(in srgb, #B39DDB 80%, #F5F2F7);
  --purple-50: color-mix(in srgb, #B39DDB 14%, #1C1A20);
  --purple-100: color-mix(in srgb, #B39DDB 20%, #1C1A20);
}

/* ---- Piñata · fiesta latina ---- */
html[data-ds-theme="pinata"] {
  color-scheme: dark;
  --color-bg: #1A0E14;
  --color-surface: #241219;
  --color-surface-muted: #311823;
  --color-border: #44202F;
  --color-border-strong: #5C2B40;
  --color-border-hover: #7A3A56;
  --color-border-accent: #7A3A56;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #FBEFF2;
  --color-text-muted: #D9A8BC;
  --color-text-subtle: #A06B84;
  /* signature action: the theme's own color, not the system's */
  --color-action: #F0459C;
  --color-action-hover: color-mix(in srgb, #F0459C 85%, #241219);
  --color-on-action: #1A0E14;
  --color-accent: #8BD450;
  --color-accent-strong: #8BD450;
  --color-accent-hover: color-mix(in srgb, #8BD450 85%, #241219);
  --color-accent-soft: color-mix(in srgb, #8BD450 16%, #241219);
  --color-on-accent: #1A0E14;
  --color-danger: #FF4B3E;
  --color-danger-hover: color-mix(in srgb, #FF4B3E 85%, #241219);
  --color-danger-soft: color-mix(in srgb, #FF4B3E 16%, #241219);
  --color-on-danger: #1A0E14;
  --color-cta: #FF9F1C;
  --color-cta-hover: color-mix(in srgb, #FF9F1C 85%, #241219);
  --color-on-cta: #1A0E14;
  --color-info: #4FC3F7;
  --color-highlight: #FFD23F;
  --color-highlight-soft: color-mix(in srgb, #FFD23F 16%, #241219);
  --color-highlight-border: color-mix(in srgb, #FFD23F 45%, #241219);
  --color-highlight-text: color-mix(in srgb, #FFD23F 80%, #FBEFF2);
  --color-highlight-deep: #FFD23F;
  --color-on-highlight: #1A0E14;
  --color-warning: #FFD23F;
  --color-warning-soft: color-mix(in srgb, #FFD23F 14%, #241219);
  --color-warning-text: color-mix(in srgb, #FFD23F 80%, #FBEFF2);
  --color-focus: #FBEFF2;
  --color-field: #1A0E14;
  --purple-400: #C77DFF;
  --purple-600: #C77DFF;
  --purple-700: color-mix(in srgb, #C77DFF 80%, #FBEFF2);
  --purple-50: color-mix(in srgb, #C77DFF 14%, #241219);
  --purple-100: color-mix(in srgb, #C77DFF 20%, #241219);
}

/* ---- Caramelo · cumple dulce ---- */
html[data-ds-theme="caramelo"] {
  color-scheme: dark;
  --color-bg: #16100F;
  --color-surface: #1F1715;
  --color-surface-muted: #2B201D;
  --color-border: #3D2D28;
  --color-border-strong: #523D36;
  --color-border-hover: #6E534A;
  --color-border-accent: #6E534A;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #FBF0EA;
  --color-text-muted: #CBB3A8;
  --color-text-subtle: #937D72;
  /* signature action: the theme's own color, not the system's */
  --color-action: #FF8FB1;
  --color-action-hover: color-mix(in srgb, #FF8FB1 85%, #1F1715);
  --color-on-action: #16100F;
  --color-accent: #7FE0C3;
  --color-accent-strong: #7FE0C3;
  --color-accent-hover: color-mix(in srgb, #7FE0C3 85%, #1F1715);
  --color-accent-soft: color-mix(in srgb, #7FE0C3 16%, #1F1715);
  --color-on-accent: #16100F;
  --color-danger: #F06262;
  --color-danger-hover: color-mix(in srgb, #F06262 85%, #1F1715);
  --color-danger-soft: color-mix(in srgb, #F06262 16%, #1F1715);
  --color-on-danger: #16100F;
  --color-cta: #7FE0C3;
  --color-cta-hover: color-mix(in srgb, #7FE0C3 85%, #1F1715);
  --color-on-cta: #16100F;
  --color-info: #90CAF9;
  --color-highlight: #FFD59E;
  --color-highlight-soft: color-mix(in srgb, #FFD59E 16%, #1F1715);
  --color-highlight-border: color-mix(in srgb, #FFD59E 45%, #1F1715);
  --color-highlight-text: color-mix(in srgb, #FFD59E 80%, #FBF0EA);
  --color-highlight-deep: #FFD59E;
  --color-on-highlight: #16100F;
  --color-warning: #FFD59E;
  --color-warning-soft: color-mix(in srgb, #FFD59E 14%, #1F1715);
  --color-warning-text: color-mix(in srgb, #FFD59E 80%, #FBF0EA);
  --color-focus: #FBF0EA;
  --color-field: #16100F;
  --purple-400: #D0A9E8;
  --purple-600: #D0A9E8;
  --purple-700: color-mix(in srgb, #D0A9E8 80%, #FBF0EA);
  --purple-50: color-mix(in srgb, #D0A9E8 14%, #1F1715);
  --purple-100: color-mix(in srgb, #D0A9E8 20%, #1F1715);
}

/* ---- Mariposa · XV lila ---- */
html[data-ds-theme="mariposa"] {
  color-scheme: dark;
  --color-bg: #120F1C;
  --color-surface: #1A1528;
  --color-surface-muted: #241D38;
  --color-border: #342A50;
  --color-border-strong: #463969;
  --color-border-hover: #604F8C;
  --color-border-accent: #604F8C;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #F4F0FB;
  --color-text-muted: #BCAFD9;
  --color-text-subtle: #82749F;
  /* signature action: the theme's own color, not the system's */
  --color-action: #C9A0F0;
  --color-action-hover: color-mix(in srgb, #C9A0F0 85%, #1A1528);
  --color-on-action: #120F1C;
  --color-accent: #A0D9B8;
  --color-accent-strong: #A0D9B8;
  --color-accent-hover: color-mix(in srgb, #A0D9B8 85%, #1A1528);
  --color-accent-soft: color-mix(in srgb, #A0D9B8 16%, #1A1528);
  --color-on-accent: #120F1C;
  --color-danger: #E87C8C;
  --color-danger-hover: color-mix(in srgb, #E87C8C 85%, #1A1528);
  --color-danger-soft: color-mix(in srgb, #E87C8C 16%, #1A1528);
  --color-on-danger: #120F1C;
  --color-cta: #F0A0D0;
  --color-cta-hover: color-mix(in srgb, #F0A0D0 85%, #1A1528);
  --color-on-cta: #120F1C;
  --color-info: #A0B8F0;
  --color-highlight: #D9DCE8;
  --color-highlight-soft: color-mix(in srgb, #D9DCE8 16%, #1A1528);
  --color-highlight-border: color-mix(in srgb, #D9DCE8 45%, #1A1528);
  --color-highlight-text: color-mix(in srgb, #D9DCE8 80%, #F4F0FB);
  --color-highlight-deep: #D9DCE8;
  --color-on-highlight: #120F1C;
  --color-warning: #D9DCE8;
  --color-warning-soft: color-mix(in srgb, #D9DCE8 14%, #1A1528);
  --color-warning-text: color-mix(in srgb, #D9DCE8 80%, #F4F0FB);
  --color-focus: #F4F0FB;
  --color-field: #120F1C;
  --purple-400: #C9A0F0;
  --purple-600: #C9A0F0;
  --purple-700: color-mix(in srgb, #C9A0F0 80%, #F4F0FB);
  --purple-50: color-mix(in srgb, #C9A0F0 14%, #1A1528);
  --purple-100: color-mix(in srgb, #C9A0F0 20%, #1A1528);
}

/* ---- Princesa · XV azul real ---- */
html[data-ds-theme="princesa"] {
  color-scheme: dark;
  --color-bg: #0A0F22;
  --color-surface: #101838;
  --color-surface-muted: #16224E;
  --color-border: #22306A;
  --color-border-strong: #2E3F88;
  --color-border-hover: #4256AE;
  --color-border-accent: #4256AE;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #EDF1FC;
  --color-text-muted: #A9B6DE;
  --color-text-subtle: #6C7CAF;
  /* signature action: the theme's own color, not the system's */
  --color-action: #6E8EF5;
  --color-action-hover: color-mix(in srgb, #6E8EF5 85%, #101838);
  --color-on-action: #0A0F22;
  --color-accent: #8FD9B8;
  --color-accent-strong: #8FD9B8;
  --color-accent-hover: color-mix(in srgb, #8FD9B8 85%, #101838);
  --color-accent-soft: color-mix(in srgb, #8FD9B8 16%, #101838);
  --color-on-accent: #0A0F22;
  --color-danger: #E87C8C;
  --color-danger-hover: color-mix(in srgb, #E87C8C 85%, #101838);
  --color-danger-soft: color-mix(in srgb, #E87C8C 16%, #101838);
  --color-on-danger: #0A0F22;
  --color-cta: #E8D080;
  --color-cta-hover: color-mix(in srgb, #E8D080 85%, #101838);
  --color-on-cta: #0A0F22;
  --color-info: #6E8EF5;
  --color-highlight: #E8D080;
  --color-highlight-soft: color-mix(in srgb, #E8D080 16%, #101838);
  --color-highlight-border: color-mix(in srgb, #E8D080 45%, #101838);
  --color-highlight-text: color-mix(in srgb, #E8D080 80%, #EDF1FC);
  --color-highlight-deep: #E8D080;
  --color-on-highlight: #0A0F22;
  --color-warning: #E8D080;
  --color-warning-soft: color-mix(in srgb, #E8D080 14%, #101838);
  --color-warning-text: color-mix(in srgb, #E8D080 80%, #EDF1FC);
  --color-focus: #EDF1FC;
  --color-field: #0A0F22;
  --purple-400: #B9A0F0;
  --purple-600: #B9A0F0;
  --purple-700: color-mix(in srgb, #B9A0F0 80%, #EDF1FC);
  --purple-50: color-mix(in srgb, #B9A0F0 14%, #101838);
  --purple-100: color-mix(in srgb, #B9A0F0 20%, #101838);
}

/* ---- Synthwave · retro neón ---- */
html[data-ds-theme="synthwave"] {
  color-scheme: dark;
  --color-bg: #120B22;
  --color-surface: #1A1032;
  --color-surface-muted: #241745;
  --color-border: #332059;
  --color-border-strong: #452C77;
  --color-border-hover: #5F3D9E;
  --color-border-accent: #5F3D9E;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #F4EEFF;
  --color-text-muted: #B9A8E0;
  --color-text-subtle: #7E6BAB;
  /* signature action: the theme's own color, not the system's */
  --color-action: #FF4F9A;
  --color-action-hover: color-mix(in srgb, #FF4F9A 85%, #1A1032);
  --color-on-action: #120B22;
  --color-accent: #3EE87F;
  --color-accent-strong: #3EE87F;
  --color-accent-hover: color-mix(in srgb, #3EE87F 85%, #1A1032);
  --color-accent-soft: color-mix(in srgb, #3EE87F 16%, #1A1032);
  --color-on-accent: #120B22;
  --color-danger: #FF4560;
  --color-danger-hover: color-mix(in srgb, #FF4560 85%, #1A1032);
  --color-danger-soft: color-mix(in srgb, #FF4560 16%, #1A1032);
  --color-on-danger: #120B22;
  --color-cta: #2DE2E6;
  --color-cta-hover: color-mix(in srgb, #2DE2E6 85%, #1A1032);
  --color-on-cta: #120B22;
  --color-info: #2DE2E6;
  --color-highlight: #FFB84D;
  --color-highlight-soft: color-mix(in srgb, #FFB84D 16%, #1A1032);
  --color-highlight-border: color-mix(in srgb, #FFB84D 45%, #1A1032);
  --color-highlight-text: color-mix(in srgb, #FFB84D 80%, #F4EEFF);
  --color-highlight-deep: #FFB84D;
  --color-on-highlight: #120B22;
  --color-warning: #FFB84D;
  --color-warning-soft: color-mix(in srgb, #FFB84D 14%, #1A1032);
  --color-warning-text: color-mix(in srgb, #FFB84D 80%, #F4EEFF);
  --color-focus: #F4EEFF;
  --color-field: #120B22;
  --purple-400: #B388FF;
  --purple-600: #B388FF;
  --purple-700: color-mix(in srgb, #B388FF 80%, #F4EEFF);
  --purple-50: color-mix(in srgb, #B388FF 14%, #1A1032);
  --purple-100: color-mix(in srgb, #B388FF 20%, #1A1032);
}

/* ---- UV · blacklight ---- */
html[data-ds-theme="uv"] {
  color-scheme: dark;
  --color-bg: #0C0716;
  --color-surface: #140C24;
  --color-surface-muted: #1D1234;
  --color-border: #2C1C4E;
  --color-border-strong: #3D2868;
  --color-border-hover: #553A8E;
  --color-border-accent: #553A8E;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #F0EAFF;
  --color-text-muted: #B4A3E0;
  --color-text-subtle: #7A68A8;
  /* signature action: the theme's own color, not the system's */
  --color-action: #B266FF;
  --color-action-hover: color-mix(in srgb, #B266FF 85%, #140C24);
  --color-on-action: #0C0716;
  --color-accent: #7CFF66;
  --color-accent-strong: #7CFF66;
  --color-accent-hover: color-mix(in srgb, #7CFF66 85%, #140C24);
  --color-accent-soft: color-mix(in srgb, #7CFF66 16%, #140C24);
  --color-on-accent: #0C0716;
  --color-danger: #FF5566;
  --color-danger-hover: color-mix(in srgb, #FF5566 85%, #140C24);
  --color-danger-soft: color-mix(in srgb, #FF5566 16%, #140C24);
  --color-on-danger: #0C0716;
  --color-cta: #C6FF4D;
  --color-cta-hover: color-mix(in srgb, #C6FF4D 85%, #140C24);
  --color-on-cta: #0C0716;
  --color-info: #66C6FF;
  --color-highlight: #FF66E0;
  --color-highlight-soft: color-mix(in srgb, #FF66E0 16%, #140C24);
  --color-highlight-border: color-mix(in srgb, #FF66E0 45%, #140C24);
  --color-highlight-text: color-mix(in srgb, #FF66E0 80%, #F0EAFF);
  --color-highlight-deep: #FF66E0;
  --color-on-highlight: #0C0716;
  --color-warning: #FF66E0;
  --color-warning-soft: color-mix(in srgb, #FF66E0 14%, #140C24);
  --color-warning-text: color-mix(in srgb, #FF66E0 80%, #F0EAFF);
  --color-focus: #F0EAFF;
  --color-field: #0C0716;
  --purple-400: #B266FF;
  --purple-600: #B266FF;
  --purple-700: color-mix(in srgb, #B266FF 80%, #F0EAFF);
  --purple-50: color-mix(in srgb, #B266FF 14%, #140C24);
  --purple-100: color-mix(in srgb, #B266FF 20%, #140C24);
}

/* ---- Estrobo · boliche láser ---- */
html[data-ds-theme="estrobo"] {
  color-scheme: dark;
  --color-bg: #060608;
  --color-surface: #0E0E12;
  --color-surface-muted: #16161C;
  --color-border: #232330;
  --color-border-strong: #303044;
  --color-border-hover: #44445E;
  --color-border-accent: #44445E;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #F2F2F8;
  --color-text-muted: #A8A8C0;
  --color-text-subtle: #6E6E88;
  /* signature action: the theme's own color, not the system's */
  --color-action: #00E5FF;
  --color-action-hover: color-mix(in srgb, #00E5FF 85%, #0E0E12);
  --color-on-action: #060608;
  --color-accent: #39FF88;
  --color-accent-strong: #39FF88;
  --color-accent-hover: color-mix(in srgb, #39FF88 85%, #0E0E12);
  --color-accent-soft: color-mix(in srgb, #39FF88 16%, #0E0E12);
  --color-on-accent: #060608;
  --color-danger: #FF3355;
  --color-danger-hover: color-mix(in srgb, #FF3355 85%, #0E0E12);
  --color-danger-soft: color-mix(in srgb, #FF3355 16%, #0E0E12);
  --color-on-danger: #060608;
  --color-cta: #FF2FB0;
  --color-cta-hover: color-mix(in srgb, #FF2FB0 85%, #0E0E12);
  --color-on-cta: #060608;
  --color-info: #00E5FF;
  --color-highlight: #FFEB3B;
  --color-highlight-soft: color-mix(in srgb, #FFEB3B 16%, #0E0E12);
  --color-highlight-border: color-mix(in srgb, #FFEB3B 45%, #0E0E12);
  --color-highlight-text: color-mix(in srgb, #FFEB3B 80%, #F2F2F8);
  --color-highlight-deep: #FFEB3B;
  --color-on-highlight: #060608;
  --color-warning: #FFEB3B;
  --color-warning-soft: color-mix(in srgb, #FFEB3B 14%, #0E0E12);
  --color-warning-text: color-mix(in srgb, #FFEB3B 80%, #F2F2F8);
  --color-focus: #F2F2F8;
  --color-field: #060608;
  --purple-400: #A64DFF;
  --purple-600: #A64DFF;
  --purple-700: color-mix(in srgb, #A64DFF 80%, #F2F2F8);
  --purple-50: color-mix(in srgb, #A64DFF 14%, #0E0E12);
  --purple-100: color-mix(in srgb, #A64DFF 20%, #0E0E12);
}

/* ---- Tropical · fiesta playa ---- */
html[data-ds-theme="tropical"] {
  color-scheme: dark;
  --color-bg: #071614;
  --color-surface: #0D211E;
  --color-surface-muted: #142E2A;
  --color-border: #1F423C;
  --color-border-strong: #2C584F;
  --color-border-hover: #3E786C;
  --color-border-accent: #3E786C;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #EDFAF5;
  --color-text-muted: #A3CCC0;
  --color-text-subtle: #6A968A;
  /* signature action: the theme's own color, not the system's */
  --color-action: #FF7059;
  --color-action-hover: color-mix(in srgb, #FF7059 85%, #0D211E);
  --color-on-action: #071614;
  --color-accent: #4FE0A3;
  --color-accent-strong: #4FE0A3;
  --color-accent-hover: color-mix(in srgb, #4FE0A3 85%, #0D211E);
  --color-accent-soft: color-mix(in srgb, #4FE0A3 16%, #0D211E);
  --color-on-accent: #071614;
  --color-danger: #FF5252;
  --color-danger-hover: color-mix(in srgb, #FF5252 85%, #0D211E);
  --color-danger-soft: color-mix(in srgb, #FF5252 16%, #0D211E);
  --color-on-danger: #071614;
  --color-cta: #A3E84D;
  --color-cta-hover: color-mix(in srgb, #A3E84D 85%, #0D211E);
  --color-on-cta: #071614;
  --color-info: #4FC9E0;
  --color-highlight: #FFD166;
  --color-highlight-soft: color-mix(in srgb, #FFD166 16%, #0D211E);
  --color-highlight-border: color-mix(in srgb, #FFD166 45%, #0D211E);
  --color-highlight-text: color-mix(in srgb, #FFD166 80%, #EDFAF5);
  --color-highlight-deep: #FFD166;
  --color-on-highlight: #071614;
  --color-warning: #FFD166;
  --color-warning-soft: color-mix(in srgb, #FFD166 14%, #0D211E);
  --color-warning-text: color-mix(in srgb, #FFD166 80%, #EDFAF5);
  --color-focus: #EDFAF5;
  --color-field: #071614;
  --purple-400: #C990E0;
  --purple-600: #C990E0;
  --purple-700: color-mix(in srgb, #C990E0 80%, #EDFAF5);
  --purple-50: color-mix(in srgb, #C990E0 14%, #0D211E);
  --purple-100: color-mix(in srgb, #C990E0 20%, #0D211E);
}

/* ---- Dorado · gala / año nuevo ---- */
html[data-ds-theme="dorado"] {
  color-scheme: dark;
  --color-bg: #0B0B0C;
  --color-surface: #131314;
  --color-surface-muted: #1C1C1E;
  --color-border: #2B2B2E;
  --color-border-strong: #3C3C40;
  --color-border-hover: #545458;
  --color-border-accent: #545458;
  --color-backdrop: rgba(0, 0, 0, 0.65);
  --color-text: #F5F1E6;
  --color-text-muted: #C2BAA3;
  --color-text-subtle: #878068;
  /* signature action: the theme's own color, not the system's */
  --color-action: #F0C75E;
  --color-action-hover: color-mix(in srgb, #F0C75E 85%, #131314);
  --color-on-action: #0B0B0C;
  --color-accent: #A8C5A0;
  --color-accent-strong: #A8C5A0;
  --color-accent-hover: color-mix(in srgb, #A8C5A0 85%, #131314);
  --color-accent-soft: color-mix(in srgb, #A8C5A0 16%, #131314);
  --color-on-accent: #0B0B0C;
  --color-danger: #D98A7E;
  --color-danger-hover: color-mix(in srgb, #D98A7E 85%, #131314);
  --color-danger-soft: color-mix(in srgb, #D98A7E 16%, #131314);
  --color-on-danger: #0B0B0C;
  --color-cta: #F0C75E;
  --color-cta-hover: color-mix(in srgb, #F0C75E 85%, #131314);
  --color-on-cta: #0B0B0C;
  --color-info: #C9D0DC;
  --color-highlight: #F0DFA0;
  --color-highlight-soft: color-mix(in srgb, #F0DFA0 16%, #131314);
  --color-highlight-border: color-mix(in srgb, #F0DFA0 45%, #131314);
  --color-highlight-text: color-mix(in srgb, #F0DFA0 80%, #F5F1E6);
  --color-highlight-deep: #F0DFA0;
  --color-on-highlight: #0B0B0C;
  --color-warning: #F0DFA0;
  --color-warning-soft: color-mix(in srgb, #F0DFA0 14%, #131314);
  --color-warning-text: color-mix(in srgb, #F0DFA0 80%, #F5F1E6);
  --color-focus: #F5F1E6;
  --color-field: #0B0B0C;
  --purple-400: #C9A9C8;
  --purple-600: #C9A9C8;
  --purple-700: color-mix(in srgb, #C9A9C8 80%, #F5F1E6);
  --purple-50: color-mix(in srgb, #C9A9C8 14%, #131314);
  --purple-100: color-mix(in srgb, #C9A9C8 20%, #131314);
}

/* ============================================================
   ROLES DERIVADOS — valen para los dieciocho temas
   ============================================================

   El relato completo de cada uno esta en docs/ESTADO.md; aca va lo que hace
   falta para no romperlos.

   VAN EN EL `body` Y NO EN EL `html`. Sobre `html` -donde cada tema declara sus
   colores- leer una ficha dentro de su propia definicion es un CICLO, y el
   navegador descarta la declaracion entera: la ficha queda sin valor en los
   dieciocho temas. En el `body` esa lectura resuelve al valor heredado.
   (Y el ejemplo no se escribe con la sintaxis de verdad a proposito: hay una
   guardia que lee las fichas que las hojas piden, y la leeria de un comentario.)

   Y SE DERIVAN, no se escriben: dieciocho valores a mano perderian el tono
   propio de cada tema -hay tibios y hay frios- y serian dieciocho lugares donde
   volver a equivocarse.

   `--color-field`: los veintiocho temas ya lo declaran IGUAL A SU `--color-bg`.
   Eso no es un olvido, es un estilo -el campo es el color de la pagina recortado
   en la tarjeta- pero se lee como un agujero y no como un control con relleno.
   La hundida lo levanta medio paso. */
body {
  /* El borde de "aca estas": resalta sin gritar. La accion pura resultaba
     demasiado y bajarla mas la volvia un borde normal. */
  --color-border-current: color-mix(in srgb, var(--color-action) 28%, var(--color-border));

  /* Decorativo, para lo que ya tiene relleno propio: ahi el borde solo delimita.
     Lo consume SOLO eso; mover `--color-border` entero se probo y se revirtio. */
  --color-border-soft: color-mix(in srgb, var(--color-border) 65%, var(--color-surface));

  /* Media tinta entre el fondo y la superficie. La piden las cajas de los pasos,
     las filas del listado y los campos, asi que la formula vive aca y no en tres
     hojas. Media y no un escalon entero: lo que va ENCIMA es `--color-surface`,
     y si llegara ahi las dos capas se igualarian. */
  --color-surface-sunken: color-mix(in srgb, var(--color-surface) 55%, var(--color-bg));
  --color-field: var(--color-surface-sunken);
}

/* --- fundamentos.css --- */
/* ============================================================
   fundamentos.css — la base del rediseno

   Se monta ENCIMA de /ds/bundle.css. El kit trae los tokens; esto
   fija las decisiones que el kit deja abiertas y que en el codigo
   anterior nunca se fijaron, que es de donde salio la incoherencia:
   cada pantalla elegia su propio espaciado y su propio tamano.

   Tres decisiones y nada mas:
     1. El ritmo es de 8. Sin excepciones fuera de un control.
     2. Los tamanos de texto son SIETE y cada uno tiene un oficio.
     3. La pantalla mide 1200 y esta centrada.

   Ademas parcha tres fugas del kit: hay componentes que consumen
   PRIMITIVAS (--neutral-50, --green-200) en vez de fichas semanticas,
   asi que bajo un tema oscuro salen casi blancos. Estan marcadas.
   ============================================================ */

:root {
  /* --- 1. RITMO ---------------------------------------------------------
     Multiplos de 8, y el nombre ES la medida: si no lo podes nombrar, no
     lo podes usar. Es lo que impide que aparezcan 13px "porque quedaba
     mejor".

     Ocho escalones y nada mas. Seis y doce son las DOS excepciones y existen
     para lo que pasa dentro de un control: el hueco entre un icono y su texto
     no puede medir ocho. Fuera de un control no se usan.

       e8    dentro de un grupo         (icono y su texto, chip y su numero)
       e16   entre controles hermanos   (un campo y el siguiente)
       e24   entre grupos               (un grupo de campos y el proximo)
       e32   entre secciones            (relleno de una tarjeta)
       e48   entre bloques              (una seccion y la que sigue)
       e64   respiro de pantalla        (cabecera y contenido)
       e128  aire de portada            (solo login y estados vacios)
  */
  /* --- EN REM, Y EL NOMBRE SIGUE SIENDO LA MEDIDA -----------------------
     Cada paso es `n / 16`, asi que con la letra del navegador en su valor de
     fabrica -16px- `--e24` dibuja exactamente 24 px, igual que antes. Lo que
     cambia es que si alguien SUBE la letra por defecto en las opciones de su
     navegador o de su telefono, el espacio crece con ella.

     Por que importa, y por que no importaba antes: el zoom -Ctrl+/- o
     pellizcar- agranda todo igual, pixeles incluidos, asi que quien usa zoom
     ya estaba servido. A quien no le llegaba nada era a quien ajusto el
     TAMANO DE LETRA POR DEFECTO y espera que las paginas lo respeten. Esta app
     se abre en el telefono de un invitado en una fiesta: en un quinceanios hay
     abuelos.

     Y esto no agrega una idea nueva, TERMINA la del kit: su escala de texto
     ya estaba en rem (`--text-md: 1rem`) y casi todos sus cortes de media
     query tambien. Era `fundamentos.css` el que la pisaba con pixeles.

     La proyeccion NO entra en esto y no es un olvido: mide todo en unidades de
     CONTENEDOR (`cqmin`, `cqh`, ADR-015) y no carga el kit (ADR-012). Un texto
     en la pared se dimensiona contra el tamano del contenedor, que es lo
     correcto para una pared, y la letra del navegador no tiene nada que hacer
     en esa cuenta. */
  --e6:   0.375rem; /* 6px.  SOLO dentro de un control: el hueco de un icono. */
  --e8:   0.5rem;   /* 8px   */
  --e12:  0.75rem;  /* 12px. SOLO dentro de un control. */
  --e16:  1rem;     /* 16px  */
  --e24:  1.5rem;   /* 24px  */
  --e32:  2rem;     /* 32px  */
  --e64:  4rem;     /* 64px  */
  --e128: 8rem;     /* 128px */

  /* --- el ritmo del kit, en su UNICA casa -------------------------------
     El kit trae su propia escala de doce pasos con base 4, y seis de esos
     pasos -4, 20, 40, 48, 80 y 96- estan fuera de la base 8. No alcanza con
     no usarla: mientras una hoja del kit dibuje con 20px, la pantalla tiene
     dos ritmos y se ve sin jerarquia, que es justo lo que se reporto.

     Hasta el 2026-10-13 esto era un REMAPEO: tokens.css declaraba los doce
     con los valores del kit y esta hoja -que va ultima en el bundle- pisaba
     seis. Funcionaba, y tenia tres costos:

       1. tokens.css dejo de ser legible como verdad. Decia
          `--space-5: 1.25rem /* 20px */` y en pantalla eran 24.
       2. El mismo valor en dos archivos, que es el defecto que este repo
          persigue en todas sus otras formas.
       3. Las colisiones quedaban invisibles, repartidas entre dos hojas.

     Ahora la escala entera se declara ACA y tokens.css no la menciona. El
     valor que se dibuja es exactamente el mismo que antes: lo que cambio es
     donde esta escrito, no cuanto mide.

     LAS TRES COLISIONES, que ahora se ven de un vistazo. Son la consecuencia
     de meter doce pasos en ocho, y no un error: el kit distinguia medidas que
     esta grilla no distingue. Si alguna vez una de las dos puntas de una
     colision tiene que verse distinta de la otra, el camino es sacar esa hoja
     del kit de `--space-*` y darle el `--e*` que corresponda.

       --space-5  y --space-6              dibujan los dos 24
       --space-8  y --space-10             dibujan los dos 32
       --space-12, --space-16 y --space-20 dibujan los tres 64

     `--space-24` no esta: ninguna hoja la lee. Una ficha declarada que nadie
     consume es la version CSS de una clave de config sin lector. */
  --space-1:  var(--e6);    /* el kit decia 4   */
  --space-2:  var(--e8);
  --space-3:  var(--e12);
  --space-4:  var(--e16);
  --space-5:  var(--e24);   /* el kit decia 20  */
  --space-6:  var(--e24);
  --space-8:  var(--e32);
  --space-10: var(--e32);   /* el kit decia 40  */
  --space-12: var(--e64);   /* el kit decia 48  */
  --space-16: var(--e64);
  --space-20: var(--e64);   /* el kit decia 80  */

  /* --- 2. TEXTO ---------------------------------------------------------
     Siete tamanos con oficio. Geist en todo; la variable del kit ya la
     resuelve local, sin Google Fonts.

     Cada rol trae su interlineado y su tracking porque van juntos: un 48
     con interlineado 1.5 se desarma, y un 14 con -0.02em se cierra.
  */
  --t-display:  3rem;     /* 48px. la promesa del login. Una sola por producto. */
  --t-titulo:   2.25rem;  /* 36px. el nombre de la pantalla.                    */
  --t-seccion:  1.5rem;   /* 24px. el nombre de una seccion de la pantalla.     */
  --t-tarjeta:  1.125rem; /* 18px. el titulo de una tarjeta o de una fila.      */
  --t-cuerpo:   1rem;     /* 16px. lectura normal.                              */
  --t-apoyo:    0.875rem; /* 14px. lo secundario: fechas, notas, metadatos.     */
  --t-etiqueta: 0.75rem;  /* 12px. rotulos de campo y chips.                    */

  /* --- 3. ANCHO ---------------------------------------------------------
     1200 y centrado. Por encima, una fila de etiqueta y control deja los
     dos extremos separados por medio metro de nada.
  */
  /* --- el foco ----------------------------------------------------------
     El borde en foco usaba `--color-action` directo, y en grafito eso es
     #F4F4F4 contra un borde en reposo de #3A3A3A: el control pasa de gris
     oscuro a casi blanco de un cuadro al otro. Se ve tosco porque NO es un
     cambio de estado, es un cambio de material.

     Un paso a mitad de camino se lee como "esto esta activo" sin el salto, y
     como sale de un `color-mix` sobre las fichas del tema, funciona igual en
     los veintiocho.

     El aro va aparte y en el color de accion con poca opacidad. Hace falta:
     el borde solo es un cambio sutil y el foco tiene que verse SIEMPRE, que
     es lo que permite usar la app con el teclado. Lo sutil es el borde; lo
     visible es el aro. */
  --color-border-focus: color-mix(in oklab, var(--color-action) 50%, var(--color-border-strong));
  --aro-focus: 0 0 0 3px color-mix(in oklab, var(--color-action) 20%, transparent);

  /* --- TRES ANCHOS DE CONTENIDO, y no uno ---------------------------------
     1200 es el ancho de una pantalla que MUESTRA COSAS: la agenda de eventos,
     una pared de doscientas fotos, las seis tarjetas de un evento. Ahi el
     ancho es util porque hay material que lo llena.

     Una pantalla de AJUSTES no tiene con que llenarlo. Un campo de nombre de
     1200 px de ancho no es generoso, es raro: el ojo recorre un renglon vacio
     de medio metro para llegar al control, y dos campos seguidos no se leen
     como una lista. Lo mismo un formulario.

     Asi que dos mas. Son medidas de CONTENIDO y no de pantalla: cuanto mide lo
     que hay adentro, no cuanto mide la ventana.

       --ancho          1200   agendas, paredes, tableros
       --ancho-medio     840   una columna con filas: miembros, ajustes
       --ancho-angosto   520   un formulario: una pregunta debajo de la otra

     520 no se eligio ahora: es el `max-width` que `evento-nuevo.css` ya tenia
     escrito a mano para su columna, y ahora sale de aca. Tener el numero en
     dos lados es como se separan. */
  --ancho: 1200px;
  --ancho-medio: 840px;
  --ancho-angosto: 520px;
  --margen-lateral: var(--e32);
}

/* --- la pagina --------------------------------------------------------- */

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

body {
  min-height: 100dvh;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-sans);
  font-size: var(--t-cuerpo);
  line-height: var(--leading-normal);
}

.marco {
  width: 100%;
  max-width: var(--ancho);
  margin: 0 auto;
  padding-left: max(var(--margen-lateral), env(safe-area-inset-left));
  padding-right: max(var(--margen-lateral), env(safe-area-inset-right));
}

@media (max-width: 47.99rem) {
  :root { --margen-lateral: var(--e16); }
}

/* --- la escala tipografica --------------------------------------------- */

.display {
  font-size: var(--t-display);
  line-height: 1.05;
  letter-spacing: -0.03em;
  font-weight: var(--weight-semibold);
  margin: 0;
  text-wrap: balance;
}

.titulo {
  font-size: var(--t-titulo);
  line-height: 1.1;
  letter-spacing: -0.025em;
  font-weight: var(--weight-semibold);
  margin: 0;
  text-wrap: balance;
}

.seccion {
  font-size: var(--t-seccion);
  line-height: 1.2;
  letter-spacing: -0.02em;
  font-weight: var(--weight-semibold);
  margin: 0;
}

.tarjeta-titulo {
  font-size: var(--t-tarjeta);
  line-height: 1.3;
  letter-spacing: -0.01em;
  font-weight: var(--weight-medium);
  margin: 0;
}

.cuerpo { font-size: var(--t-cuerpo); line-height: 1.5; margin: 0; }

.apoyo {
  font-size: var(--t-apoyo);
  line-height: 1.45;
  color: var(--color-text-muted);
  margin: 0;
}

.etiqueta {
  font-size: var(--t-etiqueta);
  line-height: 1.35;
  font-weight: var(--weight-medium);
  color: var(--color-text-muted);
  margin: 0;
}

/* Numeros que se comparan de una columna a otra. Sin esto, un 148 y un 311
   no alinean y la lista se lee torcida. */
.dato { font-variant-numeric: tabular-nums; }

/* En un telefono el display de 48 no entra: 34 es el maximo que deja la
   promesa en dos lineas sin partir una palabra. */
@media (max-width: 34rem) {
  :root { --t-display: 2.125rem; --t-titulo: 1.6875rem; --t-seccion: 1.25rem; }
}

/* --- utilidades de ritmo ------------------------------------------------
   Pocas y explicitas. No es un framework: son los cinco huecos que se
   repiten en todas las pantallas. */

.pila     { display: flex; flex-direction: column; }
/*
 * `pila-4` SE USABA EN NUEVE LUGARES Y NO EXISTIA.
 *
 * Una clase que ningun CSS define no falla ni avisa: la pila queda con hueco
 * cero y los dos renglones salen pegados. Son los nueve lugares donde un dato
 * tiene su letra chica debajo -el nombre de un espacio y el mail, el titulo de
 * un evento y su fecha- y en todos se veia apretado sin que nada lo explicara.
 *
 * El 4 va LITERAL y no como ficha, a proposito: la escala de layout arranca en
 * 6 y no tiene un escalon de 4 -se saco, y hay una prueba que no deja que
 * vuelva-. Esto no es espacio entre cosas, es el interlineado entre dos
 * renglones del MISMO dato. Que se declare una sola vez, aca, es lo que evita
 * que el numero ande suelto por nueve hojas.
 */
.pila-4   { gap: 4px; }
.pila-8   { gap: var(--e8); }
.pila-16  { gap: var(--e16); }
.pila-24  { gap: var(--e24); }
.pila-32  { gap: var(--e32); }
.pila-48  { gap: var(--e64); }

.fila     { display: flex; align-items: center; }
.fila-8   { gap: var(--e8); }
.fila-16  { gap: var(--e16); }
.fila-24  { gap: var(--e24); }

.crece { flex: 1; min-width: 0; }

/* --- parches al kit -----------------------------------------------------
   Tres componentes consumen PRIMITIVAS en vez de fichas semanticas. Las
   primitivas son del sistema claro, asi que bajo cualquiera de los 27 temas
   oscuros salen casi blancas. Es un error del kit, no una eleccion. */

/* Salia un gris casi blanco encima de la fila oscura. */
.table tbody tr:hover { background: var(--color-surface-muted); }

/* El borde de un badge solido no puede ser un verde/ambar/rojo claros. */
.badge--accent,
.badge--warning,
.badge--danger { border-color: transparent; }

/* La pastilla activa se pintaba con --neutral-900, que sobre un tema oscuro
   es invisible. Va con la ficha de accion, que cada tema define. */
.ds-snav__pill.is-active {
  background: var(--color-action);
  color: var(--color-on-action);
}
.ds-snav__pill.is-active .ds-snav__meta { color: var(--color-on-action); opacity: 0.65; }

/* --- foco ---------------------------------------------------------------
   Un anillo, el mismo en todo. El kit lo declara por componente y algunos se
   lo olvidan.

   Va en el color de ACCION del tema y no en `--color-focus`, que en los temas
   oscuros es casi blanco: un anillo blanco sobre una superficie oscura
   contrasta tanto que el elemento enfocado grita. El de accion se nota igual
   y pertenece a la paleta. */
:where(a, button, [tabindex]):focus-visible {
  outline: 2px solid var(--color-action);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
