/* THE WALL. Three layers of the same content:
   1. default (no JS, or reduced motion): a plain list of lit paintings;
   2. html.wall-on: the oblique 3D wall, built by js/wall.js, var(--step) x 100dvh of free scroll per painting, one lamp lit;
   3. html.open: one painting turned to face you, with its label. */

.wall{position:relative;background:linear-gradient(180deg,#0c0b0a 0%,#100f0e 50%,#080807 100%)}
.wall h2,.wall h3{font-family:"Archivo",sans-serif;font-weight:800;font-stretch:112%}
.intro h2{font-size:64px;line-height:.95;letter-spacing:-.035em}
.intro p{margin-top:16px;font-size:17px;line-height:1.5;color:var(--ink-2);max-width:38ch}
.wn{color:var(--ink-3);font-family:"Geist",sans-serif;font-weight:400;letter-spacing:0}

/* ---------- 1. the list ---------- */
html:not(.wall-on) .wall .sticky{padding:140px var(--gut) 0;max-width:1240px;margin:0 auto}
html:not(.wall-on) :is(.wall3d,.focus,.plan,#stops){display:none}
.pieces{list-style:none;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));align-items:end;gap:140px 9vw;max-width:1240px;margin:0 auto;padding:120px var(--gut) 180px}
.piece{display:flex;flex-direction:column;align-items:center;text-align:center;scroll-margin-top:96px}
.piece-link{position:relative;display:block;margin-top:60px;isolation:isolate;cursor:pointer}
.piece-link img{width:auto;height:auto;max-width:100%;max-height:calc(var(--k,1)*72vh);min-height:120px;box-shadow:0 34px 30px -22px rgba(0,0,0,.9)}
/* brass picture light and its beam: the same conic cone as the 3D wall, no radial blobs */
.piece-link::before{content:"";position:absolute;left:19%;right:19%;top:-44px;height:8px;border-radius:6px;
  background:linear-gradient(180deg,#d9c79f 0%,var(--lamp) 35%,#7a6a4e 70%,#4a4031 100%);box-shadow:0 2px 0 rgba(255,236,205,.95)}
.piece-link::after{content:"";position:absolute;z-index:-1;left:-55%;right:-55%;top:-40px;bottom:-22%;pointer-events:none;
  background:conic-gradient(from 145deg at 50% -8%,rgba(var(--light),0) 0deg,rgba(var(--light),0) 8deg,rgba(var(--light),.22) 12deg,rgba(var(--light),.32) 22deg,rgba(var(--light),.34) 35deg,rgba(var(--light),.32) 48deg,rgba(var(--light),.22) 58deg,rgba(var(--light),0) 62deg,rgba(var(--light),0) 70deg);
  -webkit-mask-image:linear-gradient(180deg,transparent 0%,#000 3%,#000 15%,rgba(0,0,0,.6) 45%,rgba(0,0,0,.25) 75%,transparent 100%),linear-gradient(90deg,transparent 0%,#000 14%,#000 86%,transparent 100%);-webkit-mask-composite:source-in;
          mask-image:linear-gradient(180deg,transparent 0%,#000 3%,#000 15%,rgba(0,0,0,.6) 45%,rgba(0,0,0,.25) 75%,transparent 100%),linear-gradient(90deg,transparent 0%,#000 14%,#000 86%,transparent 100%);mask-composite:intersect}
.piece h3{margin-top:34px;font-size:28px;line-height:1.05;letter-spacing:-.03em}
.piece h3 .wn{display:block;margin-top:6px;font-size:15px;line-height:1.3;color:var(--ink-2)}
.piece p{margin-top:8px;font-size:15px;line-height:1.5;color:var(--ink-2)}

/* ---------- 2. the 3D wall ---------- */
/* --step: screens of scroll per painting (0.7 keeps 23 works near 17 screens); js/wall.js reads it */
.wall{--step:.7}
.wall-on .wall{height:calc(100vh + (var(--stops) - 1)*var(--step)*100vh);height:calc(100svh + (var(--stops) - 1)*var(--step)*100svh);background:#070706}   /* svh, not dvh: the phone address bar must not resize the wall (it rebuilt the camera and flickered); the floor colour fills under the sticky when the bar hides */
.wall-on .pieces{display:none}
/* the stops only give the section its scroll length; they must never sit over the paintings (they ate every click) */
.wall-on #stops{position:absolute;inset:0;pointer-events:none}
.wall-on .stop{height:100vh;height:100svh}
.wall-on .wall .sticky{position:sticky;top:0;height:100vh;height:100svh;overflow:hidden;overflow:clip;perspective:1000px;perspective-origin:68% 42%}
/* one flat plane (no preserve-3d): the browser composites it as a single layer and moves it without repainting */
.wall3d{position:absolute;left:0;top:0;width:8600px;height:2000px;transform-origin:0 0;
  background:linear-gradient(180deg,#141311 0px,#100F0E 980px,#2a2723 1000px,#0B0B0A 1003px,#080807 1600px,#070706 2000px);will-change:transform}
.wall3d.cam{transition:transform .9s var(--ease-cam)}
/* coming back, the camera leaves the close-up gently: a fast start reads as a jump because the painting is nearest the lens */
.wall3d.cam.back{transition:transform .7s cubic-bezier(.65,0,.35,1)}
/* "The wall" on the plaster: dim until the section arrives, then a strong wash comes on over it and the lettering
   turns bone, like vinyl under a gallery light. Bone holds 7.8:1 even where the wash is brightest (plaster + 24% light). */
.walltitle{position:absolute;font-family:"Archivo",sans-serif;font-weight:800;font-stretch:125%;letter-spacing:-.04em;line-height:.85;font-size:250px;white-space:nowrap;pointer-events:none;transition:opacity .4s var(--ease)}
.walltitle .tb{color:#26231F}
.walltitle .tl{position:absolute;left:0;top:0;color:var(--ink)}
.walltitle .tw{position:absolute;left:-14%;right:-14%;top:-60%;bottom:-35%;
  background:linear-gradient(180deg,rgba(var(--light),.24) 0%,rgba(var(--light),.2) 45%,rgba(var(--light),.08) 80%,rgba(var(--light),0) 100%);
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 18%,#000 82%,transparent);mask-image:linear-gradient(90deg,transparent,#000 18%,#000 82%,transparent)}
.walltitle .lt{opacity:0;transition:opacity .9s var(--ease)}
.walltitle.on .lt{opacity:1}


/* the hang + light physics (critic-approved, from the night-studio concept) */
.work{position:absolute;display:block;cursor:pointer;color:inherit;text-decoration:none}
.work:focus-visible{outline-offset:10px}
.veil{position:absolute;inset:0;z-index:5;background:#0b0a09;opacity:0;pointer-events:none;will-change:opacity;transition:opacity .6s var(--ease)}
html.open .veil{opacity:.82}
.work.front{z-index:6}
html.open .work .tag{opacity:0}   /* the little wall tag would scale up with the painting and sit on the label text */
/* beam: conic spread from the lamp bar, crisp cone edge, strong core near the bar, distance falloff via mask */
.work .wash{position:absolute;pointer-events:none;
  -webkit-mask-image:linear-gradient(180deg,transparent 0%,#000 3%,#000 15%,rgba(0,0,0,.6) 45%,rgba(0,0,0,.25) 75%,transparent 100%),linear-gradient(90deg,transparent 0%,#000 10%,#000 90%,transparent 100%);
  -webkit-mask-composite:source-in;
          mask-image:linear-gradient(180deg,transparent 0%,#000 3%,#000 15%,rgba(0,0,0,.6) 45%,rgba(0,0,0,.25) 75%,transparent 100%),linear-gradient(90deg,transparent 0%,#000 10%,#000 90%,transparent 100%);
          mask-composite:intersect}
.work .wash::before{content:"";position:absolute;inset:0;
  background:conic-gradient(from 145deg at 50% -34%,rgba(var(--light),0) 0deg,rgba(var(--light),0) 8deg,rgba(var(--light),.28) 12deg,rgba(var(--light),.40) 22deg,rgba(var(--light),.42) 35deg,rgba(var(--light),.40) 48deg,rgba(var(--light),.28) 58deg,rgba(var(--light),0) 62deg,rgba(var(--light),0) 70deg)}
/* spill: light bouncing off the canvas onto the wall beside and below it (~0.3 canvas height) */
.work .spill{position:absolute;left:-30%;right:-30%;top:-4%;bottom:-30%;pointer-events:none;
  background:linear-gradient(180deg,rgba(var(--light),.11) 0%,rgba(var(--light),.07) 60%,rgba(var(--light),0) 100%);
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 35%,#000 65%,transparent),linear-gradient(180deg,transparent,#000 12%,#000 70%,transparent);-webkit-mask-composite:source-in;
          mask-image:linear-gradient(90deg,transparent,#000 35%,#000 65%,transparent),linear-gradient(180deg,transparent,#000 12%,#000 70%,transparent);mask-composite:intersect}
/* cast shadow straight down (the lamp is above): a static box-shadow, painted once, no live blur */
.work .canvas{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;box-shadow:0 30px 24px -18px rgba(0,0,0,.85)}
/* a diptych hangs as two panels with a 6px gap and their own shadows */
.work .split{position:absolute;top:0;bottom:0;overflow:hidden;box-shadow:0 22px 22px -12px rgba(0,0,0,.85)}
.work .split img{position:absolute;top:0;height:100%;max-width:none}   /* widths inline: js/wall.js canvasHTML() */
.work .edge{position:absolute;left:0;right:0;top:0;height:2px;background:rgba(var(--light),.6)} /* lit top edge of the stretcher */
/* falloff on the canvas itself: bright under the lamp, darker toward the bottom */
.work .shade{position:absolute;inset:0;background:linear-gradient(180deg,rgba(var(--light),.12) 0%,rgba(0,0,0,0) 22%,rgba(0,0,0,.30) 100%)}
.work .lamp{position:absolute;left:50%;transform:translateX(-50%)}
.lamp .mount{position:absolute;left:50%;transform:translateX(-50%);background:linear-gradient(180deg,#6d6150,#3a3329)}
.lamp .stem{position:absolute;left:50%;transform:translateX(-50%);width:2px;background:linear-gradient(180deg,#5c5142,#9a8866)}
.lamp .bar{position:absolute;left:0;right:0;border-radius:6px;
  background:linear-gradient(180deg,#d9c79f 0%,var(--lamp) 35%,#7a6a4e 70%,#4a4031 100%);
  box-shadow:0 1px 0 rgba(255,240,210,.5) inset}
.lamp .bar::after{content:"";position:absolute;left:4%;right:4%;bottom:-2px;height:2px;border-radius:2px;background:rgba(255,236,205,.95);opacity:0;transition:opacity .45s var(--ease)}
/* floor: true mirror reflection + a soft specular streak under each lit lamp */
.work .refl{position:absolute;left:0;width:100%;transform:scaleY(-1);opacity:.02;transition:opacity .45s var(--ease);
  -webkit-mask-image:linear-gradient(0deg,#000 0%,rgba(0,0,0,.35) 30%,transparent 55%);
          mask-image:linear-gradient(0deg,#000 0%,rgba(0,0,0,.35) 30%,transparent 55%)}
.work .refl>img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.work .streak{position:absolute;left:30%;width:40%;pointer-events:none;
  background:linear-gradient(180deg,rgba(var(--light),.08),rgba(var(--light),0));
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 40%,#000 60%,transparent);mask-image:linear-gradient(90deg,transparent,#000 40%,#000 60%,transparent)}
/* lamp states: exactly one painting (.on) is lit. Light comes on and goes off as opacity only (450ms):
   the light layers fade in, the darkness over the canvas fades out. */
.work .lt{opacity:0;transition:opacity .45s var(--ease)}
.work .dim{position:absolute;inset:0;background:rgba(9,8,7,.9);transition:opacity .45s var(--ease)}
.work.on .lt,.work.on .lamp .bar::after{opacity:1}
.work.on .dim{opacity:0}
.work.on .refl{opacity:.14}
/* tiny physical wall labels in the room: they follow the lamp; faux lines (real text is unreadable at 11 cm) */
.tag{position:absolute;width:30px;padding:4px 4px 5px;background:#B2AFAA;box-shadow:0 3px 5px -2px rgba(0,0,0,.9);opacity:.2;transition:opacity .45s var(--ease)}
.tag i{display:block;height:1px;background:#4a4743;margin:2px 0}
.work.on .tag{opacity:1}

/* HUD */
.wall-on .hud{position:absolute;z-index:10;transition:opacity .35s var(--ease),visibility .35s;will-change:opacity}
.wall-on .focus{left:var(--gut);bottom:64px;width:460px}
.focus h3{font-size:44px;line-height:1;letter-spacing:-.03em}
.focus .wnl{display:block;margin-top:8px;font:400 17px/1.3 "Geist",sans-serif;color:var(--ink-2)}   /* Ash, not Graphite: 5.8:1 even on a fully lit wall */
.focus p{font-size:15px;line-height:1.5;color:var(--ink-2);margin-top:10px}
.focus .row{display:flex;align-items:center;gap:16px;margin-top:22px}
.note{font-size:12.5px;color:var(--ink-2)}
.wall-on .intro{left:var(--gut);bottom:64px;width:520px}
.wall-on .plan{right:var(--gut);bottom:56px;width:min(520px,36vw)}
.plan .cap2{display:flex;justify-content:space-between;font-size:13px;color:var(--ink-2);margin-bottom:12px}
.plan .line{position:relative;height:46px;border-bottom:1px solid #4a4640}
.plan .line i{position:absolute;bottom:0;background:#5A554E;transition:background .42s}
.plan .line i.lit{background:#d8cdb8}
.plan .you{position:absolute;left:0;bottom:-12px;width:1px;height:62px;background:var(--ink)}
.plan .you::after{content:attr(data-label);position:absolute;bottom:-18px;left:50%;transform:translateX(-50%);font-size:12px;color:var(--ink)}
.wall-on .sticky.arrival .focus,.wall-on .sticky:not(.arrival) .intro,html.open .hud{opacity:0;visibility:hidden;pointer-events:none}

/* ---------- 3. one painting, front-on, its label beside it ---------- */
/* visibility flips at once on the way in (so focus can land on the title in the same moment) and only after the fade on
   the way out; a plain visibility transition stays "hidden" for its first frame and focus() silently fails */
.art{position:fixed;inset:0;z-index:30;pointer-events:none;opacity:0;visibility:hidden;transition:opacity .3s var(--ease),visibility 0s linear .3s;will-change:opacity}
html.open.settled .art{opacity:1;visibility:visible;transition:opacity .3s var(--ease),visibility 0s}
html.open.settled .art .ctl{pointer-events:auto}
/* the title column (owner's choice: kept beside the museum label). Its width is mirrored in js/wall.js frameFor() */
.art .left{position:absolute;left:var(--gut);top:30vh;width:min(380px,22vw)}
.art .left .k{font-size:14px;color:var(--ink-2)}
.art .left .big{font-family:"Archivo",sans-serif;font-weight:800;font-stretch:112%;font-size:min(clamp(44px,5vw,72px),calc(min(380px,22vw) / (var(--lw,8) * .7)));line-height:.95;letter-spacing:-.035em;margin-top:14px;overflow-wrap:break-word}   /* --lw: longest word, set by js; a 9-letter word still fits the column */
.art .left .wt{font:400 22px/1.2 "Geist",sans-serif;color:var(--ink-3);margin-top:12px}
/* Look closer: desktop, a 44px pill centred 40px under the painting (placeLabel); stacked, top-left beside Back */
.art .look{position:absolute;left:0;top:0;height:44px;padding:0 18px;font-size:15px;transform:translateX(-50%)}
.art .look svg{flex:none}
.aend{position:absolute;left:0;width:1px;height:1px;pointer-events:none}
/* the open painting is the way into Look closer */
.ahit{position:absolute;display:none;cursor:zoom-in}
html.wall-on.open.settled .ahit{display:block}
html:not(.wall-on) .flat-img{cursor:zoom-in}
.art .back{position:absolute;right:var(--gut);top:24px;height:48px}
/* list mode has no camera to turn, so the panel carries the painting itself on a solid ground */
.flat-img{display:none}
html:not(.wall-on) .art{background:var(--night);pointer-events:auto}
html:not(.wall-on) .flat-img{display:block;position:absolute;max-width:none;box-shadow:0 34px 30px -22px rgba(0,0,0,.9)}   /* framed by js/wall.js frameFor(), the same rule as the camera */

@media (max-width:767px){
  html:not(.wall-on) .wall .sticky{padding-top:110px}
  .intro h2{font-size:40px}
  .pieces{grid-template-columns:1fr;gap:96px;padding:90px 20px 120px}
  .piece-link img{max-height:calc(var(--k,1)*64vh)}
  .wall-on .wall .sticky{perspective-origin:50% 40%}
  .walltitle{font-size:120px;white-space:normal;width:min-content}   /* two lines, so the whole lettering is in frame on arrival */
  .wall-on .focus,.wall-on .intro{left:20px;right:20px;width:auto;bottom:28px}
  .focus h3{font-size:32px}
  .plan{display:none}
}
/* stacked open view (under 1024px): lamp and painting take the top ~60% of the screen; the name, the label and Inquire
   hang under the painting and continue below the fold. The dialog itself scrolls; js/wall.js moves the wall stage with it
   so lamp, painting and label travel as one wall. placeLabel() sets every position. */
@media (max-width:1023px){
  .art{overflow-y:auto;overscroll-behavior:contain}
  html.open.settled .art{pointer-events:auto}
  .art .kbd{display:none}   /* touch: no Esc hint, so Back and Look closer share the top row */
  .art .left{top:0;width:auto}
  .art .left .big{font-size:34px;margin-top:0}.art .left .k,.art .left .wt{display:none}
  .art .look{left:12px;top:12px;transform:none}
  .art .back{right:12px;top:12px}
  .focus .note{display:none}   /* the key hints would run into the plan strip */
}
/* ---------- Look closer: the painting alone on night, zoom and pan with transforms ---------- */
.zoom{position:fixed;inset:0;z-index:5;background:var(--night);opacity:0;visibility:hidden;pointer-events:none;overflow:hidden;
  touch-action:none;cursor:grab;transition:opacity .25s var(--ease),visibility 0s linear .25s}
.zoom:focus{outline:none}
html.zoomed .zoom{opacity:1;visibility:visible;pointer-events:auto;transition:opacity .25s var(--ease),visibility 0s}
html.zoomed .zoom.drag{cursor:grabbing}
html.zoomed .nav{opacity:0;visibility:hidden}
.zimg{position:absolute;left:0;top:0;max-width:none;transform-origin:0 0;will-change:transform;user-select:none;-webkit-user-select:none;-webkit-user-drag:none}
.zimg.anim{transition:transform .35s cubic-bezier(.23,1,.32,1)}
.zhud{position:absolute;left:50%;bottom:24px;transform:translateX(-50%);display:flex;align-items:center;gap:6px;padding:5px;border-radius:999px;
  background:rgba(24,23,21,.94);border:1px solid var(--line);box-shadow:0 18px 40px -20px rgba(0,0,0,.9);cursor:default}
.zb{width:44px;height:44px;border-radius:50%;border:1.5px solid var(--ctl);background:var(--raised);color:var(--ink);display:grid;place-items:center;cursor:pointer}
.zb svg{width:16px;height:16px}
.zb:disabled{opacity:.4;cursor:default}
.zn{min-width:64px;text-align:center;font:400 18px/1 "Geist Mono",monospace;letter-spacing:-.03em;font-variant-numeric:tabular-nums;color:var(--ink)}
.zfit{height:44px;margin-left:6px}
@media (max-width:767px){.zhud{bottom:16px}.zn{min-width:56px;font-size:16px}}
@media (prefers-reduced-motion:reduce){
  .zimg.anim,.zoom{transition:none}
  .wall3d.cam{transition:none}
  .walltitle .lt{transition-duration:.2s}
  .art{transition:opacity .2s var(--ease),visibility 0s linear .2s}
  html.open.settled .art{transition:opacity .2s var(--ease),visibility 0s}
}
/* phones: iOS composites a fading light layer on its own and flattens it when the fade ends, so a lamp came on bright
   for a moment and then visibly dimmed. On touch screens the light switches cleanly with no fade, and the floor
   reflections (23 flipped, masked copies of the paintings) are dropped to keep the walk smooth. */
@media (pointer:coarse){
  .work .lt,.work .dim,.work .refl,.lamp .bar::after{transition:none}
}
@media (pointer:coarse) and (max-width:767px){
  .work .refl,.work .streak{display:none}
}
/* lamps near the camera stay composited (js/wall.js .near), so a lamp comes on at its final brightness with no flash */
.work.near .lt,.work.near .dim,.work.near .refl,.work.near .lamp .bar::after{will-change:opacity}
/* touch screens: the light layers that switch (beam, darkness, lamp edge) stay on the GPU for good, so a lamp switch is
   an opacity change on the compositor and never repaints the wall plane (that repaint froze the walk on phones). The
   static extras (spill, canvas shading, stretcher edge) are dropped on phones to keep that layer count small. */
@media (pointer:coarse){
  .work .wash,.work .dim,.lamp .bar::after{will-change:opacity}
}
@media (pointer:coarse) and (max-width:767px){
  .work .spill,.work .shade,.work .edge{display:none}
}
/* phones: a corridor walked forward (js/wall.js CAR). The plane is the stage itself; each painting is placed in depth */
html.wall-car .wall3d{width:100%!important;height:100%;background:radial-gradient(ellipse 70% 55% at 50% 40%,#1b1916 0%,#100f0d 50%,#070706 100%);
  perspective:1000px;perspective-origin:50% 40%;transform:none;will-change:auto}
html.wall-car .wall .sticky{perspective:none}
html.wall-car .corr{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:1}
html.wall-car .wall3d{z-index:2}
html.wall-car .work,html.wall-car .walltitle{will-change:transform,opacity;transform-origin:50% 50%}
html.wall-car .work.cam{transition:transform .9s var(--ease-cam),opacity .3s}
html.wall-car .work.cam.back{transition:transform .7s cubic-bezier(.65,0,.35,1)}
html.wall-car .work.front{z-index:200!important}
html.wall-car .veil{z-index:150}
html.wall-car .seam{position:absolute;width:6000px;height:3px;background:rgba(255,236,205,.06);pointer-events:none}
html.wall-car .work .tag{display:none}
.wall .camspan{position:absolute;left:0;top:0;width:1px;pointer-events:none;visibility:hidden}
