/* The rest of the site is styled with Tailwind utility classes directly in
   the HTML (see js/tailwind-config.js for the theme). These few rules are
   the exceptions that don't map cleanly to utilities:
   - a global prefers-reduced-motion killswitch (safer as one blanket rule
     than remembering motion-reduce: on every animated element)
   - text selection color
   - the scroll-reveal system, which is gated on a JS-added ancestor class
     (<html class="js">) so the page stays fully visible with JS disabled --
     awkward to express as arbitrary-variant utility soup, clear as CSS. */

/* Self-hosted fonts. Previously loaded via Google's hosted CSS
   (fonts.googleapis.com -> fonts.gstatic.com), which cost an extra DNS +
   connection round trip before the font file itself even started
   downloading. Weights below are duplicated @font-face blocks pointing at
   the SAME file on purpose: Inter and JetBrains Mono ship as variable
   fonts, so Google's own CSS response for this exact weight list serves one
   physical woff2 per family and fans it out into one @font-face per weight
   -- this mirrors that response exactly, just self-hosted. Regenerate by
   re-fetching https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap
   with a real browser User-Agent (Google serves legacy TTF otherwise) and
   taking the "latin" blocks' src URLs. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/inter-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/fonts/inter-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/fonts/inter-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/fonts/inter-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("/assets/fonts/inter-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/jetbrains-mono-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/fonts/jetbrains-mono-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/fonts/jetbrains-mono-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* A smooth, unhurried deceleration curve (no overshoot), the same
     family of easing Apple uses across apple.com for content that
     "resolves into focus" rather than just sliding/fading in linearly. */
  --ease-elegant: cubic-bezier(0.16, 1, 0.3, 1);
}

::selection {
  background: rgba(34, 211, 238, 0.3);
}

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

/* Content tilts back in 3D and rotates flat as it settles into place. The
   perspective() lives inside the transform rather than on a parent, so each
   element is self-contained. A `perspective` property on an ancestor would
   silently change how every absolutely-positioned descendant renders. */
.js .reveal {
  opacity: 0;
  transform: perspective(1200px) translateY(28px) scale(0.97) rotateX(6deg);
  filter: blur(6px);
  transition:
    opacity 900ms var(--ease-elegant),
    transform 900ms var(--ease-elegant),
    filter 900ms var(--ease-elegant);
}

.js .reveal.is-visible {
  opacity: 1;
  transform: perspective(1200px) translateY(0) scale(1) rotateX(0deg);
  filter: blur(0);
}

.js .reveal--stagger > * {
  opacity: 0;
  transform: perspective(1200px) translateY(28px) scale(0.97) rotateX(6deg);
  filter: blur(6px);
  transition:
    opacity 900ms var(--ease-elegant),
    transform 900ms var(--ease-elegant),
    filter 900ms var(--ease-elegant);
}

.js .reveal--stagger.is-visible > * {
  opacity: 1;
  transform: perspective(1200px) translateY(0) scale(1) rotateX(0deg);
  filter: blur(0);
}

.js .reveal--stagger.is-visible > *:nth-child(2) {
  transition-delay: 90ms;
}

.js .reveal--stagger.is-visible > *:nth-child(3) {
  transition-delay: 180ms;
}

.js .reveal--stagger.is-visible > *:nth-child(4) {
  transition-delay: 270ms;
}

/* Reduced motion skips the reveal entirely. The killswitch above only
   shortens transitions, so content would still start hidden and pop in a
   frame after main.js runs: a full-page flash on reload. Must stay below
   the .reveal rules (same specificity, so source order decides). */
@media (prefers-reduced-motion: reduce) {
  .js .reveal,
  .js .reveal--stagger > * {
    opacity: 1;
    transform: none;
    filter: none;
  }
}

/* Diagonal light sweep on primary CTA buttons, triggered on hover/focus.
   A CSS-only pseudo-element sweep is simpler and cheaper than animating a
   real DOM node, and it's the kind of thing that doesn't map to a single
   Tailwind utility. */
.btn-shine {
  position: relative;
  overflow: hidden;
}

.btn-shine::after {
  content: "";
  position: absolute;
  inset: 0;
  left: -60%;
  width: 40%;
  background: linear-gradient(115deg, transparent, rgba(255, 255, 255, 0.35), transparent);
  transform: skewX(-20deg);
  transition: left 750ms var(--ease-elegant);
  pointer-events: none;
}

.btn-shine:hover::after,
.btn-shine:focus-visible::after {
  left: 130%;
}

/* Header nav link. The underline wipes in from the left and the text itself
   never moves. A link that shifts, lifts or scales makes a sparse header
   feel jumpy, and it nudges the theme toggle beside it. Only the pseudo
   element animates, so this composites on the GPU and costs no layout.
   The same rule draws a permanent underline on the current page via
   aria-current, so the active state is markup-driven rather than a second
   hand-maintained class. */
.nav-link {
  position: relative;
}

.nav-link::after {
  content: "";
  position: absolute;
  /* Tracks the link's horizontal padding, which tightens on small screens so
     the two nav links and the theme toggle still fit a 320px header. */
  left: var(--nav-underline-inset, 0.375rem);
  right: var(--nav-underline-inset, 0.375rem);
  bottom: 0.25rem;
  height: 1px;
  background: linear-gradient(90deg, transparent, #22d3ee 20%, #22d3ee 80%, transparent);
  opacity: 0;
  transform: scaleX(0);
  transform-origin: left center;
  transition:
    transform 500ms var(--ease-elegant),
    opacity 500ms var(--ease-elegant);
}

.nav-link:hover::after,
.nav-link:focus-visible::after {
  opacity: 1;
  transform: scaleX(1);
}

/* Current page: already underlined, so there is nothing to animate on hover. */
.nav-link[aria-current="page"]::after {
  opacity: 1;
  transform: scaleX(1);
  transition: none;
}

@media (min-width: 640px) {
  .nav-link::after {
    --nav-underline-inset: 0.5rem;
  }
}
