/* Home-page styles for The Color of Time.
   Pairs with the shared css/motion.css + js/motion.js. Everything generic (design tokens,
   [data-stage], [data-tilt] and the reduced-motion kill-switch) lives in
   motion.css; this file only holds the Home page's own tuning so the landing reads confident
   rather than busy. */

/* The hero's in-page jump links (#delta-e, #top) glide instead of snapping. */
html { scroll-behavior: smooth; }

/* Home leads with a calm hero, so its stage + card presentation settles a touch quicker and
   shallower than the shared defaults — the motion should feel assured, not theatrical. */
main [data-stage] { --motion-dur-stage: 980ms; }
main [data-tilt]  { --motion-dur-card: 840ms; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

/* ---------- grand entrance: "a century in three seconds" (once per page load) -----------
   A CSS-only timeline (no state change until it settles) so a stray re-render can never
   restart it: (1) the wordmark paints in fresh vermilion via a clip-path reveal (0-1.1s),
   (2) a second, sepia-toned copy of the same wordmark sweeps over it via a moving clip-path
   while a mono ΔE counter (a stack of 12 pre-rendered values, only one visible at a time)
   climbs 0.0 -> 24.7 (1.1-2.2s), (3) the veil lifts and the page beneath settles (2.2-2.8s).
   Reduced motion renders the settled (aged) frame statically and only cross-fades. */
.home-entrance {
  --entrance-fade-at: 2200ms;
  --entrance-gone-at: 2800ms;
  position: fixed; inset: 0; z-index: 500;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: clamp(14px, 2.4vw, 22px);
  background: var(--color-card-dark, #1B1712);
  opacity: 1; transform: none;
  transition: opacity .6s var(--motion-ease-presence, cubic-bezier(.16,1,.3,1)),
              transform .6s var(--motion-ease-presence, cubic-bezier(.16,1,.3,1));
}
/* The lift gains a whisper of scale so the veil reads as rising OFF the page, not sliding. */
.home-entrance.is-fading { opacity: 0; transform: translateY(-6%) scale(1.012); }

.home-entrance-eyebrow {
  font-family: var(--ff-mono); font-size: clamp(10px, 1.3vw, 12px);
  letter-spacing: .4em; text-transform: uppercase; color: rgba(237,228,210,.5);
  padding-left: .4em;
  animation: homeEntranceRise .7s var(--motion-ease-presence) both;
}

.home-entrance-mark-wrap { position: relative; display: grid; place-items: center; line-height: 1; }
.home-entrance-mark {
  grid-area: 1 / 1;
  font-family: var(--ff-display); font-weight: 600;
  font-size: clamp(44px, 9vw, 110px); line-height: .98; letter-spacing: -.02em;
  clip-path: inset(0 100% 0 0);
}
.home-entrance-mark em { font-style: italic; }
/* Two different curves on purpose: the fresh paint-in is a confident brush stroke (presence),
   while the aging veil that overtakes it is slow-then-inevitable (develop) — the same asymmetry
   the aging model itself has. */
.home-entrance-mark-fresh { color: var(--color-accent, #C8453B); animation: homeEntranceReveal 1.1s var(--motion-ease-presence, cubic-bezier(.16,1,.3,1)) both; }
.home-entrance-mark-fresh em { color: var(--color-accent, #C8453B); }
.home-entrance-mark-aged { color: #A9843F; animation: homeEntranceReveal 1.15s var(--motion-ease-develop, cubic-bezier(.3,.05,.25,1)) 1.1s both; }
.home-entrance-mark-aged em { color: #A9843F; }

.home-entrance-counter {
  display: flex; align-items: baseline; gap: 7px;
  font-family: var(--ff-mono); font-weight: 600;
  font-size: clamp(22px, 3.4vw, 34px); color: #EDE4D2;
  font-variant-numeric: tabular-nums;
}
.hec-stack { position: relative; display: inline-grid; min-width: 2.6em; }
.hec-frame {
  grid-area: 1 / 1; opacity: 0;
  animation-name: hecShow; animation-timing-function: steps(1, end);
  animation-duration: calc(1100ms / 12);
  animation-delay: calc(var(--i, 0) * (1100ms / 12) + 1100ms);
}
.hec-frame:nth-child(1)  { --i: 0; }
.hec-frame:nth-child(2)  { --i: 1; }
.hec-frame:nth-child(3)  { --i: 2; }
.hec-frame:nth-child(4)  { --i: 3; }
.hec-frame:nth-child(5)  { --i: 4; }
.hec-frame:nth-child(6)  { --i: 5; }
.hec-frame:nth-child(7)  { --i: 6; }
.hec-frame:nth-child(8)  { --i: 7; }
.hec-frame:nth-child(9)  { --i: 8; }
.hec-frame:nth-child(10) { --i: 9; }
.hec-frame:nth-child(11) { --i: 10; }
.hec-frame:nth-child(12) { --i: 11; animation-fill-mode: forwards; }
@keyframes hecShow { 0%, 100% { opacity: 1; } }
.hec-unit { font-size: .5em; color: #9C8F79; letter-spacing: .08em; }

.home-entrance-status {
  font-family: var(--ff-mono); font-size: clamp(10px, 1.2vw, 11.5px); letter-spacing: .1em;
  color: rgba(237,228,210,.55); text-align: center;
  animation: homeEntranceRise .7s var(--motion-ease-presence) 1.1s both;
}
.home-entrance-skip {
  position: absolute; left: 50%; bottom: clamp(20px, 4vh, 40px); transform: translateX(-50%);
  font-family: var(--ff-mono); font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase;
  color: rgba(237,228,210,.66);   /* 6.73:1 on the dark ground; breathe trough stays ≥ ~5:1 */
  /* rise in once, then breathe gently — an invitation, not an alarm */
  animation: homeEntranceRise .7s var(--motion-ease-presence) .3s both,
             homeSkipBreathe 3.2s var(--motion-ease-mechanical, cubic-bezier(.45,.05,.55,.95)) 1.2s infinite;
}
@keyframes homeSkipBreathe { 0%, 100% { opacity: 1; } 50% { opacity: .8; } }

@keyframes homeEntranceReveal { 0% { clip-path: inset(0 100% 0 0); } 100% { clip-path: inset(0 0 0 0); } }
@keyframes homeEntranceRise { 0% { opacity: 0; transform: translateY(6px); } 100% { opacity: 1; transform: none; } }

/* ---------- floating specimen cards: the vault (entrance) and the light (hero) ----------
   The same four masterworks appear twice. In the entrance they drift dimly upward through the
   dark — paintings asleep in storage. After the veil lifts they hang in the hero's 3D space at
   angles, each on its own float period and scrub depth. Both fields are decorative
   (aria-hidden, pointer-events none) and vanish below 1100px. */
.he-float-field { position: absolute; inset: 0; z-index: 0; pointer-events: none; perspective: 1200px; overflow: hidden; }
.home-entrance > :not(.he-float-field) { position: relative; z-index: 1; }
.he-float { position: absolute; animation: heFloatRise 3s var(--motion-ease-develop, cubic-bezier(.3,.05,.25,1)) both; }
.he-float img {
  display: block; width: 100%; border-radius:11px;
  border: 3px solid rgba(237, 228, 210, .12);
  filter: sepia(.4) brightness(.72) saturate(.8);
  box-shadow: 0 24px 48px -20px rgba(0, 0, 0, .8);
}
.hf-1 { width: clamp(130px, 13vw, 190px); top: 12%;   right: 7%;  rotate: .4 1 0 9deg;    translate: 0 0 -70px; --he-o: .30; }
.hf-2 { width: clamp(90px, 9vw, 130px);   bottom: 14%; left: 8%;   rotate: -.5 -.8 0 8deg; translate: 0 0 20px;  --he-o: .26; }
.hf-3 { width: clamp(70px, 7.5vw, 105px); top: 16%;   left: 13%;  rotate: .6 -.6 0 7deg;  translate: 0 0 -110px; --he-o: .20; }
.hf-4 { width: clamp(80px, 8.5vw, 120px); bottom: 18%; right: 15%; rotate: -.3 .9 0 8deg;  translate: 0 0 30px;  --he-o: .24; }
@keyframes heFloatRise {
  0%   { opacity: 0; transform: translateY(34px); }
  100% { opacity: var(--he-o, .26); transform: none; }
}

/* .hero-chapter-inner is position:sticky (declared above), so it is the containing block for
   this absolutely-positioned field. */
.hero-float-field { position: absolute; inset: 0; z-index: 0; pointer-events: none; perspective: 1200px; }
.hero-chapter-inner > *:not(.hero-float-field) { position: relative; z-index: 1; }
/* Base (no motion-path support): the original straight vertical drift. */
.hero-float { position: absolute; transform: translateY(calc(var(--scrub-progress, 0) * var(--hpf-drift, -40px))); }
.hero-float img {
  display: block; width: 100%; border-radius:12px;
  background: var(--color-card, #F7F1E3); padding: 4px;
  border: 1px solid rgba(34, 29, 23, .12);
  box-shadow: 0 20px 42px -22px rgba(34, 29, 23, .55);
  animation: galleryFloat 7.6s var(--motion-ease-mechanical, cubic-bezier(.45,.05,.55,.95)) infinite;
}
.hpf-1 { width: clamp(120px, 11vw, 165px); top: 3%;    right: -1.5%; rotate: .4 1 0 8deg;    translate: 0 0 -60px; --hpf-drift: -46px; --hpf-spin: -9deg; }
.hpf-2 { width: clamp(84px, 8vw, 118px);   bottom: 7%;  left: -1%;    rotate: -.5 -.8 0 7deg; translate: 0 0 26px;  --hpf-drift: 28px;  --hpf-spin: 8deg; }
.hpf-3 { width: clamp(64px, 6.5vw, 92px);  top: 7%;     left: -.5%;   rotate: .6 -.6 0 6deg;  translate: 0 0 -95px; --hpf-drift: -72px; --hpf-spin: 13deg; }
.hpf-4 { width: clamp(74px, 7.5vw, 104px); bottom: 12%; right: 4.5%;  rotate: -.3 .9 0 7deg;  translate: 0 0 34px;  --hpf-drift: 40px;  --hpf-spin: -11deg; }
.hpf-2 img { animation-duration: 8.8s; animation-delay: -3.1s; }
.hpf-3 img { animation-duration: 6.4s; animation-delay: -1.7s; }
.hpf-4 img { animation-duration: 9.6s; animation-delay: -5.2s; }
/* Scroll-driven swirls: each specimen leaves its corner and rides an authored bezier
   trajectory — a swoop, a climb, a loop-the-loop, a rising arc — through the middle of the
   scene, scrubbed by --scrub-progress (the scroll IS the timeline: no transitions here, ever).
   offset-* is transform-family (composited, no layout), so the motion.js INVARIANT holds.
   Every path starts at M 0 0 with the anchor at the element's own top-left, so progress 0 is
   EXACTLY the authored corner pose — no jump at rest. The paths are in local px, sized against
   the ≥1100px viewports this field renders on (max excursion ~560px inward, well inside the
   scene). The slow galleryFloat bob keeps breathing on the img inside, and the slot's 3D pose
   (independent rotate/translate) survives untouched; a gentle same-direction spin banks each
   card as it travels. Browsers without offset-anchor keep the straight drift above. */
@supports ((offset-path: path("M 0 0 L 1 1")) and (offset-anchor: left top)) {
  .hero-float {
    offset-anchor: left top;
    offset-rotate: 0deg;
    offset-distance: calc(var(--scrub-progress, 0) * 100%);
    transform: rotate(calc(var(--scrub-progress, 0) * var(--hpf-spin, 0deg)));
  }
  /* The trajectories thread the scene's NEGATIVE space — the open band above the headline,
     the left edge, the wide strip under the buttons — so a traveling card only ever grazes
     the copy for a beat instead of parking on it (content sits at z-index 1, but a card
     loitering under text still reads as clutter).
     top-right, largest plate: sweeps left across the top band, then curls down to tuck into
     the seam between the text column and the instrument's left edge. */
  .hpf-1 { offset-path: path("M 0 0 C -160 30, -360 80, -540 50 C -720 25, -680 180, -660 300"); }
  /* bottom-left: serpentine climb up the left edge — bowing inward under the buttons, back
     out along the lede's margin — then a swing right into the quiet pocket above the
     headline. */
  .hpf-2 { offset-path: path("M 0 0 C 130 -80, 150 -210, 40 -290 C -40 -350, -20 -450, 40 -510 C 100 -565, 180 -610, 270 -560"); }
  /* top-left, smallest and deepest: a small loop-the-loop held inside the pocket above the
     headline (the pocket is ~176px tall and the card ~100, so the loop's vertical travel
     must stay ≲76px or it lands on the display type), then a long glide right along the top
     band to peek over the instrument's shoulder. */
  .hpf-3 { offset-path: path("M 0 0 C 50 30, 80 58, 42 74 C 4 90, -16 45, 20 26 C 48 12, 130 8, 300 14 C 480 20, 580 36, 640 60"); }
  /* bottom-right: dives down-left and rides the bottom band beneath the buttons to
     settle low center-left. */
  .hpf-4 { offset-path: path("M 0 0 C -60 70, -200 100, -360 80 C -540 55, -640 30, -700 -20"); }
}
@media (max-width: 1100px) {
  .he-float-field, .hero-float-field { display: none; }
}

/* page beneath settling as the veil lifts (2.2-2.8s); pure CSS, tied to page-load only so no
   re-render can restart it — mirrors the overlay's own timing without any JS coordination. */
@keyframes homeEntranceSettle { 0% { transform: scale(1.02); } 100% { transform: scale(1); } }
.home-settle { animation: homeEntranceSettle .6s var(--motion-ease-presence) 2.2s both; transform-origin: 50% 38%; }

@media (prefers-reduced-motion: reduce) {
  .home-entrance { --entrance-fade-at: 60ms; --entrance-gone-at: 600ms; transition: opacity .5s linear !important; transform: none !important; }
  .home-entrance.is-fading { transform: none !important; }
  .home-entrance-eyebrow, .home-entrance-status, .home-entrance-skip { animation: none !important; opacity: 1 !important; transform: none !important; }
  .home-entrance-mark { animation: none !important; }
  .home-entrance-mark-fresh { clip-path: inset(0 100% 0 0) !important; }
  .home-entrance-mark-aged  { clip-path: inset(0 0 0 0) !important; }
  .hec-frame { animation: none !important; opacity: 0 !important; }
  .hec-frame:last-child { opacity: 1 !important; }
  .home-settle { animation: none !important; transform: none !important; }
  .home-entrance-skip { animation: none !important; opacity: 1 !important; }
  /* Under reduce the engine parks --scrub-progress at 1; scrub consumers must not rest in
     their scrolled pose — especially the swatch, which would read as permanently aged. */
  .hero-headline, .hero-instrument, .hero-lede,
  .gallery-card-slot { transform: none !important; rotate: none !important; translate: none !important; transition: none !important; }
  .gallery-card { animation: none !important; }
  .hero-swatch-scrub { filter: none !important; }
  /* Floating specimen fields: entrance copies vanish (its reduced frame stays minimal);
     hero copies stay as STATIC posed decor — the angle isn't motion, the drift and float are. */
  .he-float-field { display: none !important; }
  .hero-float { transform: none !important; offset-path: none !important; }
  .hero-float img { animation: none !important; }
  .cta-art-card { transition: none !important; }
  .cta-art-slot:hover .cta-art-card { transform: none !important; }
}

/* ---------- sticky hero chapter (scroll-scrubbed parallax) ----------
   Three depth rates, farthest moves most: headline -44px, instrument -28px, lede -16px.
   No transitions on any of these — the scroll position IS the timeline.
   260vh ≈ a 160vh pin: with the story split onto chapter pages the contents map is the only
   thing below, so the hero releases sooner — the swirls still read as a scene, just brisker
   (the scrub span is height + 100vh, so all consumers stretch automatically). */
.hero-chapter { height: 260vh; position: relative; }
.hero-chapter-inner { position: sticky; top: 0; min-height: 100vh; }
.hero-headline   { transform: translateY(calc(var(--scrub-progress, 0) * -44px)); }
.hero-instrument { transform: translateY(calc(var(--scrub-progress, 0) * -28px)); }
.hero-lede       { transform: translateY(calc(var(--scrub-progress, 0) * -16px)); }
.hero-swatch-scrub {
  filter: sepia(calc(var(--scrub-progress, 0) * .55)) saturate(calc(1 - var(--scrub-progress, 0) * .4));
}
@media (max-width: 760px), (max-height: 760px) {
  .hero-chapter { height: auto; }
  .hero-chapter-inner { position: static; min-height: 0; }
}

/* ---------- pigment rail: sticky scroll-scrubbed horizontal scene ----------
   The strip keeps its native overflow-x:auto (keyboard/touch fallback); the transform below
   is an additional scroll-linked auto-advance layered on top. Reduced motion and narrow
   viewports drop the pin + transform entirely so the fallback scroll is the only interaction —
   otherwise the strip would be stuck fully translated with most pigments unreachable. */
.pigment-rail { height: 220vh; position: relative; }
.pigment-rail-inner { position: sticky; top: 0; min-height: 100vh; display: flex; flex-direction: column; justify-content: center; }
.pigment-strip-track { transform: translateX(calc(var(--scrub-progress, 0) * var(--rail-overflow, 0px) * -1)); }
/* Vitrine edges while pinned: specimens emerge from and dissolve into soft edges instead of
   being guillotined by the viewport. Dropped wherever the rail is user-scrolled instead. */
.pigment-strip-viewport {
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 4%, #000 96%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 4%, #000 96%, transparent 100%);
}
/* A hairline dose gauge under the rail: fills as the scene scrubs, the instrument telling you
   how much of the shelf you've traversed. scaleX (not width) so it never causes layout. */
.rail-progress { max-width: 1240px; margin: 16px auto 0; padding: 0 clamp(18px, 5vw, 64px); }
.rail-progress i {
  display: block; height: 2px; border-radius:2px;
  background: linear-gradient(90deg, var(--color-good, #5E7A4E), var(--color-warn, #D7A33C) 55%, var(--color-accent, #C8453B));
  opacity: .7; transform-origin: 0 50%;
  transform: scaleX(var(--scrub-progress, 0));
}
@media (max-width: 760px), (max-height: 760px) {
  .pigment-rail { height: auto; }
  .pigment-rail-inner { position: static; min-height: 0; }
  .pigment-strip-track { transform: none; }
  .pigment-strip-viewport { -webkit-mask-image: none; mask-image: none; }
  .rail-progress { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .pigment-rail { height: auto !important; }
  .pigment-rail-inner { position: static !important; min-height: 0 !important; }
  .pigment-strip-track { transform: none !important; }
  .pigment-strip-viewport { -webkit-mask-image: none !important; mask-image: none !important; }
  .rail-progress { display: none !important; }
}

/* ---------- collection gallery: a field of floating specimen planes ----------
   The grid is one shared 3D space (a single perspective, vanishing point above center, like
   standing before a wall of suspended plates). Each card is a FLAT plane holding a persistent,
   individual pose — an axis-angle `rotate:` and a depth `translate:` — plus a slow private
   float, so the wall breathes without any two cards moving in sync. The independent
   rotate/translate properties compose with the `transform:` scrub parallax and with the
   [data-tilt] entrance on the card inside, so all four motion systems coexist without
   overwriting each other. Flat planes in 3D only — nothing volumetric (per the design
   review's bounds). Hover straightens a card to face the visitor. */
/* minmax(0, 1fr) — NOT bare 1fr — because bare 1fr means minmax(auto, 1fr), and the nowrap
   card captions below would size the tracks by their longest un-ellipsized line. Long Latin
   titles then push the third column past the viewport while short CJK titles fit, so the grid
   reads centered in zh/ja and overflowing in en/es/pt. Zero-min tracks stay equal thirds in
   every language and let the caption ellipsis do its job. */
.gallery-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(14px, 2vw, 22px); margin-top: 30px; perspective: 1600px; perspective-origin: 50% 34%; }
.gallery-card-slot { display: flex; min-width: 0; transition: rotate .7s var(--motion-ease-presence, cubic-bezier(.16,1,.3,1)); }
/* Six pose variants tiled over the grid; columns keep their net vertical drift (middle up,
   right down) so the triangulated scrub depth survives inside the 3D field. */
.gallery-card-slot:nth-child(6n+1) { rotate: 1 .4 0 4.5deg;    translate: 0 0 -30px; transform: translateY(calc(var(--scrub-progress, 0) * -8px)); }
.gallery-card-slot:nth-child(6n+2) { rotate: .3 -1 0 5deg;     translate: 0 0 14px;  transform: translateY(calc(var(--scrub-progress, 0) * -34px)); }
.gallery-card-slot:nth-child(6n+3) { rotate: -.6 .8 0 4deg;    translate: 0 0 -18px; transform: translateY(calc(var(--scrub-progress, 0) * 18px)); }
.gallery-card-slot:nth-child(6n+4) { rotate: -.4 -.7 0 4.2deg; translate: 0 0 10px;  transform: translateY(calc(var(--scrub-progress, 0) * -8px)); }
.gallery-card-slot:nth-child(6n+5) { rotate: .8 .5 0 3.6deg;   translate: 0 0 -24px; transform: translateY(calc(var(--scrub-progress, 0) * -34px)); }
.gallery-card-slot:nth-child(6n)   { rotate: .2 1 0 4.8deg;    translate: 0 0 6px;   transform: translateY(calc(var(--scrub-progress, 0) * 18px)); }
/* Hover OR keyboard focus straightens a card to face the visitor — parity for both inputs,
   and the focus ring draws on a flat plane instead of a foreshortened one. */
.gallery-card-slot:hover, .gallery-card-slot:focus-within { rotate: none; z-index: 2; }
/* The private float lives on the card itself as an independent `translate`, composing with the
   slot's pose and the entrance tilt. Three periods + offsets tiled differently than the pose
   variants (4n vs 6n) so period and pose never correlate — no visible repetition. */
.gallery-card { animation: galleryFloat 7.2s var(--motion-ease-mechanical, cubic-bezier(.45,.05,.55,.95)) infinite; }
/* Off-screen floats are paused from JS (IntersectionObserver) — covers .gallery-card (the
   animation lives on it) and .hero-float (whose img carries it). */
.float-paused, .float-paused img { animation-play-state: paused; }
.gallery-card-slot:nth-child(4n+2) .gallery-card { animation-duration: 8.4s; animation-delay: -2.8s; }
.gallery-card-slot:nth-child(4n+3) .gallery-card { animation-duration: 6.1s; animation-delay: -1.3s; }
.gallery-card-slot:nth-child(4n)   .gallery-card { animation-duration: 9.3s; animation-delay: -4.6s; }
@keyframes galleryFloat {
  0%, 100% { translate: 0 0; }
  50%      { translate: 0 -5px; }
}
.gallery-card {
  flex: 1; min-width: 0; display: block; text-decoration: none; color: inherit;
  background: var(--color-card, #F7F1E3); border: 1px solid rgba(34,29,23,.1);
  border-radius:24px; overflow: hidden; box-shadow: 0 16px 34px -28px rgba(34,29,23,.5);
}
.gallery-card-frame { aspect-ratio: 4 / 3; overflow: hidden; background: rgba(34,29,23,.06); }
.gallery-card-frame img { display: block; width: 100%; height: 100%; object-fit: cover; }
.gallery-card-cap { padding: 12px 14px 14px; }
.gallery-card-title { font-weight: 600; font-size: 14.5px; color: #221D17; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gallery-card-sub { font-family: var(--ff-mono); font-size: 10.5px; color: rgba(34,29,23,.7); margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }  /* .7 = 5.83:1 on the card ground (AA) */
@media (max-width: 900px) { .gallery-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) {
  .gallery-grid { grid-template-columns: 1fr; perspective: none; }
  /* Single column: the 3D field flattens — poses read as misalignment at phone widths. */
  .gallery-card-slot { rotate: none !important; translate: none !important; }
  .gallery-card { animation: none !important; }
  /* The nowrap+ellipsis above exists to keep captions from inflating row height when several
     cards sit side by side (see the minmax(0,1fr) comment above) — moot in a single column,
     where there's no sibling row to misalign. Long titles/attributions (translated dynasty
     names, multi-part mediums) were silently losing real information to "…" here, so let them
     wrap instead now that each card is free to be its own height. */
  .gallery-card-title, .gallery-card-sub { white-space: normal; overflow: visible; text-overflow: clip; }
}

/* ---------- CTA art fan ---------- */
.cta-text { flex: 1.3 1 380px; min-width: 280px; }
.cta-art { flex: 1 1 260px; min-width: 260px; display: flex; align-items: center; justify-content: center; position: relative; }
.cta-art-slot { width: clamp(96px, 12vw, 140px); margin-left: -24px; }
.cta-art-slot:first-child { margin-left: 0; }
.cta-art-card {
  margin: 0; background: rgba(237,228,210,.06); border: 1px solid rgba(237,228,210,.16);
  border-radius:19px; padding: 6px 6px 9px; box-shadow: 0 22px 40px -22px rgba(0,0,0,.6);
  transition: transform .5s var(--motion-ease-presence, cubic-bezier(.16,1,.3,1)),
              box-shadow .5s var(--motion-ease-presence, cubic-bezier(.16,1,.3,1));
}
.cta-art-slot:hover .cta-art-card { transform: translateY(-6px); box-shadow: 0 30px 54px -24px rgba(0,0,0,.75); }
.cta-art-card img { display: block; width: 100%; aspect-ratio: 3 / 4; object-fit: cover; border-radius:12px; }
.cta-art-card figcaption {
  margin-top: 7px; font-family: var(--ff-mono); font-size: 9px; letter-spacing: .03em;
  color: rgba(237,228,210,.65); text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
@media (max-width: 700px) {
  .cta-art { display: none !important; }
  .cta-text { flex-basis: 100% !important; }
}

/* ---------- contents: the exhibition map ----------
   Home now ends here: five wide chapter rows, read like a museum floor plan. Each row is one
   anchor (number · title/desc · specimen chips · arrow); the chapter pages themselves carry
   the sections that used to stack below the hero. Rows use [data-tilt] for the shared
   specimen-presentation entrance, then only composited hover motion on top. */
.contents-list { margin-top: 30px; display: flex; flex-direction: column; gap: 12px; }
.contents-row {
  display: flex; align-items: center; gap: clamp(14px, 2.4vw, 26px);
  text-decoration: none; color: var(--color-ink, #221D17);
  background: var(--color-card, #F7F1E3);
  border: 1px solid rgba(34,29,23,.1);
  border-radius:27px; corner-shape: squircle;
  padding: clamp(16px, 2.2vw, 24px) clamp(16px, 2.6vw, 30px);
  box-shadow: 0 14px 30px -26px rgba(34,29,23,.5);
}
/* The rows carry [data-tilt], and motion.css's `.js-ready [data-tilt]` (0,2,0) owns the
   `transition` shorthand at higher specificity than a bare `.contents-row` (0,1,0) — the
   whole shorthand loses, not just the properties it names, so hover easing silently dies.
   Match specificity with the compound selector AND restate transform/opacity (the tilt
   entrance's own eased properties): the shorthand that wins must carry all five. */
.contents-row[data-tilt] {
  transition: transform var(--motion-dur-card, 840ms) var(--motion-ease-presence, cubic-bezier(.16,1,.3,1)),
              opacity var(--motion-dur-card, 840ms) var(--motion-ease-presence, cubic-bezier(.16,1,.3,1)),
              translate .45s var(--motion-ease-presence, cubic-bezier(.16,1,.3,1)),
              border-color .45s var(--motion-ease-presence, cubic-bezier(.16,1,.3,1)),
              box-shadow .45s var(--motion-ease-presence, cubic-bezier(.16,1,.3,1));
}
/* `translate` (not transform) so the hover slide composes with the [data-tilt] entrance
   transform instead of overwriting it mid-ramp. */
.contents-row:hover, .contents-row:focus-visible {
  translate: 6px 0; border-color: rgba(200,69,59,.45);
  box-shadow: 0 18px 40px -26px rgba(34,29,23,.55);
}
.contents-no {
  font-family: var(--ff-display); font-weight: 600; font-style: italic;
  font-size: clamp(22px, 2.6vw, 30px); color: var(--color-accent, #C8453B);
  min-width: 2.2ch; flex-shrink: 0;
}
.contents-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.contents-title { font-family: var(--ff-display); font-weight: 600; font-size: clamp(19px, 2.2vw, 25px); letter-spacing: -.01em; }
.contents-desc { font-size: 13.5px; line-height: 1.5; color: rgba(34,29,23,.68); max-width: 68ch; }
.contents-motif { display: flex; flex-shrink: 0; }
.contents-chip {
  width: 20px; height: 30px; border-radius:8px; corner-shape: squircle;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.12), 0 2px 6px rgba(34,29,23,.18);
  margin-left: -6px; background-size: cover; background-position: center;
}
.contents-chip:first-child { margin-left: 0; }
.contents-arrow {
  font-size: 19px; color: var(--color-accent, #C8453B); flex-shrink: 0;
  transition: translate .45s var(--motion-ease-presence, cubic-bezier(.16,1,.3,1));
}
.contents-row:hover .contents-arrow, .contents-row:focus-visible .contents-arrow { translate: 4px 0; }
.contents-secondary {
  margin-top: 26px; display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
}
@media (max-width: 640px) {
  .contents-motif { display: none; }
  .contents-no { min-width: 1.6ch; }
}
@media (prefers-reduced-motion: reduce) {
  .contents-row, .contents-arrow { transition: none !important; }
  .contents-row:hover, .contents-row:focus-visible { translate: none !important; }
}
