/* Shared perf + motion layer. Linked on every page before its own <style>. */

/* ── CUSTOM CURSOR ──
   Was `cursor: url(1px.png), none !important` on `*, ::before, ::after`, which
   every element in the document then carried. site.js adds .has-custom-cursor
   only on desktop, so touch visitors keep the native cursor at no cost. */
html.has-custom-cursor,
html.has-custom-cursor body,
html.has-custom-cursor * { cursor: none !important; }

/* Hold the dot/ring back until the pointer moves so they don't flash centre-screen. */
html.has-custom-cursor #cur,
html.has-custom-cursor #cur-ring {
  opacity: 0;
  transition: opacity 0.35s ease, border-color 0.4s ease, background 0.4s ease;
}
html.cursor-live #cur { opacity: 1; }
html.cursor-live #cur-ring { opacity: 0.7; }

/* ── LAYER HYGIENE ──
   will-change pins a GPU layer for the life of the page. A reveal never
   animates again once it has played, so give the memory back. */
.reveal.visible,
.m-item.visible,
.album-folder.visible { will-change: auto; }

/* Gallery lays out 300+ tiles at once. Skip layout/paint/decode off-screen;
   `auto` remembers each real height so scrolling back stays put. */
.m-item {
  content-visibility: auto;
  contain-intrinsic-size: auto 340px;
}

/* ── LOW POWER / TOUCH / REDUCED MOTION ── */
html.perf-mode { scroll-behavior: auto !important; }

html.perf-mode #cur,
html.perf-mode #cur-ring,
html.perf-mode #petals,
html.perf-mode .petal,
html.perf-mode .floating-accent,
html.perf-mode .hero-orb,
html.perf-mode .orb,
html.perf-mode .orb-1,
html.perf-mode .orb-2,
html.perf-mode .dust,
html.perf-mode .sparkle,
html.perf-mode .noise-overlay { display: none !important; }

/* Blur is the most expensive thing here. Flat translucency instead. */
html.perf-mode .liquid-glass,
html.perf-mode header,
html.perf-mode .mobile-menu,
html.perf-mode .intro-img,
html.perf-mode .intro-text-card,
html.perf-mode .tl-item,
html.perf-mode .tour-card,
html.perf-mode .show-card,
html.perf-mode .album-info,
html.perf-mode .image-modal,
html.perf-mode .lightbox,
html.perf-mode .filter-wrap,
html.perf-mode .filter-btn,
html.perf-mode .m-item,
html.perf-mode .fact-card,
html.perf-mode .social-btn,
html.perf-mode .page-eyebrow,
html.perf-mode .hero-badge,
html.perf-mode .hero-desc,
html.perf-mode .btn,
html.perf-mode .btn-glass,
html.perf-mode .btn-glass-dark,
html.perf-mode .badge,
html.perf-mode .folder-era-badge,
html.perf-mode .stream-btn {
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
}

html.perf-mode .ambient-bg,
html.perf-mode .ambient-bg::after { animation: none !important; }

/* Specificity here has to clear the per-page rules, since this file is
   linked ahead of their <style> blocks. */
html.reduce-motion *,
html.reduce-motion *::before,
html.reduce-motion *::after {
  animation-duration: 0.01ms !important;
  animation-iteration-count: 1 !important;
  animation-delay: 0ms !important;
  transition-duration: 0.01ms !important;
  transition-delay: 0ms !important;
  scroll-behavior: auto !important;
}

html.reduce-motion .reveal,
html.reduce-motion .m-item,
html.reduce-motion .album-folder,
html.reduce-motion .stagger-2,
html.reduce-motion .stagger-3,
html.reduce-motion .stagger-4,
html.reduce-motion .stagger-5,
html.reduce-motion #app-wrapper,
html.reduce-motion .header-wrapper {
  opacity: 1 !important;
  filter: none !important;
  transform: none !important;
}

/* ── palette ──
   The pages used to hard-code rgba(42, 27, 20, x) and friends. Those are all
   rgba(var(--ink), x) now so one set of triplets swings the whole site.

   --ink and --hilite each used to cover three jobs, which only works while the
   palette points one way. Dark mode flips the foreground, and the shadows came
   with it: every card grew a pale halo and every hairline went chalky. So the
   two other jobs have their own tokens now — --shade for anything in a shadow,
   --rim for hairlines and inset rim light. In light they are the values those
   places already had, so nothing about light mode moved. */
:root {
  /* Says out loud which way round the page is. The site brings its own dark
     mode, so `only` is the part that matters: without it Chrome's auto-dark
     inverts everything a second time on top of the theme below. It also gets
     the scrollbar and form controls to follow along. */
  color-scheme: only light;

  --ink:     42, 27, 20;
  --surface: 255, 253, 249;
  --hilite:  255, 255, 255;
  --rim:     255, 255, 255;
  --shade:   42, 27, 20;
  --rose:    166, 25, 46;
  --sky:     191, 225, 246;
  --blush:   253, 229, 233;

  /* Five copies of these had drifted — index was running the glass at 0.35
     alpha and 185% saturation while the other four sat at 0.65/180%, which is
     why its header looked like glass and theirs looked like a grey bar. One
     copy now, and the pages no longer declare them. */
  --glass-bg:     rgba(var(--surface), 0.35);
  --glass-blur:   blur(24px) saturate(185%);
  --glass-border: rgba(var(--rim), 0.7);
  --glass-shadow: 0 12px 40px rgba(var(--shade), 0.08),
                  inset 0 1px 2px rgba(var(--rim), 0.8);
}

/* html.dark outranks the :root block each page ships, so these win wherever
   they are declared. Light stays the default — dark is opt-in from the header. */
html.dark {
  color-scheme: only dark;

  --ink:     238, 232, 229;
  --surface: 26, 20, 26;
  /* stays white — this is now only sheen gradients and text sitting on a
     coloured chip, both of which want white in either theme */
  --hilite:  255, 253, 250;
  /* hairlines and rim light. Runs at 0.03–1.0 alpha, so it has to read as a
     quiet lift at 0.7 rather than a drawn line. */
  --rim:     92, 78, 90;
  --shade:   0, 0, 0;
  --rose:    236, 108, 132;
  --sky:     122, 168, 206;
  --blush:   86, 52, 64;

  /* a touch more opaque than light: the nav sits over photos on four of the
     five pages and 0.35 was not enough to keep the links legible */
  --glass-bg:     rgba(var(--surface), 0.62);
  --glass-shadow: 0 14px 44px rgba(0, 0, 0, 0.55),
                  inset 0 1px 0 rgba(var(--rim), 0.55);

  --butter:    #171215;
  --espresso:  #EEE7E2;
  --cherry:    #F07086;
  /* these two are chip backgrounds as often as they are accents, and the text
     on those chips is var(--espresso), which just went light — so they have to
     go dark with it */
  --baby-blue: #24333F;
  --pale-pink: #2E2029;
  --gold:      #F0C64B;
}

/* the ring sits on the page background, so it needs the one blue that did not
   go dark */
html.dark #cur-ring { border-color: #7FB2D4; }
html.dark .noise-overlay { opacity: 0.03; }

/* ── theme toggle ── */
.theme-toggle {
  width: 36px; height: 36px; border-radius: 50%; flex-shrink: 0;
  border: 1.5px solid rgba(var(--rose), 0.5);
  background: rgba(var(--surface), 0.9);
  display: grid; place-items: center; cursor: pointer; padding: 0;
  box-shadow: inset 0 2px 5px rgba(var(--shade), 0.1), 0 2px 8px rgba(var(--rose), 0.15);
  transition: transform 0.45s cubic-bezier(0.34, 1.56, 0.64, 1),
              border-color 0.4s ease, background 0.4s ease;
}
.theme-toggle:hover { transform: rotate(-22deg) scale(1.08); }
.theme-toggle:active { transform: scale(0.94); }

/* one circle doing both jobs: a filled disc for the sun, then the fill slides
   off to one side and what is left reads as a crescent */
.tt-icon {
  width: 15px; height: 15px; border-radius: 50%;
  background: transparent;
  box-shadow: inset 0 0 0 8px var(--cherry);
  transition: box-shadow 0.45s cubic-bezier(0.22, 1, 0.36, 1),
              transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
html.dark .tt-icon {
  box-shadow: inset -5px -4px 0 0 var(--cherry);
  transform: rotate(-20deg);
}

/* Only while the theme is actually changing. Leaving this on would put a
   350ms colour transition on every hover state in the site. */
html.theme-anim,
html.theme-anim body,
html.theme-anim .ambient-bg,
html.theme-anim .section-wrapper,
html.theme-anim header,
html.theme-anim .liquid-glass {
  transition: background-color 0.4s ease, background 0.4s ease, color 0.4s ease;
}

/* --baby-blue and --pale-pink fill chips whose text is var(--espresso), so both
   had to go dark with it. These few places use them as text sitting straight on
   the page background instead, and need the light version back. */
html.dark .sec-title em,
html.dark .sp-3 { color: #8FC0DE; }
html.dark .era-year { color: #6B4653; }

/* the mark is a dark monogram on transparent */
html.dark .nav-logo img,
html.dark .footer-logo img,
html.dark .header-live-clock::before { filter: brightness(0) invert(0.92); }

/* var(--espresso) backs these tiles until their photo paints, and it just went
   light — without this you get a pale flash on a slow connection */
html.dark .g-card { background-color: #241C21; }
/* the caption scrim leaned on --rose being dark. It isn't any more, so give the
   bottom of the gradient its own colour rather than the token. */
html.dark .g-overlay {
  background: linear-gradient(to top, rgba(104, 26, 42, 0.95), rgba(0, 0, 0, 0.45) 40%, transparent 80%);
}

/* Anything built as "dark chrome on a light page" flips to a light slab in dark
   mode. On a chip or a hover state that reads as a deliberate inversion and
   looks fine; on a full-bleed panel it is a headlight. These three are panels. */
html.dark .section-wrapper.dark,
html.dark .bottom-cta {
  background: #241A22;
  /* Everything inside is written for a dark panel: --butter is the foreground,
     --baby-blue the accent, --espresso the text on the one light chip. All four
     flipped with the theme, so flip them back for this subtree rather than
     restyling every child by hand. */
  --butter:    #F4EEEA;
  --espresso:  #201820;
  --baby-blue: #BFE1F6;
  --pale-pink: #FDE5E9;
  color: var(--butter);
}
/* this one reaches for --surface as a dimmed foreground, which no longer is one */
html.dark .bottom-cta p { color: rgba(244, 238, 234, 0.62); }

/* The hero's two buttons are a light pill and a dark pill. Inverted they came
   out as two pale pills with nothing between them, so the second one takes the
   accent in dark instead of the ink. */
html.dark .btn-glass-dark {
  background: rgba(var(--rose), 0.85);
  border-color: rgba(var(--hilite), 0.28);
  color: #1B1116;
}
html.dark .btn-glass-dark:hover {
  background: rgba(var(--rose), 1);
  color: #1B1116;
}
html.dark .btn-glass { color: var(--espresso); }

/* ── ambient wash ──
   The page background is three big radial gradients in --sky, --blush and
   --surface. At the light alphas they read as pastel on cream; over a dark page
   the same alphas just lift everything to a flat grey-blue haze. Same shapes,
   much less of them, and the middle one lights rather than fills. */
html.dark .ambient-bg {
  background-image:
    radial-gradient(circle at 15% 25%, rgba(var(--sky), 0.16) 0%, transparent 52%),
    radial-gradient(circle at 85% 75%, rgba(var(--rose), 0.13) 0%, transparent 55%),
    radial-gradient(circle at 50% 45%, rgba(64, 44, 60, 0.5) 0%, transparent 72%);
}
html.dark .ambient-bg::after {
  background-image:
    radial-gradient(circle at 20% 30%, rgba(var(--rose), 0.1) 0%, transparent 55%),
    radial-gradient(circle at 80% 70%, rgba(255, 215, 0, 0.05) 0%, transparent 60%);
}
/* the hero orbs are the same story one layer up */
html.dark .hero-orb { opacity: 0.45; }

/* ── header ──
   index had the good version of this — glass at 0.35 alpha, an entrance, a
   hover, a lit underline — and the other four had drifted into a plain blurred
   bar. Rather than keep five copies in step the whole treatment lives here. The
   html prefix is what carries it past the per-page blocks, which load after
   this file. */
html .header-wrapper {
  opacity: 0;
  transform: translateZ(0);
  animation: navDrop 1.1s cubic-bezier(0.22, 1, 0.36, 1) 0.35s forwards;
}
@keyframes navDrop {
  from { opacity: 0; transform: translate3d(0, -40px, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

html header {
  max-width: 870px;
  padding: 9px 15px 9px 20px;
  border-radius: 100px;
  transition: background 0.5s var(--fluid), box-shadow 0.5s var(--fluid),
              padding 0.45s var(--fluid), max-width 0.45s var(--fluid);
}
html header:hover {
  background: rgba(var(--surface), 0.62);
  box-shadow: 0 15px 50px rgba(var(--shade), 0.14),
              inset 0 1px 2px rgba(var(--rim), 1);
}
/* tightens once you are past the hero, so the bar stops competing with the
   page — site.js sets the class from the one shared scroll listener */
html.scrolled header {
  max-width: 800px;
  padding: 6px 13px 6px 17px;
  background: rgba(var(--surface), 0.55);
  box-shadow: 0 10px 34px rgba(var(--shade), 0.13),
              inset 0 1px 2px rgba(var(--rim), 0.9);
}
html.dark header:hover,
html.dark.scrolled header { background: rgba(var(--surface), 0.8); }

/* The pill floats over a sliver of page and can afford to be see-through. The
   mobile panel drops over the middle of the hero, and at the pill's alpha the
   wordmark reads straight through the links. */
html .mobile-menu { background: rgba(var(--surface), 0.93); }

html .nav-logo {
  transition: transform 0.6s var(--spring), filter 0.6s ease;
}
html .nav-logo:hover {
  transform: scale3d(1.15, 1.15, 1) rotate(-5deg);
  filter: drop-shadow(0 4px 8px rgba(var(--rose), 0.35));
}
html .nav-links a:hover,
html .nav-links a.nav-active { color: var(--cherry); }
html .nav-links a::after {
  border-radius: 2px;
  box-shadow: 0 0 8px rgba(var(--rose), 0.6);
}
