/* ============================================================
   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;

  --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; }

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 */
.half {
  position: fixed; inset: 0;
  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: var(--corner);
  transform: translateX(-50%);
  color: var(--chalk-muted);
  display: flex; align-items: center; gap: 10px;
}
.hint::after {
  content: "";
  width: 1px; height: 18px;
  background: repeating-linear-gradient(0deg, var(--chalk-muted) 0 2px, transparent 2px 5px);
}
.corner-note {
  position: absolute; right: var(--corner); bottom: var(--corner);
  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. Scroll slides the next spread up while the miniature
   lags inside its window. */

.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; 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; }
.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: 13vh;
  will-change: transform;
}
/* 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 in its own frame. It
   bleeds past its window so it can lag behind the frame's travel.
   Display object only — the pointer stays with the table. */
.wk-frame {
  position: absolute; left: 0; top: -13%;
  width: 100%; height: 126%;
  border: 0; display: block;
  pointer-events: none;
  background: var(--wool-raised);
  will-change: transform;
}
/* tablet and below: the portrait layouts inside are taller than the
   visible band, so the 26% parallax bleed would crop their heads and
   feet — the miniature fills its window exactly instead (the engine
   drops the frame lag at the same breakpoint) */
@media (max-width: 1080px) {
  .wk-frame { top: 0; height: 100%; }
}

/* under the frame, on the table: name, type, and two ways in */
.wk-below {
  position: absolute; left: 12vw; right: 12vw; bottom: 0; height: 13vh;
  display: flex; align-items: center; gap: var(--sp-4);
  will-change: transform;
}
.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); }

/* while the last piece lifts off the table it takes no clicks */
.l-work.leaving .lay-content { pointer-events: none; }

/* ---------- 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: var(--corner);
  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 {
  position: fixed; inset: 0; width: 100%; height: 100%;
  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); }
