/* A quiet dark room for the art: near-black background, warm off-white
   serif text, small monospace labels, and one centred column. */

:root {
  --bg: #0b0b0c;
  --text: #ece8e1;
  --muted: #8d8981;
  --faint: #55524d;
  --line: rgba(236, 232, 225, 0.12);
  --serif: "Instrument Serif", "Iowan Old Style", Georgia, serif;
  --mono: "JetBrains Mono", "SFMono-Regular", Menlo, Consolas, monospace;
  --column: 920px;
  --radius: 14px;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --reveal-time: 1.4s;

  /* Set by gallery.js: the viewport size and how far each frame shrinks it. */
  --stage-w: 100vw;
  --stage-h: 100vh;
  --scale: 0.6;

  color-scheme: dark;
}

* {
  box-sizing: border-box;
}

html {
  background: var(--bg);
  scrollbar-gutter: stable;
}

html.is-locked {
  overflow: hidden;
}

body {
  position: relative;
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 400 1.3rem/1.5 var(--serif);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ---------------------------------------------------------------- pillars */

/* A column drawn in characters stands down each side of the page, from the
   very top to the very bottom (pillars.js fills in the shaft). They appear
   only where the screen is wide enough to leave them room beside the art. */
.pillars {
  position: absolute;
  inset: 0;
  display: none;
  pointer-events: none;
  user-select: none;
}

.pillar {
  position: absolute;
  top: 0;
  bottom: 0;
  margin: 0;
  overflow: hidden;
  font: 400 clamp(9px, 0.8vw, 13px) / 1.2em var(--mono);
  white-space: pre;
  color: var(--faint);
  opacity: 0.75;
}

/* Each column sits in the middle of the margin beside the art column. */
.pillar--left {
  left: max(24px, calc((100% - var(--column)) / 4 - 10.5ch));
}

.pillar--right {
  right: max(24px, calc((100% - var(--column)) / 4 - 10.5ch));
}

@media (min-width: 1240px) {
  .pillars {
    display: block;
  }
}

/* ------------------------------------------------------------------ intro */

.intro {
  min-height: 100svh;
  max-width: var(--column);
  margin: 0 auto;
  padding: 18vh max(20px, env(safe-area-inset-left)) 12vh max(20px, env(safe-area-inset-right));
  display: flex;
  flex-direction: column;
  justify-content: center;
  position: relative;
}

.eyebrow,
.piece__meta,
.piece__length,
.hint,
.scroll-cue,
.back {
  font-family: var(--mono);
  font-size: 0.72rem;
  font-weight: 400;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.eyebrow {
  margin: 0 0 1.6rem;
  color: var(--muted);
}

h1 {
  margin: 0;
  font-weight: 400;
  font-size: clamp(3.4rem, 11vw, 8.5rem);
  line-height: 0.95;
  letter-spacing: -0.02em;
}

h1 em,
h2 em,
p em {
  font-style: italic;
}

.lede {
  max-width: 34em;
  margin: 2.4rem 0 0;
  color: var(--text);
  opacity: 0.86;
  font-size: clamp(1.25rem, 2.2vw, 1.55rem);
  line-height: 1.45;
}

.hint {
  margin: 2.2rem 0 0;
  color: var(--muted);
}

.scroll-cue {
  position: absolute;
  left: 50%;
  bottom: calc(28px + env(safe-area-inset-bottom));
  translate: -50% 0;
  color: var(--faint);
}

.scroll-cue::after {
  content: "";
  display: block;
  width: 1px;
  height: 36px;
  margin: 12px auto 0;
  background: linear-gradient(var(--faint), transparent);
}

/* ---------------------------------------------------------------- opening */

/* On arrival (html.is-intro, set in index.html, run by gallery.js) the page
   holds still. The introduction stands in the middle of the screen while
   its sentence is typed out, then glides to its place; the pillars are drawn
   from the top down, and the first piece loads unseen before the page can
   be scrolled. */
html.is-intro {
  overflow: hidden;
}

/* While it stands in the middle, each line is moved there by gallery.js
   (--shift-x per line, --shift-y for all), and glides back to its place. */
.is-intro .intro .reveal {
  transition:
    opacity var(--reveal-time) var(--ease-out),
    transform var(--reveal-time) var(--ease-out),
    translate var(--glide-time, 1.1s) var(--ease-out);
}

.is-intro .intro .is-centred {
  translate: var(--shift-x, 0px) var(--shift-y, 0px);
}

/* The sentence is typed out by covers the colour of the page, one over each
   line, that gallery.js steps right a character at a time, each with a
   thin cursor at its left edge. */
.is-intro .lede {
  position: relative;
}

.typing-cover {
  position: absolute;
  background: var(--bg);
}

.typing-cursor {
  position: absolute;
  top: 8%;
  bottom: 8%;
  left: 0.04em;
  width: 0.07em;
  background: var(--text);
}

/* A cover over each pillar slides down one line at a time, as many lines
   as the screen is tall (--screen-rows, set by pillars.js). */
.is-intro .pillar::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--bg);
}

.is-intro.is-drawing-pillars .pillar::after {
  animation: draw-pillar var(--pillar-time, 1.6s) steps(var(--screen-rows, 60), end) forwards;
}

@keyframes draw-pillar {
  to {
    transform: translateY(calc(var(--screen-rows, 60) * 1.2em));
  }
}

/* ----------------------------------------------------------------- pieces */

.pieces {
  max-width: var(--column);
  margin: 0 auto;
  padding: 0 max(20px, env(safe-area-inset-left)) 0 max(20px, env(safe-area-inset-right));
}

.piece {
  padding: 14vh 0;
  border-top: 1px solid var(--line);
}

.piece__text {
  max-width: 38em;
}

.piece__meta {
  margin: 0 0 1rem;
  color: var(--muted);
}

.piece__meta span {
  color: var(--text);
  margin-right: 1.2em;
}

/* How long the animation runs, under its frame. */
.piece__length {
  margin: 1.1rem 0 0;
  color: var(--muted);
}

h2 {
  margin: 0 0 0.9rem;
  font-weight: 400;
  font-size: clamp(2.4rem, 6vw, 4rem);
  line-height: 1;
  letter-spacing: -0.01em;
}

.quote {
  margin: 1.4rem 0 0;
  padding: 0 0 0 1.3rem;
  border-left: 1px solid var(--line);
}

.quote p {
  margin: 0;
  font-style: italic;
  font-size: clamp(1.35rem, 2.4vw, 1.7rem);
  line-height: 1.35;
  opacity: 0.88;
}

.quote footer {
  margin-top: 0.9rem;
  color: var(--muted);
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
}

.quote footer::before {
  content: "\2014\00a0";
}

.quote cite {
  font-style: normal;
  color: var(--faint);
}

/* ------------------------------------------------------------------ frame */

/* The frame shows the art page at the full size of the screen, scaled
   down: opening it only removes the scaling, so the art never has to
   redraw itself at a new size. */
.frame {
  position: relative;
  width: calc(var(--stage-w) * var(--scale));
  height: calc(var(--stage-h) * var(--scale));
  margin: 3rem 0 0;
  border-radius: var(--radius);
  overflow: hidden;
  background: #050505;
  box-shadow: 0 0 0 1px var(--line), 0 30px 80px -30px rgba(0, 0, 0, 0.9);
}

.stage {
  position: absolute;
  left: 0;
  top: 0;
  width: var(--stage-w);
  height: var(--stage-h);
  transform: scale(var(--scale));
  transform-origin: 0 0;
  border-radius: calc(var(--radius) / var(--scale));
  overflow: hidden;
  background: #050505;
}

.stage.is-floating {
  position: fixed;
  z-index: 50;
}

.stage__poster,
.stage iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}

.stage__poster {
  object-fit: cover;
}

.stage iframe {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.8s ease;
}

.stage iframe.is-ready {
  opacity: 1;
}

.stage.is-interactive iframe {
  pointer-events: auto;
}

.frame__open {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  border-radius: inherit;
  background: transparent;
  cursor: zoom-in;
}

.frame__open::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgba(236, 232, 225, 0);
  transition: box-shadow 0.4s ease;
}

.frame__open:hover::after,
.frame__open:focus-visible::after {
  box-shadow: inset 0 0 0 1px rgba(236, 232, 225, 0.35);
}

.frame__open:focus-visible {
  outline: none;
}

/* ------------------------------------------------------------- full screen */

.backdrop {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: #000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.9s var(--ease-out);
}

.backdrop.is-shown {
  opacity: 1;
  pointer-events: auto;
}

.back {
  position: fixed;
  top: max(16px, env(safe-area-inset-top));
  right: max(16px, env(safe-area-inset-right));
  z-index: 60;
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
  padding: 0.75em 1.1em 0.75em 0.9em;
  border: 1px solid rgba(236, 232, 225, 0.18);
  border-radius: 999px;
  background: rgba(11, 11, 12, 0.55);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  color: var(--text);
  cursor: pointer;
  opacity: 0;
  translate: 0 -6px;
  transition: opacity 0.5s ease, translate 0.5s var(--ease-out), background 0.3s ease, border-color 0.3s ease;
}

.back.is-shown {
  opacity: 1;
  translate: 0 0;
}

.back:hover,
.back:focus-visible {
  background: rgba(30, 30, 32, 0.75);
  border-color: rgba(236, 232, 225, 0.4);
  outline: none;
}

.back svg {
  width: 14px;
  height: 14px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ----------------------------------------------------------------- reveal */

/* Text and frames drift up and fade in once, as they scroll into view.
   The visible state has no transform at all, so an opened frame can be
   positioned against the screen. */
.reveal {
  opacity: 0;
  transform: translateY(26px);
  transition:
    opacity var(--reveal-time) var(--ease-out),
    transform var(--reveal-time) var(--ease-out);
  transition-delay: calc(var(--order, 0) * 110ms);
}

.reveal.is-visible {
  opacity: 1;
  transform: none;
}

.lede.reveal.is-visible {
  opacity: 0.86;
}

@media (prefers-reduced-motion: reduce) {
  .reveal {
    transform: none;
    transition: opacity 0.6s ease;
  }
}

@media (max-width: 640px) {
  body {
    font-size: 1.18rem;
  }

  .piece {
    padding: 10vh 0;
  }

  .frame {
    margin-top: 2rem;
  }
}
