/* ============================================================
   Gwonho
   Liquid glass: a living colour field under frosted panels.
   ============================================================ */

/* ---------- RESET ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }
img, svg { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }
ol, ul { list-style: none; }

/* ---------- TOKENS ---------- */
:root {
  --base:    #05060a;
  --base-2:  #090b12;
  --text:    #f4f5fa;
  --text-2:  rgba(244, 245, 250, 0.66);
  --text-3:  rgba(244, 245, 250, 0.42);

  /* Section colours, re-set by JS as you scroll */
  --c1: #6d5cff;
  --c2: #22d3ff;
  --c3: #ff5fa2;

  --glass:        rgba(255, 255, 255, 0.038);
  --glass-strong: rgba(255, 255, 255, 0.07);
  --edge:         rgba(255, 255, 255, 0.11);
  --edge-bright:  rgba(255, 255, 255, 0.55);

  --font: "Inter", -apple-system, BlinkMacSystemFont, "SF Pro Display",
          "Helvetica Neue", "Segoe UI", Arial, sans-serif;

  --ease:     cubic-bezier(0.22, 1, 0.36, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  --gutter: clamp(1.25rem, 5vw, 5rem);
  --maxw: 1180px;

  /* One knob for vertical rhythm. Every section uses it, so the whole
     page tightens or loosens from here. */
  --section-y: clamp(4.5rem, 9vw, 7.5rem);

  /* How fast the whole page changes colour between sections */
  --tint-speed: 1.4s;
}

/* ============================================================
   CROSS PAGE TRANSITION
   Chrome and Edge morph between index.html and photography.html
   natively. Browsers without view transitions get the JS fade in
   main.js instead, so the navigation never looks abrupt.
   ============================================================ */
@view-transition { navigation: auto; }

::view-transition-old(root) {
  animation: vtOut 0.42s var(--ease) both;
}
::view-transition-new(root) {
  animation: vtIn 0.55s var(--ease-out) both;
}
@keyframes vtOut {
  to { opacity: 0; transform: scale(0.985); filter: blur(6px); }
}
@keyframes vtIn {
  from { opacity: 0; transform: scale(1.015); filter: blur(10px); }
}

/* The heading is shared between the teaser and the gallery page, so it
   travels between the two rather than cross-fading. */
#galleryTitle { view-transition-name: gallery-title; }
.field { view-transition-name: liquid-field; }

/* JS fallback for browsers without the API */
body.leaving {
  opacity: 0; transform: scale(0.99); filter: blur(5px);
  transition: opacity 0.34s var(--ease), transform 0.34s var(--ease),
              filter 0.34s var(--ease);
}

/* ---------- BASE ---------- */
html { scroll-behavior: smooth; scroll-padding-top: 90px; }

body {
  font-family: var(--font);
  background: var(--base);
  color: var(--text);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

/* Content rides above the fixed field so glass has something to sample */
main { position: relative; z-index: 1; }

.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter); }
.wrap.narrow { max-width: 760px; }

::selection { background: var(--c2); color: #05060a; }
:focus-visible { outline: 2px solid var(--c2); outline-offset: 4px; border-radius: 6px; }

.svg-defs { position: absolute; width: 0; height: 0; }

/* ============================================================
   1. THE LIQUID FIELD
   Four huge soft light sources drifting forever behind everything.
   Their colours are swapped per section by JS, and the long
   transition is what makes the whole page melt between hues.
   ============================================================ */
/* Must stay at z-index 0, never negative. A negative z-index would put
   this behind the root background, where backdrop-filter cannot sample
   it, and every glass panel would composite against flat black. */
.field {
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background:
    /* Lift the foot of the frame. Without this the top gradient falls
       to flat base and the bottom of every screen reads as a black bar. */
    radial-gradient(130% 70% at 50% 100%,
      color-mix(in srgb, var(--c2) 10%, var(--base-2)), transparent 72%),
    radial-gradient(120% 90% at 50% 0%, var(--base-2), var(--base) 70%);
  transition: background var(--tint-speed) var(--ease);
  overflow: hidden;
}

.blobs {
  position: absolute; inset: -30%;
  /* Heavy blur plus a touch of desaturation is what turns four coloured
     circles into something that reads as light rather than paint */
  filter: blur(110px) saturate(88%);
  opacity: 0.52;
}

/* A backgrounded tab should not keep re-running a 110px blur. Nothing
   changes visually, the animation is only paused while the page is
   hidden and resumes on return. */
body.page-hidden .blob {
  animation-play-state: paused;
}

/* Soft-edged light sources, not solid discs. The transparent falloff
   is what keeps this reading as ambient light under glass. */
.blob {
  position: absolute;
  border-radius: 50%;
  mix-blend-mode: screen;
  transition: background var(--tint-speed) var(--ease);
  will-change: transform;
}
.b1 {
  width: 62vw; height: 62vw; left: -6%; top: -4%;
  background: radial-gradient(circle at 50% 50%, var(--c1), transparent 66%);
  animation: float1 44s var(--ease) infinite alternate;
}
.b2 {
  width: 54vw; height: 54vw; right: -8%; top: 12%;
  background: radial-gradient(circle at 50% 50%, var(--c2), transparent 66%);
  animation: float2 53s var(--ease) infinite alternate;
}
.b3 {
  width: 50vw; height: 50vw; left: 22%; bottom: -10%;
  background: radial-gradient(circle at 50% 50%, var(--c3), transparent 68%);
  animation: float3 39s var(--ease) infinite alternate;
  opacity: 0.8;
}
.b4 {
  width: 38vw; height: 38vw; right: 18%; bottom: 6%;
  background: radial-gradient(circle at 50% 50%, var(--c2), transparent 70%);
  animation: float4 61s var(--ease) infinite alternate;
  opacity: 0.55;
}

@keyframes float1 {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  50%  { transform: translate3d(18vw, 12vh, 0) scale(1.25); }
  100% { transform: translate3d(-6vw, 26vh, 0) scale(0.9); }
}
@keyframes float2 {
  0%   { transform: translate3d(0, 0, 0) scale(1.1); }
  50%  { transform: translate3d(-22vw, 18vh, 0) scale(0.85); }
  100% { transform: translate3d(-8vw, -10vh, 0) scale(1.3); }
}
@keyframes float3 {
  0%   { transform: translate3d(0, 0, 0) scale(0.95); }
  50%  { transform: translate3d(20vw, -7vh, 0) scale(1.3); }
  100% { transform: translate3d(-16vw, 5vh, 0) scale(1); }
}
@keyframes float4 {
  0%   { transform: translate3d(0, 0, 0) scale(1.2); }
  50%  { transform: translate3d(-14vw, -4vh, 0) scale(0.85); }
  100% { transform: translate3d(10vw, 10vh, 0) scale(1.15); }
}

/* Film grain keeps the gradients from banding */
.grain {
  position: absolute; inset: -50%;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
  opacity: 0.085; pointer-events: none;
  animation: grainShift 9s steps(6) infinite;
}
@keyframes grainShift {
  0%   { transform: translate(0, 0); }
  20%  { transform: translate(-3%, 2%); }
  40%  { transform: translate(2%, -3%); }
  60%  { transform: translate(-2%, -2%); }
  80%  { transform: translate(3%, 1%); }
  100% { transform: translate(0, 0); }
}

/* Soft light that follows the cursor */
.pointer-glow {
  position: fixed; z-index: 0; left: 0; top: 0;
  width: 58rem; height: 58rem; margin: -29rem 0 0 -29rem;
  border-radius: 50%; pointer-events: none; opacity: 0;
  background: radial-gradient(circle, color-mix(in srgb, var(--c2) 34%, transparent), transparent 62%);
  filter: blur(40px);
  transition: opacity 0.8s var(--ease), background var(--tint-speed) var(--ease);
}
body.has-pointer .pointer-glow { opacity: 0.38; }

/* ============================================================
   2. GLASS
   ============================================================ */
.glass {
  position: relative;
  background: var(--glass);
  -webkit-backdrop-filter: blur(38px) saturate(200%) brightness(108%);
  backdrop-filter: blur(38px) saturate(200%) brightness(108%);
  border: 1px solid var(--edge);
  border-radius: 26px;
  box-shadow:
    /* rim light along the top edge, the tell of a real glass surface */
    inset 0 1px 0 0 var(--edge-bright),
    inset 0 0 0 1px rgba(255, 255, 255, 0.04),
    inset 0 -20px 40px -30px rgba(255, 255, 255, 0.22),
    0 30px 80px -28px rgba(0, 0, 0, 0.55);
  overflow: hidden;
  transition: transform 0.7s var(--ease), background-color 0.7s var(--ease),
              border-color 0.7s var(--ease), box-shadow 0.7s var(--ease);
}

/* A slow highlight that drifts across every panel on its own, so the
   glass looks lit rather than painted even when nothing is happening */
.glass::before {
  content: ""; position: absolute; inset: -1px; pointer-events: none;
  background:
    linear-gradient(115deg,
      transparent 34%,
      rgba(255, 255, 255, 0.05) 47%,
      rgba(255, 255, 255, 0.10) 50%,
      rgba(255, 255, 255, 0.04) 53%,
      transparent 66%),
    radial-gradient(120% 90% at 12% 0%,
      color-mix(in srgb, var(--c1) 14%, transparent), transparent 58%);
  background-size: 260% 100%, 100% 100%;
  background-position: 130% 0, 0 0;
  animation: sheen 14s var(--ease) infinite;
  transition: background var(--tint-speed) var(--ease);
}
@keyframes sheen {
  0%, 100% { background-position: 135% 0, 0 0; }
  50%      { background-position: -35% 0, 0 0; }
}

/* Specular sweep that slides across on hover */
.glass::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(105deg,
    transparent 30%,
    rgba(255, 255, 255, 0.16) 46%,
    rgba(255, 255, 255, 0.05) 54%,
    transparent 68%);
  transform: translateX(-130%);
  transition: transform 1.3s var(--ease-out);
}
.glass:hover::after { transform: translateX(130%); }


/* ============================================================
   3. CHROME
   ============================================================ */
.progress { position: fixed; inset: 0 0 auto 0; height: 2px; z-index: 200; pointer-events: none; }
.progress span {
  display: block; height: 100%; width: 0%;
  background: linear-gradient(90deg, var(--c1), var(--c2), var(--c3));
  box-shadow: 0 0 14px color-mix(in srgb, var(--c2) 60%, transparent);
  transition: width 0.1s linear, background var(--tint-speed) var(--ease);
}

.nav {
  position: fixed; inset: 14px 0 auto 0; z-index: 150;
  display: flex; justify-content: center; padding-inline: var(--gutter);
  transition: transform 0.55s var(--ease), opacity 0.55s var(--ease);
}
.nav.hidden { transform: translateY(-140%); opacity: 0; }

.nav-glass {
  display: flex; align-items: center; gap: clamp(1rem, 3vw, 2rem);
  padding: 9px 9px 9px 20px;
  border-radius: 100px;
  background: rgba(255, 255, 255, 0.06);
  -webkit-backdrop-filter: blur(34px) saturate(200%) brightness(108%);
  backdrop-filter: blur(34px) saturate(200%) brightness(108%);
  border: 1px solid var(--edge);
  box-shadow: inset 0 1px 0 var(--edge-bright), 0 12px 40px -12px rgba(0,0,0,0.65);
}

.nav-logo { font-size: 0.9375rem; font-weight: 600; letter-spacing: -0.01em; }
.nav-links { display: flex; gap: 0.25rem; }
.nav-links a {
  position: relative; font-size: 0.8125rem; color: var(--text-2);
  padding: 7px 14px; border-radius: 100px;
  transition: color 0.4s var(--ease), background-color 0.4s var(--ease);
}
.nav-links a:hover { color: var(--text); background: rgba(255,255,255,0.07); }
.nav-links a.active {
  color: var(--text);
  background: color-mix(in srgb, var(--c2) 22%, rgba(255,255,255,0.06));
}

/* ---------- SOUND PLAYER ---------- */
.player {
  position: fixed; left: var(--gutter); bottom: 22px; z-index: 150;
  display: flex; align-items: center; gap: 12px;
  padding: 10px 16px 10px 10px;
  border-radius: 100px;
  background: rgba(255, 255, 255, 0.06);
  -webkit-backdrop-filter: blur(22px) saturate(190%);
  backdrop-filter: blur(22px) saturate(190%);
  border: 1px solid var(--edge);
  box-shadow: inset 0 1px 0 var(--edge-bright), 0 14px 40px -14px rgba(0,0,0,0.7);
  transition: transform 0.7s var(--ease), box-shadow 0.7s var(--ease);
}
.player:hover { transform: translateY(-2px); }
.player.on {
  box-shadow: inset 0 1px 0 var(--edge-bright),
              0 14px 46px -12px color-mix(in srgb, var(--c2) 45%, transparent);
}

.player-btn {
  width: 34px; height: 34px; flex: none; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(255,255,255,0.09);
  border: 1px solid var(--edge);
  transition: background-color 0.4s var(--ease), border-color 0.4s var(--ease);
}
.player-btn:hover { background: rgba(255,255,255,0.16); }
.player-btn[aria-pressed="true"] {
  background: color-mix(in srgb, var(--c2) 55%, transparent);
  border-color: color-mix(in srgb, var(--c2) 70%, transparent);
}
.power {
  width: 13px; height: 13px; border-radius: 50%;
  border: 1.8px solid currentColor; border-top-color: transparent;
  transform: rotate(-45deg);
  position: relative;
}
.power::before {
  content: ""; position: absolute; left: 50%; top: -4px;
  width: 1.8px; height: 7px; background: currentColor;
  transform: translateX(-50%) rotate(45deg); transform-origin: bottom;
}

.player-meta { display: flex; flex-direction: column; line-height: 1.25; }
.player-state { font-size: 0.75rem; font-weight: 500; }
.player-mood { font-size: 0.6875rem; color: var(--text-3); }

.viz { display: flex; align-items: flex-end; gap: 2px; height: 20px; }
.viz i {
  display: block; width: 2px; height: 2px; border-radius: 2px;
  background: var(--c2);
  transition: height 0.09s linear, background-color var(--tint-speed) var(--ease);
}

/* ============================================================
   4. TYPE + SECTIONS
   ============================================================ */
.section { position: relative; padding-block: var(--section-y); }

.tag {
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--text-3);
  margin-bottom: clamp(2rem, 5vw, 3.5rem);
}
.tag.small { margin-bottom: 1rem; }

.display {
  font-size: clamp(2.5rem, 7.5vw, 5.5rem);
  font-weight: 600; line-height: 1.04; letter-spacing: -0.04em;
  text-wrap: balance;
}

.lede {
  font-size: clamp(1.0625rem, 1.9vw, 1.4375rem);
  line-height: 1.45; letter-spacing: -0.015em;
  color: var(--text-2); max-width: 46ch;
  margin-top: clamp(1.5rem, 3vw, 2.25rem);
}

/* ---------- REVEAL ---------- */
.reveal {
  opacity: 0; transform: translate3d(0, 34px, 0) scale(0.985);
  filter: blur(6px);
  transition: opacity 1.1s var(--ease-out), transform 1.1s var(--ease-out),
              filter 1.1s var(--ease-out);
  transition-delay: calc(var(--d, 0) * 100ms);
  will-change: opacity, transform, filter;
}
.reveal.in { opacity: 1; transform: none; filter: none; }

/* ---------- HERO ---------- */
.hero {
  position: relative;
  min-height: 100svh; display: grid; place-items: center;
  padding: 7rem var(--gutter) 8rem; text-align: center;
}
.hero-title { margin: 0; }
.line { display: block; overflow: hidden; padding-block: 0.08em; }
.line-in {
  display: block;
  font-size: clamp(3.25rem, 15vw, 11.5rem);
  font-weight: 600; line-height: 0.94; letter-spacing: -0.055em;
  background: linear-gradient(180deg, #ffffff 20%, rgba(255,255,255,0.62) 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  transform: translate3d(0, 108%, 0);
  animation: riseIn 1.6s var(--ease-out) 0.2s forwards;
}
@keyframes riseIn { to { transform: none; } }

.hero-sub {
  font-size: clamp(1.0625rem, 2.1vw, 1.4375rem);
  line-height: 1.45; letter-spacing: -0.015em; color: var(--text-2);
  margin: clamp(1.75rem, 4vw, 2.75rem) auto 0; max-width: 34ch;
}

/* Anchored to the foot of the hero rather than trailing the text, so the
   centred title is not left sitting above a half screen of nothing.
   It carries its own fade rather than .reveal, because sitting at the
   very bottom of the viewport puts it outside the observer's rootMargin
   on short screens, where it would never appear at all. */
.cue {
  position: absolute; left: 0; right: 0; bottom: clamp(1.75rem, 5vh, 3rem);
  margin-inline: auto; width: fit-content;
  display: inline-flex; flex-direction: column; align-items: center; gap: 0.6rem;
  opacity: 0;
  animation: cueIn 0.9s var(--ease-out) 1.15s forwards;
  font-size: 0.6875rem; font-weight: 500; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--text-3);
  transition: color 0.4s var(--ease);
}
@keyframes cueIn {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
.cue:hover { color: var(--text); }
.cue svg {
  width: 18px; height: 18px; fill: none; stroke: currentColor;
  stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round;
  animation: bob 2.4s var(--ease) infinite;
}
@keyframes bob { 0%,100% { transform: translateY(0); opacity: .55; } 50% { transform: translateY(7px); opacity: 1; } }

/* ---------- THE NAME ---------- */
.card-def {
  padding: clamp(2rem, 5vw, 3.5rem);
  max-width: 600px;
  margin-bottom: 0;
}
.def-word {
  font-size: clamp(2.75rem, 9vw, 5.5rem);
  font-weight: 300; line-height: 1; letter-spacing: -0.045em;
}
.def-phon { font-size: 0.9375rem; color: var(--text-3); margin-top: 1rem; }
.def-phon em { font-style: italic; }
.def-list { margin-top: 1.75rem; display: grid; gap: 0.6rem; counter-reset: d; }
.def-list li {
  counter-increment: d;
  font-size: clamp(1rem, 1.8vw, 1.25rem); color: var(--text-2);
  display: flex; gap: 0.75rem;
}
.def-list li::before {
  content: counter(d) ".";
  color: var(--text-3); font-variant-numeric: tabular-nums;
}


/* ---------- CHIPS ---------- */
.chips {
  display: flex; flex-wrap: wrap; gap: 0.5rem;
  margin-top: clamp(2rem, 4vw, 3rem);
}
.chip {
  position: relative;
  font-size: 0.8125rem; font-weight: 500; color: var(--text-2);
  padding: 9px 18px; border-radius: 100px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--edge);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  transition: color 0.4s var(--ease), background-color 0.4s var(--ease),
              border-color 0.4s var(--ease), transform 0.4s var(--ease),
              box-shadow 0.5s var(--ease);
}
.chip:hover { color: var(--text); background: rgba(255, 255, 255, 0.1); transform: translateY(-2px); }
.chip[aria-expanded="true"] {
  color: var(--text);
  background: color-mix(in srgb, var(--c2) 26%, rgba(255, 255, 255, 0.08));
  border-color: color-mix(in srgb, var(--c2) 45%, transparent);
  box-shadow: 0 6px 24px -8px color-mix(in srgb, var(--c2) 65%, transparent);
}

/* Expanding explanation. Height is driven in px by main.js so it can
   animate, then released to auto so it stays responsive. */
.chip-panel {
  height: 0; overflow: hidden;
  transition: height 0.55s var(--ease);
}
.chip-panel-inner { padding-top: clamp(0.875rem, 2vw, 1.25rem); }

.chip-card { padding: clamp(1.25rem, 3vw, 1.75rem); max-width: 52ch; }
.chip-card h3 {
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--text-3); margin-bottom: 0.75rem;
}
.chip-card p {
  font-size: clamp(0.9375rem, 1.5vw, 1.0625rem);
  line-height: 1.6; color: var(--text-2);
}

/* ============================================================
   PHOTO SHOWCASE (home page)
   Deliberately the same footprint as a reel frame, so photography
   and video read as equal weight on the page.
   ============================================================ */
.teaser-strip {
  width: 100%;
  margin-top: clamp(2rem, 4vw, 3rem);
  display: flex; flex-direction: column; align-items: flex-start;
  gap: clamp(1rem, 2vw, 1.5rem);
}

.pshow-frame {
  position: relative; width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: 26px; overflow: hidden;
  border: 1px solid var(--edge);
  background: #06070b;
  box-shadow:
    inset 0 1px 0 var(--edge-bright),
    0 40px 90px -40px rgba(0, 0, 0, 0.95),
    0 0 70px -30px color-mix(in srgb, var(--c2) 55%, transparent);
  cursor: grab;
  touch-action: pan-y;
}
.pshow-frame:active { cursor: grabbing; }

.pshow-track {
  display: flex; height: 100%;
  transition: transform 0.75s var(--ease);
  will-change: transform;
}
.pshow-slide {
  flex: 0 0 100%; height: 100%; display: block;
  -webkit-user-select: none; user-select: none;
}
.pshow-slide img {
  width: 100%; height: 100%; object-fit: cover;
  -webkit-user-drag: none;
}

/* Arrows sit on the photo, right and left */
.pshow-nav {
  position: absolute; top: 50%; z-index: 2;
  transform: translateY(-50%);
  width: 46px; height: 46px; border-radius: 50%;
  display: grid; place-items: center; color: #fff;
  background: rgba(10, 12, 18, 0.42);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.18);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28);
  opacity: 0.75;
  transition: opacity 0.35s var(--ease), background-color 0.35s var(--ease),
              transform 0.35s var(--ease);
}
.pshow-frame:hover .pshow-nav { opacity: 1; }
.pshow-nav:hover { background: rgba(20, 24, 34, 0.7); }
.pshow-nav.prev { left: clamp(0.75rem, 2vw, 1.25rem); }
.pshow-nav.next { right: clamp(0.75rem, 2vw, 1.25rem); }
.pshow-nav.prev:hover { transform: translateY(-50%) translateX(-3px); }
.pshow-nav.next:hover { transform: translateY(-50%) translateX(3px); }
.pshow-nav svg {
  width: 20px; height: 20px; fill: none; stroke: currentColor;
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}

/* Series name and position, bottom left of the frame */
.pshow-meta {
  position: absolute; z-index: 2;
  left: clamp(0.75rem, 2vw, 1.25rem); bottom: clamp(0.75rem, 2vw, 1.25rem);
  display: flex; align-items: center; gap: 0.7rem;
  padding: 8px 14px; border-radius: 100px;
  background: rgba(10, 12, 18, 0.42);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.16);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.24);
  color: #fff;
}
.pshow-tag {
  font-size: 0.75rem; font-weight: 600; letter-spacing: 0.02em;
}
.pshow-tag:empty { display: none; }
.pshow-count {
  font-size: 0.6875rem; letter-spacing: 0.1em;
  color: rgba(255, 255, 255, 0.6); font-variant-numeric: tabular-nums;
}

.ts-go {
  display: inline-flex; align-items: center; gap: 0.6rem;
  white-space: nowrap;
  padding: 13px 22px; border-radius: 100px;
  font-size: 0.875rem; font-weight: 500;
  background: rgba(255, 255, 255, 0.055);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid var(--edge);
  box-shadow: inset 0 1px 0 var(--edge-bright);
  transition: background-color 0.5s var(--ease), transform 0.5s var(--ease);
}
.ts-go:hover { background: rgba(255, 255, 255, 0.12); transform: translateX(4px); }
.ts-go svg {
  width: 16px; height: 16px; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
  transition: transform 0.5s var(--ease);
}
.ts-go:hover svg { transform: translateX(4px); }

/* ============================================================
   GALLERY PAGE
   ============================================================ */
.page-gallery main { padding-bottom: 4rem; }

/* Only has to clear the floating back button now, not a full nav bar */
.gal-head { padding: clamp(4.5rem, 7vw, 5.5rem) 0 clamp(1.5rem, 3vw, 2.25rem); }

/* Gallery pages carry a way out instead of a menu */
.back-fab {
  position: fixed; z-index: 150;
  top: clamp(14px, 2.5vw, 22px); left: clamp(14px, 2.5vw, 22px);
  width: 46px; height: 46px; border-radius: 50%;
  display: grid; place-items: center;
  color: var(--text-2);
  background: rgba(255, 255, 255, 0.045);
  -webkit-backdrop-filter: blur(34px) saturate(200%) brightness(108%);
  backdrop-filter: blur(34px) saturate(200%) brightness(108%);
  border: 1px solid var(--edge);
  box-shadow: inset 0 1px 0 var(--edge-bright),
              0 14px 40px -16px rgba(0, 0, 0, 0.7);
  transition: color 0.4s var(--ease), transform 0.5s var(--ease),
              background-color 0.4s var(--ease), opacity 0.35s var(--ease);
}
.back-fab:hover {
  color: var(--text); transform: translateX(-3px);
  background: rgba(255, 255, 255, 0.1);
}
.back-fab svg {
  width: 19px; height: 19px; fill: none; stroke: currentColor;
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round;
}

.back {
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-size: 0.8125rem; color: var(--text-2);
  margin-bottom: clamp(1.5rem, 3vw, 2.5rem);
  transition: color 0.4s var(--ease), gap 0.4s var(--ease);
}
.back:hover { color: var(--text); gap: 0.75rem; }
.back svg {
  width: 16px; height: 16px; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}

/* ============================================================
   GALLERY LAYOUT
   Sidebar on the left, photographs on the right. The sidebar is
   the only way categories are chosen now, so it holds the search
   box and the label tree together.
   ============================================================ */
.gal-layout {
  width: 100%; max-width: var(--maxw); margin-inline: auto;
  padding-inline: var(--gutter);
  display: grid;
  grid-template-columns: 285px minmax(0, 1fr);
  gap: clamp(1.5rem, 3.2vw, 3rem);
  align-items: start;
}
.gal-main { min-width: 0; }

/* Inside the column these already sit in a padded track, so drop the
   page level centring they carry when they are used full bleed */
.gal-main .wrap,
.gal-main .sh-grid,
.gal-main .gal-grid,
.gal-main .gal-empty {
  max-width: none; margin-inline: 0; padding-inline: 0;
}

/* ---- the bar above the photographs ---- */
.gal-bar {
  display: flex; align-items: center; gap: 0.9rem; flex-wrap: wrap;
  padding-bottom: clamp(1rem, 2.4vw, 1.6rem);
  margin-bottom: clamp(0.5rem, 1.5vw, 1rem);
  border-bottom: 1px solid var(--edge);
}
.gal-crumb {
  display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap;
  font-size: 0.8125rem; color: var(--text-3); min-width: 0;
}
.crumb-link {
  font-size: 0.8125rem; color: var(--text-3);
  transition: color 0.35s var(--ease);
}
.crumb-link:hover { color: var(--text); }
.crumb-link.is-here { color: var(--text); font-weight: 500; }
.crumb-sep { color: var(--text-3); opacity: 0.5; }

.gal-count {
  margin-left: auto;
  font-size: 0.6875rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--text-3); white-space: nowrap;
}

/* Only shows once the sidebar becomes a drawer */
.gal-browse {
  display: none; align-items: center; gap: 0.5rem;
  font-size: 0.8125rem; font-weight: 500; color: var(--text-2);
  padding: 8px 15px; border-radius: 100px;
  background: rgba(255, 255, 255, 0.05);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid var(--edge);
  box-shadow: inset 0 1px 0 var(--edge-bright);
  transition: color 0.35s var(--ease), background-color 0.35s var(--ease);
}
.gal-browse:hover { color: var(--text); background: rgba(255, 255, 255, 0.09); }
.gal-browse svg {
  width: 16px; height: 16px; fill: none; stroke: currentColor;
  stroke-width: 1.9; stroke-linecap: round;
}

/* ============================================================
   SIDEBAR
   ============================================================ */
.gal-side { position: sticky; top: clamp(78px, 11vh, 104px); }
.side-inner {
  border-radius: 22px; padding: 14px;
  background: rgba(255, 255, 255, 0.04);
  -webkit-backdrop-filter: blur(28px) saturate(190%);
  backdrop-filter: blur(28px) saturate(190%);
  border: 1px solid var(--edge);
  box-shadow: inset 0 1px 0 var(--edge-bright),
              0 24px 60px -30px rgba(0, 0, 0, 0.7);
}

/* ---- search ---- */
.gal-search { position: relative; }
.search-icon {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; pointer-events: none;
  fill: none; stroke: var(--text-3); stroke-width: 1.9;
  stroke-linecap: round; transition: stroke 0.35s var(--ease);
}
.gal-search:focus-within .search-icon { stroke: var(--c2); }

#galSearch {
  width: 100%; padding: 11px 34px 11px 36px;
  font-family: var(--font); font-size: 0.875rem; color: var(--text);
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--edge); border-radius: 12px;
  transition: border-color 0.35s var(--ease), background-color 0.35s var(--ease);
}
#galSearch::placeholder { color: var(--text-3); }
#galSearch:focus {
  outline: none;
  border-color: color-mix(in srgb, var(--c2) 55%, transparent);
  background: rgba(255, 255, 255, 0.08);
}

.search-clear {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  width: 22px; height: 22px; border-radius: 50%;
  display: grid; place-items: center; color: var(--text-3);
  transition: color 0.3s var(--ease), background-color 0.3s var(--ease);
}
.search-clear:hover { color: var(--text); background: rgba(255, 255, 255, 0.1); }
.search-clear[hidden] { display: none; }
.search-clear svg {
  width: 12px; height: 12px; fill: none; stroke: currentColor;
  stroke-width: 2.2; stroke-linecap: round;
}

/* ---- autocomplete ---- */
.gal-suggest {
  position: absolute; z-index: 60; top: calc(100% + 8px); left: 0;
  min-width: 100%; width: max-content; max-width: 380px;
  max-height: 340px; overflow-y: auto;
  padding: 6px; border-radius: 16px; list-style: none;
  background: rgba(14, 16, 24, 0.86);
  -webkit-backdrop-filter: blur(34px) saturate(200%);
  backdrop-filter: blur(34px) saturate(200%);
  border: 1px solid var(--edge);
  box-shadow: inset 0 1px 0 var(--edge-bright),
              0 30px 70px -24px rgba(0, 0, 0, 0.85);
}
.gal-suggest[hidden] { display: none; }

.sug {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 9px; border-radius: 11px; cursor: pointer;
  transition: background-color 0.2s var(--ease);
}
.sug:hover, .sug.is-on { background: rgba(255, 255, 255, 0.09); }

.sug-thumb {
  flex: none; width: 34px; height: 34px; border-radius: 8px;
  object-fit: cover; border: 1px solid var(--edge);
}
.sug-icon {
  flex: none; width: 34px; height: 34px; border-radius: 8px;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--c2) 16%, rgba(255, 255, 255, 0.05));
  border: 1px solid var(--edge);
}
.sug-icon svg {
  width: 16px; height: 16px; fill: none; stroke: var(--text-2);
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
}

.sug-text { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.sug-title {
  font-size: 0.8125rem; color: var(--text-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sug-title b { color: var(--text); font-weight: 600; }
.sug-sub {
  font-size: 0.6875rem; color: var(--text-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sug-none {
  padding: 12px 10px; font-size: 0.8125rem; color: var(--text-3);
}

/* ---- label tree ---- */
.gal-tree { margin-top: 12px; }

.tnode {
  position: relative; display: flex; align-items: center;
  border-radius: 10px;
  transition: background-color 0.3s var(--ease);
}
.tnode:hover { background: rgba(255, 255, 255, 0.05); }
.tnode.is-on { background: color-mix(in srgb, var(--c2) 17%, rgba(255,255,255,0.05)); }

/* The spine down to whatever is selected */
.tnode.is-parent .tnode-label { color: var(--text-2); }

.tnode-twist {
  flex: none; width: 20px; height: 28px;
  display: grid; place-items: center;
  color: var(--text-3); opacity: 0;
  transition: transform 0.35s var(--ease), color 0.3s var(--ease);
}
.tnode.has-kids .tnode-twist { opacity: 1; }
.tnode-twist svg {
  width: 12px; height: 12px; fill: none; stroke: currentColor;
  stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round;
}
.tnode-twist[aria-expanded="true"] { transform: rotate(90deg); }
.tnode-twist:hover { color: var(--text); }

.tnode-btn {
  flex: 1; min-width: 0;
  display: flex; align-items: center; gap: 7px;
  padding: 6px 10px 6px 2px;
  font-size: 0.8125rem; color: var(--text-3); text-align: left;
  transition: color 0.3s var(--ease);
}
.tnode-btn:hover { color: var(--text); }
.tnode.is-on .tnode-btn { color: var(--text); font-weight: 500; }

.tnode-label {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tnode-dot {
  flex: none; width: 5px; height: 5px; border-radius: 50%;
  background: var(--c2);
  box-shadow: 0 0 8px color-mix(in srgb, var(--c2) 70%, transparent);
  transition: background-color var(--tint-speed) var(--ease);
}
.tnode-count {
  margin-left: auto; flex: none;
  font-size: 0.6875rem; font-variant-numeric: tabular-nums;
  color: var(--text-3); opacity: 0.75;
}

/* Each level steps in, with a hairline showing the branch */
.tkids {
  margin-left: 12px; padding-left: 8px;
  border-left: 1px solid var(--edge);
  overflow: hidden;
}
.tkids.collapsed { display: none; }

.gal-scrim { display: none; }

/* ============================================================
   NARROW: the sidebar becomes a drawer
   ============================================================ */
@media (max-width: 900px) {
  .gal-layout { grid-template-columns: minmax(0, 1fr); }
  .gal-browse { display: inline-flex; }

  .gal-side {
    position: fixed; z-index: 160; inset: 0 auto 0 0;
    width: min(310px, 86vw); padding: 14px;
    overflow-y: auto;
    transform: translateX(-104%);
    transition: transform 0.5s var(--ease);
  }
  .gal-side.open { transform: none; }
  .side-inner { border-radius: 18px; }

  .gal-scrim {
    display: block; position: fixed; z-index: 155; inset: 0;
    background: rgba(4, 5, 9, 0.6);
    -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
  }
  .gal-scrim[hidden] { display: none; }

  /* The floating back button sits right where the drawer opens */
  .drawer-open .back-fab { opacity: 0; pointer-events: none; }
}

@media (max-width: 560px) {
  .gal-count { margin-left: 0; width: 100%; }
}

.gal-grid {
  width: 100%; max-width: var(--maxw); margin-inline: auto;
  padding-inline: var(--gutter);
  display: grid; gap: clamp(0.75rem, 1.6vw, 1.125rem);
  grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
  grid-auto-rows: clamp(160px, 19vw, 250px);
}

.gshot {
  position: relative; overflow: hidden; border-radius: 22px;
  border: 1px solid var(--edge);
  background: rgba(255, 255, 255, 0.04);
  /* No backdrop-filter. .gshot img is width and height 100% with
     object-fit cover, so the photograph covers the tile completely
     and a backdrop blur behind it can never be seen. It was costing
     a readback and a blur per tile, per frame, for nothing. */
  box-shadow: inset 0 1px 0 var(--edge-bright), 0 24px 60px -24px rgba(0,0,0,0.7);
  transition: transform 0.7s var(--ease), box-shadow 0.7s var(--ease);
}
.gshot.tall { grid-row: span 2; }
.gshot.wide { grid-column: span 2; }
.gshot.big  { grid-column: span 2; grid-row: span 2; }
.gshot:hover { transform: translateY(-5px); }

/* Entry animation, staggered by JS via --i */
.gshot.enter {
  opacity: 0; transform: translate3d(0, 26px, 0) scale(0.96); filter: blur(8px);
}
.gshot.enter.settled {
  opacity: 1; transform: none; filter: none;
  transition: opacity 0.8s var(--ease-out), transform 0.8s var(--ease-out),
              filter 0.8s var(--ease-out);
  transition-delay: calc(var(--i, 0) * 45ms);
}
/* Held out of layout while a filter hides it */
.gshot.gone { display: none; }

.gshot img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform 1.1s var(--ease);
}
.gshot:hover img { transform: scale(1.05); }
.gshot img.missing { opacity: 0; }

.gshot.no-img::before {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(140deg,
    color-mix(in srgb, var(--c1) 28%, transparent),
    color-mix(in srgb, var(--c2) 20%, transparent));
  transition: background var(--tint-speed) var(--ease);
}
.gshot.no-img::after {
  content: "images/" attr(data-name);
  position: absolute; inset: auto 0 50% 0; transform: translateY(50%);
  text-align: center; font-size: 0.6875rem; font-weight: 500;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}


.gal-foot { margin-top: clamp(3rem, 6vw, 4.5rem); }

.gal-empty {
  max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter);
  font-size: 0.9375rem; color: var(--text-3);
  padding-block: clamp(2rem, 5vw, 3.5rem);
}
.gal-empty[hidden] { display: none; }

/* The frame a deep link landed on, so it is obvious which one you
   followed before the viewer opens over it */
.located {
  animation: located 3s var(--ease) 1;
}
@keyframes located {
  0%, 100% { box-shadow: inset 0 1px 0 var(--edge-bright), 0 26px 60px -28px rgba(0,0,0,0.8); }
  15%, 70% {
    box-shadow: inset 0 1px 0 var(--edge-bright),
                0 0 0 3px color-mix(in srgb, var(--c2) 75%, transparent),
                0 0 40px color-mix(in srgb, var(--c2) 45%, transparent);
  }
}

/* Which series the open photo belongs to */
.lb-stage figcaption b {
  font-size: 0.875rem; font-weight: 600; color: var(--text);
}

/* ============================================================
   SERIES (a named body of work inside a category)
   ============================================================ */
.series {
  margin-bottom: clamp(3rem, 6vw, 4.5rem);
  transition: opacity 0.5s var(--ease);
}
.series[hidden] { display: none; }

.series-head {
  display: flex; gap: clamp(1rem, 2.5vw, 2rem);
  padding-block: clamp(1.25rem, 3vw, 2rem) clamp(1.5rem, 3vw, 2.25rem);
}
.series-rule {
  flex: none; width: 3px; border-radius: 3px;
  background: linear-gradient(180deg, var(--c1), var(--c2));
  box-shadow: 0 0 20px color-mix(in srgb, var(--c2) 55%, transparent);
}
.series-kicker {
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--text-3); margin-bottom: 0.6rem;
}
.series-title {
  font-size: clamp(1.75rem, 4.4vw, 3.25rem);
  font-weight: 600; line-height: 1.05; letter-spacing: -0.035em;
}
.series-title span {
  display: block; font-weight: 300; color: var(--text-2);
}
.series-note {
  margin-top: 0.9rem; font-size: 0.9375rem; color: var(--text-3);
}
.series-end {
  height: 1px; margin-top: clamp(1.75rem, 4vw, 2.75rem);
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--c2) 45%, transparent), transparent 65%);
}

/* Editorial grid: a wide opener, then alternating weights */
.sh-grid {
  width: 100%; max-width: var(--maxw); margin-inline: auto;
  padding-inline: var(--gutter);
  display: grid; grid-template-columns: repeat(12, 1fr);
  gap: clamp(0.6rem, 1.4vw, 1rem);
  grid-auto-rows: clamp(170px, 21vw, 340px);
}
.sh-a  { grid-column: span 12; grid-row: span 2; }
.sh-12 { grid-column: span 12; }
.sh-8  { grid-column: span 8; }
.sh-7  { grid-column: span 7; }
.sh-6  { grid-column: span 6; }
.sh-5  { grid-column: span 5; }
.sh-4  { grid-column: span 4; }

.sh {
  position: relative; overflow: hidden; border-radius: 20px;
  border: 1px solid var(--edge);
  background: rgba(255, 255, 255, 0.04);
  cursor: zoom-in;
  box-shadow: inset 0 1px 0 var(--edge-bright), 0 26px 60px -28px rgba(0,0,0,0.8);
  transition: transform 0.7s var(--ease), box-shadow 0.7s var(--ease);
}
.sh:hover { transform: translateY(-4px); }
.sh img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform 1.2s var(--ease), filter 0.6s var(--ease);
}
.sh:hover img { transform: scale(1.04); }


/* ============================================================
   LIGHTBOX
   ============================================================ */
.lb {
  position: fixed; inset: 0; z-index: 300;
  display: grid; grid-template-columns: auto 1fr auto; align-items: center;
  gap: clamp(0.5rem, 2vw, 1.5rem);
  padding: clamp(1rem, 4vw, 3rem);
  background: rgba(4, 5, 8, 0.86);
  -webkit-backdrop-filter: blur(26px) saturate(160%);
  backdrop-filter: blur(26px) saturate(160%);
  opacity: 0;
  transition: opacity 0.4s var(--ease);
}
.lb[hidden] { display: none; }
.lb.open { opacity: 1; }

.lb-stage {
  display: flex; flex-direction: column; align-items: center;
  gap: 1rem; min-width: 0; min-height: 0;
}
.lb-stage img {
  max-width: 100%; max-height: calc(100svh - 10rem);
  width: auto; height: auto; object-fit: contain;
  border-radius: 14px;
  box-shadow: 0 40px 90px -30px rgba(0, 0, 0, 0.9);
}
.lb-stage figcaption {
  display: flex; align-items: baseline; gap: 0.9rem;
  font-size: 0.875rem; color: var(--text-2);
}
.lb-stage figcaption span {
  font-size: 0.75rem; letter-spacing: 0.14em; color: var(--text-3);
  font-variant-numeric: tabular-nums;
}

.lb-nav, .lb-close {
  width: 46px; height: 46px; border-radius: 50%;
  display: grid; place-items: center; flex: none;
  background: rgba(255, 255, 255, 0.08);
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
  border: 1px solid var(--edge);
  transition: background-color 0.35s var(--ease), transform 0.35s var(--ease),
              opacity 0.35s var(--ease);
}
.lb-nav:hover, .lb-close:hover { background: rgba(255,255,255,0.18); transform: scale(1.08); }
.lb-nav[disabled] { opacity: 0.25; pointer-events: none; }
.lb-close { position: absolute; top: clamp(1rem, 3vw, 2rem); right: clamp(1rem, 3vw, 2rem); z-index: 2; }
.lb-nav svg, .lb-close svg {
  width: 20px; height: 20px; fill: none; stroke: currentColor;
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}

body.lb-open { overflow: hidden; }

/* ============================================================
   REEL
   ============================================================ */
.reel-wrap {
  width: 100%; max-width: var(--maxw); margin-inline: auto;
  padding-inline: var(--gutter);
  margin-top: clamp(2rem, 4vw, 3rem);
  display: grid; gap: clamp(2rem, 4vw, 3rem);
}

.reel-label {
  display: flex; align-items: baseline; gap: 0.7rem;
  margin-top: 0.875rem; padding-inline: 0.25rem;
}
.reel-label b {
  font-size: 0.9375rem; font-weight: 600; letter-spacing: -0.01em;
}
.reel-label span {
  font-size: 0.6875rem; font-weight: 500; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--text-3);
}

/* ---------- THE UNI PROJECT CLIP, INSIDE PSYCHOLOGY ---------- */
/* It is a phone shaped clip, so it needs its own ratio. The shared
   .reel-frame video rule is locked to 16/9 for the two landscape
   reels further down the page. */
.psyc-project {
  margin-top: clamp(2.5rem, 5vw, 4rem);
  display: grid;
  grid-template-columns: minmax(0, 290px) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: center;
}
.psyc-project .reel-frame video {
  aspect-ratio: 9 / 16;
}
.psyc-project .reel-frame { border-radius: 22px; }
.psyc-project-text .tag { margin-bottom: 0.6rem; }
.psyc-project-text p {
  color: var(--text-2);
  max-width: 34ch;
  line-height: 1.65;
}
.psyc-project-text a {
  color: var(--text);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--edge-bright);
  transition: text-decoration-color 0.4s var(--ease);
}
.psyc-project-text a:hover { text-decoration-color: var(--c2); }

@media (max-width: 720px) {
  /* Stacked, and the clip stops being the whole screen. Auto margins
     rather than a transform, see the .reveal note in CLAUDE.md. */
  .psyc-project { grid-template-columns: 1fr; }
  .psyc-project .reel { width: 100%; max-width: 260px; margin-inline: auto; }
  .psyc-project-text { text-align: center; }
  .psyc-project-text p { max-width: none; }
}

.reel-frame {
  position: relative; overflow: hidden;
  border-radius: 26px;
  border: 1px solid var(--edge);
  background: #06070b;
  box-shadow:
    inset 0 1px 0 var(--edge-bright),
    0 40px 90px -40px rgba(0, 0, 0, 0.95),
    0 0 70px -30px color-mix(in srgb, var(--c2) 55%, transparent);
  transition: box-shadow 0.8s var(--ease);
}
.reel-frame video {
  display: block; width: 100%; height: auto;
  aspect-ratio: 16 / 9; object-fit: cover;
  background: #06070b;
}

/* Sound toggle sits on the glass, bottom right of the frame */
.reel-sound {
  position: absolute; right: clamp(0.75rem, 2vw, 1.25rem);
  bottom: clamp(0.75rem, 2vw, 1.25rem); z-index: 2;
  display: inline-flex; align-items: center; gap: 0.55rem;
  padding: 9px 16px 9px 12px; border-radius: 100px;
  font-size: 0.75rem; font-weight: 500; letter-spacing: 0.01em;
  color: #fff;
  background: rgba(10, 12, 18, 0.5);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.18);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28);
  transition: background-color 0.4s var(--ease), transform 0.4s var(--ease),
              border-color 0.4s var(--ease);
}
.reel-sound:hover { background: rgba(20, 24, 34, 0.68); transform: translateY(-2px); }
.reel-sound[aria-pressed="true"] {
  background: color-mix(in srgb, var(--c2) 42%, rgba(10,12,18,0.5));
  border-color: color-mix(in srgb, var(--c2) 60%, transparent);
}

.rs-ico { position: relative; width: 18px; height: 18px; flex: none; }
.rs-ico svg {
  position: absolute; inset: 0; width: 18px; height: 18px;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease);
}
.rs-ico .rs-on  { opacity: 0; transform: scale(0.8); }
.reel-sound[aria-pressed="true"] .rs-off { opacity: 0; transform: scale(0.8); }
.reel-sound[aria-pressed="true"] .rs-on  { opacity: 1; transform: none; }

/* ============================================================
   SOCIALS
   ============================================================ */
.socials {
  display: grid; gap: clamp(0.75rem, 1.6vw, 1.125rem);
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  margin-top: clamp(2rem, 4vw, 3rem);
  max-width: 720px;
}

.social {
  position: relative; overflow: hidden;
  display: flex; align-items: center; gap: 1rem;
  padding: clamp(1rem, 2.2vw, 1.375rem);
  border-radius: 20px;
  background: var(--glass);
  -webkit-backdrop-filter: blur(22px) saturate(180%);
  backdrop-filter: blur(22px) saturate(180%);
  border: 1px solid var(--edge);
  box-shadow: inset 0 1px 0 var(--edge-bright), 0 24px 50px -30px rgba(0,0,0,0.8);
  transition: transform 0.5s var(--ease), background-color 0.5s var(--ease),
              border-color 0.5s var(--ease), box-shadow 0.5s var(--ease);
}
.social:hover { transform: translateY(-4px); background: var(--glass-strong); }

/* Brand glow, kept to the hover so the page stays its own colour */
.social.yt:hover {
  border-color: rgba(255, 0, 0, 0.42);
  box-shadow: inset 0 1px 0 var(--edge-bright), 0 26px 55px -28px rgba(255, 0, 0, 0.5);
}
.social.ig:hover {
  border-color: rgba(221, 42, 123, 0.42);
  box-shadow: inset 0 1px 0 var(--edge-bright), 0 26px 55px -28px rgba(221, 42, 123, 0.5);
}

.social-mark { flex: none; width: 34px; height: 34px; display: grid; place-items: center; }
.social-mark svg { width: 30px; height: 30px; }

/* YouTube: greyscale at rest, brand red on hover */
.yt-body { fill: var(--text-2); transition: fill 0.45s var(--ease); }
.yt-play { fill: #06070b; }
.social.yt:hover .yt-body { fill: #FF0000; }

/* Instagram: outline glyph that fills with the brand gradient on hover */
.ig-shape {
  fill: none; stroke: var(--text-2); stroke-width: 1.9;
  transition: stroke 0.45s var(--ease);
}
.ig-dot { fill: var(--text-2); transition: fill 0.45s var(--ease); }
.social.ig:hover .ig-shape { stroke: url(#igGrad); }
.social.ig:hover .ig-dot { fill: url(#igGrad); }

.social-text { display: flex; flex-direction: column; line-height: 1.3; margin-right: auto; }
.social-text b { font-size: 0.9375rem; font-weight: 600; letter-spacing: -0.01em; }
.social-text span { font-size: 0.8125rem; color: var(--text-3); }

.social-go {
  width: 16px; height: 16px; flex: none;
  fill: none; stroke: var(--text-3); stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
  transition: stroke 0.4s var(--ease), transform 0.4s var(--ease);
}
.social:hover .social-go { stroke: var(--text); transform: translate(2px, -2px); }

/* ---------- ASIDE ---------- */
.aside-note { text-align: center; }
.aside-body {
  font-size: clamp(0.9375rem, 1.5vw, 1.0625rem);
  line-height: 1.62; color: var(--text-2);
  max-width: 44ch; margin-inline: auto;
}

/* ---------- CONTACT ---------- */
.mail {
  display: inline-block;
  font-size: clamp(1.125rem, 3vw, 2rem);
  font-weight: 500; letter-spacing: -0.025em;
  margin-top: clamp(2rem, 4vw, 2.75rem); padding-bottom: 6px;
  background-image: linear-gradient(currentColor, currentColor);
  background-size: 0% 1px; background-repeat: no-repeat; background-position: 0 100%;
  transition: background-size 0.6s var(--ease), color 0.4s var(--ease);
}
.mail:hover { background-size: 100% 1px; color: var(--c2); }

/* The last section ends tighter than the rest, so the page does not
   finish on a band of empty padding */
.contact { padding-bottom: clamp(2rem, 4vw, 3rem); }

/* ============================================================
   CONTACT
   ============================================================ */
.contact-btn {
  display: inline-flex; align-items: center; gap: 0.65rem;
  margin-top: clamp(2rem, 4vw, 2.75rem);
  padding: 15px 26px; border-radius: 100px;
  font-size: 0.9375rem; font-weight: 500;
  background: color-mix(in srgb, var(--c2) 22%, rgba(255, 255, 255, 0.07));
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid color-mix(in srgb, var(--c2) 40%, var(--edge));
  box-shadow: inset 0 1px 0 var(--edge-bright),
              0 18px 44px -20px color-mix(in srgb, var(--c2) 60%, transparent);
  transition: transform 0.45s var(--ease), background-color 0.45s var(--ease),
              box-shadow 0.45s var(--ease);
}
.contact-btn:hover {
  transform: translateY(-3px);
  background: color-mix(in srgb, var(--c2) 34%, rgba(255, 255, 255, 0.1));
  box-shadow: inset 0 1px 0 var(--edge-bright),
              0 22px 54px -18px color-mix(in srgb, var(--c2) 75%, transparent);
}
.contact-btn svg {
  width: 17px; height: 17px; fill: none; stroke: currentColor;
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round;
  transition: transform 0.45s var(--ease);
}
.contact-btn:hover svg { transform: translateX(4px); }

/* ---------- overlay ---------- */
.cf {
  position: fixed; inset: 0; z-index: 300;
  display: grid; place-items: center;
  padding: clamp(1rem, 4vw, 2.5rem);
  background: rgba(4, 5, 8, 0.82);
  -webkit-backdrop-filter: blur(24px) saturate(160%);
  backdrop-filter: blur(24px) saturate(160%);
  opacity: 0; transition: opacity 0.36s var(--ease);
  overflow-y: auto;
}
.cf[hidden] { display: none; }
.cf.open { opacity: 1; }

.cf-panel {
  position: relative; width: min(560px, 100%);
  padding: clamp(1.75rem, 4vw, 2.75rem);
  border-radius: 26px;
  transform: translateY(16px) scale(0.98);
  transition: transform 0.5s var(--ease-out);
}
.cf.open .cf-panel { transform: none; }
/* The panel's own sweep would slide over the inputs */
.cf-panel::after { display: none; }

.cf-close {
  position: absolute; top: 14px; right: 14px; z-index: 2;
  width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center;
  color: var(--text-2);
  border: 1px solid var(--edge);
  background: rgba(255, 255, 255, 0.06);
  transition: color 0.3s var(--ease), background-color 0.3s var(--ease);
}
.cf-close:hover { color: var(--text); background: rgba(255, 255, 255, 0.14); }
.cf-close svg {
  width: 16px; height: 16px; fill: none; stroke: currentColor;
  stroke-width: 2; stroke-linecap: round;
}

.cf-title {
  font-size: clamp(1.5rem, 3.2vw, 2rem);
  font-weight: 600; letter-spacing: -0.03em;
  margin-bottom: clamp(1.25rem, 3vw, 1.75rem);
}

.cf-row { display: grid; gap: 0.875rem; grid-template-columns: 1fr 1fr; }
@media (max-width: 520px) { .cf-row { grid-template-columns: 1fr; } }

.cf-field { display: block; margin-bottom: 0.875rem; }
.cf-field > span {
  display: block; margin-bottom: 0.4rem;
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--text-3);
}
.cf-field input, .cf-field textarea {
  width: 100%; font: inherit; font-size: 0.9375rem; color: var(--text);
  padding: 12px 14px; border-radius: 14px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--edge);
  transition: border-color 0.3s var(--ease), background-color 0.3s var(--ease);
  resize: vertical;
}
.cf-field textarea { min-height: 120px; line-height: 1.55; }
.cf-field input:focus, .cf-field textarea:focus {
  outline: none;
  border-color: color-mix(in srgb, var(--c2) 65%, transparent);
  background: rgba(255, 255, 255, 0.08);
}

/* Honeypot. Kept out of sight without display:none, which some bots
   check for. */
.cf-trap {
  position: absolute; left: -9999px; width: 1px; height: 1px;
  overflow: hidden;
}

/* Turnstile renders its own iframe, just give it room */
.cf-turnstile { margin: 0.25rem 0 0.875rem; min-height: 65px; }
.cf-turnstile:empty { min-height: 0; margin: 0; }

.cf-error {
  font-size: 0.8125rem; color: #ff8a8a;
  margin: 0.25rem 0 0.75rem;
}
.cf-error[hidden] { display: none; }


.cf-submit {
  position: relative; width: 100%; margin-top: 0.5rem;
  padding: 14px 22px; border-radius: 100px;
  font-size: 0.9375rem; font-weight: 600;
  background: color-mix(in srgb, var(--c2) 30%, rgba(255, 255, 255, 0.09));
  border: 1px solid color-mix(in srgb, var(--c2) 48%, var(--edge));
  box-shadow: inset 0 1px 0 var(--edge-bright);
  transition: background-color 0.35s var(--ease), transform 0.35s var(--ease),
              opacity 0.3s var(--ease);
}
.cf-submit:hover { background: color-mix(in srgb, var(--c2) 42%, rgba(255,255,255,0.12)); }
.cf-submit:active { transform: scale(0.99); }
.cf-submit:disabled { opacity: 0.75; cursor: default; }
.cf-submit.busy .cf-submit-label { opacity: 0; }

.cf-spinner {
  position: absolute; left: 50%; top: 50%;
  width: 17px; height: 17px; margin: -8.5px 0 0 -8.5px;
  border-radius: 50%; opacity: 0;
  border: 2px solid rgba(255, 255, 255, 0.28);
  border-top-color: #fff;
  transition: opacity 0.25s var(--ease);
}
.cf-submit.busy .cf-spinner { opacity: 1; animation: spin 0.7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }


/* ---------- sent ---------- */
.cf-done { text-align: center; padding-block: clamp(1.5rem, 4vw, 2.5rem); }
.cf-done[hidden] { display: none; }
.cf-tick {
  display: grid; place-items: center; margin: 0 auto 1.25rem;
  width: 56px; height: 56px; border-radius: 50%;
  background: color-mix(in srgb, var(--c2) 26%, transparent);
  border: 1px solid color-mix(in srgb, var(--c2) 55%, transparent);
}
.cf-tick svg {
  width: 26px; height: 26px; fill: none; stroke: var(--text);
  stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round;
}
.cf-done h2 {
  font-size: clamp(1.5rem, 3.2vw, 2rem);
  font-weight: 600; letter-spacing: -0.03em;
}
.cf-done p { margin-top: 0.5rem; color: var(--text-2); font-size: 0.9375rem; }
.cf-again {
  margin-top: 1.75rem; padding: 11px 24px; border-radius: 100px;
  font-size: 0.875rem; font-weight: 500; color: var(--text-2);
  border: 1px solid var(--edge);
  background: rgba(255, 255, 255, 0.05);
  transition: color 0.3s var(--ease), background-color 0.3s var(--ease);
}
.cf-again:hover { color: var(--text); background: rgba(255, 255, 255, 0.12); }

.foot-row {
  display: flex; flex-wrap: wrap; gap: 1.5rem; justify-content: space-between;
  margin-top: clamp(3rem, 6vw, 4.5rem); padding-top: 1.75rem;
  border-top: 1px solid rgba(255,255,255,0.09);
  font-size: 0.75rem; color: var(--text-3);
}
.foot-row a:hover { color: var(--text); }

/* ---------- RESPONSIVE ---------- */
@media (max-width: 900px) {
  .gal-grid { grid-template-columns: repeat(2, 1fr); }
  .nav-links { display: none; }
  .nav-glass { padding: 10px 20px; }
  .teaser-strip { grid-template-columns: repeat(3, 1fr); }
  .ts-go { grid-column: 1 / -1; justify-content: center; }
}
@media (max-width: 900px) {
  /* Series drops to halves, opener stays full width */
  .sh-8, .sh-7, .sh-5, .sh-6, .sh-4 { grid-column: span 6; }
}
@media (max-width: 620px) {
  .gal-grid { grid-template-columns: 1fr; grid-auto-rows: 220px; }
  .gshot.tall, .gshot.wide, .gshot.big { grid-row: span 1; grid-column: span 1; }
  .sh-8, .sh-7, .sh-5, .sh-6, .sh-4, .sh-12 { grid-column: span 12; }
  .sh-a { grid-row: span 1; }
  .sh-grid { grid-auto-rows: clamp(200px, 55vw, 280px); }
  .lb { grid-template-columns: 1fr; }
  .lb-nav { position: absolute; bottom: 1.5rem; }
  .lb-prev { left: 25%; }
  .lb-next { right: 25%; }
  .player { left: 12px; right: 12px; bottom: 12px; justify-content: center; }
  /* Clear the full width player pill */
  .cue { bottom: 6rem; }
  .blobs { filter: blur(50px); }
  /* vw-sized blobs are tiny on a narrow tall screen, so switch to vmax.
     Opacity comes down to stop them washing the whole screen out. */
  .blobs { opacity: 0.38; }
  .b1 { width: 58vmax; height: 58vmax; }
  .b2 { width: 50vmax; height: 50vmax; }
  .b3 { width: 46vmax; height: 46vmax; }
  .b4 { width: 36vmax; height: 36vmax; }
}

/* ---------- REDUCED MOTION ---------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .reveal { opacity: 1; transform: none; filter: none; }
  .line-in { transform: none; }
  .big-text .w { color: var(--text); }
}

/* ============================================================
   LITE MODE

   perf.js counts frames for half a second on load and adds `lite` to
   the root element if the page cannot hold a reasonable rate. That
   happens when the browser is software rendering, which is more
   common than it sounds: blocklisted drivers, virtual machines,
   remote desktop, old integrated graphics, or acceleration switched
   off. It also catches a thermally throttled laptop.

   Two things are swapped, and they are the two expensive ones.

   1. The live blur(110px) over a box 160% of the viewport, which
      animates forever, becomes static radial gradients on .field.
      A gradient is already a smooth falloff, so it needs no filter
      at all, and because it still reads --c1 --c2 --c3 the palette
      keeps changing between sections exactly as before. Only the
      drift and the blur are gone.

   2. Every backdrop-filter is dropped. Those surfaces then need a
      more opaque background of their own or the text on them stops
      being legible, which is what the block at the bottom does.

   Nobody on a working GPU ever sees any of this.
   ============================================================ */

.lite .blobs { display: none; }

.lite .field {
  background:
    radial-gradient(52vw 52vw at 12% 6%,
      color-mix(in srgb, var(--c1) 34%, transparent), transparent 68%),
    radial-gradient(46vw 46vw at 92% 22%,
      color-mix(in srgb, var(--c2) 30%, transparent), transparent 68%),
    radial-gradient(42vw 42vw at 34% 96%,
      color-mix(in srgb, var(--c3) 26%, transparent), transparent 70%),
    radial-gradient(130% 70% at 50% 100%,
      color-mix(in srgb, var(--c2) 10%, var(--base-2)), transparent 72%),
    radial-gradient(120% 90% at 50% 0%, var(--base-2), var(--base) 70%);
}

/* Nothing gets to read back and blur the page behind it any more. */
.lite *, .lite *::before, .lite *::after {
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
}

/* Those panels leaned on the blur for contrast, so they get a real
   background instead. Same colour family, just opaque enough to read. */
.lite .nav-glass,
.lite .player,
.lite .glass,
.lite .social,
.lite .cf,
.lite .pshow-meta,
.lite .side-inner,
.lite .gal-suggest { background: rgba(14, 16, 22, 0.9); }

.lite .chip,
.lite .ts-go,
.lite .contact-btn,
.lite .reel-sound,
.lite .back-fab,
.lite .gal-browse,
.lite .lb-nav,
.lite .lb-close,
.lite .pshow-nav { background: rgba(20, 22, 30, 0.88); }

.lite .lb { background: rgba(5, 6, 10, 0.96); }

/* The tiles are opaque photographs, so they only need their own base
   back, not a panel colour. */
.lite .gshot, .lite .sh { background: rgba(255, 255, 255, 0.04); }

/* Soft shadows are cheap next to a blur, but there are a lot of them
   and they all repaint on scroll. Flatten the heaviest. */
.lite .gshot, .lite .sh, .lite .reel-frame { box-shadow: none; }

/* ============================================================
   HOME PHOTOGRAPHY GRID

   Replaced a one at a time carousel. Seeing several frames at once
   is what tells a visitor there is a body of work behind the link,
   which a single slide cannot do. Each tile deep links to its own
   place in the gallery.
   ============================================================ */
.ts-grid {
  width: 100%;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(0.5rem, 1.2vw, 0.875rem);
}
.ts-tile {
  position: relative; overflow: hidden;
  aspect-ratio: 4 / 3;
  border-radius: 16px;
  border: 1px solid var(--edge);
  background: rgba(255, 255, 255, 0.04);
  box-shadow: inset 0 1px 0 var(--edge-bright), 0 20px 50px -26px rgba(0,0,0,0.8);
  transition: transform 0.6s var(--ease), border-color 0.4s var(--ease);
}
.ts-tile img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform 1s var(--ease);
}
.ts-tile:hover { transform: translateY(-4px); border-color: var(--rule-2, rgba(255,255,255,0.24)); }
.ts-tile:hover img { transform: scale(1.06); }
.ts-tile:focus-visible { outline: 2px solid var(--c2); outline-offset: 3px; }

/* The first tile leads, so it gets the room to. */
.ts-tile:first-child { grid-column: span 2; grid-row: span 2; aspect-ratio: auto; }

@media (max-width: 620px) {
  .ts-grid { grid-template-columns: repeat(2, 1fr); }
  .ts-tile:first-child { grid-column: span 2; grid-row: auto; aspect-ratio: 4 / 3; }
}

.lite .ts-tile { box-shadow: none; }

/* ============================================================
   MOBILE NAV

   .nav-links is hidden under 900px, and until now nothing replaced
   it, so a phone had no navigation at all. This is the replacement.

   The hidden state is spelled out rather than left to the [hidden]
   attribute, because a class that sets display beats it. That is
   gotcha 10, and it shipped broken on the admin page three times.
   ============================================================ */
.nav-toggle { display: none; }

.nav-sheet[hidden] { display: none; }

@media (max-width: 900px) {
  .nav-toggle {
    display: grid; place-items: center;
    width: 40px; height: 40px; margin-left: auto;
    border-radius: 100px; cursor: pointer;
    color: var(--text-2);
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid var(--edge);
    transition: color 0.3s var(--ease), background-color 0.3s var(--ease);
  }
  .nav-toggle:hover, .nav-toggle[aria-expanded="true"] {
    color: var(--text); background: rgba(255, 255, 255, 0.13);
  }
  .nav-bars { display: grid; gap: 5px; width: 17px; }
  .nav-bars i {
    display: block; height: 1.5px; border-radius: 2px;
    background: currentColor;
    transition: transform 0.35s var(--ease), opacity 0.35s var(--ease);
  }
  .nav-toggle[aria-expanded="true"] .nav-bars i:first-child { transform: translateY(3.25px) rotate(45deg); }
  .nav-toggle[aria-expanded="true"] .nav-bars i:last-child  { transform: translateY(-3.25px) rotate(-45deg); }

  .nav-sheet {
    position: fixed; inset: 0; z-index: 140;
    display: grid; align-content: center; justify-items: stretch;
    padding: clamp(1.25rem, 6vw, 2.5rem);
    background: rgba(5, 6, 10, 0.82);
    -webkit-backdrop-filter: blur(28px) saturate(180%);
    backdrop-filter: blur(28px) saturate(180%);
  }
  .nav-sheet-inner {
    display: grid; gap: 0.25rem;
    max-width: 26rem; width: 100%; margin-inline: auto;
  }
  .nav-sheet-inner a {
    font-size: 1.5rem; letter-spacing: -0.02em; color: var(--text-2);
    padding: 0.6rem 0.75rem; border-radius: 14px;
    transition: color 0.3s var(--ease), background-color 0.3s var(--ease);
  }
  .nav-sheet-inner a:hover, .nav-sheet-inner a:focus-visible {
    color: var(--text); background: rgba(255, 255, 255, 0.07);
  }
  .nav-sheet-go {
    margin-top: 1rem;
    display: inline-flex !important; align-items: center; gap: 0.6rem;
    font-size: 1rem !important; font-weight: 500;
    color: var(--text) !important;
    background: rgba(255, 255, 255, 0.07);
    border: 1px solid var(--edge);
    border-radius: 100px !important;
    padding: 0.85rem 1.3rem !important;
    justify-content: center;
  }
  .nav-sheet-go svg {
    width: 16px; height: 16px; fill: none; stroke: currentColor;
    stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
  }
}

/* Nothing on a wide screen ever shows the sheet, even if a stale
   attribute survives a resize. */
@media (min-width: 901px) {
  .nav-sheet { display: none !important; }
}
