/* Optional visual effects. Opt-in per page; every effect is off under prefers-reduced-motion. */

/* ---- Edge fold: elements fold in 3D as they cross the top/bottom of the viewport ----
   Pure CSS (scroll-driven animations). Browsers without support just show the page normally. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    @keyframes fold-in {
      from { transform: perspective(900px) rotateX(-62deg); opacity: 0; }
      to   { transform: perspective(900px) rotateX(0deg);   opacity: 1; }
    }

    @keyframes fold-out {
      from { transform: perspective(900px) rotateX(0deg);  opacity: 1; }
      to   { transform: perspective(900px) rotateX(62deg); opacity: 0; }
    }

    .fx-fold .section > h2,
    .fx-fold .entry,
    .fx-fold .skills-category,
    .fx-fold .travel-map,
    .fx-fold .region-card,
    .fx-fold .photo-card {
      animation: fold-in linear both, fold-out linear forwards;
      animation-timeline: view(), view();
      animation-range: entry 0% entry 70%, exit 30% exit 100%;
    }

    /* A tilted element renders wider than its box; clip that at the container edge so phones
       never get sideways overflow. (clip, unlike hidden, doesn't create a scroll container.) */
    .fx-fold .container {
      overflow-x: clip;
    }

    /* The old scroll-reveals would fight the fold; the fold replaces them. */
    .fx-fold .section,
    html.js .fx-fold .section:not(.visible) { opacity: 1; transform: none; transition: none; }
    html.js .fx-fold .region-card,
    html.js .fx-fold .photo-card { opacity: 1; transform: none; }

    /* Hover lifts use the independent translate property so they coexist with the fold. */
    .fx-fold .entry { transition: translate 0.2s ease, border-color 0.2s ease; }
    .fx-fold .entry:hover { transform: none; translate: 5px 0; }
    .fx-fold .region-card { transition: translate 0.2s ease, border-color 0.2s ease; }
    .fx-fold .region-card:hover { transform: none; translate: 0 -5px; }
  }
}

/* ---- Glitch: brief red/amber split on page titles, about once every 10s ----
   Steps are >= 0.35s apart, so it stays under 3 changes per second. */
h1.glitch {
  position: relative;
}

@media (prefers-reduced-motion: no-preference) {
  @keyframes glitch-a {
    0%, 89%, 100% { opacity: 0; transform: none; clip-path: inset(0); }
    90%   { opacity: 1; transform: translate(-4px, 0);  clip-path: inset(8% 0 58% 0); }
    93.5% { opacity: 1; transform: translate(3px, 1px);  clip-path: inset(56% 0 10% 0); }
    97%   { opacity: 1; transform: translate(-2px, 0);  clip-path: inset(30% 0 36% 0); }
  }

  @keyframes glitch-b {
    0%, 89%, 100% { opacity: 0; transform: none; clip-path: inset(0); }
    90%   { opacity: 1; transform: translate(4px, 0);   clip-path: inset(50% 0 14% 0); }
    93.5% { opacity: 1; transform: translate(-3px, -1px); clip-path: inset(12% 0 54% 0); }
    97%   { opacity: 1; transform: translate(2px, 0);   clip-path: inset(40% 0 28% 0); }
  }

  h1.glitch::before,
  h1.glitch::after {
    content: attr(data-text) / "";
    position: absolute;
    inset: 0;
    opacity: 0;
    pointer-events: none;
    text-shadow: none;
    mix-blend-mode: multiply;
    animation-duration: 10s;
    animation-timing-function: steps(1, end);
    animation-iteration-count: infinite;
  }

  h1.glitch::before { color: var(--nerv-red);   animation-name: glitch-a; }
  h1.glitch::after  { color: var(--nerv-amber); animation-name: glitch-b; }
}

/* ---- Cursor glow: the honeycomb lights up amber near the pointer (fx.js sets --mx/--my) ---- */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  html.fx-pointer::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='49' viewBox='0 0 28 49'%3E%3Cg fill-rule='evenodd'%3E%3Cg fill='%23FF9D00' fill-opacity='0.55' fill-rule='nonzero'%3E%3Cpath d='M13.99 9.25l13 7.5v15l-13 7.5L1 31.75v-15l12.99-7.5zM3 17.9v12.7l10.99 6.34 11-6.35V17.9l-11-6.34L3 17.9zM0 15l12.98-7.5V0h-2v6.35L0 12.69v2.3zm0 18.5L12.98 41v8h-2v-6.85L0 35.81v-2.3zM15 0v7.5L27.99 15H28v-2.31h-.01L17 6.35V0h-2zm0 49v-8l12.99-7.5H28v2.31h-.01L17 42.15V49h-2z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
    -webkit-mask-image: radial-gradient(circle 200px at var(--mx, -999px) var(--my, -999px), #000 0%, transparent 100%);
    mask-image: radial-gradient(circle 200px at var(--mx, -999px) var(--my, -999px), #000 0%, transparent 100%);
  }
}

/* ---- Feliz Jueves: Thursday-only 3D terminal (built by fx.js on body.fx-jueves pages) ----
   A CSS 3D scene: grid floor, hex pedestal, rings, motes and a billboard cutout, viewed by an
   orbiting camera. Static layout lives here; all movement is inside the no-preference block below. */
.jueves {
  --ped-r: 50px;      /* hex circumradius = side length */
  --ped-a: 43.3px;    /* apothem */
  --ped-h: 26px;
  --tilt-x: -14deg;   /* resting camera */
  --tilt-y: -20deg;
  --lean-x: 0;        /* pointer offset, -1..1, set by fx.js */
  --lean-y: 0;
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 50;
  display: flex;
  flex-direction: column;
  width: 250px;
  border: 2px solid var(--nerv-red);
  background: var(--nerv-dark);
  color: var(--nerv-amber);
  font-family: var(--font-mono);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
}

.jueves-head {
  display: flex;
  align-items: stretch;
  border-bottom: 2px solid var(--nerv-red);
}

.jueves-title {
  flex: 1;
  padding: 6px 9px;
  background: repeating-linear-gradient(-45deg, var(--nerv-amber) 0 8px, var(--nerv-dark) 8px 16px) right / 40px 100% no-repeat;
}

.jueves-title,
.jueves-line,
.jueves-hud {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  white-space: nowrap;
}

.jueves-close {
  width: 30px;
  padding: 0;
  border: 0;
  border-left: 2px solid var(--nerv-red);
  background: var(--nerv-dark);
  color: var(--nerv-amber);
  font: 700 1.1rem/1 var(--font-mono);
  cursor: pointer;
}

.jueves-close:hover {
  background: var(--nerv-red);
  color: var(--nerv-white);
}

.jueves-close:focus-visible,
.jueves-stage:focus-visible {
  outline: 3px solid var(--nerv-amber);
  outline-offset: -3px;
}

.jueves-line {
  display: block;
  padding: 5px 9px;
  overflow: hidden;
  color: var(--nerv-white);
}

.jueves-stage {
  position: relative;
  display: block;
  width: 100%;
  height: 200px;
  padding: 0;
  border: 0;
  overflow: hidden;
  font: inherit;
  color: inherit;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
  perspective: 460px;
  perspective-origin: 50% 30%;
  background: radial-gradient(ellipse at 50% 85%, rgba(255, 157, 0, 0.32), transparent 62%), #121212;
}

/* Horizon glow behind the scene */
.jueves-stage::before {
  content: "";
  position: absolute;
  inset: 34% -10% 38%;
  background: radial-gradient(ellipse, rgba(230, 0, 0, 0.5), rgba(255, 157, 0, 0.18) 45%, transparent 70%);
}

/* Scanlines over the scene */
.jueves-stage::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(rgba(0, 0, 0, 0.28) 0 1px, transparent 1px 3px);
}

.jueves-hud {
  position: absolute;
  top: 6px;
  font-size: 0.58rem;
  opacity: 0.85;
}

.jueves-hud-l { left: 8px; }
.jueves-hud-r { right: 8px; }

/* World origin = centre of the floor, under the pedestal. Children are positioned around it. */
.jueves-world {
  position: absolute;
  left: 50%;
  top: 84%;
  width: 0;
  height: 0;
  transform-style: preserve-3d;
  transform: rotateX(var(--tilt-x)) rotateY(var(--tilt-y));
}

.jueves-world > *,
.jueves-ped > * {
  position: absolute;
  display: block;
}

.jueves-floor {
  left: -220px;
  top: -220px;
  width: 440px;
  height: 440px;
  transform: rotateX(90deg);
  background:
    repeating-linear-gradient(0deg, rgba(255, 157, 0, 0.8) 0 2px, transparent 2px 40px),
    repeating-linear-gradient(90deg, rgba(255, 157, 0, 0.8) 0 2px, transparent 2px 40px);
  -webkit-mask-image: radial-gradient(circle, #000 18%, transparent 66%);
  mask-image: radial-gradient(circle, #000 18%, transparent 66%);
}

.jueves-ped { transform-style: preserve-3d; }

.jueves-ped-face {
  left: calc(var(--ped-r) / -2);
  top: calc(var(--ped-h) * -1);
  width: var(--ped-r);
  height: var(--ped-h);
  box-sizing: border-box;
  border: 1px solid var(--nerv-amber);
  background: linear-gradient(#4a1010, #1a1a1a);
  /* Outward-facing only, so the far walls never show through the cap. */
  backface-visibility: hidden;
  transform: rotateY(calc(var(--i) * 60deg)) translateZ(var(--ped-a));
}

.jueves-ped-cap {
  left: calc(var(--ped-r) * -1);
  top: calc(var(--ped-a) * -1);
  width: calc(var(--ped-r) * 2);
  height: calc(var(--ped-a) * 2);
  clip-path: polygon(25% 0, 75% 0, 100% 50%, 75% 100%, 25% 100%, 0 50%);
  background:
    linear-gradient(100deg, transparent 42%, rgba(255, 255, 255, 0.75) 50%, transparent 58%) 130% 0 / 300% 100% no-repeat,
    radial-gradient(ellipse 36% 36%, rgba(0, 0, 0, 0.6), transparent) no-repeat,   /* contact shadow */
    radial-gradient(circle, var(--nerv-amber), var(--nerv-red) 75%);
  transform: translateY(calc(var(--ped-h) * -1)) rotateX(90deg);
}

/* Both rings sit below the pedestal top: a plane crossing the cutout would slice it when it hops. */
.jueves-ring {
  left: -80px;
  top: -80px;
  width: 160px;
  height: 160px;
  box-sizing: border-box;
  border: 2px dashed var(--nerv-amber);
  border-radius: 50%;
  transform: translateY(-5px) rotateX(90deg);
}

.jueves-ring-b {
  left: -62px;
  top: -62px;
  width: 124px;
  height: 124px;
  border: 2px dotted var(--nerv-red);
  transform: translateY(-17px) rotateX(90deg);
}

.jueves-mote {
  left: var(--x);
  top: var(--y);
  width: 3px;
  height: 3px;
  background: var(--nerv-amber);
  opacity: 0.6;
  transform: translateZ(var(--z));
}

/* Billboard: undoes the camera rotation so the cutout always faces the viewer. */
.jueves-bill {
  left: 0;
  top: calc(var(--ped-h) * -1);
  width: 0;
  height: 0;
  transform-style: preserve-3d;
  transform: rotateY(calc(var(--tilt-y) * -1)) rotateX(calc(var(--tilt-x) * -1));
}

.jueves-tilt,
.jueves-spin,
.jueves-hop {
  position: absolute;
  left: 0;
  top: 0;
  display: block;
  transform-style: preserve-3d;
  transform-origin: 0 0;
}

.jueves-art {
  position: absolute;
  left: -58px;
  bottom: 0;
  width: 116px;
  height: auto;
  transform-origin: 50% 100%;
}

.jueves-bubble {
  position: relative;
  display: block;
  padding: 7px 9px 9px;
  border-top: 2px solid var(--nerv-red);
  font-family: var(--font-header);
  font-size: 1.45rem;
  letter-spacing: 0.04em;
  text-align: center;
  text-transform: uppercase;
  white-space: nowrap;
}

@media (max-width: 600px) {
  .jueves { right: 8px; bottom: 8px; width: 190px; }
  .jueves-stage { height: 150px; perspective: 400px; }
  .jueves-world { scale: 0.74; }
  .jueves-bubble { font-size: 1.1rem; }
  .jueves-title, .jueves-line { font-size: 0.56rem; }
  .jueves-title { background-size: 22px 100%; }
}

@media (prefers-reduced-motion: no-preference) {
  /* Panel powers on like a CRT: a line, then the picture opens. */
  @keyframes jueves-on {
    from { transform: scale(0.02, 0.006); filter: brightness(4); }
    40%  { transform: scale(1, 0.006);    filter: brightness(4); }
    to   { transform: scale(1, 1);        filter: brightness(1); }
  }

  @keyframes jueves-off {
    from { transform: scale(1, 1);     filter: brightness(1); }
    60%  { transform: scale(1, 0.006); filter: brightness(4); }
    to   { transform: scale(0, 0.006); filter: brightness(4); }
  }

  /* Camera orbit, and its exact inverse for the billboard (same timing, so they stay in step). */
  @keyframes jueves-orbit {
    from { transform: rotateX(-14deg) rotateY(-34deg); }
    to   { transform: rotateX(-10deg) rotateY(34deg); }
  }

  @keyframes jueves-face {
    from { transform: rotateY(34deg) rotateX(14deg); }
    to   { transform: rotateY(-34deg) rotateX(10deg); }
  }

  @keyframes jueves-floor-scroll {
    to { background-position: 0 40px, 0 0; }
  }

  @keyframes jueves-ring-spin {
    from { transform: translateY(-5px) rotateX(90deg) rotateZ(0deg); }
    to   { transform: translateY(-5px) rotateX(90deg) rotateZ(360deg); }
  }

  @keyframes jueves-ring-b-spin {
    from { transform: translateY(-17px) rotateX(90deg) rotateZ(360deg); }
    to   { transform: translateY(-17px) rotateX(90deg) rotateZ(0deg); }
  }

  @keyframes jueves-mote-rise {
    from { transform: translateZ(var(--z)) translateY(20px); opacity: 0; }
    20%  { opacity: 0.9; }
    to   { transform: translateZ(var(--z)) translateY(-55px); opacity: 0; }
  }

  @keyframes jueves-sweep {
    from, 55% { background-position: 130% 0, 0 0, 0 0; }
    to        { background-position: -30% 0, 0 0, 0 0; }
  }

  @keyframes jueves-horizon {
    from { opacity: 0.55; }
    to   { opacity: 1; }
  }

  /* Cutout materialises bottom-up in coarse steps. */
  @keyframes jueves-materialise {
    from { clip-path: inset(100% 0 0 0); filter: brightness(4) saturate(0); }
    80%  { clip-path: inset(0);          filter: brightness(4) saturate(0); }
    to   { clip-path: inset(0);          filter: none; }
  }

  /* Float with squash at the bottom and stretch at the top. */
  @keyframes jueves-float {
    from { translate: 0 0;    scale: 1.05 0.95; }
    to   { translate: 0 -9px; scale: 0.97 1.04; }
  }

  @keyframes jueves-hop {
    from { transform: translateY(0) scale(1, 1); }
    15%  { transform: translateY(0) scale(1.14, 0.82); }
    50%  { transform: translateY(-20px) scale(0.93, 1.08); }
    85%  { transform: translateY(0) scale(1.1, 0.88); }
    to   { transform: translateY(0) scale(1, 1); }
  }

  @keyframes jueves-spin {
    from { transform: rotateY(0deg); }
    to   { transform: rotateY(720deg); }
  }

  @keyframes jueves-type {
    from { max-width: 0; }
    to   { max-width: 100%; }
  }

  @keyframes jueves-stamp {
    from { scale: 2.4; opacity: 0; }
    60%  { scale: 0.92; opacity: 1; }
    to   { scale: 1; opacity: 1; }
  }

  .jueves { animation: jueves-on 0.55s 0.6s ease-out backwards; }
  .jueves.is-leaving { animation: jueves-off 0.3s ease-in forwards; }

  .jueves-world { animation: jueves-orbit 5.5s ease-in-out infinite alternate; }
  .jueves-bill { animation: jueves-face 5.5s ease-in-out infinite alternate; }
  .jueves-floor { animation: jueves-floor-scroll 1.4s linear infinite; }
  .jueves-ring { animation: jueves-ring-spin 7s linear infinite; }
  .jueves-ring-b { animation: jueves-ring-b-spin 5s linear infinite; }
  .jueves-mote { animation: jueves-mote-rise var(--s) var(--d) linear infinite; }
  .jueves-ped-cap { animation: jueves-sweep 4s ease-in-out infinite; }
  .jueves-stage::before { animation: jueves-horizon 2.6s ease-in-out infinite alternate; }

  /* Lean toward the pointer */
  .jueves-tilt {
    transform: rotateY(calc(var(--lean-x) * 16deg)) rotateX(calc(var(--lean-y) * -10deg));
    transition: transform 0.25s ease-out;
  }

  .jueves-hop.is-on { animation: jueves-hop 0.55s ease-out; }
  .jueves-spin.is-on { animation: jueves-spin 0.9s cubic-bezier(0.3, 0, 0.2, 1); }

  .jueves-art {
    animation:
      jueves-materialise 0.9s 1.2s steps(14) backwards,
      jueves-float 1.8s 2.1s ease-in-out infinite alternate;
  }

  .jueves-line { animation: jueves-type 1.1s 1.3s steps(28) backwards; }

  .jueves-bubble { animation: jueves-stamp 0.35s 2.5s ease-out backwards; }

  /* Reuse the title glitch on the greeting. */
  .jueves-bubble::before,
  .jueves-bubble::after {
    content: attr(data-text);
    position: absolute;
    inset: 7px 9px 9px;
    opacity: 0;
    pointer-events: none;
  }

  .jueves-bubble::before { color: var(--nerv-red); animation: glitch-a 6s 3s steps(1) infinite; }
  .jueves-bubble::after  { color: var(--nerv-white); animation: glitch-b 6s 3s steps(1) infinite; }
}
