/* ═══════════════════════════════════════════════════════════════
   EDITORIAL LAYER — loads last.

   This file is a DELTA,
not a second stylesheet. layout.css and
   components.css already own the structure (the 12-column portfolio
   composition,
the about split,
the contact form,
the buttons) and
   that structure is good. Re-declaring it here only created two
   owners for one concern — which is how the old extra.css turned
   into an unlabelled junk drawer in the first place.

   So this layer only carries things the base genuinely lacks:
     1. a slower section rhythm
     2. an editorial voice for display type (Playfair,
tight tracking)
     3. the ornament + motion that make it feel composed
     4. the surfaces the base hardcoded,
re-pointed at tokens so
        dark mode works
     5. contrast fixes
     6. rules carried over from the retired extra.css

   Design language adapted from (references in HANDOFF.md):
     - "Editorial Collage Hero"  by felipemenezes098,
21st.dev
     - "Minimalist Hero Fashion" by Kokonut Baffier,
21st.dev
     - "Split Hero With Image Cards" / "Portfolio Hero",
21st.dev
     - wondersimple.com: one idea per section,
extreme whitespace,
photography carries the page,
performance is a design feature.
   ═══════════════════════════════════════════════════════════════ */

/* 1. RHYTHM — sections breathe -------------------------------------
   Base was a flat --space-24. This scales with the viewport so the
   page opens up on a desktop without crushing on a phone. */

.section {
  padding-block: var(--section-padding);
}

/* 2. EYEBROW — the only ornament on the page ----------------------
   Base was a plain block of highlight-coloured caps. A hairline rule
   and a muted colour make it read as a label rather than a heading. */

.eyebrow {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  letter-spacing: var(--tracking-label);
  color: var(--color-text-muted);
}

.eyebrow::before {
  content: "";
  inline-size: 2rem;
  block-size: 1px;
  background: var(--color-line-strong);
  flex: none;
}

.eyebrow.text-center { justify-content: center; }

/* 3. EDITORIAL VOICE ----------------------------------------------
   Playfair was already loaded but only reachable via .editorial-text,
.section-headline,
.quote-text,
.cta-section h2,
.footer-logo {
  font-family: var(--font-family-editorial);
  font-weight: 400;
}

.section-headline {
  line-height: 1.05;
  letter-spacing: var(--tracking-tight);
  margin-bottom: var(--space-12);
}

.quote-text { line-height: 1.55; }

.cta-section h2 { letter-spacing: var(--tracking-tight); }

/* 4. HERO — split editorial ---------------------------------------
   The previous hero laid white type over the photograph. Measured against
   the actual pixels of the current hero image,
the strip behind the
   headline averages 0.61 luminance and 77.6% of it is bright: white text
   scored 1.59:1,
and 1.01:1 at the brightest points. Reaching AA by
   darkening alone needs roughly 0.82 alpha,
which destroys the picture.

   And the hero image is admin-editable,
so any overlay tuned to today's
   photo breaks on tomorrow's. Splitting the hero removes the problem at
   the source: type sits on paper where contrast is guaranteed,
and the
   photograph stays bright and full-colour beside it. This is also what
   the Editorial Collage Hero reference actually does — a wash panel plus
   the image {
  height: auto;
  min-height: min(88vh, 900px);
  display: grid;
  grid-template-columns: 1fr 1.05fr;
  align-items: center;
  gap: clamp(2rem, 1rem + 4vw, 5.5rem);
  text-align: start;
  color: var(--color-text-primary);
  background: var(--color-bg-primary);
  max-width: var(--container-width);
  margin-inline: auto;
  padding-inline: var(--container-padding);
  padding-block: clamp(5.5rem, 4rem + 6vw, 8rem) clamp(2rem, 1rem + 3vw, 3.5rem);
  overflow: visible;
}

/* The hero already ends on generous space; a full section rhythm on top of
   it left a dead band before the first line of copy. */
.hero-split + .section { padding-block-start: clamp(3rem, 2rem + 3vw, 4.5rem); }

/* Hierarchy: the studio name was set at display size,
which made the least
   interesting words on the page the largest thing on it while the actual
   promise sat beneath in small type. The name stays the h1 for semantics
   and SEO {
  font-size: clamp(1.75rem, 1.3rem + 1.6vw, 2.5rem);
  line-height: 1.15;
  letter-spacing: var(--tracking-normal);
  margin-bottom: var(--space-4);
  color: var(--color-text-secondary);
  font-weight: 400;
  text-shadow: none;
}

/* The secondary action is a text link,
not a second button: one primary
   action per screen keeps the hierarchy readable. */
.btn-link {
  font-size: var(--font-size-sm);
  letter-spacing: var(--tracking-wide);
  color: var(--color-text-secondary);
  border-bottom: 1px solid var(--color-line-strong);
  padding-bottom: 2px;
  transition: color var(--transition-fast), border-color var(--transition-fast);
}

.btn-link:hover { color: var(--color-accent); border-color: var(--color-accent); }

@media (pointer: coarse) {
  /* 26px tall at the foot of a chapter, where it is the way back. */
  .btn-link {
    display: inline-block;
    padding-block: 0.55rem;
  }
}

/* The old full-bleed scaffolding is not used by the split hero. Its markup
   was removed as well; these guard the shared rules in layout.css. */

/* A 24s drift. Long enough that it reads as stillness { animation: hero-drift 24s var(--ease-out) forwards; }

@keyframes hero-drift {
  from { transform: scale(1.04); }
  to   { transform: scale(1); }
}


/* 4b. HEADER OVER A LIGHT HERO ------------------------------------
   The nav ships a .light-mode treatment that paints the logo,
links and
   burger white while the header is unscrolled — correct when the hero was
   a dark full-bleed photograph,
invisible now that the hero is warm paper.
   This was the reported "white text on a light background" at the top. */

.header:not(.scrolled) .logo.light-mode,
.header:not(.scrolled) .nav-link.light-mode,
.header:not(.scrolled) .mobile-menu-btn.light-mode {
  color: var(--color-text-primary);
}

.header:not(.scrolled) .nav-link.light-mode:hover { color: var(--color-accent); }

.header .nav-link::after { background: var(--color-accent); }

/* 5. PORTFOLIO CAPTIONS -------------------------------------------
   The base leaves caption text sitting directly on the photograph,
so legibility depends on whichever image is in the slot. A scrim
   on the caption block itself fixes that without touching the
   composition or the ::after hover border that layout.css owns. */

.portfolio-item-content {
  /* The base pins this box to all four edges, so a gradient on it dimmed the
     entire photograph instead of just the caption. Releasing the top lets the
     scrim collapse to a band behind the text. */
  top: auto;
  /* The base sets align-items: flex-end meaning "push to the bottom", but in a
     column flex container align-items is the CROSS axis — horizontal — and in
     RTL flex-end resolves to the LEFT. The caption was landing on the wrong
     side of every card. justify-content is what moves it down. */
  align-items: flex-start;
  justify-content: flex-end;
  background: linear-gradient(to top,
    rgba(10, 8, 7, 0.80) 0%,
    rgba(10, 8, 7, 0.45) 55%,
    rgba(10, 8, 7, 0) 100%);
  padding-block-start: var(--space-16);
}

/* 6. SURFACES THE BASE HARDCODED ----------------------------------
   These were literal light colours,
so in dark mode they produced
   light text on a light panel. Pointing them at tokens makes dark
   mode a real variant rather than an inversion that breaks. */

.header.scrolled {
  background: color-mix(in srgb, var(--color-bg-primary) 90%, transparent);
  border-block-end: 1px solid var(--color-line);
}

.testimonial-card {
  background: var(--color-bg-elevated);
  border: 1px solid var(--color-line);
  box-shadow: var(--shadow-medium);
  border-radius: var(--radius-lg);
}

/* 7. CONTACT FORM CONTRAST ----------------------------------------
   The base styles the fields for a dark panel (white text,
white
   borders) but the section sits on --color-accent-soft,
which is a
   pale blush in light mode: white-on-blush,
effectively invisible.
   This is the conversion point of the whole site,
so it gets fixed. */

.cta-section { background-color: var(--color-bg-secondary); }

.cta-section .contact-field label,
.cta-section .contact-field label span {
  /* --color-text-muted is too light against the section surface for
     text this small; secondary clears AA comfortably. */
  color: var(--color-text-secondary);
}

.cta-section .contact-field input,
.cta-section .contact-field textarea {
  color: var(--color-text-primary);
  background: var(--color-bg-elevated);
  border: 1px solid var(--color-line-strong);
  border-radius: var(--radius-md);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.cta-section .contact-field input:focus,
.cta-section .contact-field textarea:focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px var(--color-accent-soft);
}

.cta-text {
  font-size: var(--font-size-lg);
  color: var(--color-text-secondary);
  margin-bottom: var(--space-12);
}

/* 8. TESTIMONIAL CONTROLS (carried over from extra.css) ------------ */

.testimonial-footer { margin-top: 0; }

.testimonial-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  margin-top: var(--space-8);
}

.testimonial-prev,
.testimonial-next {
  background: none;
  border: 1px solid var(--color-line-strong);
  color: var(--color-text-secondary);
  inline-size: 40px;
  block-size: 40px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  transition: background-color var(--transition-fast), color var(--transition-fast),
              border-color var(--transition-fast);
}

.testimonial-prev:hover,
.testimonial-next:hover {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: #fff;
}

/* The gap is wider than the dots need, because each dot's touch target is
   wider than the dot. At the old 8px spacing the targets overlapped and a
   tap on one landed on its neighbour. */
.testimonial-dots { display: flex; gap: 1.25rem; }

.dot {
  inline-size: 7px;
  block-size: 7px;
  border-radius: 50%;
  background: var(--color-text-muted);
  opacity: 0.35;
  border: none;
  cursor: pointer;
  padding: 0;
  transition: opacity var(--transition-fast), transform var(--transition-fast),
              background-color var(--transition-fast);
}

.dot.active { opacity: 1; transform: scale(1.35); background: var(--color-accent); }

/* The dot stays 7px; the button around it does not. A pseudo element carries
   the target so the active-dot scale is untouched. The horizontal inset is
   smaller than the vertical one so neighbours cannot overlap. */
.dot { position: relative; }

.dot::after {
  content: '';
  position: absolute;
  inset: -15px -9px;
}

/* 9. FOOTER (tagline/copy/hover were only in extra.css) ------------ */

.footer-tagline {
  font-size: var(--font-size-sm);
  color: rgba(255, 255, 255, 0.5);
  margin-bottom: var(--space-6);
  font-style: italic;
}

.footer-social a {
  inline-size: 42px;
  block-size: 42px;
  display: grid;
  place-items: center;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 50%;
  color: rgba(255, 255, 255, 0.75);
  transition: background-color var(--transition-fast), color var(--transition-fast),
              border-color var(--transition-fast);
}

.footer-social a:hover {
  background: rgba(255, 255, 255, 0.1);
  color: #fff;
  border-color: rgba(255, 255, 255, 0.4);
}

.footer-copy {
  font-size: var(--font-size-xs);
  color: rgba(255, 255, 255, 0.4);
  margin-bottom: 0;
}

/* 10. PORTFOLIO PAGE (carried over from extra.css) ----------------- */

.back-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--color-text-secondary);
  font-size: var(--font-size-sm);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  transition: color var(--transition-fast);
}

.back-link:hover { color: var(--color-accent); }
.back-link svg { transform: rotate(180deg); }

@media (pointer: coarse) {
  /* 23px tall, and it is how you leave a chapter. */
  .back-link {
    padding-block: 0.6rem;
  }
}

/* 11. MOTION PREFERENCES ------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .hero-bg img { animation: none; }
}

/* 12. DARK-MODE BUTTON ---------------------------------------------
   The accent lifts to a light rose on a dark ground so it stays visible
   against the page — but the button keeps white label text from the base,
which lands at roughly 2.2:1 on that fill. On a light accent the label
   has to go dark. */

@media (prefers-color-scheme: dark) {
  .btn-primary { color: var(--color-bg-dark); }
  .btn-primary:hover { color: var(--color-bg-dark); }
}

/* 13. <picture> WRAPPER GEOMETRY -----------------------------------
   Regression from adding WebP: <picture> is an inline box by default,
so an
   <img> with height:100% inside it resolves against an auto-height parent
   rather than the card. Every fixed-aspect card (the 3/4 portfolio tiles {
  display: block;
  inline-size: 100%;
  block-size: 100%;
}


/* ══════════════════════════════════════════════════════════════════
   14. RETIRED — the album lives in css/book.css

   Sections 14 (.album-page: snap + fade) and 15 (.photo-plate: a sticky
   stage inside a 130dvh track) were the first two attempts at the album.
   Both are gone rather than left in place: css/book.css owns the page
   turn now and css/gallery.css owns the chapter pages,
and three
   stylesheets describing the same mechanism is exactly how the old
   extra.css turned into an unlabelled junk drawer.

   What survived the rewrite,
and why:
     - the caption belongs on the BOTTOM edge of a plate. The header is
       position:fixed,
so it owns the top of the viewport permanently and
       a top-anchored caption renders straight through the logo.
     - in RTL,
`flex-direction: row-reverse` flips the main axis,
so
       flex-END still resolves right; flex-START is what reaches the far
       corner.
     - proximity snap,
never mandatory,
and scroll-snap-stop: normal.
     - dvh,
never vh.
   ══════════════════════════════════════════════════════════════════ */

/* ── The reveal,
refined ─────────────────────────────────────────
   The base transitions `all`,
which makes the compositor watch every
   animatable property on the element. Same motion,
narrower scope,
and
   a shorter lift that suits a page settling rather than sliding up from
   off-screen. Still used by portfolio.html. */

.reveal {
  transform: translateY(18px);
  transition:
    opacity 0.8s cubic-bezier(0.22, 1, 0.36, 1),
    transform 0.8s cubic-bezier(0.22, 1, 0.36, 1);
}
