/* static/css/boveda.css — PROTOCOL_73 · sistema Bóveda
 *
 * Sistema visual de las superficies PÚBLICAS: la landing (sitio PHP aparte)
 * y el hub Protocol (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.
 */

/* ---------- 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 "73" — provisional: Bóveda todavía no define su mark (TOOLS.md).
   Sin glow: el CRT está derogado. */
.mark-73 {
    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;
}

/* .card — hover 260ms: sube 4px, borde a white/18, sombra profunda.
   Solo con puntero fino (§5). */
@media (hover: hover) and (pointer: fine) {
    .card-hover {
        transition: transform .26s var(--bv-ease-out),
                    border-color .26s var(--bv-ease-out),
                    box-shadow .26s var(--bv-ease-out);
    }
    .card-hover:hover {
        transform: translateY(-4px);
        border-color: rgb(255 255 255 / .18);
        box-shadow: 0 18px 40px rgb(0 0 0 / .45);
    }
}

/* ---------- 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; }
}
