/* static/css/boveda.css — PROTOCOL_73 · sistema Bóveda
 *
 * Sistema visual de las superficies PÚBLICAS: la landing (sitio PHP aparte),
 * el hub Protocol y, desde el 2026-09-09, **Deban** (este Django). Canon:
 * .claude/brand/DESIGN_SYSTEM.md, decisión de Alex del 2026-09-05.
 *
 * NO lo cargan Gengo ni Kazen: esas dos siguen en Expediente
 * (.claude/brand/EXPEDIENTE.md + static/css/expediente-tokens.css). La
 * convivencia de los dos sistemas es deliberada — antes de tocar UI, mira
 * en TOOLS.md qué sistema rige la superficie en la que estás.
 *
 * Portado desde la implementación de referencia de la landing
 * (~/protocol73/landing/assets/css/style.css) para que las dos superficies
 * públicas se vean como el mismo producto. Si divergen, gana el canon.
 *
 * OJO — Tailwind's `text-accent` / `bg-surface` / `border-rule` / `text-ink`
 * / `text-muted` / `text-success` / `text-warn` / `text-danger` NO sirven
 * aquí: esas utilidades mapean a `var(--accent)`, `var(--bg)`, etc. de
 * expediente-tokens.css (tailwind.config.js), que este archivo ni carga.
 * En Bóveda el color se consume vía las clases de este archivo (`.bv-card`,
 * `.btn-primary`, `.mark-tool`, `.chip-accent`…) o Tailwind neutral-* +
 * arbitrary values contra `--bv-*` cuando hace falta un tinte suelto.
 */

/* ---------- 1. Tokens Bóveda (§2) ---------- */
:root {
    /* Fondos y superficies (§2.1) */
    --bv-bg-rgb:      10 10 10;     /* #0a0a0a  neutral-950 — fondo base */
    --bv-surface-rgb: 23 23 23;     /* #171717  neutral-900 — tarjetas al 40% */
    --bv-rule-rgb:    38 38 38;     /* #262626  neutral-800 — bordes y riel */

    /* Rampa de texto (§2.2) */
    --bv-ink-rgb:     250 250 250;  /* neutral-50   títulos */
    --bv-body-rgb:    212 212 212;  /* neutral-300  cuerpo principal */
    --bv-muted-rgb:   163 163 163;  /* neutral-400  cuerpo secundario */
    --bv-label-rgb:   115 115 115;  /* neutral-500  etiquetas y metadatos */

    /* Acento — uno solo (§2.3). No hay segundo acento. */
    --bv-accent-rgb:  52 211 153;   /* #34d399  emerald-400 */

    /* Estados de insignia (§6): ámbar = estimado, violeta = cualitativo */
    --bv-warn-rgb:    251 191 36;   /* amber-400 */
    --bv-qual-rgb:    167 139 250;  /* violet-400 */

    --bv-bg:      rgb(var(--bv-bg-rgb));
    --bv-surface: rgb(var(--bv-surface-rgb));
    --bv-rule:    rgb(var(--bv-rule-rgb));
    --bv-ink:     rgb(var(--bv-ink-rgb));
    --bv-body:    rgb(var(--bv-body-rgb));
    --bv-muted:   rgb(var(--bv-muted-rgb));
    --bv-label:   rgb(var(--bv-label-rgb));
    --bv-accent:  rgb(var(--bv-accent-rgb));

    /* Curvas y tiempos exactos (§5) */
    --bv-ease-out:    cubic-bezier(0.23, 1, 0.32, 1);
    --bv-ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
}

/* ---------- 2. Base ---------- */
html.boveda {
    scroll-behavior: smooth;
    /* la barra sticky mide 3rem: los anclajes no quedan debajo (§4) */
    scroll-padding-top: 3rem;
    color-scheme: dark;
}

html.boveda body {
    background-color: var(--bv-bg);
    color: var(--bv-body);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    font-optical-sizing: auto;
    font-family: 'Geist', system-ui, -apple-system, sans-serif;
}

html.boveda h1,
html.boveda h2,
html.boveda h3 { color: var(--bv-ink); }

/* Números siempre tabulares (§3) */
html.boveda .font-mono,
html.boveda table,
html.boveda [data-stat-raw] { font-variant-numeric: tabular-nums; }

.font-geist-mono { font-family: 'Geist Mono', ui-monospace, monospace; }

/* Títulos (§3) */
.display     { letter-spacing: -0.03em; line-height: 1.02; }
.display-sm  { letter-spacing: -0.02em; line-height: 1.1; }

/* ---------- 3. Énfasis de copy (va dentro de los strings de i18n) ---------- */
.hl      { color: var(--bv-accent); font-weight: 700; }
.hl-ink  { color: var(--bv-ink);    font-weight: 700; }

/* ---------- 4. Eyebrow (§3) ----------
   OJO — conflicto del canon heredado de la landing, no un desvío por gusto:
   §2.2 asigna neutral-500 a «etiquetas y metadatos», pero sobre #0a0a0a ese
   tono da 4.18:1 y §7 exige 4.5:1 para texto normal, que es lo que es un
   eyebrow de 11px. La accesibilidad no es opcional, así que va en
   neutral-400 (7.85:1). El token --bv-label se conserva intacto: el arreglo
   de fondo va en `canon/`, no aquí. */
.eyebrow {
    color: var(--bv-muted);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: 0.14em;
    text-transform: uppercase;
}

/* ---------- 5. Superficies ---------- */
.section-alt { background-color: rgb(var(--bv-surface-rgb) / .28); }

/* Mark de herramienta — genérico: "73" en el hub/landing, la inicial de
   cada producto en su propia superficie ("D" en Deban, TOOLS.md). Borde +
   tinte al 12%, NUNCA relleno sólido, para no competirle a la acción
   primaria (§2.3). Renombrado de `.mark-73` el 2026-09-09 al dejar de ser
   exclusivo del hub: mismo patrón, distinta letra, sin glow (el CRT sigue
   derogado). */
.mark-tool {
    background: rgb(var(--bv-accent-rgb) / .12);
    border: 1px solid rgb(var(--bv-accent-rgb) / .35);
    color: var(--bv-accent);
    font-weight: 800;
    border-radius: .5rem;
}

/* ---------- 6. Botones (§4: rounded-full; §5: .press) ---------- */
.btn-primary,
.btn-ghost {
    border-radius: 999px;
    transition: background-color .2s var(--bv-ease-out),
                border-color .2s var(--bv-ease-out),
                color .2s var(--bv-ease-out);
}
.btn-primary {
    background: var(--bv-accent);
    color: #04140d;
    border: 1px solid var(--bv-accent);
    font-weight: 700;
}
.btn-primary:hover { background: #6ee7b7; border-color: #6ee7b7; }
.btn-ghost {
    background: transparent;
    color: var(--bv-ink);
    border: 1px solid rgb(var(--bv-rule-rgb) / .8);
}
.btn-ghost:hover { border-color: rgb(255 255 255 / .18); }

/* .btn-social — destaca sin robarle el sitio a la acción primaria: borde de
   acento y fondo al 6%, nunca relleno sólido (§2.3, 2026-09-06). */
.btn-social {
    border-radius: 999px;
    border: 1px solid rgb(var(--bv-accent-rgb) / .55);
    color: var(--bv-ink);
    background: rgb(var(--bv-accent-rgb) / .06);
    transition: background-color .2s var(--bv-ease-out), border-color .2s var(--bv-ease-out);
}
.btn-social:hover { background: rgb(var(--bv-accent-rgb) / .12); }

/* .press — 160ms, scale(0.97). Va en todo lo pulsable (§5) */
.press { transition: transform .16s var(--bv-ease-out); }
.press:active { transform: scale(0.97); }

/* ---------- 7. Insignias (§6): bordes al 70%, texto al 400 ---------- */
.pill-live {
    border: 1px solid rgb(var(--bv-accent-rgb) / .7);
    color: var(--bv-accent);
    background: transparent;
    border-radius: 999px;
}
.pill-pending {
    border: 1px solid rgb(var(--bv-warn-rgb) / .7);
    color: rgb(var(--bv-warn-rgb));
    background: transparent;
    border-radius: 999px;
}
.pill-qual {
    border: 1px solid rgb(var(--bv-qual-rgb) / .7);
    color: rgb(var(--bv-qual-rgb));
    background: transparent;
    border-radius: 999px;
}

/* ---------- 8. Tarjetas (§4: rounded-2xl) ---------- */
.bv-card {
    background: rgb(var(--bv-surface-rgb) / .4);
    border: 1px solid rgb(var(--bv-rule-rgb) / .8);
    border-radius: 1rem;
}

/* ---------- 8b. Componentes de estructura (2026-09-06) ----------
   Reinterpretación de la composición. No tocan tokens: radios, rampa de
   grises y acento siguen siendo los de §2 y §4. */

/* .row — fila de característica: un hairline inferior en vez de una caja.
   No es una tarjeta, así que no le aplica el rounded-2xl de §4. */
.row    { border-bottom: 1px solid rgb(var(--bv-rule-rgb) / .8); }
.row-on { border-bottom-color: rgb(var(--bv-accent-rgb) / .55); }

/* .ico — contenedor circular del icono de una fila */
.ico {
    width: 44px; height: 44px;
    border-radius: 999px;
    border: 1px solid rgb(var(--bv-rule-rgb) / .9);
    display: flex; align-items: center; justify-content: center;
    flex: 0 0 auto;
    transition: border-color .26s var(--bv-ease-out);
}
.ico-on { border-color: rgb(var(--bv-accent-rgb) / .5); }
.row:hover .ico { border-color: rgb(var(--bv-accent-rgb) / .5); }

/* .go — círculo de acento que acompaña al botón: la señal de «ir» */
.go {
    width: 44px; height: 44px;
    border-radius: 999px;
    background: var(--bv-accent);
    color: #04140d;
    display: inline-flex; align-items: center; justify-content: center;
    flex: 0 0 auto;
    transition: transform .26s var(--bv-ease-out);
}
a:hover > .go, button:hover > .go { transform: translateX(3px); }

/* .rail — riel de secuencia: la línea de acento se llena en 1200ms */
.rail { position: relative; }
.rail-track { background: rgb(var(--bv-rule-rgb) / .9); }
.rail-fill  { background: var(--bv-accent); transform: scaleX(0); transform-origin: left; }
.rail.is-in .rail-fill { transform: scaleX(1); transition: transform 1.2s var(--bv-ease-out); }

/* ---------- 9. Barra superior (§4, §5) ---------- */
.topbar {
    background: transparent;
    border-bottom-color: transparent;
    transition: background-color .3s var(--bv-ease-out),
                border-color .3s var(--bv-ease-out),
                backdrop-filter .3s var(--bv-ease-out);
}
.topbar.is-stuck {
    background: rgb(var(--bv-bg-rgb) / .7);
    backdrop-filter: blur(24px);
    border-bottom-color: rgb(255 255 255 / .05);
}

/* ---------- 10. Efectos — curvas y tiempos exactos (§5) ---------- */

/* .enter — entrada 700ms, escalonada con --delay */
.enter {
    opacity: 0;
    transform: translateY(18px) scale(0.985);
    animation: bv-enter .7s var(--bv-ease-out) forwards;
    animation-delay: var(--delay, 0ms);
}
@keyframes bv-enter { to { opacity: 1; transform: none; } }

/* [data-reveal] — 600ms al entrar en viewport.
   REGLA §8.3: solo funciona dentro de [data-reveal-root], y ese contenedor
   aplica will-change: transform, lo que crea un containing block. Cualquier
   hijo position:fixed se posicionaría contra la sección y no contra la
   ventana. Si necesitas un flotante, móntalo en <body>. */
[data-reveal-root] { will-change: transform; }
[data-reveal-root] [data-reveal] {
    opacity: 0;
    transform: translateY(14px);
    transition: opacity .6s var(--bv-ease-out), transform .6s var(--bv-ease-out);
    transition-delay: var(--delay, 0ms);
}
[data-reveal-root] [data-reveal].is-in { opacity: 1; transform: none; }

/* [data-stagger] — los hijos entran en cascada de 80ms */
[data-stagger] > * {
    opacity: 0;
    transform: translateY(12px);
    transition: opacity .6s var(--bv-ease-out), transform .6s var(--bv-ease-out);
}
[data-stagger].is-in > * { opacity: 1; transform: none; }
[data-stagger].is-in > *:nth-child(1) { transition-delay: 0ms; }
[data-stagger].is-in > *:nth-child(2) { transition-delay: 80ms; }
[data-stagger].is-in > *:nth-child(3) { transition-delay: 160ms; }
[data-stagger].is-in > *:nth-child(4) { transition-delay: 240ms; }
[data-stagger].is-in > *:nth-child(5) { transition-delay: 320ms; }
[data-stagger].is-in > *:nth-child(6) { transition-delay: 400ms; }

/* .w — las palabras del título suben una a una con blur, 45ms entre ellas */
.w {
    display: inline-block;
    opacity: 0;
    filter: blur(4px);
    transform: translateY(0.4em);
    animation: bv-word .7s var(--bv-ease-out) forwards;
    animation-delay: calc(var(--i, 0) * 45ms);
}
@keyframes bv-word { to { opacity: 1; filter: blur(0); transform: none; } }

/* Un título con [data-words] dentro de un [data-reveal] espera a estar en
   viewport: si no, la cascada se gasta arriba y se llega tarde a verla. */
[data-reveal-root] [data-reveal] .w       { animation-play-state: paused; }
[data-reveal-root] [data-reveal].is-in .w { animation-play-state: running; }

/* .glow — radial esmeralda al 16% */
.glow {
    position: absolute;
    pointer-events: none;
    background: radial-gradient(circle, rgb(var(--bv-accent-rgb) / .16), transparent 70%);
    filter: blur(40px);
}

/* .aurora — tres radiales al 10%, deriva 22s en bucle */
.aurora {
    position: absolute;
    inset: -20%;
    pointer-events: none;
    z-index: 0;
    filter: blur(60px);
    background:
        radial-gradient(38% 38% at 22% 28%, rgb(var(--bv-accent-rgb) / .10), transparent 70%),
        radial-gradient(34% 34% at 76% 34%, rgb(96 165 250 / .10), transparent 70%),
        radial-gradient(34% 34% at 52% 76%, rgb(244 114 182 / .10), transparent 70%);
    animation: bv-aurora 22s var(--bv-ease-in-out) infinite alternate;
}
@keyframes bv-aurora {
    from { transform: translate3d(-2%, -1%, 0) scale(1); }
    to   { transform: translate3d(2%, 1%, 0) scale(1.06); }
}

/* .drift — los glows se mueven con el scroll */
@supports (animation-timeline: view()) {
    .drift { animation: bv-drift linear both; animation-timeline: view(); }
    @keyframes bv-drift {
        from { transform: translateY(-6%); }
        to   { transform: translateY(6%); }
    }
}

/* .float — flotan 6px, 6s, desfasadas entre sí */
.float { animation: bv-float 6s var(--bv-ease-in-out) infinite alternate; }
.float:nth-child(2n) { animation-delay: -2s; }
.float:nth-child(3n) { animation-delay: -4s; }
@keyframes bv-float { from { transform: translateY(-3px); } to { transform: translateY(3px); } }

/* .bar-fill — las barras crecen a lo ancho en 1200ms al revelarse */
.bar-fill { transform: scaleX(0); transform-origin: left; }
.bar-fill.is-in { transform: scaleX(1); transition: transform 1.2s var(--bv-ease-out); }

/* .materialize — diálogos: 260ms con blur(6px) + scale(0.97), no un fade plano */
.materialize {
    animation: bv-materialize .26s var(--bv-ease-out) forwards;
}
@keyframes bv-materialize {
    from { opacity: 0; filter: blur(6px); transform: scale(0.97); }
    to   { opacity: 1; filter: blur(0);   transform: none; }
}

/* ---------- 11. Accesibilidad (§7) ---------- */
html.boveda :focus-visible {
    outline: 2px solid var(--bv-accent);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    .enter, .w, .aurora, .drift, .float, .materialize {
        animation: none !important;
        opacity: 1 !important;
        filter: none !important;
        transform: none !important;
    }
    [data-reveal-root] [data-reveal],
    [data-stagger] > * {
        opacity: 1 !important;
        transform: none !important;
        transition: none !important;
    }
    html.boveda { scroll-behavior: auto; }
}

/* ---------- 12. Módulo de producto (§4.1) ----------
   Cada herramienta ocupa su fila: numeral, cuerpo y previsualización. La
   previsualización es «un objeto que se comporta», no un adorno: enseña de
   qué va la herramienta sin pedirle al usuario que entre a averiguarlo. */

/* .tool — la fila entera reacciona como una unidad, sin volverse tarjeta:
   una fila no lleva radio (§4.1), así que el realce va en el hairline, el
   glow y la previsualización, no en una caja. */
.tool { position: relative; }
.tool::after {
    content: '';
    position: absolute;
    left: 0; right: 0; bottom: -1px;
    height: 1px;
    background: var(--bv-accent);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform .5s var(--bv-ease-out);
}
@media (hover: hover) and (pointer: fine) {
    .tool:hover::after { transform: scaleX(1); }
    .tool:hover .tool-num   { color: var(--bv-accent); }
    .tool:hover .tool-name  { color: var(--bv-ink); }
    .tool:hover .tool-glow  { opacity: 1; }
    .tool:hover .preview    { transform: translateY(-3px); border-color: rgb(255 255 255 / .14); }
}
.tool-num  { transition: color .3s var(--bv-ease-out); }
.tool-name { transition: color .3s var(--bv-ease-out); }

/* El glow de la fila: apagado en reposo, se enciende al acercarse. */
.tool-glow {
    position: absolute;
    pointer-events: none;
    opacity: 0;
    transition: opacity .5s var(--bv-ease-out);
    background: radial-gradient(60% 60% at 70% 50%, rgb(var(--bv-accent-rgb) / .10), transparent 70%);
    filter: blur(40px);
}

/* .preview — el objeto de la derecha. Translúcido sobre negro, como todas
   las superficies (§2.1); se levanta 3px cuando la fila está activa. */
.preview {
    background: rgb(var(--bv-surface-rgb) / .5);
    border: 1px solid rgb(var(--bv-rule-rgb) / .9);
    border-radius: 1rem;
    transition: transform .35s var(--bv-ease-out), border-color .35s var(--bv-ease-out);
    overflow: hidden;
}

/* .sheen — destello diagonal que barre la superficie en 900ms al hover (§5).
   Faltaba en el port inicial. */
.sheen { position: relative; overflow: hidden; }
.sheen::before {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(115deg, transparent 30%, rgb(255 255 255 / .07) 48%, transparent 62%);
    transform: translateX(-120%);
    pointer-events: none;
}
@media (hover: hover) and (pointer: fine) {
    .tool:hover .sheen::before,
    .sheen:hover::before { animation: bv-sheen .9s var(--bv-ease-out); }
}
@keyframes bv-sheen {
    to { transform: translateX(120%); }
}

/* Cuando la herramienta está archivada, nada se enciende: el estado manda
   sobre el efecto (§6 — una insignia decorativa contradice §1). */
.tool-off::after { display: none; }
.tool-off .preview { opacity: .5; }

@media (prefers-reduced-motion: reduce) {
    .tool::after, .preview, .tool-glow { transition: none; }
    .sheen::before { animation: none !important; }
}

/* ---------- 13. Accent exception — Deban (2026-09-09) ----------
   §2.3 says "one accent only" for PUBLIC MARKETING surfaces: the landing
   and the hub. Deban is not that class of surface — it is a product with
   its own identity inside the same visual umbrella, exactly like Gengo
   (teal) or Kazen (violet) are in Expediente. Alex's explicit call,
   2026-09-09: Deban migrates to Boveda but keeps its blue — the same value
   from expediente-tokens.css ([data-tool='deban']), already verified at
   6.6:1 on the dark surface (AA). Turned on with `data-tool="deban"` on
   <html> (base_boveda.html), the same mechanism Expediente already uses
   for Gengo/Kazen.

   Only the variable gets overridden: everything that already consumes
   --bv-accent (.btn-primary, .mark-tool, .pill-live, .tool, .row-on,
   .ico-on, :focus-visible, .chip-accent…) paints blue without duplicating
   a single rule. This is the ONLY documented exception to the one-accent
   rule, and it only applies under [data-tool='deban'] — do not add a
   second color here.

   Known debt (not from this change): several spots in this file and in
   base_boveda.html/landing use Tailwind's `emerald-400`/`emerald-500`
   directly instead of this variable (e.g. the wallet status "Live" dot).
   That is deliberate — there green is a universal "active/healthy"
   signal, not brand identity — but it means overriding --bv-accent-rgb
   does NOT repaint those spots, and that is fine. */
[data-tool='deban'] {
    --bv-accent-rgb: 124 160 245; /* #7CA0F5 */
}

/* ---------- 14. Soft accent chips (§2.3, 2026-09-06) ----------
   Counterpart to .btn-social for text and chips that need to touch the
   accent without being the screen's primary action. Generic: any surface
   that overrides --bv-accent-rgb (§13) inherits them for free. */
.text-accent-solid { color: var(--bv-accent); }
.chip-accent {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    border: 1px solid rgb(var(--bv-accent-rgb) / .3);
    background: rgb(var(--bv-accent-rgb) / .08);
    color: var(--bv-ink);
    border-radius: 999px;
}
.chip-plain {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    border: 1px solid rgb(var(--bv-rule-rgb) / .8);
    color: var(--bv-muted);
    border-radius: 999px;
}
.chip-plain:hover { border-color: rgb(var(--bv-accent-rgb) / .5); }
.chip-warn {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    border: 1px solid rgb(var(--bv-warn-rgb) / .4);
    color: rgb(var(--bv-warn-rgb));
    border-radius: 999px;
}
/* Dashed border = uncertain: the visual vocabulary for an approximate date
   (deban/dates.py: quarter/half/year), never for a confirmed one. */
.chip-dashed {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    border: 1px dashed rgb(var(--bv-label-rgb) / .6);
    color: var(--bv-muted);
    border-radius: 999px;
}
/* Chain color dot (2026-09-10): `Chain.color_hex` was sitting unused in
   `card.chains[].color` — the swatch carries the brand color, but the
   chip's own text/border stay on the neutral scale on purpose. Several
   chain colors (Aptos' black, Cosmos' navy) would fail AA as text on this
   dark background; a small dot only has to be a recognizable shape, not
   a contrast-safe text color, and the white ring keeps it visible even
   when the brand color is nearly as dark as the surface behind it. */
.chip-chain-dot {
    width: 8px;
    height: 8px;
    border-radius: 999px;
    flex-shrink: 0;
    background: var(--chain-color, rgb(var(--bv-rule-rgb)));
    box-shadow: 0 0 0 1px rgb(255 255 255 / .18);
}

/* ---------- 15. Deban — native form controls ----------
   `deban/forms.py` does not put classes on its widgets (frontend-engineer
   does not touch views.py/forms.py, CLAUDE.md), so the styling goes by
   type selector, with the same scope as the accent exception in §13. */
[data-tool='deban'] input[type='text'],
[data-tool='deban'] input[type='url'],
[data-tool='deban'] input[type='date'],
[data-tool='deban'] input[type='time'],
[data-tool='deban'] input[type='number'],
[data-tool='deban'] select,
[data-tool='deban'] textarea {
    width: 100%;
    min-height: 44px;
    padding: .625rem .875rem;
    background: rgb(var(--bv-surface-rgb) / .6);
    border: 1px solid rgb(var(--bv-rule-rgb) / .9);
    border-radius: .75rem;
    color: var(--bv-ink);
    font-size: 14px;
    transition: border-color .2s var(--bv-ease-out);
}
[data-tool='deban'] textarea { min-height: unset; }
[data-tool='deban'] input:focus,
[data-tool='deban'] select:focus,
[data-tool='deban'] textarea:focus {
    outline: none;
    border-color: rgb(var(--bv-accent-rgb) / .7);
}
[data-tool='deban'] input[type='checkbox'] { accent-color: var(--bv-accent); }
/* The browser's native date/time picker is born light; it gets inverted so
   it matches the near-black background (§2.1). */
[data-tool='deban'] input[type='date']::-webkit-calendar-picker-indicator,
[data-tool='deban'] input[type='time']::-webkit-calendar-picker-indicator {
    filter: invert(.8);
}

/* ---------- 16. Filter chips (Deban) ----------------------------
   A calendar is filtered mostly by time horizon, so these chips are
   primary navigation: real links, not JS, so they work without scripts
   and get indexed. The active one is marked with the tool's accent, not a
   darker gray — which one is applied has to read at a glance.
   ------------------------------------------------------------------- */
.chip-filter {
    display: inline-flex;
    align-items: center;
    min-height: 36px;
    padding: 0 .85rem;
    border-radius: 999px;
    border: 1px solid rgb(255 255 255 / .10);
    background: rgb(255 255 255 / .02);
    color: var(--bv-muted);
    font-size: 13px;
    font-weight: 500;
    line-height: 1;
    white-space: nowrap;
    transition: color .18s var(--bv-ease), border-color .18s var(--bv-ease),
                background-color .18s var(--bv-ease);
}
.chip-filter:hover {
    color: var(--bv-text);
    border-color: rgb(255 255 255 / .22);
}
.chip-filter-on {
    color: var(--bv-accent);
    border-color: rgb(var(--bv-accent-rgb) / .45);
    background: rgb(var(--bv-accent-rgb) / .10);
}

/* ---------- 17. .dial — countdown dial (§5) ----------
   Documented in DESIGN_SYSTEM.md §5 since 2026-09-06 but never built (the
   landing does not use it). First real implementation, for Deban's hero —
   the ONLY place in the protocol where something ticks live (Alex's call,
   2026-09-09), and only when a launch has a confirmed exact date and time.

   The container carries [data-reveal] and enters through the mechanism
   already in boveda.js ([data-reveal-root] [data-reveal] → .is-in); no new
   JS needed for that part. The dots light up with a 12ms stagger between
   them (transition-delay via --i, same as .w in §10). The needle turns
   over 1400ms toward the angle agenda.html's JS computes from the time
   left — the browser drives the transition, not a keyframe, because the
   angle is data, not a fixed value.

   2026-09-10: added a soft ambient accent glow (box-shadow) so the dial —
   the single place the accent gets the most real estate — reads as
   something alive rather than another gray circle. Static, not animated:
   nothing here needs a reduced-motion guard. */
.dial {
    position: relative;
    width: 208px;
    height: 208px;
    margin-inline: auto;
    border-radius: 999px;
    border: 1px solid rgb(var(--bv-rule-rgb) / .8);
    background: rgb(var(--bv-surface-rgb) / .35);
    box-shadow: 0 0 64px -8px rgb(var(--bv-accent-rgb) / .28);
}
.dial-dot {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 6px;
    height: 6px;
    margin: -3px;
    border-radius: 999px;
    background: rgb(var(--bv-rule-rgb));
    transform: rotate(calc(var(--i, 0) * 30deg)) translateY(-92px);
    opacity: 0;
    transition: opacity .3s var(--bv-ease-out), background-color .5s var(--bv-ease-out);
    transition-delay: calc(var(--i, 0) * 12ms);
}
.dial.is-in .dial-dot { opacity: 1; }
.dial-dot.is-active { background: var(--bv-accent); }
.dial-needle {
    position: absolute;
    bottom: 50%;
    left: 50%;
    width: 2px;
    height: 78px;
    margin-left: -1px;
    border-radius: 2px;
    transform-origin: 50% 100%;
    background: linear-gradient(to top, var(--bv-accent), rgb(var(--bv-accent-rgb) / 0));
    transform: rotate(0deg);
    transition: transform 1.4s var(--bv-ease-out);
}
.dial-core {
    position: absolute;
    inset: 28px;
    border-radius: 999px;
    border: 1px solid rgb(var(--bv-rule-rgb) / .7);
    background: rgb(var(--bv-bg-rgb) / .85);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .25rem;
    text-align: center;
    padding: 0 1rem;
}
.dial-time { font-size: 15px; letter-spacing: .02em; color: var(--bv-ink); }

@media (prefers-reduced-motion: reduce) {
    .dial-dot { transition: none; opacity: 1; }
    .dial-needle { transition: none; }
}

/* ---------- 18. .card-glow — imminent launches (2026-09-10) ----------
   The accent side of `deban_urgency` (deban_extras.py): a launch due in
   3 days or less gets this on top of its solid accent border-left, so the
   near-term entries visibly stand out instead of the border color doing
   all the work on its own. Static box-shadow, no animation — nothing to
   guard behind prefers-reduced-motion. Used today by detail.html's "When"
   card (the ficha) — the Cartel redesign (2026-09-11) dropped it from the
   agenda's rows in favor of a plain `deban_urgency` border, see
   deban/templates/deban/_card.html's header comment. */
.card-glow {
    box-shadow: 0 0 0 1px rgb(var(--bv-accent-rgb) / .12),
                0 12px 32px -10px rgb(var(--bv-accent-rgb) / .45);
}

/* ---------- 19. Deban — submission form (2026-09-10) ----------
   The precision picker (date_precision, now a RadioSelect) and the chain
   picker (chains, CheckboxSelectMultiple) both render their real
   input/checkbox with `sr-only` (deban/forms.py) so the wrapping card can
   be the visible, clickable surface instead of a tiny native control next
   to a label.

   Deliberately NOT `:has()`: the checked/focus state is read off a real
   DOM sibling with `~`, which needs nothing newer than CSS2 selectors —
   this picker is the least gracefully something can fail on an old
   browser (it is how a date gets recorded), so it does not lean on a
   selector still short of universal support. Same reason the input has to
   come BEFORE `.precision-card`/`.chain-chip-card` in the markup: `~`
   only looks forward. */
[data-tool='deban'] .precision-card,
[data-tool='deban'] .chain-chip-card {
    display: flex;
    cursor: pointer;
    border: 1px solid rgb(var(--bv-rule-rgb) / .9);
    background: rgb(var(--bv-surface-rgb) / .35);
    transition: border-color .18s var(--bv-ease-out), background-color .18s var(--bv-ease-out);
}
[data-tool='deban'] .precision-card {
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .375rem;
    padding: .75rem .5rem;
    border-radius: .75rem;
    text-align: center;
}
[data-tool='deban'] .chain-chip-card {
    align-items: center;
    gap: .375rem;
    padding: .4rem .8rem;
    border-radius: 999px;
    font-size: 13px;
    color: var(--bv-muted);
}
[data-tool='deban'] .precision-card .material-symbols-outlined {
    color: var(--bv-label);
    transition: color .18s var(--bv-ease-out);
}
[data-tool='deban'] label:hover .precision-card,
[data-tool='deban'] label:hover .chain-chip-card {
    border-color: rgb(255 255 255 / .18);
}
[data-tool='deban'] input:checked ~ .precision-card,
[data-tool='deban'] input:checked ~ .chain-chip-card {
    border-color: rgb(var(--bv-accent-rgb) / .7);
    background: rgb(var(--bv-accent-rgb) / .1);
    color: var(--bv-ink);
}
[data-tool='deban'] input:checked ~ .precision-card .material-symbols-outlined,
[data-tool='deban'] input:checked ~ .precision-card .precision-card-label {
    color: var(--bv-accent);
}
[data-tool='deban'] input:focus-visible ~ .precision-card,
[data-tool='deban'] input:focus-visible ~ .chain-chip-card {
    outline: 2px solid var(--bv-accent);
    outline-offset: 2px;
}

/* Live preview card (submit.html): reuses .bv-card + .chip-* verbatim so a
   drift in either place cannot make the preview lie about what the real
   card will look like. Only the dashed outer ring is new here — it marks
   the whole thing as a draft, not yet a real entry on the calendar. */
.preview-draft-ring {
    border: 1px dashed rgb(var(--bv-rule-rgb));
}

/* ---------- 20. Deban — the room / intent bar (2026-09-11) ----------
   The "Cartel editorial" redesign (SPEC-IMPLEMENTACION.md §3): "ninguna
   cifra sin su composición" — how many people marked they will be there
   never shows as a bare number, the same idea `deban/dates.py` already
   applies to dates, applied here to intent. `_room.html` (agenda's rail
   lines, the poster, the ficha) is the only consumer.

   The "new account" slice gets a diagonal-stripe texture, not just a paler
   blue, so the "some of this is unverified" signal survives someone who
   cannot tell two blues apart — same idea as `.chip-chain-dot`'s white
   ring in §14. Segment widths are real per-launch data
   (services/room.py's intent_mix), so they are set inline in the
   template, the same way `--chain-color` already is elsewhere in this
   file — not a design decision made there. */
.deban-compbar {
    display: flex;
    height: 6px;
    width: 72px;
    flex-shrink: 0;
    overflow: hidden;
    border-radius: 999px;
    background: rgb(var(--bv-bg-rgb));
    border: 1px solid rgb(var(--bv-rule-rgb) / .8);
}
.deban-compbar-lg {
    height: 8px;
    width: 100%;
    border-radius: .25rem;
}
.deban-compbar i { display: block; height: 100%; min-width: 0; width: var(--w, 0%); }
.deban-compbar-trusted { background: var(--bv-accent); }
.deban-compbar-normal { background: rgb(var(--bv-accent-rgb) / .42); }
.deban-compbar-new {
    background-color: rgb(var(--bv-muted-rgb) / .18);
    background-image: repeating-linear-gradient(-45deg,
        rgb(var(--bv-muted-rgb) / .6) 0 2px, transparent 2px 5px);
}
/* The legend's swatches (the collapsible exact count in _room.html's
   'detail' size) — same three fills as the bar above, as small squares. */
.deban-key {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 2px;
    flex-shrink: 0;
}
.deban-key-trusted { background: var(--bv-accent); }
.deban-key-normal { background: rgb(var(--bv-accent-rgb) / .42); }
.deban-key-new {
    background-color: rgb(var(--bv-muted-rgb) / .18);
    background-image: repeating-linear-gradient(-45deg,
        rgb(var(--bv-muted-rgb) / .65) 0 1.5px, transparent 1.5px 4px);
    border: 1px solid rgb(var(--bv-rule-rgb));
}

/* ---------- 21. Deban — the poster's ticker (2026-09-11) ----------
   "Cartel editorial" IS brutalist type as the protagonist — Alex's
   correction after reviewing the first pass: a plain `clamp()` topping out
   at 4.5rem read as an ordinary heading, not a poster. Ported verbatim
   from the approved prototype's mechanic (3-cartel.html's `.ticker`):
   font-size is whichever is SMALLER of an absolute per-viewport ceiling
   (`--cap`, so a very short ticker on a wide screen does not explode) and
   the container's own width divided by how many characters the ticker
   actually has (`--fit / --len`), so `$ROB` and `$PAYGENT` both fill
   roughly the same measure instead of the long one overflowing or the
   short one looking timid.

   `--len` is set inline per instance in agenda.html, computed with
   Django's stock `|length` filter (character count) — real per-instance
   data, same precedent as `--chain-color`/`--w` elsewhere in this file,
   not a design decision made in the template. `cqi` needs an ancestor
   with `container-type: inline-size` (the poster section in agenda.html
   already carries it). Only used by the poster — the rail's index lines
   stay compact on purpose (SPEC-IMPLEMENTACION.md: a list is for
   scanning, the poster is the one thing that gets to shout). */
.deban-ticker {
    --cap: clamp(4.5rem, 20vw, 10.5rem);
    --fit: 150cqi;
    display: block;
    max-width: 100%;
    overflow-wrap: normal;
    word-break: keep-all;
    hyphens: none;
    line-height: .86;
    letter-spacing: -.045em;
    font-weight: 900;
    font-size: min(var(--cap), calc(var(--fit) / var(--len, 6)));
}

/* ---------- 22. Deban — the poster's big date (2026-09-11) ----------
   Same overflow, one size down: caught in a real 390px Chrome screenshot
   (not the hand math the ticker fix shipped with — that missed this one),
   `_date_precision.html`'s 'detail' tier used a FIXED `text-3xl md:text-4xl`
   for the exact/day date string. Geist Mono is wide per character, and
   "12 Sep 2026 · 18:00 UTC" (~23 characters) at 36px does not come close
   to fitting a 390px phone — it ran off the right edge and, because
   `base_boveda.html` sets `overflow-x-hidden` on <body>, simply vanished
   instead of scrolling into view.

   Same `min(cap, fit/len)` mechanic as `.deban-ticker` (§21), tuned down
   one notch: this string is always longer than a ticker symbol, so `--fit`
   is smaller, and `--cap` tops out lower (this is the SECOND biggest
   thing on the poster, never bigger than the ticker itself). `--len`
   comes from `card.date_label|length` in `_date_precision.html` — the
   real string being printed, not a guess. Only wired into the 'detail'
   size (the ficha/poster); the 'card' size (rail lines) was already
   small enough to never have been at risk. */
.deban-date-detail {
    --cap: clamp(1.5rem, 8vw, 2.75rem);
    --fit: 130cqi;
    max-width: 100%;
    /* A safety net, not the plan: if a pathological length still does not
       fit at the smallest computed size, the row wraps to a second line
       instead of clipping — "que envuelva, nunca que se corte". */
    flex-wrap: wrap;
    row-gap: .15em;
    font-size: min(var(--cap), calc(var(--fit) / var(--len, 12)));
}

/* ---------- 23. .deban-modal — native <dialog> ----------
   The "qué es Deban" sheet that replaced the /sources/ and /about/ pages
   (2026-09-13). A real <dialog> rather than a div: Esc, focus trapping and
   inertness of the page behind come from the browser, none of it written
   here — and none of it forgotten either, which is what usually happens
   with a hand-rolled modal.

   `display` is what a <dialog> toggles, so it cannot carry the centering:
   the flex layout only applies when the element is open, hence [open].
   The `::backdrop` gets the same dim + blur as the wallet modal so the two
   read as the same system. */
.deban-modal {
    max-width: none;
    max-height: none;
    width: 100%;
    height: 100%;
    padding: 1rem;
    border: 0;
    background: transparent;
    overflow: auto;
    overscroll-behavior: contain;
}

.deban-modal[open] {
    display: flex;
    align-items: center;
    justify-content: center;
}

.deban-modal::backdrop {
    background: rgb(0 0 0 / .72);
    backdrop-filter: blur(4px);
}

/* The card slides the last few pixels into place. Skipped for anyone who
   asked the system for less motion (§1). */
@media (prefers-reduced-motion: no-preference) {
    .deban-modal[open] > * {
        animation: deban-modal-in .18s ease-out;
    }
}

@keyframes deban-modal-in {
    from { opacity: 0; transform: translateY(.5rem); }
    to   { opacity: 1; transform: none; }
}

/* Deban's "Details →" on each agenda row (_card.html). On touch screens there
   is no hover, so it is always visible; only a real mouse gets the quieter
   reveal-on-hover. Opacity lives here and not in Tailwind utilities because
   tailwind.css loads after this file and would win the tie. */
.deban-details { transition: opacity .15s ease-out; }
@media (hover: hover) and (pointer: fine) {
    .deban-details { opacity: 0; }
    .group:hover .deban-details,
    .group:focus-within .deban-details { opacity: 1; }
}
