/* Top nav — contact email on the left, availability status on the
   right, both grey (#9f9f9f, a header one-off — see CLAUDE.md). Not
   sticky (scrolls away with the header), no project links, transparent
   so <body>'s live background shows through. */
.top-nav {
  min-height: var(--nav-height);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-1) var(--space-4);
  padding: var(--space-2) var(--space-3);
  white-space: nowrap;
}

.top-nav__brand,
.top-nav__status {
  font-size: var(--font-size-nav);
  letter-spacing: 0;
  line-height: var(--line-height-heading);
  color: #9f9f9f;
}

/* Both nav items don't fit side by side on narrow screens — drop the
   status there and center the email alone. */
@media (max-width: 40rem) {
  .top-nav {
    justify-content: center;
  }

  .top-nav__status {
    display: none;
  }
}

/* Header — a centered stack: superhead ("The Independent Studio of"),
   the full-bleed display name, availability status, and the contact
   link. Everything soft-fades in on load, in that order (see the
   animation block below). */
.intro {
  padding: 2rem var(--space-3) 0;
  text-align: center;
  /* The display name is deliberately wider than the viewport (its
     "L"/"y" edges get cut off — see .intro__name below). The header's
     box is exactly viewport width, so clipping x here trims the
     overhang without creating a scroll container (clip, not hidden)
     and without the overhang widening the page's scrollable area. */
  overflow-x: clip;
}

.intro__superhead {
  font-size: var(--font-size-h2);
  letter-spacing: 0;
  line-height: var(--line-height-heading);
  /* Editable in the CMS (site.json superheadColor → --intro-superhead-color,
     set on :root by render-site.js); this is the default. */
  color: var(--intro-superhead-color, #7259ff);
}

/* Display name — sized so the text is ~103.4% of the viewport width
   at any screen size, just clipping the outer edges of the "L" and
   "y" (.intro has overflow-x: clip): the glyphs' own side bearings
   absorb the first ~22px of the 24px-per-side overhang at desktop, so
   only a few px visibly cut into the L's stem and the y's tail (an
   earlier 104% cut the stem almost entirely away; 101% didn't visibly
   clip at all). Tracking is em-based (-0.0591em = the -16px Liam
   specced at the 1440px-wide desktop size) so the wordmark's
   letterfit looks identical at every screen size — a fixed px value
   read much tighter on small screens. With tracking proportional,
   text width is a plain multiple of font-size (measured in Chromium:
   ~5.502 × font-size for "Liam Kennedy" in Maison Neue Book at this
   tracking), so 103.4% of viewport = 1.034 / 5.502 ≈ 18.79vw. If the
   name text ever changes, remeasure and update both constants. */
.intro__name {
  font-size: 18.79vw;
  letter-spacing: -0.0591em;
  line-height: 1.1;
  white-space: nowrap;
  /* text-align can't center a line wider than its box (the overflow
     all hangs off the right edge) — center the box itself instead, so
     the overhang splits evenly between the "L" and "y" sides. */
  width: max-content;
  margin-left: 50%;
  transform: translateX(-50%);
  /* Editable in the CMS (site.json nameColor → --intro-name-color); default. */
  color: var(--intro-name-color, #ff9d00);
}

/* Contact pill button — shared by the header "Get in touch" and the
   footer "Get in Touch" (both carry the .contact-pill class, see
   index.html), so they stay visually identical. H2 text on the
   superhead's purple by default, shifting to the wordmark's orange on
   hover; all three colors are editable in the CMS (site.json contact
   background/textColor/hoverBackground → the --contact-* vars, set on
   :root by render-site.js), with these as fallbacks. Less padding below
   the text than above (12px/8px) to optically center it in the pill. */
.contact-pill {
  display: inline-block;
  padding: 12px 28px 8px;
  font-size: var(--font-size-h2);
  letter-spacing: 0;
  line-height: var(--line-height-heading);
  color: var(--contact-text, #ffffff);
  background: var(--contact-bg, #7259ff);
  border-radius: 999px;
  transition: background-color 0.3s ease-in-out;
}

.contact-pill:hover,
.contact-pill:focus-visible {
  background: var(--contact-hover-bg, #ff9d00);
}

.intro__contact {
  margin-top: var(--space-2);
}

/* Load-in sequence — soft opacity fades only (no movement): superhead
   first, then the name's letters at randomized times (each
   .intro__name-letter span gets its own inline animation-delay from
   js/intro-name.js), then the contact pill. Disabled under
   prefers-reduced-motion below. */
.intro__superhead,
.intro__name,
.intro__contact,
.intro__name-letter {
  animation: intro-fade-in 0.8s ease both;
}

.intro__superhead {
  animation-delay: 0ms;
}

/* The whole name starts hidden too — the letter split (js/intro-name.js)
   happens after the browser's first paint, so without this the plain
   text flashes for an instant and then vanishes when the spans (which
   start at opacity 0) replace it. Same delay as the earliest letter, so
   the no-JS fallback simply fades the name in as one block; once split,
   the inline animation is cleared (see intro-name.js) and the letters
   take over. */
.intro__name {
  animation-delay: 200ms;
}

/* Letters start between 200–500ms (set inline per letter,
   js/intro-name.js) and run 0.5s each, so the name is fully in at
   exactly 1s — per Liam, any slower risks reading as a loading issue. */
.intro__name-letter {
  animation-duration: 0.5s;
}

.intro__contact {
  animation-delay: 800ms;
}

/* The project sections (titles, text blocks, images — everything) hold
   back until the name reads as visible (fully in at 1s), then fade in
   as one unit. Without this, section content rendered well before the
   headline finished — read as the page loading bottom-up. Empty until
   JS builds it, so no-JS is unaffected. */
main {
  animation: intro-fade-in 0.8s ease 1100ms both;
}

@keyframes intro-fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .intro__superhead,
  .intro__name,
  .intro__contact,
  .intro__name-letter,
  main {
    animation: none;
  }
}

@media (min-width: 60rem) {
  /* Kept small so the first project's grid images peek well above the
     fold on a ~900px-tall desktop viewport — the giant wordmark makes
     the header much taller than the old avatar/bio version (which had
     settled at 140px here). The section's own 120px top padding is
     most of the remaining gap to the first title. */
  .intro {
    margin-bottom: 16px;
  }
}

@media (max-width: 59.99rem) {
  .intro {
    margin-bottom: var(--space-6);
  }
}

/* Project section — alternates light/dark by position (1st, 3rd... stay
   light; 2nd, 4th... go dark), declared here as custom properties so
   it's a single source of truth: js/section-theme.js reads --section-bg
   off the active section and applies it to <body> as the user scrolls,
   rather than duplicating this odd/even logic in JS. */
.project {
  container-type: inline-size;
  /* 3x the vertical padding (was --space-5/40px) — this is what creates
     the gap between one project section and the next, since sections
     sit directly adjacent with no margin of their own. */
  padding: calc(var(--space-5) * 3) var(--space-3);
  --section-bg: var(--color-bg);
  --section-text: var(--color-text);
  --section-text-muted: var(--color-text-muted);
}

.project:nth-of-type(even) {
  --section-bg: #111111;
  --section-text: #ffffff;
  --section-text-muted: #a3a3a3;
}

/* Sticky title pill — echoes the project title in a translucent
   "liquid glass" pill pinned near the top of the viewport while its
   section is on screen, but only once the real title/subhead have
   scrolled out of frame (js/sticky-titles.js toggles .is-visible;
   inspired by jamesayres.com). The wrapper is the section's first
   child so its sticky range covers the whole section — the pill
   arrives and leaves with its own section, each section carrying its
   own. height: 0 keeps it from occupying layout space above the
   header. */
.project__sticky {
  position: sticky;
  top: var(--space-3);
  z-index: 5;
  height: 0;
  display: flex;
  justify-content: center;
  /* Without this the zero-height wrapper stretches the pill to a
     zero-height content box and the text spills out below the glass. */
  align-items: flex-start;
}

/* Same padding/shape as the header's "Get in touch" pill. Text tracks
   the live theme token so it stays legible on light and dark sections;
   the glass itself is a translucent white + backdrop blur, which reads
   as frosted glass over both themes and over passing images. */
.project__sticky-title {
  padding: 12px 28px 8px;
  border-radius: 999px;
  font-size: var(--font-size-h2);
  letter-spacing: 0;
  line-height: var(--line-height-heading);
  color: var(--current-text, var(--color-text));
  background: rgba(255, 255, 255, 0.12);
  -webkit-backdrop-filter: blur(16px) saturate(1.5);
  backdrop-filter: blur(16px) saturate(1.5);
  border: 1px solid rgba(255, 255, 255, 0.2);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
  pointer-events: none;
  opacity: 0;
  transform: translateY(-0.5rem);
  transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out,
    color 0.6s ease;
}

.project__sticky-title.is-visible {
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .project__sticky-title {
    transition: color 0.6s ease;
    transform: none;
  }
}

/* Centered above the grid at every breakpoint (was column 2–3 of the
   page grid on desktop, matching the old left-aligned header). */
.project__header {
  margin-bottom: var(--space-4);
  text-align: center;
}

/* Colored via --current-text (the live, JS-updated token — see
   section-theme.js), not this section's own static --section-text.
   That's deliberate: the body background only actually switches once
   this section becomes "active" (crosses the viewport center), which
   can happen well after its title first scrolls into view. Tying the
   title to the same live token the background uses guarantees they
   always change in lockstep — the title can never render in a color
   that doesn't match whatever the background actually is yet. */
.project__name {
  font-size: var(--font-size-h2);
  letter-spacing: 0;
  line-height: var(--line-height-heading);
  color: var(--current-text, var(--color-text));
  transition: color 0.6s ease;
}

.project__subtitle {
  font-size: var(--font-size-h2);
  letter-spacing: 0;
  line-height: var(--line-height-heading);
  color: var(--current-text-muted, var(--color-text-muted));
  margin-top: var(--space-1);
  transition: color 0.6s ease;
}

/* Phones (≤40rem, same breakpoint that drops the nav status): the
   contact/sticky pills and the project titles/subheads read oversized
   at phone widths at the full 20px H2 size — drop them to 16px (a 14px
   pass was tried first and read as too small). Padding shrinks in the
   same 16/20 ratio (12/8/28 → 9/6/22), keeping the slight bottom-trim
   that optically centers the pill text. Everything else (text blocks,
   footer, testimonials) keeps the global H2 size. */
@media (max-width: 40rem) {
  .contact-pill,
  .project__sticky-title {
    font-size: var(--font-size-base);
    padding: 9px 22px 6px;
  }

  .project__name,
  .project__subtitle {
    font-size: var(--font-size-base);
  }
}
