/* ==========================================================================
   ariamlabs.com : rough typographic build
   No frameworks, no build step, no external assets.
   ========================================================================== */

/* ---------- Tokens ------------------------------------------------------
   Match-the-product ruling (2026-08-13): values are ported verbatim from
   Obelus's shipped token sheet (Maestro_app apps/desktop/src/styles/
   tokens.css: neutral ramp, violet accent ramp, Inter stack, body
   gradient). Dark is the DEFAULT, exactly like the app; the app's light
   ramp rides prefers-color-scheme. Do not invent values here, port them. */
:root {
  --font-sans: 'Inter', ui-sans-serif, system-ui, -apple-system,
               BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-display: var(--font-sans);
  --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, "SF Mono",
               Menlo, Consolas, monospace;

  --ink:        #F4F5F8;   /* app --text-primary   (neutral-1000) */
  --ink-soft:   #AEB4C3;   /* app --text-muted     (neutral-800)  */
  --ink-mute:   #919FB6;   /* app --text-subtle    (neutral-700)  */
  /* v1.7.3 (founder 08-19): the ported navy neutrals read PURPLE next to
     the teal accent, and the teal-cast surfaces read green. The site's
     neutrals are now actually neutral (graphite); teal is seasoning only. */
  --paper:      #0A0B0D;
  --paper-warm: #14161A;
  --rule:       #2A2F38;
  --rule-soft:  #21252D;

  /* v1.7 teal pass (founder ruling 08-19): ported verbatim from the product's
     explore/obelus-green token sheet — the Obelus teal ramp replaces the
     violet one. Same seats, same names, new values. */
  --accent:          #70E8E4;  /* app --violet-300 seat (brand accent) */
  --accent-deep:     #3BC9C3;  /* app --violet-500 seat */
  --accent-bg:       #117A75;  /* app --accent-bg (violet-600 seat) */
  --accent-bg-hover: #3BC9C3;  /* app --accent-bg-hover (violet-500 seat) */
  --accent-tint: rgba(112, 232, 228, 0.07);
  --accent-edge: rgba(112, 232, 228, 0.22);
  --on-accent:   #FFFFFF;      /* app --text-on-accent */

  /* Graph canvas, ported: app --bg-canvas (neutral-100) + --graph-dot-color.
     The app paints its dots as an SVG pattern; the site paints the same
     ink as a repeating radial-gradient at the ~24px web spacing. */
  --canvas-bg: #0E0F12;
  --dot-ink:   rgba(255, 255, 255, 0.13);
  --dot-field:
    radial-gradient(var(--dot-ink) 1.3px, transparent 1.4px) 0 0 / 24px 24px,
    var(--canvas-bg);

  /* Node family colors, ported verbatim from the app's --family-* tokens.
     Used only as card edges, ports and wires in the S5 widget. The six
     destination families are ported verbatim too; they are what tints the
     app-icon tiles in S2 and S5 (see tokens.css:152/181/208/226/247/284).

     (v1.5 fix: this comment used to close after "widget." and then run on for
     three more lines of prose before closing a SECOND time. Everything after
     that first close was live CSS, and the parser ate the whole run as one
     invalid declaration, terminating at the first semicolon it found, which
     was the one at the end of --family-source. The token below was therefore
     never defined. It only escaped notice because .apptile's --tile fallback
     is always overridden by an .apptile-xx modifier in the markup.) */
  --family-source:      #9277FF;  /* app --family-source-asset */
  --family-ae:          #5C5CE0;  /* app --family-stage */
  --family-blender:     #E85D2C;  /* app --family-blender */
  --family-premiere:    #D07CE8;  /* app --family-premiere */
  --family-photoshop:   #31A8FF;  /* app --family-photoshop */
  --family-illustrator: #FF9A00;  /* app --family-illustrator */
  --family-audition:    #00E4BB;  /* app --family-audition */

  --radius-card: 14px;         /* app card radius */

  /* Card elevation, one pair of tokens so the light ramp can soften both */
  --card-lift:  0 12px 30px rgba(0, 0, 0, 0.30);
  --card-inset: inset 0 1px 0 rgba(255, 255, 255, 0.05);
  --node-shadow: 0 14px 26px rgba(0, 0, 0, 0.32);

  /* App body gradient, verbatim from explore/obelus-green (teal glow top-left,
     deep #003333-family wash at 80/10, teal-cast darks) */
  --body-gradient:
    radial-gradient(circle at top left, rgba(112, 232, 228, 0.07), transparent 26%),
    linear-gradient(180deg, #0A0C0D 0%, #0C0E10 45%, #08090B 100%);

  /* ---- RHYTHM SCALE (v1.4) -------------------------------------------
     Founder note M9: "the entire site feels cramped and doesn't have any
     polished spacing or rules I can tell." So: one ladder, and every
     vertical interval on the page is a rung on it. Nine geometric-ish
     steps in fixed rem. Nothing in this stylesheet may invent a vertical
     value any more; it picks a rung, or it picks one of the five
     composites below.

       1  0.50rem   8px    hairline gaps, chip gutters
       2  0.75rem  12px    inline clusters, note stacking
       3  1.00rem  16px    label -> its list, control padding
       4  1.50rem  24px    row padding, small block breaks
       5  2.00rem  32px    block breaks
       6  3.00rem  48px    panel padding, big breaks
       7  4.50rem  72px    section padding floor
       8  6.00rem  96px    text -> media ceiling
       9  9.00rem 144px    section padding ceiling                     */
  --space-1: 0.5rem;
  --space-2: 0.75rem;
  --space-3: 1rem;
  --space-4: 1.5rem;
  --space-5: 2rem;
  --space-6: 3rem;
  --space-7: 4.5rem;
  --space-8: 6rem;
  --space-9: 9rem;

  /* ---- COMPOSITES: the only responsive vertical values on the site ----
     Each one runs between two rungs of the ladder, so the whole page
     steps down together on a phone and up together on a desktop. Every
     section uses the SAME five, which is what makes the rhythm legible.

       pad-y    section top/bottom            72 -> 144
       eyebrow  eyebrow -> its heading        12 ->  24  (belongs to it)
       tight    heading -> its own lede       16 ->  32
       inner    heading -> body, label -> set 24 ->  48
       block    text block -> next block      32 ->  72
       media    text -> artwork / panel       48 ->  96                */
  --section-pad-y:       clamp(4.5rem, 10vw,  9rem);
  /* (phone floor raised below - v1.7.14, founder 08-19: sections read
     cramped at the 72px floor on mobile) */
  --section-gap-eyebrow: clamp(0.75rem, 1.7vw, 1.5rem);
  --section-gap-tight:   clamp(1rem, 2.4vw, 2rem);
  --section-gap-inner:   clamp(1.5rem, 3.4vw, 3rem);
  --section-gap-block:   clamp(2rem, 4.6vw, 4.5rem);
  --section-gap-media:   clamp(3rem, 6.7vw, 6rem);

  /* ---- MEASURES: three, consolidated. Nothing else caps a column. ---- */
  --measure-narrow: 34rem;   /* ~544px  forms, close column          */
  --measure:        42rem;   /* ~672px  the reading column           */
  --measure-wide:   52rem;   /* ~832px  headlines, fineprint         */
  --frame:          68rem;   /* ~1088px hero / outer frame           */

  --gutter: clamp(1.5rem, 5vw, 3rem);

  --ease: cubic-bezier(0.22, 0.61, 0.24, 1);

  color-scheme: dark;
}

@media (prefers-color-scheme: light) and (min-width: 99999px) /* DARK-ONLY: founder ruling 08-17 — light mode held until light product art exists */ {
  :root {
    --ink:        #0B0D12;   /* app light --text-primary */
    --ink-soft:   #1F2738;   /* app light neutral-900 */
    --ink-mute:   #4A5870;   /* app light neutral-700 */
    --paper:      #F2F4F9;   /* app light --bg-page base */
    --paper-warm: #DEE3ED;   /* app light neutral-150 */
    --rule:       #ACB5C8;   /* app light --border-default */
    --rule-soft:  #BFC7D7;   /* app light --border-subtle */

    --accent:          #7E64E0;  /* app light --violet-300 */
    --accent-deep:     #6A57D2;  /* app light --violet-500 */
    --accent-bg:       #5743BA;  /* app light --violet-600 */
    --accent-bg-hover: #6A57D2;
    --accent-tint: rgba(126, 100, 224, 0.06);
    --accent-edge: rgba(126, 100, 224, 0.22);
    --on-accent:   #FFFFFF;

    /* app light --bg-canvas + light --graph-dot-color (near-black dots) */
    --canvas-bg: #E5E9F1;
    --dot-ink:   rgba(11, 13, 18, 0.13);

    /* app light --family-source-asset; the other families are theme-identical */
    --family-source: #6A57D2;

    --card-lift:  0 8px 20px rgba(11, 13, 18, 0.10);
    --card-inset: inset 0 1px 0 rgba(255, 255, 255, 0.65);
    --node-shadow: 0 10px 20px rgba(11, 13, 18, 0.12);

    /* App light body gradient, verbatim */
    --body-gradient:
      radial-gradient(circle at top left, rgba(126, 100, 224, 0.04), transparent 30%),
      linear-gradient(180deg, #F2F4F9 0%, #ECEFF6 100%);

    color-scheme: light;
  }
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* clip, not hidden: hidden would make html a scroll container and break the sticky masthead */
  overflow-x: clip;
}

body {
  margin: 0;
  background: var(--body-gradient) var(--paper);
  background-attachment: fixed;
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 17px;
  line-height: 1.6;
  overflow-x: clip;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, p, figure, form { margin: 0; }
img, svg { max-width: 100%; display: block; }

a { color: inherit; }

/* the mark sprite is a definitions-only element; it must never take space */
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* App C4 ruling: text-selection = violet-300 @ 25% tint, both themes */
::selection { background: var(--accent-edge); }

a:focus-visible,
button:focus-visible,
input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  /* no border-radius here: it used to square off round controls on focus;
     the outline follows each element's own radius */
}

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

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 50;
  background: var(--accent-bg); color: var(--on-accent);
  padding: var(--space-2) var(--space-3); font-size: 0.9rem; text-decoration: none;
}
.skip:focus { left: var(--space-2); top: var(--space-2); border-radius: 6px; }

/* ---------- Layout ------------------------------------------------------ */
.wrap {
  width: 100%;
  max-width: var(--frame);
  margin: 0 auto;
  padding-inline: var(--gutter);
}

.measure      { max-width: var(--measure); }
.measure-wide { max-width: var(--measure-wide); }

.section {
  padding-block: var(--section-pad-y);
  /* anchor offset, not rhythm: it clears the sticky masthead */
  scroll-margin-top: var(--space-7);
}

/* v1.7.3 (founder 08-19): the between-section hairlines are CUT. */
.section-rule { border-top: 0; }

/* ---------- Masthead ---------------------------------------------------- */
.masthead {
  position: sticky;
  top: 0;
  z-index: 20;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  border-bottom: 1px solid transparent;
  backdrop-filter: saturate(1.4) blur(14px);
  -webkit-backdrop-filter: saturate(1.4) blur(14px);
}

@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  .masthead { background: var(--paper); }
}

.masthead-inner {
  max-width: var(--frame);
  margin: 0 auto;
  padding: var(--space-3) var(--gutter);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
}

.wordmark {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.05rem;
  letter-spacing: 0.01em;
  text-decoration: none;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
}

/* Founder's real Ariam Labs logo (08-18). The reset clamps svg to
   max-width:100%; explicit height + auto width keeps the mark crisp at
   masthead scale. Fill rides currentColor from .wordmark's ink. */
.wordmark-logo { height: 1.375rem; width: auto; display: block; }

.masthead-nav {
  display: flex;
  gap: clamp(var(--space-3), 3vw, var(--space-5));
  font-size: 0.83rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.masthead-nav a {
  color: var(--ink-mute);
  text-decoration: none;
  transition: color 0.25s var(--ease);
}
.masthead-nav a:hover { color: var(--ink); }

/* ---------- Type -------------------------------------------------------- */
.eyebrow {
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: var(--section-gap-eyebrow);
}

.display {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(2.5rem, 7.2vw, 4.75rem);
  line-height: 1.04;
  letter-spacing: -0.032em;
  max-width: 18ch;
}

.display .line { display: block; }
.display .line-quiet { color: var(--ink-mute); }

.h2 {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.95rem, 4.4vw, 3.05rem);
  line-height: 1.12;
  letter-spacing: -0.025em;
  max-width: 22ch;
  margin-bottom: var(--section-gap-inner);
}

.h2.measure-wide { max-width: var(--measure-wide); }
.h2-tight { margin-bottom: var(--section-gap-tight); }

.lead {
  font-size: clamp(1.14rem, 1.9vw, 1.36rem);
  line-height: 1.52;
  color: var(--ink-soft);
  margin-top: var(--section-gap-inner);
}

.body {
  font-size: clamp(1.05rem, 1.35vw, 1.14rem);
  line-height: 1.72;
  color: var(--ink-soft);
}

/* UNUSED since v1.5: the founder cut the S1 microline. Kept so restoring it
   is one line of markup, not a stylesheet dig. */
.microline {
  margin-top: var(--section-gap-inner);
  font-size: 0.88rem;
  color: var(--ink-mute);
  letter-spacing: 0.005em;
}

/* ---------- BEATS: the wall-of-text cure (v1.5) -------------------------
   Founder note, three sections deep: S3, the S4 centre column and the S6
   privacy argument all read as one grey slab. The cure is typographic, not
   editorial. Every one of those paragraphs is now a small stack of BEATS,
   split at sentence seams the copy already had, with one rung of the rhythm
   ladder between them, and the line the section is actually for is pulled up
   into .beat-key. No paragraph lost, gained or reordered a word.

   The gap is --section-gap-tight on purpose: it is the rung the site already
   uses for "a heading and its own lede", i.e. parts of ONE thought. A block
   break (--section-gap-block) would read as four separate paragraphs, which
   is the wall again with more air in it. */
.beats > * + * { margin-top: var(--section-gap-tight); }

.beat-key {
  color: var(--ink);
  font-size: clamp(1.16rem, 1.55vw, 1.32rem);
  line-height: 1.55;
  letter-spacing: -0.008em;
  text-wrap: pretty;
}

.kicker {
  margin-top: var(--section-gap-block);
  /* v1.7.2 (founder 08-19): the faded left rule is CUT — the line read as
     a stray mark next to the teal text. The voice carries it alone now. */
  font-size: clamp(1.02rem, 1.3vw, 1.1rem);
  line-height: 1.68;
  color: var(--ink);
}

.filemark {
  font-family: var(--font-mono);
  font-size: 0.88em;
  letter-spacing: -0.01em;
  color: var(--ink-mute);
  word-break: break-word;
}

/* ---------- Buttons ----------------------------------------------------- */
.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--section-gap-block);
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  padding: var(--space-3) var(--space-4);
  border-radius: 9px;
  border: 1px solid transparent;
  background: transparent;
  font: inherit;
  font-size: 0.98rem;
  font-weight: 500;
  letter-spacing: 0.002em;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.25s var(--ease), border-color 0.25s var(--ease),
              color 0.25s var(--ease), transform 0.25s var(--ease);
}

.btn-primary {
  background: var(--accent-bg);
  color: var(--on-accent);
}
/* v1.7.3 (founder 08-19): the asymmetric pad read as a shove, not
   balance - reverted to even padding; the icon sits in the normal flow. */
.btn-primary:hover { background: var(--accent-bg-hover); transform: translateY(-1px); }

.btn-ghost {
  border-color: var(--rule);
  color: var(--ink);
}
.btn-ghost:hover { border-color: var(--ink-mute); transform: translateY(-1px); }

.btn-glyph { font-size: 0.95em; opacity: 0.75; }

/* v1.7 — the Obelus app tile inside the hero CTA (founder 08-19: the icon,
   big enough to actually be visible, replaces the down arrow). The SVG
   carries its own rounded tile + radial ground, so the button just sizes
   and seats it. */
.btn-appicon {
  display: block;
  width: 24px;
  height: 24px;
  flex: 0 0 auto;
  border-radius: 5px;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.14);
}

/* ---------- S3 prompt box ------------------------------------------------
   v1.6 / founder note: the headline IS a prompt, typed on first scroll into
   view. The box below is the field chrome; the typing mechanics (real text
   kept in the DOM, an aria-hidden ghost layer overlaid on top so the box
   cannot reflow when typing finishes) live on .promptbox-line / -text /
   -ghost / -caret, driven by the script at the bottom of index.html.
   The heading's own rhythm gap moves onto the box (.promptbox-line zeroes
   it) so the field owns the space to whatever follows, not the text inside
   it -- the same move as every other bordered panel on the page. */
.promptbox {
  position: relative;
  margin-bottom: var(--section-gap-inner);
  /* v1.7.8 (founder 08-19): the fixed space-6 pad read heavy-handed on
     phones - runs 24px -> 48px with the viewport now. */
  padding: clamp(1.5rem, 4.5vw, 3rem);
  border: 1px solid var(--rule);
  border-radius: var(--radius-card);
  background: var(--paper-warm);
  box-shadow: var(--card-inset);
}

.promptbox-line {
  position: relative;
  margin-bottom: 0;
}

/* The real text is the only thing rendered until the script decides to
   type: no JS, reduced motion, or a crawler all see this and only this.
   opacity, not visibility: data-typing is set the instant the script runs
   (page load, ahead of the scroll trigger) and is never cleared, so a
   visibility:hidden rule here would pull the ONLY accessible copy of the
   headline out of the accessibility tree for the rest of its life -- the
   aria-hidden ghost that replaces it visually is explicitly invisible to
   assistive tech. opacity:0 hides it from sighted users exactly the same
   way (same box, same layout, zero shift) without hiding it from anyone
   else: a screen reader gets the real sentence immediately, every time. */
.promptbox[data-typing] .promptbox-text { opacity: 0; }

/* The ghost fills the exact same box (its parent .promptbox-line is the
   containing block), so the field's height is the real text's height at
   every frame of the animation, not the ghost's -- zero shift, by
   construction rather than by measurement. */
.promptbox-ghost {
  position: absolute;
  inset: 0;
}

.promptbox-caret {
  display: inline-block;
  width: 0.08em;
  height: 0.9em;
  margin-left: 0.04em;
  background: var(--accent);
  vertical-align: -0.08em;
  animation: promptbox-blink 1s steps(1) infinite;
}

@keyframes promptbox-blink { 50% { opacity: 0; } }

/* ---------- S3 turn ----------------------------------------------------- */
/* Punch-list item 3: the turn used to be centred under flush-left copy. It
   now shares the reading column's left edge, rule and all. */
/* one rung down from --section-gap-media: the beats above now end on the
   .beat-key, which already supplies emphasis, so the old media-sized hole
   read as a missing element rather than a pause. */
.turn { margin-top: var(--section-gap-block); }

.turn-rule {
  display: block;
  width: 1px;
  height: var(--space-5);
  margin: 0 0 var(--section-gap-tight);
  background: linear-gradient(to bottom, transparent, var(--accent-edge));
}

/* v1.5 / founder note: the turn was set at the same weight as the title it
   answers, so the two competed. It is now clearly SMALLER than the paired
   .h2 (about 62% of it at every width, checked at both ends of the clamp)
   and ITALIC, which is what makes it read as an aside in his voice rather
   than a second headline. It keeps the accent ink and the column edge. */
.turn-line {
  font-family: var(--font-display);
  font-size: clamp(1.32rem, 2.75vw, 1.9rem);
  font-style: italic;
  font-weight: 500;
  line-height: 1.24;
  letter-spacing: -0.012em;
  color: var(--accent);
  /* 26ch was measured for the old 3.1rem setting; at the new size it broke
     the line after "us", which is the worst possible place for an aside */
  max-width: 38ch;
  margin: 0;
}

/* ---------- S9 close form ------------------------------------------------ */
.beta-form {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--section-gap-block);
  max-width: var(--measure-narrow);
}

.beta-input {
  flex: 1 1 15rem;
  min-width: 0;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--rule);
  border-radius: 9px;
  background: var(--paper);
  color: var(--ink);
  font: inherit;
  font-size: 0.98rem;
}

.beta-input::placeholder { color: var(--ink-mute); }
.beta-input:focus { border-color: var(--accent); }

.beta-form .btn { flex: 0 0 auto; }

.form-note {
  margin-top: var(--space-3);
  font-size: 0.86rem;
  color: var(--ink-mute);
}

.form-note a {
  color: var(--ink-soft);
  text-decoration-color: var(--rule);
  text-underline-offset: 3px;
}
.form-note a:hover { color: var(--accent); }

/* Compose note: sits directly under the form, quieter than the privacy note. */
.form-note-tight { margin-top: var(--space-3); font-size: 0.81rem; }
.form-note-tight + .form-note { margin-top: var(--space-1); }

/* ---------- Footer ------------------------------------------------------ */
.site-footer {
  padding-block: var(--section-gap-media);
  font-size: 0.86rem;
  line-height: 1.9;
  color: var(--ink-mute);
}

.site-footer a { color: var(--ink-mute); text-underline-offset: 3px; }
.site-footer a:hover { color: var(--accent); }

/* ---------- Legal page -------------------------------------------------- */
.legal { padding-top: var(--section-pad-y); }

.legal .h2 { margin-bottom: var(--section-gap-inner); }

.legal-body p {
  font-size: clamp(1.02rem, 1.3vw, 1.1rem);
  line-height: 1.75;
  color: var(--ink-soft);
  margin-bottom: var(--space-4);
}

.legal-body a { color: var(--ink); text-underline-offset: 3px; }
.legal-body a:hover { color: var(--accent); }

.legal-back {
  display: inline-block;
  margin-top: var(--space-4);
  font-size: 0.9rem;
  color: var(--ink-mute);
  text-underline-offset: 3px;
}
.legal-back:hover { color: var(--accent); }

/* ---------- Reveal: REMOVED (rough site = statically visible, always) --- */

ul { margin: 0; padding: 0; }
[hidden] { display: none !important; }

/* ---------- Dark bands --------------------------------------------------
   S1, S4, S5 and S9 carry the founder's renders. That artwork is dark-theme
   app art with its own baked glow, so under the light ramp these sections
   become full-bleed dark BANDS rather than dark plates floating on a light
   page (the baked glow reads as grime on a light surface, and the node
   cards are opaque dark either way). The subtree re-declares the app's dark
   ink so every token inside it, scrims and chips and ports and the waitlist
   field, resolves against the artwork instead of against the page. */

/* Re-anchor the inherited colour to the BAND's ink, not the page's. Without
   this the headings, which carry no colour of their own, keep the value
   `body` already computed from the page ramp and go near-invisible on the
   plate under the light scheme. Harmless under the dark one. */
.band-dark { color: var(--ink); }

@media (prefers-color-scheme: light) and (min-width: 99999px) /* DARK-ONLY: founder ruling 08-17 — light mode held until light product art exists */ {
  .band-dark {
    --ink:        #F4F5F8;
    --ink-soft:   #AEB4C3;
    --ink-mute:   #919FB6;
    --paper:      #0B0D12;
    --paper-warm: #131722;
    --rule:       #2E3650;
    --rule-soft:  #242B3F;

    --accent:          #70E8E4;
    --accent-deep:     #3BC9C3;
    --accent-bg:       #117A75;
    --accent-bg-hover: #3BC9C3;
    --accent-tint: rgba(112, 232, 228, 0.07);
    --accent-edge: rgba(112, 232, 228, 0.22);

    --canvas-bg: #0F1115;
    --dot-ink:   rgba(255, 255, 255, 0.13);
    /* re-stated rather than inherited: a custom property that carries var()
       is substituted where it is DECLARED, so :root's copy has already
       baked in the light dot ink by the time it reaches here */
    --dot-field:
      radial-gradient(rgba(255, 255, 255, 0.13) 1.3px, transparent 1.4px) 0 0 / 24px 24px,
      #0F1115;

    --family-source: #9277FF;

    --card-lift:  0 12px 30px rgba(0, 0, 0, 0.30);
    --card-inset: inset 0 1px 0 rgba(255, 255, 255, 0.05);
    --node-shadow: 0 14px 26px rgba(0, 0, 0, 0.32);

    color-scheme: dark;
  }
}

/* ==========================================================================
   S1 : the hero, full bleed
   --------------------------------------------------------------------------
   Punch-list item 1. The founder's plate is 3200x1800 and weighted RIGHT:
   the app window from about 35% across, empty canvas to the left of it. Left
   alone, that bright video preview lands at 53vw, right where the reading
   column ends. So the plate is over-scaled 1.2 and pushed right 7%, which
   parks the preview past 60vw and leaves the type a near-black field. The
   over-scale is what keeps the left edge covered: the translate alone would
   open a bare strip. Measured contrast on the shipped scrim is in the
   verification notes, not guessed.
   ========================================================================== */
.hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  align-items: center;
  min-height: clamp(33rem, 82vh, 52rem);
  padding-block: var(--section-pad-y);

  /* the reading column has to end before the plate’s preview begins */
  --hero-measure: 37rem;
}

.hero-media {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--canvas-bg);
  overflow: hidden;
}

.hero-media picture { display: block; width: 100%; height: 100%; }

.hero-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 50%;
  transform: translateX(7%) scale(1.2);
}

.hero-scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(90deg,
      color-mix(in srgb, var(--paper) 94%, transparent) 0%,
      color-mix(in srgb, var(--paper) 92%, transparent) 34%,
      color-mix(in srgb, var(--paper) 84%, transparent) 50%,
      color-mix(in srgb, var(--paper) 62%, transparent) 60%,
      color-mix(in srgb, var(--paper) 22%, transparent) 70%,
      transparent 80%),
    linear-gradient(180deg,
      color-mix(in srgb, var(--paper) 58%, transparent) 0%,
      transparent 22%,
      transparent 72%,
      color-mix(in srgb, var(--paper) 68%, transparent) 100%);
}

@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  .hero-scrim { background: linear-gradient(90deg, var(--paper) 0%, var(--paper) 40%, transparent 88%); }
}

/* Asset not on disk yet: fall back to a tokens-only canvas field. */
.hero-media[data-fallback] {
  background:
    radial-gradient(120% 100% at 86% 46%, var(--accent-tint), transparent 60%),
    var(--dot-field);
}
.hero-media[data-fallback] img { display: none; }
.hero-media[data-fallback] .hero-scrim { opacity: 0.5; }

.hero-inner { position: relative; }

/* The reading column has to end before the plate's preview begins. 37rem
   lands its right edge at about 57vw at 1440 while the preview now starts at
   62vw; measured, that is the difference between the headline reading 1.6:1
   over neon and reading 10:1 over canvas. The display drops a step to match:
   at full size it would need a wider column than the plate can spare. The cap
   is repeated on .display because the class already carries a max-width of
   its own and would otherwise win on specificity. */
.hero-inner > *,
.hero-inner .measure { max-width: min(100%, var(--hero-measure)); }

.hero .display {
  font-size: clamp(2.35rem, 4.8vw, 3.4rem);
  max-width: min(100%, var(--hero-measure));
}

/* ==========================================================================
   S2 : the product, a clean band
   --------------------------------------------------------------------------
   The artwork moved up to S1, so this is type on the page surface with the
   feature box carrying the weight. v1.6 retires the single shared panel with
   an internal divider (.features/.chips, dead now the markup is two wired
   boxes) in favour of the node-wire grammar below: two independent panels
   plus a wire between them, same shape as the product's own graph.
   ========================================================================== */
.features-label { margin-bottom: var(--space-3); color: var(--ink-mute); }

/* ==========================================================================
   THE FEATURE BOX  (v1.6, founder sketch)
   --------------------------------------------------------------------------
   Two panels + a wire, the product's own grammar. Stacked by default (DOM
   order already reads top-to-bottom correctly); a 3-column grid from 52rem
   up, the same stop the old .features split used. --fwire-w is the wire
   channel's own width, not a rhythm rung: it is sized so the bezier's fixed
   34-unit control offset (see the svg path) lands at ~60px, the product's
   own horizontal control-stub length, the same way --hero-measure is a
   component measurement rather than a vertical interval.
   ========================================================================== */
.fbox {
  --fwire-w: clamp(6rem, 9vw, 11rem);
  margin-top: var(--section-gap-media);
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-6);
}

@media (min-width: 52rem) {
  .fbox {
    grid-template-columns: minmax(0, 1.4fr) var(--fwire-w) minmax(0, 1fr);
    align-items: stretch;
    gap: 0;
  }
}

.fpane {
  min-width: 0;
  padding: var(--section-gap-inner);
  border: 1px solid var(--rule);
  border-radius: var(--radius-card);
  background: var(--paper-warm);
  box-shadow: var(--card-inset);
}

.ftabs-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}

/* The input is sr-only; the face is the whole visible control. Native radio
   group gives roving focus and :checked for free, so the state below is the
   only JS this widget will ever need. */
.ftab { display: block; cursor: pointer; }

.ftab-face {
  display: inline-flex;
  align-items: center;
  padding: 0.5rem 0.9rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  font-size: 0.86rem;
  line-height: 1.25;
  color: var(--ink-mute);
  white-space: nowrap;
  transition: background-color 0.2s var(--ease), border-color 0.2s var(--ease),
              color 0.2s var(--ease);
}

.ftab:hover .ftab-face { border-color: var(--ink-mute); color: var(--ink); }

.ftab input:checked ~ .ftab-face {
  border-color: var(--accent-edge);
  background: var(--accent-tint);
  color: var(--ink);
}

/* Keyboard focus moves to the (invisible) input; forward the ring to the
   face it labels instead of drawing it around a 1px sr-only box. */
.ftab input:focus-visible { outline: none; }
.ftab input:focus-visible ~ .ftab-face { outline: 2px solid var(--accent); outline-offset: 2px; }

.fstage { margin-top: var(--section-gap-inner); }

/* One :has() rule per feature, each panel hidden until its own radio is
   checked. #ft-image starts checked in the markup, so this is also the
   correct state with no CSS at all (progressive, not a JS dependency). */
.fpanel { display: none; }
.fpane-features:has(#ft-image:checked) .fpanel[data-feature="image"] { display: block; }
.fpane-features:has(#ft-video:checked) .fpanel[data-feature="video"] { display: block; }
.fpane-features:has(#ft-audio:checked) .fpanel[data-feature="audio"] { display: block; }
.fpane-features:has(#ft-3d:checked)    .fpanel[data-feature="3d"]    { display: block; }
.fpane-features:has(#ft-seg:checked)   .fpanel[data-feature="seg"]  { display: block; }

.fmedia {
  width: 100%;
  aspect-ratio: 5 / 2;
  object-fit: cover;
  border: 1px solid var(--rule-soft);
  border-radius: 10px;
  background: var(--canvas-bg);
}

.frun {
  margin-top: var(--space-3);
  font-size: 0.94rem;
  line-height: 1.55;
  color: var(--ink-soft);
}

/* THE WIRE. Decorative only (aria-hidden, no pointer-events). Geometry and
   colour are measured off site-assets/roster/port-dot-*.png, not invented:
   every idle port dot in that set is the same two-ring construction, a
   filled disc from centre to 68% of the crop's dot radius, then a ring out
   to the edge in the app's dark surface colour. The two colours it measures
   to are, not coincidentally, tokens we already have --
     port-dot-output-source-asset-violet.png: fill rgb(146,119,255) = --accent,
       ring rgb(19,23,34) = --paper-warm, both exact.
   --portdot-in is the one new value: rgb(173,162,151), read off
   port-dot-input-required-output-silver.png, the neutral "goes to an
   output" dot rather than one specific app family, which is the right read
   for a port that fans out to six different destinations. */
.fwire-col {
  --portdot-in: #ada297;
  position: relative;
  width: 100%;
  height: var(--space-6);
}

/* width/height 100% is load-bearing, not redundant with inset:0: the <svg>
   tags carry no width/height attribute, so with only inset:0 a replaced
   element falls back to its intrinsic ratio (the square 100x100 viewBox)
   instead of stretching to fill a non-square box, which is how the
   vertical wire ended up rendering at full width x full width tall and
   overrunning the 48px channel into the panel below it. */
.fwire { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.fwire-h { display: none; }

.fport {
  --port-fill: var(--accent);
  position: absolute;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle, var(--port-fill) 0 68%, var(--paper-warm) 68% 100%);
}

.fport-in { --port-fill: var(--portdot-in); }

/* Stacked / vertical wire: matches the .fwire-v path (M 22 0 ... 74 100). */
.fport-out { top: 0; left: 22%; }
.fport-in  { top: 100%; left: 74%; }

.fw-from { stop-color: var(--accent); stop-opacity: 0.55; }
.fw-to   { stop-color: var(--portdot-in); stop-opacity: 0.55; }

@media (min-width: 52rem) {
  .fwire-col { height: auto; }
  .fwire-h { display: block; }
  .fwire-v { display: none; }

  /* Horizontal wire: matches the .fwire-h path (M 0 52 ... 100 22). */
  .fport-out { top: 52%; left: 0; }
  .fport-in  { top: 22%; left: 100%; }
}

/* ==========================================================================
   APP-ICON TILES  (founder note M2)
   --------------------------------------------------------------------------
   His words: the destinations should get "the treatment of the actual app
   icons themselves". So they are not bare glyphs in pills any more, they
   are tiles, and the tile is modelled on the product's OWN stage badge,
   the "Ae" chip in the header of node-stage-after-effects.png: a rounded
   square, the family colour laid over the card surface as a tint, a
   family-tinted edge, the mark set in family ink.

   Every tile is the same 28px square at every width, in both the S2
   connects-to strip and the S5 tab row, so the family is legible as a
   family. The colours are the app's own --family-* tokens, ported.

   This retires the five Adobe glyph paths entirely (and the licensing
   question that came with them). Blender's mark stays: Simple Icons
   ships it under CC0, so it is ours to use, and it sits INSIDE the same
   tile so the row stays uniform.
   ========================================================================== */
.apptile {
  --tile: var(--family-source);
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  border: 1px solid color-mix(in srgb, var(--tile) 42%, transparent);
  background: color-mix(in srgb, var(--tile) 15%, var(--paper-warm));
  color: color-mix(in srgb, var(--tile) 72%, var(--ink));
  font-family: var(--font-display);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1;
  -webkit-user-select: none;
  user-select: none;
}

@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  .apptile { border-color: var(--rule); background: var(--paper-warm); color: var(--ink); }
}

.apptile-ae { --tile: var(--family-ae); }
.apptile-pr { --tile: var(--family-premiere); }
.apptile-bl { --tile: var(--family-blender); }
.apptile-ps { --tile: var(--family-photoshop); }
.apptile-ai { --tile: var(--family-illustrator); }
.apptile-au { --tile: var(--family-audition); }

/* the one glyph that survives, sized to sit on the lettermarks' cap line */
.apptile-glyph { width: 17px; height: 17px; fill: currentColor; }

/* The connects-to strip is a plain wrapping row of tile + name. No pill:
   the founder's shot showed the pill outline fighting the tile inside it,
   and the tile is already the shape. */
.chips-apps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: var(--space-2) var(--space-4);
}

/* ==========================================================================
   v1.7.2 S2 FEATURE KIT (founder round 2, 08-19)
   --------------------------------------------------------------------------
   The wire connector + scroll reveal are CUT (ruled: not working, worst on
   phones). Two quiet panes on a teal-cast surface (the navy panes read
   purple on the green body). Tiles run smaller (36px); live features wear
   the accent, the two Composer features sit gray with an asterisk resolved
   by ONE footnote instead of per-tile pills. Column counts are EXPLICIT
   per width - auto-fit produced wacky pane heights at odd window sizes.
   Connections wear the apps' REAL icon palettes (founder: marketing site,
   not concerned) - solid tiles, brand letters, no house border.
   ========================================================================== */
.s2-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-5);
}
.s2-appicon {
  flex: 0 0 auto;
  order: 2;
  width: clamp(72px, 9vw, 112px);
  height: auto;
  border-radius: 22%;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.10), 0 18px 40px rgba(0, 0, 0, 0.35);
}

.fkit {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
  align-items: start;
  margin-top: var(--section-gap-block);
}
.fkit-pane {
  position: relative;
  padding: var(--space-4);
  border: 1px solid var(--rule-soft);
  border-radius: var(--radius-card);
  background: rgba(20, 22, 26, 0.75);
  box-shadow: var(--card-inset);
}
.fkit-pane .features-label { margin-bottom: var(--space-3); }

.fgrid,
.agrid {
  display: grid;
  gap: var(--space-3) var(--space-2);
  list-style: none;
  margin: 0;
  padding: 0;
}
/* v1.7.7 (founder 08-19): features run 3-up on phones too, matching the
   connections' rhythm - 3 rows instead of a 4-row tower. */
.fgrid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.agrid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (min-width: 52rem) {
  .fkit { grid-template-columns: minmax(0, 4fr) minmax(0, 3fr); align-items: stretch; }
  .fgrid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .agrid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.ftile {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
}
.ftile-name {
  font-size: 0.84rem;
  line-height: 1.25;
  letter-spacing: -0.005em;
  color: var(--ink-soft);
}
.ftile-star { color: var(--ink-mute); margin-left: 2px; }
.ftile-icon {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: 9px;
  border: 1px solid var(--rule-soft);
  background: rgba(20, 22, 26, 0.6);
  color: var(--accent);
}
.ftile-icon svg { width: 18px; height: 18px; }
.ftile-soon .ftile-icon { color: var(--ink-mute); opacity: 0.8; }
.fkit-foot { margin-top: var(--space-3); font-size: 0.78rem; color: var(--ink-mute); }

/* v1.7.3 (founder 08-19): the ACTUAL app icons (Wikimedia Commons official
   assets), not recreations. Blender's mark sits on its own dark tile so the
   row stays uniform. */
.appicon { display: block; width: 36px; height: 36px; border-radius: 8px; }
.appicon-tilewrap {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: 8px;
  background: #1B1D22;
}
.appicon-tilewrap img { display: block; width: 24px; height: 24px; }

.appchip {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
  font-size: 0.9rem;
  line-height: 1.25;
  color: var(--ink-soft);
  white-space: nowrap;
}

/* ==========================================================================
   S4 : the drifting field, v3  (v1.5 founder pass)
   --------------------------------------------------------------------------
   Four lanes, two on each side, alternating directions, two card sizes, and
   the size belongs to the LANE and not the card. What changed tonight:

   (a) HEIGHT: 140vh -> 98vh. That is the requested ~30% off, taken off the
       band and not off the type: --section-pad-y is untouched, so the copy
       keeps the same air it has in every other section and the field simply
       stops being taller than a screen and a half.

   (b) THE SCROLL-STOP BUG, root cause. It was this rule:

             .drift:hover .drift-col { animation-play-state: paused; }

       .drift is an absolutely positioned box covering the WHOLE section, and
       .drift-scrim above it is pointer-events:none, so any pointer sitting
       anywhere over the band except directly on the copy fell through to
       .drift and hovered it. On a trackpad the cursor is parked over the
       section for the entire time you are scrolling through it, which is
       exactly and only when you are looking at it. So the field reliably
       froze while being read and started again once the pointer left, which
       is the "animation halts sometimes" he saw. Verified by measuring the
       lane transform over two full loops with the pointer off the section
       (advances) and with it resting on the band (frozen).

       The pause is gone outright, and .drift is now pointer-events:none for
       the whole subtree, so decoration cannot take a pointer event again.
       (The loop itself was never the problem: each lane holds its stack
       twice and translateY(-50%) is exactly one stack, so the wrap is
       seamless. It survives that check unchanged.)

   (c) OVERLAP ON RESIZE, root cause. Lane x-positions were authored as four
       hand-picked percentages while lane WIDTHS came from clamp()s in rem.
       Percentages track the viewport, rem widths stop tracking it at both
       ends of the clamp, so between about 62rem and 80rem the two systems
       drifted apart and the lanes drove into each other. There is now ONE
       layout: .drift-rail is a flex row of lane, lane, centre gap, lane,
       lane. Every x-position is derived from that row, so lanes cannot
       collide at any width. When the window narrows the lanes shrink (they
       are flex: 0 1 auto with min-width:0) and the centre gap holds its
       floor, so the copy is the last thing to give ground rather than the
       first.

   (d) Z-DEPTH. Outer lanes read NEAR: wider, brighter, sharp, heavier
       shadow. Inner lanes read FAR: narrower, dimmer, and softly blurred.
       The blur is set on the IMG, not on .drift-track, deliberately: a
       filter on the animated element (or on an ancestor of it) can pull the
       transform off the compositor and put a 3000px-tall layer on the main
       thread, which is how a "depth" tweak turns into a second stutter bug.
       On the images it rasterises once and never again.

   (e) TILT, v1.6 REBUILD: real 3D, not a 2D rotate. The founder's note was
       explicit -- he wanted Y-axis rotation, lanes pivoting on their own
       vertical axis and curving toward the centre like a corridor, not the
       flat rotate() that drew a V out of four cards sharing one plane. So
       .drift-rail now carries ONE perspective (a single vanishing point for
       all four lanes) and .drift-col carries rotateY instead of rotate.
       Left lanes and right lanes get opposite signs (mirrored, so both
       "face" the centre rather than both leaning the same way), and the
       OUTER lane on each side (1, 4) turns a little further than the INNER
       lane (2, 3): --lane-tilt-outer / --lane-tilt-inner, both lanes on a
       side sharing the sign, differing only in magnitude.

       Depth math replaces the old in-plane displacement math: a rotateY'd
       card foreshortens (its rendered width shrinks toward its far edge)
       rather than shearing sideways in the page's own plane, so the old
       "opposite angles converge and overlap" failure mode from the 2D rotate
       does not have a direct analogue here. It is still proven, not assumed:
       the verification pass reads each lane's live getBoundingClientRect()
       (Chromium's own post-projection, axis-aligned bounding box of the
       rotated quad -- a superset of the true rotated shape, so a clean gap
       between bounding boxes is a stronger claim than a clean gap between
       the actual quads) across 375px-2560px and confirms no two lanes, and
       no lane and the protected .drift-gap, ever intersect.
   ========================================================================== */
.section-drift {
  position: relative;
  overflow: hidden;
  background: var(--dot-field);
  /* the dot tile anchors 0 0 inside --dot-field; recentre it so the pattern
     is symmetric about the browser frame (founder: "aligned in a strange
     way that is not perfectly centered") */
  background-position: 50% 0;
  min-height: 98vh;
  display: flex;
  align-items: center;
  padding-block: var(--section-pad-y);

  /* near = outer = closer to the viewer. far = inner = further back. */
  --lane-near: clamp(20rem, 25vw, 25rem);
  --lane-far:  clamp(12rem, 14.5vw, 15rem);

  /* v1.6: real 3D, one shared vanishing point. Outer lanes (1, 4) turn a
     little further than inner lanes (2, 3), which is what keeps the field
     reading as a corridor curving away rather than four flat cards nudged
     by the same amount. */
  --drift-perspective: 2600px;
  --lane-tilt-outer: 15deg;
  --lane-tilt-inner: 11deg;
}

.drift {
  position: absolute;
  inset: 0;
  overflow: hidden;
  /* decoration takes no pointer events, ever: see (b) above */
  pointer-events: none;
}

/* The rail runs a little wider than the section on both sides so the outer
   lanes bleed off the edges instead of ending on them. */
.drift-rail {
  position: absolute;
  inset: 0 -5%;
  display: flex;
  align-items: stretch;
  gap: clamp(0.75rem, 1.6vw, 1.75rem);
  /* one perspective on the shared parent = one vanishing point for all four
     lanes, which is what makes them read as sides of the same corridor
     instead of four independently-tilted cards. */
  perspective: var(--drift-perspective);
}

/* The LANE is a layout slot and nothing else: it holds a width, an angle and
   a depth. It never scrolls. rotateY, not the old 2D rotate: the lane turns
   on its own vertical axis instead of spinning flat in the page's plane, so
   what the viewer gets is actual foreshortened depth (a wall of cards
   receding toward the centre) rather than the flat skew that drew a V. */
.drift-col {
  position: relative;
  flex: 0 1 auto;
  min-width: 0;
  transform: rotateY(var(--tilt, 0deg));
  transform-origin: 50% 50%;
}

/* The TRACK is the thing that moves. It is taller than the lane and hangs
   out of both ends, which is why the lane must not clip it. */
.drift-track {
  position: absolute;
  top: var(--seed, 0%);
  left: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  animation: drift-up var(--dur, 88s) linear infinite;
  will-change: transform;
}

.drift-track[data-dir="down"] { animation-name: drift-down; }

.drift-stack { display: flex; flex-direction: column; }

/* The protected middle. No art, just the width the reading column needs.
   Its min-width is the floor the lanes have to respect. */
.drift-gap {
  flex: 1 1 auto;
  min-width: 34%;
}

/* Two coherent steps, and the step is the lane. */
.drift-near { flex-basis: var(--lane-near); opacity: 0.95; }
.drift-far  { flex-basis: var(--lane-far);  opacity: 0.48; }

/* Depth, part two: the far lanes go soft. On the IMG, never on the track. */
.drift-far .drift-card img { filter: blur(1.1px); }
.drift-far .drift-card { filter: drop-shadow(0 10px 20px rgba(0, 0, 0, 0.42)); }

/* Mirrored about the centre: both LEFT lanes lean one way, both RIGHT lanes
   lean the other, so same-side neighbours stay parallel and their gap is
   constant at every height (see (e) above). --seed is only a starting offset
   so the four lanes never line up. */
/* v1.6.1 / founder note: signs FLIPPED — he wants the lanes pointing toward
   the text the OTHER way (inner edges receding into the page, outer edges
   toward the viewer), not the first reading. */
.drift-lane-1 { --tilt: var(--lane-tilt-outer);            --seed: -9%;  }
.drift-lane-2 { --tilt: var(--lane-tilt-inner);            --seed: -23%; }
.drift-lane-3 { --tilt: calc(var(--lane-tilt-inner) * -1); --seed: -14%; }
.drift-lane-4 { --tilt: calc(var(--lane-tilt-outer) * -1); --seed: -31%; }

@keyframes drift-up   { from { transform: translate3d(0, 0, 0);     } to { transform: translate3d(0, -50%, 0); } }
@keyframes drift-down { from { transform: translate3d(0, -50%, 0);  } to { transform: translate3d(0, 0, 0);    } }

.drift-card {
  width: 100%;
  margin: 0 0 var(--space-4);
  filter: drop-shadow(0 14px 28px rgba(0, 0, 0, 0.5));
}

.drift-card img { width: 100%; height: auto; }

/* Middle column protection: heavy through the reading zone, gone by the time
   the near lanes reach the outer edges, plus a vertical fade so the lanes
   enter and leave the band instead of being sliced off. */
.drift-scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(180deg,
      var(--canvas-bg) 0%,
      color-mix(in srgb, var(--canvas-bg) 0%, transparent) 13%,
      transparent 87%,
      var(--canvas-bg) 100%),
    linear-gradient(90deg,
      transparent 0%,
      color-mix(in srgb, var(--canvas-bg) 26%, transparent) 16%,
      color-mix(in srgb, var(--canvas-bg) 78%, transparent) 27%,
      color-mix(in srgb, var(--canvas-bg) 93%, transparent) 36%,
      color-mix(in srgb, var(--canvas-bg) 93%, transparent) 64%,
      color-mix(in srgb, var(--canvas-bg) 78%, transparent) 73%,
      color-mix(in srgb, var(--canvas-bg) 26%, transparent) 84%,
      transparent 100%);
}

@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  .drift-scrim { background: linear-gradient(90deg, transparent 0%, var(--canvas-bg) 30%, var(--canvas-bg) 70%, transparent 100%); }
}

.drift-copy {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: min(52%, 46rem);
  margin-inline: auto;
  padding-inline: var(--gutter);
  /* v1.7 (founder 08-19): left-aligned inside the panel; the BOX is what
     stays centered on the page. */
  text-align: left;
  text-wrap: pretty;
}

.drift-copy .h2 {
  max-width: none;
  font-size: clamp(1.95rem, 3.4vw, 2.7rem);
}

/* v1.7 — S4 copy panel (founder 08-19). Product panel language: card
   surface + subtle border + the standard lift, on the site's radius and
   spacing rungs. Nearly opaque so the drifting far lanes read as depth
   behind it, not noise through it. */
.copybox {
  /* v1.7.2: teal-cast card surface — the old navy read purple against the
     green body (founder 08-19, same fix as the S2 panes). */
  background: rgba(20, 22, 26, 0.94);
  border: 1px solid var(--rule-soft);
  border-radius: var(--radius-card);
  box-shadow: var(--card-inset), var(--card-lift);
  /* v1.7.11 (founder 08-19): 24px -> 48px with the viewport - the fixed
     space-6 pad squeezed the text column on phones. */
  padding: clamp(1.5rem, 4.5vw, 3rem);
  /* v1.7.16 (founder 08-19): extra REAL padding at the foot - the top only
     looked padded because of the mark having its own margin. */
  padding-bottom: clamp(2.25rem, 6vw, 4rem);
}
.copybox .h2 { margin-bottom: var(--section-gap-tight); }
@media (min-width: 45rem) {
  .copybox .h2 { white-space: nowrap; }
  .beat-span { grid-column: 1 / -1; }
}

/* round 2 (founder markup 08-19): the mark rides top-center; the kicker is
   a quiet teal line over the headline (copy = VETO). */
.copybox-icon {
  display: block;
  margin: 0 auto var(--space-4);
  border-radius: 10px;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.10);
}
.copybox-kicker {
  font-size: 0.92rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--accent);
  margin-bottom: var(--space-2);
}
.beat-aside { font-style: italic; color: var(--accent); }
.beats-boxed .beat-aside,
.beats-boxed .beat-row { margin-top: var(--space-4); }

/* The two example runs: even columns across the panel, one item deep.
   Dot markers borrow the port-dot accent. Collapse to a stack on phones. */
.beat-row {
  display: grid;
  gap: var(--space-2) var(--space-4);
  margin: 0;
  padding: 0;
  list-style: none;
}
/* round 2 (founder markup 08-19): two columns, so the runs read 2+1 and
   2+2 on desktop instead of a cramped single line. Phones stack (below). */
.beat-row-3,
.beat-row-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.beat-row li {
  position: relative;
  padding-left: 1.05em;
  font-size: clamp(0.98rem, 1.2vw, 1.06rem);
  line-height: 1.5;
  color: var(--ink-soft);
  text-wrap: pretty;
}
.beat-row li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent);
}
@media (max-width: 45rem) {
  .beat-row-3,
  .beat-row-4 { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  /* Frozen, but composed: each lane keeps a different offset so the field
     still reads as a field and not as four aligned stacks. This now rides
     --seed on the TRACK, so it cannot clobber the tilt, which lives on the
     lane and is not motion. */
  .drift-track { animation: none !important; }
  .drift-lane-1 { --seed: -11%; }
  .drift-lane-2 { --seed: -34%; }
  .drift-lane-3 { --seed: -19%; }
  .drift-lane-4 { --seed: -44%; }
}

/* ==========================================================================
   S5 : the mini-graph  (v1.5, seams reworked v1.6)
   --------------------------------------------------------------------------
   The single-wire handoff widget is retired; the interactive mini-graph
   lives in minigraph.css / minigraph.js and mounts on [data-minigraph].
   Only the section band (dot canvas) is styled here.

   v1.6 / founder note: no hairline above or below this band any more (S4 and
   S5b already ship with no .section-rule -- there was never a border class
   to remove at the markup level, so this is purely additive). Two more
   background layers, painted ON TOP of the dot pattern in the same
   background-image list: a solid-canvas scrim at the head of the section
   that fades away by ~1/3 of the way down, so "One studio for the whole
   job" sits on dark and the dot grid emerges under it rather than starting
   flush; and a scrim at the tail that fades FROM transparent TO --paper,
   the page's own ink, so the band hands off to S5b (which carries no
   background of its own, just the page) without a colour step at the seam.
   The middle third is left untouched, full-strength dots, because that is
   where the graph itself needs to read clearly. */
.section-wire {
  position: relative;
  background-color: var(--canvas-bg);
  /* v1.7.2 (founder 08-19): the old bottom fade painted a slab of flat
     --paper over the teal body gradient — the "big dark line" between the
     graph and ONE STUDIO. The band now MASKS itself out at both ends so
     the dot canvas dissolves into whatever the body shows, no seam. */
  /* v1.7.3 (founder 08-19): the dots run the WHOLE section now - no
     hard start line - and the band eases in/out on a longer dark mask. */
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 7%, #000 92%, transparent 100%);
  mask-image: linear-gradient(180deg, transparent 0%, #000 7%, #000 92%, transparent 100%);
  /* v1.7.4 (founder 08-19): ONE dot layer, actually repeating - the stale
     three-layer size/position lists from the retired scrims were mapping
     the dots to a single stretched non-repeating gradient, which is why
     the band's own dots vanished and the graph canvas's dots started on a
     hard line. The band is also background-transparent now: dots over the
     page, no slab, no seam. */
  background-color: transparent;
  background-image:
    radial-gradient(var(--dot-ink) 1.3px, transparent 1.4px);
  background-size: 24px 24px;
  background-position: var(--pan-x, 0px) var(--pan-y, 0px);
  background-repeat: repeat;
}

/* the band paints ONE continuous dot field - the graph surfaces go clear
   so no edge exists where the canvas begins (site-scoped override; the
   standalone minigraph keeps its own field). */
.section-wire .mg-canvas { background: transparent; }
.section-wire .mg-world { background: transparent; }



/* ==========================================================================
   S5b : ONE STUDIO, now a section of its own  (v1.5)
   --------------------------------------------------------------------------
   It used to be a figure and a kicker hanging off the bottom of the graph
   section, which is why it read as a footnote. It is now a full section on
   the standard shell: eyebrow -> heading -> lede -> art, on the same rhythm
   composites as S2, S3 and S6, so it earns the same weight they do.

   The art below is a PLACEHOLDER (see the markup): the founder is designing
   this section himself, with real AI-space icons and the Obelus logo at the
   centre, and the local-model callout is destined for the same section. Only
   the <figure> is replaced when that lands; this shell stays.

   The ring itself is deliberately NOT competitor marks: generic subscription
   cards, all the same anonymous shape with a $ badge, ringed and wired
   around the Obelus obelisk.
   ========================================================================== */
/* the kicker is the section's lede now, so it sits on the heading's own
   interval instead of opening a block break under it */
.kicker-lede { margin-top: 0; }

.onestudio {
  margin-top: var(--section-gap-media);
  max-width: var(--measure-wide);
}

.onestudio svg { width: 100%; height: auto; }

.os-cards use { fill: none; stroke: var(--ink-mute); stroke-width: 1.2; opacity: 0.5; }
.os-links, .os-cards { stroke: var(--ink-mute); }
.os-cards .os-near { opacity: 0.66; }

.os-cards { color: var(--ink-mute); }

.onestudio .os-glyph { fill: currentColor; stroke: none; opacity: 0.55; }
.onestudio .os-badge { fill: none; stroke: currentColor; stroke-width: 1; opacity: 0.5; }
.onestudio .os-dollar { fill: none; stroke: currentColor; stroke-width: 1.1; stroke-linecap: round; opacity: 0.8; }

.os-links path {
  fill: none;
  stroke: var(--ink-mute);
  stroke-width: 1;
  stroke-dasharray: 2 5;
  stroke-linecap: round;
  opacity: 0.3;
}

.os-plate {
  fill: var(--accent-tint);
  stroke: color-mix(in srgb, var(--accent) 46%, transparent);
  stroke-width: 1.4;
}

/* Founder's knocked-out tile logo (08-18): a single FILLED path, the mark
   reads as holes. The old stroked-linework rule died with the old mark —
   CSS fill/stroke beat SVG presentation attributes, so a stroke rule here
   turns the tile into outlines (the founder's "stroke applied to the
   lines" catch). */
.os-mark path {
  fill: var(--accent);
  stroke: none;
}

@supports not (color: color-mix(in srgb, red 50%, transparent)) {
  .os-plate { stroke: var(--accent); }
}

/* ==========================================================================
   S6/S7 : built to stay yours (the ledger)
   ========================================================================== */
/* v1.7.4 (founder 08-19): the teal tint band is CUT - it read as a green
   gradient and its stops read as hairlines. */
.promise-arc { background: none; }

.ledger {
  display: grid;
  gap: var(--section-gap-media);
}

/* Redline 1: the split moved from 7/5 to 6/6. The panel carries a
   four-row group whose longest row ("Your library, browsable and
   exportable") has to land on one line at 1440.
   Punch-list item 7: the column gap gains about 2rem, so the argument stops
   crowding the panel. */
@media (min-width: 62rem) {
  .ledger {
    grid-template-columns: 6fr 6fr;
    gap: var(--section-gap-media);
    align-items: start;
  }
}

.ledger-arg .h2 { max-width: none; }

/* the second headline supplies its own air now that the hesitation beat
   is gone: no rule, no thread, just the interval */
.ledger-turn { margin-top: var(--section-gap-media); }

.ledger-lede {
  font-size: clamp(1.05rem, 1.35vw, 1.14rem);
  line-height: 1.7;
  color: var(--ink);
}

.lic {
  border: 1px solid var(--rule);
  border-radius: var(--radius-card);
  background: var(--paper-warm);
  padding: var(--section-gap-inner);
}

@media (min-width: 62rem) {
  .lic {
    align-self: start;
    position: sticky;
    top: var(--space-7);
  }
}

/* Redline 5: the panel title is a heading, not an eyebrow. Sentence case,
   because uppercase tracking at this size competes with the group labels
   below instead of sitting above them. */
.lic-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.28rem, 1.75vw, 1.5rem);
  line-height: 1.2;
  letter-spacing: -0.014em;
  color: var(--ink);
  margin-bottom: var(--section-gap-inner);
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.lic-icon { width: 22px; height: 22px; flex: 0 0 auto; color: var(--accent); }

.lic-group + .lic-group { margin-top: var(--section-gap-inner); }

.lic-label {
  font-size: 0.71rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute);
  padding-bottom: var(--space-3);
}

.lic-rows { list-style: none; }

.lic-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  /* v1.7.2 (founder 08-19): the per-row hairline dividers are CUT — just
     dot + text, tighter rhythm. */
  padding: var(--space-3) 0;
  font-size: clamp(0.96rem, 1.15vw, 1.06rem);
  line-height: 1.45;
}

.lic-dot {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  margin-top: 0.42em;
  border-radius: 50%;
  box-sizing: border-box;
}

/* what stays: the app's live port, filled */
.lic-keeps .lic-row { color: var(--ink); }
.lic-keeps .lic-dot { background: var(--accent); }

/* what ends: the app's hollow port ring, muted */
.lic-ends .lic-row { color: var(--ink-soft); }
.lic-ends .lic-dot { border: 1.5px solid var(--ink-mute); }

/* Redlines 4 and 8: the two asterisk notes live inside the panel, under a
   hairline, quiet enough to read as footnotes rather than a third group */
.lic-notes {
  /* the last row already carries 1.25rem of its own padding, so this stays
     small or a hole opens above the hairline */
  margin-top: var(--space-3);
  padding-top: var(--space-4);
}

.lic-note {
  padding-left: 0.75em;
  text-indent: -0.75em;
  font-size: 0.82rem;
  line-height: 1.62;
  color: var(--ink-mute);
}

.lic-note + .lic-note { margin-top: var(--space-2); }

/* ==========================================================================
   S8 : the letter
   --------------------------------------------------------------------------
   Punch-list item 8. The section was too plain, so it gets a background out
   of our own vocabulary and nothing else: a faint dot canvas band, masked to
   fade at top and bottom, and one quiet wire arc with a filled port and a
   hollow one. Static. It should read as intentional, not busy.
   ========================================================================== */
.letter-section {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--paper-warm);
  padding-block: var(--section-pad-y);
}

.letter-bg { position: absolute; inset: 0; z-index: -1; pointer-events: none; }

.letter-dots {
  position: absolute;
  inset: 0;
  background-image: radial-gradient(var(--dot-ink) 1.2px, transparent 1.3px);
  background-size: 26px 26px;
  opacity: 0.62;
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 24%, #000 76%, transparent 100%);
  mask-image: linear-gradient(180deg, transparent 0%, #000 24%, #000 76%, transparent 100%);
}

.letter-wire {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.letter-arc {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linecap: round;
  opacity: 0.2;
}

.letter-port { fill: var(--accent); opacity: 0.28; }
.letter-port-hollow { fill: none; stroke: var(--accent); stroke-width: 2; opacity: 0.24; }

.letter { margin-top: 0; }

.letter p {
  font-family: var(--font-display);
  font-size: clamp(1.18rem, 1.9vw, 1.44rem);
  /* v1.7.2 (founder 08-19): "space out the words" — taller lines + a hair
     of word gap so the letter reads easy instead of dense. */
  line-height: 1.9;
  word-spacing: 0.03em;
  letter-spacing: -0.003em;
  color: var(--ink);
  max-width: var(--measure);
}

.signoff {
  margin-top: var(--section-gap-inner) !important;
  font-size: clamp(1rem, 1.2vw, 1.08rem) !important;
  font-style: italic;
  color: var(--ink-mute) !important;
}

/* ==========================================================================
   S9 : the close
   ========================================================================== */
.close-section {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  align-items: center;
  min-height: clamp(28rem, 62vh, 42rem);
  /* v1.7.4 (founder 08-19): neutral ground + a subtle lined grid instead
     of the greenish wash. */
  background:
    linear-gradient(rgba(255, 255, 255, 0.028) 1px, transparent 1px) 0 0 / 100% 48px,
    linear-gradient(90deg, rgba(255, 255, 255, 0.028) 1px, transparent 1px) 0 0 / 48px 100%,
    #0A0B0D;
}

.close-media {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--canvas-bg);
  overflow: hidden;
}

.close-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 74% 50%;
}

/* Punch-list item 9. The plate is 1739x1100, so at desktop widths cover
   crops it vertically only and object-position-x does nothing at all: the
   nodes stayed parked at 43% of the band, right under the form. Pushing the
   artwork right is therefore a transform, and it is over-scaled first so
   the translate cannot open a bare strip on the left. */
@media (min-width: 64rem) {
  .close-media img { transform: translateX(11%) scale(1.24); }
}

.close-scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(90deg,
      color-mix(in srgb, var(--paper) 90%, transparent) 0%,
      color-mix(in srgb, var(--paper) 87%, transparent) 34%,
      color-mix(in srgb, var(--paper) 74%, transparent) 44%,
      color-mix(in srgb, var(--paper) 40%, transparent) 54%,
      color-mix(in srgb, var(--paper) 12%, transparent) 62%,
      transparent 70%),
    linear-gradient(180deg,
      color-mix(in srgb, var(--paper) 58%, transparent) 0%,
      transparent 22%,
      transparent 74%,
      color-mix(in srgb, var(--paper) 66%, transparent) 100%);
}

@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  .close-scrim { background: linear-gradient(90deg, var(--paper) 0%, var(--paper) 36%, transparent 84%); }
}

.close-media[data-fallback] { background: var(--dot-field); }
.close-media[data-fallback] img { display: none; }

.close-inner { position: relative; }

.close-inner .body,
.close-inner .form-note,
.close-inner .beta-form { max-width: min(100%, 24rem); }

/* ==========================================================================
   NARROW SCREENS  (v1.4 mobile pass, founder notes M1-M8)
   --------------------------------------------------------------------------
   Three stops, and they mean the same thing on every section:
     56rem  the two-column-ish layouts collapse
     48rem  PHONE. artwork is re-composed, not just squeezed
     34rem  the smallest controls go full width
   Vertical spacing is never touched here: the rhythm scale steps itself
   down through its own clamps, so a phone and a desktop are the same page
   at two sizes rather than two different pages.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .btn:hover { transform: none; }
  * { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}

@media (max-width: 56rem) {
  /* Type stops sitting BESIDE the artwork here and starts sitting ON it,
     so both plates swap their left-to-right scrim for a top-to-bottom
     one. The 48rem block below re-composes them again for phones; this
     stop is the tablet in between, and it needs its own answer or the
     lead runs straight over the bright preview. */
  .hero { min-height: 0; }
  .hero-inner > *,
  .hero-inner .measure { max-width: 100%; }
  .hero-media img { transform: none; object-position: 66% 50%; }
  .hero-scrim {
    background: linear-gradient(180deg,
      color-mix(in srgb, var(--paper) 88%, transparent) 0%,
      color-mix(in srgb, var(--paper) 82%, transparent) 48%,
      color-mix(in srgb, var(--paper) 92%, transparent) 100%);
  }

  .close-section { min-height: 0; padding-bottom: calc(var(--section-pad-y) + var(--section-gap-media)); }
  .close-media img { object-position: 70% 50%; }
  .close-scrim {
    background: linear-gradient(180deg,
      color-mix(in srgb, var(--paper) 94%, transparent) 0%,
      color-mix(in srgb, var(--paper) 90%, transparent) 62%,
      color-mix(in srgb, var(--paper) 78%, transparent) 84%,
      color-mix(in srgb, var(--paper) 88%, transparent) 100%);
  }
  .close-inner .h2,
  .close-inner .body,
  .close-inner .form-note,
  .close-inner .beta-form { max-width: 100%; }
}

/* --------------------------------------------------------------------------
   M1 : the hero on a phone
   The founder's report was "art MISSING entirely on mobile", and the cause
   was a delivery one (the 1600 plate never reached the published build).
   With it back, the composition still needs re-cutting, because his plate
   is a WIDE 16:9 and the phone is a tall 9:19.5. So instead of scrimming
   the whole thing to near-black under stacked type, the band splits: the
   top third is artwork at close to full strength, the copy sits on the
   heavy end of a single vertical scrim underneath it. The crop pushes into
   the plate's right-hand subject (the app window and its preview) and
   over-scales so no bare canvas can open at the left edge.

   PORTRAIT PLATE: when site-assets/hero-mobile.jpg lands (1200x1600 per
   the spec), uncomment the one <source> line marked HERO-MOBILE in
   index.html. Nothing else changes: it is deliberately NOT referenced
   today, because a <picture> source that 404s is final -- the browser does
   not fall back to the next candidate, which is exactly the failure the
   founder photographed.
   -------------------------------------------------------------------------- */
@media (max-width: 47.99rem) {
  .hero {
    align-items: flex-end;
    min-height: min(88vh, 46rem);
    /* the art band: everything above the eyebrow is plate at near-full
       strength, which is what "art must be clearly visible" means on a
       phone. The scrim below is measured to land at full weight one line
       ABOVE the eyebrow, never under it. */
    padding-top: calc(var(--space-9) + var(--space-5));
  }

  .hero-media img {
    object-position: 72% 44%;
    transform: scale(1.26);
  }

  .hero-scrim {
    background: linear-gradient(180deg,
      color-mix(in srgb, var(--paper) 22%, transparent) 0%,
      color-mix(in srgb, var(--paper) 28%, transparent) 12%,
      color-mix(in srgb, var(--paper) 72%, transparent) 17%,
      color-mix(in srgb, var(--paper) 92%, transparent) 23%,
      color-mix(in srgb, var(--paper) 96%, transparent) 100%);
  }

  /* the fallback field has no subject to crop to, so it keeps the old
     even wash rather than pretending there is art up top */
  .hero-media[data-fallback] .hero-scrim {
    background: linear-gradient(180deg,
      color-mix(in srgb, var(--paper) 82%, transparent) 0%,
      color-mix(in srgb, var(--paper) 90%, transparent) 100%);
  }
}

/* --------------------------------------------------------------------------
   M3 : the drift field on a phone  (v1.5, re-ruled)
   v1.4 answered "broken on mobile" by pulling the field out from behind the
   copy and parking it as a band UNDERNEATH. The founder's v1.5 note rejects
   that shape in as many words: the whole field must not be slotted under the
   text. What he wants is ONE lane per side, the text as the focus in the
   middle, and the nodes reading as peripheral elements scrolling past the
   OUTSIDE of the reading column.

   So the field goes back to being a full-height backdrop, but the two inner
   lanes are gone entirely and the two survivors are pushed most of the way
   off the screen. One number governs that: --lane-peek, the width of card
   that stays on screen. The rail's negative inset is DERIVED from it
   (peek - lane width), and so is the copy's own padding, so the reading
   column starts exactly where the visible card ends. The nodes are outside
   the text, not under it, at every width in the range, which is the part of
   his note v1.4 got wrong.

   Both survivors keep their tilt, and the pair is mirrored: lane 1 is a near
   lane on the left, lane 4 is a near lane on the right. Same z-depth grammar
   as desktop, one step per side instead of two.
   -------------------------------------------------------------------------- */
@media (max-width: 61.99rem) {
  .section-drift {
    --lane-near: 30vw;
    --lane-far:  26vw;
    /* only the outer pair survives on a phone (lane-2/-3 are display:none
       below), so only --lane-tilt-outer is live here */
    --lane-tilt-outer: 9deg;
    /* the only authored number here: how much card stays on screen */
    --lane-peek: 8vw;

    /* shorter than desktop's 98vh: on a phone the copy is the height */
    min-height: 34rem;
  }

  /* the surviving pair is pushed out until exactly --lane-peek shows */
  .drift-rail { inset: 0 calc(var(--lane-peek) - var(--lane-near)); }

  .drift-gap { min-width: 44%; }

  /* ONE lane per side. The inner pair is what was fighting the text. */
  .drift-lane-2,
  .drift-lane-3 { display: none; }

  /* the survivors are peripheral, so they sit back a step from desktop-near */
  .drift-near { opacity: 0.72; }

  /* Vertical feather only at the band's own edges; horizontally the scrim
     protects the reading column and leaves the peek strips alone, because on
     a phone those strips ARE the design element. */
  .drift-scrim {
    background: linear-gradient(180deg,
      var(--canvas-bg) 0%,
      color-mix(in srgb, var(--canvas-bg) 30%, transparent) 11%,
      transparent 26%,
      transparent 74%,
      color-mix(in srgb, var(--canvas-bg) 30%, transparent) 89%,
      var(--canvas-bg) 100%),
    linear-gradient(90deg,
      transparent 0%,
      transparent 7%,
      color-mix(in srgb, var(--canvas-bg) 55%, transparent) 11%,
      color-mix(in srgb, var(--canvas-bg) 90%, transparent) 16%,
      color-mix(in srgb, var(--canvas-bg) 90%, transparent) 84%,
      color-mix(in srgb, var(--canvas-bg) 55%, transparent) 89%,
      transparent 93%,
      transparent 100%);
  }

  /* the copy column starts where the visible card ends */
  .drift-copy {
    max-width: 100%;
    text-align: left;
    /* v1.7.11: the lane-peek allowance predates the panel - the box has
       its own surface now, so the plain gutter is enough. */
    padding-inline: var(--gutter);
  }
  .drift-copy .h2,
  .drift-copy .beats { max-width: var(--measure); }
}

@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  @media (max-width: 61.99rem) {
    .drift-scrim { background: linear-gradient(90deg, transparent 0%, transparent 8%, var(--canvas-bg) 16%, var(--canvas-bg) 84%, transparent 92%, transparent 100%); }
  }
}

/* --------------------------------------------------------------------------
   M4 : one studio + the widget on a phone
   (a) The subscription-ring graphic was drawn for a 720-wide viewBox and
       was reduced to specks on a phone. It is now CROPPED instead of
       scaled down: the svg runs 140% wide inside a clipping figure, which
       throws away the two horizontal extremes (which is why they are also
       hidden, so nothing is half a card) and buys every surviving element
       a 1.4x bump. The centre mark grows with it, which is the point of
       the graphic.
   (b) The S2 feature box already goes single column below 52rem (.fbox's
       own breakpoint, wider than this stop), so nothing phone-specific is
       needed for it here.
   -------------------------------------------------------------------------- */
@media (max-width: 47.99rem) {
  .onestudio { overflow: hidden; }
  /* max-width:none is load-bearing. The reset carries `img, svg { max-width:
     100% }`, which silently clamped this width:140% back to 100%, so the
     v1.4 "crop instead of shrink" fix has never actually run on a phone and
     the ring has been specks this whole time. Found while lifting the block
     into its own section. */
  .onestudio svg { width: 140%; max-width: none; margin-inline: -20%; }
  .onestudio .os-edge { display: none; }

  .chips-apps { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* --------------------------------------------------------------------------
   M7 : the letter's background on a phone
   The arc was drawn for a 1440x560 band. On a 390 viewport the slice puts
   its apex straight through the reading column. It is lifted clear into
   the head of the section and quietened, so it stays what it was meant to
   be: a background, not a rule through the founder's sentences.
   -------------------------------------------------------------------------- */
@media (max-width: 47.99rem) {
  /* The arc is drawn for a 1440x560 band. Sliced to 390 there is no
     placement that both keeps it an arc and keeps it out of the reading
     column, so it is lifted into the head of the section and dropped to
     texture weight: at this opacity it reads as grain behind the letter,
     not as a rule through it. The dot field carries the rest. */
  .letter-wire {
    height: 52%;
    opacity: 0.5;
  }
  .letter-dots {
    -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 16%, #000 84%, transparent 100%);
    mask-image: linear-gradient(180deg, transparent 0%, #000 16%, #000 84%, transparent 100%);
  }
}

/* --------------------------------------------------------------------------
   M8 : the waitlist plate on a phone
   Founder: "essentially unseeable". True -- at 390 the side view sat
   behind the form under a 94% scrim, which is the worst of both, a
   photograph you pay for and nobody sees. RULED: give it its own band.
   The media element leaves the background and becomes a real full-width
   strip UNDER the form, at close to full strength, with only enough of a
   top feather to join the section it belongs to. The alternative (hide it
   below 48rem) was rejected: this plate is the one piece of product proof
   at the close, and a phone is where most of this link gets opened.
   -------------------------------------------------------------------------- */
@media (max-width: 47.99rem) {
  .close-section {
    display: flex;
    flex-direction: column;
    /* the base rule centres its children; a band has to stretch */
    align-items: stretch;
    min-height: 0;
    padding-bottom: 0;
    overflow: hidden;
  }

  .close-inner { order: 1; }

  .close-media {
    order: 2;
    position: relative;
    inset: auto;
    z-index: 0;
    height: clamp(14rem, 58vw, 21rem);
    margin-top: var(--section-gap-media);
  }
  /* v1.7.10 (founder 08-19): the hairline above the art is a SCRIM now -
     the plate emerges out of the section's own dark instead of starting
     on a line. */
  .close-media::before {
    content: '';
    position: absolute;
    inset: 0 0 auto 0;
    height: 4.5rem;
    z-index: 1;
    pointer-events: none;
    background: linear-gradient(180deg, #0A0B0D 0%, transparent 100%);
  }

  /* The plate is 1739x1100 and its cards live at source x 750-1560,
     y 330-800. Squeezed to 390 those cards are specks, so the band shows
     a measured CROP at legible size instead of the whole composition at
     illegible size: the image is laid in at 2x and parked so that window
     lands in the strip. */
  .close-media img {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 200%;
    height: auto;
    max-width: none;
    object-fit: fill;
    transform: translate(-66%, -52%);
  }

  .close-scrim {
    background: linear-gradient(180deg,
      color-mix(in srgb, var(--paper) 80%, transparent) 0%,
      color-mix(in srgb, var(--paper) 12%, transparent) 34%,
      transparent 70%,
      color-mix(in srgb, var(--paper) 30%, transparent) 100%);
  }
}

@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  @media (max-width: 47.99rem) {
    .close-scrim { background: linear-gradient(180deg, var(--paper) 0%, transparent 40%); }
  }
}

/* --------------------------------------------------------------------------
   The smallest stop: controls go full width, the widget keeps real targets
   -------------------------------------------------------------------------- */
@media (max-width: 34rem) {
  .cta-row .btn { width: 100%; justify-content: center; }
  .beta-form .btn { width: 100%; justify-content: center; }
  .display { max-width: none; }
  .h2 { max-width: none; }
  .masthead-nav { gap: var(--space-3); }

}

/* footer trademark fineprint (08-17, founder-ruled logos + standard armor) */
.site-footer .fineprint { font-size: 0.72rem; color: var(--ink-faint, var(--ink-mute)); max-width: var(--measure-wide); }


/* ==========================================================================
   v1.7.2 ONE STUDIO LIVE (founder design 08-19)
   --------------------------------------------------------------------------
   Obelus tile + callout above a drifting strip of the prepared AI-tool
   marks (black tiles, white marks). Wide: horizontal strip below the
   stage, fade-masked at both ends. Phones: the strip runs VERTICALLY on
   the right, mark + callout on the left. The .is-hot tile is the one
   currently answered by the callout. GPU-only transforms; pauses when
   hovered, when off screen, and under reduced motion.
   ========================================================================== */
.onestudio-live { margin-top: var(--space-6); }

/* v1.7.3 (founder 08-19): centered composition - the mark is THE focus,
   the callout types beneath it, the strip runs under both at all widths.
   The vertical phone rail is RETIRED. */
.osl-stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
  text-align: center;
}
.osl-mark {
  flex: 0 0 auto;
  width: clamp(84px, 10vw, 112px);
  height: auto;
  border-radius: 22%;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.10), 0 14px 34px rgba(0, 0, 0, 0.4);
}
.osl-callout {
  min-height: 1.6em;
  font-size: clamp(1.05rem, 1.5vw, 1.25rem);
  color: var(--ink-soft);
}
.osl-what { color: var(--accent); font-weight: 600; }

.osl-marquee {
  display: flex;
  /* v1.7.9 (founder 08-19): a REAL scroller now - swipe on touch, drag on
     desktop, JS drifts it when idle. Scrollbar hidden; edge fades stay. */
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  cursor: grab;
  padding-block: 6px;
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 12%, #000 88%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0%, #000 12%, #000 88%, transparent 100%);
}
.osl-track {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  gap: var(--space-3);
  padding-right: var(--space-3);
  margin: 0;
  list-style: none;
  -webkit-user-select: none;
  user-select: none;
}
.osl-marquee::-webkit-scrollbar { display: none; }
.osl-marquee:active { cursor: grabbing; }

.osl-tile {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 56px;
  height: 56px;
  border-radius: 13px;
  background: #0C0D0F;
  border: 1px solid rgba(255, 255, 255, 0.07);
  transition: background 180ms ease, border-color 180ms ease;
}
.osl-tile img { width: 30px; height: 30px; display: block; opacity: 0.85; transition: opacity 180ms ease; -webkit-user-drag: none; }
/* calm highlight (founder 08-19: no scale, no glow push) */
.osl-tile.is-hot { border-color: var(--accent-edge); background: #121517; }
.osl-tile.is-hot img { opacity: 1; }

/* (auto-drift lives in JS now; reduced motion simply never drifts, and
   the strip stays hand-scrollable either way) */

@media (max-width: 51.99rem) {
  .osl-mark { width: 84px; }
}


/* v1.7.14 (founder 08-19): more air between sections on phones */
@media (max-width: 45rem) {
  :root { --section-pad-y: 6.5rem; }

  /* v1.7.16: hero rhythm opens up slightly on phones (founder 08-19) */
  .hero-inner .display { margin-bottom: var(--space-4); }
  .hero-inner .lead { margin-bottom: var(--space-5); }
  .cta-row { gap: var(--space-3); }

  /* v1.7.15: the S2 header un-flexes on phones - the tile floats, the
     headline wraps beside it, and the paragraph runs full width below
     (it was confined to the left column). */
  .s2-head { display: block; }
  .s2-appicon {
    float: right;
    margin-left: var(--space-3);
    margin-bottom: var(--space-2);
  }
}
