/* Page shell: header and the vertical rhythm between sections.
   Component-level styling (nav links, buttons, footer, etc.) lives in
   components/*.css — the site footer is components/footer.css. */

.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: var(--z-header);
  height: var(--header-height);
  display: flex;
  align-items: center;
  color: var(--color-paper);
  background-color: transparent;
  border-bottom: 1px solid rgba(255, 255, 255, 0.18);
  transition:
    background-color var(--duration-slow) var(--ease-standard),
    color var(--duration-slow) var(--ease-standard),
    border-color var(--duration-slow) var(--ease-standard);
}

.site-header:hover,
.site-header:focus-within,
.site-header--solid {
  color: var(--color-ink);
  background-color: var(--color-paper);
  border-bottom-color: var(--color-border);
}

/* Section rhythm */
.section {
  padding-block: var(--space-20);
}

@media (min-width: 1024px) {
  .section {
    padding-block: var(--space-32);
  }
}

.section--tight {
  padding-block: var(--space-12);
}

@media (min-width: 1024px) {
  .section--tight {
    padding-block: var(--space-20);
  }
}

.section--alt {
  background-color: var(--color-bg-alt);
}

.section--stone {
  background-color: var(--color-stone);
}

.section--no-top {
  padding-top: 0;
}

/* Project detail pages (body.project-page) run many plain-background
   .section elements back to back with no alternating --alt/--stone tint
   between them (see projects/melody-house) — each one's own full
   padding-block stacks at every shared boundary into a ~160px dead gap
   below 1024px, the same issue already fixed page-locally in
   pages/architectural-design.css. Tightened here to a single boundary's
   worth of space (half each side) instead of two stacked ones. Scoped to
   .project-page (not .section generally) so pages that alternate section
   backgrounds elsewhere keep their full, background-change-earned spacing.
   :has(+ .section) / "+ .section" only match true .section-to-.section
   boundaries, so edges against .panel/.panel-sequence (real background
   changes) are left untouched at .section's normal default. */
body.project-page .section:has(+ .section) {
  padding-bottom: var(--space-10);
}

body.project-page .section + .section {
  padding-top: var(--space-10);
}

@media (min-width: 1024px) {
  body.project-page .section:has(+ .section) {
    padding-bottom: var(--space-32);
  }

  body.project-page .section + .section {
    padding-top: var(--space-32);
  }
}

.section__header {
  max-width: 40rem;
  margin-bottom: var(--space-10);
}

@media (min-width: 1024px) {
  .section__header {
    margin-bottom: var(--space-16);
  }
}

/* Centered CTA below a section's content (e.g. a "View more" under a
   card grid) — mirrors .section__header's role at the opposite end. */
.section__footer {
  margin-top: var(--space-16);
  text-align: center;
}

@media (min-width: 1024px) {
  .section__footer {
    margin-top: var(--space-20);
  }
}

/* Skip link — visible on focus only, keyboard/screen-reader navigation aid */
.skip-link {
  position: absolute;
  left: var(--space-4);
  top: -3rem;
  z-index: var(--z-modal);
  background: var(--color-ink);
  color: var(--color-white);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md);
  transition: top var(--duration-fast) var(--ease-standard);
}

.skip-link:focus {
  top: var(--space-4);
}

/* Simple stack helper for vertically-spaced flex groups */
.stack {
  display: flex;
  flex-direction: column;
}

.stack > * + * {
  margin-top: var(--space-4);
}

.cluster {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
}

/* Body scroll lock — toggled on <html> by any modal/panel that needs the
   page underneath to stop scrolling (nav.js's mobile menu, staff-modal.js,
   enquiry-modal.js). Lives here (loaded on every page) rather than in a
   single component's CSS, since more than one of those triggers it. */
html[data-scroll-locked="true"] {
  overflow: hidden;
}

/* Parallax wrapper (assets/js/parallax.js) — site-wide convention for any
   image/video that should drift on scroll. Clips the oversized media the
   script scales and translates so the scrub never exposes an edge. Apply
   only to a wrapper that contains nothing but the media itself (add a
   dedicated inner wrapper first if the element also holds a caption or
   other content), then add data-parallax to that wrapper. */
[data-parallax] {
  overflow: hidden;
}

/* Scroll reveal (assets/js/scroll-reveal.js) — site-wide convention for
   fade/blur/rise-in entrance on any content block as it scrolls into view.
   Hidden state only applies once JS adds .scroll-reveal--ready to <main>,
   so content stays fully visible without JS, with reduced motion, or in
   browsers without IntersectionObserver. Values match the footer's own
   reveal (footer.css) and the panel text entrance (panel-text.js's
   TEXT_ANIMATION) — kept in sync across all three so scroll-triggered
   motion reads consistently across the site. Add a bare data-reveal
   attribute to any element that should animate in this way. */
.scroll-reveal--ready [data-reveal] {
  transition:
    opacity 0.9s cubic-bezier(0.165, 0.84, 0.44, 1),
    transform 0.9s cubic-bezier(0.165, 0.84, 0.44, 1),
    filter 0.9s cubic-bezier(0.165, 0.84, 0.44, 1);
}

/* Suppresses the transition above for the one frame in which JS first
   applies the hidden state (scroll-reveal.js), so content already painted
   in the fully-visible no-JS state snaps straight to hidden instead of
   visibly fading out before the user has scrolled at all. !important
   guarantees this wins regardless of any more specific transition-delay
   overrides layered on elsewhere (reviews.css, team-intro.css). */
.scroll-reveal--instant [data-reveal] {
  transition: none !important;
}

.scroll-reveal--ready [data-reveal=""] {
  opacity: 0;
  transform: translateY(48px);
  filter: blur(6px);
}

/* Stagger — every reveal target sits as a plain 1st/2nd/3rd child of its
   own section wrapper (image + text, small + large, or a 3-up row), so a
   single generic nth-child rule staggers all of them without needing a
   per-module override. Matches footer.css's own stagger step
   (--duration-fast between siblings). */
.scroll-reveal--ready [data-reveal]:nth-child(2) {
  transition-delay: var(--duration-fast);
}

.scroll-reveal--ready [data-reveal]:nth-child(3) {
  transition-delay: calc(var(--duration-fast) * 2);
}

.scroll-reveal--ready [data-reveal]:nth-child(4) {
  transition-delay: calc(var(--duration-fast) * 3);
}

.scroll-reveal--ready [data-reveal]:nth-child(5) {
  transition-delay: calc(var(--duration-fast) * 4);
}
