:root {
  --ink: #f5f3ee;
  --slide-duration: 700ms;   /* image wipe duration */
  --text-duration: 520ms;    /* how long EACH character/line takes to fade+move */
  --text-rise: 150px;         /* how far the block (number/divider) travels */
  --char-rise: 10px;         /* how far EACH character travels */
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html, body {
  height: 100%;
  overflow: hidden; /* navigation is fully wheel/touch driven, no native scroll */
  overscroll-behavior: none;
}

body {
  background: #05070a;
  color: var(--ink);
  font-family: 'Jost', sans-serif;
  -webkit-font-smoothing: antialiased;
}

.slides {
  position: relative;
  width: 100%;
  height: 100vh;
  height: 100dvh; /* accounts for mobile browser chrome show/hide; 100vh is the fallback */
}

.slide {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  z-index: 1;
}

/* The image itself never moves or scales — only the visible region (clip-path) */
/* changes, so switching slides reads as a clean wipe, not the photo panning.   */
.slide__visual {
  position: absolute;
  inset: 0;
  clip-path: inset(100% 0 0 0); /* fallback before JS initializes; JS controls clip-path from here on */
  transition: clip-path var(--slide-duration) cubic-bezier(0.65, 0, 0.35, 1);
}

.slide__bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
}

.slide__overlay {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(0,0,0,0.35) 0%, rgba(0,0,0,0.08) 30%, rgba(0,0,0,0.15) 65%, rgba(0,0,0,0.55) 100%);
  pointer-events: none;
}

/* ---------- Reveal: text has its own entrance/exit, independent of the image. ---------- */
/* Every title sits in one of three states:                                              */
/*   .pos-below  — hidden, waiting/leaving below (used entering from below or exiting down) */
/*   .pos-above  — hidden, waiting/leaving above (used entering from above or exiting up)    */
/*   .is-in      — fully visible, settled                                                   */
/* JS just swaps these classes; which one means "enter" vs "exit" depends on scroll direction. */

.reveal {
  display: flex;
  flex-direction: column;
  transition: opacity var(--text-duration) ease, transform var(--text-duration) cubic-bezier(0.22, 1, 0.36, 1);
}

.pos-below .reveal { opacity: 0; transform: translateY(var(--text-rise)); }
.pos-above .reveal { opacity: 0; transform: translateY(calc(-1 * var(--text-rise))); }
.is-in .reveal { opacity: 1; transform: translateY(0); }

/* no-anim: instantly snaps a title into a state with no transition, used to reset it */
/* before its next entrance so the reset itself is never seen animating.              */
.no-anim, .no-anim * {
  transition: none !important;
}

/* ---------- Brand title (Atsushi Fukuda Photography) ---------- */

.brand-title {
  position: absolute;
  z-index: 2;
  color: var(--ink);
  text-shadow: 0 2px 24px rgba(0, 0, 0, 0.35);
}

.brand-title--center {
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
}

.brand-title--center .reveal {
  align-items: center;
}

.brand-title--center .brand-title__name {
  font-size: clamp(2.2rem, 6vw, 4.5rem);
}

.brand-title--center .brand-title__sub {
  font-size: clamp(0.85rem, 1.6vw, 1.15rem);
}

.brand-title--left {
  /* Lives outside .slides as a single persistent element (see index.html), so it */
  /* needs its own fixed positioning instead of relying on a parent .slide.       */
  position: fixed;
  top: 55%;
  left: 2.75rem;
  transform: translateY(-50%);
  text-align: left;
  z-index: 55; /* above every slide, regardless of how high their z-index has climbed */
}

.brand-title--left .reveal {
  align-items: flex-start;
}

.brand-title--left .brand-title__name {
  font-size: clamp(1.5rem, 3vw, 2.1rem);
}

.brand-title--left .brand-title__sub {
  font-size: clamp(0.6rem, 1vw, 0.75rem);
}

.brand-title__name {
  font-family: 'Cormorant Garamond', serif;
  font-weight: 300;
  letter-spacing: 0.1em;
  white-space: nowrap;
}

.brand-title__sub {
  margin-top: 0.35em;
  font-family: 'Jost', sans-serif;
  font-weight: 300;
  letter-spacing: 0.5em;
  text-transform: uppercase;
  white-space: nowrap;
  padding-left: 0.5em; /* optical centering against tracking */
}

/* Individual letters (see splitChars() in main.js): each .char gets its own    */
/* staggered transition-delay, so the whole word fades in/out left to right.   */
.char {
  display: inline-block;
  transition: opacity var(--text-duration) ease, transform var(--text-duration) cubic-bezier(0.22, 1, 0.36, 1);
}

.pos-below .char { opacity: 0; transform: translateY(var(--char-rise)); }
.pos-above .char { opacity: 0; transform: translateY(calc(-1 * var(--char-rise))); }
.is-in .char { opacity: 1; transform: translateY(0); }

/* ---------- Category title (01 / mountains, etc.) ---------- */

.category-title {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  color: var(--ink);
  text-shadow: 0 2px 24px rgba(0, 0, 0, 0.35);
}

.category-title .reveal {
  align-items: center;
}

.category-title__num {
  font-family: 'Jost', sans-serif;
  font-weight: 300;
  font-size: 0.9rem;
  letter-spacing: 0.35em;
}

/* The "0" always slides in horizontally from the left; the digit after it (1/2/3) */
/* always slides in from the right — fixed per character position, independent of  */
/* which direction (pos-below/pos-above) the rest of the title is animating in.    */
.pos-below .category-title__num .char:nth-child(1),
.pos-above .category-title__num .char:nth-child(1) {
  opacity: 0;
  transform: translateX(calc(-1 * var(--char-rise)));
}

.pos-below .category-title__num .char:nth-child(2),
.pos-above .category-title__num .char:nth-child(2) {
  opacity: 0;
  transform: translateX(var(--char-rise));
}

.is-in .category-title__num .char {
  opacity: 1;
  transform: translateX(0);
}

.category-title__divider {
  position: relative;
  overflow: hidden; /* clips the bright sweep to the bar's own bounds */
  width: 1px;
  height: 30px;
  margin: 0.9rem 0;
  background: rgba(245, 243, 238, 0.55);
}

/* A brighter-white segment that sweeps top-to-bottom through the divider,   */
/* accelerating as it goes (ease-in). Only runs while the title is visible. */
.category-title__divider::after {
  content: '';
  position: absolute;
  left: 0;
  top: -35%;
  width: 100%;
  height: 35%;
  background: #ffffff;
}

.is-in .category-title__divider::after {
  animation: dividerSweep 3.2s cubic-bezier(0.55, 0, 1, 1.85) infinite;
}

@keyframes dividerSweep {
  from { top: -35%; }
  to   { top: 135%; }
}

.category-title__word {
  font-family: 'Cormorant Garamond', serif;
  font-style: italic;
  font-weight: 400;
  font-size: clamp(2.6rem, 7vw, 5.5rem);
  white-space: nowrap;
}

/* ---------- Ripple button: sits below the word, opens that category's page ---------- */

.ripple-btn {
  position: relative;
  width: 12px;
  height: 12px;
  margin-top: 2.4rem;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--ink);
  cursor: pointer;
  transition: background-color 0.25s ease, transform 0.25s ease;
}

.ripple-btn:hover {
  background: #ffffff;
  transform: scale(2.15);
}

/* Brief squash right on click, before the click-flash takes over and rockets outward. */
.ripple-btn.is-pressed {
  transition: transform 0.14s ease-in, background-color 0.14s ease;
  transform: scale(0.5);
  background: #ffffff;
}

.ripple-btn::before,
.ripple-btn::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 1px solid rgba(245, 243, 238, 0.7);
  opacity: 0;
}

/* the ripple only actually plays while its title is on screen */
.is-in .ripple-btn::before,
.is-in .ripple-btn::after {
  animation: rippleOut 2.6s linear infinite; /* pacing now comes from the keyframe stops below, not the easing curve */
}

.is-in .ripple-btn::after {
  animation-delay: 1.3s;
}

/* Uneven stop spacing = uneven speed: big jumps in scale are fast bursts, */
/* small jumps are slow drifts, so the ripple surges rather than expanding evenly. */
@keyframes rippleOut {
  0%   { transform: scale(1);    opacity: 0.55; }
  12%  { transform: scale(1.55); opacity: 0.5; }   /* quick burst */
  30%  { transform: scale(1.7);  opacity: 0.4; }    /* slow drift */
  50%  { transform: scale(2.15); opacity: 0.28; }   /* burst again */
  70%  { transform: scale(2.25); opacity: 0.16; }   /* slow drift */
  100% { transform: scale(2.7);  opacity: 0; }       /* final burst + fade */
}

/* ---------- Click-flash: circle wipe that rockets out from a clicked ripple-btn ---------- */

.click-flash {
  position: fixed;
  top: 0;
  left: 0;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: #ffffff;
  opacity: 0; /* stays invisible until a ripple-btn click sets it (see triggerFlash in main.js) —
                 without this it's a plain white circle sitting at the 0,0 corner at rest */
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 300;
  will-change: left, top, width, height;
}

/* ---------- Genre page (mountains.html, etc.): white crescent settles anchored to the ---------- */
/* top-left (picking up where the click-flash left off); a black wedge is left in the      */
/* bottom-right, and a 3-photo carousel sits within the white area near the top-left.      */

.reveal-white {
  position: fixed;
  inset: 0;
  background: #f2f2f0;
  z-index: 1;
  pointer-events: none;
}

/* Decorative line-art illustration, sitting on top of the white background but below the   */
/* carousel/text (z-index 5+) so photos and copy always read clearly over it.               */
.genre-bg {
  position: fixed;
  inset: 0;
  background-repeat: no-repeat;
  background-position: 0px -150px; /* second value = vertical position, freely adjustable */
  background-size: 100% ; /* full width; height scales to match, keeping aspect ratio */
  z-index: 2;
  pointer-events: none;
  opacity: 0;
  transform: translateY(0);
  transition: transform 0.8s cubic-bezier(0.55, 0, 1, 0.45), opacity 1.2s ease;
}

/* Arriving on the page: fades up to full opacity — see .is-visible toggled in js/mountains.js */
/* (the same rAF-delayed pattern used for .genre-stage). */
.genre-bg.is-visible {
  opacity: 1;
}

/* Leaving for a category's detail page (see js/mountains.js leaveToDetail()): the mountain    */
/* line art slides straight down off the bottom of the screen. */
.genre-bg.is-leaving {
  transform: translateY(130vh);
}

/* Moon: rises at the left horizon, arcs up through the top of the sky, sets at the right —   */
/* one continuous pass along an ellipse, looping. Positioned/scaled like .click-flash earlier: */
/* zero left/top anchor + translate(-50%,-50%), so it's centered on wherever the path puts it. */
/* The ellipse's west point (offset-distance 50%) is the rise (left, low); its top/north point */
/* (75%) is the peak of the arc; its east point (100%) is the set (right, low) — see the       */
/* angle convention already established for the carousel's own offset-path in js/mountains.js. */
.genre-moon {
  position: fixed;
  left: 0;
  top: 0;
  width: 80px;
  height: 80px;
  border-radius: 50%;
  background-image: url('../img/mountain/illust/moon.png');
  background-size: cover;
  opacity: 0;
  translate: -50% -50%;
  offset-path: ellipse(45% 45% at 50% 50%);
  offset-rotate: 0deg;
  offset-distance: 60%;
  animation: moonArc 120s linear infinite;
  z-index: 1; /* behind .genre-bg (2) — the mountain line art draws over the moon */
  pointer-events: none;
  transition: opacity 1.2s ease;
}

/* Arriving on the page: fades up to its resting 0.6 opacity — see .is-visible toggled in     */
/* js/mountains.js (the same rAF-delayed pattern used for .genre-stage). */
.genre-moon.is-visible {
  opacity: 0.6;
}

/* Leaving for a category's detail page: the moon just fades out in place (its orbit keeps   */
/* running underneath — animation and transition are independent — it just stops being seen). */
.genre-moon.is-leaving {
  opacity: 0;
}

@keyframes moonArc {
  from { offset-distance: 50%; }
  to   { offset-distance: 100%; }
}

.genre-close {
  position: fixed;
  top: 2rem;
  right: 2rem;
  z-index: 20;
  font-family: 'Jost', sans-serif;
  font-size: 0.7rem;
  letter-spacing: 0.25em;
  text-transform: uppercase;
  color: #ff0000;
  text-decoration: none;
  opacity: 0.55;
  transition: opacity 0.2s ease;
}

.genre-close:hover {
  opacity: 1;
}

/* ---------- Genre info: title + description text ---------- */
/* Spans from just right of the focused photo's circle (--info-left, computed in            */
/* js/mountains.js) all the way to the right edge — left AND right are both set, so the box  */
/* stretches to fill exactly that gap rather than being a fixed width.                       */

.genre-info {
  position: fixed;
  left: var(--info-left, 60%);
  right: 4rem;
  bottom: 20%;
  z-index: 10;
  color: #16181c;
}

/* Swap animation: driven by js/mountains.js toggling .info-out / .info-in on .genre-info      */
/* (the same pos-below/pos-above/no-anim pattern used for the slide titles elsewhere on the     */
/* site — see .reveal above). Outgoing text fades left while blurring; the new text then snaps  */
/* in unblurred-but-invisible on the right (via .no-anim, so that snap itself isn't animated)    */
/* and transitions in to settled, sharpening as it goes. The body is delayed slightly behind    */
/* the title (both entering and leaving), so the two don't move as one flat block.               */
.genre-info__title {
  transition: opacity 0.4s ease, transform 0.4s ease, filter 0.4s ease;
}

.genre-info__body {
  transition: opacity 0.4s ease 0.1s, transform 0.4s ease 0.1s, filter 0.4s ease 0.1s;
}

.genre-info.info-out .genre-info__title,
.genre-info.info-out .genre-info__body {
  opacity: 0;
  transform: translateX(-28px);
  filter: blur(10px);
}

.genre-info.info-in .genre-info__title,
.genre-info.info-in .genre-info__body {
  opacity: 0;
  transform: translateX(28px);
  filter: blur(10px);
}

.genre-info__title {
  font-family: 'Cormorant Garamond', serif;
  font-size: clamp(1.4rem, 2.2vw, 2rem);
  letter-spacing: 0.03em;
  margin-bottom: 1.4rem;
}

.genre-info__title-en {
  display: block;
  margin-top: 0.35em;
  font-family: 'Jost', sans-serif;
  font-weight: 300;
  font-size: 0.55em;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(22, 24, 28, 0.5);
}

.genre-info__body {
  font-family: 'Jost', sans-serif;
  font-weight: 300;
  font-size: clamp(0.75rem, 1vw, 0.85rem);
  line-height: 2;
  letter-spacing: 0.02em;
  color: rgba(22, 24, 28, 0.62);
}

.genre-info__body--en {
  margin-top: 0.9rem;
  color: rgba(22, 24, 28, 0.45);
}

.genre-stage {
  position: fixed;
  inset: 0;
  z-index: 5;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.6s ease;
}

.genre-stage.is-visible {
  opacity: 1;
}

.genre-carousel {
  position: absolute;
  inset: 0;
}

/* All three visible items travel along the same circular path — its center/radius (and each   */
/* slot's position along it) are computed from real viewport pixels in js/mountains.js, so the  */
/* arc always passes through the same on-screen spots regardless of viewport size.              */
/* .genre-carousel__item is the path-follower: zero-size, so the path point IS its position,     */
/* with no anchor ambiguity. .genre-carousel__frame is the visible box, centered and scaled on   */
/* that point via an ordinary translate(-50%,-50%) + scale — the same reliable pattern used for  */
/* .click-flash / .reveal-white, rather than relying on offset-path's own (poorly-supported)     */
/* anchor/box-sizing behavior.                                                                   */
.genre-carousel__item {
  position: fixed;
  left: 0;
  top: 0;
  width: 0;
  height: 0;
  offset-path: circle(var(--orbit-r, 300px) at var(--orbit-cx, 50%) var(--orbit-cy, 50%));
  offset-rotate: 0deg;
  transition: offset-distance 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}

.genre-carousel__frame {
  position: absolute;
  left: 0;
  top: 0;
  width: clamp(180px, 15vw, 300px);
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  overflow: hidden;
  cursor: pointer;
  pointer-events: auto;
  /* --scatter-x/-y are 0px normally; only .is-leaving (below) sets them, to fling each photo   */
  /* off in its own direction. They're added inside the -50%/-50% centering (not appended after */
  /* it), so they're a plain px offset unaffected by --item-scale. */
  transform: translate(calc(-50% + var(--scatter-x, 0px)), calc(-50% + var(--scatter-y, 0px))) translateX(var(--mobile-slot-x, 0px)) scale(var(--item-scale, 1));
  transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.6s ease, filter 0.6s ease;
}

.genre-carousel__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.genre-carousel__item.slot-hidden {
  offset-distance: var(--dist-focused, 50%);
}

.genre-carousel__item.slot-hidden .genre-carousel__frame {
  --item-scale: 0.2;
  opacity: 0;
  pointer-events: none;
}

.genre-carousel__item.slot-prev2 {
  offset-distance: var(--dist-prev2, 30%);
  /* z-index goes on THIS element (not the .genre-carousel__frame below): each item is
     position:fixed, which always establishes its own stacking context, so a z-index set
     only on the descendant frame can't reorder sibling items — it only stacks within its
     own item, and siblings then fall back to DOM order regardless of "z-index" on the frame.
     That was letting later-in-DOM dimmed neighbors paint over the focused photo. */
  z-index: 1;
}

.genre-carousel__item.slot-prev2 .genre-carousel__frame {
  --item-scale: 0.75;
  opacity: 0.15;
  filter: brightness(0.45);
}

.genre-carousel__item.slot-prev {
  offset-distance: var(--dist-prev, 40%);
  z-index: 2;
}

.genre-carousel__item.slot-prev .genre-carousel__frame {
  --item-scale: 1.2;
  opacity: 0.55;
  filter: brightness(0.6);
}

.genre-carousel__item.slot-focused {
  offset-distance: var(--dist-focused, 50%);
  z-index: 3;
}

.genre-carousel__item.slot-focused .genre-carousel__frame {
  --item-scale: 1.9;
  opacity: 1;
  filter: brightness(1);
  cursor: pointer; /* clicking the already-focused photo opens that category (see leaveToDetail) */
}

.genre-carousel__item.slot-next {
  offset-distance: var(--dist-next, 60%);
  z-index: 2;
}

.genre-carousel__item.slot-next .genre-carousel__frame {
  --item-scale: 1.2;
  opacity: 0.55;
  filter: brightness(0.6);
}

.genre-carousel__item.slot-next2 {
  offset-distance: var(--dist-next2, 70%);
  z-index: 1;
}

.genre-carousel__item.slot-next2 .genre-carousel__frame {
  --item-scale: 0.75;
  opacity: 0.15;
  filter: brightness(0.45);
}

/* Leaving for a category's detail page (see js/mountains.js leaveToDetail()): every visible   */
/* photo flings outward in its own direction while fading out — a scatter/explosion instead of */
/* the usual settle-into-slot motion. Each slot gets a different --scatter-x/-y (consumed by    */
/* .genre-carousel__frame's transform above) so they don't all move the same way.               */
.genre-stage.is-leaving .genre-carousel__frame {
  transition-duration: 0.8s;
}

.genre-stage.is-leaving .genre-carousel__item.slot-prev2 .genre-carousel__frame {
  --scatter-x: -260px;
  --scatter-y: -220px;
  opacity: 0;
}

.genre-stage.is-leaving .genre-carousel__item.slot-prev .genre-carousel__frame {
  --scatter-x: -380px;
  --scatter-y: 160px;
  opacity: 0;
}

.genre-stage.is-leaving .genre-carousel__item.slot-focused .genre-carousel__frame {
  --scatter-x: 30px;
  --scatter-y: -440px;
  opacity: 0;
}

.genre-stage.is-leaving .genre-carousel__item.slot-next .genre-carousel__frame {
  --scatter-x: 360px;
  --scatter-y: -100px;
  opacity: 0;
}

.genre-stage.is-leaving .genre-carousel__item.slot-next2 .genre-carousel__frame {
  --scatter-x: 320px;
  --scatter-y: 340px;
  opacity: 0;
}

/* ---------- Detail page (mountains/*.html): one photo at a time, prev/next peeking ---------- */
/* left/right with a gap, title+description below. Mouse wheel moves through the set; text     */
/* fades+blurs in the direction of travel via .shift-left/.shift-right (js/detail.js decides    */
/* which is "exit" and which is "enter" based on which way you're navigating).                 */

body.detail-page {
  background: #f2f2f0;
  color: #16181c;
  opacity: 0;
  transition: opacity 0.6s ease;
}

body.detail-page.is-visible {
  opacity: 1;
}

.detail-stage {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
}

.detail-track {
  position: relative;
  width: 100%;
  height: 100%;
}

.detail-item {
  position: absolute;
  left: 50%;
  top: var(--detail-item-cy, 40%); /* size/position computed in js/detail.js so the photo, */
  width: var(--detail-item-w, 65%); /* bars and info stack without overlapping            */
  aspect-ratio: 3 / 2;
  overflow: hidden;
  transform: translate(-50%, -50%) translateX(var(--slot-x, 0)) scale(var(--slot-scale, 1));
  opacity: var(--slot-opacity, 0);
  filter: brightness(var(--slot-brightness, 1));
  transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.6s ease, filter 0.6s ease;
  pointer-events: none;
}

.detail-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.detail-item.slot-hidden {
  --slot-x: 0vw;
  --slot-scale: 0.4;
  --slot-opacity: 0;
}

/* prev2/next2 are the same dimmed look as prev/next, just parked further off-canvas — see the  */
/* note on SLOT_CLASS in js/detail.js for why: it's what makes switching read as a pure scroll  */
/* instead of a fade. */
.detail-item.slot-prev2 {
  --slot-x: calc(-2 * (var(--detail-item-w, 65vw) + 4vw) - 10vw);
  --slot-scale: 1;
  --slot-opacity: 0.2;
  --slot-brightness: 0.6;
}

.detail-item.slot-prev {
  --slot-x: calc(-1 * (var(--detail-item-w, 65vw) + 4vw));
  --slot-scale: 1;
  --slot-opacity: 0.2;
  --slot-brightness: 0.6;
  pointer-events: auto;
  cursor: pointer;
  z-index: 1;
}

.detail-item.slot-focused {
  --slot-x: 0vw;
  --slot-scale: 1;
  --slot-opacity: 1;
  --slot-brightness: 1;
  z-index: 2;
}

.detail-item.slot-next {
  --slot-x: calc(var(--detail-item-w, 65vw) + 4vw);
  --slot-scale: 1;
  --slot-opacity: 0.2;
  --slot-brightness: 0.6;
  pointer-events: auto;
  cursor: pointer;
  z-index: 1;
}

.detail-item.slot-next2 {
  --slot-x: calc(2 * (var(--detail-item-w, 65vw) + 4vw) + 10vw);
  --slot-scale: 1;
  --slot-opacity: 0.2;
  --slot-brightness: 0.6;
}

.detail-info {
  position: fixed;
  left: 0;
  right: 0;
  top: var(--detail-info-top, 70vh); /* computed in js/detail.js from the image's own bottom
                                        edge, so this tracks it instead of a fixed viewport % */
  z-index: 3;
  text-align: center;
  color: #16181c;
  pointer-events: none;
}

.detail-info__title {
  font-family: 'Cormorant Garamond', serif;
  font-size: clamp(1rem, 2vw, 1.3rem);
  letter-spacing: 0.03em;
  margin-bottom: 0.8rem;
  transition: opacity 0.4s ease, transform 0.4s ease, filter 0.4s ease;
  line-height: 1;
}

.detail-info__title-en {
  display: block;
  margin-top: 0.35em;
  font-family: 'Jost', sans-serif;
  font-weight: 300;
  font-size: 0.55em;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(22, 24, 28, 0.5);
}

.detail-info__desc {
  font-family: 'Jost', sans-serif;
  font-weight: 300;
  font-size: clamp(0.7rem, 0.9vw, 0.8rem);
  line-height: 1;
  letter-spacing: 0.02em;
  color: rgba(22, 24, 28, 0.62);
  transition: opacity 0.4s ease 0.08s, transform 0.4s ease 0.08s, filter 0.4s ease 0.08s;
}

.detail-info__desc--en {
  margin-top: 0.3rem;
  color: rgba(22, 24, 28, 0.42);
}

.detail-info.shift-left .detail-info__title,
.detail-info.shift-left .detail-info__desc {
  opacity: 0;
  transform: translateX(-28px);
  filter: blur(10px);
}

.detail-info.shift-right .detail-info__title,
.detail-info.shift-right .detail-info__desc {
  opacity: 0;
  transform: translateX(28px);
  filter: blur(10px);
}

/* ---------- Detail page, fade variant (currently just mountains/omote-ginza.html, as a ---------- */
/* trial before rolling out to the rest): every photo sits in the exact same spot — switching   */
/* is a pure opacity crossfade, no sliding at all. Only the focused one is visible; the rest     */
/* (still positioned/sized identically) sit at opacity 0 underneath it. */

.detail-page--fade .detail-item {
  --slot-x: 0vw;
  --slot-scale: 1;
}

.detail-page--fade .detail-item.slot-prev2,
.detail-page--fade .detail-item.slot-prev,
.detail-page--fade .detail-item.slot-next,
.detail-page--fade .detail-item.slot-next2 {
  --slot-opacity: 0;
  pointer-events: none;
}

/* ---------- Progress bars: one per photo, the current one picked out in red ---------- */

.detail-bars {
  position: fixed;
  left: 50%;
  top: var(--detail-bars-top, calc(100% - 2rem)); /* just below the photo, see js/detail.js */
  transform: translateX(-50%);
  z-index: 5;
  display: flex;
  gap: 5px;
}

.detail-bar {
  width: 14px;
  height: 3px;
  background: rgba(22, 24, 28, 0.18);
  transition: background-color 0.3s ease;
}

.detail-bar.is-active {
  background: #ff1900;
}

/* ---------- Category label: plain horizontal text, just rotated 90° — not CSS vertical ---------- */
/* writing-mode, so the characters themselves stay sideways rather than being set upright.      */
/* The trailing translateY(-100%) shifts the box so that, after rotating, `left` is the text's  */
/* own left edge (rather than its right edge), so it can never run off the screen however tall */
/* the font's line box turns out to be. */

.detail-category {
  position: fixed;
  left: 0.4rem;
  top: 5%;
  transform-origin: left top;
  transform: translateY(-50%) rotate(90deg) translateY(-100%);
  font-family: 'Jost', sans-serif;
  font-weight: 300;
  font-size: 1.75rem;
  line-height: 1.2;
  letter-spacing: 0.3em;
  color: rgba(22, 24, 28, 0.4);
  white-space: nowrap;
  z-index: 5;
  pointer-events: none;
}

/* ---------- Arrows: sit right at the photo's own left/right edge (positions computed in ---------- */
/* js/detail.js's sizeDetailLayout(), so they track the image regardless of viewport size),      */
/* rather than pinned to the screen edges. Hidden automatically at the first/last photo.         */

.detail-arrow {
  position: fixed;
  top: var(--detail-arrow-top, 50%);
  z-index: 4;
  border: none;
  background: none;
  padding: 0 0.4rem;
  font-family: 'Cormorant Garamond', serif;
  font-weight: 300;
  font-size: 3rem;
  line-height: 1;
  color: #16181c;
  opacity: 0.35;
  cursor: pointer;
  transition: opacity 0.25s ease;
}

.detail-arrow:hover {
  opacity: 0.85;
}

.detail-arrow.is-hidden {
  opacity: 0;
  pointer-events: none;
}

.detail-arrow--prev {
  left: var(--detail-arrow-prev-left, 1rem);
  transform: translate(-100%, -50%);
}

.detail-arrow--next {
  left: var(--detail-arrow-next-left, calc(100% - 1rem));
  transform: translate(0, -50%);
}

/* ---------- Detail page top-right nav: menu icon (genre list) + close icon (X/CLOSE) ---------- */
/* The genre list is driven by .is-open (toggled in detail.js on mouseenter/mouseleave, with a */
/* grace-period timeout on leave) rather than plain :hover, so briefly crossing the gap between */
/* the icon and the list — or any other genre item — doesn't close it early. :focus-within is   */
/* kept alongside as the keyboard-only path, which doesn't have that gap problem. The close     */
/* icon has no such list to reach, so it stays a plain :hover/:focus-visible below.             */

.detail-nav {
  position: fixed;
  top: 2rem;
  right: 2rem;
  z-index: 20;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1.1rem;
}

.detail-nav__menu-wrap {
  position: relative;
  order: 2;
}

.detail-nav__menu {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 26px;
  height: 20px;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}

.detail-nav__menu-line {
  display: block;
  width: 100%;
  height: 1px;
  background: #16181c;
  opacity: 0.55;
  transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.3s ease;
}

.detail-nav__menu-wrap.is-open .detail-nav__menu-line,
.detail-nav__menu-wrap:focus-within .detail-nav__menu-line {
  opacity: 0;
}

.detail-nav__menu-wrap.is-open .detail-nav__menu-line:nth-child(1),
.detail-nav__menu-wrap:focus-within .detail-nav__menu-line:nth-child(1) {
  transform: translate(10px, -8px) rotate(15deg);
}

.detail-nav__menu-wrap.is-open .detail-nav__menu-line:nth-child(2),
.detail-nav__menu-wrap:focus-within .detail-nav__menu-line:nth-child(2) {
  transform: translateX(16px);
  transition-delay: 0.04s;
}

.detail-nav__menu-wrap.is-open .detail-nav__menu-line:nth-child(3),
.detail-nav__menu-wrap:focus-within .detail-nav__menu-line:nth-child(3) {
  transform: translate(10px, 8px) rotate(-15deg);
  transition-delay: 0.08s;
}

/* Genre list: anchored just below the hamburger icon regardless of the nav's own layout.    */
/* Each item slides/fades in from off-screen right individually — --i (set in detail.js) is   */
/* its top-to-bottom order, so a larger transition-delay on entry cascades top to bottom, and  */
/* --ri (its mirror, count-1 - i) does the same in reverse on exit, cascading bottom to top.   */
.detail-nav__genres {
  position: absolute;
  top: calc(100% + 0.9rem);
  right: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.7rem;
  pointer-events: none;
}

.detail-nav__menu-wrap.is-open .detail-nav__genres,
.detail-nav__menu-wrap:focus-within .detail-nav__genres {
  pointer-events: auto;
}

.detail-nav__genres-item {
  font-family: 'Jost', sans-serif;
  font-size: 1.0rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: #16181c;
  text-decoration: none;
  white-space: nowrap;
  opacity: 0;
  transform: translateX(40px);
  transition: opacity 0.3s ease, transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
  transition-delay: calc(var(--ri, 0) * 0.04s);
}

/* Current category: the page you're already on, shown in the list (not a link) in red. */
.detail-nav__genres-item.is-current {
  color: #ff1900;
  cursor: default;
}

.detail-nav__menu-wrap.is-open .detail-nav__genres-item,
.detail-nav__menu-wrap:focus-within .detail-nav__genres-item {
  opacity: 0.6;
  transform: translateX(0);
  transition-delay: calc(var(--i, 0) * 0.04s);
}

.detail-nav__menu-wrap.is-open .detail-nav__genres-item:hover,
.detail-nav__menu-wrap:focus-within .detail-nav__genres-item:focus {
  opacity: 1;
}

/* Close: X icon and CLOSE text share the same spot, swapped via opacity so hovering reads as */
/* the X breaking apart into nothing while the red text fades in behind it.                   */
.detail-nav__close {
  position: relative;
  order: 1;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  width: 72px;
  height: 22px;
  text-decoration: none;
}

.detail-nav__close-icon {
  position: absolute;
  top: 50%;
  right: 3px;
  width: 24px;
  height: 24px;
  transform: translateY(-50%);
  opacity: 0; /* the X/CLOSE crossfade only makes sense where :hover exists to trigger it — see
                 the (hover: hover) override below; touch devices just get plain CLOSE text */
  transition: opacity 0.25s ease;
}

@media (hover: hover) {
  .detail-nav__close-icon {
    opacity: 1;
  }
}

.detail-nav__close-line {
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: 1px;
  background: #16181c;
  opacity: 0.55;
  transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.3s ease;
}

.detail-nav__close-line--a {
  transform: rotate(45deg);
}

.detail-nav__close-line--b {
  transform: rotate(-45deg);
}

.detail-nav__close-text {
  position: absolute;
  top: 50%;
  right: 0;
  transform: translateY(-50%);
  font-family: 'Jost', sans-serif;
  font-size: 0.7rem;
  letter-spacing: 0.25em;
  text-transform: uppercase;
  color: #ff1900;
  opacity: 1;
  transition: opacity 0.3s ease;
}

@media (hover: hover) {
  .detail-nav__close-text {
    opacity: 0;
  }
}

.detail-nav__close:hover .detail-nav__close-icon,
.detail-nav__close:focus-visible .detail-nav__close-icon {
  opacity: 0;
}

.detail-nav__close:hover .detail-nav__close-line--a,
.detail-nav__close:focus-visible .detail-nav__close-line--a {
  transform: translate(-6px, -6px) rotate(25deg);
  opacity: 0;
}

.detail-nav__close:hover .detail-nav__close-line--b,
.detail-nav__close:focus-visible .detail-nav__close-line--b {
  transform: translate(-6px, 6px) rotate(-25deg);
  opacity: 0;
}

.detail-nav__close:hover .detail-nav__close-text,
.detail-nav__close:focus-visible .detail-nav__close-text {
  opacity: 1;
  transition-delay: 0.1s;
}

/* ---------- Flowers page (flowers.html): 9 scenes as a static 3x3 grid ---------- */
/* No carousel here — every tile is on screen at once, all rising+fading in together on load   */
/* (see js/flowers.js).                                                                        */

.flowers-grid {
  position: fixed;
  inset: 0;
  z-index: 3;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  gap: 3rem;
  padding: 6rem 6rem 6rem;
}

.flowers-grid__item {
  display: flex;
  flex-direction: column;
  min-height: 0; /* without this, the label's content size pushes the row past its 1fr share
                    and the bottom row overflows the viewport (no scroll on this site) */
  cursor: pointer;
  opacity: 0;
  transform: translateY(50px);
  transition: opacity 3s ease, transform 3s cubic-bezier(0.22, 1, 0.36, 1);
}

.flowers-grid__item.is-in {
  opacity: 1;
  transform: translateY(0);
}

.flowers-grid__item img {
  flex: 1;
  min-height: 0;
  width: 100%;
  object-fit: cover;
  display: block;
  opacity: 1; /* dimmed-by-default only makes sense as a hover reveal — see the (hover: hover)
                 override right below; touch devices just see the photo at full strength */
  transition: opacity 0.35s ease, transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}

@media (hover: hover) {
  .flowers-grid__item img,
  .snapshots-grid__item img {
    opacity: 0.3;
  }
}

.flowers-grid__item:hover img {
  opacity: 1;
  transform: scale(1.03);
}

.flowers-grid__label {
  margin-top: 0.6rem;
  text-align: center;
  font-family: 'Jost', sans-serif;
  font-weight: 300;
  font-size: 0.85rem;
  letter-spacing: 0.25em;
  text-transform: uppercase;
  color: #16181c;
}

/* Each character (see flowers.js) starts hidden, offset toward its own side, and only reveals */
/* on hover/focus of the tile; leaving just reverses the same transition.                      */
.flowers-grid__label .fchar {
  display: inline-block;
  opacity: 1; /* visible-by-default; the split left/right hidden state below only applies where
                 hover exists to reveal it — touch devices just see the plain label, no animation */
  transition: opacity 0.3s ease, transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}

@media (hover: hover) {
  .flowers-grid__label .fchar {
    opacity: 0;
  }

  .flowers-grid__label .fchar--left {
    transform: translateX(-12px);
  }

  .flowers-grid__label .fchar--right {
    transform: translateX(12px);
  }
}

.flowers-grid__item:hover .fchar,
.flowers-grid__item:focus-within .fchar {
  opacity: 1;
  transform: translateX(0);
}

/* ---------- Snapshots page (snapshots.html): same idea as flowers.html, 6 folders as a ---------- */
/* static 3x2 grid — see js/snapshots.js.                                                      */

.snapshots-grid {
  position: fixed;
  inset: 0;
  z-index: 3;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(2, 1fr);
  gap: 3rem;
  padding: 6rem 6rem 6rem;
}

.snapshots-grid__item {
  display: flex;
  flex-direction: column;
  min-height: 0; /* see the identical note on .flowers-grid__item — same overflow trap */
  cursor: pointer;
  opacity: 0;
  transform: translateY(50px);
  transition: opacity 3s ease, transform 3s cubic-bezier(0.22, 1, 0.36, 1);
}

.snapshots-grid__item.is-in {
  opacity: 1;
  transform: translateY(0);
}

.snapshots-grid__item img {
  flex: 1;
  min-height: 0;
  width: 100%;
  object-fit: cover;
  display: block;
  opacity: 1;
  transition: opacity 0.35s ease, transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}

.snapshots-grid__item:hover img {
  opacity: 1;
  transform: scale(1.03);
}

.snapshots-grid__label {
  margin-top: 0.6rem;
  text-align: center;
  font-family: 'Jost', sans-serif;
  font-weight: 300;
  font-size: 0.85rem;
  letter-spacing: 0.25em;
  text-transform: uppercase;
  color: #16181c;
}

.snapshots-grid__label .fchar {
  display: inline-block;
  opacity: 1;
  transition: opacity 0.3s ease, transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}

@media (hover: hover) {
  .snapshots-grid__label .fchar {
    opacity: 0;
  }

  .snapshots-grid__label .fchar--left {
    transform: translateX(-12px);
  }

  .snapshots-grid__label .fchar--right {
    transform: translateX(12px);
  }
}

.snapshots-grid__item:hover .fchar,
.snapshots-grid__item:focus-within .fchar {
  opacity: 1;
  transform: translateX(0);
}

/* ==================== Mobile layout (phones/narrow viewports) ==================== */
/* Everything above this point is unchanged for desktop; this block only fires under   */
/* max-width: 767px, and only touches layout (positioning/sizing) — the hover-vs-touch  */
/* interaction fallbacks above are a separate (hover: hover)/(hover: none) axis so a     */
/* touch laptop with a big screen still gets tap-friendly interactions.                 */
@media (max-width: 767px) {

  /* ---------- TOP page: enlarge the ripple-btn's tap area without changing how the tiny --------- */
  /* dot looks. box-sizing:border-box (global) means width/height include the padding, so a  */
  /* 40px box with 14px padding still leaves a 12px content box — background-clip keeps the   */
  /* painted dot exactly that size, and the ::before/::after rings are pulled in by the same   */
  /* 14px so they still trace the original 12px circle instead of the new 40px hit-box. */
  .ripple-btn {
    width: 40px;
    height: 40px;
    padding: 14px;
    background-clip: content-box;
  }

  .ripple-btn::before,
  .ripple-btn::after {
    inset: 14px;
  }

  /* ---------- TOP page: on mobile only, the persistent brand mark (Atsushi Fukuda --------- */
  /* Photography) moves from its PC spot (fixed, left-aligned, vertically centered) to sit  */
  /* centered directly below the ripple-btn circle instead — there's not enough horizontal  */
  /* room on a phone screen for it to live off to the side without crowding the title. */
  .brand-title--left {
    top: calc(50% + 120px);
    left: 50%;
    transform: translateX(-50%);
    text-align: center;
  }

  .brand-title--left .reveal {
    align-items: center;
  }

  .brand-title--left .brand-title__sub {
    padding-left: 0; /* the PC version's optical-centering nudge is wrong once this is centered */
  }

  /* ---------- Genre background illustration (mountains.html, etc.): the desktop rule sizes --------- */
  /* this to 100% width with a fixed -150px vertical offset, tuned for a wide landscape        */
  /* viewport. On a narrow portrait phone the same width-based scaling makes the image much     */
  /* shorter, so that fixed offset pushes nearly all of it above the screen, leaving a bare     */
  /* sliver up top and blank background below. `cover` fills the viewport at any aspect ratio    */
  /* instead, same as the hero slide backgrounds already do (.slide__bg). */
  .genre-bg {
    background-size: cover;
    background-position: center 30%;
  }

  /* ---------- Genre carousel (mountains.html): the desktop version sweeps photos along a --------- */
  /* circular offset-path with an info panel to its right — too wide a shape for a phone. Reuses  */
  /* the exact same slot classes js/mountains.js already assigns (no JS changes) but positions    */
  /* them as a simple left/right peek carousel instead, the same pattern .detail-item already     */
  /* uses on detail pages. --mobile-slot-x feeds into .genre-carousel__frame's transform above     */
  /* (default 0px there, so desktop — which never sets this variable — is unaffected). */
  .genre-carousel__item {
    offset-path: none;
    position: fixed;
    left: 50%;
    top: 30%;
  }

  .genre-carousel__item.slot-prev2 { --mobile-slot-x: -140vw; }
  .genre-carousel__item.slot-prev { --mobile-slot-x: -62vw; }
  .genre-carousel__item.slot-focused { --mobile-slot-x: 0px; }
  .genre-carousel__item.slot-next { --mobile-slot-x: 62vw; }
  .genre-carousel__item.slot-next2 { --mobile-slot-x: 140vw; }

  /* The desktop rule above sizes this off viewport WIDTH (clamp(180px,15vw,300px)), which is     */
  /* fine on a wide landscape screen but has no relationship to how much vertical room a portrait  */
  /* phone actually has. Sizing off viewport HEIGHT instead means the photo — and therefore the    */
  /* carousel's total footprint — scales down automatically on short phones (old iPhone SE class)  */
  /* and up on tall ones (Pixel/Pro Max class), so it never has to be re-tuned per device: a fixed  */
  /* px size tuned to look right on one height is exactly what caused it to collide with the info   */
  /* text block below on shorter screens before. */
  .genre-carousel__frame {
    width: clamp(150px, 27vh, 235px);
  }

  .genre-info {
    left: 1.5rem;
    right: 1.5rem;
    bottom: 5%;
    text-align: center;
  }

  .genre-info__title {
    margin-bottom: 0.8rem;
  }

  .genre-info__body {
    line-height: 1.5;
  }

  .genre-info__body--en {
    margin-top: 0.5rem;
  }

  /* ---------- Flowers / Snapshots grids: the only pages on the site that need real vertical --------- */
  /* scrolling (more tiles than fit one screen at a readable size) — scoped to just these two    */
  /* elements switching out of the fixed 3x3/3x2 grid into a plain scrollable single column, so   */
  /* the rest of the site's fixed-viewport, wheel/tap-only navigation is untouched. */
  .flowers-grid,
  .snapshots-grid {
    /* position:fixed + inset:0 (from the base rule above) already pins this to exactly the
       viewport size — switching display to block (single column) and adding overflow-y turns
       that fixed box into its own internal scroll container. Using min-height instead of this
       fixed sizing would let the box grow to fit all the content, and then there'd be nothing
       left to scroll. */
    display: block;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 5rem 1.5rem 3rem;
  }

  .flowers-grid__item,
  .snapshots-grid__item {
    display: block;
    margin-bottom: 2.5rem;
  }

  .flowers-grid__item:last-child,
  .snapshots-grid__item:last-child {
    margin-bottom: 0;
  }

  .flowers-grid__item img,
  .snapshots-grid__item img {
    aspect-ratio: 3 / 2;
  }

  /* ---------- Detail pages (every mountains/flowers/snapshots detail page): a wider photo --------- */
  /* fills more of a tall portrait screen instead of leaving dead space above/below it — keep in  */
  /* sync with DETAIL_WIDTH_PCT_MOBILE in js/detail.js. */
  .detail-item {
    width: var(--detail-item-w, 88%);
  }

  .detail-category {
    font-size: 1.1rem;
    left: 0.25rem;
  }

  .detail-arrow {
    padding: 0.9rem 0.7rem;
  }

  /* ---------- Progress bars: one per photo. Desktop galleries top out around a dozen photos, --------- */
  /* so the row (14px + 5px gap each) never gets close to viewport width. Mobile galleries run    */
  /* up to ~29 photos, where that same sizing produces a row wider than the screen — centered via  */
  /* left:50%/translateX(-50%), it overflowed equally off BOTH edges, cutting off the first and    */
  /* last several dots entirely. Shrinking each dot keeps the full count on-screen even at the     */
  /* largest galleries (29 * 9px - 3px = 258px, comfortably under a 320px-wide screen). */
  .detail-bars {
    gap: 3px;
    max-width: calc(100vw - 2rem);
    overflow: hidden;
  }

  .detail-bar {
    width: 6px;
  }
}

/* ---------- Flowers / Snapshots grids, phone held sideways: the fixed one-screen 3x3 / 3x2 --------- */
/* grid squashes every tile to a thin strip on a ~400px-tall screen, so here the grid keeps its */
/* 3 columns but each photo keeps a 3:2 shape and the grid scrolls vertically instead.          */
/* (Placed after the max-width: 767px block so it also overrides that block's single column.)  */
@media (max-height: 500px) and (orientation: landscape) {
  .flowers-grid,
  .snapshots-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: none;
    grid-auto-rows: auto;
    align-content: start;
    gap: 1.75rem 2rem;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 4.5rem 3rem 3rem;
  }

  /* Plain block (not the base rule's flex column): with flex, the row height was worked out   */
  /* before the 3:2 photo height, so rows came out too short and the tiles overlapped.          */
  .flowers-grid__item,
  .snapshots-grid__item {
    display: block;
    min-height: auto;
    margin-bottom: 0;
  }

  .flowers-grid__item img,
  .snapshots-grid__item img {
    width: 100%;
    height: auto;
    aspect-ratio: 3 / 2;
  }

  .flowers-grid__label,
  .snapshots-grid__label {
    display: block;
  }
}

@media (max-width: 767px), (max-height: 500px) {
  /* ---------- Genre dropdown (hamburger menu): on desktop this opens as bare text over --------- */
  /* whatever's behind it, which reads fine against the mostly-empty area to the right of the     */
  /* photo there. On mobile the photo runs nearly edge-to-edge, so that same bare text opens      */
  /* directly on top of it — a solid card behind the list keeps it readable and makes the open     */
  /* state read as an intentional menu instead of broken overlap. Kept in its own query (not    */
  /* just max-width: 767px) so a phone held sideways, which is wider than that, gets it too.     */
  .detail-nav__genres {
    max-width: calc(100vw - 2.5rem);
    padding: 1rem 1.1rem;
    background: rgba(242, 242, 240, 0.96);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
    overflow: hidden;
    /* The card itself (background/shadow/padding) has to fade with the list, not just the text  */
    /* inside it — the items already fade individually via their own opacity, but that leaves     */
    /* this panel's own background rendered at full strength underneath them the whole time,      */
    /* i.e. a white card sitting there even while "closed". */
    opacity: 0;
    transition: opacity 0.3s ease;
  }

  .detail-nav__menu-wrap.is-open .detail-nav__genres,
  .detail-nav__menu-wrap:focus-within .detail-nav__genres {
    opacity: 1;
  }

  .detail-nav__genres-item {
    font-size: 0.8rem;
    letter-spacing: 0.15em;
  }
}

/* ---------- Detail page category label, portrait screens: set as plain horizontal text in ---------- */
/* the top-left corner (level with the CLOSE link) instead of rotated down the left edge.       */
/* js/detail.js measures this label and keeps the photo below it.                               */
@media (orientation: portrait) {
  .detail-category {
    left: 1.25rem;
    top: 2rem;
    transform: none;
  }
}
