/* ============================================================================
 *  scenes.css — HERO, ABOUT, PORTAL, CONTACT
 *  ---------------------------------------------------------------------------
 *  Every scene fills the stage and is composited by JavaScript through custom
 *  properties (--o opacity, --y travel, and so on). Nothing here animates on a
 *  CSS timeline: the whole page is scrubbed, so CSS owns layout and appearance
 *  and the scene modules own position in time.
 * ========================================================================== */

.scene {
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  opacity: var(--o, 1);
  will-change: opacity, transform;
}


/* "ONLY THE SCENE YOU ARE ACTUALLY IN ACCEPTS CLICKS" — WHICH IS NOT WHAT THIS
 * SAID. `is-live` is set by commerce-intros.js on every section within 1.3
 * viewports of the camera, which on this timeline is routinely two or three of
 * them at once: nine of the thirteen scenes own less than 1.0 vh, so the next
 * section is placed less than a screen below and their full-viewport boxes
 * overlap on purpose. Re-enabling clicks on the whole box for all of them meant
 * the section later in the document covered the one in front of it.
 *
 * It is replaced by the ink-level rule below `.scene[data-off]`. */

/* A scene outside its window is invisible and untouchable — but it STAYS IN
 * THE ACCESSIBILITY TREE. `visibility: hidden` and `content-visibility: hidden`
 * both take a section out of the tree entirely, which on a page like this means
 * a screen-reader user browsing by heading can only ever reach the one section
 * currently under the scroll position. The work is the site; it cannot be
 * conditional on having scrolled to it. */
.scene[data-off="true"] {
  opacity: 0;
  pointer-events: none;
}

/* ==========================================================================
 *  A SCENE'S BOX IS NOT A SURFACE — IT IS WHERE ITS CONTENT STANDS
 *  --------------------------------------------------------------------------
 *  EVERY `.scene` IS A FULL VIEWPORT, AND MOST SCENES ARE SHORTER THAN ONE.
 *  Nine of the thirteen entries in the scene table own less than 1.0 vh, so
 *  the next section is placed LESS than a screen below this one and their
 *  boxes overlap — by design, that is what makes one section still be arriving
 *  as the last one leaves. Both are `data-off="false"` throughout, both are
 *  `z-index: 8`, so the one later in the document paints on top, and its empty
 *  upper half then sat over the lower half of the section in front of it and
 *  swallowed every click that landed there.
 *
 *  MEASURED: with TREASURES composed in the frame, `#treasures-market`'s box
 *  ran from y 398 to 1210 across `#treasures-intro`'s 0 to 812 — so "Learn
 *  more" and "Follow" at y 569 were covered, and `elementFromPoint` returned
 *  the section, not the link. The same collision took the "Company" link on
 *  New World Systems. Those links were not merely hard to hit; they could not
 *  be clicked at all, on any viewport, and nothing about them looked wrong.
 *
 *  The box still lays the content out. It just stops being a click target, and
 *  the content inside it opts back in.
 * ======================================================================== */
/* `.scene` is already `pointer-events: none`; nothing needs to turn it off
   again. What follows is the opt-in, and it is the whole mechanism. */
/* AND IT IS THE INK THAT OPTS BACK IN, NOT THE PANEL. Re-enabling the
   section's direct children was not enough: `.brandfront__panel` is itself a
   centred box several hundred pixels tall, so the neighbouring section's panel
   simply took over as the thing covering the links. Only the elements that
   actually carry ink take clicks — which also keeps the prose selectable,
   something a blanket `pointer-events: none` on the subtree would have cost. */
.scene.is-live :is(h1, h2, h3, p, ul, li, a, button, pre, small, b, span) {
  pointer-events: auto;
}

/* ==========================================================================
 *  01 · HERO
 * ======================================================================== */
.scene--hero { z-index: 6; }

.hero__field {
  position: absolute;
  left: var(--edge);
  right: var(--edge);
  top: calc(var(--edge) + var(--chrome-h));
  height: var(--band-h, 52vh);
}

/* A SOFT GOLD ATMOSPHERE BEHIND THE MARK. CSS only — no shader change, no
   reopened WebGL work. One wide, very quiet radial glow centred behind the
   wordmark, so the mark has something warm to sit in front of instead of
   flat black. It sits under everything (z-index below .hero__lockup) and
   takes no pointer events. */
.scene--hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: radial-gradient(
    46% 38% at 50% 42%,
    var(--signal-glow),
    transparent 70%
  );
}


/* THE OPENING SCREEN IS A LOCKUP, NOT A CORNER.
 *
 * It used to be a left-aligned block pulled off the left edge, with the ASCII
 * wordmark bleeding past the frame and the disciplines and record crammed
 * underneath it at 8px. Three things were wrong and all three read as
 * unfinished rather than as art: THE NAME WAS CROPPED — the bleed cut the "P"
 * clean off, so the first thing on the site was a word you had to reconstruct;
 * the whole composition sat in the top two-thirds with a dead third beneath
 * it; and nothing established what any of it was, so a visitor met an
 * illegible mark and two rows of unexplained numbers.
 *
 * It is one centred column now, distributed down the whole frame: where he is,
 * the name at full width and uncropped, what he does, what he works in, and
 * what the record is. The wordmark is still the artwork — it is simply
 * allowed to be read. */
.hero__lockup {
  position: absolute;
  left: var(--edge);
  right: var(--edge);
  top: calc(var(--chrome-h) + var(--edge));
  bottom: calc(var(--edge) + 46px);
  display: grid;
  align-content: center;
  justify-items: center;
  gap: clamp(16px, 2.6vh, 40px);
  text-align: center;
  transform: none;

  /* THE WIDTH THE ASCII MARK IS SOLVED AGAINST. The mark is a 127-column
     character grid, so its font-size is that column's width divided by 78
     (127 columns x Plex Mono's 0.5992em advance, plus a column of headroom).
     It used to read `100vw` directly, which was fine while the lockup was the
     full frame — but the wide layout below gives the lockup a little over half
     of it, and a mark still sized to the whole viewport would have run under
     the artwork and off the edge. Declaring the available width here, where
     the column is actually defined, is what keeps the two in step. */
  --mark-w: calc(100vw - var(--edge) - var(--edge));
}

.hero__kicker,
.hero__role {
  margin: 0;
  font-family: var(--mono);
  text-transform: uppercase;
  color: var(--ink-dim);
}

/* the one line that says what the name is */
.hero__role {
  font-size: clamp(10px, 0.92vw, 15px);
  letter-spacing: 0.26em;
  color: var(--ink-soft);
}
.hero__role::before,
.hero__role::after {
  content: "";
  display: inline-block;
  width: clamp(16px, 3vw, 46px);
  height: 1px;
  background: var(--signal);
  vertical-align: middle;
  opacity: 0.7;
}
.hero__role::before { margin-right: clamp(10px, 1.4vw, 20px); }
.hero__role::after  { margin-left: clamp(10px, 1.4vw, 20px); }

.hero__title {
  position: relative;
  margin: 0;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.06em;

  font-family: var(--display);
  font-variation-settings: "wdth" var(--display-wdth), "wght" var(--display-wght);
  font-weight: 900;
  font-size: var(--title-size, 12vw);
  line-height: 0.78;
  letter-spacing: -0.012em;
  color: var(--ink-soft);
  white-space: nowrap;
  /* the title is clipped by the stage as it travels, never faded out */
  /* NO BLEED. It cropped the name. The mark is centred and whole. */
  transform: translate3d(0, var(--hero-lockup-y, 0px), 0);
  will-change: transform;
}


/* The cross between the names is DRAWN, not set. As a character it depended on
 * a glyph the display face does not carry, so it fell back to whatever the next
 * font in the stack had — a thin, differently-weighted plus sign sitting in the
 * middle of a very heavy line. Two bars always match the weight around them. */





/* The earlier site's exact Gothic ASCII wordmark replaces the condensed
 * typeset name. Its geometry is frozen, so this frame sizes the original mark
 * as a single piece rather than reflowing or redrawing any characters. */
.hero__title {
  display: block;
  overflow: visible;
  font-size: inherit;
  white-space: normal;
}
.hero__wordmark {
  margin: 0 auto;
  /* LEFT, ALWAYS. The lockup centres its children, and `text-align: center`
     cascades into this <pre> — where it centres every LINE of the artwork
     independently. The rows are different lengths, so each one shifts by a
     different amount and the letterforms shear apart: the mark stops being
     blackletter and becomes noise. The block is centred by margin instead,
     which moves the whole grid without touching the rows inside it. */
  text-align: left;
  width: max-content;
  max-width: 100%;
  /* Bone, not the blue-white it was. It is the largest thing on the page, so
     it sets the page's temperature — and it was setting it to a palette the
     site retired. Held one step back from full ink so it reads as part of the
     composition rather than floating in front of it. */
  color: var(--ink-soft);
  /* DO NOT RETUNE THESE. ASCII art is a character GRID: the letterforms only
     exist because every glyph lands in an exact cell. Changing the line-height
     (0.92) shears the rows apart vertically and the blackletter falls to
     pieces; changing the divisor or the caps rescales the cell against a
     measure the art was not drawn for. This is the original declaration and it
     is the one the artwork was made against. Centre it, size the CONTAINER,
     move it — but leave the metrics alone. */
  font: 700 clamp(3px, min(calc(var(--mark-w) / 78), 1.45svh), 17px) / 0.92 var(--mono);
  font-variant-ligatures: none;
  letter-spacing: 0;
  white-space: pre;
  user-select: none;
  /* NO GLOW. The two coloured halos put light AROUND the characters, and light
   * around a mark is light it is emitting — so it hovered in front of the
   * scene instead of sitting in it. What replaces them is the opposite: one
   * tight, dark, offset shadow, which is contact. It also does real work on a
   * busy ground, holding the thin ASCII strokes legible where a bright pixel
   * lands directly behind one. */
  text-shadow: 0 1px 0 rgba(2, 4, 10, 0.85), 0 0 2px rgba(2, 4, 10, 0.55);
}

.hero__details {
  position: relative;
  z-index: 2;
  display: grid;
  justify-items: center;
  gap: clamp(14px, 2.2vh, 26px);
  margin-top: clamp(6px, 1.2vh, 16px);
  transform: translate3d(0, var(--hero-lockup-y, 0), 0);
  color: var(--ink);
  will-change: transform;
}
.hero__disciplines {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px 26px;
  margin: 0;
  padding: 0;
  list-style: none;
  font: 500 clamp(9px, 0.82vw, 13px) / 1.35 var(--mono);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.hero__disciplines li { position: relative; }
.hero__disciplines li + li::before {
  content: "";
  position: absolute;
  left: -11px;
  top: 0.15em;
  bottom: 0.15em;
  width: 1px;
  background: var(--signal);
  opacity: 0.55;
}
.hero__pills {
  display: flex;
  flex-wrap: wrap;
  gap: 9px;
}
.hero__pill-wrap { position: relative; }
.hero__pill {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  min-height: 30px;
  padding: 6px 13px;
  border: 1px solid rgba(232, 163, 61, 0.32);
  border-radius: 999px;
  /* a faint gold wash at rest, not just a clear pane — the pills read as lit
     rather than as glass sitting flat on the field */
  background: linear-gradient(180deg, rgba(232, 163, 61, 0.10), rgba(2, 5, 17, 0.22));
  color: var(--ink);
  backdrop-filter: blur(5px);
  cursor: default;
  transition: border-color 160ms ease, background-color 160ms ease, transform 160ms ease, box-shadow 160ms ease;
}
.hero__pill-wrap:hover .hero__pill,
.hero__pill:focus-visible {
  border-color: var(--signal);
  background: linear-gradient(180deg, rgba(232, 163, 61, 0.20), rgba(2, 5, 17, 0.24));
  box-shadow: 0 0 0 1px rgba(232, 163, 61, 0.18), 0 6px 18px rgba(232, 163, 61, 0.14);
  transform: translateY(-2px);
}
.hero__pill b {
  font: 800 clamp(9px, 0.72vw, 12px) / 1 var(--mono);
  letter-spacing: 0.01em;
  /* the number is the thing being told, so it carries the site's one signal
     colour — same job the gold does everywhere else on the page */
  color: var(--signal);
}
.hero__pill i {
  color: rgba(244, 239, 230, 0.68);
  font: 700 clamp(7px, 0.55vw, 9px) / 1 var(--mono);
  font-style: normal;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.hero__pill-info {
  position: absolute;
  top: calc(100% + 7px);
  left: 0;
  z-index: 12;
  width: max-content;
  max-width: min(240px, 72vw);
  padding: 8px 10px;
  border: 1px solid rgba(244, 239, 230, 0.24);
  border-radius: 7px;
  background: rgba(2, 5, 17, 0.94);
  color: rgba(244, 239, 230, 0.82);
  font: 600 8px / 1.45 var(--mono);
  letter-spacing: 0.035em;
  opacity: 0;
  transform: translateY(-3px);
  pointer-events: none;
  transition: opacity 140ms ease, transform 140ms ease;
}
.hero__pill-wrap:hover .hero__pill-info,
.hero__pill:focus-visible + .hero__pill-info {
  opacity: 1;
  transform: none;
}

/* ==========================================================================
 *  THE ORBIT — the artwork on the right of the wide layout
 *  --------------------------------------------------------------------------
 *  NOT RENDERED BELOW 900px. It is declared `display: none` here and only
 *  switched on in the wide block at the foot of this section, so a phone never
 *  builds it — three elements running perpetual 3D transforms is not free, and
 *  on a small screen it would be fighting the centred lockup for the frame.
 * ======================================================================== */
.orbit {
  display: none;
  position: absolute;
  top: 50%;
  right: clamp(24px, 6vw, 110px);
  width: min(34vw, 52vh);
  aspect-ratio: 1;
  z-index: 1;
  pointer-events: none;
  /* the rings are laid out in real 3D so a tilted circle reads as a tilted
     circle rather than as a squashed one */
  perspective: 1200px;
  transform-style: preserve-3d;
  /* the drift runs on a longer clock than any of the spins, so the whole
     assembly never settles back into an obvious repeat */
  animation: orbit-float 13s ease-in-out infinite;
}

.orbit__ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 1px solid rgba(232, 163, 61, 0.42);
  transform-style: preserve-3d;
}
/* each ring is tilted on a different axis and turns at its own rate — the
   middle one runs backwards, which is what stops the three reading as one
   object rotating */
.orbit__ring--a { animation: orbit-a 26s linear infinite; }
.orbit__ring--b {
  inset: 11%;
  border-color: rgba(244, 239, 230, 0.30);
  animation: orbit-b 19s linear infinite reverse;
}
.orbit__ring--c {
  inset: 23%;
  border-color: rgba(110, 143, 214, 0.34);
  animation: orbit-c 33s linear infinite;
}

/* a body riding two of the rings. It is a child of the ring, so it inherits
   the ring's rotation for free rather than needing a path of its own. */
.orbit__ring > i {
  position: absolute;
  top: -4px;
  left: calc(50% - 4px);
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--signal);
  box-shadow: 0 0 14px 2px rgba(232, 163, 61, 0.5);
}
.orbit__ring--b > i {
  width: 6px;
  height: 6px;
  top: -3px;
  left: calc(50% - 3px);
  background: var(--ink);
  box-shadow: 0 0 12px 2px rgba(244, 239, 230, 0.45);
}

.orbit__core {
  position: absolute;
  inset: 38%;
  border-radius: 50%;
  background: radial-gradient(circle at 42% 38%, rgba(244, 239, 230, 0.5), rgba(232, 163, 61, 0.22) 45%, transparent 72%);
  filter: blur(3px);
  animation: orbit-pulse 7s ease-in-out infinite;
}

@keyframes orbit-float {
  0%, 100% { transform: translateY(-52%); }
  50%      { transform: translateY(-48%); }
}
@keyframes orbit-a {
  from { transform: rotateX(74deg) rotateZ(0deg); }
  to   { transform: rotateX(74deg) rotateZ(360deg); }
}
@keyframes orbit-b {
  from { transform: rotateY(68deg) rotateZ(0deg); }
  to   { transform: rotateY(68deg) rotateZ(360deg); }
}
@keyframes orbit-c {
  from { transform: rotateX(28deg) rotateY(34deg) rotateZ(0deg); }
  to   { transform: rotateX(28deg) rotateY(34deg) rotateZ(360deg); }
}
@keyframes orbit-pulse {
  0%, 100% { opacity: 0.75; }
  50%      { opacity: 1; }
}

/* IT HOLDS STILL RATHER THAN DISAPPEARING. Under a reduce preference the
   assembly stops where it stands — the rings are the composition on this
   half of the screen, so removing them would leave a hole; only the motion
   is what the preference is asking about. */
@media (prefers-reduced-motion: reduce) {
  .orbit,
  .orbit__ring,
  .orbit__core { animation: none; }
  .orbit { transform: translateY(-50%); }
  .orbit__ring--a { transform: rotateX(74deg); }
  .orbit__ring--b { transform: rotateY(68deg); }
  .orbit__ring--c { transform: rotateX(28deg) rotateY(34deg); }
}

/* ==========================================================================
 *  THE WIDE LAYOUT — lockup left, artwork right
 *  --------------------------------------------------------------------------
 *  Below this width the composition stays exactly as it was: one centred
 *  column, no artwork. The split is only worth having when there is width to
 *  split.
 * ======================================================================== */
@media (min-width: 900px) {
  .hero__lockup {
    right: auto;
    width: min(58%, 900px);
    justify-items: start;
    text-align: left;
    padding-left: clamp(10px, 3vw, 54px);
    /* the mark now solves against its own column, not the viewport */
    --mark-w: min(calc(58vw - 40px), 860px);
  }
  /* the <pre> is centred by `margin: 0 auto`, which would keep it centred
     inside the left column even once the column itself moved */
  .hero__wordmark { margin-left: 0; }
  .hero__details { justify-items: start; }
  .hero__disciplines,
  .hero__pills { justify-content: flex-start; }

  .orbit { display: block; }
}

@media (max-width: 720px) {
  .hero__lockup {
    left: 14px;
    right: 14px;
    transform: translate3d(0, -5vh, 0);
  }
  .hero__details { margin-top: 14px; }
  .hero__disciplines { gap: 4px 15px; }
  .hero__disciplines li + li::before { left: -8px; }
  .hero__pill { min-height: 26px; padding: 4px 9px; }
}



/* The hero already names Patrick's disciplines. This quieter list has a
 * separate job: four current lines of inquiry, presented as editorial notes
 * instead of interactive topic posters. */











/* ==========================================================================
 *  02 · ABOUT — a narrow column that opens out into the whole page
 *  ------------------------------------------------------------------------
 *  The intro is still the framed column. After it the track goes full width
 *  and the content is spread a screen at a time: the record has no heading and
 *  arrives one number at a time, then the disciplines build into a list that
 *  stays standing. The travelling element is now the TRACK, not the column.
 * ======================================================================== */



/* the framed column, now only around the part that is actually a column */







/* (`.column__head--sub` went with "The record" and "Disciplines" — the record
   has no heading now and the disciplines have the `.studying` label instead.) */








/* The retired about scene's framed-quote .about__closing was removed here: it
   set a 3px border, an inset highlight and a hard drop shadow, and it was
   still winning the border and padding over the live rule further down this
   file — which drew a box around the closing line of the new ABOUT section. */

/* ---- THE RECORD ----------------------------------------------------------
 * Three direct facts: no frames, ornaments, or effects. The scale and weight
 * do all of the work against the space field.
 * ------------------------------------------------------------------------ */











/* ---- THE DISCIPLINES -----------------------------------------------------
 * One very small label, then the list assembles itself. Each item arrives on
 * its own and then HOLDS — see `data-hold` in about.js — so what you are
 * looking at by the bottom is not a sequence of words but the whole list,
 * standing. That is the difference between this and the record above it.
 * ------------------------------------------------------------------------ */


























/* Interactive topic posters. The timeline still owns each item's vertical
 * arrival; this layer only responds inside the item, so scroll motion and
 * pointer motion never fight for the same transform. */



















/* AI · connected nodes */









/* E-commerce · live barcode and transaction stack */










/* Systems · nested architecture */








/* Design · a moving shape grammar */








/* DeFi · linked orbital ledger */









/* History · layered archive timeline */









/* Geopolitics · mapped globe and points of tension */









/* Marketing · attention target and signal rays */









/* Media · crop frame and film-strip rhythm */










/* Game development · pixel playfield and crosshair */










/* UI/UX · functional wireframe */









@keyframes topic-drift {
  0%, 100% { translate: 0 0; }
  50% { translate: 0 -8px; }
}
@keyframes topic-spin {
  to { rotate: 360deg; }
}
@keyframes topic-scan {
  0% { translate: 0 0; }
  50% { translate: 0 25vh; }
  100% { translate: 0 0; }
}





/* ==========================================================================
 *  03 · THE PORTAL CAPSULE
 * ======================================================================== */







/* The portal is the closed Ventures scroll. The existing rise, inflate and
 * reverse-close animation still owns its geometry. The skin is deliberately
 * tactile: ragged paper, uneven foxing, rolled spindles and worn timber caps. */















/* Pixel-art pass: the doorway belongs to the same rendered world as the
 * background. Hard color ramps and stepped silhouettes replace soft gold. */








/* Clean pixel scroll: five flat material tones, no faux-gold gloss or noise. */










/* Original portal visual reset. The experimental parchment skin is retired;
 * the capsule is again the clean dark doorway from the initial build. */







/* ==========================================================================
 *  05–06 · COMMERCE PRELUDE
 *  Two compact records bridge the personal Learning chapter into the larger
 *  TREASURES world without turning either brand into another full viewport.
 * ======================================================================== */
.scene--commerce-intro {
  z-index: 8;
  display: grid;
  place-items: center;
  overflow: hidden;
  padding: calc(var(--chrome-h) + var(--edge)) var(--edge) var(--edge);
  /* NO OPACITY VARIABLE. Sections do not fade — an object in space is either
     in front of you or it is not, and `[data-off]` below is what takes an
     off-screen one out of the way. This used to read `var(--commerce-o, 0)`,
     and when the fades were deleted and the JS stopped writing that variable,
     every venture and every logo grid fell back to transparent. The page still
     had all of its content, correctly positioned, and rendered nothing. */
  transform: translate3d(var(--commerce-x, 0), var(--commerce-y, 0), 0);
}

/* ==========================================================================
 *  THE BRANDFRONTS — a mark suspended in the field, its record flying in
 *  --------------------------------------------------------------------------
 *  Made In Vintage and Thrift & Grail were a two-up grid of cards: a photo in
 *  a frame with a caption under it, twice. Two brands shrunk into thumbnails
 *  side by side reads as a logo wall — the ventures were the whole first act of
 *  the story and they were being filed.
 *
 *  This is the SYSTEMS composition applied to both: one object floating at a
 *  declared distance, one column of type arriving beside it, and a lot of empty
 *  field around them. Two rules make it work:
 *
 *  1. THE MARK IS AN OBJECT, NOT A PICTURE. No frame, no mat, no caption bar.
 *     A halo sits behind it so it separates from the starfield without being
 *     boxed, and the whole thing drifts on its own slow clock.
 *
 *  2. THE TYPE FLIES IN. Each line carries its own `--fly` delay off
 *     `data-fly`, so the column assembles line by line as the scene settles
 *     rather than appearing as a finished slab. `--commerce-o` from
 *     commerce-intros.js is the scene's own arrival; these ride on top of it.
 *
 *  MIRRORED: `--left` puts the mark left and the type right; `--right` swaps
 *  them. Nothing else differs, so the pair rhymes without repeating.
 * ======================================================================== */
.scene--brandfront {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(28px, 5vw, 92px);
  width: 100%;
  place-items: center;
}






/* ---- the suspended mark ------------------------------------------------- */



/* the halo: what stops the mark sitting flat on the starfield. Sized off the
   mark, blurred hard, and well under the brightness cap the type needs. */




@keyframes brandfront-drift {
  from { translate: -9px -16px; rotate: -0.7deg; }
  to   { translate: 9px 18px;   rotate: 0.7deg; }
}
@keyframes brandfront-drift-alt {
  from { translate: 10px 15px;  rotate: 0.8deg; }
  to   { translate: -8px -17px; rotate: -0.6deg; }
}

/* ---- the type that flies in --------------------------------------------- */
.brandfront__panel {
  display: grid;
  gap: clamp(14px, 2.2vh, 30px);
  width: min(52vw, 760px);
  color: var(--ink);
}
/* NO ARRIVAL. These lines used to fly in from the side, first on a CSS keyframe
   and then scrubbed by scroll. Both were wrong for the same reason: an object
   standing in space does not assemble itself as the camera approaches it. The
   column is simply there, at its altitude, and you scroll past it. */
.brandfront__panel > * {
  margin: 0;
  /* SCRUBBED, NOT TIMED. This was a 900ms CSS keyframe with a per-line delay,
     which starts when the element is parsed — so all thirty of them across the
     page ran to completion in the first second of load, while their sections
     were still a screen below the fold. Measured at scroll 0, every one
     reported playState "finished": the choreography existed and had never been
     seen. arrive.js drives these two properties off the section's own scroll
     progress instead, so the column assembles as you come to it. */
}



.brandfront__eyebrow {
  font-family: var(--mono);
  font-size: clamp(9px, 0.72vw, 11px);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.brandfront__name {
  font-family: var(--display);
  font-variation-settings: "wdth" 64, "wght" 900;
  font-size: clamp(44px, 7.2vw, 132px);
  line-height: 0.86;
  letter-spacing: -0.01em;
  text-transform: uppercase;
}

.brandfront__copy {
  max-width: 40ch;
  font-family: var(--prose);
  font-size: clamp(16px, 1.5vw, 25px);
  line-height: 1.5;
  color: var(--ink-soft);
}


/* Every venture has somewhere to go now: the live one to its product pages,
   and the two archived ones to made-in-vintage.html / thrift-and-grail.html,
   which are ordinary documents rather than scenes on this stage. */
.brandfront__links {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(14px, 1.8vw, 30px);
  margin-top: clamp(2px, 0.6vh, 8px);
}

.brandfront__links a {
  font-family: var(--mono);
  font-size: clamp(10px, 0.78vw, 12px);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink);
  text-decoration: none;
  padding-bottom: 3px;
  border-bottom: var(--hair) solid currentColor;
  transition: color var(--t-fast) linear, border-color var(--t-fast) linear;
}
.brandfront__links a:hover,
.brandfront__links a:focus-visible { color: var(--signal); }

/* Each venture keeps ONE colour, carried by its eyebrow and nothing else. With
 * no imagery anywhere, that single accent is all that distinguishes the
 * sections from each other — which is the point: one composition seen four
 * times, not four layouts competing. */
.brandfront--systems   .brandfront__eyebrow { color: #8cc5b3; }
.brandfront--treasures .brandfront__eyebrow { color: #d29a6d; }

/* ==========================================================================
 *  THE PRODUCT VOICE — New World OS, and only it
 *  --------------------------------------------------------------------------
 *  New World OS sat in `brandfront--systems` with the same eyebrow, the same
 *  132px display name broken over two lines, and the same evergreen as the
 *  company that makes it. Two screens running the identical composition in the
 *  identical colour read as one section shown twice.
 *
 *  It keeps the family — centred column, same rhythm, same arrival — and
 *  changes the three things that carry identity:
 *
 *  1. THE NAME IS SET IN MACHINE TYPE. Every venture on this site is a NAME,
 *     set in the compressed display face. This one is a piece of SOFTWARE you
 *     install, so it is set in the mono, tracked out, at a third of the size.
 *     That is the difference between a company and a product, said in type.
 *  2. IT TAKES THE STEEL, not the evergreen. tokens.css already reserves steel
 *     for the software side of the practice; this is the only place on the
 *     index that actually cashes that in.
 *  3. IT CARRIES A SPEC ROW rather than a year. A product states what it is;
 *     a company states how long it has been going.
 * ======================================================================== */








/* ==========================================================================
 *  NO-MARK — AND THAT IS THE WHOLE LANGUAGE
 *  There is no imagery in any venture section: no logo, no plate, no glow.
 *  The type is the composition and the shader's space is the only thing behind
 *  it, so the elegance has to come from the spacing rather than from an object.
 * ======================================================================== */
.brandfront--no-mark { grid-template-columns: minmax(0, 1fr); }
.brandfront--no-mark .brandfront__panel {
  grid-column: 1;
  justify-self: center;
  /* narrower than 46vw: there the prose ran to nearly nine words a line and
     read as a paragraph. Centred type only reads as COMPOSED when the column
     is clearly narrower than the frame it sits in. */
  width: min(38vw, 560px);
  text-align: center;
  /* the rhythm is set per-part below, so the flex gap must not also apply */
  gap: 0;
}
.brandfront--no-mark .brandfront__links { justify-content: center; }
.brandfront--no-mark .brandfront__copy { margin-left: auto; margin-right: auto; }

/* THE RULE THE COLUMN HANGS FROM. One short centred hairline at the very top.
 * Without it a centred stack floats with nothing establishing where it begins —
 * which is most of why an all-type section reads as unfinished rather than as
 * restrained.
 *
 * It lives on the COLUMN, not on the eyebrow. The three TREASURES step pages
 * have no eyebrow at all now — their faint line was the "01 / Curate" chapter
 * marker and that is gone — so anchoring the rule to the eyebrow would have
 * quietly dropped it from exactly the three pages with the least on them. */
.brandfront--no-mark .brandfront__panel::before {
  content: "";
  justify-self: center;
  width: clamp(28px, 4vw, 54px);
  height: 1px;
  background: currentColor;
  opacity: 0.4;
  margin-bottom: clamp(18px, 2.6vh, 30px);
}

/* UNEVEN LEADING. Air around the name, the prose closer in beneath it. Even
   spacing would turn the stack into a list. */
.brandfront--no-mark .brandfront__name { margin-top: clamp(14px, 2.2vh, 26px); }
.brandfront--no-mark .brandfront__copy { margin-top: clamp(14px, 2.2vh, 26px); }
.brandfront--no-mark .brandfront__links { margin-top: clamp(20px, 3vh, 36px); }
/* on a step page the name follows the rule directly, so it does not also need
   the gap that separates it from a year line */
.brandfront--no-mark .brandfront__name:first-child { margin-top: 0; }

/* The product's anchor rule goes steel too, so its column does not even BEGIN
 * the same way the company's does. This has to sit after the no-mark block
 * above: that rule paints the bar `currentColor` at the same specificity, so
 * declared any earlier this one simply loses and the line stays bone. */


/* THE MARKETPLACE RANK. The one thing on these two pages that has to be
 * SCANNABLE rather than read: three storefronts, or two channels, set larger
 * than the link row and spaced wide enough that each reads as its own
 * destination instead of as a list of words separated by dots. This is why they
 * are not `.brandfront__links` — that row is a quiet footnote, and a place to
 * actually go should not look like one. */
.brandfront__market {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: clamp(18px, 3vw, 46px);
  margin-top: clamp(20px, 3vh, 38px);
}
.brandfront__market a {
  font-family: var(--display);
  font-variation-settings: "wdth" 78, "wght" 700;
  font-size: clamp(17px, 1.7vw, 27px);
  letter-spacing: 0.01em;
  color: var(--ink);
  text-decoration: none;
  padding-bottom: 4px;
  border-bottom: 1px solid currentColor;
  transition: color var(--t-fast) linear, opacity var(--t-fast) linear;
}
.brandfront__market a span { font-size: 0.62em; opacity: 0.7; }
.brandfront__market a:hover,
.brandfront__market a:focus-visible { color: var(--signal); }
/* the quiet row, when it follows the rank, sits closer than it would alone */
.brandfront--no-mark .brandfront__market + .brandfront__links {
  margin-top: clamp(14px, 2.2vh, 26px);
}

/* ==========================================================================
 *  THE PAIR — two ventures sharing one frame
 *  --------------------------------------------------------------------------
 *  Same composition as a venture page, set twice. The only thing that changes
 *  is scale: two names at 123px on one screen would collide and would also
 *  claim the same weight as TREASURES and SYSTEMS, which each get a frame to
 *  themselves. Held here they read as a period with two names in it.
 *
 *  The gap between the columns is deliberately larger than the columns'
 *  own measure — that space is what stops the pair reading as a two-column
 *  table and keeps each side its own object.
 * ======================================================================== */
/* ==========================================================================
 *  THE STOREFRONTS PAIR — two artifacts at two altitudes
 *  --------------------------------------------------------------------------
 *  THE IDEA IS RIGHT AND THE OLD EXECUTION COULD NOT FIT. Made In Vintage sits
 *  high and Thrift & Grail hangs far below it, because they are one commerce
 *  era read as two chapters — not a two-column card grid. But both were being
 *  crammed inside a section pinned to exactly one viewport: two ~350px objects
 *  plus a ~283px altitude gap is ~636px of composition in ~542px of usable
 *  height once the fixed interface is subtracted. Measured, the upper panel was
 *  clipped under the interface (top 35 against an interface bottom of 63) and
 *  the lower one ran off the bottom of the frame.
 *
 *  So the pair is allowed to be TALLER THAN THE VIEWPORT. That is not a special
 *  case — it is the canvas model working as intended: an object taller than a
 *  screen simply occupies more of the canvas, and the camera passes the first
 *  chapter, then the second. Nothing is squeezed and nothing is clipped. The
 *  scene's own length is derived from the measured result (see config.js), so
 *  the next object still arrives on rhythm.
 * ======================================================================== */
/* WHAT WAS STILL WRONG WITH IT, MEASURED at 1440×900:
 *
 *   · The top panel's year line sat at y 41–65 with the fixed interface's own
 *     bottom edge at 65 — the exact clipping this block's comment says was
 *     fixed. `align-content: start` pinned the pair to the padding edge, and
 *     the −1.2° rotation then lifted its left side back up through it.
 *   · The section never actually grew taller than a screen. `min-height: 100vh`
 *     with content measuring 866px meant it was exactly one viewport, so the
 *     1.12 vh it owns in config.js was buying 0.12 screens of empty scroll
 *     rather than room for a second altitude.
 *   · The two panels sat in the frame's far corners — left edge at x 9, right
 *     edge at 1346 — with 518px of nothing between them. Coverage measured 25%.
 *
 * The intent was never the problem. Two objects at two altitudes is right; it
 * just needed the room it was asking for and a hard floor under the interface.
 */
.brandfront--no-mark.brandfront--pair {
  inset: 0 0 auto 0;
  height: auto;
  min-height: 100vh;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
  /* CENTRED, NOT PINNED TO THE TOP EDGE. `start` pushed the pair hard against
     the padding, which is the one place the interface is. */
  align-content: center;
  gap: clamp(28px, 4.5vw, 88px);
  /* A REAL FLOOR UNDER THE INTERFACE. The old padding cleared the interface
     exactly and left nothing for the rotation to eat. */
  padding-top: calc(var(--chrome-h) + var(--edge) + 4.5vh);
  padding-bottom: 6vh;
}
.brandfront--no-mark.brandfront--pair .brandfront__panel {
  /* The no-mark rule pins every panel to `grid-column: 1`, because it only ever
     expects one of them. Both columns were landing exactly on top of each other
     until this released them back to auto-placement. */
  grid-column: auto;
  /* wider than the old min(40vw, 520px): the pair was reading as two thin
     columns held apart by a void rather than as one era with two names in it */
  width: min(44vw, 560px);
}
/* THE TWO ARE OBJECTS FLOATING IN THE FIELD, NOT ROWS IN A TABLE.
 * Made In Vintage sits high, tipped back a touch; Thrift & Grail hangs
 * FAR BELOW it, tipped the other way. The distance between them is the
 * whole composition — this is not a stagger, it is two things at two
 * different altitudes in the same starfield. */
.brandfront--no-mark.brandfront--pair {
  align-items: start;
}
.brandfront--no-mark.brandfront--pair .brandfront__panel:nth-child(1) {
  /* no negative pull — that is what put it under the interface */
  margin-top: 0;
  transform: rotate(-1.2deg);
}
.brandfront--no-mark.brandfront--pair .brandfront__panel:nth-child(2) {
  /* A REAL ALTITUDE DIFFERENCE, not a nudge. The second chapter begins below
     the fold of the first, so the camera has finished reading Made In Vintage
     before Thrift & Grail is in the frame — which is the progression the
     section is about. Viewport-relative, so the gap is the same gesture on any
     screen; the section grows to contain it. */
  /* 30vh, not 58. The separation still has to read as two altitudes rather
     than as a stagger, but 58vh was solved against a section pinned to the top
     of the frame: it drove the pair's total height past the screen, which is
     what pushed the first chapter up into the interface. Measured against the
     panels' own height, 30vh puts a third of a panel between them — clearly
     two objects, and both of them entirely in the frame. */
  margin-top: clamp(150px, 30vh, 300px);
  transform: rotate(1.6deg);
}
.brandfront--pair .brandfront__name {
  font-size: clamp(34px, 5.4vw, 104px);
}
.brandfront--pair .brandfront__copy {
  max-width: 30ch;
}

@media (max-width: 860px) {
  /* stacked, and the second column gets air above it so the two do not read
     as one continuous list of four paragraphs */
  .brandfront--no-mark.brandfront--pair {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(34px, 7vh, 76px);
  }
  .brandfront--no-mark.brandfront--pair .brandfront__panel {
    width: min(86vw, 460px);
  }
  .brandfront--no-mark.brandfront--pair .brandfront__panel:nth-child(2) {
    margin-top: 0;
  }
}

/* ==========================================================================
 *  A DESTINATION PAGE IS NOT A VENTURE PAGE — SO IT INVERTS
 *  --------------------------------------------------------------------------
 *  "Where to buy" and "Follow along" were the venture composition at a smaller
 *  size: same centred column, same display heading, same rank of quiet links
 *  beneath. The same shape at 79px instead of 123px does not read as a
 *  different KIND of page — it reads as the same page turned down, which is
 *  exactly why they looked like a header they are not.
 *
 *  These two are the only pages here with a JOB rather than a subject. Nobody
 *  needs to read the words "Where to buy"; they need to see eBay, Depop,
 *  Grailed. So the hierarchy turns over:
 *
 *      VENTURE PAGE      huge display name  →  small mono links
 *      DESTINATION PAGE  small mono label   →  huge display destinations
 *
 *  Same materials, same centred column, same hairline — opposite emphasis.
 *  The heading becomes a label announcing what the list is, and the list
 *  becomes the thing you actually look at.
 * ======================================================================== */
.brandfront--step .brandfront__name {
  /* the face, size, weight and tracking come from the SCENE LABEL role at the
     foot of this file — this rule owns only what the inversion itself needs */
  font-variation-settings: normal;
  line-height: 1.2;
  color: var(--ink-dim);
  /* it sits directly under the rule, so it does not take the venture gap */
  margin-top: 0;
}

/* THE SENTENCE CARRIES THE WEIGHT THE HEADING GAVE UP — and it was being asked
   to do that from inside a 26ch cap, which measured 281px in a 900px column.
   A third of its own measure is not "restrained", it is the marooned block the
   brief names as this page's recurring failure: coverage across these two
   screens measured 23% and 11% of the frame. 34ch is still a comfortable
   reading measure — comfortably under the 40ch the venture prose uses — and it
   fills the column it was given. */
.brandfront--step .brandfront__copy {
  max-width: 34ch;
  font-size: clamp(17px, 1.5vw, 25px);
  line-height: 1.42;
  color: var(--ink);
  margin-top: clamp(12px, 1.8vh, 22px);
}

/* THE DESTINATIONS ARE THE PAGE. Set at venture-heading weight and stacked on
 * their own lines rather than strung across one, so each reads as a place to
 * go rather than as an item in a list. */
.brandfront--step .brandfront__market {
  flex-direction: column;
  align-items: center;
  gap: clamp(10px, 1.5vh, 20px);
  margin-top: clamp(26px, 4vh, 52px);
}
.brandfront--step .brandfront__market a {
  font-family: var(--display);
  font-variation-settings: "wdth" 70, "wght" 800;
  font-size: clamp(30px, 3.6vw, 62px);
  line-height: 1;
  text-transform: uppercase;
  border-bottom: 0;
  padding-bottom: 0;
  opacity: 0.92;
}
.brandfront--step .brandfront__market a span {
  font-size: 0.34em;
  vertical-align: middle;
  margin-left: 0.4em;
  opacity: 0.55;
}
.brandfront--step .brandfront__market a:hover,
.brandfront--step .brandfront__market a:focus-visible {
  opacity: 1;
  color: var(--signal);
}

/* ---- the channel rank, which carries a handle under each name ------------
 * "Follow along" and "Where to buy" were the same composition twice. This is
 * what separates them: the marketplace rank is three names, and the channel
 * rank is two names each with the handle it lives at. Both stay the same
 * stacked, display-size destinations — the language does not change, the
 * content does. */
.brandfront__market--handles a {
  display: grid;
  justify-items: center;
  gap: clamp(2px, 0.5vh, 6px);
}
.brandfront__market--handles b { font: inherit; }
.brandfront__market--handles small {
  font-family: var(--mono);
  font-size: var(--meta-size);
  font-weight: 400;
  letter-spacing: var(--meta-track);
  text-transform: none;
  color: var(--ink-dim);
}
.brandfront__market--handles a:hover small,
.brandfront__market--handles a:focus-visible small { color: inherit; }

/* ---- narrow: the object goes above the type, the mirror is dropped ------- */
@media (max-width: 900px) {
  .scene--brandfront {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(20px, 4vh, 44px);
  }
  
  
  
  
}
/* ==========================================================================
 *  A SECOND, SUPERSEDED COPY OF THE PAIR LAYOUT WAS DELETED FROM HERE.
 *  --------------------------------------------------------------------------
 *  Thirty-six lines of `#storefronts` rules — ID specificity, plus one
 *  `align-items: start !important` — restating the whole composition that
 *  `.brandfront--no-mark.brandfront--pair` declares 200 lines above. Being
 *  both later and heavier, this copy won every conflict, so the class block
 *  that documents the design was inert and editing it changed nothing.
 *
 *  It is where the clipping came from. The class block says, in as many words,
 *  "no negative pull — that is what put it under the interface", and then this
 *  set `margin-top: clamp(-40px, -4vh, 0px)` on the first panel and pulled it
 *  36px back up into the interface. The second panel's 42vh offset likewise
 *  overrode the tuned value and drove the pair past the bottom of the frame.
 *
 *  Two keyframe sets went with it — `storefront-drift` and
 *  `storefront-drift-far`, for a hover-bob on panels that stopped animating
 *  when the fades were removed.
 *
 *  THE LESSON, since this is the second time it has happened in this file: a
 *  rewrite appended at the bottom does not replace what it was rewriting. Edit
 *  the block that owns the thing.
 * ======================================================================== */

/* the far card is drawn at 0.78, which would render its smallest text at about
 * eight pixels. The body copy is set up to compensate so it stays legible —
 * partially, not fully: closing the gap completely would undo the distance the
 * scale is there to create. */


/* the display size was set for a card that owned 78vw. At this one it has to
   come down, or the name breaks out of its own column. */







/* Corner ticks — four small register marks inside the card, in the same family
 * as the mono labels around them. Eight one-pixel gradients, no extra element. */







/* a short rule leading into the eyebrow — the label is introduced rather than
 * simply starting, which is most of what separates set type from placed type */











/* Thrift & Grail is the cold middle of the three. Made In Vintage is warm paper
 * and TREASURES is warm dark; putting the second storefront on ink and steel
 * keeps the trio from reading as one long sepia run, and matches what the
 * chapter actually was — the same eye, run harder and faster. */














/* ---- no-media: the plate is gone, the copy takes the whole card ---------- */



/* TREASURES is a two-page chapter. This first page owns the entire viewport;
 * the existing collage and operating brief become page two immediately after. */










/* ==========================================================================
 *  07 · TREASURES — vintage commerce drifting through space
 * ======================================================================== */

























/* TREASURES chapter — composed on the same visual rhythm as New World
 * Systems: one focused object, one open text column, one contained brief. */




























@keyframes treasures-stars-drift {
  to { background-position: 40px 7px, -32px 24px; }
}

/* Open-space flight replaces the former circular portal. The PNG crosses the
 * viewport vertically with scroll progress while the star field moves more
 * slowly behind it, giving the chapter depth without introducing a new frame. */






/* Brand-led TREASURES collage: real operation photography floats as a set of
 * editorial records, with the mark acting as the quiet anchor behind them. */














/* ---- no-media: the plate is gone, only the caption pill stays anchored --- */










/* ---- THE SIGN-OFF --------------------------------------------------------
 * "Building my own way since 2021" used to own a whole scene of its own,
 * climbing out of the vanishing point. It was deleted and brought down here,
 * where it does more work: it is the last thing said before the address.
 *
 * It stands in the upper band so it does not fight the control at the centre,
 * and go.js fades it as the circle grows — the room opens straight through
 * where it was standing, so leaving it under there would put a black disc
 * halfway across the word.
 * ------------------------------------------------------------------------ */










/* ---- NEW WORLD SYSTEMS --------------------------------------------------
 * The former contact sphere is now a rotating systems globe. The land field
 * is twice the globe's width and carries a duplicated second hemisphere, so
 * translating it by exactly half creates a seamless loop instead of a jump. */







@keyframes nws-globe-spin {
  to { transform: translateX(-50%); }
}
@keyframes nws-globe-float {
  0%, 100% { margin-top: -4px; }
  50% { margin-top: 5px; }
}


















/* the sliced type inside the circle */





















/* ==========================================================================
 *  05.1 · NEW WORLD OS — THE PRODUCT PLATE
 *  --------------------------------------------------------------------------
 *  The one deliberately SMALL composed object on the site. Everything around
 *  it is full-bleed with its name set at 120–140px; this is a contained plate
 *  about 620px wide with its name at a third of that. That gap is the whole
 *  point — it is what tells you the product sits inside the company on the
 *  previous screen instead of standing next to it. Growing this to match its
 *  neighbours would flatten the hierarchy the section exists to state.
 *
 *  Its evergreen is the one New World Systems already uses, so the company and
 *  its product read as one family across the two screens.
 * ======================================================================== */


/* the same four register marks that hold the commerce cards, in the product's
   own colour — eight one-pixel gradients, no extra element */













/* ==========================================================================
 *  FINAL · CONNECT
 * ======================================================================== */
.scene--connect {
  z-index: 11;
  position: absolute;
  /* FULL VIEWPORT, like every other section. It was `inset: auto 0 0 0` —
     bottom-anchored, so the section box started 367px down and its own height
     decided where its top edge fell. On a canvas where an object's altitude is
     its section top, that offset is a lie: the last screen could never land
     square in the frame no matter what the scroll did. */
  inset: 0;
  display: grid;
  grid-template-rows: auto 1fr;
  align-content: center;
  gap: clamp(26px, 5vh, 70px);
  /* The bottom inset is the SUN'S ROOM. The disc sits on the vanishing point at
     the foot of the frame and its corona reaches up out of it, so the content
     is held clear of that band rather than standing in front of it. */
  padding: calc(var(--chrome-h) + var(--edge) + 1.5vh) var(--edge) clamp(118px, 25vh, 290px);
  color: var(--ink);
  /* NO SCRIM. This carried two stacked dark gradients — a left-to-right wash
   * and a top-to-bottom one — which put a sheet of near-black between the type
   * and the world. The section is supposed to be standing IN the space, not on
   * a panel laid over it, and the sun rising at the bottom of the frame was the
   * thing the scrim was hiding most. The words sit straight on the stars now.
   *
   * What that costs: the background behind this text is no longer a known
   * colour, so contrast here cannot be asserted from tokens alone — it is
   * checked by sampling the live canvas. Putting any wash back means
   * re-sampling, not eyeballing it. */
  /* Placed by commerce-intros.js off the same `--commerce-y` as every other
     section. It had a private `--connect-y` (whose 5vh fallback displaced it
     before the first frame) and a `--connect-o` that was rewritten to the
     constant 1 sixty times a second. */
  transform: translate3d(0, var(--commerce-y, 0), 0);
  overflow: hidden;
}









@keyframes connect-cue {
  0%, 100% { translate: 0 0; opacity: 0.55; }
  50%      { translate: 0 5px; opacity: 1; }
}

/* ==========================================================================
 *  NARROW VIEWPORTS
 * ======================================================================== */
/* The track runs the full measure at every width now. Only the framed intro
   inside it has a column's width, and that is set on the intro itself. */
@media (max-width: 1080px) {
  
  
  
  
  
  
  
  
  
  
  
  
  
  /* four across needs about 240px a column to keep a channel name on one line;
     below that the index folds to 2×2 rather than letting every name wrap */
  .connect__directory { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  
  /* 52vw is a share that only reads as "small" while the viewport is wide.
     As the screen narrows the plate has to take more of it or it stops being
     a restrained object and becomes a cramped one. */
  
}
@media (max-width: 720px) {
  .scene--commerce-intro {
    padding: calc(var(--chrome-h) + var(--edge) + 3vh) var(--edge) var(--edge);
  }
  
  
  
  /* The pair stacks below 860px, in the `.brandfront--pair` media block near
     the foot of this file. A third `#storefronts` copy of that rule lived here
     and is gone with the rest of the ID block. */

  
  /* stacked, the big hang would push Thrift & Grail off the bottom of the
     screen — it keeps a nudge and its tilt, and drops the offset */
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  .scene--connect {
    gap: 12px;
    /* the sun's room again — it is proportional to the viewport, so the narrow
       layout has to reserve it too or the type stands in the disc */
    padding: calc(var(--chrome-h) + var(--edge) + 1vh) 14px clamp(96px, 23vh, 210px);
  }
  .connect__meta { justify-content: space-between; }
  .connect__meta span { font-size: 6px; letter-spacing: 0.18em; }

  /* page one keeps every part — it is five short lines and they are the whole
     point of the screen; only the measures and the gaps come in.
     This has to restate the sun's room: the `.scene--connect` rule above sets
     it inside this same query and, at equal specificity, the later rule wins —
     so page one would take the 23vh floor and run off a 640px-tall phone. */
  .scene--connect-open {
    gap: clamp(10px, 2vh, 22px);
    padding-bottom: clamp(84px, 18vh, 170px);
  }
  .connect-open { gap: clamp(10px, 1.8vh, 20px); padding-bottom: clamp(16px, 4vh, 40px); }
  .connect-open__eyebrow { padding-top: 15px; font-size: 8px; letter-spacing: 0.26em; }
  .connect-open__title { font-size: clamp(62px, 21vw, 104px); }
  .connect-open__lede { max-width: 30ch; font-size: clamp(14px, 4.2vw, 18px); }
  
  
  
  

  /* The index stays 2×2 on phones. One column would read better but the scene
     is an absolutely-positioned stage the height of the viewport — it does not
     scroll — so four stacked groups simply run off the bottom of the screen.
     Two columns is what fits; the group descriptions come off to buy the room. */
  
  
  
  
  .connect__directory {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 18px 14px;
    align-self: start;
  }
  .connect__group {
    gap: 10px;
    padding-top: 10px;
  }
  .connect__group header { gap: 6px; }
  .connect__group header > span { font-size: 7px; }
  .connect__group h3 { font-size: clamp(20px, 6vw, 28px); }
  .connect__group header p { display: none; }
  .connect__links { grid-template-columns: 1fr; }
  .connect__links a { padding: 9px 0 8px; }
  .connect__links b { font-size: 10px; }
  .connect__links small { font-size: 8px; }
  .connect-index__foot span:nth-child(2) { display: none; }

  /* on a phone the plate is nearly the screen — the hierarchy is carried by
     the type sizes and the frame by then, not by the object's share of width */
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
}

/* ==========================================================================
 *  CONNECT — MERGED PAGE: PINNED TITLE + SLIDING DIRECTORY
 *  --------------------------------------------------------------------------
 *  "Let's connect" sits in the top half and does not move for the whole
 *  scene. The four channels ride in the bottom half on `--connect-slide`
 *  (set by connect.js from scene progress): +100% below the frame at scene
 *  start, 0 in resting position mid-scene, held there until scene end.
 *  Overrides the older three-row layout that centred the invitation and
 *  hid the index behind it.
 * ======================================================================== */
/* The three-part layout this described — a meta strip, a pinned title and a
   sliding directory — was deleted with the old Connect. The rule outlived its
   markup and, on ID specificity, went on forcing a three-row grid onto a
   section that now has ONE child: the rebuilt composition was jammed into the
   first row with two-thirds of the frame empty beneath it. Dead styling for
   removed markup does not simply sit there — it wins. */
#connect.scene--connect {
  grid-template-rows: auto;
  align-content: center;
  padding: calc(var(--chrome-h) + var(--edge) + 2vh) clamp(20px, 5vw, 108px) clamp(70px, 14vh, 170px);
  overflow: hidden;
}

/* ==========================================================================
 *  FLOAT SCENES — a scatter of marks in space
 *  --------------------------------------------------------------------------
 *  Each mark is absolutely positioned with its own --x/--y/--s/--r, so the
 *  arrangement reads as objects at different distances rather than a grid.
 *  The whole section rides `--commerce-y` from commerce-intros.js like every
 *  other scroll-past chapter — nothing fades, motion carries it in and out.
 * ======================================================================== */



@media (max-width: 720px) {
}

/* ==========================================================================
 *  ABOUT — the one section that is read
 *  --------------------------------------------------------------------------
 *  Everything else on this page is composed to be SEEN: solved to a fraction
 *  of the frame, flown past, held at a depth. This is prose, so it is built to
 *  a reading measure and nothing else. No panel and no rule sits behind it —
 *  the starfield is the background here exactly as it is under the statement,
 *  because anything drawn between the stars and the type turns the section
 *  back into a poster (the mistake the statement already made once).
 *
 *  It rides `--commerce-y` like every other scroll-past chapter, so it arrives
 *  and leaves on the same motion as the mark scatters either side of it.
 * ======================================================================== */
.scene--about {
  z-index: 3;
  position: absolute;
  /* TALLER THAN A SCREEN WHEN IT HAS TO BE — the second section allowed to be,
     after the storefronts pair, and for a better reason: this is the only
     section on the site that is READ rather than seen. Pinned to exactly one
     viewport with `overflow: hidden` above it, the prose was simply cut off on
     a short screen — measured at 1398×596 the panel ran y65..748 and the whole
     closing line was 152px below a fold that cannot be scrolled to, because a
     `.scene` does not scroll. The canvas does not care: an object taller than a
     screen occupies more of it, and the camera passes the top of the paragraph
     and then the bottom. `about` owns 1.30 vh, which covers it. */
  inset: 0 0 auto 0;
  height: auto;
  min-height: 100vh;
  display: grid;
  place-items: center;
  align-content: center;
  padding: calc(var(--chrome-h) + var(--edge) + 2vh) var(--edge) 4vh;
  transform: translate3d(var(--commerce-x, 0), var(--commerce-y, 100vh), 0);
  opacity: var(--commerce-o, 1);
  pointer-events: none;
  color: var(--ink);
}

.about__panel {
  display: grid;
  gap: clamp(14px, 2.2vh, 26px);
  /* a reading measure, not a share of the viewport — this is the only block on
     the site whose width is set by the line length that is comfortable to read */
  width: min(78ch, 90vw);
}

.about__panel > * {
  margin: 0;
}

.about__eyebrow {
  font-family: var(--mono);
  font-size: clamp(9px, 0.72vw, 11px);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.about__eyebrow::before {
  content: "";
  display: inline-block;
  width: 22px;
  height: 1px;
  background: var(--signal);
  vertical-align: middle;
  margin-right: 12px;
  opacity: 0.85;
}

/* The opening line IS the heading. Set in the display face but nowhere near
   display size — it has to stay a sentence you read, not a slab you look at. */
.about__lede {
  font-family: var(--display);
  font-variation-settings: "wdth" 84, "wght" 620;
  /* HEIGHT IS A CONSTRAINT HERE, NOT ONLY WIDTH. Sized on `vw` alone this ran
     52px on any wide screen regardless of how much room there was underneath
     it: measured at 1398×596 the lede took 232px, the panel ran y65..748, and
     152px of the site's only prose — the closing line and the last of the
     second paragraph — was below the fold of a section that cannot scroll.
     `6.2vh` binds before `3.4vw` on any viewport shorter than about 750px,
     which is every laptop with a browser toolbar open. */
  font-size: clamp(22px, min(3.4vw, 6.2vh), 52px);
  line-height: 1.22;
  letter-spacing: -0.005em;
  text-wrap: balance;
  color: var(--ink);
}

.about__body {
  display: grid;
  gap: clamp(10px, 1.5vh, 18px);
}
.about__body p {
  margin: 0;
  font-family: var(--prose);
  font-size: clamp(15px, 1.35vw, 22px);
  line-height: 1.62;
  color: var(--ink-dim);
}

.about__closing {
  font-family: var(--prose);
  font-style: italic;
  font-size: clamp(15px, 1.3vw, 21px);
  line-height: 1.55;
  color: var(--ink-soft);
  padding-top: clamp(4px, 0.8vh, 10px);
  border-top: 1px solid var(--ink-line-faint);
}

@media (max-width: 720px) {
  .about__panel { gap: 14px; }
  .about__lede { font-variation-settings: "wdth" 88, "wght" 640; }
}

/* ==========================================================================
 *  FLOAT PALETTE CORRECTION
 *  --------------------------------------------------------------------------
 *  The float scenes were authored against the retired COLD palette — a set of
 *  blue-whites (rgba(229, 237, 251, …), rgba(146, 176, 244, …)) that predate
 *  the bronze-and-gold ground in tokens.css. Left alone they were the only
 *  cool-white type on the page, which is exactly the "clinical" read the whole
 *  palette moved away from. These pull the field back onto the site's own inks.
 * ======================================================================== */

/* --------------------------------------------------------------------------
 *  WORDMARK VARIANT — for platforms whose identity has no symbol
 *  Depop, Grailed, Poshmark, Mercari and Whatnot do not have a mark to draw.
 *  Rather than invent one — which is what made the first pass of this field
 *  look approximate — they are set as TYPE in the site's own condensed face.
 *  Sized so a word carries roughly the optical weight of a 24×24 glyph beside
 *  it, so the field still reads as one set rather than icons plus captions.
 * ------------------------------------------------------------------------ */

@media (max-width: 720px) {
}

/* ==========================================================================
 *  THE HERO ON A NARROW SCREEN
 *  --------------------------------------------------------------------------
 *  THE LEFT BLEED IS A DESKTOP DEVICE, AND IT INVERTED ON A PHONE. The lockup
 *  and the title each carry a negative x offset — clamp(-110px, -7vw, -48px)
 *  and clamp(-92px, -5.2vw, -62px) — to pull the wordmark off the left edge on
 *  a wide screen. On a NEGATIVE range clamp() resolves a small viewport to the
 *  largest magnitude, not the smallest: at 375px the two together came to
 *  −110px, so the ASCII wordmark hung a third of its width off-canvas, the
 *  disciplines ran past the right edge, and the pills sat on top of the type.
 *
 *  A phone gets the composition the bleed was standing in for instead: one
 *  centred column, nothing cropped. The wordmark's own size solve already fits
 *  the measure (127 characters against a /78 divisor at a mono advance of
 *  ~0.6em), so it needs no help beyond being put back on the canvas.
 * ======================================================================== */
@media (max-width: 720px) {
  .hero__lockup {
    transform: translate3d(0, -3vh, 0);
    justify-items: center;
    text-align: center;
  }

  .hero__title {
    transform: translate3d(0, calc(var(--hero-lockup-y, 0px) - 6px), 0);
  }

  .hero__wordmark {
    transform: none;
    margin-inline: auto;
  }

  .hero__details {
    justify-items: center;
    width: 100%;
    margin-top: clamp(18px, 3vh, 28px);
  }

  .hero__disciplines,
.hero__pills { justify-content: center; }

  .hero__disciplines { gap: 4px 16px; }

  /* THE TOOLTIP IS ANCHORED TO THE ROW, NOT THE PILL. Each info panel is up to
     240px wide and was positioned from its own pill's left edge, so the second
     and third ran off the right of a 375px screen. Spanning the whole row
     cannot overflow whichever pill is pressed, and gives the sentence more
     measure than it had. */
  .hero__pills { position: relative; }

  .hero__pill-info {
    left: 0;
    right: 0;
    width: auto;
    max-width: none;
    text-align: left;
  }
}

/* ==========================================================================
 *  THE SCROLL CUE
 *  --------------------------------------------------------------------------
 *  The hero is a wordmark on a starfield. Nothing peeks above the fold, the
 *  stage is fixed so the page does not visibly move under it, and on a
 *  trackpad there is no scrollbar — so the first screen gave a visitor no
 *  reason to believe there was a second. This is the smallest thing that
 *  says otherwise, and it gets out of the way immediately: hero.js drives
 *  --cue-in to 0 across the first fraction of the descent.
 * ======================================================================== */
.hero__cue {
  position: absolute;
  left: 50%;
  bottom: clamp(14px, 3vh, 30px);
  z-index: 4;
  margin: 0;
  display: grid;
  justify-items: center;
  gap: 7px;
  translate: -50% 0;
  opacity: var(--cue-in, 1);
  transition: opacity var(--t-mid) var(--ease-out);
  pointer-events: none;
}

.hero__cue span {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

/* the rule falls, pauses, and falls again — a scroll, not a loading bar */
.hero__cue i {
  display: block;
  width: 1px;
  height: 26px;
  background: linear-gradient(180deg, var(--signal), transparent);
  animation: cue-fall 2.1s var(--ease-inout) infinite;
  transform-origin: 50% 0;
}

@keyframes cue-fall {
  0%, 12%  { transform: scaleY(0); opacity: 0; }
  40%      { transform: scaleY(1); opacity: 1; }
  72%      { transform: scaleY(1); opacity: 1; }
  100%     { transform: scaleY(1) translateY(26px); opacity: 0; }
}

@media (max-width: 720px) {
  .hero__cue { bottom: clamp(10px, 2vh, 20px); }
  .hero__cue i { height: 20px; }
}

/* ==========================================================================
 *  THE MARK FIELDS — "Building with" and "Listing on"
 *  --------------------------------------------------------------------------
 *  THE SCATTER IS GONE. These were eight and ten marks flung at hand-written
 *  --x/--y percentages, each drifting on its own CSS keyframe loop. Three
 *  things were wrong with that, and they compounded:
 *
 *    · NOTHING DECIDED WHERE ANYTHING WENT. A scatter has to be composed to
 *      read as composed; these were arbitrary, so marks crowded the edges,
 *      sat at unrelated sizes, and the two sections looked like two different
 *      accidents rather than a pair.
 *    · THE DRIFT RAN ON ITS OWN CLOCK. `float-drift` was a time-based
 *      keyframe loop on a page where every other movement is scrubbed by the
 *      scroll. Two unsynchronised motion systems on screen at once is exactly
 *      what "the motion isn't consistent" means.
 *    · THE LABELS WERE UNREADABLE. 9px mono at 68% opacity under a glyph.
 *
 *  What replaces it is ORDERED but still in space: one perspective, marks on
 *  an even grid, and each ROW parked at its own real Z so the field has depth
 *  without anything being placed by hand. The scroll flies the camera through
 *  it — see marks.js — and the arrival staggers across the grid on that same
 *  scroll clock. Uniform glyph box, so a wordmark and an icon carry the same
 *  optical weight.
 * ======================================================================== */
.scene--marks {
  z-index: 3;
  position: absolute;
  inset: 0;
  display: grid;
  align-content: center;
  justify-items: center;
  gap: clamp(26px, 5vh, 58px);
  padding: calc(var(--chrome-h) + var(--edge)) var(--edge) var(--edge);
  transform: translate3d(var(--commerce-x, 0), var(--commerce-y, 100vh), 0);
  pointer-events: none;
  color: var(--ink);
}

.marks__label {
  margin: 0;
  font-family: var(--mono);
  font-size: clamp(9px, 0.78vw, 12px);
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.marks__label::before {
  content: "";
  display: inline-block;
  width: 22px;
  height: 1px;
  background: var(--signal);
  vertical-align: middle;
  margin-right: 12px;
  opacity: 0.85;
}

/* one camera for the whole field */
.marks__field {
  perspective: 1200px;
  perspective-origin: 50% 45%;
  width: min(1500px, 100%);
}

.marks__grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(var(--per-row, 4), minmax(0, 1fr));
  gap: clamp(34px, 7vh, 86px) clamp(18px, 3vw, 56px);
  transform-style: preserve-3d;
}

.marks__item {
  display: grid;
  justify-items: center;
  gap: 11px;
  transform-style: preserve-3d;
  /* each ROW sits at its own depth — real perspective, not a fake scale, so
     the far row is genuinely smaller and genuinely parallaxes */
  /* the far row genuinely stands further away — a fact about its position in
     the space, not something that animates */
  transform: translate3d(0, 0, calc(var(--row, 0) * -70px));
}

/* ONE BOX FOR EVERY MARK. An icon and a wordmark have to occupy the same
   footprint or the grid reads as ragged — this is what stops "depop" sitting
   visually smaller than the eBay glyph beside it. */
.marks__glyph {
  display: grid;
  place-items: center;
  width: 100%;
  height: clamp(52px, 7.2vw, 108px);
  color: var(--ink-soft);
  transition: color var(--t-fast) linear;
}

.marks__glyph svg {
  width: auto;
  height: 100%;
  max-width: 100%;
  display: block;
}

/* the wordmark brands, set to match the glyphs' optical weight */
.marks__glyph b {
  font-family: var(--display);
  font-variation-settings: "wdth" 74, "wght" 800;
  font-size: clamp(20px, 2.9vw, 44px);
  line-height: 1;
  letter-spacing: -0.005em;
  white-space: nowrap;
}

/* READABLE. It was 9px at 0.68 alpha; it is the same size as every other
   label on the site now, and it clears AA against the field. */
.marks__name {
  font-family: var(--mono);
  font-size: clamp(10px, 0.86vw, 14px);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-dim);
  text-align: center;
}

/* the warm field takes the signal, the cool one takes bone — the pair reads
   as two chapters of one language rather than two colour schemes */
.scene--marks[data-tone="warm"] .marks__glyph { color: var(--signal); }

@media (max-width: 720px) {
  .marks__grid {
    grid-template-columns: repeat(var(--per-row-mobile, 3), minmax(0, 1fr));
    gap: 22px 12px;
  }
  .marks__glyph { height: 34px; }
  .marks__glyph b { font-size: 14px; }
  .marks__name { font-size: 8px; letter-spacing: 0.1em; }
  /* depth reads as clutter on a small screen — flatten it */
  .marks__item { transform: none; }
}

/* ==========================================================================
 *  STUDYING — the breadth, set as type
 *  --------------------------------------------------------------------------
 *  The two sections after this are grids of brand marks. A third grid would
 *  make the run read as a logo wall, and these are not logos — they are
 *  subjects. So they are set the way the rest of the site sets language: one
 *  centred run at display size, wrapping on its own measure, with the gold
 *  interpunct between items instead of a rule or a bullet.
 *
 *  Arrival is the same scroll clock as everything else (arrive.js), stepping
 *  along the list so it writes itself in as the section comes.
 * ======================================================================== */
.scene--studying {
  z-index: 3;
  position: absolute;
  inset: 0;
  display: grid;
  align-content: center;
  justify-items: center;
  gap: clamp(22px, 4vh, 46px);
  padding: calc(var(--chrome-h) + var(--edge)) var(--edge) var(--edge);
  transform: translate3d(var(--commerce-x, 0), var(--commerce-y, 100vh), 0);
  pointer-events: none;
  color: var(--ink);
}

.studying__label {
  margin: 0;
  font-family: var(--mono);
  font-size: clamp(9px, 0.78vw, 12px);
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.studying__label::before {
  content: "";
  display: inline-block;
  width: 22px;
  height: 1px;
  background: var(--signal);
  vertical-align: middle;
  margin-right: 12px;
  opacity: 0.85;
}

.studying__list {
  list-style: none;
  margin: 0;
  padding: 0;
  /* a measure, not the frame — the same rule the statement is built on: type
     that touches both edges has no void left to float in */
  /* a GENEROUS measure: the words are large, so the run needs room to break
     into three or four lines rather than a column one word wide */
  width: min(1240px, 92vw);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: baseline;
  gap: clamp(8px, 1vw, 16px) clamp(14px, 1.8vw, 30px);
  text-align: center;
}

.studying__list li {
  font-family: var(--display);
  font-variation-settings: "wdth" 78, "wght" 680;
  font-size: clamp(26px, 5.2vw, 88px);
  line-height: 1.06;
  letter-spacing: -0.008em;
  color: var(--ink-soft);
  position: relative;
}

/* the separator is a mark of punctuation, not furniture — and never after the
   last item, where it would read as a trailing comma */
.studying__list li:not(:last-child)::after {
  content: "·";
  position: absolute;
  right: clamp(-9px, -0.9vw, -15px);
  color: var(--signal);
  opacity: 0.72;
  font-variation-settings: "wdth" 100, "wght" 400;
}

.studying__note {
  margin: 0;
  max-width: 52ch;
  font-family: var(--prose);
  font-style: italic;
  font-size: clamp(14px, 1.2vw, 20px);
  line-height: 1.55;
  text-align: center;
  color: var(--ink-dim);
}

@media (max-width: 720px) {
  .studying__list {
    width: 94vw;
    gap: 7px 16px;
  }
  .studying__list li { font-size: clamp(17px, 6.4vw, 26px); }
  .studying__list li:not(:last-child)::after { right: -10px; }
  .studying__note { font-size: 13px; }
}

/* ==========================================================================
 *  COMPOSITION — every scene gets its own, because nine of them had the same
 *  --------------------------------------------------------------------------
 *  THE DIAGNOSIS. Studying, stack, markets, all three TREASURES screens,
 *  systems, new-world-os and connect were the identical composition: a small
 *  centred block floating in the middle of a dark frame, with different words
 *  in it. Same alignment, same scale, same structure, nine times. That is a
 *  template, not a design, and it is why every screen felt like the last one.
 *
 *  THE PRINCIPLE. An object in space has no obligation to be centred. It can
 *  sit against an edge, be cropped by the frame, tower over you, or be small
 *  and far away — and WHICH of those it is should be an argument about the
 *  content, not a default. What follows gives each scene a position and a
 *  scale it has earned, while every one of them still travels at the camera's
 *  rate and still uses the same two typefaces and one gold.
 *
 *  The frame edge is a real boundary: nothing here bleeds off-canvas, because
 *  cropped type reads as broken rather than as bold (see the hero wordmark).
 *  "Against the edge" means to the margin, not past it.
 * ======================================================================== */

/* ---- STUDYING: a wall of subjects, anchored low-left --------------------
   It was a centred paragraph of nouns. It is a breadth claim, so it should
   feel like a wall you pass rather than a caption you read. */
.scene--studying {
  justify-items: start;
  align-content: end;
  padding-bottom: clamp(64px, 14vh, 190px);
}
.scene--studying .studying__list {
  justify-content: flex-start;
  text-align: left;
  width: min(1320px, 100%);
}
.scene--studying .studying__note {
  text-align: left;
  margin-left: 2px;
}

/* ---- BUILDING WITH: the label turns and runs up the left edge -----------
   The two mark grids were indistinguishable from each other. This one keeps
   the grid and moves its label out of the column entirely. */
.scene--marks[data-tone="cool"],
#stack.scene--marks {
  grid-template-columns: auto minmax(0, 1fr);
  align-content: center;
  justify-items: stretch;
  gap: clamp(18px, 3vw, 54px);
}
#stack .marks__label {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  align-self: center;
  letter-spacing: 0.42em;
  white-space: nowrap;
}
#stack .marks__label::before {
  width: 30px;
  margin: 0 0 14px 0;
  display: block;
}

/* ---- LISTING ON: not a grid — an index ---------------------------------
   Ten marketplaces read better as a ranked list than as a second grid of
   glyphs, and it gives the pair of sections two different silhouettes. */
#markets.scene--marks {
  align-content: center;
  justify-items: center;
}
#markets .marks__grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(10px, 1.6vh, 22px) clamp(40px, 7vw, 120px);
  width: min(1180px, 100%);
  counter-reset: market;
}
#markets .marks__item {
  grid-template-columns: auto 1fr auto;
  align-items: center;
  justify-items: start;
  gap: clamp(14px, 2vw, 30px);
  padding-bottom: clamp(8px, 1.4vh, 16px);
  border-bottom: 1px solid var(--ink-line-faint);
  text-align: left;
}
#markets .marks__item::before {
  counter-increment: market;
  content: counter(market, decimal-leading-zero);
  font-family: var(--mono);
  font-size: clamp(9px, 0.8vw, 12px);
  letter-spacing: 0.16em;
  color: var(--signal);
  opacity: 0.8;
}
#markets .marks__glyph {
  height: clamp(26px, 3vw, 44px);
  width: auto;
  justify-self: start;
}
#markets .marks__name { text-align: right; justify-self: end; }

/* ---- TREASURES INTRO: monumental, and the only one that towers ----------
   It is the venture that is actually running, so it is the largest thing in
   the run and it sits off-centre with real void beside it. */
#treasures-intro.scene--brandfront { place-items: center start; }
#treasures-intro .brandfront__panel {
  width: min(88vw, 1180px);
  gap: clamp(10px, 1.6vh, 22px);
}
#treasures-intro .brandfront__name {
  font-size: clamp(64px, 15vw, 300px);
  line-height: 0.82;
  color: var(--signal);
}
#treasures-intro .brandfront__copy { max-width: 30ch; }

/* ---- WHERE TO FIND IT: right-anchored, two ranks side by side -----------
   The merge of "Where to buy" and "Follow along" — see the note in
   index.html. Anchored RIGHT so the alternation down the run survives:
   TREASURES left, this right, Systems left, New World OS right.

   The two ranks are the composition. Each is a column with its own small
   label, and they sit side by side rather than stacked, because two stacked
   ranks would just be the two old screens welded together. */
#treasures-where.scene--brandfront { place-items: center end; }
#treasures-where .brandfront__panel {
  width: min(84vw, 1080px);
  justify-items: end;
  text-align: right;
}
#treasures-where .brandfront__copy {
  max-width: 52ch;
  margin-left: auto;
}

.where {
  display: grid;
  grid-template-columns: auto auto;
  justify-content: end;
  gap: clamp(28px, 6vw, 96px);
  margin-top: clamp(24px, 4vh, 52px);
  text-align: right;
}
.where__col {
  display: grid;
  gap: clamp(8px, 1.4vh, 16px);
  justify-items: end;
}
/* the rank labels take the SCENE LABEL role, in gold — they are the only
   thing on the screen saying which list is which */
.where__label {
  font-family: var(--mono);
  font-size: var(--label-size);
  font-weight: var(--label-weight);
  letter-spacing: var(--label-track);
  text-transform: uppercase;
  color: var(--signal);
}
#treasures-where .brandfront__market {
  display: grid;
  gap: clamp(6px, 1vh, 14px);
  justify-items: end;
  margin-top: 0;
}
#treasures-where .brandfront__market a {
  font-family: var(--display);
  font-variation-settings: "wdth" 70, "wght" 800;
  /* smaller than the 4.6vw the single-rank screens used, because there are two
     ranks in the frame now instead of one */
  font-size: clamp(24px, 3.4vw, 54px);
  line-height: 1.02;
}

@media (max-width: 720px) {
  /* two ranks will not sit side by side in a phone's measure without the
     names wrapping, so they stack — still one screen, still one question */
  .where { grid-template-columns: minmax(0, 1fr); gap: clamp(20px, 4vh, 36px); }
}

/* ---- SYSTEMS: the software half, and it takes the steel ----------------- */
#systems.scene--brandfront { place-items: center start; }
#systems .brandfront__panel { width: min(70vw, 980px); }
#systems .brandfront__eyebrow { color: var(--steel); }
#systems .brandfront__eyebrow::before { background: var(--steel); }

/* ---- NEW WORLD OS: the product, and the one real destination ------------ */
#new-world-os.scene--brandfront { place-items: center end; }
#new-world-os .brandfront__panel {
  width: min(72vw, 960px);
  justify-items: end;
  text-align: right;
}
#new-world-os .brandfront__eyebrow { color: var(--steel); }
#new-world-os .brandfront__eyebrow::before { background: var(--steel); }
#new-world-os .brandfront__market a {
  font-family: var(--display);
  font-variation-settings: "wdth" 68, "wght" 820;
  font-size: clamp(26px, 4vw, 62px);
}

/* ---- narrow: the frame is too small to argue about alignment ------------ */
@media (max-width: 860px) {
  .scene--studying { align-content: center; }
  #stack.scene--marks {
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
  }
  #stack .marks__label {
    writing-mode: horizontal-tb;
    transform: none;
    letter-spacing: 0.28em;
  }
  #stack .marks__label::before { display: inline-block; width: 22px; margin: 0 12px 0 0; }
  #markets .marks__grid { grid-template-columns: minmax(0, 1fr); }
  #treasures-intro.scene--brandfront,
  #treasures-where.scene--brandfront,
  #systems.scene--brandfront,
  #new-world-os.scene--brandfront { place-items: center; }
  #treasures-where .brandfront__panel,
  #new-world-os .brandfront__panel { justify-items: start; text-align: left; }
  #treasures-where .brandfront__market,
  #new-world-os .brandfront__market { justify-items: start; }
  #treasures-intro .brandfront__panel,
  #systems .brandfront__panel { width: min(92vw, 620px); }
}

/* ---- THE MARGIN THE ANCHORED SCENES SIT AGAINST ------------------------
   `--edge` is ~13px: correct for a full-bleed stage, far too tight to be the
   left margin of a wall of display type. Type set that close to the frame
   reads as having been cropped by it rather than placed against it — the same
   failure the hero wordmark had. These scenes get a real measure. */
.scene--studying,
#treasures-intro.scene--brandfront,
#systems.scene--brandfront {
  padding-left: clamp(20px, 5vw, 108px);
}
#treasures-where.scene--brandfront,
#new-world-os.scene--brandfront {
  padding-right: clamp(20px, 5vw, 108px);
}
#stack.scene--marks { padding-left: clamp(16px, 3vw, 54px); }

/* studying was sitting so low its closing line collided with the marks
   arriving underneath it */
.scene--studying { padding-bottom: clamp(90px, 20vh, 260px); }

@media (max-width: 860px) {
  .scene--studying,
  #treasures-intro.scene--brandfront,
  #systems.scene--brandfront,
  #treasures-where.scene--brandfront,
  #new-world-os.scene--brandfront,
  #stack.scene--marks {
    padding-left: var(--edge);
    padding-right: var(--edge);
  }
  .scene--studying { padding-bottom: clamp(40px, 10vh, 90px); }
}

/* ---- A PANEL AGREES WITH ITS OWN ANCHOR --------------------------------
   The sections above were anchored left or right, but the panel's contents
   stayed centred — so TREASURES sat hard against the left margin with its
   own sentence and links floating in the middle of the frame beneath it.
   Two alignments in one composition reads as an accident. */
#treasures-intro .brandfront__panel,
#systems .brandfront__panel {
  justify-items: start;
  text-align: left;
}
#treasures-intro .brandfront__eyebrow,
#systems .brandfront__eyebrow,
#treasures-intro .brandfront__copy,
#systems .brandfront__copy,
#treasures-intro .brandfront__links,
#systems .brandfront__links {
  text-align: left;
  margin-left: 0;
  margin-right: 0;
}

#treasures-where .brandfront__eyebrow,
#treasures-where .brandfront__copy,
#treasures-where .brandfront__links,
#treasures-where .brandfront__market,
#new-world-os .brandfront__eyebrow,
#new-world-os .brandfront__copy,
#new-world-os .brandfront__links,
#new-world-os .brandfront__market {
  text-align: right;
  margin-left: 0;
  margin-right: 0;
}

@media (max-width: 860px) {
  #treasures-where .brandfront__eyebrow,
  #treasures-where .brandfront__copy,
  #treasures-where .brandfront__links,
  #treasures-where .brandfront__market,
  #new-world-os .brandfront__eyebrow,
  #new-world-os .brandfront__copy,
  #new-world-os .brandfront__links,
  #new-world-os .brandfront__market { text-align: left; }
}

@media (max-width: 860px) {
  .connect-open { text-align: left; }
  .connect__links b { font-size: 14px; }
  .connect__links small { font-size: 12px; }
}

/* ==========================================================================
 *  CONNECT, REBUILT — the address is the composition
 *  --------------------------------------------------------------------------
 *  What was here was a four-column directory of numbered categories with two
 *  links under each. That is a SITEMAP FOOTER, and no amount of type scaling
 *  fixes it, because the shape was wrong: it structured a taxonomy when the
 *  screen has exactly one job — give someone a way to reach Patrick.
 *
 *  So the email is the object. It is set at display size in the gold that
 *  means "live" everywhere else on this site, and it is the only thing on the
 *  screen asking to be clicked. Four quiet channels sit under it, and the
 *  colophon closes the page. Company and product links are NOT repeated here;
 *  they have their own screens two sections up, and listing the whole site
 *  again is the footer this replaces.
 * ======================================================================== */
.connect {
  display: grid;
  justify-items: start;
  align-content: center;
  gap: clamp(20px, 3.4vh, 46px);
  width: min(1180px, 100%);
}

.connect__eyebrow {
  margin: 0;
  font-family: var(--mono);
  font-size: clamp(9px, 0.78vw, 12px);
  font-weight: 500;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.connect__eyebrow::before {
  content: "";
  display: inline-block;
  width: 26px;
  height: 1px;
  background: var(--signal);
  vertical-align: middle;
  margin-right: 14px;
}

/* THE ONE THING ON THIS SCREEN */
.connect__primary {
  display: grid;
  gap: clamp(8px, 1.2vh, 16px);
  text-decoration: none;
  color: var(--signal);
  max-width: 100%;
}
.connect__primary-lead {
  font-family: var(--mono);
  font-size: clamp(10px, 0.85vw, 14px);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-dim);
  transition: color var(--t-fast) linear;
}
.connect__primary-address {
  font-family: var(--display);
  font-variation-settings: "wdth" 66, "wght" 880;
  /* solved to the measure, never past it — the address is long, and type that
     touches the frame reads as cropped rather than as large */
  font-size: clamp(26px, 6.2vw, 104px);
  line-height: 0.92;
  letter-spacing: -0.012em;
  word-break: break-word;
  transition: color var(--t-fast) linear;
}
.connect__primary:hover .connect__primary-address,
.connect__primary:focus-visible .connect__primary-address { color: var(--ink); }
.connect__primary:hover .connect__primary-lead,
.connect__primary:focus-visible .connect__primary-lead { color: var(--signal); }

/* the quiet index underneath */
.connect__channels {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0 clamp(16px, 2.6vw, 48px);
  width: 100%;
  border-top: 1px solid var(--ink-line-faint);
}
.connect__channels a {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: 4px 10px;
  padding: clamp(14px, 1.8vh, 24px) 0;
  min-height: 48px;
  text-decoration: none;
  color: var(--ink-soft);
  transition: color var(--t-fast) linear;
}
.connect__channels b {
  grid-column: 1;
  font-family: var(--mono);
  font-size: clamp(11px, 0.95vw, 15px);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.connect__channels small {
  grid-column: 1 / -1;
  font-family: var(--mono);
  font-size: clamp(10px, 0.82vw, 13px);
  color: var(--ink-dim);
  letter-spacing: 0.04em;
}
.connect__channels i {
  grid-column: 2;
  grid-row: 1;
  font-style: normal;
  opacity: 0.55;
}
.connect__channels a:hover,
.connect__channels a:focus-visible { color: var(--signal); }
.connect__channels a:hover i,
.connect__channels a:focus-visible i { opacity: 1; }

.connect__foot {
  margin: 0;
  display: flex;
  gap: clamp(14px, 2.4vw, 40px);
  flex-wrap: wrap;
  font-family: var(--mono);
  font-size: clamp(8px, 0.7vw, 11px);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-dim);
  opacity: 0.75;
}

@media (max-width: 860px) {
  .connect__channels { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .connect__primary-address { font-size: clamp(24px, 8.4vw, 44px); }
}
@media (max-width: 480px) {
  .connect__channels { grid-template-columns: minmax(0, 1fr); }
  .connect__channels a { border-bottom: 1px solid var(--ink-line-faint); }
}

/* ---- THE PAIR STACKS ON A PHONE ---------------------------------------
   The storefronts rewrite was appended AFTER the narrow-screen override and
   carries the same specificity, so below 860px both matched and the later one
   won: the two ventures stayed side by side in a 375px frame at 138px each —
   narrower than the words in them. Cascade order is not a detail when two
   rules are equally specific; this one has to come last. */
@media (max-width: 860px) {
  .brandfront--no-mark.brandfront--pair {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(28px, 5vh, 56px);
  }
  .brandfront--no-mark.brandfront--pair .brandfront__panel {
    width: min(88vw, 460px);
  }
  /* the altitude gap is a desktop gesture; on a phone the second chapter
     simply follows the first */
  .brandfront--no-mark.brandfront--pair .brandfront__panel:nth-child(2) {
    margin-top: clamp(40px, 9vh, 90px);
  }
  .brandfront--pair .brandfront__name { font-size: clamp(32px, 11vw, 60px); }
}

/* The `#storefronts .brandfront__panel` width this used to have to out-specify
   is deleted, and with it the reason for this block. The class rule above now
   simply wins, because nothing outranks it any more. */
@media (max-width: 860px) {
  /* the two right-anchored panels were left at their desktop proportions
     (76vw / 72vw), which is not wrong at 375px but wastes measure a phone
     cannot spare */
  #treasures-where .brandfront__panel,
  #new-world-os .brandfront__panel { width: min(92vw, 620px); }
}

/* ==========================================================================
 *  THE TYPE SYSTEM, APPLIED
 *  --------------------------------------------------------------------------
 *  Every element that performs one of the roles declared in tokens.css now
 *  takes its size and tracking from that role rather than from whatever was
 *  typed when the section was written. Scale still varies between sections —
 *  that is composition — but a SCENE LABEL is the same object everywhere,
 *  because it is doing the same job everywhere.
 * ======================================================================== */

/* ---- scene labels ------------------------------------------------------ */
.marks__label,
.studying__label,
.brandfront__eyebrow,
.connect__eyebrow,
/* TWO THAT WERE PERFORMING THE ROLE WITHOUT TAKING IT.
   · `.about__eyebrow` was simply missing from this list and kept its own
     clamp, so the site's first section label measured 10px where every later
     one measured 11.
   · `.brandfront--step .brandfront__name` is the heading the destination pages
     turn DOWN into a label (see the inversion note above) — a deliberate and
     good decision that then set its own size and tracking, 12px/0.24em against
     the role's 11px/0.28em. Inverting the hierarchy is composition; picking a
     fifth label size to do it with is the drift this block exists to stop.
     It still needs the colour and the suppressed top margin, which stay with
     the inversion rule where they belong. */
.about__eyebrow,
.brandfront--step .brandfront__name {
  font-family: var(--mono);
  font-size: var(--label-size);
  font-weight: var(--label-weight);
  letter-spacing: var(--label-track);
  text-transform: uppercase;
}

/* the one that runs vertically keeps its own wider tracking, because rotated
   type needs more air to stay readable — a deliberate exception, not a drift */
#stack .marks__label { letter-spacing: 0.42em; }

/* ---- metadata ---------------------------------------------------------- */
.marks__name,
.connect__channels small,
.connect__foot span {
  font-family: var(--mono);
  font-size: var(--meta-size);
  letter-spacing: var(--meta-track);
}
.connect__foot span { letter-spacing: 0.2em; }

/* ---- actions ----------------------------------------------------------- */
.connect__channels b,
.brandfront__links a {
  font-family: var(--mono);
  font-size: var(--action-size);
  letter-spacing: var(--action-track);
  text-transform: uppercase;
}

/* ---- prose ------------------------------------------------------------- */
.about__body p,
.brandfront__copy,
.studying__note {
  font-family: var(--prose);
  font-size: var(--prose-size);
  line-height: var(--prose-lead);
}
