/* =========================================================================
   PORTAFOLIO — WILLIAM PEÑA
   Dirección estética: "Taller cálido" — papel artesanal + alma de terminal.
   Paleta heredada "Warm Professional" (terracota/crema), ampliada con
   profundidad, textura y modo oscuro nocturno.
   ========================================================================= */

/* -------------------------------------------------------------------------
   1. TOKENS
   ------------------------------------------------------------------------- */
:root {
    /* Superficies */
    --bg-main: #FAF8F5;
    --section-alt: #F5F1EB;
    --card-bg: #FFFFFF;
    --white: #FFFFFF;
    --paper-edge: rgba(139, 69, 19, 0.10);

    /* Tinta */
    --text-main: #4A4A4A;
    --ink-strong: #2E2A26;
    --ink-soft: #7A6E63;

    /* Marca */
    --primary: #8B4513;
    --accent: #CD853F;
    --clay: #A0522D;
    --ember: #B5651D;
    --sage: #6B7D5A;
    --sand: #E8DCC8;

    /* Texto de la terminal. Deliberadamente fuera del juego claro/oscuro:
       la ventana es oscura en ambos temas, así que su texto no puede
       depender de tokens que cambian de rol entre uno y otro. --sand lo hace
       (aquí es claro, en oscuro es una superficie), y usarlo como color de
       texto dejaba la salida en 1.36:1 de contraste: ilegible.
       Medidos sobre los dos fondos: 8.3:1 y 14.3:1 el principal,
       6.0:1 y 10.4:1 la salida, 4.7:1 y 8.0:1 el prompt. */
    --term-texto: #E8DCC8;
    --term-salida: #C9BCA8;
    --term-prompt: #D89A5C;

    /* Sombras cálidas */
    --shadow-light: rgba(139, 69, 19, 0.08);
    --shadow-medium: rgba(139, 69, 19, 0.15);
    --shadow-warm: rgba(205, 133, 63, 0.1);
    --shadow-card: 0 1px 2px rgba(46, 42, 38, 0.04), 0 8px 24px -12px rgba(139, 69, 19, 0.18);

    /* Desfase del parpadeo de cada estrella; lo fija cielo.js por elemento */
    --retardo: 0s;
    --shadow-lift: 0 2px 4px rgba(46, 42, 38, 0.05), 0 24px 48px -20px rgba(139, 69, 19, 0.28);

    /* Tipografía */
    --font-display: 'Fraunces', 'Iowan Old Style', Georgia, serif;
    --font-body: 'Karla', 'Segoe UI', Tahoma, sans-serif;
    --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;

    /* Puente con Bootstrap: sus utilidades y componentes (.bg-primary,
       acordeón, focus rings…) leen SUS propias variables. Sin esto siguen
       pintando el azul de fábrica dentro de una paleta cálida. */
    --bs-primary: #8B4513;
    --bs-primary-rgb: 139, 69, 19;
    --bs-link-color: #8B4513;
    --bs-link-hover-color: #A0522D;
    /* Bootstrap pinta los enlaces con la variante -rgb, no con la anterior:
       sin estas, cualquier <a> sin clase de color sale azul de fábrica. */
    --bs-link-color-rgb: 139, 69, 19;
    --bs-link-hover-color-rgb: 160, 82, 45;
    --bs-border-color: rgba(139, 69, 19, 0.15);
    --bs-focus-ring-color: rgba(205, 133, 63, 0.28);

    /* Ritmo */
    --ancho-maximo: 1140px;
    --radius-sm: 0.65rem;
    --radius: 1rem;
    --radius-lg: 1.5rem;
    --ease-cozy: cubic-bezier(0.33, 1, 0.68, 1);
    --ease-soft: cubic-bezier(0.4, 0, 0.2, 1);
}

/* Modo oscuro: noche cálida, nunca negro puro */
:root[data-theme="dark"] {
    --bg-main: #1A1614;
    --section-alt: #221D1A;
    --card-bg: #262019;
    --paper-edge: rgba(205, 133, 63, 0.16);

    --text-main: #D8CFC4;
    --ink-strong: #F2EAE0;
    --ink-soft: #A29485;

    --primary: #D9A066;
    --accent: #E8B888;
    --clay: #C97B4A;
    --ember: #E09455;
    --sage: #9DB088;
    --sand: #3A312A;

    --shadow-light: rgba(0, 0, 0, 0.3);
    --shadow-medium: rgba(0, 0, 0, 0.45);
    --shadow-warm: rgba(217, 160, 102, 0.12);
    --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 24px -12px rgba(0, 0, 0, 0.6);
    --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.35), 0 24px 48px -20px rgba(0, 0, 0, 0.7);

    /* Puente con Bootstrap en modo oscuro */
    --bs-primary: #D9A066;
    --bs-primary-rgb: 217, 160, 102;
    --bs-link-color: #D9A066;
    --bs-link-hover-color: #E8B888;
    --bs-link-color-rgb: 217, 160, 102;
    --bs-link-hover-color-rgb: 232, 184, 136;
    --bs-border-color: rgba(205, 133, 63, 0.20);
    --bs-focus-ring-color: rgba(217, 160, 102, 0.30);

    --bs-body-bg: #1A1614;
    --bs-body-color: #D8CFC4;
    --bs-emphasis-color: #F2EAE0;
    --bs-secondary-color: #A29485;
}

/* ------------------------------------------------------------------------
   Acordeón (FAQ)
   Bootstrap declara estas variables DENTRO de `.accordion`, no en :root, y
   esa declaración gana por especificidad. Por eso se redefinen aquí sobre
   el mismo selector: si no, la cabecera abierta sigue saliendo azul.
   ------------------------------------------------------------------------ */
.accordion {
    --bs-accordion-bg: transparent;
    --bs-accordion-color: var(--text-main);
    --bs-accordion-btn-color: var(--ink-strong);
    --bs-accordion-btn-bg: transparent;
    --bs-accordion-active-bg: color-mix(in srgb, var(--accent) 14%, transparent);
    --bs-accordion-active-color: var(--primary);
    --bs-accordion-border-color: var(--paper-edge);
    --bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem color-mix(in srgb, var(--accent) 22%, transparent);
    --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23CD853F'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
    --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23CD853F'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
}

/* El chevron va incrustado como SVG, así que su color no puede heredarse:
   se sustituye por la versión ámbar del modo oscuro. */
:root[data-theme="dark"] .accordion {
    --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23D9A066'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
    --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23E8B888'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
}

.accordion-button {
    font-family: var(--font-display);
    font-weight: 600;
}

.accordion-button:not(.collapsed) {
    box-shadow: none;
}

/* Superficies claras de Bootstrap (.bg-light, .bg-white) dentro del modo
   oscuro: sin esto quedan blancas y deslumbran. */
:root[data-theme="dark"] .bg-light,
:root[data-theme="dark"] .badge.bg-light {
    background-color: var(--sand) !important;
    color: var(--ink-strong) !important;
}

/* Bootstrap compila el azul del checkbox como valor literal, no como
   variable, así que el puente de tokens no lo alcanza: se fuerza aquí. */
.form-check-input {
    border-color: var(--paper-edge);
    background-color: var(--card-bg);
}

.form-check-input:checked {
    background-color: var(--primary);
    border-color: var(--primary);
}

.form-check-input:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 0.25rem color-mix(in srgb, var(--accent) 25%, transparent);
}

.form-select:focus,
.form-control:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 0.25rem color-mix(in srgb, var(--accent) 20%, transparent);
}

/* -------------------------------------------------------------------------
   2. BASE
   ------------------------------------------------------------------------- */
* { scrollbar-color: var(--accent) transparent; }

html {
    scroll-behavior: smooth;
    scroll-padding-top: 5.5rem;
}

body {
    font-family: var(--font-body);
    font-size: 1.0125rem;
    line-height: 1.72;
    color: var(--text-main);
    background-color: var(--bg-main);
    min-width: 0;
    -webkit-font-smoothing: antialiased;
    transition: background-color 0.5s var(--ease-soft), color 0.5s var(--ease-soft);
}

/* Grano de papel: textura sutil sobre toda la página */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    opacity: 0.32;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.35'/%3E%3C/svg%3E");
}

:root[data-theme="dark"] body::before { opacity: 0.20; }

/* Sin z-index propio: el navbar (sticky, z-index 1030) debe poder quedar
   por encima. Solo se eleva sobre la textura de grano del body. */
main { position: relative; z-index: 2; }

/* Es el HEADER el que se pega, no el <nav>: el header medía exactamente lo
   mismo que el navbar, así que un sticky dentro de él no tenía recorrido. */
header {
    position: sticky;
    top: 0;
    z-index: 1031;
}

h1, h2, h3, h4, h5, h6, .font-display {
    font-family: var(--font-display);
    font-optical-sizing: auto;
    font-variation-settings: 'SOFT' 40, 'WONK' 1;
    color: var(--ink-strong);
    letter-spacing: -0.015em;
    line-height: 1.18;
}

::selection {
    background: var(--accent);
    color: var(--white);
}

/* -------------------------------------------------------------------------
   3. UTILIDADES DE COLOR (compatibilidad con plantillas existentes)
   ------------------------------------------------------------------------- */
.bg-main { background: var(--bg-main) !important; }
.bg-alt { background: var(--section-alt) !important; }
.bg-white { background-color: var(--card-bg) !important; }
.text-primary { color: var(--primary) !important; }
.text-main { color: var(--text-main) !important; }
.text-accent { color: var(--accent) !important; }
.text-muted { color: var(--ink-soft) !important; }
.text-ink { color: var(--ink-strong) !important; }
.font-mono { font-family: var(--font-mono); }

/* -------------------------------------------------------------------------
   4. SECCIONES Y TÍTULOS
   ------------------------------------------------------------------------- */
section { position: relative; }

.section-label {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--accent);
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    margin-bottom: 0.85rem;
}

.section-label::before {
    content: "";
    width: 1.75rem;
    height: 1px;
    background: currentColor;
    opacity: 0.6;
}

.section-title {
    font-size: clamp(1.9rem, 4vw, 2.9rem);
    font-weight: 600;
    margin-bottom: 0.75rem;
}

.section-lead {
    color: var(--ink-soft);
    max-width: 58ch;
    font-size: 1.02rem;
}

/* -------------------------------------------------------------------------
   5. TARJETAS
   ------------------------------------------------------------------------- */
.card-custom {
    background-color: var(--card-bg);
    border-radius: var(--radius);
    box-shadow: var(--shadow-card);
    border: 1px solid var(--paper-edge);
    transition: transform 0.45s var(--ease-cozy), box-shadow 0.45s var(--ease-cozy),
                border-color 0.45s var(--ease-cozy);
}

.card-custom:hover {
    transform: translateY(-4px);
    box-shadow: var(--shadow-lift);
    border-color: color-mix(in srgb, var(--accent) 40%, transparent);
}

/* Tarjeta de competencia con acento superior que crece al pasar el cursor */
.skill-card {
    position: relative;
    overflow: hidden;
    height: 100%;
}

.skill-card::after {
    content: "";
    position: absolute;
    inset: auto 0 0 0;
    height: 2px;
    background: linear-gradient(90deg, var(--primary), var(--accent));
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 0.5s var(--ease-cozy);
}

.skill-card:hover::after { transform: scaleX(1); }

.skill-card .skill-icon {
    font-size: 1.6rem;
    color: var(--primary);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3rem;
    height: 3rem;
    border-radius: 0.85rem;
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    transition: transform 0.5s var(--ease-cozy), background 0.4s var(--ease-soft);
}

.skill-card:hover .skill-icon {
    transform: rotate(-6deg) scale(1.08);
    background: color-mix(in srgb, var(--accent) 24%, transparent);
}

/* -------------------------------------------------------------------------
   6. BOTONES
   ------------------------------------------------------------------------- */
.btn {
    font-family: var(--font-body);
    font-weight: 600;
    letter-spacing: 0.01em;
    border-radius: 0.8rem;
    padding: 0.6rem 1.5rem;
    transition: transform 0.35s var(--ease-cozy), box-shadow 0.35s var(--ease-cozy),
                background-color 0.3s var(--ease-soft), color 0.3s var(--ease-soft);
}

.btn-primary {
    background-color: var(--primary);
    border-color: var(--primary);
    color: var(--white);
}

.btn-primary:hover,
.btn-primary:focus-visible {
    background-color: var(--clay);
    border-color: var(--clay);
    color: var(--white);
    transform: translateY(-2px);
    box-shadow: 0 10px 24px -8px var(--shadow-medium);
}

.btn-outline-primary {
    border-color: var(--primary);
    color: var(--primary);
    background: transparent;
}

.btn-outline-primary:hover,
.btn-outline-primary:focus-visible {
    background-color: var(--primary);
    border-color: var(--primary);
    color: var(--white);
    transform: translateY(-2px);
    box-shadow: 0 10px 24px -8px var(--shadow-medium);
}

:root[data-theme="dark"] .btn-primary,
:root[data-theme="dark"] .btn-outline-primary:hover {
    color: #1A1614;
}

/* Deshabilitado: mantiene la paleta en vez del azul por defecto */
.btn:disabled,
.btn.disabled,
.btn-primary:disabled {
    background-color: var(--accent);
    border-color: var(--accent);
    color: var(--white);
    opacity: 0.72;
    transform: none;
    box-shadow: none;
    cursor: progress;
}

:root[data-theme="dark"] .btn:disabled,
:root[data-theme="dark"] .btn-primary:disabled {
    color: #1A1614;
}

/* -------------------------------------------------------------------------
   7. NAVBAR
   ------------------------------------------------------------------------- */
.navbar {
    background: color-mix(in srgb, var(--bg-main) 82%, transparent) !important;
    backdrop-filter: saturate(150%) blur(14px);
    -webkit-backdrop-filter: saturate(150%) blur(14px);
    border-bottom: 1px solid transparent;
    transition: border-color 0.4s var(--ease-soft), background 0.4s var(--ease-soft),
                padding 0.4s var(--ease-soft);
    padding-block: 0.85rem;
    z-index: 1030;
}

.navbar.is-scrolled {
    border-bottom-color: var(--paper-edge);
    padding-block: 0.5rem;
    box-shadow: 0 10px 30px -22px var(--shadow-medium);
}

.navbar-brand {
    font-family: var(--font-display);
    font-size: 1.4rem !important;
    font-weight: 600;
    color: var(--ink-strong) !important;
    letter-spacing: -0.02em;
}

/* El punto de acento que cerraba el nombre pasa a ser el del dominio */
.navbar-brand .brand-tld {
    color: var(--accent);
}

/* En los móviles más estrechos (320px), «William Peña.dev» no cabe junto al
   botón de tema y al menú: le faltan 10px. Con un cuerpo algo menor vuelve a
   una sola línea. */
@media (max-width: 359.98px) {
    .navbar-brand { font-size: 1.2rem !important; }
}

.navbar-light .navbar-nav .nav-link {
    font-family: var(--font-body);
    color: var(--text-main);
    font-weight: 500;
    font-size: 0.94rem;
    padding: 0.4rem 0.9rem;
    border-radius: 0.6rem;
    position: relative;
    transition: color 0.3s var(--ease-soft), background-color 0.3s var(--ease-soft);
}

.navbar-light .navbar-nav .nav-link:hover,
.navbar-light .navbar-nav .nav-link.active {
    color: var(--primary);
    background-color: color-mix(in srgb, var(--accent) 12%, transparent);
}

.navbar-toggler {
    border: none !important;
    box-shadow: none !important;
    color: var(--primary);
}

.navbar-toggler:focus-visible { outline: 2px solid var(--accent); }

/* Interruptor de tema */
.theme-toggle {
    border: 1px solid var(--paper-edge);
    background: transparent;
    color: var(--primary);
    width: 2.4rem;
    height: 2.4rem;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1rem;
    cursor: pointer;
    transition: transform 0.5s var(--ease-cozy), background 0.3s var(--ease-soft),
                border-color 0.3s var(--ease-soft);
}

.theme-toggle:hover {
    transform: rotate(25deg);
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    border-color: var(--accent);
}

/* -------------------------------------------------------------------------
   8. HERO
   ------------------------------------------------------------------------- */
.hero {
    position: relative;
    padding-block: clamp(3rem, 8vw, 6rem) clamp(2.5rem, 6vw, 4.5rem);
    overflow: hidden;
}

/* Halo cálido de fondo, como luz de tarde entrando por una ventana */
.hero::before {
    content: "";
    position: absolute;
    top: -30%;
    right: -10%;
    width: 55vw;
    height: 55vw;
    max-width: 780px;
    max-height: 780px;
    border-radius: 50%;
    background: radial-gradient(circle,
        color-mix(in srgb, var(--accent) 22%, transparent) 0%,
        transparent 68%);
    filter: blur(20px);
    pointer-events: none;
    animation: heroGlow 14s ease-in-out infinite alternate;
}

@keyframes heroGlow {
    from { transform: translate(0, 0) scale(1); opacity: 0.85; }
    to   { transform: translate(-4%, 5%) scale(1.12); opacity: 1; }
}

.hero-name {
    font-size: clamp(2.6rem, 7.5vw, 5.1rem);
    font-weight: 600;
    line-height: 0.98;
    letter-spacing: -0.035em;
    margin-bottom: 1rem;
}

.hero-name .name-accent {
    display: block;
    color: var(--primary);
    font-style: italic;
    font-variation-settings: 'SOFT' 100, 'WONK' 1;
}

.hero-role {
    font-family: var(--font-mono);
    font-size: 0.82rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--accent);
}

.hero-scope {
    color: var(--ink-soft);
    font-size: 1.05rem;
    max-width: 46ch;
}

.avatar-frame {
    position: relative;
    display: inline-block;
    padding: 0.55rem;
    border-radius: 50%;
    background: linear-gradient(140deg,
        color-mix(in srgb, var(--accent) 55%, transparent),
        color-mix(in srgb, var(--primary) 35%, transparent));
    box-shadow: var(--shadow-lift);
}

.avatar-img {
    width: min(270px, 62vw);
    aspect-ratio: 1;
    object-fit: cover;
    border-radius: 50%;
    border: 4px solid var(--card-bg);
    display: block;
}

/* Anillo giratorio de tecnologías alrededor del avatar */
.avatar-ring {
    position: absolute;
    inset: -14px;
    border-radius: 50%;
    border: 1px dashed color-mix(in srgb, var(--accent) 55%, transparent);
    animation: spin 34s linear infinite;
    pointer-events: none;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* Métricas del hero */
.hero-stat {
    border-left: 2px solid color-mix(in srgb, var(--accent) 45%, transparent);
    padding-left: 0.9rem;
}

.hero-stat .stat-value {
    font-family: var(--font-display);
    font-size: 1.65rem;
    font-weight: 600;
    color: var(--primary);
    line-height: 1;
    display: block;
}

.hero-stat .stat-label {
    font-size: 0.78rem;
    color: var(--ink-soft);
    letter-spacing: 0.02em;
}

/* -------------------------------------------------------------------------
   9. BADGES
   ------------------------------------------------------------------------- */
.tech-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-family: var(--font-mono);
    font-size: 0.73rem;
    padding: 0.32rem 0.72rem;
    border-radius: 2rem;
    background: color-mix(in srgb, var(--accent) 13%, transparent);
    color: var(--primary);
    border: 1px solid color-mix(in srgb, var(--accent) 26%, transparent);
    transition: transform 0.35s var(--ease-cozy), background 0.3s var(--ease-soft);
}

.tech-badge:hover {
    transform: translateY(-2px);
    background: color-mix(in srgb, var(--accent) 24%, transparent);
}

.tech-badge-small {
    display: inline-block;
    font-family: var(--font-mono);
    font-size: 0.66rem;
    padding: 0.2rem 0.55rem;
    border-radius: 1rem;
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    color: var(--primary);
}

/* Badge de "en formación": honesto y visualmente distinto */
.badge-learning {
    background: color-mix(in srgb, var(--sage) 16%, transparent);
    color: var(--sage);
    border-color: color-mix(in srgb, var(--sage) 32%, transparent);
}

/* -------------------------------------------------------------------------
   10. LÍNEA DE TIEMPO (experiencia y formación)
   ------------------------------------------------------------------------- */
/* Línea y puntos comparten un único eje (--tl-eje) y se centran con
   translateX(-50%). Antes cada uno usaba un `left` fijo e independiente,
   así que los puntos caían 2,2 px a la derecha de la línea. */
.timeline {
    --tl-pad: 1.9rem;   /* separación entre el eje y el texto */
    --tl-eje: 0.6rem;   /* distancia del eje al borde del .timeline */
    --tl-punto: 0.68rem;

    position: relative;
    padding-left: var(--tl-pad);
}

.timeline::before {
    content: "";
    position: absolute;
    left: var(--tl-eje);
    transform: translateX(-50%);
    top: 0.5rem;
    bottom: 0.5rem;
    width: 1px;
    background: linear-gradient(to bottom,
        var(--accent), color-mix(in srgb, var(--accent) 15%, transparent));
}

.timeline-item { position: relative; }

.timeline-item::before {
    content: "";
    position: absolute;
    /* El item ya está desplazado por el padding: lo descontamos */
    left: calc(var(--tl-eje) - var(--tl-pad));
    transform: translateX(-50%);
    top: 0.55rem;
    width: var(--tl-punto);
    height: var(--tl-punto);
    box-sizing: border-box;
    border-radius: 50%;
    background: var(--card-bg);
    border: 2px solid var(--accent);
    transition: transform 0.4s var(--ease-cozy), background 0.3s var(--ease-soft);
}

/* El translateX debe conservarse o el punto se descentra al ampliarse */
.timeline-item:hover::before {
    background: var(--accent);
    transform: translateX(-50%) scale(1.35);
}

.timeline-date {
    font-family: var(--font-mono);
    font-size: 0.74rem;
    color: var(--accent);
    letter-spacing: 0.04em;
}

.timeline-role {
    font-family: var(--font-display);
    font-size: 1.22rem;
    font-weight: 600;
    color: var(--ink-strong);
    margin-bottom: 0.15rem;
}

/* Alcance real bajo el cargo oficial */
.timeline-scope {
    font-style: italic;
    color: var(--primary);
    font-size: 0.97rem;
    margin-bottom: 0.2rem;
}

.timeline-company {
    color: var(--ink-soft);
    font-size: 0.9rem;
}

.achievement-list {
    list-style: none;
    padding-left: 0;
    margin-bottom: 0;
}

.achievement-list li {
    position: relative;
    padding-left: 1.4rem;
    margin-bottom: 0.55rem;
    font-size: 0.95rem;
}

.achievement-list li::before {
    content: "▹";
    position: absolute;
    left: 0;
    color: var(--accent);
}

/* -------------------------------------------------------------------------
   11. TERMINAL (barra de ventana del hero)
   ------------------------------------------------------------------------- */
.console-bar {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.75rem 1.15rem;
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    border-bottom: 1px solid var(--paper-edge);
}

.console-dot {
    width: 0.68rem;
    height: 0.68rem;
    border-radius: 50%;
    background: color-mix(in srgb, var(--primary) 35%, transparent);
}

.console-dot:nth-child(2) { background: color-mix(in srgb, var(--accent) 60%, transparent); }
.console-dot:nth-child(3) { background: color-mix(in srgb, var(--sage) 60%, transparent); }

.console-title {
    font-family: var(--font-mono);
    font-size: 0.76rem;
    color: var(--ink-soft);
    margin-left: 0.5rem;
}

/* Campo trampa para bots: invisible pero enfocable por scripts */
.hp-field {
    position: absolute !important;
    left: -9999px !important;
    opacity: 0 !important;
    height: 0 !important;
    width: 0 !important;
    pointer-events: none;
}

/* -------------------------------------------------------------------------
   12. RED DE DESARROLLADORES
   ------------------------------------------------------------------------- */
.developer-card { height: 100%; }

.developer-card-inner { height: 100%; display: flex; flex-direction: column; }

.developer-avatar img {
    width: 3.2rem;
    height: 3.2rem;
    object-fit: cover;
    border: 2px solid color-mix(in srgb, var(--accent) 40%, transparent);
}

.portfolio-link {
    color: var(--primary);
    font-weight: 600;
    text-decoration: none;
    font-size: 0.9rem;
    transition: gap 0.3s var(--ease-cozy), color 0.3s var(--ease-soft);
}

.portfolio-link:hover { color: var(--accent); }

/* -------------------------------------------------------------------------
   13. REVELADO AL HACER SCROLL
   ------------------------------------------------------------------------- */
.reveal {
    opacity: 0;
    transform: translateY(22px);
    transition: opacity 0.75s var(--ease-cozy), transform 0.75s var(--ease-cozy);
    will-change: opacity, transform;
}

.reveal.is-visible {
    opacity: 1;
    transform: none;
}

/* -------------------------------------------------------------------------
   14. FOOTER
   ------------------------------------------------------------------------- */
footer {
    background: var(--section-alt) !important;
    border-top: 1px solid var(--paper-edge);
    position: relative;
    z-index: 2;
}

footer a { transition: color 0.3s var(--ease-soft); }
footer a:hover { color: var(--primary) !important; }

/* -------------------------------------------------------------------------
   15. VOLVER ARRIBA
   ------------------------------------------------------------------------- */
.back-to-top {
    position: fixed;
    right: 1.4rem;
    bottom: 1.4rem;
    width: 2.8rem;
    height: 2.8rem;
    border-radius: 50%;
    border: 1px solid var(--paper-edge);
    background: var(--card-bg);
    color: var(--primary);
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: var(--shadow-card);
    opacity: 0;
    visibility: hidden;
    transform: translateY(12px);
    transition: all 0.45s var(--ease-cozy);
    z-index: 1040;
    cursor: pointer;
}

.back-to-top.is-visible {
    opacity: 1;
    visibility: visible;
    transform: none;
}

.back-to-top:hover {
    background: var(--primary);
    color: var(--white);
    transform: translateY(-3px);
}

/* -------------------------------------------------------------------------
   16. RESPONSIVE
   ------------------------------------------------------------------------- */
@media (max-width: 991.98px) {
    .navbar-collapse {
        background: var(--card-bg);
        border: 1px solid var(--paper-edge);
        border-radius: var(--radius);
        padding: 0.75rem;
        margin-top: 0.75rem;
        box-shadow: var(--shadow-card);
    }
}

@media (max-width: 767.98px) {
    body { font-size: 0.98rem; }

    /* En móvil el mensaje ya vive en su propia fila: cabe sin estorbar */
    /* Solo se ajusta el eje: línea y puntos siguen derivando de él */
    .timeline { --tl-pad: 1.5rem; --tl-eje: 0.5rem; }
}

/* -------------------------------------------------------------------------
   17. ACCESIBILIDAD
   ------------------------------------------------------------------------- */
a:focus-visible,
button:focus-visible,
.form-control:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }

    .reveal { opacity: 1; transform: none; }
    .hero::before, .avatar-ring { animation: none; }
}

@media print {
    .navbar, .back-to-top, footer nav, .hero-cielo { display: none !important; }
    body::before { display: none; }
    .card-custom { box-shadow: none; border: 1px solid #ddd; }
}

/* =========================================================================
   18. TERMINAL DEL HERO
   ========================================================================= */
.hero-terminal {
    background: color-mix(in srgb, var(--ink-strong) 92%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lift);
    overflow: hidden;
}

:root[data-theme="dark"] .hero-terminal {
    background: rgba(0, 0, 0, 0.42);
}

.hero-terminal .console-bar {
    background: color-mix(in srgb, var(--accent) 16%, transparent);
    border-bottom-color: color-mix(in srgb, var(--accent) 22%, transparent);
}

.hero-terminal .console-title { color: var(--term-texto); }

.term-body {
    font-family: var(--font-mono);
    font-size: 0.79rem;
    line-height: 1.85;
    padding: 1rem 1.15rem;
    min-height: 11.5rem;
    color: var(--term-texto);
}

.term-line { word-break: break-word; }
.term-cmd { color: var(--term-prompt); }
.term-out { color: var(--term-salida); padding-left: 0.9rem; }

.term-cursor {
    display: inline-block;
    width: 0.5rem;
    height: 1em;
    background: var(--accent);
    vertical-align: text-bottom;
    animation: parpadeo 1.05s steps(2, start) infinite;
}

@keyframes parpadeo { to { visibility: hidden; } }

/* =========================================================================
   19. PANEL DE ESTADO EN VIVO
   ========================================================================= */
@keyframes latido {
    0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--sage) 65%, transparent); }
    70%  { box-shadow: 0 0 0 0.7rem transparent; }
    100% { box-shadow: 0 0 0 0 transparent; }
}

/* -------------------------------------------------------------------------
   CONSTELACIÓN DEL CV
   Doce estrellas —una por certificación— repartidas en los cuatro bloques de
   la columna de al lado. Ocupa el hueco que dejaba la columna corta, pero no
   es relleno: es el mismo dato dicho en la tinta del cielo del hero.
   ------------------------------------------------------------------------- */
.constelacion-cv {
    margin-top: 2.25rem;
    padding-top: 1.75rem;
    border-top: 1px dashed var(--paper-edge);
}

.constelacion-cv svg {
    display: block;
    width: 100%;
    height: auto;
}

.constelacion-cv .cv-grupo circle {
    fill: var(--accent);
    animation: cielo-parpadeo 5s ease-in-out infinite;
}

/* Cada grupo late a su ritmo; a la vez parecería un cartel de neón */
.constelacion-cv .cv-grupo:nth-child(2) circle { animation-delay: 0s; }
.constelacion-cv .cv-grupo:nth-child(3) circle { animation-delay: 1.3s; }
.constelacion-cv .cv-grupo:nth-child(4) circle { animation-delay: 2.6s; }
.constelacion-cv .cv-grupo:nth-child(5) circle { animation-delay: 3.9s; }

.constelacion-cv .cv-grupo path {
    fill: none;
    stroke: var(--accent);
    stroke-width: 0.8;
    opacity: 0.3;
}

.constelacion-cv .cv-polvo circle {
    fill: var(--ink-soft);
    opacity: 0.35;
}

@media (prefers-reduced-motion: reduce) {
    .constelacion-cv .cv-grupo circle { animation: none; }
}


/* -------------------------------------------------------------------------
   CERTIFICACIONES VERIFICABLES
   Cada entrada enlaza a su credencial en Credly. Las que no tienen insignia
   —cursos de Platzi— usan la misma pieza sin enlace, para que la lista se
   lea pareja en vez de mezclar dos formatos.
   ------------------------------------------------------------------------- */
.cert-perfil {
    display: inline-flex;
    align-items: center;
    font-family: var(--font-mono);
    font-size: 0.68rem;
    color: var(--ink-soft);
    text-decoration: none;
    white-space: nowrap;
    transition: color 0.2s;
}

a.cert-perfil:hover { color: var(--accent); }

.cert-grupo {
    margin: 1.1rem 0 0.45rem;
    font-family: var(--font-mono);
    font-size: 0.62rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

.cert-grupo:first-of-type { margin-top: 0; }

/* El hover no tiñe el fondo ni saca una barra de acento: eso es el hover por
   defecto de cualquier plantilla. Aquí se dibuja una línea de puntos entre el
   nombre y el emisor, como el sumario de un libro impreso. Encaja con la
   Fraunces y no se parece a un menú lateral. */
.cert-item {
    display: flex;
    align-items: baseline;
    gap: 0.55rem;
    padding: 0.42rem 0;
    text-decoration: none;
}

.cert-nombre {
    position: relative;
    flex-shrink: 0;
    padding-left: 1.05rem;
    font-size: 0.83rem;
    font-weight: 500;
    color: var(--ink-strong);
    line-height: 1.35;
    transition: color 0.2s;
}

/* El tramo que se rellena de puntos al pasar por encima */
.cert-puntos {
    flex: 1 1 auto;
    min-width: 1.5rem;
    align-self: flex-end;
    margin-bottom: 0.22rem;
    border-bottom: 1px dotted color-mix(in srgb, var(--accent) 55%, transparent);
    opacity: 0;
    transition: opacity 0.25s;
}

.cert-meta {
    flex-shrink: 0;
    font-family: var(--font-mono);
    font-size: 0.65rem;
    color: var(--ink-soft);
    white-space: nowrap;
    transition: color 0.2s;
}

a.cert-item:hover .cert-puntos,
a.cert-item:focus-visible .cert-puntos { opacity: 1; }

a.cert-item:hover .cert-nombre,
a.cert-item:focus-visible .cert-nombre { color: var(--primary); }

a.cert-item:hover .cert-meta,
a.cert-item:focus-visible .cert-meta { color: var(--accent); }

/* La flecha abre la línea en lugar de colgar del final. Va en posición
   absoluta sobre el hueco que ya reserva el padding: al aparecer no empuja
   el nombre ni un píxel. */
a.cert-item .cert-nombre::before {
    content: '\2197';
    position: absolute;
    left: 0.1rem;
    top: 0.12em;
    font-size: 0.72em;
    color: var(--accent);
    opacity: 0;
    transition: opacity 0.2s;
}

a.cert-item:hover .cert-nombre::before,
a.cert-item:focus-visible .cert-nombre::before { opacity: 1; }

@media (max-width: 575.98px) {
    /* En estrecho el nombre y la fecha no caben en una línea, así que se
       apilan y el tramo de puntos deja de tener sentido. */
    .cert-item { flex-direction: column; gap: 0.1rem; align-items: flex-start; }
    .cert-nombre { flex-shrink: 1; }
    .cert-meta { white-space: normal; }
    .cert-puntos { display: none; }
}


/* =========================================================================
   EL CIELO DE LOS VISITANTES
   Vive dentro del hero y sale al frente en «modo cielo». Carta celeste sobre
   pergamino, no cielo nocturno: el azul de medianoche pelearía con la paleta
   cálida del resto del sitio. Las estrellas son tinta dorada sobre papel.
   ========================================================================= */
.hero-cielo {
    position: absolute;
    inset: 0;
    z-index: 0;
    /* En reposo el cielo adorna el hero desde detrás del contenido. Los
       clics lo atraviesan: en este estado los botones mandan. */
    opacity: .34;
    pointer-events: none;
    transition: opacity .55s cubic-bezier(0.33, 1, 0.68, 1);
}

.hero.es-modo-cielo .hero-cielo {
    z-index: 3;
    opacity: 1;
    pointer-events: auto;
}

/* El telón solo entra en modo cielo: tapa el hero atenuado lo justo para
   que las estrellas se lean, sin dejar de intuirse lo que hay debajo. En
   reposo es invisible, porque ahí el protagonista es el hero. */
.hero-cielo::before {
    content: '';
    position: absolute;
    inset: 0;
    opacity: 0;
    transition: opacity .55s cubic-bezier(0.33, 1, 0.68, 1);
    background:
        radial-gradient(ellipse at 28% 22%, color-mix(in srgb, var(--accent) 9%, transparent), transparent 58%),
        radial-gradient(ellipse at 78% 74%, color-mix(in srgb, var(--sage) 8%, transparent), transparent 52%),
        color-mix(in srgb, var(--bg-main) 94%, transparent);
}

.hero.es-modo-cielo .hero-cielo::before { opacity: 1; }

/* Grano sutil: sin él el degradado se ve digital y plano */
.hero-cielo::after {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    opacity: 0;
    transition: opacity .55s cubic-bezier(0.33, 1, 0.68, 1);
    background-image: radial-gradient(circle at 50% 50%, var(--paper-edge) .5px, transparent .5px);
    background-size: 26px 26px;
}

.hero.es-modo-cielo .hero-cielo::after { opacity: .5; }

/* De adorno, las constelaciones se insinúan; al frente, se leen */
.hero-cielo .cielo-lineas line { opacity: .16; transition: opacity .55s; }
.hero.es-modo-cielo .cielo-lineas line { opacity: .28; }


/* El contenido del hero se aparta, pero no del todo: sigue ahí de fondo
   para que se entienda que es el mismo sitio y no otra página. */
.hero-contenido {
    position: relative;
    z-index: 2;
    transition: opacity .5s cubic-bezier(0.33, 1, 0.68, 1), filter .5s;
}

.hero.es-modo-cielo .hero-contenido {
    opacity: .12;
    filter: blur(2px);
    pointer-events: none;
}

.hero-cielo svg {
    display: block;
    position: relative;
    z-index: 1;
    width: 100%;
    height: 100%;
}

.hero.es-modo-cielo { cursor: crosshair; }

/* Volver al hero: abajo a la izquierda, a la altura de la invitación. Los
   tres avisos del cielo —salir, invitación y recuento— forman así una misma
   línea al pie en vez de repartirse por las esquinas. */
.cielo-salir {
    position: absolute;
    bottom: 1.6rem;
    left: 1.15rem;
    z-index: 5;
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .4rem .85rem;
    border: 1px solid var(--paper-edge);
    border-radius: 2rem;
    background: color-mix(in srgb, var(--card-bg) 92%, transparent);
    color: var(--ink-soft);
    font-size: .76rem;
    cursor: pointer;
    transition: color .2s, border-color .2s;
}

.cielo-salir:hover {
    color: var(--primary);
    border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}

/* --- Constelaciones ---------------------------------------------------- */
.cielo-lineas line {
    stroke: var(--accent);
    stroke-width: .7;
    opacity: .28;
}

/* --- Estrellas --------------------------------------------------------- */
.cielo-estrella { cursor: default; outline: none; }
.cielo-estrella .cielo-halo { fill: url(#halo-estrella); opacity: .55; }
.cielo-estrella .cielo-nucleo {
    fill: var(--accent);
    transition: r .25s cubic-bezier(0.33, 1, 0.68, 1);
}

.cielo-estrella:hover .cielo-nucleo,
.cielo-estrella:focus-visible .cielo-nucleo { fill: var(--primary); }

.cielo-estrella:hover .cielo-halo,
.cielo-estrella:focus-visible .cielo-halo { opacity: 1; }

.cielo-estrella:focus-visible .cielo-nucleo {
    stroke: var(--primary);
    stroke-width: 1.5;
}

/* La propia estrella queda marcada al volver: reconocerla es media gracia */
.cielo-estrella.es-mia .cielo-nucleo {
    fill: var(--primary);
    stroke: var(--accent);
    stroke-width: 1.2;
}

@keyframes cielo-parpadeo {
    0%, 100% { opacity: 1; }
    50%      { opacity: .5; }
}

@keyframes cielo-nacer {
    0%   { opacity: 0; transform: scale(2.6); }
    45%  { opacity: 1; transform: scale(1.35); }
    100% { opacity: 1; transform: scale(1); }
}

.cielo-estrella .cielo-nucleo {
    animation: cielo-parpadeo 4.5s ease-in-out infinite;
    animation-delay: var(--retardo, 0s);
}

.cielo-estrella.es-nueva {
    transform-box: fill-box;
    transform-origin: center;
    animation: cielo-nacer 1.4s cubic-bezier(0.33, 1, 0.68, 1);
}

/* --- El sitio elegido, antes de confirmar ------------------------------ */
@keyframes cielo-latido {
    0%, 100% { opacity: .9; r: 14; }
    50%      { opacity: .35; r: 17; }
}

.cielo-fantasma-aro {
    fill: none;
    stroke: var(--primary);
    stroke-width: 1.2;
    stroke-dasharray: 3 3;
    animation: cielo-latido 1.9s ease-in-out infinite;
}

.cielo-fantasma-nucleo { fill: var(--primary); opacity: .8; }

/* --- Globo informativo ------------------------------------------------- */
.cielo-globo {
    position: absolute;
    z-index: 3;
    pointer-events: none;
    display: flex;
    flex-direction: column;
    gap: .1rem;
    padding: .5rem .7rem;
    border: 1px solid var(--paper-edge);
    border-radius: .6rem;
    background: var(--card-bg);
    box-shadow: 0 6px 20px rgb(0 0 0 / 12%);
    font-size: .78rem;
    line-height: 1.3;
    white-space: nowrap;
}

.cielo-globo strong { color: var(--ink-strong); font-weight: 600; }
.cielo-globo span { color: var(--ink-soft); font-family: var(--font-mono); font-size: .68rem; }

/* --- Avisos y contador ------------------------------------------------- */
.cielo-aviso {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0;
    padding: 1rem;
    text-align: center;
    color: var(--ink-soft);
    font-size: .9rem;
}

.cielo-contador {
    position: absolute;
    right: 1.15rem;
    bottom: 2.2rem;
    z-index: 2;
    font-family: var(--font-mono);
    font-size: .7rem;
    color: var(--ink-soft);
}

.cielo-contador strong { color: var(--accent); font-weight: 600; }

/* --- La invitación a tocar ---------------------------------------------- */
.cielo-llamada {
    position: absolute;
    left: 50%;
    bottom: 1.6rem;
    transform: translateX(-50%);
    z-index: 2;
    display: flex;
    align-items: center;
    gap: .4rem;
    margin: 0;
    padding: .4rem .85rem;
    border: 1px solid var(--paper-edge);
    border-radius: 2rem;
    background: color-mix(in srgb, var(--card-bg) 92%, transparent);
    backdrop-filter: blur(4px);
    font-size: .78rem;
    color: var(--ink-soft);
    white-space: nowrap;
    pointer-events: none;
}

.cielo-llamada.es-ok { color: var(--sage); }
.cielo-llamada i { color: var(--accent); }
.cielo-llamada.es-ok i { color: var(--sage); }

/* --- El formulario, anclado al punto elegido ---------------------------- */
.cielo-panel {
    position: absolute;
    z-index: 4;
    width: 16.5rem;
    padding: 1rem 1.1rem 1.1rem;
    border: 1px solid var(--paper-edge);
    border-radius: .9rem;
    background: var(--card-bg);
    box-shadow: 0 14px 40px rgb(0 0 0 / 16%);
    animation: cielo-panel-entra .28s cubic-bezier(0.33, 1, 0.68, 1);
}

@keyframes cielo-panel-entra {
    from { opacity: 0; transform: scale(.94); }
    to   { opacity: 1; transform: scale(1); }
}

.cielo-panel .form-label {
    margin-bottom: .2rem;
    font-size: .72rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--ink-soft);
}

.cielo-panel-titulo {
    display: flex;
    align-items: center;
    gap: .4rem;
    margin: 0 0 .8rem;
    font-family: var(--font-display);
    font-size: .95rem;
    font-weight: 600;
    color: var(--primary);
}

.cielo-panel-titulo i { color: var(--accent); }

.cielo-panel-cerrar {
    position: absolute;
    top: .55rem;
    right: .6rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.6rem;
    height: 1.6rem;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--ink-soft);
    font-size: .7rem;
    cursor: pointer;
    transition: background .2s, color .2s;
}

.cielo-panel-cerrar:hover {
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    color: var(--primary);
}

.cielo-nota { font-size: .76rem; margin-top: .5rem; min-height: 0; }
.cielo-nota:empty { display: none; }
.cielo-nota.es-ok { color: var(--sage); }
.cielo-nota.es-error { color: var(--primary); }

.cielo-pista {
    margin-top: .55rem;
    font-size: .7rem;
    color: var(--ink-soft);
    opacity: .8;
}

@media (max-width: 575.98px) {
    /* El panel sigue al punto también aquí; solo se estrecha para caber con
       aire a los lados. La colocación la calcula cielo.js. */
    .cielo-panel {
        width: min(16.5rem, calc(100vw - 2.5rem));
        padding: .9rem 1rem 1rem;
    }

    /* En estrecho los tres no caben en una línea: la invitación sube y
       deja el pie para el botón y el recuento. */
    .cielo-llamada { bottom: 3.6rem; font-size: .72rem; }
    .cielo-salir { bottom: 1rem; left: .8rem; font-size: .7rem; }
    .cielo-contador { bottom: 1.6rem; right: .8rem; font-size: .65rem; }
}

@media (prefers-reduced-motion: reduce) {
    .cielo-estrella .cielo-nucleo,
    .cielo-estrella.es-nueva,
    .cielo-fantasma-aro { animation: none; }
}

/* =========================================================================
   20. DIAGRAMA DE ARQUITECTURA
   ========================================================================= */
.arq-envoltura {
    overflow-x: auto;
    padding-block: 0.5rem;
}

#arquitectura {
    width: 100%;
    min-width: 560px;
    height: auto;
}

.arq-nodo {
    opacity: 0;
    transition: opacity 0.65s var(--ease-cozy);
}

.arq-caja {
    fill: var(--card-bg);
    stroke: color-mix(in srgb, var(--accent) 45%, transparent);
    stroke-width: 1.5;
}

.arq-caja-destacada {
    fill: color-mix(in srgb, var(--accent) 12%, transparent);
    stroke: var(--accent);
    stroke-width: 2;
}

.arq-titulo {
    font-family: var(--font-body);
    font-size: 13px;
    font-weight: 700;
    fill: var(--ink-strong);
}

.arq-sub {
    font-family: var(--font-mono);
    font-size: 10px;
    fill: var(--ink-soft);
}

.arq-trazo {
    fill: none;
    stroke: var(--accent);
    stroke-width: 1.6;
    stroke-linecap: round;
    /* La punta de flecha hereda el color del trazo vía currentColor */
    color: var(--accent);
}

/* El regreso se distingue de la ida: discontinuo y algo más tenue */
.arq-trazo-vuelta {
    stroke-dasharray: 6 5;
    opacity: 0.75;
    color: var(--primary);
    stroke: var(--primary);
}

.arq-etiqueta {
    font-family: var(--font-mono);
    font-size: 9.5px;
    fill: var(--accent);
}

/* =========================================================================
   21. TARJETAS DE PROYECTO
   ========================================================================= */
.proyecto-card {
    position: relative;
    height: 100%;
    display: flex;
    flex-direction: column;
    /* Sin esto, cualquier hijo en línea (un badge, por ejemplo) se estira a
       todo el ancho: en un contenedor flex manda align-items, no display. */
    align-items: flex-start;
    transition: transform 0.4s var(--ease-cozy), box-shadow 0.4s var(--ease-cozy),
                border-color 0.4s var(--ease-cozy);
    will-change: transform;
}

/* Los bloques de texto sí deben ocupar el ancho completo */
.proyecto-card > p,
.proyecto-card > h3,
.proyecto-card > .destacado-cabecera,
.proyecto-card > .destacado-detalle,
.proyecto-card > .d-flex {
    align-self: stretch;
}

.proyecto-num {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    color: var(--accent);
    letter-spacing: 0.12em;
}

.proyecto-titulo {
    font-family: var(--font-display);
    font-size: 1.16rem;
    font-weight: 600;
    color: var(--ink-strong);
    line-height: 1.3;
}

.proyecto-rol {
    font-family: var(--font-mono);
    font-size: 0.68rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

/* El resultado es lo que un reclutador busca: se destaca */
.proyecto-impacto {
    margin-top: auto;
    padding-top: 0.85rem;
    border-top: 1px dashed var(--paper-edge);
    font-size: 0.86rem;
    color: var(--primary);
    font-weight: 600;
    display: flex;
    align-items: flex-start;
    gap: 0.45rem;
}

.proyecto-impacto i { margin-top: 0.15rem; flex-shrink: 0; }

/* =========================================================================
   22. BARRA DE PROGRESO DE LECTURA
   ========================================================================= */
.read-progress-track {
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 2px;
    background: transparent;
    pointer-events: none;
}

#read-progress {
    height: 100%;
    width: 0;
    background: linear-gradient(90deg, var(--primary), var(--accent));
    transition: width 0.12s linear;
}

/* =========================================================================
   23. AJUSTES RESPONSIVE DE LO NUEVO
   ========================================================================= */
@media (max-width: 767.98px) {
    .term-body { font-size: 0.72rem; min-height: 9.5rem; }
}

/* =========================================================================
   24. CINTA DE TECNOLOGÍAS
   Adorno tipográfico, no logos: los colores de marca de cada tecnología
   romperían la paleta cálida. Se usa la mono del sitio y el acento propio.
   ========================================================================= */
.tech-ribbon {
    position: relative;
    overflow: hidden;
    border-block: 1px solid var(--paper-edge);
    background: color-mix(in srgb, var(--accent) 5%, transparent);
    padding-block: 0.7rem;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
    mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}

.ribbon-track {
    display: flex;
    width: max-content;
    animation: desfilar 46s linear infinite;
}

.tech-ribbon:hover .ribbon-track { animation-play-state: paused; }

.ribbon-item {
    font-family: var(--font-mono);
    font-size: 0.76rem;
    letter-spacing: 0.06em;
    color: var(--ink-soft);
    padding-inline: 1.1rem;
    white-space: nowrap;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    transition: color 0.3s var(--ease-soft);
}

/* El icono es el "logo": Bootstrap Icons en la paleta propia, en vez de
   los logotipos de marca, cuyos colores romperían la gama cálida. */
.ribbon-item i,
.ribbon-item .logo-marca {
    color: var(--accent);
    font-size: 0.95rem;
    opacity: 0.75;
    transition: opacity 0.3s var(--ease-soft), transform 0.35s var(--ease-cozy);
}

.ribbon-item:hover i,
.ribbon-item:hover .logo-marca {
    opacity: 1;
    transform: translateY(-1px) scale(1.12);
}

.ribbon-item::after {
    content: "·";
    color: var(--accent);
    opacity: 0.45;
    margin-left: 0.2rem;
}

.ribbon-item.is-learning i { color: var(--sage); }

.ribbon-item:hover { color: var(--primary); }

/* Marca las tecnologías que aún está aprendiendo */
.ribbon-item.is-learning { color: var(--sage); }

@keyframes desfilar {
    from { transform: translateX(0); }
    to   { transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
    .ribbon-track { animation: none; }
}


/* =========================================================================
   26. NARRATIVA BACKEND → DEVOPS
   ========================================================================= */
.arco-track {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    flex-wrap: wrap;
    font-family: var(--font-mono);
    font-size: 0.78rem;
}

.arco-paso {
    padding: 0.35rem 0.85rem;
    border-radius: 2rem;
    border: 1px solid var(--paper-edge);
    color: var(--ink-soft);
    background: var(--card-bg);
}

.arco-paso.is-actual {
    border-color: var(--accent);
    color: var(--primary);
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    font-weight: 600;
}

.arco-paso.is-futuro {
    border-style: dashed;
    color: var(--sage);
    border-color: color-mix(in srgb, var(--sage) 40%, transparent);
}

.arco-flecha { color: var(--accent); opacity: 0.7; }

/* =========================================================================
   27. RUPTURA DE LA REJILLA
   Mitiga el patrón más delator del diseño generado por IA: secciones
   idénticas, todo centrado y todas las tarjetas del mismo tamaño.
   Aquí se introduce jerarquía, asimetría y elementos que se salen.
   ========================================================================= */

/* Encabezado alineado a la izquierda con una regla que se extiende:
   alterna con los centrados para romper la monotonía vertical. */
.section-head-left {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: end;
    gap: 1.5rem;
    margin-bottom: 2.5rem;
    padding-bottom: 1.1rem;
    border-bottom: 1px solid var(--paper-edge);
}

.section-head-left .section-title { margin-bottom: 0.35rem; }

.section-head-note {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    color: var(--ink-soft);
    text-align: right;
    white-space: nowrap;
}

@media (max-width: 767.98px) {
    .section-head-left { grid-template-columns: 1fr; align-items: start; }
    .section-head-note { text-align: left; }
}

/* --- Proyecto destacado: uno manda, los demás acompañan --------------- */
.proyecto-destacado {
    position: relative;
    overflow: hidden;
    padding: 2.4rem 2.2rem 2rem;
}

.proyecto-destacado .proyecto-titulo {
    font-size: clamp(1.5rem, 3.2vw, 2.1rem);
    max-width: 18ch;
}

.proyecto-destacado .proyecto-cuerpo {
    font-size: 1.02rem;
    max-width: 52ch;
}

/* --- Tarjetas secundarias, deliberadamente más sobrias --------------- */
.proyecto-mini {
    padding: 1.35rem 1.5rem;
    height: 100%;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.proyecto-mini .proyecto-titulo { font-size: 1.02rem; }

.proyecto-mini .proyecto-impacto {
    font-size: 0.79rem;
    padding-top: 0.7rem;
}

/* --- Skill destacada: una tarjeta manda sobre el resto --------------- */
.skill-card-ancha {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 1.4rem;
    align-items: start;
}

@media (max-width: 575.98px) {
    .skill-card-ancha { grid-template-columns: 1fr; }
}

/* --- Cifra suelta en el margen: ancla visual fuera de la rejilla ----- */
.cifra-margen {
    font-family: var(--font-display);
    font-size: clamp(3.2rem, 7vw, 5rem);
    font-weight: 600;
    line-height: 0.9;
    color: var(--primary);
    display: block;
}

.cifra-pie {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

/* =========================================================================
   28. COMPOSITOR DE COMMIT
   El tipo y el mensaje se leen como una sola línea de git, no como dos
   campos sueltos: refuerza el formato Conventional Commits.
   ========================================================================= */
.commit-dospuntos {
    display: flex;
    align-items: center;
    padding-inline: 0.4rem;
    font-family: var(--font-mono);
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    border-right: 1px solid var(--paper-edge);
}

/* Texto de ayuda bajo los campos */
.campo-ayuda {
    display: block;
    margin-top: 0.35rem;
    font-size: 0.72rem;
    color: var(--ink-soft);
    line-height: 1.5;
}

#preview-commit { color: var(--accent); }

/* Select del formulario, alineado con el resto.
   Bootstrap dibuja la flecha como SVG incrustado con un gris azulado fijo
   (#343a40) que el puente de tokens no puede alcanzar: se sustituye por una
   flecha en el ámbar de la marca. */
.form-select {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23CD853F' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
}

:root[data-theme="dark"] .form-select {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23D9A066' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
}

/* El selector de tipo vive DENTRO del compositor: no debe conservar caja
   propia. Va después de la regla general para ganar por orden, no por
   !important. Sin esto se ve una caja dentro de otra. */
/* =========================================================================
   29. ETIQUETA DE TIPO EN EL MURO
   Cada categoría con su matiz, siempre dentro de la paleta cálida.
   ========================================================================= */
.commit-tag {
    font-family: var(--font-mono);
    font-weight: 500;
    font-style: normal;
    margin-right: 0.15rem;
}

.commit-tag-feat     { color: var(--sage); }
.commit-tag-fix      { color: var(--clay); }
.commit-tag-docs     { color: var(--accent); }
.commit-tag-refactor { color: var(--ember); }
.commit-tag-test     { color: var(--sage); }
.commit-tag-chore    { color: var(--ink-soft); }
.commit-tag-perf     { color: var(--ember); }
.commit-tag-style    { color: var(--accent); }


/* =========================================================================
   30. TARJETA DESTACADA — piezas
   ========================================================================= */
.destacado-cabecera {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.6rem;
    width: 100%;
    margin-bottom: 1.1rem;
    padding-bottom: 0.9rem;
    border-bottom: 1px solid var(--paper-edge);
}

/* Ficha técnica al pie: da sustancia donde antes había hueco */
.destacado-detalle {
    width: 100%;
    margin-top: auto;
    margin-bottom: 1.2rem;
    padding: 0.9rem 1rem;
    border-radius: var(--radius-sm);
    background: color-mix(in srgb, var(--accent) 7%, transparent);
    border: 1px solid var(--paper-edge);
}

.detalle-titulo {
    font-family: var(--font-mono);
    font-size: 0.66rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

/* Enlace al repositorio, solo donde existe uno público */
.proyecto-enlace {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-family: var(--font-mono);
    font-size: 0.74rem;
    color: var(--accent);
    text-decoration: none;
    transition: gap 0.3s var(--ease-cozy), color 0.3s var(--ease-soft);
}

.proyecto-enlace:hover {
    color: var(--primary);
    gap: 0.6rem;
}

/* =========================================================================
   31. FAQ — dos columnas asimétricas
   La izquierda da contexto y acompaña; la derecha lleva las respuestas.
   ========================================================================= */
.faq-intro {
    position: sticky;
    top: 7rem;
}

@media (max-width: 991.98px) {
    .faq-intro { position: static; }
}

.faq-nota {
    display: flex;
    gap: 0.75rem;
    padding: 1rem 1.15rem;
    border-radius: var(--radius-sm);
    background: color-mix(in srgb, var(--accent) 8%, transparent);
    border: 1px solid var(--paper-edge);
    font-size: 0.88rem;
    color: var(--text-main);
}

.faq-nota i {
    color: var(--accent);
    font-size: 1.05rem;
    flex-shrink: 0;
    margin-top: 0.15rem;
}

.faq-nota a {
    color: var(--primary);
    font-weight: 600;
    text-decoration: none;
    border-bottom: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
}

.faq-nota a:hover { color: var(--accent); }

.faq-metricas {
    display: flex;
    gap: 2.5rem;
    margin-top: 2rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--paper-edge);
}

.faq-metrica-valor {
    display: block;
    font-family: var(--font-display);
    font-size: 1.9rem;
    font-weight: 600;
    color: var(--primary);
    line-height: 1;
}

.faq-metrica-pie {
    font-family: var(--font-mono);
    font-size: 0.68rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

/* Acordeón en lista: líneas finas en vez de cajas, más editorial */
.accordion-faq .accordion-item {
    background: transparent;
    border: 0;
    border-bottom: 1px solid var(--paper-edge);
    border-radius: 0;
}

.accordion-faq .accordion-item:first-of-type {
    border-top: 1px solid var(--paper-edge);
}

.accordion-faq .accordion-button {
    padding: 1.35rem 0.35rem;
    font-size: 1.02rem;
    background: transparent;
    gap: 0.9rem;
    align-items: flex-start;
    transition: color 0.3s var(--ease-soft), padding-left 0.35s var(--ease-cozy);
}

.accordion-faq .accordion-button:hover {
    color: var(--primary);
    padding-left: 0.75rem;
}

.accordion-faq .accordion-button:not(.collapsed) {
    background: transparent;
    color: var(--primary);
}

.faq-indice {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    color: var(--accent);
    padding-top: 0.3rem;
    flex-shrink: 0;
    opacity: 0.8;
}

.accordion-faq .accordion-body {
    padding: 0 0.35rem 1.6rem 3.1rem;
}

.faq-respuesta {
    color: var(--text-main);
    line-height: 1.75;
}

.faq-respuesta p:last-child { margin-bottom: 0; }

@media (max-width: 575.98px) {
    .accordion-faq .accordion-body { padding-left: 0.35rem; }
}

/* --- Revelado del texto: cada palabra entra desde el desenfoque -------- */
.palabra-revelada {
    display: inline-block;
    opacity: 0;
    filter: blur(8px);
    transform: translateY(4px);
    animation: revelarPalabra 0.42s var(--ease-cozy) forwards;
}

@keyframes revelarPalabra {
    to {
        opacity: 1;
        filter: blur(0);
        transform: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .palabra-revelada {
        animation: none;
        opacity: 1;
        filter: none;
        transform: none;
    }
}

/* =========================================================================
   32. REJILLA DE GADGETS — auto-ajustable
   Sustituye las columnas fijas de Bootstrap (col-lg-4 col-md-6), que obligan
   a decidir de antemano cuántas caben. Con auto-fit + minmax el navegador
   calcula las columnas según el ancho disponible: las piezas se reparten
   siempre en partes iguales (1fr), bajan de fila solas cuando no caben, y
   nunca queda una tarjeta estirada ocupando toda una fila.
   ========================================================================= */
.rejilla-auto {
    display: grid;
    /* mínimo legible por tarjeta · máximo repartido a partes iguales */
    grid-template-columns: repeat(auto-fit, minmax(17.5rem, 1fr));
    gap: 1.5rem;
    align-items: stretch;   /* todas las de una fila miden lo mismo de alto */
}

/* Con muy pocos elementos, evita tarjetas desproporcionadas */
.rejilla-auto.rejilla-max-3 {
    max-width: 68rem;
    margin-inline: auto;
}

@media (max-width: 575.98px) {
    .rejilla-auto {
        grid-template-columns: 1fr;
        gap: 1.15rem;
    }
}

/* La tarjeta ocupa toda su celda y reparte su interior en vertical, de modo
   que los pies ("Ver detalles") queden alineados entre tarjetas vecinas
   aunque los títulos ocupen distinto número de líneas. */
.gadget-card {
    display: flex;
    flex-direction: column;
    height: 100%;
    overflow: hidden;
}

.gadget-card > a {
    display: flex;
    flex-direction: column;
    height: 100%;
}

.gadget-cuerpo {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    gap: 0.5rem;
}

/* Empuja el pie hacia abajo: alinea todos los "Ver detalles" de la fila */
.gadget-pie {
    margin-top: auto;
    padding-top: 0.9rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
}

/* La imagen mantiene proporción constante para que las filas no bailen */
.gadget-medio {
    position: relative;
    aspect-ratio: 16 / 10;
    overflow: hidden;
    background: linear-gradient(135deg,
        color-mix(in srgb, var(--accent) 12%, transparent),
        color-mix(in srgb, var(--primary) 8%, transparent));
}

.gadget-medio img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform 0.6s var(--ease-cozy);
}

.gadget-card:hover .gadget-medio img { transform: scale(1.05); }

.gadget-medio .medio-vacio {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 2rem;
    color: var(--accent);
    opacity: 0.4;
}

/* =========================================================================
   33. GALERÍA ELÁSTICA — tarjetas que se expanden
   Equivale a los "expanding cards" de React, con flex-grow y transiciones:
   sin JavaScript, sin dependencias y con teclado incluido.

   Reglas de reparto:
   · Cada tarjeta parte de un ancho mínimo legible y crece a partes iguales.
   · La que recibe el cursor (o el foco) se lleva más espacio; el resto cede.
   · Si no caben todas, bajan de fila solas (flex-wrap) en vez de estrujarse.
   · Por debajo de 992 px no hay expansión: todo el contenido queda visible.
   ========================================================================= */
.galeria-elastica {
    display: flex;
    flex-wrap: wrap;
    gap: 1.1rem;
    align-items: stretch;
}

.gadget-elastico {
    position: relative;
    /* base flexible: crece a partes iguales sin bajar de un mínimo legible */
    flex: 1 1 16rem;
    min-width: 0;
    min-height: 23rem;
    border-radius: var(--radius);
    border: 1px solid var(--paper-edge);
    background: var(--card-bg);
    overflow: hidden;
    text-decoration: none;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    transition: flex-grow 0.65s var(--ease-cozy),
                box-shadow 0.45s var(--ease-cozy),
                border-color 0.45s var(--ease-cozy);
}

/* Fondo cálido propio: sin fotografías, el degradado y el icono son la
   identidad visual de cada pieza. */
.gadget-elastico::before {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(155deg,
        color-mix(in srgb, var(--accent) 16%, transparent) 0%,
        color-mix(in srgb, var(--primary) 10%, transparent) 55%,
        transparent 100%);
    transition: opacity 0.5s var(--ease-soft);
    opacity: 0.75;
}

.gadget-elastico:hover::before,
.gadget-elastico:focus-visible::before { opacity: 1; }

.gadget-elastico img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.8s var(--ease-cozy);
}

.gadget-elastico:hover img { transform: scale(1.06); }

/* Caja de tamaño fijo: sin dimensiones propias, el icono se recolocaba en
   cada fotograma mientras la tarjeta cambiaba de ancho. */
.gadget-icono {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.6rem;
    height: 2.6rem;
    flex: 0 0 auto;
    margin-bottom: 0.6rem;
    font-size: 1.45rem;
    line-height: 1;
    color: var(--primary);
    border-radius: 0.7rem;
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    opacity: 0.9;
    /* Solo se anima la opacidad: cualquier transform aquí compite con el
       reposicionamiento de la tarjeta y se percibe como un salto. */
    transition: opacity 0.4s var(--ease-soft), background 0.4s var(--ease-soft);
}

.gadget-elastico:hover .gadget-icono {
    opacity: 1;
    background: color-mix(in srgb, var(--accent) 22%, transparent);
}

/* El panel ocupa TODO el ancho de la tarjeta: si se le fija un ancho, al
   expandirse queda corto y deja ver el fondo por la derecha. */
.gadget-contenido {
    position: relative;
    z-index: 2;
    width: 100%;
    padding: 1.4rem;
    background: linear-gradient(to top,
        var(--card-bg) 62%,
        color-mix(in srgb, var(--card-bg) 70%, transparent) 88%,
        transparent 100%);
}

/* El ancho fijo se aplica solo al TEXTO, no al panel: así la maquetación no
   se recalcula mientras la tarjeta crece (que era lo que hacía saltar al
   icono) y el fondo sigue llegando de borde a borde. */
.gadget-titulo,
.gadget-detalle {
    width: 18.2rem;
    max-width: 100%;
}

.gadget-titulo {
    font-family: var(--font-display);
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--ink-strong);
    line-height: 1.25;
    margin-bottom: 0;
}

/* Detalle que solo aparece cuando la tarjeta gana espacio */
.gadget-detalle {
    display: grid;
    grid-template-rows: 0fr;
    opacity: 0;
    transition: grid-template-rows 0.55s var(--ease-cozy),
                opacity 0.4s var(--ease-soft),
                margin-top 0.55s var(--ease-cozy);
    margin-top: 0;
}

.gadget-detalle > div { overflow: hidden; min-height: 0; }

@media (min-width: 992px) {
    /* La tarjeta enfocada o señalada se lleva el espacio */
    .gadget-elastico:hover,
    .gadget-elastico:focus-visible {
        flex-grow: 2.6;
        box-shadow: var(--shadow-lift);
        border-color: color-mix(in srgb, var(--accent) 45%, transparent);
    }

    .gadget-elastico:hover .gadget-detalle,
    .gadget-elastico:focus-visible .gadget-detalle {
        grid-template-rows: 1fr;
        opacity: 1;
        margin-top: 0.7rem;
    }

}

/* Sin hover (móvil y tabletas): todo visible, nada oculto tras un gesto */
@media (max-width: 991.98px) {
    .gadget-elastico {
        flex: 1 1 15rem;
        min-height: 18rem;
    }

    .gadget-detalle {
        grid-template-rows: 1fr;
        opacity: 1;
        margin-top: 0.6rem;
    }
}

.gadget-resumen {
    color: var(--text-main);
    font-size: 0.88rem;
    line-height: 1.6;
    margin-bottom: 0.6rem;
    /* Un resumen largo se cortaba a mitad de palabra y parecía un fallo.
       Se limita a cuatro líneas y se cierra con puntos suspensivos; el
       texto completo sigue estando en la página de detalle. */
    display: -webkit-box;
    -webkit-line-clamp: 4;
    line-clamp: 4;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.gadget-cta {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-family: var(--font-mono);
    font-size: 0.74rem;
    color: var(--primary);
    margin-top: 0.5rem;
}

@media (prefers-reduced-motion: reduce) {
    .gadget-elastico,
    .gadget-detalle,
    .gadget-elastico img { transition: none; }
}

/* --- Rótulo vertical: identidad de la tarjeta mientras está estrecha ---- */
.gadget-rotulo {
    position: absolute;
    top: 1.6rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 3;
    writing-mode: vertical-rl;
    text-orientation: mixed;
    font-family: var(--font-mono);
    font-size: 0.78rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-strong);
    white-space: nowrap;
    max-height: calc(100% - 3.2rem);
    overflow: hidden;
    text-overflow: ellipsis;
    opacity: 0.85;
    transition: opacity 0.35s var(--ease-soft);
    pointer-events: none;
}

/* El contenido horizontal solo aparece cuando la tarjeta gana espacio */
.gadget-contenido { opacity: 0; transition: opacity 0.45s var(--ease-soft); }

@media (min-width: 992px) {
    /* Tarjeta activa: se oculta el rótulo vertical y entra el contenido */
    .gadget-elastico:hover .gadget-rotulo,
    .gadget-elastico:focus-visible .gadget-rotulo {
        opacity: 0;
    }

    .gadget-elastico:hover .gadget-contenido,
    .gadget-elastico:focus-visible .gadget-contenido {
        opacity: 1;
    }

    /* Las inactivas ceden espacio y pierden saturación, como en el original */
    .galeria-elastica:hover .gadget-elastico:not(:hover) { flex-grow: 0.75; }
    .galeria-elastica:hover .gadget-elastico:not(:hover) img { filter: grayscale(1); }
}

/* Sin cursor: el rótulo se retira y el contenido queda siempre visible */
@media (max-width: 991.98px) {
    .gadget-rotulo { display: none; }
    .gadget-contenido { opacity: 1; }
}

/* Apertura por toque en pantallas táctiles */
.gadget-elastico.is-abierta { flex-grow: 2.6; }
.gadget-elastico.is-abierta .gadget-rotulo { opacity: 0; }
.gadget-elastico.is-abierta .gadget-contenido { opacity: 1; }
.gadget-elastico.is-abierta .gadget-detalle {
    grid-template-rows: 1fr;
    opacity: 1;
    margin-top: 0.7rem;
}


/* =========================================================================
   34. REVELADO DEL TÍTULO
   Una cortina descubre el texto de izquierda a derecha. Reemplaza al efecto
   de letras desordenadas, que entorpecía la lectura del titular.

   La clase .titulo-cortina la añade el script, nunca la plantilla: así, si
   el JavaScript falla, el título se muestra normal en vez de quedar oculto
   tras el recorte.
   ========================================================================= */
.titulo-cortina {
    clip-path: inset(0 100% -0.35em 0);
    transition: clip-path 0.95s var(--ease-cozy);
}

.titulo-cortina.is-revelado {
    clip-path: inset(0 0 -0.35em 0);
}

@media (prefers-reduced-motion: reduce) {
    .titulo-cortina {
        clip-path: none;
        transition: none;
    }
}


/* =========================================================================
   35. MIGA DE PAN
   Bootstrap declara estas variables dentro de `.breadcrumb`, así que se
   redefinen sobre el mismo selector (igual que en el acordeón).
   ========================================================================= */
.breadcrumb {
    --bs-breadcrumb-divider-color: var(--accent);
    --bs-breadcrumb-item-active-color: var(--ink-soft);
    --bs-breadcrumb-item-padding-x: 0.6rem;
    font-family: var(--font-mono);
    font-size: 0.8rem;
}

.breadcrumb-item a {
    color: var(--primary);
    text-decoration: none;
    transition: color 0.3s var(--ease-soft);
}

.breadcrumb-item a:hover { color: var(--accent); }

/* =========================================================================
   36. LOGOS DE MARCA
   Simple Icons incrustados: monocromos, así que heredan el color del sitio
   en lugar de traer el azul de Docker o el amarillo de Python, que
   romperían la paleta cálida.
   ========================================================================= */
.logo-marca {
    width: 1em;
    height: 1em;
    fill: currentColor;
    vertical-align: -0.125em;
    flex-shrink: 0;
}

/* Logos dentro del diagrama de arquitectura */
.arq-logo {
    fill: var(--primary);
    opacity: 0.85;
}

/* Caja de la plataforma: contiene al contenedor y al servicio de datos */
.arq-plataforma {
    fill: color-mix(in srgb, var(--accent) 5%, transparent);
    stroke: color-mix(in srgb, var(--accent) 38%, transparent);
    stroke-width: 1.2;
}


/* =========================================================================
   37. ANCHO DE PÁGINA
   Bootstrap deja crecer el contenedor hasta 1320 px, y en monitores anchos
   los párrafos alcanzan longitudes incómodas de leer. Se recoge a un ancho
   más contenido; al ir sobre `.container`, afecta a todas las páginas.
   ========================================================================= */
.container,
.container-sm,
.container-md,
.container-lg,
.container-xl,
.container-xxl {
    max-width: var(--ancho-maximo);
    /* Bootstrap deja 0.75rem a cada lado; se añade un respiro extra para
       que el contenido no quede pegado al borde en pantallas estrechas. */
    padding-inline: calc(0.75rem + 5px);
}
