/* BANKED — concept study by BAD Studio.
   One room, one chair, one lens. Scroll advances the evening; the frame never
   moves. Every transition on this page is a cross-dissolve, driven from
   js/banked.js by writing opacity. Nothing here translates, scales or parallaxes. */

:root {
  --ground: #0A0807;
  --bone: #F2EDE4;
  --bone-70: rgba(242, 237, 228, .70);
  /* Quietest the type is allowed to get. Bone at .45 over this ground measures
     4.0:1 — under the floor for small text — so anything anyone has to read
     bottoms out here, and .45 is kept for rules and borders. */
  --bone-quiet: rgba(242, 237, 228, .62);
  --bone-45: rgba(242, 237, 228, .45);
  --bone-32: rgba(242, 237, 228, .32);
  --bone-18: rgba(242, 237, 228, .18);
  --ember: #C8722E;
  --serif: "Instrument Serif", "Times New Roman", serif;
  --sans: "Inter Tight", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --gutter: clamp(1.5rem, 5vw, 5rem);
  /* Horizontal framing of the photographs. Narrow viewports crop toward the
     hearth so it never leaves the frame; see the media query below. */
  --focus-x: 50%;
}

*, *::before, *::after { box-sizing: border-box; }

/* The sound control and the two states of the Reserve sheet are toggled with
   the `hidden` attribute, and every one of them also carries an author
   `display` — which silently beats the UA sheet's [hidden] rule. */
[hidden] { display: none !important; }

html { background: var(--ground); }

body {
  margin: 0;
  background: var(--ground);
  color: var(--bone);
  font-family: var(--sans);
  font-weight: 300;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; border: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

:focus-visible {
  outline: 1px solid var(--bone);
  outline-offset: 4px;
}

/* ── The stage ─────────────────────────────────────────────────────────────
   Fixed, full viewport, six stacked layers. A layer only ever changes opacity.
   Stacking is in beat order, so a layer fading in is a dissolve over the one
   below and a layer fading out reveals it again — which is how beat 5 gets
   back to the beat 3 photograph without a second copy of it. */

.stage {
  position: fixed;
  inset: 0;
  z-index: 0;
  background: var(--ground);
}

.layer {
  position: absolute;
  inset: 0;
  opacity: 0;
  will-change: opacity;
}
.layer[data-layer="1"] { opacity: 1; }
.layer[data-layer="2"] { z-index: 2; }
.layer[data-layer="3"] { z-index: 3; }
.layer[data-layer="4"] { z-index: 4; }
.layer[data-layer="6"] { z-index: 6; }

.layer img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--focus-x) 50%;
  display: block;
}

/* The plate is composed right of centre, so it is framed on itself rather than
   pulled toward the hearth on narrow viewports. */
.layer[data-layer="4"] img { object-position: var(--focus-plate, 50%) 50%; }

/* ── The fire ──────────────────────────────────────────────────────────────
   The beat-1 loop, kept for the whole evening. It shares the layers' framing
   (same photograph, same object-position) so its hearth lands exactly on the
   stills' hearth; the mask confines it there and screen blending means only
   its light touches the beat below — the frame still never moves. Opacity is
   the engine's, like every layer. Never faded in by CSS: visible by default,
   removed outright if the loop cannot prove it is playing. --fire-x is where
   the hearth sits in the *viewport*, which shifts with the crop. */
.fire {
  position: absolute;
  inset: 0;
  z-index: 7;
  opacity: 1;
  will-change: opacity;
  pointer-events: none;
  mix-blend-mode: screen;
  -webkit-mask-image: radial-gradient(42% 54% at var(--fire-x, 21%) 50%, #000 30%, transparent 74%);
  mask-image: radial-gradient(42% 54% at var(--fire-x, 21%) 50%, #000 30%, transparent 74%);
}
.fire video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--focus-x) 50%;
  display: block;
}
html[data-video="fallback"] .fire { display: none; }

/* Overlay that takes the room down behind the menu. */
.darken {
  position: absolute;
  inset: 0;
  z-index: 5;
  background: var(--ground);
  opacity: 0;
  will-change: opacity;
}

/* Photographic vignette, present at every beat. This is a treatment of the
   image, not a scrim behind a text block — it is what keeps type legible on a
   phone where the copy cannot be placed off to one side. */
.vignette {
  position: absolute;
  inset: 0;
  z-index: 8;
  pointer-events: none;
  background:
    linear-gradient(to top, rgba(10, 8, 7, .78) 0%, rgba(10, 8, 7, .30) 26%, transparent 52%),
    radial-gradient(120% 90% at 50% 42%, transparent 42%, rgba(10, 8, 7, .55) 100%);
}

/* End of the page: the room goes out. */
.blackout {
  position: absolute;
  inset: 0;
  z-index: 9;
  background: var(--ground);
  opacity: 0;
  pointer-events: none;
  will-change: opacity;
}

/* ── Copy ──────────────────────────────────────────────────────────────────
   Also fixed. It fades in and out; it never slides. */

.copy-deck {
  position: fixed;
  inset: 0;
  z-index: 10;
  pointer-events: none;
}

.copy {
  position: absolute;
  inset: 0;
  padding: max(var(--gutter), env(safe-area-inset-top)) var(--gutter)
           max(var(--gutter), env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  opacity: 0;
  visibility: hidden;
  will-change: opacity;
}
.copy > * { pointer-events: auto; }

/* Beat 1 — low in the frame, left. */
.copy--arrival { justify-content: flex-end; align-items: flex-start; }

/* Beats 2 and 6 — the dark right-hand third, high in the frame.
   Not vertically centred: measured down the right third, p95 luminance runs
   0.006 at the top, 0.034 by a third of the way down, then climbs hard to
   0.35 across the middle where the near table and the far tables catch the
   light. Bone type loses against that at 2.1:1. So the copy sits in the top
   third and has to *end* there — which is why the lede is set wide enough to
   stay short rather than as narrow a measure as it would otherwise want. */
.copy--right {
  justify-content: flex-start;
  align-items: flex-start;
  padding-top: max(var(--gutter), 8vh);
  padding-left: 58%;
}

/* Beat 5 — a column on the left, as if the card were on the table. */
.copy--menu { justify-content: center; align-items: flex-start; }

.wordmark {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(2.6rem, 8.5vw, 5.6rem);
  line-height: 1;
  letter-spacing: .20em;
  text-indent: .20em;      /* balances the trailing letterspace */
  text-transform: uppercase;
  margin: 0 0 .5rem;
  color: var(--bone);
}

.standfirst {
  margin: 0;
  font-size: clamp(.82rem, 1.5vw, .95rem);
  letter-spacing: .10em;
  text-transform: uppercase;
  color: var(--bone-70);
}

.lede {
  margin: 0 0 .9em;
  max-width: 27ch;
  font-size: clamp(1.05rem, 1.9vw, 1.45rem);
  font-weight: 300;
  line-height: 1.5;
  color: var(--bone);
  text-wrap: pretty;
}
/* The second sentence was set a stop down, which read nicely and made it the
   first thing to fail: at 70% bone it fell under 4.5:1 across 2.25% of its own
   box on short viewports, where the full-strength line above it never did. It
   is also the payoff line, so it holds full strength. */

.menu__title {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(2rem, 4.6vw, 3.2rem);
  line-height: 1;
  margin: 0 0 .45rem;
  letter-spacing: .01em;
}
.menu__note {
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(1rem, 1.9vw, 1.25rem);
  color: var(--bone-70);
  margin: 0 0 2.4rem;
}
.menu {
  list-style: none;
  margin: 0;
  padding: 0;
  font-family: var(--serif);
  font-size: clamp(1.12rem, 2.1vw, 1.5rem);
  line-height: 2.35;
  color: var(--bone);
}

.seats {
  margin: 0 0 1.6rem;
  max-width: 16ch;
  font-size: clamp(1.05rem, 1.9vw, 1.45rem);
  font-weight: 300;
  line-height: 1.5;
  color: var(--bone-70);
}

.btn {
  appearance: none;
  display: inline-block;
  background: transparent;
  border: 1px solid var(--bone-45);
  color: var(--bone);
  font: inherit;
  font-size: .82rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  text-indent: .22em;
  padding: 1.05em 2.1em;
  cursor: pointer;
  transition: border-color .35s ease, background-color .35s ease;
}
.btn:hover, .btn:focus-visible {
  border-color: var(--bone);
  background: rgba(242, 237, 228, .06);
}
.btn--wide { width: 100%; margin-top: 1.4rem; }

.sound {
  appearance: none;
  background: none;
  border: 0;
  padding: .55rem .1rem;
  margin-top: 1.4rem;
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  color: var(--bone-quiet);
  font: inherit;
  font-size: .72rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color .35s ease;
}
.sound:hover, .sound:focus-visible, .sound[aria-pressed="true"] { color: var(--bone); }
.sound svg {
  width: 1.05rem; height: 1.05rem;
  fill: none; stroke: currentColor; stroke-width: 1.4;
  stroke-linecap: round; stroke-linejoin: round;
}
.sound .spk { fill: currentColor; stroke-width: 1; }
.sound .wv { opacity: 0; }
.sound[aria-pressed="true"] .wv { opacity: 1; }
.sound[aria-pressed="true"] .mut { opacity: 0; }

.colophon {
  position: absolute;
  left: var(--gutter);
  right: var(--gutter);
  bottom: max(var(--gutter), env(safe-area-inset-bottom));
  margin: 0;
  opacity: 0;
  visibility: hidden;
  font-size: .74rem;
  line-height: 1.85;
  letter-spacing: .04em;
  color: var(--bone-quiet);
  will-change: opacity;
}
.colophon > * { margin: 0; pointer-events: auto; }
.colophon a { color: var(--bone-70); text-decoration: none; border-bottom: 1px solid var(--bone-18); }
.colophon a:hover { color: var(--bone); }

/* ── Scroll runway ─────────────────────────────────────────────────────────
   Seven empty blocks. Six beats plus a short tail the room goes dark over. */

.runway { position: relative; z-index: 1; pointer-events: none; }
.beat { height: 100svh; }
.beat--tail { height: 60svh; }

/* ── Reserve ───────────────────────────────────────────────────────────── */

.sheet {
  border: 1px solid var(--bone-18);
  background: #0F0C0A;
  color: var(--bone);
  padding: 0;
  width: min(24rem, calc(100vw - 2rem));
  margin: auto;
}
.sheet::backdrop { background: rgba(6, 5, 4, .86); }

.sheet { position: relative; }
.sheet__close {
  position: absolute;
  top: .85rem; right: .85rem;
  outline-offset: 2px;   /* the default 4px clips on the sheet's corner */
  appearance: none;
  background: none;
  border: 0;
  padding: .6rem;
  cursor: pointer;
  color: var(--bone-quiet);
}
.sheet__close:hover { color: var(--bone); }
.sheet__close svg {
  display: block; width: .85rem; height: .85rem;
  fill: none; stroke: currentColor; stroke-width: 1.3; stroke-linecap: round;
}

.sheet__body, .sheet__done { padding: clamp(1.75rem, 5vw, 2.5rem); }

.sheet__title {
  font-family: var(--serif);
  font-weight: 400;
  font-size: 2rem;
  line-height: 1;
  margin: 0 0 1.9rem;
}

.field { margin-bottom: 1.15rem; }
.field label {
  display: block;
  font-size: .68rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--bone-quiet);
  margin-bottom: .5rem;
}
.field input,
.field select {
  appearance: none;
  width: 100%;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--bone-32);
  border-radius: 0;
  color: var(--bone);
  font: inherit;
  font-size: 1rem;
  padding: .45rem 0;
  cursor: pointer;
}
.field select {
  background-image: linear-gradient(45deg, transparent 50%, var(--bone-45) 50%),
                    linear-gradient(135deg, var(--bone-45) 50%, transparent 50%);
  background-position: right .5rem center, right .215rem center;
  background-size: .32rem .32rem;
  background-repeat: no-repeat;
  padding-right: 1.4rem;
}
.field option { background: #0F0C0A; color: var(--bone); }
.field input:focus, .field select:focus { outline: none; border-bottom-color: var(--bone); }
.field input:focus-visible, .field select:focus-visible { outline: 1px solid var(--bone); outline-offset: 3px; }
/* The native date picker indicator is near-black on near-black by default. */
.field input[type="date"]::-webkit-calendar-picker-indicator {
  filter: invert(1);
  opacity: .45;
  cursor: pointer;
}
.field input[type="date"]::-webkit-calendar-picker-indicator:hover { opacity: 1; }

.sheet__done-line {
  font-family: var(--serif);
  font-size: 1.35rem;
  line-height: 1.45;
  margin: 0 0 .9rem;
}
.sheet__done-note {
  margin: 0;
  font-size: .8rem;
  letter-spacing: .04em;
  color: var(--bone-quiet);
}

/* ── Narrow viewports ──────────────────────────────────────────────────────
   Not a fallback. The 9:16 view is a crop of the same photograph, framed so
   the hearth stays in it, and the copy moves to the bottom of the frame
   because there is no dark right-hand third to sit in. */

@media (max-width: 800px) {
  /* A 9:16 window onto the 16:9 frame shows about a quarter of its width.
     18% puts the whole hearth (13–33% of the frame) inside that window; the
     default 50% would cut it in half. The plate is framed on itself.
     In that crop the hearth lands near the middle of the viewport, so the
     fire mask follows it. */
  :root { --focus-x: 18%; --focus-plate: 55%; --fire-x: 40%; }

  /* Give the arrival block real ground under it — the wordmark sat on the
     bottom edge of the frame. */
  .copy--arrival { padding-bottom: max(3.4rem, calc(var(--gutter) + 1.6rem)); }

  .copy--right,
  .copy--menu {
    justify-content: flex-end;
    padding-top: var(--gutter);
    padding-left: var(--gutter);
    padding-bottom: max(5.5rem, calc(var(--gutter) + 3.5rem));
  }
  /* Clear of the colophon, which fades in beneath it at the tail. */
  .copy--reserve { padding-bottom: max(7rem, calc(var(--gutter) + 5rem)); }
  .lede { max-width: 26ch; }
  .seats { max-width: 20ch; }
  .menu__note { margin-bottom: 1.5rem; }
  .menu { line-height: 2.05; }
  .colophon { font-size: .7rem; }
}

/* Wide but short viewports crop the frame to its width, which compresses the
   dark ceiling band the right-hand copy lives in into far fewer pixels. The
   block has to get shorter with it or it runs down into the lit tables. */
@media (min-width: 801px) and (max-height: 820px) {
  .copy--right { padding-top: max(var(--gutter), 4vh); }
  .lede {
    font-size: 1.02rem;
    line-height: 1.42;
    margin-bottom: .7em;
    max-width: 32ch;
  }
  .seats { font-size: 1.02rem; margin-bottom: 1.2rem; }
}

@media (max-width: 800px) and (max-height: 700px) {
  .menu { line-height: 1.85; font-size: 1.02rem; }
  .menu__title { font-size: 1.7rem; }
}

/* ── Without the script ────────────────────────────────────────────────────
   The stage engine is the page, so if it never runs the copy would sit at
   opacity 0 over one photograph and the whole thing would read as an empty
   image. The floor is a plain scrolling document: the same words, in order,
   over the arrival frame, with Reserve still reaching the form by its href.
   `data-js` is set in the head, before first paint, so nothing flashes. */

html:not([data-js="on"]) {
  .copy-deck { position: static; }

  /* Compact, not six empty viewports — someone here wants to read it. */
  .copy {
    position: relative;
    inset: auto;
    opacity: 1;
    visibility: visible;
    align-items: flex-start;
    padding: 3.5rem var(--gutter);
  }
  .copy--arrival { padding-top: 45svh; }

  .colophon {
    position: relative;
    left: auto; right: auto; bottom: auto;
    opacity: 1;
    visibility: visible;
    padding: 4rem var(--gutter);
  }

  .runway { display: none; }

  /* Non-modal, but it opens, it is legible and it can be filled in. */
  .sheet {
    margin: 0 var(--gutter) 4rem;
    display: none;
  }
  .sheet:target { display: block; }
  .sheet__close { display: none; }
}

/* ── Reduced motion ────────────────────────────────────────────────────────
   The loop is dropped for the still, the hearth stops breathing, and the
   dissolves are compressed to almost nothing in js/banked.js. Everything
   stays readable and Reserve stays fully usable. */

@media (prefers-reduced-motion: reduce) {
  /* The reduced-motion state is the stills, full stop — the script also
     aborts the loop's download, but the rule holds even if it never runs. */
  .fire { display: none; }
  .btn, .sound, .sheet__close { transition: none; }
}
