/* ============================================================
   CUTT — the page is a bolt of cloth.
   Scroll drives each cut along a chalked guideline; the finished
   cut parts the cloth and reveals the layer beneath.
   Layers: wool (hero) → wool (work) → wool (contact)
   ============================================================ */

:root {
  --wool:        #0B0B0C;
  --wool-raised: #141416;
  --seam:        #232326;
  --chalk:       #EDEDEA;
  --chalk-muted: #8A8A88;
  --bone:        #FBFBFA;
  --bone-raised: #F2F1ED;
  --bone-seam:   #DFDED8;
  --ink:         #0B0B0C;
  --ink-muted:   #6B6B67;

  --font-display: 'Space Grotesk', 'Inter Tight', sans-serif;
  --font-title: 'Archivo', 'Inter Tight', sans-serif; /* the mark's own voice: black, semi-condensed */
  --font-mono: 'ABC Diatype Mono', 'Geist Mono', ui-monospace, monospace;
  /* title register rides the mark's weight — bulky, poster-black */
  --title-stretch: 80%;
  --title-weight: 900;
  /* corner furniture: one inset from both edges of every corner */
  --corner: 40px;
  /* touch Safari floats its URL pill over the page's last pixels —
     everything anchored to the bottom edge lifts clear by this much
     (0 wherever the browser draws no furniture over the page) */
  --safe-b: 0px;

  --sp-1: 8px; --sp-2: 16px; --sp-3: 24px; --sp-4: 40px;
  --sp-5: 64px; --sp-6: 104px; --sp-7: 168px;
  --gutter: max(24px, 12vw);

  --ease-cut: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-micro: 240ms;
  --dur-reveal: 640ms;
  --dur-sequence: 1200ms;

  /* live vars written by the engine */
  --cx: 0;    /* cloth drift -1..1 */
  --cy: 0;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html { background: var(--wool); }
html.js { scrollbar-width: none; }
html.js::-webkit-scrollbar { display: none; }

body {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--chalk);
  background: var(--wool);
  letter-spacing: -0.005em;
  font-size: 15px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
}
html.js body { cursor: none; }
html.js a, html.js button, html.js input, html.js textarea { cursor: none; }

/* touch hardware: the engine owns the scroll — the document itself
   never moves, so no native momentum ever fights the pager */
html.touch, html.touch body { overflow: hidden; height: 100%; }
html.touch { --safe-b: 20px; }

img { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }

:focus { outline: none; }
:focus-visible { outline: 1px solid var(--chalk); outline-offset: 3px; }
[data-ground="bone"] :focus-visible { outline-color: var(--ink); }

::selection { background: var(--chalk); color: var(--wool); }

/* ---------- type ---------- */

.u {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  line-height: 1.4;
}
.d {
  font-family: var(--font-title);
  font-stretch: var(--title-stretch);
  font-weight: var(--title-weight);
  text-transform: uppercase;
  letter-spacing: 0.01em;
  font-size: clamp(40px, 4.5vw, 64px);
  line-height: 0.98;
}

/* ============================================================
   THE CLOTH
   Woven ground: a generated seamless tile (weave, grain and nap
   baked into one bitmap at whisper amplitude) under one broad
   fall of light. The earlier procedural thread gradients + SVG
   noise rasterized whole oversized layers and killed compositing.
   ============================================================ */

.cloth {
  position: absolute;
  inset: -3vh -3vw;
  pointer-events: none;
  background-color: var(--wool);
  background-image:
    linear-gradient(112deg, rgba(255,255,255,0.016) 0%, rgba(255,255,255,0) 36%, rgba(0,0,0,0.055) 82%, rgba(0,0,0,0) 100%),
    url("assets/cloth-wool.png");
  background-repeat: no-repeat, repeat;
  background-size: 100% 100%, 512px 512px;
  transform: translate3d(calc(var(--cx) * 7px), calc(var(--cy) * 5px), 0);
}

.cloth.bone {
  background-color: var(--bone);
  background-image:
    linear-gradient(100deg, rgba(0,0,0,0.022) 0%, rgba(0,0,0,0) 42%, rgba(0,0,0,0.03) 88%, rgba(0,0,0,0) 100%),
    url("assets/cloth-bone.png");
}

/* ============================================================
   LAYERS — static flow by default (no JS); the engine pins them.
   ============================================================ */

.layer { position: relative; min-height: 100vh; overflow: hidden; }
.layer[data-ground="bone"] { color: var(--ink); }
.lay-content { position: relative; }

html.js .stage { position: relative; }
html.js .layer { position: fixed; inset: 0; height: 100vh; }
html.js .l-hero      { z-index: 32; }
html.js .l-work      { z-index: 30; }
html.js .l-contact   { z-index: 26; }
html.js .layer.gone { visibility: hidden; }

/* halves built by the engine — the same 100vh box as the layers:
   an inset-0 box tracks the layout viewport, which on iOS breathes
   with the URL bar, and a half shorter than the layer it clones
   would centre its content a bar's half-height off the cut */
.half {
  position: fixed; inset: 0; height: 100vh;
  visibility: hidden;
  will-change: transform;
  overflow: hidden;
}
.half.on { visibility: visible; }
.half[data-ground="bone"] { color: var(--ink); }
.half .h-edge { position: absolute; inset: 0; width: 100%; height: 100%; }

/* ---------- hero ---------- */

.hero-content {
  position: absolute; inset: 0;
  display: grid; place-content: center; justify-items: center;
  text-align: center;
  row-gap: var(--sp-4);
}
/* the lockup: the mark, then one text line, both the same width.
   Everything derives from --lockup so the block scales as one piece;
   the line's font-size is measured so its ink spans it. */
.hero-lines {
  --lockup: 60vw;
  width: var(--lockup);
  color: var(--chalk);
  font-family: var(--font-title);
  font-stretch: var(--title-stretch);
  font-weight: var(--title-weight);
  line-height: 0.88;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  white-space: nowrap;
}
.hero-lines .hl { display: block; }
.hl-mark { width: var(--lockup); aspect-ratio: 2090 / 748; color: var(--chalk); margin-bottom: 0.55vw; }
.hl-2 { font-size: calc(var(--lockup) * 0.1099); } /* measured: ink spans the lockup at 900/80% */

.hint {
  position: absolute; left: 50%;
  bottom: calc(var(--corner) + var(--safe-b) + 6vh);
  transform: translateX(-50%);
  color: var(--chalk);
  font-size: 11px;
  display: flex; align-items: center; gap: 10px;
  /* two beckoning bobs once the arrival sequence has set it down
     (fade-in completes ~2.05s after boot); reduced motion never
     sees this — the hint is display:none there */
  animation: hint-nudge 850ms var(--ease-cut) 2350ms 2;
}
.hint::after {
  content: "";
  width: 1px; height: 26px;
  background: repeating-linear-gradient(0deg, var(--chalk) 0 2px, transparent 2px 5px);
}
@keyframes hint-nudge {
  0%, 100% { transform: translateX(-50%) translateY(0); }
  38%      { transform: translateX(-50%) translateY(9px); }
  70%      { transform: translateX(-50%) translateY(-2px); }
}
.corner-note {
  position: absolute; right: var(--corner);
  bottom: calc(var(--corner) + var(--safe-b));
  color: var(--chalk-muted);
}

/* ---------- work: one spread per commission ----------
   Each commission owns the viewport: a canvas inset in the cloth,
   and inside it the project's own document — each frame is a
   self-contained HTML miniature recreating the gist of the piece,
   not a screenshot. Under the frame: name, type, and two clear
   ways in. The spreads page like the home screen: scroll slides
   the next one up as a single piece, one page per spread. */

.l-work .lay-content { position: absolute; inset: 0; }

/* the head rides the bar's own line: the section label sits next to
   the mark, the counter takes the corner the nav left free.
   difference keeps it legible while a canvas sweeps over it */
.wk-head {
  position: absolute;
  left: calc(var(--corner) + 52px + var(--sp-3)); /* corner inset + mark + gap */
  right: var(--corner);
  top: var(--corner);
  height: calc(52px * 748 / 2090); /* the mark's height — one shared centerline */
  display: flex; justify-content: space-between; align-items: center;
  color: #8A8A8A;
}
.wk-eyebrow, .wk-count { mix-blend-mode: difference; }
.wk-count #wk-idx { color: #ECECEC; }

.wk-list { list-style: none; position: absolute; inset: 0; }
/* each item spans the viewport and rides the pager as one piece;
   without pointer-events:none the next spread's transparent box
   would swallow this one's buttons */
.wk-item { position: absolute; inset: 0; visibility: hidden; pointer-events: none; will-change: transform; }
.wk-item.on { visibility: visible; }
.wk-item .wk-below { pointer-events: auto; }

/* the canvas: most of the viewport, cloth showing around it */
.wk-canvas {
  position: absolute; left: 12vw; right: 12vw; top: 11vh;
  bottom: calc(13vh + var(--safe-b));
}
/* the frame must read even when the project inside is as dark as
   the table — a chalk hairline keeps the window's edge clear */
.wk-clip {
  position: absolute; inset: 0;
  overflow: hidden;
  border: 1px solid rgba(237,237,234,0.32);
  border-radius: 2px;
  background: var(--wool-raised);
}
/* the miniature: the project's own document, filling its window
   exactly. Display object only — the pointer stays with the table. */
.wk-frame {
  position: absolute; left: 0; top: 0;
  width: 100%; height: 100%;
  border: 0; display: block;
  pointer-events: none;
  background: var(--wool-raised);
}

/* under the frame, on the table: name, type, and two ways in */
.wk-below {
  position: absolute; left: 12vw; right: 12vw;
  bottom: var(--safe-b); height: 13vh;
  display: flex; align-items: center; gap: var(--sp-4);
}
.wk-item .wk-title {
  font-family: var(--font-title);
  font-stretch: var(--title-stretch);
  font-weight: var(--title-weight);
  text-transform: uppercase;
  letter-spacing: 0.01em;
  font-size: clamp(20px, 1.9vw, 30px);
  line-height: 1;
  white-space: nowrap;
  color: var(--chalk);
}
.wk-item .wk-meta { color: var(--chalk-muted); margin-top: 6px; white-space: nowrap; }
.wk-tags {
  display: flex; align-items: center; gap: var(--sp-1);
  list-style: none;
  color: var(--chalk-muted);
}
.wk-tags li {
  padding: 7px 12px 6px;
  border: 1px solid rgba(237,237,234,0.16);
  border-radius: 2px;
  line-height: 1;
}
.wk-actions { margin-left: auto; display: flex; gap: var(--sp-2); }
.wk-btn {
  display: inline-block;
  white-space: nowrap;
  padding: 13px 22px 12px;
  border: 1px solid rgba(237,237,234,0.4);
  border-radius: 2px;
  color: var(--chalk); line-height: 1;
  transition: background-color var(--dur-micro) ease,
              color var(--dur-micro) ease,
              border-color var(--dur-micro) ease;
}
.wk-btn:hover, .wk-btn:focus-visible {
  background: var(--chalk); color: var(--wool); border-color: var(--chalk);
}

/* crop marks: draw when the piece settles on the table */
.cm { position: absolute; width: 15px; height: 15px; pointer-events: none; }
.cm::before, .cm::after {
  content: ""; position: absolute; background: var(--chalk);
  transition: transform var(--dur-micro) var(--ease-cut);
}
.cm::before { width: 15px; height: 1px; transform: scaleX(0); }
.cm::after  { width: 1px; height: 15px; transform: scaleY(0); }
.cm-tl { left: -21px; top: -21px; }    .cm-tl::before { bottom: 0; right: 0; transform-origin: right; }  .cm-tl::after { right: 0; bottom: 0; transform-origin: bottom; }
.cm-tr { right: -21px; top: -21px; }   .cm-tr::before { bottom: 0; left: 0; transform-origin: left; }    .cm-tr::after { left: 0; bottom: 0; transform-origin: bottom; }
.cm-bl { left: -21px; bottom: -21px; } .cm-bl::before { top: 0; right: 0; transform-origin: right; }     .cm-bl::after { right: 0; top: 0; transform-origin: top; }
.cm-br { right: -21px; bottom: -21px; } .cm-br::before { top: 0; left: 0; transform-origin: left; }      .cm-br::after { left: 0; top: 0; transform-origin: top; }
.wk-item.live .cm::before { transform: scaleX(1); }
.wk-item.live .cm::after  { transform: scaleY(1); }

/* ---------- contact: the question, asked large ----------
   The last layer keeps the wool. The ask is typographic — two
   lines of title-register chalk over one field. The way to
   answer arrives only once the visitor starts to speak: from
   the second typed character, the reach field and the send
   button unfold beneath (grid-rows + opacity — one reflow at
   the moment of reveal, stillness otherwise). The form itself
   belongs to js/contact.js, not the table engine. */

.ct-content {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  row-gap: 8vh;
}

/* the ask: two lines of title-register chalk, then the field */
.ct-ask-wrap {
  display: flex; flex-direction: column; align-items: center;
  row-gap: var(--sp-4);
}
/* both heads share one cell so the sent swap happens in place */
.ct-heads { display: grid; justify-items: center; }
.ct-heads > * { grid-area: 1 / 1; }
.ct-head {
  font-size: clamp(44px, 6.8vw, 108px);
  line-height: 0.96;
  text-align: center;
}
.ct-head span { display: block; }
.ct-head-2 { color: var(--chalk-muted); }

/* --- sent: the ask is cut from the cloth and the answer takes
       its place. contact.js clones the ask head into two halves
       clipped along a slanted seam; the chalk dashes draw, the
       halves part mid-glyph (the site's own cut), and RECEIVED
       rises letter by letter out of the seam. --- */
.sent .ct-head-ask { visibility: hidden; }
.ct-headcut { position: relative; width: 100%; height: 100%; pointer-events: none; }
.ct-headcut .cuthalf {
  position: absolute; inset: 0;
  transition: transform 700ms var(--ease-cut) 280ms, opacity 620ms ease 340ms;
}
.ct-headcut .cuthalf-a { clip-path: polygon(0 0, 100% 0, 100% 51%, 0 57%); }
.ct-headcut .cuthalf-b { clip-path: polygon(0 57%, 100% 51%, 100% 100%, 0 100%); }
.ct-headcut .cutseam {
  position: absolute; left: -2%; top: 54%; width: 104%; height: 1.4px;
  background: repeating-linear-gradient(90deg, var(--chalk) 0 2px, transparent 2px 9px);
  opacity: 0.75;
  transform: rotate(-0.8deg) scaleX(0); transform-origin: left center;
  transition: transform 280ms var(--ease-cut), opacity 300ms ease 480ms;
}
.ct-headcut.go .cutseam { transform: rotate(-0.8deg) scaleX(1); opacity: 0; }
.ct-headcut.go .cuthalf-a { transform: translate3d(-18px, -22px, 0) rotate(-1.1deg); opacity: 0; }
.ct-headcut.go .cuthalf-b { transform: translate3d(18px, 24px, 0) rotate(0.9deg); opacity: 0; }

/* the answer: one huge word over one modest line. It waits in the
   same cell as the ask under visibility:hidden, so its pre-state
   is long committed when .in asks the letters to rise. */
.ct-head-recv { visibility: hidden; }
.ct-head-recv.in { visibility: visible; }
.ct-head-recv .ct-head-1 {
  font-size: 1.26em; line-height: 1;
  overflow: hidden; /* the letters rise out of the cut */
}
.ct-head-recv .rl {
  display: inline-block;
  transform: translateY(1.12em); opacity: 0;
  transition: transform 640ms var(--ease-cut), opacity 360ms ease;
  /* per-letter stagger arrives as an inline transition-delay */
}
.ct-head-recv .ct-head-2 {
  font-size: 0.30em; letter-spacing: 0.04em;
  margin-top: 14px;
  opacity: 0; transform: translateY(12px);
  transition: transform 560ms var(--ease-cut) 520ms, opacity 480ms ease 560ms;
}
.ct-head-recv.in .rl { transform: none; opacity: 1; }
.ct-head-recv.in .ct-head-2 { opacity: 1; transform: none; }

.ct-form {
  width: min(560px, 80vw);
  display: flex; flex-direction: column;
}
.ct-ask {
  width: 100%;
  display: flex; align-items: center;
  border: 1px solid rgba(237,237,234,0.3);
  border-radius: 2px;
  background: rgba(18,18,20,0.55);
  transition: border-color var(--dur-micro) ease, background-color var(--dur-micro) ease;
}
.ct-ask:hover { border-color: rgba(237,237,234,0.55); }
.ct-ask:focus-within { border-color: rgba(237,237,234,0.85); background: rgba(16,16,18,0.85); }
.ct-input {
  flex: 1; min-width: 0;
  border: 0; background: transparent;
  font-family: var(--font-display);
  font-size: 16px; letter-spacing: -0.005em;
  line-height: 1.4; /* explicit — the textarea's growth is measured in it */
  color: var(--chalk); caret-color: var(--chalk);
  padding: 21px var(--sp-3);
}
/* the message composes: Shift+Enter breaks the line and the field
   grows with its content (measured by contact.js) to five lines,
   then scrolls */
textarea.ct-input {
  resize: none;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(237,237,234,0.25) transparent;
}
.ct-input:focus, .ct-input:focus-visible { outline: none; } /* the frame carries the state */
.ct-input::placeholder { color: var(--chalk-muted); opacity: 1; }
.ct-send {
  flex: 0 0 auto;
  width: 38px; height: 38px; margin-right: 11px;
  display: grid; place-items: center;
  border: 1px solid rgba(237,237,234,0.4); border-radius: 50%;
  background: transparent; color: var(--chalk);
  transition: background-color var(--dur-micro) ease,
              color var(--dur-micro) ease,
              border-color var(--dur-micro) ease;
}
.ct-send:hover, .ct-send:focus-visible { background: var(--chalk); color: var(--wool); border-color: var(--chalk); }
.ct-send svg {
  width: 15px; height: 15px;
  fill: none; stroke: currentColor; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}

/* the way to answer unfolds only once the visitor starts to speak:
   a collapsed grid row opens from the second typed character.
   visibility rides the same beat so the hidden field never takes
   a tab stop; the delay holds it visible through the fold-away. */
.ct-more {
  display: grid; grid-template-rows: 0fr;
  opacity: 0; transform: translateY(-10px);
  visibility: hidden;
  transition:
    grid-template-rows 480ms var(--ease-cut),
    opacity 360ms ease,
    transform 480ms var(--ease-cut),
    visibility 0s 480ms;
}
.ct-ask-wrap.asked .ct-more {
  grid-template-rows: 1fr;
  opacity: 1; transform: none;
  visibility: visible;
  transition:
    grid-template-rows 480ms var(--ease-cut),
    opacity 360ms 120ms ease,
    transform 480ms var(--ease-cut),
    visibility 0s;
}
.ct-more-inner { min-height: 0; overflow: hidden; }
.ct-reach { margin-top: var(--sp-2); }

/* one quiet line for the form's states; space held so nothing jumps */
.ct-note {
  min-height: 14px;
  margin-top: var(--sp-2);
  text-align: center;
  color: var(--chalk-muted);
}
.ct-note a { border-bottom: 1px solid var(--chalk-muted); }

/* while the document is being written the send holds still */
.ct-ask-wrap.sending .ct-send { pointer-events: none; opacity: 0.55; }

/* sent: the fields fold quietly while the head is cut above them;
   the space is kept so nothing beneath the cut shifts mid-motion */
.ct-ask-wrap.sent .ct-form {
  opacity: 0; transform: translateY(14px);
  visibility: hidden; pointer-events: none;
  transition: opacity 360ms ease, transform 480ms var(--ease-cut),
              visibility 0s 480ms;
}

.foot {
  position: absolute;
  left: var(--corner); right: var(--corner);
  bottom: calc(var(--corner) + var(--safe-b));
  display: flex; justify-content: space-between; gap: var(--sp-3);
  color: var(--chalk-muted);
}
/* ---------- cuttable headlines: hover splits the type ---------- */

.cuttable { position: relative; }
.cuttable .ct-piece {
  display: block;
  transition: transform 300ms var(--ease-cut);
}
.cuttable .ct-b {
  position: absolute; inset: 0;
}
.cuttable .ct-dash {
  position: absolute; left: -3%; width: 106%; height: 1.4px;
  background: repeating-linear-gradient(90deg, currentColor 0 2px, transparent 2px 9px);
  opacity: 0; top: 50%;
  transition: opacity 200ms ease;
  pointer-events: none;
}
.cuttable.snip .ct-dash { opacity: 0.6; }
.cuttable.snip .ct-a { transform: translate3d(-5px, -4px, 0); }
.cuttable.snip .ct-b { transform: translate3d(5px, 4px, 0); }

/* ============================================================
   INSTRUMENT — guideline dots and the slit
   ============================================================ */

.instrument {
  /* 100vh, not 100%: geometry is computed in the layers' 100vh box,
     and this svg has no viewBox — a shorter iOS layout viewport
     would clip the slit's lower reach */
  position: fixed; inset: 0; width: 100%; height: 100vh;
  z-index: 40; pointer-events: none;
}
html:not(.js) .instrument { display: none; }

.cutset { opacity: 0; } /* opacity is written per-frame by the engine */

.gl-dots {
  fill: none;
  stroke-width: 1.7;
  stroke-linecap: round;
}
.cutset[data-ground="wool"] .gl-dots { stroke: rgba(237,237,234,0.75); }
.cutset[data-ground="bone"] .gl-dots { stroke: rgba(11,11,12,0.55); }

/* tapered opening: the layer beneath shows through the gap,
   its shadow inset along the cloth edges to give the cut depth.
   The same classes paint the halves' lips once the cloth parts.
   Dark cloth over dark cloth: the gap reads as shadow, not as the
   layer's own colour, or the cut disappears into the ground. */
.slit-fill { stroke: none; }
.cutset[data-reveal="bone"] .slit-fill, .half[data-reveal="bone"] .slit-fill { fill: var(--bone); }
.cutset[data-reveal="wool"] .slit-fill, .half[data-reveal="wool"] .slit-fill { fill: #000; }
.slit-inshade { fill: none; stroke-width: 9; stroke-linecap: round; }
.cutset[data-reveal="bone"] .slit-inshade, .half[data-reveal="bone"] .slit-inshade { stroke: rgba(11, 11, 12, 0.48); }
.cutset[data-reveal="wool"] .slit-inshade, .half[data-reveal="wool"] .slit-inshade { stroke: rgba(0, 0, 0, 0.72); }
.slit-tipshade { stroke: none; }

/* ---------- cursor: crosshair + act tag ---------- */

.cursor {
  position: fixed; left: 0; top: 0; z-index: 60;
  pointer-events: none; mix-blend-mode: difference;
  opacity: 0;
}
html.js .cursor { opacity: 1; }
.cur-x, .cur-y { position: absolute; background: #ececec; transition: transform 200ms var(--ease-cut); }
.cur-x { width: 15px; height: 1px; left: -7.5px; top: -0.5px; }
.cur-y { width: 1px; height: 15px; left: -0.5px; top: -7.5px; }
.cur-tag {
  position: absolute; left: 16px; top: 8px;
  color: #ececec; white-space: nowrap;
  opacity: 0; transition: opacity 200ms ease;
}
.cursor.tagged .cur-tag { opacity: 1; }
.cursor.is-hover .cur-x { transform: scaleX(0.28); }
.cursor.is-hover .cur-y { transform: scaleY(0.28); }
/* a frame that carries its own cursor (the Der lens): ours yields */
body.cursor-ceded .cursor { opacity: 0; }
@media (pointer: coarse) { .cursor { display: none; } html.js body, html.js a { cursor: auto; } }

/* ---------- the case study, hung over the table ----------
   "Case study" opens the viewer in a window over the work, not a
   separate tab: a scrim stills the cloth, the document hangs in a
   hairline window with its own crop marks and keeps its own bar. */
.study {
  position: fixed; inset: 0; z-index: 70;
  visibility: hidden;
  pointer-events: none; /* a dismissed study must not shade the table, even mid-fade */
  transition: visibility 0s var(--dur-reveal);
}
.study.on { visibility: visible; pointer-events: auto; transition: none; }
.study-scrim {
  position: absolute; inset: 0;
  background: rgba(4, 4, 5, 0.78);
  opacity: 0;
  transition: opacity var(--dur-reveal) ease;
}
.study.on .study-scrim { opacity: 1; }
.study-panel {
  position: absolute; left: 50%; top: 50%;
  width: min(1240px, 92vw); height: 86vh;
  transform: translate(-50%, calc(-50% + 12px));
  border: 1px solid rgba(237,237,234,0.32);
  border-radius: 2px;
  background: var(--wool-raised);
  opacity: 0;
  transition: opacity var(--dur-reveal) ease, transform var(--dur-reveal) var(--ease-cut);
}
.study.on .study-panel { opacity: 1; transform: translate(-50%, -50%); }
.study-frame {
  position: absolute; inset: 0; width: 100%; height: 100%;
  border: 0; display: block; border-radius: 2px;
  background: var(--wool);
}
/* the marks draw once the window has landed; retract instantly on close */
.study.on .cm::before { transform: scaleX(1); transition-delay: 280ms; }
.study.on .cm::after  { transform: scaleY(1); transition-delay: 280ms; }
/* while the study hangs: the cloth holds still, the native pointer
   serves the document */
body.study-open { overflow: hidden; }
html.js body.study-open .cursor { opacity: 0; }
html.js body.study-open, html.js body.study-open a, html.js body.study-open button { cursor: auto; }

/* ============================================================
   FURNITURE — bar and cursor, in difference
   so chalk marks read on wool and graphite on bone.
   ============================================================ */

/* difference rides the small leaf elements, never a viewport-sized
   container — Safari pays for the blended element's full box */
.bar {
  position: fixed; top: var(--corner);
  left: var(--corner); right: var(--corner);
  z-index: 46;
  display: flex; justify-content: space-between; align-items: center;
  color: #ececec;
}
.bar-mark { mix-blend-mode: difference; }
.bar-mark { display: block; opacity: 0; transition: opacity var(--dur-reveal) ease; pointer-events: none; }
.bar.bar-on .bar-mark { opacity: 1; pointer-events: auto; }
.mark-s { width: 52px; aspect-ratio: 2090 / 748; display: block; color: #ececec; }

/* ============================================================
   LOAD SEQUENCE — one orchestrated arrival, then stillness.
   ============================================================ */

html.js .seqa { opacity: 0; transition: opacity var(--dur-reveal) ease; }
html.js .seqa.in { opacity: 1; }

/* ============================================================
   RESPONSIVE
   ============================================================ */

@media (max-width: 760px) {
  :root { --sp-4: 24px; --corner: 20px; }
  .hero-lines { --lockup: 82vw; }
  .corner-note { display: none; }
  .wk-canvas { left: 24px; right: 24px; }
  .wk-below { left: 24px; right: 24px; gap: var(--sp-2); }
  .wk-tags { display: none; }
  .cm-tl, .cm-bl { left: -14px; }
  .cm-tr, .cm-br { right: -14px; }
  .cm-tl, .cm-tr { top: -14px; }
  .cm-bl, .cm-br { bottom: -14px; }
  .ct-content { padding-right: 24px; }
  .foot { flex-wrap: wrap; }
  /* the band: two buttons must clear the title column on a phone */
  .wk-actions { gap: var(--sp-1); }
  .wk-btn { padding: 11px 13px 10px; }
  /* the ask at 900/80%: sized so TELL US ABOUT IT spans the phone */
  .ct-head { font-size: clamp(30px, 10.4vw, 108px); }
  /* the meta may wrap here — the buttons need the room */
  .wk-item .wk-meta { white-space: normal; }
}

/* Safari cannot run the displacement/blur filters per frame while
   the cut animates — it gets the same cut geometry, unfrayed */
html.is-safari .slit-open,
html.is-safari .slit-inshade { filter: none; }

@media (prefers-reduced-motion: reduce) {
  .instrument, .cursor, .hint { display: none !important; }
  * { transition-duration: 1ms !important; }
}

/* static (no-JS) flow niceties */
html:not(.js) .hero-content { position: relative; min-height: 100vh; }
html:not(.js) .hint { display: none; }
html:not(.js) .l-work .lay-content { position: relative; inset: auto; }
html:not(.js) .wk-head { position: relative; left: auto; right: auto; top: auto; height: auto; padding: var(--sp-7) var(--gutter) 0; }
html:not(.js) .wk-list { position: relative; inset: auto; }
html:not(.js) .wk-item { position: relative; inset: auto; visibility: visible; padding: var(--sp-6) var(--gutter); }
html:not(.js) .wk-canvas { position: relative; left: auto; right: auto; top: auto; bottom: auto; height: 64vh; }
html:not(.js) .wk-frame { top: 0; height: 100%; pointer-events: auto; }
html:not(.js) .wk-below { position: relative; left: auto; right: auto; bottom: auto; height: auto; margin-top: var(--sp-3); flex-wrap: wrap; }
html:not(.js) .ct-content {
  position: relative; min-height: 100vh; padding-top: var(--sp-7); padding-bottom: var(--sp-7);
}
html:not(.js) .foot { position: relative; left: auto; right: auto; bottom: auto; margin-top: var(--sp-6); }
