/* Palette. */
:root {
  --ground: #faf9f7;
  --card: #ffffff;
  --ink: #18181b;
  --ink-2: rgba(24, 24, 27, 0.58);
  --ink-3: rgba(24, 24, 27, 0.42);
  --ink-body: #363338;
  --wash: rgba(24, 24, 27, 0.045);
  --line: rgba(24, 24, 27, 0.12);
  --line-strong: rgba(24, 24, 27, 0.22);
  --spark: rgba(24, 24, 27, 0.30);
  /* pale moulded plastic, lit from above */
}

:root {
  color-scheme: light;
  /* One family carries the whole page, at every size and every weight. There
     is no second face: no monospaced captions, no display cut. --display is
     kept as a name for the headline role, but it resolves to the same stack. */
  --sans: "Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  --display: "Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  /* The streak that crosses the hero title and the opportunities label: white
     core, flag colours at its shoulders. Declared once so the two cannot drift
     apart. */
  --flag-streak: linear-gradient(
    100deg,
    transparent 43%,
    rgba(0, 57, 166, 0.35) 45.5%,
    rgba(0, 57, 166, 0.95) 47.5%,
    rgba(255, 255, 255, 1) 50%,
    rgba(200, 16, 46, 0.97) 52.5%,
    rgba(200, 16, 46, 0.42) 54.5%,
    transparent 57%);
  --shell: 1120px;
  --radius: 999px;
  /* Buttons are squarer than the bar and its keys: a pill reads as a chip or
     a tag, a softened rectangle reads as something to press. */
  --radius-btn: 12px;
}

/* Lights out: everything below the bar goes dark. */
.night {
  color-scheme: dark;
  --ground: #121211;
  --card: rgba(255, 255, 255, 0.045);
  --ink: #f6f5f3;
  --ink-2: rgba(244, 243, 241, 0.60);
  --ink-3: rgba(244, 243, 241, 0.42);
  --ink-body: rgba(246, 245, 243, 0.82);
  --wash: rgba(244, 243, 241, 0.07);
  --line: rgba(244, 243, 241, 0.16);
  --line-strong: rgba(244, 243, 241, 0.34);
  --spark: rgba(255, 255, 255, 0.75);
  /* anodised black plate, brushed steel knob — the contrast between a matte
     housing and a polished part is what reads as two materials */
}

* { box-sizing: border-box; }

/* No rubber-band at either end of the page. The bounce pulled the canvas
   past the last block, and below the aurora that showed as a bare white strip. */
html,
body { overscroll-behavior-y: none; }

html {
  -webkit-text-size-adjust: 100%;
  background: var(--ground);
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.62;
  /* The column is centred in the page; the text inside it is not. Centring
     every block leaves size as the only way to rank things, and the eye has
     to hunt for the start of each line. */
  text-align: left;
  font-feature-settings: "kern" 1;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ---------- language switching ---------- */
html[lang="es"] [lang="en"],
html[lang="en"] [lang="es"] { display: none; }

/* ---------- shared elements ---------- */
a { color: inherit; }

.link {
  text-decoration: none;
  border-bottom: 1px solid var(--line-strong);
  padding-bottom: 1px;
  transition: border-color 0.15s ease;
}
.link:hover { border-color: var(--ink); }

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

.eyebrow {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin: 0;
}
/* the small capitals that name a thing: the kind of an opportunity, each
   resource filter */
.opp-kind,
.res-sel-t {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* ---------- top bar ---------- */
/* The header itself is only a rail: the bar inside it is the visible object,
   a card that floats over the page and follows the theme. */
header {
  position: sticky;
  top: 0;
  /* 50, well clear of anything else on the page. Nothing that scrolls may
     pass over the bar; when two elements tie, document order decides, and
     that is how the resource toolbar came to paint over it at 20. */
  z-index: 50;
  padding: 14px 20px 0;
  background: linear-gradient(180deg, var(--ground) 55%, transparent);
  /* leaving is slow and fades out; coming back is quick and solid */
  transition:
    transform 0.62s cubic-bezier(0.32, 0.06, 0.28, 1),
    opacity 0.52s ease;
  will-change: transform, opacity;
}
/* out of the way while you read down the page (site.js) */
header.is-hidden {
  transform: translateY(-150%);
  opacity: 0;
  transition:
    transform 0.62s cubic-bezier(0.4, 0, 0.7, 0.4),
    opacity 0.55s ease-in;
}
@media (prefers-reduced-motion: reduce) {
  header { transition: none; }
}

/* Three zones: mark, menu, language. The 1fr rails on either side keep the
   menu on the page's centre line no matter how wide the mark or the toggle get. */
.bar {
  max-width: var(--shell);
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 20px;
  /* No outline. The panel is defined by its own tone against the page, the way
     a piece of brushed acrylic is — a drawn line would flatten it back into a
     rectangle. */
  border: 0;
  /* the same 32px gutter .section-in uses, so the wordmark sits on the very
     line the headings below it start from */
  padding: 10px 32px;
  border-radius: var(--radius);
  /* Opaque. Even at 97.5% the words passing underneath stayed detectable —
     the eye picks up high-contrast edges long after the average tone has
     evened out. The backdrop-filter went with it: behind a solid fill it costs
     compositing work and changes nothing. */
  background: linear-gradient(180deg, #f2f2f4 0%, #eaeaed 52%, #e4e4e8 100%);
  box-shadow:
    /* a soft lip along the top, and the faintest rim all around — light
       catching an edge, not a line drawn on one */
    inset 0 1px 0 rgba(255, 255, 255, 0.92),
    inset 0 0 0 1px rgba(255, 255, 255, 0.5),
    inset 0 -1px 0 rgba(24, 24, 27, 0.05);
}
.night .bar {
  background: linear-gradient(180deg, #313136 0%, #28282d 52%, #212125 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.13),
    inset 0 0 0 1px rgba(255, 255, 255, 0.07),
    inset 0 -1px 0 rgba(0, 0, 0, 0.5);
}
.bar .brand { justify-self: start; }
.bar .controls { justify-self: end; }
.brand {
  display: flex;
  align-items: center;
  gap: 11px;
  text-decoration: none;
  color: var(--ink);
}
.brand svg { width: 26px; height: 26px; display: block; flex: none; }
.brand b {
  font-weight: 600;
  font-size: 15px;
  letter-spacing: 0.16em;
}
.bar nav {
  justify-self: center;
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.bar nav a {
  text-decoration: none;
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.01em;
  color: var(--ink-2);
}
/* Nav keys: a flat chip that fills on hover and inverts for the section you
   are reading. Only colours move — no bevel, nothing to glint on an edge. */
.bar nav .navlink {
  line-height: 1;
  padding: 8px 13px;
  border-radius: var(--radius);
  display: inline-block;
  white-space: nowrap;
  background: transparent;
  transition: color 0.14s ease, background-color 0.14s ease;
}
.bar nav .navlink:hover {
  color: var(--ink);
  background: var(--wash);
}
.bar nav .navlink:active,
.bar nav .navlink.is-current {
  background: var(--ink);
  color: #ffffff;
}
.night .bar nav .navlink:active,
.night .bar nav .navlink.is-current { color: #18181b; }
/* Narrow: the menu drops to its own row and scrolls sideways. Hiding it was
   fine when every key was an anchor on one page; now they are the whole site. */
@media (max-width: 980px) {
  .bar {
    grid-template-columns: 1fr auto;
    row-gap: 8px;
    /* header adds 20px of its own; 12 more lands the wordmark on the 32px
       gutter the content below uses */
    padding: 10px 12px;
  }
  .bar nav {
    grid-column: 1 / -1;
    grid-row: 2;
    justify-self: stretch;
    justify-content: flex-start;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .bar nav::-webkit-scrollbar { display: none; }
  .bar .controls { justify-self: end; }
}

/* ---------- teléfono: botón de menú y panel de navegación ---------- */
/* Ambos existen en todas las páginas pero sólo se muestran bajo 620px. En
   computador la barra no cambia en nada. */
.navtoggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: var(--radius);
  background: transparent;
  color: var(--ink);
  cursor: pointer;
}
.navtoggle-bars {
  display: grid;
  gap: 6px;
  width: 20px;
}
.navtoggle-bars span {
  display: block;
  height: 1.6px;
  border-radius: 2px;
  background: currentColor;
}

.navpanel {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 60;
  flex-direction: column;
  padding: 18px 24px 40px;
  background: var(--ground);
  overflow-y: auto;
}
.navpanel-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.navpanel-top b {
  font-family: var(--display);
  font-weight: 600;
  letter-spacing: 0.18em;
  font-size: 14px;
}
.navpanel-close {
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
}
.navpanel-close svg { width: 24px; height: 24px; }
/* los enlaces ocupan el centro de la pantalla, uno por línea */
.navpanel-links {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
}
.navpanel-links a {
  padding: 10px 8px;
  font-family: var(--display);
  font-size: 30px;
  line-height: 1.25;
  font-weight: 500;
  text-decoration: none;
  color: var(--ink);
}
.navpanel-links a.is-current { color: var(--ink-2); }
/* el idioma vive aquí abajo, ya no en la barra */
.navpanel > .toggle { align-self: center; }

/* mientras el panel está abierto la página de atrás no se desplaza */
.nav-open, .nav-open body { overflow: hidden; }

.controls { display: flex; align-items: center; gap: 8px; }
.toggle {
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.02em;
  background: transparent;
  border: 1px solid var(--line);
  color: var(--ink-2);
  padding: 7px 11px;
  border-radius: var(--radius);
  cursor: pointer;
  line-height: 1;
  transition: color 0.14s ease, background-color 0.14s ease, border-color 0.14s ease;
}
.toggle:hover { color: var(--ink); background: var(--wash); border-color: var(--line-strong); }
.toggle b { color: var(--ink); font-weight: 500; }

/* The light control: one icon button beside the language one. It shows what a
   press will do — a moon on the pale theme, a sun on the dark one — rather
   than what the page currently is. */
.toggle.icon {
  display: grid;
  place-items: center;
  padding: 0;
  width: 32px;
  height: 32px;
  border-radius: 999px;
}
.toggle.icon svg {
  grid-area: 1 / 1;
  width: 15px;
  height: 15px;
  display: block;
  transition: opacity 0.18s ease, transform 0.28s cubic-bezier(0.2, 0.8, 0.3, 1);
}
/* whichever icon is not in play is turned away rather than simply hidden, so
   the two trade places instead of blinking */
.ic-sun { opacity: 0; transform: rotate(-70deg) scale(0.6); }
.night .ic-moon { opacity: 0; transform: rotate(70deg) scale(0.6); }
.night .ic-sun { opacity: 1; transform: none; }
.ic-moon { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .toggle.icon svg { transition: none; }
}
@media (pointer: coarse) {
  .toggle.icon { width: 44px; height: 44px; }
}

/* Three tiers of action, told apart by fill rather than by size:
     .button    the one thing to do: solid ink, with an arrow
     .ghost     a real alternative: outlined, no fill of its own
     .textlink  a way onward: just words and an arrow
   The first two share a height and a shape so a row of them lines up.

   Primary: graphite block, one hairline of light along the top edge, nothing
   along the bottom. The cap lives in a variable so the contact spark can add
   its glow without wiping it. */
.button {
  /* .button dresses both <a> and <button>. An anchor inherits the page font;
     a button does not, and falls back to the browser's own. */
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.005em;
  line-height: 1;
  text-decoration: none;
  background: var(--ink);
  color: var(--ground);
  border: 0;
  cursor: pointer;
  min-height: 44px;
  padding: 0 20px;
  border-radius: var(--radius-btn);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  white-space: nowrap;
  --cap:
    inset 0 1px 0 rgba(255, 255, 255, 0.16),
    0 1px 2px rgba(24, 24, 27, 0.14),
    0 6px 16px -8px rgba(24, 24, 27, 0.45);
  box-shadow: var(--cap);
  transition: background-color 0.16s ease, transform 0.16s ease;
}
.arr {
  width: 16px;
  height: 16px;
  flex: none;
  transition: transform 0.18s ease;
}
.button:hover .arr,
.textlink:hover .arr { transform: translateX(3px); }
@media (prefers-reduced-motion: reduce) {
  .arr { transition: none; }
}
/* The hover shade is mixed from the palette rather than written out, so it
   follows wherever the palette goes. Hard-coded #ffffff broke inside
   #contacto, which inverts --ink and --ground on purpose: in night mode the
   button turned white while its text stayed var(--ground), also white. Mixing
   toward --ground keeps the background moving away from the text colour, in
   whichever direction that block happens to be pointing. */
.button:hover {
  background: color-mix(in oklab, var(--ink) 86%, var(--ground));
  transform: translateY(-1px);
}
.button:active { transform: translateY(0); }
.button.spark { animation: spark 0.62s ease-out; }
@keyframes spark {
  0%   { box-shadow: var(--cap), 0 0 0 0 rgba(255, 255, 255, 0); }
  22%  { box-shadow: var(--cap), 0 0 30px 9px var(--spark); }
  100% { box-shadow: var(--cap), 0 0 0 0 rgba(255, 255, 255, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .button.spark { animation: none; }
}

/* ---------- hero ---------- */
.hero {
  position: relative;
  isolation: isolate;
}
.hero-in {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 96px 32px 84px;
}
/* the whole hero column reads centred, over the board below it */
.hero .col {
  position: relative;
  z-index: 2;
  max-width: 720px;
  margin: 0 auto;
  text-align: center;
}
.hero h1 {
  position: relative;
  width: fit-content;
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(3.2rem, 6vw, 5.5rem);
  line-height: 1;
  letter-spacing: -0.035em;
  margin: 0 auto;
  max-width: 16ch;
  text-wrap: balance;
}
/* A single sweep of light across the title when the page opens, carrying the
   colours of the flag. The band is a gradient clipped to the letters by a copy
   of the text laid exactly over them; when the animation ends the band has
   travelled past the right edge, so nothing of it remains. The opportunities
   label carries the same sweep, so what the two share is written once. */
.hero h1::after,
.opps-label::after {
  position: absolute;
  color: transparent;
  pointer-events: none;
  background-repeat: no-repeat;
  /* A percentage here aligns that point of the image with the same point of
     the box, so with an image wider than the box the scale runs backwards:
     -70% parks the bright band off the right edge, 170% parks it off the left. */
  background-position: -70% 0;
  background-image: var(--flag-streak);
  -webkit-background-clip: text;
  background-clip: text;
}
.hero h1::after {
  content: attr(data-text);
  top: 0;
  left: 0;
  right: 0;
  /* A background is only painted inside the box. With line-height 1 the tail
     of the "y" hangs below the box, so it came out unlit. The bottom edge is
     left free (bottom: auto) — pinning it to the container would make the
     padding grow inwards instead of downwards — and the padding then extends
     the painted area well past the deepest descender. */
  bottom: auto;
  padding-bottom: 0.6em;
  box-sizing: content-box;
  background-size: 260% 100%;
}
@media (prefers-reduced-motion: no-preference) {
  .hero h1.sweep::after,
  .opps-label.sweep::after {
    /* ease-in-out: the streak slows down while it is crossing the letters,
       which is the only part anyone sees */
    animation: flag-sweep 1.9s cubic-bezier(0.35, 0.08, 0.5, 0.96) 0.7s both;
  }
  @keyframes flag-sweep {
    /* the letters sit between about 75% and 25%, so the travel is kept close
       to that: almost the whole animation happens on top of the title */
    from { background-position: 92% 0; }
    to   { background-position: 8% 0; }
  }
}

.hero p {
  margin: 26px auto 0;
  max-width: 54ch;
  font-size: 18px;
  line-height: 1.7;
  color: var(--ink-body);
  text-wrap: pretty;
}
.actions {
  margin-top: 38px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 12px;
}
/* the text link gets a little more room: it has no box to separate it. Even
   on both sides, so it stays centred when it wraps to its own line. */
.actions .textlink { padding: 0 6px; }

/* Secondary: the primary's shape drawn in outline on the page itself, so the
   solid one is the only filled thing in the row. */
.ghost {
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.005em;
  line-height: 1;
  text-decoration: none;
  min-height: 44px;
  padding: 0 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-btn);
  color: var(--ink);
  background: transparent;
  transition: color 0.14s ease, background-color 0.14s ease, border-color 0.14s ease;
}
.ghost:hover {
  border-color: var(--ink);
  /* a tint of the text colour, so it darkens a pale block and lightens a
     dark one without needing a rule per theme */
  background: color-mix(in oklab, var(--ink) 5%, transparent);
}

/* Tertiary: no box at all, the arrow says it goes somewhere. */
.textlink {
  font-size: 14px;
  font-weight: 500;
  line-height: 1;
  text-decoration: none;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  white-space: nowrap;
  color: var(--ink-2);
  transition: color 0.14s ease;
}
.textlink:hover { color: var(--ink); }

/* Dot-matrix reveal behind the hero (dot-reveal.js). Night only: on the light
   page the dots would be soot on paper. The mask keeps them around the pointer. */
.hero-dots {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* the bottom of the hero's stacking context: the words sit at 2, the board
     at 2, so nothing the dots do can land on top of them */
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.4s ease;
  -webkit-mask-image: radial-gradient(420px at var(--mx, 50%) var(--my, 45%), #000 0%, rgba(0, 0, 0, 0.5) 45%, transparent 76%);
  mask-image: radial-gradient(420px at var(--mx, 50%) var(--my, 45%), #000 0%, rgba(0, 0, 0, 0.5) 45%, transparent 76%);
}
.hero-dots.is-on { opacity: 1; }

/* A scrim between the dots and the words. It keeps the grain from crowding the
   headline while leaving it visible out at the edges — the "cute fade" the
   original drew with a masked black panel. */
.night .hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(660px 380px at 50% 34%, rgba(18, 18, 17, 0.92), rgba(18, 18, 17, 0.55) 55%, transparent 78%);
}
:root:not(.night) .hero-dots { display: none; }

/* ---------- page header (inner pages) ---------- */
/* The home page opens with the hero; every other page opens with this. */
.page-head .section-in { padding: 34px 32px 6px; }
.page-head h1 {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(2.1rem, 4.4vw, 3.3rem);
  line-height: 1.04;
  letter-spacing: -0.032em;
  margin: 12px 0 0;
  max-width: 18ch;
  text-wrap: balance;
}
.page-head .lede { margin-top: 28px; }
.page-head + section .section-in { padding-top: 34px; }

/* ---------- sections ---------- */
main { border-top: 1px solid var(--line); }
/* no rule under the floating bar when a page opens with its own header */
.is-page main { border-top: 0; }

section {
  border-bottom: 1px solid var(--line);
  /* clears the floating bar when a link jumps straight to a section */
  scroll-margin-top: 96px;
}
.section-in {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 60px 32px 64px;
  display: grid;
  /* minmax(0,…): a 1fr track's own minimum is its content, which let the wide
     content stretch the section instead of staying inside it */
  grid-template-columns: minmax(0, 1fr);
  gap: 16px;
}
.section-in h2 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 28px;
  line-height: 1.18;
  letter-spacing: -0.022em;
  margin: 10px 0 0;
  text-wrap: balance;
}
/* The reading column is capped at 62ch but sits flush against the shell's
   gutter, not centred inside it. Centred, it started 140px further right than
   any block that spans the full width — a card grid, a table, the resource
   list — and every page showed two different left edges. */
.body { width: 100%; max-width: 62ch; margin: 0; }
.lede {
  max-width: 58ch;
  font-size: 18px;
  color: var(--ink-2);
}
@media (max-width: 780px) {
  .section-in { padding: 44px 32px 48px; }
}

/* ---------- definition rows as cards, side by side ---------- */
#actividades .body,
#quienes .body,
#trabajo .body { max-width: none; }
.rows.as-cards {
  margin-top: 30px;
  border-top: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(228px, 1fr));
  gap: 16px;
}
/* Las tarjetas van invertidas respecto a la página: negras con letra clara de
   día, claras con letra negra de noche. --ink y --ground ya se intercambian
   entre los dos temas, así que basta con cruzarlos aquí y funciona en ambos. */
.rows.as-cards .row {
  display: block;
  padding: 30px 26px;
  border: 1px solid transparent;
  border-radius: 18px;
  background: var(--ink);
  color: var(--ground);
  box-shadow: 0 1px 2px rgba(24, 24, 27, 0.04);
}
/* Los tonos de texto heredan del color de la tarjeta: --ink-2 y --ink-3 son
   tintas translúcidas pensadas para el fondo de la página y aquí serían
   ilegibles. Los márgenes dan más aire entre el rótulo, el título y el texto. */
.rows.as-cards .row dt { color: inherit; opacity: 0.66; margin-bottom: 10px; }
.rows.as-cards .row .rd { color: inherit; opacity: 0.78; margin-top: 14px; line-height: 1.62; }

/* ---------- definition rows ---------- */
.rows {
  margin: 34px 0 0;
  border-top: 1px solid var(--line);
}
.row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 6px;
  padding: 20px 0;
  border-bottom: 1px solid var(--line);
}
.row dt {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
  padding-top: 2px;
}
.row dd {
  /* dd carries a 40px indent from the browser; without this reset the value
     no longer lines up with its own label */
  margin: 0;
  max-width: 54ch;
}
.row dd .rt {
  font-family: var(--display);
  font-weight: 600;
  font-size: 18px;
  display: block;
  margin-bottom: 4px;
}
.row dd .rd { color: var(--ink-2); }


/* ---------- research list ---------- */
ul.focus {
  margin: 30px 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--line);
}
ul.focus li {
  padding: 15px 0;
  border-bottom: 1px solid var(--line);
  max-width: 60ch;
}

/* ---------- contact: a lit block in its own palette ---------- */
/* The block inverts the page: black on the light theme, white on the dark one. */
#contacto:not(.plain) {
  --ground: #18181b;
  --ink: #f1f1f1;
  --ink-2: rgba(241, 241, 241, 0.68);
  --ink-3: rgba(241, 241, 241, 0.50);
  --line: rgba(241, 241, 241, 0.20);
  --line-strong: rgba(241, 241, 241, 0.38);
  --spark: rgba(255, 255, 255, 0.85);
  background: var(--ground);
  color: var(--ink);
}
.night #contacto:not(.plain) {
  --ground: #faf9f7;
  --ink: #18181b;
  --ink-2: rgba(11, 11, 11, 0.62);
  --ink-3: rgba(11, 11, 11, 0.45);
  --line: rgba(11, 11, 11, 0.16);
  --line-strong: rgba(11, 11, 11, 0.32);
  --spark: rgba(11, 11, 11, 0.35);
  background: var(--ground);
  color: var(--ink);
}

#contacto.plain {
  border-top: 1px solid var(--line);
  /* Sin la inversión, este bloque pasa a leerse sobre el fondo de la página,
     que es menos contrastado que el rectángulo oscuro: las etiquetas de los
     campos caían a 4.28 y la nota al pie a 2.65, bajo el mínimo AA de 4.5.
     Los dos tonos se refuerzan aquí, derivados de --ink para que sigan
     funcionando en claro y en oscuro. */
  --ink-2: color-mix(in oklab, var(--ink) 70%, transparent);
  --ink-3: color-mix(in oklab, var(--ink) 66%, transparent);
}

.contact-grid { margin-top: 30px; }
.contact-grid .big {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(22px, 4vw, 30px);
  line-height: 1.22;
  letter-spacing: -0.025em;
  margin: 0 0 22px;
  max-width: 20ch;
  text-wrap: balance;
}
/* ---------- the form that unfolds under the contact button ---------- */
.write-wrap { overflow: hidden; }
.write {
  max-width: 460px;
  margin: 26px 0 0;
  display: grid;
  gap: 16px;
}
.write label { display: grid; gap: 7px; }
.wl {
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--ink-2);
}
.write input,
.write textarea {
  font: inherit;
  font-size: 15px;
  color: var(--ink);
  background: color-mix(in oklab, var(--ink) 6%, transparent);
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  padding: 11px 13px;
  width: 100%;
  transition: border-color 0.14s ease, background-color 0.14s ease;
}
.write textarea { resize: vertical; line-height: 1.55; }
.write input:focus,
.write textarea:focus {
  outline: none;
  border-color: var(--ink);
  background: color-mix(in oklab, var(--ink) 11%, transparent);
}
/* the browser's own "required" styling only after a submit attempt */
.write input:user-invalid,
.write textarea:user-invalid { border-color: #e2645a; }
.write .button { justify-self: start; margin-top: 2px; }
.write-note {
  margin: 0;
  font-size: 11px;
  line-height: 1.55;
  letter-spacing: 0.02em;
  color: var(--ink-3);
}
.write-note.is-sent { color: var(--ink-2); }

/* ---------- open opportunities: a strip between the bar and the title ---------- */
/* A gap under the bar so the two read as two objects, not one stack. The
   strip keeps the page's own palette: inverted to a black band it swallowed a
   third of the first screen and fought the bar instead of sitting under it. */
.opps { margin-top: 14px; border-bottom: 1px solid var(--line); }
.opps .section-in { padding: 14px 32px; gap: 0; }
.opps-in {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 20px;
}
/* The same streak that crosses the hero title, at 11px. The size stays put:
   the glint is the point, not the type. */
.opps-label {
  position: relative;
  margin: 0;
  white-space: nowrap;
  width: fit-content;
  /* the same filled pill the current nav key wears, down to its height: the
     eyebrow's inherited line-height made it 3px taller than the key above */
  line-height: 1;
  padding: 9px 13px;
  border-radius: var(--radius);
  background: var(--ink);
  /* the ground, never a fixed white: this keeps holding if the strip is ever
     placed inside a block that flips the palette */
  color: var(--ground);
}
.opps-label::after {
  inset: 0;
  /* inset resolves against the padding box, so without the same padding the
     streak would start at the pill's edge and sit 13px left of the letters */
  padding: 9px 13px;
  /* narrower than the hero's 260%: on a 150px label a streak that wide would
     cover the whole word at once instead of travelling across it */
  background-size: 420% 100%;
}
/* An ::after cannot hold two languages, so it reads the one in play. The
   second content line gives the copy an empty alt text, so a screen reader
   does not read the label twice; a browser that does not know that form drops
   the line and keeps the first, streak included. */
html[lang="es"] .opps-label::after { content: attr(data-es); content: attr(data-es) / ""; }
html[lang="en"] .opps-label::after { content: attr(data-en); content: attr(data-en) / ""; }

/* one card on stage; the rest wait off to the right */
.opps-view { overflow: hidden; }
.opps-track {
  display: flex;
  margin: 0;
  padding: 0;
  list-style: none;
  transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
.opp {
  flex: 0 0 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 14px;
  min-width: 0;
}
.opp-t { font-size: 15px; font-weight: 600; }
.opp-when { font-size: 13px; color: var(--ink-2); }

.opps-nav { display: flex; align-items: center; gap: 4px; }
.opp-arrow {
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  padding: 0;
  color: var(--ink-3);
  background: transparent;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
}
.opp-arrow:hover { color: var(--ink); background: var(--wash); }
.opp-arrow svg { width: 15px; height: 15px; }
.opp-arrow[data-dir="-1"] svg { transform: rotate(180deg); }
.opp-dots { font-size: 11px; color: var(--ink-3); min-width: 26px; text-align: center; }

@media (prefers-reduced-motion: reduce) {
  .opps-track { transition: none; }
}
@media (max-width: 860px) {
  /* the label is the first thing to go: the card itself is the only part
     carrying information */
  .opps-in { grid-template-columns: minmax(0, 1fr) auto; gap: 12px; }
  .opps-label { display: none; }
}

/* ---------- resource index: search, filter, read ---------- */
.res-wrap { border-bottom: 1px solid var(--line); }
/* This page stacks two .section-in blocks, and their generic padding piled up
   into 130px of dead air between the lede and the search box, and 112px more
   before the first heading. Both are set explicitly here instead. */
.res-wrap > .section-in:first-child { padding-top: 26px; padding-bottom: 0; }
.res-body { padding-top: 44px; padding-bottom: 76px; }
.res-wrap .body { max-width: none; }
.res[hidden] { display: none; }

.res-tools { display: grid; gap: 12px; }
.res-find {
  position: relative;
  display: flex;
  align-items: center;
}
.res-ico {
  position: absolute;
  left: 16px;
  width: 17px;
  height: 17px;
  color: var(--ink-3);
  pointer-events: none;
}
.res-find input {
  font: inherit;
  font-size: 15px;
  width: 100%;
  padding: 13px 44px;
  color: var(--ink);
  background: color-mix(in oklab, var(--ink) 5%, transparent);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: background 0.14s ease, border-color 0.14s ease;
}
.res-find input::placeholder { color: var(--ink-3); }
.res-find input:focus {
  outline: none;
  border-color: var(--line-strong);
  background: color-mix(in oklab, var(--ink) 8%, transparent);
}
/* the browser's own clear affordance is inconsistent between engines */
.res-find input::-webkit-search-decoration,
.res-find input::-webkit-search-cancel-button { -webkit-appearance: none; }
.res-clear {
  position: absolute;
  right: 8px;
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  font: inherit;
  font-size: 18px;
  line-height: 1;
  color: var(--ink-3);
  background: transparent;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
}
.res-clear:hover { color: var(--ink); background: var(--wash); }
/* display: grid beats the browser's own [hidden] { display: none }, so the
   cross stayed on screen with nothing to clear */
.res-clear[hidden] { display: none; }

.res-selects { display: flex; flex-wrap: wrap; gap: 8px; }
.res-sel { position: relative; display: flex; align-items: center; gap: 8px; }
/* native selects: they carry their own keyboard handling and, on a phone, the
   system picker. A custom dropdown here would be worse in every way. */
.res-sel select {
  font: inherit;
  font-size: 13px;
  appearance: none;
  padding: 10px 34px 10px 14px;
  color: var(--ink-2);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
}
.res-sel select:hover { color: var(--ink); border-color: var(--line-strong); }
.res-sel select:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.res-sel::after {
  content: "";
  position: absolute;
  right: 14px;
  top: 50%;
  width: 7px;
  height: 7px;
  margin-top: -5px;
  border-right: 1.5px solid var(--ink-3);
  border-bottom: 1.5px solid var(--ink-3);
  transform: rotate(45deg);
  pointer-events: none;
}
.res-status { margin: 0; font-size: 11px; color: color-mix(in oklab, var(--ink) 63%, transparent); }

.res-area { border: 0; padding: 0; margin: 0 0 8px; }
.res-area:first-child > h2 { margin-top: 0; }
.res-area > h2 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 28px;
  letter-spacing: -0.022em;
  margin: 34px 0 0;
}
.res-area > .lede { margin-top: 8px; }
.res-group { margin-top: 28px; }
.res-group h3 {
  margin: 0;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: color-mix(in oklab, var(--ink) 72%, transparent);
}
.res-count {
  margin-left: 8px;
  font-weight: 400;
  letter-spacing: 0;
  color: color-mix(in oklab, var(--ink) 63%, transparent);
}
/* the brackets live in CSS so the script can keep writing just the number */
.res-count::before { content: "("; }
.res-count::after { content: ")"; }
.res-list { margin: 12px 0 0; padding: 0; list-style: none; border-top: 1px solid var(--line); }
.res {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 4px 20px;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
}
.res-name {
  font-size: 15px;
  line-height: 1.45;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  flex: 1 1 20rem;
}
.res-name:hover { border-bottom-color: var(--ink); }
.res-meta { display: flex; align-items: baseline; gap: 12px; flex: none; }
/* 42% ink measured 2.67:1 at this size — the domain is secondary, not invisible */
.res-dom { font-size: 11px; color: color-mix(in oklab, var(--ink) 63%, transparent); }
/* One tone for the three levels. The ramp that used to run from 42% to full
   ink could not be both subtle and legible at 11px: its top read as bold on a
   dark page, and its bottom measured 2.67:1, well under AA. The word already
   says which level it is; the colour was only decorating that. */
.res-lvl { font-size: 11px; font-weight: 400; letter-spacing: 0.06em; text-transform: uppercase; white-space: nowrap; color: color-mix(in oklab, var(--ink) 72%, transparent); }

@media (max-width: 700px) {
  .res-meta { width: 100%; justify-content: flex-start; }
}

/* the note that follows the pointer over a resource */
.res-tip {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 40;
  width: max-content;
  max-width: 340px;
  padding: 12px 14px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-2);
  /* opaque on purpose, not var(--card): that token is a 4.5% white tint at
     night, which would leave the note see-through over the list it explains.
     It floats at body level, so it never inherits a flipped palette. */
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 14px;
  box-shadow: 0 14px 34px -18px rgba(24, 24, 27, 0.5);
  /* it must never sit under its own pointer, or hovering it would flicker */
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.12s ease;
}
.res-tip.is-on { opacity: 1; }
.night .res-tip { background: #202024; }
@media (prefers-reduced-motion: reduce) {
  .res-tip { transition: none; }
}

/* ---------- founding team: three portraits in a row ---------- */
.people {
  margin-top: 34px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 22px;
}
/* advisory board: the names run as one horizontal line and wrap only when the
   screen is too narrow to hold them */
.advisors {
  display: flex;
  flex-wrap: wrap;
  column-gap: 32px;
  row-gap: 6px;
}
.advisor { white-space: nowrap; }

.person { margin: 0; }
/* every photograph is cropped square at the source, so one ratio holds the row
   even while the files load */
.person img {
  width: 100%;
  aspect-ratio: 1;
  height: auto;
  display: block;
  object-fit: cover;
  border-radius: 20px;
  background: var(--wash);
}
.person figcaption { margin-top: 14px; }
/* the founder's name links to their LinkedIn profile: no underline at rest so
   the caption stays as quiet as before, underline on hover and focus */
.pn-link {
  color: inherit;
  text-decoration-line: none;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.pn-link:hover,
.pn-link:focus-visible,
.person:hover .pn-link { text-decoration-line: underline; }
.pn {
  display: block;
  font-family: var(--display);
  font-weight: 600;
  font-size: 18px;
  line-height: 1.25;
}
.pr {
  display: block;
  margin-top: 4px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
}
@media (max-width: 760px) {
  .people { grid-template-columns: 1fr; gap: 26px; max-width: 360px; margin-inline: auto; }
}

/* the block is placeholder and says so */
.pending {
  margin: 14px 0 0;
  max-width: 62ch;
  padding: 8px 14px;
  display: inline-block;
  border: 1px dashed var(--line-strong);
  border-radius: 12px;
  font-size: 11px;
  line-height: 1.5;
  color: var(--ink-3);
}

/* ---------- footer ---------- */
footer {
  border-top: 1px solid var(--line);
  margin-top: 0;
}
/* The mark, two groups of pages and the contact column. The groups follow the
   way the site is read (what you can learn here / who CHAIS is), so the
   footer is the same map in short instead of one long list. Flush left, like
   the rest of the page. */
.foot-in {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 46px 32px 34px;
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1fr;
  gap: 32px 40px;
  align-items: start;
}
.foot-brand p {
  margin: 14px 0 0;
  max-width: 34ch;
  font-size: 13px;
  line-height: 1.6;
  color: var(--ink-2);
}
.foot-col .eyebrow { margin-bottom: 14px; }
.foot-col ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 9px;
  font-size: 13.5px;
  color: var(--ink-2);
}
.foot-col a {
  text-decoration: none;
  transition: color 0.14s ease;
}
.foot-col a:hover { color: var(--ink); }
/* Phone: the mark on its own row, the two page groups side by side, contact
   underneath. */
@media (max-width: 760px) {
  .foot-in {
    grid-template-columns: 1fr 1fr;
    gap: 34px 16px;
    padding: 38px 24px 26px;
  }
  .foot-brand,
  .foot-in > .foot-col:last-child { grid-column: 1 / -1; }
}
.foot-base {
  max-width: var(--shell);
  margin: 0 auto;
  padding: 16px 32px 44px;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  text-align: center;
  gap: 6px 24px;
  font-size: 11px;
  letter-spacing: 0.03em;
  color: var(--ink-3);
}
.foot-base .foot-note { color: var(--ink-3); }

/* ---------- load-in: only after JS confirms the page is at rest ---------- */
@media (prefers-reduced-motion: no-preference) {
  .ready .fx {
    animation: rise 0.6s cubic-bezier(0.2, 0.7, 0.2, 1) both;
  }
  .ready .fx:nth-child(2) { animation-delay: 0.06s; }
  .ready .fx:nth-child(3) { animation-delay: 0.12s; }
  .ready .fx:nth-child(4) { animation-delay: 0.16s; }
  @keyframes rise {
    from { opacity: 0.001; transform: translateY(7px); }
    to { opacity: 1; transform: none; }
  }
}

/* ---------- split-flap board ---------- */
.board {
  --flap-face: #e4e1db;
  --flap-leaf: #f0eee9;
  --flap-ink: #18181b;
  --flap-edge: rgba(24, 24, 27, 0.16);
  /* the tile a flap flashes while it spins: the page's own ink */
  --flap-accent: #18181b;
  --flap-accent-ink: #faf9f7;
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: 720px;
  margin: 48px auto 0;
  padding: 10px;
  border-radius: 14px;
  background: #cdc9c1;
  box-shadow:
    0 2px 4px -2px rgba(11, 11, 11, 0.18),
    0 34px 70px -38px rgba(11, 11, 11, 0.55);
}
.night .board {
  --flap-face: #1e1e22;
  --flap-leaf: #2a2a30;
  --flap-ink: #f6f5f3;
  --flap-edge: rgba(0, 0, 0, 0.8);
  --flap-accent: #f6f5f3;
  --flap-accent-ink: #18181b;
  background: #141417;
  box-shadow:
    0 2px 4px -2px rgba(0, 0, 0, 0.6),
    0 34px 70px -38px rgba(0, 0, 0, 0.85);
}
.board-grid {
  display: grid;
  grid-template-columns: repeat(22, 1fr);
  gap: 2px;
}

.flap {
  position: relative;
  aspect-ratio: 1 / 2;
  border-radius: 2px;
  background: var(--flap-face);
  border: 1px solid var(--flap-edge);
  perspective: 260px;
  overflow: hidden;
}
.flap-half,
.flap-leaf {
  position: absolute;
  left: 0;
  right: 0;
  height: calc(50% - 0.5px);
  overflow: hidden;
  background: var(--flap-face);
}
.flap-half.top { top: 0; }
.flap-half.bottom { bottom: 0; }

/* each half shows a double-height line of text, clipped to its own side, so the
   character reads as one glyph split across the seam */
.flap-half span,
.flap-leaf span {
  position: absolute;
  left: 0;
  right: 0;
  height: 200%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-size: clamp(6px, 1.55vw, 19px);
  line-height: 1;
  color: var(--flap-ink);
}
.flap-half.top span,
.flap-leaf.front span { top: 0; }
.flap-half.bottom span,
.flap-leaf.back span { bottom: 0; }

.flap-leaf {
  z-index: 2;
  background: var(--flap-leaf);
  backface-visibility: hidden;
}
.flap-leaf.front { top: 0; transform-origin: bottom; }
.flap-leaf.back { bottom: 0; transform-origin: top; transform: rotateX(90deg); }

.flap-split {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 1px;
  transform: translateY(-0.5px);
  background: rgba(0, 0, 0, 0.32);
  z-index: 3;
}
.flap-stripes {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 14%;
  z-index: 3;
  opacity: 0.16;
  background: repeating-linear-gradient(
    to bottom, currentColor 0 1px, transparent 1px 3px);
}

@media (max-width: 620px) {
  /* el tablero no se muestra en teléfono: ocupaba una pantalla entera y no
     aporta nada a esa altura. En computador sigue igual. */
  .board { display: none; }

  /* ---- teléfono: la barra se reduce a CHAIS, el menú y la luz ---- */
  /* los enlaces se van al panel; la marca pierde el símbolo y deja la palabra */
  .bar nav { display: none; }
  .bar .brand svg { display: none; }
  .bar #lang { display: none; }
  .navtoggle { display: inline-flex; }
  .navpanel:not([hidden]) { display: flex; }
  .bar { grid-template-columns: auto 1fr; }

  /* ---- teléfono: toda la tipografía baja un escalón ----
     Las medidas estaban pensadas para una pantalla ancha y en un teléfono
     resultaban grandes. Cada tamaño baja entre un 10 y un 25%, más cuanto
     mayor era: los titulares son los que peor sientan a esta anchura, y el
     texto corrido apenas se mueve para no perder legibilidad. */
  .hero h1 { font-size: 38px; }
  .page-head h1 { font-size: 27px; }
  .section-in h2, .res-area > h2 { font-size: 22px; }
  .lede { font-size: 16px; }
  .contact-grid .big { font-size: 19px; }
  body { font-size: 14px; }
  .navpanel-links a { font-size: 26px; }

  /* ---- teléfono: la franja de oportunidades ----
     Las tres partes de cada oportunidad se repartían por la línea y envolvían
     de forma distinta según lo largo que fuera el título, así que al rotar
     parecía descuadrada. Aquí van una bajo otra, siempre en el mismo orden y
     en la misma posición, con un alto fijo para que la franja no salte al
     cambiar de oportunidad. */
  .opp {
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    min-height: 56px;
    /* arriba, no centrado: si una oportunidad ocupa una línea más que otra,
       centrar movería también las dos primeras y se vería el salto */
    justify-content: flex-start;
  }
  .opp-kind { font-size: 9.5px; }
  /* el título reserva dos líneas aunque use una: así la tercera línea cae
     siempre a la misma altura, llame como se llame la oportunidad */
  .opp-t { font-size: 13px; line-height: 1.3; min-height: calc(13px * 1.3 * 2); }
  .opp-when { font-size: 11px; }
  .opp-dots { font-size: 10px; }
}

/* ---------- phones: hit areas ---------- */
/* Scoped to coarse pointers. With a mouse these controls are already
   comfortable and the bar was tuned at that size; a fingertip needs about
   44px (WCAG 2.2 Target Size). Everywhere possible the visible shape keeps
   its dimensions and only the padding around it grows, so nothing looks
   inflated on a phone. */
@media (pointer: coarse) {
  .bar nav .navlink { padding-top: 16px; padding-bottom: 16px; }
  .bar .brand { padding: 9px 0; }
  /* .button, .ghost and .textlink already stand 44px tall on their own */
  /* :not(.icon) — the icon button is a fixed square, it grows by size */
  .controls .toggle:not(.icon) { padding: 15px 12px; }
  /* the opportunity arrows are drawn circles: they grow rather than gain padding */
  .opp-arrow { width: 44px; height: 44px; }

  /* the light switch is a drawn object: it keeps its size and gains an
     invisible margin of hit area around it */

  /* footer: the gap between rows moves into the links themselves, so the
     column reads the same and each line is tappable end to end */
  .foot-col ul { gap: 0; }
  .foot-col li a {
    display: inline-block;
    padding: 14px 0;
    line-height: 1.15;
  }
  .foot-brand .brand { padding: 11px 0; }
  .foot-brand .brand { display: inline-flex; }
}

/* The nav row scrolls sideways under 980px. A fade on the trailing edge is
   what tells a reader there is more beyond it; without it the last key looks
   like the last key. Masked on the inline-end side only. */
@media (max-width: 980px) {
  .bar nav {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%);
    mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%);
  }
  /* once it is scrolled to the end there is nothing left to hint at */
  .bar nav.at-end {
    -webkit-mask-image: none;
    mask-image: none;
  }
}

/* ---------- aurora (under every footer) ---------- */
/* A dome of Chilean colour rising under the footer: deep navy at its core,
   then the flag's blue, a white ring, the red, and nothing.

   It is one radial gradient anchored at the bottom centre, not a stack of
   blurred shapes. Its last stop is fully transparent and sits inside the
   band, so the colour has already run out before it reaches any edge and
   overflow:hidden has nothing to cut. The earlier dome was three blurred
   ellipses whose spill did reach the top and was sliced off in a line. */
.aurora {
  position: relative;
  height: 240px;
  height: clamp(180px, 30svh, 280px);
  overflow: hidden;
  pointer-events: none;
  /* --au is the scroll progress, 0 to 1, written by site.js */
  opacity: calc(var(--au, 0) * 1.6);
}
/* a phone gets a shorter band: the same arc, without taking over the screen */
@media (max-width: 760px) {
  .aurora { height: 170px; height: clamp(140px, 22svh, 200px); }
}

/* The stage carries the scroll: the dome grows up out of the bottom edge as
   the page ends and sinks back the moment the reader turns around. */
.au-stage {
  position: absolute;
  inset: 0;
  transform: scaleY(var(--au, 0));
  transform-origin: bottom;
  will-change: transform;
}

.au-dome {
  position: absolute;
  inset: 0;
  transform-origin: bottom;
  /* Only the crest of a much wider oval shows: its half-width is 90% of the
     window (never under 560px, for phones) and its centre sits a little below
     the band, so the rings run off both sides while still high up and the
     oval's ends never come into view. The colour ends at 90% of the radius,
     inside the band, so the top edge stays clear even mid-breath. */
  --au-shape: ellipse max(90vw, 560px) 110% at 50% 108%;
  background: radial-gradient(
    var(--au-shape),
    #0a2c86 0%,
    #0039a6 22%,
    #5b7fd0 38%,
    #eef2fb 52%,
    #f3d6d8 61%,
    #d52b1e 72%,
    rgba(232, 71, 90, 0.55) 82%,
    rgba(232, 71, 90, 0) 90%);
}
/* A whisper of grain over the dome. Long, gentle gradients band on 8-bit
   screens (the pale reds showed as stripes); a little noise breaks those
   steps up so the colour reads as one surface. The grain is masked to the
   dome's own shape: laid over the whole band it tinted the bare page too and
   drew a rectangle, which is the very seam this is meant to avoid. */
.au-dome::after {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0.07;
  -webkit-mask-image: radial-gradient(var(--au-shape), #000 60%, transparent 88%);
  mask-image: radial-gradient(var(--au-shape), #000 60%, transparent 88%);
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* Dark mode: the white ring would glare on ink, so it drops to a cool grey
   and the red and blue come up a little to carry the shape. */
.night .au-dome {
  background: radial-gradient(
    var(--au-shape),
    #08389e 0%,
    #0a45c4 22%,
    #4d74d6 38%,
    #c9d3ee 52%,
    #e7b9bd 61%,
    #e8303f 72%,
    rgba(240, 82, 106, 0.55) 82%,
    rgba(240, 82, 106, 0) 90%);
}

/* The breathing: the dome swells a little on a slow cycle, so it never holds
   still long enough to look drawn. Height only, so the curve stays inside
   the band: at its tallest the colour still ends below the top edge. */
@media (prefers-reduced-motion: no-preference) {
  .au-dome { animation: au-breathe 14s ease-in-out infinite; }
  @keyframes au-breathe {
    0%, 100% { transform: scale(1, 1); }
    50%      { transform: scale(1.04, 1.05); }
  }
}
