/* ===========================================================
   Maclayne — brand tokens
   =========================================================== */
/* "Maclayne Display" — her rebuilt bubble-letter brand font, registered
   here under the CSS family name "Bubble". Brand asset, for Maclayne's
   own materials only. Headings only, always uppercase, 83 glyphs only —
   see fonts/README.md. Never used for the "Maclayne" wordmark. */
@font-face {
  font-family: 'Bubble';
  src: url('../fonts/MaclayneDisplay-Regular.otf') format('opentype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

:root {
  --lavender: #f0d8ff;
  --pale-yellow: #f0eb8c;
  --purple: #c69dcc;
  --blue: #818cc8;
  --olive: #bbb97d;
  --olive-dark: #27331a;   /* the dark field behind her video */
  --ink: #111111;
  --cream: #fffaf3;

  --font-display: 'Bubble', 'Baloo 2', system-ui, sans-serif;
  --font-body: 'Barlow Semi Condensed', 'Avenir Next Condensed', system-ui, sans-serif;

  --glass-bg: rgba(255, 250, 243, 0.84);
  --solid-bg: rgba(255, 250, 243, 0.97);
  --radius: 18px;
  /* Spotify's embed has a fixed-height header and fills the rest with
     the tracklist, so a taller frame simply shows more tracks. 352px is
     Spotify's default (3 tracks); 480px shows about six. The cover art
     is a square of the same height so the two stay level. */
  --player-h: 480px;
}

* { box-sizing: border-box; }
html {
  scroll-behavior: smooth;
  /* Cream lives on <html> so it paints the canvas. Keeping it off <body>
     is what lets the fixed hero photo (z-index:-1) stay visible — an
     opaque body background would paint straight over it. */
  background: var(--cream);
}
body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--ink);
  background: transparent;
  line-height: 1.55;
}
img { max-width: 100%; display: block; }
a { color: inherit; }
h1, h2, h3 {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.01em;
  margin: 0 0 0.6em;
}

/* honeypot field: off-screen rather than display:none, since some bots
   skip fields that are display:none but still fill off-screen ones */
.hp { position: absolute; left: -10000px; top: auto; width: 1px; height: 1px; overflow: hidden; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

/* ===========================================================
   Fixed hero photo behind the page
   NOTE: this stylesheet lives in /css, so image paths need ../
   =========================================================== */
.hero-fixed-bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  background-image: url('../images/maclayne-hero-wide.jpg');
  background-size: cover;
  background-position: center center;
  background-repeat: no-repeat;
}

/* Translucent panels let the fixed photo bleed through.
   NO backdrop-filter here — deliberately. A blur sitting over a fixed
   photo has to be recomputed on every scroll frame (the region behind
   the panel changes as it moves). Chromium does that on the GPU and
   copes; WebKit is far slower at it, and with ten large blurred panels
   the frame rate collapsed page-wide in Safari, which read as the
   photos "jittering". Plain rgba translucency costs nothing. */
.panel--glass { background: var(--glass-bg); }
/* Text-heavy sections stay near-opaque so copy remains readable */
.panel--solid { background: var(--solid-bg); }
.panel--tint { background: rgba(198, 157, 204, 0.32); }

.section { position: relative; padding: 5rem 1.5rem; }
/* The nav is sticky and ~66px tall, so a plain anchor jump lands with the
   section's top hidden under it. scroll-margin-top offsets every anchor
   target so the section starts just below the nav instead. Applies to
   the quotes band too, since it's the #press target. */
.section, .otde-quotes { scroll-margin-top: 5rem; }
.wrap { max-width: 1080px; margin: 0 auto; }
.wrap--narrow { max-width: 620px; margin: 0 auto; }
.center { text-align: center; }

/* All section headings are centred */
.section-heading { font-size: clamp(1.7rem, 3.4vw, 2.4rem); text-align: center; }
.subheading { font-size: clamp(1.2rem, 2.2vw, 1.5rem); margin-top: 3rem; text-align: center; }
/* Heading plus a trailing link (Videos) stacks centred rather than
   splitting left/right, so it matches every other section heading. */
.section-heading-row {
  display: flex; flex-direction: column; align-items: center;
  gap: 0.6rem; margin-bottom: 2rem;
}
.section-heading-row .section-heading { margin: 0; }
.lead { font-size: 1.12rem; max-width: 48ch; margin: 0 auto 1.5rem; }

/* ===========================================================
   Fixed photo backdrops
   Every in-page photo background uses this instead of
   `background-attachment: fixed`, which iOS Safari sizes against
   the whole document rather than the viewport (so it renders
   zoomed and wrong on phones).

   The inner layer is exactly one viewport tall, and js/main.js
   offsets it so it lands flush with the top of the screen. That
   gives the same result as `position: fixed; inset: 0` — the photo
   holds still while the page scrolls past. `overflow: clip` (not
   hidden) trims it to the section without creating a scroll
   container, which would break the offset.
   =========================================================== */
/* ===========================================================
   Fixed photo layers (wide screens)
   Copied property-for-property from the technique Hostinger uses on
   maclayne.com, read from the live site in Safari's Web Inspector
   (their .block-background--fixed / .block-background__image--fixed):

     wrapper: position:absolute; inset:0; bottom:-1px; overflow:hidden;
              clip-path:inset(0); backface-visibility:hidden;
              pointer-events:none
     image:   a real <img>; position:fixed; top:0; left:0; width:100%;
              height:100lvh; object-fit:cover

   Why this one works where the others didn't:
   - `clip-path: inset(0)` clips the fixed image to the section but
     does NOT make the wrapper its containing block (only transform /
     filter / backdrop-filter / will-change / contain do that), so the
     image stays genuinely viewport-fixed. The old `clip: rect()` form
     of this trick is mis-rendered by WebKit — that was the green.
   - A real <img> with object-fit gets its own decoded compositor
     layer. It's decoded once and just moves under the clip, so a
     scrollbar drag has nothing to re-rasterise. `background-attachment:
     fixed` re-rasterises the exposed region on every scroll — that was
     the ~100ms spike in Timelines.
   - `bottom: -1px` avoids a hairline seam at the section edge.
   =========================================================== */
.bg-parallax {
  position: absolute;
  inset: 0;
  bottom: -1px;
  overflow: hidden;
  clip-path: inset(0);
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  pointer-events: none;
  z-index: 0;
}
.bg-parallax__img {
  position: fixed;
  top: 0; left: 0;
  width: 100%;
  height: 100lvh;
  object-fit: cover;
  object-position: var(--bg-pos, center center);
  display: block;
  max-width: none;   /* override the global img { max-width:100% } */
}

/* content sits above the photo layer */
.photo-band > :not(.bg-parallax),
.otde-wrap > :not(.bg-parallax) { position: relative; z-index: 1; }

/* ===========================================================
   Off The Deep End photo — the album section and the
   full-bleed quotes band. Fully transparent: no scrim, so the
   photo reads clearly.
   =========================================================== */
.otde-bg {
  position: relative;
  --bg: url('../images/otde-hero.jpg');
  background-color: var(--olive-dark); /* shows while the photo loads */
  color: var(--cream);
}

/* ===========================================================
   Full-bleed photo bands (the parallax strips on maclayne.com)
   =========================================================== */
.photo-band {
  position: relative;
  height: clamp(260px, 52vh, 560px);
  background-color: transparent;   /* see note on .otde-wrap */
}
.photo-band--aug2025 {
  --bg: url('../images/maclayneaug2025.jpg');
  /* tall portrait photo: the sky fills the top two thirds, so pull the
     crop down to keep her figure and the hill in frame */
  --bg-pos: center 72%;
}

/* ===========================================================
   Nav
   =========================================================== */
.site-nav {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: center; justify-content: space-between;
  padding: 0.8rem 1.5rem;
  /* no backdrop-filter: a blurred sticky header over moving content is
     the single worst-case scroll cost in WebKit (re-blurs the full
     viewport width every frame). Near-opaque instead. */
  background: rgba(255, 250, 243, 0.96);
  border-bottom: 1px solid rgba(17,17,17,0.06);
}
/* The "Maclayne" wordmark is always this image, never live text */
.nav-mark { display: inline-flex; align-items: center; text-decoration: none; }
.nav-mark-img { height: 26px; width: auto; }
.site-nav nav { display: flex; align-items: center; gap: 1.3rem; }
.site-nav nav a { text-decoration: none; font-size: 0.95rem; font-weight: 600; opacity: 0.85; }
.site-nav nav a:hover { opacity: 1; }
.nav-cta {
  background: var(--purple); color: #fff !important; padding: 0.5rem 1rem;
  border-radius: 999px; opacity: 1 !important;
}
.nav-toggle { display: none; flex-direction: column; gap: 4px; background: none; border: 0; cursor: pointer; }
.nav-toggle span { width: 22px; height: 2px; background: var(--ink); }

@media (max-width: 900px) {
  .nav-toggle { display: flex; }
  .site-nav nav {
    position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; align-items: flex-start; gap: 0;
    background: var(--cream); border-bottom: 1px solid rgba(17,17,17,0.08);
    max-height: 0; overflow: hidden; transition: max-height 0.25s ease;
  }
  .site-nav nav.open { max-height: 520px; }
  .site-nav nav a { padding: 0.85rem 1.5rem; width: 100%; }
  .nav-cta { border-radius: 0; text-align: center; }
}

/* ===========================================================
   Hero — wordmark sits low in the frame so the photo reads
   =========================================================== */
.hero {
  min-height: 92vh; display: flex; flex-direction: column;
  align-items: center; justify-content: center; text-align: center;
  /* pushes the lockup down the frame so it sits over the grass rather
     than across her face */
  padding-top: 42vh;
  padding-bottom: 6vh;
}
.hero-inner { max-width: 1120px; width: 100%; }
.hero-wordmark-heading { margin: 0 0 1.4rem; text-align: center; }
.hero-wordmark-img {
  display: block; margin: 0 auto;
  width: clamp(280px, 62vw, 780px);
  height: auto;
}
/* Tagline: a soft rounded plate. A true ellipse (border-radius: 50%)
   tapers to points at the left and right edges, so this uses a large
   fixed radius instead — rounded ends, no pinch. */
.hero-tag {
  display: inline-block;
  font-size: clamp(0.9rem, 1.8vw, 1.1rem);
  font-weight: 600;
  line-height: 1.4;
  background: rgba(240, 216, 255, 0.85);
  color: var(--ink);
  padding: 0.85rem 2.25rem;
  border-radius: 999px;
  margin: 0;
  max-width: 94%;
}
@media (min-width: 860px) {
  .hero-tag { white-space: nowrap; padding: 0.95rem 2.75rem; }
}
.scroll-cue {
  position: absolute; bottom: 1.6rem; left: 50%; transform: translateX(-50%);
  color: var(--cream); font-size: 1.6rem; text-decoration: none;
  text-shadow: 0 2px 8px rgba(0,0,0,0.5);
  animation: bob 2.2s ease-in-out infinite;
}
@keyframes bob {
  0%,100% { transform: translate(-50%, 0); }
  50%     { transform: translate(-50%, 8px); }
}

/* ===========================================================
   New album / signup
   =========================================================== */
.album-teaser-photo {
  width: 100%; max-width: 480px; height: auto;
  aspect-ratio: 768 / 710; object-fit: cover;
  margin: 0 auto 2rem; border-radius: var(--radius);
  box-shadow: 0 12px 34px rgba(17,17,17,0.16);
}
.signup-form {
  display: flex; gap: 0.6rem; max-width: 430px;
  margin: 0 auto 0.5rem; flex-wrap: wrap; justify-content: center;
}
.signup-form input {
  flex: 1 1 220px; padding: 0.8rem 1rem; border-radius: 999px;
  border: 1px solid rgba(17,17,17,0.2); font-size: 1rem; font-family: inherit;
}
.btn, .signup-form button {
  padding: 0.8rem 1.6rem; border-radius: 999px; border: 0; cursor: pointer;
  font-family: var(--font-body); font-weight: 700; font-size: 1rem;
  text-decoration: none; display: inline-block;
}
.signup-form button, .btn--primary { background: var(--ink); color: var(--cream); }
.signup-form button:hover, .btn--primary:hover { background: var(--purple); }
.form-note { min-height: 1.4em; font-size: 0.9rem; opacity: 0.75; }

.afa-credit { margin-top: 2.75rem; }
.afa-credit__label {
  font-size: 0.9rem; letter-spacing: 0.02em; opacity: 0.7; margin-bottom: 0.8rem;
}
/* AFA logo: source PNG already has transparency, so no blend hack needed */
.afa-logo { margin: 0 auto; width: 100%; max-width: 260px; height: auto; }

/* ===========================================================
   Upcoming shows — each card is one big link to its ticket page
   =========================================================== */
.shows-list { list-style: none; margin: 2rem 0 0; padding: 0; display: grid; gap: 1rem; }
.show-card {
  display: grid; grid-template-columns: 96px 1fr auto;
  align-items: center; gap: 1.25rem;
  background: #fff; border-radius: var(--radius); padding: 1.1rem 1.4rem;
  box-shadow: 0 6px 20px rgba(17,17,17,0.07);
  text-decoration: none; color: var(--ink);
  transition: background-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
}
.show-card:hover, .show-card:focus-visible {
  background: var(--lavender);
  box-shadow: 0 10px 26px rgba(17,17,17,0.14);
  transform: translateY(-2px);
}
.show-date {
  font-family: var(--font-display); text-align: center; line-height: 1.1;
  background: var(--lavender); border-radius: 12px; padding: 0.6rem 0.4rem;
  transition: background-color 0.2s ease;
}
.show-card:hover .show-date { background: #fff; }
.show-date .month { display: block; font-size: 0.72rem; letter-spacing: 0.06em; }
.show-date .day { display: block; font-size: 1.5rem; }
.show-venue { display: block; }
.show-venue-name {
  display: block; font-size: 1.1rem; font-weight: 700; margin-bottom: 0.2em;
}
.show-venue-meta { display: block; font-size: 0.95rem; opacity: 0.75; }
.show-chevron {
  width: 22px; height: 22px; justify-self: end; flex-shrink: 0;
  opacity: 0.4; transition: opacity 0.2s ease, transform 0.2s ease;
}
.show-card:hover .show-chevron { opacity: 1; transform: translateX(3px); }
@media (max-width: 620px) {
  .show-card { grid-template-columns: 70px 1fr auto; gap: 1rem; padding: 1rem; }
}

/* ===========================================================
   Off The Deep End — square cover art beside a wider Spotify
   player, both locked to the same height so they line up.
   352px is the Spotify embed's own fixed render height.
   =========================================================== */
.album-title {
  font-size: clamp(1.8rem, 4.4vw, 2.9rem);
  line-height: 1.15;
  color: var(--lavender);
}
.album-row {
  display: flex; align-items: stretch; justify-content: center;
  gap: 1.5rem; flex-wrap: wrap; margin-top: 2.5rem;
}
.album-cover {
  width: var(--player-h); height: var(--player-h);
  aspect-ratio: 1 / 1; object-fit: cover;
  border-radius: var(--radius);
  box-shadow: 0 18px 44px rgba(0,0,0,0.55);
  flex: 0 0 auto;
}
.spotify-embed {
  width: 460px; height: var(--player-h);
  max-width: 100%;
  border-radius: 14px; overflow: hidden;
  box-shadow: 0 18px 44px rgba(0,0,0,0.5);
  flex: 0 1 460px;
}
.spotify-embed iframe { display: block; width: 100%; height: 100%; }
#music { padding-bottom: 2.5rem; }   /* quotes follow directly; no need for full section gap */
@media (max-width: 820px) {
  /* stacked: let the art scale with the viewport instead of a fixed px */
  .album-cover { width: min(352px, 100%); height: auto; }
  /* the album section's bottom padding and the quotes band's top padding
     stack to 10rem when the layout is a single column — tighten both */
  #music { padding-bottom: 2rem; }
  .otde-quotes { padding-top: 2.5rem; }
  /* Natural order on mobile: title -> artwork -> player -> quotes.
     (An earlier version flattened this section with display:contents to
     move the quotes up. That also detached the artwork and player from
     the rule that lifts content above the fixed photo, so they were
     painted behind it. Don't reintroduce it.) */
}

/* ===========================================================
   Off The Deep End quotes — full-bleed photo band under the
   album artwork and stream player
   =========================================================== */
.otde-quotes {
  position: relative;
  --bg: url('../images/otde-hero.jpg');
  background-color: transparent;   /* nothing behind the photo but page cream */
  padding: 2.5rem 1.5rem 5rem;     /* tighter above the quotes (they follow the player) */
  color: var(--cream);
}
.otde-quotes-grid {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 2rem; align-items: start;
}
.otde-quote { margin: 0; text-align: center; }
/* Keep each quote narrow so it wraps to a few short lines and stays
   clear of her figure in the middle of the photo, as on her site. */
.otde-quote p,
.otde-quote cite { max-width: 420px; margin-left: auto; margin-right: auto; }
/* both quotes sit at the same height */
.otde-quote--lower { padding-top: 0; }
.otde-quote p {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: clamp(1.3rem, 2.4vw, 1.9rem);
  line-height: 1.25;
  color: var(--lavender);
  margin: 0 auto 0.8em;
  text-shadow: 0 2px 12px rgba(0,0,0,0.6);
}
.otde-quote p em { font-style: italic; }
.otde-quote cite {
  display: block; font-style: normal;
  font-size: clamp(0.95rem, 1.4vw, 1.1rem);
  color: var(--cream); margin-bottom: 1.4em;
  text-shadow: 0 2px 10px rgba(0,0,0,0.7);
}
@media (max-width: 760px) {
  .otde-quotes-grid { grid-template-columns: 1fr; gap: 3rem; }
}

/* Pill buttons — olive/green base, darker purple on hover */
.pill {
  display: inline-block; text-decoration: none;
  padding: 0.85rem 1.9rem; border-radius: 999px;
  font-size: 0.95rem; font-weight: 600;
  background: var(--olive);
  color: var(--ink);
  transition: background-color 0.2s ease, color 0.2s ease;
}
.pill:hover, .pill:focus-visible {
  background: #8e5f9c;   /* darker purple than --purple, for white text contrast */
  color: #fff;
}


/* ===========================================================
   General press quotes — inside .otde-wrap, so they sit over the
   same road photo as the album quotes above them. Set in lavender
   and cream like those, with text shadow for legibility.
   =========================================================== */
.quotes-on-photo {
  background: transparent;
  padding-top: 0;          /* the quotes band above already provides the gap */
  color: var(--cream);
}
.quote-grid {
  display: grid; gap: 2.5rem;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
/* each quote is a cream card floating over the photo */
.big-quote {
  margin: 0;
  padding: 1.6rem 1.7rem;
  background: rgba(255, 250, 243, 0.72);   /* translucent: the photo reads through */
  border-radius: var(--radius);
  box-shadow: 0 12px 34px rgba(0,0,0,0.22);
  display: flex; flex-direction: column;
}
.big-quote p {
  font-size: 1.05rem; font-style: italic; line-height: 1.55;
  color: var(--ink);
  margin: 0 0 1em;
  flex: 1;                 /* pushes the citation to the card's foot */
}
.big-quote cite {
  font-style: normal; font-size: 0.86rem; font-weight: 600;
  color: var(--purple);
}

/* ===========================================================
   About — bio left, vertical portrait right
   =========================================================== */
.about-grid {
  display: grid; grid-template-columns: 1.55fr 1fr;
  gap: 3rem; align-items: start;
}
.about-copy p { margin: 0 0 1.2em; }
.about-copy p:last-child { margin-bottom: 0; }
.about-photo {
  width: 100%; height: auto;
  /* vertical crop matching her site's portrait framing */
  aspect-ratio: 531 / 615;
  object-fit: cover;
  object-position: 46% 35%;
  border-radius: var(--radius);
  box-shadow: 0 14px 36px rgba(17,17,17,0.2);
  position: sticky; top: 6rem;
}
@media (max-width: 860px) {
  .about-grid { grid-template-columns: 1fr; }
  .about-photo {
    position: static; max-width: 380px; margin: 0 auto;
    grid-row: 1; /* photo above the bio on narrow screens */
  }
  .about-copy { grid-row: 2; }
}

/* ===========================================================
   Videos — embedded players: one featured, then a row of three
   =========================================================== */
.video-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.25rem; }
.video-card {
  border-radius: var(--radius); overflow: hidden;
  background: #fff; box-shadow: 0 6px 18px rgba(17,17,17,0.08);
}
/* 16:9 box the iframe fills — keeps layout stable before it loads */
.video-frame { position: relative; aspect-ratio: 16 / 9; background: #000; }
.video-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.video-card span { display: block; padding: 0.9rem 1rem; font-weight: 700; font-size: 0.95rem; }

.video-card--featured {
  max-width: 860px; margin: 0 auto 1.25rem;
}
.video-card--featured span { padding: 1.1rem 1.3rem; font-size: 1.15rem; }

.youtube-link { display: inline-flex; align-items: center; gap: 0.5rem; text-decoration: none; font-weight: 700; }
.youtube-link svg { fill: #FF0000; }
@media (max-width: 760px) {
  .video-grid { grid-template-columns: 1fr; }
}

/* ===========================================================
   Merch
   =========================================================== */
.merch-hero {
  width: 100%; max-width: 560px; height: auto;
  aspect-ratio: 768 / 516; object-fit: cover;
  margin: 0 auto 1.5rem; border-radius: var(--radius);
}
.merch-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 1rem; margin: 0 0 2rem;
}
.merch-grid img {
  width: 100%; height: auto;
  aspect-ratio: 1 / 1; object-fit: cover;
  border-radius: var(--radius);
}

/* ===========================================================
   Instagram
   =========================================================== */
.ig-header { display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem; }
.ig-avatar {
  width: 62px; height: 62px; border-radius: 50%;
  object-fit: cover; flex-shrink: 0;
  /* Instagram's gradient ring */
  border: 2px solid #fff;
  box-shadow: 0 0 0 4px var(--purple);
}
.ig-handle { font-weight: 700; margin: 0 0 0.2em; }
.ig-follow { font-size: 0.9rem; text-decoration: underline; }
/* Four across, and 4:5 portrait tiles — the shape Instagram's grid and
   feed posts use now, rather than the old square. Her photos are already
   roughly 4:5, so they crop very little. */
.ig-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; }
.ig-grid .ig-tile {
  aspect-ratio: 4 / 5; background: var(--lavender) center/cover no-repeat;
  position: relative; overflow: hidden; display: block;
}
.ig-tile-badge {
  position: absolute; top: 10px; right: 10px;
  width: 26px; height: 26px;
  filter: drop-shadow(0 1px 3px rgba(0,0,0,0.6));
}
/* 9 tiles are rendered. At 4 across the ninth would sit alone on a
   third row, so it's hidden here; at 3 across (phones) all 9 show as a
   full 3 x 3. Either way, no half-empty last row. */
.ig-grid .ig-tile:nth-child(9) { display: none; }
@media (max-width: 560px) {
  /* 4 tiles get too small to read on a phone; Instagram itself drops to 3 */
  .ig-grid { grid-template-columns: repeat(3, 1fr); gap: 3px; }
  .ig-grid .ig-tile:nth-child(9) { display: block; }
  .ig-tile-badge { width: 20px; height: 20px; top: 8px; right: 8px; }
}

/* ===========================================================
   Contact
   =========================================================== */
.contact-email { margin: 0 0 1.75rem; font-weight: 600; }
.contact-card {
  background: #fff; border-radius: var(--radius); padding: 2rem;
  box-shadow: 0 12px 34px rgba(17,17,17,0.14);
  display: flex; flex-direction: column; gap: 1.1rem;
}
/* minmax(0, 1fr) rather than 1fr: an <input> has an intrinsic
   min-content width from its default size attribute, which can push a
   plain 1fr track wider than its container and spill the fields out
   past the edge of the card. Capping the minimum at 0 (plus min-width
   on the controls) keeps them inside. */
.field-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.1rem; }
@media (max-width: 560px) { .field-row { grid-template-columns: 1fr; } }
.field { display: flex; flex-direction: column; gap: 0.4rem; text-align: left; min-width: 0; }
.field label { font-size: 0.85rem; font-weight: 700; }
.field input, .field textarea {
  padding: 0.75rem 0.9rem; border-radius: 10px; border: 1px solid rgba(17,17,17,0.2);
  font-family: inherit; font-size: 1rem; resize: vertical;
  width: 100%; min-width: 0; max-width: 100%;
}
.contact-card .btn { align-self: flex-start; }

/* ===========================================================
   Footer
   =========================================================== */
.site-footer {
  position: relative;
  text-align: center; padding: 3.5rem 1.5rem 2rem;
  background: var(--ink); color: var(--cream);
}
.footer-logo {
  height: 44px; width: auto; margin: 0 auto 0.8rem;
  filter: brightness(0) invert(1);
}
.footer-tag { opacity: 0.8; max-width: 48ch; margin: 0 auto 1.4rem; }
.footer-social { display: flex; justify-content: center; gap: 1.3rem; margin-bottom: 1.5rem; }
.footer-social a { text-decoration: none; font-weight: 700; }
.footer-credits, .footer-copyright { font-size: 0.8rem; opacity: 0.55; margin: 0.3em 0; }

/* ===========================================================
   Accessibility
   =========================================================== */
:focus-visible { outline: 3px solid var(--purple); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .scroll-cue { animation: none; }
}

/* ===========================================================
   Photo backgrounds
   All screen sizes: the fixed <img> layers above. The Off The Deep
   End photo is ONE layer on .otde-wrap, covering both sections.
   No JavaScript, no background-attachment.
   =========================================================== */
.otde-wrap {
  position: relative;
  --bg: url('../images/otde-hero.jpg');
  /* transparent, like Hostinger's wrapper. A dark fallback here leaked
     through for a frame during scroll on iOS and tinted Safari's bottom
     toolbar green. With nothing behind the photo but the page's cream,
     any exposed sliver matches the toolbar and is invisible. */
  background-color: transparent;
}
/* the sections inside must not paint over the photo */
.otde-bg, .otde-quotes { background-color: transparent; }

/* Mobile uses the same fixed layers as desktop — this is how Hostinger
   does it too (their 100lvh / svh units exist for iOS). Earlier mobile
   attempts here failed under the backdrop-filter frame-rate collapse,
   not on their own merits.

   IF THIS MISBEHAVES ON A PHONE, paste this block back in to fall back
   to scrolling photos on narrow screens:

   @media (max-width: 900px) {
     .bg-parallax { display: none; }
     .photo-band, .otde-wrap {
       background-image: var(--bg); background-size: cover;
       background-position: var(--bg-pos, center center);
       background-repeat: no-repeat;
     }
   }
*/
