/**
 * base.css — reset, tipografía, layout y componentes.
 * Color, espacio, tipo y movimiento salen de quarks/tokens.css.
 * Ningún hex ni px de tipografía aquí. Ningún !important.
 */

@layer reset, base, layout, components, motion;

/* ══ RESET ══════════════════════════════════════════════════════════════ */
@layer reset {
  *, *::before, *::after { box-sizing: border-box; }
  * { margin: 0; }

  html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
  @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

  body { min-height: 100svh; }
  img, picture, svg, video, canvas { display: block; max-width: 100%; }
  input, button, textarea, select { font: inherit; color: inherit; }
  ul[class], ol[class] { list-style: none; padding: 0; }

  p, li { overflow-wrap: break-word; }
  h1, h2, h3, h4 { overflow-wrap: normal; hyphens: manual; }
}

/* ══ BASE ═══════════════════════════════════════════════════════════════ */
@layer base {
  body {
    position: relative;
    background: var(--bg);
    color: var(--text);
    font-family: var(--font-body);
    font-size: var(--type-md);
    line-height: var(--leading-normal);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
  }
  body.is-loading { overflow: hidden; }

  h1, h2, h3 {
    font-family: var(--font-display);
    font-weight: 620;
    line-height: var(--leading-tight);
    letter-spacing: var(--tracking-tight);
    text-wrap: balance;
  }
  h1 { font-size: var(--type-3xl); }
  h2 { font-size: var(--type-2xl); }
  h3 { font-size: var(--type-xl); line-height: var(--leading-snug); }

  a { color: inherit; text-decoration: none; }

  :focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 4px; }
  ::selection { background: var(--accent-solid); color: var(--accent-ink); }

  code, kbd, samp { font-family: var(--font-mono); font-size: 0.9em; }
}

/* ══ CAPAS DE FONDO ═════════════════════════════════════════════════════ */
@layer base {
  /* El shader vive fijo detrás de todo. Si WebGL falla, el gradiente CSS de
     respaldo ya está pintado debajo y no se nota el hueco. */
  .bg-shader {
    position: fixed;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: var(--layer-shader);
    opacity: var(--shader-opacity);
    pointer-events: none;
    background:
      radial-gradient(120% 90% at 18% 8%, color-mix(in srgb, var(--accent) 30%, transparent), transparent 60%),
      radial-gradient(100% 80% at 85% 30%, color-mix(in srgb, var(--accent) 16%, transparent), transparent 65%);
  }

  .grain {
    position: fixed;
    inset: 0;
    z-index: var(--layer-grain);
    pointer-events: none;
    opacity: var(--grain-opacity);
    mix-blend-mode: overlay;
  }
  /* overlay sobre fondo claro lava el grano; multiply lo conserva */
  :root[data-theme='light'] .grain { mix-blend-mode: multiply; }
  .grain {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  }

  /* Cortina de entrada */
  .curtain {
    position: fixed;
    inset: 0;
    z-index: var(--layer-preloader);
    background: var(--bg);
    display: grid;
    place-items: center;
  }
  .curtain__label {
    font-family: var(--font-mono);
    font-size: var(--type-2xs);
    letter-spacing: var(--tracking-widest);
    text-transform: uppercase;
    color: var(--text-dim);
  }

  /* `mix-blend-mode: difference` se probó y se descartó: difference(negro, X)
     = X — un no-op. En claro `--text` es casi negro, así que el cursor
     devolvía el color de fondo intacto, invisible de verdad, no solo sutil.
     El header ya había resuelto este mismo problema apagando `difference` en
     claro (ver `.site-header` más abajo); el cursor sigue el mismo patrón:
     color sólido de `--text`/`--accent`, ya verificado con contraste AA/AAA
     contra `--bg` en los dos temas porque es el mismo par que usa todo el
     texto del sitio. Nada que dependa de qué haya debajo. */
  .cursor {
    position: fixed;
    top: 0; left: 0;
    width: 34px; height: 34px;
    margin: -17px 0 0 -17px;
    border: 1px solid var(--border-strong);
    border-radius: 50%;
    z-index: var(--layer-cursor);
    pointer-events: none;
    transition: width var(--motion-quick) var(--ease-out),
                height var(--motion-quick) var(--ease-out),
                margin var(--motion-quick) var(--ease-out),
                border-color var(--motion-quick) var(--ease-out),
                background var(--motion-quick) var(--ease-out);
  }
  /* El punto es el ancla de posición exacta — igual que el "dot" del cursor
     de v1. El anillo solo. */
  .cursor::after {
    content: '';
    position: absolute;
    top: 50%; left: 50%;
    width: 6px; height: 6px;
    margin: -3px 0 0 -3px;
    border-radius: 50%;
    background: var(--text);
    transition: background var(--motion-quick) var(--ease-out);
  }
  .cursor.is-active {
    width: 62px; height: 62px; margin: -31px 0 0 -31px;
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 12%, transparent);
  }
  .cursor.is-active::after { background: var(--accent); }
  @media (pointer: coarse) { .cursor { display: none; } }
}

/* ══ LAYOUT ═════════════════════════════════════════════════════════════ */
@layer layout {
  .skip-link {
    position: absolute; left: var(--space-sm); top: -6rem;
    z-index: var(--layer-preloader);
    background: var(--bg); color: var(--text);
    padding: var(--space-2xs) var(--space-sm);
    border: 1px solid var(--border-strong);
    transition: top var(--motion-quick) var(--ease-out);
  }
  .skip-link:focus { top: var(--space-sm); }

  .site-header {
    position: sticky; top: 0;
    z-index: var(--layer-header);
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-sm);
    padding: var(--space-sm) var(--gutter);
    mix-blend-mode: difference;
  }
  /* difference funciona sobre oscuro; sobre claro deja el header casi
     invisible (blanco sobre blanco). En tema claro no se puede resolver con
     blend mode, así que el header lleva color sólido de verdad.
     Antes iba a 72% + blur: con la lavanda tan clara del fondo, esa
     transparencia dejaba pasar las curvas de nivel a medio difuminar
     — se leía sucio, no liviano. Sin blur y con más cuerpo el header
     se lee como una superficie propia, no como un velo sobre el ruido. */
  :root[data-theme='light'] .site-header {
    mix-blend-mode: normal;
    color: var(--text);
    background: color-mix(in srgb, var(--bg) 94%, transparent);
    border-bottom: 1px solid var(--border);
  }
  .site-header__mark {
    font-family: var(--font-mono);
    font-size: var(--type-sm);
    font-weight: 600;
    letter-spacing: var(--tracking-wide);
  }
  .site-header__nav { display: flex; align-items: center; gap: var(--space-2xs); }

  .site-header__lang, .site-header__theme, .site-header__link {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 44px; min-height: 44px;
    padding: 0 var(--space-2xs);
    font-family: var(--font-mono);
    font-size: var(--type-2xs);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    background: none; border: 1px solid transparent;
    border-radius: var(--radius-pill);
    cursor: pointer;
    transition: border-color var(--motion-quick) var(--ease-out);
  }
  .site-header__lang:hover, .site-header__theme:hover, .site-header__link:hover { border-color: currentColor; }
  .site-header__theme::before { content: '◐'; font-size: var(--type-lg); }

  main { position: relative; z-index: var(--layer-content); }

  main > section, main > article {
    max-width: var(--shell);
    margin-inline: auto;
    padding-inline: var(--gutter);
    padding-block: var(--space-2xl);
  }

  .site-footer {
    position: relative;
    z-index: var(--layer-content);
    display: flex; flex-wrap: wrap; gap: var(--space-sm) var(--space-lg);
    justify-content: space-between; align-items: center;
    max-width: var(--shell); margin-inline: auto;
    padding: var(--space-lg) var(--gutter) var(--space-2xl);
    border-top: 1px solid var(--border);
    font-family: var(--font-mono);
    font-size: var(--type-2xs);
    letter-spacing: var(--tracking-wide);
    color: var(--text-dim);
  }
  .site-footer p { display: flex; gap: var(--space-md); }
  .site-footer a:hover { color: var(--text); }
}

/* ══ COMPONENTES ════════════════════════════════════════════════════════ */
@layer components {

  .eyebrow {
    display: inline-flex; align-items: center; gap: var(--space-2xs);
    font-family: var(--font-mono);
    font-size: var(--type-2xs);
    letter-spacing: var(--tracking-widest);
    text-transform: uppercase;
    color: var(--text-muted);
  }
  .eyebrow::before {
    content: ''; width: 2rem; height: 1px;
    background: currentColor; opacity: 0.5;
  }

  /* ── HERO ─────────────────────────────────────────────────────────── */
  .hero {
    min-height: 100svh;
    display: grid;
    align-content: center;
    /* Relleno proporcional a la altura de la ventana, no fijo: con 11rem el
       botón principal caía por debajo del pliegue en pantallas de portátil. */
    padding-block: clamp(3.5rem, 9vh, 7rem) var(--space-2xl);
  }

  /* Por encima del fondo generativo de toda la página (.bg-shader). */
  .hero__copy {
    position: relative;
    z-index: var(--layer-content);
    max-width: 46rem;
  }

  /* El h1 lleva nombre y rol. Se maquetan como dos bloques distintos pero
     siguen siendo un solo titular para el buscador. */
  .hero__title {
    display: grid;
    gap: var(--space-2xs);
  }

  /* Tres escalones de peso: el nombre manda, la frase acompaña, las
     disciplinas apenas susurran. Sin esa diferencia el hero se ve cargado
     porque tres líneas seguidas piden la misma atención. */
  .hero__statement {
    display: block;
    font-size: var(--type-2xl);
    font-weight: 500;
    line-height: var(--leading-snug);
    letter-spacing: var(--tracking-tight);
    color: var(--text);
  }

  .hero__name {
    display: block;
    font-size: var(--type-hero);
    font-weight: 660;
    line-height: 0.9;
    letter-spacing: var(--tracking-hero);
    /* "Iván Santander" son 14 caracteres: cabe en una línea hasta muy grande,
       así que la escala puede ser agresiva sin romper la composición. */
    text-wrap: nowrap;
  }
  @media (max-width: 30rem) {
    .hero__name { text-wrap: wrap; }
  }

  .hero__role {
    display: block;
    font-family: var(--font-mono);
    font-size: var(--type-xs);
    font-weight: 400;
    line-height: var(--leading-snug);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    margin-top: var(--space-2xs);
    /* Azul desaturado propio: el acento a plena saturación competía con
       el nombre. Sigue leyéndose azul, deja de gritar. */
    color: var(--accent-muted);
  }

  .hero__lead {
    max-width: 46ch;
    margin-top: var(--space-lg);
    color: var(--text-muted);
    font-size: var(--type-lg);
    line-height: 1.5;
  }
  .hero__lead p + p { margin-top: var(--space-sm); }
  /* "Construyo software." es la frase ancla: va en el color pleno */
  .hero__actions { display: flex; flex-wrap: wrap; gap: var(--space-xs); margin-top: var(--space-lg); }

  /* Cifras en una fila compacta: en bloques grandes competían con el nombre y
     el hero se leía sobrecargado. Aquí informan sin pedir turno. */
  .hero__readout {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-md) var(--space-lg);
    margin-top: var(--space-xl);
    padding-top: var(--space-md);
    border-top: 1px solid var(--border);
  }
  .hero__readout > div {
    display: flex;
    align-items: baseline;
    gap: var(--space-2xs);
  }
  .hero__readout dd {
    font-family: var(--font-display);
    font-size: var(--type-lg);
    font-weight: 600;
    line-height: 1;
    color: var(--accent-muted);
    font-variant-numeric: tabular-nums;
  }
  .hero__readout dt {
    font-family: var(--font-mono);
    font-size: var(--type-2xs);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--text-dim);
  }

  /* ── BOTONES ──────────────────────────────────────────────────────── */
  .btn {
    position: relative;
    display: inline-flex; align-items: center;
    min-height: 52px;
    padding: 0 var(--space-lg);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-pill);
    font-size: var(--type-sm);
    font-weight: 500;
    overflow: hidden;
    isolation: isolate;
    transition: color var(--motion-base) var(--ease-out),
                border-color var(--motion-base) var(--ease-out);
  }
  /* Relleno que sube desde abajo en el hover, en vez de un cambio de color */
  .btn::before {
    content: '';
    position: absolute; inset: 0;
    z-index: -1;
    background: var(--text);
    transform: translateY(101%);
    transition: transform var(--motion-base) var(--ease-inout);
  }
  .btn:hover { color: var(--bg); border-color: var(--text); }
  .btn:hover::before { transform: translateY(0); }

  /* El único lugar del sitio con degradado: un guiño contenido al acabado
     metálico del que viene el acento ("dynamic gradient when light
     reflects off its surface", como lo describe Apple del Sky Blue) — sin
     meterlo en el token plano ni repetirlo en otro sitio. */
  .btn--primary {
    background: linear-gradient(135deg,
      color-mix(in srgb, var(--accent-solid) 88%, white) 0%,
      var(--accent-solid) 50%,
      color-mix(in srgb, var(--accent-solid) 88%, black) 100%);
    color: var(--accent-ink);
    border-color: var(--accent-solid); font-weight: 600;
  }
  .btn--primary::before { background: var(--text); }
  .btn--primary:hover { color: var(--bg); }

  /* ── TESIS ────────────────────────────────────────────────────────── */
  .thesis__title { font-size: var(--type-4xl); max-width: 13ch; font-weight: 650; }
  .thesis__body {
    max-width: var(--measure);
    margin-top: var(--space-lg);
    color: var(--text-muted);
    font-size: var(--type-lg);
  }
  .thesis__note {
    max-width: var(--measure);
    margin-top: var(--space-lg);
    padding-left: var(--space-md);
    border-left: 1px solid var(--accent);
    color: var(--text-muted);
    font-size: var(--type-sm);
  }

  .section-title { font-size: var(--type-4xl); font-weight: 650; }
  .section-head { display: grid; gap: var(--space-sm); margin-bottom: var(--space-xl); }
  .section-head .prose { max-width: var(--measure); color: var(--text-muted); }

  /* ── TARJETAS ─────────────────────────────────────────────────────── */
  .work-index__list { display: grid; gap: 1px; background: var(--border); border-block: 1px solid var(--border); }

  .work-card {
    position: relative;
    background: var(--bg);
    overflow: hidden;
    isolation: isolate;
  }
  /* Barrido claro al pasar por encima: la fila se enciende de izquierda a
     derecha en vez de simplemente cambiar de fondo. */
  .work-card::before {
    content: '';
    position: absolute; inset: 0;
    z-index: -1;
    background: var(--surface);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--motion-slow) var(--ease-inout);
  }
  .work-card:hover::before { transform: scaleX(1); }

  .work-card__link {
    display: grid;
    gap: var(--space-sm) var(--space-xl);
    padding: var(--space-lg) var(--space-sm);
  }
  @media (min-width: 60rem) {
    .work-card__link {
      grid-template-columns: 7rem minmax(0, 1.6fr) minmax(0, 1fr);
      grid-template-areas: 'meta title extra';
      align-items: start;
      padding: var(--space-xl) var(--space-md);
    }
    .work-card__meta-top { grid-area: meta; }
    .work-card__body     { grid-area: title; }
    .work-card__extra    { grid-area: extra; }
  }

  .work-card__meta-top {
    display: flex; flex-direction: column; gap: var(--space-3xs);
    font-family: var(--font-mono);
    font-size: var(--type-2xs);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--text-dim);
  }
  /* El nombre del proyecto va aquí, no dentro del título: así los títulos son
     frases con mayúscula inicial y "mini-astro" o "blog-26" no arrancan
     una oración en minúscula. */
  .work-card__project {
    color: var(--text);
    text-transform: none;
    letter-spacing: var(--tracking-normal);
    font-size: var(--type-xs);
    /* La regla global `p { overflow-wrap: break-word }` parte nombres largos
       ("Consolidación de arquitectura") a media palabra en esta columna
       angosta. Con guion silábico se lee "Con­soli­da­ción" en vez de
       "Consolidaci" + "ón" sueltos. */
    overflow-wrap: normal;
    word-break: normal;
    hyphens: auto;
  }

  .work-card__title {
    font-size: var(--type-2xl);
    font-weight: 600;
    letter-spacing: var(--tracking-tight);
    line-height: var(--leading-snug);
    transition: transform var(--motion-base) var(--ease-out);
  }
  .work-card:hover .work-card__title { transform: translateX(10px); }

  .work-card__headline { color: var(--text-muted); font-size: var(--type-sm); margin-top: var(--space-2xs); max-width: 48ch; }

  .work-card__stack { display: flex; flex-wrap: wrap; gap: var(--space-3xs); margin-top: var(--space-sm); }
  .work-card__stack li {
    font-family: var(--font-mono);
    font-size: var(--type-2xs);
    letter-spacing: var(--tracking-wide);
    color: var(--text-dim);
    padding: 0.22rem 0.6rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
  }

  /* Cifras grandes a la derecha: densidad sin necesitar imágenes */
  .work-card__figures { display: grid; gap: var(--space-sm); }
  .work-card__figures div { display: flex; align-items: baseline; gap: var(--space-2xs); }
  .work-card__figures b {
    font-family: var(--font-display);
    font-size: var(--type-xl);
    font-weight: 650;
    font-variant-numeric: tabular-nums;
    color: var(--text);
    min-width: 3.5ch;
  }
  .work-card__figures span {
    font-family: var(--font-mono);
    font-size: var(--type-2xs);
    letter-spacing: var(--tracking-wide);
    color: var(--text-dim);
  }

  .work-card__extra {
    display: flex; flex-direction: column;
    gap: var(--space-md);
    justify-content: space-between;
    height: 100%;
  }
  .work-card__arrow {
    font-family: var(--font-mono);
    font-size: var(--type-2xs);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--text-dim);
    transition: transform var(--motion-base) var(--ease-out), color var(--motion-base) var(--ease-out);
  }
  .work-card:hover .work-card__arrow { transform: translateX(8px); color: var(--accent); }

  /* ── CASO ─────────────────────────────────────────────────────────── */
  .case__head { padding-bottom: var(--space-xl); border-bottom: 1px solid var(--border); }
  .case__intro { max-width: var(--measure); }
  .case__title { font-size: var(--type-4xl); font-weight: 650; margin-top: var(--space-sm); }
  .case__headline { font-size: var(--type-lg); color: var(--text-muted); margin-top: var(--space-md); line-height: 1.5; }

  .case__facts {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: var(--space-md); margin-top: var(--space-xl);
  }
  .case__facts dt {
    font-family: var(--font-mono); font-size: var(--type-2xs);
    letter-spacing: var(--tracking-widest); text-transform: uppercase; color: var(--text-dim);
  }
  .case__facts dd { margin-top: var(--space-3xs); font-size: var(--type-sm); }

  .case__stack { display: flex; flex-wrap: wrap; gap: var(--space-2xs); margin-top: var(--space-lg); }
  .case__stack li {
    font-family: var(--font-mono); font-size: var(--type-2xs);
    letter-spacing: var(--tracking-wide); padding: 0.3rem 0.7rem;
    border: 1px solid var(--border); border-radius: var(--radius-pill); color: var(--text-muted);
  }

  .case__metrics {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
    gap: var(--space-lg); margin-top: var(--space-xl);
    padding-top: var(--space-lg); border-top: 1px solid var(--border);
  }
  .metric__value {
    font-family: var(--font-display); font-size: var(--type-3xl); font-weight: 650;
    line-height: 1; color: var(--accent); font-variant-numeric: tabular-nums;
  }
  .metric__label {
    font-family: var(--font-mono); font-size: var(--type-2xs);
    letter-spacing: var(--tracking-wide); color: var(--text-dim); margin-top: var(--space-2xs);
  }

  .case__body { margin-top: var(--space-2xl); }
  .case__foot { margin-top: var(--space-2xl); padding-top: var(--space-md); border-top: 1px solid var(--border); }
  .case__pager { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-md); margin-bottom: var(--space-lg); }
  .case__pager-link { display: grid; gap: var(--space-2xs); padding: var(--space-sm) 0; color: var(--text); text-decoration: none; }
  .case__pager-link--next { text-align: end; }
  .case__pager-dir { font-family: var(--font-mono); font-size: var(--type-xs); letter-spacing: var(--tracking-wide); color: var(--text-muted); }
  .case__pager-title { font-weight: 600; }
  a.case__pager-link:hover .case__pager-dir, a.case__pager-link:hover .case__pager-title { color: var(--accent); }
  .work-preview__more { margin-top: var(--space-lg); }
  .link-back { font-family: var(--font-mono); font-size: var(--type-xs); letter-spacing: var(--tracking-wide); color: var(--text-muted); }
  .link-back:hover { color: var(--accent); }

  /* ── PROSA ────────────────────────────────────────────────────────── */
  .prose { max-width: var(--measure); }
  .prose > * + * { margin-top: var(--space-md); }
  .prose h2 { font-size: var(--type-2xl); margin-top: var(--space-2xl); font-weight: 620; }
  .prose h3 { font-size: var(--type-xl); margin-top: var(--space-xl); }
  .prose p { color: var(--text-muted); }
  .prose strong { color: var(--text); font-weight: 600; }
  .prose ul, .prose ol { padding-left: var(--space-md); color: var(--text-muted); }
  .prose li + li { margin-top: var(--space-2xs); }
  .prose blockquote { padding-left: var(--space-md); border-left: 1px solid var(--accent); }
  .prose blockquote p { color: var(--text); }
  .prose table { width: 100%; border-collapse: collapse; font-size: var(--type-sm); }
  .prose :is(th, td) { text-align: left; padding: var(--space-2xs) var(--space-xs); border-bottom: 1px solid var(--border); }
  .prose td { color: var(--text-muted); }
  .prose th {
    font-family: var(--font-mono); font-size: var(--type-2xs);
    letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--text-dim);
  }
  .prose > table { display: block; overflow-x: auto; }

  .about h2 { font-size: var(--type-2xl); margin-top: var(--space-2xl); }

  /* ── DOS COLUMNAS EN PANTALLAS ANCHAS ────────────────────────────────
     Caso y "sobre mí": texto a la izquierda con su ancho de lectura y, a la
     derecha, la ficha arriba y un índice fijo que acompaña el scroll. */
  .case__panel { margin-top: var(--space-xl); }
  .case__panel .case__facts { margin-top: 0; }
  .case__layout { margin-top: var(--space-2xl); }
  .case__layout .case__body { margin-top: 0; }
  .case__aside, .about__aside { margin-top: var(--space-2xl); display: grid; gap: var(--space-md); align-content: start; }

  .toc__title {
    font-family: var(--font-mono); font-size: var(--type-2xs);
    letter-spacing: var(--tracking-widest); text-transform: uppercase; color: var(--text-dim);
  }
  .toc__list { list-style: none; padding: 0; margin-top: var(--space-xs); display: grid; gap: var(--space-3xs); border-left: 1px solid var(--border); }
  .toc__list a {
    display: block; padding: 0.35rem 0 0.35rem var(--space-sm); margin-left: -1px;
    border-left: 1px solid transparent; font-size: var(--type-sm); color: var(--text-muted); text-decoration: none;
    transition: color .2s, border-color .2s;
  }
  .toc__list a:hover { color: var(--text); }
  .toc__list a[aria-current='true'] { color: var(--accent); border-left-color: var(--accent); }
  .about__contact { list-style: none; padding: 0; display: grid; gap: var(--space-2xs); font-size: var(--type-sm); }
  .about__contact a { color: var(--text-muted); }
  .about__contact a:hover { color: var(--accent); }
  .prose h2[id] { scroll-margin-top: 6rem; }

  @media (min-width: 64rem) {
    .case__head {
      display: grid; grid-template-columns: minmax(0, 1fr) minmax(16rem, 22rem);
      column-gap: var(--space-2xl); align-items: end;
    }
    .case__intro { max-width: 52rem; }
    .case__panel {
      margin-top: 0; padding: var(--space-lg);
      border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface);
    }
    .case__panel .case__facts { grid-template-columns: 1fr; gap: var(--space-sm); }
    .case__metrics { grid-column: 1 / -1; }

    .case__layout, .about {
      display: grid; grid-template-columns: minmax(0, var(--measure)) minmax(14rem, 20rem);
      justify-content: space-between; column-gap: var(--space-2xl);
    }
    .about { max-width: var(--shell); }
    .about__main { max-width: var(--measure); }
    .case__aside, .about__aside {
      margin-top: 0; position: sticky; top: 6rem; align-self: start;
      max-height: calc(100vh - 8rem); overflow-y: auto;
    }
    .about__aside { margin-top: calc(var(--type-4xl) * 1.6); }
  }

  /* Cabecera: al hacer scroll gana fondo para que el contenido no pase por
     debajo del logo y del menú (page.js pone .is-scrolled). */
  .site-header { transition: background-color .25s, border-color .25s; border-bottom: 1px solid transparent; }
  .site-header.is-scrolled {
    mix-blend-mode: normal; color: var(--text);
    background: color-mix(in srgb, var(--bg) 88%, transparent);
    backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
    border-bottom-color: var(--border);
  }

  .contact .section-title { max-width: 15ch; }
  /* Sin esto el <h2> y el <div class="prose"> quedan pegados (el reset pone
     margin:0 en todo): cuando el título rompe a dos líneas, la segunda línea
     se monta sobre la primera línea del párrafo. */
  .contact .prose { margin-top: var(--space-lg); }
  .contact__actions { display: flex; flex-wrap: wrap; gap: var(--space-xs); margin-top: var(--space-lg); }
  .contact__location {
    margin-top: var(--space-lg);
    font-family: var(--font-mono); font-size: var(--type-2xs);
    letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--text-dim);
  }

  /* ── LAYOUT CENTRADO ─────────────────────────────────────────────────
     Antes todo cargaba a la izquierda y la mitad derecha quedaba vacía. Ahora
     la composición es simétrica: hero y encabezados centrados, cifras en una
     banda a lo ancho y los casos en una rejilla de tarjetas que ocupa todo el
     ancho. Casos y "sobre mí" conservan su texto con índice lateral. */

  /* Hero */
  .hero { justify-items: center; text-align: center; }
  .hero__copy { max-width: 72rem; width: 100%; margin-inline: auto; display: grid; justify-items: center; }
  .hero__title { justify-items: center; }
  .hero__lead { max-width: 42rem; margin-inline: auto; }
  .hero__actions { justify-content: center; }
  .hero__readout {
    width: 100%; max-width: 54rem; margin: var(--space-2xl) auto 0;
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
    padding: 0; border-block: 1px solid var(--border);
  }
  .hero__readout > div { display: grid; justify-items: center; gap: var(--space-3xs); padding: var(--space-md) var(--space-sm); }
  .hero__readout > div + div { border-left: 1px solid var(--border); }
  .hero__readout dd { font-size: var(--type-2xl); font-weight: 600; line-height: 1; color: var(--text); }
  /* Tamaños contenidos: nombre, subtítulo, texto y cifras caben en la
     primera pantalla sin que ninguno grite más que el nombre. */
  .hero__name { font-size: clamp(2.75rem, 1.2rem + 5vw, 6rem); }
  .hero__statement { font-size: var(--type-xl); }
  .hero__lead { max-width: 36rem; font-size: var(--type-lg); }
  .hero__readout { max-width: 36rem; margin-top: var(--space-xl); }
  .hero__readout > div { padding-block: var(--space-sm); }
  @media (max-width: 30rem) {
    .hero__readout > div { padding-inline: var(--space-3xs); }
    .hero__readout dt { letter-spacing: 0.04em; }
  }

  /* Encabezados de sección */
  .thesis, .section-head { text-align: center; justify-items: center; }
  .thesis { display: grid; }
  .thesis__title, .section-title { max-width: none; margin-inline: auto; }
  .thesis__title { font-size: var(--type-2xl); }
  .thesis__body { max-width: 38rem; margin-inline: auto; margin-top: var(--space-md); font-size: var(--type-xl); line-height: 1.4; color: var(--text); }
  .thesis__body p { color: inherit; }
  .thesis__body p + p { margin-top: var(--space-sm); }
  .section-head .prose { max-width: 44rem; margin-inline: auto; }

  /* Casos: rejilla de tarjetas (home y /trabajo/) */
  .work-index__title, .work-index__lead { text-align: center; margin-inline: auto; }
  .work-index__lead { max-width: 44rem; color: var(--text-muted); margin-top: var(--space-sm); }
  .work-index__list {
    display: grid; gap: var(--space-md);
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 22rem), 1fr));
    background: none; border: 0; margin-top: var(--space-xl);
  }
  .work-card { border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface); transition: border-color .25s, transform .25s; }
  .work-card:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--border)); transform: translateY(-3px); }
  .work-card::before { transform-origin: bottom; transform: scaleY(0); }
  .work-card:hover::before { transform: scaleY(1); }
  .work-card { display: flex; }
  .work-card__link { flex: 1; display: flex; flex-direction: column; gap: var(--space-md); padding: var(--space-lg); }
  .work-card__meta-top { flex-direction: row; justify-content: space-between; gap: var(--space-sm); }
  .work-card__title { font-size: var(--type-xl); line-height: 1.2; }
  .work-card:hover .work-card__title { transform: none; color: var(--accent); }
  .work-card:hover .work-card__arrow { transform: translateX(4px); }
  .work-card__headline {
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4; overflow: hidden;
  }
  .work-card__extra { margin-top: auto; height: auto; display: grid; gap: var(--space-md); padding-top: var(--space-md); border-top: 1px solid var(--border); }
  .work-card__figures { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-sm); }
  .work-card__figures > div { display: grid; gap: var(--space-3xs); align-content: start; }
  .work-card__figures b { font-size: var(--type-lg); }
  .work-card__figures span { font-size: var(--type-2xs); line-height: 1.3; }
  /* Sin cifras, la tarjeta muestra el rol a lo ancho */
  .work-card__figures > div:only-child { grid-column: 1 / -1; }
  .work-card__figures > div:only-child b { display: none; }

  /* Antes de esto: tarjetas en rejilla */
  .secondary .section-head .prose { max-width: 72rem; width: 100%; }
  .work-preview__more { text-align: center; }
  .secondary .prose ul {
    list-style: none; padding: 0; text-align: left;
    display: grid; gap: var(--space-md);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
    margin-top: var(--space-lg);
  }
  .secondary .prose li {
    margin: 0; padding: var(--space-md);
    border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface);
    color: var(--text); font-size: var(--type-sm);
  }

  /* Contacto: cierre centrado */
  .contact { text-align: center; display: grid; justify-items: center; }
  .contact .section-title { max-width: none; }
  .contact .prose { margin-inline: auto; max-width: 40rem; }
  .contact__actions { justify-content: center; }

  /* En pantallas muy anchas el contenido se centra en --shell; la cabecera
     iba pegada a los bordes y el logo no se alineaba con nada. */
  .site-header { padding-inline: max(var(--gutter), calc((100% - var(--shell)) / 2 + var(--gutter))); }
}

/* ══ MOTION ═════════════════════════════════════════════════════════════ */
@layer motion {
  /* El anillo `.cursor` de motion.js sigue al mouse con lerp — hay que
     apagar el cursor nativo o se ven los dos a la vez: la flecha real y el
     anillo con retraso detrás, como un segundo cursor suelto.
     `*` (no `html`) porque `cursor` no se hereda cuando un descendiente
     declara el suyo propio: botones y links ya traen `cursor: pointer`
     (layer `layout`/`components`), y eso gana sobre un valor heredado sin
     importar el layer. Solo apagando el cursor en cada elemento por igual
     compite de verdad — y como `motion` es el último layer (ver el orden
     arriba), gana ahí también, sin necesitar !important.
     Mismo guard que motion.js: solo cuando el anillo se activa de verdad. */
  @media (pointer: fine) and (prefers-reduced-motion: no-preference) {
    * { cursor: none; }
  }

  /* Máscara por palabra. inline-block para que el texto siga fluyendo y
     rompiendo línea con normalidad.
     El padding tiene que cubrir la descendente completa (p, q, j, g) o
     overflow:hidden les corta la cola — pasaba con "por" y "que". El margen
     negativo devuelve ese espacio para que el interlineado no cambie. */
  .ln {
    display: inline-block;
    overflow: hidden;
    vertical-align: bottom;
    /* El h1 va con interlineado 0.86, así que la caja de línea es MÁS CORTA
       que los glifos y overflow:hidden les cortaba 50px por abajo. El relleno
       cubre ascendentes y descendentes; el margen negativo devuelve el espacio
       para que el interlineado apretado se mantenga. */
    padding: 0.2em 0.04em 0.34em;
    margin: -0.2em -0.04em -0.34em;
  }
  .ln-in { display: inline-block; will-change: transform; }

  /* Estado inicial solo si JS va a animar: sin JS todo se ve completo.
     Mismo problema que en .ln: h1/h2/h3 llevan interlineado 0.98, más
     apretado que la altura real del glifo. clip-path recorta al borde de
     la propia caja — incluso ya revelado del todo (inset 0 0 0 0 sigue
     siendo el límite del elemento), así que cualquier título con
     descendente (g, j, q, y) quedaba con la cola cortada de forma
     permanente, no solo durante la animación. Pasó desapercibido mientras
     los títulos no tenían esas letras; "Systems age too" y "envejecen" sí.
     Mismo arreglo: relleno que cubre la descendente + margen negativo que
     devuelve el espacio para que el interlineado no se mueva. */
  .js [data-reveal] {
    clip-path: inset(0% 0% 100% 0%);
    padding-bottom: 0.3em;
    margin-bottom: -0.3em;
  }
  .js [data-reveal].is-in { clip-path: inset(0% 0% 0% 0%); }
  .js body.is-loading [data-hero-in] { opacity: 0; }

  @media (prefers-reduced-motion: reduce) {
    .js [data-reveal] { clip-path: none; }
    .js body.is-loading [data-hero-in] { opacity: 1; }
    .curtain { display: none; }
  }
}

/* ══ SELECTOR DE PALETA — TEMPORAL ══════════════════════════════════════
   Herramienta de decisión, no parte del diseño. Se borra junto con
   public/js/palette-picker.js cuando la paleta esté elegida. */
@layer components {
  .palette-picker {
    position: fixed;
    left: var(--space-sm);
    bottom: var(--space-sm);
    z-index: var(--layer-cursor);
    display: flex;
    align-items: center;
    gap: var(--space-xs);
    padding: var(--space-2xs) var(--space-xs);
    background: color-mix(in srgb, var(--bg) 82%, transparent);
    backdrop-filter: blur(14px);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-pill);
    font-family: var(--font-mono);
    font-size: var(--type-2xs);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--text-dim);
  }
  .palette-picker__swatches { display: flex; gap: var(--space-3xs); }
  .palette-picker button {
    width: 44px; height: 44px;
    display: grid; place-items: center;
    background: none; border: none; cursor: pointer;
    border-radius: 50%;
  }
  .palette-picker button::before {
    content: '';
    width: 18px; height: 18px;
    border-radius: 50%;
    background: var(--sw);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--sw) 40%, transparent);
    transition: transform var(--motion-quick) var(--ease-out),
                box-shadow var(--motion-quick) var(--ease-out);
  }
  .palette-picker button:hover::before { transform: scale(1.18); }
  .palette-picker button[aria-checked='true']::before {
    transform: scale(1.25);
    box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--sw);
  }
  .palette-picker__name { color: var(--text); min-width: 9ch; }

  @media (max-width: 48rem) {
    .palette-picker__label, .palette-picker__name { display: none; }
  }
}


/* Enlace a la v1 (portafolio 2022 con 3D). Deliberadamente el elemento más
   tenue del footer: es una nota al pie, no una llamada a la acción.
   Se controla con LINK_V1 en site.config.mjs. */
@layer components {
  .site-footer__v1 {
    opacity: 0.4;
    transition: opacity var(--motion-quick) var(--ease-out),
                color var(--motion-quick) var(--ease-out);
  }
  .site-footer__v1:hover { opacity: 1; color: var(--accent); }
}

/* ── Enlaces de prueba y resumen del caso ─────────────────────────────── */
@layer components {
  .case__links { display: flex; flex-wrap: wrap; gap: var(--space-sm); margin-top: var(--space-lg); }
  .case__links a {
    display: inline-flex; align-items: center; gap: 0.4em;
    font-family: var(--font-mono);
    font-size: var(--type-2xs);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--accent);
    border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
    padding-bottom: 2px;
    transition: border-color var(--motion-quick) var(--ease-out);
  }
  .case__links a:hover { border-bottom-color: var(--accent); }

  /* Lo que se lleva quien solo pasa los ojos */
  .case__summary {
    display: grid;
    gap: var(--space-md);
    margin-top: var(--space-2xl);
    padding: var(--space-lg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--surface);
  }
  @media (min-width: 52rem) {
    .case__summary { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-lg); }
  }
  .case__summary dt {
    font-family: var(--font-mono);
    font-size: var(--type-2xs);
    letter-spacing: var(--tracking-widest);
    text-transform: uppercase;
    color: var(--accent);
    margin-bottom: var(--space-2xs);
  }
  .case__summary dd { font-size: var(--type-sm); color: var(--text); line-height: 1.5; }
}

/* ── AVISO DE ATAJOS DE TECLADO ─────────────────────────────────────────
   keys.js lo crea solo donde hay atajos (casos, home, /trabajo/) y solo con
   teclado y ratón. Aparece, se va solo a los pocos segundos y se puede
   cerrar; cerrado o usado el atajo, no vuelve a salir. */
@layer components {
  .keys-hint {
    position: fixed; left: 50%; bottom: var(--space-lg); z-index: var(--layer-header);
    display: flex; align-items: center; gap: var(--space-sm);
    max-width: calc(100vw - 2 * var(--gutter));
    padding: var(--space-2xs) var(--space-2xs) var(--space-2xs) var(--space-md);
    border: 1px solid var(--border); border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--bg) 88%, transparent);
    backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
    font-size: var(--type-sm); color: var(--text-muted);
    opacity: 0; transform: translate(-50%, 12px);
    transition: opacity .3s, transform .3s;
    pointer-events: none;
  }
  .keys-hint.is-visible { opacity: 1; transform: translate(-50%, 0); pointer-events: auto; }
  .keys-hint kbd {
    display: inline-grid; place-items: center; min-width: 1.6em; height: 1.6em; padding: 0 0.35em;
    border: 1px solid var(--border); border-radius: 6px;
    font-family: var(--font-mono); font-size: var(--type-sm); line-height: 1; color: var(--text);
  }
  .keys-hint__close {
    display: grid; place-items: center; width: 2rem; height: 2rem; border-radius: 50%;
    border: 0; background: transparent; color: var(--text-muted); cursor: pointer; font-size: var(--type-lg); line-height: 1;
  }
  .keys-hint__close:hover { color: var(--text); background: var(--surface); }
  @media (prefers-reduced-motion: reduce) { .keys-hint { transition: none; } }
}
