/* ============================================================
   GRAFIK — /estudio/ — hoja de estilos del configurador
   Prefijo `.es-` en toda clase propia, para que nada de esto
   pueda chocar con `styles.css` del sitio público.
   ============================================================

   Herencia de tokens: SE REPLICAN los valores de :root de styles.css
   (con prefijo --es-) en vez de hacer `@import url('/styles.css')`.

   Por qué: styles.css trae, además de los tokens, reglas de elemento sin
   prefijo — `body`, `a`, `img, svg`, `.wrap`, `.section`, `.btn`... (ver
   líneas 40-90 de ese archivo). Un @import las trae completas a esta
   página. Esta hoja SÍ necesita definir sus propios `body`/`html`/`button`
   base (es la única hoja que carga /estudio/), así que ese @import sólo
   sumaría reglas duplicadas y, peor, un `.btn` o `.wrap` que cambie de
   forma en el sitio público movería el configurador sin que nadie lo haya
   tocado aquí. Copiar los ~10 valores de paleta es más barato que cargar
   ese acoplamiento invisible. Si la paleta de marca cambia, se actualiza
   en los dos archivos — el mismo trade-off que ya asume email-templates.js
   (§ Paleta de marca del spec). */

:root {
  --es-negro:       #0C0C0C;
  --es-carbon:      #181818;
  --es-carbon-mid:  #222222;
  --es-carbon-hi:   #2E2E2E;
  /* #666666 daba 3.09:1 sobre --es-carbon: por debajo del 4.5:1 de WCAG AA.
     #8A8A8A da 5.14:1 sobre carbon y 5.67:1 sobre negro, y sigue siendo
     claramente más apagado que --es-plata, así que la jerarquía se conserva.
     OJO: este token también se usa como color de BORDE (.es-chip:hover,
     .es-btn-outline:hover, el borde izquierdo de .es-callout). Ahí el umbral
     es 3:1 (WCAG 1.4.11, componentes no textuales) y #666666 ya lo cumplía —
     subirlo no rompe nada, pero tampoco era lo que estaba mal. */
  --es-plata-dim:   #8A8A8A;
  --es-plata:       #B8B8B8;
  --es-rojo:        #D02B34;
  /* El rojo de marca como TEXTO da 3.45:1 sobre --es-carbon, que no pasa AA.
     Como relleno (fondos, bordes, el punto del logo) es correcto y no se
     toca — por eso esto es un token aparte y no un cambio a --es-rojo. Mismo
     tono (H≈356°), aclarado: 4.79:1 sobre carbon, 5.27:1 sobre negro. */
  --es-rojo-texto:  #E2545D;
  --es-rojo-dark:   #A0202A;
  --es-blanco:      #F0F0EE;
  --es-wa:          #25D366;

  --es-serif: 'Barlow Condensed', sans-serif; /* display / wordmark */
  --es-sans:  'Barlow', sans-serif;           /* body / controles */

  --es-radius: 4px;
  --es-nav-h: 64px; /* alto de la cabecera propia del estudio, no el del sitio público */
}

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

html { background: var(--es-negro); }

body {
  min-height: 100vh;
  background: var(--es-negro);
  color: var(--es-blanco);
  font-family: var(--es-sans);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

img, svg { display: block; max-width: 100%; }
button { font: inherit; color: inherit; }
a { color: inherit; }

.es-app {
  display: flex;
  flex-direction: column;
  gap: 24px;
  max-width: 1180px;
  margin: 0 auto;
  padding: 24px 20px 56px;
}

/* ---- Cabecera ---- */
.es-header {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-bottom: 16px;
  border-bottom: 1px solid rgba(184, 184, 184, 0.15);
}

/* Fila del wordmark con la salida de ayuda a la derecha. */
.es-header-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

/* CTA de ayuda / pedido especial (a un costado, no compite con el CTA del resumen). */
.es-help-cta {
  min-height: 44px; /* objetivo táctil, igual que el resto de controles (A3) */
  padding: 8px 16px;
  font-size: 12px;
  text-align: left;
}
.es-help-cta svg { flex: none; }
.es-help-cta-short { display: none; }
@media (max-width: 640px) {
  .es-help-cta { padding: 8px 14px; }
  .es-help-cta-full { display: none; }
  .es-help-cta-short { display: inline; }
}

.es-wordmark {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  width: fit-content;
  /* Es un enlace de vuelta al inicio, o sea un objetivo táctil como cualquier
     otro: medía 33 px de alto. No cambia nada visualmente (el contenido ya va
     centrado), sólo reserva el alto mínimo. */
  min-height: 44px;
  font-family: var(--es-serif);
  font-weight: 900;
  font-size: 22px;
  letter-spacing: 0.02em;
  text-decoration: none;
  color: var(--es-blanco);
}

.es-wordmark-mark { color: var(--es-rojo); flex: none; }

.es-context {
  color: var(--es-plata);
  font-size: 14px;
  font-weight: 400;
  margin: 0;
}

/* ---- Indicador de pasos (estudio/ui/step-indicator.js) ----
   Tercera fila del header, sólo de lectura — no hay wizard que navegar.
   Blanco sobre rojo en el círculo "hecho" es la misma combinación que ya
   usa .es-chip.is-selected (línea ~700), probada contra A1. */
.es-steps {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
  list-style: none;
}

.es-steps-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--es-plata-dim);
}

.es-steps-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 1.5px solid rgba(184, 184, 184, 0.35);
  font-size: 10px;
  font-weight: 700;
}

.es-steps-item.is-done { color: var(--es-plata); }
.es-steps-item.is-done .es-steps-num {
  background: var(--es-rojo);
  border-color: var(--es-rojo);
  color: var(--es-blanco);
}

/* ---- Estados de la página ----
   #es-app trae data-state: "loading" (default) | "ready" | "catalog-error"
   | "konva-unavailable". Un único atributo decide qué bloque se ve; boot.js
   y el script inline de index.html sólo tocan ese atributo, nunca `hidden`
   elemento por elemento. */

#es-skeleton,
#es-notice-catalog,
#es-notice-konva,
#es-main { display: none; }

.es-app[data-state="loading"] #es-skeleton { display: grid; }
.es-app[data-state="ready"] #es-main { display: grid; }
.es-app[data-state="catalog-error"] #es-notice-catalog { display: flex; }
.es-app[data-state="konva-unavailable"] #es-notice-konva { display: flex; }

/* ---- Skeleton (catálogo/Babel cargando) ---- */
.es-skeleton {
  grid-template-columns: 1fr;
  gap: 16px;
}

.es-skeleton-stage {
  aspect-ratio: 1 / 1;
  border-radius: var(--es-radius);
  background: var(--es-carbon);
}

.es-skeleton-panel { display: flex; flex-direction: column; gap: 10px; }

.es-skeleton-line {
  height: 44px;
  border-radius: var(--es-radius);
  background: var(--es-carbon);
}

.es-skeleton-line--short { width: 60%; }

.es-skeleton-stage, .es-skeleton-line {
  animation: es-pulse 1.4s ease-in-out infinite;
}

@keyframes es-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}

@media (prefers-reduced-motion: reduce) {
  .es-skeleton-stage, .es-skeleton-line { animation: none; }
}

/* ---- Avisos (error de catálogo / Konva no disponible) ---- */
.es-banner {
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
  padding: 20px;
  border-radius: var(--es-radius);
  border: 1px solid var(--es-rojo-dark);
  background: rgba(208, 43, 52, 0.08);
}

.es-banner p { color: var(--es-blanco); }
.es-banner strong { color: var(--es-blanco); }

.es-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 12px 20px;
  border-radius: var(--es-radius);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-decoration: none;
  border: 1px solid transparent;
}

.es-btn-wa { background: var(--es-wa); color: var(--es-negro); }
.es-btn-wa:hover { filter: brightness(1.08); }

/* ---- Layout principal: canvas manda ---- */
.es-main {
  grid-template-columns: 1fr;
  gap: 24px;
  /* Un item de grid/flex sin min-width:0 usa el ancho mínimo de SU
     contenido como piso, sin importar lo que digan sus propios `width`.
     Konva mete un <div class="konvajs-content" style="width:900px"> dentro
     de #es-stage (abajo); sin cortar esa cadena aquí y en .es-stage-wrap,
     ese 900px se propaga hacia arriba y saca la página de su viewport en
     móvil aunque #es-stage tenga overflow:hidden. */
  min-width: 0;
}

.es-stage-wrap {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
  /* Contexto de posicionamiento para lo que se superpone al canvas
     (view-pill.js, canvas-dropzone.js — el logo-toolbar.js va en flujo
     normal, debajo, no absoluto). Los puntos de montaje viven en
     estudio/index.html como hermanos de #es-stage. */
  position: relative;
}

.es-controls { min-width: 0; }

.es-stage {
  width: 100%;
  aspect-ratio: 1 / 1; /* valor por defecto; boot.js lo ajusta al canvas_size real de la prenda */
  border-radius: var(--es-radius);
  border: 1px solid rgba(184, 184, 184, 0.15);
  background: var(--es-carbon);
  overflow: hidden;
  min-width: 0;
}

/* Konva envuelve sus <canvas> (uno por Layer) en un <div class="konvajs-content">
   con width/height inline fijos en píxeles de prenda (p.ej. 900×900) — ese div
   es el que hay que forzar a 100%, no sólo los <canvas>, si no el ancho fijo
   de 900px se propaga hacia arriba (ver min-width:0 de .es-main/.es-stage-wrap
   más arriba) y desborda el layout en pantallas angostas. El !important gana
   a los estilos inline que pone Konva. */
.es-stage > div {
  width: 100% !important;
  height: 100% !important;
}

.es-stage canvas {
  display: block;
  width: 100% !important;
  height: 100% !important;
}

/* Con un logo colocado, arrastrarlo o escalarlo con el dedo no debe desplazar la
   página: sin esto el navegador cancela el gesto (pointercancel) y se lleva el
   scroll. Sólo aplica con logo (konva-adapter.js#syncSelection); sin él, la
   prenda no captura gestos y se puede pasar de largo con el dedo. */
.es-stage[data-editing="true"] { touch-action: none; }
.es-stage:focus-visible { outline: 2px solid var(--es-rojo); outline-offset: 2px; }

/* ---- Vista, superpuesta al canvas (estudio/ui/view-pill.js) ----
   Sólo se monta cuando la prenda trae vistas de presentación (gorra:
   izq/der; playera: espalda) — ver ViewPill, `if (!views.length) return null`.
   min-height:44px en los chips, igual que el resto de controles interactivos
   del sitio: el área táctil no se negocia por verse "flotante" (WCAG 2.5.5,
   tests/e2e/a11y.spec.js A3). */
.es-view-pill {
  position: absolute;
  top: 14px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  max-width: calc(100% - 32px);
  pointer-events: none; /* el hueco entre chips no debe robarle el drag al logo */
}

.es-view-pill-row {
  display: flex;
  gap: 2px;
  padding: 4px;
  border-radius: 999px;
  background: rgba(24, 24, 24, 0.9);
  border: 1px solid rgba(184, 184, 184, 0.18);
  pointer-events: auto;
}

.es-view-pill-chip {
  min-height: 44px;
  padding: 0 18px;
  border: none;
  border-radius: 999px;
  background: transparent;
  color: var(--es-plata);
  font-family: var(--es-sans);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: background-color .15s, color .15s;
}
.es-view-pill-chip:hover:not(:disabled) { color: var(--es-blanco); }
.es-view-pill-chip.is-selected { background: var(--es-blanco); color: var(--es-negro); }
.es-view-pill-chip:disabled { opacity: 0.5; cursor: not-allowed; }
.es-view-pill-chip:focus-visible { outline: 2px solid var(--es-rojo); outline-offset: 2px; }

.es-view-pill-hint {
  pointer-events: none;
  max-width: 100%;
  padding: 4px 12px;
  border-radius: 999px;
  background: rgba(12, 12, 12, 0.75);
  color: var(--es-plata);
  font-size: 12px;
  line-height: 1.4;
  text-align: center;
}

/* ---- Controles ---- */
.es-controls {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.es-field { display: flex; flex-direction: column; gap: 10px; }

.es-field-label {
  font-family: var(--es-serif);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--es-plata);
}

.es-garment-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.es-garment-option {
  min-height: 44px;
  padding: 0 16px;
  border-radius: 999px;
  border: 1.5px solid rgba(184, 184, 184, 0.3);
  background: transparent;
  color: var(--es-blanco);
  font-size: 14px;
  cursor: pointer;
  transition: background-color .12s, border-color .12s;
}

.es-garment-option:hover { border-color: var(--es-plata); }

.es-garment-option[aria-pressed="true"] {
  background: var(--es-rojo);
  border-color: var(--es-rojo-dark);
  font-weight: 600;
}

/* ---- Swatches de color ----
   Área táctil mínima 44×44 (WCAG 2.5.5); el estado seleccionado se marca
   con un anillo de dos tonos + una insignia de punto en una esquina, no
   sólo con un borde sutil. */
.es-swatch-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.es-swatch {
  position: relative;
  width: 44px;
  height: 44px;
  min-width: 44px;
  min-height: 44px;
  border-radius: 50%;
  border: 2px solid rgba(184, 184, 184, 0.25);
  background: var(--es-swatch-color, var(--es-carbon-hi));
  cursor: pointer;
  transition: transform .12s, box-shadow .12s;
}

.es-swatch:hover { transform: translateY(-2px); }

.es-swatch[aria-checked="true"] {
  border-color: var(--es-blanco);
  box-shadow: 0 0 0 3px var(--es-negro), 0 0 0 5px var(--es-rojo);
}

.es-swatch[aria-checked="true"]::after {
  /* Insignia de "seleccionado": punto rojo dentro de un círculo blanco fijo,
     en la esquina. Fondo fijo (no hereda --es-swatch-color) para que se lea
     igual de claro sobre un swatch claro que sobre uno oscuro — un check o
     una X dibujados con bordes finos se pierden o se confunden con "no"
     según el color de fondo; un punto no admite esa ambigüedad. */
  content: "";
  position: absolute;
  top: -4px;
  right: -4px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--es-rojo) 36%, var(--es-blanco) 40%);
  box-shadow: inset 0 0 0 1px rgba(12, 12, 12, 0.4);
}

.es-disclaimer {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--es-plata);
  padding-top: 4px;
  border-top: 1px solid rgba(184, 184, 184, 0.12);
}

/* ---- Sin JavaScript ---- */
.es-noscript {
  max-width: 480px;
  margin: 80px auto;
  padding: 24px;
  text-align: center;
  border: 1px solid rgba(184, 184, 184, 0.2);
  border-radius: var(--es-radius);
}

.es-noscript a {
  display: inline-block;
  margin-top: 12px;
  color: var(--es-wa);
  font-weight: 600;
}

/* ---- Escritorio: canvas a la izquierda, controles a la derecha ----
   760px (antes 960px: se corrió hacia abajo al añadir el breakpoint de
   1100px de aquí abajo, que es el que ahora hace de "escritorio completo").
   Éste queda como el escalón intermedio — tablet y escritorio angosto —,
   variación menor de un breakpoint que ya estaba probado. */
@media (min-width: 760px) {
  .es-main {
    grid-template-columns: minmax(0, 1fr) 320px;
    align-items: start;
  }

  .es-skeleton {
    grid-template-columns: minmax(0, 1fr) 320px;
    align-items: start;
  }
}

/* ---- Escritorio ancho: 3 columnas — prenda/color/técnica | canvas | tallas/resumen ----
   A partir de aquí el sidebar de controles cabe partido en dos sin apretar
   el canvas. Los 4 paneles pasan a ser hijos DIRECTOS del grid de .es-main
   vía `display:contents` en los dos niveles que los envuelven hoy —
   `.es-controls` es el `<aside>` de index.html, `#es-controls` es el `<div>`
   donde React monta (ver estudio/ui/studio-app.js) — y cada uno se coloca
   por su propia clase de layout (`es-panel-prenda`/`es-panel-logo` a la
   izquierda, `es-panel-tallas`/`es-panel-resumen` a la derecha, ya
   existentes). `.es-stage-wrap` no se mueve de sitio en el DOM: solo ocupa
   las dos filas del grid en la columna central.

   Nota de accesibilidad: `display:contents` en el `<aside>` le quita su rol
   implícito de landmark ("complementary") en algunos navegadores. Se acepta
   aquí porque `tests/e2e/a11y.spec.js` no depende de landmarks (solo
   contraste, área táctil y desbordamiento) y la ruta es privada/sin indexar —
   si más adelante se necesita el landmark de vuelta, hay que resolverlo con
   un `role` en otro nodo que si tenga caja, no revirtiendo este layout. */
@media (min-width: 1100px) {
  .es-app { max-width: 1440px; }

  .es-main {
    grid-template-columns: 300px minmax(0, 1fr) 340px;
    align-items: start;
  }

  .es-controls,
  #es-controls { display: contents; }

  .es-stage-wrap { grid-column: 2; grid-row: 1 / span 2; }
  .es-panel-prenda { grid-column: 1; grid-row: 1; }
  .es-panel-logo { grid-column: 1; grid-row: 2; }
  .es-panel-tallas { grid-column: 3; grid-row: 1; }
  .es-panel-resumen { grid-column: 3; grid-row: 2; }
  .es-disclaimer { grid-column: 3; grid-row: 3; }
}

/* ============================================================
   estudio/ui/panel-tallas.js + estudio/ui/panel-resumen.js
   ============================================================ */

/* ---- Panel genérico: tarjeta con borde sutil sobre --es-carbon ----
   Lo comparten el <fieldset> de tallas y el <section> de resumen. Un
   <fieldset> trae borde/relleno propios del navegador; se pisan aquí en vez
   de "resetearlos" aparte porque las reglas explícitas del autor ya ganan a
   la hoja de estilos por defecto sin necesitar mayor especificidad. */
.es-panel {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
  padding: 20px;
  border: 1px solid rgba(184, 184, 184, 0.15);
  border-radius: var(--es-radius);
  background: var(--es-carbon);
}

/* Sólo resetea el padding por defecto del navegador en <legend> (varía
   entre motores); el margen lo pone .es-panel-title igual que en los otros
   3 paneles — panel-tallas.js le pasa esa misma clase en vez de
   .es-field-label para que los 4 títulos se vean iguales. Se mantiene
   <legend> (no <h2>) para no perder el nombre accesible que le da al
   <fieldset> que envuelve las tallas. */
.es-panel > legend { padding: 0; }

/* Etiqueta pequeña reusada por los steppers de talla y los campos de
   contacto — no es --es-field-label (esa es para títulos de sección). */
.es-label-sm {
  font-size: 13px;
  font-weight: 600;
  color: var(--es-plata);
}

/* Mensaje de error de un campo puntual, enlazado vía aria-describedby. */
.es-error-text {
  font-size: 12px;
  /* A 12px y en rojo de marca esto daba 3.45:1 — el peor sitio posible para un
     contraste flojo, porque es justo el texto que explica por qué un dato no
     se aceptó. Con --es-rojo-texto sube a 4.79:1. */
  color: var(--es-rojo-texto);
}

/* ---- Tallas ---- */
.es-tallas-grid {
  display: grid;
  /* 134px = 44 (botón −) + 44 (input) + 44 (botón +) + 2 de bordes. Con el
     mínimo anterior de 96px la celda se quedaba en ~133 y el input caía a
     43 px de ancho: los dos botones sí cumplían 44, pero el campo donde se
     teclea la cantidad —el control más usado de todo el embudo— no. Lo
     encontró a11y.spec.js (A3). */
  grid-template-columns: repeat(auto-fill, minmax(134px, 1fr));
  gap: 12px;
}

.es-tallas-grid--single {
  grid-template-columns: minmax(160px, 240px);
}

.es-talla-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.es-talla-stepper {
  display: flex;
  align-items: stretch;
  border: 1px solid rgba(184, 184, 184, 0.25);
  border-radius: var(--es-radius);
  overflow: hidden;
}

/* Botones +/− con área táctil de 44px (WCAG 2.5.5), igual que .es-swatch. */
.es-talla-btn {
  flex: none;
  width: 44px;
  min-height: 44px;
  border: 0;
  background: var(--es-carbon-hi);
  color: var(--es-blanco);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  transition: background-color .12s;
}

.es-talla-btn:hover { background: var(--es-carbon-mid); }
.es-talla-btn:active { background: var(--es-rojo-dark); }

.es-talla-input {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 44px;
  border: 0;
  border-left: 1px solid rgba(184, 184, 184, 0.25);
  border-right: 1px solid rgba(184, 184, 184, 0.25);
  background: var(--es-carbon-mid);
  color: var(--es-blanco);
  text-align: center;
  font: inherit;
  font-size: 16px; /* 16px en iOS evita el auto-zoom del input al enfocar */
}

/* Se ocultan las flechas nativas del input number: el stepper propio (+/−)
   ya cubre esa interacción y las flechas nativas son más difíciles de
   tocar en móvil que los 44px de .es-talla-btn. */
.es-talla-input::-webkit-outer-spin-button,
.es-talla-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.es-talla-input { appearance: textfield; }

.es-tallas-total {
  display: flex;
  justify-content: space-between;
  padding-top: 12px;
  border-top: 1px solid rgba(184, 184, 184, 0.12);
  font-weight: 600;
}

.es-tallas-errors {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 10px 12px;
  border-radius: var(--es-radius);
  border: 1px solid var(--es-rojo-dark);
  background: rgba(208, 43, 52, 0.08);
  font-size: 13px;
}

/* ---- Resumen + contacto ---- */
.es-resumen-status {
  color: var(--es-plata);
  font-size: 14px;
}

.es-resumen-error {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 12px 14px;
  border-radius: var(--es-radius);
  border: 1px solid var(--es-rojo-dark);
  background: rgba(208, 43, 52, 0.08);
}

.es-resumen-error-hint {
  font-size: 12.5px;
  color: var(--es-plata);
}

/* Resumen sin precios (Etapa A): lista prenda/color/técnica/tallas. */
.es-resumen-summary {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 4px 14px;
  margin: 0;
  font-size: 14px;
}
.es-resumen-summary-label { color: var(--es-plata); }
.es-resumen-summary-value { margin: 0; color: var(--es-blanco); }

.es-resumen-contact {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.es-input {
  min-height: 44px;
  padding: 0 12px;
  border-radius: var(--es-radius);
  border: 1px solid rgba(184, 184, 184, 0.25);
  background: var(--es-carbon-mid);
  color: var(--es-blanco);
  font: inherit;
  font-size: 16px; /* evita auto-zoom de iOS al enfocar, igual que .es-talla-input */
}

/* El placeholder nunca se estilizó, así que caía al gris por defecto del
   navegador (#757575 en Chromium): 3.45:1 sobre --es-carbon-mid, por debajo
   de AA. Es texto que el cliente tiene que leer para saber qué formato se
   espera en el campo del teléfono. --es-plata-dim da 4.61:1 y sigue siendo
   claramente más apagado que --es-blanco, así que un placeholder no se
   confunde con un valor ya escrito. Lo encontró a11y.spec.js (A1). */
.es-input::placeholder,
.es-talla-input::placeholder {
  color: var(--es-plata-dim);
  opacity: 1; /* Firefox le aplica opacity .54 por defecto */
}

.es-input:focus {
  outline: 2px solid var(--es-rojo);
  outline-offset: 1px;
}

.es-resumen-submit {
  justify-content: center;
  width: 100%;
  min-height: 48px;
}

.es-resumen-submit:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  filter: none;
}

/* Mensaje tras enviar el pedido (panel-resumen.js#formOrDone). --es-wa
   como acento: mismo verde que ya usa el CTA de WhatsApp, "esto va a buen
   puerto" en vez de un rojo de alerta. */
.es-resumen-done {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px;
  border: 1px solid var(--es-wa);
  border-radius: var(--es-radius);
  background: rgba(37, 211, 102, 0.08);
}
.es-resumen-done-title { font-weight: 700; color: var(--es-blanco); font-size: 15px; }
.es-resumen-done-text { font-size: 13px; line-height: 1.5; color: var(--es-plata); }
/* .es-resumen-done es flex column: sin esto el link de variante "outline" se
   estira a 100% de ancho (align-items:stretch, el default). Clase propia en
   vez de anidar el selector de esa variante bajo .es-resumen-done: el
   escáner de css-classes.test.js busca la SUBCADENA de esa variante seguida
   de una llave, no un selector real — un anidado la habría hecho "encontrar"
   la definición aquí, antes de llegar al bloque que sí declara su color. */
.es-resumen-done-link { align-self: flex-start; }

/* ═══ Incremento 8 ════════════════════════════════════════════════════════
   Los dos agentes que construyeron los paneles se interrumpieron justo antes
   de añadir su CSS, así que estas reglas completan lo que faltaba siguiendo
   las convenciones ya establecidas arriba: prefijo .es-, tokens --es-*, y
   44px de área táctil mínima en todo lo interactivo (WCAG 2.5, igual que el
   resto del sitio). */

/* ── Utilidad ── */
.es-sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ── Panel de prenda ── */
.es-panel-title {
  font-family: var(--es-serif);
  font-size: 20px;
  font-weight: 900;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--es-blanco);
  margin: 0 0 14px;
}

.es-label-note { color: var(--es-plata); font-weight: 400; text-transform: none; letter-spacing: 0; }

.es-chip-row { display: flex; flex-wrap: wrap; gap: 8px; }

.es-chip {
  min-height: 44px;
  padding: 10px 16px;
  border: 1px solid var(--es-carbon-hi);
  border-radius: 2px;
  background: var(--es-carbon);
  color: var(--es-plata);
  font-family: var(--es-sans);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.es-chip:hover:not(:disabled) { border-color: var(--es-plata-dim); color: var(--es-blanco); }
.es-chip.is-selected { border-color: var(--es-rojo); background: var(--es-rojo); color: var(--es-blanco); }
.es-chip:disabled { opacity: 0.45; cursor: not-allowed; }
.es-chip:focus-visible { outline: 2px solid var(--es-rojo); outline-offset: 2px; }

.es-hint { margin: 10px 0 0; font-size: 13px; line-height: 1.5; color: var(--es-plata); }

/* Advertencia de contraste: informa, NO bloquea. El equipo revisa el arte
   antes de producción de todos modos, y el cliente puede tener una razón. */
.es-warning {
  margin: 12px 0 0;
  padding: 10px 14px;
  border-left: 3px solid var(--es-plata-dim);
  background: rgba(184, 184, 184, 0.08);
  font-size: 13px; line-height: 1.5;
  color: var(--es-plata);
}
.es-warning.is-strong {
  border-left-color: var(--es-rojo);
  background: rgba(208, 43, 52, 0.10);
  color: var(--es-blanco);
}

/* Resolución de impresión. En el caso bueno NO es un aviso —es sólo el dato de
   a qué tamaño sale la estampa, que el cliente no puede deducir de una
   pantalla—, así que se presenta neutro y discreto. Cuando la resolución no da,
   el componente le añade .es-warning (y .is-strong si es grave) y entonces sí
   toma el tratamiento de aviso, sin que esta regla tenga que saber nada. */
.es-print-quality {
  margin: 10px 0 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--es-plata);
}
/* Con .es-warning encima, gana el padding/borde de aquélla: se repite el
   margen para que no quede pegado al bloque anterior. */
.es-print-quality.es-warning { margin-top: 12px; }

/* ── Panel de logo ── */
.es-dropzone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 132px;
  padding: 20px 16px;
  border: 1px dashed var(--es-carbon-hi);
  border-radius: 3px;
  background: var(--es-carbon);
  color: var(--es-plata);
  text-align: center;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
.es-dropzone:hover, .es-dropzone.is-dragover {
  border-color: var(--es-rojo);
  background: rgba(208, 43, 52, 0.06);
}
.es-dropzone:focus-visible { outline: 2px solid var(--es-rojo); outline-offset: 2px; }

.es-dropzone-title { font-weight: 600; color: var(--es-blanco); font-size: 15px; }
.es-dropzone-formats { font-size: 12px; color: var(--es-plata-dim); }
.es-file-input, .es-logo-input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }

.es-logo-hint { margin: 10px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--es-plata-dim); }

/* ---- Subir logo, superpuesto al canvas (estudio/ui/canvas-dropzone.js) ----
   #es-view-pill-mount y #es-canvas-dropzone-mount se sacan del flujo del
   flex de .es-stage-wrap por completo (position:absolute + inset:0): así no
   suman su propio `gap` entre #es-stage y #es-logo-toolbar-mount aunque
   estén vacíos antes de que React monte. El toolbar (más abajo) SÍ queda en
   flujo normal a propósito — "flotante" es de estilo, no de posición. */
#es-view-pill-mount,
#es-canvas-dropzone-mount {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.es-canvas-dropzone-wrap { position: relative; width: 100%; height: 100%; }

.es-canvas-dropzone {
  position: absolute;
  inset: 16px;
  min-height: 0;
  pointer-events: auto;
}

.es-canvas-dropzone-error { margin-top: 4px; }

/* Aviso de "sin transparencia": es AVISO y no error a propósito. El §6 del
   spec decidió no remover fondos automáticamente y apoyarse en el soporte
   humano que el sitio ya ofrece, así que bloquear aquí sería cortar una venta
   que el equipo puede rescatar por WhatsApp. */
.es-logo-alert {
  margin: 12px 0 0;
  padding: 12px 14px;
  border-left: 3px solid var(--es-rojo);
  background: rgba(208, 43, 52, 0.10);
  font-size: 13px; line-height: 1.55;
  color: var(--es-blanco);
}
/* :not(.es-btn): esta regla es para el <a> de texto plano (el aviso de
   stageError en studio-app.js). Sin la exclusión, su especificidad (clase+
   elemento) le gana a `.es-btn-wa` y pinta el botón "Escribir por WhatsApp"
   del aviso de transparencia con el mismo verde de su propio fondo —
   texto invisible, un botón que se ve vacío. */
.es-logo-alert a:not(.es-btn) { color: var(--es-wa); font-weight: 600; }

.es-logo-card {
  display: flex; align-items: center; gap: 12px;
  margin-top: 12px; padding: 12px 14px;
  border: 1px solid var(--es-carbon-hi);
  border-radius: 3px;
  background: var(--es-carbon);
}
.es-logo-card-info { min-width: 0; flex: 1; }
.es-logo-card-name {
  display: block; font-weight: 600; color: var(--es-blanco); font-size: 14px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.es-logo-card-meta { display: block; font-size: 12px; color: var(--es-plata-dim); margin-top: 2px; }

.es-logo-range-row { display: flex; flex-direction: column; gap: 6px; }
.es-logo-range-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.es-logo-range-value { font-variant-numeric: tabular-nums; color: var(--es-plata); font-size: 13px; }

/* Los sliders NO llevan clase propia en el marcado: son <input type="range">
   pelados dentro de .es-logo-range-row. Las reglas anteriores apuntaban a
   .es-logo-scale/.es-logo-rotation, que no existen, así que nunca aplicaron y
   los controles salían con el azul por defecto del navegador en vez del rojo de
   marca. Es el caso inverso al que cubre css-classes.test.js: CSS definido que
   nadie usa — inofensivo salvo porque el estilo que creías puesto no lo está.

   El pulgar nativo mide ~12-16px según el navegador: se reporta difícil de
   tomar con el dedo o el mouse pese a que el <input> ya reserva 44px de alto
   táctil (el hit-box es grande, pero el círculo que se VE es chico). Track y
   pulgar se redefinen a mano en vez de sólo agrandar con accent-color, que no
   controla tamaño — sólo color, y de forma no uniforme entre navegadores. */
.es-logo-range-row input[type="range"] {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  min-height: 44px; /* área táctil; el track visual queda centrado dentro */
  background: transparent;
  cursor: pointer;
}
.es-logo-range-row input[type="range"]::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 999px;
  background: var(--es-carbon-hi);
}
.es-logo-range-row input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 24px;
  height: 24px;
  margin-top: -10px; /* centra el pulgar de 24px sobre el track de 4px */
  border-radius: 50%;
  border: 2px solid var(--es-blanco);
  background: var(--es-rojo);
  box-shadow: 0 1px 3px rgba(12, 12, 12, 0.4);
}
.es-logo-range-row input[type="range"]::-moz-range-track {
  height: 4px;
  border-radius: 999px;
  background: var(--es-carbon-hi);
}
.es-logo-range-row input[type="range"]::-moz-range-thumb {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 2px solid var(--es-blanco);
  background: var(--es-rojo);
  box-shadow: 0 1px 3px rgba(12, 12, 12, 0.4);
}
.es-logo-range-row input[type="range"]:focus-visible {
  outline: 2px solid var(--es-rojo);
  outline-offset: 3px;
}

/* Input numérico junto al slider: mismo valor, editable a mano para quien
   quiera un número exacto en vez de arrastrar. */
.es-logo-range-number {
  width: 64px;
  min-height: 44px; /* mismo mínimo táctil que el resto de controles (A3) */
  padding: 4px 6px;
  border-radius: var(--es-radius);
  border: 1px solid rgba(184, 184, 184, 0.3);
  background: var(--es-carbon);
  color: var(--es-blanco);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.es-logo-range-number:focus-visible {
  outline: 2px solid var(--es-rojo);
  outline-offset: 2px;
}
/* El botón de reinicio de rotación no debe ocupar todo el ancho: se lee como un
   campo de texto y no como una acción. */
.es-logo-range-row .es-btn-sm { flex: 0 0 auto; width: auto; }
.es-logo-range-row { display: flex; align-items: center; gap: 10px; }

/* ---- Toolbar flotante del logo (estudio/ui/logo-toolbar.js) ----
   En flujo normal debajo del canvas (no position:absolute) — "flotante" es
   de estilo: fondo semitransparente, borde, sombra. Grupos separados por una
   línea vertical, como el mockup de referencia; envuelve en varias líneas
   (flex-wrap) en vez de scroll horizontal, más accesible con zoom/teclado. */
.es-logo-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 4px;
  margin-top: 12px;
  padding: 8px 12px;
  border-radius: var(--es-radius);
  border: 1px solid rgba(184, 184, 184, 0.15);
  background: rgba(24, 24, 24, 0.92);
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.35);
}

.es-lt-group {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  padding: 6px 14px;
}
.es-lt-group + .es-lt-group { border-left: 1px solid rgba(184, 184, 184, 0.15); }

.es-lt-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 0 16px;
  border: 1px solid rgba(184, 184, 184, 0.25);
  border-radius: var(--es-radius);
  background: transparent;
  color: var(--es-plata);
  font-family: var(--es-sans);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: border-color .15s, color .15s, background-color .15s;
}
.es-lt-btn:hover:not(:disabled) { border-color: var(--es-plata-dim); color: var(--es-blanco); }
.es-lt-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.es-lt-btn:focus-visible { outline: 2px solid var(--es-rojo); outline-offset: 2px; }

/* Escala/Rotación reusan .es-field/.es-logo-range-*: mismo componente
   RangeField de antes, sólo que ahora vive en el flujo horizontal de la
   toolbar. Ancho fijo para que el grupo no se estire más de la cuenta, y
   slider+botón de reinicio en fila (no columna, como en el sidebar
   angosto) — hay espacio de sobra en un grupo horizontal. */
.es-lt-field { width: 200px; }
.es-lt-field .es-logo-range-row { flex-direction: row; }

/* ── Huecos de tallas y resumen ── */
.es-tallas-general-errors { margin: 10px 0 0; padding: 0; list-style: none; }

/* ── Página de pedido (estudio/pedido/) ───────────────────────────────────
   Vista de sólo lectura que se abre desde la liga del mensaje de WhatsApp.
   El preview manda: es el punto de la página. */

.es-pedido { max-width: 760px; margin: 0 auto; padding: 24px var(--es-gut, 20px) 64px; }
.es-pedido-head { margin-bottom: 20px; }
.es-pedido-folio {
  font-family: var(--es-serif);
  font-size: 40px; font-weight: 900; letter-spacing: 0.02em;
  color: var(--es-blanco); margin: 2px 0 6px;
}
.es-pedido-estado {
  display: inline-block; padding: 5px 12px;
  border: 1px solid var(--es-carbon-hi); border-radius: 2px;
  font-size: 13px; font-weight: 600; color: var(--es-plata);
}
.es-pedido-item {
  margin: 20px 0;
  padding: 18px;
  border: 1px solid var(--es-carbon-hi); border-radius: 3px;
  background: var(--es-carbon);
}
.es-pedido-preview {
  display: block; width: 100%; height: auto;
  max-width: 100%;
  border-radius: 2px; background: var(--es-negro);
  margin-bottom: 16px;
}
.es-pedido-datos {
  display: grid; grid-template-columns: auto 1fr; gap: 6px 16px;
  margin: 0 0 16px; font-size: 14px;
}
.es-pedido-datos dt { color: var(--es-plata-dim); }
.es-pedido-datos dd { margin: 0; color: var(--es-blanco); }
.es-pedido-chip-color {
  display: inline-block; width: 13px; height: 13px;
  border: 1px solid var(--es-carbon-hi); border-radius: 50%;
  vertical-align: -2px; margin-right: 5px;
}
.es-pedido-total {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 16px; padding-top: 16px;
  border-top: 1px solid var(--es-carbon-hi);
  font-size: 15px; color: var(--es-plata);
}
.es-pedido-total strong {
  font-family: var(--es-serif); font-size: 30px; font-weight: 900;
  color: var(--es-blanco);
}

@media (max-width: 640px) {
  .es-pedido-folio { font-size: 32px; }
  .es-pedido-datos { grid-template-columns: 1fr; gap: 2px 0; }
  .es-pedido-datos dd { margin-bottom: 8px; }
}

/* ── Variantes de botón ───────────────────────────────────────────────────
   Faltaban y el efecto era grave aunque invisible para los tests: sin regla
   propia, .es-btn-outline y .es-btn-danger caían al fondo por defecto del
   navegador (ButtonFace, ~#EFEFEF) conservando el color de texto de .es-btn
   (#F0F0EE). Texto blanco sobre fondo blanco: los botones "Ajustar al área",
   "Quitar logo" y "Cambiar" se veían como rectángulos vacíos.
   Se detectó mirando la captura de producción, no con una aserción. */

.es-btn-outline {
  background: transparent;
  border: 1px solid var(--es-carbon-hi);
  color: var(--es-plata);
}
.es-btn-outline:hover:not(:disabled) {
  border-color: var(--es-plata-dim);
  color: var(--es-blanco);
  background: rgba(240, 240, 238, 0.04);
}

.es-btn-danger {
  background: transparent;
  border: 1px solid var(--es-rojo-dark);
  /* --es-rojo-texto, no --es-rojo: aquí el rojo es texto sobre el fondo de la
     tarjeta del logo (--es-carbon), donde el rojo de marca sólo llega a 3.45:1. */
  color: var(--es-rojo-texto);
}
.es-btn-danger:hover:not(:disabled) {
  background: var(--es-rojo);
  border-color: var(--es-rojo);
  color: var(--es-blanco);
}

/* Compacto, pero sin bajar de 44px de alto: el área táctil no se negocia
   (WCAG 2.5, igual que el resto del sitio). Lo que se reduce es el ancho. */
.es-btn-sm {
  min-height: 44px;
  padding: 8px 14px;
  font-size: 13px;
}

/* Modificadores de panel: separan visualmente y existen para que tallas y
   resumen puedan divergir sin tocar .es-panel. */
.es-panel-tallas,
.es-panel-resumen { margin-top: 4px; }

/* Etiqueta de campo del panel de prenda. La encontró el test de clases
   huérfanas en su primera ejecución: mi comprobación manual anterior la dio por
   existente porque `.es-label-sm` la contiene como substring. */
.es-label {
  display: block;
  margin-bottom: 8px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--es-plata);
}

/* ── Contenido estático indexable bajo el configurador (SEO) ─────────────── */
.es-about {
  max-width: 1180px;
  margin: 0 auto;
  padding: 8px 20px 32px;
  color: var(--es-plata);
  font-size: 15px;
  line-height: 1.6;
}
/* Desplegable cerrado por defecto: ocupa una fila y no distrae del configurador. */
.es-about-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 44px;
  padding: 10px 0;
  border-top: 1px solid rgba(184, 184, 184, 0.15);
  color: var(--es-plata);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  list-style: none;
}
.es-about-summary::-webkit-details-marker { display: none; }
.es-about-summary::after {
  content: '';
  flex: none;
  width: 8px;
  height: 8px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform 0.15s ease;
}
.es-about[open] > .es-about-summary::after { transform: rotate(-135deg); }
.es-about-summary:hover { color: var(--es-blanco); }
.es-about-summary:focus-visible { outline: 2px solid var(--es-rojo); outline-offset: 2px; }
.es-about-title {
  margin: 32px 0 12px;
  color: var(--es-blanco);
  font-family: var(--es-serif);
  font-size: 26px;
  font-weight: 800;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.es-about-lead { max-width: 640px; }
.es-about-steps { margin: 16px 0 0 20px; display: grid; gap: 8px; max-width: 640px; }
.es-about-steps strong { color: var(--es-blanco); }
.es-about-faq { max-width: 640px; }
.es-about-faq dt { margin-top: 14px; color: var(--es-blanco); font-weight: 600; }
.es-about-links { margin-top: 28px; }
.es-about-links a { color: var(--es-plata); text-decoration: underline; text-underline-offset: 3px; }
.es-about-links a:hover { color: var(--es-blanco); }
/* Misma anchura que la app en pantallas grandes (va después de .es-about: si no, la base le gana). */
@media (min-width: 1100px) { .es-about { max-width: 1440px; } }

/* ── Aviso "ábrelo en computadora" ───────────────────────────────────────── */
.es-desktop-note {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin: 0;
  padding: 12px 14px;
  border: 1px solid rgba(240, 240, 238, 0.18);
  background: rgba(240, 240, 238, 0.04);
  color: var(--es-plata);
  font-size: 13px;
  line-height: 1.45;
}
.es-desktop-note strong { color: var(--es-blanco); font-weight: 600; }
.es-desktop-note-ico { flex: none; margin-top: 1px; color: var(--es-rojo-texto); }
@media (max-width: 960px), (pointer: coarse) {
  .es-desktop-note {
    border-color: var(--es-rojo);
    background: rgba(208, 43, 52, 0.12);
    font-size: 14px;
  }
}
