/* ---------- Reset & base ---------- */

*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  background: #041e3a;
}
img { max-width: none; display: block; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }

:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
  border-radius: 2px;
}

:root {
  --wall-main: #041e3a;
  --wall-about: #320b09;
  --ink-main: #f1e0ca;
  --ink-about: #fff2e2;
  /* Set by script.js: the track's uniform transform-scale. Used to convert
     real-px env() insets into the track's local (pre-scale) units. */
  --stage-scale: 1;
}

/* ---------- App shell ---------- */

#app.gallery {
  position: fixed;
  inset: 0;
  width: 100dvw;
  height: 100dvh;
  overflow: hidden;
  background: var(--wall-main);
}

/* ---------- Room walls, lighting, floor (all viewport-locked) ---------- */

.wall-bg {
  position: fixed;
  inset: 0;
  opacity: 0;
  z-index: 0;
}
.wall-bg.is-active { opacity: 1; }
.wall-bg--main { background: var(--wall-main); }
/* The about wall is a hard cut drawn over the main wall: in continuous mode
   script.js pins it opaque and clips it to the room seam's screen position. */
.wall-bg--about { background: var(--wall-about); z-index: 1; }

.wall-lighting { position: fixed; inset: 0; z-index: 2; pointer-events: none; overflow: hidden; }
.wall-lighting > span { position: absolute; display: block; }
.wall-lighting__beam {
  inset: 0;
  mix-blend-mode: screen;
  background: radial-gradient(35% 85% at 50% -10%,
    rgba(199, 224, 255, 0.22) 0%, rgba(166, 199, 242, 0.1) 40%, rgba(128, 166, 217, 0) 100%);
}
.wall-lighting__core {
  left: 23.611%; top: 0; width: 52.778%; height: 77.5%;
  mix-blend-mode: screen;
  filter: blur(55px);
  background: linear-gradient(180deg,
    rgba(209, 232, 255, 0.18) 0%, rgba(178, 209, 250, 0.07) 55%, rgba(140, 178, 229, 0) 100%);
}
.wall-lighting__glow {
  left: 0; top: 0; width: 100%; height: 27.5%;
  mix-blend-mode: screen;
  filter: blur(18px);
  background: linear-gradient(180deg,
    rgba(191, 222, 255, 0.14) 0%, rgba(166, 204, 247, 0.05) 60%, rgba(140, 184, 235, 0) 100%);
}
.wall-lighting__vignette {
  inset: 0;
  mix-blend-mode: multiply;
  background: radial-gradient(55% 55% at 50% 50%,
    rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0) 55%, rgba(0, 0, 0, 0.28) 100%);
}

/* Floor: starts at the canvas's floor line (y=739 of 800, placed by
   script.js via --floor-top) and runs to the bottom of the screen, so on a
   tall screen the room just gets more floor. Texture at the stage scale. */
.floor {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  top: var(--floor-top, 92.375dvh);
  z-index: 3;
  pointer-events: none;
  /* The texture doesn't tile, so on screens wider than the canvas the outer
     planks are continued sideways (stretched 2px edge columns) instead of
     repeating the image with a visible seam. */
  background:
    url('assets/floor.png') center top / calc(1465.875px * var(--stage-scale)) calc(375.925px * var(--stage-scale)) no-repeat,
    url('assets/floor-edge-left.png') left top / 50% calc(375.925px * var(--stage-scale)) no-repeat,
    url('assets/floor-edge-right.png') right top / 50% calc(375.925px * var(--stage-scale)) no-repeat;
}

/* ---------- Filmstrip track ----------
   Every tier lays out on the same 800px-tall canvas as Figma; script.js
   scales it (down only) and centres it vertically, so track and stop
   heights stay at that fixed local value. */

.filmstrip {
  position: relative;
  z-index: 4;
  display: flex;
  align-items: stretch;
  height: 800px;
  width: max-content;
  transform-origin: 0 0;
  will-change: transform;
}

.stop {
  height: 800px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
}

/* The hallway stop's width is set inline by script.js (it ends where the
   gallery begins); flex-basis must stay auto so that width wins. */
.beat { flex: 0 0 auto; width: 100vw; }

.scene { display: flex; align-items: center; flex: 0 0 auto; gap: 300px; }
.spacer { flex: 0 0 auto; height: 1px; width: 0; }
.piece-box { position: relative; flex: 0 0 auto; }
.frame-slot { display: contents; }

.room-seam { position: relative; flex: 0 0 240px; width: 240px; height: 100%; }

#projects { display: contents; }

/* ---------- Scene ink ---------- */

.scene--main { color: var(--ink-main); }
.scene--about { color: var(--ink-about); }

/* ---------- Physical frames ----------
   Each artwork = a frame PNG (.frame__art) + a cream mat with the photo
   (.frame__mat) placed in the frame's window. All insets are percentages of
   the frame's own box, taken from the Figma instances, so one rule per
   frame type works at any size. The type comes from content.json. */

.frame {
  position: absolute;
  display: block;
  filter: drop-shadow(0 5px 5px rgba(15, 11, 8, 0.24)) drop-shadow(0 18px 17px rgba(15, 11, 8, 0.18));
  -webkit-tap-highlight-color: transparent;
  transition:
    transform 420ms cubic-bezier(0.22, 0.8, 0.24, 1),
    filter 420ms cubic-bezier(0.22, 0.8, 0.24, 1);
}
.frame:disabled { cursor: default; }

/* Hover: the frame eases a few px off the wall -- a small lift with a
   softer, deeper shadow, and the photo brightens a touch. Real hover
   pointers only (a sticky hover state after a tap would look stuck on
   phones); keyboard focus gets the same, touch gets a light press. */
@media (hover: hover) and (pointer: fine) {
  .frame:not(:disabled):hover {
    transform: translateY(-5px) scale(1.012);
    filter: drop-shadow(0 9px 9px rgba(15, 11, 8, 0.26)) drop-shadow(0 30px 28px rgba(15, 11, 8, 0.22));
  }
  .frame:not(:disabled):hover .frame__img { filter: brightness(1.04); }
}
.frame:not(:disabled):focus-visible {
  transform: translateY(-5px) scale(1.012);
  filter: drop-shadow(0 9px 9px rgba(15, 11, 8, 0.26)) drop-shadow(0 30px 28px rgba(15, 11, 8, 0.22));
}
.frame:not(:disabled):active {
  transform: translateY(-2px) scale(0.996);
  transition-duration: 140ms;
}

.frame__art { position: absolute; display: block; pointer-events: none; user-select: none; -webkit-user-drag: none; }
.frame__mat {
  position: absolute;
  display: block;
  overflow: hidden;
  background:
    linear-gradient(180deg, rgba(219, 201, 178, 0.4) 0%, rgba(195, 185, 172, 0.4) 100%),
    #ffffff;
}
.frame__mat::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  border-radius: inherit;
  box-shadow: inset 0 1px 4px 0 #a8a39e;
}
.frame__img, .frame__placeholder {
  position: absolute;
  left: var(--pad-x);
  top: var(--pad-y);
  width: calc(100% - 2 * var(--pad-x));
  height: calc(100% - 2 * var(--pad-y));
  object-fit: cover;
  background: #ffffff;
  border-radius: inherit;
  box-shadow:
    0 1px 1px rgba(0, 0, 0, 0.09),
    0 0 1px rgba(0, 0, 0, 0.1),
    0 3px 2.5px rgba(0, 0, 0, 0.05);
  opacity: 0;
  transition: opacity 400ms ease, filter 420ms cubic-bezier(0.22, 0.8, 0.24, 1);
}
.frame__placeholder { opacity: 1; background: #f4efe8; }
.frame.has-image .frame__img { opacity: 1; }
.frame.has-image .frame__placeholder { opacity: 0; }

.frame[data-frame="rectangle"] .frame__art { left: 0.764%; top: 1.277%; width: 98.215%; height: 97.274%; }
.frame[data-frame="rectangle"] .frame__mat { left: 9.383%; top: 11.728%; width: 80.697%; height: 75.57%; --pad-x: 1.126%; --pad-y: 1.461%; }

.frame[data-frame="square"] .frame__art { left: 0.19%; top: 0; width: 99.62%; height: 100%; }
.frame[data-frame="square"] .frame__mat { left: 15.751%; top: 15.789%; width: 68.498%; height: 68.045%; --pad-x: 2.62%; --pad-y: 2.7%; }

.frame[data-frame="renaissance"] .frame__art { left: 0; top: 0; width: 100%; height: 100%; }
.frame[data-frame="renaissance"] .frame__mat { left: 17.248%; top: 18.132%; width: 65.31%; height: 66.209%; --pad-x: 1.781%; --pad-y: 2.49%; }

.frame[data-frame="renaissance-portrait"] .frame__art { left: 0; top: 0; width: 100%; height: 100%; }
.frame[data-frame="renaissance-portrait"] .frame__mat { left: 15.84%; top: 17.41%; width: 65.68%; height: 64.91%; --pad-x: 2.49%; --pad-y: 1.78%; }

.frame[data-frame="circle"] { border-radius: 50%; overflow: hidden; }
.frame[data-frame="circle"] .frame__art { left: -3.333%; top: -2.963%; width: 106.296%; height: 106.296%; }
.frame[data-frame="circle"] .frame__mat { left: 10.446%; top: 10.423%; width: 79.131%; height: 79.131%; border-radius: 50%; --pad-x: 2.64%; --pad-y: 2.64%; }

/* ---------- Shared "on the wall" text ---------- */

.project-title { margin: 0; font-size: 16px; font-weight: 400; line-height: 1.6; }
.project-notes { display: flex; flex-direction: column; gap: 8px; }
.project-meta { margin: 0; font-size: 12px; font-weight: 600; line-height: 1.6; }
.project-desc { margin: 0; font-size: 11px; line-height: 1.6; }
.project-desc .project-proof { font-style: normal; }
.project-desc .project-proof.weight-medium { font-weight: 500; }
.project-desc .project-proof.weight-semibold { font-weight: 600; }
.project-desc .project-proof.weight-bold { font-weight: 700; }

/* ---------- Piece: self-portrait ---------- */

.piece--portrait .piece-box { width: 481px; height: 302px; }
.piece--portrait .frame { left: 0; top: 0.727px; width: 162px; height: 162px; }
.reveal-copy {
  --reveal-gap: 24px;
  position: absolute;
  left: 181px; top: 186px;
  width: 300px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--reveal-gap);
}
.reveal-copy .wordmark { width: 188.555px; height: 31.998px; }
.reveal-copy .role, .reveal-copy .tagline { margin: 0; font-size: 11px; line-height: 1.6; }
/* role + tagline are one text block in Figma; the gap is only below the wordmark */
.reveal-copy .tagline { margin-top: calc(-1 * var(--reveal-gap)); }

/* ---------- Piece: projects ---------- */

.project-copy {
  position: absolute;
  left: 0;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  gap: 40px;
}
.frame-cluster { position: absolute; inset: 0; }

/* Geometry: literal coordinates from Figma 489:7505, slot order = the
   images array order in content.json. */
[data-project="wave"] .piece-box { width: 1200px; height: 540px; }
[data-project="wave"] .project-copy { width: 200px; top: calc(50% + 3.5px); }
[data-project="wave"] .frame:nth-of-type(1) { left: 677px; top: 55px; width: 523.342px; height: 430.74px; }
[data-project="wave"] .frame:nth-of-type(2) { left: 392px; top: 257.441px; width: 273px; height: 266px; }
[data-project="wave"] .frame:nth-of-type(3) { left: 344px; top: 17px; width: 321px; height: 226.442px; }

[data-project="cyberkongz"] .piece-box { width: 1200px; height: 540px; }
[data-project="cyberkongz"] .project-copy { width: 200px; top: calc(50% + 3.5px); }
[data-project="cyberkongz"] .frame:nth-of-type(1) { left: 781px; top: 98px; width: 419px; height: 345px; }
[data-project="cyberkongz"] .frame:nth-of-type(2) { left: 597px; top: 89px; width: 162px; height: 162px; }
[data-project="cyberkongz"] .frame:nth-of-type(3) { left: 361px; top: 45.282px; width: 206px; height: 200.718px; }
[data-project="cyberkongz"] .frame:nth-of-type(4) { left: 438px; top: 277px; width: 321px; height: 226.442px; }

[data-project="touchbiz"] .piece-box { width: 1004px; height: 540px; }
[data-project="touchbiz"] .project-copy { width: 228px; top: calc(50% + 38.5px); }
[data-project="touchbiz"] .frame:nth-of-type(1) { left: 612.657px; top: 316px; width: 206.72px; height: 145.826px; }
[data-project="touchbiz"] .frame:nth-of-type(2) { left: 392px; top: 316px; width: 198.992px; height: 193.89px; }
[data-project="touchbiz"] .frame:nth-of-type(3) { left: 482.22px; top: 25.521px; width: 337.026px; height: 277.392px; }
[data-project="touchbiz"] .frame:nth-of-type(4) { left: 841px; top: 239.903px; width: 160.873px; height: 156.748px; }
[data-project="touchbiz"] .frame:nth-of-type(5) { left: 841px; top: 58px; width: 160.873px; height: 156.748px; }

[data-project="ballogy"] .piece-box { width: 1048px; height: 540px; }
[data-project="ballogy"] .project-copy { width: 228px; top: calc(50% - 12.5px); }
[data-project="ballogy"] .frame:nth-of-type(1) { left: 407.135px; top: 22px; width: 303.637px; height: 214.194px; }
[data-project="ballogy"] .frame:nth-of-type(2) { left: 392px; top: 256.112px; width: 318.796px; height: 262.387px; }
[data-project="ballogy"] .frame:nth-of-type(3) { left: 729px; top: 139px; width: 318.796px; height: 262.387px; }

[data-project="mantis"] .piece-box { width: 1052px; height: 358px; }
[data-project="mantis"] .project-copy { width: 228px; top: calc(50% - 9px); }
[data-project="mantis"] .frame:nth-of-type(1) { left: 371px; top: 36.883px; width: 321.959px; height: 265.098px; }
[data-project="mantis"] .frame:nth-of-type(2) { left: 708.327px; top: 0; width: 287.673px; height: 202.932px; }

/* ---------- Piece: about intro / CTA ---------- */

.piece--cta .piece-box {
  width: 1200px; height: 540px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 40px;
}
.piece--cta .frame { position: relative; flex: 0 0 auto; width: 220.5px; height: 312px; }
.about-cta { display: flex; flex-direction: column; gap: 8px; max-width: 300px; text-align: center; }
.about-cta p { margin: 0; font-size: 12px; line-height: 1.6; }
.about-cta .cta-headline { font-weight: 600; }
.about-cta a { color: inherit; font-weight: 600; text-decoration-thickness: from-font; }

/* ---------- Piece: bio ---------- */

.piece--bio .piece-box { width: 1200px; height: 540px; }
.bio-note { position: absolute; left: 0; top: 124px; width: 299px; display: flex; flex-direction: column; gap: 8px; }
.bio-name { margin: 0; font-size: 12px; font-weight: 600; line-height: 1.6; }
.bio-paragraphs p { margin: 0 0 1.6em 0; font-size: 12px; line-height: 1.6; }
.bio-paragraphs p:last-child { margin-bottom: 0; }
.bio-tools { position: absolute; left: 0; top: 340px; width: 291px; }
.bio-tools p { margin: 0; font-size: 12px; font-weight: 600; line-height: 1.6; }
.bio-tools .tools-lines { font-weight: 400; }
.bio-photos { position: absolute; inset: 0; }

.bio-photos .frame:nth-of-type(1) { left: 432px; top: 134.194px; width: 193.371px; height: 188.412px; }
.bio-photos .frame:nth-of-type(2) { left: 786.117px; top: 30px; width: 191.263px; height: 186.359px; }
.bio-photos .frame:nth-of-type(3) { left: 640.39px; top: 87.26px; width: 133.228px; height: 129.812px; }
.bio-photos .frame:nth-of-type(4) { left: 993.338px; top: 177.375px; width: 206.982px; height: 292.872px; }
.bio-photos .frame:nth-of-type(5) { left: 637.574px; top: 227.125px; width: 343.549px; height: 282.875px; }

/* ---------- Piece: timeline ---------- */

.piece--timeline .piece-box { width: 1200px; height: 540px; display: flex; align-items: flex-start; gap: 100px; }
.timeline-header { margin: 0; flex: 0 0 auto; font-size: 12px; font-weight: 600; line-height: 1.6; white-space: nowrap; }
.timeline-body { flex: 1 1 0; min-width: 0; display: flex; align-items: flex-start; gap: 32px; }
.timeline-entries { flex: 1 1 0; min-width: 0; display: flex; align-items: flex-start; gap: 24px; }
.timeline-entries__group { flex: 0 0 215px; width: 215px; display: flex; flex-direction: column; gap: 24px; }
.timeline-entry { display: flex; flex-direction: column; gap: 8px; }
.timeline-heading { margin: 0; font-size: 12px; font-weight: 600; line-height: 1.6; }
.timeline-desc { margin: 0; font-size: 12px; font-weight: 400; line-height: 1.6; }
.piece--timeline .frame { position: relative; flex: 0 0 467.435px; width: 467.435px; height: 384.881px; }

/* ---------- Piece: closer ---------- */

.piece--closer .piece-box { width: 1200px; height: 540px; }
.timeline-closer__text {
  position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 219px; display: flex; flex-direction: column;
}
.timeline-closer__label { margin: 0; font-size: 12px; font-weight: 600; line-height: 1.6; }
.timeline-closer__line { margin: 0 0 1.6em 0; font-size: 12px; font-weight: 400; line-height: 1.6; }
.timeline-closer__line:last-child { margin-bottom: 0; }
.timeline-closer__photos { position: absolute; inset: 0; }

.timeline-closer__photos .frame:nth-of-type(1) { left: 564.883px; top: 109px; width: 389.68px; height: 320.858px; }
.timeline-closer__photos .frame:nth-of-type(2) { left: 978.894px; top: 208.816px; width: 221.184px; height: 221.184px; }
.timeline-closer__photos .frame:nth-of-type(3) { left: 304px; top: 109px; width: 237.064px; height: 237.064px; }

/* ---------- Piece: resume + contact ---------- */

.piece--resume .piece-box { width: 1200px; height: 540px; display: flex; align-items: center; justify-content: center; }
.resume-contact { display: flex; flex-direction: column; gap: 24px; }
.resume-contact__item { display: flex; flex-direction: column; gap: 8px; }
.resume-contact__label { margin: 0; font-size: 12px; font-weight: 600; line-height: 1.6; }
/* Same "liquid glass" treatment as the nav pill's buttons, as a pill:
   inset highlights, a blurred dark ring (::before) and a blurred gradient
   rim-light (::after) under a backdrop blur, with the same hover lift and
   press. Insets replace the nav buttons' fixed --w/--h so it fits any label. */
.resume-contact__button {
  --tr: 18%;
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  width: fit-content;
  min-height: 36px;
  padding: 7px 14px 7px 16px;
  border-radius: 9999px;
  border: 1px double rgba(51, 51, 51, 0.08);
  box-shadow:
    inset 2px -2px 1px -1px rgba(255, 255, 255, 0.9),
    inset -2px 2px 1px -1px rgba(255, 255, 255, 0.9),
    inset 6px -6px 1px -6px rgba(255, 255, 255, 0.55),
    inset -6px 6px 1px -6px rgba(255, 255, 255, 0.55),
    inset 0 0 2px rgba(0, 0, 0, 0.8),
    0 4px 8px rgba(0, 0, 0, 0.2);
  background: rgba(0, 0, 0, 0.02);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  filter: brightness(0.9);
  color: inherit;
  font-size: 12px;
  line-height: 1.6;
  text-decoration: none;
  white-space: nowrap;
  transition: transform 250ms ease;
}
.resume-contact__button::before {
  content: "";
  position: absolute;
  z-index: 1;
  top: 35%;
  left: 8px;
  right: 8px;
  height: calc(100% - 16px);
  border-radius: inherit;
  border: 1px solid rgba(0, 0, 0, 0.9);
  filter: blur(8px);
  pointer-events: none;
}
.resume-contact__button::after {
  content: "";
  position: absolute;
  z-index: 2;
  inset: 0;
  border-radius: inherit;
  filter: blur(7px);
  background: linear-gradient(
    45deg,
    rgba(255, 255, 255, 0.8) 0%,
    transparent var(--tr),
    transparent calc(100% - var(--tr)),
    rgba(255, 255, 255, 0.8) 100%
  );
  pointer-events: none;
}
.resume-contact__button > span {
  position: relative;
  z-index: 3;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.3));
}
.resume-contact__button:hover { transform: translateY(-3px); }
.resume-contact__button:active { transform: scale(0.94); }

/* ---------- Hallway (foreground intro layer) ----------
   .hallway__stage is the Figma "Subtract" wall graphic at its local size
   (1465.875 x 927.093); script.js positions/scales it so the doorway stays
   centred and the switch stays on screen, and zooms it away as you walk
   through. The extensions continue the walls on viewports wider/taller
   than the graphic. */

.hallway { position: fixed; inset: 0; z-index: 6; pointer-events: none; overflow: hidden; }
.hallway__dim {
  position: absolute;
  inset: -4% -3% 0;
  background: linear-gradient(180deg,
    rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.4425) 42.65%, rgba(0, 0, 0, 0.2937) 91.3%, rgba(0, 0, 0, 0) 100%);
  filter: blur(17px);
  transition: opacity 900ms ease;
}
#app[data-lights="on"] .hallway__dim { opacity: 0; }

.hallway__stage {
  position: absolute;
  left: 0; top: 0;
  width: 1465.875px;
  height: 927.093px;
  transform-origin: 0 0;
  will-change: transform, opacity;
}
/* Clipped at local y=890 (the Figma canvas's bottom edge) -- below that the
   graphic's door jambs flare outward in perspective, which only looks right
   when it's off screen. Taller viewports get the straight extension below. */
/* Also trimmed 12px at the sides and 2px at the top: the graphic's outermost
   pixels are anti-aliased and would show as hairlines against the
   extensions on screens wider/taller than the canvas. Each extension tucks
   8px under the graphic so the joins never leave a sub-pixel gap. */
/* The sides also fade out over 24px (local x 12-36 and its mirror), onto
   side extensions whose colour is the per-row average of that same band --
   so the wall continues with no visible seam or tone step. */
.hallway__walls {
  position: absolute; inset: 0; width: 100%; height: 100%;
  clip-path: inset(2px 12px 37.093px 12px);
  -webkit-mask-image: linear-gradient(to right, transparent 12px, #000 36px, #000 calc(100% - 36px), transparent calc(100% - 12px));
          mask-image: linear-gradient(to right, transparent 12px, #000 36px, #000 calc(100% - 36px), transparent calc(100% - 12px));
  user-select: none; -webkit-user-drag: none;
}
.hallway__ext { position: absolute; display: block; }
.hallway__ext--left {
  right: calc(100% - 44px); top: 0; width: 6000px; height: 7000px;
  background: #ccc8c7 url('assets/hallway-edge-left.png?v=2') right top / 1px 923px repeat-x;
}
.hallway__ext--right {
  left: 1421.875px; top: 0; width: 6000px; height: 7000px;
  background: #cdc9c7 url('assets/hallway-edge-right.png?v=2') left top / 1px 923px repeat-x;
}
/* Ceiling above the graphic, for screens taller than the canvas (it stays
   at 1:1 and centred there, so there's room above it). */
.hallway__ext--top {
  left: -6000px; bottom: calc(100% - 10px); width: 13465.875px; height: 6000px;
  background:
    url('assets/hallway-edge-top.png') 6012px 0 / 1441.875px 1px repeat-y,
    linear-gradient(to right, #bbafa3 50%, #b8aea4 50%);
}
.hallway__ext--bottom {
  /* starts 10px under the (clipped) graphic, which paints over it, so the
     join never leaves a sub-pixel gap for the dark room to show through */
  /* spans only the graphic's fully opaque middle (x 36 to 1429.875) -- its
     darker outer columns would otherwise show as lines beside the side
     extensions, which cover the corners instead */
  left: 36px; top: 880px; width: 1393.875px; height: 6000px;
  background: url('assets/hallway-edge-bottom.png') -24px top / 1441.875px 1px repeat-y;
}

.light-switch {
  position: absolute;
  left: 985.5px; top: 476px;
  width: 64px; height: 64px;
  pointer-events: auto;
  -webkit-tap-highlight-color: transparent;
}
/* Generous invisible hit area -- on small phones the switch renders ~40px. */
.light-switch::before { content: ""; position: absolute; inset: -18px; }
.light-switch__img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  filter:
    drop-shadow(-1px 0 1px rgba(0, 0, 0, 0.1))
    drop-shadow(-3px 0 1.5px rgba(0, 0, 0, 0.09))
    drop-shadow(-8px 0 2.5px rgba(0, 0, 0, 0.05));
  transition: opacity 120ms linear;
}
.light-switch__img--on { opacity: 0; }
#app[data-lights="on"] .light-switch__img--on { opacity: 1; }
#app[data-lights="on"] .light-switch__img--off { opacity: 0; }
.light-switch:focus-visible { outline: 2px solid #f1e0ca; outline-offset: 4px; border-radius: 6px; }

/* A slow, soft glow while the room is dark, so the switch reads as the
   thing to press; a quick wiggle when someone tries to scroll first. */
#app[data-lights="off"] .light-switch::after {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: 10px;
  pointer-events: none;
  animation: switch-glow 2.6s ease-in-out infinite;
}
@keyframes switch-glow {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 236, 205, 0); }
  50% { box-shadow: 0 0 18px 4px rgba(255, 236, 205, 0.45); }
}
.light-switch.is-nudging { animation: switch-nudge 520ms cubic-bezier(0.36, 0.07, 0.19, 0.97); }
@keyframes switch-nudge {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-4px) rotate(-3deg); }
  40% { transform: translateX(4px) rotate(3deg); }
  60% { transform: translateX(-3px) rotate(-2deg); }
  80% { transform: translateX(2px); }
}

/* ---------- Nav pill (prev/next) ---------- */

.nav-pill {
  position: fixed;
  left: 50%;
  bottom: calc(24px + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  z-index: 8;
  display: flex;
  align-items: center;
  gap: 12px;
  transition: gap 300ms ease;
}
/* Unfolded foldable: keep each button in its own pane, clear of the hinge. */
#app[data-posture="unfolded"] .nav-pill { gap: 64px; }

/* "Liquid glass" button treatment -- layered inset highlights + a blurred
   dark ring (::before) and a blurred gradient rim-light (::after) underneath
   a backdrop-blur, giving each button a frosted, beveled-glass look. */
.nav-arrow {
  --w: 36px;
  --h: 36px;
  --r: 9999px;
  --tr: 25%;
  position: relative;
  width: var(--w);
  height: var(--h);
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--r);
  border: 1px double rgba(51, 51, 51, 0.08);
  box-shadow:
    inset 2px -2px 1px -1px rgba(255, 255, 255, 0.9),
    inset -2px 2px 1px -1px rgba(255, 255, 255, 0.9),
    inset 6px -6px 1px -6px rgba(255, 255, 255, 0.55),
    inset -6px 6px 1px -6px rgba(255, 255, 255, 0.55),
    inset 0 0 2px rgba(0, 0, 0, 0.8),
    0 4px 8px rgba(0, 0, 0, 0.2);
  background: rgba(0, 0, 0, 0.02);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  filter: brightness(0.9);
  cursor: pointer;
  transition: transform 250ms ease, opacity 200ms ease;
}
.nav-arrow::before {
  content: "";
  position: absolute;
  z-index: 1;
  top: 35%;
  left: 50%;
  transform: translateX(-50%);
  width: calc(var(--w) - 16px);
  height: calc(var(--h) - 16px);
  border-radius: var(--r);
  border: 1px solid rgba(0, 0, 0, 0.9);
  filter: blur(8px);
  pointer-events: none;
}
.nav-arrow::after {
  content: "";
  position: absolute;
  z-index: 2;
  width: var(--w);
  height: var(--h);
  border-radius: var(--r);
  filter: blur(7px);
  background: linear-gradient(
    45deg,
    rgba(255, 255, 255, 0.8) 0%,
    transparent var(--tr),
    transparent calc(100% - var(--tr)),
    rgba(255, 255, 255, 0.8) 100%
  );
  pointer-events: none;
}
.nav-arrow img {
  position: relative;
  z-index: 3;
  display: block;
  width: 16px;
  height: 16px;
  filter: drop-shadow(0 4px 3px rgba(0, 0, 0, 0.3));
}
.nav-arrow:hover { transform: translateY(-3px); }
.nav-arrow:active { transform: scale(0.94); }
.nav-arrow:disabled { opacity: 0.3; pointer-events: none; }

/* ---------- "Swipe to keep going" cue ----------
   Touch-first devices only; appears once the lights are on and fades out
   for good as soon as the visitor starts moving (script.js sets
   data-peek="hidden"). Never intercepts touches. */

.mobile-peek {
  display: none;
  position: fixed;
  right: calc(14px + env(safe-area-inset-right, 0px));
  top: 50%;
  transform: translateY(-50%);
  z-index: 8;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  color: var(--ink-main);
  opacity: 0.75;
  pointer-events: none;
  transition: opacity 350ms ease;
}
.mobile-peek__arrow { font-size: 22px; animation: peek-nudge 1.6s ease-in-out infinite; }
.mobile-peek__label { font-size: 10px; letter-spacing: 0.04em; text-transform: uppercase; writing-mode: vertical-rl; }
@keyframes peek-nudge { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(4px); } }
@media (max-width: 767px), (hover: none) and (pointer: coarse) {
  .mobile-peek { display: flex; }
}
#app[data-lights="off"] .mobile-peek,
#app[data-peek="hidden"] .mobile-peek,
#app[data-at-end="true"] .mobile-peek { opacity: 0; }
/* In the hallway the cue sits on the pale wall, below the light switch:
   dark ink for contrast, and moved down so it never covers the switch. */
#app[data-scene="intro"] .mobile-peek { top: 72%; color: #4a403a; }

/* ---------- Lightbox ---------- */

.lightbox { position: fixed; inset: 0; z-index: 20; display: flex; align-items: center; justify-content: center; }
.lightbox[hidden] { display: none; }
.lightbox__backdrop { position: absolute; inset: 0; background: rgba(4, 10, 20, 0.88); }
.lightbox__content {
  position: relative; z-index: 1; margin: 0;
  max-width: min(88vw, 1100px); max-height: 82dvh;
  display: flex; flex-direction: column; gap: 10px;
}
.lightbox__img { max-width: 100%; max-height: 74dvh; width: auto; height: auto; object-fit: contain; background: #efeeea; }
.lightbox__caption { color: #efeeea; font-size: 13px; text-align: center; }
.lightbox__close {
  position: absolute; top: -42px; right: 0; z-index: 2;
  color: #efeeea; font-size: 14px; letter-spacing: 0.04em; text-transform: uppercase; padding: 8px 4px;
}
.lightbox__close:hover { text-decoration: underline; }

.safe-area-probe {
  position: fixed; left: 0; top: 0; width: 0; height: 0;
  visibility: hidden; pointer-events: none;
  padding:
    env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px)
    env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px);
}

/* ---------- Reduced motion: plain stacked, natively scrolling ---------- */

@media (prefers-reduced-motion: reduce) {
  #app.gallery { position: static; height: auto; overflow: visible; }
  .filmstrip { display: block; width: 100%; height: auto; transform: none !important; }
  .scene { display: block; }
  .stop { width: 100% !important; height: auto; min-height: 100dvh; padding: 10dvh 6vw 14dvh; }
  .beat--hallway, .hallway, .room-seam { display: none; }
  .frame__img, .frame__placeholder { transition: none; }
  .frame:not(:disabled):hover, .frame:not(:disabled):focus-visible, .frame:not(:disabled):active {
    transform: none !important;
  }

  .piece-box,
  [data-project] .piece-box,
  .piece--portrait .piece-box, .piece--cta .piece-box, .piece--bio .piece-box,
  .piece--timeline .piece-box, .piece--closer .piece-box, .piece--resume .piece-box {
    width: 100% !important; max-width: 720px; height: auto !important;
    display: flex !important; flex-direction: column !important; align-items: flex-start !important;
    gap: 24px !important;
  }
  .project-copy, .reveal-copy, .bio-note, .bio-tools, .timeline-closer__text {
    position: static !important; transform: none !important; width: 100% !important; max-width: 420px;
  }
  .frame-cluster, .bio-photos, .timeline-closer__photos {
    position: static !important; inset: auto; width: 100% !important; height: auto !important;
    display: flex; flex-wrap: wrap; gap: 16px;
  }
  .frame {
    position: relative !important; left: auto !important; top: auto !important;
    width: min(300px, 80vw) !important; height: auto !important; flex: 0 0 auto !important;
  }
  .frame[data-frame="rectangle"] { aspect-ratio: 523.342 / 430.74; }
  .frame[data-frame="square"] { aspect-ratio: 273 / 266; }
  .frame[data-frame="renaissance"] { aspect-ratio: 321 / 226.442; }
  .frame[data-frame="renaissance-portrait"] { aspect-ratio: 220.5 / 312; width: min(220px, 60vw) !important; }
  .frame[data-frame="circle"] { aspect-ratio: 1 / 1; width: min(220px, 60vw) !important; }
  .timeline-body, .timeline-entries { flex-direction: column; gap: 24px; }
  .timeline-entries__group { width: 100%; flex-basis: auto; }
  .floor { display: none; }
}

body[data-reduced-motion="true"] .filmstrip { display: block; width: 100%; height: auto; transform: none !important; }
body[data-reduced-motion="true"] .scene { display: block; }
body[data-reduced-motion="true"] #app.gallery { position: static; height: auto; overflow: visible; }
body[data-reduced-motion="true"] .hallway { display: none; }
