/* ==========================================================================
   PORTFOLIO STYLES
   Theme: dark aerospace / robotics, pink accent
   Organized top-to-bottom to match the HTML: variables, base, background,
   nav, hero, sections, cards, forms, footer, responsive, animations.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. CSS VARIABLES (design tokens)
   Change these to re-theme the whole site from one place.
-------------------------------------------------------------------------- */
:root {
  /* Colors */
  --color-bg: #0c0910;          /* near-black background, faint violet cast */
  --color-bg-alt: #120d19;      /* slightly lighter panel background */
  --color-surface: #181022;     /* card / panel surface */
  --color-border: #32213f;      /* subtle borders */

  --color-text: #ece5f2;        /* main body text */
  --color-text-dim: #a294ae;    /* secondary / muted text */

  --color-accent: #ff5c9d;      /* hot pink — primary accent */
  --color-accent-dim: #8f3a63;  /* dimmer pink for borders, backdrop waves etc */
  --color-warn: #c77dff;        /* violet — used sparingly for highlights */

  /* Typography */
  --font-heading: 'Orbitron', sans-serif;
  --font-body: 'JetBrains Mono', monospace;

  /* Layout */
  --max-width: 1100px;
  --section-padding: 6rem 1.5rem;

  /* Motion */
  --transition-fast: 0.2s ease;
  --transition-med: 0.4s ease;
}

/* --------------------------------------------------------------------------
   2. RESET / BASE STYLES
-------------------------------------------------------------------------- */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

body {
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  line-height: 1.6;
  overflow-x: hidden; /* prevent horizontal scroll from the backdrop */
}

h1, h2, h3 {
  font-family: var(--font-heading);
  font-weight: 700;
  line-height: 1.2;
  color: var(--color-text);
}

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

ul {
  list-style: none;
}

img {
  max-width: 100%;
  display: block;
}

/* --------------------------------------------------------------------------
   3. WAVE BACKDROP
   Replaces what used to be a blueprint grid: faint sweeping brush-stroke
   waves drifting very slowly across a dark ground. Each wave is a ribbon
   built from many fine striations, like a dry brush dragged across the
   page, so it reads as texture rather than as clean vector curves.

   The strokes are generated as SVG by setupWaveBackground() in
   script.js — this rule only sets the ground colour, positions the
   layer, and drives the drift. Because the colour lives here and the
   geometry lives there, tune the *look* in script.js (WAVE_* constants)
   and the *placement* here.

   position: fixed keeps it behind all content and unaffected by scrolling.
-------------------------------------------------------------------------- */
.page-backdrop {
  position: fixed;
  inset: 0; /* top/right/bottom/left: 0 */
  z-index: -1;
  background-color: var(--color-bg);
  overflow: hidden; /* clip the oversized SVG below */
}

/* One layer per ribbon, stacked on top of each other, each deliberately
   larger than the viewport (and offset back by half the overflow) so the
   drift never pulls an edge into view.

   will-change is load-bearing, not a micro-optimisation: it promotes each
   layer so its strokes are rasterised ONCE into a GPU texture, after
   which animating it costs a texture move rather than a re-render of
   every path. Without it the backdrop visibly stutters. */
.page-backdrop .wave-layer {
  position: absolute;
  top: -6%;
  left: -6%;
  width: 112%;
  height: 112%;

  /* An <svg> clips to its own box by default, which sliced the taller
     ribbons off with a hard flat edge wherever a crest ran past the
     element. Their artwork is deliberately larger than the box (the
     strokes overrun so their ends stay off screen), so that clip has to
     be off. .page-backdrop above still has overflow: hidden, so nothing
     actually escapes the viewport — it just isn't cut mid-wave. */
  overflow: visible;

  will-change: transform, translate, rotate;
}

/* A soft radial glow so the field isn't perfectly flat/uniform */
.page-backdrop::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(
    ellipse at 50% 0%,
    rgba(255, 92, 157, 0.08),
    transparent 60%
  );
}

/* Each ribbon drifts, bobs and rocks on three separate periods, at its
   own pace — the distances and speeds come from script.js (see the flow*
   fields on WAVE_RIBBONS) and land here as custom properties. Because
   every layer moves at unrelated speeds, the field never reads as one
   flat image being slid around, so no second "whole field" animation is
   needed on top (there used to be one; it cost a re-render per frame
   for motion the per-layer drift already provides).

   "alternate" is doing real work: the strokes have randomised breaks
   and so aren't horizontally periodic, meaning a one-way loop could not
   restart without visibly jumping. Easing smoothly back the other way
   sidesteps that entirely — and because the three axes reverse at
   different moments, the turnarounds never line up into anything that
   reads as oscillation.

   Travel stays well inside the 300 canvas units the strokes overrun the
   viewport by, so no stroke end is ever pulled into view. */
.wave-layer {
  /* Three animations, one per axis. They can run at genuinely independent
     periods because they drive three SEPARATE properties — `transform`,
     `translate` and `rotate`. Folding them into one `transform` animation
     would force a single shared period, and the motion would collapse
     back into a straight-line shuttle.

     `translate` and `rotate` are individual transform properties; where a
     browser is too old to support them the layer simply slides without
     the bob and rock, which degrades cleanly. */
  animation-name: wave-slide, wave-bob, wave-rock;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-direction: alternate;
  /* durations + delays are set per ribbon by script.js */
}

@keyframes wave-slide {
  from {
    transform: translateX(calc(var(--flow-shift, 0px) * -1));
  }
  to {
    transform: translateX(var(--flow-shift, 0px));
  }
}

@keyframes wave-bob {
  from {
    translate: 0 calc(var(--flow-rise, 0px) * -1);
  }
  to {
    translate: 0 var(--flow-rise, 0px);
  }
}

/* The rock is what actually sells it as wave motion: rotating the ribbon
   slightly keeps shifting where its crests fall, so the wave reads as
   undulating in place rather than being carried along rigidly. Kept
   under 1.5deg — past that the strokes start to visibly pivot. */
@keyframes wave-rock {
  from {
    rotate: calc(var(--flow-tilt, 0deg) * -1);
  }
  to {
    rotate: var(--flow-tilt, 0deg);
  }
}

/* Respect users who prefer less motion */
@media (prefers-reduced-motion: reduce) {
  .wave-layer {
    animation: none;
    /* Nothing is moving, so don't hold GPU textures for it either. */
    will-change: auto;
  }
  html {
    scroll-behavior: auto;
  }
}

/* --------------------------------------------------------------------------
   4. NAVIGATION
-------------------------------------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(12, 9, 16, 0.85);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--color-border);
}

.navbar {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 1rem 1.5rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.logo {
  font-family: var(--font-heading);
  font-size: 1.1rem;
  font-weight: 700;
  letter-spacing: 1px;
}

.nav-links {
  display: flex;
  gap: 2rem;
}

.nav-links a {
  font-size: 0.9rem;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--color-text-dim);
  position: relative;
  padding-bottom: 4px;
  transition: color var(--transition-fast);
}

.nav-links a::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 0;
  height: 2px;
  background: var(--color-accent);
  transition: width var(--transition-fast);
}

.nav-links a:hover {
  color: var(--color-accent);
}

.nav-links a:hover::after {
  width: 100%;
}

/* Hamburger button — hidden on desktop, shown on mobile via media query */
.nav-toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0.5rem;
}

.nav-toggle span {
  width: 24px;
  height: 2px;
  background: var(--color-accent);
  transition: transform var(--transition-fast), opacity var(--transition-fast);
}

/* --------------------------------------------------------------------------
   5. HERO SECTION
-------------------------------------------------------------------------- */
.hero {
  min-height: 90vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 2rem 1.5rem;
  position: relative;
}

.hero-content {
  max-width: 750px;
}

/* The name starts invisible and slides up into place. JS adds the
   "show" class once the intro sequence reaches this step. */
.hero-title {
  font-size: clamp(2.2rem, 6vw, 3.6rem);
  color: var(--color-accent);
  margin-bottom: 0.8rem;
  opacity: 0;
  transform: translateY(15px);
  transition: opacity 0.8s ease, transform 0.8s ease;
}

.hero-title.show {
  opacity: 1;
  transform: translateY(0);
}

.hero-subtitle {
  color: var(--color-text-dim);
  font-size: 1.05rem;
}

/* The tagline lives inside a plain inline span — script.js fills it in
   one character at a time, so there's no fade needed here; the text
   itself appearing IS the animation. */
.hero-line {
  white-space: pre-wrap; /* preserves spacing and allows wrapping on small screens */
}

/* --------------------------------------------------------------------------
   6. BUTTONS (shared across sections)
-------------------------------------------------------------------------- */
.btn {
  display: inline-block;
  padding: 0.85rem 1.8rem;
  font-family: var(--font-body);
  font-size: 0.9rem;
  letter-spacing: 1px;
  text-transform: uppercase;
  border-radius: 4px;
  border: 1px solid transparent;
  cursor: pointer;
  transition: transform var(--transition-fast), box-shadow var(--transition-fast), background var(--transition-fast);
}

.btn-primary {
  background: var(--color-accent);
  color: #17060e;
  font-weight: bold;
}

.btn-primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 0 20px rgba(255, 92, 157, 0.4);
}

/* --------------------------------------------------------------------------
   7. SHARED SECTION LAYOUT
-------------------------------------------------------------------------- */
.section {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: var(--section-padding);
}

.section-heading {
  margin-bottom: 3rem;
  text-align: center;
}

.section-heading h2 {
  font-size: clamp(1.6rem, 4vw, 2.2rem);
}

/* --------------------------------------------------------------------------
   8. ABOUT SECTION
-------------------------------------------------------------------------- */
.about-content {
  max-width: 700px;
  margin: 0 auto;
}

.about-content p {
  color: var(--color-text-dim);
  margin-bottom: 1rem;
}

/* Subsections within About — a smaller heading than the section's own
   h2, so it reads as "part of About" rather than a new top-level
   section. Not currently used, but here if a subsection comes back. */
.about-content h3 {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--color-text);
  margin: 2rem 0 0.8rem;
}

/* --------------------------------------------------------------------------
   9. PROJECTS SECTION
-------------------------------------------------------------------------- */
.projects-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1.5rem;
}

.project-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 8px;
  overflow: hidden; /* keeps the media's corners rounded to match the card */
  display: flex;
  flex-direction: column;
  transform: translateY(var(--lift, 0px));
  transition: transform var(--transition-fast), box-shadow var(--transition-fast), border-color var(--transition-fast);
}

/* The hover lift is expressed as a variable rather than a transform of
   its own, so it composes with the scroll-assembly transform above
   instead of replacing it — otherwise hovering a card mid-animation
   would snap it straight to its final position. */
.project-card:hover {
  --lift: -6px;
  border-color: var(--color-accent);
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
}

/* Media area for each project's photo or video.
   To add your own media, replace the ".project-media-placeholder" div in
   the HTML with an <img> or <video> tag — this wrapper will automatically
   crop it to a consistent 16:9 shape via object-fit: cover. */
.project-media {
  aspect-ratio: 16 / 9;
  background: var(--color-bg-alt);
  border-bottom: 1px solid var(--color-border);
  overflow: hidden;
}

.project-media img,
.project-media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.project-media-placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-dim);
  font-size: 0.8rem;
  letter-spacing: 1px;
  text-transform: uppercase;
}

.project-body {
  padding: 1.8rem;
  display: flex;
  flex-direction: column;
  flex-grow: 1;
}

.project-card-header {
  display: flex;
  align-items: center;
  flex-wrap: wrap; /* if the date doesn't fit next to the badge, it drops
                       to its own line instead of squeezing the badge */
  justify-content: space-between;
  gap: 0.4rem 0.5rem;
  margin-bottom: 1rem;
  font-size: 0.75rem;
  letter-spacing: 1px;
}

.project-status {
  color: var(--color-accent);
  border: 1px solid var(--color-accent-dim);
  border-radius: 20px;
  padding: 0.2rem 0.7rem;
  white-space: nowrap; /* never let the badge itself wrap mid-word */
}

.project-date {
  color: var(--color-text-dim);
  white-space: nowrap;
}

.project-body h3 {
  font-size: 1.1rem;
  margin-bottom: 0.3rem;
}

.project-subtitle {
  color: var(--color-accent);
  font-size: 0.8rem;
  margin-bottom: 0.8rem;
}

.project-body p {
  color: var(--color-text-dim);
  font-size: 0.9rem;
  margin-bottom: 1.2rem;
  flex-grow: 1; /* pushes tags to the bottom so cards align evenly */
}

.project-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.project-tags li {
  font-size: 0.75rem;
  color: var(--color-warn);
  background: rgba(199, 125, 255, 0.08);
  border: 1px solid rgba(199, 125, 255, 0.25);
  border-radius: 4px;
  padding: 0.2rem 0.6rem;
}

/* Small "view full write-up" hint at the bottom of each card — the
   whole card is a link, this just makes that obvious. */
.project-view-link {
  display: inline-block;
  margin-top: 1rem;
  font-size: 0.85rem;
  color: var(--color-accent);
}

.project-card:hover .project-view-link {
  text-decoration: underline;
}

/* --------------------------------------------------------------------------
   MINI CAD PROJECTS SECTION
   Smaller, simpler cards than the Featured Projects ones above — no
   status badge, no tags, no write-up link, just a photo, a title, and
   one line of description. Fits more per row since each card is lighter.
-------------------------------------------------------------------------- */
.mini-projects-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 1.2rem;
}

.mini-project-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 8px;
  overflow: hidden;
  transition: transform var(--transition-fast), border-color var(--transition-fast);
}

.mini-project-card:hover {
  transform: translateY(-4px);
  border-color: var(--color-accent-dim);
}

.mini-project-media {
  aspect-ratio: 4 / 3;
  background: var(--color-bg-alt);
  border-bottom: 1px solid var(--color-border);
  overflow: hidden;
}

.mini-project-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.mini-project-card h3 {
  font-size: 0.95rem;
  color: var(--color-text);
  margin: 0.9rem 0.9rem 0.3rem;
}

.mini-project-card p {
  font-size: 0.8rem;
  color: var(--color-text-dim);
  margin: 0 0.9rem 0.9rem;
}

/* --------------------------------------------------------------------------
   LEARNING SECTION
   A casual tag list, not a formal skills grid — same idea as project
   tags, just standalone and centered under a short intro line.
-------------------------------------------------------------------------- */
.learning-content {
  max-width: 700px;
  margin: 0 auto;
  text-align: center;
}

.learning-content p {
  color: var(--color-text-dim);
  margin-bottom: 1.2rem;
}

.learning-tags {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.6rem;
}

.learning-tags a {
  display: inline-block;
  font-size: 0.85rem;
  color: var(--color-accent);
  background: rgba(255, 92, 157, 0.06);
  border: 1px solid var(--color-accent-dim);
  border-radius: 20px;
  padding: 0.35rem 0.9rem;
  transition: background var(--transition-fast), border-color var(--transition-fast), transform var(--transition-fast);
}

.learning-tags a:hover {
  background: rgba(255, 92, 157, 0.15);
  border-color: var(--color-accent);
  transform: translateY(-2px);
}

/* --------------------------------------------------------------------------
   10. PROJECT DETAIL PAGE
   Styles for the individual "projects/*.html" write-up pages — a link
   back to the grid, a big hero image/video, and a readable article body.
-------------------------------------------------------------------------- */
.back-link {
  display: inline-block;
  margin-bottom: 2rem;
  color: var(--color-accent);
  font-size: 0.9rem;
}

.back-link:hover {
  text-decoration: underline;
}

.project-detail-header {
  margin-bottom: 2rem;
}

.project-detail-header h1 {
  font-size: clamp(1.8rem, 4vw, 2.6rem);
  margin: 0.8rem 0 0.3rem;
}

.project-detail-header .project-subtitle {
  font-size: 1rem;
  margin-bottom: 1rem;
}

.project-detail-header .project-tags {
  margin-top: 1rem;
}

.project-detail-header .project-date {
  margin-left: 0.75rem;
}

.project-hero-media {
  width: 100%;
  aspect-ratio: 16 / 9; /* keeps a consistent shape even before media is added */
  max-height: 500px;
  overflow: hidden;
  border-radius: 8px;
  border: 1px solid var(--color-border);
  background: var(--color-bg-alt);
  margin-bottom: 2.5rem;
}

.project-hero-media img,
.project-hero-media video {
  width: 100%;
  max-height: 500px;
  object-fit: cover;
  display: block;
}

/* Add this modifier class alongside ".project-hero-media" when a photo
   is square/portrait-ish and cropping it to 16:9 would cut off part of
   the subject — shows the whole image letterboxed instead of cropping. */
.project-hero-media--contain img,
.project-hero-media--contain video {
  object-fit: contain;
}

/* The written write-up itself — narrower than the full page width so
   long paragraphs stay comfortable to read. */
.project-article {
  max-width: 720px;
  margin: 0 auto;
}

.project-article h2 {
  font-size: 1.3rem;
  color: var(--color-accent);
  margin: 2.5rem 0 1rem;
}

.project-article h2:first-child {
  margin-top: 0;
}

.project-article h3 {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--color-text);
  margin: 1.8rem 0 0.6rem;
}

.project-article p {
  color: var(--color-text-dim);
  margin-bottom: 1.2rem;
}

/* Fallback link shown under a <video> for browsers/formats that can't
   play it inline (e.g. HEVC .mov files in Chrome/Firefox). */
.project-media-fallback {
  display: block;
  margin: -1rem 0 1.5rem;
  font-size: 0.8rem;
  color: var(--color-text-dim);
}

.project-media-fallback a {
  color: var(--color-accent);
}

.project-article ul {
  margin: 0 0 1.2rem 1.2rem;
  color: var(--color-text-dim);
  list-style: disc;
}

.project-article li {
  margin-bottom: 0.4rem;
}

.project-article img,
.project-article video {
  width: 100%;
  border-radius: 8px;
  border: 1px solid var(--color-border);
  margin: 1rem 0 1.5rem;
}

/* Puts a video + image (or any two media items) side by side instead of
   stacked, at a smaller fixed height — handy for an intro pair you want
   visible together without a full page of scrolling. Stacks back to a
   single column on narrow screens. */
.project-media-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
  margin: 1rem 0 1.5rem;
}

.project-media-pair img,
.project-media-pair video {
  width: 100%;
  height: 240px;
  object-fit: cover;
  margin: 0; /* the pair wrapper handles spacing instead */
}

@media (max-width: 600px) {
  .project-media-pair {
    grid-template-columns: 1fr;
  }

  .project-media-pair img,
  .project-media-pair video {
    height: 220px;
  }
}

.project-article figcaption {
  font-size: 0.8rem;
  color: var(--color-text-dim);
  text-align: center;
  margin-top: -1rem;
  margin-bottom: 1.5rem;
}

/* --------------------------------------------------------------------------
   11. CONTACT SECTION
-------------------------------------------------------------------------- */
.contact-wrapper {
  max-width: 600px;
  margin: 0 auto;
  text-align: center;
}

.contact-intro {
  color: var(--color-text-dim);
  margin-bottom: 2rem;
}

.contact-form {
  text-align: left;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 8px;
  padding: 2rem;
}

.form-group {
  margin-bottom: 1.2rem;
}

.form-group label {
  display: block;
  font-size: 0.8rem;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--color-accent);
  margin-bottom: 0.4rem;
}

.form-group input,
.form-group textarea {
  width: 100%;
  background: var(--color-bg-alt);
  border: 1px solid var(--color-border);
  border-radius: 4px;
  padding: 0.75rem;
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: 0.9rem;
  resize: vertical;
  transition: border-color var(--transition-fast);
}

.form-group input:focus,
.form-group textarea:focus {
  outline: none;
  border-color: var(--color-accent);
}

.form-status {
  margin-top: 1rem;
  font-size: 0.85rem;
  color: var(--color-accent);
  min-height: 1.2em; /* reserves space so layout doesn't jump when text appears */
}

/* --------------------------------------------------------------------------
   12. FOOTER
-------------------------------------------------------------------------- */
.site-footer {
  border-top: 1px solid var(--color-border);
  padding: 2rem 1.5rem;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: center;
  justify-content: space-between;
  max-width: var(--max-width);
  margin: 0 auto;
  color: var(--color-text-dim);
  font-size: 0.85rem;
}

.footer-links {
  display: flex;
  gap: 1.5rem;
}

.footer-links a {
  color: var(--color-text-dim);
  transition: color var(--transition-fast);
}

.footer-links a:hover {
  color: var(--color-accent);
}

/* --------------------------------------------------------------------------
   NAME-EXPLODE / SECTION-ASSEMBLE SCROLL EFFECT
   script.js splits text into per-letter spans and drives a --progress
   custom property (0–1) on each element as you scroll. The actual
   motion is just that one variable plugged into the transform/opacity
   formulas below — see script.js for how --progress, --dx, --dy and
   --rot get set.
-------------------------------------------------------------------------- */

/* Word wrappers keep line breaks at real spaces. Without this, a row of
   inline-block letters lets the browser break in the middle of a word. */
.word {
  display: inline-block;
  white-space: nowrap;
}

/* Exploding text (the hero name and tagline) — letters start in place
   (progress 0) and fly apart/fade out (progress 1) as the hero scrolls
   out of view. */
.explode-letters .letter {
  display: inline-block;
  transform:
    translate(calc(var(--dx) * var(--progress, 0)), calc(var(--dy) * var(--progress, 0)))
    rotate(calc(var(--rot) * var(--progress, 0) * 1deg));
  opacity: calc(1 - var(--progress, 0));
}

/* Assembling text (the "Featured Projects" heading) — the inverse:
   letters start scattered/invisible (progress 0) and fly together into
   place (progress 1) as the heading scrolls into view. */
.assemble-letters .letter {
  display: inline-block;
  transform:
    translate(calc(var(--dx) * (1 - var(--progress, 0))), calc(var(--dy) * (1 - var(--progress, 0))))
    rotate(calc(var(--rot) * (1 - var(--progress, 0)) * 1deg));
  opacity: var(--progress, 0);
}

/* Content rising and scaling into place as its section arrives,
   staggered one item after another (script.js tags the items with
   "flow-item" and gives each its own --progress). Used for the project
   cards, mini-CAD cards, learning tags, and contact blocks alike.

   This block only applies once script.js has added "section-anim" to
   the section, so with JS off — or under reduced motion, where the
   effect bails out early — everything is simply visible. */
.section-anim .flow-item {
  transform:
    translateY(calc(var(--lift, 0px) + 70px * (1 - var(--progress, 1))))
    scale(calc(0.92 + 0.08 * var(--progress, 1)));
  opacity: var(--progress, 1);
}

/* --------------------------------------------------------------------------
   13. SCROLL REVEAL ANIMATION
   Elements start hidden/offset via .reveal, then JS adds .reveal-visible
   (via IntersectionObserver) once they enter the viewport.
-------------------------------------------------------------------------- */
.reveal {
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 0.8s ease, transform 0.8s ease;
}

.reveal-visible {
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .reveal {
    transition: none;
  }
}

/* --------------------------------------------------------------------------
   14. RESPONSIVE / MEDIA QUERIES
-------------------------------------------------------------------------- */
@media (max-width: 768px) {
  /* Turn the nav links into a slide-down mobile menu */
  .nav-links {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    gap: 0;
    background: var(--color-bg-alt);
    border-bottom: 1px solid var(--color-border);
    max-height: 0;
    overflow: hidden;
    transition: max-height var(--transition-med);
  }

  .nav-links.open {
    max-height: 300px;
  }

  .nav-links li {
    border-top: 1px solid var(--color-border);
  }

  .nav-links a {
    display: block;
    padding: 1rem 1.5rem;
  }

  .nav-toggle {
    display: flex;
  }

  /* Turn the hamburger into an "X" when the menu is open */
  .nav-toggle.open span:nth-child(1) {
    transform: translateY(7px) rotate(45deg);
  }
  .nav-toggle.open span:nth-child(2) {
    opacity: 0;
  }
  .nav-toggle.open span:nth-child(3) {
    transform: translateY(-7px) rotate(-45deg);
  }
}

@media (max-width: 480px) {
  .hero-title {
    font-size: 2rem;
  }
}
