/* saimc-public.css — subconjunto de saimc-material.css para la capa pública
   ------------------------------------------------------------------
   `_public_shell.html` cargaba la hoja entera del operador (~28 KB en disco,
   ~6 KB gzipeados) para usar solo los tokens, el reset base, las tres fuentes
   que la home pública necesita y `.m3-subtitle`. Todo lo demás —topnav,
   curaduría, tablas, botones, chips, snackbar— es de `catalog`/`curation`/
   `control` y nunca se ejecuta en una página pública de solo lectura.

   Si una vista pública nueva necesita una clase del operador, se agrega acá
   explícitamente: el criterio es "lo que usa la home pública", no "lo que
   podría llegar a usar".

   Ese criterio manda sobre los COMPONENTES, no sobre los tokens: desde el
   2026-09-04 el bloque `:root` lo genera `tools/build_css_tokens.py` desde
   `src/saimc/web/design_tokens.css`, completo y para las dos hojas. Los 13
   tokens que antes vivían acá eran copia manual y se habían desincronizado
   en silencio. */

/* Fuente única: `src/saimc/web/design_tokens.css`. Recibe el bloque COMPLETO,
   no los 13 tokens que antes se copiaban a mano. Medido el 2026-09-04: el
   bloque son 1.567 B crudos y esta hoja pasa de 1.129 a 1.790 B gzipeados,
   +661 B por visita nueva. Se paga porque el recorte de esta hoja siempre fue
   de componentes —topnav, tablas, botones: de ahí salían los ~22 KB— y porque
   la lista corta mantenida a mano ya se había desincronizado sin que nada lo
   dijera. Los comentarios de cada token se quedan en la fuente y no viajan. */
/* >>> BEGIN tokens generados — los edita tools/build_css_tokens.py, no vos */
:root {
  --m3-primary: #292524;
  --m3-primary-hover: #0c0a09;
  --m3-on-primary: #ffffff;
  --m3-primary-container: #f0efed;
  --m3-on-primary-container: #292524;
  --m3-primary-fixed-dim: #e7e5e4;

  --m3-tertiary: #4b607c;

  --m3-bg: #f5f5f5;
  --m3-surface: #ffffff;
  --m3-surface-container: #f0efed;
  --m3-surface-container-high: #e7e5e4;
  --m3-surface-hover: #fafafa;
  --m3-surface-variant: #f0efed;

  --m3-on-surface: #0c0a09;
  --m3-on-surface-variant: #4e4e4e;
  --m3-outline: #6f6962;
  --m3-outline-variant: #d6d3d1;
  --m3-divider: #e7e5e4;
  --m3-disabled: #a8a29e;

  --m3-error: #dc2626;
  --m3-error-container: #fee2e2;
  --m3-on-error-container: #7f1d1d;
  --m3-success-container: #c3ecc9;
  --m3-on-success-container: #05210d;
  --m3-warn-container: #ffe6a8;
  --m3-on-warn-container: #3d2f00;
  --m3-warn: #e0a700;
  --m3-success: #1b6b4f;
  --m3-warn-strong: #8a6212;
  --m3-warn-wash: #fdf8ec;
  --m3-error-wash: #fef2f2;

  --m3-prov-0: #3f5fd6;  --m3-prov-0-soft: #dbe4ff;  --m3-prov-0-wash: #f6f8ff;
  --m3-prov-1: #7048e8;  --m3-prov-1-soft: #e5dbff;  --m3-prov-1-wash: #f9f6ff;
  --m3-prov-2: #0b7285;  --m3-prov-2-soft: #c5f6fa;  --m3-prov-2-wash: #f0fbfc;
  --m3-prov-9: #6f7979;  --m3-prov-9-soft: #e3e9ea;  --m3-prov-9-wash: #f7fafa;

  --m3-acento-0: #b7791f;
  --m3-acento-1: #55672f;
  --m3-acento-2: #a8432b;
  --m3-acento-3: #7a4a52;

  --m3-space-1: 4px;
  --m3-space-2: 8px;
  --m3-space-3: 12px;
  --m3-space-4: 16px;
  --m3-space-5: 20px;
  --m3-space-6: 24px;
  --m3-space-7: 28px;
  --m3-space-8: 32px;
  --m3-space-14: 56px;
  --m3-space-24: 96px;

  --m3-radius-xs: 4px;
  --m3-radius-sm: 8px;
  --m3-radius-md: 12px;
  --m3-radius-lg: 16px;
  --m3-radius-full: 100px;

  --m3-elev-1: 0 1px 2px rgba(12, 10, 9, .06);
  --m3-elev-2: 0 1px 2px rgba(12, 10, 9, .12);
  --m3-elev-3: 0 8px 24px rgba(12, 10, 9, .12);

  --m3-font: Inter, "Noto Color Emoji", system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
  --m3-mono: "Roboto Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  --ph-font-heading: "Guani Display", Inter, system-ui, sans-serif;
  --ph-accent: #292524;
  --ph-on-accent: #ffffff;
  --ph-accent-wash: rgba(41, 37, 36, .06);
  --ph-accent-2: #6f9bc4;
  --ph-accent-2-line: #3f6a93;
  --ph-median: #a7e5d3;
  --ph-price-low: #dbe7f3;
  --ph-price-high: #292524;
  --ph-elev: 0 1px 2px rgba(12, 10, 9, .05), 0 4px 14px rgba(12, 10, 9, .05);
  --ph-scrim: rgba(12, 10, 9, .35);
  --ph-sheet-shadow: 0 -8px 30px rgba(12, 10, 9, .16);
  --ph-radius-lg: 24px;
  --ph-radius-sm: 14px;
  --ph-side-w: clamp(30rem, 32vw, 36rem);
  --ph-bar-h: 9rem;
  --ph-barra-h: calc(4.25rem + env(safe-area-inset-bottom, 0px));
  --ph-dusk-noche: #0b2c43;
  --ph-dusk-indigo: #2f3a80;
  --ph-dusk-mamey: #c8507a;
  --ph-dusk-flama: #f08a3c;
  --ph-dusk-guayaba: #ffd072;
  --ph-dusk-guayaba-halo: rgba(255, 208, 114, .35);
  --ph-dusk-guayaba-borde: rgba(255, 208, 114, 0);
  --ph-dusk-sil-lejos: #3b3f86;
  --ph-dusk-sil-medio: #1b3159;
  --ph-dusk-sil-cerca: #0a2233;
  --ph-dusk-tinta: #08202f;
  --ph-dusk-texto: #ffffff;
  --ph-dusk-texto-suave: rgba(255, 255, 255, .88);
  --ph-dusk-vidrio: rgba(255, 255, 255, .14);
  --ph-dusk-vidrio-borde: rgba(255, 255, 255, .3);
  --ph-dusk-vidrio-oscuro: rgba(8, 32, 47, .62);
  --ph-dusk-sombra: rgba(0, 0, 0, .35);
  --ph-dusk-manana: #fde7bf;
  --ph-dusk-sombra-dia: rgba(11, 44, 67, .14);
  --ph-dusk-brillo: rgba(255, 255, 255, .55);
  --ph-orb-mint: #a7e5d3;
  --ph-orb-peach: #f4c5a8;
  --ph-orb-lavender: #c8b8e0;
  --ph-orb-sky: #a8c8e8;
  --ph-orb-rose: #e8b8c4;
  --ph-orb-opacity: .55;
}

:root[data-theme="dark"] {
  --m3-primary: #f5f5f5;
  --m3-primary-hover: #ffffff;
  --m3-on-primary: #0c0a09;
  --m3-primary-container: #292524;
  --m3-on-primary-container: #f5f5f5;
  --m3-tertiary: #b7c8e4;

  --m3-bg: #0c0a09;
  --m3-surface: #1c1917;
  --m3-surface-container: #231f1d;
  --m3-surface-container-high: #2e2926;
  --m3-surface-hover: #262220;
  --m3-surface-variant: #2e2926;

  --m3-on-surface: #f5f5f5;
  --m3-on-surface-variant: #a8a29e;
  --m3-outline: #8b857f;
  --m3-outline-variant: #3a3532;
  --m3-divider: #292524;
  --m3-disabled: #6b6560;

  --m3-error: #f87171;
  --m3-error-container: #5c1a1a;
  --m3-on-error-container: #ffdad6;
  --m3-success-container: #1f4d2c;
  --m3-on-success-container: #c3ecc9;
  --m3-warn-container: #4a3b00;
  --m3-on-warn-container: #ffe6a8;
  --m3-warn: #f0bb2e;
  --m3-success: #6fdba3;
  --m3-warn-strong: #f0bb2e;
  --m3-warn-wash: #241f0d;
  --m3-error-wash: #2b1616;

  --m3-prov-0: #93a9ff;  --m3-prov-0-soft: #1f2c54;  --m3-prov-0-wash: #12172a;
  --m3-prov-1: #bda3ff;  --m3-prov-1-soft: #2d2154;  --m3-prov-1-wash: #191428;
  --m3-prov-2: #6ad7e6;  --m3-prov-2-soft: #103439;  --m3-prov-2-wash: #0d1f22;
  --m3-prov-9: #a3adad;  --m3-prov-9-soft: #2a3434;  --m3-prov-9-wash: #1a2222;

  --m3-acento-0: #d9a54c;
  --m3-acento-1: #8ba15c;
  --m3-acento-2: #d97a63;
  --m3-acento-3: #b97e88;

  --ph-accent: #f5f5f5;
  --ph-on-accent: #0c0a09;
  --ph-accent-wash: rgba(245, 245, 245, .08);
  --ph-accent-2: #8fb6dc;
  --ph-accent-2-line: #cfe3f5;
  --ph-median: #a7e5d3;
  --ph-price-low: #1f2a35;
  --ph-price-high: #cfe0f1;
  --ph-orb-mint: #2f8f78;
  --ph-orb-peach: #b8754d;
  --ph-orb-lavender: #6f5aa8;
  --ph-orb-sky: #3b74ac;
  --ph-orb-rose: #a85a73;
  --ph-orb-opacity: .3;
}
/* <<< END tokens generados */

/* Landing congelado (2026-10-13). El resto del sistema pasó al estilo
   ElevenLabs (docs/decisions.md, 2026-10-13); el landing queda fuera por
   pedido del operador, así que acá se fijan los valores anteriores de los
   tokens que cambiaron, solo dentro de `.lp`. Vive en la hoja y no en la
   plantilla porque el test de la landing prohíbe colores a mano en su
   `<style>`. Cuando se lo lleve al estilo nuevo, se borra este bloque. */
.lp {
  --m3-primary: #00696e;
  --m3-primary-hover: #00565b;
  --m3-on-primary: #ffffff;
  --m3-primary-container: #cce8e9;
  --m3-on-primary-container: #041f21;
  --m3-primary-fixed-dim: #b8ecef;
  --m3-bg: #f2f6f6;
  --m3-surface: #ffffff;
  --m3-surface-container: #eef4f4;
  --m3-surface-container-high: #e3e9ea;
  --m3-surface-hover: #f7fafa;
  --m3-surface-variant: #dbe5e5;
  --m3-on-surface: #191c1c;
  --m3-on-surface-variant: #3f4949;
  --m3-outline: #6f7979;
  --m3-outline-variant: #c4cfcf;
  --m3-divider: #eef2f2;
  --m3-disabled: #8a9494;
  --m3-error: #ba1a1a;
  --m3-error-container: #ffdad6;
  --m3-on-error-container: #410002;
  --m3-error-wash: #fdeced;
  --m3-elev-1: 0 1px 3px rgba(0, 0, 0, .08);
  --m3-elev-2: 0 1px 2px rgba(0, 0, 0, .2);
  --m3-elev-3: 0 4px 12px rgba(0, 0, 0, .18);
  --m3-font: Roboto, "Noto Color Emoji", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --ph-font-heading: "Alice", Georgia, serif;
  --ph-accent: #05668d;
  --ph-on-accent: #ffffff;
  --ph-accent-wash: rgba(5, 102, 141, .1);
  --ph-accent-2: #028090;
  --ph-accent-2-line: #015a63;
  --ph-median: #aec092;
  --ph-price-low: #e8f1f6;
  --ph-price-high: #05668d;
  --ph-elev: 0 1px 2px rgba(5, 32, 48, .08), 0 4px 14px rgba(5, 32, 48, .06);
  --ph-scrim: rgba(5, 32, 48, .35);
  --ph-sheet-shadow: 0 -8px 30px rgba(5, 32, 48, .18);
}
:root[data-theme="dark"] .lp {
  --m3-primary: #6cd4d9;
  --m3-primary-hover: #58c1c6;
  --m3-on-primary: #003337;
  --m3-primary-container: #004f54;
  --m3-on-primary-container: #b8ecef;
  --m3-bg: #0e1414;
  --m3-surface: #141b1b;
  --m3-surface-container: #1a2222;
  --m3-surface-container-high: #222d2d;
  --m3-surface-hover: #1e2727;
  --m3-surface-variant: #2a3535;
  --m3-on-surface: #e1e3e3;
  --m3-on-surface-variant: #bdc7c7;
  --m3-outline: #8a9494;
  --m3-outline-variant: #3f4949;
  --m3-divider: #2a3535;
  --m3-disabled: #6b7575;
  --m3-error: #ffb4ab;
  --ph-accent: #7ec8ea;
  --ph-on-accent: #00344a;
  --ph-accent-wash: rgba(126, 200, 234, .14);
  --ph-accent-2: #4fb8c7;
  --ph-accent-2-line: #bdf0f5;
  --ph-median: #c3d4a8;
  --ph-price-low: #16303c;
  --ph-price-high: #7ec8ea;
}

* { box-sizing: border-box; }

html, body { margin: 0; padding: 0; }

body {
  background: var(--m3-bg);
  color: var(--m3-on-surface);
  font: 400 14px/20px var(--m3-font);
  -webkit-font-smoothing: antialiased;
}

/* El acento de la capa pública es `--ph-accent` (azul), NO `--m3-primary`
   (teal). El 2026-09-04 se decidió que son dos colores de marca deliberados
   —el visitante ve azul, el operador teal— y esta hoja seguía pintando cada
   enlace y cada anillo de foco con el teal del operador. La home no lo
   delataba porque no tiene enlaces sueltos en el cuerpo: se veía en
   `/anuncio/<id>` («← Volver a precios») y en el pie del shell. */
a { color: var(--ph-accent); text-decoration: none; }
a:hover { color: var(--ph-accent); text-decoration: underline; }

:focus-visible { outline: 2px solid var(--ph-accent); outline-offset: 2px; }
::selection { background: var(--m3-surface-container-high); }

/* Roboto, Roboto Mono y Alice vendorizadas en /static/fonts, subset latin.
   Material Symbols Rounded también: los chips de producto de la home usan sus
   ligaduras (`product_nav.PRODUCT_ICONS`). Es el mismo archivo subseteado que
   sirve la capa del operador; se regenera con `python tools/build_icon_font.py`
   y el `?v=` sube cada vez que cambia el subconjunto. */
@font-face {
  font-family: "Material Symbols Rounded";
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url("/static/fonts/material-symbols-rounded.woff2?v=3") format("woff2");
}

.m3-icon {
  font-family: "Material Symbols Rounded";
  font-weight: 400;
  font-size: 20px;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  white-space: nowrap;
  font-feature-settings: "liga";
  -webkit-font-feature-settings: "liga";
  vertical-align: middle;
  user-select: none;
}
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/roboto.woff2") format("woff2");
}

@font-face {
  font-family: "Roboto Mono";
  font-style: normal;
  font-weight: 100 700;
  font-display: swap;
  src: url("/static/fonts/roboto-mono.woff2") format("woff2");
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/inter.woff2?v=1") format("woff2");
}
/* Mismo archivo que Inter, declarado en peso 300 fijo: el navegador acota el
   eje variable al rango declarado, así que todo lo que pida
   `--ph-font-heading` sale en Light (la firma editorial del estilo) sin
   tocar cada regla que dice `font-weight: 400`. No suma una descarga. */
@font-face {
  font-family: "Guani Display";
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url("/static/fonts/inter.woff2?v=1") format("woff2");
}
@font-face {
  font-family: "Alice";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/alice.woff2") format("woff2");
}

/* Noto Color Emoji, el mismo archivo que sirve la capa del operador. La home
   pública casi no trae emoji, pero un nombre de producto puede tenerlo y la
   familia ya está en el stack de `--m3-font`: sin este `@font-face` la home
   caería al emoji del sistema, que en el Linux del operador no existe. Se
   regenera con `python tools/build_emoji_font.py`. */
@font-face {
  font-family: "Noto Color Emoji";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/noto-color-emoji.woff2") format("woff2");
}

.m3-subtitle {
  margin: var(--m3-space-1) 0 0; font-size: 14px; line-height: 20px;
  color: var(--m3-on-surface-variant); max-width: 62ch; text-wrap: pretty;
}

/* ── Shell público (`_public_shell.html`) ─────────────────────────────────
   Estas seis clases estaban en el markup del shell desde que existe la capa
   pública y NO tenían ninguna regla en ninguna hoja: `.m3-public`,
   `.m3-public-top`, `.m3-kicker`, `.m3-title`, `.m3-public-main` y
   `.m3-public-foot`.

   Nadie lo vio porque las dos vistas que más se miran lo tapan sin quererlo:
   `public_home.html` sobrescribe `{% block heading %}` y `{% block subtitle %}`
   en vacío —su titular vive en la barra del lienzo— y trae ~600 líneas de
   `<style>` propio. La única vista que renderiza el shell tal cual es
   `/anuncio/<id>` (`public_evidence.html`), y salía a sangre contra los
   bordes de la ventana, con el `<h1>` en el default del navegador (Times
   bold) en vez del serif editorial.

   La voz es la misma que ya usa la home en sus ventanas: `--ph-font-heading`
   (Alice) en peso 400 para el titular, el kicker en versalitas chicas, y el
   pie separado por un filete. */
.m3-public {
  max-width: 64rem;
  /* `min-height` + columna para que el pie quede abajo también en una página
     corta como `/anuncio/<id>`, sin `position: absolute`. */
  min-height: 100vh; min-height: 100dvh;
  margin: 0 auto;
  padding: 0 clamp(1rem, 4vw, 2.5rem) var(--m3-space-8);
  display: flex; flex-direction: column;
}

.m3-public-top { padding: clamp(1.75rem, 5vw, 3rem) 0 var(--m3-space-5); }

/* Fila del kicker: el toggle de tema vive acá y no suelto en `.m3-public-top`
   para que `public_home.html` —que esconde todo `.m3-public-top` porque su
   titular vive en la barra del lienzo (ver comentario más arriba)— no se
   quede sin forma de cambiar de tema. Esa vista mete el suyo propio dentro
   del dock del compás (`.ph-menu-item`, `public_home.html`); los dos
   comparten la clase `.theme-toggle` que cablea `_theme_toggle_script.html`. */
.m3-public-top-row { display: flex; align-items: center; justify-content: space-between; gap: var(--m3-space-3); }

.ph-theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none; width: 40px; height: 40px; border-radius: var(--ph-radius-sm);
  background: transparent; border: none; padding: 0;
  color: var(--m3-on-surface-variant); cursor: pointer;
}
.ph-theme-toggle:hover { background: var(--m3-surface-container-high); }

/* ── Blanco táctil de 44px (`.ph-tap-44`) ──────────────────────────────────
   Medido con Chromium a 390x844 contra el sitio en vivo (2026-09-17): los
   controles de la capa pública quedaban entre 15 y 35px de alto. El mapa
   tenía 11 por debajo del mínimo, el tablero 378 y el detalle de producto 99
   —26 de ellos los enlaces «ver anuncio», de 65x15—.

   Agranda el área sensible sin mover el dibujo. Es deliberado: la isla de
   filtros flota sobre el mapa —cada píxel que crece es mapa que tapa, y en un
   teléfono acostado el alto disponible es ~390px— y el operador había pedido
   expresamente compactar esos controles (decisions.md, 2026-09-14).

   `<select>` e `<input>` son elementos reemplazados y no admiten `::after`:
   esos crecen de verdad, con `min-height` en su propia regla.

   Nació en el `<style>` de `public_home.html` y se mudó acá el 2026-09-17,
   mismo camino que ya recorrieron `.ph-card` y `.ph-coverage`: dejó de ser
   exclusiva del mapa. */
.ph-tap-44 { position: relative; }
.ph-tap-44::after {
  content: ""; position: absolute; left: 0; right: 0; top: 50%;
  height: 44px; transform: translateY(-50%);
  /* No pinta nada: sólo recibe el toque. */
}
/* Para lo que tampoco llega a 44 de ancho (un ícono suelto), el área crece
   en los dos ejes. */
.ph-tap-44.ph-tap-ancho::after { left: 50%; right: auto; width: 44px; transform: translate(-50%, -50%); }

/* ── Tarjeta de producto (`.ph-card`) ──────────────────────────────────────
   Vivía solo en el `<style>` de `public_home.html` («mientras sea la única
   vista pública»). Dejó de serlo el 2026-09-16: el modo Tablero
   (`public_dashboard.html`) reusa la misma tarjeta vía
   `_product_card_item.html` sin cargar ese `<style>` — moverla acá es lo que
   hace que las dos vistas la pinten igual, en vez de que la segunda copia el
   marcado y no el CSS (mismo tipo de bug que `--m3-tertiary` citado sin
   definir, 2026-09-04). `.ph-note` y `.ph-chip` viajan con ella por la misma
   razón: la nota de «muestra en formación» y el rótulo de estado del pie de
   cada tarjeta. */
.ph-note { margin: 0; font-size: .8125rem; line-height: 1.6;
           color: var(--m3-on-surface-variant); text-wrap: pretty; }

/* Campo de texto de la capa pública (2026-09-16, caja de búsqueda del
   tablero): NO reusa `.m3-input` —esa vive solo en saimc-material.css, la
   hoja del operador— y copiarla tal cual traería su anillo de foco en
   `--m3-primary` (teal), rompiendo la separación de acento ya decidida
   más arriba en este archivo (`--ph-accent`, azul, es el de esta hoja). */
.ph-input { width: 100%; padding: var(--m3-space-3) var(--m3-space-4);
            font: 400 15px var(--m3-font); color: var(--m3-on-surface);
            background: var(--m3-surface);
            border: 1px solid var(--m3-outline); border-radius: var(--m3-radius-xs); }
.ph-input:hover { border-color: var(--m3-on-surface); }
.ph-input:focus { border-color: var(--ph-accent); border-width: 2px; padding: 11px 15px; outline: none; }

/* Oculta visualmente sin sacar del árbol de accesibilidad —un `<label>`
   que ya dice lo mismo que el `placeholder` del campo, por ejemplo—.
   Mismo nombre que ya usa `docs_index.html`, pero esa plantilla vive en
   `saimc-material.css` y esta clase faltaba ahí también: se agrega acá
   porque es lo que este slice necesita, no se toca la hoja del operador. */
.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; }

.ph-surface { border-radius: var(--ph-radius-sm); background: var(--m3-surface);
              box-shadow: var(--ph-elev); }

/* `auto-fit` + `minmax` se acomoda solo al ancho del contenedor —ventana
   flotante o sección del tablero—, sin corte responsive propio. */
.ph-cards { display: grid; gap: .8rem; padding: 0; margin: 0; list-style: none;
            grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }
.ph-card { padding: 1rem 1.1rem .9rem; }
.ph-card-top { display: flex; align-items: baseline; justify-content: space-between; gap: .75rem; }
/* Ícono + nombre como un solo grupo: `.ph-card-top` reparte dos extremos con
   `space-between` — un tercer hijo suelto (el ícono) lo empujaría al medio.
   El ancla visual es `.ph-fpanel-illustration`/`.ph-fpanel-icon`, la misma
   ilustración/glifo que ya usaba el panel flotante, no un ícono nuevo. */
.ph-card-title { display: inline-flex; align-items: center; gap: .4rem; min-width: 0; }
.ph-fpanel-icon { font-size: 20px; color: var(--ph-accent); align-self: center; }
.ph-fpanel-illustration { width: 20px; height: 20px; color: var(--ph-accent); align-self: center; flex: none; }
.ph-card-product { font-family: var(--ph-font-heading); font-size: 1.25rem; }
.ph-card-place { font-size: .6875rem; letter-spacing: .1em; text-transform: uppercase; opacity: .65; }
/* `format_label`/`variant_label` (card_view.py): metadata condicional entre
   el titular y el precio — «Bolsa 1kg», «girasol Vitalín importado». Mismo
   tratamiento que `.ph-card-median`: texto secundario, no un chip — no son
   un estado, son una aclaración. */
.ph-card-format, .ph-card-variant { margin-top: .3rem; font-size: .8125rem; opacity: .75; }
.ph-card-range { display: flex; align-items: baseline; gap: .4rem; margin-top: .8rem;
                 font-variant-numeric: tabular-nums; }
.ph-card-range .num { font-family: var(--ph-font-heading); font-size: 1.65rem; line-height: 1;
                       color: var(--ph-accent); }
.ph-card-range .sep { opacity: .45; font-size: 1.1rem; }
.ph-card-unit { font-size: .75rem; opacity: .65; }
.ph-card-median { margin-top: .45rem; font-size: .8125rem; opacity: .75; }
/* «¿Sube o baja?» (spec.md §6.7): mismo número sin color que
   `.ph-cifra-cambio` del panel flotante, reusado tal cual. */
.ph-cifra-cambio { font-size: .8125rem; font-weight: 500; color: var(--m3-on-surface); }
.ph-card-trend { margin-top: .3rem; }
.ph-card-sample { display: flex; align-items: center; justify-content: space-between; gap: .75rem;
                   margin-top: .8rem; padding-top: .65rem; border-top: 1px solid var(--m3-divider); }
.ph-card-n { font-weight: 600; font-variant-numeric: tabular-nums; }
.ph-chip { flex: none; white-space: nowrap; font-size: .6875rem; letter-spacing: .04em;
           text-transform: uppercase; padding: .3rem .75rem; border-radius: 999px;
           background: var(--ph-accent-wash); color: var(--ph-accent); }

/* Recorrido hacia atrás hasta el anuncio (§6.5, 2026-09-17): migradas desde
   el `<style>` de `public_home.html`, donde vivían asumiendo que solo esa
   plantilla pintaba `_product_card_item.html` — dejó de ser cierto con el
   tablero. `.ph-table-card` es un contenedor de SCROLL, no una superficie
   (`.ph-evidence-card`, más abajo en esta hoja, es la tarjeta con fondo y
   sombra de `/anuncio/<id>`; nombre distinto a propósito, ver su propio
   comentario). */
.ph-table-card { overflow-x: auto; }
.ph-coverage { width: 100%; border-collapse: collapse; font-size: .875rem; }
.ph-coverage caption { text-align: left; padding: .9rem 1rem 0; font-size: .8125rem;
                       color: var(--m3-on-surface-variant); }
/* Padding vertical a .75rem (2026-09-17, mobile-first): con .5rem la fila
   medía 37px de alto, por debajo del blanco táctil de 44px — y es la fila
   que trae el enlace «ver anuncio» (65x15 medido, el más chico de todo el
   detalle de producto). Se sube el padding de toda la fila en vez de
   extender el área del enlace con un `::after` (patrón de `.ph-tap-44`, más
   abajo en esta hoja): las filas van pegadas sin espacio entre sí, y
   extender solo el enlace habría invadido la fila vecina. Sube el alto de
   TODAS las columnas —día y precio también—, que es preferible a una fila
   dispareja. */
.ph-coverage th, .ph-coverage td { text-align: left; padding: .75rem .7rem;
               border-bottom: 1px solid var(--m3-divider); }
.ph-coverage th:last-child, .ph-coverage td:last-child { padding-right: 1rem; }
.ph-coverage th:first-child, .ph-coverage td:first-child { padding-left: 1rem; }
.ph-coverage td.num { text-align: right; font-variant-numeric: tabular-nums; }
.ph-coverage tr.is-sin-datos { opacity: .55; }
.ph-evo-anuncios summary { cursor: pointer; font-size: .8125rem; font-weight: 600; color: var(--ph-accent); }
.ph-evo-anuncios .ph-table-card { max-height: 16rem; overflow-y: auto; margin-top: .4rem; }

/* Identidad de provincia por posición en el catálogo (Dirección D, agrupar
   el tablero, 2026-09-16): `card_view.province_color_slots` decide el slot
   (0/1/2/9), estas cuatro clases son la única forma de citarlo en CSS.

   Nunca la variante corta-el-token-a-mano de armar el nombre completo del
   token adentro de un atributo `style`: Jinja interpolando el número deja
   el prefijo `--m3-prov-` pegado a una llave `{{` en el HTML servido, y
   `TestTokensMaterial` escanea el texto crudo de cada archivo —comentarios
   incluidos, ya lo probó esta misma frase— buscando exactamente ese
   prefijo sin número como un token citado y nunca definido.

   Mismo mecanismo que `.m3-slot-N` en saimc-material.css (la matriz de
   `/control`): `--prov` es genérico, se fija una vez acá y CUALQUIER hijo
   lo hereda solo — el índice de salto y el borde de `.ph-card` dentro de
   cada `<details>` no repiten la clase. */
.ph-prov-0 { --prov: var(--m3-prov-0); }
.ph-prov-1 { --prov: var(--m3-prov-1); }
.ph-prov-2 { --prov: var(--m3-prov-2); }
.ph-prov-9 { --prov: var(--m3-prov-9); }

/* Mismo mecanismo que la hoja del operador (saimc-material.css): dos
   `.m3-icon` estáticos por botón, CSS alterna cuál se ve — el subsetter de
   íconos solo puede ver nombres literales en las plantillas, no un
   `textContent` puesto por JS en runtime. */
.theme-toggle-icon-light { display: none; }
:root[data-theme="dark"] .theme-toggle-icon-dark { display: none; }
:root[data-theme="dark"] .theme-toggle-icon-light { display: inline; }

/* Mismo tratamiento que `.ph-win-tag` de la home: versalita chica y espaciada
   que dice el contexto sin competir con el titular. */
.m3-kicker {
  font-size: 11px; line-height: 16px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--m3-on-surface-variant);
}

.m3-title {
  margin: var(--m3-space-2) 0 0;
  font-family: var(--ph-font-heading); font-weight: 400;
  /* Escala fluida: 26px en teléfono, 38px en escritorio. Sin `clamp` el
     titular de `/anuncio/<id>` («El anuncio detrás de este precio») envuelve
     a tres líneas en 360px. */
  font-size: clamp(1.625rem, 1.1rem + 2.2vw, 2.375rem);
  line-height: 1.1; letter-spacing: -.02em;
  color: var(--m3-on-surface);
  max-width: 34ch; text-wrap: balance;
}

/* Orbes de atmósfera (estilo ElevenLabs, 2026-10-13): el único «color» de la
   cabecera son manchas pastel difusas detrás del titular. Decoran; el texto
   se lee sobre el lienzo (la opacidad baja en oscuro, ver `--ph-orb-opacity`). */
.m3-public-top { position: relative; isolation: isolate; }
.m3-public-top::before {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  inset: -30% -12% -20% 30%;
  background:
    radial-gradient(40% 55% at 30% 40%, var(--ph-orb-mint), transparent 70%),
    radial-gradient(35% 50% at 62% 30%, var(--ph-orb-peach), transparent 70%),
    radial-gradient(38% 55% at 80% 62%, var(--ph-orb-lavender), transparent 70%),
    radial-gradient(34% 50% at 48% 78%, var(--ph-orb-sky), transparent 70%);
  filter: blur(28px); opacity: var(--ph-orb-opacity);
}

/* La misma atmósfera para el tablero (`.ph-layout`, y `.ph-atmosfera` para
   quien la quiera suelta).
   Queda dentro del ancho del contenedor (nada de márgenes negativos: un
   elemento difuminado que desborda abre scroll horizontal en el teléfono). */
.ph-atmosfera, .ph-layout { position: relative; isolation: isolate; }
.ph-atmosfera::before, .ph-layout::before {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  inset: -8px 0 auto 0; height: 360px;
  background:
    radial-gradient(28% 58% at 68% 22%, var(--ph-orb-peach), transparent 70%),
    radial-gradient(24% 55% at 82% 50%, var(--ph-orb-lavender), transparent 70%),
    radial-gradient(34% 60% at 58% 8%, var(--ph-orb-mint), transparent 70%),
    radial-gradient(30% 55% at 30% 5%, var(--ph-orb-sky), transparent 70%);
  filter: blur(36px); opacity: var(--ph-orb-opacity);
  -webkit-mask-image: linear-gradient(#000 40%, transparent);
  mask-image: linear-gradient(#000 40%, transparent);
}

.m3-public-main { flex: 1; min-width: 0; }

.m3-public-foot {
  margin-top: var(--m3-space-8); padding-top: var(--m3-space-5);
  border-top: 1px solid var(--m3-divider);
  font-size: 12px; line-height: 18px;
  color: var(--m3-on-surface-variant);
}
.m3-public-foot p { margin: 0 0 var(--m3-space-2); max-width: 62ch; text-wrap: pretty; }
.m3-public-foot p:last-child { margin-bottom: 0; }
/* El pie es texto secundario: el enlace hereda su gris y se distingue por el
   subrayado, en vez de meter un azul saturado en el bloque más apagado. */
.m3-public-foot a { color: inherit; text-decoration: underline; }

/* ── Tarjeta de evidencia (`/anuncio/<id>`) ───────────────────────────────
   Clase propia y NO `.ph-table-card`: ese nombre ya existe en el `<style>`
   de la home con otro trabajo (contenedor de scroll de una tabla, muchas
   veces anidado adentro de una ventana que ya tiene superficie). Darle fondo
   y sombra al nombre compartido habría puesto tarjeta sobre tarjeta en la
   home. Misma superficie que `.ph-win`/`.ph-fpanel`, eso sí. */
.ph-evidence-card {
  border-radius: var(--ph-radius-sm);
  background: var(--m3-surface);
  box-shadow: var(--ph-elev);
  padding: clamp(1rem, 3vw, 1.5rem);
  overflow-x: auto;
}
.ph-evidence-text {
  margin: 0;
  font: 400 14px/22px var(--m3-mono);
  /* El anuncio se muestra tal como se publicó: envuelve, no se recorta ni
     abre scroll horizontal, y una palabra sin espacios (una URL pegada) se
     parte antes que desbordar. */
  white-space: pre-wrap; word-break: break-word;
}

.m3-public-back { margin-top: var(--m3-space-5); }

/* ── Gráficos de precio (movidos desde `public_home.html`, 2026-09-18) ──
   El tablero (dirección C) muestra el detalle de un producto con los
   mismos parciales que el mapa (`_province_compare`, `_slope_chart`,
   `_steps_chart`, `_calendar_chart`, `_chart_help`). Su CSS vivía solo en
   el `<style>` del mapa, así que en el tablero se habrían pintado sin
   estilo — mismo motivo por el que `.ph-card` se mudó acá el 2026-09-16.
   `--prov` lo fijan las clases `.ph-prov-N` de esta hoja; el mapa las
   redefine en su `<style>` con su propia paleta. */
.ph-empty { margin: 0; font-size: .9375rem; color: var(--m3-on-surface-variant); }

.ph-rango { display: block; width: 100%; height: auto; overflow: visible; }
.ph-rango-grid { stroke: var(--m3-divider); stroke-width: 1; }
.ph-rango-base { stroke: var(--m3-outline-variant); stroke-width: 1; }
.ph-rango-tick, .ph-rango-n { font: 400 9.5px var(--m3-mono); fill: var(--m3-on-surface-variant); font-variant-numeric: tabular-nums; }
.ph-rango-barra { fill: var(--ph-accent); fill-opacity: .3; }
.ph-rango-mediana { stroke: var(--ph-accent); stroke-width: 2.5; stroke-linecap: round; }
.ph-rango-fuera { fill: var(--m3-warn-strong); }

/* ── Provincias lado a lado (slice 2 de los gráficos). Cada fila lleva su
   nombre a la izquierda y, desde el 2026-09-20, la tinta de su provincia: el
   detalle del tablero pone la misma leyenda debajo de este gráfico y del de
   pasos, así que si acá fuera todo de un color la leyenda no explicaría
   nada. En el mapa hay más provincias que tintas y el color se repite; no
   confunde porque ahí la identidad la sigue dando el nombre de la fila. En
   la hoja de provincia las demás se atenúan para que la propia resalte. */
.ph-compara-fig { margin: 0; display: grid; gap: .3rem; }
.ph-compara-titulo { font-family: var(--ph-font-heading); font-size: 1rem; color: var(--m3-on-surface); }
.ph-compara-titulo span { margin-left: .35rem; font-family: var(--m3-font); font-size: .75rem; color: var(--m3-on-surface-variant); }
.ph-compara { display: block; width: 100%; height: auto; overflow: visible; }
.ph-compara-prov { font-size: 11px; fill: var(--m3-on-surface-variant); }
.ph-compara-linea { stroke: var(--prov, var(--ph-accent)); stroke-width: 2; stroke-linecap: round; }
.ph-compara-caja { fill: var(--prov, var(--ph-accent)); fill-opacity: .32; }
.ph-compara-mediana { fill: var(--prov, var(--ph-accent)); stroke: var(--m3-surface); stroke-width: 2; }
/* La mediana escrita al final de la fila: es la cifra que se viene a buscar,
   así que va con la tinta del texto y no con la gris de los ejes. */
.ph-compara-mediana-txt { font: 500 11px var(--m3-mono); fill: var(--m3-on-surface); font-variant-numeric: tabular-nums; }
.ph-compara-fila.is-atenuada { opacity: .35; }

/* ── «Cómo se movió» (slice 3 de los gráficos). El color sigue a la
   provincia (dos tintas validadas para daltonismo), siempre con leyenda:
   nunca el color solo. La etiqueta al final de cada línea se sacó el
   2026-09-20 —dos líneas que terminan cerca se pisaban— y la leyenda quedó
   como el único lugar donde se dice de quién es cada tinta. */
.ph-evo { display: grid; gap: .6rem; padding-bottom: 1.1rem; border-bottom: 1px solid var(--m3-divider); }
.ph-evo:last-of-type { border-bottom: 0; }
.ph-evo .ph-compara-titulo { margin: 0; font-weight: 400; }
.ph-evo-sub { font-size: .8125rem; font-weight: 500; color: var(--m3-on-surface); }
.ph-evo-sub span { margin-left: .35rem; font-weight: 400; font-size: .75rem; color: var(--m3-on-surface-variant); }
.ph-evo-leyenda { display: flex; flex-wrap: wrap; gap: .2rem .9rem; margin: 0; padding: 0; list-style: none; font-size: .75rem; color: var(--m3-on-surface-variant); }
.ph-evo-leyenda li { display: inline-flex; align-items: center; gap: .35rem; }
.ph-evo-muestra { width: 14px; height: 3px; border-radius: 2px; background: var(--prov); }
.ph-cal-leyenda svg { width: 12px; height: 12px; }
.ph-pendiente, .ph-pasos, .ph-calendario { display: block; width: 100%; height: auto; overflow: visible; }
.ph-calendario { max-width: 26rem; }
.ph-pendiente-col { font-size: 11px; font-weight: 500; fill: var(--m3-on-surface); }
.ph-pendiente-linea, .ph-pasos-linea { fill: none; stroke: var(--prov); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.ph-pendiente-punto, .ph-pasos-punto { fill: var(--prov); stroke: var(--m3-surface); stroke-width: 2; }
.ph-pendiente-etq { font-size: 11px; fill: var(--m3-on-surface-variant); font-variant-numeric: tabular-nums; }
.ph-cal-dia { stroke: var(--m3-surface); stroke-width: 1; }
.ph-cal-0 { fill: var(--m3-surface-container-high); stroke: var(--m3-outline-variant); }
.ph-cal-1 { fill: var(--ph-accent); fill-opacity: .3; }
.ph-cal-2 { fill: var(--ph-accent); fill-opacity: .62; }
.ph-cal-3 { fill: var(--ph-accent); }

/* ── Acordeón «qué es este gráfico» (_chart_help.html), mismo patrón que
   `.ph-evo-anuncios`: cerrado no estorba, no hace falta JS. */
.ph-chart-help { margin-top: .4rem; }
.ph-chart-help summary {
  cursor: pointer; display: inline-flex; align-items: center; gap: .3rem;
  font-size: .75rem; font-weight: 600; color: var(--ph-accent);
}
.ph-chart-help summary .m3-icon { font-size: 16px; }
.ph-chart-help p {
  margin: .35rem 0 0; font-size: .8125rem; line-height: 1.45;
  color: var(--m3-on-surface-variant);
}


/* ── Cintillo de precios (prototipo aprobado el 2026-09-26) ───────────────
   La misma franja bajo la cabecera de las tres vistas: la mediana entre las
   provincias con dato de cada producto, con su cambio (sin flecha ni color,
   §12.6). Se desplaza despacio y sin parar; se detiene al pasar el cursor o al
   enfocar un enlace, y con «reducir movimiento» queda quieto y se recorre a
   mano. Mide 2.75rem y no los 40 px del prototipo: los enlaces son tocables y
   el sitio pide 44 px. El Mapa y Explorar, que ocupan la pantalla entera, le
   ceden ese alto con `.ph-cintillo + …` en sus propias hojas. */
.ph-cintillo {
  box-sizing: border-box; display: flex; align-items: stretch; height: 2.75rem; overflow: hidden;
  background: var(--m3-surface-container);
  border-top: 1px solid var(--m3-divider); border-bottom: 1px solid var(--m3-divider);
  font-family: var(--m3-font); color: var(--m3-on-surface);
}
.ph-cintillo-rotulo {
  position: relative; z-index: 1; flex: none; display: flex; align-items: center; gap: var(--m3-space-2);
  padding: 0 var(--m3-space-4); white-space: nowrap; font-size: .6875rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--m3-on-primary-container);
  background: var(--m3-primary-container); border-right: 1px solid var(--m3-divider);
}
.ph-cintillo-punto { width: 7px; height: 7px; border-radius: 50%; background: var(--m3-primary); animation: ph-late 2s ease-in-out infinite; }
.ph-cintillo-pista {
  flex: 1; min-width: 0; overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, black 4%, black 96%, transparent);
  mask-image: linear-gradient(90deg, transparent, black 4%, black 96%, transparent);
}
.ph-cintillo-cinta { display: flex; width: max-content; height: 100%; animation: ph-cinta 46s linear infinite; }
.ph-cintillo-grupo { display: flex; align-items: stretch; gap: var(--m3-space-8); margin: 0; padding: 0 var(--m3-space-8) 0 0; list-style: none; }
.ph-cintillo-grupo li { display: flex; }
.ph-cintillo-item {
  display: flex; align-items: center; gap: .5rem; white-space: nowrap; font-size: .8125rem;
  color: var(--m3-on-surface); text-decoration: none;
}
.ph-cintillo-item:hover { text-decoration: none; color: var(--m3-primary); }
.ph-cintillo-acento { width: 9px; height: 9px; border-radius: 3px; flex: none; }
.ph-acento-0 { background: var(--m3-acento-0); }
.ph-acento-1 { background: var(--m3-acento-1); }
.ph-acento-2 { background: var(--m3-acento-2); }
.ph-acento-3 { background: var(--m3-acento-3); }
.ph-cintillo-nombre { color: var(--m3-on-surface-variant); }
.ph-cintillo-cifra { font-family: var(--m3-mono); font-weight: 700; }
.ph-cintillo-unidad { font-size: .6875rem; color: var(--m3-on-surface-variant); }
.ph-cintillo-cambio { font-family: var(--m3-mono); font-size: .75rem; color: var(--m3-on-surface-variant); }
.ph-cintillo-pista:hover .ph-cintillo-cinta,
.ph-cintillo-pista:focus-within .ph-cintillo-cinta { animation-play-state: paused; }
@keyframes ph-cinta { to { transform: translateX(-50%); } }
@keyframes ph-late { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(.7); } }
/* En el teléfono el rótulo se queda solo en el punto que late (como en el
   prototipo móvil): cada píxel es de la cinta. El `aria-label` de la región ya
   dice qué es. */
@media (max-width: 720px) {
  .ph-cintillo-rotulo { padding: 0 var(--m3-space-3); }
  .ph-cintillo-texto { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .ph-cintillo-cinta { animation: none; }
  .ph-cintillo-punto { animation: none; }
  .ph-cintillo-pista { overflow-x: auto; -webkit-mask-image: none; mask-image: none; }
  .ph-cintillo-grupo[aria-hidden="true"] { display: none; }
}

/* ── Barra inferior de vistas (prototipo móvil aprobado el 2026-09-26) ────
   En el teléfono el selector Mapa / Tablero / Explorar va abajo, al alcance
   del pulgar. Fuera del teléfono no existe: cada vista conserva el suyo.
   `--ph-barra-h` es lo que las pantallas completas (el mapa y Explorar) le
   restan a su alto y lo que el Tablero reserva al final para no taparse. */
.ph-barra-vistas { display: none; }
@media (max-width: 720px) {
  .ph-barra-vistas {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); align-items: center;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 1400; height: var(--ph-barra-h);
    padding: 0 var(--m3-space-2) env(safe-area-inset-bottom, 0px); box-sizing: border-box;
    background: var(--m3-surface); border-top: 1px solid var(--m3-divider);
  }
  .ph-barra-item {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
    min-height: 3.5rem; font-size: .75rem; color: var(--m3-on-surface-variant); text-decoration: none;
    -webkit-tap-highlight-color: transparent;
  }
  .ph-barra-item:hover { text-decoration: none; }
  .ph-barra-icono {
    display: flex; align-items: center; justify-content: center; width: 3.5rem; height: 1.875rem;
    border-radius: 15px; transition: background .2s, transform .12s;
  }
  .ph-barra-item:active .ph-barra-icono { transform: scale(.92); }
  .ph-barra-item[aria-current="true"] { color: var(--m3-on-surface); font-weight: 500; }
  .ph-barra-item[aria-current="true"] .ph-barra-icono { background: var(--m3-primary-container); color: var(--m3-on-primary-container); }
}
@media (prefers-reduced-motion: reduce) {
  .ph-barra-icono { transition: none; }
  .ph-barra-item:active .ph-barra-icono { transform: none; }
}

/* ── Selector de período: 7 / 30 / 90 días o un rango de fechas ───────────
   Lo comparten el tablero y Explorar (2026-09-26); el mapa tiene el suyo. Es
   un `<details>` con el par Desde/Hasta y un «Ver» explícito: sin auto-envío,
   porque son dos campos.

   La fila entera es una capa más alta que sus vecinas: los bloques de la página
   entran con una animación de `opacity`/`transform` y cada uno queda como capa
   propia, así que un `z-index` DENTRO de la fila no alcanza —medido: el buscador
   y el panel siguiente se dibujaban encima del formulario—. Y es a la fila, no al
   botón, que se ancla el formulario: arranca en su borde izquierdo y cabe en el
   teléfono (anclado al botón, que queda a la derecha de los chips, se salía). */
.ph-periodo { position: relative; z-index: 5; display: flex; flex-direction: column; gap: var(--m3-space-2); }
@media (min-width: 48rem) {
  .ph-periodo { flex-direction: row; flex-wrap: wrap; align-items: center; column-gap: var(--m3-space-3); }
}
.ph-periodo-etiqueta { font-size: .75rem; color: var(--m3-outline); }
.ph-periodo-chips { display: flex; flex-wrap: wrap; gap: var(--m3-space-2); }
.ph-periodo-chip {
  display: inline-flex; align-items: center; gap: var(--m3-space-2); min-height: 44px; padding: 0 var(--m3-space-3);
  box-sizing: border-box; border-radius: 22px; border: 1px solid var(--m3-outline-variant);
  background: var(--m3-surface); color: var(--m3-on-surface); font: 400 14px var(--m3-font);
  text-decoration: none; white-space: nowrap; cursor: pointer; list-style: none;
}
.ph-periodo-chip:hover { text-decoration: none; border-color: var(--m3-outline); }
.ph-periodo-chip.is-active {
  background: var(--m3-primary-container); border-color: var(--m3-primary);
  color: var(--m3-on-primary-container); font-weight: 500;
}
.ph-periodo-chip .m3-icon { font-size: 18px; }
.ph-periodo-custom > summary { min-height: 44px; }
.ph-periodo-custom > summary::-webkit-details-marker { display: none; }
.ph-periodo-form {
  position: absolute; z-index: 20; top: calc(100% + 8px); left: 0; width: min(19rem, 100%); box-sizing: border-box;
  display: flex; flex-direction: column; gap: var(--m3-space-3); padding: var(--m3-space-4); border-radius: 16px;
  background: var(--m3-surface); border: 1px solid var(--m3-outline-variant); box-shadow: var(--m3-elev-2);
}
.ph-periodo-campo { display: flex; flex-direction: column; gap: var(--m3-space-1); font-size: .75rem; color: var(--m3-on-surface-variant); }
.ph-periodo-form input {
  min-height: 44px; padding: 0 var(--m3-space-3); box-sizing: border-box; border: 1px solid var(--m3-outline-variant);
  border-radius: 12px; background: var(--m3-surface); color: var(--m3-on-surface); font: 400 16px var(--m3-font);
}
.ph-periodo-form input:focus-visible { outline: 2px solid var(--m3-primary); outline-offset: 2px; }
.ph-periodo-enviar {
  min-height: 44px; border: 0; border-radius: 22px; background: var(--m3-primary); color: var(--m3-on-primary);
  font: 500 14px var(--m3-font); cursor: pointer;
}

/* ── Por qué confiamos en cada dato (B4) ───────────────────────────────────
   Enlace a la metodología junto a la frase de procedencia, y el desglose por
   provincia. Solo tokens; el único corte responsive es el de 720 px. */
.ph-metodo-link { white-space: nowrap; color: var(--m3-primary); text-decoration: none; font-weight: 500; }
.ph-metodo-link:hover { text-decoration: underline; }
.ph-confianza {
  margin: var(--m3-space-3) 0; padding: var(--m3-space-1) var(--m3-space-3);
  border: 1px solid var(--m3-outline-variant); border-radius: 16px; background: var(--m3-surface);
}
.ph-confianza summary { min-height: 44px; display: flex; align-items: center; cursor: pointer; list-style: none; }
.ph-confianza summary::-webkit-details-marker { display: none; }
.ph-confianza-titulo { display: flex; flex-direction: column; gap: 2px; }
.ph-confianza-titulo b { font: 400 17px var(--ph-font-heading, var(--m3-font)); color: var(--m3-on-surface); }
.ph-confianza-titulo span { font-size: 12px; color: var(--m3-outline); }
.ph-confianza-lista { list-style: none; margin: 0; padding: var(--m3-space-1) 0 var(--m3-space-2); display: grid; gap: var(--m3-space-3); }
.ph-confianza-prov { margin: 0; font-size: 14px; font-weight: 500; color: var(--m3-on-surface); }
.ph-confianza-prov small { margin-left: 6px; font-size: 12px; font-weight: 400; color: var(--m3-outline); }
.ph-conf-linea { margin: 2px 0 0; display: flex; flex-wrap: wrap; gap: 0 var(--m3-space-3); font-size: 13px; line-height: 1.5;
                 color: var(--m3-on-surface-variant); }
