/* ---------------------------------------------------------------------------
   OVERTURE — loading → 3D project space → carousel
   One continuous sequence. Every spatial transform is written by
   assets/js/overture.js in a single rAF pass; this file only describes the
   surfaces, never their position in space.
   --------------------------------------------------------------------------- */

.overture{
  --card-w:clamp(260px,34vw,520px);
  --card-h:calc(var(--card-w) * 0.75);   /* 4:3 still, per the design system */
  position:relative;
  height:100svh;
  background:var(--surface-page);
  /* body resolved --text-primary before [data-theme="night"] scoped it here,
     so re-declare it: descendants inherit a computed colour, not the var. */
  color:var(--text-primary);
}

/* One viewport, in normal flow, pinned to nothing.
   This used to be a tall scroll track with the scene held in place — first by
   position:sticky, then by position:fixed. Both make Chromium drop the pinned
   preserve-3d layer once the scroll offset passes roughly a viewport: the
   geometry stays perfect and not a single pixel is painted. There is no
   reliable way to keep a 3D scene of this size pinned over a long track, so
   the track is gone. The carousel is driven directly instead — by dragging,
   which is what it wanted to be driven by anyway. */
.overture__pin{
  position:relative;
  height:100%;
  overflow:hidden;
  background:var(--surface-page);
  isolation:isolate;
}

/* --- the camera rig ------------------------------------------------------- */

.stage{
  position:absolute;
  inset:0;
  perspective:1500px;
  perspective-origin:50% 50%;
  touch-action:pan-y;
}
.overture.is-resolved .stage{cursor:grab}
.overture.is-resolved .stage.is-dragging{cursor:grabbing}
/* A card is a link and has to look like one. The grab cursor belongs to the
   space between the cards, not to the work itself. */
.overture.is-resolved .slab{cursor:pointer}
.overture.is-resolved .stage.is-dragging .slab{cursor:grabbing}

.world{
  position:absolute;
  inset:0;
  transform-style:preserve-3d;
  will-change:transform;
  /* The world is an invisible full-viewport plane sitting at z:0. Every card
     the ring has turned away from the front is *behind* that plane, so it
     swallowed their clicks — only the centred card, which happens to be
     coplanar with it, was ever clickable. It must never take a hit itself;
     the cards opt back in below and events still bubble to the stage. */
  pointer-events:none;
}

/* --- a project in space --------------------------------------------------- */

.slab{
  position:absolute;
  left:50%;
  top:50%;
  width:var(--card-w);
  margin-left:calc(var(--card-w) / -2);
  margin-top:calc(var(--card-h) / -2);
  display:flex;
  flex-direction:column;
  gap:var(--space-4);
  color:var(--text-primary);
  text-decoration:none;
  transform-style:preserve-3d;
  backface-visibility:hidden;
  /* Pivot on the centre of the *frame*, not of the whole card. Every position
     in overture.js is computed for the frame's centre; without this, the meta
     below it would drag the origin down and every rotation would swing wide. */
  transform-origin:50% calc(var(--card-h) / 2);
  pointer-events:auto;
}
.slab:hover{color:var(--text-primary)}

.slab__frame{
  display:block;
  position:relative;
  height:var(--card-h);
  background:var(--surface-media);
  border:var(--border-width-hairline) solid var(--border-hairline);
  overflow:hidden;
}
/* Full colour, and no CSS filter — a filter here would force a filtered
   compositor layer per card inside the 3D scene, which is what exhausted the
   GPU. The room desaturates the cards for free: everything but the centred
   project sits at low opacity over near-black, which mutes it, so colour
   arrives exactly on whichever project has your attention. */
.slab__frame img{
  width:100%;height:100%;object-fit:cover;
}

/* Placeholder still, in the design system's own language: flat media surface,
   hairline, mono label. Each frame carries a deterministic tonal weight so
   depth still reads before the photography is in place. */
.slab__wash{position:absolute;inset:0;opacity:0.85}
.slab__ph{
  position:absolute;inset:0;
  display:flex;align-items:flex-end;
  padding:var(--space-4);
  font-family:var(--font-mono);
  font-size:var(--size-meta);
  letter-spacing:var(--ls-meta);
  color:var(--text-muted);
}
.slab__idx{
  position:absolute;top:var(--space-4);left:var(--space-4);
  font-family:var(--font-mono);
  font-size:var(--size-meta);
  color:var(--text-muted);
}
/* over photography the index knocks out of the image, per the design system */
.slab__frame.has-img .slab__idx{color:var(--paper);text-shadow:0 1px 4px rgba(11,11,10,0.6)}

.slab__meta{
  display:flex;flex-direction:column;gap:var(--space-2);
}
.slab__row{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--space-4);
}
.slab__title{
  font-size:var(--size-heading-s);
  font-weight:var(--weight-semibold);
  text-transform:uppercase;
  letter-spacing:0.02em;
  transition:color var(--dur-fast) var(--ease-out);
}
.slab.is-focus .slab__title{color:var(--text-link-hover)}
.slab__year{
  font-family:var(--font-mono);
  font-size:var(--size-meta);
  color:var(--text-muted);
  white-space:nowrap;
}
.slab__tags{display:flex;flex-wrap:wrap;gap:var(--space-2)}

/* --- atmospheric layer ---------------------------------------------------- */
/* Unlabelled plates from the archive. They carry depth, never content. */

.plate{
  position:absolute;left:50%;top:50%;
  width:var(--card-w);
  height:calc(var(--card-w) * 0.72);
  margin-left:calc(var(--card-w) / -2);
  margin-top:calc(var(--card-w) * -0.36);
  background:var(--surface-media);
  border:var(--border-width-hairline) solid var(--border-hairline);
  backface-visibility:hidden;
  pointer-events:none;
}
.plate::after{
  content:attr(data-mark);
  position:absolute;left:var(--space-3);bottom:var(--space-2);
  font-family:var(--font-mono);
  font-size:var(--size-meta);
  color:var(--text-faint);
  opacity:0.5;
}

/* --- the invisible structure, made just barely visible -------------------- */

.rail{
  position:absolute;left:50%;top:50%;
  background:var(--border-hairline);
  backface-visibility:hidden;
  pointer-events:none;
  opacity:0;
}

/* --- the far statement ---------------------------------------------------- */
/* The hero line lives inside the space, at the back of the composition. */

.horizon{
  position:absolute;left:50%;top:50%;
  width:min(2400px,210vw);
  margin-left:max(-1200px,-105vw);
  text-align:center;
  color:var(--text-primary);
  opacity:0;
  pointer-events:none;
  backface-visibility:hidden;
}
/* The overture drives .horizon's own transform, so the block cannot be moved
   with a translate there. It moves on the inner wrapper instead, which leaves
   the 3D placement alone.

   A starting position only. Where the block actually lands depends on the
   stage transform and the perspective, so overture.js measures it and writes
   the real offset here as an inline style. This value is what a visitor with
   no JavaScript gets. */
.horizon__inner{transform:translateY(26vh)}

/* The quote is a whole sentence rather than three words, so it takes a size
   that can hold a line of prose at the back of a room, not a size built for a
   slogan. */
.horizon__quote{
  margin:0;
  font-size:clamp(1.75rem,4.2vw,4.25rem);
  font-weight:var(--weight-semibold);
  line-height:var(--lh-display);
  letter-spacing:var(--ls-display);
  text-transform:uppercase;
  text-wrap:balance;
}
/* An attribution has to be readable to be an attribution. It sits in mono, at
   a fraction of the size, and carries most of the element's opacity so it does
   not disappear into the wall behind the work. */
/* The horizon sits at the back of the room, where the perspective scales it to
   about 42% of its declared size. At the old value this line landed at under
   six effective pixels: a grey smudge rather than a credit. Sized here so it
   arrives at around ten, which is still much quieter than the quote. */
.horizon__by{
  margin-top:clamp(0.75rem,1.4vw,1.75rem);
  font-family:var(--font-mono);
  font-size:clamp(1.25rem,2.5vw,2.2rem);
  letter-spacing:var(--ls-meta);
  text-transform:uppercase;
}

/* --- the loading counter, which becomes the space ------------------------- */

.counter{
  position:absolute;left:50%;top:50%;
  width:100%;
  margin-left:-50%;
  margin-top:-0.5em;
  display:flex;align-items:baseline;justify-content:center;gap:var(--space-4);
  transform-style:preserve-3d;
  backface-visibility:hidden;
  pointer-events:none;
}
.counter__n{
  font-size:clamp(4.5rem,20vw,17rem);
  font-weight:var(--weight-semibold);
  line-height:1;
  letter-spacing:var(--ls-display);
  font-variant-numeric:tabular-nums;
  font-feature-settings:"tnum" 1;
}
.counter__pc{
  font-family:var(--font-mono);
  font-size:clamp(0.9rem,2vw,1.4rem);
  color:var(--text-muted);
  letter-spacing:var(--ls-meta);
}

/* --- atmosphere ----------------------------------------------------------- */

.veil{position:absolute;inset:0;pointer-events:none}

.veil--fog{
  /* depth haze: the room dissolves into the page colour at top and bottom */
  background:linear-gradient(to top,rgba(11,11,10,0.42),rgba(11,11,10,0) 26%,rgba(11,11,10,0) 72%,rgba(11,11,10,0.34));
  z-index:2;
}
.veil--vignette{
  background:radial-gradient(128% 100% at 50% 46%,rgba(11,11,10,0) 46%,rgba(11,11,10,0.4) 100%);
  z-index:3;
}
.veil--grain{
  z-index:4;
  opacity:0.14;
  mix-blend-mode:overlay;
  background-image:url("../img/grain.svg");
  animation:grain 1.1s steps(1) infinite;
}
@keyframes grain{
  0%{transform:translate3d(0,0,0)}
  25%{transform:translate3d(-3%,2%,0)}
  50%{transform:translate3d(2%,-3%,0)}
  75%{transform:translate3d(-2%,-2%,0)}
  100%{transform:translate3d(3%,1%,0)}
}

/* --- HUD ------------------------------------------------------------------ */

.hud{
  position:absolute;inset:0;
  z-index:5;
  pointer-events:none;
  padding:calc(var(--nav-height) + var(--space-6)) var(--page-margin) var(--space-8);
  display:flex;flex-direction:column;justify-content:space-between;
}
.hud a,.hud button{pointer-events:auto}

.hud__head{
  display:flex;align-items:flex-start;justify-content:space-between;
  gap:var(--space-5);
}
/* Below about 500px the identity line no longer fits beside the view toggle
   and stacks five words deep, straight over the first project. The nav's
   wordmark already says whose site this is, so on a phone the head keeps the
   toggle and drops the line. */
@media (max-width:560px){
  .hud__head .label{display:none}
}

.hud__head,.hud__foot{
  opacity:0;
  transform:translateY(12px);
  transition:opacity var(--dur-reveal) var(--ease-out),transform var(--dur-reveal) var(--ease-out);
}

/* --- view toggle: carousel ⇄ grid ----------------------------------------- */

.viewtoggle{
  display:inline-flex;
  border:var(--border-width-hairline) solid var(--border-hairline);
  background:rgba(11,11,10,0.35);
  -webkit-backdrop-filter:blur(6px);
  backdrop-filter:blur(6px);
}
.viewtoggle button{
  appearance:none;
  border:0;margin:0;
  padding:9px 16px 8px;
  background:transparent;
  color:var(--text-muted);
  font-family:var(--font-core);
  font-size:var(--size-label);
  font-weight:var(--weight-semibold);
  text-transform:uppercase;
  letter-spacing:var(--ls-label);
  line-height:1;
  cursor:pointer;
  transition:background var(--dur-fast) var(--ease-out),color var(--dur-fast) var(--ease-out);
}
.viewtoggle button + button{border-left:var(--border-width-hairline) solid var(--border-hairline)}
.viewtoggle button:hover{color:var(--text-primary)}
.viewtoggle button.is-on{background:var(--paper);color:var(--ink-900)}

/* In the grid every project is already on screen: there is no "current" one
   to count, and nothing to step through. */
.overture.is-grid .counterline{opacity:0;pointer-events:none}
/* The grid travels past the top and bottom of its band, so it needs a real
   edge to disappear into rather than being cut by the nav.

   Both fades reach full before the HUD does. The card titles used to appear
   only on hover here, so a thin gradient was enough to see cards off the
   screen; now that every card carries its name, that name would otherwise
   scroll straight through "Continue" and the line above it. */
.overture.is-grid .veil--fog{
  background:linear-gradient(to top,
    rgba(11,11,10,0.99) 0%,
    rgba(11,11,10,0.86) 12%,
    rgba(11,11,10,0)    29%,
    rgba(11,11,10,0)    74%,
    rgba(11,11,10,0.95) 82%,
    rgba(11,11,10,0.99) 100%);
}
.overture.is-grid .veil--vignette{opacity:0.45}
/* The whole right-hand foot goes in grid mode. Measured on a 682 px viewport,
   the card titles occupy 10.5% to 21% up from the bottom edge and "Continue"
   occupies 9.4% to 12.9%: they are in the same band, so no amount of tuning
   the fog makes both readable. Neither piece is needed here. The carousel has
   to say it can be dragged, because a ring of cards in perspective does not
   look like something you scroll; a grid does. And "Continue" only skips to
   the section below, which the nav does from any scroll position. */
.overture.is-grid .hud__right{display:none}
.overture.is-grid .stage{cursor:default}
.overture.is-resolved .hud__head,
.overture.is-resolved .hud__foot{opacity:1;transform:none}
.overture.is-resolved .hud__foot{transition-delay:120ms}

/* Coming back to the carousel mid-session: the overture is already over, so
   nothing should announce itself. The counter never existed on this visit and
   the HUD is simply there, rather than arriving. */
.overture.is-instant .counter{display:none}
.overture.is-instant .hud__progress{display:none}
.overture.is-instant .hud__head,
.overture.is-instant .hud__foot{transition:none}

.hud__foot{
  display:flex;align-items:flex-end;justify-content:space-between;gap:var(--space-6);
}
.hud__right{display:flex;flex-direction:column;align-items:flex-end;gap:var(--space-3);text-align:right}

.hud__progress{
  position:absolute;left:var(--page-margin);right:var(--page-margin);bottom:var(--space-8);
  height:1px;background:var(--border-hairline);
  transition:opacity var(--dur-slow) var(--ease-out);
}
.hud__progress span{
  display:block;height:100%;width:100%;
  background:var(--text-primary);
  transform:scaleX(0);
  transform-origin:0 50%;
}
.overture.is-loaded .hud__progress{opacity:0}

.counterline{
  display:flex;align-items:center;gap:var(--space-4);
  font-family:var(--font-mono);font-size:var(--size-meta);letter-spacing:var(--ls-meta);
}
.counterline i{font-style:normal;opacity:0.4}
.counterline__nav{display:flex;align-items:center}
.counterline__nav button{
  background:none;border:0;margin:0;padding:var(--space-2);
  font-family:var(--font-core);font-size:var(--size-body-m);line-height:1;
  color:inherit;cursor:pointer;
  transition:opacity var(--dur-fast) var(--ease-out);
}
.counterline__nav button:hover{opacity:0.5}

.hud__hint{
  font-family:var(--font-mono);font-size:var(--size-meta);
  letter-spacing:var(--ls-meta);color:var(--text-muted);
}

/* --- skip ----------------------------------------------------------------- */

.skip{
  position:absolute;right:var(--page-margin);bottom:calc(var(--space-8) + 22px);
  z-index:6;
  background:none;border:0;padding:var(--space-2) 0;margin:0;
  font-family:var(--font-core);font-size:var(--size-label);font-weight:var(--weight-semibold);
  text-transform:uppercase;letter-spacing:var(--ls-label);
  color:var(--text-muted);cursor:pointer;
  transition:opacity var(--dur-base) var(--ease-out),color var(--dur-fast) var(--ease-out);
}
.skip:hover{color:var(--text-primary)}
.overture.is-resolved .skip{opacity:0;pointer-events:none}

/* --- reduced motion ------------------------------------------------------- */

@media (prefers-reduced-motion:reduce){
  .veil--grain{animation:none}
}

/* --- narrow --------------------------------------------------------------- */

@media (max-width:760px){
  .overture{--card-w:min(58vw,280px)}
  .hud__foot{flex-direction:column;align-items:flex-start;gap:var(--space-5)}
  .hud__right{align-items:flex-start;text-align:left}
  .veil--grain{display:none}
}

/* The caption under a card is written for a card 380px wide. On a phone that
   card is 209px, and the caption did not shrink with it: the name and the
   credit went on sharing one baseline row, so "Mude – João Machado" was
   deciding its line breaks inside about 130px and arriving as two lines, and
   the two category chips wrapped to a second row underneath. Between them they
   made the caption 124px tall, which put "Exhibition" directly on top of the
   counter and the scrub — measured at 360x780, the meta ended 21px below where
   the foot of the HUD begins.

   So the caption states less on a small card. The name takes the full width on
   its own line with the credit under it, which is the order you read them in
   anyway, and the chips go: on a 209px card they are a third level of
   information competing with the photograph, and every one of them is repeated
   in the work grid further down the same page and again on the project itself.
   Nothing here is the only place a reader can learn it. */
@media (max-width:700px){
  .slab__row{
    flex-direction:column;
    align-items:flex-start;
    gap:2px;
  }
  .slab__tags{display:none}
}
