/* =========================================================
   Top Thanakorn — Editor
   v3d-r2-quieter visual system
   Antonio (display) + Archivo Black (sub-display) + IBM Plex Mono (labels)
   ========================================================= */

:root {
  --ink:    #0a0a0a;
  --paper:  #ffffff;
  --soft:   #f4f4f4;
  --rule:   2px;
  --gutter: clamp(32px, 5vw, 72px);
  /* Easing system — pick by intent, not by feel */
  --ease:        cubic-bezier(.2,.7,.2,1);     /* legacy default (still used widely) */
  --ease-out:    cubic-bezier(.16,1,.3,1);     /* smooth deceleration — entrances, focus mode */
  --ease-snappy: cubic-bezier(.32,.72,0,1);    /* quick + responsive — hover, tap */
  --ease-spring: cubic-bezier(.34,1.56,.64,1); /* subtle overshoot — focus tile lift, mini morph */
  /* Burnt-orange accent — punctuation only */
  --accent: #C25A1F;
  --feat-bg:     #0a0a0a;
  --feat-fg:     #C25A1F;
  --feat-border: #C25A1F;
  --pin-color:   #C25A1F;
}

*  { box-sizing: border-box; margin: 0; padding: 0; }
html, body { background: var(--paper); color: var(--ink); }
body {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 13px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
img, video, iframe { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; padding: 0; }

/* =========================================================
   Top bar
   ========================================================= */

.topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  padding: clamp(16px, 2.2vw, 22px) var(--gutter);
  display: flex;
  justify-content: space-between;
  align-items: center;
  /* Liquid Glass — frosted, neutral, rim-lit */
  background: rgba(255, 255, 255, 0.65);
  -webkit-backdrop-filter: blur(20px) saturate(1.5);
  backdrop-filter:         blur(20px) saturate(1.5);
  border-bottom: 1px solid rgba(10, 10, 10, 0.08);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.6),
    0 6px 24px -12px rgba(10, 10, 10, 0.18);
}
/* Fallback if backdrop-filter unsupported */
@supports not (backdrop-filter: blur(1px)) {
  .topbar { background: rgba(255, 255, 255, 0.92); }
}
.topbar .mark {
  font-family: "Archivo Black", sans-serif;
  font-size: 14px;
  letter-spacing: 0;
}
.topbar nav a {
  font-family: "IBM Plex Mono", monospace;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin-left: 24px;
  transition: opacity .15s var(--ease-snappy);
}
.topbar nav a:hover { opacity: 0.55; }

/* =========================================================
   Hero — quiet eyebrow, loud type
   ========================================================= */

.hero {
  position: relative;
  padding: clamp(96px, 14vh, 180px) var(--gutter) clamp(140px, 18vh, 220px);
  border-bottom: var(--rule) solid var(--ink);
  /* Dark hero — mosaic + marquee earn the contrast flip */
  background: #0a0a0a;
  color: var(--paper);
}

/* ---------- Hero thumbnail mosaic ----------
   Full-bleed 4×3 grid of work thumbnails. Each cell crossfades to a
   different project on a stagger. JS populates and cycles. */
.hero-mosaic {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(3, 1fr);
  gap: 4px;
  padding: 4px;
  opacity: 0.62;          /* brighter — work shows through */
  pointer-events: none;
  /* Global brightness pulse stays on the parent — the per-cell jitter
     happens on .mc children instead so cells glitch out of sync. */
  animation: crt-flicker 4.5s steps(40, end) infinite;
  /* Faint chromatic aberration — the look of a poorly-tuned signal */
  filter: drop-shadow(1px 0 0 rgba(120, 40, 40, 0.18))
          drop-shadow(-1px 0 0 rgba(40, 120, 140, 0.14));
}
.hero-mosaic .mc {
  position: relative;
  overflow: hidden;
  border-radius: 6px;
  background: #050505;
  /* Per-cell glitch — each cell gets randomized --glitch-delay / -duration
     and one of three keyframe variants (.glitch-a/b/c) at mount time so
     cells glitch desynced. steps(1, end) keeps the dropouts crisp. */
  animation-duration: var(--glitch-duration, 6s);
  animation-delay: var(--glitch-delay, 0s);
  animation-iteration-count: infinite;
  animation-timing-function: steps(1, end);
}
.hero-mosaic .mc.glitch-a { animation-name: cell-glitch-a; }
.hero-mosaic .mc.glitch-b { animation-name: cell-glitch-b; }
.hero-mosaic .mc.glitch-c { animation-name: cell-glitch-c; }
.hero-mosaic .mc-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 1.4s var(--ease);
  filter: saturate(0.95) brightness(0.95);   /* less heavy-handed */
}
.hero-mosaic .mc-img.is-current { opacity: 1; }

/* Soft curved-CRT vignette — sits above mosaic, below text/scanlines. */
.hero-vignette {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: radial-gradient(
    ellipse 80% 60% at 50% 50%,
    transparent 50%,
    rgba(0, 0, 0, 0.4) 100%
  );
}

/* Lighter dark gradient — let mosaic show through, beef up text-shadow instead */
.hero-overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background:
    radial-gradient(ellipse at 50% 38%, rgba(0,0,0,0.18) 0%, rgba(0,0,0,0.55) 75%),
    linear-gradient(180deg, rgba(0,0,0,0.35) 0%, rgba(0,0,0,0.65) 100%);
}

/* CRT scanlines — fixed 3px-period horizontal stripes via repeating gradient.
   mix-blend-mode: multiply makes them darken what's behind without wiping it.
   Gentle opacity flicker keeps them feeling alive. */
.hero-scanlines {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background: repeating-linear-gradient(
    0deg,
    rgba(0, 0, 0, 0.18) 0px,
    rgba(0, 0, 0, 0.18) 1px,
    transparent 1px,
    transparent 3px
  );
  mix-blend-mode: multiply;
  opacity: 0.7;
  animation: crt-flicker 4.5s steps(40, end) infinite;
}

/* Analog TV motion ====================================== */
@keyframes crt-flicker {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.96; }
  52%      { opacity: 0.85; }   /* brief deeper dropout */
  53%      { opacity: 1; }
  78%      { opacity: 0.93; }
  79%      { opacity: 1; }
}
/* Three per-cell glitch variants — different jitter offsets + dropout
   moments so adjacent cells with different durations + variants never
   sync. Cells stay stable for ~88-92% of each cycle, glitch in brief
   5-10% windows. */
@keyframes cell-glitch-a {
  0%, 47%, 53%, 88%, 100% { transform: translate(0, 0); opacity: 1; }
  48%                     { transform: translate(0, 1px); opacity: 0.4; }
  50%                     { transform: translate(0, 0); opacity: 1; }
  90%                     { transform: translate(-2px, 1px); opacity: 0.85; }
  91%                     { transform: translate(2px, -1px); opacity: 0.7; }
  92%                     { transform: translate(-1px, 0); opacity: 1; }
}
@keyframes cell-glitch-b {
  0%, 22%, 28%, 76%, 100% { transform: translate(0, 0); opacity: 1; }
  23%                     { transform: translate(2px, 0); opacity: 0.55; }
  25%                     { transform: translate(-2px, 0); opacity: 0.85; }
  26%                     { transform: translate(0, 0); opacity: 1; }
  78%                     { transform: translate(0, 2px); opacity: 0.5; }
  79%                     { transform: translate(0, -1px); opacity: 1; }
}
@keyframes cell-glitch-c {
  0%, 64%, 70%, 100%      { transform: translate(0, 0); opacity: 1; }
  65%                     { transform: translate(-3px, 0); opacity: 0.6; }
  66%                     { transform: translate(3px, 1px); opacity: 0.75; }
  67%                     { transform: translate(0, -1px); opacity: 0.95; }
  68%                     { transform: translate(0, 0); opacity: 1; }
  35%                     { transform: translate(1px, 1px); opacity: 0.85; }
  36%                     { transform: translate(0, 0); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-mosaic, .hero-mosaic .mc, .hero-scanlines { animation: none !important; }
}

/* Floating Liquid Glass count chip — re-tinted for dark hero */
.hero-tag {
  position: absolute;
  top: clamp(96px, 14vh, 180px);
  right: var(--gutter);
  font-family: "IBM Plex Mono", monospace;
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--paper);
  padding: 8px 14px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  backdrop-filter:         blur(14px) saturate(1.4);
  border: 1px solid rgba(255, 255, 255, 0.16);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.18),
    0 2px 10px -4px rgba(0, 0, 0, 0.4);
  display: inline-flex; align-items: center; gap: 8px;
  z-index: 3;
}
.hero-tag .dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 2px rgba(194, 90, 31, 0.28);
}
@supports not (backdrop-filter: blur(1px)) {
  .hero-tag { background: rgba(20, 20, 20, 0.85); }
}

/* ---------- Brand marquee ---------- */
.brand-marquee {
  position: absolute;
  bottom: clamp(36px, 6vh, 60px);
  left: 0;
  right: 0;
  z-index: 4;
  overflow: hidden;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, black 8%, black 92%, transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0%, black 8%, black 92%, transparent 100%);
}
.marquee-track {
  display: inline-flex;
  align-items: center;
  gap: 22px;
  white-space: nowrap;
  font-family: "IBM Plex Mono", monospace;
  font-size: 11.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
  animation: marquee-scroll 28s linear infinite;
  will-change: transform;
}
.brand-marquee:hover .marquee-track { animation-play-state: paused; }
.marquee-track .m-item { display: inline-block; }
.marquee-track .m-sep {
  display: inline-block;
  color: var(--accent);
  opacity: 0.55;
}
@keyframes marquee-scroll {
  0%   { transform: translateX(0); }
  100% { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
  .marquee-track { animation: none; }
}

/* =========================================================
   Hero ambient glass blobs — soft warm/neutral discs blurred
   heavily under the hero name. Sit behind h1 (z-index: 0),
   shift on scroll via --scroll-y CSS var.
   ========================================================= */

.hero { isolation: isolate; overflow: hidden; }
.hero h1, .hero .role, .hero .bio, .hero-tag { position: relative; z-index: 4; }

.hero-blobs {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;
  mix-blend-mode: screen;
}
.hero-blobs::before,
.hero-blobs::after,
.hero-blobs > .blob {
  content: "";
  position: absolute;
  border-radius: 50%;
  filter: blur(80px);
  opacity: 0.22;
  will-change: transform;
  transform: translate3d(0, calc(var(--scroll-y, 0) * 0.10), 0);
}
.hero-blobs::before {
  width: 46vw; height: 46vw;
  top: -8vw; left: -6vw;
  background: radial-gradient(closest-side, #f5e9dc 0%, rgba(245,233,220,0) 70%);
}
.hero-blobs::after {
  width: 38vw; height: 38vw;
  bottom: -10vw; right: -4vw;
  background: radial-gradient(closest-side, #ffe7d1 0%, rgba(255,231,209,0) 70%);
  transform: translate3d(0, calc(var(--scroll-y, 0) * -0.08), 0);
  opacity: 0.2;
}
.hero-blobs > .blob {
  width: 32vw; height: 32vw;
  top: 18vh; right: 22vw;
  background: radial-gradient(closest-side, #ffffff 0%, rgba(255,255,255,0) 70%);
  opacity: 0.28;
  transform: translate3d(0, calc(var(--scroll-y, 0) * 0.12), 0);
}

/* Glass plane riding on top of the blobs to bind them into a frosted layer */
.hero-glass {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  -webkit-backdrop-filter: saturate(1.15);
  backdrop-filter:         saturate(1.15);
}

@media (prefers-reduced-motion: reduce) {
  .hero-blobs::before,
  .hero-blobs::after,
  .hero-blobs > .blob { transform: none; }
}

/* =========================================================
   Now Playing HUD (mobile / scroll-driven)
   ========================================================= */

/* Mini player + now-playing pill are deprecated — focus mode uses
   TikTok-style swipe nav now, no morph-to-mini path. Hide them at the
   stylesheet level so the dormant JS state machine can't surface them.
   To revive: remove the two `display: none !important` lines below. */
.mini-player,
.now-playing { display: none !important; }

.now-playing {
  position: fixed;
  left: 50%;
  bottom: calc(28px + env(safe-area-inset-bottom, 0px));
  transform: translate(-50%, 24px);
  z-index: 60;
  max-width: min(560px, calc(100vw - 32px));
  padding: 12px 18px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.4);
  -webkit-backdrop-filter: blur(28px) saturate(1.6);
  backdrop-filter:         blur(28px) saturate(1.6);
  border: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.18),
    0 12px 36px -8px rgba(0, 0, 0, 0.45);
  color: var(--paper);
  font-family: "IBM Plex Mono", monospace;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  display: none;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  opacity: 0;
  transition: transform .35s var(--ease), opacity .25s var(--ease);
}
.now-playing.is-on {
  display: inline-flex;
  opacity: 1;
  transform: translate(-50%, 0);
}
.now-playing .np-pulse {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
  box-shadow: 0 0 0 0 rgba(194, 90, 31, 0.55);
  animation: np-pulse 1.6s var(--ease) infinite;
}
.now-playing .np-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.now-playing .np-name {
  font-family: "Archivo Black", sans-serif;
  font-size: 11px;
  letter-spacing: 0.04em;
  margin-right: 8px;
}
.now-playing .np-dir {
  color: rgba(255, 255, 255, 0.7);
}
@keyframes np-pulse {
  0%   { box-shadow: 0 0 0 0   rgba(194, 90, 31, 0.55); }
  70%  { box-shadow: 0 0 0 10px rgba(194, 90, 31, 0); }
  100% { box-shadow: 0 0 0 0   rgba(194, 90, 31, 0); }
}
@supports not (backdrop-filter: blur(1px)) {
  .now-playing { background: rgba(0, 0, 0, 0.85); }
}
@media (hover: hover) and (pointer: fine) {
  /* Desktop already gets hover feedback — hide HUD */
  .now-playing { display: none !important; }
}
body.is-mini .now-playing { display: none !important; }

/* =========================================================
   Dim layer (Focus mode) — solid dim + frosted glass refraction
   The pinned tile (z 80), metadata panel (z 85) and mini player
   (z 90) sit above this. No aurora; just plain dark + blur.
   ========================================================= */
.dim-layer {
  position: fixed;
  inset: 0;
  z-index: 70;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.72);
  opacity: 0;
  /* visibility:hidden (not display:none) keeps the GPU layer composed
     at idle so backdrop-filter doesn't have to be built when focus mode
     triggers — that build was the source of the entrance frame drop. */
  visibility: hidden;
  pointer-events: none;
  transform: translateZ(0);     /* force own GPU layer */
  will-change: opacity;
  /* 50ms exit delay — tile starts retreating first, dim follows for a
     more orchestrated feel. body.is-focus override below cancels delay
     on enter so dim leads in immediately.
     visibility flips AFTER the fade-out completes (.65s + .05s buffer)
     so the layer stays composed throughout the exit animation. */
  transition: opacity .65s var(--ease-out) 50ms,
              visibility 0s linear .7s;
}
body.is-focus .dim-layer {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity .65s var(--ease-out) 0ms,
              visibility 0s linear 0ms;
}

/* Glass refraction layer — blurs page tiles behind .dim-layer.
   28px (was 40px) — still clearly frosted but ~40% less GPU work on iOS,
   which helps the entrance compositor. */
.dim-layer::after {
  content: "";
  position: absolute;
  inset: 0;
  -webkit-backdrop-filter: blur(28px) saturate(1.4);
  backdrop-filter:         blur(28px) saturate(1.4);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
  pointer-events: none;
}
@supports not (backdrop-filter: blur(1px)) {
  .dim-layer { background: rgba(0, 0, 0, 0.88); }
}

/* Pre-focus stage: dim already half-on, tile already lifted, but scroll
   still unlocked so we can smooth-center the tile. */
body.is-focus-pending .dim-layer {
  opacity: 0.5;
  visibility: visible;
  pointer-events: none;
  transition: opacity .35s var(--ease),
              visibility 0s linear 0ms;
}
body.is-focus-pending .tile.is-pinned { z-index: 80; }

/* ---------- TikTok feed peek tiles (mobile focus mode) ----------
   Two ghost tile-frames that sit one viewport above (prev) and one below
   (next) the pinned tile. They show the static thumbnail only — no live
   iframe, no audio. Translate together with the pinned tile during a swipe
   so the feed feels continuous. JS positions them via inline transform/
   left/top/width to match the pinned tile's bounding rect. */
.focus-peek {
  position: fixed;
  z-index: 79;                  /* below pinned (80), above dim (70) */
  pointer-events: none;
  border-radius: 16px;
  overflow: hidden;
  opacity: 0;
  transform: translateY(-100vh);
  will-change: transform;
  box-shadow:
    0 14px 38px -10px rgba(10, 10, 10, 0.32),
    0 4px 14px -8px rgba(10, 10, 10, 0.18);
}
body.is-focus .focus-peek { opacity: 1; }
.focus-peek > .tile-frame {
  position: relative;
  margin: 0;
  /* Inline width/height set by JS to mirror pinned tile rect. */
}
/* Strip pin indicator + caption styling on the cloned ghost */
.focus-peek .cap-pin,
.focus-peek .tile-caption { display: none; }

/* Pinned tile sits above the dim while in focus mode */
body.is-focus .tile.is-pinned { z-index: 80; }

/* Lock page scroll while in focus — gesture is captured as tear-away intent */
body.is-focus {
  overflow: hidden;
  overscroll-behavior: contain;
  touch-action: none;
}

/* =========================================================
   Mini player (floating glass, bottom-right)
   ========================================================= */
.mini-player {
  position: fixed;
  right: calc(20px + env(safe-area-inset-right, 0px));
  bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  z-index: 90;
  width: 280px;
  aspect-ratio: 16 / 9;
  border-radius: 16px;
  overflow: hidden;
  background: rgba(10, 10, 10, 0.55);
  -webkit-backdrop-filter: blur(28px) saturate(1.6);
  backdrop-filter:         blur(28px) saturate(1.6);
  border: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.16),
    0 24px 60px -16px rgba(0, 0, 0, 0.55);
  opacity: 0;
  pointer-events: none;
  transform-origin: top left;
  /* transform animation handled inline via FLIP; opacity handled via class */
  transition: opacity .35s var(--ease);
  cursor: pointer;
}
.mini-player.is-open {
  opacity: 1;
  pointer-events: auto;
}
@supports not (backdrop-filter: blur(1px)) {
  .mini-player { background: rgba(10, 10, 10, 0.92); }
}
.mini-host {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  overflow: hidden;
  pointer-events: none;
}
/* (Plan B: tiles + mini show thumbnails only; YouTube iframe lives only in
   the modal player. No iframe rules needed for the mini-host.) */
.mini-controls {
  position: absolute;
  top: 8px; left: 8px; right: 8px;
  display: flex;
  justify-content: space-between;
  z-index: 2;
  pointer-events: none;
}
.mini-btn {
  pointer-events: auto;
  width: 28px; height: 28px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.18);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  backdrop-filter:         blur(14px) saturate(1.4);
  border: 1px solid rgba(255, 255, 255, 0.28);
  color: #fff;
  font-family: "IBM Plex Mono", monospace;
  font-size: 14px;
  line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: background .2s var(--ease), transform .2s var(--ease);
}
.mini-btn:hover { background: rgba(255, 255, 255, 0.3); transform: scale(1.06); }
.mini-meta {
  position: absolute;
  left: 12px; right: 12px; bottom: 10px;
  display: flex; gap: 8px; align-items: baseline;
  font-family: "IBM Plex Mono", monospace;
  font-size: 9.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #fff;
  z-index: 2;
  pointer-events: none;
  text-shadow: 0 1px 2px rgba(0,0,0,0.6);
  min-width: 0;
}
.mini-title {
  font-family: "Archivo Black", sans-serif;
  font-size: 10.5px;
  letter-spacing: 0.04em;
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mini-dir { color: rgba(255, 255, 255, 0.7); flex-shrink: 0; }
@media (max-width: 480px) {
  .mini-player { width: min(280px, 70vw); right: 14px; bottom: 14px; }
}
.hero .role {
  font-family: "IBM Plex Mono", monospace;
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  margin-bottom: clamp(40px, 6vh, 80px);
  color: rgba(255, 255, 255, 0.65);
}
.hero h1 {
  font-family: "Antonio", sans-serif;
  font-weight: 700;
  font-size: clamp(72px, 17vw, 280px);
  line-height: 0.86;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: var(--paper);
  text-shadow: 0 6px 36px rgba(0, 0, 0, 0.6), 0 2px 12px rgba(0, 0, 0, 0.45);
}
.hero h1 .name-1,
.hero h1 .name-2 { display: inline-block; will-change: transform, opacity; }
.hero .bio {
  margin-top: clamp(40px, 6vh, 72px);
  max-width: 56ch;
  font-family: "IBM Plex Mono", monospace;
  font-size: 13px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.85);
}

/* ---------- Kinetic name reveal (first session load only) ---------- */
@keyframes hero-reveal-large {
  from { opacity: 0; transform: translateY(40px); }
  to   { opacity: 1; transform: none; }
}
@keyframes hero-reveal-small {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}
body.hero-anim .hero .role         { animation: hero-reveal-small .55s var(--ease) 0s    both; }
body.hero-anim .hero h1 .name-1    { animation: hero-reveal-large .65s var(--ease) .2s   both; }
body.hero-anim .hero h1 .name-2    { animation: hero-reveal-large .65s var(--ease) .4s   both; }
body.hero-anim .hero-tag           { animation: hero-reveal-small .5s  var(--ease) .65s  both; }
body.hero-anim .brand-marquee      { animation: hero-reveal-small .55s var(--ease) .8s   both; }
@media (prefers-reduced-motion: reduce) {
  body.hero-anim .hero .role,
  body.hero-anim .hero h1 .name-1,
  body.hero-anim .hero h1 .name-2,
  body.hero-anim .hero-tag,
  body.hero-anim .brand-marquee { animation: none; }
}

/* =========================================================
   Section header
   ========================================================= */

.section-head {
  padding: clamp(56px, 8vh, 112px) var(--gutter) clamp(40px, 5vh, 72px);
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 24px;
  flex-wrap: wrap;
}
.section-head h2 {
  font-family: "Archivo Black", sans-serif;
  font-size: clamp(20px, 2vw, 28px);
  text-transform: uppercase;
  letter-spacing: -0.005em;
}

/* =========================================================
   About — work history + education panel between work and footer.
   Dark band for visual contrast against the light work grid above
   and the frosted-glass contact footer below. Sarabun body, single
   centered column, max-width 720px. Eyebrow in mono accent caps.
   ========================================================= */

.about {
  background: #0a0a0a;
  color: var(--paper);
  border-top: var(--rule) solid var(--ink);
  padding: clamp(80px, 12vh, 140px) var(--gutter);
}
.about-inner {
  max-width: 720px;
  margin: 0 auto;
}
.about-eyebrow {
  display: block;
  font-family: "IBM Plex Mono", monospace;
  font-size: 12px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--accent);
  margin-bottom: clamp(24px, 3.5vh, 40px);
}
.about-body {
  font-family: "Sarabun", system-ui, -apple-system, sans-serif;
  font-weight: 400;
  font-size: clamp(16px, 1.5vw, 18px);
  line-height: 1.7;
  color: rgba(255, 255, 255, 0.82);
  margin: 0 0 clamp(16px, 2.5vh, 24px);
  letter-spacing: 0.005em;
}
.about-body em.accent {
  font-style: normal;
  color: var(--accent);
  font-weight: 500;
}
.about-meta {
  margin: clamp(20px, 3vh, 32px) 0 0;
  font-family: "IBM Plex Mono", monospace;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
}
@media (max-width: 600px) {
  .about { padding: clamp(64px, 10vh, 100px) var(--gutter); }
  .about-body { font-size: 15px; line-height: 1.65; }
}

/* =========================================================
   Filter chips — quiet, mono, no pill
   ========================================================= */

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 12px;
  padding: 0 var(--gutter) clamp(40px, 5vh, 64px);
}
.filter-pill {
  font-family: "IBM Plex Mono", monospace;
  font-size: 11.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
  /* Liquid Glass pill */
  padding: 8px 14px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.55);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  backdrop-filter:         blur(14px) saturate(1.4);
  border: 1px solid rgba(10, 10, 10, 0.1);
  color: var(--ink);
  opacity: 0.78;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    0 2px 10px -4px rgba(10, 10, 10, 0.12);
  transition: opacity .2s var(--ease-snappy), color .2s var(--ease-snappy),
              border-color .2s var(--ease-snappy), background .25s var(--ease-snappy),
              box-shadow .2s var(--ease-snappy),
              transform .2s var(--ease-snappy);
}
.filter-pill:hover { opacity: 1; transform: translateY(-1px); }
.filter-pill.is-active {
  opacity: 1;
  color: var(--accent);
  border-color: rgba(194, 90, 31, 0.45);
  background: rgba(255, 255, 255, 0.75);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.7),
    inset 0 0 0 1px rgba(194, 90, 31, 0.25),
    0 4px 14px -4px rgba(194, 90, 31, 0.25);
}
.filter-pill .count {
  margin-left: 8px;
  font-variant-numeric: tabular-nums;
  opacity: 0.55;
}
@supports not (backdrop-filter: blur(1px)) {
  .filter-pill { background: rgba(255, 255, 255, 0.92); }
}

/* =========================================================
   Load More — progressive reveal for the work grid.
   Initial state shows INITIAL_VISIBLE tiles; the rest carry
   `.tile.is-hidden` until the user clicks the button below
   the grid. Filter changes reset to this collapsed state.
   ========================================================= */

.tile.is-hidden { display: none; }

.load-more-row {
  display: flex;
  justify-content: center;
  padding: clamp(24px, 4vh, 48px) var(--gutter) clamp(56px, 8vh, 96px);
}
.load-more {
  font-family: "IBM Plex Mono", monospace;
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  /* Liquid Glass pill — matches filter-pill family but a touch larger
     to read as a primary action. */
  padding: 12px 22px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.55);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  backdrop-filter:         blur(14px) saturate(1.4);
  border: 1px solid rgba(10, 10, 10, 0.12);
  color: var(--ink);
  opacity: 0.85;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.6),
    0 4px 16px -6px rgba(10, 10, 10, 0.18);
  transition: opacity .2s var(--ease-snappy), color .2s var(--ease-snappy),
              border-color .2s var(--ease-snappy), background .25s var(--ease-snappy),
              box-shadow .2s var(--ease-snappy),
              transform .2s var(--ease-snappy);
}
.load-more:hover {
  opacity: 1;
  color: var(--accent);
  transform: translateY(-1px);
  border-color: rgba(194, 90, 31, 0.35);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.7),
    0 6px 20px -6px rgba(194, 90, 31, 0.22);
}
.load-more.is-hidden { display: none; }
@supports not (backdrop-filter: blur(1px)) {
  .load-more { background: rgba(255, 255, 255, 0.92); }
}

/* =========================================================
   Work — Mosaic + slot-machine spotlight
   6-up grid, featured = 4-col span. Frame is the only rounded
   surface; all metadata sits in a clean caption block BELOW
   the frame in monochrome typography. Nothing overlays the
   thumbnail / video preview.
   ========================================================= */

.work {
  padding: 0 var(--gutter) clamp(80px, 10vh, 140px);
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  grid-auto-rows: auto;
  grid-auto-flow: dense;
  column-gap: clamp(10px, 1.2vw, 18px);
  row-gap:    clamp(28px, 3vh, 44px);
}

.tile {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 14px;
  cursor: pointer;
  grid-column: span 2;
  /* container-level transitions for any future tile-wide effects */
  transition: transform .35s var(--ease);
}
/* All tiles are uniform — no featured override. */

/* The rounded frame is the visual unit. Nothing else lives inside it. */
.tile-frame {
  position: relative;
  overflow: hidden;
  border-radius: 16px;
  background: var(--ink);
  border: 1px solid var(--ink);
  aspect-ratio: 16 / 9;
  outline: 0px solid var(--accent);
  outline-offset: 0px;
  transform-origin: center center;
  /* Slower, more breathing scale for spotlight; snappy for outline tweaks. */
  transition: transform .5s var(--ease-out),
              box-shadow .5s var(--ease-out),
              outline-width .25s var(--ease-snappy),
              outline-offset .25s var(--ease-snappy);
  /* Pre-compose for GPU compositing during scale + drop-shadow animations */
  transform: translateZ(0);
}
/* Only put will-change on tiles that are actually doing work right now —
   a blanket will-change on all 64 frames eats memory. */
.tile.is-spotlight .tile-frame,
.tile.is-pinned    .tile-frame,
.tile:hover        .tile-frame { will-change: transform, box-shadow; }
/* (was: .tile.feat .tile-frame { border-radius: 22px; } — featured tiles removed) */

/* Spotlight + pinned: subtle scale + lift drop shadow */
.tile.is-spotlight .tile-frame,
.tile.is-pinned    .tile-frame {
  transform: scale(1.04);
  box-shadow:
    0 14px 38px -10px rgba(10, 10, 10, 0.32),
    0 4px 14px -8px rgba(10, 10, 10, 0.18);
}
.tile.is-spotlight,
.tile.is-pinned { z-index: 5; }

/* Pinned indicator OUTSIDE the frame: orange outline offset by 6px */
.tile.is-pinned .tile-frame {
  outline: 2px solid var(--accent);
  outline-offset: 6px;
  box-shadow:
    0 14px 38px -10px rgba(194, 90, 31, 0.32),
    0 4px 14px -8px rgba(10, 10, 10, 0.18);
}

/* In FOCUS mode: lift the pinned tile-frame OUT of the mosaic grid and
   anchor it to a fixed viewport rect.
   IMPORTANT: WebKit has a bug where elements with mask-image/clip-path
   ignore `position: fixed` and stay in their original flow. We neutralise
   those properties here, and use !important to defeat any cascade fights
   with the spotlight transform: scale(1.04) rule. */
body.is-focus .tile.is-pinned,
body.is-focus-pending .tile.is-pinned {
  z-index: 80;
  /* Neutralise any property that would create a fixed-positioning
     containing block at the parent level — the .tile must NOT trap its
     fixed-positioned child. */
  transform: none !important;
  filter: none !important;
  perspective: none !important;
}
body.is-focus .tile.is-pinned .tile-frame,
body.is-focus-pending .tile.is-pinned .tile-frame {
  position: fixed !important;
  left: 50% !important;
  top: 35vh !important;
  transform: translate(-50%, -50%) !important;
  width: calc(100vw - 48px) !important;
  max-width: 600px !important;
  aspect-ratio: 16 / 9 !important;
  height: auto !important;
  /* Neutralise the WebKit fixed-positioning bug triggered by mask-image / clip-path.
     The rounded corners are still enforced by border-radius + overflow: hidden. */
  -webkit-mask-image: none !important;
          mask-image: none !important;
  -webkit-clip-path: none !important;
          clip-path: none !important;
  outline-offset: 4px;
  box-shadow:
    0 24px 60px -16px rgba(10, 10, 10, 0.55),
    0 8px 24px -10px rgba(194, 90, 31, 0.28);
  /* Springy, deliberate lift into the hero rect — feels alive, not snappy. */
  transition: width .6s var(--ease-spring),
              top .6s var(--ease-spring),
              transform .6s var(--ease-spring),
              box-shadow .6s var(--ease-out);
}
@media (min-width: 760px) {
  body.is-focus .tile.is-pinned .tile-frame,
  body.is-focus-pending .tile.is-pinned .tile-frame {
    width: min(calc(100vw - 96px), 720px) !important;
    max-width: 90vw !important;
  }
}
@media (min-width: 1280px) {
  body.is-focus .tile.is-pinned .tile-frame,
  body.is-focus-pending .tile.is-pinned .tile-frame {
    width: min(calc(100vw - 160px), 900px) !important;
    max-width: 90vw !important;
  }
}

.tile-frame img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
  filter: saturate(0.55) brightness(0.92);
  transition: filter .5s var(--ease), opacity .35s var(--ease);
  z-index: 2;
  will-change: transform, filter, opacity;
}
.tile.is-active .tile-frame img,
.tile:hover    .tile-frame img {
  filter: saturate(1) brightness(1);
}
/* When PLAYING: thumbnail crossfades out, iframe shows through */
.tile.is-playing .tile-frame img {
  opacity: 0;
  transition: opacity .3s var(--ease), filter .5s var(--ease);
}

/* Iframe sits below the thumbnail until .is-playing reveals it */
.tile-frame .iframe-slot {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  border-radius: inherit;
  z-index: 1;
}

/* Tile / focus-mode iframe — full video, no crop. YouTube UI may show on
   the edges; accepted trade-off so the video itself isn't clipped. */
.tile-frame .iframe-slot iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  pointer-events: none;
}

/* Mini player iframe — keep the 5% scale crop. Mini is smaller, so the
   bit of YouTube UI the crop hides matters more visually than the slight
   magnification. */
.mini-host iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  pointer-events: none;
  transform: scale(1.10);
  transform-origin: center center;
}

/* Focus-mode nav arrows — prev / next project. Liquid Glass pills
   matching the mini-player buttons. Visible only while body.is-focus
   or is-focus-pending. z-index 86 sits above the dim layer (70),
   pinned tile (80), and metadata panel (85). */
.focus-nav {
  position: fixed;
  top: 50vh;
  z-index: 86;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: rgba(0, 0, 0, 0.45);
  -webkit-backdrop-filter: blur(20px) saturate(1.5);
  backdrop-filter:         blur(20px) saturate(1.5);
  border: 1px solid rgba(255, 255, 255, 0.18);
  color: var(--paper);
  cursor: pointer;
  pointer-events: none;
  opacity: 0;
  transform: translateY(-50%);
  transition: opacity .35s var(--ease-out),
              background .25s var(--ease-snappy),
              transform .25s var(--ease-snappy);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.18),
    0 8px 24px -10px rgba(0, 0, 0, 0.55);
}
.focus-nav svg { width: 22px; height: 22px; }
/* Default (desktop / fine-pointer): show horizontal chevrons (← →), hide vertical */
.focus-nav .icon-vertical { display: none; }
.focus-nav-prev { left: 16px; }
.focus-nav-next { right: 16px; }
body.is-focus .focus-nav,
body.is-focus-pending .focus-nav {
  opacity: 1;
  pointer-events: auto;
}
.focus-nav:hover {
  background: rgba(0, 0, 0, 0.6);
  transform: translateY(-50%) scale(1.06);
}
@supports not (backdrop-filter: blur(1px)) {
  .focus-nav { background: rgba(0, 0, 0, 0.78); }
}
@media (max-width: 760px) {
  .focus-nav      { width: 36px; height: 36px; }
  .focus-nav svg  { width: 18px; height: 18px; }
  .focus-nav-prev { left: 10px; }
  .focus-nav-next { right: 10px; }
}

/* Coarse-pointer / no-hover devices (mobile, tablet): swap to vertical
   chevrons (↑ prev / ↓ next). Direction matches the way phone users
   already think about scrolling — vertical, not horizontal. */
@media (hover: none) and (pointer: coarse) {
  .focus-nav .icon-horizontal { display: none; }
  .focus-nav .icon-vertical   { display: block; }
}

/* iOS Safari rounded-clip belt-and-suspenders */
.tile-frame {
  -webkit-clip-path: inset(0 round 16px);
          clip-path: inset(0 round 16px);
  -webkit-mask-image: linear-gradient(white, white);
          mask-image: linear-gradient(white, white);
}
/* (was: .tile.feat .tile-frame { clip-path 22px } — featured tiles removed) */
.mini-host {
  -webkit-clip-path: inset(0 round 16px);
          clip-path: inset(0 round 16px);
  -webkit-mask-image: linear-gradient(white, white);
          mask-image: linear-gradient(white, white);
}


/* ---------- Ken-Burns animation (active tile thumbnails + mini) ---------- */
@keyframes ken-burns {
  0%   { transform: scale(1.00) translate( 0%,  0%); }
  100% { transform: scale(1.08) translate( 2%, -2%); }
}
.tile.is-active .tile-frame img,
.mini-player.is-open .mini-host img {
  animation: ken-burns 10s cubic-bezier(.4, .0, .2, 1) infinite alternate;
}
@media (prefers-reduced-motion: reduce) {
  .tile.is-active .tile-frame img,
  .mini-player.is-open .mini-host img { animation: none; }
}

/* Mini player play CTA — repurposed: tap = expand back to focus */
.mini-play {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%) scale(0.85);
  width: 40px; height: 40px;
  border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(0, 0, 0, 0.4);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
  backdrop-filter:         blur(16px) saturate(1.4);
  border: 1px solid rgba(255, 255, 255, 0.2);
  color: var(--paper);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.2),
    0 4px 14px -6px rgba(0, 0, 0, 0.5);
  cursor: pointer;
  z-index: 3;
  opacity: 0;
  pointer-events: none;
  transition: opacity .25s var(--ease), transform .25s var(--ease);
}
.mini-play svg { width: 16px; height: 16px; transform: translateX(1px); }
.mini-player.is-open .mini-play {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
  pointer-events: auto;
}
@supports not (backdrop-filter: blur(1px)) {
  .mini-play { background: rgba(0, 0, 0, 0.78); }
}

/* mini-thumb fills the host as a poster underneath the iframe; crossfade
   out once mini.is-playing flips on. */
.mini-host img.mini-thumb {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  border-radius: inherit;
  filter: saturate(1) brightness(1);
  z-index: 2;
  transition: opacity .3s var(--ease);
}
.mini-player.is-playing .mini-host .mini-thumb { opacity: 0; }
.mini-host iframe { z-index: 1; }

/* Hide mini-play once iframe is actually playing — it's no-op visual then */
.mini-player.is-open.is-playing .mini-play {
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, -50%) scale(0.85);
}

/* ---------- Caption (lives BELOW the frame, never on top) ---------- */
.tile-caption {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 0 2px;
  align-items: flex-start;
  /* Fade in/out smoothly when entering/exiting focus mode (rule below) */
  transition: opacity .35s var(--ease-out);
}
/* In focus mode the caption is redundant with the metadata panel below
   the video — hide it without changing layout (visibility + opacity, NOT
   display:none, so the grid cell stays put and FLIP math stays correct). */
body.is-focus .tile.is-pinned .tile-caption,
body.is-focus-pending .tile.is-pinned .tile-caption {
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
}
.tile-caption .cap-pin {
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 2px rgba(194, 90, 31, 0.18);
  margin-bottom: 2px;
  opacity: 0;
  transform: scale(0.5);
  transition: opacity .25s var(--ease), transform .25s var(--ease);
}
.tile.is-pinned .tile-caption .cap-pin { opacity: 1; transform: scale(1); }

.tile-caption .cap-title {
  font-family: "Antonio", sans-serif;
  font-weight: 700;
  font-size: 16px;
  line-height: 1.1;
  letter-spacing: -0.005em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0;
}
/* (was: .tile.feat .tile-caption .cap-title — featured tiles removed) */

.tile-caption .cap-meta {
  font-family: "IBM Plex Mono", monospace;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted, #6a6a6a);
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  align-items: baseline;
}
.tile-caption .cap-meta > * {
  display: inline-flex;
  align-items: baseline;
}
.tile-caption .cap-meta > * + *::before {
  content: "·";
  margin-right: 10px;
  opacity: 0.4;
}
.tile-caption .cap-cat  { opacity: 0.65; }
/* (was: .tile-caption .cap-feat — featured tiles removed) */
.tile-caption .cap-dir-muted { opacity: 0.45; }
.tile-caption .cap-year { font-variant-numeric: tabular-nums; opacity: 0.75; }

/* Pinned: nudge title color to ink (already there) + show outline-offset border */
/* Featured caption gets a touch more left padding so the pin dot sits aligned */

.empty {
  grid-column: 1 / -1;
  text-align: center;
  padding: 80px 0;
  font-family: "IBM Plex Mono", monospace;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.55;
}

/* =========================================================
   Contact
   ========================================================= */

.contact {
  position: relative;
  border-top: var(--rule) solid var(--ink);
  padding: clamp(96px, 14vh, 180px) var(--gutter);
  display: flex;
  flex-direction: column;
  gap: clamp(32px, 5vh, 64px);
  /* Liquid Glass plane — subtle, sits over body color */
  background: rgba(255, 255, 255, 0.55);
  -webkit-backdrop-filter: blur(20px) saturate(1.4);
  backdrop-filter:         blur(20px) saturate(1.4);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.7),
    0 -10px 40px -16px rgba(10, 10, 10, 0.12);
}
@supports not (backdrop-filter: blur(1px)) {
  .contact { background: rgba(255, 255, 255, 0.9); }
}
.contact .label {
  font-family: "IBM Plex Mono", monospace;
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
/* Email becomes a glass pill button at the larger sizes */
.contact a.email {
  font-family: "Antonio", sans-serif;
  font-weight: 700;
  font-size: clamp(36px, 8vw, 128px);
  line-height: 0.9;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  word-break: break-word;
  align-self: flex-start;
  color: var(--accent);
  /* Liquid Glass pill */
  padding: 14px 28px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.55);
  -webkit-backdrop-filter: blur(18px) saturate(1.5);
  backdrop-filter:         blur(18px) saturate(1.5);
  border: 1px solid rgba(194, 90, 31, 0.25);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.7),
    0 6px 24px -10px rgba(194, 90, 31, 0.3);
  transition: transform .2s var(--ease-snappy), box-shadow .2s var(--ease-snappy), background .25s var(--ease-snappy);
}
.contact a.email:hover {
  transform: translateY(-2px);
  background: rgba(255, 255, 255, 0.7);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.8),
    0 12px 32px -10px rgba(194, 90, 31, 0.42);
}
@supports not (backdrop-filter: blur(1px)) {
  .contact a.email { background: rgba(255, 255, 255, 0.92); }
}
.contact .row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 24px;
  flex-wrap: wrap;
  font-family: "IBM Plex Mono", monospace;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  border-top: var(--rule) solid var(--ink);
  padding-top: clamp(20px, 3vh, 32px);
}
.contact .row .links { display: flex; gap: 24px; }
.contact .row a { transition: color .2s var(--ease), opacity .2s var(--ease); }
.contact .row a:hover { color: var(--accent); opacity: 1; }

/* Fine-print AI note — sits below the copyright row, very faint */
.contact .made-with-ai {
  margin: 0;
  font-family: "IBM Plex Mono", monospace;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(10, 10, 10, 0.32);
  text-align: center;
}

/* =========================================================
   Inline Focus Metadata Panel — replaces the modal player
   Glass card pinned to the bottom of the viewport while in
   focus mode; sits above the dim layer so the spec is legible.
   ========================================================= */

.focus-meta {
  position: fixed;
  left: 24px;
  right: 24px;
  bottom: calc(24px + env(safe-area-inset-bottom, 0px));
  max-width: 600px;
  margin: 0 auto;
  padding: 18px 22px 20px;
  border-radius: 16px;
  background: rgba(20, 20, 20, 0.55);
  -webkit-backdrop-filter: blur(28px) saturate(1.5);
  backdrop-filter:         blur(28px) saturate(1.5);
  border: 1px solid rgba(255, 255, 255, 0.12);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.18),
    0 24px 60px -16px rgba(0, 0, 0, 0.55);
  color: var(--paper);
  z-index: 85;
  opacity: 0;
  transform: translateY(20px);
  transition: opacity .35s var(--ease-out), transform .45s var(--ease-spring);
  pointer-events: none;
  max-height: calc(42vh - env(safe-area-inset-bottom, 0px));
  overflow-y: auto;
}
/* 80ms entrance stagger — dim & tile lead, panel follows for a produced feel.
   Default rule (above) has zero delay so the EXIT slides down immediately. */
body.is-focus #focus-meta,
body.is-focus-pending #focus-meta { transition-delay: 80ms; }
@supports not (backdrop-filter: blur(1px)) {
  .focus-meta { background: rgba(20, 20, 20, 0.93); }
}
body.is-focus #focus-meta,
body.is-focus-pending #focus-meta {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}
body.is-mini #focus-meta {
  opacity: 0;
  transform: translateY(24px);
  pointer-events: none;
}

.focus-meta .fm-eyebrow {
  font-family: "IBM Plex Mono", monospace;
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
  margin-bottom: 10px;
}
.focus-meta .fm-eyebrow .fm-sep { opacity: 0.45; margin: 0 6px; }

.focus-meta .fm-title {
  font-family: "Antonio", sans-serif;
  font-weight: 700;
  font-size: clamp(20px, 5vw, 32px);
  letter-spacing: -0.005em;
  line-height: 1.05;
  text-transform: uppercase;
  margin: 0 0 16px;
  color: var(--paper);
  word-break: break-word;
}

.focus-meta .fm-spec {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px 20px;
  margin: 0;
}
.focus-meta .fm-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.focus-meta .fm-row dt {
  font-family: "IBM Plex Mono", monospace;
  font-size: 9px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
  margin: 0;
}
.focus-meta .fm-row dd {
  margin: 0;
  font-family: "IBM Plex Mono", monospace;
  font-size: 12.5px;
  line-height: 1.35;
  color: rgba(255, 255, 255, 0.93);
  word-break: break-word;
}
@media (max-width: 360px) {
  .focus-meta { left: 16px; right: 16px; padding: 16px 18px 18px; }
  .focus-meta .fm-spec { gap: 10px 14px; }
}

/* =========================================================
   Animation
   ========================================================= */

.fade-up {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity .6s var(--ease), transform .6s var(--ease);
}
.fade-up.is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; }
}

/* =========================================================
   Responsive
   ========================================================= */

@media (max-width: 1280px) {
  .work { grid-template-columns: repeat(4, 1fr); }
}
@media (max-width: 760px) {
  :root { --gutter: 22px; }
  .work {
    grid-template-columns: repeat(2, 1fr);
    row-gap: 28px;
  }
  .tile-caption .cap-title { font-size: 14.5px; }
  .tile-caption .cap-meta  { font-size: 10.5px; }
  .topbar nav a { margin-left: 14px; }
  .hero h1 { font-size: clamp(56px, 18vw, 120px); }
  .hero-tag { display: none; }
}

/* =========================================================
   Landscape phone (orientation + low height) — DISABLED for
   playback diagnostic. The block is preserved as a comment so
   it can be restored once playback is confirmed unrelated.

@media (orientation: landscape) and (max-height: 540px) {
  .mini-player { width: 200px; }
  body.is-focus .topbar,
  body.is-focus-pending .topbar,
  body.is-focus #focus-meta,
  body.is-focus-pending #focus-meta,
  body.is-focus .now-playing,
  body.is-focus-pending .now-playing { display: none !important; }
  body.is-focus .tile.is-pinned .tile-frame,
  body.is-focus-pending .tile.is-pinned .tile-frame {
    top: 50vh !important;
    left: 50vw !important;
    transform: translate(-50%, -50%) !important;
    width: min(100vw, calc(100vh * 16 / 9)) !important;
    max-width: 100vw !important;
    height: auto !important;
    aspect-ratio: 16 / 9 !important;
    border-radius: 0 !important;
    outline: none !important;
    box-shadow: none !important;
  }
  body.is-focus .dim-layer,
  body.is-focus-pending .dim-layer { background: rgba(0, 0, 0, 0.82); }
}
   ========================================================= */
