/* ============================================
   eftones — Efren Lozano Photography
   css/style.css
   © Efren Lozano. All Rights Reserved.

   WCAG 2.1 AAA Compliant
   All text contrast ratios verified >= 7:1
   ============================================ */

/* --- RESET --- */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

/* --- DARK THEME (default) ---
   Contrast ratios against bg #0a0a08:
   --fg        #e8e4d9  15.60:1  ✓ AAA
   --fg-dim    #b0a898   8.40:1  ✓ AAA
   --fg-mute   #b0a898   8.40:1  ✓ AAA (same — decorative use)
   --fg-ghost  decorative only, never carries text
   ----------------------------------------- */
:root,
[data-theme="dark"] {
  --bg:           #0a0a08;
  --bg-overlay:   rgba(10,10,8,0.97);
  --fg:           #e8e4d9;
  --fg-dim:       #b0a898;
  --fg-mute:      #b0a898;
  --fg-ghost:     #2a2a22;
  --border:       #3a3a30;
  --toggle-bg:    #2a2a22;
  --toggle-thumb: #b0a898;
  --focus-ring:   #e8e4d9;
  --font-display: 'Poppins', sans-serif; /* the nav widget's labels are Arvo Bold first with
    this only as a fallback, so this mainly changes the gallery title/caption and the
    About sleeve's name/side-labels — the places that use it directly. */
  --font-body:    'Quicksand', sans-serif;
  --about-accent: #e95c2e; /* CB Burnt Orange — fixed across themes, like a series accent.
    5.7:1 on dark bg (AA), 3.0:1 on light bg (large-text/UI floor) — the best dual-theme
    balance in the palette; only used for small labels/headings, never body copy. */
}

/* --- LIGHT THEME ---
   Contrast ratios against bg #f2efe8:
   --fg        #1a1a16  15.20:1  ✓ AAA
   --fg-dim    #4a4440   8.34:1  ✓ AAA
   --fg-mute   #504840   7.81:1  ✓ AAA
   ----------------------------------------- */
[data-theme="light"] {
  --bg:           #f2efe8;
  --bg-overlay:   rgba(242,239,232,0.97);
  --fg:           #1a1a16;
  --fg-dim:       #4a4440;
  --fg-mute:      #504840;
  --fg-ghost:     #ccc8be;
  --border:       #c0bcb0;
  --toggle-bg:    #c0bcb0;
  --toggle-thumb: #4a4440;
  --focus-ring:   #1a1a16;
}

/* --- BASE --- */
html {
  font-size: 16px; /* WCAG 1.4.4 — base for rem scaling */
  text-size-adjust: 100%;
}

body {
  width: 100%; height: 100%;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  line-height: 1.6; /* WCAG 1.4.12 */
  transition: background 0.3s ease, color 0.3s ease;
}

/* --- SCREEN READER ONLY UTILITY (WCAG 1.3.1) --- */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}

/* --- SKIP LINK (WCAG 2.4.1) --- */
.skip-link {
  position: absolute;
  top: -100%;
  left: 16px;
  z-index: 9999;
  background: var(--fg);
  color: var(--bg);
  font-family: var(--font-body);
  font-size: 0.75rem;
  letter-spacing: 2px;
  text-transform: uppercase;
  text-decoration: none;
  padding: 8px 16px;
  transition: top 0s;
}

.skip-link:focus {
  top: 16px;
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
}

/* --- GLOBAL FOCUS STYLES (WCAG 2.4.7, 2.4.11, 2.4.12) ---
   Visible, high-contrast focus indicator on all interactive elements
   ---------------------------------------------------------------- */
:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 3px;
}

/* Remove default outline only when focus-visible is supported */
:focus:not(:focus-visible) {
  outline: none;
}

/* --- NAV WIDGET ---
   A small fixed card floating at the left edge, vertically
   centered — not a full-height rail. Three columns: a decorative
   color spine, a rotated ink nameplate, and a stack of nav
   buttons. Colors are fixed across both themes (like a printed
   label), not theme tokens. */
.nav-widget {
  --nav-pad: 12px; /* one shared inset for every column/row in the widget — text to edge, always this value */
  position: fixed;
  left: 0; top: 50%;
  transform: translateY(-50%);
  z-index: 300;
  display: flex;
  align-items: stretch;
  overflow: hidden; /* clips content while JS animates width/height between states */
  animation: nav-widget-enter 0.7s cubic-bezier(.16,1,.3,1) both;
  box-shadow: 0px 1px 3px 0px rgba(0,0,0,0.30), 0px 4px 8px 3px rgba(0,0,0,0.15); /* M3 Elevation/Light/3 */
}

/* slides in from off-screen left on page load, settling into its
   centered resting position */
@keyframes nav-widget-enter {
  from { transform: translate(-100%, -50%); opacity: 0; }
  to   { transform: translate(0, -50%); opacity: 1; }
}

.nav-color-bar {
  width: 10px;
  display: flex;
  flex-direction: column;
}

.nav-bar-seg { flex: 1; }

/* the widget has two mutually-exclusive states — the plain menu,
   and the drilled-in Sessions list — toggled via [hidden]. Scoped
   with :not([hidden]) so the display:flex here can't out-rank the
   [hidden] attribute's own display:none. */
.nav-plain:not([hidden]),
.nav-sessions:not([hidden]),
.nav-gallery:not([hidden]) {
  display: flex;
  align-items: stretch;
}

.nav-nameplate {
  background: #0d101c;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--nav-pad);
}

.nav-nameplate span {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  font-family: 'Arvo', var(--font-display);
  font-weight: 700;
  text-transform: uppercase;
  font-size: 18px;
  color: #fff;
  white-space: nowrap;
}

.nav-buttons {
  background: #1ebcdf;
  display: flex;
  flex-direction: column;
  min-width: 194px;
}

.nav-btn {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  color: #0d101c;
  font-family: 'Arvo', var(--font-display);
  font-weight: 700;
  text-transform: uppercase;
  font-size: 18px;
  padding: var(--nav-pad);
  cursor: pointer;
  min-height: 44px; /* WCAG 2.5.5 */
  text-decoration: none;
  transition: background 0.15s ease;
}

.nav-btn:hover,
.nav-btn:focus-visible { background: rgba(13,16,28,0.12); }

/* mobile-only abbreviated label (e.g. "MAP" for "reciprocity map") —
   the full text stays the a11y name via aria-label on the link, so
   swapping which span is visible is purely cosmetic. */
.nav-btn-short { display: none; }

/* --- SESSIONS (drilled-in state) ---
   Replaces the nameplate with two rotated ink bars — a back arrow,
   then a static "SESSIONS" label — followed by a plain scrolling
   text list of sets, no thumbnails, on the spine's own blue. */
.nav-bar {
  background: #0d101c;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  padding: var(--nav-pad);
}

/* scoped to the label only (not .nav-bar-back) — the back arrow is a
   single glyph that must stay upright and pointing left, not inherit
   this vertical rotation meant for the "SESSIONS"/"EXTRAS" text. */
.nav-bar-label span {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  font-family: 'Arvo', var(--font-display);
  font-weight: 700;
  text-transform: uppercase;
  font-size: 16px;
  color: #fff;
  white-space: nowrap;
}

/* the arrow is a single glyph, not a word — it stays upright so it
   reads as pointing left, rather than inheriting the vertical text
   rotation used for "SESSIONS". */
.nav-bar-btn {
  background: none;
  border: none;
  cursor: pointer;
  color: #fff;
  font-size: 20px;
  line-height: 1;
  padding: 10px 0;
  min-height: 44px; /* WCAG 2.5.5 */
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.nav-bar-btn:hover,
.nav-bar-btn:focus-visible { opacity: 0.7; }

.nav-sessions-list {
  list-style: none;
  margin: 0;
  background: #1a96d5;
  display: flex;
  flex-direction: column;
  min-width: 220px;
  max-height: 70vh;
  overflow-y: auto;
  padding: 6px 0;
}

.session-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  color: #fff;
  font-family: 'Arvo', var(--font-display);
  font-weight: 700;
  text-transform: uppercase;
  font-size: 14px;
  padding: var(--nav-pad);
  cursor: pointer;
  min-height: 36px;
  transition: background 0.15s ease;
}

.session-item:hover,
.session-item:focus-visible { background: rgba(255,255,255,0.15); }

/* --- GALLERY (drilled-in state, viewing a set) ---
   Just a back arrow, styled identically to the Sessions/Extras/About
   back button (.nav-bar/.nav-bar-btn) — the gallery viewer's own text
   column now carries the title/set-name/frame-number info (see
   .lb-side in the gallery viewer), so the nav no longer needs to
   repeat that information. */

/* --- CORNER THEME TOGGLE ---
   Relocated out of the old masthead into its own small fixed
   control, since the nav widget above has no room for it. */
.corner-toggle {
  position: fixed;
  top: 16px; right: 20px;
  z-index: 600; /* above the gallery viewer / about panel (z-index 500) so it never gets covered by them */
}

/* --- THEME TOGGLE (WCAG 4.1.2) --- */
.theme-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  user-select: none;
  min-height: 44px; /* WCAG 2.5.5 */
  background: var(--bg);
  padding: 2px 12px;
  border-radius: 999px;
}

.toggle-label {
  font-family: var(--font-body);
  font-size: 0.4375rem; /* 7px */
  letter-spacing: 3px;
  text-transform: uppercase;
  color: var(--fg-dim);
  transition: color 0.2s;
  min-width: 28px;
  text-align: right;
}

.theme-toggle:hover .toggle-label { color: var(--fg); }

.toggle-track {
  width: 28px; height: 14px;
  background: var(--toggle-bg);
  border-radius: 7px;
  position: relative;
  transition: background 0.3s;
  border: 2px solid var(--border); /* visible border for non-color indication */
  flex-shrink: 0;
}

.toggle-thumb {
  position: absolute;
  top: 1px; left: 1px;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--toggle-thumb);
  transition: transform 0.3s ease, background 0.3s;
}

.theme-toggle.is-light .toggle-thumb {
  transform: translateX(14px);
  background: var(--fg);
}

.theme-toggle.is-light .toggle-track { background: var(--fg-ghost); }

/* --- CANVAS (scrollable main content) --- */
#canvas,
#main-content {
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding-top: 24px;
  padding-left: 270px; /* clears the fixed nav widget */
  padding-bottom: 24px;
  /* WCAG 1.4.12 — scroll without loss of content */
  scroll-behavior: smooth;
}

/* --- FEATURED PHOTO (home page) ---
   Fills the otherwise-blank space beside the nav widget on first
   load — a record-label-style layout, title + a divider + the
   session name (echoing the vinyl sleeve's own title band), paired
   with a square, clickable photo. The set's own accent (--tint)
   colors the session name, same convention as the gallery's frame
   count. */
.featured {
  display: flex;
  align-items: center; /* centers .featured-row, as a block, in the viewport */
  justify-content: center;
  min-height: calc(100vh - 48px); /* fills the viewport minus #main-content's own top/bottom padding */
}

.featured[hidden] { display: none; }

/* the text/photo pair itself — top-aligned to each other, so the
   title sits flush with the top of the photo. .featured (above)
   centers this whole row as one block instead. */
.featured-row {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 256px;
  flex-wrap: wrap;
}

.featured-text { max-width: 380px; min-width: 0; }

.featured-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(28px, 4vw, 46px);
  line-height: 1.05;
  letter-spacing: -0.3px;
  color: var(--fg);
}

.featured-rule {
  width: 72px; /* fallback — js/main.js sets this to 3/4 of the title's own rendered width */
  height: 4px;
  margin: 6px 0 8px;
  background: var(--fg);
}

.featured-session {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 0.75rem;
  letter-spacing: 0.5px;
  color: color-mix(in srgb, var(--tint, #e95c2e) 55%, var(--fg));
}

.featured-photo {
  flex-shrink: 0;
  width: min(560px, 52vw, 58vh);
  aspect-ratio: 1 / 1;
  position: relative;
  overflow: hidden;
  border: none;
  padding: 0;
  cursor: pointer;
  background: var(--fg-ghost);
  box-shadow: 0 14px 34px rgba(0,0,0,0.3);
}

.featured-photo img,
.featured-photo svg {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.4s ease;
}

.featured-photo:hover img,
.featured-photo:focus-visible img { transform: scale(1.04); }

/* --- GALLERY VIEWER (docked beside the nav widget) ---
   No longer a full-bleed modal — it's a panel that sits in the
   remaining space to the right of the nav widget (which carries the
   back link, frame number and photo title while this is open), using
   the page's own theme background rather than a fixed dark room. Info
   sits in a single column left of the photo — like liner notes next
   to album art — sized around the image's own rendered width, so it
   self-adjusts to it. Each set's own accent (--tint) carries the
   duotone identity from the grid tiles into the count here.
   WCAG 1.4.13 — dismissible via Escape / the nav's back button,
   content persists on hover/focus.
   ----------------------------------------------------------------- */
.lightbox {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  left: 270px; /* clears the nav widget, same offset as #main-content */
  background: var(--bg);
  z-index: 500;
  isolation: isolate;
}

.lightbox[hidden] { display: none; }

.lb-nav { display: contents; }

.lb-prev, .lb-next {
  position: absolute; top: 0; bottom: 0; z-index: 4;
  width: 64px;
  display: flex; align-items: center;
  background: none; border: none; cursor: pointer;
  color: var(--fg-mute);
  font-size: 2rem; line-height: 1;
  transition: color 0.2s, background 0.2s;
}

.lb-prev { left: 0; justify-content: flex-start; padding-left: 14px; }
.lb-next { right: 0; justify-content: flex-end; padding-right: 14px; }

.lb-prev:hover, .lb-next:hover { color: var(--fg); }
.lb-prev:hover { background: linear-gradient(90deg, var(--bg-overlay), transparent); }
.lb-next:hover { background: linear-gradient(270deg, var(--bg-overlay), transparent); }

/* stage: text column | image column — echoes the Featured layout's
   own text-block-beside-photo pairing (see .featured-row), text on
   the left. The text column is stretched to the photo's own full
   height (the taller of the two, in practice) and split top/bottom:
   title, rule (same convention as .featured-rule) and session name
   line up with the top of the photo; frame number, description,
   count and credit line up with its bottom — leaving whatever gap
   falls out in between. */
.lb-stage {
  position: absolute; inset: 0;
  display: grid;
  grid-template-columns: minmax(220px, 340px) minmax(0, max-content);
  align-items: stretch;
  /* grid's default align-content behaves as stretch, which would
     inflate the single auto row to fill this absolutely-positioned
     panel's full height — well past the photo's own rendered height
     — and align-items: stretch above would then stretch .lb-side to
     match, spreading its space-between content past the photo's top
     and bottom edges instead of bounding it to them. Pinning the row
     to its own content height (the photo's) and centering that row
     in the panel keeps .lb-side's stretch scoped to the photo. */
  align-content: center;
  gap: 40px;
  padding: 56px 64px;
  z-index: 2;
}

.lb-side {
  min-width: 0;
  max-width: 380px;
  text-align: left;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

/* .lb-main is sized to the PHOTO alone — width: fit-content shrink-
   wraps it to the image's own rendered width (a standard CSS
   shrink-to-fit calculation, not a percentage/flex resolution, so it
   doesn't depend on JS measuring anything after the image loads).
   It's also the tallest item in the row in practice, so it's what
   .lb-side stretches to match; centered here in case the text column
   ever ends up taller instead. */
.lb-main {
  width: fit-content;
  max-width: 100%;
  min-width: 0;
  display: flex;
  align-items: center;
}

.lb-photo-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 32px;
  letter-spacing: 1px;
  line-height: 1.05;
  color: var(--fg);
}

/* the rule sits between the title and the session name below it —
   sized to 3/4 of the title's own rendered width by js/main.js, same
   convention (and same syncRuleWidth helper) as .featured-rule. */
.lb-rule {
  width: 72px; /* fallback until JS measures the title */
  height: 4px;
  margin: 10px 0 8px;
  background: var(--fg);
}

.lb-img-wrap { min-width: 0; }

/* The full-size viewer shows photos true-to-color, undoctored — no
   frame or border around the print, it just sits on the page. */
.lb-img-frame {
  position: relative;
  max-width: 100%;
}

.lb-img-frame img,
.lb-img-frame svg {
  max-width: 100%; max-height: 62vh;
  object-fit: contain; display: block;
}

.lb-set-label {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 20px;
  letter-spacing: 0.5px;
  line-height: 1.2;
  color: var(--fg);
}

.lb-frame-badge {
  display: inline-block;
  background: var(--tint, #e95c2e);
  color: #0d101c; /* overridden inline per photo — see js/main.js readableTextColor */
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.625rem;
  letter-spacing: 0.5px;
  padding: 4px 9px;
  border-radius: 3px;
  margin-bottom: 10px;
}

.lb-side p {
  font-size: 0.5rem; letter-spacing: 2px;
  color: var(--fg-dim); text-transform: uppercase;
  line-height: 1.7;
}

/* this label sits directly on the theme background, so a bright
   per-set accent can lose most of its contrast in light mode — blend
   it toward the theme's own foreground color to keep it readable. */
.lb-count { margin-top: 12px !important; color: color-mix(in srgb, var(--tint, #e95c2e) 55%, var(--fg)) !important; }

.lb-credit {
  color: var(--fg-mute) !important;
  margin-top: 14px !important;
}

/* --- ABOUT PANEL --- */
/* Docked beside the nav widget, same as the gallery viewer — a record-
   sleeve layout. A fixed-color title band ("printed" cream stock, not
   a theme token — same rule the nav widget follows) sits above a
   single square photo; the bio line, the catalogue-style "Side A /
   Side B" lists (numbered via CSS counters, like a tracklist) and the
   imprint line all sit directly on top of that photo, like liner
   notes printed on the cover art, darkened with a scrim so they stay
   legible over any part of the image. */
.about-panel {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  left: 270px; /* clears the nav widget, same offset as #main-content */
  background: var(--bg);
  z-index: 500;
  display: flex; justify-content: center;
  align-items: center; /* fallback for browsers without safe/unsafe alignment support */
  align-items: safe center; /* on short viewports, center normally but fall back to
    start alignment before the sleeve overflows both edges — plain center here would
    push the top half of the overflow above scrollTop:0, permanently unreachable */
  padding: 18px 28px;
  overflow-y: auto; /* safety net on short viewports — the layout is sized to avoid needing it */
}

.about-panel[hidden] { display: none; }

.vinyl-sleeve {
  display: flex;
  flex-direction: column;
  /* sized by viewport height rather than a fixed pixel cap, so it
     fills almost the full browser height — the panel's own padding
     plus the shortfall from 100vh leaves just enough room around it
     to see the drop shadow rather than cropping into it. */
  width: min(90vh, 100%, 900px);
  aspect-ratio: 1 / 1;
  flex-shrink: 0;
  box-shadow: 0 18px 44px rgba(0,0,0,0.35);
}

/* ---- title band — fixed print colors, like the nav widget ---- */
.sleeve-header {
  flex-shrink: 0;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  background: #ece6d6;
  color: #14120f;
  padding: 18px 22px 14px;
  border-bottom: 4px solid var(--about-accent);
}

.sleeve-name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(28px, 5.5vw, 46px);
  line-height: 0.88;
  letter-spacing: 1px;
  text-transform: uppercase;
}

.sleeve-handle {
  margin-top: 6px;
  font-family: var(--font-body);
  font-size: 0.5625rem; /* 9px */
  letter-spacing: 4px;
  text-transform: uppercase;
  color: var(--about-accent);
}

/* bio sits beside the name, in the title band itself now, rather than
   over the photo — frees up the art below for Side A / Side B. */
.sleeve-header-bio {
  max-width: 260px;
  text-align: right;
  font-family: var(--font-body);
  font-size: 0.5625rem; /* 9px */
  letter-spacing: 1px;
  line-height: 1.6;
  text-transform: uppercase;
  color: rgba(20,18,15,0.72);
}

/* ---- the photo, and everything printed on top of it ---- */
.sleeve-art {
  position: relative;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.sleeve-art img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  z-index: 0;
}

/* halftone screen — matches the grid tiles' print texture */
.sleeve-grain {
  position: absolute; inset: 0;
  pointer-events: none;
  z-index: 1;
  mix-blend-mode: multiply;
  opacity: 0.15;
  background-image: radial-gradient(circle, #000 34%, transparent 35%);
  background-size: 3.2px 3.2px;
}

/* darkens top and bottom (where the bio/imprint text sits) more than
   the middle, so the liner-note text reads regardless of what part of
   the photo is underneath it */
.sleeve-scrim {
  position: absolute; inset: 0;
  pointer-events: none;
  z-index: 1;
  background: linear-gradient(
    180deg,
    rgba(4,4,4,0.55) 0%,
    rgba(4,4,4,0.08) 22%,
    rgba(4,4,4,0.08) 55%,
    rgba(4,4,4,0.28) 75%,
    rgba(4,4,4,0.6) 100%
  );
}

/* the tracklist-style catalogue — a translucent glass plate over the
   middle of the photo, so the list text stays legible no matter how
   busy that part of the image is */
.sleeve-sides {
  position: relative;
  z-index: 2;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px 22px;
  padding: 10px 20px;
  background: rgba(4,4,4,0.42);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  border-top: 1px solid rgba(255,255,255,0.08);
  border-bottom: 1px solid rgba(255,255,255,0.08);
}

.sleeve-side { counter-reset: track; min-width: 0; }

.sleeve-side-label {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 17px;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--about-accent);
  margin-bottom: 8px;
}

.sleeve-section { margin-bottom: 12px; }
.sleeve-section:last-child { margin-bottom: 0; }

.sleeve-section-title {
  font-family: var(--font-body);
  font-size: 0.375rem; /* 6px */
  font-weight: 400;
  letter-spacing: 3px; text-transform: uppercase;
  color: rgba(243,239,230,0.55); margin-bottom: 6px;
}

.sleeve-list { list-style: none; display: flex; flex-direction: column; gap: 6px; }

.sleeve-list li {
  counter-increment: track;
  position: relative;
  display: flex; flex-direction: column; gap: 2px;
  padding-left: 15px;
  font-size: 0.5rem; /* 8px */
  letter-spacing: 0.3px; color: rgba(243,239,230,0.88); line-height: 1.4;
}

.sleeve-list li::before {
  content: counter(track, decimal-leading-zero);
  position: absolute; left: 0; top: 0;
  font-family: var(--font-body);
  font-size: 0.5rem;
  color: var(--about-accent);
}

.sleeve-list-title { font-size: 0.5rem; letter-spacing: 0.3px; color: rgba(243,239,230,0.88); line-height: 1.35; }
.sleeve-list-meta  { font-size: 0.375rem; letter-spacing: 1.5px; color: rgba(243,239,230,0.55); text-transform: uppercase; }

.sleeve-list-link {
  text-decoration: none;
  color: inherit;
  display: flex;
  flex-direction: column;
  gap: 2px;
  border-bottom: 1px solid rgba(255,255,255,0.15);
  padding-bottom: 4px;
  transition: border-color 0.2s, color 0.2s;
}

.sleeve-list-link:hover .sleeve-list-title { color: #fff; }
.sleeve-list-link:hover { border-bottom-color: var(--about-accent); }

/* ---- imprint line, printed at the very bottom of the sleeve ---- */
.sleeve-footer {
  position: relative;
  z-index: 2;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px 14px;
  padding: 10px 18px;
}

.sleeve-link {
  font-size: 0.4375rem; letter-spacing: 2px; text-transform: uppercase;
  color: rgba(243,239,230,0.85); text-decoration: underline; /* WCAG 1.4.1 — not color alone */
  text-underline-offset: 3px;
  min-height: 44px; /* WCAG 2.5.5 */
  display: inline-flex; align-items: center;
  transition: color 0.2s;
}

.sleeve-link:hover { color: #fff; }

.sleeve-copy {
  font-size: 0.375rem; letter-spacing: 1.5px;
  color: rgba(243,239,230,0.55); text-transform: uppercase;
}

/* --- NOISE (decorative, hidden from AT) --- */
.noise-overlay {
  position: fixed; inset: 0; pointer-events: none; z-index: 700; opacity: 0.04;
  /* feTurbulence outputs colorful RGB static by default — the
     feColorMatrix desaturates it to grayscale film grain, which
     also avoids a colorful first-paint flash some browsers (notably
     Firefox) show while the inline SVG filter is still rasterizing. */
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 180px;
}

/* matches found-film.html / reciprocity.html's own scan-line — a
   faint line sweeping down the page. The reduced-motion override
   below already covers this via the universal `*` rule. */
.scan-line {
  position: fixed; top: -1px; left: 0; right: 0;
  height: 1px;
  background: rgba(232,228,217,0.025);
  z-index: 690; pointer-events: none;
  animation: scan 10s linear infinite;
}

@keyframes scan { 0% { top: -1px; } 100% { top: 100vh; } }

/* --- SCROLLBAR --- */
::-webkit-scrollbar { width: 4px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: var(--fg-ghost); }

/* --- REDUCED MOTION (WCAG 2.3.3) --- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .noise-overlay { display: none; }
}

/* --- HIGH CONTRAST MODE (WCAG 1.4.11) --- */
@media (forced-colors: active) {
  .toggle-track { border: 2px solid ButtonText; }
  .toggle-thumb { background: ButtonText; }
  .nav-widget { border: 2px solid ButtonText; }
  .nav-sessions-list { border: 2px solid ButtonText; }
}

/* --- RESPONSIVE --- */
@media (max-width: 768px) {
  /* below desktop width there's no room for the widget as a tall
     left-edge card — it collapses to a horizontal bar pinned across
     the top instead, spine on top, nameplate below it, buttons
     wrapping into a 2x2 grid. */
  .nav-widget {
    left: 50%; top: 0;
    transform: translateX(-50%);
    flex-direction: column;
    width: 100%;
    max-width: 340px;
    animation-name: nav-widget-enter-mobile;
  }

  /* mobile pins the widget across the top instead of vertically
     centered on the left, so it slides down rather than in from
     the side. */
  @keyframes nav-widget-enter-mobile {
    from { transform: translate(-50%, -100%); opacity: 0; }
    to   { transform: translate(-50%, 0); opacity: 1; }
  }

  .nav-color-bar { flex-direction: row; width: 100%; height: 10px; }

  .nav-plain:not([hidden]),
  .nav-sessions:not([hidden]),
  .nav-gallery:not([hidden]) { flex-direction: column; width: 100%; }

  .nav-nameplate { width: 100%; height: 24px; padding: 4px var(--nav-pad); }
  .nav-nameplate span { writing-mode: horizontal-tb; transform: none; font-size: 12px; letter-spacing: 1px; }

  /* single row instead of a 2x2 grid — every button shares the row
     equally and the long "reciprocity map" label swaps to "MAP" (see
     .nav-btn-short above) so nothing wraps to a second line. */
  .nav-buttons { flex-direction: row; flex-wrap: nowrap; width: 100%; }
  .nav-btn {
    flex: 1 1 0;
    width: auto;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    font-size: 10.5px;
    letter-spacing: 0.3px;
    padding: 4px 3px;
    min-height: 40px;
    white-space: nowrap;
  }
  .nav-btn-short { display: inline; }
  .nav-btn-full { display: none; }

  .nav-bar { width: 100%; height: 30px; }
  .nav-bar span { writing-mode: horizontal-tb; transform: none; font-size: 13px; }
  .nav-bar-btn { padding: 0; min-height: 30px; font-size: 18px; }

  .nav-sessions-list { width: 100%; max-height: 40vh; min-width: 0; }
  .session-item { font-size: 13px; }

  .corner-toggle { top: auto; bottom: 16px; right: 16px; }

  .theme-toggle { min-height: 32px; }

  .toggle-label { font-size: 6px; letter-spacing: 2px; min-width: 24px; }

  #canvas, #main-content { padding-top: 90px; padding-left: 14px; }

  .featured { min-height: 0; padding: 8px 4px 24px; }
  .featured-row { flex-direction: column; align-items: center; gap: 24px; }
  /* matching .featured-text's width to .featured-photo's (both
     min(320px, 78vw)) means align-items: center above centers the
     two boxes by the same amount, so their left edges coincide —
     letting text-align: left inside .featured-text line the title,
     rule, and session name up with the photo's own left edge. */
  .featured-text { text-align: left; width: min(320px, 78vw); max-width: 100%; }
  .featured-rule { margin-left: 0; margin-right: auto; }
  .featured-photo { width: min(320px, 78vw); }

  /* about panel + lightbox: no room to dock beside the nav widget,
     which is now a horizontal bar pinned across the top — both sit
     below it instead, full width, same offset #main-content uses. */
  .about-panel {
    top: 90px; left: 0; right: 0; bottom: 0;
    padding: 16px;
  }

  /* not enough height to keep it a true square at this width without
     crowding the liner-note text — let the sleeve grow taller instead
     of staying 1:1, same clears-the-nav-bar reasoning as the gallery
     viewer's own mobile stack. Centering it vertically (the desktop
     behavior) would overflow both above and below the scroll
     container here, and the top half of that overflow is unreachable
     by scrolling — so top-align instead, which keeps the whole sleeve
     reachable by scrolling straight down. */
  .about-panel { align-items: flex-start; }
  .vinyl-sleeve { width: 100%; max-width: 380px; aspect-ratio: auto; margin: 0 auto; }
  .sleeve-art { min-height: 480px; }

  .sleeve-name { font-size: 30px; }

  /* not enough width to sit the bio beside the name — stack it below
     instead, left-aligned like the name above it. */
  .sleeve-header { flex-direction: column; align-items: flex-start; gap: 10px; }
  .sleeve-header-bio { max-width: 100%; text-align: left; }

  .sleeve-sides {
    grid-template-columns: 1fr;
    gap: 18px;
  }

  .lightbox {
    top: 90px; left: 0; right: 0; bottom: 0;
  }

  /* not enough width for a side column either — stack instead, image
     on top, info below, centered. The text column is source-order
     first (it sits left of the photo on desktop), so flip it after
     the image here via order rather than reordering the markup. */
  /* single fit-content(88vw) column instead of 1fr: the track sizes
     to the widest row (normally the photo, capped at the same 88vw
     the image itself is capped to below) and is then centered as a
     block via justify-content, so .lb-main and .lb-side — both left/
     start-aligned within it — share the photo's own left edge rather
     than each other centering independently. */
  .lb-stage {
    grid-template-columns: fit-content(88vw);
    justify-content: center;
    grid-template-rows: auto auto;
    gap: 14px;
    padding: 20px 20px 24px;
    overflow-y: auto;
  }

  .lb-main { order: 1; max-width: 100%; display: flex; justify-content: flex-start; }
  .lb-side { order: 2; text-align: left; max-width: 100%; }

  /* .lb-side no longer stretches to the photo's height at this width
     (both rows just size to their own content), so the top/bottom
     split needs an explicit gap instead of justify-content: space-
     between, which would have nothing to distribute. */
  .lb-side-top { margin-bottom: 18px; }

  .lb-photo-title { font-size: 24px; text-align: left; }

  .lb-rule { margin-left: 0; margin-right: auto; }

  .lb-img-frame img,
  .lb-img-frame svg { max-width: 88vw; max-height: 42vh; }

  .lb-set-label { font-size: 22px; text-align: left; }

  .lb-side p { max-width: 46ch; margin: 0; }

  .lb-prev, .lb-next { width: 44px; }
}

@media (max-width: 360px) {
  /* the stacked widget still crowds this width — drop its buttons
     to a single column rather than let them wrap awkwardly. */
  .nav-btn { width: 100%; }
}

/* --- PRINT (WCAG 1.3.4) --- */
@media print {
  .nav-widget, .corner-toggle, .lightbox, .about-panel,
  .noise-overlay, .skip-link { display: none !important; }
  #canvas, #main-content { position: static; padding: 0; overflow: visible; }
  body { background: white; color: black; }
}
