/* AROL — the museum.
   Three acts: a door you open, a hall you walk into, and a piece you go up to.
   Everything photographic is a real photograph; the code only lights and moves it. */

:root{
  --void:#050404;
  --stone:#C9BFAF;          /* the hall's own lit limestone */
  --bone:#EFE7DA;
  --ash:#8D857A;
  --blood:#8E1119;
  --ember:#C2504A;
  --gilt:#C8AC72;
  /* Bodoni Moda, the face this project used in the Obsidian direction: a didone whose
     hairlines and knife-edge serifs read far more gothic than a humanist garamond, and
     which keeps the glam. Its optical-size axis is loaded so the hairlines thicken by
     themselves at label sizes instead of disappearing. */
  --disp:'Bodoni Moda',Didot,'Times New Roman',serif;
  --ui:'Jost',system-ui,-apple-system,sans-serif;
  --slow:cubic-bezier(.66,0,.24,1);
  --out:cubic-bezier(.22,1,.3,1);
  --pad:clamp(20px,4.2vw,74px);
  --shotpad:clamp(34px,7vh,86px);   /* JS reads this so the zoom lands exactly */
  /* The order of the whole handoff lives here, in one place. The model travels alone for
     the first second — nothing else may appear before it lands, or the arrival is given
     away before it happens — and only then does the detail image rise under it while the
     model fades off it. Move --flight and everything downstream stays in step. */
  --flight:1300ms;                 /* the walk up (app.js dolly) */
  --flight-ease:cubic-bezier(.45,.02,.14,1);
  /* when the detail image may start to show: the figure is within a pixel of its mark by
     here. At 940ms it was still travelling, and the close-up rose beside it as a double. */
  --land:1180ms;
}
*{box-sizing:border-box}
html,body{height:100%}
body{margin:0;background:var(--void);color:var(--bone);font-family:var(--ui);
  font-optical-sizing:auto;
  overflow:hidden;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
body.is-in{overflow-y:auto;overflow-x:hidden}
img{display:block}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}

/* ─────────────────────────────── act one: the door ─────────────────────────── */

/* The photograph has been widened with generated stone (widen_door.py) to about 2.2:1, so
   it can COVER the window edge to edge: on ordinary screens only the outer wall is cropped.
   The cover is capped so the original portal is never cut — on a tall, narrow window (a
   phone) the scene stops at the portal's width and the rest is the photograph's own black. */
/* The scene itself is see-through: behind the doors is the real showroom, so as the
   leaves part you look straight into it — and the walk through the doorway carries on into
   that same room. Where the photograph cannot cover the window (a phone: the portal is
   never cropped, so it letterboxes) the page marks the scene boxed and it takes a dark
   ground instead; there the glimpse gives way to the dark until you are through. (A huge
   box-shadow on the frame did this once — but the frame scales ~8x on the way through,
   and a shadow that size made the compositor carry an enormous layer.) */
.scene{position:fixed;inset:0;z-index:40;overflow:hidden;background:transparent}
.scene.is-boxed{background:#05090B}
.scene.is-boxed.is-through{background-color:transparent;transition:background-color 700ms ease 1700ms}
.scene__in{position:absolute;left:50%;top:50%;
  width:min(max(100vw,calc(100vh * var(--sar))),calc(100vw * var(--sfit)));
  aspect-ratio:var(--sar);
  transform:translate(-50%,-50%) scale(1);
  transform-origin:50% var(--doory);
  transition:transform 2400ms var(--slow),opacity 700ms ease 1700ms}

/* the doorway: an arch cut in the stone, with the hall behind it and the leaves in it */
.doorway{position:absolute;left:var(--dl);top:var(--dt);width:var(--dw);height:var(--dh);
  clip-path:var(--arch);perspective:1400px;perspective-origin:50% 42%}
.leaf{position:absolute;top:0;height:100%;width:50%;
  background-image:var(--door);background-repeat:no-repeat;
  background-size:var(--bgw) var(--bgh);
  transition:transform 1700ms var(--slow);
  backface-visibility:hidden}
.leaf--l{left:0;transform-origin:left center;background-position:var(--bglx) var(--bgy)}
/* The house mark, set into the doors. It is a bilaterally symmetric pair of wings, so
   each leaf carries one: closed, the whole mark reads across the seam; open, each wing
   goes with the door it belongs to. The shadow is what sets it into the timber. */
.leaf__mark{position:absolute;top:var(--markt);width:var(--markw);
  aspect-ratio:var(--markar);z-index:2;
  background-repeat:no-repeat;background-size:100% 100%;
  filter:drop-shadow(2px 5px 7px rgba(0,0,0,.9))}
.leaf--l .leaf__mark{right:0;background-image:var(--mark-l)}
.leaf--r .leaf__mark{left:0;background-image:var(--mark-r)}
.leaf--r{right:0;transform-origin:right center;background-position:var(--bgrx) var(--bgy)}
/* The jamb the leaf turns against — and, now, the whole of the darkening as it swings
   away. It sits above the mark as well as the timber, so the leaf goes into shadow as one
   thing. Fading an overlay costs the compositor nothing; re-filtering the leaf on every
   frame of the swing cost it a great deal. */
.leaf::after{content:'';position:absolute;inset:0;z-index:3;opacity:0;
  transition:opacity 1700ms var(--slow)}
.leaf--l::after{background:linear-gradient(90deg,rgba(0,0,0,.58),rgba(0,0,0,.94))}
.leaf--r::after{background:linear-gradient(270deg,rgba(0,0,0,.58),rgba(0,0,0,.94))}

.stone{position:absolute;inset:0;background:var(--surround) center/100% 100% no-repeat;
  z-index:3;pointer-events:none}
/* the light that falls out of the doorway once it is open */
.spill{position:absolute;left:var(--dl);top:var(--dt);width:var(--dw);height:var(--dh);
  z-index:4;pointer-events:none;opacity:0;clip-path:var(--arch);
  background:radial-gradient(60% 46% at 50% 54%,rgba(226,214,190,.36),transparent 72%);
  mix-blend-mode:screen;transition:opacity 1900ms ease 500ms}

.scene.is-open .scene__in{will-change:transform,opacity}
.scene.is-open .leaf--l{transform:rotateY(78deg)}
.scene.is-open .leaf--r{transform:rotateY(-78deg)}
.scene.is-open .leaf::after{opacity:1}
.scene.is-open .spill{opacity:1}
.scene.is-through .scene__in{transform:translate(-50%,-50%) scale(var(--fly));opacity:0}
.scene.is-through{pointer-events:none}
.scene.is-gone{display:none}


/* ──────────────────────── act two: the gallery of models ──────────────────── */
/* A costume exhibition, lit the way costume exhibitions actually are: the room is black,
   each figure stands in its own oval of light thrown on the wall behind it, and its own
   shadow falls huge into that oval. The light is the design. Everything else recedes. */

.hall{position:fixed;inset:0;z-index:1;overflow:hidden;background:#040303;
  perspective:1600px;transition:transform 2600ms var(--slow),filter 2200ms var(--slow)}
/* Seen through the doorway the room is further off, so it starts a little smaller and comes
   up to full size as the camera passes through — the room approaching, not just appearing. */
/* v31, "when you open the door it looks odd, we might need to soften this transition": seen
   through the opening doors the room is out of focus and dim — warm light and shapes, not a
   sharp picture cut out by the arch — and it comes into focus and up to full light as you
   pass through, which is what your eyes do stepping out of the dark. On the window-sized
   .hall, never on the long photograph inside it, so the blur stays cheap. */
body:not(.is-in) .hall{transform:scale(.9);filter:blur(9px) brightness(.55)}
/* Two jobs, deliberately on two elements. The frame carries the parallax and answers the
   pointer quickly; the photograph inside it carries the slow recede. Sharing one transform
   meant every mouse move inherited the recede's one-second curve, which is what made the
   room feel like it was wading. */
.hall__fit{position:absolute;left:0;top:0;
  transform:translate3d(var(--px,0),var(--py,0),0);
  transition:transform .42s var(--out)}
/* while the hall is being dragged it follows the hand exactly */
.hall__fit.is-held{transition:none}
/* The hall is longer than the window. A quiet line under the room says so, and goes for good
   once the visitor has walked a little way. */
.walk{position:fixed;left:0;right:0;bottom:calc(clamp(18px,3.4vh,40px) + 34px);z-index:29;
  margin:0;text-align:center;font-size:10px;letter-spacing:.34em;text-transform:uppercase;
  color:var(--ash);opacity:0;pointer-events:none;transition:opacity 1.2s ease}
.walk span{display:inline-block;margin:0 .9em;color:var(--blood);letter-spacing:0;
  animation:walk 2.6s var(--out) infinite alternate}
.walk span:first-child{animation-direction:alternate-reverse}
@keyframes walk{from{transform:translateX(-4px)}to{transform:translateX(4px)}}
body.is-in .walk{opacity:.85;transition-delay:2.4s}
body.is-in .walk.is-done,body.is-reading .walk,body.is-wing .walk{opacity:0;transition-delay:0s}
@media (prefers-reduced-motion:reduce){.walk span{animation:none}}
/* on a phone the rooms' names wrap onto two lines, so the line sits clear above them */
@media (max-width:600px){.walk{bottom:calc(clamp(18px,3.4vh,40px) + 64px)}}
/* the stage (hall_gen.py, after Greg's two costume-exhibition references): already lit the
   way it should be, so it is shown nearly as photographed — the old stone wall had to be
   taken right down, and the same treatment here would put out the oxblood */
.hall__bg{width:100%;height:100%;object-fit:cover;
  transform:scale(1.06);transform-origin:50% 40%;
  /* v31: the velvet salon (hall_v4.py salon velvet, "needs a unique feel"), shown nearly as
     photographed — it is already low and warm */
  filter:brightness(.92) saturate(.96) contrast(1.03);
  -webkit-mask-image:linear-gradient(180deg,transparent 0,#000 9%,#000 90%,transparent 100%);
  mask-image:linear-gradient(180deg,transparent 0,#000 9%,#000 90%,transparent 100%);
  transition:transform 1.1s var(--out),filter .7s ease}
.hall::after{content:'';position:absolute;inset:0;pointer-events:none;z-index:6;
  /* the top shade is kept thin: with the camera stepped in, the front row's heads (and the
     glasses) stand in what used to be its deepest part */
  background:linear-gradient(180deg,rgba(4,3,3,.66) 0,transparent 13% 76%,rgba(4,3,3,.6)),
             radial-gradient(118% 72% at 50% 42%,transparent 56%,rgba(4,3,3,.62))}

.gallery{position:absolute;inset:0;z-index:2}

.figures{position:absolute;inset:0;z-index:2}
.fig{position:relative;flex:0 0 clamp(108px,15.2vw,252px);cursor:pointer;
  transform:translate3d(0,var(--fy,0),0);transform-origin:50% 100%;
  transition:transform .8s var(--out),opacity 1s ease,filter .5s ease;
  opacity:0;filter:brightness(.92) contrast(1.02) saturate(.88)}
.hall.is-lit .fig{opacity:1}

/* the oval of light on the wall behind, which is the whole look */
.fig__spot{position:absolute;left:50%;bottom:-16%;z-index:0;pointer-events:none;
  width:178%;height:172%;transform:translateX(-50%);
  background:radial-gradient(closest-side ellipse at 50% 46%,
    rgba(208,220,240,.62) 0 30%,rgba(174,190,214,.34) 54%,
    rgba(150,166,192,.12) 70%,transparent 80%);
  filter:blur(9px);mix-blend-mode:screen;transition:opacity .55s ease}
/* the figure's own shadow, thrown long into that oval */
.fig__shadow{position:absolute;left:50%;bottom:0;z-index:1;pointer-events:none;
  width:100%;transform:translateX(17%) scale(1.14,1.34) skewX(13deg);
  transform-origin:50% 100%;opacity:.62;filter:blur(4px) brightness(0);
  transition:opacity .55s ease}
.fig__shadow img{width:100%;height:auto;display:block}

.fig__me{width:100%;height:auto;position:relative;z-index:3;
  filter:drop-shadow(0 12px 16px rgba(0,0,0,.8));
  /* a bust cut off square reads as pasted; let it go down into the dark of the plinth */
  -webkit-mask-image:linear-gradient(180deg,#000 0 76%,rgba(0,0,0,.45) 92%,transparent 100%);
  mask-image:linear-gradient(180deg,#000 0 76%,rgba(0,0,0,.45) 92%,transparent 100%)}
/* the bright core where it meets the deck, and the dark contact under it */
.fig__pool{position:absolute;left:50%;bottom:0;z-index:2;pointer-events:none;
  width:150%;height:20%;transform:translate(-50%,52%);mix-blend-mode:screen;
  background:radial-gradient(50% 50% at 50% 50%,rgba(238,222,192,.34),transparent 66%);
  filter:blur(8px);transition:opacity .5s ease}
.fig__cast{position:absolute;left:50%;bottom:0;z-index:2;pointer-events:none;
  width:92%;height:9%;transform:translate(-50%,44%);
  background:radial-gradient(50% 50% at 50% 50%,rgba(0,0,0,.95),transparent 70%);
  filter:blur(4px)}

/* the museum label, angled on the deck the way a real one leans */
.fig__plate{position:absolute;left:50%;top:calc(100% + clamp(10px,1.8vh,26px));z-index:4;
  transform:translateX(-50%) rotateX(38deg);transform-origin:50% 0;
  white-space:nowrap;text-align:center;pointer-events:none;
  padding:10px 17px 11px;
  background:linear-gradient(180deg,#3B342C,#1A1512);
  box-shadow:0 1px 0 rgba(240,222,190,.34) inset,0 -1px 0 rgba(0,0,0,.7) inset,
    0 10px 18px -8px rgba(0,0,0,.92);
  transition:opacity .4s ease}
.fig__plate b{display:block;font-family:var(--disp);font-weight:400;
  font-size:clamp(10px,.92vw,15px);letter-spacing:.2em;color:#F0E6D4;
  text-shadow:0 1px 0 rgba(0,0,0,.8)}
.fig__plate i{display:block;margin-top:4px;font-style:normal;font-size:8px;
  letter-spacing:.24em;text-transform:uppercase;color:#B4A78F}

@media (hover:hover) and (pointer:fine){
  .fig:hover{filter:brightness(1.12) contrast(1.03) saturate(.98);z-index:5;
    transform:translate3d(0,-1.4vh,0) scale(1.03)}
  .fig:hover .fig__spot{opacity:1.55}
  .fig:hover .fig__shadow{opacity:.58}
  .fig:hover .fig__pool{opacity:1.6}
  .hall.is-hover .hall__bg{filter:brightness(.2) saturate(.45) contrast(1.08)}
}
.fig:focus-visible{outline:1px solid var(--gilt);outline-offset:12px}

/* Full-length figures stand on the stage. x/y/s are shares of the photograph, measured off
   its own pools of light (build_site.py); --fh is the photograph's drawn height, set by the
   page, so a figure is sized and placed in the picture's terms and stays on its pool at any
   window shape. Its wall spot and thrown shadow belong to the old room: here the light comes
   straight down, so all it keeps is the pool at its feet and the dark contact under them. */
.fig--full{position:absolute;flex:none;padding:0;
  left:calc(var(--x) - var(--s) * var(--ar) * var(--fh) / 200);
  top:calc(var(--y) - var(--s) * var(--fh) / 100);
  width:calc(var(--s) * var(--ar) * var(--fh) / 100);
  height:calc(var(--s) * var(--fh) / 100);
  filter:brightness(.97) contrast(1.02) saturate(.96)}
.fig--full .fig__spot,.fig--full .fig__shadow{display:none}
.fig--full .fig__me{width:100%;height:100%;
  -webkit-mask-image:none;mask-image:none;
  filter:drop-shadow(0 6px 10px rgba(0,0,0,.7))}
.fig--full .fig__pool{width:170%;height:9%;transform:translate(-50%,50%);
  background:radial-gradient(50% 50% at 50% 50%,rgba(240,206,190,.22),transparent 70%)}
.fig--full .fig__cast{width:78%;height:2.6%;transform:translate(-50%,40%)}
/* Its own spotlight ("make the spotlights shine on the mannequins"). The room's beams are
   painted in and, since the figures were regrouped, land on empty floor, so each figure
   carries one: a cone of warm light in the haze from the top of the room down to its feet
   (img/beam.webp, generate/beam.py), and the light itself falling on the figure from above
   — head and shoulders brightest, which is where the glasses are. --yn is the feet line as
   a plain number, so the cone can reach from the room's top edge to the floor. */
.fig--full .fig__beam{position:absolute;left:50%;bottom:-1.5%;z-index:0;pointer-events:none;
  width:calc(var(--s) * var(--fh) / 100 * .9);
  height:calc(var(--yn) * var(--fh) / 100);
  transform:translateX(-50%);opacity:.24;mix-blend-mode:screen;
  background:url(img/beam.webp) center/100% 100% no-repeat;transition:opacity .5s ease}
.fig--full .fig__lit{position:absolute;inset:0;z-index:4;pointer-events:none;
  -webkit-mask-size:100% 100%;mask-size:100% 100%;
  background:radial-gradient(ellipse 64% 32% at 50% 7%,rgba(255,238,214,.72),
    rgba(255,236,214,.2) 62%,transparent 100%);
  /* soft-light, not screen: screen lifted the black wigs to grey and flattened the glasses;
     this brightens what the light catches and leaves the blacks black */
  mix-blend-mode:soft-light;transition:opacity .9s ease}
.fig--full:hover .fig__beam{opacity:.4}
/* the walk up carries the figure off its spot and onto the close-up, which is lit as
   photographed — so the lamp goes out as it leaves, and the swap has nothing to jump */
.hall.is-away .fig__beam,.hall.is-away .fig.is-chosen .fig__lit{opacity:0}
/* Its reflection in the polished black floor, which is what plants a figure on a glossy
   floor far more surely than any shadow: the same picture turned over at the feet, faint,
   gone by the knees. The mask is drawn before the flip, so it is opaque at the feet. */
.fig__mirror{position:absolute;left:0;top:100%;width:100%;height:100%;z-index:1;
  pointer-events:none;transform:scaleY(-1);opacity:.17;filter:blur(1.5px) brightness(.8);
  -webkit-mask-image:linear-gradient(180deg,transparent 62%,rgba(0,0,0,.9) 100%);
  mask-image:linear-gradient(180deg,transparent 62%,rgba(0,0,0,.9) 100%)}
.hall.is-away .fig.is-chosen .fig__mirror{opacity:0;transition:opacity .3s ease}
/* the label: a bone card leaning on the stage's edge, as in the reference */
.fig--full .fig__plate{top:calc(100% + clamp(8px,1.6vh,22px));padding:7px 13px 8px;
  background:linear-gradient(180deg,#EEE7DA,#D4CAB8);
  box-shadow:0 1px 0 rgba(255,255,255,.55) inset,0 9px 14px -6px rgba(0,0,0,.92)}
.fig--full .fig__plate b{color:#1B1513;text-shadow:none;font-size:clamp(9px,.78vw,13px)}
.fig--full .fig__plate i{color:#6E6155}
@media (hover:hover) and (pointer:fine){
  /* a standing figure is not lifted off its stage — it is lit */
  .fig.fig--full:hover{transform:translate3d(0,var(--fy,0),0) scale(1.012);
    filter:brightness(1.12) contrast(1.03) saturate(1)}
  .hall.is-hover .hall__bg{filter:brightness(.66) saturate(.88) contrast(1.04)}
}

/* the hall's own furniture */
/* The house is identified by the mark and nothing else — no wordmark beside it. So it is
   given the size a signature deserves, and it is the artwork exactly as drawn: the red
   star-work is what carries it against a dark room. */
.brand{position:fixed;top:clamp(14px,2.6vh,30px);left:var(--pad);z-index:29;
  display:block;opacity:0;transition:opacity 1s ease .6s,transform .5s var(--out)}
.brand img{width:clamp(88px,8.6vw,164px);height:auto;
  filter:drop-shadow(0 3px 14px rgba(0,0,0,.95))
         drop-shadow(0 0 22px rgba(142,17,25,.35))}
.brand:hover{transform:scale(1.035)}
/* Above the rooms (28) and below the piece (30): standing in one room you can walk
   straight into the next, and the mark stays on the wall the whole time. Buried under the
   rooms, the only way out of one was the Close button. */
.ways{position:fixed;bottom:clamp(18px,3.4vh,40px);left:0;right:0;z-index:29;
  display:flex;justify-content:center;gap:clamp(20px,4vw,64px);
  opacity:0;transition:opacity 1s ease .9s}
.ways a{font-size:10px;letter-spacing:.34em;text-transform:uppercase;color:var(--ash);
  padding-bottom:7px;border-bottom:1px solid transparent;transition:color .35s ease,
  border-color .35s ease;text-decoration:none}
.ways a:hover{color:var(--bone);border-color:var(--blood)}
body.is-in .brand,body.is-in .ways{opacity:1}
body.is-wing .brand{opacity:1}
/* the room's furniture steps back while a piece is being looked at */
body.is-reading .ways{opacity:0;pointer-events:none}
body.is-reading .brand{opacity:.35}

/* ─────────────────────────────── act three: the piece ──────────────────────── */

/* zoomed about the middle of the window, not of the long photograph — walked to one end of
   the hall, a zoom about the photograph's centre swung the room sideways */
.hall.is-away .hall__bg{transform:translateX(calc(var(--px,0px) * .0943)) scale(1.16);
  filter:brightness(.19) saturate(.42) contrast(1.05);
  will-change:transform,filter}
/* The figures left behind change opacity and nothing else. At six per cent neither a
   blur nor a brightness change can be seen, and both were being computed on five large
   cut-outs on every frame of the flight. */
.hall.is-away .fig{opacity:.06}

/* The clicked model comes forward, alone. Its travel is measured in JS so it finishes
   exactly on the detail image's box; only then do the two dissolve into one another.
   Two things had been making it lurch: the transform-origin moved from the model's feet
   to its middle at the instant of the click, which no transition can smooth over, and
   the bottom mask was being dropped mid-flight. Both stay put now. */
.hall.is-away .fig.is-chosen{filter:none;z-index:40;will-change:transform;
  transform:translate3d(var(--zx,0),var(--zy,0),0) scale(var(--zs,1));
  transition:transform var(--flight) var(--flight-ease),filter .5s ease;
  animation:hand 1820ms linear forwards}
/* It holds full strength until the close-up has risen all the way OVER it (the panel is
   the upper layer), and only then goes. Fading both at once left each half transparent at
   the midpoint, and the dark room showed through as a dip in the light. */
@keyframes hand{0%,90%{opacity:1}100%{opacity:0}}
.hall.is-away .fig.is-chosen .fig__spot,
.hall.is-away .fig.is-chosen .fig__shadow,
.hall.is-away .fig.is-chosen .fig__pool,
.hall.is-away .fig.is-chosen .fig__cast{opacity:0;transition:opacity .4s ease}
.hall.is-away .fig.is-chosen .fig__plate,.hall.is-away .fig.is-chosen::after{opacity:0}

.piece{position:fixed;inset:0;z-index:30;display:grid;
  grid-template-columns:minmax(0,1fr) minmax(320px,min(41vw,620px));
  pointer-events:none;opacity:0;transition:opacity .5s ease}
.piece.is-on{pointer-events:auto;opacity:1}
.piece__card{position:relative;height:100%;overflow-y:auto;overscroll-behavior:contain;
  padding:clamp(56px,9vh,104px) clamp(26px,3.4vw,58px) clamp(40px,7vh,80px);
  background:linear-gradient(255deg,rgba(6,5,5,.988) 62%,rgba(6,5,5,.94));
  border-left:1px solid rgba(239,231,218,.1);will-change:transform;
  transform:translateX(100%);transition:transform .8s var(--slow)}
/* the written detail arrives after the model has landed, never alongside it */
.piece.is-on .piece__card{transform:none;
  transition:transform .86s var(--out) calc(var(--land) - 340ms)}
.shut{position:absolute;top:clamp(18px,3vh,30px);right:clamp(20px,2.4vw,34px);
  font-size:10px;letter-spacing:.3em;text-transform:uppercase;color:var(--ash)}
.shut:hover{color:var(--bone)}
.piece__no{font-size:10px;letter-spacing:.34em;text-transform:uppercase;color:var(--gilt)}
.piece h2{margin:16px 0 0;font-family:var(--disp);font-weight:400;
  font-size:clamp(38px,4.6vw,72px);line-height:.94;letter-spacing:.012em}
.piece__p{margin:20px 0 0;font-family:var(--disp);font-size:clamp(22px,2vw,30px);
  color:var(--ember)}
.piece__say{margin:26px 0 0;max-width:34ch;font-size:14.5px;line-height:1.72;color:#B9B1A6}
.views{display:flex;gap:10px;margin:30px 0 0}
.views button{width:70px;height:70px;overflow:hidden;background:#0C0A0A;
  border:1px solid rgba(239,231,218,.14);opacity:.6;transition:opacity .3s ease,
  border-color .3s ease}
.views button img{width:100%;height:100%;object-fit:cover}
.views button.on,.views button:hover{opacity:1;border-color:var(--gilt)}
.facts{margin:30px 0 0;padding:0;list-style:none;border-top:1px solid rgba(239,231,218,.12)}
.facts li{display:flex;justify-content:space-between;gap:18px;padding:13px 0;
  border-bottom:1px solid rgba(239,231,218,.12);font-size:13.5px}
.facts span{color:var(--ash);font-size:9.5px;letter-spacing:.24em;text-transform:uppercase;
  padding-top:2px}
.claim{display:inline-block;margin-top:34px;padding:17px 46px;background:var(--blood);
  color:#fff;font-size:10.5px;letter-spacing:.3em;text-transform:uppercase;
  text-decoration:none;transition:background .3s ease,transform .12s ease}
.claim:hover{background:#A6141D}
.claim:active{transform:scale(.985)}

/* The chosen piece, large, on the left of the panel. The views stack on top of one
   another and cross-fade, so switching view never moves the layout. */
.piece__shot{position:relative;pointer-events:none;overflow:hidden}
/* No scale on the way in. The model is flying to exactly this box, so anything that moves
   the image as it appears breaks the join — it can only rise in place, underneath. */
.piece__shot img{position:absolute;inset:var(--shotpad);margin:auto;
  max-width:calc(100% - 2 * var(--shotpad));
  max-height:calc(100% - 2 * var(--shotpad));
  width:auto;height:auto;object-fit:contain;opacity:0;
  transition:opacity 440ms cubic-bezier(.4,0,.2,1) var(--land);
  filter:drop-shadow(0 34px 54px rgba(0,0,0,.92));
  -webkit-mask-image:linear-gradient(180deg,#000 0 88%,transparent 100%);
  mask-image:linear-gradient(180deg,#000 0 88%,transparent 100%)}
/* the model's own view wears exactly the mask the figure wears, so at the moment of the
   swap the two are the same picture, softened at the bottom in the same place */
.piece__shot img:first-child{
  -webkit-mask-image:linear-gradient(180deg,#000 0 76%,rgba(0,0,0,.45) 92%,transparent 100%);
  mask-image:linear-gradient(180deg,#000 0 76%,rgba(0,0,0,.45) 92%,transparent 100%)}
.piece.is-on .piece__shot img.on{opacity:1}
/* once it has arrived, changing view is immediate — the arrival delay applies only once */
.piece.is-settled .piece__shot img{transition:opacity 340ms ease}

@media (max-width:900px){
  .piece{grid-template-columns:1fr;grid-template-rows:40vh 1fr}
  .piece__shot img{inset:14px;max-width:calc(100% - 28px);max-height:calc(100% - 28px)}
  .piece__card{border-left:0;border-top:1px solid rgba(239,231,218,.1);
    transform:translateY(100%)}
  .ways{gap:16px;flex-wrap:wrap;padding:0 16px}
  .ways a{font-size:9px;letter-spacing:.22em}
}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{transition-duration:1ms!important;animation-duration:1ms!important}
}

/* ─────────────────────────────── the cursor ────────────────────────────────── */
/* The house's own four-point star, following the pointer. With no Enter button on the
   door, this is what says the room is open: it swells over anything that can be opened. */
/* The star is hidden by default and only shown where a pointer can actually use it. This
   ordering matters: the `display:none` used to sit AFTER the media query, where — same
   specificity, later rule — it beat the `display:block` inside it. The star never drew,
   while `cursor:none` still hid the real arrow, so the page had no cursor at all. */
.cur{display:none}
@media (hover:hover) and (pointer:fine){
  body.has-cur,body.has-cur *{cursor:none}
  body.has-cur .cur{display:block;
    position:fixed;left:0;top:0;width:20px;height:20px;z-index:200;pointer-events:none;
    margin:-10px 0 0 -10px;
    transition:width .34s var(--out),height .34s var(--out),margin .34s var(--out),
      opacity .3s ease}
  /* clean: the star alone — no pale outline, no glow ("the cursor has the white border
     around it, remove that, make it more clean") */
  body.has-cur .cur svg{width:100%;height:100%;display:block}
  body.has-cur .cur.is-hot{width:40px;height:40px;margin:-20px 0 0 -20px}
  body.has-cur .cur.is-off{opacity:0}
}

/* ────────────────────── the other rooms of the house ───────────────────────── */
/* Not modals over a website — rooms you walk into. The gallery stays behind, pushed back
   and out of focus, and the room you have entered rises into the light from the floor.
   Same building, same light, one flight of stairs further in. */

.wing{position:fixed;inset:0;z-index:28;overflow:hidden;
  opacity:0;pointer-events:none;transition:opacity .5s ease;
  will-change:opacity;
  background:
    radial-gradient(130% 62% at 50% -6%,rgba(228,208,170,.085),transparent 62%),
    linear-gradient(180deg,rgba(5,4,4,.955),rgba(5,4,4,.995) 46%)}
.wing.is-on{opacity:1;pointer-events:auto}
.wing__in{position:absolute;inset:0;overflow-y:auto;overscroll-behavior:contain;
  padding:clamp(60px,11vh,132px) var(--pad) clamp(76px,12vh,128px);
  /* it arrives from below, the way the room does */
  transform:translateY(34px);transition:transform .82s var(--out)}
.wing.is-on .wing__in{transform:none}
.wing__in--read{max-width:min(1180px,100%);margin:0 auto}
.wing>.shut{position:fixed;top:clamp(18px,3vh,32px);right:var(--pad);z-index:2}

.wing__head{max-width:46ch}
.wing__no{margin:0;font-size:10px;letter-spacing:.42em;text-transform:uppercase;
  color:var(--gilt)}
.wing__t{margin:.28em 0 0;font-family:var(--disp);font-weight:300;
  font-size:clamp(46px,7.4vw,112px);line-height:.88;letter-spacing:.008em;
  text-wrap:balance}
.wing__lead{margin:clamp(18px,2.6vh,30px) 0 0;font-family:var(--disp);
  font-size:clamp(20px,2.1vw,31px);line-height:1.42;color:#C6BEB1;max-width:30ch}

/* a single plate, lit like everything else in the building */
.wing__plate{margin:clamp(34px,6vh,72px) 0 0;overflow:hidden;background:#0A0908}
.wing__plate img{width:100%;height:clamp(220px,42vh,420px);object-fit:cover;
  filter:brightness(.88) contrast(1.05)}
.wing__plate--wide img{height:clamp(180px,34vh,340px);object-position:50% 42%}

.trip{display:grid;gap:clamp(8px,1.2vw,18px);margin:clamp(34px,6vh,72px) 0 0;
  grid-template-columns:repeat(auto-fit,minmax(min(240px,100%),1fr))}
.trip figure{margin:0;overflow:hidden;background:#0A0908}
.trip img{width:100%;aspect-ratio:1;object-fit:cover;
  filter:brightness(.86) contrast(1.06);transition:transform 1.1s var(--out),filter .6s ease}
.trip figure:hover img{transform:scale(1.045);filter:brightness(1) contrast(1.06)}

.notes{display:grid;gap:clamp(26px,3.4vw,54px);margin:clamp(38px,6vh,74px) 0 0;
  grid-template-columns:repeat(auto-fit,minmax(min(260px,100%),1fr))}
.notes h3{margin:0 0 14px;font-family:var(--ui);font-weight:400;font-size:10px;
  letter-spacing:.3em;text-transform:uppercase;color:var(--gilt);
  padding-bottom:13px;border-bottom:1px solid rgba(239,231,218,.14)}
.notes p{margin:0;font-size:14.5px;line-height:1.78;color:#ADA59A}
.wing__end{margin:clamp(40px,6vh,76px) 0 0;font-family:var(--disp);
  font-size:clamp(19px,2vw,28px);line-height:1.45;color:#C6BEB1;max-width:34ch}

/* the three steps of a commission, numbered because they genuinely are in order */
.steps{counter-reset:s;list-style:none;margin:clamp(38px,6vh,74px) 0 0;padding:0;
  display:grid;gap:clamp(24px,3vw,48px);
  grid-template-columns:repeat(auto-fit,minmax(min(250px,100%),1fr))}
.steps li{counter-increment:s;position:relative;padding-top:52px}
.steps li::before{content:counter(s,decimal-leading-zero);position:absolute;top:0;left:0;
  font-family:var(--disp);font-size:30px;line-height:1;color:var(--blood)}
.steps b{display:block;font-family:var(--disp);font-weight:400;font-size:23px;
  letter-spacing:.01em;color:var(--bone);margin-bottom:11px}
.steps span{display:block;font-size:14.5px;line-height:1.78;color:#ADA59A}

.ask{margin:clamp(40px,6vh,78px) 0 0}
.ask a{display:inline-block;padding:17px 46px;background:var(--blood);color:#fff;
  font-size:10.5px;letter-spacing:.3em;text-transform:uppercase;text-decoration:none;
  transition:background .3s ease}
.ask a:hover{background:#A6141D}
/* a fact that has not been confirmed is shown as missing, never invented */
.ask .todo{display:inline-block;padding:16px 30px;border:1px dashed rgba(200,172,114,.5);
  font-family:var(--disp);font-style:italic;font-size:17px;color:var(--gilt)}

/* the filter, set as a row of plain words — a museum labels, it does not put up chips */
.sift{display:flex;gap:clamp(14px,1.8vw,28px);margin:clamp(22px,3vh,34px) 0 0}
.sift button{padding:0 0 7px;font-size:10px;letter-spacing:.28em;text-transform:uppercase;
  color:var(--ash);border-bottom:1px solid transparent;
  transition:color .3s ease,border-color .3s ease}
.sift button:hover{color:var(--bone)}
.sift button.on{color:var(--bone);border-color:var(--blood)}

/* ── the run of cases ── */
/* The collection is walked, not scrolled through: one long lit run, each piece in its own
   case. A case lights as it comes level with you and goes out again behind you, which is
   the same light the gallery is built on.

   The plate of the wing sits beside the run rather than above it. Stacked, the title ate
   the screen and the cases were pushed off the bottom — and a run you cannot see is not a
   gallery, it is a list. */
.wing[data-wing="collection"] .wing__in{
  display:grid;grid-template-columns:minmax(250px,26%) minmax(0,1fr);
  grid-template-rows:auto auto;align-content:center;
  gap:0 clamp(24px,4vw,72px);overflow:hidden;padding-right:0}
/* placed by hand: left to auto-placement the hint took the head's cell and the whole
   wing came out back to front, with the run on the left */
.wing[data-wing="collection"] .wing__head{grid-column:1;grid-row:1;max-width:34ch}
.wing[data-wing="collection"] .wing__t{font-size:clamp(40px,4.6vw,76px)}
.wing[data-wing="collection"] .wing__lead{font-size:clamp(17px,1.4vw,22px);max-width:28ch}
.wing[data-wing="collection"] .run{grid-column:2;grid-row:1 / span 2;align-self:center}
/* The run is cut off by the edge of its column, and a name sliced down the middle reads
   as a typo rather than as a gallery continuing past the wall. So it dissolves at both
   ends instead of being clipped. */
.run{-webkit-mask-image:linear-gradient(90deg,transparent 0,#000 26px,
    #000 calc(100% - 96px),transparent 100%);
  mask-image:linear-gradient(90deg,transparent 0,#000 26px,
    #000 calc(100% - 96px),transparent 100%);
  display:flex;gap:clamp(10px,1.6vw,30px);align-items:center;
  min-width:0;min-height:0;max-height:100%;
  padding:clamp(16px,3vh,38px) var(--pad) clamp(16px,3vh,38px) 0;
  overflow-x:auto;overflow-y:hidden;scroll-snap-type:x proximity;
  scrollbar-width:thin;scrollbar-color:rgba(200,172,114,.45) transparent}
.run::-webkit-scrollbar{height:2px}
.run::-webkit-scrollbar-thumb{background:rgba(200,172,114,.5)}
.run:focus-visible{outline:1px solid var(--gilt);outline-offset:6px}

.case{position:relative;flex:0 0 clamp(160px,20vw,270px);scroll-snap-align:center;
  background:none;border:0;padding:0;text-align:left;text-decoration:none;color:inherit;
  opacity:.3;transform:translate3d(0,14px,0);
  transition:opacity .62s ease,transform .62s var(--out)}
.case.is-lit{opacity:1;transform:none}
/* display:block matters — a span is inline, and aspect-ratio does nothing to an inline
   box, so the plates collapsed to nothing and the labels landed on top of each other */
.case__plate{display:block;position:relative;aspect-ratio:1;overflow:hidden;
  background:#090807;box-shadow:inset 0 0 0 1px rgba(239,231,218,.07);
  max-height:46vh}
.case__plate img{width:100%;height:100%;object-fit:cover;
  transition:transform 1.2s var(--out)}
.case:hover .case__plate img{transform:scale(1.05)}
/* the light falling into the case from above */
.case__plate::after{content:'';position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,rgba(238,222,190,.15),transparent 42%),
             linear-gradient(0deg,rgba(4,3,3,.72),transparent 46%)}
/* an empty case is what a museum shows when the piece is not installed yet */
.case__none{position:absolute;inset:0;display:grid;place-items:center;
  font-size:9px;letter-spacing:.28em;text-transform:uppercase;color:#5D564E}
/* two lines' worth whether the name needs them or not, so every plate in the run hangs
   on the same line — a name that wraps must not drag its case out of true */
.case__t{display:block;margin-top:15px;font-family:var(--disp);font-size:16px;
  line-height:1.25;min-height:2.5em;letter-spacing:.14em;color:var(--bone)}
.case__m{display:block;margin-top:6px;font-size:9px;letter-spacing:.24em;
  text-transform:uppercase;color:var(--ash)}
.case__m b{font-weight:400;color:var(--ember)}
.case--shut .case__plate img{filter:grayscale(1) brightness(.34)}
.case--shut .case__m b{color:var(--ash)}
.run__hint{margin:clamp(20px,3vh,32px) 0 0;font-size:9px;letter-spacing:.3em;
  text-transform:uppercase;color:#5D564E}
.wing[data-wing="collection"] .run__hint{grid-column:1;grid-row:2;
  margin:clamp(20px,3vh,34px) 0 0}

body.is-wing .ways a.on{color:var(--bone);border-color:var(--blood)}
body.is-wing .hall{pointer-events:none}

@media (max-width:900px){
  .wing__in{padding-top:clamp(52px,9vh,90px)}
  .case{flex-basis:clamp(150px,58vw,230px)}
  /* narrow: the plate goes back above the run, and the column scrolls as one */
  .wing[data-wing="collection"] .wing__in{display:block;overflow-y:auto;
    padding-right:var(--pad)}
  .wing[data-wing="collection"] .run__hint{margin:clamp(18px,3vh,28px) 0 0}
  .run{margin:clamp(26px,4vh,44px) calc(var(--pad) * -1) 0;padding-left:var(--pad)}
}

/* ─────────────────────────────────── the air ───────────────────────────────── */
/* Light does not stop at the surfaces it lands on — it hangs in the room in front of them.
   That is the whole of the ethereal: haze around the arch, a line of light where the doors
   meet, and dust turning slowly in it. Every part of it is light ADDED to the photograph,
   screen-blended, never paint laid over it. Nothing here is drawn scenery. */

/* The breathing and the going-out are kept on two separate dials. When the doors opened the
   breathing used to be switched off (animation:none) in the same instant as the fade began,
   so the light jumped to full and then dropped: it read as a switch, not a dying glow. Now
   --breath keeps breathing and --lit is turned down underneath it, and opacity is the two
   multiplied, so the fade starts from wherever the breath happens to be. */
@property --breath{syntax:'<number>';inherits:false;initial-value:1}
@property --lit{syntax:'<number>';inherits:false;initial-value:1}
@keyframes breathe{0%,100%{--breath:.68}50%{--breath:1}}

/* the porch full of air, which is what lifts the surround off flat black */
.aura{position:absolute;left:50%;top:var(--doory);
  width:calc(var(--photow) * 1.24);height:calc(var(--photoh) * 1.12);
  transform:translate(-50%,-50%);z-index:5;pointer-events:none;mix-blend-mode:screen;
  background:radial-gradient(44% 40% at 50% 50%,rgba(192,202,224,.16),
    rgba(146,158,186,.062) 48%,transparent 76%);
  filter:blur(26px);animation:breathe 13s ease-in-out infinite;
  opacity:calc(var(--breath) * var(--lit))}

/* the light that is already on in the room behind, escaping where the doors meet. It lives
   INSIDE the doorway, above the timber (leaves are z auto) and below the mark (z 2): light
   from the crack falls on the wood and the mark, fixed to the door, stands in front of it.
   Laid over everything, it washed across the mark and the mark read as pasted on. */
.seam{position:absolute;inset:0;z-index:1;pointer-events:none;mix-blend-mode:screen;
  background:
    radial-gradient(34% 38% at 50% 56%,rgba(224,212,186,.20),transparent 76%),
    linear-gradient(90deg,transparent calc(50% - 30px),rgba(238,228,204,.26) 50%,
      transparent calc(50% + 30px)),
    linear-gradient(90deg,transparent calc(50% - 5px),rgba(248,242,226,.34) 50%,
      transparent calc(50% + 5px));
  -webkit-mask-image:linear-gradient(180deg,transparent 9%,#000 33%,#000 77%,transparent 97%);
  mask-image:linear-gradient(180deg,transparent 9%,#000 33%,#000 77%,transparent 97%);
  filter:blur(3px);animation:breathe 7.5s ease-in-out infinite;
  opacity:calc(var(--breath) * var(--lit))}
/* once they are open the seam has nothing left to leak through: it goes out as the gap
   opens, quickly at first and then a long tail */
.scene.is-open .seam{--lit:0;transition:--lit 1300ms cubic-bezier(.25,.6,.3,1)}
/* Once the doors part, the porch's own air goes. Its grain and haze are blended onto the
   stone, and inside the open doorway there is nothing under them in their layer — the room
   behind is a different layer — so they drew a flat grey veil over the glimpse of the
   showroom. The room carries its own grain and light; these give way to it. */
.scene.is-open::before,.scene.is-open .spill{opacity:0;animation:none;
  transition:opacity 1400ms ease}
/* the porch's haze dims over the whole swing of the doors, not ahead of it */
.scene.is-open .aura{--lit:0;transition:--lit 1700ms cubic-bezier(.4,.1,.3,1)}

/* Crystal dust, turning in the light. The explicit width and height are load-bearing: a
   canvas is a replaced element, so inset:0 does NOT stretch it — it keeps its intrinsic
   300x150 and the field renders into a stamp in the corner. */
.motes{position:absolute;inset:0;width:100%;height:100%;
  pointer-events:none;mix-blend-mode:screen;
  opacity:0;transition:opacity 1.8s ease .5s}
.scene .motes{z-index:5}
.motes.is-on{opacity:1}
.scene.is-through .motes{opacity:0;transition:opacity 600ms ease}
.motes--hall{z-index:5;opacity:0}
.hall.is-lit .motes--hall.is-on{opacity:.8}
.hall.is-away .motes--hall{opacity:0;transition:opacity .5s ease}

/* ────────────────────────────────── the grain ──────────────────────────────── */
/* The gallery is a composite: a photographed wall, busts cut from other photographs, and
   light drawn in code. Each carries its own noise, or none, and that mismatch is what
   quietly says "collage". One grain over all of it is the oldest fix there is — at this
   amplitude it adds no visible noise, it only removes the difference between the parts.
   This is not the brutalist scanline-and-halftone treatment, which degrades an image on
   purpose; and it is laid over the photographic layers only, so text panels stay clean. */
.scene::before,.hall::before{content:'';position:absolute;inset:0;pointer-events:none;
  background:url("img/grain.webp") repeat;background-size:168px 168px;
  /* soft-light, not overlay: overlay amplifies deviations in the highlights, which put
     visible mottling straight into the light pools — the one place it must not go */
  mix-blend-mode:soft-light;opacity:.55}
.scene::before{z-index:6}
.hall::before{z-index:7}
/* it belongs to the room, so it steps back with the room */
.hall::before{transition:opacity .6s ease}
.hall.is-away::before{opacity:.24}
