/* ═══════════════════════════════════════════════════════════════════════════════════════════
   EL FRAMEWORK DEL CLIENTE — el port de `Resources/Styles/Styles.xaml`.

   ⚠ ES PROPIO Y NO ES EL DEL BACKOFFICE, a pedido del dueño y con una razón técnica que lo
   sostiene: el kit del portal (`Cf`/`Cy`/`Px`) es de ESCRITORIO —ningún control suyo declara altura
   mínima y un botón mide ~30 px—, y este aplicativo se toca con el pulgar. Tampoco es Bootstrap: la
   plantilla de `dotnet new blazorwasm` lo traía y se quitó entero (44 archivos) el primer día.

   ⚠ TRES BOTONES Y UNA TARJETA, igual que en MAUI. El kit del portal ofrece 7 superficies × 7 tonos
   y no declara cuál es la pareja del producto. Acá se decide, y se decide corto: uno que empuja,
   uno que acompaña, uno que no pesa.

   ⚠⚠ Y ACÁ VIVE LO QUE MAUI DA GRATIS Y LA WEB HAY QUE PEDIR. Es la lista que separa una PWA que
   se siente aplicación de una que se siente página abierta en el navegador, y cada línea abajo
   tiene su comentario. Ninguna es cosmética.
   ═══════════════════════════════════════════════════════════════════════════════════════════ */

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

html {
  /* ⚠ NADA DE `100vh`. En un teléfono `vh` mide la ventana CON la barra de URL retraída, así que
     una pantalla de `100vh` queda más alta que el hueco visible y la app arranca con scroll que no
     debería existir. `dvh` es la altura DINÁMICA, la que de verdad hay. */
  height: 100dvh;
  /* Sin esto, un tirón hacia abajo dispara el pull-to-refresh del navegador y RECARGA la app —
     borrando el estado en memoria— justo cuando la persona quiso desplazar una lista. */
  overscroll-behavior: none;
  /* iOS/Safari agrandan el texto al girar a horizontal; en una app eso descuadra la escala. */
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  height: 100dvh;
  overflow: hidden;              /* el scroll vive DENTRO de cada cara, no en la página */
  font-family: var(--letra);
  font-size: var(--cuerpo);
  line-height: 1.45;
  /* ⚠ EXPLÍCITOS Y DESDE TOKEN. Un `body` transparente hereda el piso de quien lo aloja, y el
     resultado es el texto de un tema sobre el fondo del otro. */
  color: var(--texto);
  background: var(--fondo);
  -webkit-font-smoothing: antialiased;
}

/* ═══ EN PANTALLA GRANDE, LA APP NO SE ESTIRA ══════════════════════════════════════════════════
   Medido el 2026-09-04 en app.consumoregional.org a 1568 px: el campo del teléfono y el botón
   «Continuar» ocupaban el ancho ENTERO del monitor y el texto quedaba diminuto arriba a la
   izquierda. No es un defecto de estilo: es una interfaz pensada para el pulgar, servida en una
   pantalla catorce veces más ancha que el pulgar.

   ⚠ SE ACOTA EL CONTENEDOR, NO SE REESCRIBEN LAS PANTALLAS. Cada cara ya sabe ocupar el 100% de lo
     que le den; dándole 460 px se ven exactamente como en el teléfono, sin una sola regla nueva por
     pantalla. Tocar los anchos cara por cara sería doce lugares donde equivocarse y un dato que se
     desincroniza con el primer panel nuevo.

   ⚠ Y NO SE TOCA NADA POR DEBAJO DE 560 px: ahí `#app` sigue siendo el `body` entero, que es el caso
     de verdad —el teléfono— y el que no se puede romper por arreglar el escritorio. */
#app {
  height: 100dvh;
  overflow: hidden;
}

@media (min-width: 560px) {
  body {
    /* El piso de afuera del "teléfono". Más oscuro que el de la app para que el marco se lea. */
    background: #E7DFDD;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  @media (prefers-color-scheme: dark) { body { background: #0E0B0C; } }

  #app {
    /* 460 × 900 es un teléfono grande de verdad. `min()` con `100dvh` evita que en una laptop de
       poca altura el marco se salga de la ventana y aparezca el scroll que `overflow:hidden` de
       `body` justamente evita. */
    width: 460px;
    height: min(900px, 100dvh);
    background: var(--fondo);
    overflow: hidden;
    position: relative;
    border-radius: 22px;
    box-shadow: 0 2px 6px rgba(0,0,0,.10), 0 18px 50px rgba(0,0,0,.18);

    /* ⚠⚠ ESTA LÍNEA ES LA QUE HACE QUE EL MARCO SIRVA, y sin ella todo lo de arriba es decorado.
       Las caras se posicionan con `position: fixed`, y `fixed` se mide contra la VENTANA, no contra
       su padre: medido el 2026-09-04 con `#app` ya en 460 px, `.panel` seguía calculando 3440 px y
       se salía del marco por los cuatro costados. Un `transform` —aunque sea la identidad— convierte
       al elemento en BLOQUE CONTENEDOR de sus descendientes fijos, y recién ahí `fixed` significa
       «fijo dentro del teléfono» en vez de «fijo en el monitor».
       No se cambia `.panel` a `absolute`: son doce caras y la barra inferior, y `fixed` es correcto
       en el teléfono, que es el caso que manda. Se arregla en el contenedor, una vez. */
    transform: translateZ(0);
  }
}

/* ⚠ EL DESTELLO GRIS DEL TOQUE. Android dibuja un rectángulo de resalte sobre cualquier cosa
   tocable; es la marca visual más delatora de «esto es una página web». */
* { -webkit-tap-highlight-color: transparent; }

/* ⚠ EL CROMO NO SE SELECCIONA. Un toque sostenido sobre un botón levanta el menú de selección de
   texto del navegador. El CONTENIDO sí se selecciona —un código de premio hay que poder copiarlo—,
   así que esto va sobre los controles y no sobre `body`. */
button, .barra, .cabecera, .chip, label { user-select: none; -webkit-user-select: none; }

/* ⚠ `manipulation` APAGA DOS COSAS: el doble-toque para hacer zoom, y con él los ~300 ms que el
   navegador espera para saber si venía un segundo toque. Ese retardo es exactamente la sensación
   de «la app va lenta» que ninguna optimización de red arregla. */
button, a, input, select, textarea, .tocable { touch-action: manipulation; }

/* ⚠ EL FOCO SE VE. Se quita el anillo por defecto sólo para el puntero (`:focus-visible` no dispara
   con el dedo) y se dibuja uno propio para el teclado. Quitarlo del todo deja la app inoperable
   para quien navega con teclado, que en web es gente real y no un caso de laboratorio. */
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--primario);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ⚠ EL TÍTULO QUE RECIBE EL FOCO POR NAVEGAR NO DIBUJA ANILLO — y sin esta regla lo dibujaba en las
   SIETE caras. `App.razor:4` monta `<FocusOnNavigate Selector="h1" />`, que en cada cambio de ruta
   le pone `tabindex="-1"` al primer `<h1>` —el título de la cabecera— y lo enfoca. Eso es una
   función de accesibilidad y se queda: es lo que hace que un lector de pantalla anuncie la pantalla
   nueva, porque en una SPA la ruta cambia sin que el navegador mueva el foco a ningún lado.

   Lo que NO se queda es el anillo. Medido en vivo: al tocar «Premios» en la barra,
   `h1.matches(':focus-visible')` daba `true` y el título salía recuadrado en `--primario`. Chrome
   considera «visible» un foco PROGRAMÁTICO cuando la última interacción fue del teclado o del dedo,
   así que el recuadro aparecía en cada toque de la barra — sobre el nombre de la app, que es lo
   primero que se mira.

   ⚠ Y NO ES ACCESIBILIDAD QUE SE PIERDE, que es la duda razonable acá: con `tabindex="-1"` el
   título NO está en el orden de tabulación, así que a este foco no se puede llegar con el teclado
   nunca. El anillo no podía estar diciendo «tu teclado está acá» — sólo podía ser ruido. El
   anuncio del lector de pantalla, que es para lo que existe el foco, no depende del contorno. */
h1[tabindex="-1"]:focus,
h1[tabindex="-1"]:focus-visible { outline: none; }

/* ⚠ QUIEN PIDIÓ MENOS MOVIMIENTO RECIBE MENOS MOVIMIENTO. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

img { max-width: 100%; display: block; }


/* ══ LA ARMAZÓN ════════════════════════════════════════════════════════════════════════════════
   Un panel ocupa el alto entero; el scroll es SUYO, no de la página. Así la barra de abajo no se
   va con el desplazamiento y el rebote de iOS no arrastra el cromo. */

.panel {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  background: var(--fondo);
}

.cuerpo {
  flex: 1;
  overflow-y: auto;
  /* ⚠ El impulso nativo del scroll en iOS. Sin esto la lista frena en seco y se siente rígida. */
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  padding: 16px;
  /* ⚠ EL HUECO DE LA BARRA DE ABAJO MÁS EL INDICADOR DE INICIO. `env(safe-area-inset-bottom)` es
     la franja del gesto en un iPhone sin botón: sin sumarla, el último elemento de toda lista
     queda debajo de una barra que el sistema dibuja encima y no se puede tocar nunca. */
  padding-bottom: calc(16px + var(--alto-barra, 0px) + env(safe-area-inset-bottom, 0px));
}

.pila { display: flex; flex-direction: column; gap: 12px; }
.fila { display: flex; align-items: center; gap: 10px; }
.crece { flex: 1; min-width: 0; }   /* `min-width:0` o un texto largo revienta la fila */

/* ══ LA TARJETA ════════════════════════════════════════════════════════════════════════════════
   ⚠ Lo que de verdad la separa del fondo no es su borde: es que su PISO sea otro color. El borde
   apenas la remata. */
.hoja {
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio-tarjeta);
  padding: 16px;
}
.hoja--elevada { background: var(--elevada); }

/* ══ LOS TRES BOTONES ══════════════════════════════════════════════════════════════════════════ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: var(--alto-tactil);
  padding: 0 20px;
  border: 0;
  border-radius: var(--radio-control);
  font-family: inherit;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  background: none;
  color: inherit;
  transition: opacity .12s ease, background-color .12s ease;
}

/* ⚠ EL BOTÓN APAGADO TIENE QUE VERSE APAGADO. `Tomar()` deshabilita los botones durante cada
   llamada al servidor; sin esta regla uno inactivo se ve idéntico a uno vivo, y la persona toca
   creyendo que no pasó nada. Es la misma razón por la que el `Tema.xaml` repite su `CommonStates`
   en los tres estilos. */
.btn:disabled { opacity: .45; cursor: default; }
.btn:not(:disabled):active { opacity: .8; }

.btn--principal { background: var(--primario); color: var(--sobre-primario); }
.btn--contorno  { border: 1px solid var(--borde-fuerte); color: var(--primario); padding: 0 14px; font-size: 15px; }
.btn--fantasma  { color: var(--tenue); font-size: 15px; font-weight: 500; }
.btn--ancho     { width: 100%; }

/* ══ LOS CAMPOS ════════════════════════════════════════════════════════════════════════════════ */

.campo {
  width: 100%;
  min-height: var(--alto-tactil);
  padding: 0 14px;
  /* ⚠ 16 px NO ES ESTÉTICA: por debajo de eso iOS hace zoom al enfocar el campo y la pantalla
     salta de golpe. Es la razón número uno de que un formulario se sienta web en un teléfono. */
  font-size: var(--texto-campo);
  font-family: inherit;
  color: var(--texto);
  background: var(--superficie);
  border: 1px solid var(--borde-fuerte);
  border-radius: var(--radio-control);
}
.campo::placeholder { color: var(--sutil); }
.campo:focus-visible { border-color: var(--primario); outline-offset: 0; }
.campo--cifras { font-family: var(--letra-cifras); letter-spacing: .08em; }

/* ══ LA TIPOGRAFÍA ═════════════════════════════════════════════════════════════════════════════ */

.t-grande   { font-size: var(--titulo-grande); font-weight: 700; line-height: 1.1; letter-spacing: -.02em; }
.t-titulo   { font-size: var(--titulo);        font-weight: 700; line-height: 1.2; text-wrap: balance; }
.t-sub      { font-size: var(--subtitulo);     font-weight: 600; }
.t-menor    { font-size: var(--menor); }
.t-minimo   { font-size: var(--minimo); }
.t-tenue    { color: var(--tenue); }
.t-sutil    { color: var(--sutil); }
/* Cifras que se comparan en columna: sin ancho fijo la coma baila entre filas. */
.t-cifra    { font-variant-numeric: tabular-nums; font-family: var(--letra-cifras); }

/* ══ LA BARRA DE ABAJO ═════════════════════════════════════════════════════════════════════════
   Cuatro destinos. ⚠ El relleno de abajo suma el `safe-area`: en un teléfono sin botón físico, la
   franja del gesto de inicio se dibuja ENCIMA de la app, y sin ese hueco el último renglón de
   íconos queda debajo de algo que el sistema pinta y el dedo no alcanza. */
.barra {
  display: flex;
  background: var(--superficie);
  border-top: 1px solid var(--borde);
  padding-bottom: env(safe-area-inset-bottom, 0px);
}
.barra__item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-height: 56px;
  border: 0;
  background: none;
  color: var(--sutil);
  font-family: inherit;
  font-size: var(--minimo);
  cursor: pointer;
}
.barra__item[aria-current="page"] { color: var(--primario); font-weight: 600; }

/* ══ LOS ESTADOS DE LA PANTALLA ════════════════════════════════════════════════════════════════
   ⚠ TRES DESENLACES Y NO DOS. «Cargando», «vacío» y «falló» son cosas distintas, y colapsar las
   dos últimas es la mentira visual que el repo prohíbe por doctrina: una lista vacía porque no hay
   nada y una vacía porque la llamada murió se ven igual, y la persona espera algo que no viene. */
.vacio {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 40px 24px;
  text-align: center;
  color: var(--sutil);
}
/* ⚠ EL RESETEO DEL `<p>` VA ACÁ Y NO EN CADA CARA, PORQUE EL HUECO ERA DEL FRAMEWORK. `.vacio` es
   un flex column con `gap: 8px`, y el navegador le pone `margin-block: 1em` a cada `<p>`; en un
   contenedor flex los márgenes NO SE COLAPSAN, se SUMAN al `gap`. O sea que los dos renglones de
   TODOS los vacíos de la app —los tres desenlaces de las siete caras— quedaban con 8 + ~26 px en
   vez de los 8 declarados. Se ve como aire de más y desparejo; nunca como un error, que es por lo
   que sobrevivió a siete revisiones.
   ⚠ Y se arregla en el compartido porque el elemento es compartido: `Sorteos.razor` y
   `Premios.razor` lo tenían idéntico, y seis copias de la misma regla en seis `.razor.css` es
   exactamente la duplicación que este archivo existe para evitar. Lo PROPIO de una cara sigue
   yendo en su CSS con alcance. */
.vacio p { margin: 0; }
.vacio--falla { color: var(--error-texto); }


/* ══ EL ARRANQUE Y LA CAÍDA ════════════════════════════════════════════════════════════════════
   ⚠ ESTAS DOS REGLAS VOLVIERON DESPUÉS DE HABERSE PERDIDO, y la forma en que se perdieron vale
   más que el arreglo: al vaciar este archivo para sacarle Bootstrap a la plantilla, se fueron con
   él los estilos de `#blazor-error-ui` y de `.loading-progress`, que la plantilla también pone
   acá. Nada avisó — el `#blazor-error-ui` quedó en `display: block` permanente, o sea la barra de
   «algo se rompió» encendida SIEMPRE, y sólo no se veía porque `.panel` es `position: fixed` y la
   tapa. Se cazó leyendo el árbol accesible de la página corriendo, no el CSS. */

/* ⚠ OCULTO POR DEFECTO. Blazor lo enciende poniéndole `style.display = 'block'` cuando algo se
   rompe de verdad; sin este `none`, la app arranca gritando que falló. */
#blazor-error-ui {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1000;   /* por encima de `.panel`, o el aviso queda debajo de lo que falló */
  padding: 14px 20px calc(14px + env(safe-area-inset-bottom, 0px));
  background: var(--error-tinte);
  color: var(--error-texto);
  font-size: var(--menor);
  box-shadow: 0 -2px 12px rgb(0 0 0 / .18);
}
#blazor-error-ui .reload { color: var(--error-texto); text-decoration: underline; }
#blazor-error-ui .dismiss { float: right; cursor: pointer; padding: 0 6px; }

/* El anillo del arranque: los ~2 s en que WebAssembly se descarga y compila. Sin esto la app abre
   en blanco, que en un teléfono se lee como «no cargó» y provoca el recargue. */
.loading-progress {
  position: fixed;
  inset: 0;
  margin: auto;
  width: 8rem;
  height: 8rem;
}
.loading-progress circle {
  fill: none;
  stroke: var(--primario-tinte);
  stroke-width: .5rem;
  transform-origin: 50% 50%;
  transform: rotate(-90deg);
}
.loading-progress circle:last-child {
  stroke: var(--primario);
  /* `--blazor-load-percentage` lo va escribiendo el runtime: la barra es real, no una animación. */
  stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8) 500%;
  transition: stroke-dasharray .05s ease-in-out;
}
.loading-progress-text {
  position: fixed;
  inset: 0;
  display: grid;
  place-content: center;
  color: var(--tenue);
  font-size: var(--menor);
}
.loading-progress-text::after {
  content: var(--blazor-load-percentage-text, "Abriendo…");
}
