/* ─────────────────────────────────────────────────────────────────
   ARCHIVE — Emmanuel Oshowobi
   A personal media archive, 2025–2026.

   The ground is dark on purpose: the photographs and clips are the only
   light in the room, so every bit of chrome is warm-grey and gets out of
   their way. Hierarchy comes from size, space and colour — never weight.
   ───────────────────────────────────────────────────────────────── */

:root {
  /* THE SAME ROOM YOU CAME FROM.

     This was #0d0c0b — luminance 12. Rasterising the home page's cabin
     wall and sampling it gives 46 in the darkest corner and 113 where
     the window's light falls on it:

       top-left 69   top-centre 113   mid-left 73
       bottom-centre 72   bottom-right 46

     So the archive was four to nine times darker than the room you had
     just been sitting in, which is why going through the window read as
     a cut to somewhere else rather than as walking further into the same
     aeroplane. The ground is that wall now — its hue, its gradient
     direction, and the lower half of its range, so the photographs still
     have something to be brighter than. */
  /* The cabin wall's own three stops, at its own positions. A two-stop
     approximation got the tone but not the wall: the real one turns
     over at 42% and it is that turn, not the endpoints, that reads as a
     curved panel rather than a flat backdrop. */
  --wall-top: #615d59;
  --wall-mid: #4a4744;
  --wall-bottom: #333130;
  --ground: var(--wall-mid);
  --ground-lift: #5c5854;

  /* And the rest of what is actually on that wall, all carried over at
     the values the cabin uses: the pebble texture of the trim panel,
     the daylight the window throws onto it, the falloff toward the
     edges of frame, and the seam where the sidewall panel meets the
     dado below it. */
  --wall-dot: rgba(255, 255, 255, 0.07);
  --wall-spill: #eaf1f8;
  --wall-vignette: #0e1116;
  --wall-seam-dark: rgba(16, 18, 22, 0.42);
  --wall-seam-light: rgba(255, 255, 255, 0.09);
  --ink: #f2efe9;
  /* Lifted with the ground. These were tuned against #0d0c0b, where they
     measured 6.53:1 and 3.35:1 — on the cabin wall they fell to 2.91:1
     and 1.49:1, which is below readable and near invisible. Solved
     against the TOP of the gradient, the hardest place on the page to
     read, keeping the warm hue the originals had:

     Re-solved after the wall gained its real three stops and its spill,
     against the ground UNDER SMALL TEXT rather than the brightest pixel
     on the page — the spill's peak is mid-top and nothing is set on it:

       ground        chrome corners 79   captions 74   timeline 54
       muted #c9c0b6      4.54:1            4.91:1        6.72:1
       subtle #a39c94     3.01:1            3.25:1        4.44:1  */
  --ink-muted: #c9c0b6;
  --ink-subtle: #a39c94;
  /* Hairlines need more alpha on a lighter ground to hold the same
     weight against it. */
  --rule: rgba(242, 239, 233, 0.20);
  --rule-soft: rgba(242, 239, 233, 0.11);
  /* THE SAME YELLOW AS THE HOME PAGE, not a second accent.

     This was #d08c3f and the home page's is #e8dc38 — two different
     hues doing the same job, which is one of the clearest ways two
     screens announce they were designed apart. The yellow is the one
     that survives both grounds: it reads on this near-black and it
     reads on the note's paper, where the amber would go muddy. */
  --accent: #e8dc38;

  --font-display: 'Instrument Serif', 'Iowan Old Style', Georgia, serif;
  --font-ui: 'Instrument Sans', -apple-system, 'Segoe UI', sans-serif;
  /* Both carried over from the home page. The mono is the label voice
     for the whole site; the hand is used once, on arrival. */
  --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', monospace;
  --font-hand: 'Caveat', 'Bradley Hand', cursive;

  --radius: 6px;
  --radius-sm: 4px;
  /* Pills, as on the note's two entry links. */
  --radius-pill: 2em;

  --dur-fast: 150ms;
  --dur-mid: 220ms;
  --dur-slow: 420ms;
  --ease-out: cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-soft: cubic-bezier(0.16, 1, 0.3, 1);

  --pad: 2.4rem;
  color-scheme: dark;
}

* { box-sizing: border-box; margin: 0; padding: 0; border: 0 solid; }

html {
  font-size: clamp(8px, 0.667vw, 14px);
  background: var(--wall-bottom);
  -webkit-text-size-adjust: 100%;
}

body {
  font-family: var(--font-ui);
  font-size: 1.5rem;
  font-weight: 400;
  color: var(--ink);
  /* The wall itself: three stops at the cabin's own positions. */
  background: linear-gradient(
    to bottom,
    var(--wall-top) 0%,
    var(--wall-mid) 42%,
    var(--wall-bottom) 100%);
  overflow: hidden;
  height: 100svh;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
}

button { font: inherit; color: inherit; background: none; cursor: pointer; }
a { color: inherit; text-decoration: none; }

/* Metadata voice: small, tracked, quiet. Used for every label on the page.

   MONOSPACE, to match the home page.

   This was Instrument Sans at 500 with 0.09em of tracking, and the home
   page sets every equivalent label — the floating tags on the globe, the
   scroll cue, both entry links — in JetBrains Mono. Small type is most
   of what you read on either screen, so a different family here is the
   single loudest way the two say they are not the same site. Tracking
   comes down with it: mono is already wide, and 0.09em on top of it was
   compensating for a proportional face. */
.meta {
  font-family: var(--font-mono);
  font-size: 1.08rem;
  font-weight: 400;
  line-height: 1.2;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* The hand, used exactly once — the name you arrive on. It is the
   home page's own headline face, and this is the only place on this
   screen where a human wrote something rather than a system logged it. */
.hand {
  font-family: var(--font-hand);
  font-weight: 600;
  letter-spacing: 0.005em;
}

.serif { font-family: var(--font-display); font-weight: 400; letter-spacing: -0.01em; }

/* ─── atmosphere ─────────────────────────────────────────────── */
/* The cabin's own grain, the same tile at the same opacity and the same
   blend. Two rooms lit alike still read as different rooms if one has a
   surface and the other is a flat fill — this is the plastic of the
   sidewall carried through the window. */
/* The trim panel's pebble texture. Same 6px pitch and same 0.55 dot as
   the cabin draws, as a tiled radial rather than an SVG pattern — it is
   the same picture by different means. */
.wall {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
.wall__dots {
  position: absolute;
  inset: 0;
  opacity: 0.55;
  background-image: radial-gradient(var(--wall-dot) 0.55px, transparent 0.55px);
  background-size: 6px 6px;
}
/* Daylight on the wall. In the cabin this is thrown by the window; here
   you have just come through one, so it falls from the same place —
   high and central — and dies out toward the edges of frame. */
.wall__spill {
  position: absolute;
  inset: 0;
  background: radial-gradient(72% 58% at 50% 8%,
    rgba(234, 241, 248, 0.30),
    rgba(234, 241, 248, 0.13) 55%,
    rgba(234, 241, 248, 0) 100%);
}
/* And the falloff the cabin has, at its own colour and strength: the
   seat, the seat back and the curve of the wall all eat light long
   before the frame edge. */
.wall__vignette {
  position: absolute;
  inset: 0;
  background: radial-gradient(78% 72% at 50% 42%,
    rgba(14, 17, 22, 0) 0%,
    rgba(14, 17, 22, 0.13) 58%,
    rgba(14, 17, 22, 0.42) 100%);
}
/* The seam where the sidewall panel meets the dado. One horizontal line,
   low, does more for "this is a wall in an aeroplane" than any amount
   of shading — the cabin's own comment, and it holds here too. */
.wall__seam {
  position: absolute;
  left: 0;
  right: 0;
  top: 86%;
  height: 2px;
  background: linear-gradient(to bottom, var(--wall-seam-dark) 0 1px, var(--wall-seam-light) 1px 2px);
}

.grain {
  position: fixed;
  inset: 0;
  z-index: 41;
  pointer-events: none;
  opacity: 0.055;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 180px 180px;
}

/* A vignette instead of film grain — it seats the strip in the dark
   without putting texture over the photographs. */
.vignette {
  position: fixed;
  inset: 0;
  z-index: 40;
  pointer-events: none;
  /* Only the top bar now — the wall carries its own falloff, and two
     vignettes stacked was the archive sitting in a darker room than the
     one it is supposed to be in. */
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.20), transparent 16%);
}

/* ─── the year, living behind everything ─────────────────────── */
.year {
  position: fixed;
  inset: 0;
  z-index: 0;
  display: grid;
  place-items: center;
  pointer-events: none;
}
.year span {
  position: absolute;
  font-family: var(--font-display);
  font-size: 46rem;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--ink);
  opacity: 0;
  transform: scale(1.04);
  transition: opacity var(--dur-slow) var(--ease-out), transform 900ms var(--ease-soft);
  font-variant-numeric: tabular-nums;
}
.year span.is-on { opacity: 0.07; transform: none; }

/* ─── chrome ─────────────────────────────────────────────────── */
.mark {
  position: fixed;
  text-align: left;
  cursor: pointer;
  top: var(--pad);
  left: var(--pad);
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 0.9rem;
}
.mark__body { display: grid; gap: 0.4rem; }
.mark__name { font-family: var(--font-display); font-size: 2.6rem; line-height: 1; }

/* The arrow is folded away until you reach for it, so the mark reads as
   a title first and a way out second. */
.mark__arrow {
  display: flex;
  width: 0;
  opacity: 0;
  overflow: hidden;
  color: var(--accent);
  transition: width var(--dur-slow) var(--ease-soft), opacity var(--dur-mid) var(--ease-out);
}
.mark__arrow svg { width: 1.8rem; height: 1rem; }
.mark:hover .mark__arrow, .mark:focus-visible .mark__arrow { width: 1.8rem; opacity: 1; }

/* Sub-label swaps to the destination on hover — same slot, no reflow. */
.mark__sub { position: relative; display: grid; }
.mark__here, .mark__home {
  grid-area: 1 / 1;
  transition: opacity var(--dur-mid) var(--ease-out);
}
.mark__home { opacity: 0; color: var(--accent); white-space: nowrap; }
.mark:hover .mark__here, .mark:focus-visible .mark__here { opacity: 0; }
.mark:hover .mark__home, .mark:focus-visible .mark__home { opacity: 1; }

.status {
  position: fixed;
  top: var(--pad);
  right: var(--pad);
  z-index: 20;
  text-align: right;
  display: grid;
  gap: 0.4rem;
}
.status__now { color: var(--ink); font-variant-numeric: tabular-nums; }

/* ─── stage ──────────────────────────────────────────────────── */
.stage {
  position: fixed;
  inset: 0;
  z-index: 2;
  overflow: hidden;
  opacity: 0;
  transition: opacity 900ms var(--ease-out);
}
/* The strip is the page now — there is no overlay to wait behind. */
.stage { opacity: 1; }

.carousel { position: absolute; inset: 0; cursor: grab; user-select: none; touch-action: none; }
.carousel.is-dragging { cursor: grabbing; }
.carousel__slides { position: absolute; inset: 0; }

.carousel-item {
  position: absolute;
  top: 0;
  left: 0;
  width: 26rem;
  /* No `will-change` here: app.js sets it on the cards that are on
     screen and clears it on the ones that are not. As a blanket rule it
     promoted every item in the set at once. */
  cursor: pointer;
  perspective: 1000px;
  transform-style: preserve-3d;
}
.carousel-item__content { position: relative; }   /* will-change: see .carousel-item */

/* Present to assistive tech, absent from the picture. */
.vh {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

/* The cards are operable now, so they have to show where focus is. The
   gallery defined no focus style at all and was riding on whatever the
   browser draws, which on a near-black ground is close to nothing.
   Same token and the same 2px the home page uses for its own controls.
   `:focus-visible`, so a mouse click never draws a ring. */
.carousel-item:focus-visible,
.lightbox__close:focus-visible,
.lightbox__nav:focus-visible,
.mark:focus-visible,
.recall:focus-visible,
.player__pill:focus-visible,
.player__list button:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 4px;
  border-radius: 2px;
}

.carousel-item__media {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--ground-lift);
  box-shadow:
    inset 0 0 0 1px var(--rule-soft),
    0 1.2rem 3rem rgba(0, 0, 0, 0.45);
  transition: transform var(--dur-slow) var(--ease-soft), box-shadow var(--dur-slow) var(--ease-out);
}
.carousel-item:hover .carousel-item__media {
  transform: translateY(-0.6rem);
  box-shadow:
    inset 0 0 0 1px var(--rule),
    0 2rem 4.5rem rgba(0, 0, 0, 0.6);
}

.carousel-item__media img,
.carousel-item__media video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  opacity: 0;
  transition: opacity 700ms var(--ease-out);
  -webkit-user-drag: none;
}
.carousel-item__media img.is-loaded,
.carousel-item__media video.is-loaded { opacity: 1; }

/* Video badge — a still frame and a moving one should be tellable apart
   at a glance, even before the play control below is noticed. */
.carousel-item__tag {
  position: absolute;
  top: 0.9rem;
  left: 0.9rem;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.35rem 0.7rem;
  border-radius: 999px;
  background: rgba(13, 12, 11, 0.62);
  backdrop-filter: blur(6px);
  font-size: 1rem;
  letter-spacing: 0.1em;
  color: var(--ink);
}
.carousel-item__tag::before {
  content: '';
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--accent);
}

/* Play control. A clip's card shows its first frame and waits to be
   asked — so this is both the affordance and the thing that keeps the
   footage off the wire for a visitor who only scrolls past.

   A <span>, not a <button>, and that is deliberate: the card is already
   role=button, and nesting one interactive control inside another is a
   promise neither keyboard nor screen reader can keep. Pointer users
   press this; keyboard users press the card and get the lightbox, where
   the video is a real element with real controls. */
.carousel-item__play {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 2;
  width: 4.6rem;
  height: 4.6rem;
  margin: -2.3rem 0 0 -2.3rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  /* Same ground as the Motion pill, plus a hairline: the disc sits over
     whatever the first frame happens to be, and a bright sky washed a
     flat scrim out to a grey smudge. The ring holds its edge on both. */
  background: rgba(13, 12, 11, 0.62);
  backdrop-filter: blur(6px);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.22),
    0 0.2rem 1.4rem rgba(0, 0, 0, 0.5);
  transition:
    transform var(--dur-slow) var(--ease-soft),
    opacity var(--dur-slow) var(--ease-out),
    background var(--dur-slow) var(--ease-out);
}
.carousel-item__play svg {
  width: 2.2rem;
  height: 2.2rem;
  fill: var(--ink);
  /* The triangle's mass sits left of its bounding box; nudge it back
     onto the disc's centre the way every play button has to. */
  transform: translateX(0.1rem);
}
/* Scale only. The frame around it already lifts 0.6rem on hover, and
   lifting the disc too moved it roughly 17px — a third of its own
   height — out from under the pointer that was reaching for it. */
.carousel-item:hover .carousel-item__play {
  background: rgba(13, 12, 11, 0.72);
  transform: scale(1.06);
}

/* Play and pause live in the same disc; only one is ever drawn. */
.carousel-item__play .is-pause { display: none; }
.carousel-item__media.is-playing .is-play { display: none; }
.carousel-item__media.is-playing .is-pause { display: block; }

/* Once it is running, the control gets out of the way of the picture —
   and comes back the moment the pointer does, so it is never a clip you
   cannot stop. */
.carousel-item__media.is-playing .carousel-item__play { opacity: 0; }
.carousel-item:hover .carousel-item__media.is-playing .carousel-item__play { opacity: 1; }

/* Reduced motion never autoplays, so the control would toggle nothing. */
@media (prefers-reduced-motion: reduce) {
  .carousel-item__play { display: none; }
}

/* Caption sits under the frame and fades up on hover. */
.carousel-item__cap {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  margin-top: 1.2rem;
  display: flex;
  gap: 0.8rem;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translateY(0.4rem);
  transition: opacity var(--dur-mid) var(--ease-out), transform var(--dur-slow) var(--ease-soft);
}
.carousel-item:hover .carousel-item__cap { opacity: 1; transform: none; }
.carousel-item__cap b { font-weight: 500; color: var(--ink); }
.carousel-item__cap i { font-style: normal; color: var(--ink-subtle); }

/* ─── timeline ───────────────────────────────────────────────── */
.timeline {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 7rem;
  z-index: 10;
  /* Live now. It was `none`, which made the one element showing the
     whole archive the one element you could not use. */
  pointer-events: auto;
  cursor: grab;
  touch-action: none;        /* the browser must not claim the drag */
}
.timeline.is-scrubbing { cursor: grabbing; }
/* The ruler and its cursor are decoration; only the band and the month
   labels take a pointer. */
.timeline__ruler canvas,
.timeline__cursor { pointer-events: none; }
.timeline__ruler { position: relative; height: 5rem; }
.timeline__ruler canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }

.timeline__months {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  height: 5.6rem;      /* ticks · month · year, each on its own line */
  overflow: hidden;
}
.timeline__track { position: relative; height: 100%; will-change: transform; }
.timeline__track span,
.timeline__track button {
  position: absolute;
  top: 1.4rem;
  transform: translateX(-50%);
  white-space: nowrap;
  transition: color var(--dur-mid) var(--ease-out);
}
/* The month labels are buttons now. Strip the widget back to the text
   it already was — the affordance is the cursor and the hover, not a
   box drawn around every month. */
/* Neutralise the UA button and NOTHING else. `.meta` already sets the
   family, size, weight, tracking, case and colour on this element, so
   anything font-shaped here — `font: inherit` included — wins on
   specificity and quietly undoes it: the months came out mixed-case and
   oversized the moment they stopped being spans. */
.timeline__track button {
  appearance: none;
  background: none;
  border: 0;
  text-align: inherit;
  padding: 0.4rem 0.6rem;
  margin: -0.4rem -0.6rem;   /* a tap target, not a visible box */
  cursor: pointer;
}
.timeline__track button:hover { color: var(--ink); }
.timeline__track button:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
  border-radius: 2px;
}
.timeline__track span.is-year {
  top: 3.4rem;
  font-family: var(--font-display);
  font-size: 1.6rem;
  letter-spacing: 0;
  text-transform: none;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

.timeline__cursor {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 1px;
  height: 5rem;
  background: linear-gradient(to bottom, transparent, var(--accent));
}

.hint {
  /* Lower case, like the home page's own cue. The rest of the labels on
     this screen are data — dates, counts, ordinals — and stay upper; this
     one is an instruction to a person, and the home page says it in
     lower case at the bottom of the sky. */
  text-transform: none;
  letter-spacing: 0.14em;
  position: absolute;
  left: 50%;
  bottom: 14rem;
  transform: translateX(-50%);
  color: var(--ink-subtle);
  transition: opacity var(--dur-slow) var(--ease-out);
}
.hint.is-gone { opacity: 0; }

/* Return to the present. Only offered once you have actually travelled,
   so it never sits there as clutter at the start. */
.recall {
  position: absolute;
  left: 50%;
  bottom: 14rem;
  z-index: 11;
  display: inline-flex;
  align-items: center;
  gap: 0.9rem;
  padding: 0.9rem 1.5rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: rgba(13, 12, 11, 0.72);
  backdrop-filter: blur(8px);
  color: var(--ink-muted);
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, 0.6rem);
  transition: opacity var(--dur-mid) var(--ease-out),
              transform var(--dur-slow) var(--ease-soft),
              visibility var(--dur-mid), border-color var(--dur-mid), color var(--dur-mid);
}
.recall.is-on { opacity: 1; visibility: visible; transform: translate(-50%, 0); pointer-events: auto; }
.recall:hover { border-color: var(--ink-subtle); color: var(--ink); }
.recall svg { width: 1.6rem; height: 0.9rem; color: var(--accent); transition: transform var(--dur-slow) var(--ease-soft); }
.recall:hover svg { transform: translateX(-0.4rem); }

/* ─── player ─────────────────────────────────────────────────── */
.player {
  position: fixed;
  right: var(--pad);
  top: calc(var(--pad) + 5.2rem);
  z-index: 20;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-slow) var(--ease-out) 300ms;
}
.player { opacity: 1; pointer-events: auto; }

.player__pill {
  position: relative;
  display: flex;
  align-items: center;
  gap: 1rem;
  width: 21rem;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: rgba(13, 12, 11, 0.7);
  backdrop-filter: blur(8px);
  overflow: hidden;
  transition: border-color var(--dur-mid) var(--ease-out);
}
.player__pill:hover { border-color: var(--ink-subtle); }

.player__eq { display: flex; align-items: flex-end; gap: 2px; width: 1.4rem; height: 1.4rem; }
.player__eq i { flex: 1; background: var(--ink-subtle); height: 30%; border-radius: 1px; transition: background var(--dur-mid); }
.player.is-playing .player__eq i { background: var(--accent); animation: eq 900ms ease-in-out infinite; }
.player.is-playing .player__eq i:nth-child(2) { animation-delay: 150ms; }
.player.is-playing .player__eq i:nth-child(3) { animation-delay: 300ms; }
@keyframes eq { 0%, 100% { height: 25%; } 50% { height: 100%; } }

.player__marquee { flex: 1; overflow: hidden; white-space: nowrap; mask-image: linear-gradient(90deg, #000 86%, transparent); }
.player__track { display: inline-block; font-size: 1.2rem; color: var(--ink-muted); }
.player.is-playing .player__track { animation: marquee 14s linear infinite; }
@keyframes marquee { 0%, 8% { transform: translateX(0); } 92%, 100% { transform: translateX(calc(-100% + 13rem)); } }

.player__list {
  position: absolute;
  right: 0;
  top: calc(100% + 0.6rem);
  width: 30rem;
  list-style: none;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--ground-lift);
  overflow: hidden;
  opacity: 0;
  visibility: hidden;
  transform: translateY(0.4rem);
  transition: opacity var(--dur-mid) var(--ease-out), transform var(--dur-slow) var(--ease-soft), visibility var(--dur-mid);
}
.player:hover .player__list, .player:focus-within .player__list { opacity: 1; visibility: visible; transform: none; }
.player__list li + li { border-top: 1px solid var(--rule-soft); }
.player__list button {
  display: flex; align-items: baseline; gap: 1.2rem; width: 100%;
  padding: 0.9rem 1.2rem; text-align: left; font-size: 1.25rem; color: var(--ink-muted);
  transition: background var(--dur-fast), color var(--dur-fast);
}
.player__list button:hover { background: rgba(242, 239, 233, 0.04); color: var(--ink); }
.player__list li.is-active button { color: var(--ink); }
.player__list li.is-active .n { color: var(--accent); }
.player__list .n { font-size: 1.05rem; color: var(--ink-subtle); font-variant-numeric: tabular-nums; }

/* ─── lightbox ───────────────────────────────────────────────── */
.lightbox {
  position: fixed; inset: 0; z-index: 50;
  display: grid; place-items: center;
  opacity: 0; visibility: hidden;
  transition: opacity var(--dur-slow) var(--ease-out), visibility var(--dur-slow);
}
.lightbox.is-open { opacity: 1; visibility: visible; }
.lightbox__bg { position: absolute; inset: 0; background: rgba(8, 7, 7, 0.94); backdrop-filter: blur(10px); }

.lightbox__figure {
  position: relative;
  height: 76vh;
  max-width: 84vw;
  overflow: hidden;
  border-radius: var(--radius);
  will-change: transform;
  box-shadow: 0 3rem 8rem rgba(0, 0, 0, 0.7);
}
.lightbox__figure img, .lightbox__figure video { width: 100%; height: 100%; object-fit: cover; display: block; }

.lightbox__cap {
  position: absolute; left: 50%; bottom: 6rem;
  transform: translateX(-50%);
  display: flex; gap: 1rem; white-space: nowrap;
}
.lightbox__cap b { font-weight: 500; color: var(--ink); }
.lightbox__cap i { font-style: normal; color: var(--ink-subtle); }

.lightbox__close {
  position: absolute; top: var(--pad); right: var(--pad);
  padding: 0.8rem 1.4rem; border: 1px solid var(--rule); border-radius: var(--radius-pill);
  transition: border-color var(--dur-mid), background var(--dur-mid);
}
.lightbox__close:hover { border-color: var(--ink-subtle); background: var(--ground-lift); }

.lightbox__nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 4.6rem; height: 4.6rem;
  border: 1px solid var(--rule); border-radius: 50%;
  color: var(--ink-muted);
  transition: border-color var(--dur-mid), color var(--dur-mid), background var(--dur-mid);
}
.lightbox__nav:hover { border-color: var(--ink-subtle); color: var(--ink); background: var(--ground-lift); }
.lightbox__nav--prev { left: var(--pad); }
.lightbox__nav--next { right: var(--pad); }

/* ─── mobile ─────────────────────────────────────────────────── */
@media (max-width: 800px) {
  html { font-size: clamp(9px, 2.6vw, 14px); }
  :root { --pad: 1.8rem; }
  .carousel-item { width: 19rem; }
  .year span { font-size: 22rem; }
  .mark__name { font-size: 2.2rem; }
  .status { display: none; }
  .timeline { bottom: 9rem; }
  .player { top: var(--pad); right: var(--pad); }
  .player__list { width: 26rem; }
  .hint { display: none; }
  .recall { bottom: 16rem; }
  .lightbox__figure { height: auto; max-height: 64vh; max-width: 88vw; }
  .lightbox__nav { display: none; }
  .lightbox__cap { bottom: 3rem; white-space: normal; text-align: center; width: 80vw; justify-content: center; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}
