/* =============================================================
   projects.css — shared stylesheet for /projects/* hero sections
   Single source of truth so all project pages stay consistent.
   Loaded on every project page; the per-page <style> blocks are
   intentionally LIGHTER and shouldn't redeclare these rules.
   ============================================================= */

/* ─── Hero section ─── */
section.hero,
.project-header {
  position: relative;
  width: 100%;
  height: 100vh;
  min-height: 560px;
  overflow: hidden;
  background: var(--charcoal);
  margin-top: 0;
}
@media (max-width: 768px) {
  section.hero,
  .project-header {
    height: 100svh;
    min-height: 520px;
  }
}

/* ─── Hero image + zoom (bio pattern: CSS keyframes on image) ─── */
.hero-img-wrap {
  position: absolute;
  inset: 0;
  overflow: hidden;
  will-change: transform;
  z-index: 0;
}
.hero-img,
.hero-img-wrap > img,
.hero-img-wrap img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform-origin: center;
  /* Promote to its own compositor layer so the zoom runs purely on the GPU
     and isn't re-rastered on every frame. */
  will-change: transform;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  transform: translateZ(0);
  /* Linear easing over 40s reads as a steadier, calmer drift than ease-in-out
     does at this duration (ease-in-out compresses motion at the ends). */
  animation: heroZoom 40s linear infinite alternate;
}
@keyframes heroZoom {
  from { transform: translateZ(0) scale(1); }
  to   { transform: translateZ(0) scale(1.12); }
}
@media (prefers-reduced-motion: reduce) {
  .hero-img,
  .hero-img-wrap > img,
  .hero-img-wrap img { animation: none; }
}

/* ─── Gradients (left text-fade + bottom text-fade) ─── */
.hero-gradient {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(to right,
    rgba(20, 20, 18, 0.45) 0%,
    rgba(20, 20, 18, 0.28) 25%,
    rgba(20, 20, 18, 0.08) 48%,
    transparent 64%);
  pointer-events: none;
}
.hero-gradient-bottom {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(to top,
    rgba(20, 20, 18, 0.42) 0%,
    rgba(20, 20, 18, 0.12) 28%,
    transparent 58%);
  pointer-events: none;
}
.hero-dim {
  position: absolute;
  inset: 0;
  z-index: 2;
  background: rgba(20, 20, 18, 0);
  pointer-events: none;
}

/* ─── Overlay (content container, bottom-anchored) ─── */
.hero-overlay {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: flex-start;
  padding: 48px 60px;
}
@media (max-width: 768px) {
  .hero-overlay { padding: 32px 24px; }
}

/* ─── Eyebrow (category line above the title) ─── */
.hero-eyebrow,
.eyebrow.hero-eyebrow,
#heroEyebrow,
.hero__eyebrow {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 20px;
  /* Over dark hero imagery the light brand green is the readable accent */
  color: var(--brand-green);
  /* Canonical entrance: slide in from the left */
  animation: heroEyebrowSlide 0.9s cubic-bezier(0.23, 1, 0.32, 1) 0.25s both;
}
@keyframes heroEyebrowSlide {
  0%   { opacity: 0; transform: translateX(-40px); }
  100% { opacity: 1; transform: translateX(0); }
}
.hero-eyebrow::before,
#heroEyebrow::before {
  content: "";
  display: block;
  width: 32px;
  height: 0.5px;
  background: var(--brand-green);
  flex-shrink: 0;
}
.hero-eyebrow span,
.eyebrow span,
#heroEyebrow span {
  font-family: 'Jost', sans-serif;
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--brand-green-dark);
  line-height: 1;
}

/* ─── H1 title (every selector variant collapsed) ─── */
section.hero h1,
.project-header h1,
.hero-overlay h1,
.hero-content h1,
.hero-title,
.hero__title,
#heroTitle,
#heroH1 {
  font-family: 'Cormorant Garamond', serif;
  font-weight: 300;
  font-size: clamp(36px, 6vw, 72px);
  line-height: 1.1;
  color: var(--warm-white);
  margin: 0 0 28px;
  letter-spacing: -0.01em;
  /* Canonical entrance: fade up after the eyebrow */
  animation: heroTitleFadeUp 1.0s cubic-bezier(0.23, 1, 0.32, 1) 0.45s both;
}
@keyframes heroTitleFadeUp {
  0%   { opacity: 0; transform: translateY(28px); }
  100% { opacity: 1; transform: translateY(0); }
}
section.hero h1 em,
.project-header h1 em,
.hero-overlay h1 em,
.hero-content h1 em,
.hero-title em,
.hero__title em,
#heroTitle em,
#heroH1 em {
  font-style: italic;
  color: var(--stone-light);
  font-weight: 300;
}

/* ─── Hero meta (Label / Value details under title) ─── */
.hero-meta {
  display: flex;
  gap: 40px;
  flex-wrap: wrap;
  margin-top: 0;
  max-width: 880px;
}
.hero-meta-item {
  display: flex;
  flex-direction: column;
  gap: 4px;
  /* Canonical entrance: staggered fade up after title */
  animation: heroMetaFadeUp 0.8s cubic-bezier(0.23, 1, 0.32, 1) both;
}
.hero-meta-item:nth-child(1) { animation-delay: 0.75s; }
.hero-meta-item:nth-child(2) { animation-delay: 0.85s; }
.hero-meta-item:nth-child(3) { animation-delay: 0.95s; }
.hero-meta-item:nth-child(4) { animation-delay: 1.05s; }
.hero-meta-item:nth-child(5) { animation-delay: 1.15s; }
@keyframes heroMetaFadeUp {
  0%   { opacity: 0; transform: translateY(20px); }
  100% { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .hero-eyebrow, #heroEyebrow, .hero__eyebrow,
  .hero-title, #heroTitle, #heroH1, .hero__title,
  section.hero h1, .project-header h1,
  .hero-meta-item {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}
.hero-meta-label,
.hero-stat-label,
.meta-label {
  font-family: 'Jost', sans-serif;
  font-weight: 500;
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(250, 250, 247, 0.45);
  line-height: 1.4;
}
.hero-meta-value,
.hero-stat-value,
.meta-value {
  font-family: 'Jost', sans-serif;
  font-weight: 400;
  font-size: 14px;
  letter-spacing: 0.06em;
  color: rgba(250, 250, 247, 0.85);
  line-height: 1.5;
  text-transform: none;
}
.hero-meta-value a,
.hero-stat-value a,
.meta-value a {
  color: inherit;
  text-decoration: none;
}
.hero-meta-value a:hover,
.hero-stat-value a:hover {
  color: var(--brand-green);
}
@media (max-width: 768px) {
  .hero-meta { gap: 24px; }
}

/* ─── Hero-stats (used by St James, same look as hero-meta) ─── */
.hero-stats {
  display: flex;
  gap: 40px;
  flex-wrap: nowrap;
  margin-top: 8px;
  padding-top: 24px;
  border-top: 0.5px solid rgba(255, 255, 255, 0.15);
}
.hero-stat-item {
  display: flex;
  flex-direction: column;
  gap: 4px;
  animation: heroMetaFadeUp 0.8s cubic-bezier(0.23, 1, 0.32, 1) both;
}
.hero-stat-item:nth-child(1) { animation-delay: 0.75s; }
.hero-stat-item:nth-child(2) { animation-delay: 0.85s; }
.hero-stat-item:nth-child(3) { animation-delay: 0.95s; }
.hero-stat-item:nth-child(4) { animation-delay: 1.05s; }
@media (max-width: 768px) {
  .hero-stats { gap: 24px; flex-wrap: wrap; padding-top: 16px; }
}

/* ─── Project credits band (above CTA) ─── */
.project-credits-band {
  padding: 44px 60px 4px;
}
.project-credits-inner {
  max-width: 1100px;
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: 6px 14px;
  text-align: center;
}
.project-credits-label {
  font-family: 'Jost', sans-serif;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  opacity: 0.45;
}
.project-credits-list {
  font-family: 'Jost', sans-serif;
  font-size: 13.5px;
  letter-spacing: 0.02em;
  opacity: 0.85;
}
.project-credits-list a { font-weight: 500; }
@media (max-width: 768px) {
  .project-credits-band { padding: 32px 24px 4px; }
}


/* ─── Audit fixes — impeccable critique 2026-07-17 ──────────────────────
   This file loads AFTER each page's inline <style>, so equal-specificity
   rules here win. Fixes apply to every project page incl. julies-restaurant. */

/* P0 — .cta-outlined was authored for dark sections (DESIGN.md: "cta-btn:
   dark sections") but sits on the light .cta-band: white-on-cream, measured
   1.08:1 text / 1.02:1 border. The page's only conversion element. */
.cta-band .cta-outlined{color:var(--charcoal);border-color:rgba(28,28,26,.55)}
.cta-band .cta-outlined:hover{color:var(--brand-green-dark);border-color:var(--brand-green-dark)}

/* P1 — spec panel ("Material logic") used #6B6560, a hex absent from the
   DESIGN.md token table, with the LIGHT-surface italic token on it: "logic"
   rendered at 1.47:1. The first fix forced var(--near-black) on every page,
   which made the band dark even on light project pages.
   Now driven entirely off theme-aware tokens: --cream is #F5F0E8 on light
   pages and #141412 on dark, and --charcoal inverts with it — so the band
   reads cream on light pages, near-black on dark, and contrast holds in both
   without a second hard-coded branch. Label/detail use opacity rather than a
   fixed rgba so they mute correctly against either surface. */
.spec-panel{background:var(--cream);color:var(--charcoal)}
.spec-panel h2{color:var(--charcoal)}
.spec-panel h2 em{color:var(--italic-on-light)}
.spec-panel .spec-value{color:var(--charcoal)}
.spec-panel .spec-label{color:var(--charcoal);opacity:.62}
.spec-panel .spec-detail{color:var(--charcoal);opacity:.75}

/* P3 — every paragraph computed margin:0; multi-paragraph passages rendered
   as one unbroken slab. `+ p` keeps the first para flush to the heading. */
.lead-right p + p,
.editorial p + p,
.editorial-cream p + p{margin-top:1.2em}

/* ─── Scroll reveal for figures ─────────────────────────────────────────
   Figures had no entrance at all. Gated behind .js-reveal (set by script) so
   that if JS fails the images ship VISIBLE rather than blank. */
.js-reveal .pe-body-fig,
.js-reveal .pe-body-row > figure{opacity:0;transform:translateY(22px);
  transition:opacity .8s cubic-bezier(.23,1,.32,1),transform .8s cubic-bezier(.23,1,.32,1)}
.js-reveal .pe-body-fig.is-in,
.js-reveal .pe-body-row > figure.is-in{opacity:1;transform:none}

/* Body-section eyebrows reuse the existing heroEyebrowSlide gesture
   (translateX(-40px) -> 0) rather than inventing a second one. */
.js-reveal .eyebrow:not(.hero-eyebrow){opacity:0}
.js-reveal .eyebrow:not(.hero-eyebrow).is-in{
  animation:heroEyebrowSlide .9s cubic-bezier(.23,1,.32,1) both}

@media (prefers-reduced-motion: reduce){
  .js-reveal .pe-body-fig,
  .js-reveal .pe-body-row > figure,
  .js-reveal .eyebrow:not(.hero-eyebrow){opacity:1;transform:none;transition:none;animation:none}
}

/* ── MAP: desktop drops the "Scroll to zoom" hint; mobile goes full-bleed ── */
/* The hint only ever fired on a wheel event, so it never appeared on touch
   devices anyway. Retired at every width. */
.map-zoom-hint{ display: none; }
@media (max-width: 700px){
  .map-wrap{ width: 100%; border-radius: 0; }
}

/* ── MOBILE: counter only, and full-width solos go edge-to-edge ──
   At phone width the image is too small to share with a caption, so the
   caption text is dropped and only the NN / NN counter remains. The text
   lives on visually; alt attributes are separate, so nothing is lost to
   assistive tech. */
@media (max-width: 700px){
  .v4-cap span:not(.v4-num){ display: none; }
  .v4-num{ margin-bottom: 0; }
  .pe-body-fig.pe-fig-wide{ width: 100%; }
}

/* ── QUOTE ATTRIBUTION ──
   The inline rule hardcodes rgba(28,28,28,.5), which sits at 1.08:1 on the dark
   background — the cite was present but invisible in dark mode. Inheriting the
   quote's own (themed) colour flips with the theme automatically. */
.pe-quote cite{ color: inherit; opacity: .55; }

/* ── VIDEO PLAYER: match the image column ──
   The player was capped at max-width:1400px while full-width figures use
   calc(100% - 2*var(--fig-gutter)) with no cap — 385px narrower at 1920.
   The section's own 60px/24px padding already equals the gutter, so dropping
   the cap lands it exactly on the image width; mobile goes full-bleed to match
   .pe-fig-wide. */
/* NB: build_grill.py injects the player's CSS at </head>, i.e. AFTER this
   stylesheet's <link> — so these need a body ancestor to out-specify it
   (0,1,1 vs 0,1,0). projects.css only wins on source order against the page's
   main inline <style>, not against that appended block. */
body .video-player-wrap{ max-width: none; }
@media (max-width: 700px){
  body .video-player-section{ padding-left: 0; padding-right: 0; }
  body .video-player-wrap{ border-radius: 0; }
}

/* ── MAP: click-to-explore badge (interaction toggles on click; page scrolls freely when idle) ── */
.map-wrap .mb-map-badge{ position:absolute; left:16px; bottom:16px; z-index:3; pointer-events:none;
  font-family:'Jost',sans-serif; font-size:10px; font-weight:500; letter-spacing:.18em; text-transform:uppercase;
  color:#fff; background:rgba(20,18,14,.72); padding:7px 14px; border-radius:20px;
  box-shadow:0 2px 10px rgba(0,0,0,.35); opacity:.72; transition:opacity .3s; }
.map-wrap:hover .mb-map-badge{ opacity:.9; }
.map-wrap .map-live .mb-map-badge{ opacity:1; background:var(--brand-green-dark,#4DAF92); }
