/* ============================================================================
   tokens.css — Estándar visual Inntegra / Cingolani.ar
   Versión: 2026-07-23 · Fuente única: DEV\_Estandares\tokens.css (NO editar copias)

   CÓMO SE USA
   1. Copiar este archivo completo al proyecto (dónde, lo dice el arquetipo:
      A → inicio de resources/css/app.css · B → inicio de style.css
      C → primer bloque del <style> de app.html).
   2. Elegir la paleta del proyecto poniendo el atributo en el <html>:
        <html data-palette="4-indigo-saas">
      Paletas disponibles (referencia visual: docs/standards/mockup-paletas.html):
        1-banca-marina      · Tradicional (banca, seguros, fintech)
        2-salud-serena      · Tradicional (salud, clínicas, bienestar)
        3-gobierno-sobrio   · Tradicional (gobierno, trámites, B2G)
        4-indigo-saas       · App (B2B moderno, tipo Linear/Stripe)
        5-azul-producto     · App (azul clásico de apps masivas)
        6-carbon-minimal    · App (monocromo tipo Notion/Vercel)
        7-teal-inntegra     · App (marca Inntegra; DEFAULT si no hay atributo)
        8-violeta-creativo  · Joven (comunidades, herramientas creativas)
        9-coral-calido      · Joven (consumo, food, lifestyle)
        10-menta-fresca     · Joven (hábitos, fitness, fresco)
        11-violeta-electrico· Joven (indigo-violeta app moderna; marca Nete.ar)
        12-momix            · App (marca Momix — CRM inmobiliario; navy + teal)
   3. Modo oscuro: agregar la clase .dark al <html> (ver design-system.md).

   REGLAS
   - Los componentes usan SOLO los nombres de variables (var(--accent)), nunca hex.
   - Cambiar de paleta = cambiar el data-palette. Nada más.
   - Un cambio de valores se hace en _Estandares\tokens.css y se re-copia.
   ========================================================================== */

/* ── Tokens independientes de la paleta ────────────────────────────────── */
:root {
  --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Inter, sans-serif;
  --radius-lg: 16px;    /* cards, modales, contenedores */
  --radius-md: 10px;    /* botones, inputs */
  --radius-pill: 999px; /* badges, chips */
  --ring: color-mix(in srgb, var(--accent) 45%, transparent); /* anillo de foco */

  /* Semánticos — modo claro (iguales para todas las paletas) */
  --ok: #10B981;   --ok-soft: #D9F2E7;   --ok-strong: #0B6E4F;
  --warn: #F59E0B; --warn-soft: #FDF0D9; --warn-strong: #935F06;
  --err: #EF4444;  --err-soft: #FCE3E3;  --err-strong: #A32B2B;

  /* Default sin data-palette = 7-teal-inntegra (claro) */
  --surface-page: #F1F5F6; --surface: #FFFFFF; --surface-2: #E7EEEF;
  --text-1: #16282B; --text-2: #587076; --text-3: #85989D;
  --border-default: #DBE4E5; --border-strong: #C2D1D3;
  --accent: #2E878C; --accent-hover: #226E72; --accent-fg: #FFFFFF;
  --accent-soft: #E6F5F6; --accent-soft-fg: #1D5E62;
}

html.dark {
  /* Semánticos — modo oscuro */
  --ok: #34D399;   --ok-soft: #123A2C;   --ok-strong: #5CDFA8;
  --warn: #FBBF24; --warn-soft: #3D2E0E; --warn-strong: #F5C462;
  --err: #F87171;  --err-soft: #41201F;  --err-strong: #F79E9A;

  /* Default sin data-palette = 7-teal-inntegra (oscuro) */
  --surface-page: #0C1516; --surface: #152123; --surface-2: #1C2B2D;
  --text-1: #E2EDEE; --text-2: #8CA4A7; --text-3: #688084;
  --border-default: #233436; --border-strong: #32484B;
  --accent: #4FBDC2; --accent-hover: #74CDD1; --accent-fg: #0C1516;
  --accent-soft: #163336; --accent-soft-fg: #86DCE0;
}

/* ── 1 · Banca marina ───────────────────────────────────────────────────── */
html[data-palette='1-banca-marina'] {
  --surface-page: #F2F4F8; --surface: #FFFFFF; --surface-2: #E9EDF3;
  --text-1: #17233B; --text-2: #5A6580; --text-3: #8891A6;
  --border-default: #DBE1EA; --border-strong: #C2CBD9;
  --accent: #2050A0; --accent-hover: #183E7E; --accent-fg: #FFFFFF;
  --accent-soft: #E7EEF9; --accent-soft-fg: #1B4383;
}
html[data-palette='1-banca-marina'].dark {
  --surface-page: #0C1322; --surface: #151E31; --surface-2: #1C2740;
  --text-1: #E4E9F3; --text-2: #93A0B8; --text-3: #6E7B95;
  --border-default: #26324A; --border-strong: #374763;
  --accent: #7FA6E8; --accent-hover: #9BBAF0; --accent-fg: #0C1322;
  --accent-soft: #1C2A44; --accent-soft-fg: #AFC8F2;
}

/* ── 2 · Salud serena ───────────────────────────────────────────────────── */
html[data-palette='2-salud-serena'] {
  --surface-page: #F3F7F5; --surface: #FFFFFF; --surface-2: #E9F0EC;
  --text-1: #182A22; --text-2: #5B6D64; --text-3: #88998F;
  --border-default: #DCE5E0; --border-strong: #C3D2CA;
  --accent: #1E7A5A; --accent-hover: #175F46; --accent-fg: #FFFFFF;
  --accent-soft: #E2F1EA; --accent-soft-fg: #14563F;
}
html[data-palette='2-salud-serena'].dark {
  --surface-page: #0D1512; --surface: #16211C; --surface-2: #1D2B24;
  --text-1: #E2EDE7; --text-2: #90A69B; --text-3: #6C8276;
  --border-default: #24352D; --border-strong: #33493E;
  --accent: #56C69A; --accent-hover: #74D3AE; --accent-fg: #0D1512;
  --accent-soft: #1B2F26; --accent-soft-fg: #8FDDBB;
}

/* ── 3 · Gobierno sobrio ────────────────────────────────────────────────── */
html[data-palette='3-gobierno-sobrio'] {
  --surface-page: #F3F2F1; --surface: #FFFFFF; --surface-2: #EAE9E7;
  --text-1: #121312; --text-2: #595D5F; --text-3: #87898B;
  --border-default: #DCDBD9; --border-strong: #C4C3C0;
  --accent: #1D70B8; --accent-hover: #155B99; --accent-fg: #FFFFFF;
  --accent-soft: #E4EFF8; --accent-soft-fg: #144E7F;
}
html[data-palette='3-gobierno-sobrio'].dark {
  --surface-page: #121312; --surface: #1C1D1C; --surface-2: #242625;
  --text-1: #EBEBE9; --text-2: #9DA1A3; --text-3: #787C7E;
  --border-default: #2E302F; --border-strong: #404342;
  --accent: #64AEE4; --accent-hover: #85C0EC; --accent-fg: #121312;
  --accent-soft: #1D2B37; --accent-soft-fg: #9CCBEF;
}

/* ── 4 · Índigo SaaS ────────────────────────────────────────────────────── */
html[data-palette='4-indigo-saas'] {
  --surface-page: #F7F8FC; --surface: #FFFFFF; --surface-2: #EEF0F8;
  --text-1: #1C1E2B; --text-2: #61667C; --text-3: #8D91A5;
  --border-default: #E2E4EE; --border-strong: #C9CCDD;
  --accent: #4F46E5; --accent-hover: #4238CA; --accent-fg: #FFFFFF;
  --accent-soft: #ECEBFC; --accent-soft-fg: #3B32B4;
}
html[data-palette='4-indigo-saas'].dark {
  --surface-page: #0F1016; --surface: #181A23; --surface-2: #20222E;
  --text-1: #E6E7EF; --text-2: #9A9EB4; --text-3: #757990;
  --border-default: #292C3B; --border-strong: #3A3E51;
  --accent: #8F94F5; --accent-hover: #A8ACF8; --accent-fg: #0F1016;
  --accent-soft: #232343; --accent-soft-fg: #B3B6FA;
}

/* ── 5 · Azul producto ──────────────────────────────────────────────────── */
html[data-palette='5-azul-producto'] {
  --surface-page: #F4F8FB; --surface: #FFFFFF; --surface-2: #EAF1F6;
  --text-1: #15222C; --text-2: #5A6B79; --text-3: #8797A4;
  --border-default: #DDE5EC; --border-strong: #C4D1DB;
  --accent: #0273BB; --accent-hover: #025F9A; --accent-fg: #FFFFFF;
  --accent-soft: #E2F1FA; --accent-soft-fg: #085280;
}
html[data-palette='5-azul-producto'].dark {
  --surface-page: #0C141B; --surface: #152029; --surface-2: #1C2934;
  --text-1: #E3EBF1; --text-2: #8FA2B1; --text-3: #6B7E8D;
  --border-default: #243441; --border-strong: #334757;
  --accent: #4BB8F0; --accent-hover: #71C8F4; --accent-fg: #0C141B;
  --accent-soft: #16374B; --accent-soft-fg: #8AD2F6;
}

/* ── 6 · Carbón minimal ─────────────────────────────────────────────────── */
html[data-palette='6-carbon-minimal'] {
  --surface-page: #F6F6F5; --surface: #FFFFFF; --surface-2: #ECECEA;
  --text-1: #171717; --text-2: #666663; --text-3: #92928F;
  --border-default: #E3E3E1; --border-strong: #CACAC7;
  --accent: #1A1A1A; --accent-hover: #000000; --accent-fg: #FFFFFF;
  --accent-soft: #EDEDEB; --accent-soft-fg: #333333;
}
html[data-palette='6-carbon-minimal'].dark {
  --surface-page: #0B0B0C; --surface: #161617; --surface-2: #1F1F20;
  --text-1: #EDEDEC; --text-2: #9C9C99; --text-3: #767674;
  --border-default: #2A2A2B; --border-strong: #3C3C3D;
  --accent: #F2F2F0; --accent-hover: #FFFFFF; --accent-fg: #111111;
  --accent-soft: #242425; --accent-soft-fg: #D4D4D2;
}

/* ── 7 · Teal Inntegra (la marca; igual al default de :root) ────────────── */
html[data-palette='7-teal-inntegra'] {
  --surface-page: #F1F5F6; --surface: #FFFFFF; --surface-2: #E7EEEF;
  --text-1: #16282B; --text-2: #587076; --text-3: #85989D;
  --border-default: #DBE4E5; --border-strong: #C2D1D3;
  --accent: #2E878C; --accent-hover: #226E72; --accent-fg: #FFFFFF;
  --accent-soft: #E6F5F6; --accent-soft-fg: #1D5E62;
}
html[data-palette='7-teal-inntegra'].dark {
  --surface-page: #0C1516; --surface: #152123; --surface-2: #1C2B2D;
  --text-1: #E2EDEE; --text-2: #8CA4A7; --text-3: #688084;
  --border-default: #233436; --border-strong: #32484B;
  --accent: #4FBDC2; --accent-hover: #74CDD1; --accent-fg: #0C1516;
  --accent-soft: #163336; --accent-soft-fg: #86DCE0;
}

/* ── 8 · Violeta creativo ───────────────────────────────────────────────── */
html[data-palette='8-violeta-creativo'] {
  --surface-page: #FAF8FE; --surface: #FFFFFF; --surface-2: #F2EFFA;
  --text-1: #241C36; --text-2: #6B6382; --text-3: #9891AB;
  --border-default: #E7E2F2; --border-strong: #D0C8E3;
  --accent: #7C3AED; --accent-hover: #6A28DB; --accent-fg: #FFFFFF;
  --accent-soft: #F1E9FE; --accent-soft-fg: #5B21B6;
}
html[data-palette='8-violeta-creativo'].dark {
  --surface-page: #120F1D; --surface: #1C172B; --surface-2: #251F38;
  --text-1: #EAE5F5; --text-2: #A79EC0; --text-3: #82789F;
  --border-default: #2F2745; --border-strong: #41375C;
  --accent: #A78BFA; --accent-hover: #BDA7FC; --accent-fg: #120F1D;
  --accent-soft: #2B2152; --accent-soft-fg: #C4B0FD;
}

/* ── 9 · Coral cálido ───────────────────────────────────────────────────── */
html[data-palette='9-coral-calido'] {
  --surface-page: #FDF8F4; --surface: #FFFFFF; --surface-2: #F6EDE5;
  --text-1: #33231C; --text-2: #7A6659; --text-3: #A18E81;
  --border-default: #EEE2D9; --border-strong: #DBC9BC;
  --accent: #D9532F; --accent-hover: #C24329; --accent-fg: #FFFFFF;
  --accent-soft: #FBE9E2; --accent-soft-fg: #A03A20;
}
html[data-palette='9-coral-calido'].dark {
  --surface-page: #191110; --surface: #241A17; --surface-2: #2F221E;
  --text-1: #F3E9E4; --text-2: #B49A8D; --text-3: #8F776B;
  --border-default: #3A2B25; --border-strong: #4F3B33;
  --accent: #FF8A68; --accent-hover: #FFA687; --accent-fg: #191110;
  --accent-soft: #40241B; --accent-soft-fg: #FFB39A;
}

/* ── 10 · Menta fresca ──────────────────────────────────────────────────── */
html[data-palette='10-menta-fresca'] {
  --surface-page: #F2FBF6; --surface: #FFFFFF; --surface-2: #E7F4ED;
  --text-1: #122A20; --text-2: #55705F; --text-3: #83988C;
  --border-default: #DCEBE2; --border-strong: #C2DACB;
  --accent: #0E9F6E; --accent-hover: #0B7F58; --accent-fg: #FFFFFF;
  --accent-soft: #DDF4E9; --accent-soft-fg: #0A6B4A;
}
html[data-palette='10-menta-fresca'].dark {
  --surface-page: #0B1611; --surface: #14211B; --surface-2: #1B2C24;
  --text-1: #E1EFE7; --text-2: #8BA795; --text-3: #678272;
  --border-default: #22362C; --border-strong: #314B3E;
  --accent: #3BD597; --accent-hover: #61E0AC; --accent-fg: #0B1611;
  --accent-soft: #14382A; --accent-soft-fg: #7BE8BC;
}

/* ── 11 · Violeta eléctrico (indigo-violeta app moderna; marca Nete.ar) ──── */
html[data-palette='11-violeta-electrico'] {
  --surface-page: #F4F3FB; --surface: #FFFFFF; --surface-2: #ECEBF8;
  --text-1: #1B1B2E; --text-2: #65647F; --text-3: #9695AE;
  --border-default: #E4E3F1; --border-strong: #D2D1E8;
  --accent: #6644EE; --accent-hover: #5836E2; --accent-fg: #FFFFFF;
  --accent-soft: #ECE9FE; --accent-soft-fg: #4A2FC2;
}
html[data-palette='11-violeta-electrico'].dark {
  --surface-page: #0F0E1A; --surface: #181726; --surface-2: #211F34;
  --text-1: #EAE9F6; --text-2: #A5A3C1; --text-3: #79779A;
  --border-default: #292742; --border-strong: #393658;
  --accent: #A79BFA; --accent-hover: #BBB1FC; --accent-fg: #120F22;
  --accent-soft: #272150; --accent-soft-fg: #C7BFFC;
}

/* ── 12 · Momix (marca Momix — CRM inmobiliario; navy + teal) ───────────── */
html[data-palette='12-momix'] {
  --surface-page: #F8FAFC; --surface: #FFFFFF; --surface-2: #F1F5F9;
  /* text-2/text-3 corridos un escalón de la rampa slate (2026-07-29): con #64748B/#94A3B8 el
     texto secundario daba 4,34:1 y el terciario 2,34-2,56:1 sobre los tres fondos — por debajo
     del AA 4.5:1 de WCAG, medido con axe-core sobre pantallas reales. Ahora 6,92+ y 4,81+.
     Se corre la MISMA rampa en vez de inventar colores, así se conserva la jerarquía entre los
     tres niveles de texto. */
  --text-1: #0F172A; --text-2: #475569; --text-3: #5E6D82;
  --border-default: #E2E8F0; --border-strong: #CBD5E1;
  /* accent teal claro → texto oscuro (blanco sobre #22D3A6 no pasa AA 4.5:1) */
  --accent: #22D3A6; --accent-hover: #14B8A6; --accent-fg: #08111F;
  --accent-soft: #DDFBF5; --accent-soft-fg: #0B6E63;
}
html[data-palette='12-momix'].dark {
  --surface-page: #08111F; --surface: #0F172A; --surface-2: #1E293B;
  /* Mismo corrimiento que en claro, para el otro lado: en oscuro el terciario (#64748B) daba
     3,07-3,97:1. Ahora 5,71+ y el secundario 9,85+. */
  --text-1: #F1F5F9; --text-2: #CBD5E1; --text-3: #94A3B8;
  --border-default: #334155; --border-strong: #475569;
  --accent: #22D3A6; --accent-hover: #4ADEC4; --accent-fg: #08111F;
  --accent-soft: #12362D; --accent-soft-fg: #7CEAD7;
}

/* ════════════════════════════════════════════════════════════════════════
   Momix — componentes base (design-system.md). Tokens arriba: NO editar.
   ════════════════════════════════════════════════════════════════════════ */
/* Tipografía Manrope (self-hosted, variable, font-src 'self') */
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/assets/fonts/manrope-latin.woff2') format('woff2');
}
:root { --font-sans: 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Inter, sans-serif; }
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0; background: var(--surface-page); color: var(--text-1);
  font: 400 14px/1.5 var(--font-sans);
}
h1, h2, h3 { font-weight: 500; margin: 0 0 8px; }
h1 { font-size: 22px; } h2 { font-size: 17px; } h3 { font-size: 14.5px; }
p { margin: 0 0 10px; }
a { color: var(--accent-soft-fg); text-decoration: none; }
a:hover { text-decoration: underline; }
.muted { color: var(--text-2); }
.tnum { font-variant-numeric: tabular-nums; }
:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }

/* Botones */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  height: 36px; padding: 0 14px; border-radius: var(--radius-md);
  font: 500 14px/1 var(--font-sans); cursor: pointer; text-decoration: none;
  border: 1px solid transparent; background: none; color: var(--text-1);
  transition: transform 160ms ease-out, background 150ms ease;
}
.btn:hover { text-decoration: none; }
.btn:active { transform: scale(0.97); }
.btn-primary { background: var(--accent); color: var(--accent-fg); }
.btn-primary:hover { background: var(--accent-hover); }
.btn-secondary { background: var(--surface); border-color: var(--border-default); }
.btn-secondary:hover { background: var(--surface-2); }
.btn-ghost:hover { background: var(--surface-2); }
.btn-danger { background: var(--err); color: #fff; }
.btn-block { width: 100%; }
.btn[disabled] { opacity: .55; cursor: not-allowed; transform: none; }

/* Visualmente oculto pero accesible (screen readers) — para un <h1> de página que ya se ve
   reflejado en otro lado (ej. el journey de "Nueva propiedad"), sin duplicar el texto en pantalla. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0,0,0,0);white-space:nowrap;border:0}

/* Cards, badges, inputs */
.card {
  background: var(--surface); border: 1px solid var(--border-default);
  border-radius: var(--radius-lg); padding: 18px 20px;
}
.badge {
  display: inline-flex; align-items: center; gap: 5px; padding: 2px 10px;
  border-radius: var(--radius-pill); font-size: 12px; font-weight: 500;
  background: var(--accent-soft); color: var(--accent-soft-fg);
}
.badge-ok { background: var(--ok-soft); color: var(--ok-strong); }
.badge-warn { background: var(--warn-soft); color: var(--warn-strong); }
.badge-err { background: var(--err-soft); color: var(--err-strong); }
label { display: block; font-weight: 500; margin: 0 0 6px; }
.input {
  width: 100%; height: 38px; padding: 0 12px; border-radius: var(--radius-md);
  border: 1px solid var(--border-strong); background: var(--surface);
  color: var(--text-1); font: 400 14px var(--font-sans);
}
.input:focus-visible { outline: 2px solid var(--ring); outline-offset: 0; border-color: var(--accent); }
.field { margin-bottom: 14px; }
.hint { font-size: 12.5px; color: var(--text-3); margin-top: 5px; }
/* Campo con error (REVISION-UX-2 puntos 2 y 3) — mismo tratamiento tanto si lo marcó "Revisar y
   activar" en vivo como si vino de un error real del server al recargar. Cubre los distintos
   wrappers de input que usa el form de propiedad (input simple, unit-field, stepper, money-field,
   calc-field, textarea) con un solo selector por tipo. */
.field.has-error > .input, .field.has-error .unit-field, .field.has-error .stepper-mid,
.field.has-error .money-field, .field.has-error > .textarea, .calc-field.has-error .unit-field {
  border-color: var(--err);
}
.field-error-msg { margin: 5px 0 0; font-size: 12.5px; color: var(--err-strong); }
.step-card.has-error { border-color: var(--err); }
.form-error {
  background: var(--err-soft); color: var(--err-strong);
  border-radius: var(--radius-md); padding: 9px 12px; margin-bottom: 14px; font-size: 13px;
}
.form-info {
  background: var(--accent-soft); color: var(--accent-soft-fg);
  border-radius: var(--radius-md); padding: 9px 12px; margin-bottom: 14px; font-size: 13px;
}

/* ── Tamaño del texto (desplegable Normal/Grande/Muy grande): escala toda la interfaz — pensado para gente grande ── */
html.fs-2 body{zoom:1.12}
html.fs-3 body{zoom:1.25}
.fs-menu{position:relative}
.fs-btn.on{color:var(--accent);background:var(--accent-soft)}
.fs-menu-panel{right:0;left:auto;min-width:170px}
.fs-menu-panel .menu-item.current{background:var(--surface-2);color:var(--accent-soft-fg);font-weight:600}
.fs-opt-glyph{font-weight:700;line-height:1;flex:none;width:20px;text-align:center;color:var(--text-2)}
.fs-opt-sm{font-size:13px}
.fs-opt-md{font-size:16px}
.fs-opt-lg{font-size:19px}
.fs-menu-panel .menu-item.current .fs-opt-glyph{color:inherit}

/* Logo */
.logo { display: inline-flex; align-items: center; gap: 9px; font-weight: 500; color: var(--text-1); }
.logo-mark {
  width: 30px; height: 30px; border-radius: 9px; background: #0F172A;
  display: inline-flex; align-items: center; justify-content: center; flex: none;
}
.logo-mark svg { color: #22D3A6; }
.logo-word { font-size: 17px; letter-spacing: -.01em; }

/* Marca del broker (abajo del logo Momix) + selector de modo broker del SuperAdmin */
.broker-switch { position: relative; }
.broker-brand {
  display: flex; align-items: center; gap: 9px; width: 100%; text-align: left;
  padding: 7px 10px; margin-bottom: 10px; color: var(--text-1); font-weight: 500;
}
button.broker-brand { background: none; border: 0; cursor: pointer; border-radius: var(--radius-md); font-family: inherit; font-size: inherit; }
button.broker-brand:hover { background: var(--surface-2); }
.broker-brand-name {
  flex: 1; min-width: 0; font-size: 13.5px; line-height: 1.25;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.broker-caret { color: var(--text-3); flex: none; }
.bk {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  border-radius: 9px; overflow: hidden; font-weight: 600; color: #fff;
}
.bk img { width: 100%; height: 100%; object-fit: cover; }
.bk-30 { width: 30px; height: 30px; font-size: 12px; }
.bk-24 { width: 24px; height: 24px; font-size: 10.5px; border-radius: 7px; }
.bk-64 { width: 64px; height: 64px; font-size: 24px; border-radius: 14px; }
.bk-sa { background: #0F172A; color: #22D3A6; }
.broker-panel {
  left: 4px; right: 4px; top: calc(100% - 4px); min-width: 0;
  max-height: min(420px, 60vh); overflow: auto;
}
.broker-search { padding: 8px; border-bottom: 1px solid var(--border-default); position: sticky; top: 0; background: var(--surface); z-index: 1; }
.broker-search .input { height: 32px; font-size: 13px; }
.broker-panel .menu-item { gap: 9px; }

/* Auth (login / reset) */
.auth-wrap {
  min-height: 100dvh; display: flex; align-items: center; justify-content: center;
  padding: 24px; background: var(--surface-page);
}
.auth-card { width: 100%; max-width: 380px; }
.auth-card .card { padding: 26px 26px 22px; }
.auth-brand { display: flex; justify-content: center; margin-bottom: 18px; }
.auth-title { text-align: center; margin-bottom: 4px; }
.auth-sub { text-align: center; color: var(--text-2); margin-bottom: 18px; }
.auth-foot { text-align: center; margin-top: 16px; font-size: 12.5px; color: var(--text-3); }

/* Shell del dashboard */
.layout { display: flex; min-height: 100dvh; }
/* position:sticky + align-self:flex-start = la barra acompaña el scroll en páginas largas (una
   ficha de propiedad o el form de alta miden varios miles de px). Sin align-self, el stretch por
   defecto del flex la hace tan alta como TODA la página y sticky no tiene contra qué pegarse.
   El overflow propio es para el caso opuesto: un menú más alto que la pantalla (SuperAdmin en
   fs-3) tiene que poder scrollear adentro de la barra. */
.sidebar {
  width: 232px; flex: none; background: var(--surface); border-right: 1px solid var(--border-default);
  padding: 16px 12px; display: flex; flex-direction: column; gap: 4px;
  position: sticky; top: 0; align-self: flex-start; height: 100dvh;
  overflow-y: auto; overscroll-behavior: contain;
}
.sidebar .logo { padding: 4px 10px 14px; }
.nav-item {
  display: flex; align-items: center; gap: 10px; padding: 9px 12px;
  border-radius: var(--radius-md); color: var(--text-2); font-weight: 500;
}
.nav-item:hover { background: var(--surface-2); text-decoration: none; }
.nav-item.active { background: var(--accent-soft); color: var(--accent-soft-fg); }
.nav-item.disabled { opacity: .5; cursor: default; }
.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.topbar {
  display: flex; align-items: center; gap: 12px; padding: 12px 20px;
  border-bottom: 1px solid var(--border-default); background: var(--surface);
}
.topbar .spacer { flex: 1; }
.content { padding: 22px 20px; max-width: 1100px; width: 100%; margin: 0 auto; }
/* Opt-in por página ($contentWide en shell_start.php): solo lo pide el form de propiedad, que a
   partir de 1600px pone la vista previa en un riel al costado y necesita más de 1100px para que
   entren las dos columnas. Por debajo de ese ancho se comporta igual que cualquier otra página. */
@media (min-width: 1600px) { .content.content-wide { max-width: 1560px; } }
.grid-kpi { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 14px; }
.kpi-num { font-size: 26px; font-weight: 500; font-variant-numeric: tabular-nums; }
/* Subtítulo del KPI (2026-07-29): el número grande es acumulado y envejece mal — a los dos años
   "300 vendidas" no informa nada. Esta línea da el ritmo actual ("12 en los últimos 30 días"). */
.kpi-sub { margin: 2px 0 0; font: 400 12px/1.3 var(--font-sans); color: var(--text-3); }

.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: var(--radius-md); border: 0;
  background: none; color: var(--text-2); cursor: pointer;
}
.icon-btn:hover { background: var(--surface-2); color: var(--text-1); }

/* Toggle de tema: luna en claro, sol en oscuro. Los dos SVG están siempre en el DOM y se
   intercambian por CSS — el <html class="dark"> ya está puesto por el script de head.php antes
   del primer pintado, así que no hay parpadeo ni dependencia de app.js. Se usa .theme-ico-*
   (clases propias) y no el atributo [hidden]: en este proyecto ya se pisó tres veces el mismo
   error de que un `display` propio le gana por cascada al [hidden]{display:none} del navegador. */
.theme-btn { position: relative; }
.theme-ico { transition: transform .25s ease, opacity .2s ease; }
.theme-ico-sun { display: none; }
html.dark .theme-ico-moon { display: none; }
html.dark .theme-ico-sun { display: inline-block; }
.theme-btn:hover .theme-ico { transform: rotate(-18deg); }
html.dark .theme-btn:hover .theme-ico { transform: rotate(45deg); }
@media (prefers-reduced-motion: reduce) { .theme-ico { transition: none; } }

/* Drawer móvil (design-system.md) */
.nav-toggle { display: none; }
.scrim { display: none; }
@media (max-width: 860px) {
  body { overflow-x: hidden; }
  .nav-toggle { display: inline-flex; }
  .sidebar {
    position: fixed; top: 0; left: 0; height: 100dvh; width: 250px; z-index: 60;
    transform: translateX(-100%); transition: transform .22s ease;
  }
  .layout.nav-open .sidebar { transform: none; }
  .scrim {
    display: block; position: fixed; inset: 0; background: rgba(0,0,0,.45);
    opacity: 0; visibility: hidden; transition: opacity .2s, visibility .2s; z-index: 55;
  }
  .layout.nav-open .scrim { opacity: 1; visibility: visible; }
}

/* Empty state */
.empty {
  text-align: center; padding: 40px 20px; color: var(--text-2);
  display: flex; flex-direction: column; align-items: center; gap: 8px;
}
.empty svg { color: var(--text-3); }

/* Landing */
.hero {
  background: linear-gradient(160deg, #0F172A, #1E293B); color: #F1F5F9;
  padding: 72px 24px 64px; text-align: center;
}
.hero .logo-word { color: #fff; font-size: 22px; }
.hero h1 { font-size: clamp(26px, 5vw, 40px); max-width: 17em; margin: 22px auto 10px; text-wrap: balance; }
.hero .tagline { color: #94A3B8; font-size: 16px; margin-bottom: 26px; }
.hero .tagline b { color: #22D3A6; font-weight: 500; }
.features {
  max-width: 960px; margin: -30px auto 0; padding: 0 20px 56px;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 14px;
}
.feature { text-align: left; }
.feature svg { color: var(--accent); margin-bottom: 8px; }

/* 403 */
.denied-wrap {
  min-height: 100dvh; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 12px; padding: 32px; text-align: center;
}
.denied-badge {
  width: 64px; height: 64px; border-radius: 50%; background: var(--err-soft);
  color: var(--err-strong); display: flex; align-items: center; justify-content: center;
  font-size: 18px; font-weight: 500;
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* Tablas de datos y helpers de plataforma */
.table-wrap{overflow-x:auto;background:var(--surface);border:1px solid var(--border-default);border-radius:var(--radius-lg)}
table.data{border-collapse:collapse;width:100%;min-width:640px;font-size:13.5px}
table.data th,table.data td{text-align:left;padding:10px 14px;border-bottom:1px solid var(--border-default);vertical-align:middle}
table.data thead th{background:var(--surface-2);font-weight:500;font-size:12.5px;color:var(--text-2)}
table.data tbody tr:last-child td{border-bottom:0}
tr.row-module td{background:var(--surface-2);font-weight:500}
td.cell-center,th.cell-center{text-align:center}
.inline-form{display:inline-flex;gap:6px;align-items:center;flex-wrap:wrap}
.type-chips{display:flex;flex-wrap:wrap;gap:4px 12px;max-width:520px}
.type-chips label{display:flex;align-items:center;gap:4px;font-size:12.5px;white-space:nowrap;color:var(--text-2)}
.input-xs{height:30px;width:76px;padding:0 8px;border:1px solid var(--border-strong);border-radius:8px;background:var(--surface);color:var(--text-1);font:400 13px var(--font-sans)}
.btn-xs{height:30px;padding:0 10px;font-size:12.5px}
.link-box{display:flex;gap:8px;align-items:center}
.link-box input{flex:1}

/* ── Cuenta en la sidebar + avatares ── */
.nav-group{margin:14px 12px 4px;font-size:11.5px;font-weight:500;letter-spacing:.06em;text-transform:uppercase;color:var(--text-3)}
.nav-spacer{flex:1}
.nav-account{border-top:1px solid var(--border-default);margin-top:8px;padding-top:12px}
.nav-account-name{font-weight:500}
.avatar-sm,.avatar-lg{display:inline-flex;align-items:center;justify-content:center;border-radius:999px;
  background:var(--accent-soft);color:var(--accent-soft-fg);font-weight:500;overflow:hidden;flex:none}
.avatar-sm{width:26px;height:26px;font-size:12px}
.avatar-lg{width:64px;height:64px;font-size:26px}
.avatar-sm img,.avatar-lg img{width:100%;height:100%;object-fit:cover}

/* ── Solapas (Perfil / Seguridad) ── */
.sectabs{display:flex;gap:2px;border-bottom:1px solid var(--border-default);margin-bottom:18px;flex-wrap:wrap}
.sectab{background:none;border:0;font-family:inherit;font-size:14px;font-weight:500;color:var(--text-2);
  padding:10px 16px;cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px;text-decoration:none}
.sectab:hover{color:var(--text-1);text-decoration:none}
.sectab.active{color:var(--accent-soft-fg);border-bottom-color:var(--accent)}
.sectab:focus-visible{outline:2px solid var(--ring);outline-offset:2px}

/* ── Toggle / switch ── */
.toggle-row{display:flex;gap:12px;align-items:flex-start}
.switch{position:relative;display:inline-block;width:42px;height:24px;flex:none;margin-top:2px}
.switch input{position:absolute;opacity:0;width:100%;height:100%;margin:0;cursor:pointer}
.slider{position:absolute;inset:0;background:var(--border-strong);border-radius:999px;transition:background .18s}
.slider::before{content:"";position:absolute;left:3px;top:3px;width:18px;height:18px;background:#fff;border-radius:50%;transition:transform .18s}
.switch input:checked + .slider{background:var(--accent)}
.switch input:checked + .slider::before{transform:translateX(18px)}
.switch input:focus-visible + .slider{outline:2px solid var(--ring);outline-offset:2px}

/* ── Segmented control (política 2FA) ── */
.seg{display:inline-flex;background:var(--surface-2);border-radius:var(--radius-md);padding:3px;gap:2px}
.seg-opt{position:relative}
.seg-opt input{position:absolute;opacity:0;inset:0;cursor:pointer;margin:0}
.seg-opt span{display:block;padding:7px 14px;border-radius:calc(var(--radius-md) - 3px);color:var(--text-2);font-weight:500;font-size:13.5px}
.seg-opt input:checked + span{background:var(--surface);color:var(--text-1);box-shadow:0 1px 2px rgba(0,0,0,.08)}
.seg-opt input:focus-visible + span{outline:2px solid var(--ring);outline-offset:2px}
.seg-opt input:disabled{cursor:not-allowed}
.seg-opt input:disabled + span{cursor:not-allowed}
/* Variante de .seg con <button> en vez de radios, para segmentados que NO son campos del
   formulario (hoy: Fotos/Planos del uploader). Mismo look que .seg-opt span; el estado activo
   sale de aria-pressed, que es además lo que lee un lector de pantalla. */
.seg-btn{display:block;padding:7px 14px;border:0;background:none;cursor:pointer;
  border-radius:calc(var(--radius-md) - 3px);color:var(--text-2);font:500 13.5px/1.4 var(--font-sans)}
.seg-btn[aria-pressed="true"]{background:var(--surface);color:var(--text-1);box-shadow:0 1px 2px rgba(0,0,0,.08)}
.seg-btn:focus-visible{outline:2px solid var(--ring);outline-offset:2px}
.seg-btn-count{margin-left:5px;color:var(--text-3);font-variant-numeric:tabular-nums}
.seg-btn[aria-pressed="true"] .seg-btn-count{color:var(--text-2)}

/* Cobertura cochera (2026-07-28): depende de Cocheras — en 0 se grisa y se deshabilita entera, así
   queda claro que no aplica sin cocheras (antes quedaba activa y con impacto visual igual). */
.garage-coverage-field.is-disabled{opacity:.45}

/* ── Protecciones activas (checklist RO) ── */
.sec-guards{list-style:none;margin:0;padding:0}
.sec-guard{display:flex;gap:10px;align-items:flex-start;padding:11px 0;border-bottom:1px solid var(--border-default)}
.sec-guard:last-child{border-bottom:0}
.sec-guard svg{color:var(--ok);flex:none;margin-top:1px}
.sec-guard strong{display:block}
.sec-guard .muted{font-size:13px}

/* ── Recovery codes + setup TOTP ── */
.recovery-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:8px;margin-top:12px}
.recovery-grid code,.setup-secret code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  background:var(--surface-2);border:1px solid var(--border-default);border-radius:8px;padding:8px 10px;text-align:center;font-size:14px}
.setup-secret{margin:12px 0}
.setup-secret code{display:inline-block;letter-spacing:2px;font-size:16px}

/* ── Avatar (clases, sin style inline por la CSP) ── */
.av{display:inline-flex;align-items:center;justify-content:center;border-radius:999px;overflow:hidden;flex:none;font-weight:500;color:#fff;line-height:1}
.av img{width:100%;height:100%;object-fit:cover}
.av-24{width:24px;height:24px;font-size:11px}
.av-32{width:32px;height:32px;font-size:14px}
.av-40{width:40px;height:40px;font-size:17px}
.av-64{width:64px;height:64px;font-size:26px}
.av-c0{background:#0F8A7E}.av-c1{background:#2563EB}.av-c2{background:#7C3AED}.av-c3{background:#DC2626}
.av-c4{background:#0891B2}.av-c5{background:#059669}.av-c6{background:#D97706}.av-c7{background:#DB2777}

/* ── Menú de usuario (topbar dropdown) ── */
.usermenu{position:relative}
.usermenu-trigger{display:inline-flex;align-items:center;gap:8px;height:40px;padding:0 8px 0 6px;border:1px solid transparent;border-radius:var(--radius-md);background:none;color:var(--text-1);cursor:pointer;font:500 14px var(--font-sans)}
.usermenu-trigger:hover{background:var(--surface-2)}
.usermenu-trigger:focus-visible{outline:2px solid var(--ring);outline-offset:2px}
.usermenu-name{max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.usermenu-caret{color:var(--text-3);transition:transform .18s}
.usermenu-trigger[aria-expanded="true"] .usermenu-caret{transform:rotate(180deg)}
.menu-panel{position:absolute;top:calc(100% + 6px);right:0;min-width:248px;background:var(--surface);
  border:1px solid var(--border-default);border-radius:var(--radius-lg);box-shadow:0 8px 28px rgba(15,23,42,.14);
  padding:6px;z-index:70;opacity:0;visibility:hidden;transform:translateY(-4px);transition:opacity .16s,transform .16s,visibility .16s}
.menu-panel.open{opacity:1;visibility:visible;transform:none}
html.dark .menu-panel{box-shadow:0 8px 28px rgba(0,0,0,.45)}
.menu-head{display:flex;gap:10px;align-items:center;padding:10px;border-bottom:1px solid var(--border-default);margin-bottom:6px}
.menu-head-info{min-width:0;display:flex;flex-direction:column;gap:2px}
.menu-head-info strong{font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.menu-head-info .muted{font-size:12.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.menu-role{align-self:flex-start;margin-top:2px}
.menu-item{display:flex;align-items:center;gap:10px;width:100%;padding:9px 10px;border-radius:var(--radius-md);
  color:var(--text-1);font:500 14px var(--font-sans);text-decoration:none;background:none;border:0;cursor:pointer;text-align:left}
.menu-item:hover{background:var(--surface-2);text-decoration:none}
.menu-item svg{color:var(--text-2);flex:none}
.menu-item-btn{color:var(--err-strong)}
.menu-item-btn svg{color:var(--err)}
.menu-sep{height:1px;background:var(--border-default);margin:6px 4px}

/* ── Login: divisor y botones de proveedor ── */
.auth-divider{display:flex;align-items:center;gap:12px;color:var(--text-3);font-size:12.5px;margin:16px 0}
.auth-divider::before,.auth-divider::after{content:"";flex:1;height:1px;background:var(--border-default)}
.btn-provider{margin-top:8px;justify-content:center;gap:10px}
.btn-provider svg{flex:none}

/* ── Campo de contraseña con ojo (mostrar/ocultar — estándar auth.md) ── */
.input-wrap{position:relative}
.input-wrap .input{padding-right:44px}
.pw-toggle{position:absolute;right:5px;top:50%;transform:translateY(-50%);width:32px;height:32px;
  display:inline-flex;align-items:center;justify-content:center;border:0;background:none;color:var(--text-3);cursor:pointer;border-radius:8px}
.pw-toggle:hover{color:var(--text-1);background:var(--surface-2)}
.pw-toggle:focus-visible{outline:2px solid var(--ring);outline-offset:1px}
.pw-toggle .icon-eye-off{display:none}
.pw-toggle.on .icon-eye{display:none}
.pw-toggle.on .icon-eye-off{display:inline-block}

/* ── Encabezado de página + filtros ── */
.page-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:16px;flex-wrap:wrap}
.page-head h1{margin:0}
.filters{display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap;margin-bottom:18px}
.filters .field{margin:0}
.filters .input{min-width:150px}

/* ── Filtros avanzados del listado (2026-07-29, paridad TodresApp 0301/0302/0303/0305/0306) ──
   Operación y Estado pasan de un <select> de un solo valor a fichas multi-selección: buscar
   "venta o alquiler" es el caso normal y antes había que hacer dos búsquedas. Los filtros menos
   usados (mías / ambientes / precio) viven detrás de un <details> nativo que arranca ABIERTO si
   alguno está aplicado — si no, un filtro activo quedaría escondido y el listado parecería roto. */
.filters-adv{flex-direction:column;align-items:stretch;gap:12px}
.filters-row{display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap}
.filters-chips{display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.filters-label{font:600 12px/1 var(--font-sans);color:var(--text-3);text-transform:uppercase;
  letter-spacing:.04em;margin-right:2px}
/* La ficha ES el <label>: el checkbox real vive adentro (accesible, navegable por teclado) y el
   estado se pinta con :has(:checked), que refleja el DOM real — no con una clase que haya que
   mantener sincronizada desde JS (ese error ya se cometió con .tile.on, ver CLAUDE.md). */
.fchip{position:relative;display:inline-flex;align-items:center;cursor:pointer}
.fchip input{position:absolute;opacity:0;inset:0;margin:0;cursor:pointer}
.fchip span{display:block;padding:6px 12px;border-radius:999px;border:1px solid var(--border-default);
  background:var(--surface);color:var(--text-2);font:500 13px/1.2 var(--font-sans);white-space:nowrap}
.fchip:hover span{border-color:var(--border-strong);color:var(--text-1)}
.fchip:has(input:checked) span{background:var(--accent-soft);border-color:var(--accent);color:var(--accent-soft-fg)}
.fchip:has(input:focus-visible) span{outline:2px solid var(--ring);outline-offset:2px}
.filters-more{border-top:1px solid var(--border-default);padding-top:10px}
.filters-more summary{cursor:pointer;font:600 13px/1 var(--font-sans);color:var(--accent-soft-fg);
  width:max-content;padding:2px 0}
.filters-more summary:focus-visible{outline:2px solid var(--ring);outline-offset:3px}
.filters-more-body{display:flex;gap:16px;flex-wrap:wrap;align-items:center;margin-top:10px}
.filters-group{display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.filters-num{width:110px;min-width:0}
/* 0307 — con JS los filtros se aplican solos, así que el botón "Filtrar" sobra visualmente. NO se
   saca del DOM: es el camino sin JS y el que espera quien navega por teclado, así que queda
   accesible por foco (aparece al tabular hasta él) aunque no ocupe lugar. */
.filters.is-auto .filters-submit{position:absolute;width:1px;height:1px;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}
.filters.is-auto .filters-submit:focus-visible{position:static;width:auto;height:auto;clip:auto;
  clip-path:none;overflow:visible}
@media (max-width:640px){
  .filters-more-body{flex-direction:column;align-items:flex-start}
  .filters-num{width:100%}
}

/* Badges de la card del listado (0310/0311) + contador de vistas (0312) */
.prop-flags{position:absolute;top:8px;right:8px;display:flex;flex-direction:column;gap:4px;align-items:flex-end}
.prop-flags:empty{display:none}
.prop-views{position:absolute;bottom:8px;right:8px;display:inline-flex;align-items:center;gap:4px;
  padding:2px 7px;border-radius:999px;background:rgba(8,17,31,.62);color:#fff;
  font:600 11px/1 var(--font-sans);font-variant-numeric:tabular-nums}

/* ── Grillas de formulario ── */
.form-grid-2{display:grid;grid-template-columns:repeat(2,1fr);gap:0 16px}
.form-grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:0 16px}
.form-grid-4{display:grid;grid-template-columns:repeat(4,1fr);gap:0 16px}
/* REVISION-UX-4 punto B1 (2026-07-29) — container query en vez de media query, ver .prop-form
   más abajo: un media query mide el VIEWPORT completo, y con el sidebar fijo de 232px un
   viewport de 1024px le deja al form ~792px reales — la franja 861–1180px (tablet apaisada,
   notebooks) quedaba con columnas apretadas porque el viewport todavía no había cruzado el
   breakpoint de 860px aunque el ESPACIO REAL disponible ya era angosto. El container query mide
   el ancho real de .prop-form (`container-name: propform`), así que resuelve tablet apaisada,
   notebook de 1280px Y el usuario con fs-3 (zoom:1.25, que achica el ancho efectivo pero un media
   query en px nunca lo nota) con la misma regla. El breakpoint global de form-grid-*
   (880/560px, más abajo) sigue igual para el resto de las páginas (owners, neighborhoods, import,
   property.php) que no pasaron por este rediseño. */
@container propform (max-width:820px){.prop-form .form-grid-2,.prop-form .form-grid-3,.prop-form .form-grid-4{grid-template-columns:1fr}}
.form-section{border-top:1px solid var(--border-default);padding-top:14px;margin-top:14px}
.form-section:first-child{border-top:0;margin-top:0;padding-top:0}
.form-section h3{margin-bottom:10px;color:var(--text-2)}

/* ── Nueva propiedad — 3 pasos (2026-07-29, rediseño design_handoff_nueva_propiedad) ──
   Columna de contenido del form propia (property-new.php/property-edit.php ya no ponen
   class="card" en el <form>: cada card vive DENTRO, una por subgrupo). */
/* container-type:inline-size habilita los @container propform de acá abajo (B1) — mide el ancho
   REAL de este contenedor (lo que sobra después del sidebar), no el viewport completo. */
.prop-form{max-width:1060px;margin:0 auto;display:flex;flex-direction:column;gap:14px;
  container-type:inline-size;container-name:propform}
/* REVISION-UX-4 punto B4 (2026-07-29) — riel derecho permanente de vista previa en pantallas
   grandes: en un 27" (≥1600px) el form quedaba en su columna de 1060px con enormes márgenes
   muertos, y la vista previa (lo más valioso) seguía escondida en el paso 3. bindPreviewRail()
   (app.js) MUEVE el nodo real de .preview-wrap acá (mismo patrón "mover, no clonar" del modal),
   visible sticky durante los 3 pasos. Por debajo de 1600px el riel queda vacío/oculto y la previa
   vive donde siempre, adentro del step3-grid. */
.prop-preview-rail{display:none}
.prop-preview-rail:empty{display:none}
/* BUG del 2026-07-29 (reportado por Máximo con captura): a ≥1600px el form se aplastaba a 0px de
   ancho y todo el contenido quedaba superpuesto con la vista previa. DOS causas encadenadas, las
   dos del mismo tipo que el bug de las 2 columnas de la ficha — una regla que depende de un
   contenedor que no puede darle el ancho que pide:
   1. `.content` tiene `max-width:1100px`. El riel vive adentro, así que por ancha que fuera la
      pantalla la fila nunca tenía más de 1060px reales para form (1060) + gap (32) + riel (360)
      = 1452. Se resuelve dejando que ESTA página pida más ancho (.content-wide, shell_start.php).
   2. `.prop-form` tiene `container-type:inline-size` (B1), que aplica contención de tamaño en el
      eje inline: su ancho intrínseco pasa a ser 0 sin importar lo que tenga adentro. Como item
      flex con `flex:0 1 auto`, su base era entonces 0 y nunca crecía — de ahí el ancho 0 medido.
      Se resuelve con `flex:1 1 0` explícito (crece hasta su max-width) + `min-width:0`.
   Verificado con el harness de tests/ux (superposición al 86% antes, cero después). */
@media (min-width:1600px){
  .prop-layout{display:flex;gap:32px;align-items:flex-start;justify-content:center}
  .prop-form{margin:0;flex:1 1 0;min-width:0}
  .prop-preview-rail:not(:empty){display:block;width:360px;flex:none;position:sticky;
    top:calc(var(--journey-h, 130px) + 12px)}
  /* Journey y barra inferior se alinean con el bloque form+riel (1060+32+360), no con los 1060
     de siempre — si no, quedan visiblemente descentrados respecto de lo que enmarcan. */
  .prop-journey-top,.prop-bottombar-inner{max-width:1452px}
  /* La previa ya no vive en el step3-grid a este ancho — vuelve a una sola columna, mismo
     criterio que la franja angosta (B1) pero por el motivo opuesto: sobra lugar, no falta. */
  .step3-grid{grid-template-columns:1fr}
}
.step-head{display:flex;align-items:center;gap:11px;margin:14px 0 -4px}
.step-head:first-child{margin-top:0}
.step-num{width:26px;height:26px;border-radius:50%;flex:none;border:1.5px solid var(--border-strong);
  color:var(--text-3);display:inline-flex;align-items:center;justify-content:center;font:700 12.5px/1 var(--font-sans)}
.step-head h2{font:600 17px/1.3 var(--font-sans);margin:0}
.step-head-sub{font:400 13px/1.4 var(--font-sans);color:var(--text-3)}
.step-head-rule{flex:1;height:1px;background:var(--border-default)}
.step-card{background:var(--surface);border:1px solid var(--border-default);border-radius:var(--radius-lg);padding:18px 22px}
.step-card-head{display:flex;align-items:center;gap:11px;margin-bottom:15px;flex-wrap:wrap}
.step-card-head h3{font:600 15px/1.3 var(--font-sans);margin:0}
.step-card-head .hint{margin:0;font-size:13px}
.step-card-head .step-card-chip{margin-left:auto;display:inline-flex;align-items:center;gap:6px;
  font:500 12px/1 var(--font-sans);color:var(--accent-soft-fg);background:var(--accent-soft);
  padding:5px 11px;border-radius:var(--radius-pill)}

/* Fila colapsable ("Detalles opcionales", "Propietario y datos privados", "Otras N comodidades"):
   el contenido SIGUE en el DOM siempre — igual criterio que $typeWrapOpen — solo cambia `hidden`,
   nunca se borra.
   REVISION-UX-4 punto A1 (2026-07-29) — antes se leía como una ETIQUETA informativa, no como un
   control: el chevron era del mismo color que el texto secundario y no había ninguna otra señal
   de que eso se abre. Ahora: texto de acción explícito ("Abrir"/"Cerrar") + el chevron como un
   afford real (círculo con fondo/borde propios, no un glifo pegado al texto), los dos a la
   DERECHA — el clic sigue funcionando en toda la fila (sigue siendo un solo <button>). */
/* `color` explícito (2026-07-29): .collapse-row es un <button>, y un <button> NO hereda el color
   del documento — la hoja del navegador le pone `buttontext`, que es negro. En modo claro pasaba
   desapercibido; en oscuro el título quedaba negro puro sobre el card (#000 sobre #1e293b,
   1,43:1), tan tapado que el subtítulo gris se leía mejor que el título. Detectado por axe-core
   en la suite de UX. */
.collapse-row{display:flex;align-items:center;gap:10px;margin-top:14px;padding:11px 14px;
  border:1px solid var(--border-default);border-radius:var(--radius-md);background:var(--surface-2);
  color:var(--text-1);
  cursor:pointer;text-align:left;width:100%;transition:background .15s ease,border-color .15s ease}
.collapse-row:hover{background:color-mix(in srgb, var(--accent) 6%, var(--surface-2))}
.collapse-row:focus-visible{outline:2px solid var(--ring);outline-offset:2px}
/* Abierta: borde más marcado y esquinas inferiores rectas — se lee como una tapa unida al
   contenido que revela justo debajo (.collapse-body), no como un botón suelto. */
.collapse-row.is-open{border-color:var(--border-strong);border-bottom-left-radius:0;border-bottom-right-radius:0}
.collapse-row-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.collapse-row-action{flex:none;font:600 13px/1 var(--font-sans);color:var(--accent-soft-fg)}
.collapse-row-chevron{flex:none;width:28px;height:28px;border-radius:50%;display:flex;
  align-items:center;justify-content:center;background:var(--surface);
  border:1px solid var(--border-default);color:var(--text-2);transition:transform .15s ease}
.collapse-row-chevron svg{width:15px;height:15px}
.collapse-row.is-open .collapse-row-chevron{transform:rotate(180deg)}
/* Candado de "Propietario y datos privados": es el ÚNICO ícono de esta familia que no es un
   chevron, y girarlo 180° no significaba nada (un candado dado vuelta sigue cerrado). Con
   .collapse-row-lock el círculo no rota; lo que se mueve es el arco del candado, que se abre —
   el gesto que el ícono representa de verdad. transform-box:view-box permite fijar el pivote en
   coordenadas del propio SVG: el pie izquierdo del arco (7,11), así levanta el lado derecho,
   igual que el ícono "lock-open" de Lucide. */
.collapse-row-lock{transition:none;transform:none}
.collapse-row-lock svg{overflow:visible}
.lock-shackle{transform-box:view-box;transform-origin:7px 11px;
  transition:transform .3s cubic-bezier(.34,1.32,.64,1)}
.collapse-row.is-open .lock-shackle{transform:rotate(-24deg)}
/* transform:none tiene que ir ACÁ y no en `.collapse-row-lock` a secas: la regla del chevron
   (`.collapse-row.is-open .collapse-row-chevron{transform:rotate(180deg)}`) le gana por
   especificidad a un selector de una sola clase, así que el candado seguía dando la vuelta.
   Verificado con el navegador: antes de este fix el círculo medía matrix(-1,0,0,-1,0,0). */
.collapse-row.is-open .collapse-row-lock{transform:none;border-color:var(--accent);color:var(--accent-soft-fg)}
@media (prefers-reduced-motion: reduce){ .lock-shackle{transition:none} }
.collapse-row-title{font:600 13px/1.3 var(--font-sans)}
.collapse-row-sub{font:400 12.5px/1.4 var(--font-sans);color:var(--text-3)}
.collapse-body{margin-top:12px}
.collapse-body[hidden]{display:none}

/* ── Videos (REVISION-UX-4 punto A5, 2026-07-29) — hasta 10, name="video_url[]", reordenables
   por arrastre (el primero es el que se usa en la ficha/página pública). ── */
.video-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:6px 0}
.video-row.is-dragging{opacity:.4}
.video-row-index{flex:none;font:600 12px/1 var(--font-sans);color:var(--text-3);min-width:52px}
.video-row-index[hidden]{display:none}
.video-row .input{flex:1;min-width:200px}
.video-row-error{flex-basis:100%;margin:0 0 0 60px}
.has-error-border{border-color:var(--err) !important}
.extref-remove,.video-row .extref-remove{flex:none;width:30px;height:30px;display:flex;
  align-items:center;justify-content:center;border:1px solid var(--border-default);
  border-radius:var(--radius-md);background:var(--surface);color:var(--text-2);cursor:pointer}
.extref-remove:hover{color:var(--err-strong);border-color:var(--err)}

/* ── Publicación equivalente en otro portal (REVISION-UX-4 punto A6, 2026-07-29) — hasta 5. ── */
.extref-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:8px 0;
  border-top:1px solid var(--border-default)}
.extref-row:first-child{border-top:0}
.extref-row .input{flex:1;min-width:180px}
.extref-detected-chip{flex:none;font:600 11.5px/1 var(--font-sans);color:var(--accent-soft-fg);
  background:var(--accent-soft);padding:4px 8px;border-radius:var(--radius-pill)}
.extref-row-error{flex-basis:100%;margin:0}
.extref-row-dup{flex-basis:100%;margin:2px 0 0;font-size:12px;color:var(--warn-strong)}
.extref-row-dup a{color:inherit;text-decoration:underline;text-underline-offset:2px}

/* ── Marketplaces (REVISION-UX-5, 2026-07-29) — panel fijo por marketplace tildado, revelado por
   el tile_group multi de arriba. Reemplaza el selector .seg por fila de "Publicación equivalente"
   (esa clase sigue viva solo para las filas 'otro' heredadas de antes de la unificación). ── */
.mkt-panel{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:10px 0;
  border-top:1px solid var(--border-default)}
/* Mismo bug de especificidad ya documentado en este proyecto (dropzone de fotos, 2026-07-28):
   .mkt-panel{display:flex} y el [hidden]{display:none} del navegador tienen la MISMA
   especificidad — sin este empate a favor, el orden de cascada (esta hoja va DESPUÉS) hacía que
   los 4 paneles se vieran siempre, tildado o no (bug real, visto con un screenshot real). */
.mkt-panel[hidden]{display:none}
.mkt-panel-head{display:flex;align-items:center;gap:8px;flex:none;font:600 13.5px/1 var(--font-sans);min-width:130px}
.mkt-panel-head img{display:block}
.mkt-panel .input{flex:1;min-width:180px}
.mkt-panel .tile-group{flex-basis:100%;margin-top:4px}

/* ── Aviso de posible duplicado (parte G) — ámbar, nunca bloquea el guardado. ── */
.dup-warning{display:flex;gap:11px;align-items:flex-start;margin-top:12px;padding:11px 13px;
  border-radius:var(--radius-md);background:var(--warn-soft);border:1px solid rgba(245,158,11,.35)}
.dup-warning[hidden]{display:none}
.dup-warning-ico{flex:none;margin-top:1px;color:var(--warn-strong)}
.dup-warning-ico svg{width:17px;height:17px}
.dup-warning-title{font:600 13px/1.4 var(--font-sans);color:var(--warn-strong);margin:0}
.dup-warning-text{margin:2px 0 0;font:400 12.5px/1.45 var(--font-sans);color:var(--warn-strong)}
.dup-warning-text a{color:inherit;text-decoration:underline;text-underline-offset:2px}
.dup-warning-dismiss{border:0;background:transparent;color:inherit;text-decoration:underline;
  text-underline-offset:2px;font:inherit;cursor:pointer;padding:0}

/* "Dónde está": dirección a la izquierda, mapa a la derecha (README: grilla 1fr 320px). */
.location-grid{display:grid;grid-template-columns:1fr 320px;gap:16px;align-items:start}
@container propform (max-width:820px){.location-grid{grid-template-columns:1fr}}

/* ── Inicio rápido por link de portal (parte F, 2026-07-29) — solo en alta. ── */
.quickstart{display:flex;gap:10px;align-items:center;flex-wrap:wrap;padding:12px 14px;
  border:1px solid var(--border-strong);border-radius:var(--radius-lg);
  background:linear-gradient(180deg,var(--surface),var(--surface-page))}
.quickstart-title{font:600 13.5px/1 var(--font-sans);flex:none}
.quickstart-input-wrap{flex:1;min-width:280px;display:flex;align-items:center;gap:8px;height:36px;
  padding:0 12px;border:1px solid var(--border-strong);border-radius:var(--radius-md);background:var(--surface)}
.quickstart-input-ico{flex:none;color:var(--text-3);display:flex}
.quickstart-input-ico svg{width:15px;height:15px}
.quickstart-input{flex:1;min-width:0;border:0;outline:none;background:transparent;font:400 13.5px var(--font-sans);color:var(--text-1)}
.quickstart-input::placeholder{color:var(--text-3)}
.quickstart-dictate svg{width:15px;height:15px}
.quickstart-status{flex-basis:100%;margin:0;font-size:12.5px;color:var(--text-2)}
.quickstart-status.is-error{color:var(--err-strong)}
.quickstart-status.is-ok{color:var(--ok-strong)}
.quickstart-dictate.is-on{border-color:var(--err);color:var(--err-strong)}
/* Resalta un instante cada campo que "Traer datos" completó solo. */
.quickstart-filled{animation:quickstartFill 2.2s ease}
@keyframes quickstartFill{0%{background:var(--accent-soft);border-radius:var(--radius-md)}100%{background:transparent}}
@media (prefers-reduced-motion:reduce){.quickstart-filled{animation:none}}
/* Parte H (mobile <860px): objetivo táctil mínimo 44px (36px en desktop) + el input pasa a
   ancho completo en su propia fila en vez de compartirla con los botones. */
@media (max-width:860px){
  .quickstart-input-wrap{height:44px;flex-basis:100%}
  .quickstart .btn{height:44px}
}

/* ── Journey de 3 pasos (parte A, reemplaza .prop-sticky) + calidad del aviso (parte D) ──
   Sticky arriba de todo (mismo criterio que .prop-sticky: position:sticky, sin bleed porque ya
   no vive dentro de un <form class="card">). */
.prop-journey{position:sticky;top:0;z-index:15;background:var(--surface);
  border-bottom:1px solid var(--border-default);margin-bottom:14px}
.prop-journey-top{max-width:1060px;margin:0 auto;padding:14px 24px 0;display:flex;align-items:center;gap:14px}
.prop-journey-title-wrap{min-width:0;flex:1}
.prop-journey-crumb{font:400 12.5px/1 var(--font-sans);color:var(--text-3)}
.prop-journey-title-row{display:flex;align-items:center;gap:10px;margin-top:3px}
.prop-journey-quality{display:flex;flex-direction:column;gap:5px;width:170px;flex:none}
.prop-journey-quality-row{display:flex;justify-content:space-between;font:500 12px/1 var(--font-sans);color:var(--text-2)}
.prop-journey-quality-row span:last-child{color:var(--text-1);font-weight:700}
.prop-journey-quality-bar{height:6px;border-radius:999px;background:var(--surface-2);overflow:hidden}
.prop-journey-quality-bar > div{height:100%;border-radius:999px;background:var(--accent);transition:width .2s ease}
.prop-journey-quality-bar.is-low > div{background:var(--warn)}

.prop-journey-steps{max-width:1060px;margin:0 auto;padding:16px 24px 0;display:flex;align-items:flex-start}
.journey-step{flex:1;min-width:0;display:flex;flex-direction:column;gap:7px;padding:0 0 14px;
  border:0;background:transparent;cursor:pointer;text-align:left;font:inherit;color:inherit}
.journey-step-top{display:flex;align-items:center;gap:8px}
.journey-step-circle{width:26px;height:26px;border-radius:50%;flex:none;border:1.5px solid var(--border-strong);
  color:var(--text-3);display:inline-flex;align-items:center;justify-content:center;font:700 12px/1 var(--font-sans)}
.journey-step-line{flex:1;height:2px;border-radius:999px;background:var(--border-default)}
.journey-step:last-child .journey-step-line{visibility:hidden}
.journey-step-body{padding-right:14px;min-width:0}
.journey-step-label{display:block;font:600 13.5px/1.3 var(--font-sans);color:var(--text-2)}
.journey-step-meta{display:block;margin-top:2px;font:400 12.5px/1.35 var(--font-sans);color:var(--text-3);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.journey-step.is-current .journey-step-circle{background:var(--text-1);color:var(--accent);border-color:transparent}
.journey-step.is-current .journey-step-label{color:var(--text-1)}
.journey-step.is-done .journey-step-circle{background:var(--accent);color:var(--accent-fg);border-color:transparent}
.journey-step.is-done .journey-step-line{background:var(--accent)}
.journey-step-circle svg{width:13px;height:13px}

/* Paso con un error de server sin resolver (REVISION-UX-2 punto 3) — gana a is-current/is-done
   mientras esté presente: un error real pesa más que "en qué círculo estoy mirando ahora". */
.journey-step.has-error .journey-step-circle{border-color:var(--err);color:var(--err-strong)}
.journey-step.has-error .journey-step-label{color:var(--err-strong)}

/* REVISION-UX-4 punto B3 (2026-07-29) — journey que se condensa al scrollear: en un notebook de
   1366×768 (el monitor más común de oficina) el cromo fijo (topbar+journey+bottombar) se comía
   un tercio de la pantalla. Pasados 120px de scroll (bindPropertyJourney, app.js) el journey ya
   cumplió su función informativa del primer vistazo, así que pasa a una sola línea ~56px: círculos
   más chicos, sin las líneas de resumen, con la barra de calidad como una línea de 3px pegada al
   borde inferior en vez del bloque completo de la derecha. Vuelve a expandirse solo al subir. */
.journey-step-meta{transition:opacity .16s ease,max-height .16s ease;max-height:20px;overflow:hidden}
.prop-journey.is-condensed .journey-step-meta{max-height:0;opacity:0}
.prop-journey-quality{transition:opacity .16s ease,max-height .16s ease;max-height:60px;overflow:hidden}
.prop-journey.is-condensed .prop-journey-quality{max-height:0;opacity:0;width:0;padding:0}
.journey-step-circle{transition:width .16s ease,height .16s ease,font-size .16s ease}
.prop-journey.is-condensed .journey-step-circle{width:20px;height:20px;font-size:10.5px}
.prop-journey.is-condensed .journey-step-circle svg{width:11px;height:11px}
.prop-journey-top{transition:padding .16s ease}
.prop-journey.is-condensed .prop-journey-top{padding-top:8px}
.journey-step{transition:padding .16s ease}
.prop-journey.is-condensed .journey-step{padding-bottom:8px}
.prop-journey.is-condensed .prop-journey-steps{padding-top:8px}
/* Barra de calidad condensada — 0 de alto (invisible) salvo en modo condensado. */
.prop-journey-condensed-bar{height:0;overflow:hidden;transition:height .16s ease;background:var(--surface-2)}
.prop-journey-condensed-bar > div{height:3px;background:var(--accent)}
.prop-journey.is-condensed .prop-journey-condensed-bar{height:3px}
@media (prefers-reduced-motion:reduce){
  .journey-step-meta,.prop-journey-quality,.journey-step-circle,.prop-journey-top,.journey-step,.prop-journey-condensed-bar{transition:none}
}

/* Resumen de errores del server — vive DENTRO de .prop-journey (mismo bloque sticky), no arriba
   del form: así nunca queda fuera de vista con la cabecera pegajosa ocupando la parte alta. */
.prop-journey-errors{max-width:1060px;margin:10px auto 0;padding:10px 24px 14px}
.prop-journey-error{margin:0;padding:7px 11px;font:400 13px/1.4 var(--font-sans);color:var(--err-strong);
  background:var(--err-soft);border-radius:var(--radius-md)}
.prop-journey-error + .prop-journey-error{margin-top:6px}

/* Parte H (mobile <860px): journey a una línea compacta (círculo + nombre, sin resumen) con
   scroll horizontal — la barra de calidad baja debajo del título (wrap del propio flex). */
@media (max-width:860px){
  .prop-journey-top{flex-wrap:wrap;padding:12px 16px 0;gap:10px}
  .prop-journey-quality{width:100%}
  .prop-journey-steps{padding:12px 16px 0;overflow-x:auto;-webkit-overflow-scrolling:touch;
    scrollbar-width:none}
  .prop-journey-steps::-webkit-scrollbar{display:none}
  .journey-step{flex:none;flex-direction:row;align-items:center;min-height:44px;padding:0 16px 12px 0}
  .journey-step-top{gap:6px}
  .journey-step-line{width:18px;flex:none}
  .journey-step-body{padding-right:0}
  .journey-step-meta{display:none}
  .journey-step-label{white-space:nowrap}
  .prop-journey-errors{padding:8px 16px 12px}

  /* REVISION-UX-4 punto B5 (2026-07-29): va más lejos que el condensado de B3 — en mobile
     .prop-journey-steps arranca OCULTO siempre (no solo tras 120px de scroll) y en su lugar se ve
     una sola línea "Paso N de 3 · Nombre" (bindPropertyJourney, app.js); tocarla revela el
     detalle de los 3 pasos debajo, sin perder nada del mecanismo existente (mismo <button>,
     mismo click-para-scrollear). */
  .journey-mobile-summary{display:flex;align-items:center;gap:8px;width:100%;min-height:44px;
    padding:10px 16px;border:0;background:transparent;color:var(--text-1);cursor:pointer;
    font:600 13.5px/1.3 var(--font-sans);text-align:left}
  .journey-mobile-summary:focus-visible{outline:2px solid var(--ring);outline-offset:-2px}
  .journey-mobile-summary span:first-child{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .journey-mobile-caret{flex:none;color:var(--text-3);transition:transform .15s ease}
  .journey-mobile-caret svg{width:15px;height:15px}
  .prop-journey.mobile-steps-open .journey-mobile-caret{transform:rotate(180deg)}
  .prop-journey-steps{display:none}
  .prop-journey.mobile-steps-open .prop-journey-steps{display:flex}
}
@media (min-width:861px){.journey-mobile-summary{display:none}}

/* ── Paso 3 "Publicación": checklist "antes de activar" (parte D) + vista previa (parte E) ── */
/* REVISION-UX-4 punto C2 (2026-07-29): la previa pasa de 340px a 420px — "es la recompensa de
   haber cargado todo, tiene que verse como una ficha de verdad, no como un thumbnail". */
.step3-grid{display:grid;grid-template-columns:1fr 420px;gap:22px;align-items:start}
@container propform (max-width:820px){.step3-grid{grid-template-columns:1fr}}

/* REVISION-UX-4 punto C2 (2026-07-29) — "Revisión final" pasa a compartir card con la vista
   previa (.step3-grid, ver más arriba): .review-col ya no dibuja su propia caja (fondo/borde) —
   el .step-card que envuelve todo el panel ya la da — salvo en estado "listo para publicar",
   donde SÍ gana un fondo propio para leerse como un cambio de estado real. */
.review-col{display:flex;flex-direction:column}
.review-col.is-ready{background:var(--ok-soft);border-radius:var(--radius-md);padding:14px;margin:-2px}
.review-ready{display:flex;flex-direction:column;align-items:flex-start;gap:6px;margin-bottom:14px}
.review-ready svg{width:26px;height:26px;color:var(--ok-strong);stroke-width:2.5}
.review-ready-title{margin:0;font:700 16px/1.2 var(--font-sans);color:var(--ok-strong)}
.review-ready .btn-activate{margin-top:4px}
.quality-panel-head{display:flex;justify-content:space-between;font:600 13px/1 var(--font-sans)}
.quality-panel-head span:last-child{color:var(--text-2)}
.review-col [data-quality-bar-2-wrap]{margin-top:9px;background:var(--border-default)}
/* Separa bloqueantes (property_quality()['blocking']) de recomendaciones (['quality']) — parte D,
   REVISION-UX-2 punto 1: solo la primera lista puede bloquear "Revisar y activar". */
.quality-group-title{margin:14px 0 6px;font:600 11.5px/1 var(--font-sans);letter-spacing:.03em;
  text-transform:uppercase;color:var(--warn-strong)}
.quality-group-title:first-of-type{margin-top:12px}
.quality-group-title.is-ok{color:var(--ok-strong)}
.quality-group-title-optional{color:var(--text-3)}
.quality-checklist{display:flex;flex-direction:column;gap:8px;margin-top:12px}
.quality-check-item{display:flex;align-items:flex-start;flex-wrap:wrap;gap:4px 9px;font:400 12.5px/1.4 var(--font-sans);color:var(--text-2)}
.quality-check-label{flex:1;min-width:0}
.quality-check-badge{width:16px;height:16px;border-radius:50%;flex:none;display:inline-flex;align-items:center;
  justify-content:center;background:var(--border-default);color:var(--text-3)}
.quality-check-badge svg{width:10px;height:10px;stroke-width:3}
.quality-check-item.is-done{color:var(--text-1)}
.quality-check-item.is-done .quality-check-badge{background:var(--accent);color:var(--accent-fg)}
/* Acción por ítem pendiente (C2) — "Faltan 2 fotos → Subir fotos" en vez de una lista pasiva. */
.quality-check-action{flex:none;margin-left:25px;border:0;background:none;padding:0;
  font:600 12px/1.4 var(--font-sans);color:var(--accent-soft-fg);text-decoration:underline;
  text-underline-offset:2px;cursor:pointer}
.quality-check-action:hover{color:var(--accent-hover)}
/* Ítem "bloqueado con motivo" (punto 11) — ej. Fotos antes de que exista property_id: nunca
   ámbar, solo texto tenue con un link real al lugar que lo desbloquea. */
.quality-check-item.is-locked{color:var(--text-3)}
.quality-locked-link{border:0;background:none;padding:0;font:inherit;color:inherit;
  text-decoration:underline;text-underline-offset:2px;cursor:pointer;text-align:left}
.quality-locked-link:hover{color:var(--text-2)}
/* Resalta el panel un instante cuando "Revisar y activar" lo redirige acá en vez de enviar. */
.quality-panel-flash{animation:qualityFlash .9s ease}
@keyframes qualityFlash{0%,100%{box-shadow:none}30%{box-shadow:0 0 0 3px var(--warn)}}
@media (prefers-reduced-motion:reduce){.quality-panel-flash{animation:none}}

/* ── Línea de resumen copiable (REVISION-UX-4 punto C3, 2026-07-29) ── */
.prop-summary-line{display:flex;align-items:center;gap:14px;flex-wrap:wrap;padding:12px 16px;
  border:1px solid var(--border-default);border-radius:var(--radius-md);background:var(--accent-soft)}
.prop-summary-line p{flex:1;min-width:200px;margin:0;font:500 13.5px/1.4 var(--font-sans);color:var(--accent-soft-fg)}
.prop-summary-line .btn{flex:none}
.prop-summary-line .btn svg{width:14px;height:14px;margin-right:4px}
.prop-summary-line .btn.is-copied{color:var(--ok-strong);border-color:var(--ok)}

/* REVISION-UX-4 punto B2 (2026-07-29) — offset dinámico: top:96px fijo asumía una altura del
   journey que ya no es constante (errores de server, journey condensado al scrollear — B3 — y
   la propia altura cambia con fs-2/fs-3). --journey-h la publica bindPropertyJourney() (app.js)
   con la altura REAL medida en vivo; 130px es el fallback antes de que corra el JS. */
.preview-wrap{position:sticky;top:calc(var(--journey-h, 130px) + 12px)}
.preview-wrap-head{display:flex;align-items:center;gap:8px;margin-bottom:6px}
.preview-wrap-head .field-label{margin:0;flex:1;min-width:0}
/* REVISION-UX-4 punto C2 — segmentado Escritorio/Celular EN LÍNEA (antes solo dentro del modal):
   reusa .preview-device-seg/.preview-device-opt (mismo look, ver el bloque del modal más abajo)
   pero íconos en vez de texto — acá el espacio es más chico que la cabecera del modal. */
.preview-device-seg-inline{flex:none}
.preview-device-seg-inline .preview-device-opt{padding:6px 8px;display:flex}
.preview-device-seg-inline .preview-device-opt svg{width:15px;height:15px}
/* Ancho "celular" — mismo criterio que .preview-modal-stage[data-device="mobile"], acá aplicado
   directo sobre .preview-wrap en vez del stage del modal. */
.preview-wrap[data-device="mobile"] .preview-card{width:390px;max-width:100%;margin:0 auto}
.preview-expand-btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;
  flex:none;border:1px solid var(--border-default);border-radius:var(--radius-md);background:var(--surface);
  color:var(--text-2);cursor:pointer}
.preview-expand-btn:hover{background:var(--surface-2);color:var(--text-1)}
.preview-expand-btn svg{width:15px;height:15px}
.preview-card{border:1px solid var(--border-default);border-radius:var(--radius-lg);overflow:hidden;background:var(--surface)}
.preview-cover{height:172px;background:var(--surface-2);position:relative}
.preview-cover img{width:100%;height:100%;object-fit:cover;display:block}
.preview-cover-count{position:absolute;left:10px;top:10px;font:600 11px/1 var(--font-sans);
  background:var(--text-1);color:var(--accent);padding:4px 8px;border-radius:6px}
.preview-cover[data-empty] .preview-cover-count{background:var(--surface);color:var(--text-2);
  border:1px solid var(--border-default);box-shadow:none}
.preview-body{padding:14px 15px}
.preview-price-row{display:flex;align-items:baseline;gap:8px}
.preview-price{font:700 21px/1.2 var(--font-sans);font-variant-numeric:tabular-nums}
.preview-op{font:500 12.5px/1 var(--font-sans);color:var(--text-2)}
.preview-title{margin-top:6px;font:500 14px/1.4 var(--font-sans);text-wrap:pretty}
.preview-loc{margin-top:3px;font:400 13px/1.4 var(--font-sans);color:var(--text-2)}
.preview-metrics{display:flex;gap:14px;flex-wrap:wrap;margin-top:10px;padding-top:10px;
  border-top:1px solid var(--border-default);font:500 12.5px/1 var(--font-sans);color:var(--text-2)}

/* ── Modal de vista previa a pantalla completa (REVISION-UX-3 punto 1) — propio, no reusa
   .modal-scrim/.modal-box (esos son para confirmaciones chicas, max-width:400px): acá hace falta
   "pantalla casi completa". El contenido (.preview-card) es el MISMO nodo que vive en el
   sidebar — bindPreviewModal (app.js) lo mueve, nunca lo clona. ── */
.preview-modal-scrim{position:fixed;inset:0;z-index:100;display:none;background:rgba(15,23,42,.55);
  padding:24px}
.preview-modal-scrim.open{display:flex}
.preview-modal-box{margin:auto;width:100%;height:100%;max-width:1100px;background:var(--surface-page);
  border-radius:var(--radius-lg);display:flex;flex-direction:column;overflow:hidden;
  box-shadow:0 24px 70px rgba(0,0,0,.35)}
.preview-modal-head{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:14px 16px;border-bottom:1px solid var(--border-default);background:var(--surface);flex:none}
.preview-device-seg{display:inline-flex;background:var(--surface-2);border-radius:var(--radius-md);padding:3px;gap:2px}
.preview-device-opt{border:0;background:none;padding:7px 14px;border-radius:calc(var(--radius-md) - 3px);
  color:var(--text-2);font:500 13.5px/1 var(--font-sans);cursor:pointer}
.preview-device-opt.is-on{background:var(--surface);color:var(--text-1);box-shadow:0 1px 2px rgba(0,0,0,.08)}
.preview-modal-close{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;
  flex:none;border:0;border-radius:var(--radius-md);background:none;color:var(--text-2);cursor:pointer}
.preview-modal-close:hover{background:var(--surface-2);color:var(--text-1)}
.preview-modal-close svg{width:17px;height:17px}
/* BUG real (2026-07-29, reportado por Máximo con captura de pantalla): align-self:flex-start
   anclaba la tarjeta arriba del todo del stage — con una tarjeta corta (sin fotos, sin datos
   cargados todavía) y un modal de altura completa, el resultado era una tarjeta chica perdida en
   un mar de espacio vacío, se leía como roto. align-items:center (patrón estándar de lightbox) la
   centra en el alto disponible; con contenido más alto que el viewport sigue siendo scrolleable
   igual, el overflow:auto no se pierde. */
.preview-modal-stage{flex:1;overflow:auto;display:flex;justify-content:center;align-items:center;padding:32px 20px}
.preview-modal-stage[data-device="desktop"] .preview-card{width:100%;max-width:480px}
.preview-modal-stage[data-device="mobile"] .preview-card{width:390px;max-width:100%}
/* Dentro del modal el sticky del sidebar no aplica — es el mismo nodo, así que se lo neutraliza acá. */
.preview-modal-stage .preview-wrap,.preview-modal-stage .preview-card{position:static}
.preview-modal-public-link{display:block;text-align:center;padding:12px 16px;border-top:1px solid var(--border-default);
  background:var(--surface);color:var(--accent-soft-fg);font:500 13.5px/1 var(--font-sans);
  text-decoration:none;flex:none}
.preview-modal-public-link:hover{text-decoration:underline}
@media (max-width:640px){
  .preview-modal-scrim{padding:0}
  .preview-modal-box{border-radius:0}
  .preview-modal-stage[data-device="mobile"] .preview-card{width:100%}
}

.textarea{height:auto;padding:10px 12px;line-height:1.5;resize:vertical;font:400 14px var(--font-sans)}

/* Dictado por voz en Descripción (REVISION-UX-4 punto A8, 2026-07-29) — botón en la esquina
   superior derecha del textarea, oculto hasta que bindDictation() confirme soporte real. */
.textarea-mic-wrap{position:relative}
.textarea-mic{position:absolute;top:8px;right:8px;z-index:1;width:30px;height:30px;
  display:flex;align-items:center;justify-content:center;background:var(--surface);
  border:1px solid var(--border-default);border-radius:50%;color:var(--text-2);cursor:pointer}
.textarea-mic[hidden]{display:none}
.textarea-mic:hover{color:var(--accent);background:var(--surface-2)}
.textarea-mic svg{width:15px;height:15px}
.textarea-mic-dot{position:absolute;top:-2px;right:-2px;width:9px;height:9px;border-radius:50%;
  background:var(--err);opacity:0}
.textarea-mic.is-on{color:var(--err-strong);border-color:var(--err)}
.textarea-mic.is-on .textarea-mic-dot{opacity:1;animation:micPulse 1.1s ease-in-out infinite}
@keyframes micPulse{0%,100%{opacity:1}50%{opacity:.25}}
@media (prefers-reduced-motion:reduce){.textarea-mic.is-on .textarea-mic-dot{animation:none}}
.textarea-mic-hint{margin-top:4px}
.textarea-mic-hint.is-listening{color:var(--err-strong)}
.textarea-mic-hint.is-error{color:var(--err-strong)}
.textarea-mic-hint[hidden]{display:none}

.quickstart-retry-photos{border:0;background:transparent;color:inherit;text-decoration:underline;
  text-underline-offset:2px;cursor:pointer;font:inherit;padding:0}

/* ── Toolbar Tarjetas/Lista (2026-07-29): toggle de vista de la grilla de Propiedades, sin form —
   dos <button> planos (no .seg/.seg-opt, que es radio+span pensado para campos de formulario) con
   aria-pressed, persistido en localStorage (bindPropertiesView, app.js). Mismo lenguaje visual que
   .seg (fondo surface-2 + pill activa) para que se sienta parte de la misma familia de controles. */
.list-toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:12px}
.list-toolbar-count{margin:0}
.view-toggle{display:inline-flex;background:var(--surface-2);border-radius:var(--radius-md);padding:3px;gap:2px}
.view-toggle-btn{display:flex;align-items:center;gap:6px;padding:7px 12px;border:0;background:transparent;
  color:var(--text-2);font-weight:500;font-size:13.5px;font-family:inherit;border-radius:calc(var(--radius-md) - 3px);cursor:pointer;white-space:nowrap}
.view-toggle-btn svg{flex:none}
.view-toggle-btn[aria-pressed="true"]{background:var(--surface);color:var(--text-1);box-shadow:0 1px 2px rgba(0,0,0,.08)}
.view-toggle-btn:focus-visible{outline:2px solid var(--ring);outline-offset:2px}

/* ── Grilla de propiedades ── */
/* [hidden] explícito: .prop-grid/.prop-list fijan su propio `display`, que empata en
   especificidad con el [hidden]{display:none} nativo del navegador y le gana por orden de
   cascada — mismo bug ya visto con .mkt-panel/.photo-uploader-controls en este proyecto. */
.prop-grid[hidden],.prop-list[hidden]{display:none}
.prop-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:16px}
.prop-card{display:flex;flex-direction:column;background:var(--surface);border:1px solid var(--border-default);
  border-radius:var(--radius-lg);overflow:hidden;text-decoration:none;color:var(--text-1);transition:transform .14s,box-shadow .14s}
.prop-card:hover{transform:translateY(-2px);box-shadow:0 8px 24px rgba(15,23,42,.1);text-decoration:none}
html.dark .prop-card:hover{box-shadow:0 8px 24px rgba(0,0,0,.4)}
.prop-thumb{position:relative;aspect-ratio:16/10;background:var(--surface-2);display:flex;align-items:center;justify-content:center;color:var(--text-3)}
.prop-status{position:absolute;top:8px;left:8px}
.prop-body{padding:12px 14px;display:flex;flex-direction:column;gap:3px}
.prop-price{font-size:17px;font-weight:500;font-variant-numeric:tabular-nums}
.prop-title{font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.prop-meta,.prop-stats{font-size:12.5px}
.prop-stats{display:flex;gap:10px;margin-top:2px}

/* ── Lista de propiedades (vista alternativa a .prop-grid, misma data) ── */
.prop-list{display:flex;flex-direction:column;gap:8px}
.prop-list-row{display:flex;align-items:center;gap:14px;padding:10px 14px;background:var(--surface);
  border:1px solid var(--border-default);border-radius:var(--radius-md);text-decoration:none;color:var(--text-1);
  transition:box-shadow .1s}
.prop-list-row:hover{box-shadow:0 4px 14px rgba(15,23,42,.08);text-decoration:none}
html.dark .prop-list-row:hover{box-shadow:0 4px 14px rgba(0,0,0,.35)}
.prop-list-thumb{position:relative;flex:none;width:64px;height:48px;border-radius:8px;overflow:hidden;
  background:var(--surface-2);display:flex;align-items:center;justify-content:center;color:var(--text-3)}
.prop-list-thumb img{width:100%;height:100%;object-fit:cover;display:block}
.prop-list-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.prop-list-title-row{display:flex;align-items:center;gap:8px;min-width:0}
.prop-list-title{font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.prop-list-meta{font-size:12.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.prop-list-stats{flex:none;display:none;gap:10px;font-size:12.5px}
.prop-list-price{flex:none;min-width:110px;text-align:right;font-weight:500;font-variant-numeric:tabular-nums}
@media (min-width:700px){ .prop-list-stats{display:flex} }
@media (max-width:480px){
  .prop-list-row{gap:10px;padding:8px 10px}
  .prop-list-thumb{width:48px;height:36px}
  .prop-list-price{min-width:0;font-size:13.5px}
}

/* ── Detalle ── */
.detail-grid{display:grid;grid-template-columns:1fr 340px;gap:16px;align-items:start}
.detail-grid > div{display:flex;flex-direction:column;gap:16px;min-width:0}
.detail-price{font-size:24px;font-weight:500;display:flex;align-items:center;gap:12px;font-variant-numeric:tabular-nums}
.gallery-empty{display:flex;flex-direction:column;align-items:center;gap:8px;color:var(--text-3);padding:40px 20px}
.stat-row{display:flex;flex-wrap:wrap;gap:20px;margin-top:14px}
.stat-num{font-size:20px;font-weight:500;font-variant-numeric:tabular-nums}
.kv{display:grid;grid-template-columns:auto 1fr;gap:8px 16px;margin:0}
.kv dt{color:var(--text-2);font-size:13px}
.kv dd{margin:0;text-align:right;font-size:13.5px}
.private-card{border-color:color-mix(in srgb,var(--warn) 40%,var(--border-default))}
.timeline{list-style:none;margin:0;padding:0;font-size:13px}
.timeline li{padding:6px 0;border-bottom:1px solid var(--border-default)}
.timeline li:last-child{border-bottom:0}
/* ── Auditoría (2026-07-28): diff campo a campo dentro de un ítem del timeline ── */
.audit-changes{list-style:none;margin:6px 0 0;padding:0 0 0 4px;font-size:12.5px}
.audit-changes li{padding:2px 0;border-bottom:0}

/* ── Modal de confirmación (propio, nunca confirm() nativo) ── */
.modal-scrim{position:fixed;inset:0;background:rgba(8,17,31,.55);display:none;align-items:center;justify-content:center;z-index:80;padding:20px}
.modal-scrim.open{display:flex}
.modal-box{background:var(--surface);border:1px solid var(--border-default);border-radius:var(--radius-lg);
  padding:22px;max-width:400px;width:100%;box-shadow:0 20px 60px rgba(0,0,0,.3)}
.modal-msg{margin:0 0 18px;font-size:15px}
.modal-actions{display:flex;justify-content:flex-end;gap:10px}

@media (max-width:880px){
  .detail-grid{grid-template-columns:1fr}
  .form-grid-3,.form-grid-4{grid-template-columns:repeat(2,1fr)}
}
@media (max-width:560px){
  .form-grid-2,.form-grid-3,.form-grid-4{grid-template-columns:1fr}
}

/* ── Galería de propiedad ── */
.gallery-cover{aspect-ratio:16/10;border-radius:var(--radius-md);overflow:hidden;background:var(--surface-2)}
.gallery-cover img{width:100%;height:100%;object-fit:cover;display:block}
.gallery-thumbs{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.gthumb{position:relative;width:72px;height:56px;border-radius:8px;overflow:hidden;border:2px solid transparent}
.gthumb.is-cover{border-color:var(--accent)}
.gthumb img{width:100%;height:100%;object-fit:cover;display:block}
.gthumb-tag{position:absolute;bottom:2px;left:2px;background:rgba(8,17,31,.75);color:#fff;font-size:9px;padding:1px 4px;border-radius:4px}
.manage-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(100px,1fr));gap:10px}
.manage-img{position:relative;aspect-ratio:1;border-radius:8px;overflow:hidden;background:var(--surface-2)}
.manage-img img{width:100%;height:100%;object-fit:cover;display:block}
.manage-cover-tag{position:absolute;top:4px;left:4px;background:var(--accent);color:var(--accent-fg);font-size:10px;font-weight:500;padding:1px 6px;border-radius:5px}
.manage-actions{position:absolute;bottom:0;left:0;right:0;display:flex;justify-content:space-between;gap:4px;padding:4px;background:linear-gradient(transparent,rgba(8,17,31,.6))}
/* REVISION-UX-4 punto B5 (2026-07-29) — objetivos táctiles: en touch, "Portada" y "✕" (30px,
   btn-xs) quedaban apretados en una celda de ~105px, dos botones diminutos superpuestos sobre la
   miniatura. Celdas más anchas + botones a 44px (mínimo táctil real, no solo el CSS de escritorio). */
@media (max-width:860px){
  .manage-grid{grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:12px}
  .manage-actions .btn-xs{height:44px;padding:0 12px;font-size:13px}
}
.photo-uploader-controls{display:flex;flex-direction:column;margin-bottom:4px}
.photo-uploader-controls[hidden]{display:none}
.prop-thumb img{width:100%;height:100%;object-fit:cover;display:block}

/* ── Selector de barrios N:M ── */
.nb-picker{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px}
.nb-item{display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border:1px solid var(--border-strong);border-radius:var(--radius-pill);cursor:pointer;font-size:13px}
.nb-item.on{border-color:var(--accent);background:var(--accent-soft);color:var(--accent-soft-fg)}
.nb-item input[type=radio]{margin-left:2px}
.nb-tags{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}

/* ── Ubicación en el mapa (MapLibre self-hosted, pin arrastrable) ── */
.map-widget{position:relative;margin:10px 0 4px;border:1px solid var(--border-default);border-radius:var(--radius-md);overflow:hidden;
  display:flex;flex-direction:column}
.map-widget-canvas{height:320px;width:100%;position:relative}
.map-widget-canvas .maplibregl-canvas{cursor:grab}
.map-widget-canvas .maplibregl-canvas:active{cursor:grabbing}
/* Pantalla completa (2026-07-28): el widget entero (canvas + barra), no solo el canvas — la
   Fullscreen API nativa hace que :fullscreen ocupe todo el viewport solo, pero el canvas de
   adentro tiene una altura fija en px que no crece con el contenedor — hay que estirarlo a mano. */
.map-widget:fullscreen{height:100vh}
.map-widget:fullscreen .map-widget-canvas{height:auto;flex:1}
.map-widget:-webkit-full-screen{height:100vh}
.map-widget:-webkit-full-screen .map-widget-canvas{height:auto;flex:1}
/* Cartel "Arrastrá el marcador…": angosto para no taparle el paso a los controles nativos
   (zoom +/− y pantalla completa, arriba a la derecha, ~39px de columna) y más chico/discreto que
   antes (2026-07-28, a pedido de Máximo — antes ocupaba todo el ancho y a veces les tapaba el
   control si el texto envolvía a 2 líneas). */
/* Buscador dentro del mapa (REVISION-UX-4 punto A9, 2026-07-29 — "arriba a la izquierda, como
   Google"): mismo endpoint que el campo Dirección de arriba, para no obligar al agente a salir
   del mapa e ir a buscar en otro lado cuando el mapa es justamente donde está mirando. */
.map-widget-search{position:absolute;top:10px;left:10px;z-index:3;display:flex;align-items:center;gap:7px;
  width:min(230px,calc(100% - 68px));height:36px;padding:0 10px;background:var(--surface);
  border:1px solid var(--border-default);border-radius:var(--radius-md);box-shadow:0 3px 10px rgba(0,0,0,.14)}
.map-widget-search-ico{flex:none;color:var(--text-3);display:flex}
.map-widget-search-ico svg{width:15px;height:15px}
.map-widget-search-input{flex:1;min-width:0;border:0;outline:none;background:transparent;
  font:400 13px var(--font-sans);color:var(--text-1)}
.map-widget-search-input::placeholder{color:var(--text-3)}
.map-widget-search .city-suggest{position:absolute;top:100%;left:0;margin-top:4px}
/* El cartel "Arrastrá el marcador…" baja debajo del buscador (antes arrancaba en top:10px, mismo
   lugar) para no superponerse. */
/* Ayuda "(i)" al lado del título de un campo (item 11 del feedback del 2026-07-29): reemplaza al
   cartel que flotaba sobre el mapa y tapaba justo la zona donde uno quiere soltar el pin.
   <details>/<summary> nativos — teclado y lector de pantalla sin una línea de JS. */
/* El popover se ancla al INICIO de la fila del label, no al ícono: anclado al ícono se salía del
   viewport cuando el campo cae en la columna derecha del formulario (detectado por el harness de
   tests/ux a 1366px: right 1399 sobre un viewport de 1366). Desde el borde izquierdo del label
   siempre entra, porque el label nunca es más angosto que su propia columna. */
.field-label-with-help{display:inline-flex;align-items:center;gap:7px;position:relative}
.field-help{display:inline-block}
.field-help summary{list-style:none;cursor:pointer;width:17px;height:17px;border-radius:50%;
  border:1px solid var(--border-strong);color:var(--text-3);background:var(--surface);
  display:flex;align-items:center;justify-content:center;
  font:700 11px/1 var(--font-sans);font-style:italic;user-select:none}
.field-help summary::-webkit-details-marker{display:none}
.field-help summary:hover{color:var(--text-1);border-color:var(--text-3)}
.field-help summary:focus-visible{outline:2px solid var(--ring);outline-offset:2px}
/* Chrome oculta el contenido de un <details> cerrado con `content-visibility`, no con
   `display:none` — y eso NO alcanza para un hijo `position:absolute`: el popover se seguía
   midiendo (y potencialmente viendo) con el detalle cerrado. Se explicita. */
.field-help:not([open]) .field-help-body{display:none}
.field-help-body{position:absolute;z-index:20;top:calc(100% + 6px);left:0;width:min(280px,70vw);
  padding:9px 11px;border:1px solid var(--border-default);border-radius:var(--radius-md);
  background:var(--surface);box-shadow:0 6px 20px -8px rgba(15,23,42,.35);
  font:400 12.5px/1.45 var(--font-sans);color:var(--text-2);font-style:normal;text-transform:none;
  letter-spacing:normal;font-weight:400}
.map-widget-use-near-done{color:var(--ok-strong);font-weight:600}
.map-widget-tip{position:absolute;top:56px;left:10px;right:49px;z-index:2;display:flex;align-items:center;gap:6px;
  background:var(--surface);border:1px solid var(--border-default);border-radius:var(--radius-md);
  box-shadow:0 3px 10px rgba(0,0,0,.14);padding:6px 6px 6px 10px;font-size:12px;color:var(--text-2)}
.map-widget-tip span{flex:1}
.map-widget-tip-close{flex:none;border:0;background:transparent;color:var(--text-3);font-size:16px;line-height:1;
  cursor:pointer;padding:2px 6px;border-radius:var(--radius-md)}
.map-widget-tip-close:hover{color:var(--text-1);background:var(--surface-2)}
/* Aviso de distancia (punto 2 de la revisión 2026-07-29): la Dirección resolvió lejos de donde
   está el pin (>5 km) — nunca bloquea, solo informa; mismo criterio ámbar que el resto de los
   avisos del proyecto (duplicados, etc.). Misma posición que .map-widget-tip (arriba del canvas,
   deja el margen de los controles de zoom a la derecha). */
.map-widget-distance-warn{position:absolute;top:56px;left:10px;right:49px;z-index:2;display:flex;align-items:center;gap:6px;
  background:var(--warn-soft);border:1px solid color-mix(in srgb,var(--warn) 45%,var(--border-default));
  border-radius:var(--radius-md);box-shadow:0 3px 10px rgba(0,0,0,.14);padding:6px 10px;
  font-size:12px;color:var(--warn-strong)}
.map-widget-distance-warn[hidden]{display:none}
.map-widget-equalize{margin-top:8px}
.map-widget-equalize:disabled{opacity:.5;cursor:not-allowed}
/* Barra inferior — FUERA del canvas (flujo normal, no flota encima del mapa): coordenadas, botón
   de copiar y "cerca de X", todo junto (2026-07-28, a pedido de Máximo — antes flotaba sobre el
   mapa y tapaba esa esquina). */
.map-widget-bar{display:flex;align-items:center;gap:8px;padding:6px 10px;background:var(--surface);
  border-top:1px solid var(--border-default);font:600 12px var(--font-sans);color:var(--text-2);
  font-variant-numeric:tabular-nums}
.map-widget-coords{flex:none;white-space:nowrap}
.map-widget-copy{flex:none;display:flex;align-items:center;justify-content:center;width:24px;height:24px;
  border:1px solid var(--border-default);border-radius:6px;background:var(--surface-2);color:var(--text-3);
  cursor:pointer;padding:0}
.map-widget-copy:hover{color:var(--text-1);background:var(--surface-3,var(--surface-2))}
.map-widget-copy svg{width:13px;height:13px}
.map-widget-copy.copied{color:var(--accent);border-color:var(--accent)}
.map-widget-near{flex:1;min-width:0;display:flex;align-items:center;gap:8px;font-weight:400;color:var(--text-3)}
.map-widget-near:empty{display:none}
.map-widget-near > span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* "Usar esta dirección" (REVISION-UX-4 punto A9 — sync bidireccional): antes "Cerca de X" solo
   informaba, arrastrar el pin nunca tocaba el campo Dirección. Ahora ofrece completarlo, sin
   pisarlo nunca en silencio. */
.map-widget-use-near{flex:none;border:0;background:transparent;color:var(--accent-soft-fg);
  font:600 12px var(--font-sans);text-decoration:underline;text-underline-offset:2px;cursor:pointer;padding:0}
.map-widget-use-near:hover{color:var(--accent-hover)}
/* Botón "recentrar" — mismo look que los controles nativos de MapLibre, debajo de zoom +/− Y del
   botón de pantalla completa (2026-07-28: se sumó ese control arriba, hay que correrlo más abajo
   para no pisarlo). */
.map-widget-recenter{position:absolute;top:125px;right:10px;z-index:2;width:29px;height:29px;
  display:flex;align-items:center;justify-content:center;background:var(--surface);
  border:1px solid var(--border-default);border-radius:4px;color:var(--text-2);cursor:pointer;
  box-shadow:0 1px 4px rgba(0,0,0,.16)}
.map-widget-recenter:hover{color:var(--text-1);background:var(--surface-2)}
.map-widget-recenter svg{width:16px;height:16px}
/* "Mi ubicación" (REVISION-UX-4 punto A9, 2026-07-29) — abajo a la derecha, mismo lugar que
   Google Maps; hijo de .map-widget-canvas (no de .map-widget) para anclar al borde real del
   mapa, no al borde del widget entero (que incluye la barra de coordenadas debajo). */
.map-widget-geolocate{position:absolute;bottom:10px;right:10px;z-index:2;width:34px;height:34px;
  display:flex;align-items:center;justify-content:center;background:var(--surface);
  border:1px solid var(--border-default);border-radius:50%;color:var(--text-2);cursor:pointer;
  box-shadow:0 1px 4px rgba(0,0,0,.16)}
.map-widget-geolocate:hover{color:var(--accent);background:var(--surface-2)}
.map-widget-geolocate svg{width:17px;height:17px}
.map-widget-geolocate.is-loading{color:var(--accent);animation:mapGeoPulse 1s ease-in-out infinite}
.map-widget-geolocate:disabled{cursor:default}
@keyframes mapGeoPulse{0%,100%{opacity:1}50%{opacity:.4}}
@media (prefers-reduced-motion:reduce){.map-widget-geolocate.is-loading{animation:none}}
.map-widget-geolocate-status{position:absolute;bottom:52px;right:10px;z-index:2;max-width:220px;
  padding:8px 10px;background:var(--surface);border:1px solid var(--border-default);
  border-radius:var(--radius-md);box-shadow:0 3px 10px rgba(0,0,0,.14);font-size:12px;color:var(--text-2);
  text-align:right}
.map-widget-geolocate-status[hidden]{display:none}
/* "Levantar" el pin mientras se arrastra (2026-07-28): se desvanece un poco y gana una sombra
   marcada — la sensación de "despegado de la superficie" de Google Maps, sin tocar ningún
   transform/posición propia del marcador (eso definiría literalmente dónde cae, y depende de
   offsets internos de MapLibre — más seguro no tocarlo). */
.momix-pin-draggable{transition:opacity .15s ease,filter .15s ease}
.momix-pin-draggable.is-dragging{opacity:.85;filter:drop-shadow(0 10px 8px rgba(0,0,0,.45))}
/* Atribución (© OpenMapTiles © OpenStreetMap contributors, requisito de licencia — no se puede
   sacar, ver CLAUDE.md) más chica y discreta (2026-07-28, a pedido de Máximo): el botón "i"
   colapsado baja de opacidad hasta que se lo toca. */
.maplibregl-ctrl-attrib{font-size:10px !important}
.maplibregl-ctrl-attrib-button{opacity:.55}
.maplibregl-ctrl-attrib-button:hover,.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-button{opacity:1}
.city-suggest{position:absolute;z-index:5;margin-top:4px;width:100%;max-width:360px;max-height:280px;overflow-y:auto;
  background:var(--surface);border:1px solid var(--border-default);border-radius:var(--radius-md);
  box-shadow:0 12px 30px rgba(0,0,0,.2)}
.city-suggest-item{display:flex;align-items:center;gap:10px;width:100%;text-align:left;border:0;
  background:transparent;padding:8px 12px;font:inherit;cursor:pointer}
.city-suggest-item:hover,.city-suggest-item.active{background:var(--surface-2)}
.city-suggest-ico{flex:none;display:flex;color:var(--text-3)}
.city-suggest-item.active .city-suggest-ico{color:var(--accent)}
.city-suggest-ico svg{width:16px;height:16px}
.city-suggest-body{display:flex;flex-direction:column;gap:1px;min-width:0;flex:1}
.city-suggest-main{font-size:13px;color:var(--text-1);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.city-suggest-main mark{background:none;color:inherit;font-weight:700;padding:0}
.city-suggest-sub{font-size:12px;color:var(--text-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.city-suggest-empty{padding:14px 12px;margin:0;text-align:center;color:var(--text-3);font-size:13px}

/* ── Fotos y planos: dropzone (D14 uploads-ui, docs/standards/ux-templates.md) ──────
   Reglas visibles ANTES de elegir (tipos + peso máximo), progreso por archivo, rechazo con el
   motivo exacto en la fila del archivo — la validación real siempre está en el server
   (process_property_image, lib/images.php); esto es solo la capa de UX. */
.dropzone{position:relative;border:2px dashed var(--border-default);border-radius:var(--radius-md);
  padding:22px 16px;text-align:center;cursor:pointer;transition:border-color .15s,background .15s}
.dropzone:hover,.dropzone:focus-visible{border-color:var(--accent);background:var(--accent-soft)}
.dropzone.is-dragover{border-color:var(--accent);background:var(--accent-soft)}
.dropzone:focus-visible{outline:2px solid var(--ring);outline-offset:2px}
.dropzone-icon{width:26px;height:26px;color:var(--text-3);margin:0 auto 6px}
.dropzone-text{margin:0;font-size:14px;color:var(--text-2)}
.dropzone-link{color:var(--accent);font-weight:600;text-decoration:underline}
.dropzone-rules{margin:4px 0 0;font-size:12px;color:var(--text-3)}
.dropzone-title{margin:0 0 2px;font:600 15px/1.3 var(--font-sans);color:var(--text-1)}
/* El selector de tipo va ARRIBA del dropzone: primero se elige qué se sube, después se suelta.
   Debajo quedaba después de la acción que condiciona, que es el orden inverso al del uso. */
.dropzone-cat{margin-bottom:10px}

/* Grillas separadas por tipo (item 6 del feedback del 2026-07-29): una sola grilla mezclada
   obligaba a abrir cada miniatura para saber si el plano ya estaba cargado. */
/* Arrastrar para reordenar (0428) + deshacer el borrado (0429) */
.manage-img{cursor:grab}
.manage-img.is-dragging{opacity:.4;cursor:grabbing}
.manage-img.is-deleting img,.manage-img.is-deleting .manage-actions,
.manage-img.is-deleting .manage-cover-tag{opacity:.15;pointer-events:none}
.photo-undo{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:6px;background:rgba(8,17,31,.82);color:#fff;
  font:500 12px/1.3 var(--font-sans);text-align:center;padding:6px;border-radius:8px}
.photo-undo-btn{border:1px solid rgba(255,255,255,.5);background:transparent;color:#fff;
  border-radius:999px;padding:4px 12px;font:600 12px/1 var(--font-sans);cursor:pointer}
.photo-undo-btn:hover{background:rgba(255,255,255,.15)}
.photo-undo-btn:focus-visible{outline:2px solid #fff;outline-offset:2px}

/* Editar una nota en la línea de tiempo (0617) */
.pf-note-edit{margin-top:6px}
.pf-note-edit textarea{width:100%;font:inherit}
.pf-note-edit-actions{display:flex;gap:6px;margin-top:6px}
.pf-note-actions{display:flex;gap:4px;align-items:center;margin-top:2px}
.pf-owner-link{margin:-4px 0 10px;font-size:13px}

/* 0719 — una fila de precio por operación en la ficha pública (Venta $X · Alquiler $Y) */
.pub2-price-row{display:flex;align-items:baseline;justify-content:space-between;gap:10px;
  padding:6px 0;border-bottom:1px solid var(--border-default)}
.pub2-price-row:last-of-type{border-bottom:0}
.pub2-price-op{font:600 12.5px/1 var(--font-sans);color:var(--text-3);text-transform:uppercase;letter-spacing:.04em}
.pub2-price-row .pub2-contact-price{font-size:22px}
/* 0714 — datos de la propiedad en la ficha del cliente (orientación, disposición, estado…) */
.pub2-specs{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:8px 18px;margin:0}
.pub2-specs>div{display:flex;justify-content:space-between;gap:10px;padding:5px 0;
  border-bottom:1px solid var(--border-default)}
.pub2-specs dt{color:var(--text-3);font-size:13px;margin:0}
.pub2-specs dd{margin:0;font-weight:500;font-size:13.5px;text-align:right}

.photo-groups{display:flex;flex-direction:column;gap:14px;margin-top:12px}
.photo-groups:empty{display:none}
.photo-group[hidden]{display:none}
.photo-group-title{margin:0 0 7px;font:600 12.5px/1 var(--font-sans);color:var(--text-3);
  text-transform:uppercase;letter-spacing:.04em}

.photo-upload-list{display:flex;flex-direction:column;gap:8px;margin-top:12px}
.photo-upload-row{display:flex;align-items:center;gap:10px;padding:8px 10px;
  border:1px solid var(--border-default);border-radius:var(--radius-md);background:var(--surface)}
.photo-upload-row.is-error{border-color:var(--err)}
.photo-upload-thumb{flex:none;width:38px;height:38px;border-radius:6px;object-fit:cover;background:var(--surface-2)}
.photo-upload-body{flex:1;min-width:0}
.photo-upload-name{font-size:13px;color:var(--text-1);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.photo-upload-progress{height:4px;border-radius:2px;background:var(--surface-2);margin-top:6px;overflow:hidden}
.photo-upload-progress-bar{height:100%;background:var(--accent);width:0%;transition:width .2s ease}
.photo-upload-error{font-size:12px;color:var(--err-strong);margin-top:3px}
.photo-upload-dismiss{flex:none;border:0;background:transparent;color:var(--text-3);cursor:pointer;
  font-size:16px;line-height:1;padding:4px 6px;border-radius:var(--radius-md)}
.photo-upload-dismiss:hover{color:var(--text-1);background:var(--surface-2)}

/* ── Modal de login sobre la landing ── */
.login-modal{position:fixed;inset:0;z-index:90;display:none;align-items:flex-start;justify-content:center;
  padding:40px 20px;overflow-y:auto;background:rgba(8,17,31,.66);backdrop-filter:blur(3px)}
.login-modal.open{display:flex}
.login-modal-box{position:relative;width:100%;max-width:400px;margin:auto}
.login-modal-brand{display:flex;justify-content:center;margin-bottom:16px}
.login-modal-brand .logo-word{color:#fff}
.login-modal-card{padding:26px 26px 22px;animation:loginpop .2s ease-out}
.login-modal-close{position:absolute;top:-6px;right:-6px;width:34px;height:34px;border-radius:999px;border:0;
  background:var(--surface);color:var(--text-2);cursor:pointer;display:flex;align-items:center;justify-content:center;
  box-shadow:0 2px 8px rgba(0,0,0,.2)}
.login-modal-close:hover{color:var(--text-1);background:var(--surface-2)}
.login-modal-close:focus-visible{outline:2px solid var(--ring);outline-offset:2px}
@keyframes loginpop{from{opacity:0;transform:translateY(-8px) scale(.98)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.login-modal-card{animation:none}}

/* ── Notas y solicitudes en la propiedad ── */
.note-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}
.note-item{padding:10px 0;border-top:1px solid var(--border-default)}
.note-item:first-child{border-top:0;padding-top:0}
.note-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:13px}
.note-head .muted{font-size:12px}
.note-del{margin-left:auto}

/* ── Página pública de propiedad ── */
body.pub{background:var(--surface-page)}
.pub-top{padding:16px 20px;border-bottom:1px solid var(--border-default);background:var(--surface)}
.pub-wrap{max-width:760px;margin:0 auto;padding:20px}
.pub-gallery{margin-bottom:18px}
.pub-cover{aspect-ratio:16/10;border-radius:var(--radius-lg);overflow:hidden;background:var(--surface-2)}
.pub-cover img{width:100%;height:100%;object-fit:cover;display:block}
.pub-thumbs{display:flex;gap:8px;margin-top:8px;overflow-x:auto}
.pub-thumbs img{width:96px;height:72px;object-fit:cover;border-radius:8px;flex:none}
.pub-head{margin-bottom:14px}
.pub-price{font-size:26px;font-weight:500;font-variant-numeric:tabular-nums}
.pub-head h1{font-size:22px;margin:2px 0 4px}
.pub-stats{display:flex;flex-wrap:wrap;gap:20px;padding:14px 0;border-top:1px solid var(--border-default);border-bottom:1px solid var(--border-default);margin-bottom:16px}
.pub-stat strong{font-size:18px;font-variant-numeric:tabular-nums}
.pub-desc{margin-bottom:20px}
.pub-cta{max-width:360px}
.pub-foot{text-align:center;padding:24px;color:var(--text-3);font-size:13px}

/* ── Banner de impersonación ── */
.imp-banner{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;background:var(--warn-soft);color:var(--warn-strong);padding:8px 16px;font-size:13px;font-weight:500;border-bottom:1px solid var(--warn)}
.imp-banner .muted{color:inherit;opacity:.8;font-weight:400}

/* ── Bottom-nav mobile (PWA) ── */
.bottom-nav{display:none}
@media (max-width:640px){
  .bottom-nav{display:flex;position:fixed;bottom:0;left:0;right:0;z-index:50;
    background:var(--surface);border-top:1px solid var(--border-default);
    padding:6px 4px calc(6px + env(safe-area-inset-bottom)) 4px;justify-content:space-around;align-items:center}
  .bn-item{flex:1;display:flex;flex-direction:column;align-items:center;gap:2px;padding:4px;border:0;background:none;
    color:var(--text-3);font:500 10px var(--font-sans);cursor:pointer;text-decoration:none}
  .bn-item.on{color:var(--accent-soft-fg)}
  .bn-item span{white-space:nowrap}
  .bn-add{flex:none;width:48px;height:48px;border-radius:999px;background:var(--accent);color:var(--accent-fg);
    display:flex;align-items:center;justify-content:center;text-decoration:none;margin-top:-16px;box-shadow:0 4px 12px rgba(34,211,166,.4)}
  .main .content{padding-bottom:76px}
  /* Nueva propiedad (parte H): a este ancho el bottom-nav YA está fijo al piso — la barra
     inferior del form (también fija desde 860px) tiene que apilarse arriba de él, no pisarlo. */
  .prop-bottombar{bottom:76px}
}

/* ── Estado de carga en botones ── */
.btn.is-loading{pointer-events:none}
.btn.is-loading::before{content:"";width:14px;height:14px;margin-right:6px;border:2px solid currentColor;
  border-top-color:transparent;border-radius:50%;display:inline-block;vertical-align:-2px;animation:btnspin .6s linear infinite}
@keyframes btnspin{to{transform:rotate(360deg)}}

/* ── Checklist de contraseña en vivo (refuerzo positivo) ── */
.pw-check{list-style:none;margin:8px 0 0;padding:0;display:flex;flex-direction:column;gap:5px}
.pw-check li{font-size:12.5px;color:var(--text-3);display:flex;align-items:center;gap:8px;transition:color .15s}
.pw-check li::before{content:"";width:15px;height:15px;border-radius:50%;border:1.5px solid var(--border-strong);flex:none;transition:background .15s,border-color .15s}
.pw-check li.ok{color:var(--ok-strong)}
.pw-check li.ok::before{background:var(--ok);border-color:var(--ok)}

/* ── Paletazo de comandos (⌘K) ─────────────────────────────────────────── */
.cmdk-trigger{display:inline-flex;align-items:center;gap:8px;height:34px;padding:0 9px 0 11px;margin-right:4px;
  background:var(--surface-2);border:1px solid var(--border-default);border-radius:var(--radius-md);
  color:var(--text-3);font:inherit;font-size:13px;line-height:1;cursor:pointer;max-width:230px;
  transition:border-color .15s,color .15s}
.cmdk-trigger:hover{border-color:var(--border-strong);color:var(--text-2)}
.cmdk-trigger>span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cmdk-kbd{margin-left:auto;font:600 11px var(--font-sans);color:var(--text-3);background:var(--surface);
  border:1px solid var(--border-default);border-radius:6px;padding:2px 6px;white-space:nowrap}
@media (max-width:640px){
  .cmdk-trigger{max-width:none;width:36px;justify-content:center;padding:0}
  .cmdk-trigger>span,.cmdk-kbd{display:none}
}

.cmdk{position:fixed;inset:0;z-index:120;display:none;align-items:flex-start;justify-content:center;
  padding:14vh 16px 16px;background:rgba(8,17,31,.5);backdrop-filter:blur(2px)}
.cmdk.on{display:flex}
html.cmdk-open{overflow:hidden}
.cmdk-panel{width:100%;max-width:560px;background:var(--surface);border:1px solid var(--border-default);
  border-radius:var(--radius-lg);box-shadow:0 24px 60px rgba(0,0,0,.35);overflow:hidden;animation:cmdkin .13s ease-out}
@keyframes cmdkin{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}
.cmdk-head{display:flex;align-items:center;gap:10px;padding:12px 14px;border-bottom:1px solid var(--border-default)}
.cmdk-ico{color:var(--text-3);flex:none}
.cmdk-input{flex:1;min-width:0;border:0;background:transparent;color:var(--text-1);font:inherit;font-size:15px;outline:none}
.cmdk-input::placeholder{color:var(--text-3)}
.cmdk-esc{flex:none;font:600 11px var(--font-sans);color:var(--text-3);border:1px solid var(--border-default);border-radius:6px;padding:2px 6px}
.cmdk-list{max-height:52vh;overflow-y:auto;padding:6px}
.cmdk-group{font:600 11px var(--font-sans);letter-spacing:.04em;text-transform:uppercase;color:var(--text-3);padding:10px 10px 4px;margin:0}
.cmdk-item{display:flex;align-items:center;gap:10px;padding:9px 10px;border-radius:var(--radius-md);text-decoration:none;color:var(--text-1)}
.cmdk-item.active{background:var(--accent-soft)}
.cmdk-item.active .cmdk-t{color:var(--accent-soft-fg)}
.cmdk-item-body{display:flex;flex-direction:column;gap:1px;min-width:0;flex:1}
.cmdk-t{font-size:14px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cmdk-s{font-size:12px;color:var(--text-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cmdk-badge{flex:none;font-size:11px;font-weight:600;color:var(--text-2);background:var(--surface-2);border-radius:999px;padding:2px 9px}
.cmdk-meta{flex:none;font-size:12px;color:var(--text-3);white-space:nowrap}
.cmdk-empty{padding:26px 10px;text-align:center;color:var(--text-3);font-size:13px;margin:0}

/* ── Etiqueta de grupo (fichas, barrios) — igual que <label> ── */
.field-label{display:block;font-weight:500;margin:0 0 6px}

/* ── Fichas / radio-cards (Operación, Tipo): mismo tamaño SIEMPRE, grilla responsive ── */
.tile-group{display:grid;grid-template-columns:repeat(auto-fill,minmax(96px,1fr));gap:8px}
.tile{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;
  text-align:center;height:74px;padding:8px 6px;border:1.5px solid var(--border-default);border-radius:var(--radius-md);
  background:var(--surface);color:var(--text-2);cursor:pointer;font:inherit;
  transition:border-color .12s,background .12s,color .12s;-webkit-tap-highlight-color:transparent}
.tile:hover{border-color:var(--border-strong);color:var(--text-1)}
.tile input{position:absolute;opacity:0;width:1px;height:1px;pointer-events:none}
.tile-ico{display:flex;align-items:center;justify-content:center;color:var(--text-3);transition:color .12s}
.tile-ico svg{width:20px;height:20px}
.tile-ico img{display:block;width:20px;height:20px;object-fit:contain}   /* logos reales (Buscador): a color, sin tinte */
.tile-label{font-size:11.5px;font-weight:500;line-height:1.15;max-width:100%;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.tile-hint{font-size:11px;color:var(--text-3)}
/* El look de "seleccionada" sale SOLO de :has(input:checked) — nunca de una clase estática (2026-07-28:
   una clase "on" escrita al renderizar se quedaba pegada para siempre si el agente clickeaba otra
   ficha con un click nativo, dejando dos fichas "prendidas" a la vez). */
.tile:has(input:checked){border-color:var(--accent);background:var(--accent-soft);color:var(--accent-soft-fg)}
.tile:has(input:checked) .tile-ico{color:var(--accent-soft-fg)}
.tile:has(input:focus-visible),.tile-toggle:focus-visible{outline:2px solid var(--ring);outline-offset:1px}
.tile:has(input:checked)::after{content:"✓";position:absolute;top:4px;right:5px;width:14px;height:14px;border-radius:50%;
  background:var(--accent);color:var(--accent-fg);font-size:9px;line-height:14px;text-align:center;font-weight:700}
/* Ficha "+" fija (nunca cambia): despliega las opciones extra en la misma grilla, en fila
   nueva; la selección se ve en su propia ficha. El + rota a × mientras está abierta. */
.tile-ico-plus svg{transition:transform .15s}
.tile-group.more-open .tile-ico-plus svg{transform:rotate(45deg)}
.tile-more{display:contents}
.tile-more .tile:first-child{grid-column-start:1}   /* las opciones del "+" arrancan en fila nueva (abajo) */
.tile-group.js-collapsible:not(.more-open) .tile-more{display:none}
/* Operación y Tipo lado a lado en desktop (apilan en mobile vía .form-grid-2) */
.tiles-2col{gap:0 24px}

/* ── Comodidades adicionales (2026-07-27): subtítulo por grupo dentro de la sección ── */
.form-subheading{margin:18px 0 8px;font-size:13px;font-weight:600;color:var(--text-2)}
.form-subheading:first-of-type{margin-top:4px}

/* ── Subtipo de propiedad (2026-07-28): recuadro insertado dentro de Tipo — el fondo propio y el
   rótulo "pill" (en vez de un field-label genérico) comunican que es parte de Tipo, no una sección
   nueva del formulario (opción elegida tras revisar mockups). ── */
.subtype-inset{margin-top:14px;background:var(--surface-2);border:1px solid var(--border-default);
  border-radius:var(--radius-md);padding:12px 14px 14px}
.subtype-pill{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;font-weight:700;
  color:var(--accent-soft-fg);background:var(--accent-soft);padding:4px 10px;border-radius:var(--radius-pill);
  margin-bottom:10px}
.subtype-pill svg{width:13px;height:13px}

/* ── Orientación (REVISION-UX-3 punto 6, 2026-07-29): un solo control circular — reemplaza la
   cruz de 8 fichas de 54px + rosa de los vientos gigante detrás (parecían 8 controles separados
   en vez de uno). La rosa ahora es parte visual del propio dial (no un adorno detrás de fichas
   ajenas), con los 8 sectores clickeables superpuestos y el valor elegido en el centro. ── */
.compass-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:6px}
.compass-head .field-label{margin:0}
.compass-head .hint{margin:0}
.orientation-dial{position:relative;width:180px;height:180px;margin:4px auto 0}
.orientation-dial-rose{position:absolute;inset:14px;color:var(--border-strong);pointer-events:none;z-index:0}
.orientation-dial-rose svg{width:100%;height:100%}
/* Centro: el valor elegido en texto, o "Sin elegir" — actualizado en vivo por bindOrientationDial (app.js). */
.orientation-dial-center{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:2;
  width:64px;height:64px;border-radius:50%;background:var(--surface);border:1px solid var(--border-default);
  display:flex;align-items:center;justify-content:center;text-align:center;padding:4px;
  font:600 11px/1.2 var(--font-sans);color:var(--text-2);pointer-events:none}
/* 8 sectores clickeables — posiciones fijas (siempre son los mismos 8 puntos cardinales, no hace
   falta calcularlas): objetivo táctil de 44px (WCAG), sobre la rosa (z-index:1). */
.orientation-pt{position:absolute;z-index:1;width:44px;height:44px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;transform:translate(-50%,-50%);
  cursor:pointer;background:transparent;border:1.5px solid transparent;
  transition:border-color .12s,background .12s}
.orientation-pt input{position:absolute;opacity:0;width:1px;height:1px;pointer-events:none}
.orientation-pt span{font:700 11px/1 var(--font-sans);color:var(--text-3)}
.orientation-pt:hover{background:var(--surface-2)}
.orientation-pt:has(input:checked){background:var(--accent-soft);border-color:var(--accent)}
.orientation-pt:has(input:checked) span{color:var(--accent-soft-fg)}
.orientation-pt:has(input:focus-visible){outline:2px solid var(--ring);outline-offset:1px}
.orientation-pt-n{top:2%;left:50%}
.orientation-pt-ne{top:14.6%;left:85.4%}
.orientation-pt-e{top:50%;left:98%}
.orientation-pt-se{top:85.4%;left:85.4%}
.orientation-pt-s{top:98%;left:50%}
.orientation-pt-so{top:85.4%;left:14.6%}
.orientation-pt-o{top:50%;left:2%}
.orientation-pt-no{top:14.6%;left:14.6%}

/* ── Precio por operación: nombre + moneda + importe + estado propio por fila ── */
.op-price-list{display:flex;flex-direction:column;gap:10px}
.op-price-row{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.op-price-row[hidden]{display:none}
.op-price-name{flex:0 0 88px;font-weight:500;font-size:13.5px}
.op-price-row .money-field{flex:1;min-width:150px;max-width:260px}

/* ── Importe: chips de moneda (izquierda, reusa .seg/.seg-opt) + valor con formato (derecha) ── */
.amount-row{display:flex;gap:8px;align-items:stretch}
.amount-row .seg{flex:none;align-items:center}
.amount-row .money-field{flex:1;min-width:0}
.money-field{display:flex;align-items:center;border:1px solid var(--border-default);border-radius:var(--radius-md);
  background:var(--surface);transition:border-color .12s}
.money-field:focus-within{outline:2px solid var(--ring);outline-offset:0;border-color:var(--accent)}
.money-sym{flex:none;padding:0 2px 0 12px;color:var(--text-2);font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap}
.money-sym:empty{padding:0}
.money-field .money-input{border:0;background:transparent;flex:1;min-width:0;font-variant-numeric:tabular-nums}
.money-field .money-input:focus-visible{outline:none;border:0}

/* ── Stepper −/+ (ambientes, dormitorios, baños, cocheras, superficies, antigüedad) ── */
.stepper{display:flex;align-items:stretch;max-width:190px}
.stepper-btn{flex:none;width:40px;border:1px solid var(--border-default);background:var(--surface-2);color:var(--text-1);
  font-size:19px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;user-select:none;
  transition:background .12s}
.stepper-btn:first-child{border-radius:var(--radius-md) 0 0 var(--radius-md);border-right:0}
.stepper-btn:last-child{border-radius:0 var(--radius-md) var(--radius-md) 0;border-left:0}
.stepper-btn:hover{background:var(--border-default)}
.stepper-btn:disabled{opacity:.4;cursor:not-allowed;background:var(--surface-2)}
.stepper-mid{flex:1;min-width:0;display:flex;align-items:center;border-top:1px solid var(--border-default);border-bottom:1px solid var(--border-default);background:var(--surface)}
.stepper-mid:focus-within{border-color:var(--accent)}
.stepper .stepper-input{border:0;border-radius:0;background:transparent;text-align:center;flex:1;min-width:0;font-variant-numeric:tabular-nums;-moz-appearance:textfield}
.stepper .stepper-input:focus-visible{outline:none}
.stepper .stepper-input::-webkit-outer-spin-button,.stepper .stepper-input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.stepper-unit{flex:none;padding:0 9px 0 2px;color:var(--text-3);font-size:12.5px;white-space:nowrap;font-variant-numeric:tabular-nums}

/* ── Precio + expensas lado a lado ── */
.price-cols{display:grid;grid-template-columns:1fr 1fr;gap:6px 24px}
@media (max-width:640px){.price-cols{grid-template-columns:1fr;gap:6px}}

/* Chip de moneda: mismo look neutro que Precio por operación (2026-07-28) — antes tenía acento
   propio acá y quedaban desparejos entre las dos secciones; ahora comparten el .seg-opt base. */

/* ── Campo con unidad fija a la derecha (m²) ── */
.unit-field{display:flex;align-items:center;border:1px solid var(--border-default);border-radius:var(--radius-md);
  background:var(--surface);transition:border-color .12s}
.unit-field:focus-within{outline:2px solid var(--ring);outline-offset:0;border-color:var(--accent)}
.unit-field .unit-input{border:0;background:transparent;flex:1;min-width:0;font-variant-numeric:tabular-nums}
.unit-field .unit-input:focus-visible{outline:none;border:0}
.unit-suf{flex:none;padding:0 12px 0 4px;color:var(--text-3);font-weight:500;font-variant-numeric:tabular-nums}
/* Ícono A LA IZQUIERDA DEL TÍTULO (2026-07-28, corregido — antes vivía adentro del input): Superficie
   cubierta/semicubierta usan el ícono del Tipo elegido (se actualiza en vivo, ver bindUnitTypeIcon
   en app.js), Frente/Largo del terreno usan el ícono de Lote (fijo, no cambia con el Tipo). */
.field-label-row{display:flex;align-items:center;gap:6px}
.field-label-ico{display:inline-flex;flex:none;color:var(--text-3)}
.field-label-ico svg{width:16px;height:16px}

/* ── Superficie total: valor calculado, sin caja de input (Cubierta + Semicubierta) ── */
.sqm-total-stat{height:38px;display:flex;align-items:center;gap:8px;font-size:19px;font-weight:600;
  color:var(--text-1);font-variant-numeric:tabular-nums}
.sqm-total-ico{display:flex;flex:none;color:var(--text-3)}
.sqm-total-ico svg{width:20px;height:20px}
[data-land-section][hidden]{display:none}   /* .form-grid-3 es display:grid; sin esto el [hidden] no oculta */

/* Piso / N° de lote (REVISION-UX-3 punto 8) — según el Tipo, como mucho UNO de los dos se
   muestra nunca: el panel visible ocupa el ancho completo en vez de dejar la mitad de la fila
   vacía (antes quedaba un hueco al lado, sin ningún campo). */
[data-type-panel][hidden]{display:none}
.form-grid-2-solo:has([data-type-panel][hidden]) > [data-type-panel]:not([hidden]){grid-column:1 / -1}

/* ── Visualizador de conteo: un íconito por unidad (ambientes/dorm./baños/cocheras) ── */
.count-viz{display:flex;flex-wrap:wrap;align-items:center;gap:4px;margin-top:7px;min-height:18px}
/* Un solo lenguaje visual (REVISION-UX-3 punto 3): TODOS los íconos del viz — antes .cv-i (línea,
   accent) y .cv-obj (relleno, text-2) eran dos tratamientos distintos; ahora un solo selector. */
.count-viz .cv-i{width:16px;height:16px;color:var(--accent);flex:none}
.count-viz .cv-more{font-size:14px;font-weight:600;color:var(--text-3);line-height:1;padding-left:2px}

/* Separador liviano dentro de una .step-card entre dos subgrupos de campos (ej. "Cómo es":
   superficies/ambientes arriba, Comodidades abajo). */
.step-card-divider{height:1px;background:var(--border-default);margin:16px 0 14px}

/* ── Comodidades: zona principal (chips) + zona secundaria colapsable (REVISION-UX-3 punto 4,
   reemplaza la versión "sugeridas por Tipo" de la revisión anterior — mismo componente base) ── */
.amenities-head{display:flex;align-items:center;gap:10px;margin-bottom:9px;flex-wrap:wrap}
.amenities-head .hint{margin:0}
.amenities-count{margin-left:auto;font:500 12.5px/1 var(--font-sans);color:var(--text-2)}
.amenities-chips{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px}
/* Chip de 34px con ícono de 16px + etiqueta (punto 4) — reemplaza el punto de estado genérico:
   el ícono YA comunica de qué se trata, y el cambio de color en is-on ya marca el estado. */
.amenity-chip{display:inline-flex;align-items:center;gap:7px;height:34px;padding:0 13px 0 10px;
  border:1.5px solid var(--border-default);border-radius:var(--radius-pill);background:var(--surface);
  color:var(--text-2);font:500 13px/1 var(--font-sans);cursor:pointer;position:relative;
  transition:border-color .12s,background .12s,color .12s}
.amenity-chip:hover{border-color:var(--border-strong)}
.amenity-chip-ico{display:flex;flex:none;width:16px;height:16px;color:var(--text-3)}
.amenity-chip-ico svg{width:16px;height:16px}
.amenity-chip.is-on{border-color:var(--accent);background:var(--accent-soft);color:var(--accent-soft-fg)}
.amenity-chip.is-on .amenity-chip-ico{color:var(--accent-soft-fg)}
/* Marcada pero no habitual para el Tipo actual (punto 4/5) — nunca desaparece, solo se distingue
   con un borde punteado; sigue mostrando is-on si está tildada. */
.amenity-chip.is-unusual{border-style:dashed}
.amenity-chip.is-unusual.is-on{border-color:var(--border-strong)}
/* Promovida por el perfil del usuario (punto 4) — punto chico en la esquina + title explicando
   por qué está acá (bindAmenitiesSuggested, app.js). */
.amenity-chip.is-promoted::after{content:"";position:absolute;top:3px;right:3px;width:5px;height:5px;
  border-radius:50%;background:var(--accent)}
/* Zona secundaria: fila ancha tipo collapse-row (mismo componente que "Detalles opcionales",
   bindCollapseRows en app.js) — el buscador vive ADENTRO del cuerpo, como ayuda, no arriba. */
.amenities-search-input{width:100%;height:34px;margin-bottom:12px}
.amenities-all[hidden]{display:none}
/* Un grupo entero (encabezado + fichas) se oculta junto si el filtro no le deja ninguna ficha
   visible (punto 6) — antes solo se escondían las fichas, el <h4> quedaba huérfano. */
.amenities-group[hidden]{display:none}
.amenities-empty{margin:10px 0 0;text-align:center;color:var(--text-3);font-size:13px}
.amenities-empty[hidden]{display:none}
/* Parte H (mobile <860px): objetivo táctil mínimo 44px en los chips (34px en desktop, donde
   el mouse no lo necesita) y en el buscador de la zona secundaria. */
@media (max-width:860px){
  .amenity-chip{height:44px}
  .amenities-search-input{height:44px}
}

/* ── Barra inferior pegajosa del form de propiedad (parte D, reemplaza .form-actions) ── */
/* Negative margin = bleed hasta el borde de .content (padding:22px 20px, style.css arriba) —
   así la barra queda pegada al piso del viewport en vez de flotar adentro del padding. */
.prop-bottombar{position:sticky;bottom:0;z-index:12;background:var(--surface);
  border-top:1px solid var(--border-default);box-shadow:0 -8px 24px -20px rgba(15,23,42,.5);
  margin:18px -20px -22px}
.prop-bottombar-inner{max-width:1060px;margin:0 auto;padding:12px 20px;display:flex;align-items:center;gap:14px;flex-wrap:wrap}
/* REVISION-UX-4 punto B6 (2026-07-29): entre 861 y 1000px (todavía en modo "desktop" — el sticky
   de acá abajo, no el fixed de <860px) la línea de faltantes salta de renglón y la barra sticky
   crece a dos filas, corriendo todo el contenido de arriba cada vez que cambia de 1 a 2 líneas.
   Altura mínima fija para las dos filas: nunca más "salto" de layout en ese rango angosto. */
@media (min-width:861px) and (max-width:1000px){.prop-bottombar-inner{min-height:64px}}
.prop-bottombar-missing{display:flex;align-items:center;gap:9px;min-width:0}
.prop-bottombar-dot{width:9px;height:9px;border-radius:50%;background:var(--warn);flex:none}
.prop-bottombar-dot.is-ok{background:var(--ok)}
.prop-bottombar-missing-label{font:500 13px/1.4 var(--font-sans)}
.prop-bottombar-missing-text{font:400 13px/1.4 var(--font-sans);color:var(--text-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.prop-bottombar-spacer{flex:1}
.prop-bottombar-autosave{font:400 12.5px/1 var(--font-sans);color:var(--text-3)}
/* Botón invertido (fondo oscuro + texto acento). Solo funciona en tema CLARO: `--text-1` cambia
   con el tema, así que en oscuro quedaba teal sobre casi blanco (1,75:1) — el botón más
   importante del formulario era el menos legible de la barra. En oscuro se usa el par estándar
   del design system (fondo acento + `--accent-fg`), que el propio set de tokens garantiza AA. */
.btn-activate{background:var(--text-1);color:var(--accent);border-color:transparent;display:inline-flex;align-items:center;gap:8px}
.btn-activate:hover{background:var(--text-1);opacity:.9}
html.dark .btn-activate,html.dark .btn-activate:hover{background:var(--accent);color:var(--accent-fg)}
.prop-bottombar-count{font:700 11px/1 var(--font-sans);background:rgba(34,211,166,.18);padding:3px 6px;border-radius:5px}

/* Parte H (mobile <860px): la barra queda fija al viewport (no sticky-en-flujo) y el botón
   principal ("Revisar y activar") pasa a ocupar el ancho disponible en su propia fila.
   El indicador de autoguardado se oculta acá — ya vive también en .prop-journey (arriba),
   no se pierde información, solo el duplicado visual. .prop-form suma padding-bottom para que
   el último card nunca quede tapado por la barra fija (ver reserva exacta más abajo). */
@media (max-width:860px){
  /* REVISION-UX-4 punto B5 — tablet vertical (768px): a este ancho el form ocupaba el 100% de la
     pantalla sin límite (1060px nunca restringe por debajo de 860 igual), y las líneas de texto
     pasaban los ~90 caracteres cómodos de leer. max-width:720px, igual que cualquier columna de
     lectura — el resto de la maqueta (grillas de 1 columna, etc.) ya está resuelto arriba. */
  .prop-form{padding-bottom:190px;max-width:720px}
  /* Teclado virtual: con el foco en un campo de la mitad inferior, el navegador auto-scrollea
     el input a la vista pero no sabe que la barra inferior FIJA tapa esa franja — scroll-margin
     le reserva el mismo alto que ya reserva .prop-form (barra + safe-area) para que el campo
     quede realmente visible arriba de la barra, no tapado por ella. */
  .prop-form input,.prop-form select,.prop-form textarea{scroll-margin-bottom:100px}
  .prop-bottombar{position:fixed;left:0;right:0;bottom:0;margin:0;z-index:16;
    box-shadow:0 -8px 24px -14px rgba(15,23,42,.4);
    padding-bottom:env(safe-area-inset-bottom,0)}
  .prop-bottombar-inner{max-width:none;padding:10px 14px;gap:8px}
  .prop-bottombar-missing{flex:1 1 100%;order:1}
  .prop-bottombar-spacer,.prop-bottombar-autosave{display:none}
  .prop-bottombar .btn-activate{order:2;flex:1 1 100%;justify-content:center;min-height:44px}
  .prop-bottombar .btn-secondary{order:3;flex:1;min-height:44px;justify-content:center}
  .prop-bottombar .btn-ghost{order:4;flex:none;min-height:44px;display:inline-flex;align-items:center;padding:0 16px}
}

/* ── Switch compacto en línea (Sin expensas, Apto crédito): reusa .switch/.slider ── */
.switch-inline{display:inline-flex;align-items:center;gap:9px;cursor:pointer;font-size:13.5px;font-weight:500;height:38px}
.expenses-switch-wrap{display:flex;flex-direction:column;justify-content:center}

/* ── "Alquilada" + "Fin de contrato" (REVISION-UX-3 punto 7) — el switch y su campo van en su
   propia columna (no una grilla de 2 con un field-label vacío de relleno); "Fin de contrato" se
   revela recién al marcar "Alquilada", nunca queda como un campo gris sin contexto. ── */
.rented-field{margin-bottom:14px}
.rented-reveal{margin-top:10px;max-width:260px;animation:rentedReveal .15s ease}
.rented-reveal[hidden]{display:none}
@keyframes rentedReveal{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.rented-reveal{animation:none}}

/* ── Antigüedad: stepper + "A estrenar"/"En construcción" a la derecha (2026-07-28: se sacó la
   animación de la persona que envejece — no quedaba bien). ── */
.age-row{display:flex;align-items:center;gap:18px;flex-wrap:wrap}
.age-row .stepper{flex:0 0 auto;width:150px}
.age-row .stepper-input:disabled{color:var(--text-3);cursor:not-allowed}
.age-switches{display:flex;gap:8px;flex-wrap:wrap}
.age-switch-btn{flex:none;padding:8px 13px;border:1.5px solid var(--border-default);border-radius:var(--radius-md);
  background:var(--surface);color:var(--text-2);font:500 13px var(--font-sans);cursor:pointer;
  transition:border-color .12s,background .12s,color .12s}
.age-switch-btn:hover{border-color:var(--border-strong);color:var(--text-1)}
.age-switch-btn.on{border-color:var(--accent);background:var(--accent-soft);color:var(--accent-soft-fg)}
.age-switch-btn:focus-visible{outline:2px solid var(--ring);outline-offset:1px}

/* ── Estado: cabecera con ficha de color ── */
.st-head{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:12px}
.st-badge{--st-c:#64748B;display:inline-flex;align-items:center;font-size:12.5px;font-weight:600;padding:4px 12px;border-radius:999px;
  background:color-mix(in srgb,var(--st-c) 15%,transparent);color:var(--st-c);border:1px solid color-mix(in srgb,var(--st-c) 32%,transparent)}
.st-badge[data-st="borrador"],.st-dot[data-st="borrador"]{--st-c:#5B6675}       /* gris oscuro */
.st-badge[data-st="activo"],.st-dot[data-st="activo"]{--st-c:#0E9F6E}           /* verde */
.st-badge[data-st="reservada"],.st-dot[data-st="reservada"]{--st-c:#0EA5E9}     /* celeste */
.st-badge[data-st="vendida"],.st-dot[data-st="vendida"]{--st-c:#3B82F6}         /* azul */
.st-badge[data-st="uso_interno"],.st-dot[data-st="uso_interno"]{--st-c:var(--accent)}   /* color de la página */
.st-badge[data-st="suspendido"],.st-badge[data-st="no_activo"],.st-dot[data-st="suspendido"]{--st-c:#94A3B8}  /* gris claro */

/* ── Estado: el badge de la barra sticky es un desplegable para cambiarlo directo ── */
.st-menu{position:relative;flex:none}
.st-badge-btn{font-family:inherit;cursor:pointer;gap:7px}
.st-badge-btn .st-caret{transition:transform .15s}
.st-badge-btn[aria-expanded="true"] .st-caret{transform:rotate(180deg)}
.st-menu-panel{min-width:185px}
.st-menu-panel .menu-item.current{background:var(--surface-2)}
.st-dot{--st-c:#64748B;width:10px;height:10px;border-radius:50%;background:var(--st-c);flex:none}

/* ── Acciones del form + indicador de autoguardado ── */
.form-actions{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-top:18px;padding-top:16px;border-top:1px solid var(--border-default)}
.btn-success{background:var(--ok);color:#fff;border-color:transparent}
.btn-success:hover{background:var(--ok-strong)}
.autosave-ind{font-size:12.5px;color:var(--text-3);min-height:16px}
.autosave-ind.saving{color:var(--text-3)}
.autosave-ind.saved{color:var(--ok-strong)}

/* ── Título espejo + ficha de estado dentro de .prop-journey (parte A del rediseño 2026-07-29;
   el contenedor .prop-sticky que las envolvía se reemplazó por .prop-journey más arriba en este
   archivo — estas dos reglas quedaron porque .prop-journey-title-row las sigue usando tal cual) ── */
.prop-sticky-title{font-size:17px;font-weight:600;color:var(--text-1);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.st-badge-lg{font-size:13.5px;padding:6px 15px;flex:none}

/* ── Importador de portales: progreso por tandas + resumen ── */
.import-bar{height:8px;border-radius:999px;background:var(--surface-2);overflow:hidden;margin-top:12px}
.import-bar-fill{height:100%;background:var(--accent);border-radius:999px;transition:width .35s ease}
.import-stats{display:flex;flex-wrap:wrap;gap:10px;margin-top:6px}
.import-stat{flex:1 1 110px;padding:12px 14px;border:1px solid var(--border-default);border-radius:var(--radius-md);
  background:var(--surface-2);display:flex;flex-direction:column;gap:2px}
.import-stat strong{font-size:22px;line-height:1.1;color:var(--text-1)}
.import-stat span{font-size:12.5px;color:var(--text-3)}
.import-stat.is-bad strong{color:var(--err)}

/* ── Campos calculados (Superficie total / del terreno) — REVISION-UX-4 punto A2 (2026-07-29):
   vuelve a un solo patrón "Ajustar manualmente" para los DOS campos (pisa la decisión de la
   revisión anterior, que en Superficie total sacaba el control por completo y en Superficie del
   terreno usaba una casilla con motivo propio) — el input a mano NUNCA está visible de entrada:
   se ve solo el valor calculado + la fórmula, y el link recién revela el input al hacer clic, ya
   precargado con el valor calculado (input siempre en el DOM, sincronizado en vivo por
   bindSqmTotal()/bindLandSection() aunque esté deshabilitado — "precargado" sale gratis de eso,
   no hace falta copiarlo a mano al abrir). Con el input abierto, el link cambia a "Volver al
   cálculo automático". */
.calc-head{display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap}
.calc-toggle{background:none;border:0;padding:2px 0;font:inherit;font-size:13px;color:var(--text-2);
  cursor:pointer;text-decoration:underline;text-underline-offset:2px;display:inline-block;margin-top:4px}
.calc-toggle[hidden]{display:none}
.calc-toggle:hover{color:var(--accent-hover)}
.calc-toggle:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* Leyenda bajo el valor calculado (ej. "Cubierta + semicubierta") — oculta en modo manual. */
.calc-formula-hint{margin:2px 0 0;font-size:12px;color:var(--text-3)}
.calc-formula-hint[hidden]{display:none}

/* ── Video de la propiedad (mt_25): iframe responsive 16:9 ── */
.video-embed{position:relative;width:100%;padding-top:56.25%;border-radius:var(--radius-md);
  overflow:hidden;background:var(--surface-2)}
.video-embed iframe{position:absolute;inset:0;width:100%;height:100%;border:0}

/* ═══════════════════ Ficha de propiedad — rediseño (FICHA-PROPIEDAD.md, 2026-07-29) ═══════════════════ */

/* ── A: barra de identidad pegajosa (misma familia visual que .prop-journey del alta) ── */
.pf-bar{position:sticky;top:0;z-index:15;background:var(--surface);border-bottom:1px solid var(--border-default)}
.pf-bar-inner{max-width:1120px;margin:0 auto;padding:12px 24px;display:flex;align-items:center;gap:14px}
.pf-bar-thumb,.pf-bar-thumb-ph{width:44px;height:34px;border-radius:8px;flex:none;background:var(--surface-2);object-fit:cover}
.pf-bar-thumb-ph{display:flex;align-items:center;justify-content:center;color:var(--text-3)}
.pf-bar-id{min-width:0;flex:1}
.pf-bar-title-row{display:flex;align-items:center;gap:9px;min-width:0}
.pf-bar-back{color:var(--text-3);flex:none;display:flex}
.pf-bar-title{margin:0;font-size:17px;font-weight:600;color:var(--text-1);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.pf-bar-meta{margin:2px 0 0;font-size:12.5px}
.pf-bar-price{font-size:19px;font-weight:700;font-variant-numeric:tabular-nums;flex:none;white-space:nowrap}
.pf-bar-actions{display:flex;gap:8px;flex:none}

/* ── .pf-page envuelve TODA la ficha (barra + body + barra fija de mobile) y establece la
   containment UNA sola vez para las tres — Sección E: container queries, no viewport (el sidebar
   de 232px y el zoom de fs-2/3 hacen que el viewport mienta sobre el ancho real disponible, mismo
   criterio que .prop-form del alta).
   Dos bugs reales encontrados con un screenshot real de Chrome headless a 1440px (Máximo reportó
   la ficha "totalmente desfazada"), los dos por el mismo motivo raíz — un elemento no puede
   responder a @container sobre SÍ MISMO ni sobre algo que no sea su descendiente:
   1) .pf-body no puede ser a la vez el elemento con max-width:1120px Y el que establece el
      container-type — su ancho nunca supera 1120px, así que "@container pf (max-width:1200px)"
      quedaba SIEMPRE verdadero (1120 < 1200) sin importar cuán ancha fuera la pantalla real, y la
      ficha quedaba en una sola columna siempre. Fix: .pf-page (sin tope de ancho) es el
      contenedor; .pf-body es un simple descendiente centrado que solo pone el tope visual.
   2) .pf-bar (barra de identidad) y .pf-actions-fixed-bar (barra fija de mobile) vivían FUERA de
      ese contenedor (antes .pf-wrap, ahora .pf-page) — sus reglas "@container pf" (ocultar
      metadatos en mobile, mostrar la barra fija) nunca podían aplicar porque ninguna de las dos
      era descendiente del contenedor. Fix: las tres viven ahora dentro de .pf-page. */
.pf-page{container-type:inline-size;container-name:pf}
.pf-body{max-width:1120px;margin:20px auto 0;padding:0 24px 40px}
.pf-grid{display:grid;grid-template-columns:1fr 372px;gap:20px;align-items:start}
.pf-grid > .pf-c-gallery,.pf-grid > .pf-c-price,.pf-grid > .pf-c-descr,.pf-grid > .pf-c-map{grid-column:1}
.pf-grid > .pf-c-actions,.pf-grid > .pf-c-shares,.pf-grid > .pf-c-owner,.pf-grid > .pf-c-activity{grid-column:2}

/* ── Precio y operaciones ── */
.pf-op-row{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;padding:8px 0}
.pf-op-row + .pf-op-row{border-top:1px solid var(--border-default)}
.pf-op-name{font-size:12.5px;color:var(--text-3);flex:none;width:100%}
.pf-op-price{font-size:30px;font-weight:700;font-variant-numeric:tabular-nums;color:var(--text-1)}
.pf-op-row.is-secondary .pf-op-price{font-size:24px;font-weight:600;color:var(--text-2)}
.pf-op-unit{font-size:13px;color:var(--text-3)}
.pf-price-chip{display:inline-flex;align-items:center;gap:5px;font-size:12px;font-weight:600;padding:3px 10px;border-radius:999px}
.pf-price-chip.is-down{background:var(--ok-soft);color:var(--ok-strong)}
.pf-price-chip.is-up{background:var(--warn-soft);color:var(--warn-strong)}
.pf-price-history-btn{background:none;border:0;padding:0;font:inherit;font-size:12.5px;color:var(--text-2);cursor:pointer;text-decoration:underline;text-underline-offset:2px}
.pf-price-history-btn:hover{color:var(--accent-hover)}
.pf-stats{display:flex;flex-wrap:wrap;gap:0;margin-top:14px;border-top:1px solid var(--border-default);padding-top:14px}
.pf-stat{padding:0 16px;border-left:1px solid var(--border-default);display:flex;flex-direction:column;gap:2px}
.pf-stat:first-child{padding-left:0;border-left:0}
.pf-stat-num{font-size:22px;font-weight:600;font-variant-numeric:tabular-nums;color:var(--text-1)}
.pf-stat-lbl{font-size:12px;color:var(--text-3)}
.pf-price-history-panel{margin-top:14px;padding-top:14px;border-top:1px solid var(--border-default)}
.pf-price-history-panel[hidden]{display:none}
.pf-ph-row{display:flex;align-items:center;gap:8px;font-size:13px;padding:5px 0;flex-wrap:wrap}
.pf-ph-chart{margin-bottom:10px}

/* ── Descripción y comodidades ── */
.pf-descr-text{white-space:pre-line;line-height:1.65;margin:0}
.pf-amenity-group + .pf-amenity-group{margin-top:14px}
.pf-amenity-group-title{margin:0 0 8px;font-size:12.5px;font-weight:600;color:var(--text-3);text-transform:uppercase;letter-spacing:.03em}
.pf-amenity-chips{display:flex;flex-wrap:wrap;gap:8px}
.pf-amenity-chip{display:inline-flex;align-items:center;gap:6px;font-size:13px;padding:6px 12px 6px 8px;border-radius:999px;
  background:var(--surface-2);color:var(--text-1)}
.pf-amenity-chip svg{width:15px;height:15px;color:var(--accent);flex:none}

/* ── Mapa de solo lectura ── */
.pf-map-addr{margin:0 0 8px;font-size:13.5px}
.pf-map-approx-note{margin:6px 0 0;font-size:12px;color:var(--text-3)}
.pf-map-readonly{height:220px;border-radius:var(--radius-md);overflow:hidden;background:var(--surface-2)}

/* ── Acciones (columna de trabajo) ── */
.pf-actions-primary{display:block;width:100%;text-align:center;margin-bottom:8px}
/* min-width:0 en los botones: un ítem de grid/flex tiene min-width:auto implícito (el tamaño de
   su contenido SIN achicar), así que "Llamar al dueño"/"Copiar ficha" podían forzar cada columna
   1fr más ancha que el propio celular angosto y desbordar toda la tarjeta (bug real, visto con
   un screenshot de Chrome headless a 390px) — con min-width:0 el texto trunca con ellipsis en
   vez de reventar el layout. */
.pf-actions-secondary{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:14px}
.pf-actions-secondary .btn{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pf-actions-status{padding-top:14px;border-top:1px solid var(--border-default)}
.pf-actions-status .st-menu{width:100%}
.pf-actions-status .st-badge-btn{width:100%;justify-content:space-between}
.pf-actions-status form .menu-item{width:100%}

/* ── Links compartidos con métrica ── */
.pf-share-item{padding:10px 0}
.pf-share-item + .pf-share-item{border-top:1px solid var(--border-default)}
.pf-share-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:6px}
.pf-share-label{font-weight:500;font-size:13.5px}
.pf-share-metric{font-size:12.5px;color:var(--text-3)}
.pf-share-metric strong{color:var(--text-1);font-weight:600}

/* ── Propietario ── */
.pf-owner-head{display:flex;align-items:center;gap:10px}
.pf-owner-name{font-weight:600;font-size:14.5px}
.pf-contact-gap{margin-top:10px;padding:8px 11px;font-size:12.5px;border-radius:var(--radius-md);
  background:var(--warn-soft);color:var(--warn-strong)}

/* ── C2: timeline unificado ── */
.pf-tl-tabs{display:flex;gap:4px;margin-bottom:12px;border-bottom:1px solid var(--border-default)}
.pf-tl-tab{background:none;border:0;border-bottom:2px solid transparent;padding:8px 4px;margin-right:14px;font:inherit;
  font-size:13.5px;font-weight:500;color:var(--text-3);cursor:pointer}
.pf-tl-tab.is-on{color:var(--accent);border-bottom-color:var(--accent)}
.pf-tl-tab-count{display:inline-flex;align-items:center;justify-content:center;min-width:16px;height:16px;padding:0 4px;
  margin-left:5px;border-radius:999px;background:var(--warn-soft);color:var(--warn-strong);font-size:10.5px;font-weight:700}
.pf-tl-list{list-style:none;margin:0;padding:0}
.pf-tl-item{display:flex;gap:10px;padding:9px 0;border-top:1px solid var(--border-default)}
.pf-tl-item:first-child{border-top:0;padding-top:0}
.pf-tl-item[hidden]{display:none}
.pf-tl-dot{width:9px;height:9px;border-radius:50%;flex:none;margin-top:5px;background:var(--text-3)}
.pf-tl-dot.is-accent{background:var(--accent)}
.pf-tl-dot.is-warn{background:var(--warn)}
.pf-tl-body{min-width:0;flex:1;font-size:13.5px}
.pf-tl-when{font-size:11.5px;color:var(--text-3)}
.pf-tl-more{width:100%;margin-top:10px}
.pf-tl-note-form{margin-top:14px;padding-top:14px;border-top:1px solid var(--border-default)}

/* ── B: galería y visor de fotos ── */
.pgallery-card{padding:0;overflow:hidden}
.pgallery-empty{display:flex;flex-direction:column;align-items:center;gap:8px;color:var(--text-3);padding:40px 20px}
/* Track de scroll nativo + scroll-snap: es lo que hace de swipe (B5) Y de mecanismo único de
   navegación (arrows/teclado/miniatura hacen scrollTo al slide destino) — cada foto vive en su
   propio <img> SIEMPRE presente en el DOM, así nunca hay un blanco/spinner intermedio (B2) sin
   necesitar una segunda capa de crossfade por opacidad. */
.pgallery-stage{position:relative;aspect-ratio:16/9;background:var(--surface-2);overflow:hidden}
.pgallery-track{display:flex;width:100%;height:100%;overflow-x:auto;scroll-snap-type:x mandatory;
  scroll-behavior:smooth;-webkit-overflow-scrolling:touch;scrollbar-width:none}
.pgallery-track::-webkit-scrollbar{display:none}
@media (prefers-reduced-motion:reduce){.pgallery-track{scroll-behavior:auto}}
.pgallery-slide{flex:0 0 100%;width:100%;height:100%;scroll-snap-align:center;scroll-snap-stop:always;display:flex}
.pgallery-slide[hidden]{display:none}
.pgallery-slide img{width:100%;height:100%;object-fit:contain}
.pgallery-arrow{position:absolute;top:50%;transform:translateY(-50%);width:38px;height:38px;border-radius:50%;border:0;
  background:rgba(8,17,31,.55);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;
  opacity:0;transition:opacity .15s ease}
.pgallery-stage:hover .pgallery-arrow,.pgallery-arrow:focus-visible{opacity:1}
.pgallery-arrow-prev{left:10px}
.pgallery-arrow-next{right:10px}
.pgallery-arrow:hover{background:rgba(8,17,31,.75)}
.pgallery-counter{position:absolute;top:10px;left:10px;background:rgba(8,17,31,.6);color:#fff;font-size:12px;
  font-weight:600;padding:4px 10px;border-radius:999px;font-variant-numeric:tabular-nums}
.pgallery-tabs{position:absolute;top:10px;right:10px;display:flex;gap:2px;background:rgba(8,17,31,.55);border-radius:999px;padding:3px}
.pgallery-tab{background:none;border:0;color:#fff;font:inherit;font-size:12px;font-weight:600;padding:4px 10px;
  border-radius:999px;cursor:pointer;opacity:.75}
.pgallery-tab.is-on{background:#fff;color:var(--text-1);opacity:1}
.pgallery-expand{position:absolute;bottom:10px;right:10px;width:32px;height:32px;border-radius:8px;border:0;
  background:rgba(8,17,31,.55);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer}
.pgallery-expand:hover{background:rgba(8,17,31,.75)}
.pgallery-strip{display:flex;gap:8px;padding:10px;overflow-x:auto;scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){.pgallery-strip{scroll-behavior:auto}}
.pgallery-thumb{position:relative;flex:none;width:78px;height:58px;padding:0;border-radius:8px;overflow:hidden;
  border:2px solid transparent;cursor:pointer;background:var(--surface-2)}
.pgallery-thumb.is-on{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
.pgallery-thumb[hidden]{display:none}
.pgallery-thumb img{width:100%;height:100%;object-fit:cover;display:block}
.pgallery-thumb-tag{position:absolute;bottom:2px;left:2px;background:rgba(8,17,31,.75);color:#fff;font-size:9px;padding:1px 4px;border-radius:4px}
.pgallery-dots{display:none;gap:5px;justify-content:center;padding:8px 0}
.pgallery-dot{width:6px;height:6px;border-radius:999px;background:var(--border-strong);flex:none;transition:width .15s ease,background .15s ease}
.pgallery-dot.is-on{width:16px;background:var(--accent)}
.pgallery-dot[hidden]{display:none}

/* Visor a pantalla completa: fondo fijo oscuro, no sigue el tema claro (es un visor, no la página) */
.pgallery-viewer{position:fixed;inset:0;z-index:200;background:#0B1220;display:flex;flex-direction:column}
.pgallery-viewer[hidden]{display:none}
.pgallery-viewer-bar{display:flex;align-items:center;gap:12px;padding:12px 16px;color:#fff}
.pgallery-viewer-counter{font-size:13px;font-weight:600;font-variant-numeric:tabular-nums}
.pgallery-viewer-chip{font-size:12px;color:rgba(255,255,255,.6);flex:1}
.pgallery-tabs-viewer{position:static;background:rgba(255,255,255,.1)}
.pgallery-viewer-close{background:none;border:0;color:#fff;width:32px;height:32px;border-radius:8px;cursor:pointer;
  display:flex;align-items:center;justify-content:center}
.pgallery-viewer-close:hover{background:rgba(255,255,255,.12)}
.pgallery-viewer-stage{position:relative;flex:1;display:flex;align-items:center;justify-content:center;min-height:0;overflow:hidden}
.pgallery-viewer-img{max-width:100%;max-height:100%;object-fit:contain;cursor:zoom-in;touch-action:none;user-select:none}
.pgallery-viewer-img.is-zoomed{cursor:grab}
.pgallery-viewer-img.is-dragging{cursor:grabbing}
.pgallery-viewer-stage .pgallery-arrow{width:48px;height:48px}
.pgallery-strip-viewer{display:flex;gap:8px;padding:14px 16px;overflow-x:auto;justify-content:center}

/* ── Modo cliente: barra oscura + panel de contacto pegajoso (D1) ── */
.pub2-bar{position:sticky;top:0;z-index:20;background:#0B1220;color:#fff;padding:10px 20px;display:flex;
  align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;font-size:13px}
.pub2-bar span{color:rgba(255,255,255,.75)}
/* Mismo criterio que .pf-wrap/.pf-body: el contenedor de la @container query no puede ser el
   mismo elemento que su propio tope de ancho (ver el bug real documentado arriba). */
.pub2-wrap{container-type:inline-size;container-name:pub2}
.pub2-body{max-width:1120px;margin:24px auto 0;padding:0 24px 40px;display:grid;grid-template-columns:1fr 340px;gap:24px;
  align-items:start}
.pub2-contact{position:sticky;top:20px;padding:20px;border:1px solid var(--border-default);border-radius:var(--radius-lg);
  background:var(--surface);display:flex;flex-direction:column;gap:12px}
.pub2-contact-price{font-size:26px;font-weight:700;font-variant-numeric:tabular-nums}
.pub2-contact-broker{display:flex;align-items:center;gap:10px;padding-top:10px;border-top:1px solid var(--border-default)}
.pub2-contact-broker-name{font-weight:600;font-size:13.5px}
.pub2-contact-broker-agency{font-size:12px;color:var(--text-3)}
.pub2-stats{display:flex;flex-wrap:wrap;gap:16px;margin:10px 0}
.pub2-stat{display:flex;flex-direction:column;gap:2px}
.pub2-stat-num{font-size:19px;font-weight:600;font-variant-numeric:tabular-nums}
.pub2-stat-lbl{font-size:11.5px;color:var(--text-3)}
.pub2-title{margin:0;font-size:25px;font-weight:600;letter-spacing:-.015em;text-wrap:pretty}
.pub2-chips{display:flex;flex-wrap:wrap;gap:8px}
.pub2-chip{display:inline-flex;align-items:center;height:34px;padding:0 13px;border-radius:999px;border:1px solid var(--border-default);font-size:13.5px;font-weight:500}

/* ── E: responsive de la ficha — container queries sobre el CONTENEDOR de la ficha ──
   El doc pedía ">1200px de contenedor" para 2 columnas, pero .pf-wrap vive DENTRO de .content
   (compartido con toda la app), que tiene su propio max-width:1100px (border-box) — 1060px de
   ancho real disponible tras su padding, siempre, sin importar cuán ancha sea la pantalla. Pedir
   ">1200" era literalmente imposible de cumplir (bug real, encontrado con un screenshot real de
   Chrome headless a 1440px: la ficha quedaba en una sola columna siempre). 1000px dejan margen
   bajo ese techo de ~1060px: cualquier pantalla de escritorio normal (≥~1280px de viewport) llega
   a los 1060px reales y activa las 2 columnas; solo una ventana angosta o una tablet colapsa. */
@container pf (max-width:1000px){
  .pf-grid{grid-template-columns:1fr}
  .pf-grid > .pf-c-actions,.pf-grid > .pf-c-shares,.pf-grid > .pf-c-owner,.pf-grid > .pf-c-activity{grid-column:1}
  .pf-c-actions{order:-1}
  .pf-c-shares{order:6}
  .pf-c-owner{order:7}
  .pf-c-activity{order:8}
}
@container pf (max-width:839px){
  .pf-body{padding:0 0 84px}
  .pf-c-gallery .card,.pgallery-card{border-radius:0;border-left:0;border-right:0}
  .pf-c-price,.pf-c-descr,.pf-c-map,.pf-c-shares,.pf-c-owner,.pf-c-activity{margin-left:16px;margin-right:16px}
  .pf-c-actions{margin-left:16px;margin-right:16px}
  /* Condensada a atrás+miniatura+título+estado (doc E, "<840: título+estado+un botón") — precio
     y acciones se sacan de acá: la barra fija de abajo YA tiene Editar/Compartir, y el precio ya
     está repetido en la tarjeta de Precio y operaciones — mantener los tres (miniatura+precio+
     acciones) en la misma fila que el título es lo que desbordaba el ancho real en un celular
     angosto (bug real, encontrado con un screenshot de Chrome headless a 390px: el botón "Editar"
     quedaba directamente fuera de la pantalla, sin scroll horizontal visible para llegar a él). */
  .pf-bar-meta,.pf-bar-price,.pf-bar-actions{display:none}
  .pf-map-readonly{height:180px}
  .pf-actions-fixed-bar{position:fixed;bottom:0;left:0;right:0;z-index:16;display:flex;gap:8px;padding:10px 16px;
    padding-bottom:calc(10px + env(safe-area-inset-bottom));background:var(--surface);border-top:1px solid var(--border-default)}
  .pf-actions-fixed-bar .btn{flex:1}
}
@container pf (min-width:840px){.pf-actions-fixed-bar{display:none}}
@container pf (min-width:1001px){.pgallery-dots{display:none !important}}

@container pf (max-width:1023px){
  .pgallery-thumb{width:88px;height:64px}
}
@container pf (max-width:639px){
  .pgallery-stage{aspect-ratio:4/3}
  .pgallery-strip{display:none}
  .pgallery-dots{display:flex}
}
/* Colisión real con el bottom-nav de la PWA (mismo problema ya resuelto para .prop-bottombar,
   ver el bloque "Bottom-nav mobile" más arriba): bottom-nav es fixed+z-index:50, y a este ancho
   .pf-actions-fixed-bar TAMBIÉN es fixed con bottom:0 — sin este ajuste quedaba completamente
   tapada debajo (bug real, encontrado con un screenshot de Chrome real vía Puppeteer a 390px: la
   barra de Editar/Compartir nunca se veía). Tiene que declararse DESPUÉS del bloque
   "@container pf (max-width:839px)" de más arriba (que fija bottom:0) para ganarle por orden de
   cascada — @media, no @container, porque bottom-nav en sí se activa por ancho de VIEWPORT. */
@media (max-width:640px){
  .pf-actions-fixed-bar{bottom:76px}
}

@container pub2 (max-width:900px){
  .pub2-body{grid-template-columns:1fr}
  .pub2-contact{position:static;order:-1}
}
