/* flow.css — the gallery as a sliding album (Cover Flow): one plate faces you,
   the rest recede to either side over a dark floor that reflects them.
   Positions come from [data-offset="n"] so the page never needs an inline
   style. The homepage no longer mounts an album: since 2026-09-07 its Museum
   card is a portal picture (styles.css .business-portal-*), so this sheet is
   loaded by /gallery/ only. */

.flow {
  --flow-w: clamp(220px, 30vw, 360px);       /* the centre plate */
  --flow-first: calc(var(--flow-w) * .66);   /* centre → nearest side plate */
  --flow-step: calc(var(--flow-w) * .30);    /* each further plate back */
  --flow-depth: -150px;
  --flow-tilt: 62deg;
  --flow-top: 4%;
  --flow-paper: #eae8dc;
  --flow-mute: #a9ada5;
  position: relative;
  isolation: isolate;
  display: block;
  overflow: hidden;
  /* room = plate (1.25w) + caption offset (.26w) + caption + controls, over a 4 % top margin */
  min-height: calc(var(--flow-w) * 1.57 + 130px);
  color: var(--flow-paper);
  background:
    radial-gradient(ellipse 72% 42% at 50% 64%, rgba(255, 255, 255, .07), transparent 70%),
    linear-gradient(#0c0e12, #06070a);
  border: 1px solid var(--celadon-deep, #48604c);
  border-radius: 3px;
  perspective: 1100px;
  perspective-origin: 50% 42%;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}

.flow:focus-visible {
  outline: 3px solid var(--cinnabar, #d2352c);
  outline-offset: 4px;
}

.flow-track {
  position: absolute;
  inset: 0;
  display: block;
  transform-style: preserve-3d;
}

.flow-item {
  position: absolute;
  top: var(--flow-top);
  left: 50%;
  display: block;
  width: var(--flow-w);
  aspect-ratio: 4 / 5;
  margin: 0 0 0 calc(var(--flow-w) / -2);
  overflow: visible;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  transform-style: preserve-3d;
  cursor: pointer;
}

.flow-item > a {
  display: block;
  height: 100%;
  color: inherit;
}

.flow-item img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: #111;
}

.flow-item > img:not(.flow-reflection),
.flow-item > a > img {
  border-radius: 2px;
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, .10),
    0 26px 60px rgba(0, 0, 0, .62);
}

/* The floor: a flipped copy fading into the dark. */
.flow-reflection {
  position: absolute;
  top: 100%;
  left: 0;
  opacity: .5;
  transform: scaleY(-1);
  -webkit-mask-image: linear-gradient(to top, rgba(0, 0, 0, .72), transparent 52%);
  mask-image: linear-gradient(to top, rgba(0, 0, 0, .72), transparent 52%);
  pointer-events: none;
}

/* 花信 is live wherever it hangs: the same renderer as /rose/ draws over the
   plate's own still at a card budget, and fades in only once a frame has
   actually rendered, so no WebGL2 (or a lost context) leaves the still. */
.flow-item > a { position: relative; }

.flow-canvas {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: block;
  width: 100%;
  height: 100%;
  opacity: 0;
  pointer-events: none;
}

.flow-canvas.is-rendered { opacity: 1; }

/* Where each plate sits in the row. Nearer plates stack over farther ones. */
.flow-item[data-offset="0"]  { z-index: 20; transform: translateZ(0); cursor: default; }
.flow-item[data-offset="-1"] { z-index: 19; transform: translateX(calc(var(--flow-first) * -1)) translateZ(var(--flow-depth)) rotateY(var(--flow-tilt)); }
.flow-item[data-offset="-2"] { z-index: 18; transform: translateX(calc((var(--flow-first) + var(--flow-step)) * -1)) translateZ(var(--flow-depth)) rotateY(var(--flow-tilt)); }
.flow-item[data-offset="-3"] { z-index: 17; transform: translateX(calc((var(--flow-first) + var(--flow-step) * 2) * -1)) translateZ(var(--flow-depth)) rotateY(var(--flow-tilt)); }
.flow-item[data-offset="-4"] { z-index: 16; transform: translateX(calc((var(--flow-first) + var(--flow-step) * 3) * -1)) translateZ(var(--flow-depth)) rotateY(var(--flow-tilt)); }
.flow-item[data-offset="1"]  { z-index: 19; transform: translateX(var(--flow-first)) translateZ(var(--flow-depth)) rotateY(calc(var(--flow-tilt) * -1)); }
.flow-item[data-offset="2"]  { z-index: 18; transform: translateX(calc(var(--flow-first) + var(--flow-step))) translateZ(var(--flow-depth)) rotateY(calc(var(--flow-tilt) * -1)); }
.flow-item[data-offset="3"]  { z-index: 17; transform: translateX(calc(var(--flow-first) + var(--flow-step) * 2)) translateZ(var(--flow-depth)) rotateY(calc(var(--flow-tilt) * -1)); }
.flow-item[data-offset="4"]  { z-index: 16; transform: translateX(calc(var(--flow-first) + var(--flow-step) * 3)) translateZ(var(--flow-depth)) rotateY(calc(var(--flow-tilt) * -1)); }
/* Plates beyond the fourth wait one step past each end of the row, faded
   out, so a plate entering the row slides in along it and fades up instead
   of appearing in place at the centre (which only shows once an album holds
   more than nine plates, as the movies focus view does). */
.flow-item[data-offset="-5"] { z-index: 15; opacity: 0; pointer-events: none; transform: translateX(calc((var(--flow-first) + var(--flow-step) * 4) * -1)) translateZ(var(--flow-depth)) rotateY(var(--flow-tilt)); }
.flow-item[data-offset="5"]  { z-index: 15; opacity: 0; pointer-events: none; transform: translateX(calc(var(--flow-first) + var(--flow-step) * 4)) translateZ(var(--flow-depth)) rotateY(calc(var(--flow-tilt) * -1)); }

/* Only the facing plate is named, beneath its reflection. (.flow-item prefix outranks
   the base .plate-artwork figcaption rules on the gallery page.) */
.flow-item .flow-caption {
  position: absolute;
  top: calc(100% + var(--flow-w) * .26);
  right: auto;
  bottom: auto;
  left: 50%;
  display: grid;
  grid-template-columns: 1fr;
  justify-items: center;
  row-gap: .3rem;
  width: max(calc(var(--flow-w) * 1.7), 260px);
  margin: 0;
  padding: 0;
  background: none;
  box-shadow: none;
  backdrop-filter: none;
  transform: translateX(-50%);
  color: var(--flow-paper);
  line-height: 1.1;
  text-align: center;
  opacity: 0;
  pointer-events: none;
}

.flow-item[data-offset="0"] .flow-caption { opacity: 1; }

.flow-item .flow-caption b {
  color: var(--flow-paper);
  font-family: var(--font-cjk);
  font-size: clamp(1.3rem, 2.6vw, 1.9rem);
  font-weight: 700;
  letter-spacing: .18em;
}

.flow-item .flow-caption span,
.flow-item .flow-caption i {
  color: var(--flow-mute);
  font-family: var(--font-display);
  font-size: clamp(.86rem, 1.5vw, 1.05rem);
  font-style: normal;
  letter-spacing: -.01em;
}

/* Corner controls, like the transport on the original album flow. */
.flow-controls {
  position: absolute;
  right: 14px;
  bottom: 12px;
  left: 14px;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3, 12px);
}

.flow:not(.is-live) .flow-controls { display: none; }

.flow-nav {
  display: inline-grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, .28);
  border-radius: 50%;
  background: rgba(255, 255, 255, .08);
  color: var(--flow-paper);
  cursor: pointer;
}

.flow-nav svg { width: 22px; height: 22px; }

.flow-nav:hover { background: rgba(255, 255, 255, .16); }
.flow-nav:focus-visible { outline: 3px solid var(--cinnabar, #d2352c); outline-offset: 3px; }
.flow-nav:disabled { opacity: .3; cursor: default; }

.flow-counter {
  display: flex;
  gap: var(--space-4, 16px);
  margin: 0;
  color: var(--flow-mute);
  font-family: var(--font-mono);
  font-size: .72rem;
  letter-spacing: .1em;
}

.flow-hint::before {
  content: "·";
  padding-right: var(--space-4, 16px);
  color: var(--cinnabar, #d2352c);
}

/* Without JavaScript the album is a plain strip you can scroll. */
.flow:not(.is-live) {
  min-height: 0;
  perspective: none;
}

.flow:not(.is-live) .flow-track {
  position: static;
  display: flex;
  gap: 16px;
  overflow-x: auto;
  padding: 24px;
  scroll-snap-type: x mandatory;
}

.flow:not(.is-live) .flow-item {
  position: static;
  flex: 0 0 var(--flow-w);
  margin: 0;
  scroll-snap-align: center;
  cursor: auto;
}

.flow:not(.is-live) .flow-reflection { display: none; }

.flow:not(.is-live) .flow-item .flow-caption {
  position: static;
  width: auto;
  margin-top: 12px;
  transform: none;
  opacity: 1;
}

@media (max-width: 760px) {
  .flow {
    --flow-w: clamp(170px, 56vw, 250px);
    --flow-first: calc(var(--flow-w) * .58);
    --flow-step: calc(var(--flow-w) * .20);
    --flow-depth: -110px;
    --flow-tilt: 56deg;
    perspective: 800px;
  }

  .flow-counter .flow-hint { display: none; }
}

@media (prefers-reduced-motion: no-preference) {
  .flow-item {
    transition: transform 560ms cubic-bezier(.22, .9, .3, 1), opacity 320ms ease;
  }

  .flow-item .flow-caption { transition: opacity 320ms ease; }

  .flow-canvas { transition: opacity 420ms cubic-bezier(.16, 1, .3, 1); }
}

/* The room: an installation wall shared by /gallery/ and the movies focus
   view. The wall is lit by
   indirect washes only: there is no fixture, strip, or luminous enclosure
   around the work. Colours come from --flow-room-* tokens, which take the
   gallery palette when a page defines --gallery-* and otherwise fall back to
   the same values, so a room looks the same wherever it is built. */
.flow--room {
  --flow-room-wall: var(--gallery-wall, #090b0f);
  --flow-room-wall-raised: var(--gallery-wall-raised, #11161d);
  --flow-room-floor: var(--gallery-floor, #06070a);
  --flow-room-light: var(--gallery-light, #c5d0dc);
  --flow-room-ink: var(--gallery-ink, #e8edf2);
  --flow-room-muted: var(--gallery-ink-muted, #aeb8c3);
  --flow-room-cinnabar: var(--gallery-cinnabar, #e25c50);
  /* What a page may set: the plate's ratio (height over width), the height
     of the label under it, whether the floor carries a sheen, and the frame
     finish (see the frame recipe). Every measure below derives from those. */
  --flow-ratio: 1.25;
  --flow-label-h: 100px;
  --flow-sheen: 0;
  /* the plate answers to width and to height, so the case fits one screen;
     the same width in every room, whatever the ratio, so a poster and a
     picture hang at one size and the taller one is simply taller */
  --flow-w: clamp(200px, min(30vw, 36vh), 376px);
  --flow-first: calc(var(--flow-w) * .62);
  --flow-step: calc(var(--flow-w) * .28);
  --flow-depth: -132px;
  --flow-tilt: 58deg;
  /* air above the frame: as little as a 720px-tall dialog can spare, more
     as the screen grows, never so much that the plate drifts from the line */
  --flow-top: clamp(16px, calc(6vh - 28px), 40px);
  /* the hung work: plate height, moulding width, and the wall left between
     the frame's foot and the floor line */
  --flow-plate-h: calc(var(--flow-w) * var(--flow-ratio));
  /* A room does not change height for a taller picture. The wall is built
     for the tallest work any room hangs, a 2:3 poster, and every work
     stands on that work's foot line: the floor line, the rail, the plaque
     and the controls sit at one height in every room, and a shorter
     picture simply leaves more wall above its frame. */
  --flow-ratio-max: 1.5;
  --flow-plate-h-max: calc(var(--flow-w) * var(--flow-ratio-max));
  --flow-frame: clamp(10px, calc(var(--flow-w) * .042), 16px);
  /* the wall under the frame: the label's height plus its offset, and a
     little of the plate, so the label clears the floor line at every height */
  --flow-hang: calc(var(--flow-w) * .1 + var(--flow-label-h) + 8px);
  --flow-floor-y: calc(var(--flow-top) + var(--flow-plate-h-max) + var(--flow-frame) + var(--flow-hang));
  /* the floor: deep enough for the rail and the controls, or for the sheen
     of the work when the room has one, whichever asks for more */
  --flow-floor-d: max(calc(var(--flow-rail-drop) + var(--flow-rail-h) + var(--space-8, 64px) + var(--space-3, 12px)), calc(var(--flow-plate-h-max) * .2 * var(--flow-sheen) + var(--space-8, 64px) + var(--space-3, 12px)));
  --frame-w: var(--flow-frame);
  --flow-paper: var(--flow-room-ink);
  --flow-mute: var(--flow-room-muted);
  min-height: calc(var(--flow-floor-y) + var(--flow-floor-d));
  background:
    radial-gradient(ellipse 72% 46% at 50% 39%, color-mix(in srgb, var(--flow-room-light) 10%, transparent), transparent 72%),
    radial-gradient(ellipse 100% 46% at 50% -6%, color-mix(in srgb, var(--flow-room-light) 14%, transparent), transparent 76%),
    linear-gradient(180deg, transparent var(--flow-floor-y), color-mix(in srgb, var(--flow-room-floor) 72%, transparent) 100%);
  border: 0;
  border-radius: 0;
}

.flow--room::before {
  position: absolute;
  inset: 0;
  z-index: 0;
  background:
    radial-gradient(ellipse 56% 30% at 50% calc(var(--flow-floor-y) + var(--flow-plate-h) * .12), color-mix(in srgb, var(--flow-room-light) 7%, transparent), transparent 76%),
    linear-gradient(180deg, transparent var(--flow-floor-y), color-mix(in srgb, var(--flow-room-floor) 48%, transparent) 100%);
  pointer-events: none;
  content: "";
}

/* The architecture: the skirting line where the wall meets the floor, and
   the floor itself catching a little of the wash before it falls to black.
   A line, not a fixture. */
.flow--room::after {
  position: absolute;
  inset: 0;
  z-index: 0;
  background:
    linear-gradient(color-mix(in srgb, var(--flow-room-light) 22%, transparent), color-mix(in srgb, var(--flow-room-light) 22%, transparent)) 0 var(--flow-floor-y) / 100% 1px no-repeat,
    linear-gradient(180deg, color-mix(in srgb, var(--flow-room-light) 7%, transparent), transparent 62%) 0 calc(var(--flow-floor-y) + 1px) / 100% calc(100% - var(--flow-floor-y)) no-repeat;
  pointer-events: none;
  content: "";
}

.flow--room .flow-track {
  z-index: 1;
}

.flow--room .flow-item > img:not(.flow-reflection),
.flow--room .flow-item > a > img {
  border-radius: 0;
  /* the sight edge: a dark rabbet line, then the moulding's inner fillet */
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, .72),
    0 0 0 3px var(--frame-fillet);
}

/* The mirror starts at the floor line, so the frame's foot and the wall
   beneath it are mirrored too: the picture's reflection drops by the moulding
   and the hang, twice, and the ::after pseudo mirrors the moulding itself. */
/* A museum floor is polished stone, not a mirror: the reflections are soft
   and short, a sheen that says "floor" without repeating the work. */
.flow--room .flow-reflection {
  top: calc(100% + (var(--flow-frame) + var(--flow-hang)) * 2);
  /* plate.css soaks every plate picture in with a forwards-filled animation
     whose end state (full opacity, no blur, no flip) would override this
     sheen: the mirror image sits the animation out. */
  animation: none;
  opacity: calc(.16 * var(--flow-sheen));
  filter: blur(9px);
  -webkit-mask-image: linear-gradient(to top, transparent, rgba(0, 0, 0, .55) 9%, transparent 34%);
  mask-image: linear-gradient(to top, transparent, rgba(0, 0, 0, .55) 9%, transparent 34%);
}

/* The label: a small card on the wall beneath the frame, the way a museum
   names a work. Left-set type in ink on pale board, one hairline round it. */
/* The label is a plaque: a small brass plate under the frame, brushed
   along its length, lit from above, two screw heads at its ends, and the
   words cut into it. It takes its metal from the frame's fillet, so it is
   gilt under a gilt moulding and brass under lacquer, and it is exactly as
   tall as the room reserves for it, so it sits the same distance above the
   floor line in every room whether it carries a title or a line of a film. */
.flow--room .flow-item .flow-caption {
  --plaque: var(--frame-fillet);
  --plaque-lit: color-mix(in srgb, var(--plaque) 82%, white);
  --plaque-dark: color-mix(in srgb, var(--plaque) 94%, black);
  --plaque-ink: #221a0c;
  --plaque-screw: 11px;
  top: calc(100% + var(--flow-frame) + 10px);
  justify-items: start;
  row-gap: 1px;
  min-height: var(--flow-label-h);
  width: max-content;
  align-content: center;
  max-width: min(calc(var(--flow-w) * 1.2), calc(100vw - var(--space-6, 32px)));
  padding: 6px calc(var(--plaque-screw) * 2 + 6px) 5px;
  color: var(--plaque-ink);
  background:
    radial-gradient(circle at var(--plaque-screw) 50%, var(--plaque-lit) 0 .8px, var(--plaque-dark) 1.4px 2.4px, color-mix(in srgb, var(--plaque-lit) 60%, transparent) 2.8px 3.2px, transparent 3.8px),
    radial-gradient(circle at calc(100% - var(--plaque-screw)) 50%, var(--plaque-lit) 0 .8px, var(--plaque-dark) 1.4px 2.4px, color-mix(in srgb, var(--plaque-lit) 60%, transparent) 2.8px 3.2px, transparent 3.8px),
    repeating-linear-gradient(0deg, transparent 0 1px, rgba(255, 255, 255, .025) 1px 1.5px, transparent 1.5px 3px),
    linear-gradient(168deg, var(--plaque-lit) 0%, var(--plaque) 36%, var(--plaque) 62%, var(--plaque-dark) 100%);
  border-radius: 2px;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .22),
    inset 0 -1px 0 rgba(0, 0, 0, .2),
    0 0 0 1px color-mix(in srgb, var(--plaque-dark) 72%, black),
    0 4px 9px color-mix(in srgb, var(--flow-room-floor) 40%, transparent);
  line-height: 1.15;
  text-align: left;
  /* engraved: a hairline of light under each stroke */
  text-shadow: 0 1px 0 rgba(255, 255, 255, .14);
}

.flow--room .flow-item .flow-caption b {
  color: var(--plaque-ink);
  font-size: .98rem;
  letter-spacing: .14em;
}

.flow--room .flow-item .flow-caption span,
.flow--room .flow-item .flow-caption i {
  color: color-mix(in srgb, var(--plaque-ink) 92%, var(--plaque));
  font-family: var(--font-mono);
  font-size: .62rem;
  letter-spacing: .08em;
}

.flow--room .flow-controls {
  /* the counter's line box is the room's, not the host page's */
  line-height: 1.3;
  right: var(--space-4, 16px);
  bottom: var(--space-4, 16px);
  left: var(--space-4, 16px);
}

.flow--room .flow-nav {
  border-color: color-mix(in srgb, var(--flow-room-light) 28%, transparent);
  background: color-mix(in srgb, var(--flow-room-wall-raised) 74%, transparent);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--flow-room-light) 16%, transparent);
}

.flow--room .flow-nav:hover {
  background: color-mix(in srgb, var(--flow-room-wall-raised) 92%, transparent);
}

.flow--room .flow-nav:focus-visible {
  outline-color: var(--flow-room-cinnabar);
}

.flow--room .flow-counter {
  color: var(--flow-room-muted);
}

.flow--room .flow-hint::before {
  color: var(--flow-room-cinnabar);
}

.flow--room:not(.is-live) {
  background: var(--flow-room-wall);
}

.flow--room:not(.is-live) .flow-track {
  gap: calc(var(--space-4, 16px) + var(--flow-frame) * 2);
  padding: calc(var(--space-5, 24px) + var(--flow-frame));
}

.flow--room:not(.is-live) .flow-item::after { display: none; }

.flow--room:not(.is-live) .flow-item .flow-caption {
  margin-top: calc(var(--space-3, 12px) + var(--flow-frame));
  transform: none;
}

@media (max-width: 760px) {
  .flow--room {
    --flow-w: clamp(184px, min(64vw, 36vh), 286px);
    --flow-first: calc(var(--flow-w) * .56);
    --flow-step: calc(var(--flow-w) * .18);
    --flow-depth: -92px;
    --flow-tilt: 54deg;
  }
}

/* ── The museum frame ──
   A moulding drawn round any picture: .framed hangs a figure on a page wall;
   every plate in the room wears the same moulding. The pseudo sits behind the
   picture and extends --frame-w past it on every side, lit from the upper
   left, its profile stepped by inset rings (a bright outer lip, a lifted
   outer band, a shaded inner band), and it carries the cast shadow the
   picture used to. The default finish is the scroll's own 铜鼻 brass; a page
   sets --frame-finish-face / -lit / -dark / -fillet once (on its body) for
   another finish, and every frame on the page takes it. */
.framed,
.flow--room .flow-item {
  --frame-w: 14px;
  --frame-face: var(--frame-finish-face, var(--brass, #b8925a));
  --frame-lit: var(--frame-finish-lit, var(--brass-lit, #ecd7a3));
  --frame-dark: var(--frame-finish-dark, var(--brass-dark, #6b5330));
  --frame-fillet: var(--frame-finish-fillet, color-mix(in srgb, var(--frame-lit) 62%, var(--frame-face)));
  --frame-shadow: rgba(0, 0, 0, .38);
  isolation: isolate;
}

.flow--room .flow-item {
  --frame-w: var(--flow-frame);
  /* the plate's proportion derives from the page's one knob; its foot
     stands on the room's common foot line whatever its height */
  top: calc(var(--flow-top) + var(--flow-plate-h-max) - var(--flow-plate-h));
  aspect-ratio: 1 / var(--flow-ratio);
}

.framed::before,
.flow--room .flow-item::before,
.flow--room .flow-item::after {
  position: absolute;
  inset: calc(var(--frame-w) * -1);
  z-index: -1;
  background: linear-gradient(152deg, color-mix(in srgb, var(--frame-lit) 72%, var(--frame-face)) 0%, var(--frame-face) 34%, var(--frame-face) 62%, var(--frame-dark) 100%);
  box-shadow:
    inset 0 0 0 1px color-mix(in srgb, var(--frame-lit) 55%, transparent),
    inset 0 0 0 calc(var(--frame-w) * .36) color-mix(in srgb, var(--frame-lit) 15%, transparent),
    inset 0 0 0 calc(var(--frame-w) * .4) color-mix(in srgb, var(--frame-dark) 35%, transparent),
    inset 0 0 0 calc(var(--frame-w) - 1px) color-mix(in srgb, var(--frame-dark) 24%, transparent),
    0 2px 3px rgba(0, 0, 0, .35),
    0 18px 36px var(--frame-shadow);
  pointer-events: none;
  content: "";
}

/* The moulding in the floor: mirrored across the floor line like the picture. */
.flow--room .flow-item::after {
  inset: auto calc(var(--flow-frame) * -1);
  top: calc(100% + var(--flow-frame) + var(--flow-hang) * 2);
  height: calc(100% + var(--flow-frame) * 2);
  opacity: calc(.05 * var(--flow-sheen));
  filter: blur(9px);
  transform: scaleY(-1);
  -webkit-mask-image: linear-gradient(to top, transparent, rgba(0, 0, 0, .55) 9%, transparent 34%);
  mask-image: linear-gradient(to top, transparent, rgba(0, 0, 0, .55) 9%, transparent 34%);
}

/* The lights are on the facing work: the plates to either side, moulding,
   picture, 花信's live canvas and sheen alike, fall back into the room's
   half-light, so the eye goes where the keys go. A hovered side plate comes
   up a little. The filter sits on the plate, not its picture: the gallery's
   picture runs plate.css's soak-in animation, whose final filter value fills
   forward and would override a filter set on the image itself. */
.flow--room.is-live .flow-item:not([data-offset="0"]) {
  filter: brightness(.5) saturate(.85);
}

.flow--room.is-live .flow-item:not([data-offset="0"]):hover {
  filter: brightness(.72) saturate(.95);
}

@media (prefers-reduced-motion: no-preference) {
  .flow--room .flow-item {
    transition:
      transform 560ms cubic-bezier(.22, .9, .3, 1),
      opacity 320ms ease,
      filter 560ms cubic-bezier(.22, .9, .3, 1);
  }
}

/* Keyboard focus on the live room marks the facing frame, the work the arrow
   keys move, instead of drawing a ring round the whole case. Without
   JavaScript there is no facing plate, so the strip keeps its own ring. */
.flow--room.is-live:focus-visible {
  outline: none;
}

.flow--room.is-live:focus-visible .flow-item[data-offset="0"]::before {
  outline: 3px solid var(--flow-room-cinnabar);
  outline-offset: 2px;
}

/* 护栏: the wooden barrier between the visitor and the wall, as in front of
   the Mona Lisa — a turned beech handrail on two steel posts, hung a fixed
   drop below the skirting line in front of every plate, so a shallow room
   and a deep one show the rail at the same place under the wall. It is
   drawn at the case's own scale (a hair of translateZ only to sort it in
   front of the facing plate) so its position is exact wherever the room's
   height lands, and it never crosses the label. Wood: a cylinder lit from above (bright crest under the
   top edge, warm face, shaded underside) with two overlaid grain periods; the
   ends are rounded; a soft shadow falls on the floor. Posts: brushed steel
   cylinders with a collar under the rail. Both are pseudos on the track, so
   they walk in with the room, and they hide with the no-JS strip. */
.flow--room {
  --flow-rail-lit: #d2bc99;
  --flow-rail-face: #b99d78;
  --flow-rail-shade: #8a663d;
  --flow-rail-grain: rgba(110, 68, 30, .10);
  --flow-rail-grain-soft: rgba(90, 55, 25, .06);
  --flow-rail-grain-pale: rgba(255, 242, 214, .08);
  --flow-rail-post: #879196;
  --flow-rail-post-lit: #b1b9bc;
  --flow-rail-post-dark: #363c40;
  --flow-rail-h: 18px;
  --flow-rail-drop: 24px;   /* the rail hangs this far below the skirting line, in every room */
  --flow-rail-post-w: 8px;
}

.flow--room .flow-track::before,
.flow--room .flow-track::after {
  position: absolute;
  right: 12%;
  left: 12%;
  transform: translateZ(2px);
  pointer-events: none;
  content: "";
}

.flow--room .flow-track::before {
  top: calc(var(--flow-floor-y) + var(--flow-rail-drop));
  z-index: 40;
  height: var(--flow-rail-h);
  border-radius: calc(var(--flow-rail-h) / 2);
  /* grain: three soft streak periods a degree off horizontal drift along the
     length and cross one another into figure, in a darker and a paler tone;
     a long warm-to-pale drift along the length; turned ends; then the
     cylinder's light */
  background:
    repeating-linear-gradient(1.2deg, transparent 0 3px, var(--flow-rail-grain) 4px, transparent 5.5px 9px),
    repeating-linear-gradient(-.8deg, transparent 0 5px, var(--flow-rail-grain-pale) 6px, transparent 7px 13px),
    repeating-linear-gradient(.4deg, transparent 0 7px, var(--flow-rail-grain-soft) 9px, transparent 11px 21px),
    linear-gradient(90deg, rgba(0, 0, 0, .28) 0, transparent 8px, transparent calc(100% - 8px), rgba(0, 0, 0, .28) 100%),
    linear-gradient(90deg, rgba(120, 80, 40, .08) 0%, transparent 22%, rgba(255, 250, 235, .06) 48%, transparent 70%, rgba(120, 80, 40, .06) 100%),
    linear-gradient(180deg, var(--flow-rail-shade) 0%, var(--flow-rail-lit) 16%, var(--flow-rail-face) 46%, var(--flow-rail-face) 62%, var(--flow-rail-shade) 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .18),
    inset 0 -1px 0 rgba(0, 0, 0, .25),
    0 2px 2px rgba(0, 0, 0, .25),
    0 16px 22px -8px rgba(0, 0, 0, .5);
}

.flow--room .flow-track::after {
  --post: linear-gradient(90deg, var(--flow-rail-post-dark), var(--flow-rail-post) 30%, var(--flow-rail-post-lit) 42%, var(--flow-rail-post) 58%, var(--flow-rail-post-dark));
  --collar: linear-gradient(180deg, rgba(0, 0, 0, .55), rgba(0, 0, 0, .55));
  --foot: radial-gradient(ellipse at 50% 40%, var(--flow-rail-post) 0%, var(--flow-rail-post-dark) 55%, transparent 72%);
  top: calc(var(--flow-floor-y) + var(--flow-rail-drop) + var(--flow-rail-h) / 2);
  bottom: 6px;
  z-index: 39;
  /* collar under the rail, the post, and a foot plate where it meets the floor */
  background:
    var(--collar) calc(18% - 1.28px) 0 / calc(var(--flow-rail-post-w) + 4px) 5px no-repeat,
    var(--collar) calc(82% + 1.28px) 0 / calc(var(--flow-rail-post-w) + 4px) 5px no-repeat,
    var(--foot) calc(18% - var(--flow-rail-post-w) * .704) 100% / calc(var(--flow-rail-post-w) * 3.2) 10px no-repeat,
    var(--foot) calc(82% + var(--flow-rail-post-w) * .704) 100% / calc(var(--flow-rail-post-w) * 3.2) 10px no-repeat,
    var(--post) 18% 0 / var(--flow-rail-post-w) calc(100% - 4px) no-repeat,
    var(--post) 82% 0 / var(--flow-rail-post-w) calc(100% - 4px) no-repeat;
  filter: drop-shadow(3px 6px 4px rgba(0, 0, 0, .5));
}

.flow--room:not(.is-live) .flow-track::before,
.flow--room:not(.is-live) .flow-track::after {
  display: none;
}

/* On a phone the rail is thinner and shorter, clear of the corner controls.
   (After the rail rules, so these win the cascade.) */
@media (max-width: 760px) {
  .flow--room {
    --flow-rail-h: 12px;
    --flow-rail-drop: 20px;
    --flow-rail-post-w: 6px;
  }

  .flow--room .flow-track::before,
  .flow--room .flow-track::after {
    right: 19%;
    left: 19%;
  }
}

/* Give mobile navigation its own band below the rail and posts. The room
   grows beneath the furniture; the record keeps its right-side clearance. */
@media (max-width: 44rem) {
  .flow--room {
    min-height: calc(var(--flow-floor-y) + var(--flow-floor-d) + var(--space-8, 64px));
  }
  .flow--room .flow-track::after { bottom: calc(6px + var(--space-8, 64px)); }
  .flow--room .flow-controls { right: 96px; }
  .flow--room .flow-nav { flex-shrink: 0; }
}

/* Entering the room: the wall's lights come up and the row of frames is
   walked up to, from a few steps back, as the album goes live or the focus
   view opens. Reduced motion gets the room already lit and in place. */
@media (prefers-reduced-motion: no-preference) {
  .flow--room.is-live .flow-track {
    animation: flow-enter 1200ms var(--ease-out, cubic-bezier(.16, 1, .3, 1)) both;
  }

  .flow--room.is-live::before,
  .flow--room.is-live::after,
  .flow--room.is-live .flow-controls {
    animation: flow-lights 1400ms ease-out both;
  }

  @keyframes flow-enter {
    from { opacity: 0; transform: translateZ(-260px) translateY(12px); }
  }

  @keyframes flow-lights {
    from { opacity: 0; }
  }
}

/* Shared contact sheet: images and labels come from each flow item. */
.flow-overview {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 32px 24px;
  max-width: 1100px;
  margin: 24px auto 0;
  padding: 0 24px 96px;
}
.flow-overview button {
  display: grid;
  align-content: start;
  gap: 8px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--gallery-ink);
  font: 400 1rem/1.3 var(--font-body);
  text-align: left;
  cursor: pointer;
}
.flow-overview img {
  display: block;
  width: 100%;
  height: auto;
}
.flow-overview small { color: var(--gallery-ink-muted); font-size: .8rem; }
.flow-overview button[aria-current] img {
  outline: 2px solid var(--frame-finish-fillet, var(--gallery-rim));
  outline-offset: 4px;
}
.flow-overview button:hover > .flow-overview-label { text-decoration: underline; text-underline-offset: 4px; }
.flow-overview button:focus-visible {
  outline: 2px solid var(--gallery-ink);
  outline-offset: 6px;
}
.flow.is-overview { background: none; }
.flow.is-overview::before, .flow.is-overview::after,
.flow.is-overview > .flow-track, .flow.is-overview > .flow-controls { display: none; }
@media (max-width: 700px) {
  .flow-overview { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px 16px; padding-inline: 16px; }
}
.flow-overview[hidden] { display: none; }

/* Experience links stay visible on touch screens; only the facing plaque acts. */
.flow-caption .flow-open {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  color: inherit;
  font: 600 .75rem var(--font-mono);
  text-decoration: underline;
  text-underline-offset: 4px;
  pointer-events: none;
}
.flow-item[data-offset="0"] .flow-open,
.flow:not(.is-live) .flow-open { pointer-events: auto; }
.flow-item > a:focus-visible {
  outline: 3px solid var(--flow-paper);
  outline-offset: 5px;
}
.flow-open:hover { text-decoration-thickness: 2px; }

.flow-open:focus-visible {
  outline: 2px solid var(--plaque-ink);
  outline-offset: 0;
  border-radius: 0;
}

/* Compact labels for still works; the shared floor line stays in place. */
.flow--room .flow-item:not(:has(.flow-open)) .flow-caption { min-height: 56px; }

/* The wall label: what a museum hangs beside a work whose making is on record.
   A small "i" is cut into the plaque's right end and opens a card on the wall
   (see .flow-label below for where the card hangs). Both are out of flow inside
   the plaque, which is itself absolutely positioned, so the room reserves
   exactly the space it always did: opening a label moves neither the floor
   line, the rail, nor the controls. The plaque widens by the mark's width and
   its max-width widens by the same amount, so the words keep the width they
   had and the plaque keeps its height. The label has no motion in any motion
   setting: it is there, then it is not. */
.flow--room .flow-item .flow-caption:has(.flow-label-open) {
  padding-right: calc(var(--plaque-screw) * 2 + 32px);
  max-width: min(calc(var(--flow-w) * 1.2 + 26px), calc(100vw - var(--space-6, 32px)));
}

.flow-label-open {
  position: absolute;
  top: 50%;
  right: 0;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: none;
  transform: translateY(-50%);
  color: inherit;
  pointer-events: none;
  cursor: pointer;
}

.flow-item[data-offset="0"] .flow-label-open { pointer-events: auto; }

.flow-label-open > span {
  display: grid;
  place-items: center;
  width: 19px;
  height: 19px;
  border: 1px solid color-mix(in srgb, currentColor 62%, transparent);
  border-radius: 50%;
  font: 600 .66rem/1 var(--font-mono);
  letter-spacing: 0;
}

.flow-label-open:hover > span { border-color: currentColor; }

.flow-label-open:focus-visible {
  outline: 2px solid var(--plaque-ink);
  outline-offset: -4px;
  border-radius: 2px;
}

/* The card hangs on the wall beside the plaque, in the clear band between the
   feet of the frames and the floor line: the room leaves nothing under a
   plaque but floor, rail and the transport, and a label does not stand on the
   floor. Below 760px the wall runs out sideways and the floor runs deep, so
   there the card drops under the plaque instead. Either way it is absolutely
   positioned inside the plaque and changes no measure the room is built from. */
.flow-label {
  position: absolute;
  top: 0;
  left: calc(100% + 12px);
  z-index: 3;
  width: 264px;
  /* never past the wall's edge: half the room, less half this plaque */
  max-width: max(196px, calc(50vw - 50% - 56px));
  padding: 11px 13px;
  border: 1px solid color-mix(in srgb, var(--flow-room-light) 20%, transparent);
  border-radius: 2px;
  background: color-mix(in srgb, var(--flow-room-wall-raised) 94%, black);
  box-shadow: 0 10px 24px color-mix(in srgb, var(--flow-room-floor) 64%, transparent);
  color: var(--flow-room-ink);
  line-height: 1.4;
  pointer-events: auto;
  text-align: left;
  text-shadow: none;
}

.flow-label[hidden] { display: none; }

.flow-label dl {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 5px 12px;
  margin: 0;
}

.flow-label dt {
  color: var(--flow-room-muted);
  font: .56rem/1.5 var(--font-mono);
  letter-spacing: .1em;
  text-transform: uppercase;
}

.flow-label dd {
  margin: 0;
  color: var(--flow-room-ink);
  font: .62rem/1.5 var(--font-mono);
  letter-spacing: 0;
  overflow-wrap: break-word;
}

@media (max-width: 760px) {
  .flow-label {
    top: calc(100% + 10px);
    left: 50%;
    width: min(320px, calc(100vw - 40px));
    max-width: none;
    transform: translateX(-50%);
  }

  /* Under the plaque is the floor, and the room draws its rail over the whole
     track. A card the visitor has opened is nearer than the rail, so the work
     holding it comes forward for as long as the card is open. Nothing else in
     the plate reaches the floor: the room's sheen is off, so this moves no
     picture and no moulding across the rail. */
  .flow--room .flow-item:has(.flow-label:not([hidden])) { z-index: 41; }
}
.flow-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  pointer-events: none;
}
.flow-item[data-video-ready] .flow-video { opacity: 1; }
.flow-motion {
  position: absolute;
  z-index: 2;
  right: 10px;
  bottom: 10px;
  min-height: 44px;
  padding: 8px 12px;
  border: 1px solid var(--flow-mute);
  border-radius: 2px;
  background: #0c0e12;
  color: var(--flow-paper);
  font: .75rem var(--font-mono);
  cursor: pointer;
}
.flow-item:not([aria-current]) .flow-motion,
.flow-motion[hidden] { display: none; }
.flow-motion:focus-visible { outline: 2px solid var(--flow-paper); outline-offset: 3px; }
