/* ═══════════════════════════════════════════════════════════════════════════
   ONEWAY — THE POPIT & THE WORLD  (oneway-popit.css)

   The canonical material, reconstructed for every surface: iOS · Android ·
   Safari · Chrome · Mac · PC. Companion: oneway-popit.js.

   CANON — this file IMPLEMENTS, it does not invent (LBP §22):
     docs/product/DESIGN_LANGUAGE.md   (the ratified language, 2026-07-17)
     context/LBP_POPIT_DESIGN_LAWS.md  (§15 material · §17 hue · §19 what a
                                        Popit is · §20 windows-not-cards)
   Every value below is carried over from the proving ground (index.html).
   What changed is HOW it is achieved, never WHAT it looks like.

   ── THE ONE ENGINEERING LAW OF THIS FILE ──────────────────────────────────
   A resting Popit uses NO backdrop-filter, NO mix-blend-mode, NO filter:url().
   Those three are the only per-frame costs in the old material: each forces
   the compositor to re-sample the moving world behind the element on EVERY
   frame, per bead. They are also exactly what the mobile safe floor strips —
   which is why mobile used to get a degraded product.

   CONSEQUENCE (and the acceptance test): the resting Popit renders
   IDENTICALLY with html[data-fx="safe"] on or off. Verify with the A/B in
   popit-lab.html before shipping any change to this file.

   The ONE open sheet on screen may use backdrop-filter (glass you see the
   world through, §3) — one element is affordable everywhere; N beads are not.
   It carries an opaque fallback so the safe floor and older Android still
   land on the same composition.

   ── PRE-PAINT CONTRACT (host page, in <head>, before any stylesheet) ───────
   <script>(function(){try{var d=document.documentElement;
     if((matchMedia&&matchMedia('(pointer:coarse)').matches)||innerWidth<768)
       d.setAttribute('data-fx',(new URLSearchParams(location.search).get('fx')||'safe'));
     if(/Windows|Win32|Win64/i.test(navigator.userAgent||''))d.classList.add('ow-win');
   }catch(e){}})();</script>
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 0 · THE SAFE FLOOR (permanent mobile baseline — never remove) ─────────
   Kept in force as a guard. After this reconstruction it should change
   NOTHING about a resting Popit; it still protects the one open sheet and
   any host-page surface that has not been converted yet. */
html[data-fx="safe"] *,
html[data-fx="safe"] *::before,
html[data-fx="safe"] *::after{
  backdrop-filter:none !important; -webkit-backdrop-filter:none !important;
  filter:none !important; mix-blend-mode:normal !important; will-change:auto !important;
}


/* ═══ 0.5 · LIGHT AND DARK — one material, two grounds ══════════════════════
   (founder, 2026-07-30: "make sure everything works in a light mode.")

   THE DISTINCTION THAT MAKES THIS ONE STYLESHEET RATHER THAN TWO:

   · A CANDY BEAD'S WHITES ARE SPECULAR. The wet highlight, the catchlight, the
     glossy top — those are light REFLECTING off a physical object. A red
     lollipop has a white highlight in a dark room and in a bright one. They do
     not flip, and the bead therefore needs almost nothing to work on light.

   · A GLASS PANEL'S WHITES ARE SURFACE. The body, the inner rim, the seam, the
     text — those describe the material itself against its ground, and every
     one of them inverts.

   So the tokens below are only the SURFACE values. Specular highlights stay
   hardcoded white on purpose; changing them would make the beads look like
   plastic printed in the wrong ink.

   Set html[data-theme="light"][data-theme="light"] (or leave it to prefers-color-scheme). The hue
   contract is untouched: a Center's identity is the same colour in both
   grounds — light is a different room, not a different Center. */

/* ── BORDER-BOX. A declared width is the width you SEE. ────────────────────
   Without this, `width:100%` plus `padding:0 18px` is the container plus 36px,
   and the whole system was written as if that were not true. It cost three
   real defects, all invisible until measured against the rendered document:

     · the App's main column was 36px wider than the viewport ON EVERY PAGE,
       so every surface scrolled sideways and every full-bleed row ran off the
       right edge;
     · a message row (.ow-thread) overhung its own list by 26px;
     · the desktop nav rail declares 96px, drew 116px, and the column beside
       it was offset by the declared 96 — so the rail sat under the content.

   It also settles a real inconsistency in the material: a bead declares
   124×174 with 18/14 padding, which content-box renders at 152×210 loose but
   124 wide inside .po-grid (where width:auto lets the column decide). The same
   object was two different sizes depending on its arrangement. Border-box
   makes the declared size the true size in both, which is what --po-col:124px
   always assumed. */
*,*::before,*::after{box-sizing:border-box}

:root{
  --ow-ground:#000;                    /* the world's floor */
  --ow-ink:#fff;                       /* type on that floor */
  --ow-ink-2:rgba(255,255,255,.68);
  /* ── .48 CLEARS THE FLOOR AND MISSES THE PANEL ────────────────────────
     Dark's tertiary ink was tuned against #000, where .48 gives 4.92:1 and
     passes comfortably. Almost nothing is read directly on the floor: a Popit's
     quiet line sits on a LIFTED PANEL, and the panels are tinted by each
     Center's hue, so the real grounds measured on Home are rgb(34,44,73),
     rgb(34,44,74) and rgb(73,35,44) — not black.

     On those, .48 reads 4.39, 4.38 and 4.29. Each of the three needs .49-.50
     to clear AA, so this is not a redesign, it is a rounding error that was
     measured against the wrong surface. .52 clears all three with headroom and
     leaves the ramp intact: ink-2 stays 7.71 on the same panel.

     The light side was matched to dark's FLOOR ramp in an earlier commit and is
     unaffected — it was measured independently at 4.74 on its own panel. */
  --ow-ink-3:rgba(255,255,255,.52);
  /* THE POPIT/CARD BODY. One token because three surfaces paint the same
     material — the feed card, the Home canvas tile and the profile tile — and
     the moment each carried its own literal they drifted (two of them ended up
     on the opaque panel, which is a black slab with a tinted edge). */
  /* ★ FOUNDER/459, 2026-09-20: *"lighten up the darker parts of the final
     popits."* The floor of the card's gradient was rgba(8,9,20,.55) — near
     black at more than half cover, so the lower half of every Popit sank
     into the page. The floor is now a lifted indigo at less cover, and the
     opaque plate the rows sit on (`--ow-plate`) is the same lift. Every
     rule that painted the literal now paints the token, so the material
     cannot drift into three floors again. */
  --ow-tile-base:rgba(48,50,92,.50);
  --ow-tile-mid:rgba(40,42,78,.44);
  --ow-plate:#1c2042;
  --ow-panel-1:rgba(40,39,70,.95);     /* the glass body, top → bottom — lifted (459) */
  --ow-panel-2:rgba(24,25,48,.96);
  --ow-sheet-1:rgba(34,33,58,.9);
  --ow-sheet-2:rgba(14,14,28,.93);
  --ow-rim:rgba(255,255,255,.09);      /* the material's own inner edge */
  --ow-lift:rgba(255,255,255,.2);      /* the top edge-light */
  --ow-seam:rgba(255,255,255,.24);     /* the stitched thread */
  --ow-seam-bead:rgba(255,255,255,.46);
  --ow-weight:rgba(0,0,0,.4);          /* the weighted base */
  --ow-drop:rgba(0,0,0,.66);           /* the contact shadow */
  --ow-chrome:rgba(4,5,12,.92);        /* nav + header */
  /* THE SAME GROUND AS A TRIPLET. `--ow-chrome` bakes in one alpha, so a
     surface that needs SEVERAL — a dock that is solid under its icons and
     dissolves at its top edge — cannot be built from it without hardcoding the
     colour, which is how the dock came to paint a near-black bar under
     near-black icons in light mode. */
  --ow-chrome-rgb:4,5,12;
  --ow-scrim:rgba(4,5,14,.5);
  /* THE FIELD GROUND. `.ow-fld` hardcoded `rgba(0,0,0,.26)` + `color:#fff`,
     which is every input in the App — login, signup, the composer, search,
     settings — rendering as a dark slab with white text on the LIGHT ground a
     first-time visitor lands in. Tokenised so one field material serves both. */
  --ow-field:rgba(0,0,0,.26);
  --ow-field-edge:rgba(255,255,255,.08);
  --ow-grain:.14;
  --ow-stars:1;
}
/* DOUBLED TO MATCH ITS TWIN'S WEIGHT, not for any effect on what it matches.
   The system half below is `html:not([data-theme="dark"]):not([data-theme="light"])`
   — two attribute selectors, (0,2,1) — while this was one, (0,1,1). The two
   halves of one rule weighing differently is the defect that made the explicit
   Light theme lose to `.po-card[data-blended]` for weeks, and the invariant
   that now guards it cannot tell a harmless weight gap from a harmful one.
   Neither can a person reading the file. So they are made equal. */
html[data-theme="light"][data-theme="light"]{
  --ow-tile-base:rgba(255,255,255,.62);
  --ow-ground:#eceaf3;
  --ow-ink:#14131f;
  /* ── THE LIGHT RAMP MIRRORED DARK'S ALPHAS, NOT DARK'S CONTRAST ─────────
     Dark sets --ow-ink-2 at .68 and --ow-ink-3 at .48 over #000, and delivers
     21 : 9.40 : 4.92 — each step roughly half the last, every rung above AA.
     Light copied those alphas (.7 and .5) and delivered 21 : 5.69 : 3.16,
     because its ground is #eceaf3 and its panels are translucent lavender, not
     black. Equal alpha over a lighter floor is simply less contrast. The
     tertiary rung landed at 3.16:1 — below the 4.5 that body text needs.

     Measured on the real Home canvas: "6 POSTS" 3.33, "said" 3.21, "NOTHING
     HERE YET" 3.19/3.16, "Nothing right now" 3.17.

     .86 and .64 put light at 21 : 9.15 : 4.74 — the dark theme's own ramp,
     within a tenth on every rung. The target is not invented here; it is what
     this palette already does in the theme that works.

     ★ CROSS-LANE: --ow-ink-2/-3 are app-wide, so this darkens muted type on
       every surface, not only Popits. Two values, trivially reverted. */
  --ow-ink-2:rgba(20,19,31,.86);
  --ow-ink-3:rgba(20,19,31,.64);
  --ow-panel-1:rgba(255,255,255,.9);
  --ow-panel-2:rgba(243,241,249,.94);
  --ow-sheet-1:rgba(255,255,255,.88);
  --ow-sheet-2:rgba(240,238,248,.93);
  --ow-rim:rgba(20,19,31,.09);
  --ow-lift:rgba(255,255,255,.9);
  --ow-seam:rgba(20,19,31,.2);
  --ow-seam-bead:rgba(255,255,255,.72);   /* still on candy — stays bright */
  --ow-weight:rgba(60,52,96,.1);
  --ow-drop:rgba(48,40,80,.2);
  --ow-chrome:rgba(252,251,255,.9);
  --ow-chrome-rgb:252,251,255;
  --ow-scrim:rgba(230,228,240,.6);
  --ow-field:rgba(20,19,31,.045);
  --ow-field-edge:rgba(20,19,31,.13);
  --ow-grain:.06;
  --ow-stars:0;                        /* a daylit sky has no stars */
}
@media (prefers-color-scheme:light){
  html:not([data-theme="dark"]):not([data-theme="light"]){
    --ow-tile-base:rgba(255,255,255,.62);
    --ow-ground:#eceaf3; --ow-ink:#14131f;
    /* the ramp reasoned about above, kept identical in the system-light scope */
    --ow-ink-2:rgba(20,19,31,.86); --ow-ink-3:rgba(20,19,31,.64);
    --ow-panel-1:rgba(255,255,255,.9); --ow-panel-2:rgba(243,241,249,.94);
    --ow-sheet-1:rgba(255,255,255,.88); --ow-sheet-2:rgba(240,238,248,.93);
    --ow-rim:rgba(20,19,31,.09); --ow-lift:rgba(255,255,255,.9);
    --ow-seam:rgba(20,19,31,.2); --ow-seam-bead:rgba(255,255,255,.72);
    --ow-weight:rgba(60,52,96,.1); --ow-drop:rgba(48,40,80,.2);
    --ow-chrome:rgba(252,251,255,.9); --ow-chrome-rgb:252,251,255;
    --ow-scrim:rgba(230,228,240,.6);
    --ow-field:rgba(20,19,31,.045); --ow-field-edge:rgba(20,19,31,.13);
    --ow-grain:.06; --ow-stars:0;
  }
}

/* ═══ 1 · TOKENS ═══════════════════════════════════════════════════════════
   THE HUE CONTRACT — one representation, platform-wide.
   A Center owns a PAIR (§17). Both are "R,G,B" triplets so every surface can
   write rgba(var(--ow-h1), a). Set them on :root for the world, or on any
   ancestor to scope a Center. oneway-popit.js owns the tween and the
   angle→pair bridge for the backend's single resolved_hue integer. */
:root{
  --ow-h1:124,45,255;          /* ONEWAY primary   — violet */
  --ow-h2:93,226,255;          /* ONEWAY secondary — cyan   */

  /* ── A BRAND HUE IS NOT AN INK ──────────────────────────────────────────
     --ow-h1/--ow-h2 are hues for painting ON A DARK FLOOR: a cyan that glows
     against #000. Used as TYPE on the light theme's panel they are unreadable
     — measured, on the real Home canvas in system light:

         "Tokyo" / "New York" / "London"   rgba(var(--ow-h2),.98)   1.30:1
         "NEXT" / "LATEST" / "WHAT'S HAPPENING"  rgb(var(--ow-h1))  2.74:1

     AA wants 4.5:1 for text this size. 1.3:1 is not a near-miss, it is pale
     blue on pale lavender — the city name, which the founder said is the one
     line that must always be visible, was the least visible thing on screen.

     The hue cannot simply be darkened by a fixed amount, because it is not a
     constant: oneway-popit.js tweens --ow-h1/--ow-h2 to each Center's own
     resolved_hue (this canvas was running 160,92,255, not the :root violet).
     A mix ratio tuned for cyan turns violet to near-black, and one tuned for
     violet leaves cyan illegible — luminance varies enormously across hues.

     So clamp PERCEPTUAL LIGHTNESS and keep hue and chroma. Every hue darkens
     exactly as much as it needs and no more. Measured against the two light
     grounds actually sampled on screen — the panel rgb(214,212,230) and the
     tile rgb(236,234,243) — at min(l,.44):

         cyan  rgb(0,97,123)   4.81 / 5.88      violet rgb(97,0,217)   5.74 / 7.02
         green rgb(0,108,31)   4.56 / 5.57      warm   rgb(134,57,0)   5.55 / 6.78
         pink  rgb(156,0,105)  5.51 / 6.74      tweened rgb(105,10,188) 6.08 / 7.44

     .48 was tried first and fails green (3.82) and cyan (4.07). .44 is the
     loosest clamp that clears AA for every hue, so it is the one that keeps
     the most colour while still being readable.

     On dark, the hue is already the ink and passes as-is — no clamp there. */
  --ow-h1-ink:rgb(var(--ow-h1));
  --ow-h2-ink:rgb(var(--ow-h2));
}

/* ── THE FOUR THEME STATES, WRITTEN OUT ─────────────────────────────────
   First attempt put the system-light selector — html:not([data-theme="dark"])
   :not([data-theme="light"]) — OUTSIDE any media query, on the reasoning that
   it "means no explicit theme, so system". It does mean that, and it matches
   on system DARK just as happily. At (0,2,1) it also outranked the
   prefers-color-scheme:dark rule written underneath to undo it, so ordering
   could not save it either: anyone on system dark with no theme attribute
   would have got the darkened light accent on a black floor.

   It measured clean because the test set data-theme="dark" explicitly, which
   is the one dark path the bug did not touch. Two dark states exist and only
   one of them was exercised.

   So: every scope is stated, and each is guarded by the media query it needs.
   Light clamps a lightness CEILING (the hue must come down to be read on a
   pale panel); dark clamps a FLOOR (it must come up off a dark one). Same
   token, same idea, opposite direction.

   Dark's floor is .72, measured against the real dark panel rgb(31,37,63):
   at .70 the brand violet still reads 4.44, a hair under AA; at .72 it clears.
   Unclamped it was 3.94. */

/* light — fallback for engines without relative colour syntax. A single ratio
   over-darkens violet to keep cyan legible; that is the price of not being
   able to ask a colour how light it is. */
html[data-theme="light"][data-theme="light"]{
  --ow-h1-ink:color-mix(in srgb,rgb(var(--ow-h1)) 34%,#14131f);
  --ow-h2-ink:color-mix(in srgb,rgb(var(--ow-h2)) 34%,#14131f);
}
@media (prefers-color-scheme:light){
  html:not([data-theme="dark"]):not([data-theme="light"]){
    --ow-h1-ink:color-mix(in srgb,rgb(var(--ow-h1)) 34%,#14131f);
    --ow-h2-ink:color-mix(in srgb,rgb(var(--ow-h2)) 34%,#14131f);
  }
}
@supports (color:oklch(from red min(l,.5) c h)){
  html[data-theme="light"][data-theme="light"]{
    --ow-h1-ink:oklch(from rgb(var(--ow-h1)) min(l,.44) c h);
    --ow-h2-ink:oklch(from rgb(var(--ow-h2)) min(l,.44) c h);
  }
  @media (prefers-color-scheme:light){
    html:not([data-theme="dark"]):not([data-theme="light"]){
      --ow-h1-ink:oklch(from rgb(var(--ow-h1)) min(l,.44) c h);
      --ow-h2-ink:oklch(from rgb(var(--ow-h2)) min(l,.44) c h);
    }
  }
  /* doubled to weigh (0,2,1), the same as the light twins above — the file's
     own convention, stated in the theme note at the top */
  html[data-theme="dark"][data-theme="dark"]{
    --ow-h1-ink:oklch(from rgb(var(--ow-h1)) max(l,.72) c h);
    --ow-h2-ink:oklch(from rgb(var(--ow-h2)) max(l,.72) c h);
  }
  @media (prefers-color-scheme:dark){
    html:not([data-theme="light"]):not([data-theme="light"]){
      --ow-h1-ink:oklch(from rgb(var(--ow-h1)) max(l,.72) c h);
      --ow-h2-ink:oklch(from rgb(var(--ow-h2)) max(l,.72) c h);
    }
  }
}
:root{

  /* ── THE LIKED STATE IS RED, AND IT IS ONE VALUE ────────────────────────
     ★ FOUNDER, 2026-08-29: *"The active heart should be clearly, unmistakably
       red. Not muted pink. Not barely distinguishable from the inactive
       state. Use a strong saturated red for the active state while preserving
       the existing animation."*

     It was `#ff3b5c` — 100% red but 36% blue, which is the pink he named — and
     it was written as a FALLBACK on four properties with no token behind it,
     so a fifth surface would have got a fifth colour. One token, defined here,
     used by the Popit, the rail, the Post viewer and the shared viewer, so
     "liked" cannot look like two different things on two screens.

     The colour is presentation. The liked state itself is canonical backend
     state and this changes nothing about it. */
  --ow-like:#ff0f2d;           /* strong saturated red — the active like */
  --ow-like-rgb:255,15,45;

  /* THREE VOICES, NEVER MORE (§1 of the language) */
  /* ── SATOSHI IS THE DISPLAY FACE, AND IT IS LOADED ────────────────────
     ★ I BROKE THIS TODAY AND AM RECORDING IT HERE RATHER THAN QUIETLY
       PUTTING IT BACK.

     I measured the App's webfonts with `grep 'family='` — which only ever sees
     GOOGLE Fonts — concluded that "the only webfont this App fetches is
     Outfit", decided Satoshi was a dead reference, and replaced it with Space
     Grotesk. Every heading in the product changed face on that reasoning.

     Satoshi is fetched from FONTSHARE, a different CDN, and has been since
     commit 28dd763: `oneway-app.html` carries
     `api.fontshare.com/v2/css?f[]=satoshi@700,800,900`. `oneway-os.html`
     carries it too, and `index.html` calls it in its own words "the canonical
     DISPLAY face (wordmark, headlines, Center names, key numbers)".

     A PARTIAL MEASUREMENT PRESENTED AS A RESULT — this repo's oldest recorded
     mistake, and I made it about the brand's own typeface. `grep family=`
     answers "which Google families", never "which fonts load". The question
     is what the document actually fetches, and the instrument for it is the
     network panel or every `<link rel=stylesheet>` in the shell.

     Space Grotesk stays LOADED and stays offered as a wordmark face a person
     can choose for their own name — that part was additive and is unaffected.
     It is not the platform's display face.

     ★ AND THEN IT WAS — BY THE LANDING PAGE, NOT BY ME. index.html (79ad6a66,
       2026-09-14, founder/390 "more minimal and futuristic font wise") put
       Space Grotesk in front of Satoshi in ITS display stack and wrote "the
       App and OS tokens follow once he does". FOUNDER/445, 2026-09-20: *"no
       more non actual font have same fonts as our landing page."* That is
       the word. This stack is now index.html's `--font-display`, verbatim;
       the landing page is the source and this token copies it. Satoshi is
       still loaded and still second. (S16.) */
  /* ★ FOUNDER/461, 2026-09-20: *"our font just still istn there … used
     across the whole platform."* One face for every voice — the way
     Instagram Sans is Instagram and Chirp is X — and it is self-hosted
     (frontend/shared/oneway-fonts.css, /assets/fonts), so it is there.
     The three voices remain three TOKENS (size, weight and tracking still
     differ by voice) but they name one family. */
  --ow-font-display:'ONEWAY Sans',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
  --ow-font:'ONEWAY Sans',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
  /* ── THE THIRD VOICE IS NO LONGER A HAIRLINE ──────────────────────────
     ★ FOUNDER, 2026-08-26: *"start ditching that sub font that we are using
       that is thin. It doesn't really fit the platform."*

     It was Poiret One — an art-deco display face with ONE weight, 400, used on
     twenty-eight eyebrows and small-caps labels that all ask for `font-weight:
     600`. A face with no bold cannot answer that, so every one of those labels
     was a SYNTHESISED bold: the browser smearing a hairline sideways. That is
     why it read as thin and slightly wrong rather than as deliberate.

     The third voice still exists — small, tracked, uppercase is a real
     register and §16.4 reserves it — but it is now the platform's own family
     carrying it, which has genuine weights. One token, so all twenty-eight
     sites move together and none can be missed. */
  --ow-font-atmos:'ONEWAY Sans',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;

  /* motion */
  --ow-ease:cubic-bezier(.4,0,.2,1);
  --ow-spring:cubic-bezier(.34,1.56,.64,1);
  --ow-settle:cubic-bezier(.16,.84,.28,1);

  /* the sheet's safe-area-aware gutter */
  --ow-gut:max(16px,env(safe-area-inset-left));
}
/* THE WINDOWS OVERRIDE IS RETIRED WITH THE FACE THAT NEEDED IT. It existed
   because Windows could not draw the Poiret hairline at label sizes; the
   platform family renders correctly on both, so a per-OS font swap is now a
   difference with no cause — and a second definition of the third voice that
   could drift from the first. (§16.4) */

/* Local aliases. Every Popit reads --h/--h2 from context and NEVER hardcodes
   a hue (§17). Scope a Center by setting --ow-h1/--ow-h2 on any ancestor. */
.po,.po-sheet,.po-glance{ --h:var(--ow-h1); --h2:var(--ow-h2); }

/* ══ THE BUTTON — AND IT ALREADY EXISTED ═══════════════════════════════════
   ★ FOUNDER, 2026-09-10: *"those buttons will not cut it and look ai generated
     and we have built better buttons before for the app."*

   He was right and I had not looked. I wrote a new `.ow-b` — flat fill, plain
   ring, three weights — and `.po-act` was already there, used NINETY-ONE times,
   and better in every respect: the four-corner signature, a hue-tinted
   gradient, an inset lift highlight over a hue ring, a hover that rises a pixel
   and throws a glow, and real STATES a new component did not have at all —
   `on` reading as held rather than as a call to action, `working` un-pressable
   so a double tap cannot fire twice, `failed` carrying an amber ring instead of
   going quiet. It even has a room scale. My version was a worse copy of a
   finished thing, which is the "never a second implementation" law broken by
   the person who keeps citing it.

   SO `.ow-b` IS DELETED — it had zero call sites — and the only genuinely
   missing piece is added to the button that exists.

   WHAT WAS MISSING: SIMPLE MODE. `.po-act` has no `data-simple` variant, and
   the founder asked for one — *"more rounded and, like, modern Twitter and
   Instagram buttons."* The radius becomes a token so simple mode can swap it,
   and simple mode also drops the gradient, the inset lift and the glow for a
   flat fill. That is the whole point of simple mode: the familiar thing, not
   the ONEWAY thing.

   AND THE TWO STRAGGLERS JOIN IT. `.ow-cv-add` (Add, Arrange) was a uniform
   999px pill and the Feed/Scroll toggle was `11px 9px 12px 10px` — a third and
   fourth shape on the same screen as `.po-act`. They read the token now, so
   Home stops speaking three dialects. */
/* ── ROUNDED, BECAUSE A CONTROL IS NOT A POSTER ────────────────────────────
   ★ FOUNDER, 2026-09-10: *"buttons need redesign anyways to more tradional
     rouded buttons one poeple would want to stare at every day."*

   The four-corner signature is right for a POPIT — a card you look at once and
   remember. It is wrong for a button you press forty times a day: at 36px the
   asymmetry stops reading as craft and starts reading as a rendering error,
   and it is the thing on screen that ages fastest. The mark belongs on the
   object, not on the control that acts on it.

   14px on a 36px control is the ratio Instagram and Twitter both settled on
   independently, which is the strongest argument available that it is simply
   what a button looks like now. Simple mode goes further to a full pill.

   THE SIGNATURE IS NOT DELETED — `.po-card`, `.po-glance` and the Popit
   silhouette keep their own radii. Only the CONTROL is calmed. */
:root{ --ow-btn-r:14px; }
html[data-simple="1"]{ --ow-btn-r:999px; }

html[data-simple="1"] .po-act{
  background:rgba(var(--h,var(--ow-h1)),.16);
  box-shadow:none;border:0}
html[data-simple="1"] .po-act[data-act="on"]{
  background:rgb(var(--h,var(--ow-h1)));color:#07070c;box-shadow:none}
@media (hover:hover) and (pointer:fine){
  html[data-simple="1"] .po-act:hover:not(:disabled){
    transform:none;background:rgba(var(--h,var(--ow-h1)),.24);box-shadow:none}
}
.ow-cv-add{border-radius:var(--ow-btn-r) !important}
.ow-feedswitch button{border-radius:var(--ow-btn-r)}




/* ═══ 2 · THE WORLD ════════════════════════════════════════════════════════
   Atmosphere is structural — no scene is ever flat black (§4). Four fixed
   layers, cheapest first. Tiers are assigned by measured capability in
   oneway-popit.js (data-ow-tier), never by platform sniffing:
     tier 2  full     — + canvas starfield (desktop / capable)
     tier 1  standard — + CSS starfield    (phones, tablets: DEFAULT)
     tier 0  reduced  — static; no drift   (reduced-motion / low-end)
   Tier 1 is the important one: mobile used to get nothing here. A CSS
   starfield is two composited layers moved by transform — effectively free —
   so phones now get a real world instead of an empty one. */
.ow-world{position:fixed;inset:0;z-index:-10;pointer-events:none;overflow:hidden;contain:strict}
/* ── PLAIN: THE WORLD PUT AWAY ────────────────────────────────────────────
   ★ FOUNDER, 2026-08-26: *"add a plain mode where it's just a black or white
     background instead of the galaxy."*
   The ground is whichever the theme already names — #000 dark, #eceaf3 light —
   so plain does not need its own colour decision and cannot disagree with the
   theme. The world is DISPLAY:NONE rather than transparent: its canvas layer
   runs an animation frame loop, and a plain world that still costs a rAF every
   frame is the expensive half of the thing the person just turned off. */
html[data-world="plain"] .ow-world{display:none}
html[data-world="plain"] body{background:var(--ow-ground)}
/* the Popit faces carry their own translucency, which over a flat ground reads
   thin — so on plain they sit on a little more of their own body */
html[data-world="plain"] .ow-cv-face,
html[data-world="plain"] .po-card[data-blended] .po-card__face{
  background-color:color-mix(in srgb, var(--ow-ground) 78%, transparent)}

/* 2.1 — the hue field. Always on, every tier, pure CSS. */
.ow-world__field{position:absolute;inset:0;background-color:var(--ow-ground);
  background-image:
    radial-gradient(115% 75% at 16% 4%,  rgba(var(--ow-h1),.09), transparent 56%),
    radial-gradient(115% 80% at 86% 20%, rgba(var(--ow-h2),.08), transparent 56%),
    radial-gradient(140% 95% at 52% 114%,rgba(var(--ow-h1),.07), transparent 60%);
}

/* 2.2 — the world wash. THIS is what the hue tween drives, so entering a
   Center re-colours the world itself (§17, §2 of the language). */
.ow-world__wash{position:absolute;inset:0;
  background:
    radial-gradient(60% 50% at 12% 10%, rgba(var(--ow-h1),.10), transparent 70%),
    radial-gradient(55% 52% at 88% 84%, rgba(var(--ow-h2),.09), transparent 72%);
  transition:background 260ms linear;
}

/* 2.3 — nebulae + ghost outlines. One element, six pools; the old build
   injected 11 divs at random positions. Drift is transform-only. */
.ow-world__neb{position:absolute;inset:-12%;
  background:
    radial-gradient(circle 300px at 12% 22%, rgba(var(--ow-h1),.075), transparent 68%),
    radial-gradient(circle 260px at 78% 12%, rgba(var(--ow-h2),.065), transparent 68%),
    radial-gradient(circle 340px at 62% 74%, rgba(var(--ow-h1),.06),  transparent 70%),
    radial-gradient(circle 220px at 26% 82%, rgba(var(--ow-h2),.05),  transparent 68%),
    radial-gradient(circle 280px at 92% 58%, rgba(var(--ow-h1),.045), transparent 70%),
    radial-gradient(circle 200px at 46% 40%, rgba(var(--ow-h2),.04),  transparent 66%);
  will-change:transform;
  animation:ow-neb 92s ease-in-out infinite alternate;
}
@keyframes ow-neb{ from{transform:translate3d(0,0,0) scale(1)} to{transform:translate3d(-2.5%,1.8%,0) scale(1.06)} }

/* 2.4 — THE CSS STARFIELD (tier ≥1). Two tiled layers at different scales
   and speeds: parallax depth from two composited transforms. No canvas, no
   rAF, no JS. This is what gives phones a real sky. */
.ow-world__stars{position:absolute;inset:-25%;opacity:0}
[data-ow-tier="1"] .ow-world__stars,
[data-ow-tier="2"] .ow-world__stars{opacity:var(--ow-stars)}
.ow-world__stars::before,
.ow-world__stars::after{content:"";position:absolute;inset:0;
  background-repeat:repeat;will-change:transform}
/* A TILE THAT REPEATS EVERY 260px IS A WALLPAPER, AND THE EYE FINDS IT.
   Twelve stars over 260px and seven over 420px meant the SAME twelve stars
   redrawn ten times across a laptop, in lockstep with a second layer whose
   tile shared a factor with the first — which is exactly the "duplicates of
   each other" that makes a sky read as cheap (founder, 2026-08-26).

   Now 64 stars over 980px and 26 over 1490px, drawn with a minimum-distance
   rejection so there is neither a clump nor a grid in them. A 2560px screen
   sees the far tile 2.6 times instead of ten. The keyframes travel exactly
   one tile, so the loop is seamless rather than a jump. */
/* far field — small, dim, slow */
.ow-world__stars::before{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='980' height='980'%3E%3Cg fill='%23fff'%3E%3Ccircle cx='443' cy='549' r='0.91' opacity='0.41'/%3E%3Ccircle cx='181' cy='502' r='0.76' opacity='0.51'/%3E%3Ccircle cx='92' cy='297' r='0.50' opacity='0.52'/%3E%3Ccircle cx='680' cy='41' r='0.94' opacity='0.57'/%3E%3Ccircle cx='641' cy='603' r='0.53' opacity='0.26'/%3E%3Ccircle cx='518' cy='58' r='0.55' opacity='0.34'/%3E%3Ccircle cx='29' cy='455' r='0.67' opacity='0.53'/%3E%3Ccircle cx='509' cy='627' r='0.70' opacity='0.47'/%3E%3Ccircle cx='448' cy='273' r='0.95' opacity='0.58'/%3E%3Ccircle cx='823' cy='694' r='0.61' opacity='0.33'/%3E%3Ccircle cx='283' cy='69' r='0.83' opacity='0.39'/%3E%3Ccircle cx='830' cy='379' r='0.93' opacity='0.53'/%3E%3Ccircle cx='1' cy='206' r='0.91' opacity='0.41'/%3E%3Ccircle cx='961' cy='389' r='0.49' opacity='0.46'/%3E%3Ccircle cx='763' cy='264' r='0.49' opacity='0.37'/%3E%3Ccircle cx='945' cy='743' r='0.51' opacity='0.34'/%3E%3Ccircle cx='99' cy='59' r='0.85' opacity='0.32'/%3E%3Ccircle cx='548' cy='438' r='0.55' opacity='0.49'/%3E%3Ccircle cx='128' cy='631' r='0.51' opacity='0.39'/%3E%3Ccircle cx='209' cy='264' r='0.94' opacity='0.52'/%3E%3Ccircle cx='298' cy='867' r='0.56' opacity='0.39'/%3E%3Ccircle cx='98' cy='970' r='0.56' opacity='0.34'/%3E%3Ccircle cx='238' cy='589' r='0.64' opacity='0.41'/%3E%3Ccircle cx='940' cy='474' r='0.74' opacity='0.54'/%3E%3Ccircle cx='179' cy='151' r='0.90' opacity='0.52'/%3E%3Ccircle cx='725' cy='922' r='0.55' opacity='0.56'/%3E%3Ccircle cx='865' cy='591' r='0.66' opacity='0.29'/%3E%3Ccircle cx='234' cy='690' r='0.58' opacity='0.52'/%3E%3Ccircle cx='585' cy='288' r='0.54' opacity='0.49'/%3E%3Ccircle cx='548' cy='835' r='0.76' opacity='0.35'/%3E%3Ccircle cx='899' cy='200' r='0.46' opacity='0.35'/%3E%3Ccircle cx='437' cy='59' r='0.54' opacity='0.38'/%3E%3Ccircle cx='561' cy='129' r='0.63' opacity='0.55'/%3E%3Ccircle cx='961' cy='644' r='0.80' opacity='0.45'/%3E%3Ccircle cx='18' cy='892' r='0.80' opacity='0.57'/%3E%3Ccircle cx='21' cy='623' r='0.69' opacity='0.49'/%3E%3Ccircle cx='313' cy='979' r='0.49' opacity='0.43'/%3E%3Ccircle cx='722' cy='690' r='0.85' opacity='0.55'/%3E%3Ccircle cx='345' cy='671' r='0.90' opacity='0.54'/%3E%3Ccircle cx='409' cy='775' r='0.88' opacity='0.44'/%3E%3Ccircle cx='612' cy='375' r='0.74' opacity='0.45'/%3E%3Ccircle cx='973' cy='862' r='0.81' opacity='0.38'/%3E%3Ccircle cx='720' cy='569' r='0.67' opacity='0.53'/%3E%3Ccircle cx='82' cy='735' r='0.46' opacity='0.45'/%3E%3Ccircle cx='684' cy='487' r='0.76' opacity='0.55'/%3E%3Ccircle cx='433' cy='874' r='0.61' opacity='0.47'/%3E%3Ccircle cx='195' cy='422' r='0.85' opacity='0.55'/%3E%3Ccircle cx='820' cy='832' r='0.81' opacity='0.56'/%3E%3Ccircle cx='271' cy='166' r='0.68' opacity='0.35'/%3E%3Ccircle cx='855' cy='41' r='0.80' opacity='0.44'/%3E%3Ccircle cx='303' cy='776' r='0.46' opacity='0.30'/%3E%3Ccircle cx='671' cy='195' r='0.69' opacity='0.32'/%3E%3Ccircle cx='267' cy='339' r='0.80' opacity='0.43'/%3E%3Ccircle cx='602' cy='741' r='0.65' opacity='0.51'/%3E%3Ccircle cx='613' cy='892' r='0.64' opacity='0.44'/%3E%3Ccircle cx='707' cy='802' r='0.77' opacity='0.49'/%3E%3Ccircle cx='209' cy='882' r='0.94' opacity='0.57'/%3E%3Ccircle cx='784' cy='169' r='0.62' opacity='0.51'/%3E%3Ccircle cx='506' cy='709' r='0.87' opacity='0.48'/%3E%3Ccircle cx='930' cy='84' r='0.56' opacity='0.43'/%3E%3Ccircle cx='593' cy='27' r='0.93' opacity='0.43'/%3E%3Ccircle cx='264' cy='464' r='0.51' opacity='0.40'/%3E%3Ccircle cx='764' cy='22' r='0.55' opacity='0.33'/%3E%3Ccircle cx='673' cy='316' r='0.63' opacity='0.46'/%3E%3C/g%3E%3C/svg%3E");
  background-size:980px 980px;
  animation:ow-star-far 420s linear infinite;
}
/* near field — larger, brighter, faster (the parallax) */
.ow-world__stars::after{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1490' height='1490'%3E%3Cg fill='%23fff'%3E%3Ccircle cx='817' cy='515' r='1.36' opacity='0.52'/%3E%3Ccircle cx='619' cy='1451' r='0.91' opacity='0.58'/%3E%3Ccircle cx='335' cy='521' r='1.45' opacity='0.54'/%3E%3Ccircle cx='1088' cy='666' r='0.95' opacity='0.58'/%3E%3Ccircle cx='608' cy='1181' r='1.19' opacity='0.49'/%3E%3Ccircle cx='1004' cy='445' r='1.16' opacity='0.50'/%3E%3Ccircle cx='1128' cy='875' r='1.24' opacity='0.56'/%3E%3Ccircle cx='921' cy='1158' r='0.91' opacity='0.76'/%3E%3Ccircle cx='547' cy='207' r='0.94' opacity='0.54'/%3E%3Ccircle cx='103' cy='593' r='1.10' opacity='0.66'/%3E%3Ccircle cx='1304' cy='1042' r='1.43' opacity='0.68'/%3E%3Ccircle cx='1439' cy='306' r='1.02' opacity='0.77'/%3E%3Ccircle cx='33' cy='1376' r='1.40' opacity='0.47'/%3E%3Ccircle cx='225' cy='1365' r='1.43' opacity='0.73'/%3E%3Ccircle cx='459' cy='11' r='1.23' opacity='0.74'/%3E%3Ccircle cx='1414' cy='767' r='0.86' opacity='0.48'/%3E%3Ccircle cx='669' cy='344' r='0.87' opacity='0.49'/%3E%3Ccircle cx='168' cy='247' r='0.87' opacity='0.70'/%3E%3Ccircle cx='439' cy='1321' r='1.02' opacity='0.66'/%3E%3Ccircle cx='702' cy='963' r='1.45' opacity='0.54'/%3E%3Ccircle cx='1401' cy='1357' r='1.12' opacity='0.50'/%3E%3Ccircle cx='259' cy='966' r='1.24' opacity='0.48'/%3E%3Ccircle cx='47' cy='951' r='0.88' opacity='0.62'/%3E%3Ccircle cx='1378' cy='13' r='1.07' opacity='0.71'/%3E%3Ccircle cx='341' cy='328' r='1.23' opacity='0.51'/%3E%3Ccircle cx='445' cy='982' r='1.29' opacity='0.69'/%3E%3C/g%3E%3C/svg%3E");
  background-size:1490px 1490px;
  animation:ow-star-near 300s linear infinite;
}
@keyframes ow-star-far { to{transform:translate3d(-980px,-980px,0)} }
@keyframes ow-star-near{ to{transform:translate3d(-1490px,1490px,0)} }

/* 2.5 — the canvas layer (tier 2 only; oneway-popit.js mounts it). */
.ow-world__canvas{position:absolute;inset:0;width:100%;height:100%;display:none}
[data-ow-tier="2"] .ow-world__canvas{display:block}

/* tier 0 — the world goes still. Nothing is removed; motion stops. */
[data-ow-tier="0"] .ow-world__neb,
[data-ow-tier="0"] .ow-world__stars::before,
[data-ow-tier="0"] .ow-world__stars::after{animation:none}


/* ═══ 3 · THE RESTING POPIT — the bead ═════════════════════════════════════
   A gummy, hand-cut, internally-lit object that HANGS in the world. It is
   never a rectangle in a grid (§15, §1). Structure:

     .po            layout + bob (transform only, compositor)
       ::before     the light spill into the world  (opacity/scale, compositor)
       ::after      the selection ring              (opacity, compositor)
       .po-face     the material                    (static paint, never redrawn)
         ::before   the stitched seam
         ::after    the woven grain
       .po-ic / .po-lb / .po-sig   content, above the material

   Every animated property is transform or opacity. Every painted property is
   static. That is the whole performance story. */

/* THE SILHOUETTE IS LINEAR (founder, 2026-07-28). The proving ground ran radii
   of ~50px on a 124px box — 40% of the width — so the corners consumed the
   sides and the object read as a bean. A Popit is a hand-cut TILE: you must be
   able to see its straight runs. The radius is now a token at roughly a sixth
   of the short edge, still 8-value asymmetric so it stays hand-cut and no two
   are identical — but the edges are edges. This is also what makes a widget
   grid possible: beans do not tile, tiles do. */
.po{
  --sc:1; --tilt:0deg; --po-r:22px;
  position:relative;flex:0 0 auto;
  width:124px;height:174px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;
  padding:18px 14px;border:0;background:none;cursor:pointer;color:#fff;
  font-family:var(--ow-font);
  border-radius:var(--po-r);
  transform-origin:50% -42px;      /* hangs from an anchor ABOVE itself — so
                                      any float reads as a suspended object,
                                      not a div translating */
  transform:translate3d(0,0,0) rotate(var(--r1,0deg)) scale(var(--sc));
  contain:layout paint style;      /* invalidation cannot escape the bead */
  -webkit-tap-highlight-color:transparent;
  transition:transform .3s var(--ow-spring);
}

/* 3.1 — the light spill. Was a 96px box-shadow on the body, which meant every
   hover re-painted the whole bead. Now its own layer: opacity + scale only. */
.po::before{
  content:"";position:absolute;inset:-34%;z-index:0;pointer-events:none;
  border-radius:50%;
  background:radial-gradient(closest-side, rgba(var(--h),.62), rgba(var(--h),.16) 58%, transparent 78%);
  opacity:.82;
  transform:translate3d(0,0,0) scale(1);
  transition:opacity .28s var(--ow-ease), transform .28s var(--ow-ease);
}

/* 3.2 — the selection ring (LBP's non-negotiable signifier, §11). Opacity
   only, so focus/hover never repaints the material. */
.po::after{
  content:"";position:absolute;inset:-3px;z-index:3;pointer-events:none;
  border-radius:inherit;
  box-shadow:0 0 0 3px rgba(255,255,255,.9), 0 0 0 7px rgba(var(--h),.5);
  opacity:0;
  transition:opacity .2s var(--ow-ease);
}

/* 3.3 — THE MATERIAL. Seven gradient layers + the inset lighting, carried
   over value-for-value from the proving ground. Painted once, cached in the
   layer, never re-painted. No backdrop-filter: on a saturated candy body a
   3px blur contributed almost nothing and cost a per-frame re-sample. */
.po-face{
  position:absolute;inset:0;z-index:1;pointer-events:none;
  border-radius:inherit;
  /* ── ONE POPIT MATERIAL, ON EVERY SURFACE ──────────────────────────────
     ★ FOUNDER, 2026-08-26: *"make popits on profile and popits on home the
       same."*

     They were two materials. Home's Popits are drawn by the canvas and profile
     Popits by this tile, and this tile still wore the CANDY pass — wet
     highlight, second catchlight, glossy top, a .95-alpha fill and a 3px candy
     edge. That is the LittleBigPlanet direction the founder ruled out, kept
     alive on the one surface that had not been touched since. So the same
     person's Popits looked like two different products depending on which
     screen they were on.

     This is now the blended feed card's declaration, which is what the Home
     canvas was moved to earlier today — the same three layers, the same
     tokens, the same rim. Not "similar": the identical recipe, so the two
     cannot drift again without both changing.

     Light, not glass: the hue lights the information, it does not fill a
     rectangle, and there is no specular anywhere on it. */
  background:
    radial-gradient(130% 80% at 18% -6%,  rgba(var(--h),.46),  transparent 64%),
    radial-gradient(120% 90% at 92% 108%, rgba(var(--h2),.30), transparent 60%),
    linear-gradient(158deg, rgba(var(--h),.18), rgba(var(--h3,var(--ow-h3,var(--h2))),.12) 46%,
                            rgba(var(--h2),.10) 70%, var(--ow-tile-base));
  box-shadow:
    inset 0 2px 1px var(--ow-lift),
    0 0 34px -10px rgba(var(--h),.50),
    0 18px 44px -16px var(--ow-drop);
}

/* ══ THE LINES AROUND A POPIT ARE GONE ══════════════════════════════════════
   ★ FOUNDER/437, 2026-09-19: *"we are gonna be rulling to remove the lines
     around popits for mainstreamabilty."*

   Two kinds of line ran round every Popit: the 1.5px hue rim inside the
   edge, and the hand-cut dashed seam a few pixels in. Both are removed from
   every recipe in this file — the face, the card, the glance, the sheet's
   plane and seam, the plate, the Center page's event row — and nothing else
   moves: the top lift (a specular, not a ring), the hue washes, the grain,
   the weight and the glow outside all stay. A Popit is now light on cloth
   with no frame, which is how a post reads on every mainstream app. The
   seam rules below are kept as `display:none` rather than deleted, so the
   `--seam` wander and `--po-seam` inset every surface still sets have
   somewhere to land without erroring. */

/* 3.4 — the stitched seam. Was a dashed ring displaced by an SVG turbulence
   filter (filter:url(#poSeam) + mix-blend-mode:screen) — the single most
   expensive property in the old bead and the first thing the safe floor
   stripped, so mobile never saw it at all.

   Reconstructed: a dashed ring that INHERITS the hand-cut radius (so it
   follows the silhouette exactly, at any size, resolution-independent) and
   sits very slightly off-true, rotated a fraction of a degree per variant.
   A hand-sewn thread drifts relative to the edge — which is what turbulence
   was approximating. Zero filter, zero blend, ships everywhere. */
.po-face::before{
  content:"";position:absolute;inset:7px;border-radius:inherit;pointer-events:none;
  border:1.5px dashed rgba(255,255,255,.46);
  transform:rotate(var(--seam,.5deg));
  display:none;                           /* founder/437: no lines around a Popit */
}

/* 3.5 — the woven grain. The noise SVG is rasterised once by the browser and
   cached as an image (not a live filter), so it is safe. mix-blend-mode:
   overlay is dropped; opacity carries it. */
.po-face::after{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  background:
    repeating-linear-gradient(46deg,  rgba(255,255,255,.05) 0 1px, transparent 1px 4px),
    repeating-linear-gradient(-44deg, rgba(0,0,0,.055)      0 1px, transparent 1px 4px),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='90' height='90'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
  background-size:auto,auto,120px 120px;
  opacity:.28;
}

/* 3.6 — content sits above the material */
/* NO `filter` ANYWHERE ON A BEAD — not even a cheap drop-shadow on the icon.
   The safe floor is a blunt `filter:none !important`, so any use of it means
   mobile renders differently from desktop. Parity is achieved by not depending
   on the effect: the icon's separation comes from a soft dark pool behind it
   (a painted gradient, not a filter), which is what the drop-shadow was
   simulating anyway. This is what makes the safe-floor A/B come out identical. */
.po-ic{position:relative;z-index:2;width:60px;height:60px;display:grid;place-items:center}
.po-ic::before{content:"";position:absolute;inset:-4px;border-radius:50%;
  background:radial-gradient(closest-side, rgba(0,0,0,.28), transparent 72%)}
.po-ic svg{position:relative;width:48px;height:48px;stroke:#fff;fill:none;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round}
/* Type is CONTAINED BY THE SILHOUETTE. A flex child does not shrink below its
   content width without min-width:0 — without it a long signal escapes the
   bead and lands on its neighbours, which is the clipping §18 forbids. The
   containment lives here rather than on the label so every future part of a
   bead inherits it. */
.po-lb,.po-sig{min-width:0;max-width:100%;overflow-wrap:anywhere;hyphens:auto}
.po-lb{position:relative;z-index:2;font-family:var(--ow-font-display);font-weight:650;
  font-size:13px;letter-spacing:.04em;text-align:center;line-height:1.12;color:#fff;
  text-shadow:0 1px 3px rgba(0,0,0,.45);
  transform:rotate(var(--lblrot,-1deg))}
/* THE GLANCE SIGNAL (§3 depth 1) — at most one live signal at rest. It is the
   whole of the Curiosity Law (§12): the bead teases, it never explains. Two
   lines is the ceiling; a signal that needs more is not a glance, and the
   sentence belongs inside the open Popit. */
.po-sig{position:relative;z-index:2;font-family:var(--ow-font-atmos);font-weight:600;
  font-size:12px;letter-spacing:.1em;text-align:center;
  line-height:1.28;color:rgba(255,255,255,.86);
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;
  text-shadow:0 1px 4px rgba(0,0,0,.6)}

/* 3.7 — hover / focus. Compositor only. */
@media (hover:hover) and (pointer:fine){
  .po:hover{transform-origin:50% 50%;
    transform:translate3d(0,-8px,0) scale(calc(var(--sc) * 1.06)) rotate(var(--tilt));
    animation-play-state:paused}
  .po:hover::before{opacity:1;transform:translate3d(0,0,0) scale(1.1)}
  .po:hover::after{opacity:1}
}
.po:focus-visible{outline:none}
.po:focus-visible::after{opacity:1}
/* 3.2b — WHILE IT IS OPEN, ITS PLACE STAYS LIT. §4 says an open Popit is
   "tethered to its origin — you can feel where it came from", and the FLIP
   hides the bead so the sheet can be the only copy of it. That left a HOLE in
   the arrangement, and a hole is what makes the sheet read as a dialog that
   arrived rather than as this object, opened.

   So the selection ring — already the LBP signifier for "this is the one you
   are on" (§11) — simply stays. `visibility` is inherited, so the ring takes
   it back while the bead itself remains hidden: one pseudo-element, the
   object's own hue, on the object's own box, which means it holds its place
   through any scroll or resize without a second element to keep in sync (§18). */
.po[data-po-open]{visibility:hidden}
.po[data-po-open]::after{visibility:visible;opacity:1;
  box-shadow:0 0 0 2px rgba(var(--h),.85), 0 0 0 6px rgba(var(--h),.22),
             0 0 30px -6px rgba(var(--h),.9)}
@media (prefers-reduced-motion:no-preference){
  .po[data-po-open]::after{animation:po-socket 2.6s ease-in-out infinite}
}
@keyframes po-socket{
  0%,100%{box-shadow:0 0 0 2px rgba(var(--h),.85), 0 0 0 6px rgba(var(--h),.22),
                     0 0 30px -6px rgba(var(--h),.9)}
  50%    {box-shadow:0 0 0 2px rgba(var(--h),.6),  0 0 0 9px rgba(var(--h),.12),
                     0 0 40px -6px rgba(var(--h),.7)}
}
.po:active{transform:translate3d(0,-3px,0) scale(calc(var(--sc) * .985))}

/* 3.8 — THE FLOAT. Two corrections from the proving ground (founder, 2026-07-28):

   IT IS NOT THE DEFAULT. A Popit at rest is PLACED — it sits where it was put,
   square to its neighbours. Objects on a shelf do not drift. Floating is now
   opt-in (.po-float) and belongs to the rare hero moment, never to a profile,
   a grid or a feed.

   AND WHEN IT DOES FLOAT IT IS BARELY THERE. The old amplitude was 12-18px and
   up to 9deg of lean — that is a mobile, not a resting object. It is now ~3px
   and a third of a degree: enough that the object is alive, not enough that
   you can point at the movement. If a viewer notices the animation, it is too
   much.

   Runs only while in view (.po-live via IntersectionObserver), so a feed of
   200 objects animates the few you can actually see. */
@keyframes ow-bob{
  0%  {transform:translate3d(0,0,0) rotate(var(--r1,0deg)) scale(var(--sc))}
  50% {transform:translate3d(var(--bobX,1px), calc(var(--bobY,3px)*-1),0)
                rotate(var(--r2,.2deg)) scale(var(--sc))}
  100%{transform:translate3d(0,0,0) rotate(var(--r1,0deg)) scale(var(--sc))}
}
.po.po-float.po-live{animation:ow-bob var(--bobD,7s) ease-in-out infinite;will-change:transform}
/* not floating, or off-screen: no animation, no layer promotion, no memory */
.po:not(.po-float.po-live){will-change:auto}
[data-ow-tier="0"] .po{animation:none !important;will-change:auto}

/* 3.9 — HAND-CUT VARIANTS. Six silhouettes assigned by data-v, which
   oneway-popit.js derives from a hash of the object's OWN id — so a given
   object is always the same shape (Gate 5: identical inputs, identical
   rendering) while a set never looks machine-stamped.

   The variance now lives where it survives being linear: a few px of radius
   per corner, the seam a fraction off-true, the label barely off-level. Hand-
   cut, not hand-thrown. No lean at rest — a placed object is level. */
.po[data-v="0"],
.ow-cv-t[data-v="0"] .ow-cv-face{border-radius:22px 19px 23px 20px;--seam:.4deg; --lblrot:-.5deg;--bobD:7.0s;--bobY:3px;--bobX:1px; --r2:.22deg}
.po[data-v="1"],
.ow-cv-t[data-v="1"] .ow-cv-face{border-radius:20px 23px 19px 22px;--seam:-.5deg;--lblrot:.6deg; --bobD:8.2s;--bobY:2px;--bobX:-1px;--r2:-.18deg}
.po[data-v="2"],
.ow-cv-t[data-v="2"] .ow-cv-face{border-radius:23px 20px 22px 19px;--seam:.6deg; --lblrot:-.7deg;--bobD:6.4s;--bobY:3px;--bobX:1px; --r2:.26deg}
.po[data-v="3"],
.ow-cv-t[data-v="3"] .ow-cv-face{border-radius:19px 22px 20px 23px;--seam:-.35deg;--lblrot:.4deg;--bobD:9.1s;--bobY:2px;--bobX:1px; --r2:-.24deg}
.po[data-v="4"],
.ow-cv-t[data-v="4"] .ow-cv-face{border-radius:21px 24px 21px 18px;--seam:.5deg; --lblrot:-.4deg;--bobD:7.6s;--bobY:3px;--bobX:-1px;--r2:.2deg}
.po[data-v="5"],
.ow-cv-t[data-v="5"] .ow-cv-face{border-radius:24px 18px 22px 21px;--seam:-.6deg;--lblrot:.5deg; --bobD:8.8s;--bobY:2px;--bobX:1px; --r2:-.2deg}
/* desynced phase so a set never pulses in unison */
.po[data-v="1"].po-float.po-live{animation-delay:-1.4s}
.po[data-v="2"].po-float.po-live{animation-delay:-3.3s}
.po[data-v="3"].po-float.po-live{animation-delay:-2.2s}
.po[data-v="4"].po-float.po-live{animation-delay:-4.1s}
.po[data-v="5"].po-float.po-live{animation-delay:-.9s}


/* ═══ 4 · THE GLANCE TIER ══════════════════════════════════════════════════
   The full bead is for objects you are meant to meet — a cluster, a hero, a
   perspective's few most-alive Popits. A feed, an Information tab or a search
   result is 50–200 objects, and the old system had no answer for that: the
   expensive bead was the ONLY resting form.

   .po-glance keeps the silhouette, the hue and the internal bloom — the three
   things that make it recognisable at a distance (§13 Silhouette Test) — and
   drops the seam, the grain, the spill and the bob. Roughly one-sixth the
   paint, and it still could not appear in a Stripe dashboard (Law 0). */
.po-glance{
  position:relative;flex:0 0 auto;
  display:flex;align-items:center;gap:12px;
  padding:12px 16px;border:0;cursor:pointer;color:#fff;
  font-family:var(--ow-font);text-align:left;
  border-radius:15px 13px 16px 14px;
  /* ── THE CHIP CARRIES ITS OWN GROUND ────────────────────────────────
     ★ FOUNDER, 2026-09-09: *"Profiles and Centers still need professional
       design refinement."*

     `color:#fff` above is unconditional, and the hue layers are TRANSLUCENT —
     `rgba(var(--h),.9)` composites over whatever is behind the row. In dark
     that lands dark and white text is right; in light it lands pale and the
     text disappears into it. Measured on a Center's About in light mode: white
     type on a pale green chip, with the provenance line under it effectively
     invisible.

     The fix is not to make the ink theme-aware — that would give one component
     two different looks and lose the vivid chip the dark surface is built
     around. The chip simply stops borrowing the page's ground and brings its
     own, so the hue composites over the same dark base in both themes and the
     white it was always designed for is correct in both.

     Last layer in the stack is the bottom-most: the hue still reads over it,
     the internal bloom is untouched. */
  /* ── THE ROW WEARS THE HOME POPIT'S MATERIAL ──────────────────────────
     ★ FOUNDER/400, 2026-09-14: *"I prefer the more dim holographic look the
       app has… I want the whole app and OS to move to the app home design
       for puppets. They look fantastic. And maybe just ease up on the dark
       parts a little bit for some hues."*
     ★ FOUNDER/399: *"Poppets without their glow is just an it."*

     This row was a filled gradient — rgba(hue,.9) over a near-black — a
     coloured bar with a bloom inside it. The Home Popit (.po-face, §3.3) is
     LIGHT, NOT GLASS: two hue washes lighting the information over a dark
     ground, a lift at the top edge, a 1.5px hue rim, and the hue's own glow
     outside. That is the identical recipe here, at row scale — the same
     three layers and the same rim — with the ground lifted a shade
     (rgba(14,16,30,.48) against the tile's rgba(8,9,20,.55)) because a row
     of text sits closer to the eye than a tile and its dark read heavier.
     The glow is the last layer, in the row's own hue, so a Center's row is
     lit the way its bead is. */
  background:
    radial-gradient(130% 80% at 18% -6%,  rgba(var(--h),.46),  transparent 64%),
    radial-gradient(120% 90% at 92% 108%, rgba(var(--h2),.30), transparent 60%),
    linear-gradient(158deg, rgba(var(--h),.16), rgba(var(--h3,var(--ow-h3,var(--h2))),.11) 46%,
                            rgba(var(--h2),.10) 70%, var(--ow-tile-mid)),
    linear-gradient(0deg, var(--ow-plate), var(--ow-plate));
  box-shadow:
    inset 0 2px 1px var(--ow-lift, rgba(255,255,255,.28)),
    0 0 34px -10px rgba(var(--h),.50),
    0 10px 24px -14px var(--ow-drop, rgba(0,0,0,.6));
  min-height:60px;
  -webkit-tap-highlight-color:transparent;
  transition:transform .22s var(--ow-spring), box-shadow .22s var(--ow-ease);
}
.po-glance[data-v="1"]{border-radius:13px 16px 14px 15px}
.po-glance[data-v="2"]{border-radius:16px 14px 15px 13px}
.po-glance[data-v="3"]{border-radius:14px 15px 13px 16px}
.po-glance[data-v="4"]{border-radius:15px 17px 14px 13px}
.po-glance[data-v="5"]{border-radius:17px 13px 15px 14px}
@media (hover:hover) and (pointer:fine){
  .po-glance:hover{transform:translate3d(0,-2px,0);
    box-shadow:inset 0 2px 1px rgba(255,255,255,.4),
      0 0 44px -8px rgba(var(--h),.7), 0 16px 30px -14px rgba(0,0,0,.65)}
}
.po-glance:focus-visible{outline:2px solid rgba(var(--h),.95);outline-offset:3px}
.po-glance__ic{flex:none;width:34px;height:34px;display:grid;place-items:center}
/* THE DATE MARK — Apple Calendar's idea: the day lives ON the icon, so a row
   needs no line underneath explaining when it is. Same 34px footprint as the
   glyph it replaces, so a list of dated and undated rows still lines up. */
/* the face on a row — a picture, or initials lit by the row's own hue (founder/407) */
.po-glance__face{overflow:hidden;border-radius:12px 10px 11px 9px;
  background:radial-gradient(120% 90% at 30% 10%, rgba(var(--h),.85), rgba(var(--h2),.55) 70%);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.35),0 0 14px -3px rgba(var(--h),.7)}
/* the two orientations (founder/409): a person is round, a Center a tile,
   and either may choose the other; `data-shape` is the owner's answer */
.po-glance__face[data-shape="round"]{border-radius:50%}
.po-glance__face[data-shape="rounded"]{border-radius:12px 10px 11px 9px}
.po-glance__face[data-shape="square"]{border-radius:6px}
.po-glance__face img{width:100%;height:100%;object-fit:cover;display:block}
.po-glance__face i{font-style:normal;font-family:var(--ow-font-display);font-weight:650;font-size:14px;letter-spacing:.02em;color:#fff;text-shadow:0 1px 3px rgba(0,0,0,.4)}
.po-glance__date{flex-direction:column;gap:0;border-radius:9px;
  background:rgba(255,255,255,.10);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.16);
  overflow:hidden;line-height:1}
.po-glance__mon{display:block;width:100%;text-align:center;
  font-family:var(--ow-font-atmos);font-weight:650;font-size:7.5px;
  letter-spacing:.1em;
  padding:2.5px 0 2px;background:rgba(var(--h,var(--ow-h1)),.85);color:#07070c}
.po-glance__day{display:block;width:100%;text-align:center;
  font-family:var(--ow-font-display);font-weight:650;font-size:15px;
  letter-spacing:-.01em;padding:2px 0 3px;color:#fff}
html[data-theme="light"][data-theme="light"] .po-glance__day{color:#fff}

.po-glance__ic svg{width:22px;height:22px;stroke:#fff;fill:none;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round}
.po-glance__lb{font-family:var(--ow-font-display);font-weight:600;font-size:14px;
  letter-spacing:-.01em;line-height:1.15}
.po-glance__sig{display:block;font-family:var(--ow-font);font-weight:500;
  font-size:11.5px;letter-spacing:0;text-transform:none;
  color:rgba(255,255,255,.78);margin-top:2px}

/* Long lists: skip layout+paint for rows outside the viewport entirely.
   Progressive — browsers without support simply render normally.

   THE INTRINSIC SIZE MUST MATCH THE REAL ROW — measured, never guessed. 84px
   is a row with an icon, a label and a signal; `auto` keeps the real size once
   a row has been seen. This is not a detail: with a wrong placeholder (60px)
   every reveal corrected the page height, reflowing the document mid-scroll —
   41 of 200 frames over budget. Correcting it to the measured height took that
   to 0 at steady state.

   MEASURED (1000 rows, 120Hz, fast programmatic scroll):
     initial mount   17.1ms with · 32.4ms without   → keep it
     steady scroll    0 dropped frames either way   → costs nothing
   Re-measure with popit-lab.html if the row's content ever changes height. */
.po-list{display:flex;flex-direction:column;gap:10px}
.po-list > .po-glance{content-visibility:auto;contain-intrinsic-size:auto 84px}


/* ═══ 5 · ARRANGEMENT ══════════════════════════════════════════════════════
   THE DEFAULT IS ALIGNED (founder, 2026-07-28). Popits are PLACED: they sit
   on a grid, square to each other, in rows you can read. The hand-scattered
   cluster was the proving ground's hero treatment, and treating it as the
   normal arrangement made every surface look like a mobile hanging in space.
   Scattering is now the exception and is declared, never inherited.

   5.1 — THE GRID. One column vocabulary for every surface: profiles, feeds,
   Information tabs, Center windows. Widgets claim spans of it (§5.3), so a
   1×1 and a 2×2 land on the same rhythm without either knowing about the
   other. Auto-fill means it reflows from phone to ultrawide with no
   breakpoints of its own. */
.po-grid{
  --po-col:124px;                     /* one column unit */
  --po-row:143px;                     /* one ROW unit — spans multiply it */
  --po-gap:14px;
  display:grid;
  grid-template-columns:repeat(auto-fill, minmax(var(--po-col), 1fr));
  /* THE GRID MUST BE MODULAR. With implicit rows, a 1-row widget stretches to
     whatever the tallest thing in its row happens to be — so a one-line `said`
     sitting beside a 2×2 gallery became a tall empty slab. Fixed row units mean
     a span is a real multiple and every widget is the size it declared. */
  grid-auto-rows:var(--po-row);
  gap:var(--po-gap);
  align-items:stretch;
  margin:0 auto;
}
.po-grid > .po{width:auto;height:auto;min-height:0}

/* 5.2 — a plain aligned row, for a small declared set (App · OS; a pair of
   perspectives). Equal weight — never hero and sidekick. */
.po-row{display:flex;flex-wrap:wrap;justify-content:center;
  gap:clamp(12px,2vw,22px);margin:0 auto}

/* 5.3 — WIDGET SPANS. A widget is a Popit that claims more of the grid. The
   sizes are deliberately few: more sizes means a profile that never resolves
   into a composition. Height follows the span so the grid stays on rhythm. */
.po--1x1{grid-column:span 1}
.po--2x1{grid-column:span 2}
.po--2x2{grid-column:span 2;grid-row:span 2}
.po--3x1{grid-column:span 3}
.po--4x1{grid-column:span 4}
.po--4x2{grid-column:span 4;grid-row:span 2}
/* wide widgets read left-aligned — centred content in a wide tile looks lost */
.po--2x1,.po--3x1,.po--4x1,.po--4x2{align-items:flex-start;text-align:left}
.po--2x1 .po-lb,.po--3x1 .po-lb,.po--4x1 .po-lb,.po--4x2 .po-lb,
.po--2x1 .po-sig,.po--3x1 .po-sig,.po--4x1 .po-sig,.po--4x2 .po-sig{text-align:left}
/* a widget never leans, at any size */
.po--2x1,.po--2x2,.po--3x1,.po--4x1,.po--4x2{--lblrot:0deg}

/* PHONES: THE GRID IS TWO COLUMNS — DECLARED, BECAUSE IT WAS NEVER MEASURED.
   This block said "the grid collapses to two columns" and it did not. `auto-fill`
   with `minmax(96px,1fr)` inside a 339px column yields THREE tracks of 106px, so
   every wide widget — and on a profile they are nearly all `2x1` — filled two
   thirds and left a dead column down the right of the screen. Founder,
   2026-08-17: *"People still don't have widgets on their profiles"* and *"this
   looks like crap."* The widgets were there; a third of the phone was empty
   beside them, which reads as broken rather than as a layout.

   The clamp below is the tell: `.po--3x1/4x1/4x2 → span 2` was written for a
   two-column grid. Half this block already assumed the count the other half
   failed to produce. Stating the count directly is the whole fix — `auto-fill`
   is right everywhere it can be measured against real width, and wrong here,
   where the answer was asserted in a comment instead. */
@media (max-width:560px){
  .po-grid{--po-col:96px;--po-row:112px;--po-gap:10px;
    grid-template-columns:repeat(2,minmax(0,1fr))}
  .po-ic{width:44px;height:44px} .po-ic svg{width:34px;height:34px}
  .po-lb{font-size:11px} .po-sig{font-size:8.5px;letter-spacing:.09em}
  /* nothing wider than the screen: every span above two clamps to the full row */
  .po--2x1,.po--3x1,.po--4x1,.po--4x2{grid-column:span 2}
}

/* 5.4 — THE CLUSTER (the exception, declared). One elevated object, the
   others lower. Reserved for a hero moment — a landing, an arrival. It is
   never the arrangement for a profile, a feed or an Information tab. */
.po-cluster{display:flex;align-items:flex-start;justify-content:center;
  gap:clamp(10px,1.8vw,26px);flex-wrap:nowrap;
  padding:40px 0 32px;margin:0 auto;max-width:700px}
.po-cluster > .po:nth-child(1){margin-top:40px;--sc:.9;z-index:1}
.po-cluster > .po:nth-child(2){margin-top:-14px;width:140px;height:196px;--sc:1.08;z-index:3}
.po-cluster > .po:nth-child(3){margin-top:36px;--sc:.94;z-index:2}
.po-cluster--pair > .po{width:122px;height:170px;margin-top:0;--sc:1}
@media (max-width:560px){
  .po-cluster{gap:8px;padding:28px 0 22px}
  .po-cluster > .po{width:92px;height:132px}
  .po-cluster > .po:nth-child(1){margin-top:26px}
  .po-cluster > .po:nth-child(2){width:104px;height:150px;margin-top:-8px}
  .po-cluster > .po:nth-child(3){margin-top:24px}
}


/* ═══ 6 · THE OPEN POPIT — THE PROJECTION ══════════════════════════════════
   (founder, 2026-08-16: "I want ONEWAY to feel like a digital pane of the
   future and the Popits should be our hologram technology that makes the whole
   platform feel alive, futuristic, spacious and seamless to use. It is the
   make or break moment.")

   THE MODEL THAT FOLLOWS FROM THAT SENTENCE, and everything below is one
   consequence of it:

       THE BEAD IS THE EMITTER.  THE OPEN POPIT IS THE PROJECTION.

   It is not a new language — §22 already names "the holographic interaction
   language" among the things ONEWAY has and must be implemented rather than
   substituted, and §3/§7 already say translucent, world-visible-through,
   tethered to origin. What was built instead was a panel on a curtain: an
   8px-blurred scrim that took the world away, a 440px card at screen centre,
   and no visible relationship to the object it came from. Nothing about that
   is projected. It is a dialog.

   FOUR THINGS MAKE IT A PROJECTION RATHER THAN A PANEL, and each is a named
   word from the founder's sentence:

   SPACIOUS   — it claims real space and then leaves most of it empty. A
                hologram is not a dense card; the air around the content is
                what makes it read as suspended rather than printed.
   SEAMLESS   — the world does not go away. It recedes and keeps moving, so
                there is never a moment where the platform stops and a dialog
                starts. The pane is IN the world, not over it.
   ALIVE      — it is lit from within by the object's own hue and it drifts,
                just barely (§24.3: if a viewer can point at the movement, it
                is too much).
   FUTURISTIC — light, not plastic. The bead stays candy — it is a thing you
                pick up, and the founder called it beautiful. The projection is
                its light: edge-lit, translucent, spilling onto the world.

   No X button. You leave by looking away — scrim, Esc, or swipe (§5). */

/* 6.0 — THE WORLD RECEDES, IT DOES NOT LEAVE. A curtain is the seam the
   founder felt: the platform stopped and something else began. So the wash
   dims and pushes the world back while leaving it legible and still moving —
   the starfield keeps drifting, a Center keeps its colour, and the projection
   is visibly suspended IN somewhere rather than pasted over nothing. */
/* ── OPENING A POPIT IS A PROJECTION, NOT A DIALOG ───────────────────────
   ★ FOUNDER, 2026-08-25: *"the things surrounding the galaxy background should
     fade out and the popit should feel like it's a projected popit taking up
     the user's attention on the galaxy."*

   The pane already called itself projected light — it spills onto what is
   behind it and carries an interference seam. But two things underneath it
   contradicted that every time it opened:

     1. NOTHING AROUND IT MOVED. The feed, the header and the dock stayed at
        full strength behind a flat wash, so the pane read as a card laid on a
        busy page rather than a projection with the room's attention on it.
     2. THE SCRIM COVERED THE GALAXY. `inset:0` at a flat `--ow-scrim` painted
        over the one thing that should have been revealed. The galaxy is the
        GROUND a Popit is projected onto (record 343: it is the visual
        environment, not a graph) — covering it is backwards.

   So: the app recedes, and the galaxy comes forward. */
.po-scrim{position:fixed;inset:0;z-index:300;
  /* A VIGNETTE, NOT A WALL. Clear where the pane will be, so the galaxy reads
     around and behind it; heavy at the edges, where the surroundings were. */
  background:
    radial-gradient(126% 96% at 50% 46%,
      transparent 0%,
      color-mix(in srgb, var(--ow-scrim) 55%, transparent) 44%,
      color-mix(in srgb, var(--ow-scrim) 116%, transparent) 100%);
  opacity:0;pointer-events:none;transition:opacity .42s var(--ow-ease)}
.po-scrim.is-on{opacity:1;pointer-events:auto}
/* `color-mix` is recent; without it the wash is the flat token it always was
   rather than nothing at all. */
@supports not (color: color-mix(in srgb, red 50%, transparent)){
  .po-scrim{background:var(--ow-scrim)}
}

/* THE SURROUNDINGS STEP BACK. `.ow-app` is everything a person was reading;
   while a Popit is open it is context, not content. Opacity and blur only —
   both compositor-cheap — plus a hair of scale so it reads as DEPTH rather
   than as a dimmer switch. */
.po-locked .ow-app{
  opacity:.16;
  transform:scale(.986);
  transform-origin:50% 46%;
  pointer-events:none;
  transition:opacity .42s var(--ow-ease), transform .42s var(--ow-ease),
             filter .42s var(--ow-ease)}
html:not([data-fx="safe"]) .po-locked .ow-app{filter:blur(7px) saturate(.72)}

/* AND THE GALAXY COMES FORWARD. It is the only thing that must not fade —
   the Popit is projected ONTO it, so it gains a little light as the room
   around it loses it. Never opacity: the world is the ground, and a ground
   that dims is just a darker room. */
.po-locked .ow-world{
  filter:saturate(1.18) brightness(1.14);
  transition:filter .52s var(--ow-ease)}
html[data-fx="safe"] .po-locked .ow-world{filter:none}

@media (prefers-reduced-motion:reduce){
  .po-locked .ow-app{transition:none;transform:none;filter:none;opacity:.3}
  .po-locked .ow-world{transition:none;filter:none}
}
/* the blur is a nicety, not the composition — gated so it never costs on a
   device that would struggle, and the wash reads correctly without it. It is
   HALF what it was: 8px erased the world, 4px pushes it behind the glass. */
/* ASK ABOUT BOTH SPELLINGS, OR SAFARI SKIPS THE WHOLE BLOCK.
   Safari only learned unprefixed `backdrop-filter` in 17.4. Before that it
   supports the effect ONLY as `-webkit-backdrop-filter` — so a gate written
   `@supports (backdrop-filter:blur(1px))` evaluates FALSE there and drops
   everything inside, INCLUDING the `-webkit-` declaration written to serve
   exactly that browser. The fallback was sealed inside the gate that
   excluded it, so the glass did not degrade — it vanished, and every Popit
   and sheet rendered flat. That is what "depth is not showing" looks like.
   Testing both spellings costs nothing and is the whole fix. */
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  html:not([data-fx="safe"]) .po-scrim{backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}
}

/* 6.0b — THE BEAM. §4 requires the open object stay "tethered to its origin —
   you can feel where it came from", and it never was: the FLIP animated from
   the bead and then the relationship vanished, which is exactly why the open
   state read as an arrival rather than a projection.

   Position comes from oneway-popit.js, which measures the emitter and the pane
   once on open and again on resize (§18). It is scroll-independent by
   construction: the body is locked while a Popit is open, so the emitter
   cannot move under it. Bright at the emitter, gone by the pane — a projection
   reads FROM its source, and a line of even weight would read as a connector
   on a diagram. */
.po-beam{position:fixed;z-index:300;height:var(--beam-w,120px);pointer-events:none;
  left:var(--beam-x,0);top:var(--beam-y,0);width:var(--beam-l,0);
  transform-origin:0 50%;transform:rotate(var(--beam-a,0deg)) translateY(-50%);
  opacity:0;transition:opacity .4s var(--ow-ease);
  background:linear-gradient(90deg,
    rgba(var(--h),.34) 0%, rgba(var(--h),.16) 34%, rgba(var(--h),.05) 68%, transparent 100%);
  -webkit-mask-image:linear-gradient(90deg,#000 0%,rgba(0,0,0,.35) 60%,transparent 100%);
          mask-image:linear-gradient(90deg,#000 0%,rgba(0,0,0,.35) 60%,transparent 100%);
  /* the cone: narrow at the emitter, open at the pane */
  clip-path:polygon(0 44%, 100% 0, 100% 100%, 0 56%)}
.po-beam.is-on{opacity:1}
html[data-fx="safe"] .po-beam{display:none}   /* light costs; the floor keeps it off */
@media (prefers-reduced-motion:reduce){ .po-beam{transition:none} }

.po-stage{position:fixed;inset:0;z-index:301;display:grid;place-items:center;
  pointer-events:none;
  padding:max(20px,env(safe-area-inset-top)) max(20px,env(safe-area-inset-right))
          max(20px,env(safe-area-inset-bottom)) max(20px,env(safe-area-inset-left))}

.po-sheet{
  /* THE REST ANGLE. A pane suspended in space is not machine-aligned, so it
     rests slightly off-true. A pane resting AGAINST an edge is — a real object
     on a surface aligns to that surface, and a tilted bottom sheet leaves a
     visible wedge of gap in one corner. The media query owns this; the FLIP in
     oneway-popit.js reads it, so the two can never disagree. */
  --po-rest-rot:-1.2deg;
  position:relative;pointer-events:auto;display:none;
  /* SPACIOUS IS AIR, NOT SIZE — and getting that backwards is measurable.
     440px was a card: every room a column of cramped rows with nowhere for the
     object to be. But the first correction went to 620×min(84vh,820px), which
     on a 1280×860 screen is 635×735 — 71% of the viewport, a centred takeover
     (§2 forbids exactly this) with no room left for the pane to step aside from
     its own emitter, so the projection had nothing to project across.

     The air came from the padding and the measure, not from the box. So the
     box comes back to a portion of the screen, and the room stays generous. */
  /* ── AN OPENED POPIT TAKES THE SCREEN ─────────────────────────────────
     ★ FOUNDER, 2026-09-09: *"when opened they occupy the whole screen with the
       transparent popit glow overlay over the galaxy background and the content
       inside the popit goes over it, transparent when possible."*

     THIS OVERRULES A DERIVED RULE, AND THE OVERRULE IS THE POINT. The comment
     that used to sit here reasoned its way from 620px down to 560px because
     "§2 forbids a centred takeover" — a spec line an agent was applying. The
     founder record is the layer above the specification (BIOS: the founder
     record wins until the discrepancy is explicitly resolved), and he has now
     resolved it: the pane IS the screen.

     It does not become a page by being that size. A page is opaque and has
     edges; this is a glow over the galaxy with the object's content floating on
     it — the sky is still there, still drifting, still in this object's colour.
     That is the difference between taking the screen and covering it. */
  width:min(1100px,100vw);height:100dvh;max-height:100dvh;
  overflow:hidden auto;-webkit-overflow-scrolling:touch;
  overscroll-behavior:contain;             /* the page behind never scrolls */
  /* ── THE PANE'S INK FOLLOWS ITS GROUND, AND I BROKE THIS TODAY ────────
     ★ MEASURED after taking the Post's material for this pane, in light theme:
       40 of 42 text runs failing AA, white at 1.46 : 1. The whole opened Popit
       was unreadable.

     I CAUSED IT WITH A CORRECT CHANGE. The founder's ruling was *"popits must
     be taken from posts exactly"*, and a Post's ground is `--ow-tile-base` —
     rgba(8,9,20,.55) on dark and rgba(255,255,255,.62) on LIGHT. Carrying that
     across was right; leaving `color:#fff` beside it was not. The pane went
     from a 90%-opaque dark slab, where white was always correct, to a
     theme-following ground where it is correct exactly half the time.

     The rule this file already states, two hundred lines down, is the one I
     walked past: *"ink and ground are chosen together or not at all. Take the
     theme token only if your ground takes it too."* I took the ground and kept
     the ink.

     FOUND BY RUNNING THE CONTRAST PROBE ON THE SHEET — which had never been
     done, because that probe defaults to `.ow-cv-face` and the opened pane is
     a different surface. Checking tiles in both themes was not enough. */
  color:var(--ow-ink);text-align:left;
  border-radius:30px 26px 29px 27px;
  /* ── THE GROUND IS THE GALAXY, IN THIS OBJECT'S OWN HUE ────────────────
     ★ FOUNDER, 2026-09-09: *"these when opened are too dull. think of each as
       a mini openable interface to be projected over galaxy background in
       exact hue closed post popits backgrounds only."*

     He is naming a material that already exists and is used in exactly one
     place. `.po-card[data-blended] .po-card__face` — a CLOSED Post Popit —
     lets the world through a hue field: two radial pools of the object's own
     light over `--ow-tile-base`, which is 55% ink rather than a wall. That is
     why a closed Post reads as suspended in the galaxy.

     THE OPEN PANE DID NOT DO THAT. `--ow-sheet-1/2` are 90% and 93% opaque, so
     the pane painted the sky out and became a slab — the scrim goes to the
     trouble of a vignette to reveal the galaxy (§ above, founder 2026-08-25)
     and then the pane covered it anyway. Dull is the correct word for it: a
     projection with an opaque back is a card.

     CARRIED OVER VALUE-FOR-VALUE from the blended card face, with the pane's
     own top-light kept — a projection is brightest where it begins. One
     material in two sizes, never a third invention. */
  /* ── THE PANE IS GLOW, NOT PANEL ──────────────────────────────────────
     ★ FOUNDER, 2026-09-09: *"the transparent popit glow overlay over the galaxy
       background."*

     `--ow-tile-base` is the closed tile's floor — 55% ink in dark, 62% white in
     light — and at a tile's size that is right: a small object needs a body.
     At full screen the same floor is a wall, and it paints out the one thing
     the scrim goes to the trouble of revealing.

     So the floor goes and the LIGHT stays. Four pools of the object's own hue,
     nothing under them, and the galaxy is what you see between them. The
     content that lands on this is transparent wherever it can be — the rule for
     everything inside is that it may use light and type, and may not use a
     panel, because a panel here is a page pasted over the sky. */
  background:
    radial-gradient(78% 46% at 50% -6%,   rgba(var(--h),.34), transparent 62%),
    radial-gradient(66% 44% at 8% 22%,    rgba(var(--h),.20), transparent 66%),
    radial-gradient(72% 50% at 96% 76%,   rgba(var(--h2),.18), transparent 64%),
    radial-gradient(120% 70% at 50% 116%, rgba(var(--h2),.16), transparent 60%);
  box-shadow:
    inset 0 3px 2px var(--ow-lift),
    inset 0 -34px 60px var(--ow-weight),
    inset 0 0 0 2px var(--ow-rim),
    /* the object's light reaching in from every edge — at full screen this is
       what says "you are inside the Popit" rather than "a panel is on top" */
    inset 0 0 190px -60px rgba(var(--h),.85),
    /* and a second, tighter ring of it just inside the frame, so the edge is
       LIT rather than DRAWN — the difference between a glow and a border */
    inset 0 0 46px -8px rgba(var(--h),.30),
    /* EDGE-LIT. A projected pane is brightest where it ends — this is the one
       line that most separates "light" from "plastic", and it is the object's
       OWN hue, never a fixed accent (§17). */
    0 0 0 1.5px rgba(var(--h),.9), 0 0 0 6px rgba(var(--h),.16),
    0 60px 120px -34px rgba(0,0,0,.8),
    /* and it SPILLS — light from the pane lands on the world around it, which
       is what stops it reading as a rectangle pasted on top */
    0 0 150px -30px rgba(var(--h),.85);
}
.po-sheet.is-open{display:block}
/* AT FULL SCREEN THERE IS NO OFF-TRUE REST AND NO HARD EDGE. A tilted pane and
   a rounded corner are how a small suspended object announces that it is an
   object; the screen itself is not an object, and a 1.2deg tilt on it shows the
   page behind the corners. The edge-light stays — that is the glow. */
.po-sheet{--po-rest-rot:0deg;border-radius:0}
/* the stage stops padding a pane that is the screen */
.po-stage:has(.po-sheet.is-open){padding:0}
.po-sheet::-webkit-scrollbar{width:0}
/* the glass — one element, gated, with an opaque fallback that keeps the
   composition identical when it is unavailable or stripped */
/* ASK ABOUT BOTH SPELLINGS, OR SAFARI SKIPS THE WHOLE BLOCK.
   Safari only learned unprefixed `backdrop-filter` in 17.4. Before that it
   supports the effect ONLY as `-webkit-backdrop-filter` — so a gate written
   `@supports (backdrop-filter:blur(1px))` evaluates FALSE there and drops
   everything inside, INCLUDING the `-webkit-` declaration written to serve
   exactly that browser. The fallback was sealed inside the gate that
   excluded it, so the glass did not degrade — it vanished, and every Popit
   and sheet rendered flat. That is what "depth is not showing" looks like.
   Testing both spellings costs nothing and is the whole fix. */
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  @media (min-width:641px){
    /* A FOURTH RECIPE, and the same correction. This one only applies above
       641px, which is why it survived every phone sweep: a desktop opened
       Popit was a different material from the Post beside it and nothing
       measured at that width. The BLUR is the reason this rule exists and it
       stays; the ground it also declared does not. */
    html:not([data-fx="safe"]) .po-sheet{
      backdrop-filter:blur(16px) saturate(170%);
      -webkit-backdrop-filter:blur(16px) saturate(170%);
    }
  }
}
/* the sheet's own seam — same family as the bead, same reconstruction.
   THE PANE IS PROJECTED LIGHT, so the seam carries a faint interference across
   it: the light is not perfectly still. It is deliberately at the threshold of
   perception — §24.3's test applies to every kind of movement in this world,
   not only to float: if a viewer can point at it, it is too much. Compositor
   only (transform + opacity on one already-promoted layer), so it costs
   nothing against the 8.33ms budget. */
.po-sheet__seam{position:absolute;inset:9px;border-radius:inherit;pointer-events:none;
  border:1.5px dashed var(--ow-seam);transform:rotate(-.35deg);z-index:2;
  overflow:hidden;display:none}          /* founder/437: no lines around a Popit */
.po-sheet__seam::after{content:"";position:absolute;inset:-30% -10%;
  background:repeating-linear-gradient(180deg,
    rgba(var(--h),.05) 0 1px, transparent 1px 5px);
  opacity:.5;will-change:transform}
html[data-fx="safe"] .po-sheet__seam::after{display:none}
@media (prefers-reduced-motion:no-preference){
  .po-sheet.is-open .po-sheet__seam::after{animation:po-interfere 9s linear infinite}
}
@keyframes po-interfere{
  from{transform:translate3d(0,0,0)}
  to  {transform:translate3d(0,10px,0)}   /* two grid periods — it never seams */
}

/* 6.1 — THE IMAGERY PLANE (§19 "meaningful imagery" · §20.2 "a window into
   something real"). The object's own material when it exists; when it does
   not, the plane does not vanish — it becomes the tier-4 environmental
   treatment derived from the Center's OWN hue (§20.1). Honest either way:
   ONEWAY never fills it with stock or generated scenery (Law 15). */
.po-sheet__plane{position:relative;height:172px;overflow:hidden;
  border-radius:22px 19px 21px 20px;
  margin:16px 16px 0;
  /* THE ENVIRONMENTAL TREATMENT (§20.1 tier 4). With no real material this is
     what you look into — and it must read as a SPACE, not a coloured rectangle
     at the top of an object (§20.3 names that as a page template pretending to
     be a place). So it is built like a place: light pooling up from a floor, a
     faint horizon, depth falling away at the top, the world's own ghost
     geometry suspended in it. Derived entirely from the Center's own hue —
     nothing is stock, nothing is generated (Law 15). */
  background:
    radial-gradient(70% 46% at 50% 92%,  rgba(var(--h2),.7),  transparent 72%),   /* light off the floor */
    radial-gradient(52% 32% at 20% 80%,  rgba(var(--h),.45),  transparent 76%),   /* a second source, off-axis */
    linear-gradient(180deg, transparent 54%, rgba(var(--h2),.2) 63%, transparent 70%), /* the horizon */
    radial-gradient(120% 95% at 76% -10%,rgba(var(--h2),.6),  transparent 60%),   /* sky wash */
    radial-gradient(135% 110% at 16% -6%,rgba(var(--h),.9),   rgba(var(--h),.16) 72%),
    linear-gradient(180deg,#242243,#0f0e1e);
  box-shadow:
    inset 0 30px 46px -34px rgba(0,0,0,.5),     /* depth falls away at the top */
    inset 0 -26px 54px -24px rgba(0,0,0,.3)}
/* structure suspended in the space — the same ghost-outline vocabulary the
   world uses, so the inside of an object belongs to the same place as the
   outside. Hidden the moment real material exists. */
.po-sheet__plane::before{content:"";position:absolute;inset:0;pointer-events:none;
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='180'%3E%3Cg fill='none' stroke='%23ffffff' stroke-opacity='.07' stroke-width='1'%3E%3Crect x='34' y='96' width='46' height='58' rx='6'/%3E%3Crect x='96' y='72' width='38' height='82' rx='5' transform='rotate(-2 115 113)'/%3E%3Crect x='196' y='104' width='54' height='50' rx='7' transform='rotate(3 223 129)'/%3E%3Cpath d='M0 154h300'/%3E%3C/g%3E%3Cg fill='%23ffffff' fill-opacity='.09'%3E%3Ccircle cx='150' cy='40' r='1.4'/%3E%3Ccircle cx='232' cy='26' r='1'/%3E%3Ccircle cx='66' cy='32' r='1.2'/%3E%3C/g%3E%3C/svg%3E");
  background-size:cover;background-position:center bottom}
.po-sheet__plane:has(img)::before{display:none}
.po-sheet__plane img{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;display:block}
/* the name is worn ON the view, legible over any photograph (§20.3) */
.po-sheet__plane::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,transparent 46%,rgba(6,7,16,.34) 72%,rgba(6,7,16,.74) 100%)}
.po-sheet__id{position:absolute;left:22px;right:22px;bottom:16px;z-index:1}
.po-sheet__kind{display:block;font-family:var(--ow-font-atmos);font-weight:600;
  font-size:12px;letter-spacing:.01em;
  color:rgba(255,255,255,.78);margin-bottom:5px;
  text-shadow:0 1px 10px rgba(0,0,0,.85)}
/* THE NAME AND ITS SHADOW ARE ONE DECISION. The shadows here are a black halo,
   written when this sat over a photograph or a dark plane and white was always
   right. With the pane taking the Post's theme-following ground, the ink must
   follow too — and a black halo under dark ink on a light ground is a smudge,
   so the shadow goes with it. On dark, `--ow-ink` is #fff and the halo returns
   through the media query below, unchanged. */
.po-sheet__name{display:block;font-family:var(--ow-font-display);font-weight:700;
  font-size:26px;letter-spacing:-.01em;line-height:1.06;color:var(--ow-ink);
  text-shadow:none}
/* ── ONLY A PHOTOGRAPH EARNS WHITE ────────────────────────────────────────
   My first version also matched `:not([data-face])`, on the assumption that a
   plane with no face falls back to the dark environmental treatment — a
   near-black "place" that white belongs on. THAT TREATMENT IS GONE: removing
   the header band this morning set `background:none` on the open plane, so a
   plane with no face and no image is now transparent and sits on the sheet's
   own theme-following ground. The override was keeping white exactly where the
   ground had stopped being dark. Measured: the name still failed at 1.46 : 1
   in light with the rest of the pane already fixed.

   A PHOTO DOES NOT GET LIGHTER WHEN YOU DO, so an image keeps white and its
   halo in both themes. Everything else follows the theme. */
.po-sheet__plane:has(img) .po-sheet__name{color:#fff;
  text-shadow:0 2px 22px rgba(0,0,0,.85),0 0 46px rgba(0,0,0,.5)}
/* imagery provenance — where the picture came from, on inspection (§1 table) */
.po-sheet__src{position:absolute;right:16px;top:14px;z-index:1;
  font-family:var(--ow-font-atmos);font-size:9px;letter-spacing:.16em;
  color:rgba(255,255,255,.5)}

/* ── AT FULL SCREEN, TILE-SCALE LANGUAGE BECOMES WRONG ────────────────────
   ★ FOUNDER, 2026-09-09: *"what i see here is not structurally right in browser
     preview"* and *"the transparent popit glow overlay over the galaxy
     background."*

   Three things that are RIGHT on a 124x174 bead and wrong on a screen, seen in
   the dark-mode capture before anything else was judged:

     1. THE DASHED SEAM. `.po-sheet__seam` is a 1.5px dashed rectangle inset
        9px — the interference edge of a small projected object, and the LBP
        stitch that says "hand-made". Drawn around a whole screen it stops being
        a seam and becomes a dotted box: it reads as a placeholder outline, the
        exact thing founder 2026-09-02 ordered eliminated.
     2. THE GRAB HANDLE. A bottom sheet has a handle because it is a sheet you
        pull. A projection that IS the screen has nothing to pull.
     3. THE HARD EDGE. `inset 0 0 0 2px var(--ow-rim)` and a drop shadow draw
        the boundary of a panel. A glow has no boundary — the light simply runs
        out, which is what makes it a projection rather than a page.

   None of these is deleted. Each is scoped to the pane that is NOT the screen,
   so a Popit opened in a smaller context keeps the material it was designed
   with. The screen gets the same light with the furniture taken off. */
.po-sheet.is-open .po-sheet__seam{display:none}
.po-sheet.is-open .po-sheet__grab{display:none}

/* ── FILLING IN A PLACED POPIT ────────────────────────────────────────────
   ★ FOUNDER, 2026-09-09: *"i want them to be fully customizeable."*

   It rides the selection, in the panel the Clock's zones and the colour wheel
   already share, so a Popit has ONE settings surface rather than three absolute
   boxes at the same coordinates. Nothing here is a modal and nothing navigates
   away: the thing being changed is the thing under your thumb, which is the
   rule the zone chooser set. */
.ow-cv-cfg{display:flex;flex-direction:column;gap:9px;min-width:210px}
.ow-cv-cfg__row{display:flex;flex-direction:column;gap:4px}
.ow-cv-cfg__k{font-family:var(--ow-font-atmos);font-weight:600;font-size:9px;
  letter-spacing:.01em;color:rgba(var(--h),.86)}
.ow-cv-cfg__in{font:inherit;font-size:13px;color:var(--ow-ink);
  background:rgba(255,255,255,.06);border:0;border-radius:10px 8px 11px 9px;
  padding:8px 10px;width:100%;min-width:0;
  box-shadow:inset 0 0 0 1px rgba(var(--h),.26)}
.ow-cv-cfg__in:focus{outline:none;
  box-shadow:inset 0 0 0 1.5px rgba(var(--h),.7),0 0 22px -8px rgba(var(--h),.8)}
textarea.ow-cv-cfg__in{resize:none;min-height:56px;line-height:1.45}
.ow-cv-cfg__links{display:flex;flex-direction:column;gap:6px}
/* the name is the thing a person reads, the address is the thing they paste —
   so the name takes the room and the URL sits under it rather than beside it,
   which at this width would give both of them too little */
.ow-cv-cfg__pair{display:flex;flex-direction:column;gap:3px}
.ow-cv-cfg__in--url{font-size:11.5px;color:var(--ow-ink-2)}

/* A PLACEMENT THAT CANNOT ANSWER HERE, SEEN BY SOMEBODY WHO CANNOT MOVE IT.
   Its cells are kept so the arrangement does not reflow between the owner's
   view and a visitor's — the same layout, with a quiet gap in it — and nothing
   is drawn, because there is nothing a visitor can learn or do here. */
.ow-cv-t[data-void]{visibility:hidden;pointer-events:none}
/* And for the owner: it reads as something to attend to rather than as content.
   The dashed edge is the file's own unfinished-state language, and the tile
   keeps its material so it is still recognisably the Popit they placed. */
.ow-cv-face.is-misplaced{opacity:.72}
.ow-cv-face.is-misplaced .ow-cv-sub{color:rgba(var(--h),.9)}

/* ── THE WOVEN GRAIN, TAKEN FROM THE POST ─────────────────────────────────
   ★ FOUNDER, 2026-09-09: *"look at POSTS RIGHT now they are best representation
     of how popits look if anything only improve their design but be CAREFUL."*

   Read off the running app, not the stylesheet: `.po-card__face::after` carries

     repeating-linear-gradient(46deg,
       rgba(255,255,255,.05) 0 1px, transparent 1px 4px)

   A one-pixel thread every four pixels at 46 degrees. It is the LBP signature —
   the thing that makes a Post read as a MADE material rather than a coloured
   rectangle — and the opened pane had none of it. This is the single largest
   reason the open state looked generic beside the closed one.

   IT IS THE POST'S VALUES, UNCHANGED. Not a re-tuned version: same angle, same
   period, same alpha. The only addition is that it is masked away from the
   centre, because a texture that is uniform across a whole screen stops being a
   grain and becomes noise over the content — the mask keeps it where a person
   reads it as the material's edge, which is where it lives on a Post-sized
   object anyway.

   COST: one extra composited layer, no paint per frame — it does not animate.
   The interference seam is the thing that moves, and it is already scoped off
   at full screen. */
/* ── AND IT WAS ONE THREAD OF THREE ──────────────────────────────────────
   ★ CAUGHT BY `tools/probe/material.js` the first time it was pointed at an
     opened Popit, 2026-09-12. I wrote this rule myself, from the Post, and
     took only the 46-degree light thread. The Post's weave is THREE layers:
     that thread, a -44-degree dark one crossing it, and a turbulence field
     over both. One thread is a hatch; three are a cloth, and the difference is
     the whole reason the comment above says the grain is why a Post reads as a
     made material.

   THE MASK IS WHY ONE LAYER SEEMED ENOUGH and it is also why three are safe:
   the reasoning above — "a texture uniform across a whole screen stops being a
   grain and becomes noise over the content" — is answered by the mask, not by
   thinning the cloth. Masked away from the centre, all three layers sit only
   where a Post-sized object wears them anyway.

   So the weave comes from the ONE declaration every other surface takes, and
   what stays here is what is genuinely the sheet's: its mask and its z-index.
   A change to the material now reaches the opened Popit too. */
.po-sheet.is-open::after{
  z-index:0;
  -webkit-mask-image:radial-gradient(120% 90% at 50% 50%,
    transparent 0%, transparent 34%, #000 96%);
          mask-image:radial-gradient(120% 90% at 50% 50%,
    transparent 0%, transparent 34%, #000 96%);
}
/* ── THE SAFE FLOOR DOES NOT GET TO TAKE THIS ─────────────────────────────
   ★ A -> C, 2026-09-09: *"what else data-fx='safe' strips that is not a
     per-frame cost. That floor is only allowed to remove per-frame compositor
     work; I already found it killing a static box-shadow."*

   THE FIRST ONE I FOUND WAS THIS, AND IT IS MINE. I added the strip in the
   same commit as the grain, out of habit, three lines under a comment I had
   just written saying *"one extra composited layer, no paint per frame — it
   does not animate."* My own note is the proof it did not belong here.

   The law at the top of this file is exact about what the floor may remove:
   backdrop-filter, filter, mix-blend-mode and will-change — *"the only
   per-frame costs in the old material: each forces the compositor to re-sample
   the moving world behind the element on EVERY frame."* A static
   repeating-linear-gradient with a static mask is painted once and composited;
   it costs a phone nothing per frame, and taking it away is precisely the
   *"degraded product"* on mobile that the law exists to prevent.

   The seam three rules up is a real strip and stays one: it ANIMATES
   (`po-interfere`, translating forever). That is the distinction — does it
   move, not does it look expensive. */
/* the content rides above the grain */
.po-sheet__plane,.po-sheet__body,.po-sheet__stage{position:relative;z-index:1}

/* ── THE HEADER IS GONE ───────────────────────────────────────────────────
   ★ FOUNDER, 2026-09-09: *"having those ugly and generic headers."*

   A POST HAS NO HEADER. Its identity — the lit orb, the name, the time — sits
   in the body with everything else, on the one surface. `.po-sheet__plane` was
   a banded region at the top of the pane with the kind in small caps above a
   30px name: a page template's masthead, which §20.3 already names as the
   failure mode and which the plane's own comment claims to have escaped.

   It did escape it for an object WITH A FACE — the widget's own body carried
   across. What stayed generic is the branded band around it. So the band goes:
   no separate ground, no inset margin, no horizon, no vignette. The name is set
   ON the one surface the way a Post's name is, and the object's face sits with
   it rather than under it. */
.po-sheet.is-open .po-sheet__plane{
  margin:0;height:auto;min-height:0;border-radius:0;
  background:none;box-shadow:none;
  padding:max(26px,env(safe-area-inset-top)) 26px 4px}
.po-sheet.is-open .po-sheet__plane::before,
.po-sheet.is-open .po-sheet__plane::after{display:none}
.po-sheet.is-open .po-sheet__id{position:static;left:auto;right:auto;bottom:auto}
/* the eyebrow becomes the object's own light rather than white-on-a-photograph,
   because there is no photograph under it any more */
/* the eyebrow is the hue's INK for the same reason the fact keys are: the raw
   hue measured 1.79 : 1 on a light ground */
.po-sheet.is-open .po-sheet__kind{color:var(--ow-h1-ink);text-shadow:none;
  font-size:9.5px;letter-spacing:.22em}
.po-sheet.is-open .po-sheet__name{font-size:clamp(22px,5.5vw,28px);
  text-shadow:0 2px 26px rgba(0,0,0,.6)}
/* an object WITH a face keeps it, and it now sits under the name on the same
   surface instead of inside a framed band */
.po-sheet.is-open .po-sheet__plane[data-face]{padding-bottom:10px}

/* ═══ 6.1a — THE STAGE: A POPIT THAT IS AN INTERFACE, NOT A DOCUMENT ═══════
   ★ FOUNDER, 2026-09-09: *"think of each as a mini openable interface to be
     projected over galaxy background in exact hue"* · *"i want full games to be
     able to be built uploaded and played in these like newgrounds"* · *"the
     animation will lead to that and be used for anything."*

   A room is a document: a plane, a lead, sections you read. A STAGE is the same
   pane with the reading furniture taken out — the thing fills it corner to
   corner and the pane is its frame. A published Popit app or game runs here,
   and so does any native kind whose honest form is an instrument rather than a
   record.

   IT IS THE SAME PANE. Same unfold out of the same tile, same galaxy ground in
   the object's own hue, same edge-light, same close. `openApp` used to append
   its own overlay with a Close bar, so a game arrived from nowhere while every
   other Popit unfolded from the thing you touched — one shelf, two ways of
   opening, and the game was the one that felt bolted on.

   NO CHROME INSIDE THE FRAME. The bar is OUTSIDE the stage's inner box, over
   the pane's own light, so an app owns every pixel it was given and nothing of
   ONEWAY's is drawn on top of somebody else's work. */
/* THE CLOSED APP TILE. Its name is the signal, so it is set as a name rather
   than as a number — `--ow-cv-sig` is tuned for a count and a title set at 40px
   wraps to three lines in a 6x4. Everything else about the tile is the shared
   material, unchanged, on purpose. */
.ow-cv-sig--app{font-size:clamp(calc(17px * var(--cv-type,1)),4.6cqw,25px);line-height:1.1;
  letter-spacing:-.01em}

.po-sheet__stage{position:relative;display:flex;flex-direction:column;
  height:100%;border-radius:inherit;overflow:hidden}
/* the stage's bar is the same masthead one level in — same ruling, same fix:
   it is not a bar, it is the name set on the surface, and the app's floor
   begins under it rather than inside a chrome band */
.po-stage__bar{flex:none;display:flex;align-items:baseline;gap:10px;
  padding:max(26px,env(safe-area-inset-top)) 26px 14px;
  position:relative;z-index:2;background:none;box-shadow:none}
.po-stage__kind{font-family:var(--ow-font-atmos);font-weight:600;font-size:9.5px;
  letter-spacing:.01em;color:rgba(var(--h),.95)}
.po-stage__name{font-family:var(--ow-font-display);font-weight:650;font-size:16px;
  letter-spacing:-.01em;color:#fff;text-shadow:0 1px 14px rgba(0,0,0,.7)}
/* THE STAGE FLOOR. Darker than the pane so what runs on it reads as lit FROM
   the pane rather than pasted onto it — the same relationship the plane has to
   the room, one level in. The hue rim is the object's, never a fixed accent. */
.po-stage__in{position:relative;flex:1 1 auto;min-height:0;margin:0 12px 12px;
  border-radius:20px 17px 19px 18px;overflow:hidden;
  /* AN APP GETS A FLOOR, AND ONLY AN APP. A game draws its own world and needs
     something to draw it against — but it is kept as shallow as it can be, so
     the galaxy still reads at the edges and the stage is a lit area of the sky
     rather than a black box cut into it. */
  /* MEASURED IN DARK MODE: at .34/.52 this was a black box cut into the sky,
     which is the panel the founder's "transparent when possible" forbids. An
     app still needs something to draw against, so the floor stays — as a
     DARKENING of the galaxy rather than a lid on it. The stars keep showing
     through, which is the whole test. */
  background:
    radial-gradient(120% 70% at 50% -10%, rgba(var(--h),.16), transparent 62%),
    linear-gradient(180deg, rgba(4,5,12,.16), rgba(4,5,12,.30));
  box-shadow:inset 0 0 0 1px rgba(var(--h),.22),
             inset 0 0 60px -18px rgba(var(--h),.30)}
/* an app owns the whole floor — no border, no padding, no rounding fight */
.po-stage__in iframe{position:absolute;inset:0;width:100%;height:100%;
  border:0;display:block;background:transparent}
.po-stage__wait{position:absolute;inset:0;display:flex;align-items:center;
  justify-content:center;font-family:var(--ow-font-atmos);font-size:10.5px;
  letter-spacing:.2em;color:rgba(255,255,255,.5)}
/* the pane is already the screen (founder, 2026-09-09), so a stage inherits it
   and only widens the measure — an app is not read in a column */
.po-sheet:has(.po-sheet__stage){width:100vw}
.po-stage__in{margin:0 max(14px,3vw) max(14px,3vh)}

/* ═══ 6.1b — THE OBJECT'S OWN FACE, INSIDE THE PLANE ════════════════════════
   (founder, 2026-08-16: Popits "are visually beautiful as buttons, but when
   you open them, they do not feel as widgets or as opening parts of the ONEWAY
   world yet — a major issue.")

   BOTH HALVES OF THAT SENTENCE HAD ONE CAUSE AND IT WAS THIS PLANE. With no
   photograph it fell to the tier-4 environmental treatment and became a 172px
   coloured rectangle with the title on it — a hero banner (§20.3), which is
   the same "180px empty colored banner" §0 names as the first pass's failure.
   So the first thing anyone met on opening ANY object was dead colour, and the
   living tile they had just tapped was nowhere on the screen.

   The plane now holds the object itself. A widget's face is its own body — the
   field of ties, the shelf of marks, the rhythm, the cover — at room scale; a
   bead's face is its icon-object in the candy it wore on the shelf. Everything
   here is derived from the object's own reality, so §20.1's order of
   preference is unbroken: real material still wins outright, and nothing is
   ever stock or generated (Law 15).

   THE PLANE IS STILL A PLACE, NOT A HEADER. The environmental treatment stays
   underneath as the space the object is suspended IN — you are looking into
   somewhere, and the thing is in there. */
/* A PICTURE IS FRAMED. THE OBJECT IS NOT.
   A photograph is something you look at THROUGH a window, so it keeps its
   plane — inset, rounded, edged. The object's own face is not a picture of the
   thing, it IS the thing, and putting it in a framed box at the top of the pane
   re-creates the banner in a different material: a rectangle with a hard edge
   and the title underneath is a page template however good the contents are
   (§20.3). So with a face the frame simply goes, and the object is suspended in
   the pane's own light with you — which is the seam the founder felt on
   2026-08-16, and the last one between the closed object and the open one. */
.po-sheet__plane[data-face]{height:auto;min-height:230px;margin:0;
  border-radius:inherit;background:none;box-shadow:none;
  display:flex;flex-direction:column;justify-content:center;
  padding:34px 30px 92px}          /* the name is worn low, over the grounding */
/* the ghost geometry is the treatment for an EMPTY plane — behind a real
   object it is clutter suspended in the same space, reading as debris */
.po-sheet__plane[data-face]::before{display:none}
/* and the grounding under the name softens: there is no frame edge for it to
   sit against any more, so a hard band would draw the box back in */
.po-sheet__plane[data-face]::after{
  background:linear-gradient(180deg,transparent 40%,rgba(6,7,16,.22) 74%,rgba(6,7,16,.5) 100%)}
.po-sheet__face{position:relative;z-index:1;display:flex;
  align-items:center;justify-content:flex-start;width:100%;
  /* the object is suspended in the space, so it never touches the walls */
  min-height:106px}
/* type inside the plane sits on a dark space, exactly as it does on the
   widget's own dark body — the same rules, so the face cannot read differently
   opened than it did closed */
.po-sheet__face,.po-sheet__face .po-said,.po-sheet__face .po-num,
.po-sheet__face .po-guest__m,.po-sheet__face .po-guest__m b{color:#fff}
.po-sheet__face .po-guest__m span,.po-sheet__face .po-w__foot,
.po-sheet__face .po-w__note{color:rgba(255,255,255,.76)}
.po-sheet__face .po-field__key{color:rgba(255,255,255,.6)}

/* AT ROOM SCALE. A field that was 104px on a tile has space to be read here,
   and that extra room is the whole reward for opening (§12) — the same object,
   with enough space to actually meet it. */
.po-sheet__face .po-field{min-height:186px}
.po-sheet__face .po-id{--s:34px}
.po-sheet__face .po-id[data-w="2"]{--s:40px}
.po-sheet__face .po-id[data-w="3"]{--s:48px}
.po-sheet__face .po-id--me{--s:44px}
.po-sheet__face .po-num{font-size:44px}
.po-sheet__face .po-said{font-size:19px;line-height:1.45}
.po-sheet__face .po-said::before{font-size:30px}
.po-sheet__face .po-rhythm{height:96px;gap:6px}
.po-sheet__face .po-swatch i{height:76px}
.po-sheet__face .po-cover{min-height:150px}
.po-sheet__face .po-guest{gap:11px}
.po-sheet__face .po-guest__m{font-size:14px}
.po-sheet__face .po-w__foot{-webkit-line-clamp:4;font-size:14px}

/* A SHELF AT ROOM SCALE — the marks a person earned, the links they keep, the
   kinds they can make. Same gummy object as the tile, given room to be picked
   out individually rather than counted. */
.po-marks--room{gap:11px}
.po-sheet__face .po-mark{width:52px;height:52px;border-radius:16px 13px 17px 14px}
.po-sheet__face .po-mark svg{width:25px;height:25px}
.po-sheet__face .po-mark--more{font-size:13px}

/* A BEAD'S FACE — the icon-object it wore on the shelf, at scale, so crossing
   the threshold is continuous and you can see what you opened. */
.po-sheet__face[data-kind="mark"]{flex-direction:column;align-items:flex-start;gap:14px}
.po-sheet__ic{position:relative;width:84px;height:84px;flex:none;display:grid;
  place-items:center;border-radius:26px 22px 27px 23px;
  background:
    radial-gradient(33% 20% at 39% 12%,  rgba(255,255,255,.8), transparent 72%),
    radial-gradient(128% 84% at 50% 124%,rgba(var(--h2),.9),  transparent 62%),
    linear-gradient(159deg, rgba(var(--h),.95) 8%, rgba(var(--h),.8) 46%, rgba(var(--h2),.6) 100%);
  box-shadow:
    inset 0 4px 4px rgba(255,255,255,.8),
    inset 0 0 26px rgba(var(--h),.8),
    inset 0 0 0 2px rgba(255,255,255,.25),
    0 0 0 2px rgba(var(--h),.6),
    0 0 46px -8px rgba(var(--h),.9),
    0 16px 30px -14px rgba(0,0,0,.7)}
.po-sheet__ic svg{width:40px;height:40px;stroke:#fff;fill:none;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round}
.po-sheet__sig{font-family:var(--ow-font);font-weight:500;font-size:13px;
  color:rgba(255,255,255,.86)}

/* YOUR STANDING, looked into rather than offered — the state the controls in
   the room are currently in. */
.po-standing--room{justify-content:flex-start;font-size:12px;gap:9px}
.po-standing--room b{font-size:13px;letter-spacing:.14em;color:#fff}
.po-standing--room span{color:rgba(255,255,255,.7)}

/* the grounding gradient must sit ABOVE the face so the name stays legible on
   it, and it is pointer-transparent already so nothing in the face is blocked */
.po-sheet__plane[data-face]::after{z-index:2}
.po-sheet__plane[data-face] .po-sheet__id{z-index:3}

@media (max-width:640px){
  .po-sheet__plane[data-face]{min-height:184px;padding:16px 16px 84px}
  .po-sheet__face{min-height:92px}
  .po-sheet__face .po-field{min-height:160px}
  .po-sheet__face .po-num{font-size:40px}
  .po-sheet__ic{width:70px;height:70px}
  .po-sheet__ic svg{width:34px;height:34px}
}

/* SPACIOUS is a measurement, not a mood. The pane grew to 620px, and content
   at the old 22px/14px rhythm simply spread out into a wider dense block —
   which is a bigger card, not a more spacious one. The air scales with it. */
.po-sheet__body{padding:22px 30px 30px;display:flex;flex-direction:column;gap:20px}
.po-sheet__glance{font-size:15.5px;line-height:1.68;color:var(--ow-ink-2);
  max-width:52ch}                          /* a measure, so a wide pane still reads */
.po-sheet__label{font-family:var(--ow-font-atmos);font-weight:600;font-size:10.5px;
  letter-spacing:.2em;color:rgba(var(--h),.95);
  padding-bottom:8px;border-bottom:1px solid rgba(var(--h),.22);margin-bottom:2px}

/* 6.2 — THE TIME AXIS (§23). What the runtime already returns and nothing has
   ever rendered: time_back (what happened) and time_forward (what's next).
   Information suspended in the object's own light — no cards inside a card. */
.po-time{list-style:none;margin:0;padding:0}
.po-time li{position:relative;padding:9px 0 9px 20px;font-size:13.5px;
  color:var(--ow-ink);line-height:1.5}
.po-time li + li::after{content:"";position:absolute;top:0;left:2px;right:10px;height:1px;
  background:linear-gradient(90deg,transparent,rgba(var(--h),.4),transparent)}
.po-time li::before{content:"";position:absolute;left:3px;top:50%;margin-top:-3px;
  width:6px;height:6px;border-radius:50%;background:rgb(var(--h));
  box-shadow:0 0 11px rgba(var(--h),.95)}
.po-time time{display:block;font-family:var(--ow-font);font-weight:500;font-size:11.5px;
  letter-spacing:0;text-transform:none;color:var(--ow-ink-3);margin-top:3px}
/* backward reads dimmer than forward — the past is behind you */
.po-time--back li{color:var(--ow-ink-2)}
.po-time--back li::before{background:rgba(var(--h),.55);box-shadow:none}
/* A HISTORY IS TYPED, AND THE RUNTIME ALREADY TYPES IT. `time_back` entries
   carry a `kind` — decision · repair · revision · outcome · memory — and the
   App was throwing it away and drawing every entry as the same dot. A decision
   and a repair are not the same event, and a reader scanning a history is
   scanning for exactly that difference. Shape, not colour, so it survives both
   grounds and the Silhouette Test. */
.po-time li[data-k="decision"]::before{border-radius:2px;transform:rotate(45deg)}
.po-time li[data-k="revision"]::before{border-radius:1px;width:5px;height:9px;margin-top:-4px}
.po-time li[data-k="repair"]::before{border-radius:50%;background:none;
  box-shadow:inset 0 0 0 2px rgba(var(--h),.8)}
.po-time li[data-k="outcome"]::before{width:9px;height:9px;margin-top:-4px;
  border-radius:50%;background:rgb(var(--h));box-shadow:0 0 12px rgba(var(--h),.95)}

/* ═══ 6.2a — PLATES: IMAGERY AS THE LANGUAGE, NOT AS AN ASSET ══════════════
   (founder, 2026-08-16: "upcoming events shouldn't be structured like that
   whatsoever… I want a lot more photos than you're thinking right now. Photos
   should naturally complement the Popits.")

   A `<ul>` of titles and dates is a timetable. A gathering is something you
   look at and decide you want to be at — so it gets a picture's worth of space
   whether or not a picture has arrived yet.

   THE PART THAT MAKES THIS HONEST: measured 2026-08-16, every `cover_image_url`
   in this environment is empty and the one gallery holds nothing. A design that
   only works with photographs would have shipped an empty surface. §20.1 is
   already explicit — "'we have no photography' is not a blocker; treating
   imagery as an asset instead of a language is" — so the plate is BUILT from
   the place's own light and a photograph REPLACES that light when it exists.
   Same box, same composition, both ways: the day a Center uploads its first
   picture, nothing is redesigned around it.

   Never stock, never generated, never a grey box (Law 15). The angle is
   derived from the object's own id, so a set reads as several places rather
   than one gradient repeated, and the same object looks the same on every
   device and every reload (Gate 5). */
.po-plates{display:grid;gap:12px;
  /* GENEROUS ON PURPOSE. "A lot more photos than you're thinking" is a size
     instruction as much as a count — at thumbnail scale a photograph becomes a
     bullet point with a picture next to it. Two across on a pane, one on a
     phone, and each tall enough to be looked at. */
  grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}
.po-plate{position:relative;margin:0;overflow:hidden;
  aspect-ratio:4/3;min-height:150px;
  border-radius:20px 17px 19px 18px;
  display:flex;flex-direction:column;justify-content:flex-end;
  /* the place's own light, built like a space rather than a swatch: a floor it
     pools up from, a horizon, depth falling away at the top */
  background:
    radial-gradient(72% 48% at 50% 100%, rgba(var(--h2),.72), transparent 72%),
    radial-gradient(50% 34% at 18% 84%,  rgba(var(--h),.46),  transparent 78%),
    linear-gradient(180deg, transparent 52%, rgba(var(--h2),.2) 62%, transparent 72%),
    radial-gradient(120% 96% at 80% -12%, rgba(var(--h2),.55), transparent 62%),
    linear-gradient(var(--ang,150deg), rgba(var(--h),.85), rgba(var(--h2),.42) 76%),
    linear-gradient(180deg,#221f3f,#0f0e1e);
  box-shadow:
    inset 0 26px 42px -30px rgba(0,0,0,.55),
    0 12px 30px -14px var(--ow-drop);
  transition:transform .2s var(--ow-spring), box-shadow .2s var(--ow-ease)}
/* hand-cut, like everything else on this platform */
.po-plate[data-v="1"]{border-radius:17px 20px 18px 19px}
.po-plate[data-v="2"]{border-radius:19px 18px 20px 17px}
.po-plate[data-v="3"]{border-radius:18px 19px 17px 20px}
.po-plate[data-v="4"]{border-radius:21px 18px 19px 17px}
.po-plate[data-v="5"]{border-radius:18px 21px 17px 19px}
/* A REAL PICTURE REPLACES THE LIGHT ENTIRELY — it does not sit on top of it */
.po-plate img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  display:block;z-index:0}
/* the grounding, so a name is legible over a photograph AND over the light */
.po-plate::after{content:"";position:absolute;inset:0;pointer-events:none;z-index:1;
  background:linear-gradient(180deg,transparent 38%,rgba(6,7,16,.5) 74%,rgba(6,7,16,.86) 100%)}
/* WITH NO PHOTOGRAPH, THE OBJECT'S OWN GLYPH stands in the space — a room with
   something in it, rather than an empty coloured rectangle. It leaves as soon
   as a real picture arrives. */
.po-plate__ic{position:absolute;left:50%;top:44%;transform:translate(-50%,-50%);
  z-index:0;opacity:.34}
.po-plate__ic svg{width:44px;height:44px;stroke:#fff;fill:none;stroke-width:1.5;
  stroke-linecap:round;stroke-linejoin:round}
.po-plate:has(img) .po-plate__ic{display:none}
.po-plate figcaption{position:relative;z-index:2;padding:14px 16px 15px;
  display:flex;flex-direction:column;gap:2px}
.po-plate__when{font-family:var(--ow-font-atmos);font-weight:600;font-size:9.5px;
  letter-spacing:.01em;color:rgba(255,255,255,.82);
  text-shadow:0 1px 8px rgba(0,0,0,.8)}
.po-plate__t{font-family:var(--ow-font-display);font-weight:650;font-size:17px;
  line-height:1.14;letter-spacing:-.015em;color:#fff;
  text-shadow:0 1px 14px rgba(0,0,0,.85)}
.po-plate__s{font-size:12px;color:rgba(255,255,255,.78);
  text-shadow:0 1px 10px rgba(0,0,0,.8)}
@media (hover:hover) and (pointer:fine){
  .po-plate:hover{transform:translate3d(0,-2px,0);
    box-shadow:
      0 0 30px -10px rgba(var(--h),.6), 0 16px 34px -14px var(--ow-drop)}
}
@media (max-width:640px){
  .po-plates{grid-template-columns:1fr;gap:10px}
  .po-plate{aspect-ratio:16/10;min-height:132px}
}

/* 6.2b — THE INFOBOX. `identity` is the object's own record, and a record
   reads as pairs — the one place in this component where a table is the honest
   form, because that is what the data is. Labels in the atmospheric voice
   (§16.4 permits it for the KIND of a thing), values in the working one. */
/* ── A READOUT, NOT A SPREADSHEET ────────────────────────────────────────
   ★ FOUNDER, 2026-09-09: *"these when opened are too dull"* and *"make these
     intentional and look at POSTS RIGHT now."*

   THIS WAS THE DULL THING, and it is in every room at once. A two-column grid
   with a rule between every row is a table: the eye reads down a column of
   labels, the values are secondary, and every Popit looked like the same form
   with different words in it. On a full screen it was a spreadsheet floating in
   space.

   A POST'S HIERARCHY IS THE ANSWER AND IT IS ALREADY IN THE FILE. A Post sets
   the NAME large and the time small above it — the thing you came for is the
   big one, and its label is a whisper. Facts get the same order: the value
   leads, the key is a small caps line above it in the object's own light.

   AND THEY SIT SIDE BY SIDE. A location has a city, a region and a country;
   those are three readings of one place, not three rows of a record, so they
   are laid out as cells that flow. `auto-fit` with a 132px floor means a long
   value takes the width it needs and short ones pair up, without anything
   choosing a column count in advance.

   THE RULES BETWEEN ROWS ARE GONE. A hairline per row is furniture; what
   separates one reading from the next here is space and the drop from value to
   the next key — the same thing that separates one Post from the next in a
   feed. */
.po-facts{margin:0;display:grid;gap:18px 26px;
  grid-template-columns:repeat(auto-fit,minmax(132px,1fr))}
.po-fact{display:flex;flex-direction:column;gap:3px;min-width:0}
/* the key is the hue's INK, not the raw hue: at .82 alpha it measured 4.40 to
   4.49 on dark — under the 4.5 floor by a hair, and 1.66 on light. `--ow-h1-ink`
   is the same hue, clamped only where the ground needs it. */
.po-fact dt{margin:0;font-family:var(--ow-font-atmos);font-weight:600;font-size:9px;
  letter-spacing:.2em;color:var(--ow-h1-ink)}
.po-fact dd{margin:0;font-family:var(--ow-font-display);font-weight:600;
  font-size:clamp(15px,2.6vw,19px);line-height:1.24;letter-spacing:-.01em;
  color:var(--ow-ink);overflow-wrap:anywhere;
  /* the value is lit rather than printed — the same relationship the Post's
     name has to the material it sits on */
  text-shadow:0 1px 16px rgba(0,0,0,.5)}
/* A LONG VALUE IS A SENTENCE, NOT A HEADLINE. A guestbook entry and a city name
   are both "facts" and setting a paragraph at 19px display weight is shouting.
   The switch is on length, measured in the value itself, so nothing has to be
   declared per kind. */
.po-fact dd:not(:empty){--po-fact-len:0}
/* A LITERAL THAT IS CORRECT, and said so because `ink-literals.cjs` asks.
   This is inside `.po-sheet`, whose ground is the object's own light over the
   galaxy in BOTH themes — it does not follow the theme, so neither does its
   ink. The probe's own note: "if the ground genuinely does not follow the
   theme, a literal is CORRECT. Say so in a comment on the rule." */
.po-facts--prose .po-fact dd{font-family:var(--ow-font);font-weight:500;
  font-size:14.5px;line-height:1.6;color:rgba(255,255,255,.92);text-shadow:none}
.po-facts--prose{grid-template-columns:1fr;gap:16px}

/* the state a thing is in, riding with the lead sentence */
.po-state{display:inline-block;margin-left:9px;vertical-align:1px;
  font-family:var(--ow-font-atmos);font-weight:600;font-size:9.5px;
  letter-spacing:.01em;color:rgba(var(--h),.95);
  padding:3px 9px;border-radius:9px 7px 10px 8px;
  box-shadow:inset 0 0 0 1px rgba(var(--h),.4)}

/* 6.2c — REVEAL MORE, IN PLACE (founder, 2026-08-16: "click on reveal more
   information and display more graphics"). It is a <details>: the pane GROWS,
   it is never covered. A second surface stacked on an open Popit would be the
   modal the standing rules forbid, and it would break the one thing this whole
   component is for — that you are inside one object, not in a stack of them. */
.po-more{margin:0}
.po-more > summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:9px;
  font-family:var(--ow-font-atmos);font-weight:600;font-size:10.5px;
  letter-spacing:.2em;color:rgba(var(--h),.95);
  padding:11px 0;border-top:1px solid rgba(var(--h),.22)}
.po-more > summary::-webkit-details-marker{display:none}
/* the affordance is a chevron that turns — the one rotation in the component
   that a viewer IS meant to notice, because it is answering their click */
.po-more > summary::after{content:"";flex:none;width:7px;height:7px;margin-left:auto;
  border-right:1.5px solid rgba(var(--h),.9);border-bottom:1.5px solid rgba(var(--h),.9);
  transform:rotate(45deg) translate(-1px,-1px);
  transition:transform .2s var(--ow-spring)}
.po-more[open] > summary::after{transform:rotate(-135deg) translate(-2px,-2px)}
.po-more > summary b{font-weight:600;font-size:10px;color:var(--ow-ink-3);
  padding:2px 7px;border-radius:8px 6px 9px 7px;
  box-shadow:inset 0 0 0 1px rgba(var(--h),.3)}
.po-more > summary:focus-visible{outline:2px solid rgba(var(--h),.95);outline-offset:3px}
@media (hover:hover) and (pointer:fine){
  .po-more > summary:hover{color:rgb(var(--h))}
}
.po-more__in{padding:2px 0 12px}
@media (prefers-reduced-motion:reduce){ .po-more > summary::after{transition:none} }

/* 6.3 — the way out. Speaks up only when someone is visibly hunting for it. */
.po-out{position:fixed;left:50%;bottom:24px;z-index:320;
  transform:translateX(-50%) translateY(8px);
  font-family:var(--ow-font-atmos);font-size:10.5px;letter-spacing:.2em;
  color:rgba(255,255,255,.72);
  background:rgba(6,7,16,.72);border:1px solid rgba(255,255,255,.14);
  border-radius:14px 10px 13px 11px;padding:9px 16px;pointer-events:none;
  opacity:0;transition:opacity .4s ease,transform .4s ease}
.po-out.is-on{opacity:1;transform:translateX(-50%) translateY(0)}

/* 6.4 — MOBILE: a sheet on a phone is a BOTTOM SHEET. This is the native
   grammar on both iOS and Android, and it is also the only way the object
   stays reachable by thumb. The FLIP still originates at the bead, so it is
   the same unfold — it simply comes to rest against the bottom edge. */
.po-sheet__grab{display:none;position:sticky;top:0;z-index:3;
  padding:10px 0 4px;text-align:center;cursor:grab}
.po-sheet__grab::before{content:"";display:inline-block;width:38px;height:4px;
  border-radius:99px;background:rgba(255,255,255,.34)}

@media (max-width:640px){
  /* ── IT IS NOT A BOTTOM SHEET ANY MORE ─────────────────────────────────
     ★ FOUNDER, 2026-09-09: *"I very strongly dislike the cards coming up only
       to the bottom of screen, being fully like cards, and having those ugly
       and generic headers, all strong parts of design so far have been
       intentional. make these intentional and look at POSTS RIGHT now they are
       best representation of how popits look."*

     THIS RULE WAS THE THING HE WAS LOOKING AT. `place-items:end center` seats
     the pane against the bottom edge, `max-height:88vh` stops it short of the
     top, `border-radius:24px 21px 0 0` rounds the two corners that meet the
     world — and `linear-gradient(180deg, rgba(28,27,50,.975),
     rgba(11,11,22,.985))` is NINETY-SEVEN AND A HALF PERCENT OPAQUE. A card,
     rising to the bottom of the screen, painting out the galaxy. Every word of
     his sentence is this declaration block.

     WHAT REPLACES IT IS NOT INVENTED. It is the live computed material of a
     Post, read off `.po-card__face` in the running app rather than from the
     stylesheet, because he said to look at Posts right now:

       background-image:
         radial-gradient(130% 80% at 18% -6%,  rgba(H,.46),  transparent 64%)
         radial-gradient(120% 90% at 92% 108%, rgba(H2,.30), transparent 60%)
         linear-gradient(158deg, rgba(H,.16), rgba(H2,.10) 62%, rgba(8,9,20,.55))
       box-shadow:
         inset 0 2px 1px   rgba(255,255,255,.20)
         inset 0 0 0 1.5px rgba(H,.30)
         0 0 34px -10px    rgba(H,.50)
         0 18px 44px -16px rgba(0,0,0,.66)
       background-color: rgba(0,0,0,0)      <- THERE IS NO SOLID GROUND

     That last line is the whole difference. A Post has no background COLOUR;
     the only opaque thing in it is `--ow-tile-base` at .55 sitting at the far
     end of one gradient. That is why the constellation web reads straight
     through a Post and why it does not look like a card. Carried here
     value-for-value, with only the two OUTER shadows dropped — a drop shadow
     and an outward glow both fall off-screen when the object is the screen. */
  .po-stage{place-items:stretch;padding:0}
  .po-sheet{--po-rest-rot:0deg;
    width:100%;max-width:100%;height:100dvh;max-height:100dvh;
    border-radius:0;
    padding-bottom:env(safe-area-inset-bottom);
    background:
      radial-gradient(130% 80% at 18% -6%,  rgba(var(--h),.46), transparent 64%),
      radial-gradient(120% 90% at 92% 108%, rgba(var(--h2),.30), transparent 60%),
      linear-gradient(158deg, rgba(var(--h),.18), rgba(var(--h3,var(--ow-h3,var(--h2))),.12) 46%,
                            rgba(var(--h2),.10) 70%, var(--ow-tile-base));
    box-shadow:
      inset 0 2px 1px rgba(255,255,255,.20),   /* the wet top edge, exactly */
      /* the near glow becomes an inward one: at screen scale the Post's
         `0 0 34px -10px` lands outside the viewport and is simply not seen */
      inset 0 0 120px -40px rgba(var(--h),.50)}
  .po-sheet__name{font-size:25px}
  .po-out{display:none}
}

/* while dragging, the sheet must not fight the finger */
.po-sheet.is-dragging{transition:none}


/* ═══ 7 · ACCESSIBILITY ════════════════════════════════════════════════════ */
.po:focus-visible::after,.po-glance:focus-visible{outline-offset:3px}
@media (prefers-reduced-motion:reduce){
  .po,.po.po-live,.po-glance,.ow-world__neb,
  .ow-world__stars::before,.ow-world__stars::after{animation:none !important}
  .po,.po-glance,.po::before,.po::after,.po-sheet{transition:none !important}
}
@media (prefers-contrast:more){
  .po-face{box-shadow:inset 0 6px 5px rgba(255,255,255,.9), inset 0 0 0 2px rgba(255,255,255,.5),
    0 0 0 3px rgba(var(--h),.9), 0 22px 44px -10px rgba(0,0,0,.6)}
  .po-lb,.po-sig{text-shadow:0 1px 4px #000,0 0 12px #000}
}
/* scroll lock (iOS-safe: position:fixed + top; oneway-popit.js restores exactly) */
.po-locked{position:fixed !important;left:0;right:0;width:100%;overflow:hidden}

/* ═══ 8 · PRINT ════════════════════════════════════════════════════════════ */
@media print{ .ow-world,.po-scrim,.po-out{display:none !important} }


/* ═══ 9 · POPIT WIDGETS — a profile, displayed to other people ══════════════
   (founder, 2026-07-28: "widgets are what people display to other users when
   clicked on their profile.")

   So a widget is not decoration on a page — it is how a VISITOR meets a person.
   Two consequences drive everything here:

   1 · A WIDGET NEEDS A CONTENT REGION. A bead says icon + label + one signal.
       A 2×2 widget has to actually show something — a constellation of Centers,
       a shelf of marks, a gallery cover. So the tile gains .po-w__body, and
       every widget kind fills it differently while the material stays one thing.

   2 · THE SAME PROFILE IS A DIFFERENT PROJECTION PER VISITOR (the Perspective
       Law). A widget declares who may see it; the grid renders what THIS viewer
       is entitled to. A stranger and a fellow member of your community open the
       same profile and honestly see different sets. Nothing is hidden by CSS —
       oneway-popit.js never builds the tiles a viewer may not have.

   The material law of §0 still holds: no backdrop-filter, no blend, no
   filter:url() anywhere in a widget. Widget bodies are painted, never composited
   per frame, so a profile of twelve widgets costs what a profile of one does. */

.po-w{
  justify-content:flex-start;align-items:stretch;text-align:left;
  gap:0;padding:14px 15px 13px;
}
.po-w__head{position:relative;z-index:2;display:flex;flex-direction:column;gap:1px;flex:none}
.po-w__eyebrow{font-family:var(--ow-font-atmos);font-weight:600;font-size:9px;
  letter-spacing:.2em;color:rgba(255,255,255,.66)}
.po-w__label{font-family:var(--ow-font-display);font-weight:650;font-size:15px;
  letter-spacing:-.01em;line-height:1.14;color:#fff;text-shadow:0 1px 3px rgba(0,0,0,.4)}
/* the content region — the thing that makes a widget a widget */
.po-w__body{position:relative;z-index:2;flex:1 1 auto;min-height:0;margin:10px 0 0;
  display:flex;align-items:center;justify-content:flex-start;
  /* GUARD: a widget declared a size, so its content lives inside it. Without
     this a body that outgrows its tile spills over the footer and the tile
     below — which is what a four-row link list did at 2x2. Content that does
     not fit belongs in the room, not over the edge. */
  overflow:hidden}
.po-w__foot{position:relative;z-index:2;flex:none;margin-top:8px;
  font-family:var(--ow-font);font-weight:500;font-size:11.5px;letter-spacing:0;
  text-transform:none;color:var(--ow-ink-2);
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}
/* a 1×1 stays a bead — there is no room for a body, and pretending otherwise
   is how a widget grid turns into a wall of unreadable tiles */
.po-w.po--1x1{align-items:center;text-align:center;justify-content:center;padding:16px 10px}
.po-w.po--1x1 .po-w__body{margin:6px 0 0;justify-content:center}
.po-w.po--1x1 .po-w__label{font-size:13px}

/* 9.1 — BELONGING. The Centers a person belongs to, each in ITS OWN hue pair
   (§17). This is the widget nothing else can build: it is made of the
   relationship graph. Dots are placed from the data, not scattered randomly —
   the same person always reads the same constellation. */
.po-cons{position:relative;width:100%;height:100%;min-height:96px}
.po-cons__l{position:absolute;height:1px;transform-origin:0 50%;
  background:linear-gradient(90deg,rgba(255,255,255,.24),rgba(255,255,255,.05))}
.po-cons__d{position:absolute;width:var(--d,14px);height:var(--d,14px);
  margin:calc(var(--d,14px) / -2) 0 0 calc(var(--d,14px) / -2);
  border-radius:5px 4px 5px 4px;
  background:radial-gradient(60% 50% at 34% 24%,rgba(255,255,255,.7),transparent 70%),
    linear-gradient(150deg,rgb(var(--ch1)),rgba(var(--ch2),.75));
  box-shadow:0 0 12px -1px rgba(var(--ch1),.9), inset 0 1px 0 rgba(255,255,255,.5)}
.po-cons__d[data-me]{--d:20px;border-radius:7px 6px 7px 5px;
  box-shadow:0 0 18px -1px rgba(var(--ch1),1), inset 0 1px 0 rgba(255,255,255,.6),
    0 0 0 2px rgba(255,255,255,.35)}

/* 9.2 — MARKS. A Center issues one when a person actually turns up, so the
   shelf is a true record of where they have been — Popits as collectibles,
   which is the whole LBP lineage made literal. Each mark carries the issuing
   Center's hue, so the shelf reads as a map of a life. */
.po-marks{display:flex;flex-wrap:wrap;gap:7px;align-content:flex-start;width:100%}
.po-mark{position:relative;width:34px;height:34px;flex:none;
  border-radius:11px 9px 12px 10px;
  background:radial-gradient(55% 45% at 32% 22%,rgba(255,255,255,.7),transparent 70%),
    linear-gradient(152deg,rgb(var(--ch1)),rgba(var(--ch2),.7));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.55), inset 0 0 12px rgba(var(--ch1),.7),
    0 0 0 1px rgba(var(--ch1),.5), 0 3px 8px -3px rgba(0,0,0,.6);
  display:grid;place-items:center}
.po-mark svg{width:17px;height:17px;stroke:#fff;fill:none;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round}
.po-mark--more{background:none;box-shadow:inset 0 0 0 1.5px rgba(255,255,255,.22);
  font-family:var(--ow-font-atmos);font-size:10px;letter-spacing:.06em;color:rgba(255,255,255,.66)}

/* 9.3 — GUESTBOOK. The only widget whose content comes from OTHER PEOPLE —
   which is exactly why it belongs on a surface built for visitors. */
.po-guest{display:flex;flex-direction:column;gap:7px;width:100%}
/* the remainder, named rather than dropped — same rule as `links`' "All N" */
/* inside the sheet the ground is always dark, so the raw hue reads; on a tile
   it follows the theme and must use the clamped ink. Same rule, two grounds. */
.po-sheet__face .po-guest__more{color:rgba(var(--h),.9)}
.po-guest__more{display:block;margin-top:6px;
  font-family:var(--ow-font-atmos);font-weight:600;font-size:9.5px;
  letter-spacing:.01em;color:var(--ow-h1-ink)}
.po-guest__m{display:flex;gap:9px;align-items:baseline;font-size:12.5px;line-height:1.4;
  color:rgba(255,255,255,.92)}
.po-guest__m b{font-family:var(--ow-font-display);font-weight:600;flex:none;
  color:#fff;font-size:12px}
.po-guest__m span{color:rgba(255,255,255,.78);min-width:0;overflow-wrap:anywhere}

/* 9.4 — A BIG NUMBER (Since · counts). The declarative voice doing what it is
   for: key numbers (§1). */
.po-num{font-family:var(--ow-font-display);font-weight:700;letter-spacing:-.01em;
  line-height:.92;font-size:34px;color:#fff;text-shadow:0 2px 14px rgba(0,0,0,.5)}
.po-w.po--1x1 .po-num{font-size:27px}

/* 9.5 — MY COLOURS. A person's own hue pair, as an object. */
.po-swatch{display:flex;gap:6px;width:100%}
.po-swatch i{flex:1 1 0;height:34px;border-radius:9px 7px 10px 8px;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.5), 0 0 14px -3px rgba(0,0,0,.5)}

/* 9.6 — A COVER (gallery · a place · a moment). Real material when it exists;
   the hue-derived environmental treatment when it does not (§20.1 tier 4) —
   never stock, never generated (Law 15). */
.po-cover{position:relative;width:100%;height:100%;min-height:64px;overflow:hidden;
  border-radius:12px 10px 13px 11px;
  background:
    radial-gradient(70% 46% at 50% 96%, rgba(var(--h2),.6), transparent 72%),
    linear-gradient(180deg, transparent 56%, rgba(var(--h2),.16) 64%, transparent 72%),
    radial-gradient(130% 100% at 20% -8%, rgba(var(--h),.7), rgba(var(--h),.1) 74%),
    linear-gradient(180deg,#201f3c,#0d0c1a);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.12)}
.po-cover img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}

/* 9.6b — THE COMPRESSED COVER. A gallery declared minSize 2x2 for a real
   reason — a cover full-bleeding a one-row tile is a 50px stripe, not a cover.
   But four of the kinds a profile is mostly made of declared the same minimum,
   so every caller's 2x1 was raised and profiles flattened into a uniform grid
   of 2x2s. The composition rules underneath were sound (first gallery hero,
   the rest supporting) and never got to run.

   The fix is not a smaller cover, it is a DIFFERENT COMPOSITION at one row:
   the cover becomes a square thumb holding its own aspect, and the type stands
   beside it. The content still means something, so the minimum can come down
   and the rhythm the grid was built for finally happens. */
.po--2x1 .po-w__body,
.po--3x1 .po-w__body,
.po--4x1 .po-w__body{
  margin-top:8px;
}
/* THE WHOLE TILE BECOMES THE ROW, not just its body. The first attempt made
   only the body a flex row, which left the thumb sitting alone under the title
   with dead space beside it — the title lives in the head, above. At one row
   the composition is: image, then everything the tile has to say, reading
   left to right like a line in a list. */
.po--2x1:has(.po-cover),
.po--3x1:has(.po-cover),
.po--4x1:has(.po-cover){
  display:grid;
  grid-template-columns:auto 1fr;
  grid-template-areas:"thumb head" "thumb foot";
  grid-template-rows:auto 1fr;
  align-items:start;
  column-gap:12px;row-gap:2px;
}
.po--2x1:has(.po-cover) > .po-w__head,
.po--3x1:has(.po-cover) > .po-w__head,
.po--4x1:has(.po-cover) > .po-w__head{grid-area:head;align-self:end}
.po--2x1:has(.po-cover) > .po-w__foot,
.po--3x1:has(.po-cover) > .po-w__foot,
.po--4x1:has(.po-cover) > .po-w__foot{grid-area:foot;align-self:start;margin:0}
.po--2x1:has(.po-cover) > .po-w__body,
.po--3x1:has(.po-cover) > .po-w__body,
.po--4x1:has(.po-cover) > .po-w__body{
  grid-area:thumb;margin:0;align-self:stretch;display:flex;
}
.po--2x1 .po-w__body > .po-cover,
.po--3x1 .po-w__body > .po-cover,
.po--4x1 .po-w__body > .po-cover{
  /* SQUARE, so the image is cropped the way a photograph survives being
     cropped — from the middle — rather than sliced into a band. It takes the
     full height of the tile, so the row has a real anchor rather than a stamp. */
  flex:0 0 auto;width:auto;aspect-ratio:1/1;min-height:0;height:100%;
  border-radius:11px 9px 12px 10px;
}
/* at one row the head and the thumb sit on the same line, so the label may not
   run past two — a third line pushes the thumb out of the tile it declared */
.po--2x1 .po-w__label,
.po--3x1 .po-w__label{
  font-size:14px;-webkit-line-clamp:2;
  display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;
}

/* 9.7 — AN AUTHORED LINE. The one widget a person simply writes. It is marked
   as authored on purpose: the honest widgets stay trustworthy only if the
   written one is visibly a person talking, not a derived fact. */
/* THE INK IS THE WORLD'S, NOT A COLOUR. Both of these were hardcoded white.
   The line itself survived only because a later rule re-set it for the light
   theme; the OPENING QUOTE had no such rescue and rendered white-on-white — a
   faint smudge in front of every authored line on a light profile, which is
   what it looked like: a broken glyph rather than a quotation mark. Same shape
   as the lens plate fixed earlier today, and the same fix: read the token, so
   the mark is legible in whichever theme the reader is actually in. */
.po-said{font-size:13.5px;line-height:1.5;color:var(--ow-ink);
  font-style:normal;overflow-wrap:anywhere}
.po-said::before{content:"“";font-family:var(--ow-font-display);font-size:22px;
  line-height:0;color:var(--ow-ink-3);margin-right:2px;vertical-align:-2px}

/* 9.8 — A RHYTHM. Earned, never gamified: bars are real attendance. */
.po-rhythm{display:flex;align-items:flex-end;gap:4px;width:100%;height:46px}
.po-rhythm i{flex:1 1 0;border-radius:3px 2px 3px 2px;min-height:4px;
  height:calc(var(--v,50) * 1%);
  background:linear-gradient(180deg,rgba(255,255,255,.7),rgba(var(--h2),.8));
  box-shadow:0 0 8px -2px rgba(var(--h),.9)}
.po-rhythm i[data-off]{background:rgba(255,255,255,.13);box-shadow:none;height:14%}

/* 9.9 — the visitor's own standing, stated honestly at the top of a profile. */
.po-standing{display:flex;flex-wrap:wrap;gap:7px;justify-content:center;
  font-family:var(--ow-font-atmos);font-size:10px;letter-spacing:.16em;
  color:rgba(255,255,255,.6)}
.po-standing b{color:rgba(var(--ow-h2),1);font-weight:600}

/* 9.10 — WIDGET MATERIAL: A WIDGET IS GLASS, NOT CANDY ─────────────────────
   Found by putting real widgets on a real profile. The bead's material is a
   SATURATED candy body — correct for a 124px object you pick up, and wrong for
   a 265px panel: at that scale the same gradient stack flattens into a pale
   slab and the eyebrow goes illegible on it.

   The canon already had the answer and the two materials were always distinct
   (§3 vs §15): a BEAD is candy — small, bright, pickable. A SHEET is hue-lit
   dark glass — something you look INTO. A widget is a window a visitor looks
   through, so it belongs to the sheet's half of the family.

   Same family, same hand-cut edge, same seam, same hue inheritance, same
   §0 law (no backdrop-filter, no blend, no filter) — deeper body, so content
   reads. A 1×1 stays candy: at bead scale it IS a bead. */
.po-w:not(.po--1x1) > .po-face{
  /* THE SAME DECLARATION AS THE FEED CARD AND THE HOME CANVAS TILE.
     This was the profile's own arrangement over the OPAQUE panel, which is a
     dark slab with a tinted edge rather than a hue-lit body — the same mistake
     the Home tile carried until today. Identical recipe now, so a person's
     Popits are one object whichever surface they are standing on. */
  background:
    radial-gradient(130% 80% at 18% -6%,  rgba(var(--h),.46),  transparent 64%),
    radial-gradient(120% 90% at 92% 108%, rgba(var(--h2),.30), transparent 60%),
    linear-gradient(158deg, rgba(var(--h),.18), rgba(var(--h3,var(--ow-h3,var(--h2))),.12) 46%,
                            rgba(var(--h2),.10) 70%, var(--ow-tile-base));
  box-shadow:
    inset 0 2px 1px var(--ow-lift),
    0 0 34px -10px rgba(var(--h),.50),
    0 18px 44px -16px var(--ow-drop);
}
/* the grain sits far back on a dark body — at candy strength it reads as noise */
.po-w:not(.po--1x1) > .po-face::after{opacity:var(--ow-grain)}
/* the seam is the family signature and must stay visible on the darker body */
.po-w:not(.po--1x1) > .po-face::before{border-color:var(--ow-seam)}
/* the spill is a whisper at panel scale — a 2×2 glowing like a bead is a lamp */
.po-w:not(.po--1x1)::before{inset:-16%;opacity:.42}
.po-w:not(.po--1x1):hover::before{opacity:.6;transform:translate3d(0,0,0) scale(1.04)}

/* type now sits on a dark field, so it can carry real contrast */
.po-w:not(.po--1x1) .po-w__eyebrow{color:rgba(var(--h2),.92)}
.po-w:not(.po--1x1) .po-w__foot{color:var(--ow-ink-2);text-shadow:none}
.po-w:not(.po--1x1) .po-w__label{color:var(--ow-ink);text-shadow:none}
.po-w:not(.po--1x1) .po-w__note{color:var(--ow-ink-2)}
.po-w:not(.po--1x1) .po-said,.po-w:not(.po--1x1) .po-num,
.po-w:not(.po--1x1) .po-guest__m,.po-w:not(.po--1x1) .po-guest__m b{color:var(--ow-ink)}
.po-w:not(.po--1x1) .po-guest__m span{color:var(--ow-ink-2)}
/* constellation threads read against the dark body */
.po-w .po-cons__l{background:linear-gradient(90deg,rgba(255,255,255,.3),rgba(255,255,255,.06))}

/* ═══ 10 · THE IDENTITY FIELD ═══════════════════════════════════════════════
   (founder, 2026-07-28: "where she's been would have the icons of centers she
   visited in proximity, and then also centers she's a part of — her world. And
   where she's been should look similar, but her world should also show
   following, with people and centers.")

   So these are ONE component with two readings, not two widgets that happen to
   resemble each other:

     WHERE SHE'S BEEN   places she actually went, laid out BY PROXIMITY — near
                        things sit near each other, so the field reads as a map
                        of a life rather than a list of names.
     HER WORLD          who and what she belongs to and follows, laid out by
                        RELATIONSHIP proximity — she is the centre, and closeness
                        means a stronger tie.

   THE READABLE CONVENTION: A PLACE IS A TILE · A PERSON IS A ROUND. It needs no
   legend, it survives the Silhouette Test (§13), and it lets one field carry
   Centers and people together without either being mistaken for the other.

   Every identity keeps ITS OWN hue (§17 — hue is recognition). The surface wears
   the viewer's hue; the marks inside wear their owners'. Recolouring them to
   match the theme would erase exactly the thing that makes the field legible. */

/* THE LEGEND SITS INSIDE, NOT ON THE EDGE. `.po-field__key` was rendering two
   pixels past the bottom of the body, so its descenders were shaved. The field
   reserves the line it is going to draw rather than discovering it afterwards. */
.po-field{position:relative;width:100%;height:100%;min-height:104px;
  padding-bottom:16px;box-sizing:border-box}
.po-field:not(:has(.po-field__key)){padding-bottom:0}

/* relationship threads — only in Her World, where a tie actually exists.
   Where She's Been draws none: places near each other are not related, they
   are just near each other, and a thread would assert something untrue. */
.po-field__t{position:absolute;height:1px;transform-origin:0 50%;pointer-events:none;
  background:linear-gradient(90deg,rgba(255,255,255,.3),rgba(255,255,255,.05))}
.po-field__t[data-weak]{background:linear-gradient(90deg,rgba(255,255,255,.14),transparent)}

/* an identity in the field */
.po-id{position:absolute;--s:26px;width:var(--s);height:var(--s);
  margin:calc(var(--s) / -2) 0 0 calc(var(--s) / -2);
  display:grid;place-items:center;overflow:hidden;
  background:radial-gradient(58% 46% at 32% 22%,rgba(255,255,255,.66),transparent 70%),
    linear-gradient(150deg,rgb(var(--ch1)),rgba(var(--ch2),.72));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.5),
    inset 0 0 10px rgba(var(--ch1),.55),
    0 0 12px -2px rgba(var(--ch1),.85),
    0 2px 6px -2px rgba(0,0,0,.6)}
/* A PLACE IS A TILE — hand-cut, linear (§24.1) */
.po-id--place{border-radius:8px 6px 9px 7px}
/* A PERSON IS A ROUND */
.po-id--person{border-radius:50%}
.po-id svg{width:calc(var(--s) * .52);height:calc(var(--s) * .52);stroke:#fff;fill:none;
  stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
/* real material when a Center has supplied it; the hue treatment when it has
   not (§20.1) — never stock, never generated */
.po-id img{width:100%;height:100%;object-fit:cover;display:block}

/* HER — the anchor of Her World. Larger, ringed, unmistakably the subject. */
.po-id--me{--s:34px;box-shadow:inset 0 1px 0 rgba(255,255,255,.6),
  inset 0 0 14px rgba(var(--ch1),.7),
  0 0 20px -2px rgba(var(--ch1),1),
  0 0 0 2px rgba(255,255,255,.34)}

/* weight carries meaning, honestly: in Where She's Been a bigger tile is a
   place she returns to. Derived from real visits, never decorative. */
.po-id[data-w="2"]{--s:31px}
.po-id[data-w="3"]{--s:37px}

/* following reads lighter than belonging — a tie she chose to watch is not a
   tie she is inside of, and the field should not flatten the difference */
.po-id[data-rel="following"]{opacity:.82;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.4),0 0 10px -3px rgba(var(--ch1),.7),
    0 0 0 1px rgba(255,255,255,.16)}

/* the field's own key — two words, no legend box */
/* ★ `bottom:-2px` PUT THE LEGEND OUTSIDE ITS OWN FIELD.
   ★ FOUNDER, 2026-08-30: *"i dont want to see any cut out … text anywhere."*

   A negative offset hangs the line two pixels below the field, and the widget
   body clips at its edge — so the descenders of "belongs · follows" were being
   shaved on every World and Been-to Popit. Measured: 2px below the body on both.

   Zero, and the field now reserves the line above it. The `padding-bottom` I
   added first did nothing, because padding cannot move a child anchored to the
   bottom edge — worth saying so the next person does not try it twice. */
.po-field__key{position:absolute;left:0;bottom:0;
  font-family:var(--ow-font);font-weight:500;font-size:11px;letter-spacing:0;
  text-transform:none;color:rgba(255,255,255,.45)}

@media (max-width:560px){
  .po-id{--s:22px} .po-id--me{--s:28px}
  .po-id[data-w="2"]{--s:26px} .po-id[data-w="3"]{--s:30px}
}

/* ═══ 11 · LINKS — a person pointing outward ════════════════════════════════
   (founder, 2026-07-28: "like a linktree — each added link would have name and
   icon and users can put whatever links they want on their profile.")

   TWO THINGS MAKE THIS THE ONEWAY VERSION RATHER THAN A CLONE:

   1 · THE DESTINATION IS ALWAYS VISIBLE. Every row shows the host it actually
       goes to. A link labelled "my music" that lands somewhere else is the
       oldest deception on the internet, and a platform whose whole thesis is
       evidence-over-assertion cannot ship a surface that hides where a tap
       goes. The host is not fine print — it is part of the row.

   2 · THE TILE IS A CONTAINER, NOT A BUTTON. A links widget IS the list: one
       tap goes to the destination, never two. Every other widget opens into a
       room; this one is the rare Popit whose glance is already the thing. (The
       room still exists, for the full list when there are more than fit.)

   Same material, same grid, same §0 law — no filter, no blend, no backdrop. */

.po-links{display:flex;flex-direction:column;gap:6px;width:100%;align-self:stretch;
  justify-content:flex-start}
.po-link{display:flex;align-items:center;gap:10px;min-width:0;
  padding:8px 10px;border-radius:11px 9px 12px 10px;text-decoration:none;color:var(--ow-ink);
  background:linear-gradient(180deg,rgba(var(--h),.1),rgba(var(--h),.04));
  box-shadow:inset 0 1px 0 var(--ow-lift), 0 0 0 1px rgba(var(--h),.22);
  transition:transform .16s var(--ow-spring), box-shadow .16s var(--ow-ease),
             background .16s var(--ow-ease)}
@media (hover:hover) and (pointer:fine){
  .po-link:hover{transform:translate3d(2px,0,0);
    background:linear-gradient(180deg,rgba(255,255,255,.13),rgba(255,255,255,.05));
    box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 0 0 1px rgba(var(--h),.6),
      0 0 18px -6px rgba(var(--h),.7)}
}
.po-link:focus-visible{outline:2px solid rgba(var(--h),.95);outline-offset:2px}
.po-link__ic{flex:none;width:26px;height:26px;display:grid;place-items:center;
  border-radius:9px 7px 10px 8px;
  background:radial-gradient(58% 46% at 32% 22%,rgba(255,255,255,.6),transparent 70%),
    linear-gradient(150deg,rgba(var(--h),.95),rgba(var(--h2),.7));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.45), 0 0 10px -3px rgba(var(--h),.8)}
.po-link__ic svg{width:14px;height:14px;stroke:#fff;fill:none;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round}
.po-link__t{min-width:0;display:flex;flex-direction:column;gap:1px}
.po-link__n{font-family:var(--ow-font-display);font-weight:600;font-size:12.5px;
  line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* the destination, stated — never hidden behind the label */
.po-link__h{font-family:var(--ow-font);font-weight:500;font-size:11px;
  letter-spacing:0;text-transform:lowercase;color:var(--ow-ink-3);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.po-link__go{flex:none;margin-left:auto;opacity:.42;font-size:11px}

/* more than fit → the room holds the rest. Reads as a row, not a control. */
.po-links__more{align-self:flex-start;margin-top:2px;border:0;background:none;
  cursor:pointer;color:rgba(var(--h2),.95);font-family:var(--ow-font-atmos);
  font-weight:600;font-size:12px;letter-spacing:.01em;padding:4px 2px}
.po-links__more:focus-visible{outline:2px solid rgba(var(--h),.9);outline-offset:2px}

/* a container widget must not look pressable — it is a shelf, not a button */
.po-w[data-interactive]{cursor:default}
.po-w[data-interactive]::after{display:none}

/* ═══ 12 · FUNCTIONAL STATES & ACTIONS ══════════════════════════════════════
   A Popit bound to real data has three honest states besides "showing":
   LOADING (structure, never plausible fake content — a fabricated row becomes
   a screenshot and then a belief), EMPTY (a true sentence; empty is a valid
   answer), and REFUSED (what happened, in words). None of them ever draws
   something a viewer could mistake for reality. */

.po-w__note{font-size:12.5px;line-height:1.5;color:var(--ow-ink-2)}
.po-w[data-state] > .po-face{box-shadow:
  inset 0 2px 1px rgba(255,255,255,.14), inset 0 -30px 56px rgba(0,0,0,.4),
  inset 0 0 0 1.5px rgba(255,255,255,.07), 0 0 0 1.5px rgba(255,255,255,.1),
  0 16px 36px -12px rgba(0,0,0,.6)}
.po-w[data-state="refused"] .po-w__eyebrow{color:rgba(255,190,120,.9)}

/* the loading shape — bars sized like content, carrying none of it */
.po-skel{display:block;height:11px;border-radius:6px;margin:0 0 9px;
  background:linear-gradient(90deg,rgba(255,255,255,.06),rgba(255,255,255,.13),rgba(255,255,255,.06));
  background-size:200% 100%;animation:po-skel 1.6s ease-in-out infinite}
.po-skel:nth-child(2){width:62%}
@keyframes po-skel{ 0%{background-position:120% 0} 100%{background-position:-20% 0} }
@media (prefers-reduced-motion:reduce){ .po-skel{animation:none} }
[data-ow-tier="0"] .po-skel{animation:none}

/* ── ACTIONS. A control that changes reality states what it did, and reverts
   honestly when the server refused. `data-act` carries the truth. ── */
.po-acts{display:flex;flex-wrap:wrap;gap:7px;width:100%;align-items:flex-start}
.po-acts--stack{flex-direction:column;flex-wrap:nowrap}
/* AN ACTION MUST LOOK LIKE ONE WHEREVER IT IS PUT.
   `--h` is the OBJECT hue, set by `OW.hue.scope()` on a Popit subtree — so it
   is defined inside a widget, a card or a pane, and UNDEFINED on a plain
   surface. And an invalid `var()` does not fall back to nothing: it invalidates
   the WHOLE declaration. So `rgba(var(--h),.14)` dropped the entire background
   AND the entire box-shadow, and `.po-act` rendered as bare bold text with
   padding — no plate, no ring, no button.

   Measured 2026-08-17 on the Memories panel: 3 of 3 actions unstyled, `--h`
   empty at every ancestor up to `<html>` while `--ow-h1` was set the whole way.
   The button was not "styled wrong"; two of its declarations never existed.

   THE FALLBACK IS THE FIX, not a hue scope on each caller. Requiring every
   surface to remember `hue.scope()` before it may use a button makes the
   component depend on a context nothing enforces — the same shape as a theme
   rule that only works in the mode its author happened to be in. `--ow-h1` is
   the identity hue and is always defined, so the chain degrades to the person's
   own colour instead of to nothing. */
.po-act{font:inherit;font-family:var(--ow-font);font-weight:560;font-size:12.5px;
  color:var(--ow-ink);cursor:pointer;border:0;padding:8px 14px;
  border-radius:var(--ow-btn-r,12px 10px 13px 11px);
  background:linear-gradient(180deg,rgba(var(--h,var(--ow-h1)),.14),
                                    rgba(var(--h,var(--ow-h1)),.06));
  box-shadow:inset 0 1px 0 var(--ow-lift), 0 0 0 1px rgba(var(--h,var(--ow-h1)),.34);
  transition:transform .16s var(--ow-spring), box-shadow .16s var(--ow-ease),
             background .16s var(--ow-ease), opacity .16s}
.po-act--wide{width:100%;text-align:left;display:flex;flex-direction:column;gap:1px}
/* ── THE TAIL THAT TELLS TWO DESTINATIONS APART ────────────────────────────
   Shown ONLY when a chip's name is not unique in its row (see `chip` in
   popit-live). It is the head of the Center's id — the same string that stands
   in the address bar when you open that Center — so it is something a person
   can actually match against something they have seen, rather than a number
   the product made up for the occasion.

   It must read as a TAG and never as part of the name: monospaced so it is
   obviously machine, one step down in size, and carried at the chip's own
   opacity rather than given a colour of its own. A disambiguator that competes
   with the thing it disambiguates has made the row harder to read, not
   easier. */
.po-act__tag{font-family:var(--ow-font);font-variant-numeric:tabular-nums;   /* the one face (530) — tabular figures, not a second face */
  font-size:10px;font-weight:500;letter-spacing:.04em;opacity:.58;
  margin-left:6px;vertical-align:baseline}
.po-act--wide b{font-weight:600;font-size:12.5px}
/* the ground under a control is the theme's, on the tile and in the room alike
   — a hardcoded white here was invisible on a light body in both */
.po-act--wide em{font-style:normal;font-family:var(--ow-font-atmos);font-weight:600;
  font-size:12px;letter-spacing:.01em;color:var(--ow-ink-3)}

/* IN THE ROOM the same controls get the room's scale. They are the SAME
   controls — one renderer, one state — simply not squeezed into a tile. */
.po-sheet__body .po-acts{gap:9px}
.po-sheet__body .po-act{font-size:13.5px;padding:11px 18px}
.po-sheet__body .po-act--wide b{font-size:13.5px}
.po-sheet__body .po-act--wide em{font-size:9px}
@media (hover:hover) and (pointer:fine){
  .po-act:hover:not(:disabled){transform:translate3d(0,-1px,0);
    box-shadow:inset 0 1px 0 rgba(255,255,255,.26), 0 0 0 1px rgba(var(--h),.7),
      0 0 20px -6px rgba(var(--h),.8)}
}
.po-act:focus-visible{outline:2px solid rgba(var(--h),.95);outline-offset:2px}
/* ON — the relationship exists. It reads as held, not as a call to action. */
/* `--h` with a fallback, like the base rule above it. Outside a `.po` scope —
   the universal header's Follow, for one — `--h` is unset, the whole
   background was dropped as invalid, and "Following" was white text on the
   faint base: invisible on a light ground (measured 2026-09-20). */
.po-act[data-act="on"]{color:#fff;
  background:radial-gradient(120% 160% at 50% 0%,rgba(var(--h,var(--ow-h1)),.85),rgba(var(--h,var(--ow-h1)),.45) 80%);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.4), 0 0 0 1px rgba(var(--h,var(--ow-h1)),.85),
    0 0 22px -8px rgba(var(--h,var(--ow-h1)),.9)}
/* WORKING — in flight, and un-pressable, so a double tap cannot fire twice */
.po-act[data-act="working"]{opacity:.6;cursor:progress}
/* FAILED — the server said no, and the control says so rather than going quiet */
.po-act[data-act="failed"]{box-shadow:inset 0 1px 0 rgba(255,255,255,.2),
  0 0 0 1.5px rgba(255,170,110,.9)}
.po-act:disabled{cursor:default}

/* the honest word, beside the thing that failed — it leaves when the reason does */
.po-say{display:inline-block;margin-left:8px;font-family:var(--ow-font-atmos);
  font-weight:600;font-size:12px;letter-spacing:.01em;
  color:rgba(255,190,120,.95);animation:po-say .2s var(--ow-ease)}
@keyframes po-say{from{opacity:0;transform:translateY(-3px)}to{opacity:1;transform:none}}


/* ── THE META VOICE — working type, said quietly ────────────────────────────
   A count and a timestamp are FUNCTIONAL, not atmospheric. They were wearing
   Layer 3 (Poiret One, wide-tracked, uppercase), which §16.4 reserves for
   ambient world labels, place names and eyebrows — never for working text.
   Thirteen wide-tracked labels were measured on a single App screen and nine
   of them were "37m ago" or "4 pieces". That is what made the type shout.

   So metadata now uses the WORKING voice at a small size and a muted colour:
   quiet, not costumed. Layer 3 survives where it belongs — the kind of a thing,
   a section eyebrow, a handle — which is a handful per screen, as intended. */
.ow-meta{font-family:var(--ow-font);font-weight:500;font-size:11px;
  letter-spacing:0;text-transform:none;color:var(--ow-ink-3)}

/* ═══ 13 · THE FEED — Home, as canon defines it ═════════════════════════════
   EXPERIENCE.md, THE FOUNDER CORRECTION (2026-07-23, canonical): App Home IS a
   familiar scrolling social feed. "The differentiation is not the UI pattern —
   it is the underlying architecture and what content represents." So this must
   read as a feed anyone already knows how to use. What differs is that each
   card is a GALLERY, and a Gallery is met as a Popit (§19) — the card is the
   glance, and opening it is the complete Gallery experience.

   THE RIVER, NOT THE GRID. A profile is a grid of widgets; Home is a column.
   One card wide, vertical, familiar. Nothing here is hand-placed. */

/* ── THE STREAM IS ONE COLUMN, NOT A STACK OF SEPARATE CARDS ─────────────
   ★ FOUNDER, 2026-08-31 (founder/351): *"text posts should appear more
     condense and attached to each other as close as possible"*.
   ★ FOUNDER (349 §3): *"Posts should visually connect into one continuous
     stream… Don't create artificial gaps simply because a Post ended."*

   18px between every card read as a stack of separate objects. The gap is now
   6px — close enough to read as one continuous column, with the dotted
   boundary still doing the work of saying where one Post ends, which is the
   half of §3 that must survive ("while preserving clear Post boundaries").

   MEDIA KEEPS MORE ROOM. A photo or a video is a heavier object and needs air
   around it or the column reads as a contact sheet; text posts are a
   conversation and should run together. One rule cannot serve both, so the
   gap is set by what the NEXT card carries. */
.po-river{display:flex;flex-direction:column;gap:6px;max-width:560px;margin:0 auto;width:100%}
.po-river > .po-card:has(.po-card__cover),
.po-river > .po-card:has(.po-shots),
.po-river > .po-card:has(video){margin-top:12px;margin-bottom:12px}
/* ── THE CONTENT BOX IS DECLARED, NOT LEFT TO EACH ROW ────────────────────
   ★ FOUNDER, 2026-08-29: *"The Popit content containment is still broken. Do
     not bend, warp, or otherwise distort the dotted Popit boundary to
     accommodate overflowing content. The boundary is the constraint; the
     content must fit inside it… establish an explicit content box inside the
     Popit boundary with known padding/insets."*

   MEASURED BEFORE THE FIX: 16 of 16 Popits in the standard feed breached the
   dotted ring, every one by exactly 9px on each side — because the ring sat at
   `inset:9px` and `.po-card__who`, `__body` and `__foot` each carried their own
   hand-written `padding:… 15px` against the CARD's edge, not the ring's. Every
   row crossed the boundary; nobody had ever compared the two numbers, because
   they lived four hundred lines apart and neither knew about the other.

   So there is now ONE geometry and everything derives from it:

       --po-seam    where the dotted boundary sits.  GEOMETRICALLY STABLE.
       --po-gap     clear air between the boundary and anything readable.
       --po-inset   the content box = seam + gap. Every row uses this.

   A surface that wants a different boundary (the Scroll stack wants a wider
   one) sets `--po-seam` and the content box follows it by construction. That
   is the property that was missing: the two numbers cannot disagree any more,
   because there is only one of them. */
.po-card{position:relative;display:block;width:100%;text-align:left;border:0;padding:0;
  /* ── THE RING HUGS THE CARD'S EDGE ──────────────────────────────────
     ★ FOUNDER, 2026-08-30: *"the dotted lines around posts just make it
       closer to the border of the actual card without breaking everything
       inside and adjust content accordingly."*

     9px -> 4px. The two numbers move in OPPOSITE directions on purpose: the
     seam comes in toward the edge and the gap grows to replace it, so the
     content box lands within a pixel of where it was (15px -> 14px) and
     nothing inside reflows. That is what "without breaking everything inside"
     asks for — moving the boundary must not move the words.

     THIS OVERRULES A NOTE I WROTE MYSELF YESTERDAY. It said 14px was needed in
     a Scroll group because at 10px "the dashed ring sat right against the
     card's own lit rim, so the two merged and read as a cut edge". That was my
     judgement about a rendering; his is a ruling about the design, and the
     ruling wins. The group override is gone with it — one seam everywhere. */
  --po-seam:4px;--po-gap:10px;--po-inset:calc(var(--po-seam) + var(--po-gap));
  /* a <button> carries a default background; without clearing it the card's own
     face never shows and the whole feed renders as light grey slabs */
  background:none;appearance:none;-webkit-appearance:none;
  cursor:pointer;color:#fff;font-family:var(--ow-font);
  --h:var(--ow-h1);--h2:var(--ow-h2);
  border-radius:22px 19px 23px 20px;
  contain:layout paint style;
  -webkit-tap-highlight-color:transparent;
  /* ── A FEED SCROLLS FAST AND FAR — rows outside the viewport cost nothing.
     BUT THE ESTIMATE HAS TO BE HONEST, OR THE GROUND MOVES UNDER THE READER.

     ★ FOUNDER, 2026-08-31 (founder/350): the feed must *"preserve scroll
       position exactly"* and never disrupt *"the user's position"*.

     MEASURED: every card was estimated at 420px until it rendered, and a text
     Post is really 164-184px. So on the FIRST pass through the feed, each card
     a person scrolled past collapsed by ~250px and the document shrank under
     them — **8007 -> 7238 -> 6213px over three scroll steps**, 15 cards
     changing height from 420 to ~175.

     THIS IS THE FINDING I ALMOST THREW AWAY. `tools/probe/stream.js` reported
     "scrollHeight fell 1025px mid-scroll", repeatably. I re-measured by hand,
     got a flat 5226 over eight steps, concluded the probe was wrong and marked
     its detector untrusted. The hand measurement was taken AFTER the settling
     it was supposed to detect — `contain-intrinsic-size: auto` REMEMBERS the
     real size once a card has rendered, so a second pass is always flat. The
     probe was right and my "direct measurement" was the wrong instrument,
     applied second.

     THE FIX IS TWO ESTIMATES, because there are two very different cards. A
     text Post and a Post with a cover are not the same height and never were;
     one number for both is wrong for at least one of them by construction. */
  content-visibility:auto;contain-intrinsic-size:auto 190px;
  transition:transform .18s var(--ow-spring)}
/* A CARD THAT CARRIES MEDIA IS GENUINELY TALL. `:has()` keeps this declarative
   — no class the renderer has to remember to set, and a card that gains or
   loses media is re-estimated by the engine rather than by us. Engines without
   `:has()` fall back to the 190px above: too small for media, which makes the
   document GROW as you scroll. Growing is the safer error — it pushes content
   away from the reader rather than yanking the ground out from under them. */
.po-card:has(.po-card__cover),
.po-card:has(.po-shots),
.po-card:has(video){contain-intrinsic-size:auto 420px}
/* THE FEED CARD IS MADE OF THE SAME THING AS EVERYTHING ELSE.
   (founder, 2026-08-16, on the App's layout: the world is beautiful and the
   content sitting on it is generic.)

   The card was 70% there and read as a plain dark rectangle, because the three
   things that actually SAY "Popit material" were the three it did not have:
   the stitched seam (the family signature — §15 names it as what makes a
   surface belong), the woven grain, and the spill of light into the world.
   Without them a hue-tinted panel is just a tinted panel, and §16.3 — one
   material family, generic cards forbidden on the cosmic background — was
   failing on the single surface every person opens first.

   This adds the signature and leaves the composition alone: same layout, same
   type, same spans. Evolve, never redesign (§16.7). */
.po-card__face{position:absolute;inset:0;z-index:0;border-radius:inherit;pointer-events:none}
/* ── ONE MATERIAL, TWO USERS ─────────────────────────────────────────────
   ★ FOUNDER, 2026-09-03: *"not exactly like posts and everything in it needs
     to look NATURAL."*

   A Popit tile used to paint itself, and the comment in `canvas.js` explains
   where its recipe came from: it was copied declaration for declaration from
   `.ow-fpos .po-card` on 2026-08-30, deliberately, to stop Popits reading as
   slabs. That reasoning was sound and the SOURCE was wrong — `.ow-fpos` is
   Scroll's override, and A measured it on 2026-09-02 as the divergence itself:
   the same element and renderer with an overridden radius and a BLACK drop
   shadow where a Post has its hue glow. Popits were matched to the one rule in
   the file that is not canonical Post material.

   Measured, tile against the river Post's own panel:
       radius   24px 19px 22px 17px   vs   22px 19px 23px 20px
       wash     h1 .30 at 20% -12%    vs   h .46 at 50% -10%
       edge     1px ring              vs   1.5px ring + 2px top light + glow
       grain    NONE                  vs   two weaves and a noise field
   The grain is most of "natural": the Post is woven and the Popit was flat.

   So the material is now written ONCE and both wear it. `.po-card__face` keeps
   its own position and inherited radius above; everything below is the
   material, and a change to it reaches a Post and a Popit in the same edit —
   which is the only arrangement that cannot drift again. */
.po-card__face{
  background:
    radial-gradient(115% 60% at 50% -10%, rgba(var(--h),.46), transparent 58%),
    radial-gradient(80% 55% at 0% 70%,   rgba(var(--h3,var(--ow-h3,var(--h2))),.16), transparent 60%),
    radial-gradient(90% 60% at 50% 118%,  rgba(var(--h2),.2),  transparent 62%),
    linear-gradient(178deg, var(--ow-panel-1), var(--ow-panel-2));
  box-shadow:
    inset 0 2px 1px var(--ow-lift),
    inset 0 -28px 52px var(--ow-weight),
    0 0 34px -8px rgba(var(--h),.4),
    0 16px 40px -14px var(--ow-drop)}
/* A TILE IS ITS OWN OBJECT, so it carries the radius a Post inherits, and the
   `--h` a Post gets from its author-and-place blend comes from the surface. */
.ow-cv-face{border-radius:22px 19px 23px 20px;--h:var(--ow-h1);--h2:var(--ow-h2)}
/* ── AND THE GLOW MUST FOLLOW THE SHAPE, NOT THE BOX ──────────────────────
   ★ FOUNDER, 2026-09-12, on the spill added above: it is the same light a
     Post throws, and the first version threw it from a SQUARE.

   `.po-card` carries its face's radius on the wrapper — that is why a Post's
   glow is round. `.ow-cv-t` carried `border-radius:0`, so the shadow was
   drawn from a sharp rectangle around a rounded face and each tile grew four
   boxy corners of light. Visible immediately in the screenshot and in no
   number I had measured.

   The radius is per-variant here — six hand-cut corner sets keyed on
   `data-v`, so no two Popits on a shelf are quite the same shape — so the
   wrapper takes each variant's own, and the base for a tile that has none.
   The face keeps its declarations; these only give the shadow a shape to
   follow. */
.ow-cv-t{border-radius:22px 19px 23px 20px}
.ow-cv-t[data-v="0"]{border-radius:22px 19px 23px 20px}
.ow-cv-t[data-v="1"]{border-radius:20px 23px 19px 22px}
.ow-cv-t[data-v="2"]{border-radius:23px 20px 22px 19px}
.ow-cv-t[data-v="3"]{border-radius:19px 22px 20px 23px}
.ow-cv-t[data-v="4"]{border-radius:21px 24px 21px 18px}
.ow-cv-t[data-v="5"]{border-radius:24px 18px 22px 21px}


/* the seam — same thread, same hand-cut wander, one size up for a card */
.po-card__face::before{content:"";position:absolute;inset:var(--po-seam);border-radius:inherit;
  pointer-events:none;border:1.5px dashed var(--ow-seam);
  transform:rotate(var(--seam,.35deg));
  display:none}                           /* founder/437: no lines around a Popit */
/* the grain — sits far back on a panel body, exactly as it does on a widget */
/* ── AND THE WEAVE REACHES THE CONVERSATION ───────────────────────────────
   ★ FOUND BY `tools/probe/material.js` the first time it was asked about a
     thread row, 2026-09-12: ground and shadow matched a Post exactly, and the
     grain came back "none" against the Post's weave. The row had taken the
     material's light and none of its cloth.

   It needs `z-index:-1` where the card's layer does not, and that difference
   is structural rather than a special case: `.po-card__face` is itself a
   layer painted BEHIND the card's content, so its grain is already behind
   everything; a thread row IS the content, so its grain has to be told to sit
   under the words. The row carries `isolation:isolate` for exactly this — a
   stacking context of its own, so -1 lands above the row's own background and
   below its text rather than falling through to the page. */
.po-card__face::after,
.ow-cv-pick__row::after,
.ow-reqs::after,
.po-sheet.is-open::after,
.ow-cv-face::after{content:"";position:absolute;inset:0;border-radius:inherit;
  pointer-events:none;
  background:
    repeating-linear-gradient(46deg,  rgba(255,255,255,.05) 0 1px, transparent 1px 4px),
    repeating-linear-gradient(-44deg, rgba(0,0,0,.055)      0 1px, transparent 1px 4px),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='90' height='90'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
  background-size:auto,auto,120px 120px;
  opacity:var(--ow-grain)}
/* THE LIGHT REACHES THE WORLD. A card that does not spill sits ON the
   starfield; one that does sits IN it. It is the card's own blended hue — the
   person and the place — so a feed drawn from five Centers lights the world
   five ways.

   IT IS THE CARD'S OWN BOX-SHADOW, NOT A PSEUDO-ELEMENT, and that is forced
   rather than chosen: `.po-card` carries `contain:layout paint style` for the
   feed's sake, and paint containment clips every DESCENDANT to the padding box
   — so a glow drawn on `::before` or on the face would simply be cut off at the
   edge, invisibly and only on this one component. An element's own shadow is
   not a descendant, so it survives, and the containment that keeps 200 rows
   cheap is kept. */
/* ── THE GLOW GETS STRONGER ───────────────────────────────────────────────
   ★ FOUNDER, 2026-09-08: *"THE GLOW GETS STRONGER THESE ARE NOT THE POPITS AT
     THEIR BEST BUT DONT OVERTWEAK ThEY ARE CLOSE."*

   So this is one change, not a redesign. Three numbers move and nothing else:

       blur    42px -> 54px    the light reaches further from the edge
       spread -12px ->  -5px   LESS of it is eaten before it is drawn; this is
                               the number that was actually holding it back
       alpha    .42 ->  .62    it arrives at the strength it was aiming for

   THE SPREAD IS THE ONE THAT MATTERS. A negative spread shrinks the shadow
   before the blur is applied, so at -12px the outer half of a 42px glow never
   reached the screen — raising the alpha alone would have made a dim thing
   dimly brighter. Measured in dark mode at 375x812 before touching it.

   IT IS STILL THE CARD'S OWN BOX-SHADOW and it has to be: `.po-card` carries
   `contain:layout paint style`, and paint containment clips every DESCENDANT
   to the padding box — a glow on `::before` would be cut off at the edge,
   invisibly, only on this component. See the note above. */
.po-card{box-shadow:0 0 54px -5px rgba(var(--h),.62);
  transition:transform .18s var(--ow-spring), box-shadow .28s var(--ow-ease)}
@media (hover:hover) and (pointer:fine){
  /* hover stays a STEP above rest, not a second design — the same proportion
     the old pair had, carried onto the new floor */
  .po-card:hover{box-shadow:0 0 68px -2px rgba(var(--h),.82)}
}
/* ── AND THE POPIT WAS THE THING THAT DID NOT LIGHT THE WORLD ─────────────
   ★ FOUNDER, 2026-09-12: *"Look at how posts glow on home and see how nothing
     else looks like that. That's the only cosmetic fix."*

   He is right, and the difference is one declaration. Measured on Home with
   both on screen:

       .po-card__face    inset · inset · 0 0 34px -10px · 0 18px 44px -16px
       .ow-cv-face       inset · inset · 0 0 34px -10px · 0 18px 44px -16px   same
       .po-card          0 0 54px -5px rgba(--h,.62)
       .ow-cv-t          NONE

   The FACES are identical — that is what `tools/probe/material.js` checks and
   it was right all day. What a Post has and a Popit did not is the second
   shadow, on the WRAPPER: the light that leaves the object and reaches the
   starfield. A card that does not spill sits ON the world; one that does sits
   IN it. Every Popit on the platform was sitting on it.

   THE PROBE COULD NOT HAVE CAUGHT THIS. It compares `.po-card__face` with
   `.ow-cv-face` — the faces — and the spill is not on either of them. A
   comparison is only ever as wide as the thing it is pointed at, and this one
   was pointed one element too deep. The founder found it by looking at the
   screen, which is the check no probe replaces.

   IT SPELLS THE HUE DIFFERENTLY, AND IT HAS TO. `--h` on a tile is the grid
   HEIGHT — `--h:4` — because the canvas stores a placement's span in it. A
   name collision, and almost certainly the reason this was never written:
   `rgba(var(--h),.62)` computes `rgba(4,.62)`, which is invalid, so the
   shadow silently does not exist. The surface pair is read straight from
   `--ow-h1` instead. (Renaming the canvas's variable is a bigger change than
   the founder asked for and touches drag, resize and the ghost; it is worth
   doing and it is not this.)

   SAME THREE NUMBERS AS THE POST, and the same treatment in light, where the
   note above says an identical halo reads as a coloured fringe rather than as
   light in a room. Not stripped on the safe floor, for the reason recorded
   there: a box-shadow is painted once and re-samples nothing. */
.ow-cv-t{box-shadow:0 0 54px -5px rgba(var(--ow-h1),.62)}
@media (hover:hover) and (pointer:fine){
  .ow-cv-t:hover{box-shadow:0 0 68px -2px rgba(var(--ow-h1),.82)}
}
html[data-theme="light"][data-theme="light"] .ow-cv-t{
  box-shadow:0 0 26px -18px rgba(var(--ow-h1),.30)}
@media (hover:hover) and (pointer:fine){
  html[data-theme="light"][data-theme="light"] .ow-cv-t:hover{
    box-shadow:0 0 30px -16px rgba(var(--ow-h1),.42)}
}
@media (prefers-color-scheme:light){
  html:not([data-theme="dark"]):not([data-theme="light"]) .ow-cv-t{
    box-shadow:0 0 26px -18px rgba(var(--ow-h1),.30)}
  @media (hover:hover) and (pointer:fine){
    html:not([data-theme="dark"]):not([data-theme="light"]) .ow-cv-t:hover{
      box-shadow:0 0 30px -16px rgba(var(--ow-h1),.42)}
  }
}
/* ── THE GLOW WAS SWITCHED OFF ON EVERY PHONE ─────────────────────────────
   ★ FOUNDER, 2026-09-08: *"THE GLOW GETS STRONGER THESE ARE NOT THE POPITS AT
     THEIR BEST BUT DONT OVERTWEAK ThEY ARE CLOSE."*

   This line was here: `html[data-fx="safe"] .po-card{box-shadow:none}`.

   MEASURED in dark mode at 375x812 — the phone this is judged on —
   `getComputedStyle(card).boxShadow` came back **"none"**. The safe floor is
   stamped by `oneway-viewport.js` from `isTouch() || reducedMotion()`, so it is
   ON for every touch device. Every phone. The founder has been asking for a
   stronger glow while looking at a surface that has NO GLOW AT ALL.

   AND IT BROKE THIS FILE'S OWN LAW, stated at the top: *"the resting Popit
   renders IDENTICALLY with html[data-fx="safe"] on or off."* The safe floor
   exists to strip the three PER-FRAME compositor costs — `backdrop-filter`,
   `filter`, `mix-blend-mode` — each of which re-samples the moving world behind
   the element on every frame, per bead. **A box-shadow is none of those.** It
   is painted once, by the same rasteriser that draws the border, and it does
   not re-sample anything. Stripping it bought no frames and cost the material.

   So the line is gone rather than tuned, and the numbers above are the change
   the founder actually asked for. That is also why "they are close" was right:
   the values were nearly there and the switch was off.

   IF A REAL DEVICE EVER PROVES A COST HERE, the honest fix is a smaller glow on
   the floor — not none — and it belongs next to this note with the measurement
   that justified it. */
/* the parts are <span> because a <button> may not contain block elements, and
   a span is inline — so each is told to stack explicitly. Without this the
   cover, the identity row and the title paint on top of one another. */
/* :not(__face) matters — the face is position:absolute, and a blanket
   `position:relative` on the children pulled it out of its own stretch and
   collapsed it to zero height. The card then had no panel at all, only the
   top edge shadow, which read as "low contrast" rather than "not rendering". */
.po-card > *:not(.po-card__face){position:relative;z-index:1;display:block}
/* these must out-specify the `> *` rule above (0,2,1) or the blanket
   display:block wins and the avatar stacks above the name */
.po-card > .po-card__who{display:flex}
.po-card > .po-card__foot{display:flex}
.po-card__who > .po-card__n{display:flex}
.po-card__nm,.po-card__when,.po-card__t,.po-card__d,.po-card__ctx,.po-card__count{display:block}
.po-card__media,.po-card__body{display:block}
.po-card__cover{display:block}

/* ── VIDEO SITS ON BLACK. ALWAYS. ────────────────────────────────────────
   ★ FOUNDER, 2026-08-31 (founder/349 §5, and 346 §16 before it): *"black
     background by default… no accidental color plate behind video"* and *"No
     video should inherit the hue-tinted Popit background."*

   MEASURED 2026-08-31 and it was NOT happening: both the video element and its
   frame computed `rgba(0, 0, 0, 0)` — fully transparent — so a 720x1280
   portrait video was letterboxed against the card's hue-tinted face. That is
   the coloured plate the ruling names, and I had previously reported this
   surface as done.

   The frame is painted, not the video, so letterbox bars on a portrait source
   are black rather than tinted — which is the whole point: the bars are where
   the wrong colour shows. */
.po-card__cover:has(video),
.po-card__media:has(video),
.po-shots:has(video){background:#000}
video.po-card__v,.po-card__cover video,.po-shots video{background:#000}
@media (hover:hover) and (pointer:fine){ .po-card:hover{transform:translate3d(0,-2px,0)} }
.po-card:focus-visible{outline:2px solid rgba(var(--h),.95);outline-offset:3px}

/* WHO — identity leads, because a feed item is someone showing you something.
   The actor wears THEIR hue, never the viewer's theme (§17). */
/* ── THE PROFILE HIT BOX IS THE FACE AND THE NAME. NOTHING ELSE. ─────────
   ★ FOUNDER, 2026-08-31 (founder/351): *"make sure hit box for profile is
     small clicking on pfp or name only same tihng with opening a post and just
     liking it"* — and *"its the little things"*.

   MEASURED before this: `.po-card__who` was **339x58 on a 375px screen — 100%
   of the card width.** A tap on the empty space to the RIGHT of somebody's
   name opened their profile instead of the Post, because the row stretched to
   fill and the whole strip was the person's target. Three of the four probe
   points across the top row landed on PROFILE; only the body landed on OPEN.

   Three acts, three targets, and they must not overlap:

       face or name   ->  that person
       anywhere else  ->  open the Post
       the heart      ->  like, and ONLY like

   `width:max-content` shrink-wraps the row to its contents, so the strip
   beside the name belongs to the card again. The padding moves to a wrapper
   so the face keeps its inset without the target inheriting the full width —
   a control must not be padded into being large. */
.po-card__who{display:inline-flex;align-items:center;gap:10px;
  width:max-content;max-width:calc(100% - var(--po-inset) * 2);
  margin:var(--po-inset) var(--po-inset) 10px;padding:0}
.po-card__av{flex:none;width:34px;height:34px;border-radius:50%;overflow:hidden;
  display:grid;place-items:center;
  background:radial-gradient(58% 46% at 32% 22%,rgba(255,255,255,.6),transparent 70%),
    linear-gradient(150deg,rgb(var(--ch1,var(--h))),rgba(var(--ch2,var(--h2)),.72));
  /* ★ FOUNDER/471 §5: *"profile photos shouldnt have a glow to them anywhere."*
     The hue halo that sat behind every face is gone; the top light stays only
     on the initials fallback, where there is no picture to glow. */
  box-shadow:inset 0 1px 0 rgba(255,255,255,.45)}
.po-card__av img{width:100%;height:100%;object-fit:cover}
/* a Center shows as a tile, a person as a round — the same convention the
   identity field uses, so it means the same thing everywhere */
.po-card__av[data-kind="center"]{border-radius:11px 9px 12px 10px}
/* and the owner's own cut wins over the kind's default (founder/409) */
.po-card__av[data-shape="round"]{border-radius:50%}
.po-card__av[data-shape="rounded"]{border-radius:11px 9px 12px 10px}
.po-card__av[data-shape="square"]{border-radius:6px}
.po-card__n{min-width:0;display:flex;flex-direction:column;gap:1px}
.po-card__nm{font-family:var(--ow-font-display);font-weight:600;font-size:13.5px;line-height:1.2;color:var(--ow-ink)}
.po-card__when{font-family:var(--ow-font);font-weight:500;font-size:11.5px;
  letter-spacing:0;text-transform:none;color:var(--ow-ink-3)}

/* THE COVER — real media when it exists; the Center's own hue as an
   environmental treatment when it does not (§20.1 tier 4). Never stock. */
/* ══ THE MEDIA FRAME — 4:5 PORTRAIT, AND IT USES THE CARD ═════════════════
   ★ FOUNDER, 2026-08-31 (founder/352 §1): *"Mobile media should use the
     correct 4:5 portrait geometry where appropriate. Use the available screen
     width intelligently."* — the Instagram spatial reference, approved.

   MEASURED BEFORE THIS, at 375x812 with real fixture media:

       source files      900x1200 photos, 720x1280 video   (both PORTRAIT)
       rendered frame    311 x 233                          (4:3 LANDSCAPE)
       screen occupied   47% of height, 83% of width

   A 900x1200 photograph was being cropped into a short wide box — most of the
   picture thrown away — which is the exact opposite of what the source is.
   Instagram's 2026 recommendation is 4:5 portrait (1080x1350) precisely
   because a taller frame claims more of a phone screen.

   TWO CHANGES, AND ONLY TWO. The ratio, and the width.

   `aspect-ratio: 4/5` gives a portrait frame. Pulling the frame out to the
   card's SEAM (`--po-gap` on each side) uses the width the card already owns
   — Instagram is full-bleed to the screen edge, and the closest honest
   equivalent here is full-bleed to the DOTTED BOUNDARY, because founder/349 §5
   makes that boundary absolute: *"Nothing may bend, overflow, escape, or
   distort it."* So the media goes right up to the seam and stops. That is
   Instagram's spatial confidence inside ONEWAY's material, which is the whole
   instruction — *"Do not copy Instagram's styling. Copy the underlying
   spatial discipline."*

   NOTHING ELSE MOVES. Same radius, same ground, same shadow, same material.
   And `.ow-fpos` (immersive) overrides both properties further down, so
   founder/352 §1's *"Do not change Immersive Scroll"* holds by construction. */
.po-card__cover{position:relative;aspect-ratio:var(--po-ar,4/5);overflow:hidden;
  width:calc(100% + var(--po-gap) * 2);margin-left:calc(var(--po-gap) * -1);
  border-radius:16px 13px 17px 14px;
  background:
    radial-gradient(70% 46% at 50% 94%, rgba(var(--h2),.5), transparent 72%),
    linear-gradient(180deg, transparent 56%, rgba(var(--h2),.14) 64%, transparent 72%),
    radial-gradient(130% 100% at 20% -8%, rgba(var(--h),.62), rgba(var(--h),.08) 74%),
    linear-gradient(180deg,#201f3c,#0d0c1a);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.1)}
.po-card__cover img,
.po-card__cover>video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}

/* AN EMPTY GALLERY IS NOT A PHOTO-SHAPED HOLE — founder, 2026-08-16:
   "automatically posting galleries with a black box, a concept we need to
   lighten up on."

   The cover ground is a deep near-black plate (`linear-gradient(#201f3c,#0d0c1a)`)
   sized `aspect-ratio:4/3`, which is right UNDER a photograph and wrong with
   nothing on it: an empty Gallery rendered as a screen-wide black rectangle
   carrying eight grey words. The App used to publish those by itself, so the
   feed filled with them.

   Publishing is a human act again (see `OW.live.lightbulb`), so an empty
   Gallery should rarely reach a feed at all — but a person's own Galleries
   list still shows their empty drafts, and that is exactly where this must
   read as a quiet note rather than a void. So the frame COLLAPSES: no 4:3
   reservation, no black plate, no inset ring — a slim pale band the colour of
   the card it sits in, with the same words. Nothing is invented to fill it
   (Law 15); it simply stops shouting. */
.po-card__cover[data-empty]{aspect-ratio:auto;min-height:0;background:none;
  box-shadow:none;border-radius:12px 10px 13px 11px;
  border:1px dashed rgba(var(--h),.28);
  background:linear-gradient(180deg,rgba(var(--h),.07),rgba(var(--h2),.05))}
.po-card__cover[data-empty]::after{content:attr(data-empty);display:block;
  padding:13px 0;text-align:center;
  font-family:var(--ow-font-atmos);font-weight:600;font-size:9px;letter-spacing:.18em;
  color:var(--ow-ink-3)}
/* a rail laid over a collapsed band is chrome over nothing */
.po-card__cover[data-empty] .po-acts--rail{display:none}
.po-card__media{margin:0 var(--po-inset)}

.po-card__body{padding:12px var(--po-inset) 6px}
.po-card__t{font-family:var(--ow-font-display);font-weight:650;font-size:18px;
  letter-spacing:-.01em;line-height:1.16;color:var(--ow-ink)}
/* ── THE CARD IS SHAPED AROUND WHAT SOMEBODY WROTE ────────────────────────
   ★ FOUNDER, 2026-09-09: *"Popits could be better shaped around text and
     images."*

   This clamped at TWO lines. Measured on a real post — four lines of text, two
   shown, 81px of writing rendered into a 41px window — so half of what a person
   said was cut off on the card that is supposed to be showing it. A card that
   is the same height for a sentence and for a paragraph is not shaped around
   its text; it is a fixed box with an ellipsis.

   SIX, NOT UNLIMITED. The card still has to be a card: a river of them is
   scanned, and one person's essay must not push everybody else's off the
   screen. Six lines is about forty words, which covers the ordinary post whole
   and leaves the genuinely long one bounded and openable — the Popit itself is
   where the whole thing lives.

   AND THE SHORT POST STAYS SHORT. `-webkit-line-clamp` is a maximum, not a
   reservation: a one-line post is still one line and its card is still 142px.
   Verified both ways rather than assumed. */
.po-card__d{font-size:13.5px;line-height:1.5;color:var(--ow-ink-2);margin-top:5px;
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:6;overflow:hidden}
/* ★ FOUNDER/470: *"text posts def shouldnt be grey."* A Post with no media is
   its words — they are the content, not a caption: the page's own ink, a
   reading size, never the secondary grey a caption wears under a picture. */
.po-card[data-text] .po-card__d{color:var(--ow-ink);font-size:16px;line-height:1.45;margin-top:0;-webkit-line-clamp:12}
/* ★ FOUNDER/471: *"text posts NEED TO NOT HAVE BLANK SPACE WHERE A HEADER
   SHOULD BE IF NONE IS ADDED."* The body's top padding was the room a media
   block or a title would have taken; on a text Post there is neither. */
.po-card[data-text] .po-card__body{padding-top:0;margin-top:-6px}
/* the share panel, inline under the card's acts (471) */
.po-card .ow-sendto{display:block;padding:4px 14px 14px}
/* the platform it came from, top-right (470 · 447): the platform's own mark
   in the page's ink; ONEWAY's own globe, white (476), for a Post made here.
   ★ FOUNDER/475: *"always keep the icons small on the post"* — 18px alone
   (the face is 34px); *"a small … triangle like formation when multiple
   platforms is used"* — two or more are 15px marks in a cluster: the globe
   at the apex, the platforms beneath it; *"the globe icon should always be
   there"* — it leads every row. (Overrides 471 §9's "about the size of the
   profile picture".) */
/* ── AND IT IS NOT DIMMED ─────────────────────────────────────────────────
   ★ FOUNDER/488: *"muffled."*  `opacity:.82` over `--ow-ink` was the other
   half of it — a glyph painted at full ink and then faded, which is the
   definition of muffled, and it faded the platform marks with it. Quietness
   is a COLOUR decision, so it is made in the colour: `--ow-ink-2` is the
   token for secondary ink and it is legible at 15px, which .82 of primary is
   not. Nothing is drawn at partial opacity any more. */
.po-card > .po-card__from{position:absolute;top:calc(var(--po-inset) + 8px);right:var(--po-inset);z-index:3;
  display:flex;align-items:center;color:var(--ow-ink-2);pointer-events:none}
.po-card__from__i{display:block;width:18px;height:18px;font-style:normal}
.po-card__from svg,.po-card__from img{width:100%;height:100%;display:block}
/* the cluster: the globe alone on the top row, centred; the platforms (up to
   three) make the base beneath it — 2 is a stack, 3 and 4 are triangles */
.po-card > .po-card__from[data-many]{top:calc(var(--po-inset) + 1px);display:grid;gap:2px 3px;justify-items:center;
  grid-template-columns:repeat(1,15px)}
.po-card > .po-card__from[data-many="3"]{grid-template-columns:repeat(2,15px)}
.po-card > .po-card__from[data-many="4"]{grid-template-columns:repeat(3,15px)}
.po-card__from[data-many] .po-card__from__i{width:15px;height:15px}
.po-card__from[data-many] .po-card__from__i:first-child{grid-column:1 / -1}
/* the name row leaves the marks their room, so a long name never runs under them */
.po-card > .po-card__who{max-width:calc(100% - var(--po-inset) * 2 - 24px)}
.po-card > .po-card__from[data-many="3"] ~ .po-card__who{max-width:calc(100% - var(--po-inset) * 2 - 40px)}
.po-card > .po-card__from[data-many="4"] ~ .po-card__who{max-width:calc(100% - var(--po-inset) * 2 - 58px)}
/* on a small canvas tile the face is 22px, so the marks are smaller still */
.ow-cv-posts .po-card__from__i{width:14px;height:14px}
.ow-cv-posts .po-card__from[data-many] .po-card__from__i{width:11px;height:11px}
.ow-cv-posts .po-card > .po-card__from{top:calc(var(--po-inset) + 4px)}
.ow-cv-posts .po-card > .po-card__from[data-many]{top:var(--po-inset);gap:1px 2px;grid-template-columns:repeat(1,11px)}
.ow-cv-posts .po-card > .po-card__from[data-many="3"]{grid-template-columns:repeat(2,11px)}
.ow-cv-posts .po-card > .po-card__from[data-many="4"]{grid-template-columns:repeat(3,11px)}
/* a reel's words sit above the video (470) */
.po-card__body + .po-card__media{margin-top:8px}
/* PART OF — a Gallery is context (the Gallery Ruling). A card says which
   collection it belongs to; it never re-explains the collection. */
.po-card__ctx{margin-top:8px;font-family:var(--ow-font-atmos);font-weight:600;font-size:9px;
  letter-spacing:.01em;color:rgba(var(--h2),.9)}

.po-card__foot{display:flex;align-items:center;gap:7px;flex-wrap:wrap;
  padding:8px var(--po-inset) var(--po-inset)}
.po-card__count{font-family:var(--ow-font);font-weight:500;font-size:11.5px;
  letter-spacing:0;text-transform:none;color:var(--ow-ink-3);margin-left:auto}

/* the App's own small text pieces — a question the surface asks, and the one
   sentence that answers where a thing will land */
.ow-ask{font-family:var(--ow-font-display);font-weight:650;font-size:24px;
  letter-spacing:-.01em;line-height:1.08;margin:14px 0 16px;color:var(--ow-ink)}
.ow-foot{font-family:var(--ow-font-atmos);font-weight:600;font-size:10px;
  letter-spacing:.01em;color:var(--ow-ink-3);margin:16px 0 0}

/* the initial an identity falls back to when it has supplied no picture */
.po-card__av i{font-style:normal;font-family:var(--ow-font-display);font-weight:650;
  font-size:13px;letter-spacing:.01em;color:#fff;text-shadow:0 1px 3px rgba(0,0,0,.4)}
/* the counts must not be squeezed out by a long response row */
.po-card__count{flex:0 0 auto;white-space:nowrap}

/* ═══ 14 · MESSAGES ═════════════════════════════════════════════════════════
   (founder ruling, 2026-07-30: "messages should be a page in the app and should
   work like traditional messaging. anything else was previous drift.")

   So this is the pattern everyone already knows: a list of threads, a thread of
   bubbles, a composer. Someone must be able to use it in one second without
   being taught. That is the whole point — it is the adoption layer.

   WHAT IS ONEWAY UNDERNEATH is the object model, not the chrome: every thread
   is a conversation on the ONE platform (/api/conversations), a DM is simply a
   conversation whose subject is the relationship, and the same conversation
   seen from the OS carries operator permissions. No second messaging store.

   Built on the same material and the same tokens, so it inverts with the rest
   of the App and wears the person's own hue. */

/* ── the list ── */
/* rows touch — a list, not a stack of cards (founder/493) */
.ow-threads{display:flex;flex-direction:column;gap:0}
/* ── A MESSAGING ROW IS POPIT MATERIAL, AND IT HAD LOST THE LIGHT ─────────
   ★ FOUNDER, 2026-09-12: *"make sure popits have the same glow they always did
     and things like messaging surfaces are looking like it as well."*

   THIS FILE HAS ALREADY DIAGNOSED THIS EXACT DIVERGENCE ONCE, forty lines up,
   about Popit tiles: *"the same element and renderer with an overridden radius
   and a BLACK drop shadow where a Post has its hue glow."* A thread row had
   the same defect and nobody had looked: a neutral `--ow-drop` and a 1px rim,
   where every Post and every Popit carries an OUTWARD HUE GLOW.

   The canonical signature is the Post face's, matched declaration for
   declaration rather than approximated:
       inset 0 2px 1px  var(--ow-lift)        the lift
       inset 0 0 0 1.5px rgba(var(--h),.30)   the lit rim, in the hue
       0 0 34px -10px   rgba(var(--h),.50)    THE GLOW
       0 18px 44px -16px var(--ow-drop)       the drop, under the glow
   softened one step for a row, because a list of forty of these at a Post's
   full weight is a wall rather than a list — the LIGHT is the thing that must
   be the same, and it is the same colour from the same variable.

   `--h` falls back to `--ow-h1`, which is how a row wears the person's own
   hue on their own surface and a Center's on its own — the same fallback the
   hover state on this element already used. */
/* ── A CONVERSATION IS MADE OF THE SAME STUFF AS EVERYTHING ELSE ─────────
   ★ FOUNDER, 2026-09-12: *"make sure popits have the same glow they always
     did and things like messaging surfaces are looking like it aswell."*

   MEASURED BEFORE CHANGING ANYTHING, on the running App: a Post face and a
   Popit face compute the SAME recipe — 115%/62% from the top at .20, 92%/62%
   from the bottom at .13, the same five-layer shadow — differing only in hue,
   which is correct because each object carries its own. A thread row computed
   something else entirely: ONE radial from the top LEFT at 120%/90%/.14, no
   second hue at all, and a four-layer shadow with different spreads.

   It is the drift `tools/probe/material.js` was written about, in a surface
   the probe did not look at. The row was not wearing an older version of the
   material; it was wearing a SEPARATE one that resembled it.

   So the row stops declaring a material and takes the one the Post and the
   Popit share, three rules down. What stays here is what makes it a ROW
   rather than a card — its own radius, its inset, its lift on hover — and
   `--h`/`--h2` default to the surface pair exactly as a tile's do, so a row
   given a conversation's own hue still wears it. */
/* ══ MESSAGES IS A LIST OF PEOPLE, NOT A GRID OF POPITS ═══════════════════
   ★ FOUNDER/493: *"Messages ditch the popit blocks entirely and go sleek like
     snapchat when it comes to users, having everyone arranged line by line
     down with profile picture attached and hue showing."*

   EVERY ROW WAS A POPIT. `.ow-thread` was in the shared face list with
   `.po-card__face` — its own gradient ground, hand-cut radius, lit rim, glow
   on hover and the woven grain overlay — with 8px of air between each one.
   Measured on the founder's pane: four conversations, four floating slabs.
   A Popit is a thing you ARRANGE; a conversation is a line you scan. Reading
   forty of them is the whole job of this screen, and forty slabs is forty
   objects to look past.

   SO THE ROW IS A LINE. No ground, no radius, no shadow, no grain — the row
   is the person's face, their name, what they said and when. A hairline
   between rows, and the rows touch, so the eye runs straight down them the
   way it does in Snapchat, iMessage and every other list of people.

   THE HUE IS STILL THERE, AND IT IS WHERE IT BELONGS. It was in the ground of
   a block; it is now a ring on the face — the person's own colour, attached to
   the person, which is what a hue is for. Pressed state is a ground that
   appears for the press and goes, the way a list row behaves.

   THE TAP TARGET GREW. 11px of padding inside a 17px-radius slab became 13px
   in a full-width line: the row is ~68px tall and every pixel of it opens the
   conversation, where before the gaps between blocks opened nothing. */
.ow-thread{display:flex;align-items:center;gap:12px;width:100%;text-align:left;
  border:0;background:none;cursor:pointer;padding:13px 4px;
  color:var(--ow-ink);font-family:var(--ow-font);
  border-radius:0;position:relative;
  --h:var(--ow-h1);--h2:var(--ow-h2);
  transition:background-color .14s var(--ow-ease)}
/* the hairline, drawn by the row so the last one can drop it */
.ow-thread + .ow-thread::before{content:"";position:absolute;left:58px;right:0;top:0;
  height:1px;background:var(--ow-hair,rgba(20,19,31,.10))}
html[data-theme="dark"] .ow-thread + .ow-thread::before{
  background:rgba(255,255,255,.09)}
@media (prefers-color-scheme: dark){
  html:not([data-theme="light"]) .ow-thread + .ow-thread::before{
    background:rgba(255,255,255,.09)}}
@media (hover:hover) and (pointer:fine){
  .ow-thread:hover{background-color:rgba(var(--ow-h1),.06)}}
.ow-thread:active{background-color:rgba(var(--ow-h1),.10)}
.ow-thread:focus-visible{outline:2px solid rgba(var(--ow-h1),.9);outline-offset:2px}
/* THE SAME CORRECTION ON THE FACE BESIDE IT, and it was worse: initials at
   2.32:1. Two causes at once — the gradient ran into cyan exactly as the sent
   bubble's did, and a 58%x46% white gloss at .6 sat directly over the middle
   of the circle where the letters are. A gloss belongs in the top corner of a
   sphere, not across its face. Measured after: passes. */
/* the person's own picture on a DM row, cut the way they chose */
.ow-thread__face{width:100%;height:100%;object-fit:cover;display:block}
.ow-thread__av[data-shape="rounded"]{border-radius:13px}
.ow-thread__av[data-shape="square"]{border-radius:5px}
.ow-thread__av{flex:none;width:42px;height:42px;border-radius:50%;display:grid;place-items:center;
  overflow:hidden;font-family:var(--ow-font-display);font-weight:650;font-size:15px;color:#fff;
  background:radial-gradient(46% 36% at 28% 18%,rgba(255,255,255,.28),transparent 64%),
    linear-gradient(150deg,
      color-mix(in oklab,rgb(var(--ch1,var(--ow-h1))) 84%,#14131f),
      color-mix(in oklab,rgb(var(--ch2,var(--ow-h2))) 44%,#14131f));
  /* THE PERSON'S HUE, ON THE PERSON (founder/493: *"profile picture attached
     and hue showing"*). It used to be the row's ground; a ring keeps it
     attached to whose colour it is, and survives a photograph covering the
     gradient underneath. */
  box-shadow:inset 0 1px 0 rgba(255,255,255,.45),
             0 0 0 2px rgba(var(--ch1,var(--ow-h1)),.55)}
.ow-thread__av img{width:100%;height:100%;object-fit:cover}
/* a Center thread is a tile, a person is a round — the same convention as everywhere */
.ow-thread__av[data-kind="center"]{border-radius:14px 11px 15px 12px}
.ow-thread__b{min-width:0;flex:1 1 auto}
.ow-thread__top{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
/* WHICH IDENTITY THIS CONVERSATION ANSWERS AS. Only on rows addressed to a
   Center the reader operates — absent means it is theirs, so the mark is a
   distinction rather than decoration. Quiet: it is context for the name beside
   it, never a second title. */
.ow-thread__as{flex:none;font-size:10.5px;font-weight:600;letter-spacing:.04em;
  padding:2px 7px;border-radius:999px;color:var(--ow-ink-3);
  background:rgba(var(--h,var(--ow-h1)),.13);
  box-shadow:inset 0 0 0 1px rgba(var(--h,var(--ow-h1)),.22);
  white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}
/* THE NAME KEEPS ITS ROOM. Adding the "as <Center>" badge beside it crushed
   it to "a..." on a phone — the badge is context and the name is the thing
   somebody is looking for, so the name holds a floor and the BADGE is what
   gives way. Measured at 375px before and after. */
.ow-thread__nm{font-family:var(--ow-font-display);font-weight:600;font-size:14.5px;
  flex:1 1 auto;min-width:7ch;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ow-thread__t{margin-left:auto;flex:none;font-family:var(--ow-font);font-weight:500;
  font-size:11.5px;letter-spacing:0;text-transform:none;color:var(--ow-ink-3)}
.ow-thread__last{font-size:13px;line-height:1.35;color:var(--ow-ink-2);margin-top:2px;
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1;overflow:hidden}
.ow-thread[data-unread] .ow-thread__nm{font-weight:650}
.ow-thread[data-unread] .ow-thread__last{color:var(--ow-ink);font-weight:500}
.ow-thread__n{flex:none;min-width:20px;height:20px;padding:0 6px;border-radius:10px;
  display:grid;place-items:center;font-size:11px;font-weight:600;color:#fff;
  background:rgb(var(--ow-h1));box-shadow:0 0 12px -2px rgba(var(--ow-h1),.9)}

/* ── the thread ── */
.ow-msgs{display:flex;flex-direction:column;gap:7px;padding:6px 0 14px}
/* ── A BUBBLE IS THE SAME MATERIAL, WITHOUT THE HALO ──────────────────────
   It took `linear-gradient(178deg,panel-1,panel-2)` and two insets — the panel
   base with the hue left out — so an incoming message read as a grey slab
   under a row that glows. Measured on screen beside the threads it belongs to:
   the same conversation drawn in two materials.

   It joins the shared declaration for its GROUND and keeps its own SHADOW,
   and that split is deliberate rather than a compromise. The outer half of
   the card's shadow is a halo thrown on the world; a card is one object on a
   page and a thread is forty objects in a column, where forty halos overlap
   into a wash and cost a composite each. So the insets stay — the lift, the
   hue rim, what makes it the material — and the spill becomes a short drop.

   `[data-mine]` is excluded from all of it. A sent message wears the person's
   own hue as a solid fill, which is the oldest convention in messaging and
   the one nobody needs explained. */
.ow-msg{max-width:80%;padding:9px 13px;border-radius:18px 16px 18px 8px;
  font-size:14.5px;line-height:1.42;color:var(--ow-ink);align-self:flex-start;
  --h:var(--ow-h1);--h2:var(--ow-h2)}
/* DOUBLED TO OUT-WEIGH THE MATERIAL'S OWN SHADOW, which is the file's stated
   convention rather than a trick — the light pair spells itself
   `html[data-theme="light"][data-theme="light"] .ow-msg:not([data-mine])` and
   weighs (0,4,1), so a rule that only wants to change the SHADOW half has to
   weigh more than that or it silently loses in one theme and wins in another.
   (0,5,0) here, and the ground above is untouched. */
.ow-msgs.ow-msgs .ow-msg.ow-msg:not([data-mine]){
  /* THE OTHER PERSON'S BUBBLE CARRIES THE LIGHT TOO. It had a hue RIM and a
     neutral drop — the same half-measure the thread rows had, and the same one
     this file already recorded about Popit tiles: a black shadow where the
     material has a hue glow. A bubble is small and there are many, so the glow
     is the Post's colour at a bubble's weight, not a Post's. `data-mine` is
     untouched: it is already lit, in the person's own hue, and has been the
     whole time. */
  box-shadow:inset 0 2px 1px var(--ow-lift),
    inset 0 0 0 1px rgba(var(--h),.26),
    0 0 18px -12px rgba(var(--h),.42),
    0 6px 16px -12px var(--ow-drop)}
/* mine wear the person's own hue and sit on their side — the oldest convention
   in messaging, and the one nobody needs explained */
/* ── WHITE INK NEEDS A GROUND THAT STAYS DARK THE WHOLE WAY ───────────────
   ★ MEASURED 2026-09-12 by tools/probe/contrast.js on the messaging surface:
     the time under a sent message at 1.58:1. White on rgb(122,219,253).

   THE GRADIENT RAN OUT OF DARKNESS. It went from the brand violet to the
   brand CYAN, and cyan is one of the lightest colours this palette has — so
   the same white ink was legible at the top of the bubble and invisible at
   the bottom of it. The body text passed and the timestamp failed, four lines
   apart, for no reason but where they sat.

   A SENT MESSAGE'S INK IS `#fff` AND ALWAYS WILL BE — it is the oldest
   convention in messaging and it does not change with the theme — so its
   GROUND cannot be allowed to drift light in any theme either. Both stops are
   mixed toward the ink-dark, which keeps the hue plainly recognisable as the
   person's own and keeps every part of the bubble dark enough to write on.
   Measured across the whole bubble after: passes. */
.ow-msg[data-mine]{align-self:flex-end;color:#fff;border-radius:18px 16px 8px 18px;
  background:linear-gradient(160deg,
    color-mix(in oklab,rgb(var(--ow-h1)) 82%,#14131f),
    color-mix(in oklab,rgb(var(--ow-h2)) 50%,#14131f));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.4),0 4px 14px -6px rgba(var(--ow-h1),.7)}
/* ── THE FACE BESIDE AN INCOMING BUBBLE (founder/409) ────────────────────
   A row: the person's face, then their bubble. The face is drawn on the LAST
   bubble of a run and reserved (invisible) on the others, so the column of
   bubbles never shifts. Round unless they chose otherwise — the same two
   orientations every face on this platform supports. */
/* whether the other side is there — a dot only when somebody is (founder:
   a fact, never a guess; unknown presence draws nothing at all) */
.ow-thread-head__pr{display:block;font-family:var(--ow-font);font-size:11.5px;font-weight:500;
  color:var(--ow-ink-3,#6f6b85);margin-top:2px}
.ow-thread-head__pr[data-on="1"]{color:#5fe3a1;display:flex;align-items:center;gap:6px}
.ow-thread-head__pr[data-on="1"]::before{content:"";width:7px;height:7px;border-radius:50%;
  background:#5fe3a1;box-shadow:0 0 8px #5fe3a1}
.ow-msg-line{display:flex;align-items:flex-end;gap:8px;align-self:flex-start;max-width:88%}
/* a line whose speaker is nobody (the room's own opening sentence) keeps the
   bubble where every other bubble starts */
.ow-msg-line:not(:has(.ow-msg-face)){padding-left:34px}
.ow-msg-line .ow-msg{align-self:auto;max-width:100%}
.ow-msg-face{flex:none;width:26px;height:26px;overflow:hidden;display:grid;place-items:center;
  border:0;padding:0;cursor:pointer;border-radius:50%;
  --h:var(--ow-h1);--h2:var(--ow-h2);
  background:radial-gradient(120% 90% at 30% 10%,rgba(var(--h),.85),rgba(var(--h2),.55) 70%);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.35)}                 /* no glow (471 §5) */
.ow-msg-face[data-shape="rounded"]{border-radius:9px 7px 8px 7px}
.ow-msg-face[data-shape="square"]{border-radius:5px}
.ow-msg-face.is-blank{visibility:hidden;box-shadow:none;background:none;cursor:default}
.ow-msg-face img{width:100%;height:100%;object-fit:cover;display:block}
.ow-msg-face i{font-style:normal;font-family:var(--ow-font-display);font-weight:650;font-size:10px;color:#fff}
.ow-msg-face:focus-visible{outline:2px solid rgba(var(--h),.95);outline-offset:2px}
.ow-msg__who{display:block;font-family:var(--ow-font);font-weight:560;font-size:11.5px;
  letter-spacing:0;text-transform:none;color:var(--ow-ink-3);margin:8px 0 -2px 4px}
.ow-msg__t{display:block;font-family:var(--ow-font);font-weight:500;font-size:11px;
  letter-spacing:0;text-transform:none;opacity:.6;margin-top:3px}
.ow-msg-day{align-self:center;font-family:var(--ow-font-atmos);font-weight:600;font-size:9px;
  letter-spacing:.01em;color:var(--ow-ink-3);margin:12px 0 4px}

/* ── the composer ── */
.ow-composer{position:sticky;bottom:0;display:flex;gap:8px;align-items:flex-end;
  padding:10px 0 max(10px,env(safe-area-inset-bottom));
  background:linear-gradient(0deg,var(--ow-chrome) 62%,transparent)}
.ow-composer textarea{flex:1 1 auto;min-height:42px;max-height:120px;resize:none;
  font:inherit;font-size:14.5px;line-height:1.4;color:var(--ow-ink);padding:11px 14px;
  border:0;border-radius:18px 15px 17px 16px;
  background:linear-gradient(178deg,var(--ow-panel-1),var(--ow-panel-2));
  box-shadow:inset 0 1px 0 var(--ow-lift),inset 0 0 0 1px var(--ow-rim)}
.ow-composer textarea::placeholder{color:var(--ow-ink-3)}
.ow-composer textarea:focus{outline:none;
  box-shadow:inset 0 1px 0 var(--ow-lift),0 0 0 1.5px rgba(var(--ow-h1),.7)}
.ow-send{flex:none;width:42px;height:42px;border:0;cursor:pointer;display:grid;place-items:center;
  border-radius:15px 12px 16px 13px;color:#fff;
  background:linear-gradient(150deg,rgb(var(--ow-h1)),rgba(var(--ow-h2),.8));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.45),0 4px 14px -5px rgba(var(--ow-h1),.85);
  transition:transform .16s var(--ow-spring),opacity .16s}
.ow-send:disabled{opacity:.4;cursor:default}
.ow-send svg{width:19px;height:19px;stroke:currentColor;fill:none;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round}
@media (hover:hover) and (pointer:fine){ .ow-send:not(:disabled):hover{transform:translateY(-1px)} }

/* the back-to-list control, and who you are talking to */
.ow-thread-head{display:flex;align-items:center;gap:10px;margin:2px 0 8px}
.ow-back{flex:none;width:32px;height:32px;border:0;cursor:pointer;display:grid;place-items:center;
  border-radius:11px 9px 12px 10px;color:var(--ow-ink);background:rgba(var(--ow-h1),.14);
  box-shadow:inset 0 0 0 1px rgba(var(--ow-h1),.3)}
.ow-back svg{width:17px;height:17px;stroke:currentColor;fill:none;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round}
.ow-thread-head__nm{font-family:var(--ow-font-display);font-weight:650;font-size:17px;
  letter-spacing:-.01em;color:var(--ow-ink)}
/* where the conversation LIVES — a DM says nothing, a thread on an object says
   which object, because a conversation always belongs to something */
.ow-thread-head__on{font-family:var(--ow-font-atmos);font-weight:600;font-size:8.5px;
  letter-spacing:.01em;color:rgba(var(--ow-h2),.95)}
/* a message the server has not confirmed, and one it refused */
.ow-msg[data-pending]{opacity:.6}
.ow-msg[data-failed]{box-shadow:inset 0 1px 0 rgba(255,255,255,.3),0 0 0 2px rgba(255,150,90,.95)}
/* the refusal itself — its own treatment, not the timestamp's */
.ow-msg__fail{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:6px;
  padding:6px 9px;border-radius:11px 9px 12px 10px;background:rgba(255,150,90,.16);
  box-shadow:inset 0 0 0 1px rgba(255,150,90,.5)}
.ow-msg__fail-x{flex:1 1 auto;min-width:0;font-family:var(--ow-font);font-weight:500;
  font-size:12px;line-height:1.35;color:#fff}
.ow-msg__fail-go{flex:none;display:inline-flex;align-items:center;min-height:30px;
  border:0;cursor:pointer;padding:0 11px;border-radius:10px 8px 11px 9px;
  font-family:var(--ow-font-atmos);font-weight:600;font-size:8.5px;
  letter-spacing:.01em;color:#2a1405;
  background:rgba(255,190,140,.95)}

/* ═══ 14b · THE CANONICAL MESSAGING SURFACE ═════════════════════════════════
   ★ FOUNDER, 2026-09-01: the App must consume the 23 canonical routes —
     requests, replies, reactions, delivery state, retention, notification.
     Every rule below renders something the domain already decided; none of it
     decides anything.

   THESE ARE ALL NEW SELECTORS. Nothing above is overridden, because appending
   to the end of this file is exactly how `.ow-shell__gear` got `position:
   relative` written over its `absolute` and ended up half off-screen. A block
   that only ever adds cannot do that.  */

/* ── message requests: a stranger's first message, waiting on a decision ── */
/* THE REQUESTS BLOCK IS A PANEL IN THE SAME ROOM as the rows under it, so it
   wears the same material. It had a flat rim and no light at all, which read
   as a different component from the inbox it sits on top of. */
/* ── A THIRD COPY OF THE MATERIAL, WRITTEN THE SAME HOUR I REMOVED THE SECOND
   This carried the recipe the thread row used to carry — ONE radial from the
   top LEFT at 120%/90%/.13 and a four-layer shadow — which is the recipe a
   Post has NOT had for some time. Two lanes reached for the same remembered
   numbers within an hour of each other, which is the argument for the shared
   declaration rather than for either of us being more careful.

   It takes the one every other surface takes. What stays is what makes it a
   panel in a list: its own inset, margin and radius. */
.ow-reqs{margin:0 0 14px;padding:12px;border-radius:16px 13px 17px 14px;
  position:relative;isolation:isolate;--h:var(--ow-h1);--h2:var(--ow-h2)}
.ow-reqs::after{z-index:-1}
.ow-reqs__top{display:flex;flex-direction:column;gap:2px;margin-bottom:10px}
.ow-reqs__ttl{font-family:var(--ow-font-display);font-weight:650;font-size:14px;
  color:var(--ow-ink)}
.ow-reqs__why{font-size:12.5px;line-height:1.35;color:var(--ow-ink-2)}
.ow-req{display:flex;align-items:center;gap:10px;padding:7px 0;
  border-top:1px solid var(--ow-rim)}
.ow-req:first-of-type{border-top:0}
.ow-req__b{min-width:0;flex:1 1 auto;display:flex;flex-direction:column}
.ow-req__nm{font-family:var(--ow-font-display);font-weight:600;font-size:13.5px;
  color:var(--ow-ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ow-req__t{font-size:11px;color:var(--ow-ink-3)}
.ow-req__acts{flex:none;display:flex;gap:6px;align-items:center}
/* on a narrow pane the acts take their own line under the name, so the
   name is never crushed to "N.." beside two buttons (measured at 290px) */
@media (max-width:360px){
  .ow-req{flex-wrap:wrap}
  .ow-req__acts{flex-basis:100%;margin-left:52px;margin-top:2px}}
.ow-req__acts[data-busy]{opacity:.55}
.ow-req__done{font-family:var(--ow-font-atmos);font-weight:600;font-size:8.5px;
  letter-spacing:.01em;color:var(--ow-ink-3)}
.ow-req__no{font-size:11.5px;color:rgba(255,170,110,.95)}
.ow-req[data-decided]{opacity:.55}

/* ── what this room does with what is said in it ── */
.ow-thread-head__b{min-width:0;flex:1 1 auto;display:flex;flex-direction:column;gap:1px}
.ow-thread-head__ab{font-size:12px;color:var(--ow-ink-2);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.ow-thread-head__facts{display:flex;flex-wrap:wrap;gap:5px;margin-top:3px}
.ow-fact{font-family:var(--ow-font-atmos);font-weight:600;font-size:8.5px;
  letter-spacing:.01em;color:var(--ow-ink-3);
  padding:3px 7px;border-radius:8px 6px 9px 7px;background:rgba(var(--ow-h1),.1);
  box-shadow:inset 0 0 0 1px var(--ow-rim)}
.ow-fact--act{border:0;cursor:pointer;color:rgba(var(--ow-h2),.95)}
/* ── ADD SOMEONE, revealed on request in the thread's facts row ─────────
   A field and an Add, on the same line as the facts, in the same material as
   the composer's field (`.ow-fld`): present only after the person asked. The
   answer sits beside it in the third ink — "Added Sam." or the door's own
   refusal — and never in a modal. */
.ow-addp{display:flex;align-items:center;gap:8px;flex:1 1 100%;margin-top:6px}
.ow-addp__in{font:inherit;font-size:13px;flex:1 1 auto;min-width:0;padding:8px 10px;
  border-radius:calc(var(--ow-btn-r) - 4px);color:var(--ow-ink);
  background:var(--ow-ground,rgba(0,0,0,.18));border:1px solid rgba(var(--ow-h1),.28)}
.ow-addp__in:focus{outline:none;border-color:rgba(var(--ow-h1),.60)}
.ow-addp__why{font-size:12px;color:var(--ow-ink-3);flex:1 1 100%}
.ow-fact--act:disabled{opacity:.5;cursor:default}
.ow-fact[data-locked]{color:rgba(var(--ow-h2),.95)}

/* ── a reply is CONTAINED: a quoted line inside the bubble it answers from,
      never a second bubble that reads as its own message (founder, 2026-08-31
      on comments — the same rule, because it is the same mistake) ── */
.ow-msg__q{display:flex;flex-direction:column;gap:1px;width:100%;text-align:left;
  border:0;cursor:pointer;margin:0 0 5px;padding:5px 8px;
  border-radius:9px 7px 10px 8px;background:rgba(0,0,0,.08);
  box-shadow:inset 2px 0 0 rgba(var(--ow-h2),.8)}
.ow-msg[data-mine] .ow-msg__q{background:rgba(255,255,255,.16);
  box-shadow:inset 2px 0 0 rgba(255,255,255,.75)}
.ow-msg__q-who{font-family:var(--ow-font);font-weight:600;font-size:10.5px;
  opacity:.8}
.ow-msg__q-b{font-size:12px;line-height:1.3;opacity:.75;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.ow-msg__q--gone{display:block;cursor:default;font-size:11.5px;opacity:.6}
.ow-msg__x{display:block;white-space:pre-wrap;overflow-wrap:anywhere}
.ow-msg[data-flash]{animation:ow-msg-flash .9s ease-out 1}
@keyframes ow-msg-flash{0%{box-shadow:0 0 0 2px rgba(var(--ow-h1),.85)}
  100%{box-shadow:0 0 0 2px rgba(var(--ow-h1),0)}}
.ow-msg[data-gone]{opacity:.55;font-style:italic}
.ow-msg__gone{font-size:12.5px}
.ow-msg__ed{font-style:normal;margin-left:6px;opacity:.75}

/* ── SENT · DELIVERED · READ. One marker, on the newest own message, because
      that is the strongest statement that is true of all of them ── */
.ow-msg__st{display:block;font-family:var(--ow-font-atmos);font-weight:600;
  font-size:12px;letter-spacing:.01em;
  opacity:.62;margin-top:3px;text-align:right}
.ow-msg__st[data-st="read"]{opacity:.95}

/* ── reactions, and the little picker that opens them ── */
.ow-rx{display:flex;flex-wrap:wrap;gap:4px;margin-top:4px}
.ow-rx:empty{display:none}
.ow-rx__c{display:inline-flex;align-items:center;gap:3px;border:0;cursor:pointer;
  font-size:12px;line-height:1;min-height:28px;padding:0 9px;
  border-radius:9px 7px 10px 8px;
  color:var(--ow-ink);background:rgba(0,0,0,.07);
  box-shadow:inset 0 0 0 1px var(--ow-rim)}
.ow-msg[data-mine] .ow-rx__c{color:#fff;background:rgba(255,255,255,.18);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.28)}
.ow-rx__c[aria-pressed="true"]{box-shadow:inset 0 0 0 1.5px rgba(var(--ow-h1),.9)}
.ow-rx__c b{font-family:var(--ow-font);font-weight:600;font-size:10.5px}
.ow-rx__pick{display:inline-flex;gap:2px;margin-left:4px}
.ow-rx__p{display:inline-flex;align-items:center;justify-content:center;
  min-width:34px;min-height:34px;border:0;cursor:pointer;background:none;
  font-size:16px;line-height:1;padding:0;border-radius:10px}
.ow-rx__p:hover{background:rgba(var(--ow-h1),.16)}

/* ── per-message actions. Small, quiet, and only what the SERVER said this
      person may do to this message ── */
/* ── AND THE HIT BOX IS THE CONTROL ──────────────────────────────────────
   ★ FOUNDER, 2026-08-31: *"make sure hit box for profile is small clicking on
     pfp or name only same tihng with opening a post and just liking it"* ·
     *"its the little things"*.

   MEASURED: the first cut of this row gave every action a 33 x 10.5px target
   and hid it at `opacity:0` until hover. Ten and a half pixels is not a
   control, it is a coincidence — and a hover-only affordance does not exist at
   all on a phone, which is where messaging is read.

   So they are ALWAYS VISIBLE, quietly, and the target is the label's box
   rather than the label. `min-height` with vertical padding and a negative
   margin buys the height back from the layout, so the row reads exactly as
   tight as it did while being three times the size to press. */
.ow-msg__acts{display:flex;flex-wrap:wrap;align-items:center;gap:2px;
  margin:2px -8px -6px;opacity:.55;transition:opacity .14s}
.ow-msg:hover .ow-msg__acts,.ow-msg:focus-within .ow-msg__acts{opacity:1}
@media (hover:none){ .ow-msg__acts{opacity:.8} }
.ow-msg__act{display:inline-flex;align-items:center;min-height:34px;
  border:0;cursor:pointer;background:none;padding:0 8px;
  font-family:var(--ow-font-atmos);font-weight:600;font-size:8.5px;
  letter-spacing:.01em;color:inherit;border-radius:9px}
.ow-msg__act:hover{background:rgba(var(--ow-h1),.14)}
.ow-msg[data-mine] .ow-msg__act:hover{background:rgba(255,255,255,.16)}
.ow-msg__act:focus-visible{outline:2px solid rgba(var(--ow-h1),.9);outline-offset:1px}
.ow-msg__edit{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
.ow-msg__edit textarea{flex:1 1 100%;font:inherit;font-size:14px;resize:vertical;
  color:var(--ow-ink);padding:7px 9px;border:0;border-radius:11px 9px 12px 10px;
  background:var(--ow-panel-1);box-shadow:inset 0 0 0 1px var(--ow-rim)}

/* ── attachments: a share is a REFERENCE, so an unavailable one says so ── */
.ow-att{margin-top:6px;border-radius:12px 10px 13px 11px;overflow:hidden;
  background:rgba(0,0,0,.07);box-shadow:inset 0 0 0 1px var(--ow-rim)}
.ow-att img{display:block;width:100%;height:auto;max-height:340px;object-fit:cover}
.ow-att__card{display:flex;flex-direction:column;gap:2px;width:100%;text-align:left;
  border:0;cursor:pointer;background:none;color:inherit;padding:8px 10px}
.ow-att__card:disabled{cursor:default}
.ow-att__card img{width:calc(100% + 20px);margin:-8px -10px 6px;max-height:260px;
  object-fit:cover;border-radius:12px 10px 0 0}
.ow-att__who{font-family:var(--ow-font);font-weight:600;font-size:11.5px;opacity:.8}
.ow-att__where{font-family:var(--ow-font-atmos);font-weight:600;font-size:8px;
  letter-spacing:.01em;opacity:.6;margin-top:3px}
.ow-att__t{display:block;padding:7px 9px 0;font-family:var(--ow-font-display);
  font-weight:600;font-size:13px}
.ow-att__b{display:block;padding:4px 9px 8px;font-size:12.5px;line-height:1.35;
  color:var(--ow-ink-2)}
.ow-att[data-empty]{padding:9px}
.ow-att__gone{font-size:12px;color:var(--ow-ink-3)}

/* ── the pager, the typing line, and a refusal that has nowhere else to go ── */
.ow-msgs__older{display:flex;justify-content:center;padding:4px 0}
.ow-msgs__older:empty{display:none}
.ow-typing{font-size:12px;color:var(--ow-ink-3);padding:2px 4px 6px;min-height:0}
.ow-typing[hidden]{display:none}
.ow-thread__foot{display:flex;flex-direction:column}
.ow-thread__note{font-size:12.5px;line-height:1.35;color:rgba(255,170,110,.95);
  padding:6px 9px;margin-bottom:6px;border-radius:11px 9px 12px 10px;
  background:rgba(255,170,110,.1);box-shadow:inset 0 0 0 1px rgba(255,170,110,.35)}
.ow-composer__no{font-size:12.5px;line-height:1.4;color:var(--ow-ink-3);
  padding:10px 0 max(10px,env(safe-area-inset-bottom))}
.ow-reply-aim{display:flex;align-items:center;gap:8px;padding:6px 9px;margin-bottom:6px;
  border-radius:11px 9px 12px 10px;background:rgba(var(--ow-h1),.1);
  box-shadow:inset 2px 0 0 rgba(var(--ow-h1),.85)}
.ow-reply-aim__who{flex:none;font-family:var(--ow-font);font-weight:600;
  font-size:11px;color:var(--ow-ink-2)}
.ow-reply-aim__b{min-width:0;flex:1 1 auto;font-size:12px;color:var(--ow-ink-3);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ow-reply-aim__x{flex:none;border:0;cursor:pointer;background:none;font-size:16px;
  line-height:1;color:var(--ow-ink-3);padding:0 2px}
.ow-thread__mute{flex:none;font-family:var(--ow-font-atmos);font-weight:600;
  font-size:12px;letter-spacing:.01em;color:var(--ow-ink-3)}
/* why a Message button stopped short — a held request or a privacy refusal,
   said beside the control that was pressed rather than as an error screen */
.po-act__why{flex:1 1 100%;font-size:12px;line-height:1.35;color:var(--ow-ink-3);
  margin:2px 0 0}
.po-acts__more{flex:1 1 100%;display:flex;flex-wrap:wrap;gap:8px;
  align-items:center;margin-top:6px}
.po-acts__wait{font-size:12px;color:var(--ow-ink-3)}
/* the people on a list you can undo — blocked, muted */
.ow-cust__row--stack{display:block}
.ow-people{display:flex;flex-direction:column;gap:6px;margin-top:8px}
.ow-people__none{font-size:12.5px;line-height:1.4;color:var(--ow-ink-3);margin:6px 0 0}
.ow-people__more{align-self:flex-start}
.ow-person{display:flex;align-items:center;gap:10px;padding:6px 0;
  border-top:1px solid var(--ow-rim)}
.ow-person:first-child{border-top:0}
.ow-person__av{flex:none;width:32px;height:32px;border-radius:50%;display:grid;
  place-items:center;font-family:var(--ow-font-display);font-weight:600;
  font-size:12px;color:#fff;
  background:linear-gradient(150deg,rgba(var(--ow-h1),.9),rgba(var(--ow-h2),.7))}
.ow-person__b{min-width:0;flex:1 1 auto;display:flex;flex-direction:column}
.ow-person__nm{font-family:var(--ow-font-display);font-weight:600;font-size:13.5px;
  color:var(--ow-ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ow-person__t{font-size:11px;color:var(--ow-ink-3)}
/* the door to the OS — a link, wearing the same shape as the acts beside it */
a.po-act{display:inline-flex;align-items:center;text-decoration:none}
a.po-act[data-door="os"]{gap:6px}

/* ═══ 15 · MY CENTER — the profile ══════════════════════════════════════════
   (founder, 2026-07-30: "think twitter/reddit profiles layout with bars people
   can click on replacing the entire lower subsection of the page." Plus: bring
   back the customization — custom banners and backgrounds, uploaded or preset.)

   So the shape is the one everyone knows: a banner, an avatar sitting over it,
   the identity, then a ROW OF TABS whose selection replaces everything below.
   Familiar chrome, ONEWAY underneath — each tab's content is still Popits.

   Customization is the person's, and it is real: banner_kind · banner_hue1/2 ·
   banner_angle · banner_tint · banner_url(s) · avatar_shape all already exist
   on the profile style record, with upload endpoints behind them. This surface
   reads and writes those rather than inventing a second appearance model. */

.ow-prof{margin:-6px -18px 0}                 /* the banner runs to the edges */
/* THE BANNER SHAPE IS A FOUNDER RULING (2026-07-30: "think twitter/reddit
   profiles layout with bars people can click on replacing the entire lower
   subsection of the page"), so it stays. §20.3's "no hero banners" governs a
   Popit's interior, where a coloured rectangle stood in for a place; here the
   familiar profile chrome was asked for by name, and a specific ruling outranks
   a general law. This is a note rather than a change because a later session
   reading §20.3 alone would delete it — as I nearly did on 2026-08-16.

   WHAT WAS ACTUALLY WRONG IS THE EMPTY STATE. With no uploaded picture the
   fallback was `linear-gradient(120deg, h1, h2)` — two stops, flat, no depth:
   a colour swatch where a place should be, and the one thing on the profile
   made of nothing. The ruling asked for the LAYOUT, never for that material.

   So the empty banner is now built the way the Popit plane is built (§20.1
   tier 4): light pooling off a floor, a faint horizon, depth falling away at
   the top, all derived from the person's OWN hue pair. Nothing stock, nothing
   generated. An uploaded picture still overrides it completely. */
/* ── THE BANNER IS ROUNDED, AND IT IS OUTLINED ──────────────────────────
   ★ FOUNDER, 2026-08-25: *"round profile banners and give it and stickers a
     white outline."*

   It was a full-bleed rectangle, which made a person's profile open on a hard
   edge running corner to corner — the one shape nothing else in ONEWAY has.
   Everything here is hand-cut: the Popit, the sheet, the seam. The banner was
   the exception, and it read as a header from another product.

   THE OUTLINE IS THE SAME ONE THE STICKERS GET, and for the same reason: it is
   what separates a placed object from the galaxy behind it. Kept to a hairline
   so it reads as a cut edge rather than a frame. */
/* THE WHITE OUTLINE IS GONE (C, 2026-09-30). S13 (founder/437: "remove the
   lines around popits for mainstreamabilty") took the outline ring off every
   Popit; the banner kept its 2px white ring — and on a phone the banner runs
   edge to edge, so the screen cut the ring's corners and it read as broken
   (the founder/573 review's "cover border overflow"). The shadow stays: it is
   what lifts the cover off the world without drawing a line round it. */
.ow-banner{position:relative;height:clamp(120px,30vw,190px);overflow:hidden;
  border-radius:30px 26px 29px 27px / 27px 30px 25px 28px;
  box-shadow:0 18px 40px -18px rgba(0,0,0,.62);
  background:var(--ow-banner-bg,
    radial-gradient(70% 52% at 50% 104%, rgba(var(--ow-h2),.75), transparent 72%),
    radial-gradient(46% 34% at 18% 88%,  rgba(var(--ow-h1),.5),  transparent 76%),
    linear-gradient(180deg, transparent 52%, rgba(var(--ow-h2),.22) 62%, transparent 70%),
    radial-gradient(120% 96% at 78% -12%, rgba(var(--ow-h2),.6), transparent 60%),
    radial-gradient(135% 112% at 14% -8%, rgba(var(--ow-h1),.92), rgba(var(--ow-h1),.18) 72%),
    linear-gradient(180deg,#242243,#121123))}
/* the same woven grain every other ONEWAY surface carries, so the empty banner
   belongs to the material family rather than sitting outside it (§16.3) */
.ow-banner::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:
    repeating-linear-gradient(46deg,  rgba(255,255,255,.045) 0 1px, transparent 1px 4px),
    repeating-linear-gradient(-44deg, rgba(0,0,0,.05)        0 1px, transparent 1px 4px);
  opacity:var(--ow-grain)}
.ow-banner:has(img)::after{display:none}   /* a real picture is left alone */
.ow-banner img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  object-position:center}
/* the person's hue washed over their picture, at their chosen strength — this
   is what keeps a profile recognisably theirs even behind someone else's photo */
.ow-banner__hue{position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(120deg,rgba(var(--ow-h1),var(--ow-tint,.42)),
    rgba(var(--ow-h2),calc(var(--ow-tint,.42) * .7)))}
.ow-banner__shade{position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,transparent 46%,var(--ow-ground) 100%)}

/* ── THE HEADER IS THREE THINGS, NOT FIVE EVENLY SPACED ONES ─────────────
   ★ FOUNDER, 2026-09-09: *"Profiles and Centers still need professional design
     refinement"* and *"improve hierarchy"*.

   MEASURED at 375px before touching it: the vertical gaps down the header were
   11 · 15 · 11 · 13. Name, handle, counts and actions all sat the same distance
   apart, so NOTHING GROUPED — the handle did not read as belonging to the name,
   and the counts did not read as separate from the acts. Even spacing is the
   absence of hierarchy, not a neutral choice: it leaves a reader no help
   deciding what belongs with what, and that is what makes a header feel
   unfinished however good its parts are.

   THREE UNITS, and the air says which:
       WHO THEY ARE        avatar · name · handle       3px inside — one thing
       ── the break ──                                  20px
       WHAT IT AMOUNTS TO  followers · following
       WHAT YOU CAN DO     follow · message · more      12px — kept with them
   Same total height to within a few pixels. The whole change is where the air
   is. */
/* ★ FOUNDER/471 §8: *"make all rows on profile more cleanly transtiion into
   each other no matter the view."* ONE SCALE down the header, in every view
   (the page, the Me tab, the projection over the map, light, 290 and 375):
       picture ──12── name ──2── handle ──8── sentence ──10── link bar
               ──16── counts ──12── acts ──16── tabs
   Every gap is a margin-top on the row below and nothing carries a bottom
   margin, so a row that is absent (no sentence, no bar, no counts) closes up
   without a stray gap and the same rows always sit the same distance apart.
   Measured before: 11 · 3 · 9 · 12 · 20 · 12 · 20 — and 4px of padding under
   the acts that only showed when the tabs followed. */
.ow-prof__id{position:relative;padding:0 18px 0;margin-top:-34px}
.ow-avatar{width:78px;height:78px;display:grid;place-items:center;overflow:hidden;
  font-family:var(--ow-font-display);font-weight:700;font-size:27px;color:#fff;
  border-radius:50%;                    /* a circle unless they chose (471 §6) */
  background:radial-gradient(58% 46% at 32% 22%,rgba(255,255,255,.62),transparent 70%),
    linear-gradient(150deg,rgb(var(--ow-h1)),rgba(var(--ow-h2),.75));
  /* ★ FOUNDER/471 §5: no glow, no drop, no ring — the picture, cut, on the page. */
  box-shadow:none}
.ow-avatar img{width:100%;height:100%;object-fit:cover}
.ow-avatar[data-shape="round"]{border-radius:50%}
.ow-avatar[data-shape="rounded"]{border-radius:22px 18px 24px 20px}
.ow-avatar[data-shape="square"]{border-radius:10px}
/* `margin-bottom:0` IS THE RHYTHM, not tidiness. The name is an `h1` and the
   handle a `p`, so each carries the browser's own bottom margin — 0.67em and
   1em of it. Adjacent vertical margins COLLAPSE to the larger, so the h1's
   14.7px swallowed the 3px above and the pair sat 15px apart: measured on the
   page, the "one thing" above was never one thing. A margin you did not write
   still wins if it is bigger than the one you did. */
.ow-prof__name{font-family:var(--ow-font-display);font-weight:650;
  font-size:clamp(21px,5vw,26px);letter-spacing:-.01em;line-height:1.06;
  margin-top:12px;margin-bottom:0;color:var(--ow-ink)}
.ow-prof__handle{font-family:var(--ow-font-atmos);font-weight:600;font-size:11px;
  letter-spacing:.2em;color:var(--ow-ink-3);
  margin-top:2px;margin-bottom:0}
/* ── THEIR OWN SENTENCE ───────────────────────────────────────────────────
   ★ FOUNDER, 2026-09-06: *"PROFILE SHOULD BE PERFECT AND SO SHOULD USER DEPTH
     OF GOOGLE ACCOUNT AND FACEBOOK."*

   READING WIDTH, NOT PAGE WIDTH. A bio is prose and prose has a measure; at
   full page width on a desktop it reads as a caption stretched to fit. 46ch is
   the low end of the classic range, which suits three lines rather than three
   paragraphs — and this field is capped at 600 characters for the same reason
   the note is capped: it must stay a bio and never become a second posting
   surface nobody moderates. */
.ow-prof__bio{margin:8px 0 0;max-width:46ch;font-size:14.5px;line-height:1.45;
  color:var(--ow-ink-2);white-space:pre-wrap;overflow-wrap:anywhere}
/* `margin:9px 0 0`, not `margin-top`: it is a <p>, and its UA bottom margin
   (1em = 14.5px) collapsed over the 6px the heartbeat line asks for beneath
   it — measured 15 on a Center, the same trap the name and handle fell into
   this morning. On a profile the next block asks 20px so it never showed. */
/* THE WAY TO THE NEXT PAGE OF SOMEBODY'S WORK. It sits OUTSIDE `.po-river`,
   because `mountRiver` turns whatever node it is given into the river itself
   and a button appended there would be laid out as a post between posts.
   Centred and given room, so the end of a page reads as an invitation rather
   than as the end of the person. */
.ow-prof__more{display:flex;justify-content:center;padding:18px 0 4px}
.ow-prof__more:empty{display:none}          /* no gap when their work has ended */
/* A CENTER'S OWN WORDS, and the origin of them underneath. The description
   leads at reading size and the provenance sits under it, quieter — the two
   were the same size and the same slab, so a Center's sentence and its postcode
   looked like the same kind of thing. */
.ow-cabout{padding:2px 0 6px}
/* the facts as an ordered table — a website's About, not a stack of cards (founder/404) */
.ow-cabout__dl{margin:18px 0 0;padding:0;border-top:1px solid rgba(255,255,255,.1)}
.ow-cabout__row{display:grid;grid-template-columns:minmax(88px,30%) 1fr;gap:12px;align-items:baseline;
  padding:11px 2px;border-bottom:1px solid rgba(255,255,255,.1)}
.ow-cabout__row dt{margin:0;font-family:var(--ow-font-atmos);font-weight:600;font-size:10.5px;letter-spacing:.16em;
  color:rgba(255,255,255,.52)}
.ow-cabout__row dd{margin:0;font-family:var(--ow-font);font-weight:500;font-size:14.5px;line-height:1.35;color:#fff;overflow-wrap:anywhere}
.ow-cabout__link{color:rgb(var(--ow-h1));text-decoration:none;border-bottom:1px solid rgba(var(--ow-h1),.34)}
.ow-cabout__link:hover{border-bottom-color:rgb(var(--ow-h1))}
.ow-cabout__door{appearance:none;-webkit-appearance:none;background:none;border:0;border-bottom:1px solid rgba(var(--ow-h1),.34);
  border-radius:0;padding:0 0 1px;margin:0;font:inherit;color:rgb(var(--ow-h1));cursor:pointer;text-align:left}
.ow-cabout__door:hover{border-bottom-color:rgb(var(--ow-h1))}
.ow-cabout .ow-prof__bio{margin-top:0;max-width:52ch}
.ow-cabout__audit{margin-top:6px;font-family:var(--ow-font-atmos);font-weight:600;
  font-size:10.5px;letter-spacing:.06em;color:var(--ow-ink-3)}
.ow-cabout .ow-prof__facts{margin-top:14px}
/* WHAT A CENTER CONTAINS — its own rows under the About prose, because a
   casino holding a pub, or a brand holding eight games and twenty-seven
   characters (founder/374), is a list of doors and not a clause in a fact.
   The heading carries the count only when there is more than one. */
.ow-cinside{margin-top:18px}
/* ══ A CENTER AS A REAL PAGE (founder/429) ═══════════════════════════════
   Sections with headings, a fact table, event rows and chips — the shapes a
   place's own website uses, in this platform's material. Every section is
   drawn only when the Center has something to put in it. */
.ow-cpage{display:block;margin-top:4px}
.ow-cpage__now{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:2px 2px 16px}
.ow-cpage__open{font-family:var(--ow-font);font-weight:650;font-size:12.5px;padding:6px 12px;
  border-radius:999px;color:#0b1f14;background:#5fe3a1;box-shadow:0 0 18px -6px #5fe3a1}
.ow-cpage__open[data-open="0"]{color:#fff;background:rgba(255,255,255,.12);box-shadow:none}
.ow-cpage__lt,.ow-cpage__hb{font-family:var(--ow-font);font-size:12.5px;color:var(--ow-ink-3)}
.ow-cpage__h{display:flex;align-items:baseline;justify-content:space-between;gap:10px;
  margin:20px 2px 8px}
.ow-cpage__ht{font-family:var(--ow-font);font-weight:600;font-size:13px;
  letter-spacing:0;color:var(--ow-ink-2)}
.ow-cpage__door{border:0;background:none;cursor:pointer;font-family:var(--ow-font);
  font-weight:600;font-size:12px;color:rgb(var(--ow-h2,93,226,255));padding:0}
.ow-cpage__door:hover{text-decoration:underline}
.ow-cpage__b{display:flex;flex-direction:column;gap:8px}
.ow-cpage__row{display:flex;align-items:baseline;gap:12px;padding:7px 2px;
  border-top:1px solid rgba(255,255,255,.07)}
.ow-cpage__row:first-child{border-top:0}
.ow-cpage__k{flex:none;min-width:8.5em;font-family:var(--ow-font-atmos);font-size:10px;
  font-weight:600;letter-spacing:.01em;color:var(--ow-ink-3)}
.ow-cpage__v{font-family:var(--ow-font);font-size:14px;font-weight:500;color:var(--ow-ink)}
.ow-cpage__v--link{color:rgb(var(--ow-h2,93,226,255));text-decoration:none}
.ow-cpage__v--link:hover{text-decoration:underline}
.ow-cpage__ev{display:flex;flex-direction:column;align-items:flex-start;gap:3px;width:100%;
  text-align:left;border:0;cursor:default;padding:11px 14px;color:var(--ow-ink);
  --h:var(--ow-h1);--h2:var(--ow-h2);
  border-radius:15px 13px 16px 14px;
  background:radial-gradient(130% 80% at 18% -6%,rgba(var(--h),.30),transparent 64%),
    linear-gradient(158deg,rgba(var(--h),.12),rgba(var(--h3,var(--ow-h3,var(--h2))),.08) 46%,
                            rgba(var(--h2),.07) 70%,var(--ow-tile-mid)),
    linear-gradient(0deg,var(--ow-plate),var(--ow-plate));
  box-shadow:inset 0 2px 1px rgba(255,255,255,.22),
    0 0 26px -12px rgba(var(--h),.7)}
button.ow-cpage__ev{cursor:pointer;transition:transform .18s var(--ow-spring,cubic-bezier(.2,.7,.2,1))}
button.ow-cpage__ev:hover{transform:translateY(-2px)}
.ow-cpage__ev b{font-family:var(--ow-font);font-weight:600;font-size:14.5px;line-height:1.25}
.ow-cpage__ev span{font-size:12.5px;color:var(--ow-ink-3)}
/* A ROW THAT IS A THING (founder/448 · 459): its title and its line stacked,
   and its OWN photograph beside them when the record has one — a jazz night
   with the band, an exhibition with the gallery — so a hotel's page and a
   museum's page differ in what they show, not only in what they say. */
.ow-cpage__t{display:flex;flex-direction:column;gap:3px;min-width:0;flex:1 1 auto;align-items:flex-start;text-align:left}
.ow-cpage__t b,.ow-cpage__t span{display:block}
.ow-cpage__ev[data-pic]{flex-direction:row;align-items:center;gap:12px;padding:9px 12px 9px 9px}
.ow-cpage__pic{flex:none;width:64px;height:64px;object-fit:cover;display:block;
  border-radius:13px 11px 14px 12px;background:rgba(var(--h,var(--ow-h1)),.18)}
/* the Visit block's first row says whether it is open now */
.ow-cpage__row[data-open="1"] .ow-cpage__v{color:#7ef0b3}
.ow-cpage__row[data-open="0"] .ow-cpage__v{color:var(--ow-ink-3)}
html[data-theme="light"][data-theme="light"] .ow-cpage__row[data-open="1"] .ow-cpage__v{color:#0f8a4d}
/* THE CENTER'S FEED UNDER ITS MAIN (founder/459) — the same river a person's
   page draws, after the arrangement and the page */
.ow-center-feed{margin-top:6px}
.ow-cfeed .po-river{margin-top:4px}
/* the places a person runs, belongs to and follows — each drawn as the Center
   it is, with its own face and hue, and each a door (founder/429 · 409) */
.ow-cpage__faces{display:flex;flex-wrap:wrap;gap:8px}
/* the room's ink on a 12% tint — white was the dark room's answer, and in the
   light room every place a person belongs to read white on pastel */
.ow-cpage__who{display:inline-flex;align-items:center;gap:9px;border:0;cursor:pointer;color:var(--ow-ink);
  padding:6px 13px 6px 6px;border-radius:999px;font-family:var(--ow-font);font-size:13px;
  --h:var(--ow-h1);--h2:var(--ow-h2);
  background:rgba(var(--h),.12);box-shadow:inset 0 0 0 1px rgba(var(--h),.30);
  transition:transform .18s var(--ow-spring,cubic-bezier(.2,.7,.2,1)),background .18s}
.ow-cpage__who:hover{transform:translateY(-2px);background:rgba(var(--h),.22)}
.ow-cpage__who b{font-weight:600}
.ow-cpage__who span{font-family:var(--ow-font-atmos);font-size:10px;font-weight:600;letter-spacing:.14em;
  color:var(--ow-ink-3)}
.ow-cpage__wf{width:26px;height:26px;flex:none;overflow:hidden;display:grid;place-items:center;
  border-radius:9px 7px 8px 7px;
  background:radial-gradient(120% 90% at 30% 10%,rgba(var(--h),.9),rgba(var(--h2),.6) 70%);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.35)}
.ow-cpage__wf[data-shape="round"]{border-radius:50%}
.ow-cpage__wf[data-shape="square"]{border-radius:5px}
.ow-cpage__wf img{width:100%;height:100%;object-fit:cover;display:block}
.ow-cpage__wf i{font-style:normal;font-family:var(--ow-font-display);font-weight:650;font-size:10px;color:#fff}
.ow-cpage__chips{display:flex;flex-wrap:wrap;gap:7px}
/* the ink is the room's, not white — white was a dark-room assumption, and in
   the light room "Ocean-view rooms" was white on pale pink (founder/513 tour) */
.ow-cpage__chip{font-family:var(--ow-font);font-weight:560;font-size:12.5px;color:var(--ow-ink);
  padding:6px 12px;border-radius:13px 9px 12px 10px;background:rgba(var(--ow-h1),.16);
  box-shadow:inset 0 0 0 1px rgba(var(--ow-h1),.32)}
.ow-cinside__h{font-family:var(--ow-font-atmos);font-weight:600;font-size:10.5px;
  letter-spacing:.01em;color:var(--ow-ink-3);
  padding:0 2px 8px}
/* THE SMALL FACTS. They wrap rather than truncate — a long city name and a long
   domain are both ordinary, and an ellipsis in the middle of somebody's town is
   the platform deciding their identity does not fit. */
.ow-prof__facts{display:flex;flex-wrap:wrap;gap:6px 14px;margin-top:8px}   /* one scale (471 §8) */
.ow-prof__fact{font-family:var(--ow-font-atmos);font-weight:600;font-size:11.5px;
  letter-spacing:.06em;color:var(--ow-ink-3);overflow-wrap:anywhere}
/* A LINK LOOKS LIKE A LINK. It is the one fact that is also a door, and the
   person's own hue is what this whole page is already wearing. */
.ow-prof__fact--link{color:rgb(var(--ow-h1));text-decoration:none;
  border-bottom:1px solid rgba(var(--ow-h1),.34);padding-bottom:1px}
.ow-prof__fact--link:hover{border-bottom-color:rgb(var(--ow-h1))}
/* A DOOR IN looks the same as a door out — the hue and the underline say
   "this opens"; a button's own chrome is not part of the fact. */
button.ow-prof__fact--door{appearance:none;-webkit-appearance:none;background:none;
  border:0;border-bottom:1px solid rgba(var(--ow-h1),.34);border-radius:0;
  padding:0 0 1px;margin:0;font:inherit;letter-spacing:inherit;line-height:inherit;
  color:rgb(var(--ow-h1));cursor:pointer;text-align:left}
button.ow-prof__fact--door:hover{border-bottom-color:rgb(var(--ow-h1))}
button.ow-prof__fact--door:focus-visible{outline:2px solid rgba(var(--ow-h1),.95);outline-offset:3px}
/* ── THE ONE LINE ON THE PAGE THAT CHANGES BY ITSELF ──────────────────────
   `presence.happening` — *"Things are coming up — Next: Rooftop dinner ·
   2026-08-20"*. The runtime had already composed it in finished English and
   nothing rendered it: a read nobody drew, landing on the founder's own
   question about what a Center actually contains.

   THE RELATIONSHIP IS THE SPEC, not the numbers. It sits BETWEEN the handle and
   the name in weight — louder than the address, quieter than the name — because
   it is neither identity nor chrome. Measured against its neighbours: the name
   is 800 at clamp(22–28px) on `--ow-ink`; the handle is 700 at 11px uppercase
   on `--ow-ink-3`. This lands at 600/13.5px on `--ow-ink-2`: the only line
   between them, which is exactly its standing.

   THE LIT DOT IS THE WHOLE POINT. Without it this reads as one more piece of
   sub-text; with it, it reads as NOW. It takes the Center's own hue rather than
   a status colour, because what is live here belongs to this place — and a
   green "online" pip would be a second vocabulary for something the hue already
   says (§17). */
.ow-prof__live{
  margin:6px 0 0;font-size:13.5px;line-height:1.45;font-weight:500;
  color:var(--ow-ink-2);max-width:52ch}
.ow-prof__live::before{
  content:"";display:inline-block;width:6px;height:6px;margin:0 8px 1px 0;
  border-radius:50%;background:rgb(var(--ow-h1));
  box-shadow:0 0 10px -1px rgba(var(--ow-h1),.9)}
/* on the map sheet the profile sits over the dark ground — same law as the
   section head and the tab seam */
.ow-map__sheet .ow-prof__live{color:rgba(255,255,255,.72)}
/* a person who asked for stillness does not get a glow that pulses attention */
@media (prefers-reduced-motion:reduce){
  .ow-prof__live::before{box-shadow:none}
}
.ow-prof__stats{display:flex;gap:16px;margin-top:16px;flex-wrap:wrap}
.ow-prof__stat{font-size:13px;color:var(--ow-ink-2)}
/* a count that opens its list is a button drawn as the words it was (472) */
button.ow-prof__stat{appearance:none;-webkit-appearance:none;background:none;border:0;padding:0;
  font:inherit;font-size:13px;color:var(--ow-ink-2);cursor:pointer;-webkit-tap-highlight-color:transparent}
button.ow-prof__stat:focus-visible{outline:2px solid rgba(var(--ow-h1),.9);outline-offset:3px;border-radius:6px}
/* the list of who somebody follows / who follows them, over the page */
.ow-people{padding:calc(14px + env(safe-area-inset-top)) 14px 40px}
.ow-people__head{margin:0 4px 14px}
.ow-people__h{font-family:var(--ow-font-display);font-weight:650;font-size:clamp(21px,5vw,26px);
  letter-spacing:-.01em;line-height:1.06;margin:0;color:var(--ow-ink)}
.ow-people__who{margin:2px 0 0;font-size:15px;color:var(--ow-ink-3)}
.ow-prof__stat b{font-family:var(--ow-font-display);font-weight:650;color:var(--ow-ink);
  margin-right:4px}
.ow-prof__acts{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap}

/* ── THE BARS. Selecting one replaces everything below it. ── */
/* NO negative margin here: `.ow-prof` is ALREADY pulled out to both edges, so
   bleeding again pushed the bar 18px past the right of the document and gave
   the whole page a sideways scroll. The 12px is the scroll inset, not a bleed. */
/* AND THE BAR MUST LOOK SCROLLABLE, BECAUSE IT IS. Seven tabs need ~729px and a
   phone gives 375, so the scrollbar is hidden (correct — a native bar under a
   nav rail is noise) and the last visible label was sliced clean through its
   letters at the screen edge: "Understa|". A hard cut mid-word reads as broken
   layout, not as "there is more this way".

   THE FADE IS ON `--more`, WHICH JS SETS FROM `scrollWidth` (see profileShell).
   CSS cannot ask whether a row overflows, so a mask declared here unconditionally
   would dim the last tab on every desktop that has room — a fade over nothing,
   which is the same class of wrong as the cut. It is measured, not assumed, and
   it lifts once the person has scrolled to the end. */
.ow-tabs{display:flex;gap:2px;margin:16px 0 0;padding:0 12px;overflow-x:auto;
  scrollbar-width:none;border-bottom:1px solid var(--ow-rim)}
.ow-tabs--more{
  -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 34px),transparent);
  mask-image:linear-gradient(90deg,#000 calc(100% - 34px),transparent)}
.ow-tabs::-webkit-scrollbar{display:none}
.ow-tab{flex:0 0 auto;border:0;background:none;cursor:pointer;position:relative;
  font:inherit;font-weight:560;font-size:13.5px;color:var(--ow-ink-3);
  padding:12px 14px 11px;white-space:nowrap;
  -webkit-tap-highlight-color:transparent;transition:color .16s var(--ow-ease)}
.ow-tab[aria-selected="true"]{color:var(--ow-ink);font-weight:600}

/* ── THE SEAM: WHERE THE WORLD'S VIEW ENDS AND YOURS BEGINS ────────────────
   My Center carried NINE tabs in one flat strip, and the strip was doing two
   different jobs without saying so: five are what ANYONE SEES (Profile · World ·
   Galleries · Story · Memories) and four are controls only the owner touches
   (Understanding · Appearance · Your activity · Settings). Interleaved, which is
   why *"what does my profile look like to someone else"* had no answer on the
   one screen whose job is to answer it. The surfaces session partitioned them
   and emits this marker at the boundary.

   IT IS A SEAM, NOT A TAB, AND THE BORDER DOES THE WORK. The hairline is the
   structure; the word only names it. `pointer-events:none` so it cannot be
   tapped, and no `cursor:pointer`, because anything in a tab strip that looks
   pressable and is not is worse than no marker at all.

   ONE DELIBERATE CHANGE from the values I was handed: `--ow-ink-3` is EXACTLY
   the colour of an unselected `.ow-tab`, so at that value the seam read as one
   more tab wearing a smaller size. Opacity takes it below the quietest real
   label without inventing a colour — a hardcoded rgba here would be the
   one-ground-two-themes mistake this file has already paid for three times. */
.ow-tabs__edge{
  flex:none;align-self:center;pointer-events:none;user-select:none;
  margin:0 4px 0 10px;padding:0 0 0 11px;
  border-left:1px solid var(--ow-rim);
  font-size:12px;font-weight:600;letter-spacing:.1em;
  color:var(--ow-ink-3);opacity:.62;white-space:nowrap;
}
/* on the map sheet the strip sits over the dark ground — the hairline has to
   come with it, same law as the section head above */
.ow-map__sheet .ow-tabs__edge{border-left-color:rgba(255,255,255,.18);
  color:rgba(255,255,255,.55);opacity:1}
.ow-tab[aria-selected="true"]::after{content:"";position:absolute;left:12px;right:12px;
  bottom:-1px;height:2.5px;border-radius:3px;background:rgb(var(--ow-h1));
  box-shadow:0 0 12px rgba(var(--ow-h1),.9)}
.ow-tab:focus-visible{outline:2px solid rgba(var(--ow-h1),.9);outline-offset:-4px}
.ow-tab__n{margin-left:6px;font-size:11px;opacity:.6}
/* THE PANEL PAYS BACK THE BLEED. `.ow-prof` is pulled out by −18px so the
   banner can run to the edges; everything inside has to put that back or it
   hangs off both sides. `.ow-prof__id` already did — the panel did not, so
   every tab's contents were clipped at the left and overflowed at the right
   (visible the moment the World tab held a real row). */
.ow-panel{padding:16px 18px 0}

/* ── A MARK THAT SAYS ITS OWN NAME ───────────────────────────────────────
   The name used to live only in a `title` tooltip on an `aria-hidden` element:
   invisible on a phone, excluded from screen readers, unreachable by keyboard.
   `fieldBody` now draws it when the field is sparse enough to hold it (≤4).

   CAPPED, BECAUSE THE BOX IS SMALLER THAN IT LOOKS. Measured on a phone the
   field is 214px wide and a full name at 11px runs 138px — one per row. The
   label ellipsises rather than being trusted to be short; a name that overruns
   its box is worse than the tooltip it replaced. */
.po-id--named{overflow:visible}
/* ★ A NAME IS NEVER ELLIPSISED.
   ★ FOUNDER, 2026-08-30: *"i dont want to see any cut out or ... text
     anywhere."*

   This capped the label at 11ch and cut the rest — so a Center called
   "Atlantic City Boardwalk" appeared as "Atlantic Ci…". The comment that
   introduced it argued a name overrunning its box is worse than no name, which
   was right about the overrun and wrong about the remedy: the answer is to let
   it WRAP onto a second line inside the field, not to shave it.

   Two short lines, word-broken, and the field's own placement now leaves room
   for them (see `placeItems`). Nothing is hidden. */
.po-id__nm{
  position:absolute;left:calc(100% + 6px);top:50%;transform:translateY(-50%);
  font-size:11px;font-weight:560;line-height:1.18;letter-spacing:-.01em;
  color:var(--ow-ink-2);
  white-space:normal;overflow-wrap:normal;word-break:normal;
  max-width:12ch;pointer-events:none}
/* past the halfway line a label would run off the right edge — flip it */
.po-id--named[style*="left:5"] .po-id__nm,
.po-id--named[style*="left:6"] .po-id__nm,
.po-id--named[style*="left:7"] .po-id__nm,
.po-id--named[style*="left:8"] .po-id__nm,
.po-id--named[style*="left:9"] .po-id__nm{
  left:auto;right:calc(100% + 6px);text-align:right}
.ow-map__sheet .po-id__nm{color:rgba(255,255,255,.72)}

/* ── FINDING ONE OF THIRTY-SEVEN ─────────────────────────────────────────
   Lightbulb went from four kinds to forty-seven the day the founder said it is
   *"the creation and starting point for a new piece of business media or a
   project."* Thirty-seven media tiles need a way to FIND one, not another shelf
   to sit on — a sub-taxonomy would have been somebody's invented categories,
   hardcoded in the App, with the next registered type falling outside all of
   them. That is the stale list a third time.

   The surfaces session shows this only when the count earns it, so the
   ten-tile project group has no filter — chrome for a problem that group does
   not have. */
.ow-lb__find{
  display:block;width:100%;box-sizing:border-box;margin:2px 0 12px;
  font:inherit;font-size:13.5px;padding:10px 13px;
  border-radius:14px 11px 13px 12px;
  border:1px solid var(--ow-field-edge);background:var(--ow-field);
  color:var(--ow-ink)}
.ow-lb__find::placeholder{color:var(--ow-ink-3)}
.ow-lb__find:focus-visible{outline:2px solid rgba(var(--ow-h1),.9);outline-offset:2px}

/* ── AN EMPTY WIDGET IS THE PRIMARY SURFACE, NOT THE EXCEPTION ───────────
   A person can now choose "Been to" BEFORE they have been anywhere — that is
   the point of choosing rather than mirroring their history. Measured on a real
   profile: posts 2 · visited 1 · rhythm 2 and ZERO of everything else, so on
   this platform an empty widget is what most people will see most of the time.

   TWO LINES, AND THE SECOND ONE IS THE PRODUCT. The first says the true thing
   ("Nowhere yet"). The second says WHAT WOULD FILL IT — *"Places you go appear
   here."* An empty state that only reports emptiness is a dead end; one that
   explains itself is an invitation.

   A SENTENCE, NEVER A BUTTON. There is nowhere to send someone yet, and a
   control that promises what the product cannot keep is the defect the founder
   named on the Discovery map and the surfaces session correctly refused for
   `can_do`.

   AND IT IS NOT `--ow-ink-3`. That is the tab-seam colour — the quietest thing
   in the App, correct for a divider and wrong for the line doing the most work
   on the surface. This lands between: quieter than the headline, louder than
   chrome. */
.po-empty__what{
  margin:5px 0 0;font-size:12px;line-height:1.5;color:var(--ow-ink-2);
  opacity:.78;max-width:34ch}
.po--1x1 .po-empty__what{display:none}   /* a 1×1 has no room for a sentence */
.ow-map__sheet .po-empty__what{color:rgba(255,255,255,.6);opacity:1}

/* ── MEMORIES — nostalgia, and the honest edge of it ──────────────────────
   Founder, 2026-08-17: *"a memory or a highlight is basically just a
   recommendation engine saying, remember these things from x years ago. It's
   like a nostalgia thing."* The runtime shipped `GET /api/me/memories` and the
   App called it ZERO times — the third build-with-no-window in one day. The
   behaviour lives in `memories()` (the surfaces file, the other session's);
   every class it reaches for is here, because the material is one file and a
   surface that ships its own stylesheet is how a design language dies.

   TOKENS ONLY, NO LITERAL COLOUR. Both grounds are served by one rule, which
   is the law two separate defects broke today — a lens plate and a quotation
   mark, each hardcoded white and each invisible on the ground its author was
   not sitting in.

   `.ow-mem__miss` IS DELIBERATELY QUIET AND DELIBERATELY NOT HIDDEN. It is the
   surface admitting what it cannot see — today, events attended. A person has
   to be able to tell *"you were nowhere that year"* from *"we did not look"*,
   and a nostalgia engine that silently omits its own blind spot is inventing a
   past by omission. Quieter than the memories, never absent. */
.ow-mem__head{display:flex;align-items:baseline;gap:10px;margin:2px 0 14px}
.ow-mem__t{font-family:var(--ow-font-display);font-weight:650;font-size:20px;
  letter-spacing:-.01em;color:var(--ow-ink)}
.ow-mem__n{font-size:12px;font-weight:560;color:var(--ow-ink-3)}
.ow-mem__yr{margin:0 0 20px}
.ow-mem__yrt{font-family:var(--ow-font-display);font-weight:600;font-size:14px;
  letter-spacing:-.01em;color:var(--ow-ink-2);margin:0 0 8px}
.ow-mem__c{display:flex;flex-direction:column;gap:4px;align-items:flex-start;
  text-align:left;font:inherit;cursor:pointer;padding:12px 14px;
  border-radius:16px 13px 15px 14px;border:1px solid var(--ow-rim);
  background:linear-gradient(152deg,var(--ow-sheet-1),var(--ow-sheet-2));
  box-shadow:inset 0 1px 0 var(--ow-lift),0 6px 16px -10px var(--ow-drop)}
/* a memory is a full-width card in a column, not a tile in the Popit grid —
   it is a sentence about a year, and a sentence wants a line to sit on */
.ow-mem__yr .ow-mem__c{width:100%}
.ow-mem__k{font-size:10.5px;font-weight:600;letter-spacing:.08em;
  color:rgb(var(--ow-h1))}
.ow-mem__ct{font-weight:600;font-size:14.5px;color:var(--ow-ink)}
.ow-mem__off{font-size:11px;color:var(--ow-ink-3)}
.ow-mem__widen{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0 6px}
.ow-mem__miss{font-size:11.5px;line-height:1.5;color:var(--ow-ink-3);
  margin:12px 0 0;max-width:52ch}

/* ── THREE CLASSES THE APP DRAWS AND THE MATERIAL NEVER DEFINED ───────────
   Found by sweeping every class the App EMITS against every class this file
   DEFINES. This is the same shape as "a write nobody reads", one layer out: a
   surface renders, the element is correct, and there is simply no material for
   it — so it falls back to the browser's defaults inside a design language.

   AND THE MECHANISM IS WORTH MORE THAN THE FIX. `.ow-sec` and `.ow-pill` ARE
   styled — in `dashboard.html`, the OS. The App loads only this stylesheet, so
   a class borrowed from the OS renders BARE here while looking perfectly fine
   to whoever wrote it in the other surface. Two products, two stylesheets, one
   class name. Anything the App draws needs its material HERE.

   `.ow-sec` was the worst of them: Discovery, signed out — the first screen a
   stranger sees — rendered "Places anyone can walk into" as a browser-default
   h2 (24px, `margin:19.92px 0`, subtitle the same colour as the heading, no
   hierarchy at all). Measured, not guessed: `getComputedStyle` reported the
   browser's own `0.83em` margins. */
.ow-sec{margin:2px 0 16px}
.ow-sec__t{font-family:var(--ow-font-display);font-weight:650;font-size:21px;
  letter-spacing:-.01em;line-height:1.15;color:var(--ow-ink);margin:0}
/* the subtitle must READ as subordinate — it was rendering at full-strength ink
   and the same size as body copy, so the pair had no hierarchy */
.ow-sec__s{margin:5px 0 0;font-size:12.5px;line-height:1.5;color:var(--ow-ink-3);
  max-width:54ch}

/* a count, said quietly beside the thing it counts */
.ow-pill{display:inline-flex;align-items:center;gap:5px;
  padding:5px 10px 6px;border-radius:11px 9px 12px 10px;
  font-size:11.5px;font-weight:560;color:var(--ow-ink-2);
  border:1px solid var(--ow-rim);
  background:linear-gradient(152deg,var(--ow-sheet-1),var(--ow-sheet-2))}

/* naming a new gallery — the field and the button already have their material,
   so this is only the column that holds them */
.ow-galnew{display:flex;flex-direction:column;gap:9px;margin:10px 0 4px;width:100%}

/* ── WHERE YOU ARE — the privacy control, and the sentence is the control ──
   The runtime shipped the location ruling; the settings UI is the surfaces
   session's. This is the material for it, and it carries ONE rule that is not
   cosmetic.

   `followers` AND `friends` DIFFER ONLY IN THE SENTENCE. One is everyone who
   follows you; the other is mutual only. The NAMES are near-synonyms in ordinary
   English, so the explanatory line is the entire difference between sharing your
   location with people you chose and sharing it with anyone who pressed a
   button. **`.ow-loc__opt span` must never be made smaller or quieter than it is
   here** — it is not a caption under a control, it IS the control.

   The words themselves are the SERVER'S (`audience_words`) and are never
   authored here. Two descriptions of one privacy setting is how a person shares
   more than they meant to.

   Styling this while the founder has not ruled on what `everyone` means is
   deliberate and is harm-REDUCTION, not endorsement: unstyled, the name ran into
   the sentence with no gap, which makes the wrong pick MORE likely on exactly
   the screen where a wrong pick matters most. The open ruling is in the
   register. */
.ow-loc{display:flex;flex-direction:column;gap:10px;width:100%}
.ow-loc__now{margin:0;font-size:13.5px;font-weight:560;color:var(--ow-ink)}
.ow-loc__opts{display:flex;flex-direction:column;gap:7px}
.ow-loc__opt{display:flex;flex-direction:column;gap:2px;align-items:flex-start;
  text-align:left;width:100%;font:inherit;cursor:pointer;padding:10px 13px;
  border-radius:14px 11px 13px 12px;border:1px solid var(--ow-rim);
  background:linear-gradient(152deg,var(--ow-sheet-1),var(--ow-sheet-2));
  box-shadow:inset 0 1px 0 var(--ow-lift)}
.ow-loc__opt b{font-weight:600;font-size:13.5px;color:var(--ow-ink);
  text-transform:capitalize}
/* the sentence — see above. 11.5px/1.45 is the floor, not a starting point. */
.ow-loc__opt span{font-size:11.5px;line-height:1.45;color:var(--ow-ink-3)}
.ow-loc__opt[aria-pressed="true"]{border-color:rgba(var(--ow-h1),.55);
  background:linear-gradient(152deg,rgba(var(--ow-h1),.16),rgba(var(--ow-h1),.07))}
/* the CHOSEN option's sentence gets LOUDER, never dimmer — the thing you are
   currently sharing under is the one you most need to be able to re-read */
.ow-loc__opt[aria-pressed="true"] span{color:var(--ow-ink-2)}
.ow-loc__why{margin:2px 0 0;font-size:11.5px;color:var(--ow-ink-3)}

/* ── CUSTOMIZE — the person's own appearance, edited where it is seen ── */
.ow-cust{display:flex;flex-direction:column;gap:16px}
.ow-cust__row{display:flex;flex-direction:column;gap:8px}
.ow-cust__lbl{font-family:var(--ow-font-atmos);font-weight:600;font-size:10px;
  letter-spacing:.2em;color:var(--ow-ink-3)}
/* ── WHAT YOUR PROFILE SHOWS, AND HOW YOUR NAME IS SET ────────────────────
   ★ FOUNDER, 2026-09-12: *"remove or clean up random white highlighted text
     here also do heavy design cleanups."*

   THE WHITE WAS THE BROWSER'S. Measured on the Appearance panel: fourteen
   `.ow-wpick__opt` buttons computing `rgb(239,239,239)` — `buttonface`, the UA
   default — on a dark surface. Not a highlight anybody designed: the ENTIRE
   `ow-wpick` family, five classes, plus `ow-choice` and `ow-wmpick`, had NO
   RULE IN THIS FILE AT ALL. The markup was written and the material never was,
   so every one of them fell back to a raw HTML control.

   That is the third class-nobody-styles on this platform today — login's
   notes, the thread rows' missing glow, and this — and it is the loudest,
   because a `<button>` with no background does not disappear, it turns white.

   THEY ARE POPIT MATERIAL, not new chrome: the same panel ground, the same lit
   rim, the same hand-cut radii and the same pressed treatment the Feed/Scroll
   switch and `.po-act` already use, so the Appearance panel reads as one
   surface rather than as three generations of control. */
.ow-wpick{display:flex;flex-direction:column;gap:10px}
.ow-wpick__now{margin:0;font-size:12.5px;line-height:1.5;color:var(--ow-ink-3)}
.ow-wpick__grid{display:grid;gap:8px;
  grid-template-columns:repeat(auto-fill,minmax(148px,1fr))}
/* a choice, not a slab: the name leads, the sentence explains, the number says
   where it will sit — three weights in one control so it can be read at a
   glance rather than parsed */
.ow-wpick__opt{position:relative;display:flex;flex-direction:column;gap:3px;
  text-align:left;cursor:pointer;border:0;font-family:var(--ow-font);
  padding:11px 13px;color:var(--ow-ink);
  border-radius:15px 12px 16px 13px;
  background:
    radial-gradient(120% 90% at 16% -20%, rgba(var(--ow-h1),.12), transparent 62%),
    linear-gradient(178deg,var(--ow-panel-1),var(--ow-panel-2));
  box-shadow:inset 0 2px 1px var(--ow-lift),
    inset 0 0 0 1.5px rgba(var(--ow-h1),.20),
    0 0 20px -14px rgba(var(--ow-h1),.40),
    0 8px 22px -16px var(--ow-drop);
  transition:box-shadow .16s var(--ow-ease),transform .12s var(--ow-spring)}
.ow-wpick__opt b{font-family:var(--ow-font-display);font-weight:600;
  font-size:13.5px;line-height:1.2}
.ow-wpick__opt span{font-size:11.5px;line-height:1.35;color:var(--ow-ink-3)}
.ow-wpick__opt:active{transform:scale(.985)}
.ow-wpick__opt[aria-pressed="true"]{
  box-shadow:inset 0 1px 0 rgba(255,255,255,.35),
    0 0 0 1.5px rgba(var(--ow-h1),.85),
    0 0 22px -8px rgba(var(--ow-h1),.75)}
/* the position it will take, shown only once it HAS one */
.ow-wpick__n{position:absolute;top:9px;right:10px;font-style:normal;
  font-family:var(--ow-font-atmos);font-weight:650;font-size:10px;
  min-width:17px;height:17px;border-radius:999px;display:grid;place-items:center;
  color:#fff;background:rgb(var(--ow-h1))}
.ow-wpick__n:empty{display:none}

/* ── A ROW OF CHOICES — Theme · World · Layout · Your name ────────────────
   One pressed language with the Feed/Scroll switch: the chosen one wears the
   hue, the rest are quiet. `.ow-wmpick` adds nothing but room, because each of
   its buttons carries its own typeface inline and a face needs a little air
   around it to be judged. */
.ow-choice{gap:6px;flex-wrap:wrap}
.ow-choice .po-act{font-size:12.5px;padding:8px 13px}
.ow-choice .po-act[data-act="on"],
.ow-choice .po-act[aria-pressed="true"]{color:#fff;
  background:linear-gradient(150deg,rgb(var(--ow-h1)),rgba(var(--ow-h2),.8));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.35)}
.ow-wmpick .po-act{font-size:14px;padding:9px 15px;line-height:1.15}

.ow-picks{display:grid;grid-template-columns:repeat(auto-fill,minmax(88px,1fr));gap:8px}
.ow-pick{position:relative;aspect-ratio:16/9;border:0;cursor:pointer;overflow:hidden;
  border-radius:13px 10px 14px 11px;padding:0;
  box-shadow:inset 0 0 0 1px var(--ow-rim)}
.ow-pick img{width:100%;height:100%;object-fit:cover;display:block}
.ow-pick[aria-pressed="true"]{box-shadow:0 0 0 2.5px rgb(var(--ow-h1)),
  0 0 16px -4px rgba(var(--ow-h1),.9)}
.ow-swatches{display:flex;gap:8px;flex-wrap:wrap}
.ow-sw{width:34px;height:34px;border:0;cursor:pointer;border-radius:11px 9px 12px 10px;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.4),inset 0 0 0 1px var(--ow-rim)}
/* THE CUSTOM SWATCH — a real colour input wearing the same hand-cut shape as
   the twelve. The native control is deliberately kept (eyedropper, recents, the
   OS palette) and only its chrome is hidden: rebuilding a colour picker worse
   than the platform's is the clearest possible case of a second way to do a
   solved thing. */
.ow-sw--custom{position:relative;overflow:hidden;
  background:conic-gradient(from 210deg,#ff5a7a,#ffd05a,#5affa8,#5ad1ff,#a95aff,#ff5a7a)}
.ow-sw--custom::after{content:"";position:absolute;inset:33%;border-radius:50%;
  background:var(--ow-ground);opacity:.86}
.ow-sw__pick{position:absolute;inset:-8px;width:calc(100% + 16px);
  height:calc(100% + 16px);opacity:0;cursor:pointer;border:0;padding:0}

.ow-sw[aria-pressed="true"]{box-shadow:0 0 0 2.5px var(--ow-ink),inset 0 1px 0 rgba(255,255,255,.4)}
.ow-upload{display:inline-flex;align-items:center;gap:8px;cursor:pointer;
  font-size:12.5px;font-weight:560;color:var(--ow-ink);padding:9px 14px;
  border-radius:12px 10px 13px 11px;background:rgba(var(--ow-h1),.14);
  box-shadow:inset 0 0 0 1px rgba(var(--ow-h1),.35)}
.ow-upload input{display:none}
.ow-note{font-size:12px;line-height:1.5;color:var(--ow-ink-3)}

/* ── THE TWO THINGS YOU DO WITH WHAT YOU JUST MADE ────────────────────────
   ★ FOUNDER, 2026-09-07: *"There's basically no friction."*
   Post it and Send to a friend are the same size because they are the same
   size of decision. They wrap on a narrow screen rather than shrinking — a
   half-width button is a button people miss. */
/* ── NO WALKTHROUGH ───────────────────────────────────────────────────────
   ★ FOUNDER, 2026-09-07: *"You may feel like a walk through with steps that
     should feel like a tool that people everyday naturally use."*
   The numbered rail is gone. What is left is the thing you made, what to do
   with it, and — quieter, below — the words and the place, as fields rather
   than as screens. */
.ow-compose__more{margin-top:14px}
.ow-compose__moretoggle{font-family:var(--ow-font-atmos);font-weight:600;
  font-size:12px;letter-spacing:.01em;
  color:var(--ow-ink-3);background:none;border:0;padding:6px 0;cursor:pointer}
.ow-compose__moretoggle:hover{color:var(--ow-ink-2)}
.ow-compose__morebody{display:flex;flex-direction:column;gap:8px;margin-top:6px}
/* `display:flex` BEATS the browser's own `[hidden]{display:none}`, so the
   collapsed field opened itself the moment it was styled — measured on screen:
   "ADD WORDS" with the name field already showing under it. Any rule that sets
   `display` on an element toggled by `hidden` has to say this. */
.ow-compose__morebody[hidden]{display:none}
/* WHERE IT GOES IS A SENTENCE, not a screen. The label sits with the chips
   rather than above a page of them. */
.ow-compose__where{display:flex;flex-wrap:wrap;align-items:center;gap:8px 10px;
  margin-top:12px}
.ow-compose__wherelbl{font-family:var(--ow-font-atmos);font-weight:600;
  font-size:12px;letter-spacing:.2em;
  color:var(--ow-ink-3)}
.ow-compose__twoacts{display:flex;flex-wrap:wrap;gap:8px}
.ow-compose__twoacts .ow-btn{flex:1 1 44%;min-width:150px}
/* SEND IS THE QUIETER OF THE TWO. Posting is the default act on this surface;
   sending is deliberate, and a second solid button beside the first reads as
   two primaries and slows the choice down.

   SCOPED TO THE ROW, and the reason is measured rather than stylistic: `.ow-btn`
   is declared LATER in this file and sets `background` as a shorthand, so a
   single-class rule here computed to `rgba(0,0,0,0)` and the two buttons
   rendered identically. Caught by reading the computed style rather than the
   screenshot — they looked deliberate, which is exactly how a rule that never
   applied survives. */
.ow-compose__twoacts .ow-compose__sendbtn{
  background:rgba(var(--ow-h1),.14);color:var(--ow-ink);
  box-shadow:inset 0 0 0 1px rgba(var(--ow-h1),.34)}
/* the words are ink: a person's own hue as text vanished on the light floor
   when the hue is pale (loop.ada's cyan, measured at 290 on the edit screen);
   the hue stays where it reads — the tint and the ring */
.ow-compose__twoacts .ow-compose__sendbtn:hover{background:rgba(var(--ow-h1),.2)}

/* ── YOU — the fields a person writes about themselves ────────────────────
   ★ FOUNDER, 2026-09-06: *"PROFILE SHOULD BE PERFECT AND SO SHOULD USER DEPTH
     OF GOOGLE ACCOUNT AND FACEBOOK."*
   SAME MATERIAL AS THE COMPOSER, deliberately — a field you type into should
   look the same everywhere on this platform, or Settings becomes a form and
   the rest of the App stays an app. */
.ow-you{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:12px}
.ow-you__f{display:flex;flex-direction:column;gap:5px;min-width:0}
/* THE BIO GETS THE WHOLE WIDTH. It is prose next to five short answers, and a
   three-line box in a 210px column is a box nobody writes three lines in. */
.ow-you__f:has(textarea){grid-column:1/-1}
.ow-you__lbl{font-family:var(--ow-font-atmos);font-weight:600;font-size:10px;
  letter-spacing:.2em;color:var(--ow-ink-3)}
.ow-you__in{font:inherit;font-size:14.5px;line-height:1.4;color:var(--ow-ink);
  padding:10px 13px;border:0;border-radius:15px 12px 16px 13px;width:100%;
  box-sizing:border-box;resize:vertical;
  background:linear-gradient(178deg,var(--ow-panel-1),var(--ow-panel-2));
  box-shadow:inset 0 1px 0 var(--ow-lift),inset 0 0 0 1px var(--ow-rim)}
.ow-you__in::placeholder{color:var(--ow-ink-3)}
.ow-you__in:focus{outline:none;
  box-shadow:inset 0 1px 0 var(--ow-lift),0 0 0 1.5px rgba(var(--ow-h1),.7)}
/* WHAT HAPPENED TO YOUR WRITING, said in the field's own space so the section
   never reflows when it appears. Empty until there is something true to say. */
.ow-you__say{min-height:14px;font-family:var(--ow-font-atmos);font-weight:600;
  font-size:12px;letter-spacing:.01em;color:transparent}
.ow-you__say[data-s]{color:var(--ow-ink-3)}
.ow-you__say[data-s="ok"]{color:rgb(var(--ow-h1))}
.ow-you__say[data-s="bad"]{color:var(--ow-bad,#e0526b)}
.ow-you__why{font-size:11px;line-height:1.4;color:var(--ow-ink-3)}

/* THE COMPOSER — the stepped path at the top of Create.
   Hand-cut radii and a hue read from context (§2, §3): never a uniform corner,
   never a hardcoded colour. The frame holds its shape before the stream
   arrives, so opening this section does not reflow the page underneath it. */
/* READ THE HUE FROM CONTEXT, exactly as a Popit does (§2). Without this local
   alias `--h` is undefined here — it is declared on `.po,.po-sheet,.po-glance`
   and nothing else — so every `rgba(var(--h),…)` below silently computes to
   transparent. The shutter and the button rendered as invisible text until
   this line existed, and the radii looked fine throughout, which is why it read
   as a layout problem rather than a missing variable. */
.ow-compose{--h:var(--ow-h1);--h2:var(--ow-h2);margin:0 0 22px}

/* THE STEP RAIL. A person can always see how many steps there are and which
   one they are on — the founder's "everything should have steps to it". Steps
   ahead of what has been made are disabled rather than hidden, because a
   disabled step still tells you the shape of the path. */
.ow-compose__steps{display:flex;gap:4px;overflow-x:auto;padding:0 0 12px;
  scrollbar-width:none}
.ow-compose__steps::-webkit-scrollbar{display:none}
/* all five fit a 375px phone — a rail whose last step is permanently off the
   edge hides exactly the information the rail exists to give */
.ow-compose__step{font:inherit;font-size:10.5px;font-weight:560;cursor:pointer;
  white-space:nowrap;padding:7px 9px;border:0;
  border-radius:13px 10px 12px 11px;
  color:var(--ow-ink-3);background:rgba(var(--h),.09);
  box-shadow:inset 0 0 0 1px rgba(var(--h),.16);transition:color .15s ease}
.ow-compose__step[aria-current="step"]{color:#fff;background:rgba(var(--h),.92);
  box-shadow:0 6px 18px -9px rgba(var(--h),.9)}
.ow-compose__step:disabled{opacity:.4;cursor:default}

.ow-compose__stage{min-height:120px}
.ow-compose__why{font-size:12.5px;line-height:1.5;color:var(--ow-ink-3);margin:0 2px 10px}

/* STEP 1 IS A VIEWFINDER, NOT A LIST. The frame is drawn immediately and the
   camera only wakes on the tap — so the surface LOOKS like a camera without
   asking for one on arrival, which is the rule that outranks it. */
/* FLEX, NOT GRID. Three children in a `place-items:center` grid become three
   ROWS and spread down the whole frame — the ring sat at a third, the label at
   two thirds and the hint near the floor, reading as three unrelated things
   rather than one control. A column groups them. */
.ow-compose__lensdoor{position:relative;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:9px;
  width:100%;aspect-ratio:4/5;max-height:38vh;cursor:pointer;border:0;
  font:inherit;color:var(--ow-ink);margin:0 0 10px;
  border-radius:22px 19px 23px 20px;overflow:hidden;
  /* A LENS IS NOT A HOLE — AND IT IS NOT A BLACK SLAB EITHER. The frame was
     first translucent, so the world's starfield showed through it and it read
     as a gap in the page. I fixed that with an opaque plate hardcoded to
     #161428 -> #0c0b16 and shipped a NEW defect: on the light theme that is a
     large black rectangle in the middle of a pale surface — the same slab
     complaint, caused by the fix for the previous one.
     The ground is the WORLD'S, not a colour of mine: `--ow-ground` is already
     the floor in both themes, so the plate is opaque and still belongs to
     whichever theme is on. */
  background:radial-gradient(120% 80% at 50% -12%,rgba(var(--h),.34),transparent 64%),
             linear-gradient(180deg,
               color-mix(in srgb, var(--ow-ground) 88%, rgb(var(--h))),
               var(--ow-ground));
  /* no ring around the plane (S13, 437): the lift and the glow, no line */
  box-shadow:inset 0 2px 1px var(--ow-lift),0 22px 60px -28px rgba(var(--h),.75);
  transition:transform .14s ease}
.ow-compose__lensdoor:active{transform:scale(.988)}
/* the shutter ring — the one shape everyone already reads as "camera" */
.ow-compose__ring{width:66px;height:66px;border-radius:50%;
  border:3.5px solid rgba(var(--h),.92);background:rgba(var(--h),.16);
  box-shadow:0 0 34px -6px rgba(var(--h),.9)}
.ow-compose__lenslbl{font-family:var(--ow-font-display);font-weight:600;font-size:15px}
.ow-compose__lenshint{font-family:var(--ow-font-atmos);font-weight:600;font-size:9px;
  letter-spacing:.01em;color:var(--ow-ink-3)}

/* the other ways sit UNDER the lens, quieter — cost rises as you descend */
.ow-compose__ways{display:grid;gap:9px;grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
.ow-compose__way{font:inherit;text-align:left;cursor:pointer;border:0;
  display:flex;flex-direction:column;gap:3px;padding:14px 15px;
  border-radius:18px 14px 19px 15px;
  color:var(--ow-ink);
  /* the Popit material, no ring (S13) — a door is a small Popit */
  background:radial-gradient(130% 80% at 18% -6%,rgba(var(--h),.30),transparent 64%),
    linear-gradient(158deg,rgba(var(--h),.14),rgba(var(--h3,var(--ow-h3,var(--h2))),.10) 50%,var(--ow-tile-base));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.14),0 12px 30px -18px rgba(var(--h),.7);transition:transform .12s ease}
.ow-compose__way:active{transform:scale(.985)}
.ow-compose__way b{font-family:var(--ow-font-display);font-weight:600;font-size:14px}
.ow-compose__way span{font-size:11.5px;line-height:1.45;color:var(--ow-ink-3)}
.ow-compose__imp{grid-column:1/-1;display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.ow-compose__imp .ow-fld{flex:1 1 220px}

/* THE LENS. The controls sit ON the frame — the picture is the surface, not a
   preview pane with a toolbar under it. */
/* ── AN OPEN CAMERA FILLS THE SCREEN ──────────────────────────────────────
   ★ FOUNDER, 2026-09-07: *"a tool that people everyday naturally use."*

   MEASURED at 375x812, the phone the founder will test on: the viewfinder came
   out 339x424 — the top half of the screen, with a text composer and a list of
   business document types stacked underneath it. A camera that occupies half a
   scrolling page is a CARD ABOUT A CAMERA. Nothing anybody uses every day looks
   like that; every camera on every phone fills the glass.

   `aspect-ratio:4/5` with `max-height:60vh` is what held it there. The ratio is
   right for a still frame in a review strip and wrong for a live viewfinder,
   where the job is to show as much of what you are pointing at as possible.

   So an OPEN lens takes the height it can have and lets the ratio follow: the
   viewport, less the header, the nav and the shutter bar's own room. `dvh`
   rather than `vh` because a phone's URL bar changes the answer, and `vh`
   would put the shutter under the browser chrome exactly when somebody
   reaches for it. The 4/5 ratio stays for everything that is not the live
   lens. */
.ow-compose__lens{position:relative;display:grid;place-items:center;
  width:100%;height:min(72dvh,620px);min-height:340px;overflow:hidden;
  border-radius:22px 19px 23px 20px;
  background:radial-gradient(120% 80% at 50% -10%,rgba(var(--h),.16),transparent 60%),
             rgba(0,0,0,.06);
  box-shadow:inset 0 0 0 1.4px rgba(var(--h),.28)}
.ow-compose__lens video{width:100%;height:100%;object-fit:cover;display:block}
.ow-compose__bar{position:absolute;left:0;right:0;bottom:14px;
  display:flex;align-items:center;justify-content:center;gap:14px}
.ow-compose__shutter{width:62px;height:62px;border-radius:50%;cursor:pointer;
  border:3.5px solid rgba(255,255,255,.92);background:rgba(255,255,255,.28);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
  transition:transform .12s ease}
.ow-compose__shutter:active{transform:scale(.93)}
/* FILMING LOOKS DIFFERENT FROM PHOTOGRAPHING, and recording looks different
   again — a person must never have to guess whether the camera is running. */
.ow-compose__shutter.is-rec{background:rgba(255,70,90,.5)}
.ow-compose__shutter.is-on{background:rgba(255,70,90,.95);border-radius:16px;
  animation:owRec 1.1s ease-in-out infinite}
@keyframes owRec{50%{opacity:.55}}
.ow-compose__flip{font:inherit;font-size:12px;font-weight:500;cursor:pointer;
  padding:8px 13px;border-radius:14px 11px 13px 10px;color:#fff;border:0;
  background:rgba(0,0,0,.42);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px)}

/* STEP 2 — what you have picked, before anything is written anywhere. */
.ow-compose__picks{display:grid;gap:8px;
  grid-template-columns:repeat(auto-fill,minmax(104px,1fr));margin-bottom:11px}
.ow-compose__pick{position:relative;aspect-ratio:1;overflow:hidden;
  border-radius:15px 12px 16px 13px;background:rgba(var(--h),.10);
  box-shadow:inset 0 0 0 1px rgba(var(--h),.22)}
.ow-compose__pick img,.ow-compose__pick video{width:100%;height:100%;
  object-fit:cover;display:block}
.ow-compose__tag{position:absolute;left:7px;top:7px;padding:3px 7px;
  border-radius:8px 6px 9px 7px;background:rgba(0,0,0,.55);color:#fff;
  font-family:var(--ow-font-atmos);font-weight:600;font-size:8.5px;
  letter-spacing:.01em;}
.ow-compose__trim{position:absolute;left:6px;bottom:6px;font:inherit;
  font-size:10.5px;font-weight:560;cursor:pointer;border:0;padding:5px 9px;
  border-radius:10px 8px 11px 9px;color:#fff;background:rgba(var(--h),.85);
  -webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}
/* the trimmer takes the stage — a range you are cutting needs to be seen big */
.ow-trim{display:flex;flex-direction:column;gap:10px}
.ow-trim__v{width:100%;max-height:44vh;border-radius:18px 15px 19px 16px;
  background:#0c0b16;display:block}
.ow-trim__row{display:flex;flex-direction:column;gap:7px}
.ow-trim__row input[type=range]{width:100%;accent-color:rgb(var(--h))}
.ow-compose__rm{position:absolute;right:6px;bottom:6px;font:inherit;
  font-size:10.5px;font-weight:560;cursor:pointer;border:0;padding:5px 9px;
  border-radius:10px 8px 11px 9px;color:#fff;background:rgba(0,0,0,.55);
  -webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}

/* STEP 5 — the summary, and the one line that matters: nothing has gone out. */
.ow-compose__sum{padding:14px 16px;border-radius:18px 15px 19px 16px;
  background:rgba(var(--h),.09);box-shadow:inset 0 0 0 1px rgba(var(--h),.2)}
.ow-compose__sum p{margin:0 0 5px;font-size:13px;line-height:1.5;color:var(--ow-ink-2)}
.ow-compose__sum p:last-child{margin-bottom:0}
.ow-compose__done{display:flex;flex-direction:column;gap:10px}
/* SEND IT TO SOMEBODY — the destination step's second question. It is a
   heading rather than a label because it opens a group of choices, and it
   wears the same ink as the step's own prose so it reads as part of one
   question and not as a new screen. `--ow-ink-2` is the token, never a
   literal: six literal-white rules in this file once made a whole surface
   invisible in light mode. */
.ow-compose__to{margin:16px 2px 8px;font-size:12px;letter-spacing:.08em;
  color:var(--ow-ink-3);font-weight:500}

.ow-compose__foot{display:flex;align-items:center;gap:9px;flex-wrap:wrap;
  margin-top:13px}

/* THE STATUS LINE IS DELIBERATELY NOT .ow-note — that class is display:none at
   every call site in this file, so anything written there is written to nobody.
   A failed capture, upload or share must be able to say so. */
.ow-compose__status{min-height:1.2em;margin:9px 2px 0;
  font-size:12.5px;line-height:1.4;color:var(--ow-ink-2)}
.ow-compose__status.is-bad{color:#ff8095}

/* FORM CONTROLS DO NOT INHERIT TYPE. A <button>, <input> or <textarea> falls
   back to the browser's own face unless told otherwise — which is how the whole
   App navigation ended up rendering in Arial beside Outfit everywhere else.
   Declared once here so no future control can fall through the same gap. */
button,input,textarea,select{font-family:inherit}

/* ═══ 15a · THE HIT TARGET IS NOT THE GLYPH ════════════════════════════════
   ★ FOUNDER, 2026-08-29: *"why the hell when i click like repost goes up."*

   MEASURED, not guessed: the response controls render at 25x25px with 15px
   between them — a 40px pitch. Apple's minimum touch target is 44px and
   Google's is 48px. So the visible glyph was HALF the size a finger can
   reliably hit, the dead space between two controls was 15px wide, and a
   press aimed at Like that landed eight pixels low hit Repost instead. The
   server was right every time; the finger never reached the button it meant.

   THE GLYPH STAYS EXACTLY AS IT IS. `.po-acts--rail` removed the button
   chrome on purpose — the founder's own instruction, "not as buttons, but as
   actual icons" — so making the icons bigger is not the fix. The CLICKABLE
   AREA grows instead, as an invisible pseudo-element centred on the glyph.

   SIZED SO THEY CANNOT OVERLAP. Two adjacent targets that overlap would move
   the mis-click from one neighbour to the other rather than removing it — so
   the pitch is opened first, and the target is set just under it. Rail pitch
   25+18=43 with a 40px target leaves 3px of true dead space; the row is the
   same arithmetic sideways. */
.po-acts .po-act{position:relative}
.po-acts .po-act::after{content:"";position:absolute;
  left:50%;top:50%;transform:translate(-50%,-50%);
  width:max(40px, calc(100% + 12px));
  height:max(40px, calc(100% + 12px));
  /* the target is for pointers only — it must never paint, and must never
     sit above the tally it surrounds */
  z-index:0}
.po-acts .po-act > svg,
.po-acts .po-act > b{position:relative;z-index:1}

/* room for the bigger targets, so they meet without overlapping */
.po-acts--rail{gap:18px}
.po-card__foot .po-acts{gap:22px}
/* THE 145px CARD NO LONGER EXISTS. This tightened the controls to 14px because
   four of them had to fit a half-width tile; since 2026-08-31 the immersive
   group packs ONE ACROSS at Home's width, so the card is ~394px at a 430px
   viewport and the justification is gone. Home's 20px stands in both. */
.ow-fpos .po-card[data-fsize="sm"] .po-card__foot .po-acts{gap:20px}
.ow-fpos .po-card[data-fsize="sm"] .po-acts .po-act::after{
  width:max(32px, calc(100% + 10px));height:max(32px, calc(100% + 10px))}

/* ═══ 15b · THE MEDIA STRIP — ONE POST, ORDERED PIECES ═════════════════════
   ★ FOUNDER, 2026-08-26: *"Horizontal swiping changes the media item and
     therefore its associated caption… Do not flatten the slideshow into four
     independent Posts."*

   A horizontal CSS snap scroller, for the same reason the vertical feed is one:
   the platform's own scroller is the only thing that feels native under a
   thumb, and nothing runs in the gesture path. `overscroll-behavior-x: contain`
   is what stops a swipe that reaches the last photo from continuing into the
   browser's back gesture. */
.po-shots{display:flex;width:100%;height:100%;
  overflow-x:auto;overflow-y:hidden;
  scroll-snap-type:x mandatory;overscroll-behavior-x:contain;
  -webkit-overflow-scrolling:touch;scrollbar-width:none}
.po-shots::-webkit-scrollbar{display:none}
.po-shot{flex:0 0 100%;width:100%;height:100%;position:relative;
  scroll-snap-align:center;scroll-snap-stop:always;display:block}
.po-shot img,.po-shot video{width:100%;height:100%;object-fit:cover;display:block}

/* HOW MANY, AND WHICH ONE. Dots rather than a counter: a person swiping does
   not read "3 / 4", they look for where they are. Pressing one goes there. */
.po-shots__dots{position:absolute;left:0;right:0;bottom:9px;z-index:3;
  display:flex;justify-content:center;gap:5px;pointer-events:none}
.po-shots__dots i{width:5px;height:5px;border-radius:50%;
  background:rgba(255,255,255,.42);pointer-events:auto;cursor:pointer;
  box-shadow:0 0 0 1px rgba(0,0,0,.18);
  transition:background .18s var(--ow-ease),transform .18s var(--ow-spring)}
.po-shots__dots i[data-on]{background:#fff;transform:scale(1.28)}

/* THE CAPTION BELONGS TO THE PIECE, and changes with it. Sits on the image
   with its own scrim, because it is about the photograph rather than about the
   Post — the Post's own words are in the body below. An item with no caption
   collapses the line entirely; it never renders an empty bar. */
.po-shots__cap{position:absolute;left:0;right:0;bottom:20px;z-index:2;
  padding:22px 14px 8px;
  font-family:var(--ow-font);font-size:12.5px;line-height:1.4;color:#fff;
  text-shadow:0 1px 10px rgba(0,0,0,.8);
  background:linear-gradient(180deg,transparent,rgba(0,0,0,.5));
  pointer-events:none}
.po-shots__cap[data-empty="1"]{display:none}

/* ═══ 16b · ALIVE, WITHOUT BEING NOISY ══════════════════════════════════════
   ★ FOUNDER, 2026-08-29: *"We're not just building technically correct
     realtime… Fast ≠ abrupt. The interface should feel alive without becoming
     noisy."* — and the list it has to satisfy: no layout jumping, no flashes
     during reconciliation, optimistic interaction, and server reconciliation
     that does not visibly fight the user's action.

   THE RULE THAT DECIDES EVERY ONE OF THESE: motion is for a change the person
   did not cause. Their own press is already answered by `aria-pressed` the
   instant they make it — animating that again would be the interface applauding
   itself. A number that moves because somebody ELSE acted is the case that
   needs to be legible, because nothing on screen explains it. */

/* ── A COUNT THAT CHANGES UNDER YOU ─────────────────────────────────────────
   The tally is swapped by the realtime client, and a bare text swap is the
   "flash during reconciliation" the founder named. This lifts and settles it
   over 260ms — long enough to notice, short enough not to wait for.

   NO LAYOUT JUMPING: the animation is `transform` and `opacity` only, and the
   tally reserves its own width, so 9 -> 10 cannot shove the icons beside it.
   Nothing here re-flows. */
.po-act > b{display:inline-block;min-width:1ch;text-align:left;
  font-variant-numeric:tabular-nums;
  will-change:transform,opacity}
@keyframes ow-tally{
  0%  {transform:translateY(-0.42em);opacity:0}
  55% {transform:translateY(.06em);  opacity:1}
  100%{transform:none;               opacity:1}}
.po-act > b[data-live]{animation:ow-tally .26s var(--ow-ease) both}

/* ── THE PRESS ITSELF ───────────────────────────────────────────────────────
   One beat on the glyph, on the person's OWN action. It runs on `transform`,
   so it cannot disturb a neighbour, and it is suppressed for anyone who has
   asked for less motion. */
/* ═══ EXPRESSIVE RESPONSES ═════════════════════════════════════════════════
   ★ FOUNDER, 2026-08-29: *"make animations that are expressive and futuristic
     for like dislike repost and favorite."*

   FOUR DIFFERENT GESTURES, because four different meanings. A like that looks
   like a repost teaches nothing; the motion should say which thing happened
   before the person reads the icon.

     LIKE      a heart lands — overshoot, settle, and a ring of light leaves it
     DISLIKE   a short downward knock. Heavier, lower, no light: it is not a
               celebration and animating it like one would be dishonest
     REPOST    a rotation, because the glyph is a cycle and the act is travel
     FAVOURITE a lift and a snap-back, the motion of putting something away

   EVERY ONE RUNS ON `transform`, `opacity` AND `filter` ONLY, so none of them
   can reflow a neighbour or move the row under a thumb. The ring is a
   pseudo-element that starts at zero size, so it occupies no space and cannot
   push anything. */

/* the light that leaves a like */
.po-act[data-po-respond="like"]{position:relative}
.po-act[data-po-respond="like"]::before{
  content:"";position:absolute;left:50%;top:50%;width:6px;height:6px;
  margin:-3px 0 0 -3px;border-radius:50%;pointer-events:none;
  box-shadow:0 0 0 0 rgba(var(--h,var(--ow-h1)),.55);opacity:0;z-index:0}
@keyframes ow-ring{
  0%  {opacity:.9; box-shadow:0 0 0 0 rgba(var(--h,var(--ow-h1)),.55)}
  70% {opacity:.35;box-shadow:0 0 0 17px rgba(var(--h,var(--ow-h1)),0)}
  100%{opacity:0;  box-shadow:0 0 0 20px rgba(var(--h,var(--ow-h1)),0)}}
.po-act[data-po-respond="like"][data-act="on"]::before{
  animation:ow-ring .52s var(--ow-ease) both}

@keyframes ow-liked{
  0%  {transform:scale(1) rotate(0deg)}
  30% {transform:scale(1.42) rotate(-7deg)}
  55% {transform:scale(.9)  rotate(4deg)}
  78% {transform:scale(1.1) rotate(-2deg)}
  100%{transform:scale(1)   rotate(0deg)}}
.po-act[data-po-respond="like"][data-act="on"] > svg{
  animation:ow-liked .46s var(--ow-spring) both;
  filter:drop-shadow(0 0 9px rgba(var(--h,var(--ow-h1)),.7))}

/* DISLIKE — a knock downward. Deliberately unglamorous. */
@keyframes ow-disliked{
  0%  {transform:translateY(0) scale(1)}
  35% {transform:translateY(3px) scale(1.16)}
  65% {transform:translateY(1px) scale(.95)}
  100%{transform:translateY(0) scale(1)}}
.po-act[data-po-respond="dislike"][data-act="on"] > svg{
  animation:ow-disliked .38s var(--ow-ease) both}

/* REPOST — the glyph is a cycle, so the motion is one. */
@keyframes ow-reposted{
  0%  {transform:rotate(0deg) scale(1)}
  45% {transform:rotate(200deg) scale(1.18)}
  100%{transform:rotate(360deg) scale(1)}}
.po-act[data-po-repost][data-act="on"] > svg{
  animation:ow-reposted .58s var(--ow-spring) both;
  filter:drop-shadow(0 0 8px rgba(var(--h,var(--ow-h1)),.6))}

/* FAVOURITE — lifted, then put away. */
@keyframes ow-saved{
  0%  {transform:translateY(0) scale(1)}
  30% {transform:translateY(-6px) scale(1.14)}
  60% {transform:translateY(1px) scale(.96)}
  100%{transform:translateY(0) scale(1)}}
.po-act[data-po-save][data-act="on"] > svg{
  animation:ow-saved .46s var(--ow-spring) both;
  filter:drop-shadow(0 0 8px rgba(var(--h,var(--ow-h1)),.55))}

/* ── DOUBLE TAP ANYWHERE ON A POST ────────────────────────────────────────
   ★ FOUNDER: *"people should be able to double tap any kind of post to like
     it."*  The heart blooms from where the finger landed, then leaves. It is
   `position:fixed` and `pointer-events:none`, so it belongs to no card's
   layout and cannot be clipped by one. */
.ow-bloom{position:fixed;z-index:400;pointer-events:none;
  width:96px;height:96px;margin:-48px 0 0 -48px;
  color:rgb(var(--h,var(--ow-h1)));
  animation:ow-bloom .72s var(--ow-spring) both}
.ow-bloom svg{width:100%;height:100%;fill:currentColor;
  filter:drop-shadow(0 0 22px rgba(var(--h,var(--ow-h1)),.75))}
@keyframes ow-bloom{
  0%  {opacity:0;   transform:scale(.3)  rotate(-14deg)}
  22% {opacity:1;   transform:scale(1.16) rotate(6deg)}
  45% {opacity:1;   transform:scale(.98) rotate(-2deg)}
  100%{opacity:0;   transform:scale(1.5) rotate(0deg) translateY(-26px)}}

/* ── A COMMENT ARRIVING ─────────────────────────────────────────────────────
   Inserted at its canonical place in the thread, then eased in — so the thread
   never visibly reorders itself to accommodate it. `grid-template-rows: 0fr ->
   1fr` animates a height the browser computes, which is what makes this
   possible WITHOUT measuring anything in JavaScript and without a hard-coded
   height that would clip a long comment. */
@keyframes ow-arrive{
  from{opacity:0;transform:translateY(-6px)}
  to  {opacity:1;transform:none}}
.ow-cmt[data-arrived]{animation:ow-arrive .3s var(--ow-ease) both}
.ow-cmt--opening{display:grid;grid-template-rows:0fr;
  transition:grid-template-rows .28s var(--ow-ease)}
.ow-cmt--opening.is-in{grid-template-rows:1fr}
.ow-cmt--opening > *{overflow:hidden;min-height:0}

/* ── SOMEBODY IS TYPING ─────────────────────────────────────────────────────
   Three dots, and the row holds its height whether or not anyone is typing —
   otherwise the composer hops every time a person starts and stops, which is
   layout jumping caused by the very feature meant to feel alive. It occupies
   its space always and only its CONTENT fades. */
.ow-typing{min-height:18px;display:flex;align-items:center;gap:7px;
  padding:2px 2px 0;font-size:12px;color:var(--ow-ink-3);
  opacity:0;transition:opacity .2s var(--ow-ease)}
.ow-typing[data-on]{opacity:1}
.ow-typing__dots{display:inline-flex;gap:3px;align-items:center}
.ow-typing__dots i{width:4px;height:4px;border-radius:50%;
  background:currentColor;opacity:.45;
  animation:ow-typedot 1.1s ease-in-out infinite}
.ow-typing__dots i:nth-child(2){animation-delay:.16s}
.ow-typing__dots i:nth-child(3){animation-delay:.32s}
@keyframes ow-typedot{
  0%,60%,100%{transform:translateY(0);  opacity:.35}
  30%        {transform:translateY(-3px);opacity:1}}

/* ── AND IT ALL STOPS WHEN SOMEBODY HAS ASKED IT TO ────────────────────────
   `reduce_motion` is a real ONEWAY setting and the OS-level preference is a
   real request. Both are honoured, and what remains is the STATE — the count
   still changes, the comment still appears, the indicator still shows. Motion
   is removed; information never is. */
@media (prefers-reduced-motion:reduce){
  .po-act > b[data-live],
  .po-act[data-act="on"] > svg,
  .po-act[data-po-respond="like"][data-act="on"]::before,
  .ow-bloom,
  .ow-cmt[data-arrived],
  .ow-typing__dots i{animation:none}
  .ow-bloom{display:none}
  .ow-cmt--opening{transition:none}
}
html[data-ow-motion="reduce"] .po-act > b[data-live],
html[data-ow-motion="reduce"] .po-act[data-act="on"] > svg,
html[data-ow-motion="reduce"] .po-act[data-po-respond="like"][data-act="on"]::before,
html[data-ow-motion="reduce"] .ow-cmt[data-arrived],
html[data-ow-motion="reduce"] .ow-typing__dots i{animation:none}
html[data-ow-motion="reduce"] .ow-bloom{display:none}
html[data-ow-motion="reduce"] .ow-cmt--opening{transition:none}

/* ═══ 16 · THE IMMERSIVE VIEWER ═════════════════════════════════════════════
   ★ FOUNDER, 2026-08-26: *"immersive should bypass the popit start and
     completely and utterly switch the page to a black scrolling background —
     think of it like tiktok youtube shorts and instagram reels just better
     integrated into the platform, and a VIEWING type rather than a POSTING
     type."*

   WHAT THIS REPLACES. Immersive used to be one class on the feed column:
   every card became `height:100svh`. So "Doors at seven." — fifteen characters
   — took a whole screen, and a person scrolled a full page to reach the next
   fifteen. That is the TikTok clone the founder ruled out by name; full-screen
   is what VIDEO earns, not what a sentence is owed. The composition now lives
   in `oneway-feed.js`; this file dresses it.

   TWO RULES DECIDE EVERYTHING BELOW:

   1. IT IS A TAKEOVER. The header, the nav, the mode switch and the Home
      canvas all go. Not dimmed, not pushed aside — gone, because a viewing
      surface with a posting surface's furniture on it is a feed with effects,
      which is the thing being replaced.

   2. THE GROUND IS BLACK AND THE GALAXY IS STILL IN IT. Black is the founder's
      word and it is also what makes a photograph and a hue-lit Popit read.
      The constellations stay (§9 asks for them by name) — the coloured
      atmosphere is what goes, so what remains over black is star field and
      structure rather than a purple wash. No second sky is drawn: this is the
      SAME `.ow-world__far` canvas the rest of ONEWAY uses, with the tinted
      layers in front of it turned off. */

/* THE GROUND UNDER THE VIEWER. Black in dark; the theme's own ground in light,
   because a text group's page is transparent and a black html behind a pale
   Galaxy shows at every edge. A VIDEO position paints its own black regardless
   — that is the media treatment, not the theme. */
html[data-ow-view="immersive"]{background:#000}
html[data-theme="light"][data-theme="light"][data-ow-view="immersive"]{
  background:var(--ow-bg,#eceaf3)}
@media (prefers-color-scheme: light) {
  /* THE TWINS MUST WEIGH THE SAME — see the .ow-feedswitch note below. The
     explicit half is (0,3,1); one `:not()` made this (0,2,1). */
  html:not([data-theme="dark"]):not([data-theme="light"])[data-ow-view="immersive"]{
    background:var(--ow-bg,#eceaf3)}
}
html[data-ow-view="immersive"] body{overflow:hidden;background:transparent}
/* the App's furniture — a viewing surface has none of it */
/* ── ATMOSPHERE, SO THE GROUND HAS DEPTH ───────────────────────────────────
   A flat dark rectangle behind the Popits is what "shallow" looks like. This
   is one fixed layer of very soft, very large radial washes in the person's
   own hue pair — far bigger than any structure, so it reads as distance rather
   than as decoration, and it costs a single paint. */
html[data-ow-view="immersive"] body::before{
  content:"";position:fixed;inset:0;z-index:-4;pointer-events:none;
  background:
    radial-gradient(90% 60% at 18% 8%,  rgba(var(--ow-h1),.13), transparent 62%),
    radial-gradient(80% 55% at 86% 26%, rgba(var(--ow-h2),.10), transparent 60%),
    radial-gradient(120% 80% at 50% 108%,rgba(var(--ow-h1),.09), transparent 66%),
    radial-gradient(60% 40% at 70% 62%, rgba(255,255,255,.035), transparent 70%)}

/* ── SCROLL LOSES THE TOP BAR AND KEEPS THE DOCK, LIKE REELS AND TIKTOK ────
   ★ FOUNDER/478 + /479: *"feel just like Instagram, TikToks, or Twitters."*
   This selector list ended in a comma and ran into the canvas rule below it,
   so neither the top bar nor the dock was ever hidden — the wordmark sat
   under the floating switch — and the dock, at z-index 70 under a scroller
   at 120, was VISIBLE AND DEAD: a tap on Discovery landed on the page behind
   it (measured 2026-09-21 at 290px, elementFromPoint → .ow-fgroup). Reels and
   TikTok keep the tab bar and lose the top bar; so does Scroll now — the dock
   rides above the scroller, the media runs full-bleed beneath it, and the
   caption, the rail and the group clear it by `--ow-dock`. */
html[data-ow-view="immersive"] .ow-top{display:none}
html[data-ow-view="immersive"] .ow-nav{z-index:130}
html[data-ow-view="immersive"]{--ow-dock:calc(60px + env(safe-area-inset-bottom))}
@media (max-width:899px){
  /* the phone pill is sticky in the page's flow; over a fixed scroller it has
     to be fixed itself, at the same offset, centred the way it was */
  html[data-ow-view="immersive"] .ow-nav{position:fixed;left:50%;right:auto;transform:translateX(-50%);
    bottom:max(10px,calc(env(safe-area-inset-bottom) + 6px))}
}
@media (min-width:900px){html[data-ow-view="immersive"]{--ow-dock:0px}}
html[data-ow-view="immersive"] .po-acts--rail{bottom:calc(11px + var(--ow-dock))}
html[data-ow-view="immersive"] .ow-fpos[data-kind="media"] .po-card__foot{
  padding-bottom:calc(env(safe-area-inset-bottom) + 20px + var(--ow-dock) - env(safe-area-inset-bottom))}
html[data-ow-view="immersive"] .ow-fpos[data-kind="group"],
html[data-ow-view="immersive"] .ow-fpos[data-kind="end"]{padding-bottom:var(--ow-dock)}
/* ── A HOME CANVAS WITH NOTHING ON IT ─────────────────────────────────────
   ★ FOUNDER, 2026-09-06: *"Bake each page… I don't wanna see anything that's
     blatantly wrong when I test it."*

   MEASURED that day on a brand-new account: 249 of the first 512 pixels of
   Home were an empty dashed box and the 240px plane reserved beneath it, and
   the first Post was cut in half at the fold. The reserved plane is right for a
   canvas with tiles landing in it — it stops the page reflowing — and wrong for
   one that has nothing to land. `app/home/home.js` marks the case; this is what
   the mark does. Nothing about the canvas itself changes: the class is on THIS
   FILE'S OWN wrapper, so a canvas anywhere else on the platform is untouched.

   THE INVITATION STAYS. It is not a placeholder — it is the only way a person
   discovers they can arrange their Home at all — so it becomes one quiet line
   in their own light instead of a full-width dashed rectangle that reads as a
   component that failed to load. */
.ow-home-canvas.is-empty .ow-cv{min-height:0;height:auto}
/* AND WITH SOMETHING ON IT, THE PLANE STILL FITS WHAT IS THERE — until a
   person taps Arrange, when the full six rows come back because that room IS
   the working surface. See `watchEmptyCanvas` in app/home/home.js for the
   measurement: 240px reserved for a 70px tile put the feed switch at y=347 of
   a 551px screen. */
.ow-home-canvas.is-fitted .ow-cv{min-height:0}
.ow-home-canvas.is-empty .ow-cv-empty{
  width:auto;padding:9px 15px;font-size:12.5px;font-weight:560;
  border-radius:13px 10px 14px 11px;
  /* the words in the theme's own ink, the person's hue in the tint and the
     edge: a pale hue (a yellow) as text on the pale ground was unreadable
     (2026-09-26 sweep, a new account) */
  color:var(--ow-ink);background:rgba(var(--ow-h1),.14);
  border:0;box-shadow:inset 0 0 0 1px rgba(var(--ow-h1),.45)}
.ow-home-canvas.is-empty .ow-cv-empty:hover{background:rgba(var(--ow-h1),.16)}

/* the identity widget goes with them — Scroll is the media, full-screen, and
   a greeting floating above a video is the one thing on Home that did not
   step aside because it was added after this rule was written (today) */
html[data-ow-view="immersive"] .ow-hello,
html[data-ow-view="immersive"] .ow-home-canvas,
html[data-ow-view="immersive"] .ow-around{display:none}
/* the Feed's own 1px paging sentinel has no place in the scroller: it sat
   above the first position and put every settle 1px off (measured 2026-09-21,
   founder/478) — and out of layout it cannot trigger the Feed's pager either */
html[data-ow-view="immersive"] .ow-river__ahead{display:none}

/* ── THE WAY BACK IS THE SWITCH, NOT A CROSS ──────────────────────────────
   ★ FOUNDER, 2026-08-29: *"rather than a X button have the switch from feed to
     scrolling like on standard feed."*

   A cross says "close this", which is the wrong idea: Feed and Scroll are two
   ways of reading the same thing, and the control that moves between them is
   the same control in both. So the switch floats here instead of being hidden
   and replaced by a different affordance — one control, one meaning, in both
   projections. */
html[data-ow-view="immersive"] .ow-feedswitch{
  display:flex;position:fixed;z-index:132;
  top:calc(env(safe-area-inset-top) + 10px);
  left:50%;transform:translateX(-50%);
  margin:0;background:rgba(10,12,20,.44);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.18);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}
html[data-ow-view="immersive"] .ow-feedswitch button{color:rgba(255,255,255,.72)}
html[data-ow-view="immersive"] .ow-feedswitch button[aria-pressed="true"]{color:#fff}
/* in light the plate flips, so it reads on a pale Galaxy too.

   ── THE SYSTEM HALF WAS PAINTING ON SYSTEM-DARK DEVICES ──────────────────
   `html:not([data-theme="dark"])` sat OUTSIDE `prefers-color-scheme`, so a
   person on a system-dark device who had never chosen a theme matched it — and
   got the pale plate, white on black, in the immersive viewer. Nobody who
   builds this sees it, because they are sitting in the mode that looks right.

   ── AND THE TWINS DID NOT WEIGH THE SAME ─────────────────────────────────
   `:not()` contributes its ARGUMENT's weight. The explicit half carries
   `[data-theme="light"]` twice plus `[data-ow-view]` — three attributes; the
   system half carried one `:not()` plus `[data-ow-view]` — two. Two selectors
   that read as a matched pair, computing (0,3,1) and (0,2,1), so anything
   landing between them wins against one and loses to the other.

   The second `:not([data-theme="light"])` fixes both at once: it makes the
   weights equal AND it is correct on its own terms, because the system rule
   must stand down when a person HAS chosen light — the explicit rule already
   says that, and two rules saying it differently is how they drift. Same
   pattern as the base palette at the top of this file. */
html[data-theme="light"][data-theme="light"][data-ow-view="immersive"] .ow-feedswitch{
  background:rgba(255,255,255,.72);
  box-shadow:inset 0 0 0 1px rgba(20,20,40,.14),0 6px 18px -10px rgba(20,20,40,.4)}
html[data-theme="light"][data-theme="light"][data-ow-view="immersive"] .ow-feedswitch button{
  color:var(--ow-ink-3)}
html[data-theme="light"][data-theme="light"][data-ow-view="immersive"] .ow-feedswitch button[aria-pressed="true"]{
  color:#fff}
@media (prefers-color-scheme:light){
  html:not([data-theme="dark"]):not([data-theme="light"])[data-ow-view="immersive"] .ow-feedswitch{
    background:rgba(255,255,255,.72);
    box-shadow:inset 0 0 0 1px rgba(20,20,40,.14),0 6px 18px -10px rgba(20,20,40,.4)}
  html:not([data-theme="dark"]):not([data-theme="light"])[data-ow-view="immersive"] .ow-feedswitch button{
    color:var(--ow-ink-3)}
  html:not([data-theme="dark"]):not([data-theme="light"])[data-ow-view="immersive"] .ow-feedswitch button[aria-pressed="true"]{
    color:#fff}
}

/* the group clears the floating switch */
html[data-ow-view="immersive"] .ow-fgroup{
  top:calc(env(safe-area-inset-top) + 58px)}
/* one text Post per page (480): the Post sits in the middle of its page, the
   same on every page — a text page is that Post, not a stack */
html[data-ow-view="immersive"] .ow-fgroup[data-n="1"]{justify-content:center;
  bottom:calc(env(safe-area-inset-bottom) + 30px + var(--ow-dock,0px))}

/* ── THE GALAXY IS LEFT ALONE ──────────────────────────────────────────────
   ★ FOUNDER: *"It should be the static environment behind the text Post
     group, exactly as it exists in the normal Oneway feed… Do not animate the
     Galaxy away between every text group. Do not make the Galaxy itself a feed
     item."*

   An earlier pass here dimmed every coloured world layer to reach "a black
   scrolling background", which made the group's environment a different
   Galaxy from Home's. It is now UNTOUCHED — same wash, same net, same
   constellations — and the DARKNESS BELONGS TO THE VIDEO POSITION instead
   (below), which is the only place the founder asked for black. Nothing
   fades, nothing transitions: a video's page is simply opaque and a group's
   page is simply not. */

/* ── THE SCROLLER ─────────────────────────────────────────────────────────
   Fixed over the whole page and TRANSPARENT, so the black ground and the
   constellations behind it are the background rather than a picture of one.

   `scroll-snap-stop: always` is the single most important line here: it is
   what makes a hard fling advance exactly ONE position instead of three, on
   touch, done by the platform rather than by a script in the gesture path. */
.ow-fscroll{position:fixed;inset:0;z-index:120;
  max-width:none;width:100%;height:100%;margin:0;padding:0;gap:0;
  display:block;
  overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;
  /* the pager owns scrollTop arithmetically (index × page); the browser's
     scroll anchoring re-adjusting it when the window's spacers change is
     what put a Home-key jump 1,600px short (measured 2026-09-21, 478) */
  overflow-anchor:none;
  scroll-snap-type:y mandatory;
  -webkit-overflow-scrolling:touch;
  scrollbar-width:none;background:transparent;outline:none}
.ow-fscroll::-webkit-scrollbar{display:none}
/* ONLY PAGES LIVE IN THE SCROLL VIEW. Home's highlights band sits at the top
   of the river in the feed; left in place here it pushed every page down by
   its own height (245px, measured in the founder's pane 2026-09-26), so the
   pager's `index × page` landed between pages and the snap put the person
   back where they started — the wheel, the keys and a swipe all did nothing. */
.ow-fscroll > .ow-hlband{display:none}

/* ── ONE FEED POSITION = ONE PAGE ─────────────────────────────────────────
   A group of five text Posts and a single video are the same thing here: one
   page, one swipe. That is the founder's rule — *"the group is one feed
   position"* — expressed as geometry rather than as logic. */
/* `contain: LAYOUT` only — never `paint`.
   ★ FOUNDER, 2026-08-29: *"fix the clipping of the popit boxes and ensure it
     doesn't happen again."*
   `contain: paint` promises the browser that nothing draws outside this box,
   and the browser enforces it by CLIPPING — so every Popit's outer glow, its
   soft drop shadow and the hue bleed at its edge were cut off square at the
   page boundary. `layout` still gives the isolation that matters (a position
   cannot affect any other's layout) and costs nothing here, because each page
   is a fixed height anyway.

   AND IT CANNOT COME BACK: the group and the position both state
   `overflow:visible` explicitly, so a future `contain` or an inherited
   `overflow:hidden` has to override a written rule rather than win by
   default. */
.ow-fpos{position:relative;width:100%;
  height:100svh;min-height:100svh;
  scroll-snap-align:start;scroll-snap-stop:always;
  display:flex;align-items:stretch;justify-content:center;
  overflow:visible;
  contain:layout}
/* a video still fills and centres — it is one object, not a collection */
.ow-fpos[data-kind="media"],
.ow-fpos[data-kind="end"]{align-items:center}

/* ── THE GROUP ────────────────────────────────────────────────────────────
   A column would be the uniform masonry grid the founder ruled out, so each
   Popit carries its own width and lateral offset, seeded from its post id in
   `oneway-feed.js`. `max-width` is written against the SAME offset that moved
   it, so no seed can ever push a Popit off its own page. */
/* ★ FOUNDER, 2026-08-26: *"they should appear from top to the bottom by
     default and be easy to scroll right past."*

   So the group FILLS the page from the top rather than floating centred in it.
   Centred, a group of four sat as a clump in the middle with dead bands above
   and below — which reads as "the layout happened to place some cards here",
   the exact impression the founder ruled out. Top-aligned, the page reads as a
   section of content that begins where the page begins. */
/* `space-between` is what makes "top to the bottom" and "less posts" both
   true at once: six small Popits are DISTRIBUTED down the page rather than
   stacked at the top of it. It only ever spreads free space, so a group that
   overflows still overflows — which is what the fit test reads. */
/* ── THE STACK ────────────────────────────────────────────────────────────
   ★ FOUNDER, 2026-08-29: *"the popits should be like the one saying 'personal
     from operator' and basically touching each other… coherent and take up the
     whole screen."*

   One column, full width, nearly touching, filling the page. Three earlier
   versions tried to make this interesting — a wrapping row box, a
   pair/pair/single rhythm, hand-composed coordinates — and every one of them
   read as boxes arranged on a background rather than as a body of content. The
   fix was to stop arranging.

   `justify-content:center` keeps a short stack optically placed rather than
   pinned to the top; a full stack fills the box regardless, because `fits`
   packs until it does. */
/* PACKED FROM THE TOP, NOT CENTRED. Centring made the fit test lie: a centred
   stack moves UP as it grows, so `offsetTop + height` under-reported by half of
   every card added and the packer stopped at three while leaving 470px empty.
   From the top the arithmetic is simply the content height, and the stack fills
   the page because `fits` packs until it does. */
/* ── 4px, BECAUSE THE PAGE HAS TO COME OUT FULL ───────────────────────────
   ★ FOUNDER, 2026-08-29: *"the popits should be… basically touching each
     other"* and *"screens should appear full unless there are no posts to
     populate them."*

   Those two sentences are the same number. At 6px a 460x900 page fitted five
   Popits and used 679 of 812px — a sixth needed 137 and only 133 was left, so
   the page was 84% full and MISSED BY FOUR PIXELS. At 4px the same six fit in
   806 of 812. The tighter gap is not a fudge to win the arithmetic; it is
   nearer to what "basically touching" asks for, and it is what makes the page
   come out full instead of nearly full. */
.ow-fgroup{position:absolute;display:flex;flex-direction:column;
  gap:4px;justify-content:flex-start;
  top:calc(env(safe-area-inset-top) + 56px);
  bottom:calc(env(safe-area-inset-bottom) + 30px);
  left:50%;transform:translateX(-50%);
  /* ── ROOM FOR THE GLOW, OR THE CARD LOOKS CUT ─────────────────────────
     ★ FOUNDER, 2026-08-29: *"THE POSTS ARE STILL CLIPPING."*  They were, and
     my two earlier diagnoses were both wrong.

     THE REAL CAUSE: `.po-card` carries `box-shadow: 0 0 42px -12px rgba(--h)`
     — the hue glow that makes a Popit look lit rather than drawn. The scroller
     above it has `overflow-x: hidden`, which it must (a horizontal scrollbar
     on a vertical feed is a defect of its own). With the group only 10px from
     the viewport edge, 30px of that glow fell outside and was chopped FLAT on
     both sides — so every Popit had two hard vertical edges where it should
     have had light falling off.

     Neither `contain: paint` nor the rotating seam was doing this; both were
     real bugs and neither was this one. The fix is simply room: the group
     keeps 30px of margin, which is where the glow lives.

     ── NARROWED TO HOME'S GUTTER, 2026-08-31, AND THE TRADE IS STATED ──────
     ★ FOUNDER: *"make sure popits and posts on home and scrolling look
       consistent and exactly like posts on home right now."*

     30px each side made the immersive card 359px at a 430px viewport where
     Home's is 394px — 91.6% against 83.5%, a 35px difference a person reads as
     a different component. Home uses 18px gutters, so this now does too:
     `100% - 36px`.

     THE COST, STATED RATHER THAN DISCOVERED LATER: the glow is
     `0 0 42px -12px`, roughly 30px of reach. At an 18px gutter its outer ~12px
     falls outside the scroller's `overflow-x: hidden` and is clipped — the
     same edge the founder reported twice, now a soft tail rather than a third
     of the glow. Home does not have this problem because Home scrolls the
     DOCUMENT, so its glow paints over the page rather than against a fixed
     clip box.

     If the clipped tail reads worse than the narrower card, this is one number
     and reverting it to `100% - 60px` restores the old behaviour exactly. */
  width:min(680px, 100% - 36px);
  box-sizing:border-box}
.ow-fgroup{overflow:visible}
/* ── ON A PHONE THE FEED REACHES FOR THE EDGE ─────────────────────────────
   ★ FOUNDER/485, 2026-09-21: *"this isnt shapped right at all photos to
     small whatsoever … does not feel like a social media."*

   Measured at 290 before this: the page gutter is 18px, the card 254, and
   the picture inside its seam 246 — 85% of the screen, in a frame, under a
   header, with a rail on top of it. Instagram's picture is 100% of the
   screen. The card is ONEWAY's material and stays a card, so it does not go
   to zero; but on a phone the feed is the page, and the greeting's 18px is
   not the feed's. The river steps 10px out of the page gutter on each side
   — card 274, picture 266 — and the Scroll's group page takes the same
   8px so Home and Scroll stay one component (founder, 2026-08-31). The glow
   is `0 0 26px -18px` now, about 8px of reach: it still fits the gutter. */
@media (max-width:599px){
  /* ANY feed river, not only the one Home happens to mount as a direct child.
     Scoped to `.ow-main > .ow-surface` this reached Home and missed the
     profile, so the same card was 274px on Home and 254px on a profile
     (measured 2026-09-22 at 290) — 20px of difference a person reads as two
     different components. A river is a river wherever it is mounted. */
  .ow-surface .po-river:not(.ow-fscroll):not(.ow-cv-posts .po-river){
    margin-left:-10px;margin-right:-10px;width:auto}
  .ow-fgroup{width:min(680px, 100% - 16px)}
}
.ow-fgroup > .po-card,
.ow-fgroup > .ow-xc-slot{
  position:relative;width:100%;margin:0;flex:0 0 auto;min-width:0;
  overflow:visible}

/* ── THE SEAM DOES NOT ROTATE ON A WIDE POPIT ────────────────────────────
   ★ FOUNDER, 2026-08-29: *"THE CLIPPING STILL ISN'T FIXED."*  It was not, and
   `contain: paint` was not the cause — that was a real latent bug and a wrong
   diagnosis of this one.

   THE ACTUAL CAUSE: `.po-card__face::before` is the hand-cut seam — a dashed
   ring `inset:9px`, `transform:rotate(.35deg)`. Rotation displaces a rectangle
   by `length x sin(angle)`, so the wider the card the further its ENDS travel.
   On the old 148px Popits that was 0.9px and invisible. On a full-width 355px
   Scroll Popit it is 2.2px at each end — the ring's corners swing past the
   card's own rounded edge and are cut, which reads exactly like a clipped box.

   The seam stays; it just stops rotating at this width. The wander is a
   signature of a small hand-cut object, and a 355px panel is not one. */
.ow-fpos[data-kind="group"] .ow-fgroup > .po-card{--seam:0deg}
/* AND IT NEEDS ROOM TO BE SEEN AS A SEAM. At 10px on a 680px card the dashed
   ring sat right against the card's own lit rim, so the two merged and the
   ring read as a cut edge rather than as stitching. 14px separates them at
   every width, and the corner radius is reduced by the same amount so the
   ring follows the card's shape instead of bulging toward it. */
/* THE RING'S RADIUS FOLLOWS THE CARD, MINUS THE INSET. `border-radius:inherit`
   gives the pseudo the card's FULL radius while sitting 4px inside it, which
   is very slightly too round for the offset shape — invisible at 4px, and it
   was visible at 14px. Stated per corner so the ring follows the hand-cut
   silhouette rather than bulging toward it. */
.ow-fpos[data-kind="group"] .ow-fgroup > .po-card .po-card__face::before{
  border-radius:18px 13px 20px 15px}

/* ── AN EMPTY GALLERY SAYS NOTHING IN A TEXT GROUP ────────────────────────
   The collapsed "nothing in it yet" band is the honest empty state on a
   person's own Galleries list, where they are looking at their own drafts. In
   a Scroll text group it is 59px of chrome about an absence, on a page whose
   whole job is to carry as many readable Posts as it can hold — and measured,
   it cost half a Popit slot per card. The group is the TEXT environment; a
   Post with nothing in it contributes its words and nothing else. */
.ow-fpos[data-kind="group"] .po-card__media:has(.po-card__cover[data-empty]){
  display:none}

/* ── WHO YOU KNOW LIKED IT ────────────────────────────────────────────────
   A quiet line, not a badge: it is context for the Post, and shouting it would
   put it in competition with the words it is about. The heart is the same red
   the pressed control uses, so the two read as the same act. */
/* A <button> now (founder/488) — it opens the list of who liked this. The
   button's own chrome is cleared so the line looks exactly as it did; only
   the cursor and the press state say it is a control. */
.po-card__known{display:flex;align-items:center;gap:6px;
  background:none;border:0;box-shadow:none;appearance:none;-webkit-appearance:none;
  width:100%;text-align:left;font-family:inherit;cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  padding:0 var(--po-inset) 6px;
  font-family:var(--ow-font);font-size:12px;font-weight:500;
  color:var(--ow-ink-3)}
.po-card__known svg{width:13px;height:13px;flex:none;
  fill:var(--ow-like);stroke:var(--ow-like)}
.po-card__known span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* THE HOVER AFFORDANCE CARRIES NO COLOUR. It was `color:var(--ow-ink)` —
   the dark token — which is right on a card and wrong on a photograph: over
   the Scroll view's media page the line turned near-black under the cursor,
   on a ground the rest of the card paints white. A control that has to know
   what it is sitting on is a control that will be wrong on the next ground.
   An underline reads as "this opens" in every ground and has no colour to
   get wrong. */
.po-card__known:hover span,
.po-card__known:focus-visible span{text-decoration:underline;text-underline-offset:2px}
.po-card__known:active{transform:scale(.99)}
.ow-fpos[data-kind="group"] .po-card__known{
  padding:0 var(--po-inset) 8px;font-size:12.5px}
.ow-fpos[data-kind="media"] .po-card__known{
  position:relative;z-index:2;padding-left:18px;padding-right:64px;
  color:rgba(255,255,255,.86);text-shadow:0 1px 10px rgba(0,0,0,.8)}

/* ── THE AUTHOR'S NOTE HAS A PLACE ───────────────────────────────────────
   ★ FOUNDER, 2026-08-29: *"there is no room to see users notes on the post
     they uploaded."*

   The note is what a person is saying RIGHT NOW, and it travels with their
   face everywhere — that is the whole point of it. In the Scroll Popit it was
   inheriting the small-card treatment: clamped narrow, tucked against the
   avatar, easy to miss entirely. It now sits on its own line above the author
   with room to be read, and the Popit grows for it rather than squeezing it. */
/* THE ROOM IS FOR THE NOTE, AND ONLY WHEN THERE IS ONE.
   ★ FOUNDER/482: *"the cards on home should look the same as ones on
     scroll."* The padding above was on EVERY group card, note or not —
   measured 2026-09-21 at 290: the same Post was 145px on Home and 159px
   in Scroll, the whole 14px being this padding on an author row that had
   nothing above it. Wrapping stays (harmless on one row); the padding
   follows the note. */
.ow-fpos[data-kind="group"] .po-card__who{flex-wrap:wrap;row-gap:6px}
.ow-fpos[data-kind="group"] .po-card__who:has(> .po-note){
  padding-top:var(--po-inset)}
.ow-fpos[data-kind="group"] .po-note{
  position:static;order:-1;flex:0 0 100%;max-width:100%;
  padding:5px 11px 6px;font-size:12px;
  background:rgba(var(--h,var(--ow-h1)),.16);
  box-shadow:inset 0 0 0 1px rgba(var(--h,var(--ow-h1)),.26)}
.ow-fpos[data-kind="group"] .po-note__t{
  font-size:12px;white-space:normal;overflow:visible;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}

/* ── LARGE INTERACTIONS. NO BUTTONS. ─────────────────────────────────────
   ★ FOUNDER, 2026-08-29: *"no BUTTONS. they should be large INTERACTIONS."*

   I got this backwards on the first attempt and put plates behind the glyphs.
   The founder's original instruction stands and has not changed since
   2026-08-03: *"not as buttons, but as actual icons."* What "more normally
   shaped" asked for was the ICON — bigger, properly proportioned, with room
   around it — not a container to put it in.

   So: no ground, no border, no plate. Big glyphs, generous spacing, the hue
   when they are on. The hit area is still 44px (an invisible pseudo-element,
   see §15a) — the target is large without anything being drawn. */
.ow-fpos[data-kind="group"] .po-card__foot .po-acts .po-act{
  width:auto;height:auto;padding:0;
  background:none;box-shadow:none;border:0;border-radius:0;
  color:var(--ow-ink-2);
  transition:color .16s var(--ow-ease), transform .18s var(--ow-spring)}
/* HOME'S GLYPH, 19px, IN BOTH. These were 27px and 24px — a Like that is a
   different SIZE in each feed is a different control to the eye, and the
   founder's instruction is that a Post look the same in both.
   THE TOUCH TARGET IS NOT WHAT SHRINKS: `.po-act::after` carries an invisible
   32px-minimum hit area independent of the glyph, so this is a visual change
   and not a reachability one (346 §8). */
.ow-fpos[data-kind="group"] .po-card__foot .po-acts .po-act > svg{
  stroke-width:1.7}
/* HOME'S ACTION RHYTHM, IN BOTH PRESENTATIONS. These were 26px and 22px
   against Home's 20px — a small number that moves every control's position, so
   the thumb finds Like in a different place depending on which feed it is in.
   346 §8 treats hit targets as a first-class concern; a target that migrates
   between presentations is the same defect as one that is too small. */
.ow-fpos[data-kind="group"] .po-card__foot .po-acts{gap:20px;align-items:center}
/* THE NUMBER BELONGS TO ITS METRIC.
   ★ FOUNDER: *"the number should be way closer to the metric."*  It sat 6px
   out and, with a 26px gap to the next control, read as floating between the
   two — ambiguous about which icon it counted. 3px puts it against its own
   glyph and the gap does the separating. */
/* THE SIZE IS HOME'S, because a tally that changes size between feeds is the
   same defect as a glyph that does (founder/490). Measured: 12px on Home and
   the profile, 12.5px here, 11.5px on a media page — three sizes for one
   number. Only the POSITION is this block's business. */
.ow-fpos[data-kind="group"] .po-card__foot .po-acts .po-act > b{
  position:static;transform:none;margin-left:3px;
  font-weight:600;font-variant-numeric:tabular-nums}

/* ── A LIKED HEART IS RED EVERYWHERE ──────────────────────────────────────
   ★ FOUNDER, 2026-08-29: *"verify inactive -> neutral, active -> strong red,
     and that the state remains correct across Normal Feed, Immersive Feed,
     Post viewer, shared Post viewer, reload, real-time counter updates."*

   The red used to be scoped to `[data-kind="group"]` — so the SAME canonical
   liked Post was red in the Scroll stack and the author's hue in the standard
   feed. One state, two appearances, which is the projection law broken on the
   most-pressed control in the App. This rule carries no surface scope on
   purpose: any `.po-act` for `like` that is pressed is red, wherever it is.
   The animation is untouched — only the colour it animates in. */
.po-act[data-po-respond="like"][aria-pressed="true"]{color:var(--ow-like)}
.po-act[data-po-respond="like"][aria-pressed="true"] > svg{
  fill:var(--ow-like);stroke:var(--ow-like);
  filter:drop-shadow(0 0 10px rgba(var(--ow-like-rgb),.85))}
.po-act[data-po-respond="like"][aria-pressed="true"] > b{color:var(--ow-like)}
/* the rail over media paints its controls white; the liked heart outranks that
   for the same reason it outranks the hue */
.po-acts--rail .po-act[data-po-respond="like"][aria-pressed="true"],
.po-acts--rail .po-act[data-po-respond="like"][aria-pressed="true"] > b{
  color:var(--ow-like)}

/* A LIKED HEART IS RED. ★ FOUNDER: *"a heart should glow red."*
   Not the author's hue — every other state on this card is the hue, and a
   like has to be legible as a like across a feed of forty different colours.
   Red is the one colour nobody has to learn. The glow is the same red, so the
   press reads at a glance and from the corner of the eye. */
.ow-fpos[data-kind="group"] .po-card__foot .po-acts
  .po-act[data-po-respond="like"][aria-pressed="true"]{color:var(--ow-like)}
.ow-fpos[data-kind="group"] .po-card__foot .po-acts
  .po-act[data-po-respond="like"][aria-pressed="true"] > svg{
  fill:var(--ow-like);stroke:var(--ow-like);
  filter:drop-shadow(0 0 10px rgba(var(--ow-like-rgb),.85))}
.ow-fpos[data-kind="group"] .po-card__foot .po-acts
  .po-act[data-po-respond="like"][aria-pressed="true"] > b{color:var(--ow-like)}
/* the other three keep the author's hue — they are not the same act */
.ow-fpos[data-kind="group"] .po-card__foot .po-acts .po-act[aria-pressed="true"]{
  color:rgb(var(--h,var(--ow-h1)))}
.ow-fpos[data-kind="group"] .po-card__foot .po-acts .po-act:active{transform:scale(.9)}
@media (hover:hover) and (pointer:fine){
  .ow-fpos[data-kind="group"] .po-card__foot .po-acts .po-act:hover{
    color:var(--ow-ink);transform:scale(1.1)}
}

/* ── A POPIT ARRIVES, IT DOES NOT APPEAR ──────────────────────────────────
   ★ FOUNDER: *"when displayed as popits it should be animated fluid in our
     style and alive."*

   The stack rises in, staggered by each Popit's place in it, so a page reads
   as one body of content settling rather than six rectangles switching on. It
   runs ONCE per mount, on `transform` and `opacity` only — nothing here can
   move a neighbour or cost a frame during a swipe, and the whole sequence is
   over in under half a second so it never delays a press.

   `--i` is set by the composer; the timing lives here. */
@keyframes ow-popit-in{
  0%  {opacity:0; transform:translateY(14px) scale(.97)}
  60% {opacity:1; transform:translateY(-2px) scale(1.004)}
  100%{opacity:1; transform:none}}
.ow-fpos[data-kind="group"] .ow-fgroup > .po-card{
  animation:ow-popit-in .42s var(--ow-spring) both;
  animation-delay:calc(var(--i, 0) * 42ms);
  transition:transform .2s var(--ow-spring)}
.ow-fpos[data-kind="group"] .ow-fgroup > .po-card:active{transform:scale(.988)}
@media (hover:hover) and (pointer:fine){
  .ow-fpos[data-kind="group"] .ow-fgroup > .po-card:hover{
    transform:translateY(-2px)}
}
@media (prefers-reduced-motion:reduce){
  .ow-fpos[data-kind="group"] .ow-fgroup > .po-card{animation:none}
}
html[data-ow-motion="reduce"] .ow-fpos[data-kind="group"] .ow-fgroup > .po-card{
  animation:none}

/* ── WHICH ENVIRONMENT A POSITION STANDS IN ───────────────────────────────
   ★ FOUNDER: *"Video does not use the Galaxy background… It is the
     conventional immersive black viewing experience."* And for text: *"the
     Galaxy is simply the environment in which the grouped text content
     exists."*

   So: a group's page is TRANSPARENT and the normal ONEWAY Galaxy is what the
   Posts are standing in; a video's page is opaque black. One rule each, no
   animation between them — the black slides over the Galaxy exactly as fast as
   the scroller moves, because it IS the scroller moving. */
.ow-fpos[data-kind="group"],
.ow-fpos[data-kind="end"]{background:transparent}
.ow-fpos[data-kind="media"]{background:#000}

/* ── A POST POPIT IN THE GALAXY ───────────────────────────────────────────
   The material is the same `.po-card`; only its ground changes, because the
   page under it changed. The hue is untouched — it is still the author blended
   with the place, so a page of five Posts is five different lights rather than
   five identical panels.

   ── AND THE CODE USED TO CONTRADICT THAT SENTENCE ────────────────────────
   ★ FOUNDER, 2026-09-02: *"posts are looking great widgets and scroll should
     look EXACTLY EXACTLY like the posts."*

   This block opened by declaring the material identical and then overrode two
   material properties:

       border-radius: 22px 17px 24px 19px      vs the Post's 22px 19px 23px 20px
       box-shadow:    0 22px 60px -26px rgba(0,0,0,.95)
                                               vs the Post's
                      0 0 42px -12px rgba(var(--h),.42)

   MEASURED, not assumed: four cards in one mode returned identical radii, so
   it was a flat override rather than the per-card variation it resembles. And
   the shadow is the one that mattered — the Post's glow is HUE-TINTED, so it
   carries the author blended with the place, which is the exact property the
   comment above says is untouched. Scroll replaced it with a BLACK DROP. A
   page of five Posts was five different lights in the Feed and five identical
   dark plates in Scroll.

   Both removed. The GROUND still changes — `.po-card__face` below is rewritten
   for a black page, and that is legitimately the ground rather than the
   material. Radius and elevation are the card itself, and the card is the
   same card. */
.ow-fpos .po-card > .po-card__face{
  border-radius:inherit;
  background:
    radial-gradient(125% 95% at 20% -12%, rgba(var(--h),.30), transparent 62%),
    radial-gradient(105% 85% at 90% 110%, rgba(var(--h2),.19), transparent 66%),
    linear-gradient(168deg, rgba(255,255,255,.058), rgba(255,255,255,.016));
  box-shadow:inset 0 0 0 1px rgba(var(--h),.34),
             inset 0 1px 0 rgba(255,255,255,.11)}
/* IN LIGHT, A WHITE FILM IS NOTHING. The Popit needs an opaque ground of its
   own or it dissolves into the sky — which is what the hue washes alone did.
   Written for both explicit light and the system default, because a person who
   has chosen nothing and whose OS is light gets the same page. */
/* THE SYSTEM HALF IS GUARDED AND WEIGHS THE SAME — see the note at
   .ow-feedswitch. Unguarded, a system-dark reader who never chose a theme got
   this pale card face in the immersive viewer. */
html[data-theme="light"][data-theme="light"] .ow-fpos .po-card > .po-card__face{
  background:
    radial-gradient(125% 95% at 20% -12%, rgba(var(--h),.16), transparent 62%),
    radial-gradient(105% 85% at 90% 110%, rgba(var(--h2),.11), transparent 66%),
    linear-gradient(168deg, rgba(255,255,255,.94), rgba(255,255,255,.86));
  box-shadow:inset 0 0 0 1px rgba(var(--h),.30),
             0 10px 30px -14px rgba(20,20,40,.28)}
@media (prefers-color-scheme:light){
  html:not([data-theme="dark"]):not([data-theme="light"]) .ow-fpos .po-card > .po-card__face{
  background:
    radial-gradient(125% 95% at 20% -12%, rgba(var(--h),.16), transparent 62%),
    radial-gradient(105% 85% at 90% 110%, rgba(var(--h2),.11), transparent 66%),
    linear-gradient(168deg, rgba(255,255,255,.94), rgba(255,255,255,.86));
  box-shadow:inset 0 0 0 1px rgba(var(--h),.30),
             0 10px 30px -14px rgba(20,20,40,.28)}
}

/* Same correction as `.ow-fexit` below: the dark half was a weight lighter than
   its light twin, and the explicit-dark selector did not exist at all. */
html[data-theme="dark"][data-theme="dark"] .ow-fpos .po-card > .po-card__face{
  background:
    radial-gradient(125% 95% at 20% -12%, rgba(var(--h),.30), transparent 62%),
    radial-gradient(105% 85% at 90% 110%, rgba(var(--h2),.19), transparent 66%),
    linear-gradient(168deg, rgba(255,255,255,.058), rgba(255,255,255,.016));
  box-shadow:inset 0 0 0 1px rgba(var(--h),.34),
             inset 0 1px 0 rgba(255,255,255,.11)}
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]):not([data-theme="light"]) .ow-fpos .po-card > .po-card__face{
    background:
      radial-gradient(125% 95% at 20% -12%, rgba(var(--h),.30), transparent 62%),
      radial-gradient(105% 85% at 90% 110%, rgba(var(--h2),.19), transparent 66%),
      linear-gradient(168deg, rgba(255,255,255,.058), rgba(255,255,255,.016));
    box-shadow:inset 0 0 0 1px rgba(var(--h),.34),
               inset 0 1px 0 rgba(255,255,255,.11)}
}
/* ── TYPE FOLLOWS THE GROUND, AND THE GROUND IS NOT ALWAYS BLACK ──────────
   The first version of this hardcoded white and said "the ground here is
   black". That is true of a VIDEO or a PHOTO — the media fills the frame — and
   false of a text group, whose ground is the Galaxy, which follows the
   person's theme. In light mode that produced white type on a pale sky:
   author, time and body all invisible, on every Popit, on every group page.

   So the two kinds are separated. A media position keeps white with a shadow,
   because it sits on a photograph. A GROUP uses the theme's own ink, because
   it sits on whatever the Galaxy is that day. */
.ow-fpos[data-kind="group"] .po-card__nm,
.ow-fpos[data-kind="group"] .po-card__t{color:var(--ow-ink)}
/* ── THE WORDS ARE THE WORDS, ON EVERY SURFACE (founder/490) ────────────
   This quietened the body to `--ow-ink-2` on a Scroll text page while Home,
   the profile and the Popit drew the same sentence at full `--ow-ink`.
   Measured 2026-09-22 at 290: rgb(20,19,31) on Home, rgba(20,19,31,.86) in
   Scroll — one Post, two greys, and the Scroll copy reads as a draft of the
   Home one. It was written when the group sat directly on the Galaxy with no
   card under it; it has had a card since. The card owns its ink. */
.ow-fpos[data-kind="group"] .po-card__d{color:var(--ow-ink)}
.ow-fpos[data-kind="group"] .po-card__when,
.ow-fpos[data-kind="group"] .po-card__count{color:var(--ow-ink-3)}
.ow-fpos[data-kind="group"] .po-card__foot .po-act{color:var(--ow-ink-2)}
.ow-fpos[data-kind="group"] .po-card__foot .po-act[aria-pressed="true"]{
  color:rgb(var(--h))}
/* A NAME THAT DOES NOT FIT IS TRUNCATED, NOT SLICED. A narrow Popit is narrower
   than plenty of real handles, and the card clipped them mid-glyph with no
   ellipsis — measured: a 98px name in a 74px box, cut at the edge with nothing
   to say it had been. */
.ow-fpos .po-card__nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  max-width:100%}
/* the media page's ink is declared ONCE, further down — see "EVERYTHING ON A
   PHOTOGRAPH IS WHITE". This block used to declare half of it and the other
   block the rest, and the later one flattened the hierarchy this one set. */
.ow-fpos[data-kind="media"] .po-card__foot .po-act{color:rgba(255,255,255,.78)}
.ow-fpos[data-kind="media"] .po-card__foot .po-act[aria-pressed="true"]{
  color:rgb(var(--h))}

/* ── SIZE IS DENSITY, NOT THREE COMPONENTS ────────────────────────────────
   The Popit law, applied to a Post: a short one is a SIGNAL and a long one is
   RICH CONTENT, and they are the same component at two densities. What varies
   is how much of the body is shown and how loudly — never which controls
   exist, because a Post whose actions disappeared at one size would be a Post
   a person cannot answer. The counts go at `sm` (a tally is context); the
   glyphs never do. */
/* CLAMPING IS DENSITY AND STAYS; TYPE SIZE IS IDENTITY AND GOES.
   How many LINES of a Post show on a shared page is a legitimate difference
   between presentations. What SIZE those lines are is not — 14.5px here against
   Home's 13.3px made the same sentence a different sentence. */
.ow-fpos .po-card[data-fsize="sm"] .po-card__d{-webkit-line-clamp:2}
/* THE COUNT COMES BACK.
   ★ FOUNDER, 2026-08-31: *"exactly like posts on home right now."* Home draws
   the tally beside the heart; hiding it here meant a Post with one like showed
   one on Home and none in Scroll — the same Post disagreeing with itself about
   how many people liked it, which is worse than either answer. */
/* AND THE CONTROLS MUST FIT ON ONE LINE, WHICH IS NOT A COSMETIC POINT.
   The feed row's controls are 19px glyphs with a 20px gap: four of them need
   ~166px, and a small Popit is ~150px wide. So the Save wrapped onto a second
   row, every small card grew by ~40px, and a group that had room for eight
   Posts fitted three. Measured 2026-08-26 — cards came out 202px where the one
   card that happened not to wrap was 163px.

   Tightened rather than thinned out: the same four controls, closer together
   and slightly smaller, because dropping one would make a Post a person cannot
   answer — and `nowrap` so a longer set can never silently re-inflate the
   whole composition again. */
/* THE ACTION ROW IS IDENTITY TOO — a Like that is a different size in each
   presentation is a different control to the thumb, and 346 §8 makes hit
   targets a first-class concern. Home's row stands in both. */
.ow-fpos .po-card[data-fsize="sm"] .po-card__foot .po-acts{flex-wrap:nowrap}

/* ── A POST IS THE SAME OBJECT IN BOTH PRESENTATIONS ──────────────────────
   ★ FOUNDER, 2026-08-31: *"make sure popits and posts on home and scrolling
     look consistent and exactly like posts on home right now."*

   THIS REVERSES A RULING AND SAYS SO. On 2026-08-26 the founder asked for
   *"less posts and having the existing be smaller"*, and this block delivered
   that by shrinking the card's CHROME — a 25px avatar, 12px name, 10px
   timestamp, no body top padding. Measured at a 430px viewport it produced two
   visibly different components:

       Home    394px wide (91.6%)  ·  18px gutters  ·  34px avatar  ·  12px body pad
       Scroll  359px wide (83.5%)  ·  36px gutters  ·  24px avatar  ·  0 body pad

   The distinction that was missing: DENSITY may vary between presentations,
   IDENTITY may not. How many Posts share a page is density. The avatar, the
   type sizes and the padding rhythm are what make a Post recognisably the same
   object — shrink those and a person reads it as a different component, which
   is exactly what the founder is seeing.

   So the chrome is Home's at every size, and "less posts" is now delivered by
   the COMPOSER — `oneway-feed.js` packs one across at Home's width and fits
   fewer per page. The size classes survive below and govern TEXT CLAMPING
   only, which is density without touching identity. */
.ow-fpos .po-card[data-fsize="sm"] .po-note{max-width:100%;padding:3px 8px 4px}
.ow-fpos .po-card[data-fsize="sm"] .po-note__t{font-size:10px}
.ow-fpos .po-card[data-fsize="md"] .po-card__d{-webkit-line-clamp:6;
  font-size:15.5px;line-height:1.5}
.ow-fpos .po-card[data-fsize="lg"] .po-card__d{-webkit-line-clamp:12;
  font-size:17px;line-height:1.55}

/* ── VIDEO — THE ONE THING THAT EARNS THE WHOLE PAGE ──────────────────────
   ★ FOUNDER: *"Do not create a separate social object simply because the Post
     contains video… Extend the existing Popit rendering system so that a Post
     Popit knows how to represent its media state."*

   So this is not a video renderer. It is the same `.po-card`, in a position
   whose kind is `media`, told to let its media fill the frame. Every social
   part of it — author, hue, caption, time, like, dislike, comment, repost,
   save, share, notes — is the same DOM in the same place it is everywhere
   else. */
.ow-fpos[data-kind="media"]{padding:0}
.ow-fpos[data-kind="media"] > .po-card{
  width:100%;height:100%;border-radius:0;box-shadow:none;
  display:flex;flex-direction:column;justify-content:flex-end}
.ow-fpos[data-kind="media"] > .po-card > .po-card__face{display:none}
/* ── THE RAIL MUST BE REACHABLE ────────────────────────────────────────────
   ★ FOUNDER, 2026-08-29: *"why the hell when i click like repost goes up."*

   MEASURED: every point over the Like control resolved to `.po-card__who` —
   the author block — so on a media position the ENTIRE response rail was
   unclickable. Not mis-aimed: dead.

   `z-index:0` on an absolutely positioned element creates a STACKING CONTEXT,
   and the rail lives inside this element. So the rail's own `z-index:2` was
   competing only with its siblings INSIDE `__media`, while `__who` sat at
   `z-index:2` in the card's context — above the whole media layer, rail
   included. `z-index:auto` removes the trap: the media layer stops being a
   context, the image still paints underneath (it has no z-index of its own),
   and the rail rises into the card's stacking order where it belongs. */
.ow-fpos[data-kind="media"] .po-card__media{position:absolute;inset:0;margin:0;
  z-index:auto}
/* and it sits above the author block, not level with it — same z-index would
   leave this to DOM order, which is exactly the accident being fixed */
.ow-fpos[data-kind="media"] .po-acts--rail{z-index:4}
/* ── VIDEO CONTROLS: ALMOST NOTHING, DRAWN ────────────────────────────────
   ★ FOUNDER, 2026-08-29: the list of what a modern video feed needs — and,
     under it, *"no BUTTONS. they should be large INTERACTIONS."*

   A feed that draws a control bar over every clip is a video player with a
   feed attached. So there are three marks and no bar:

     · the SOUND control, always visible, because muted-by-default is a promise
       the person must be able to see and undo;
     · a PROGRESS line one pixel tall along the bottom edge — position without
       an object;
     · a PLAY mark that exists only while the video is paused, centred, because
       a paused video with no affordance reads as broken.

   The play/pause TARGET is the whole frame. That is the largest interaction
   there is, and it needs nothing drawn at all. */
.ow-vctl{position:absolute;inset:0;z-index:5;pointer-events:none}
.ow-vctl > *{pointer-events:auto}
/* the sound control sits LEFT of the platform mark's column in the Scroll
   view — measured 2026-09-21 at 290 and 375: the globe was under the mute
   button, top-right, both at the same inset */
.ow-vctl__sound{position:absolute;right:calc(14px + 18px + 12px);
  top:calc(env(safe-area-inset-top) + 18px);
  width:38px;height:38px;display:grid;place-items:center;
  border:0;border-radius:50%;cursor:pointer;
  background:rgba(0,0,0,.34);color:#fff;
  backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
  transition:background .16s var(--ow-ease), transform .16s var(--ow-spring)}
.ow-vctl__sound:active{transform:scale(.92)}
.ow-vctl__sound svg{width:19px;height:19px;fill:none;stroke:currentColor;
  stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.ow-vctl__sound svg path:first-child{fill:currentColor;stroke:none}

/* THE LINE IS POSITION, NOT A SCRUBBER. A draggable bar invites a person to
   aim at three pixels; the founder's *"progress indication where
   appropriate"* is indication. It sits on the safe-area edge so it is never
   under a home indicator. */
.ow-vctl__line{position:absolute;left:0;right:0;
  bottom:calc(env(safe-area-inset-bottom) + 0px);
  height:2px;background:rgba(255,255,255,.16);pointer-events:none}
.ow-vctl__line > i{display:block;height:100%;width:0;
  background:rgba(255,255,255,.9)}

.ow-vctl__play{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:76px;height:76px;display:grid;place-items:center;
  border:0;border-radius:50%;cursor:pointer;
  background:rgba(0,0,0,.34);color:#fff;
  backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
  animation:ow-arrive .18s var(--ow-ease) both}
.ow-vctl__play[hidden]{display:none}
.ow-vctl__play svg{width:34px;height:34px;fill:currentColor;stroke:none;
  margin-left:4px}          /* a triangle looks off-centre when it is centred */

/* GRACEFUL FAILURE. A dead source used to be a black rectangle with a rail
   floating on it and no explanation. */
.ow-fpos[data-media-failed] .ow-vctl__line,
.ow-fpos[data-media-failed] .ow-vctl__play{display:none}
.ow-fpos[data-media-failed]::after{content:"This video would not play.";
  position:absolute;left:0;right:0;top:50%;transform:translateY(-50%);
  text-align:center;font-family:var(--ow-font);font-weight:500;font-size:13px;
  color:rgba(255,255,255,.62);pointer-events:none;z-index:4}

/* ── BLACK IS THE GROUND, NOT A TINTED PLATE ──────────────────────────────
   ★ FOUNDER, 2026-08-29: *"Black is the default background/environment for
     immersive video positions. No Galaxy behind the video by default. No
     decorative gradient unless the product design explicitly calls for one.
     The Galaxy belongs behind the text composition environment."*

   The POSITION was already black. The COVER inside it was not: `.po-card__cover`
   carries the hue-tinted plate that stands in for missing artwork in the
   standard feed — two hue radials over `linear-gradient(#201f3c,#0d0c1a)` — and
   it was still painting it underneath the video. Any letterboxing, any frame
   before the first decode, any failed load showed a navy panel instead of
   black, which is precisely the "decorative gradient" this ruling names.

   The one gradient that stays is the legibility scrim (`::after`), and it is
   not decoration: the author, note and controls are laid OVER the footage, and
   without it they are unreadable on a bright frame. */
.ow-fpos[data-kind="media"] .po-card__cover{width:100%;height:100%;
  border-radius:0;aspect-ratio:auto;background:#000;box-shadow:none}
/* A PHOTOGRAPH FILLS THE PAGE EXACTLY AS A VIDEO DOES.
   ★ FOUNDER, 2026-08-29: *"posts with photos do not appear here but just
     appear as a normal video would."*
   The strip keeps working inside it — a multi-photo Post is still ONE Post
   with ordered pieces, so swiping sideways changes the picture and its caption
   while swiping up moves to the next feed position. Two axes, two meanings. */
.ow-fpos[data-kind="media"] .po-shots,
.ow-fpos[data-kind="media"] .po-shot{height:100%}
.ow-fpos[data-kind="media"] .po-shot img,
.ow-fpos[data-kind="media"] .po-shot video{object-fit:cover}
/* the dots and the per-photo caption clear the author block at the bottom */
.ow-fpos[data-kind="media"] .po-shots__dots{bottom:auto;top:calc(env(safe-area-inset-top) + 22px)}
.ow-fpos[data-kind="media"] .po-shots__cap{bottom:auto;
  top:calc(env(safe-area-inset-top) + 40px);
  background:none;text-align:center;padding:6px 18px;
  text-shadow:0 1px 14px rgba(0,0,0,.9)}
.ow-fpos[data-kind="media"] .po-card__cover::after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(0,0,0,.5) 0%,transparent 26%,
    transparent 44%,rgba(0,0,0,.74) 100%)}
.ow-fpos[data-kind="media"] .po-card__cover video{position:absolute;inset:0;
  width:100%;height:100%;object-fit:cover;display:block}
.ow-fpos[data-kind="media"] .po-card__who{position:relative;z-index:2;
  padding:calc(env(safe-area-inset-top) + 52px) 18px 0}
.ow-fpos[data-kind="media"] .po-card__body,
.ow-fpos[data-kind="media"] .po-card__foot{position:relative;z-index:2;
  padding-left:18px;padding-right:18px}
.ow-fpos[data-kind="media"] .po-card__foot{
  padding-bottom:calc(env(safe-area-inset-bottom) + 20px);
  /* CLEAR THE RAIL. The response rail is pinned to the right edge over the
     media, and `.po-card__count` ("in Ada's Place") carries `margin-left:auto`
     — so the Place name ran straight underneath the Save icon. Measured on the
     photo position: the two overlapped by about 20px. The foot reserves the
     rail's width instead of the two competing for the same corner. */
  padding-right:64px}
.ow-fpos[data-kind="media"] .po-card__d{-webkit-line-clamp:3;font-size:15.5px}
.ow-fpos[data-kind="media"] .po-card__t{font-size:22px}
/* ── EVERYTHING ON A PHOTOGRAPH IS WHITE, AND THE HIERARCHY SURVIVES ─────
   ★ FOUNDER/490: *"visual syncronization."*

   TWO BLOCKS USED TO DECLARE THIS and the later one won, so a timestamp came
   out the same full white as the author's name — on every other surface it is
   64% ink and reads as the quiet half of that line. One block now, and the
   same three weights the card uses in ink: the name and title full, the words
   just under, the time and place quiet.

   THE PLATFORM MARK IS IN THE LIST. `.po-card__from` was not, so on a media
   page the globe stayed `--ow-ink-2` — near-black on a photograph. Measured
   2026-09-22: rgba(20,19,31,.86) over a night shot of the boardwalk, which is
   invisible. It is the one mark on the card that says the Post is ONEWAY's,
   and it was the one mark not told what it was sitting on. */
.ow-fpos[data-kind="media"] .po-card__nm,
.ow-fpos[data-kind="media"] .po-card__t{color:#fff;
  text-shadow:0 1px 12px rgba(0,0,0,.78)}
.ow-fpos[data-kind="media"] .po-card__d{color:rgba(255,255,255,.94);
  text-shadow:0 1px 12px rgba(0,0,0,.78)}
.ow-fpos[data-kind="media"] .po-card__when,
.ow-fpos[data-kind="media"] .po-card__count{color:rgba(255,255,255,.64);
  text-shadow:0 1px 12px rgba(0,0,0,.78)}
.ow-fpos[data-kind="media"] .po-card > .po-card__from{color:rgba(255,255,255,.82);
  filter:drop-shadow(0 1px 8px rgba(0,0,0,.7))}

/* THE POOLED PLAYER (OW.media). One of a tiny pool of <video> elements moved
   between items rather than one per post — so it is styled by its CLASS, not
   by where it happens to be mounted, and it fills whichever item is holding
   it. Without this it rendered at its intrinsic video size in the corner. */
/* THE CHILD SELECTOR IS NOT DECORATION — it is what makes this rule apply.
   `.po-card > *:not(.po-card__face)` (§ the card, above) sets every child to
   `position:relative; display:block`, and at (0,2,0) it outranks a bare
   `.po-card__vid` at (0,1,0). So the pooled player was laid out IN FLOW: it
   sat below the caption instead of behind it, the author row was pushed 36px
   above the top of the screen, and the video filled 667px of an 812px page.
   Measured with a probe clip, because the corpus contains no video to catch it
   with — and it means video never actually filled the frame in the immersive
   mode this replaced either. */
.po-card > .po-card__vid{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;z-index:0;background:#000;border:0;display:block}
.ow-fpos[data-kind="media"] .po-card__vid{border-radius:0}
/* a card holding a player hides the still it was showing, so the two do not
   cross-fade into each other mid-scroll */
.po-card:has(> .po-card__vid) .po-card__cover img{opacity:0}

/* ── THE WAY OUT ──────────────────────────────────────────────────────────
   A takeover with no exit is a trap. One control, top-left, where every
   full-screen viewer on every phone puts it. */
.ow-fexit{position:fixed;z-index:132;
  top:calc(env(safe-area-inset-top) + 12px);left:14px;
  width:38px;height:38px;padding:0;border:0;cursor:pointer;
  border-radius:13px 10px 12px 11px;
  display:grid;place-items:center;color:#fff;
  background:rgba(255,255,255,.10);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.20);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  transition:background .16s var(--ow-ease),transform .16s var(--ow-spring)}
.ow-fexit:hover{background:rgba(255,255,255,.17)}
.ow-fexit:active{transform:scale(.94)}
.ow-fexit svg{width:19px;height:19px;stroke:currentColor;fill:none;
  stroke-width:2;stroke-linecap:round}
.ow-fexit:focus-visible{outline:2px solid rgba(var(--ow-h1),.95);outline-offset:2px}
/* WHITE ON A PALE SKY IS NOTHING. The way out has to be findable in both
   grounds, so in light it inverts — dark glyph on a light plate. */
html[data-theme="light"][data-theme="light"] .ow-fexit{
  color:var(--ow-ink);background:rgba(255,255,255,.72);
  box-shadow:inset 0 0 0 1px rgba(20,20,40,.14),0 6px 18px -10px rgba(20,20,40,.4)}
@media (prefers-color-scheme:light){
  html:not([data-theme="dark"]):not([data-theme="light"]) .ow-fexit{
    color:var(--ow-ink);background:rgba(255,255,255,.72);
    box-shadow:inset 0 0 0 1px rgba(20,20,40,.14),0 6px 18px -10px rgba(20,20,40,.4)}
}
/* ── THE DARK HALF WEIGHED LESS THAN THE LIGHT ONE, AND HAD NO EXPLICIT TWIN
   `html:not([data-theme="light"])` is (0,1,1); the two light selectors above
   are (0,2,1), because the file doubles them deliberately (see the note at the
   top of the theme block). Unequal halves are the defect that made the explicit
   Light theme lose to `.po-card[data-blended]` for weeks, and neither half is
   visible to whoever writes it — you are sitting in the mode that looks right.

   AND THE EXPLICIT DARK RULE WAS SIMPLY ABSENT. Somebody who chooses Dark while
   their system is Light matched neither half and fell through to the base rule,
   which happens to be the dark styling — correct by luck, not by rule. */
html[data-theme="dark"][data-theme="dark"] .ow-fexit{
  color:#fff;background:rgba(255,255,255,.10);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.20)}
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]):not([data-theme="light"]) .ow-fexit{
    color:#fff;background:rgba(255,255,255,.10);
    box-shadow:inset 0 0 0 1px rgba(255,255,255,.20)}
}
/* the overlay's own way back sits above it; the viewer's steps aside, so a
   person is never offered two identical crosses meaning different things */
.ow-fexit--over{z-index:150}
html[data-ow-over="1"] .ow-fexit:not(.ow-fexit--over){display:none}

/* ── OPENING A POST WITHOUT LEAVING THE FEED ──────────────────────────────
   ★ FOUNDER: *"Clicking/tapping an individual Post inside a group should
     open/focus that specific Post WITHOUT DESTROYING THE RELATIONSHIP TO THE
     FEED."*

   So the post surface is laid OVER the viewer rather than routed to. The
   scroller keeps its scroll position underneath; closing this returns the
   person to the exact position they pressed from, not to the top of the feed.
   It is `OW.live.post` — the same surface a post opens into from anywhere
   else, given a different host. */
.ow-fover{position:fixed;inset:0;z-index:140;
  overflow-y:auto;overscroll-behavior:contain;
  /* THE GROUND UNDER THE POST, NOT A BLACK SHEET. `OW.live.post` renders with
     the theme's own ink, so a hardcoded near-black backing put dark text on a
     dark ground in light mode — the surface was there and unreadable. The
     token follows the person's room; the opacity is what makes it read as an
     overlay rather than a page. */
  background:color-mix(in srgb, var(--ow-ground, #000) 97%, transparent);
  padding:calc(env(safe-area-inset-top) + 56px) 0 calc(env(safe-area-inset-bottom) + 24px);
  -webkit-overflow-scrolling:touch;
  animation:ow-fover-in .22s var(--ow-ease) both}
@keyframes ow-fover-in{from{opacity:0;transform:translateY(14px)}
                       to{opacity:1;transform:none}}
.ow-fover__in{max-width:620px;margin:0 auto;padding:0 18px}

/* ── THE END OF THE FEED IS A PAGE, NOT A FOOTER ──────────────────────────
   Appended as a bare child of a snap scroller it would be a page with no snap
   alignment — the half-scrolled limbo a person lands in at the bottom. It gets
   a position of its own, and it still says the feed ENDED: infinite scroll is
   the one mechanic canon forbids by name. */
.ow-fpos[data-kind="end"]{flex-direction:column}
.ow-fpos[data-kind="end"] .ow-caught{height:auto;justify-content:center}
/* THE END OF THE FEED READS IN BOTH GROUNDS. It sits on the Galaxy, not on a
   photograph, so it takes the theme's ink like the groups do — it was white,
   which vanished on a pale sky. */
.ow-fpos[data-kind="end"] .ow-caught b{color:var(--ow-ink)}
.ow-fpos[data-kind="end"] .ow-caught span{color:var(--ow-ink-3)}

/* THE MEASURING PEN. Cards are laid out at their real width to be measured and
   never painted — `visibility:hidden` still lays out, `display:none` does not,
   which is the whole reason this is not simply hidden. */
/* It is a real `.ow-fpos` — it carries that class so every rule scoped to a
   position applies to it — lifted out of view and never painted.
   `visibility:hidden` still lays out; `display:none` does not, which is the
   whole reason this is not simply hidden. */
.ow-fmeasure{position:absolute;left:0;top:0;width:100%;height:100svh;
  visibility:hidden;pointer-events:none;z-index:-1}

/* THE GROUND THE REMOVED POSITIONS USED TO OCCUPY. Exactly their height, so
   `scrollTop == index * pageHeight` stays true after a prune and nothing moves
   under the person's thumb. It renders nothing and catches nothing. */
.ow-fspacer{display:block;width:100%;flex:0 0 auto;
  pointer-events:none;background:none}

/* ── THE LINE THAT MAKES "MEASURED" TRUE ──────────────────────────────────
   `.po-card` carries `content-visibility:auto; contain-intrinsic-size:auto
   420px` — a real optimisation for a long scrolling column, and a trap for
   anything that asks a card how tall it is. A skipped card answers with the
   HINT, so every measurement came back 420px and the packer put one Post on a
   page that had room for four. It also makes an off-screen position's card
   report a size it does not have.

   Measured 2026-08-26: three short Posts reported 175 · 420 · 420 — the first
   because it happened to be the one on screen. That is the codebase's own
   recurring defect shape (a partial measurement presented as a result), caught
   here only because the composition it produced was visibly wrong.

   Off in both places: a card in the pen is BEING measured, and a card in a
   position is one page from view with a fixed page height above it, so there
   is nothing for the skipping to save. */
.ow-fmeasure .po-card,
.ow-fpos .po-card{content-visibility:visible;contain-intrinsic-size:auto}

@media (prefers-reduced-motion:reduce){
  .ow-fover{animation:none}
}

/* the switch between the two formats */
/* ★ FOUNDER/459: made better with the Center buttons — the same lifted
   material, no ring, the pressed side in the three colours */
.ow-feedswitch{display:flex;gap:4px;padding:3px;margin:0 0 12px;width:max-content;
  border-radius:16px 13px 17px 14px;
  background:linear-gradient(158deg, rgba(var(--ow-h1),.14), rgba(var(--ow-h3,var(--ow-h2)),.10) 52%, var(--ow-tile-base));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.14)}
.ow-feedswitch button{font-family:var(--ow-font);font-weight:600;font-size:13px;
  border:0;cursor:pointer;padding:9px 15px;border-radius:13px 11px 14px 12px;
  color:var(--ow-ink-2);background:none;transition:color .16s,background .16s}
.ow-feedswitch button[aria-pressed="true"]{color:#fff;
  background:linear-gradient(150deg,rgb(var(--ow-h1)),rgb(var(--ow-h3,var(--ow-h2))) 58%,rgba(var(--ow-h2),.85));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.35)}

/* ── WHICH FEED (founder, chat 2026-10-03: custom feeds, and a Center's feed
   browsed like For You) — inside the worlds bar, right after For You, in the
   bar's own buttons: Following, the pinned feeds, + Feeds, your places */
.ow-worlds__feeds{display:contents}
.ow-worlds__b[data-src]{cursor:pointer}
.ow-feedtabs__add{display:inline-flex;align-items:center;gap:5px}
.ow-feedtabs__add svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round}
.ow-feedchoice{margin:0 0 4px}
/* the Feeds page */
.ow-feeds__sec{display:flex;flex-direction:column;gap:10px;padding:6px 0 18px;
  border-bottom:1px solid var(--ow-line, rgba(127,127,127,.18))}
.ow-feeds__sec:last-child{border-bottom:0}
.ow-feedrow__w{appearance:none;-webkit-appearance:none;border:0;background:none;padding:0;text-align:left;cursor:pointer;font-family:var(--ow-font)}
.ow-feedrow .ow-reach__acct{white-space:normal}
.ow-feedrow .ow-reach__mk svg{fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round}
.ow-feedpicked,.ow-feedfound{display:flex;flex-wrap:wrap;gap:8px}
.ow-feedpicked:empty,.ow-feedfound:empty{display:none}
.ow-feedpick{appearance:none;-webkit-appearance:none;border:0;cursor:pointer;font:600 13px/1 var(--ow-font);
  padding:9px 12px;border-radius:13px 11px 14px 12px;color:var(--ow-ink);
  background:color-mix(in srgb, var(--ow-ink) 7%, transparent)}
.ow-feedpick--add{color:var(--ow-ink-2)}
.ow-feedhead{display:flex;flex-direction:column;gap:6px;margin:4px 0 16px}
.ow-feedhead__n{margin:0;font-family:var(--ow-font-display);font-weight:700;font-size:22px;color:var(--ow-ink)}
.ow-feedhead__s{margin:0;font-size:14px;line-height:1.45;color:var(--ow-ink-2)}
.ow-feedhead__acts{display:flex;gap:10px;flex-wrap:wrap;margin-top:6px}

/* ── UPCOMING — the person's bookings, on Home (founder, chat 2026-10-03) ── */
.ow-upcoming{display:flex;flex-direction:column;gap:6px;margin:0 0 14px;padding:12px 14px;
  border-radius:20px 17px 21px 18px;
  background:linear-gradient(158deg, rgba(var(--ow-h1),.12), rgba(var(--ow-h3,var(--ow-h2)),.08) 52%, var(--ow-tile-base));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.14)}
.ow-upcoming__h{font-size:12.5px;font-weight:650;letter-spacing:.02em;color:var(--ow-ink-3)}
.ow-upcoming__row{appearance:none;-webkit-appearance:none;border:0;background:none;cursor:pointer;text-align:left;
  display:flex;align-items:center;gap:12px;padding:6px 0;font-family:var(--ow-font);color:var(--ow-ink)}
.ow-upcoming__face{flex:none;width:38px;height:38px;border-radius:11px 10px 12px 11px;overflow:hidden;display:grid;place-items:center;
  background:color-mix(in srgb, var(--ow-ink) 7%, transparent)}
.ow-upcoming__face img{width:100%;height:100%;object-fit:cover;display:block}
.ow-upcoming__face svg,.ow-now__ic svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.ow-upcoming__w{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}
.ow-upcoming__w b{font-size:15px;font-weight:600}
.ow-upcoming__w span{font-size:13px;color:var(--ow-ink-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ow-upcoming__st{flex:none;font-size:12px;font-weight:600;color:var(--ow-ink-2)}

/* ── THE BOOKING BANNER — S2: a bar, not a box. Nothing behind it dims or
   stops; it sits under the header, says one thing, and goes with one press. */
.ow-now{position:fixed;left:12px;right:12px;top:calc(env(safe-area-inset-top, 0px) + 64px);z-index:70;
  display:flex;align-items:center;gap:10px;padding:10px 8px 10px 12px;max-width:560px;margin:0 auto;
  border-radius:18px 15px 19px 16px;color:var(--ow-ink);
  /* OPAQUE: the hue wash is laid over the world's own floor, so nothing
     under the bar reads through it */
  background:linear-gradient(150deg, rgba(var(--ow-h1),.20), rgba(var(--ow-h3,var(--ow-h2)),.12) 55%, transparent), var(--ow-ground, #eceaf3);
  box-shadow:0 10px 30px rgba(0,0,0,.18), inset 0 1px 0 rgba(255,255,255,.2);
  -webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);animation:ow-now-in .32s var(--ow-settle, ease) both}
@keyframes ow-now-in{from{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.ow-now{animation:none}}
.ow-now__ic{flex:none;width:34px;height:34px;display:grid;place-items:center;border-radius:10px;
  background:color-mix(in srgb, var(--ow-ink) 8%, transparent)}
.ow-now__w{flex:1 1 auto;min-width:0;appearance:none;-webkit-appearance:none;border:0;background:none;padding:0;
  text-align:left;cursor:pointer;font-family:var(--ow-font);color:inherit;display:flex;flex-direction:column;gap:2px}
.ow-now__w b{font-size:14.5px;font-weight:650;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ow-now__w span{font-size:12.5px;color:var(--ow-ink-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ow-now__x{flex:none;width:34px;height:34px;border:0;background:none;cursor:pointer;color:var(--ow-ink-2);
  display:grid;place-items:center;border-radius:50%}
.ow-now__x svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round}

/* ── THE IDENTITY WIDGET — the first thing on Home ──────────────────────────
   ★ FOUNDER/250 §5: *"The primary widget should be an identity-oriented
     widget. It can include: a greeting … relevant contextual information."*

   THE NAME LEADS AT THE SAME SIZE AS A PROFILE'S, because it is the same fact
   about the same person; Home is their world and this is them standing in it.
   The standing sentence sits under it at reading size, in the second ink — a
   fact about the world, one step quieter than the person.

   NO SLAB. It is not a card in the person's feed; it is the person. A frame
   around it would make it one tile among the tiles they arranged below, and
   the ordering is the meaning — them, then what they put here, then what
   their world did. Air above the canvas separates the three. */
.ow-hello{padding:6px 0 14px}
/* the greeting (founder/460): the rotation's word, and — at a Center —
   "Hello Ada, welcome to Showboat!" with the Center's own face before it */
.ow-hello__hi{margin:0 0 2px;font-family:var(--ow-font);font-weight:500;font-size:15px;
  letter-spacing:.01em;color:var(--ow-ink-2);display:flex;align-items:center;gap:8px;cursor:default}
.ow-hello__hi[data-at]{color:var(--ow-ink);cursor:pointer;font-family:var(--ow-font-display);
  font-weight:650;font-size:19px;line-height:1.2;letter-spacing:-.015em;align-items:flex-start;margin-bottom:6px}
.ow-hello__hi[data-at] .ow-hello__at{width:26px;height:26px;margin-top:-1px}
.ow-hello__at{width:22px;height:22px;object-fit:cover;display:block;border-radius:7px 5px 6px 5px}
.ow-hello__at[data-shape="round"]{border-radius:50%}
.ow-hello__at[data-shape="square"]{border-radius:3px}
.ow-hello__name{font-family:var(--ow-font-display);font-weight:650;
  font-size:clamp(21px,5vw,26px);letter-spacing:-.01em;line-height:1.06;
  margin:0;color:var(--ow-ink)}
.ow-hello__standing{margin:6px 0 0;max-width:46ch;font-size:14.5px;line-height:1.45;
  color:var(--ow-ink-2)}

/* ── THE WORLDS BAR — every button is a Center ────────────────────────────
   ★ FOUNDER, 2026-09-10 (docs/founder/366): a horizontal discovery bar like
     YouTube's topic buttons, and every button OPENS a Center. Entering another
     world, not filtering this one.

   IT BLEEDS TO THE SCREEN EDGES on purpose: a row that scrolls inside the
   18px column reads as a boxed control; one that runs edge to edge reads as
   the top of the world, which is what YouTube's row does and why it feels
   like navigation. The mask fades the ends so the cut-off chip says "more".

   ONE PRESSED LANGUAGE. "For You" wears the same hue gradient the pressed
   Feed/Scroll switch wears, so the two rows read as one system rather than
   two controls somebody designed on different days. A world that has chosen a
   hue carries it as a thin ring — identity, not decoration, and nothing is
   invented for a world that has none.

   NO EXPLAINERS. A chip is a name. Founder: "no subphrases randomly
   explaining people's things." */
.ow-worlds{display:flex;gap:6px;align-items:center;
  margin:0 -18px 10px;padding:2px 18px 6px;
  overflow-x:auto;overflow-y:hidden;scrollbar-width:none;
  -webkit-overflow-scrolling:touch;overscroll-behavior-x:contain;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 14px,#000 calc(100% - 26px),transparent);
          mask-image:linear-gradient(90deg,transparent,#000 14px,#000 calc(100% - 26px),transparent)}
.ow-worlds::-webkit-scrollbar{display:none}
/* ★ FOUNDER/459, 2026-09-20: *"the for you feed scroll and center buttons
   need to be made better."* A Center button was a thin pill with a 1px ring
   — a line (437) around a name. It is a small Popit now: the Center's own
   face at 32px, its name, and ITS OWN light as the material (its hue, the
   third colour, the lifted floor), no ring at all. A world with no hue of
   its own wears the world's. */
.ow-worlds{gap:8px}
.ow-worlds__b{flex:none;font-family:var(--ow-font);font-weight:600;font-size:13.5px;
  line-height:1;white-space:nowrap;border:0;cursor:pointer;padding:12px 16px;
  border-radius:16px 13px 17px 14px;color:var(--ow-ink);
  --wh:var(--ow-h1);--wh3:var(--ow-h3,var(--ow-h2));
  background:
    radial-gradient(120% 90% at 18% -10%, rgba(var(--wh),.36), transparent 62%),
    linear-gradient(158deg, rgba(var(--wh),.16), rgba(var(--wh3),.12) 52%, var(--ow-tile-base));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.16), 0 12px 26px -16px rgba(var(--wh),.7);
  transition:color .16s,background .16s,transform .12s}
.ow-worlds__b:active{transform:scale(.97)}
/* a world wearing its own face (founder/409): the picture leads the name,
   cut the way the Center chose — a tile unless it said round */
.ow-worlds__b.has-face{display:inline-flex;align-items:center;gap:10px;padding:5px 16px 5px 6px}
.ow-worlds__face{width:32px;height:32px;object-fit:cover;display:block;
  border-radius:10px 8px 11px 9px}
.ow-worlds__face[data-shape="round"]{border-radius:50%}
.ow-worlds__face[data-shape="square"]{border-radius:5px}
/* its own light, when it chose one: the angle becomes the material */
.ow-worlds__b[style*="--w-hue"]{
  background:
    radial-gradient(120% 90% at 18% -10%, hsl(var(--w-hue) 100% 68% / .36), transparent 62%),
    linear-gradient(158deg, hsl(var(--w-hue) 100% 68% / .16), hsl(calc(var(--w-hue) + 42) 100% 66% / .12) 52%, var(--ow-tile-base));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.16), 0 12px 26px -16px hsl(var(--w-hue) 100% 60% / .7)}
.ow-worlds__b.is-here{color:#fff;cursor:default;
  background:linear-gradient(150deg,rgb(var(--ow-h1)),rgb(var(--ow-h3,var(--ow-h2))) 58%,rgba(var(--ow-h2),.85));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.35), 0 12px 26px -14px rgba(var(--ow-h1),.8)}
/* the immersive viewer is the media, full-screen; the worlds go with the
   canvas and the footnote, and come back with them */
html[data-ow-view="immersive"] .ow-worlds{display:none}

/* THE END OF THE FEED. It is a real place to arrive at, not a spinner that
   never resolves — the person has seen what their world did. */
/* AROUND ONEWAY — the public river shown under a person's own quiet feed.
   Deliberately quieter than the person's own world: a seam above it, muted
   label, and no accent. It is real content, and it is honestly not yours. */
.ow-around{display:flex;flex-direction:column;gap:4px;padding:26px 2px 12px;
  margin-top:14px;border-top:1px solid var(--ow-seam,rgba(255,255,255,.08))}
.ow-around__title{font-family:var(--ow-font-display);font-weight:650;
  font-size:12px;letter-spacing:.01em;
  color:var(--ow-ink-3)}
.ow-around__why{margin:0;font-size:13px;line-height:1.5;color:var(--ow-ink-3);
  max-width:44ch}

.ow-caught{display:flex;flex-direction:column;align-items:center;gap:6px;
  padding:38px 20px 30px;text-align:center}
.ow-caught b{font-family:var(--ow-font-display);font-weight:650;font-size:16px;
  color:var(--ow-ink)}
.ow-caught span{font-size:13px;line-height:1.5;color:var(--ow-ink-3);max-width:34ch}

/* ═══ 18 · THE GALLERY VIEWER ═══════════════════════════════════════════════
   The Gallery is the core content object (objects/GALLERY.md) — a PLACE where
   media exists together. Opening one from the feed had no surface at all: the
   App carried a complete read contract it never called, so a card could be
   liked and saved but never entered.

   It reads OLDEST-FIRST, deliberately. A Gallery is a STORY — demolition,
   flooring, reveal — and reading it forward is what removes the need for
   "Part 1 / Part 2". The FEED is newest-first, because discovering a moment
   and reading a story are different acts (the 2026-07-25 ruling). */

.ow-gal{margin:-6px -18px 0}
.ow-gal__head{padding:16px 18px 4px}
.ow-gal__who{display:flex;align-items:center;gap:10px}
.ow-gal__av{width:34px;height:34px;flex:none;border-radius:12px 10px 13px 11px;
  display:grid;place-items:center;overflow:hidden;color:#fff;
  font-family:var(--ow-font-display);font-weight:650;font-size:13px;
  background:linear-gradient(150deg,rgb(var(--ch1,var(--ow-h1))),rgba(var(--ch2,var(--ow-h2)),.75));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.45)}
.ow-gal__av img{width:100%;height:100%;object-fit:cover}
.ow-gal__nm{font-family:var(--ow-font-display);font-weight:600;font-size:14px;
  color:var(--ow-ink)}
.ow-gal__t{font-family:var(--ow-font-display);font-weight:650;
  font-size:clamp(20px,4.8vw,25px);letter-spacing:-.01em;line-height:1.08;
  margin:12px 0 0;color:var(--ow-ink)}
.ow-gal__sub{font-family:var(--ow-font-atmos);font-weight:600;font-size:10.5px;
  letter-spacing:.2em;color:var(--ow-ink-3);margin-top:7px}
.ow-gal__desc{font-size:14px;line-height:1.55;color:var(--ow-ink-2);margin-top:10px;
  max-width:62ch}

/* THE STORY. Each piece full-width in reading order; the caption belongs to
   the piece, not to a lightbox nobody opened. */
.ow-gal__story{display:flex;flex-direction:column;gap:14px;padding:18px 18px 0}
.ow-piece{position:relative;border-radius:20px 16px 21px 17px;overflow:hidden;
  background:linear-gradient(160deg,rgba(var(--ow-h1),.22),rgba(var(--ow-h2),.1));
  box-shadow:inset 0 1px 0 var(--ow-lift),inset 0 0 0 1px var(--ow-rim)}
.ow-piece img,.ow-piece video{display:block;width:100%;height:auto;
  max-height:76vh;object-fit:cover}
.ow-piece__cap{padding:10px 14px 12px;font-size:13.5px;line-height:1.45;
  color:var(--ow-ink-2)}
/* a contribution the owner has not approved is VISIBLE to them and marked —
   never silently mixed in with what is already public */
.ow-piece[data-pending]{box-shadow:inset 0 0 0 1.5px rgba(var(--ow-h1),.85)}
.ow-piece[data-pending]::before{content:"awaiting your approval";position:absolute;
  top:10px;left:10px;z-index:2;font-family:var(--ow-font-atmos);font-weight:600;
  font-size:12px;letter-spacing:.01em;color:#fff;
  padding:5px 9px;border-radius:9px 7px 10px 8px;background:rgba(0,0,0,.62);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.28)}
/* an empty Gallery says so rather than rendering an empty frame */
.ow-gal__none{padding:26px 18px;color:var(--ow-ink-3);font-size:13.5px;line-height:1.5}

/* ═══ 19 · A SWITCH ═════════════════════════════════════════════════════════
   A person's own setting. The label leads and the control follows, because the
   thing being decided matters more than the mechanism — and the whole row is
   the target, which is the only size that works under a thumb. */
.ow-switch{display:flex;align-items:center;gap:14px;width:100%;text-align:left;
  border:0;cursor:pointer;font:inherit;color:var(--ow-ink);padding:12px 14px;
  border-radius:16px 13px 17px 14px;
  background:linear-gradient(178deg,var(--ow-panel-1),var(--ow-panel-2));
  box-shadow:inset 0 1px 0 var(--ow-lift),inset 0 0 0 1px var(--ow-rim);
  -webkit-tap-highlight-color:transparent}
.ow-switch + .ow-switch{margin-top:8px}
.ow-switch__b{min-width:0;flex:1 1 auto}
.ow-switch__b b{display:block;font-family:var(--ow-font-display);font-weight:600;
  font-size:14.5px;letter-spacing:-.01em}
.ow-switch__b span{display:block;font-size:12.5px;line-height:1.45;
  color:var(--ow-ink-3);margin-top:3px}
.ow-switch__k{flex:none;width:44px;height:26px;border-radius:14px;position:relative;
  background:var(--ow-rim);transition:background .18s var(--ow-ease)}
.ow-switch__k::after{content:"";position:absolute;top:3px;left:3px;width:20px;height:20px;
  border-radius:50%;background:var(--ow-ink);opacity:.72;
  transition:transform .2s var(--ow-spring),opacity .18s}
.ow-switch[aria-pressed="true"] .ow-switch__k{
  background:linear-gradient(150deg,rgb(var(--ow-h1)),rgba(var(--ow-h2),.85))}
.ow-switch[aria-pressed="true"] .ow-switch__k::after{transform:translateX(18px);
  background:#fff;opacity:1}
.ow-switch:focus-visible{outline:2px solid rgba(var(--ow-h1),.9);outline-offset:2px}
/* the server refused it — the row says so instead of lying quietly */
.ow-switch[data-failed]{box-shadow:inset 0 1px 0 var(--ow-lift),
  0 0 0 1.5px rgba(255,170,110,.9)}

/* REDUCE MOTION IS A SETTING, NOT ONLY A MEDIA QUERY. A person who asks for it
   in the App means it on every device, not just one that reports the OS
   preference — so the App stamps the root and the world obeys both signals. */
html[data-motion="reduced"] *{animation:none !important;transition:none !important}
html[data-motion="reduced"] .ow-world__neb,
html[data-motion="reduced"] .ow-world__stars{animation:none !important}

/* ═══ 20 · WHEN SOMETHING DOES NOT LOAD ═════════════════════════════════════
   Founder (2026-08-02): a failure takes down its own block, never the page —
   and when several fail at once they share ONE retry rather than asking the
   same question five times.

   The grouped control sits above the thumb-line so it is reachable without
   scrolling, and it states the count plainly: a person deciding whether to
   retry wants to know if it is one thing or everything. */
.ow-retry-all{position:sticky;bottom:8px;z-index:66;display:flex;align-items:center;
  gap:12px;margin:14px 0 2px;padding:11px 14px;
  border-radius:17px 14px 18px 15px;
  background:linear-gradient(178deg,var(--ow-panel-1),var(--ow-panel-2));
  box-shadow:inset 0 1px 0 var(--ow-lift),inset 0 0 0 1px var(--ow-rim),
    0 10px 26px -12px var(--ow-drop)}
.ow-retry-all span{flex:1 1 auto;min-width:0;font-size:13px;line-height:1.4;
  color:var(--ow-ink-2)}
.ow-retry-all .po-act{flex:none}
/* a block that failed keeps its own retry beneath its reason */
.po-w[data-state="refused"] .po-act{margin-top:10px}

/* ═══ 21 · THE RESPONSE BAR ═════════════════════════════════════════════════
   A heart, a thumbs-down, a repost, a save — and beside them, how many opened
   it. Founder (2026-08-02): all three are separate metrics on a Gallery and
   none of them is summed into a score.

   The glyph carries the meaning and the number sits with it, so the bar reads
   at a glance without a word of chrome. `.po-act--stat` is the one that is not
   a button: views are something that happened, not something you can press. */
.po-act--icon{display:inline-flex;align-items:center;gap:6px;padding:8px 11px;
  line-height:1}
.po-act--icon svg{width:17px;height:17px;stroke:currentColor;fill:none;
  stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round;display:block}
.po-act--icon b{font-family:var(--ow-font);font-weight:600;font-size:12.5px;
  letter-spacing:0}
/* pressed = this is YOUR answer. The heart fills, because a half-hearted
   heart is the one control everyone reads wrong. */
.po-act--icon[aria-pressed="true"] svg{fill:currentColor;stroke:currentColor}
.po-act--icon[data-po-respond="dislike"][aria-pressed="true"]{color:var(--ow-ink)}
/* views are a fact, not an affordance — no press state, no pointer */
.po-act--stat{cursor:default;opacity:.72}
.po-act--stat svg{fill:none}

/* ═══ 22 · THE ONEWAY GLOBE — built the way Vegas signs animate ════════════
   Founder (2026-08-02): recreate the Harrah's globe; a left-to-right sweep
   does not work; deeper, more vibrant purple.

   IT DID NOT WORK BECAUSE IT WAS NOT THE TECHNIQUE. From the Neon Museum on
   how vintage signs actually animate:

     · CHASING runs along the sign's BORDER — bulbs wired in sequence around
       the perimeter, "producing the impression of continuous movement."
       Around the edge, never across the face.
     · FLASHING CIRCUITS light "sections of tubing in coordinated patterns",
       which is how they simulated "flowing water, SPINNING WHEELS, cascading
       letters." A turning wheel on a static sign is GROUPS firing in a
       repeating phase cycle — the wagon-wheel effect. Not a smooth sweep.

   So the rim carries a chase around its circumference, and the meridians fire
   in three coordinated circuits. Three phases is what reads as rotation;
   lighting them one after another just reads as lighting them one after
   another, which is exactly what it looked like.

   NEON IS GEOMETRY — a wide dim halo beneath a thin bright core. No filter, so
   nothing the mobile safe floor strips, and it renders identically everywhere.
   Strokes SCALE with the sphere (a non-scaling stroke made the 20px globe a
   solid blob), and below 34px the cage drops to the three lines that still
   say "globe" when that is all the room there is. */
.ow-globe{--g:52px;--cycle:1.45s;--turn-n:15;
  display:inline-block;width:var(--g);height:var(--g);
  line-height:0;color:rgb(var(--ow-h1))}
.ow-globe svg{width:100%;height:100%;display:block;overflow:visible;
  shape-rendering:geometricPrecision}
/* EVERY SHAPE IN THE CAGE IS A TUBE — including <path>.
   This rule listed ellipse, circle and line. When the meridians became half-arcs
   they became PATHS, and a path with no rule inherits the SVG defaults: fill
   black, stroke none. So every arc rendered as a solid black shape with no tube
   at all — invisible against the ground, and painting over the parallels behind
   it, which is exactly why half the sphere looked empty and the horizontals had
   "disappeared". The turn was animating opacity on black fills the whole time
   and measured perfectly while showing nothing. A selector that lists element
   NAMES is a list that goes stale the moment the geometry changes; this one now
   covers the shape it draws, whatever tag carries it. */
.ow-globe ellipse,.ow-globe circle,.ow-globe line,.ow-globe path{fill:none;
  stroke:rgb(var(--ow-h1));stroke-linecap:round}

/* THE HALO MUST NOT FILL THE SPHERE. Lines each carrying a wide bloom overlap
   into a solid ball — the cage disappears and it reads as a purple marble. A
   neon tube's glow is TIGHT; the darkness between tubes is what makes it a
   sign. So the halo is narrow and faint, and the core carries the brightness.

   SHARPNESS IS CONTRAST, NOT WIDTH. A softer picture is a halo doing the work
   the core should do — so the halo stays down and the lit core goes to full,
   which widens the gap between a lit tube and an unlit one. That gap is what
   the eye reads as focus.

   BUT THE UNLIT FLOOR CANNOT BE NEAR ZERO. Fifteen arcs with three lit and the
   rest at .17 meant HALF THE SPHERE WENT DARK at every instant — it read as a
   broken globe, not a turning one, and it was why the horizontals disappeared.
   The sign is always on; the light TRAVELS OVER a cage that is already lit.
   That is also what a neon globe actually is: every tube is glowing, and the
   animation is the brighter pass moving across them.

   AND THE LIMB MUST NOT EAT THE PICTURE. "Highlight the globe features" is not
   "make the rim enormous": at 2.2 core over a 4.6 halo it rendered as a thick
   bright ring with a faint cage hiding inside it, which is a ring, not a globe.
   The features are highlighted RELATIVE to the cage, and the cage has to be
   present enough for the relation to mean anything. */
.ow-globe__halo *{stroke-width:3;opacity:.1}
.ow-globe__core *{stroke-width:1.3;opacity:.44}

/* ── THE LINES THAT ARE NOT VERTICAL ─────────────────────────────────────────
   Founder: "the lines that aren't vertical need to be better defined."

   They were being treated as filler — thin, dim, and breathing on an animation
   of their own, which on a turning globe is exactly wrong: the parallels are
   the one part of a rotating sphere that DOES NOT MOVE. Dimming and pulsing
   them removed the only fixed reference the turn could be read against, so the
   whole thing shimmered instead of rotating.

   They are STRUCTURE, and structure SITS BACK. Corrected twice, in opposite
   directions, which is the useful part: first they were so faint the sphere
   read as a disc, then heavy enough that the eye landed on the horizontals
   instead of the turn. The right weight is present-but-subordinate — enough
   latitude to make it a sphere, quiet enough that the travelling light is what
   you actually watch. Steady, never joining the turn, breathing so slightly
   that you would only notice if it stopped. */
.ow-globe__core .ow-globe__par{stroke-width:1.15;opacity:.36}
.ow-globe__halo .ow-globe__par{stroke-width:2.5;opacity:.075}
.ow-globe__core .ow-globe__eq{stroke-width:1.3;opacity:.5}
.ow-globe__halo .ow-globe__eq{stroke-width:2.9;opacity:.1}
/* the limb and the axis complete the read: a globe is known by its edge, its
   equator and its poles */
.ow-globe__core .ow-globe__rim{stroke-width:1.85;opacity:.86}
.ow-globe__halo .ow-globe__rim{stroke-width:3.9;opacity:.2}
.ow-globe__core .ow-globe__ax{stroke-width:1.55}
.ow-globe__halo .ow-globe__ax{stroke-width:3.4}

/* ── THE TURN ── one direction, one motion ───────────────────────────────────
   Every arc runs the same keyframe, offset by its place across the face. The
   bump occupies ~26% of the cycle, so with fifteen stops roughly four arcs are
   lit at graduated levels at any instant: a gradient, gliding — which is what
   rotation is. Narrower and it strobes; wider and the whole cage is lit and
   nothing appears to move.

   The strike is instant and the decay eases. That is a tube striking, and it
   is the whole difference between neon and a cross-fade. */
.ow-globe[data-state="loading"] .ow-globe__mer{
  animation:ow-turn var(--cycle) linear infinite;
  animation-delay:calc(var(--k) / var(--turn-n) * var(--cycle) * -1)}
.ow-globe[data-state="loading"] .ow-globe__halo .ow-globe__mer{
  animation-name:ow-turn-halo}
@keyframes ow-turn{
  0%    {opacity:.44}
  4%    {opacity:1}      /* the strike */
  9%    {opacity:.9}
  17%   {opacity:.66}     /* the phosphor letting go */
  26%   {opacity:.44}
  100%  {opacity:.44}
}
@keyframes ow-turn-halo{
  0%    {opacity:.1}
  4%    {opacity:.7}
  9%    {opacity:.48}
  17%   {opacity:.24}
  26%   {opacity:.1}
  100%  {opacity:.1}
}
/* ── THE LIFE IN IT ── flash, without a second direction ─────────────────────
   Founder: "a little bit more animated and flashy." The constraint that cannot
   be traded for it is the one that took three attempts to earn — ONE direction.
   So none of this travels: every addition either happens IN PLACE or rides the
   turn that already exists.

   1 · THE LIMB FLICKERS like a tube that has been on since 1966 — a stutter,
       never a fade, and off-beat from the turn so the two never lock into a
       pattern the eye can predict.
   2 · THE PARALLELS BREATHE, slowly and out of phase with each other, at a
       period unrelated to the turn. They still do not move; a lit sign is
       never perfectly steady, and dead-steady is what read as flat.
   3 · THE STRIKING ARC OVERSHOOTS to white before settling into hue. A real
       tube's strike is hotter than its burn, and that flash of near-white at
       the head is most of what "neon" means. It is the head only — the rest of
       the cage stays the Center's colour, so the identity never washes out. */
.ow-globe[data-state="loading"] .ow-globe__rim{
  animation:ow-sign-beat 4.3s steps(1,end) infinite}
@keyframes ow-sign-beat{
  0%,72%  {opacity:.86}
  74%     {opacity:1}
  76%     {opacity:.42}
  78%     {opacity:1}
  81%     {opacity:.6}
  83%     {opacity:1}
  100%    {opacity:.86}
}
.ow-globe[data-state="loading"] .ow-globe__par{
  animation:ow-breathe 3.7s ease-in-out infinite;
  animation-delay:calc(var(--i,0) * -.62s)}
@keyframes ow-breathe{
  0%,100% {opacity:.32}
  50%     {opacity:.44}
}
.ow-globe[data-state="loading"] .ow-globe__eq{
  animation-name:ow-breathe-eq}
@keyframes ow-breathe-eq{
  0%,100% {opacity:.46}
  50%     {opacity:.58}
}
/* THE HOT HEAD. A second copy of the cage, white, visible only at the instant
   of the strike — so the overshoot is additive light rather than the hue being
   drained out of the tube. It rides the SAME delay as the turn, so it cannot
   drift out of step with the arc it belongs to. */
.ow-globe__hot *{stroke:#fff;stroke-width:.85;opacity:0;fill:none}
.ow-globe[data-state="loading"] .ow-globe__hot .ow-globe__mer{
  animation:ow-hot var(--cycle) linear infinite;
  animation-delay:calc(var(--k) / var(--turn-n) * var(--cycle) * -1)}
@keyframes ow-hot{
  0%   {opacity:0}
  3%   {opacity:.85}
  8%   {opacity:.18}
  14%  {opacity:0}
  100% {opacity:0}
}

/* ── SUCCESS ── deliberately NOT the loading animation done faster ───────────
   Loading is a band crossing a sphere; success is the opposite gesture — the
   turn stops dead, every tube strikes at once, one bright arc runs the limb a
   single lap, and the sphere throws two rings off its edge. Motion OUT of the
   object rather than across it, which is why it reads as arrival and not as
   another lap of the same thing. */
.ow-globe__lap,.ow-globe__burst{opacity:0;
  transform-box:view-box;transform-origin:50px 50px}
.ow-globe__lap{stroke-width:2.6;stroke-linecap:round;stroke-dasharray:26 250}
.ow-globe__burst{stroke-width:2}
/* ★ THE ARRIVAL, 2026-08-30. Founder: *"create a more noticeable and special
   completion animation the whole thing can rapidly flash for ideas but dont
   have it perfectly in my head like i do other concepts / the loading
   concept."*

   So this one is proposed rather than transcribed, and it is built out of the
   vocabulary already ruled on rather than a new idea: it is the ATTRACT
   SEQUENCE a marquee runs when it comes on. Three movements, and each one is
   answering a constraint that is already law here.

   1 · THE SLAM. The travelling band stops DEAD and every tube in the sphere
       strikes at once. Loading is a light crossing a globe; arrival is the
       globe deciding, and the first thing that says so is the motion ending.

   2 · THE BURST — the whole sign flashing, which is the founder's own word.
       FOUR THINGS MAKE IT A SIGN AND NOT A STROBE:
         · every transition is `steps(1,end)`, so it is on or off and never in
           between. A fade here reads as a web spinner, which is the one thing
           the globe has never been allowed to be.
         · the beats are UNEVEN. A metronome reads as a loading indicator; real
           flashing circuits do not divide evenly, and the irregularity is most
           of what makes it look like hardware.
         · the dark side gets SHALLOWER each time — .12, .18, .30, .52 — so the
           sign is visibly settling rather than repeating. It has a direction,
           and the direction is toward lit.
         · THE LIMB LEADS BY A BEAT. A negative delay on the rim fires the
           border circuit fractionally before the cage, the way a bordered sign
           actually wires. Craft, at no cost, and no second motion.

   3 · THE SETTLE. It holds LIT, and brighter than it burns while working, so
       the end state is legibly different from the state it came out of.

   THE WHITE IS ADDITIVE, NOT DRAINED. The hot layer — already in the markup —
   fires with the first three hits and stops, so the flashes are hotter than the
   hue and the hold is pure hue. That overshoot is most of what "neon" means,
   and doing it by adding white ON TOP means the Center's colour never washes
   out of the mark.

   STILL NO RINGS LEAVE THE GLOBE (founder, 2026-08-26) and still no filter, so
   this survives the mobile safe floor unchanged. Reduced motion is handled
   where it always was, at the bottom of this section: the sign simply arrives
   already lit. */
.ow-globe[data-state="done"] .ow-globe__core *{
  animation:ow-sign-arrive 2s 1 both}
.ow-globe[data-state="done"] .ow-globe__halo *{
  animation:ow-sign-arrive-halo 2s 1 both}
.ow-globe[data-state="done"] .ow-globe__hot *{
  animation:ow-arrive-hot 2s 1 both}
/* the border circuit fires first — one frame's worth, felt rather than seen */
.ow-globe[data-state="done"] .ow-globe__rim{animation-delay:-42ms}
/* THE VICTORY LAP RUNS DURING THE SETTLE, on a sign that is already lit and
   holding. Through the flashing it was invisible, and two motions at once is
   the thing three earlier passes were spent removing. */
.ow-globe[data-state="done"] .ow-globe__lap{
  animation:ow-lap .54s cubic-bezier(.3,.7,.2,1) 1.48s 1 both}

/* ★ IT HAS TO FEEL LIKE AN EVENT. Founder, 2026-08-30: *"make it cool to fast
   should feel like an event."*

   THE FIRST TWO PASSES WERE A STUTTER, NOT AN OCCASION — a uniform flicker
   that started at full brightness and got quieter. Nothing in it built to
   anything, which is why it read as fast even when it was long enough.

   An event has a SHAPE, and this is the shape a marquee actually has when it
   comes on at dusk:

     1 · THE CUT-OUT (0–180ms). The turn stops and the sign goes DARK. This is
         the most important 180ms in the sequence and it is the one thing the
         earlier passes had none of — you cannot make something feel like an
         arrival without a silence in front of it. The globe was working a
         moment ago; now there is nothing, and the eye stays.
     2 · THE TEASE (220–560ms). Two lone strikes, wide apart, a tube trying to
         catch and not holding. Sparse on purpose: this is the part that says
         something is ABOUT to happen rather than that it already has.
     3 · THE BUILD (660–1440ms). Now it accelerates — seven hits with the gaps
         closing 7·7·6·6·5·4·4 — so the sequence is visibly running toward
         something instead of repeating.
     4 · THE PAYOFF (1440ms). Everything strikes and STAYS. The white overshoot
         is at its maximum here and nowhere else, so the brightest instant in
         the whole animation is the instant it arrives.
     5 · THE SETTLE (1440–2000ms). It cools out of white into the Center's hue
         and holds there, and the lap runs the limb once while it does. That
         cooling is what a real tube does after a strike, and it is what turns
         the payoff into a held note rather than a cut. */
@keyframes ow-sign-arrive{
  0%   {opacity:.44; animation-timing-function:steps(1,end)}
  2%   {opacity:0;   animation-timing-function:steps(1,end)}   /* the cut-out */
  9%   {opacity:0;   animation-timing-function:steps(1,end)}   /* the silence  */
  11%  {opacity:.85; animation-timing-function:steps(1,end)}   /* tease one    */
  13%  {opacity:.02; animation-timing-function:steps(1,end)}
  20%  {opacity:.02; animation-timing-function:steps(1,end)}
  22%  {opacity:.95; animation-timing-function:steps(1,end)}   /* tease two    */
  25%  {opacity:.03; animation-timing-function:steps(1,end)}
  30%  {opacity:.03; animation-timing-function:steps(1,end)}
  33%  {opacity:1;   animation-timing-function:steps(1,end)}   /* the build    */
  36%  {opacity:.05; animation-timing-function:steps(1,end)}
  40%  {opacity:1;   animation-timing-function:steps(1,end)}
  43%  {opacity:.06; animation-timing-function:steps(1,end)}
  47%  {opacity:1;   animation-timing-function:steps(1,end)}
  50%  {opacity:.07; animation-timing-function:steps(1,end)}
  53%  {opacity:1;   animation-timing-function:steps(1,end)}
  56%  {opacity:.08; animation-timing-function:steps(1,end)}
  59%  {opacity:1;   animation-timing-function:steps(1,end)}
  61%  {opacity:.1;  animation-timing-function:steps(1,end)}
  64%  {opacity:1;   animation-timing-function:steps(1,end)}
  66%  {opacity:.12; animation-timing-function:steps(1,end)}
  68%  {opacity:1;   animation-timing-function:steps(1,end)}
  70%  {opacity:.14; animation-timing-function:steps(1,end)}
  72%  {opacity:1;   animation-timing-function:cubic-bezier(.16,.9,.24,1)}
  100% {opacity:.94}                                           /* held lit */
}
/* THE HALO IS CAPPED WHERE IT IS HELD AND NOT WHERE IT SPIKES. A SUSTAINED
   halo at full stops being a glow and becomes a fill — every tube's bloom
   overlapping into a marble. A 40ms spike has no time to read as a fill and
   reads as a bloom, which is what a flashbulb looks like. So it swings to .88
   on the payoff and settles back under the cap. */
@keyframes ow-sign-arrive-halo{
  0%   {opacity:.1;  animation-timing-function:steps(1,end)}
  2%   {opacity:0;   animation-timing-function:steps(1,end)}
  9%   {opacity:0;   animation-timing-function:steps(1,end)}
  11%  {opacity:.5;  animation-timing-function:steps(1,end)}
  13%  {opacity:.01; animation-timing-function:steps(1,end)}
  20%  {opacity:.01; animation-timing-function:steps(1,end)}
  22%  {opacity:.6;  animation-timing-function:steps(1,end)}
  25%  {opacity:.01; animation-timing-function:steps(1,end)}
  30%  {opacity:.01; animation-timing-function:steps(1,end)}
  33%  {opacity:.66; animation-timing-function:steps(1,end)}
  36%  {opacity:.02; animation-timing-function:steps(1,end)}
  40%  {opacity:.7;  animation-timing-function:steps(1,end)}
  43%  {opacity:.02; animation-timing-function:steps(1,end)}
  47%  {opacity:.74; animation-timing-function:steps(1,end)}
  50%  {opacity:.02; animation-timing-function:steps(1,end)}
  53%  {opacity:.78; animation-timing-function:steps(1,end)}
  56%  {opacity:.03; animation-timing-function:steps(1,end)}
  59%  {opacity:.82; animation-timing-function:steps(1,end)}
  61%  {opacity:.03; animation-timing-function:steps(1,end)}
  64%  {opacity:.85; animation-timing-function:steps(1,end)}
  66%  {opacity:.04; animation-timing-function:steps(1,end)}
  68%  {opacity:.88; animation-timing-function:steps(1,end)}
  70%  {opacity:.04; animation-timing-function:steps(1,end)}
  72%  {opacity:.88; animation-timing-function:cubic-bezier(.16,.9,.24,1)}
  100% {opacity:.32}
}
/* THE WHITE PEAKS AT THE PAYOFF AND NOWHERE ELSE, then COOLS. The teases are
   barely white — a tube that has not caught yet is not hot — and each build hit
   is hotter than the last, so the white is itself part of the crescendo. Then
   it drains over the settle: a real tube cools out of white into its colour,
   and that cooling is why the end reads as a held note. It reaches zero, so the
   mark always finishes in the Center's hue and never a washed-out white. */
@keyframes ow-arrive-hot{
  0%   {opacity:0;   animation-timing-function:steps(1,end)}
  9%   {opacity:0;   animation-timing-function:steps(1,end)}
  11%  {opacity:.34; animation-timing-function:steps(1,end)}
  13%  {opacity:0;   animation-timing-function:steps(1,end)}
  22%  {opacity:.46; animation-timing-function:steps(1,end)}
  25%  {opacity:0;   animation-timing-function:steps(1,end)}
  33%  {opacity:.58; animation-timing-function:steps(1,end)}
  36%  {opacity:0;   animation-timing-function:steps(1,end)}
  40%  {opacity:.64; animation-timing-function:steps(1,end)}
  43%  {opacity:0;   animation-timing-function:steps(1,end)}
  47%  {opacity:.7;  animation-timing-function:steps(1,end)}
  50%  {opacity:0;   animation-timing-function:steps(1,end)}
  53%  {opacity:.76; animation-timing-function:steps(1,end)}
  56%  {opacity:0;   animation-timing-function:steps(1,end)}
  59%  {opacity:.82; animation-timing-function:steps(1,end)}
  61%  {opacity:0;   animation-timing-function:steps(1,end)}
  64%  {opacity:.88; animation-timing-function:steps(1,end)}
  66%  {opacity:0;   animation-timing-function:steps(1,end)}
  68%  {opacity:.94; animation-timing-function:steps(1,end)}
  70%  {opacity:0;   animation-timing-function:steps(1,end)}
  72%  {opacity:1;   animation-timing-function:cubic-bezier(.3,.1,.5,1)}
  100% {opacity:0}                                          /* cooling to hue */
}
/* AT 20px THERE IS NO EVENT TO STAGE. The small globe is a busy indicator
   beside a button, and a two-second occasion there is the animation getting in
   the way of the thing it is reporting on. Same choreography, compressed until
   it reads as one quick flash — which is all that size can say anyway. */
.ow-globe[data-small][data-state="done"] .ow-globe__core *,
.ow-globe[data-small][data-state="done"] .ow-globe__halo *,
.ow-globe[data-small][data-state="done"] .ow-globe__hot *{animation-duration:.66s}
.ow-globe[data-small][data-state="done"] .ow-globe__lap{
  animation-delay:.49s;animation-duration:.2s}

@keyframes ow-lap{
  0%   {opacity:0;  stroke-dashoffset:276}
  10%  {opacity:1}
  100% {opacity:0;  stroke-dashoffset:0}
}
@keyframes ow-burst{
  0%   {opacity:0;   transform:scale(.94)}
  9%   {opacity:.85; transform:scale(1)}
  100% {opacity:0;   transform:scale(1.46)}
}

/* BELOW ~34px there is no room for eleven lines. Rather than draw a smudge
   and call it an icon, the small sizes keep the rim, the equator and the
   polar meridian — the three that still say "globe". */
.ow-globe[data-small] .ow-globe__par:not(.ow-globe__eq),
.ow-globe[data-small] .ow-globe__mer:not(.ow-globe__ax){display:none}
.ow-globe[data-small] .ow-globe__halo *{stroke-width:6}

/* REDUCE MOTION — the sign stays LIT rather than blank. Someone who asked for
   stillness gets a globe, not an empty space. */
html[data-motion="reduced"] .ow-globe *{animation:none !important}
html[data-motion="reduced"] .ow-globe__core *{opacity:.8 !important}
html[data-motion="reduced"] .ow-globe__halo *{opacity:.24 !important}
@media (prefers-reduced-motion:reduce){
  .ow-globe *{animation:none !important}
  .ow-globe__core *{opacity:.75}
  .ow-globe__halo *{opacity:.24}
}

/* where a loading globe sits inside a surface */
.ow-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:12px;padding:46px 20px;text-align:center}
.ow-loading__w{font-family:var(--ow-font-atmos);font-weight:600;font-size:10px;
  letter-spacing:.01em;color:var(--ow-ink-3)}

/* ═══ 23 · THE DOOR — what a person who has not arrived yet is shown ═════════
   Founder (2026-08-03): the loading globe, the ONEWAY hero logo, and a way in.
   Not an error state: this is the front of the building, not a locked cupboard,
   so it is composed like a room — centred, unhurried, with the mark alive. */
.ow-door{--h:var(--ow-h1);
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:18px;padding:56px 22px 64px;text-align:center;min-height:52svh}
/* .po-act PAINTS FROM var(--h), which a Popit sets on its own scope. On the
   door there is no Popit above it, so every action resolved to no background,
   no border and no lift — the way in rendered as three words of plain text. A
   component that inherits its material from an ancestor needs that ancestor
   declared, not assumed; the door declares it. */
.po-act--primary{padding:11px 20px;font-size:13.5px;
  background:linear-gradient(180deg,rgba(var(--h),.34),rgba(var(--h),.17));
  box-shadow:inset 0 1px 0 var(--ow-lift), 0 0 0 1px rgba(var(--h),.62),
             0 6px 22px -10px rgba(var(--h),.7)}
.po-act--ghost{background:none;color:var(--ow-ink-2);
  box-shadow:0 0 0 1px rgba(var(--h),.2)}
.ow-door__acts .po-act{padding:11px 20px;font-size:13.5px}
.ow-door__logo{height:26px;width:auto;opacity:.92;
  filter:drop-shadow(0 2px 18px rgba(var(--ow-h1),.28))}
/* NO `invert(1)` — the door swaps the FILE in light mode (popit-live.js), for
   the same reason the top mark does: the safe floor strips filters, so the
   invert never ran on the tier most people are on. A filter here would now
   double-invert the black asset back to white. */
html[data-theme="light"][data-theme="light"] .ow-door__logo{
  filter:drop-shadow(0 2px 18px rgba(var(--ow-h1),.2))}
.ow-door__title{font-family:var(--ow-font-display);font-weight:700;letter-spacing:-.01em;font-size:22px;line-height:1.1;color:var(--ow-ink);margin:2px 0 0}
.ow-door__why{margin:0;max-width:34ch;color:var(--ow-ink-2);font-size:15px;
  line-height:1.55}
.ow-door__acts{display:flex;gap:10px;flex-wrap:wrap;justify-content:center;margin-top:4px}
.ow-door__acts .po-act{text-decoration:none;min-width:112px;justify-content:center}
/* THE DOOR INSIDE A SURFACE, not as the whole page. Same language, less room:
   there is a header above it and a navigation bar below it, and at the page
   Door's spacing the action landed under the dock — on a phone, the one thing
   to do on an empty screen was the one thing you had to scroll to find.
   Padding and the minimum only; type, mark and centring are the Door's. */
.ow-door--inline{padding:22px 22px 18px;min-height:auto;gap:14px}

/* the mark keeps turning behind the door — the platform is not idle, the person
   simply has not said who they are */
.ow-door .ow-globe{margin-bottom:2px}

/* ═══ 24 · THE SKY — the constellation field ════════════════════════════════
   Founder (2026-08-03): sign-up is the basis; "the main things are backgrounds
   and layouts."

   The background of ONEWAY is a NETWORK — drifting points that draw a line to
   whatever they come near. It is the product's own thesis rendered as
   atmosphere, which is why it belongs on every surface rather than on one page.

   .ow-net   the live canvas (capable devices only — see the JS)
   .ow-sky   the static field UNDER it: a hue wash plus a woven network that
             needs no script, so a phone, a reduced-motion reader and the first
             paint before JS all get a real background instead of black
   .ow-worldwash  the global hue wash the world-tween drives */
.ow-sky,.ow-net,.ow-worldwash{position:fixed;inset:0;pointer-events:none}
.ow-sky{z-index:-3;
  background:
    radial-gradient(115% 75% at 16% 4%,  rgba(var(--ow-h1),.10), transparent 56%),
    radial-gradient(115% 80% at 86% 20%, rgba(var(--ow-h2),.085),transparent 56%),
    radial-gradient(140% 95% at 52% 114%,rgba(var(--ow-h1),.07), transparent 60%);
  transition:background .8s var(--ow-ease)}
/* the static network — the same vocabulary the canvas draws, as one cached
   image. No filter, no blend, nothing the safe floor strips. */
/* ── THE TILED CONSTELLATION IS RETIRED ──────────────────────────────────
   It was ONE hand-drawn constellation — five paths, eleven stars — in a 420px
   tile set to repeat: the same picture 5.3 times across a 2226px screen at
   opacity .5. That is the "same constellation pattern again" the founder kept
   seeing, and no amount of added randomness elsewhere could have hidden it,
   because it was not random at all.

   `OW.farField` replaces it: position-seeded, seven different grammars, drawn
   to `.ow-world__far`. Kept as a rule with no image so the safe floor and any
   surface still mounting `.ow-sky` degrades to plain ground rather than to a
   missing layer. */
.ow-sky::after{content:none}
.ow-world__far{position:absolute;inset:0;width:100%;height:100%;
  pointer-events:none;opacity:1}
html[data-fx="safe"] .ow-world__far{opacity:.6}
/* THE FIELD BEHIND THE FIELD. Smaller, dimmer and very slightly soft, so the
   eye reads it as further away rather than as more of the same. `scale` shrinks
   the whole drawing, which is what makes its structures smaller than the near
   field's WITHOUT the generator having to know anything about depth. */
.ow-world__deep{position:absolute;inset:0;width:100%;height:100%;
  pointer-events:none;opacity:.5;
  transform:scale(.72);transform-origin:52% 44%;
  filter:blur(.4px)}
/* the safe floor drops the far layer first — it is the one that costs the most
   and carries the least */
html[data-fx="safe"] .ow-world__deep{display:none}
@media (prefers-reduced-motion:reduce){ .ow-world__deep{filter:none} }
/* the live canvas sits above the static field and replaces it visually */
.ow-net{z-index:-2;opacity:.9}
html:not([data-fx="safe"]) .ow-net ~ .ow-sky::after,
.ow-net + .ow-worldwash ~ .ow-sky::after{opacity:.2}
.ow-worldwash{z-index:-1;
  background:radial-gradient(60% 50% at 12% 10%, rgba(var(--ow-h1),.10), transparent 70%),
             radial-gradient(55% 52% at 88% 84%, rgba(var(--ow-h2),.09), transparent 72%)}

/* a surface sitting IN the world rather than on a page */
.ow-inworld{position:relative;z-index:1}

/* ═══ 25 · RESPONSES AS ICONS, IN TWO PLACES ════════════════════════════════
   Founder (2026-08-03): TikTok's side rail for the scrolling feed, Instagram
   and Twitter's bottom row — "but not as buttons, but as actual icons."

   THE BUTTON CHROME IS THE THING BEING REMOVED. .po-act paints a filled,
   bordered, lifted control, which is right for "Follow" or "Sign in" and wrong
   for a heart: on a feed the response IS the glyph, and drawing a box round it
   makes ten boxes compete with the photograph they are about. The behaviour is
   untouched — the same element, the same aria-pressed, the same fill-on-press
   — only the material is taken away.

   ONE SET OF CONTROLS, TWO PLACEMENTS. The rail and the row render identical
   markup; nothing about the like is different because it moved. */
.po-acts--rail .po-act,
.po-card__foot .po-acts .po-act{
  background:none;box-shadow:none;padding:0;border-radius:0;
  color:var(--ow-ink-2);
  transition:transform .18s var(--ow-spring),color .18s var(--ow-ease)}
.po-acts--rail .po-act:hover,
.po-card__foot .po-acts .po-act:hover{color:var(--ow-ink);transform:scale(1.08)}
.po-acts--rail .po-act[aria-pressed="true"],
.po-card__foot .po-acts .po-act[aria-pressed="true"]{color:rgb(var(--ow-h1))}

/* ── THE RAIL — over the media, right edge, glyph above its tally ──────────── */
.po-card__media{position:relative}
.po-acts--rail{position:absolute;right:9px;bottom:11px;width:auto;
  flex-direction:column;flex-wrap:nowrap;align-items:center;gap:15px;
  z-index:2;pointer-events:auto}
.po-acts--rail .po-act{flex-direction:column;gap:3px}
.po-acts--rail .po-act svg{width:25px;height:25px;stroke-width:1.75;
  /* a glyph must survive whatever photograph is under it — a shadow, not a
     plate, because a plate behind every icon rebuilds the buttons this exists
     to remove. Not `filter`: the mobile safe floor strips it, and these sit on
     the one surface a phone spends all its time looking at. */
  paint-order:stroke;stroke-linejoin:round}
.po-acts--rail .po-act b{font-size:11.5px;font-weight:600;letter-spacing:.01em}
/* the scrim IS the legibility, and it costs one gradient rather than a filter
   per icon — it also keeps the bottom of a bright photo from fighting the title */
.po-acts--rail::before{content:"";position:absolute;inset:-14px -12px -14px -18px;
  border-radius:20px;pointer-events:none;z-index:-1;
  background:radial-gradient(120% 90% at 70% 60%,rgba(0,0,0,.42),transparent 72%)}
.po-acts--rail .po-act,.po-acts--rail .po-act b{color:#fff}
.po-acts--rail .po-act[aria-pressed="true"]{color:rgb(var(--ow-h1))}

/* ── THE ROW — under a post with no media, Instagram/Twitter placement ─────── */
/* ── SIX ACTS, EACH WITH ITS NUMBER, AT A PHONE'S WIDTH ───────────────────
   A fixed gap was affordable while most tallies were absent. Now that every
   act carries one (founder/488) the row is six glyphs plus six numbers —
   measured ~298px of content in a 226px row at 290 CSS px, and the Share fell
   off the right edge.

   SO THE ROW DISTRIBUTES INSTEAD OF SPACING. `space-between` pins the first
   and last act to the row's own edges and shares what is left, which is what
   every feed does and what makes the row read as one object rather than a
   cluster. The gap becomes a FLOOR, not a fixed value, so the controls can
   never touch and never overflow — at any width, in any feed, with however
   many acts the server offers. Nothing here depends on there being six.

   IT IS LAST ON PURPOSE: the fixed `gap:` declarations further up set the old
   value for the Scroll view's sizes, and this supersedes them so one rule
   governs the row everywhere. */
.po-card__foot .po-acts{gap:6px;align-items:center;
  justify-content:space-between;width:100%;flex-wrap:nowrap}
.po-card__foot .po-acts .po-act{flex:0 0 auto}
.po-card__foot .po-acts .po-act{flex-direction:row;gap:6px}
.po-card__foot .po-acts .po-act svg{width:19px;height:19px}
.po-card__foot .po-acts .po-act b{font-size:12px;font-weight:600}

/* a count of zero is not a number to print — the glyph alone is the honest
   state, and "0" reads as a judgement nobody made */
.po-acts .po-act b:empty{display:none}

/* ── THE SCROLL'S MEDIA PAGE SHAPES THE ROW INTO THE RAIL ──────────────────
   ★ FOUNDER, 2026-08-03: the side rail *"for the scrolling feed"*, the bottom
     row for *"Instagram, Twitter"*.
   ★ FOUNDER/485, 2026-09-21: Home *"does not feel like a social media"* —
     photos too small, and a TikTok rail sat on top of every picture.

   The responses are mounted ONCE, in the foot's row, on every card (see the
   placement block in oneway-popit.js). Here, and only here — a picture or a
   video that IS the whole page — that row becomes the rail: same controls,
   same DOM, the presentation decides the shape. Everything the old
   `.po-acts--rail` rules did, keyed to the page instead of to the Post. */
.ow-fpos[data-kind="media"] .po-card__foot .po-acts{
  position:absolute;right:9px;bottom:calc(11px + var(--ow-dock,0px));width:auto;
  flex-direction:column;flex-wrap:nowrap;align-items:center;gap:15px;
  z-index:4;pointer-events:auto}
.ow-fpos[data-kind="media"] .po-card__foot .po-acts .po-act{
  flex-direction:column;gap:3px;background:none;box-shadow:none;padding:0;
  color:#fff}
.ow-fpos[data-kind="media"] .po-card__foot .po-acts .po-act svg{
  width:25px;height:25px;stroke-width:1.75;paint-order:stroke;stroke-linejoin:round}
.ow-fpos[data-kind="media"] .po-card__foot .po-acts .po-act b{
  font-size:11.5px;font-weight:600;letter-spacing:.01em;color:#fff}
.ow-fpos[data-kind="media"] .po-card__foot .po-acts::before{content:"";position:absolute;
  inset:-14px -12px -14px -18px;border-radius:20px;pointer-events:none;z-index:-1;
  background:radial-gradient(120% 90% at 70% 60%,rgba(0,0,0,.42),transparent 72%)}
.ow-fpos[data-kind="media"] .po-card__foot .po-acts .po-act[aria-pressed="true"],
.ow-fpos[data-kind="media"] .po-card__foot .po-acts .po-act[aria-pressed="true"] b{
  color:rgb(var(--ow-h1))}
.ow-fpos[data-kind="media"] .po-card__foot .po-acts .po-act[data-po-respond="like"][aria-pressed="true"],
.ow-fpos[data-kind="media"] .po-card__foot .po-acts .po-act[data-po-respond="like"][aria-pressed="true"] b{
  color:var(--ow-like)}

/* ═══ 26 · THE SURFACE VOCABULARY — taken from sign-up, which is the basis ═══
   Founder (2026-08-03): "we have a sign up page which is a perfect basis for how
   the final should look like. the main things are backgrounds and layouts."

   So these are not designed here. They are sign-up's own values — the card, the
   section label, the field, the row, the primary — lifted verbatim into the one
   stylesheet every surface loads, so sign-in, the door and the App compose the
   same thing instead of each approximating it. That approximation is exactly
   what produced two rejected login screens tonight: I had the tokens and built
   my own components out of them, which is a palette, not a design language.

   THE HUE ENTERS AS ACCENT, NOT AS PAINT. The card stays the neutral glass it
   already is; focus rings, section rules and the live network take the person's
   colour. Repainting the whole surface in someone's hue is how a product with a
   hue system ends up looking like a themed skin. */
.ow-card{width:100%;max-width:500px;position:relative;overflow:hidden;
  padding:30px;border-radius:18px;
  background:linear-gradient(180deg,rgba(28,30,40,.62),rgba(18,20,28,.72));
  border:1px solid rgba(255,255,255,.09);
  box-shadow:0 14px 38px rgba(0,0,0,.42),
             0 1px 0 rgba(255,255,255,.06) inset,
             0 -1px 0 rgba(0,0,0,.35) inset}
/* ASK ABOUT BOTH SPELLINGS, OR SAFARI SKIPS THE WHOLE BLOCK.
   Safari only learned unprefixed `backdrop-filter` in 17.4. Before that it
   supports the effect ONLY as `-webkit-backdrop-filter` — so a gate written
   `@supports (backdrop-filter:blur(1px))` evaluates FALSE there and drops
   everything inside, INCLUDING the `-webkit-` declaration written to serve
   exactly that browser. The fallback was sealed inside the gate that
   excluded it, so the glass did not degrade — it vanished, and every Popit
   and sheet rendered flat. That is what "depth is not showing" looks like.
   Testing both spellings costs nothing and is the whole fix. */
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  html:not([data-fx="safe"]) .ow-card{
    backdrop-filter:blur(22px) saturate(155%);
    -webkit-backdrop-filter:blur(22px) saturate(155%)}
}
.ow-card>*{position:relative;z-index:1}
.ow-card__h{font-size:20px;font-weight:600;letter-spacing:-.015em;color:#fff;
  line-height:1.12;margin:0 0 6px}
.ow-card__sub{font-size:12px;font-weight:500;line-height:1.45;
  color:var(--ow-ink-3);margin:0 0 24px}

/* the section rule — small caps, wide tracking, the one thing that gives these
   surfaces their order without drawing another box */
.ow-lbl{font-size:12px;font-weight:680;letter-spacing:.01em;
  color:rgba(235,240,250,.42);margin:4px 0 12px}

.ow-row{display:flex;gap:12px}
.ow-row .ow-fld{flex:1;min-width:0}
.ow-fld{margin-bottom:15px}
.ow-fld label{display:block;font-size:12px;font-weight:650;
  letter-spacing:.09em;color:var(--ow-ink-3);margin-bottom:7px}
.ow-fld label .opt{font-weight:500;font-size:9.5px;letter-spacing:.1em;
  color:var(--ow-ink-3);opacity:.7;margin-left:6px}
.ow-fld input,.ow-fld select,.ow-fld textarea{width:100%;
  background:var(--ow-field);border:1px solid var(--ow-field-edge);
  border-radius:11px;padding:12px 14px;font-size:16px;color:var(--ow-ink);
  font-family:var(--ow-font);outline:none;
  transition:border-color .18s,background .18s,box-shadow .18s}
.ow-fld input::placeholder,.ow-fld textarea::placeholder{color:var(--ow-ink-3)}
/* the hue arrives HERE — on the thing you are touching */
/* the focused field deepens its OWN ground — `rgba(0,0,0,.36)` here was a dark
   grey block under dark ink in the light room (measured on the Lightbulb's
   "Say something", 2026-09-21) */
.ow-fld input:focus,.ow-fld select:focus,.ow-fld textarea:focus{
  background:color-mix(in srgb,var(--ow-field) 86%,rgb(var(--ow-h1)) 14%);border-color:rgba(var(--ow-h1),.55);
  box-shadow:0 0 0 3px rgba(var(--ow-h1),.16)}
.ow-fld input:-webkit-autofill{-webkit-text-fill-color:#fff;
  -webkit-box-shadow:0 0 0 60px rgb(19,18,28) inset;caret-color:#fff}

/* ── THE COMPOSER'S SEND IS NOT A WHITE SLAB ──────────────────────────────
   `.ow-btn` is a full-width pill with a white gradient and `color:#050505`.
   Under the comment box in dark mode that is a bright bar with black ink
   across the bottom of the thread — reported from two directions tonight, once
   as "light fill, near-black text" and once measured as transparent-with-black
   ink where something had overridden the ground. Either way it is the only
   element on the surface that does not follow the theme.

   The comment composer gets its own control: small, inline with the field, and
   wearing the PERSON'S HUE when there is something to send — the same signal
   the rest of the App uses for "this will do something now". `.ow-btn` itself
   is untouched; other surfaces still want the slab. */
.ow-cmtsend{border:0;border-radius:12px 10px 13px 11px;cursor:pointer;
  padding:9px 16px;margin:0;width:auto;flex:none;
  font-family:var(--ow-font);font-weight:650;font-size:12.5px;
  color:var(--ow-ink-3);background:rgba(var(--ow-h1),.10);
  box-shadow:inset 0 0 0 1px rgba(var(--ow-h1),.20);
  transition:color .16s,background .16s,box-shadow .16s,transform .15s var(--ow-spring)}
.ow-cmtsend:not(:disabled){color:#fff;
  background:linear-gradient(150deg,rgb(var(--ow-h1)),rgba(var(--ow-h2),.85));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.34),0 6px 16px -6px rgba(var(--ow-h1),.7)}
.ow-cmtsend:not(:disabled):hover{transform:translateY(-1px)}
.ow-cmtsend:disabled{cursor:default}

/* THE FIELD AND ITS CONTROL SIT ON ONE ROW, and the row stays with the thread
   rather than floating: a composer that jumps as the list grows is the layout
   shift the surface already refuses elsewhere. */
.ow-fld--cmt{display:flex;align-items:flex-end;gap:9px}
/* ── THE INVITATION TO ADD DEPTH, which is not a field ──────────────────────
   ★ FOUNDER/369: optional depth "while not making them feel like empty boxes
     that a user has to either live leave in or leave out."

   A row of quiet text controls under the body: "Add a headline", and later
   the rest of a thing's depth. It reads as a sentence you can press, never as
   a box — no ground, no border, the third ink — so a composer with nothing
   added looks like one field and a line of small print, which is what it is.
   Pressed, the control is gone and the field is there. */
.ow-depth{display:flex;flex-wrap:wrap;gap:4px 14px;margin:-6px 0 14px}
.ow-depth:empty{display:none}
.ow-depth__add{font:inherit;font-size:12.5px;font-weight:560;color:var(--ow-ink-3);
  background:none;border:0;padding:4px 0;cursor:pointer;
  transition:color .16s ease}
.ow-depth__add::before{content:'+';margin-right:5px;opacity:.7}
.ow-depth__add:hover,.ow-depth__add:focus-visible{color:var(--ow-ink)}
.ow-depth__add:focus-visible{outline:2px solid rgba(var(--ow-h1),.55);outline-offset:2px;
  border-radius:4px}
.ow-fld--cmt textarea{flex:1 1 auto;min-width:0}
.ow-fld--cmt .ow-cmt__av{margin-bottom:2px}

.ow-btn{width:100%;border-radius:11px;padding:13px 14px;font-size:13px;
  font-weight:650;font-family:var(--ow-font);cursor:pointer;
  background:linear-gradient(180deg,rgba(255,255,255,.96),rgba(225,230,240,.92));
  color:#050505;border:1px solid rgba(255,255,255,.28);margin-top:8px;
  box-shadow:0 8px 18px rgba(0,0,0,.26),0 1px 0 rgba(255,255,255,.7) inset;
  transition:transform .15s cubic-bezier(.3,1.4,.5,1),opacity .15s,box-shadow .15s}
.ow-btn:hover{transform:translateY(-1px)}
.ow-btn:disabled{opacity:.55;cursor:default;transform:none}
/* the room's ink and an edge in the person's light — `#fff` on a white rim
   was a dark-room assumption, and in the light room every quiet button in the
   App ("Sign out everywhere else", "Show all") was white on near-white:
   there, pressable, and unreadable (founder/513 tour) */
.ow-btn--quiet{background:none;color:var(--ow-ink);
  border:1px solid rgba(var(--ow-h1),.38);box-shadow:none}
.ow-btn--quiet:hover{border-color:rgba(var(--ow-h1),.6);background:rgba(var(--ow-h1),.08)}

/* the wordmark, worn as the page's identity rather than a caption */
.ow-hero{display:flex;flex-direction:column;align-items:center;gap:8px;
  margin-bottom:18px}
.ow-hero__logo{height:34px;width:auto;
  filter:drop-shadow(0 3px 26px rgba(var(--ow-h1),.34))}
html[data-fx="safe"] .ow-hero__logo{filter:none}
.ow-hero__k{font-family:var(--ow-font-atmos);font-weight:600;font-size:10.5px;
  letter-spacing:.01em;color:var(--ow-ink-3)}

.ow-note{margin-top:12px;padding:11px 13px;font-size:12.5px;line-height:1.45;
  border-radius:11px;display:none}
.ow-note.show{display:block}
.ow-note--bad{color:#ffc2c2;background:rgba(239,68,68,.10);
  border:1px solid rgba(239,68,68,.32)}
.ow-note--good{color:#b6f5cf;background:rgba(34,197,94,.10);
  border:1px solid rgba(34,197,94,.32)}

/* ═══ 27 · THE WORLD MUST BE VISIBLE ════════════════════════════════════════
   The sky, the network and the wash all sit at NEGATIVE z-index so they stay
   behind every surface without needing a stacking context. That only works if
   the body is transparent: a negative-z child of <body> paints BEHIND body's
   own background, so an opaque body renders the entire world invisible while
   every probe still reports it present and correct. Exactly the shape of defect
   this codebase keeps meeting — measurable, and not on screen.

   The ground therefore belongs to <html>, and any page that sets a background
   on <body> is overriding it back into blackness. */
html{background:var(--ow-ground)}
body{background:transparent}

/* ═══ 28 · THE CROSSING ═════════════════════════════════════════════════════
   The landing's step-in motion (§5), generalised so any surface can hand off to
   another without the world blinking. The values are the landing's, unchanged —
   .55s cubic-bezier(.4,0,.2,1), opacity plus a 2px blur.

   THE WORLD LAYERS ARE EXCLUDED BY NAME, exactly as they are on the landing:
   the constellation, the field and the wash are fixed and must not move, or the
   whole effect inverts and it reads as the world leaving instead of the page. */
/* the dissolve itself is applied by OW.crossing in JS — see the comment there
   for why a selector was the wrong tool. What remains here is only the stage. */
/* NO ANIMATION ON THE STAGE. A running CSS animation outranks an inline style,
   so `ow-cross-in` was beating `style="opacity:1"` and holding the globe at its
   0% keyframe whenever the frame loop was throttled — the crossing dissolved
   the page and showed nothing. The fade-in is now a transition the JS triggers,
   which loses to nothing and degrades to "already visible" rather than to
   "invisible". Visibility is never allowed to depend on motion running. */
.ow-crossing{position:fixed;inset:0;z-index:400;display:flex;
  flex-direction:column;align-items:center;justify-content:center;gap:16px;
  pointer-events:none;opacity:1;
  transition:opacity .4s cubic-bezier(.4,0,.2,1)}

/* ARRIVING — DELIBERATELY NOT A HOLD.
   I had the destination start its content at opacity:0 and release it on boot,
   so the second half of a crossing would not flash. It cost more than it bought:
   the page rendered INVISIBLE when anything about the release went wrong, and a
   blank product is a far worse failure than an un-animated one. A transition is
   a nicety; being able to see the page is not.

   The seam is closed the cheap way instead — both surfaces mount the same world
   from the same seed in the same hue, so the constellation is already identical
   across the load. That is what actually makes it read as continuous; hiding
   the content was never doing that work. */
html body > *:not(.ow-sky):not(.ow-net):not(.ow-worldwash){
  transition:opacity .45s cubic-bezier(.4,0,.2,1)}

/* an error states who broke it, then what to do — title carries, line explains */
.ow-note b{display:block;font-weight:650;font-size:13px;margin-bottom:3px}
.ow-note span{display:block;opacity:.82}


/* ═══ A POST WEARS ITS PERSON AND ITS PLACE ═════════════════════════════════
   `OW.card` sets --ch1/--ch2 on a post card to the blend of the author's hue
   and the Center's. The card already draws from those variables; this makes the
   blend actually visible on the body rather than only on the avatar — a soft
   wash and an edge in the pair, so a feed of many Centers reads as many places
   instead of one. Deliberately restrained: the words are the content, the
   colour is only the ground they sit on. */
/* A CARD THAT IS ONLY TEXT SHOULD NOT BE A BOX AROUND TEXT.
   Founder, 2026-08-15: *"we shouldn't have those blocks if they're just gonna
   be used to display text… let it be a colorful array of user hues and center
   hues once the world populates."*

   So the panel steps back and the COLOUR does the work: the face carries the
   person-and-place pair across the whole tile rather than a grey slab with a
   tinted edge. Two people posting in one Center read as neighbours; the same
   person posting in two Centers reads as two places. That is the array. */
/* ── AND THE HOME CANVAS TILE WEARS IT TOO ───────────────────────────────
   The comment on the sibling of this rule (`.po-w:not(.po--1x1) > .po-face`)
   already promises "THE SAME DECLARATION AS THE FEED CARD AND THE HOME CANVAS
   TILE" — the tile was simply never added to it.

   I first attached `.ow-cv-face` to the OLDER `.po-card__face` recipe above,
   which is why a Popit and a Post still measured differently after the
   material was supposedly shared: `115% 60% at 50% -10%` against
   `130% 80% at 18% -6%`. Two Post materials exist in this file and I joined
   the one that is not canonical — the same mistake, one layer down, as
   matching `.ow-fpos` in the first place.

   `--h`/`--h2` are the difference that remains and SHOULD remain: a Post wears
   its author-and-place blend, a Popit wears the surface's own pair. Same
   recipe, different light. */
.po-card[data-blended] .po-card__face,
.ow-cv-face,
.ow-cv-pick__row,
.ow-reqs,
.ow-msg:not([data-mine]){
  background:
    radial-gradient(130% 80% at 18% -6%, rgba(var(--h), .46), transparent 64%),
    radial-gradient(120% 90% at 92% 108%, rgba(var(--h2), .30), transparent 60%),
    linear-gradient(158deg, rgba(var(--h),.18), rgba(var(--h3,var(--ow-h3,var(--h2))),.12) 46%,
                            rgba(var(--h2),.10) 70%, var(--ow-tile-base));
  box-shadow:
    inset 0 2px 1px var(--ow-lift),
    0 0 34px -10px rgba(var(--h), .50),
    0 18px 44px -16px var(--ow-drop);
}
/* the words stay the content; the colour is only what they sit on */
.po-card[data-blended] .po-card__t{text-shadow:0 1px 18px rgba(0,0,0,.55)}

/* ═══ 25 · THE MAP ══════════════════════════════════════════════════════════
   Real Centers in the real world. The tiles are somebody else's drawing, so the
   surface keeps its own voice at the EDGES — the frame, the pins and the search
   are ours, the geography is OpenStreetMap's, and the two are not blended into
   a pretend house style. */
.ow-map{margin:0 0 18px}
.ow-map__bar{display:flex;gap:8px;align-items:center;margin:0 0 10px}
.ow-map__canvas{height:clamp(220px,42vh,380px);border-radius:18px 16px 19px 15px;
  overflow:hidden;background:rgba(var(--ow-h1),.06);
  box-shadow:inset 0 0 0 1px rgba(var(--ow-h1),.18)}
.ow-map__canvas .leaflet-container{background:transparent;font-family:var(--ow-font)}
/* THE PIN IS A POINT OF LIGHT, not a balloon — the same vocabulary as the sky,
   because a Center on a map and a Center in the constellation are one thing. */
.ow-map__pin i{display:block;width:12px;height:12px;border-radius:50%;
  background:var(--pin);box-shadow:0 0 0 3px rgba(255,255,255,.10),0 0 16px -2px var(--pin)}
.ow-map__pin i[data-kind="person"]{width:10px;height:10px;border-radius:50%;
  box-shadow:0 0 0 3px rgba(255,255,255,.22),0 0 14px -2px var(--pin)}

/* ═══ 26 · THE MAP IS THE PAGE ═════════════════════════════════════════════
   Founder, 2026-08-15: *"The map needs to feel like it is the page itself, not
   like the map is something inside of the page… users should feel like it's
   coming out of the map."*

   So it does not live in the column. `.ow-main` insets its children by 18px and
   caps them at 640 — correct for a feed, and exactly what made the map read as
   a rectangle placed on a page. This breaks out of that entirely: fixed to the
   viewport, edge to edge, UNDER the wordmark (z 60) and the dock (z 70) so both
   float on the world rather than bracketing it.

   Everything of ours then RISES OUT OF IT — the search, the hour, the sheet —
   each lifted on its own glass rather than sitting in a container. Nothing
   frames the map, because a frame is the thing that says "this is a component". */
.ow-surface--map{padding:0!important;max-width:none!important}
.ow-map{position:fixed;inset:0;z-index:1;border-radius:0;overflow:hidden;
  margin:0;height:auto}
.ow-map__canvas{position:absolute;inset:0;height:100%;border-radius:0;box-shadow:none}
.ow-map__canvas .leaflet-container{background:#0b0d18;font-family:var(--ow-font)}
.ow-map__canvas .leaflet-control-attribution{background:rgba(0,0,0,.30);
  color:rgba(255,255,255,.45);font-size:9px;border-radius:6px 0 0 0;
  padding:1px 6px}
.ow-map__canvas .leaflet-control-attribution a{color:rgba(255,255,255,.65)}
/* THE HOUR'S LIGHT, painted over the tiles — see `skyGrade`. Never a filter:
   the safe tier sets `filter:none` on everything, so a filtered grade is
   invisible on most phones. */
.ow-map__wash{position:absolute;inset:0;pointer-events:none;z-index:400;
  transition:background-image 1.4s cubic-bezier(.22,.61,.36,1)}
/* AND THE WORLD'S OWN EDGES — a deep vignette top and bottom so the wordmark
   and the dock have something to sit against without either needing a bar. */
.ow-map__wash::after{content:'';position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(0,0,0,.48),transparent 16%,transparent 66%,rgba(0,0,0,.52));
  pointer-events:none}
/* SEARCH — rising out of the map, not resting in a bar. Sits below the mark. */
.ow-map__search{position:absolute;left:16px;right:16px;top:calc(52px + env(safe-area-inset-top));
  z-index:500}
.ow-map__search input{width:100%;box-sizing:border-box;height:44px;padding:0 18px;
  border:0;border-radius:22px 20px 23px 19px;
  font-family:var(--ow-font);font-size:14px;color:var(--ow-ink);
  background:rgba(10,12,24,.52);
  backdrop-filter:blur(18px) saturate(1.4);-webkit-backdrop-filter:blur(18px) saturate(1.4);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.12),
             0 18px 40px -26px rgba(0,0,0,1),
             0 0 30px -14px rgba(var(--ow-h1),.5)}
html[data-fx="safe"] .ow-map__search input{background:rgba(10,12,24,.86)}
.ow-map__search input::placeholder{color:rgba(255,255,255,.42)}
.ow-map__search input:focus{outline:0;
  box-shadow:inset 0 0 0 1px rgba(var(--ow-h1),.6),0 18px 44px -22px rgba(var(--ow-h1),.7)}
.ow-map__search input[data-busy]{opacity:.65}
/* THE SHEET — the places, coming UP out of the world. It stops short of the
   top so the map is never fully covered, and clears the dock so the thumb
   never has to choose between them. */
/* THE SHEET IS CHROME, NOT WORLD. When a Center is projected over the map
   (founder/396: over the world, not taking it over), the world — streets,
   pins — stays behind the veil; the sheet of cards does not: at ten percent
   through the veil "PLACES YOU COULD KNOW" and two card titles still read as
   words inside the Center's own page (measured 2026-09-19, a pin opened
   from Atlantic City). It steps out while the Center is open and is back
   the moment it closes. */
html.ow-proj-locked .ow-map__sheet{visibility:hidden}
.ow-map__sheet{position:absolute;left:0;right:0;bottom:0;z-index:480;
  height:var(--sheet-h,auto);max-height:var(--sheet,30%);
  overflow:auto;-webkit-overflow-scrolling:touch;
  transition:max-height .42s cubic-bezier(.22,.61,.36,1);
  padding:14px 18px calc(78px + env(safe-area-inset-bottom));
  border-radius:28px 24px 0 0;
  background:linear-gradient(180deg,rgba(10,11,22,.42),rgba(7,8,18,.93) 40%);
  backdrop-filter:blur(26px) saturate(1.3);-webkit-backdrop-filter:blur(26px) saturate(1.3);
  box-shadow:0 -1px 0 rgba(255,255,255,.10),0 -40px 70px -50px rgba(0,0,0,1)}
html[data-fx="safe"] .ow-map__sheet{background:rgba(7,8,18,.95)}
/* A SHEET WITH NOTHING IN IT IS NOT A SHEET. It used to be `:empty`, which
   stopped being true the moment the handle became a real child of it. */
.ow-map__sheet:empty{display:none}
.ow-map__sheet:has(> :only-child){display:none}
/* THE SHEET RESTS LOW so the pin it is describing stays in the world. */
.ow-map__sheet[data-sheet="open"]{--sheet:76%}
.ow-map__sheet[data-drag]{transition:none;max-height:none}
/* THE HANDLE IS A REAL CONTROL — you can drag it, tap it, or reach it with a
   keyboard, which a ::before could never be. */
.ow-map__grab{position:sticky;top:0;z-index:2;display:block;width:100%;
  border:0;background:none;padding:6px 0 12px;cursor:grab;touch-action:none;
  -webkit-appearance:none;appearance:none}
.ow-map__grab[aria-expanded]::before{content:'';display:block;width:38px;height:4px;
  border-radius:2px;margin:0 auto;background:rgba(255,255,255,.26);
  transition:background .2s,width .3s cubic-bezier(.22,.61,.36,1)}
.ow-map__grab:hover::before,.ow-map__grab:focus-visible::before{
  background:rgba(255,255,255,.52);width:52px}
.ow-map__grab:active{cursor:grabbing}
/* THE DOCK FLOATS ON THE WORLD while the map is the page.
   This lost to `html[data-fx="safe"] .ow-nav`, which sets a solid chrome slab
   and sits later in the sheet at equal specificity — so on every phone (the
   safe tier IS the phone) the map ended at a black bar. Same intent, written
   so it actually wins, and it stays a gradient rather than going fully clear:
   the safe tier has no backdrop blur, and dock labels still have to be
   readable over a city at night. */
html body:has(.ow-surface--map) .ow-nav,
html[data-fx="safe"] body:has(.ow-surface--map) .ow-nav{
  background:linear-gradient(0deg,rgba(4,5,12,.86),rgba(4,5,12,.34) 62%,transparent)}
/* A PIN IS A POINT OF LIGHT — the same vocabulary as the sky, because a Center
   on a map and a Center in the constellation are one thing. */
.ow-map__pin i{display:block;width:13px;height:13px;border-radius:50%;
  background:var(--pin);
  box-shadow:0 0 0 3px rgba(255,255,255,.16),0 0 22px -2px var(--pin),
             0 0 44px -6px var(--pin)}
.ow-map__pin i[data-kind="person"]{width:11px;height:11px;
  box-shadow:0 0 0 3px rgba(255,255,255,.34),0 0 18px -2px var(--pin)}

/* THE ANSWERS, rising out of the search rather than dropping out of a bar. */
.ow-map__found{position:absolute;left:0;right:0;top:52px;display:none;
  flex-direction:column;gap:6px}
.ow-map__found.is-open{display:flex}
.ow-map__hit{display:flex;align-items:baseline;gap:8px;width:100%;text-align:left;
  border:0;cursor:pointer;padding:11px 16px;border-radius:16px 14px 17px 13px;
  font-family:var(--ow-font);color:var(--ow-ink);
  background:rgba(10,12,24,.72);
  backdrop-filter:blur(18px) saturate(1.35);-webkit-backdrop-filter:blur(18px) saturate(1.35);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.10),0 14px 34px -24px rgba(0,0,0,1)}
html[data-fx="safe"] .ow-map__hit{background:rgba(10,12,24,.94)}
.ow-map__hit:hover{box-shadow:inset 0 0 0 1px rgba(var(--ow-h1),.45)}
/* ── A SEARCH RESULT DOES NOT CUT ITS OWN ANSWER ──────────────────────────
   ★ FOUNDER, 2026-08-28: *"i dont want to see any cut out or ... text
     anywhere."*

   The row was one line — name, region, kind — with the region on `flex:1` and
   `text-overflow:ellipsis`. The name has no basis, so it takes what it needs
   and the region gets the remainder. Measured 2026-09-02 on a live search:
   "Clark County, Ne…", "San Miguel Count…", "Santa Bárbara, H…", and on the
   longest name the region was **0px wide** — the county gone entirely from a
   result whose whole job is telling you WHICH Las Vegas this is. Four of them
   are returned; the region is the only thing that separates them.

   So the region takes its own line rather than the leftovers. Name and kind
   keep the first line, nothing is nowrap, and a long name wraps in whole words
   instead of being shortened. */
.ow-map__hit{flex-wrap:wrap}
.ow-map__hit b{font-size:14px;font-weight:500;flex:1 1 auto;min-width:0;
  white-space:normal;overflow-wrap:normal;word-break:normal}
.ow-map__hit span{font-size:12px;opacity:.6;order:3;flex:1 1 100%;
  white-space:normal;overflow:visible;text-overflow:clip;line-height:1.35}
.ow-map__hit i{font-style:normal;font-size:12px;letter-spacing:.01em;
  opacity:.55;flex:none;order:2;align-self:baseline}
.ow-map__hit i[data-kind="center"]{color:rgb(var(--ow-h1));opacity:.9}
.ow-map__hit i[data-kind="event"]{color:rgb(var(--ow-h2));opacity:.9}

/* ═══ THE EARTH ═════════════════════════════════════════════════════════════
   Offered only at tier 2 — the platform's own capability read — and loaded only
   when asked. The flat map stays mounted underneath: leaving the globe is
   instant because it was never torn down, and if the globe fails to arrive the
   product is exactly what it was. */
.ow-map__earthcanvas{position:absolute;inset:0;z-index:2;opacity:0;pointer-events:none;
  transition:opacity .9s cubic-bezier(.22,.61,.36,1)}
.ow-map[data-earth="on"] .ow-map__earthcanvas{opacity:1;pointer-events:auto}
.ow-map[data-earth="on"] .ow-map__canvas{opacity:0;transition:opacity .7s ease}
/* ON THE GLOBE THE WASH IS ALTITUDE-AWARE — full on the ground where Cesium
   fades its own night out, out of the way from orbit where its terminator is
   the real thing. Set by the camera, never guessed. */
.ow-map[data-earth="on"] .ow-map__wash{opacity:var(--earth-wash,.9);
  transition:opacity .6s ease}
.ow-map__earthcanvas .cesium-viewer-bottom,
.ow-map__earthcanvas .cesium-widget-credits{display:none!important}
/* the control — quiet, and it names the state it will move you TO */
/* ── A FLOATING CONTROL CANNOT GUESS WHERE THE SHEET'S TOP IS ────────────
   ★ MEASURED 2026-09-12 on Discovery at 285x490: the Earth chip sat at
     x195-269, y362-394, and the sheet's top edge was at y343. The chip was
     drawn ON the sheet's copy — "places and people you already know. It grows
     as your wor|" with the last word behind the button — and at
     `[data-sheet="open"]` it is buried entirely.

   `bottom:96px` IS A GUESS AT A MOVING EDGE. The sheet is `bottom:0` with
   `max-height:var(--sheet)`, so its top is 30% of the map up from the bottom
   — 147px on this viewport, and 96 is not 147. The number was calibrated
   against some other screen and has been wrong on phones ever since.

   So the chip is anchored to the SAME value the sheet's height comes from.
   `--sheet` moves up to `.ow-map` where both can read it; the sheet keeps its
   own override for the open state, which the chip deliberately does NOT
   follow — see below. */
.ow-map{--sheet:30%}
/* AND IT GOES WHEN THE MAP DOES. Mirroring the open state onto the parent was
   my first answer and the screenshot refused it: at 76% the chip climbed to
   the top of the map and landed on the SEARCH FIELD — one overlap traded for
   another, because at that height there is no map left to put a map control
   on. A control for a surface nobody can see is not a control. It leaves, and
   comes back with the map. Same for a sheet mid-drag: better absent than
   sliding across moving text. */
.ow-map:has(> .ow-map__sheet[data-sheet="open"]) .ow-map__earth,
.ow-map:has(> .ow-map__sheet[data-drag]) .ow-map__earth{
  opacity:0;pointer-events:none}
.ow-map__earth{position:absolute;right:16px;bottom:calc(var(--sheet,30%) + 14px);
  transition:opacity .2s var(--ow-ease,ease);z-index:500;
  border:0;cursor:pointer;padding:9px 15px;border-radius:16px 14px 17px 13px;
  font-family:var(--ow-font);font-size:12px;letter-spacing:.01em;
  color:var(--ow-ink);background:rgba(10,12,24,.62);
  backdrop-filter:blur(16px) saturate(1.3);-webkit-backdrop-filter:blur(16px) saturate(1.3);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.12),0 14px 34px -24px rgba(0,0,0,1)}
html[data-fx="safe"] .ow-map__earth{background:rgba(10,12,24,.9)}
/* NEAR ME — the Earth's twin on the left, on every device (founder/435: ultra
   usable). Same plate, same hour rules; it says what it is doing while it
   asks the browser, and what happened if the answer was no. */
.ow-map__near{position:absolute;left:16px;bottom:calc(var(--sheet,30%) + 14px);
  transition:opacity .2s var(--ow-ease,ease);z-index:500;
  border:0;cursor:pointer;padding:9px 15px;border-radius:14px 17px 13px 16px;
  font-family:var(--ow-font);font-size:12px;letter-spacing:.01em;
  color:var(--ow-ink);background:rgba(10,12,24,.62);
  backdrop-filter:blur(16px) saturate(1.3);-webkit-backdrop-filter:blur(16px) saturate(1.3);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.12),0 14px 34px -24px rgba(0,0,0,1)}
.ow-map__near[disabled]{opacity:.7;cursor:default}
html[data-fx="safe"] .ow-map__near{background:rgba(10,12,24,.9)}
.ow-map[data-hour="day"] .ow-map__near,.ow-map[data-hour="day"] .ow-map__earth{
  background:rgba(255,255,255,.78);color:#141828;
  box-shadow:inset 0 0 0 1px rgba(20,24,40,.12),0 14px 34px -24px rgba(0,0,0,.6)}
.ow-map__earth[aria-pressed="true"]{box-shadow:inset 0 0 0 1px rgba(var(--ow-h1),.55),
  0 0 26px -12px rgba(var(--ow-h1),.8)}
/* THEY REST ABOVE THE DOCK (founder/570). Both were anchored to `--sheet`, the
   height of the bottom sheet founder/438 retired, so they floated a third of
   the way up the map with nothing under them. On a phone they sit just clear
   of the dock; where the dock is a rail they sit low and clear of the rail. */
.ow-map__near,.ow-map__earth{bottom:calc(86px + env(safe-area-inset-bottom))}
.ow-map__near{left:calc(var(--ow-rail,0px) + 16px)}
@media (min-width:900px){.ow-map__near,.ow-map__earth{bottom:28px}}

/* ═══ A PLACE WITH PEOPLE IN IT ═════════════════════════════════════════════
   A pin was a dot; a Centre is somewhere inhabited. The disc breathes, its
   people orbit it in their own hues, and nothing is synchronised — a map where
   everything pulses in time is a machine, and one where things drift is alive.
   Motion is slow enough to be felt rather than watched, and it stops entirely
   for anyone who asked for less. */
.ow-map__pin{overflow:visible!important}
.ow-map__pin i{display:block;width:14px;height:14px;border-radius:50%;
  background:var(--pin);
  box-shadow:0 0 0 3px rgba(255,255,255,.16),0 0 24px -2px var(--pin),
             0 0 52px -8px var(--pin);
  animation:ow-pinbreathe var(--d,7s) ease-in-out infinite}
@keyframes ow-pinbreathe{
  0%,100%{transform:scale(1);box-shadow:0 0 0 3px rgba(255,255,255,.16),0 0 24px -2px var(--pin),0 0 52px -8px var(--pin)}
  50%    {transform:scale(1.14);box-shadow:0 0 0 4px rgba(255,255,255,.22),0 0 34px -2px var(--pin),0 0 70px -6px var(--pin)}}
.ow-map__pin i[data-kind="person"]{width:11px;height:11px;
  box-shadow:0 0 0 3px rgba(255,255,255,.34),0 0 18px -2px var(--pin)}
/* THE PEOPLE, around the place they belong to */
.ow-map__pin u{position:absolute;left:50%;top:50%;width:16px;height:16px;margin:-8px;
  border-radius:50%;text-decoration:none;
  display:grid;place-items:center;
  font-family:var(--ow-font);font-size:8px;font-weight:600;color:#fff;
  background:var(--pc);box-shadow:0 0 0 2px rgba(8,9,20,.65),0 0 14px -3px var(--pc);
  transform:translate(var(--x),var(--y));
  animation:ow-orbit var(--d,6s) ease-in-out infinite}
@keyframes ow-orbit{
  0%,100%{transform:translate(var(--x),var(--y)) scale(1)}
  50%    {transform:translate(calc(var(--x) * 1.16),calc(var(--y) * 1.16)) scale(.94)}}
/* the Centre says its own name, quietly, without a click */
.ow-map__pin b{position:absolute;left:50%;top:22px;transform:translateX(-50%);
  white-space:nowrap;font-family:var(--ow-font);font-size:10px;font-weight:500;
  letter-spacing:.02em;color:#fff;padding:3px 9px;border-radius:9px 7px 10px 8px;
  background:rgba(8,9,20,.62);box-shadow:inset 0 0 0 1px rgba(255,255,255,.10),
             0 0 18px -6px var(--pin);
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}
@media (prefers-reduced-motion: reduce){
  .ow-map__pin i,.ow-map__pin u{animation:none}}
/* no pin binds a popup any more (founder/405 · 406); Leaflet's own popup
   dressing is kept for any future non-pin use and costs nothing unused */
.ow-map__canvas .leaflet-popup-content-wrap{background:rgba(10,12,24,.9);color:#fff;
  border-radius:16px 14px 17px 13px;box-shadow:inset 0 0 0 1px rgba(255,255,255,.12)}
.ow-map__canvas .leaflet-popup-content span{opacity:.65;font-size:11px}
.ow-map__canvas .leaflet-popup-tip{background:rgba(10,12,24,.9)}

/* ARRIVAL — the flat map cannot lean, so it settles: a breath of scale that
   resolves as the camera stops. Same intent as the globe's tilt, expressed in
   what this engine can actually do. */
.ow-map__canvas[data-arriving]{transform:scale(1.014);transition:transform 1.5s cubic-bezier(.22,.61,.36,1)}
.ow-map__canvas{transition:transform 1.1s cubic-bezier(.22,.61,.36,1)}
/* AND THE WORLD TAKES THE COLOUR OF WHERE YOU LANDED — briefly, then it lets
   go, so the map never permanently belongs to the last thing you looked at. */
.ow-map[data-arrived] .ow-map__wash::before{content:'';position:absolute;inset:0;
  background:radial-gradient(120% 80% at 50% 55%,rgba(var(--arrive),.22),transparent 70%);
  animation:ow-arrive 9s ease-out forwards;pointer-events:none}
@keyframes ow-arrive{0%{opacity:0}12%{opacity:1}70%{opacity:.7}100%{opacity:0}}
@media (prefers-reduced-motion: reduce){
  .ow-map__canvas[data-arriving]{transform:none}
  .ow-map[data-arrived] .ow-map__wash::before{animation:none;opacity:.5}}

/* ═══ THE COLOURED ARRAY — places as their own colour, not blocks of text ════
   Founder, 2026-08-15: *"We shouldn't have those blocks if they're just gonna
   be used to display text… Let it be a colorful array of user hues and center
   hues once the world populates."* A tile IS its Center's hue; its size is how
   much life the Center actually has; the array is deliberately uneven, because
   a grid of equal boxes is the generic thing this replaces. */
.ow-map__array{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 18px}
/* ══ A SMALL POPIT IS THE SAME CLOTH AS A POST ON HOME ═══════════════════════
   ★ FOUNDER/437, 2026-09-19: *"make the popits seen small and look like the
     same material as posts on home."*

   This tile was the "coloured array" of 2026-08-15 — a filled hue gradient
   with an ink chosen per tile by luminance. Beside a Post on Home it read as
   a different object: a solid block where a Post is light on dark cloth.
   Now it is the Post's own recipe at tile scale — the two hue washes over
   the dark ground, the top lift, the hue's glow outside, no rim (437's
   second ruling) — so a Center in the sheet, a Post in the feed and a Popit
   on the canvas are one material at three sizes. The ink is always white,
   because the ground is always dark; the per-tile `--ink` the array computed
   for the bright gradient is no longer needed and is ignored. */
.ow-map__tile{flex:1 1 128px;min-height:66px;border:0;cursor:pointer;
  display:flex;flex-direction:column;justify-content:flex-end;gap:3px;
  padding:12px 13px;text-align:left;color:#fff;
  font-family:var(--ow-font);
  border-radius:20px 15px 21px 14px;
  background:
    radial-gradient(130% 80% at 18% -6%,  rgba(var(--h),.46),  transparent 64%),
    radial-gradient(120% 90% at 92% 108%, rgba(var(--h2),.30), transparent 60%),
    linear-gradient(158deg, rgba(var(--h),.16), rgba(var(--h3,var(--ow-h3,var(--h2))),.11) 46%,
                            rgba(var(--h2),.10) 70%, var(--ow-tile-mid)),
    linear-gradient(0deg, var(--ow-plate), var(--ow-plate));
  box-shadow:
    inset 0 2px 1px var(--ow-lift, rgba(255,255,255,.28)),
    0 0 34px -10px rgba(var(--h),.50),
    0 10px 24px -14px var(--ow-drop, rgba(0,0,0,.6));
  transition:transform .5s cubic-bezier(.22,.61,.36,1),box-shadow .4s}
.ow-map__tile[data-size="big"]{flex:1 1 100%;min-height:104px;
  border-radius:26px 20px 27px 19px}
.ow-map__tile[data-size="mid"]{flex:1 1 46%}
/* A QUIET CENTER IS SMALLER, NEVER DULLER.
   Life was carried by size AND saturation, so a world where nothing has
   happened yet drained to grey — which is the "too professional" the colour
   was added to fix. SIZE is the honest signal, because it is relative and
   still readable when every value is zero; colour is identity, and identity
   does not fade because a place is new. */
.ow-map__tile[data-size="small"]{flex:0 1 auto;min-height:0;padding:10px 15px;
  border-radius:17px 13px 18px 12px}
/* a tile wearing the place's own face (founder/409) — the picture sits above
   the name, a tile unless the Center chose round; the tile grows to hold it */
.ow-map__tile.has-face{min-height:96px;justify-content:flex-end}
.ow-map__tile-face{width:36px;height:36px;object-fit:cover;display:block;margin-bottom:auto;
  border-radius:11px 9px 12px 10px;box-shadow:0 6px 14px -6px rgba(0,0,0,.6)}
.ow-map__tile-face[data-shape="round"]{border-radius:50%}
.ow-map__tile-face[data-shape="square"]{border-radius:5px}
.ow-map__tile b{font-weight:500;font-size:14px;line-height:1.2;letter-spacing:-.01em}
.ow-map__tile[data-size="big"] b{font-size:19px}
.ow-map__tile span{font-size:11px;opacity:.82;letter-spacing:.01em}
.ow-map__tile:hover,.ow-map__tile:focus-visible{transform:translateY(-2px);
  box-shadow:inset 0 2px 1px rgba(255,255,255,.4),
    0 0 44px -8px rgba(var(--h),.7), 0 16px 30px -14px rgba(0,0,0,.65)}
.ow-map__tile:active{transform:translateY(0) scale(.985)}
/* PEOPLE ARE THE SAME VOCABULARY AS THE ORBIT AROUND A PIN — a point of
   light with an initial in it, so a person on the map and a person in the
   sheet are recognisably one thing. */
.ow-map__array[data-kind="people"]{gap:7px}
.ow-map__face{display:flex;align-items:center;gap:8px;border:0;cursor:pointer;
  padding:5px 13px 5px 5px;border-radius:999px;color:var(--ow-ink);
  font-family:var(--ow-font);font-size:13px;
  background:rgba(255,255,255,.07);box-shadow:inset 0 0 0 1px rgba(255,255,255,.10);
  transition:background .25s,transform .4s cubic-bezier(.22,.61,.36,1)}
.ow-map__face i{display:grid;place-items:center;width:26px;height:26px;flex:none;overflow:hidden;
  border-radius:50%;font-style:normal;font-size:11px;font-weight:600;color:#fff;
  background:linear-gradient(146deg,rgb(var(--h)),rgb(var(--h2)));
  box-shadow:0 0 16px -4px rgb(var(--h))}
.ow-map__face i[data-shape="rounded"]{border-radius:9px 7px 8px 7px}
.ow-map__face i[data-shape="square"]{border-radius:5px}
.ow-map__face i img{width:100%;height:100%;object-fit:cover;display:block}
/* the name, and under it the reason this person is being shown at all */
.ow-map__facen{display:flex;flex-direction:column;align-items:flex-start;gap:1px;min-width:0;text-align:left}
.ow-map__facen em{font-style:normal;font-size:10.5px;line-height:1.25;color:var(--ow-ink-3,#6f6b85);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:26ch}
.ow-map__face:hover,.ow-map__face:focus-visible{background:rgba(255,255,255,.13);
  transform:translateY(-1px)}
@media (prefers-reduced-motion: reduce){
  .ow-map__tile,.ow-map__face{transition:none}
  .ow-map__tile:hover,.ow-map__face:hover{transform:none}}

/* ═══ THE CHROME DRESSES FOR THE HOUR ═══════════════════════════════════════
   Every one of these was tuned against a dark basemap, because every test until
   2026-08-16 happened to run at night. In daylight the same near-black
   gradients read as grime over a pale city: the map goes bright and the
   furniture stays in the dark with it. The hour is measured, not guessed — it
   is the same `skyGrade` that chooses the cartography. */
/* A LIGHT VEIL, NOT A BLEACH. At .34 the top sixth of the city went to milk —
   the vignette exists to give the wordmark something to sit against, not to
   hide the map under it. */
.ow-map[data-hour="day"] .ow-map__wash::after{
  background:linear-gradient(180deg,rgba(255,255,255,.20),transparent 11%,
             transparent 74%,rgba(20,24,40,.22))}
.ow-map[data-hour="day"] .ow-map__search input{
  background:rgba(255,255,255,.80);color:#141828;
  box-shadow:inset 0 0 0 1px rgba(20,24,40,.12),0 10px 30px -18px rgba(20,24,40,.5)}
.ow-map[data-hour="day"] .ow-map__search input::placeholder{color:rgba(20,24,40,.5)}
/* THE DOCK SITS AGAINST THE GROUND, so it follows it too. The ground follows
   the person's theme since founder/570, so the wordmark needs no rule here:
   the theme already chose its file (applyMode). The invert this replaced
   turned the light theme's black mark white on a light map wherever filters
   survive the tier. */
html body:has(.ow-map[data-hour="day"]) .ow-nav,
html[data-fx="safe"] body:has(.ow-map[data-hour="day"]) .ow-nav{
  background:linear-gradient(0deg,rgba(246,247,253,.94),rgba(246,247,253,.44) 62%,transparent)}
html body:has(.ow-map[data-hour="day"]) .ow-nav__b{color:rgba(20,24,40,.62)}
html body:has(.ow-map[data-hour="day"]) .ow-nav__b[aria-current="page"]{color:#141828}
/* THE SEARCH PLANE BY DAY (438). The veil over the world was near-black at
   every hour, so a paper city went to slate the moment a word was typed and
   the rows on it stayed night-dressed. By day the veil is paper and the rows,
   chips and headings are ink on it — the same dressing the field above them
   already wears. */
.ow-map[data-hour="day"][data-find="on"] .ow-map__wash{
  background-image:linear-gradient(180deg,rgba(246,247,253,.82),rgba(246,247,253,.90))!important}
.ow-map[data-hour="day"] .ow-find__chip,.ow-map[data-hour="day"] .ow-find__back{
  color:rgba(20,24,40,.72);background:rgba(20,24,40,.07)}
.ow-map[data-hour="day"] .ow-find__chip[aria-pressed="true"]{color:#fff}
.ow-map[data-hour="day"] .ow-find__back{color:#141828;background:rgba(20,24,40,.10)}
.ow-map[data-hour="day"] .ow-find__h{color:rgba(20,24,40,.5)}
.ow-map[data-hour="day"] .ow-find__none,.ow-map[data-hour="day"] .ow-here__none{color:rgba(20,24,40,.6)}
.ow-map[data-hour="day"] .ow-find__row{color:#141828;
  background:
    radial-gradient(130% 80% at 18% -6%, rgba(var(--h,var(--ow-h1)),.16), transparent 64%),
    linear-gradient(0deg, rgba(255,255,255,.74), rgba(255,255,255,.74));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.9),0 0 0 1px rgba(20,24,40,.06)}
.ow-map[data-hour="day"] .ow-find__t span{color:rgba(20,24,40,.6)}
.ow-map[data-hour="day"] .ow-find__k{color:rgba(20,24,40,.38)}
.ow-map[data-hour="day"] .ow-find__pic[data-glyph="pin"]{background:rgba(20,24,40,.07)}
.ow-map[data-hour="day"] .ow-find__pic[data-glyph="pin"]::after{background:rgba(20,24,40,.7);box-shadow:0 0 0 3px rgba(20,24,40,.10)}

/* ═══ ON A WIDE SCREEN THE SHEET STANDS BESIDE THE WORLD ════════════════════
   Founder: *"The map needs to feel like it is the page itself, not like the map
   is something inside of the page."* A bottom sheet is exactly right on a phone
   — it rises out of the world under your thumb. On a desktop the same sheet at
   76% is a wall: 800x612 measured, and the map was a strip above it.

   So the geometry changes and nothing else does. Same element, same content,
   same peek-and-open behaviour — it just rises from the LEFT instead of the
   bottom, because that is the edge a wide frame has to spare. The map keeps the
   page at both shapes, which is the whole requirement. */
@media (min-width: 760px){
  .ow-map__sheet{top:0;left:var(--ow-rail,0px);right:auto;bottom:0;
    width:var(--sheet,88px);max-height:none;
    height:auto;padding:calc(104px + env(safe-area-inset-top)) 20px 28px;
    border-radius:0 30px 26px 0;
    background:linear-gradient(90deg,rgba(7,8,18,.94),rgba(10,11,22,.62));
    box-shadow:1px 0 0 rgba(255,255,255,.09),40px 0 70px -50px rgba(0,0,0,1);
    /* NO WIDTH TRANSITION, AND THAT IS A FIX RATHER THAN A COMPROMISE.
       Measured: `getAnimations()` reported a `width` transition stuck at
       currentTime 0 in state "running", pinning the panel at its 88px peek
       even against an inline `width:390px`. Finishing the animation by hand
       jumped it straight to 390. The cascade was never wrong — an animation
       was holding the used value.

       Animating `width` also forces layout on every frame, which is the one
       thing motion guidance is unanimous about. The panel now resizes at once
       and its CONTENTS arrive, which composites, cannot stall the box, and
       reads better: the column is there instantly and the places settle into
       it. */
    transition:none}
  .ow-map__sheet > *{transition:opacity .34s ease .06s,transform .42s cubic-bezier(.22,.61,.36,1) .06s}
  .ow-map__sheet[data-sheet="open"] > *{opacity:1;transform:none}
  .ow-map__sheet[data-sheet="open"]{--sheet:390px}
  .ow-map__sheet[data-sheet="peek"] > :not(.ow-map__grab){opacity:.9;transform:translateX(-4px)}
  /* the handle turns with the sheet — it is the same control, rotated */
  .ow-map__grab{position:absolute;top:0;right:6px;width:26px;height:100%;padding:0;
    cursor:ew-resize}
  .ow-map__grab[aria-expanded]::before{width:4px;height:38px;margin:0}
  .ow-map__grab:hover::before,.ow-map__grab:focus-visible::before{width:4px;height:52px}
  /* PEEKED, IT IS A COLUMN OF COLOUR — the places are still there as light,
     which is a truer preview than a truncated word. */
  .ow-map__sheet[data-sheet="peek"] .ow-group,
  .ow-map__sheet[data-sheet="peek"] .ow-map__tile span,
  .ow-map__sheet[data-sheet="peek"] .ow-map__face b,
  .ow-map__sheet[data-sheet="peek"] .ow-caught{display:none}
  .ow-map__sheet[data-sheet="peek"] .ow-map__tile{flex:0 0 auto;width:44px;height:44px;
    min-height:0;padding:0;overflow:hidden;border-radius:15px 11px 16px 10px}
  .ow-map__sheet[data-sheet="peek"] .ow-map__tile b{display:none}
  .ow-map__sheet[data-sheet="peek"] .ow-map__array{gap:7px}
  .ow-map__sheet[data-sheet="peek"] .ow-map__face{padding:0}
  /* THE SEARCH IS CENTRED ON THE MAP, AND IT STAYS A FIELD (founder/573).
     It cleared the rail AND the side column founder/438 retired, so on a wide
     screen it ran from x=507 to 1263 of 1280: hard against the right edge,
     nowhere near the wordmark it sits under. Centred in the map's own width
     beside the rail, and capped where a field stops reading as a field; the
     results plane follows it so the rows start under the words typed. */
  .ow-map__search{left:calc(var(--ow-rail,0px) + 24px);right:24px;
    max-width:640px;margin:0 auto}
  .ow-find{left:var(--ow-rail,0px)}
}
/* THE GROUP HEADING HAS TO SURVIVE THE SHEET'S OWN GRADIENT, which fades from
   near-transparent at the top — a heading tuned for the solid part vanished in
   the first 40 pixels. */
.ow-map__sheet .ow-group{color:rgba(255,255,255,.78);text-shadow:0 1px 10px rgba(0,0,0,.8)}
/* AND THE SECTION HEAD ON THE SHEET TAKES THE SAME LIGHT — the sheet sits over
   the map and is dark in BOTH themes, so `--ow-ink` (which is near-black on the
   light theme) rendered "Places anyone can walk into" dark-on-dark: the first
   screen a signed-out stranger sees, in the App's own colours, barely legible.

   I INTRODUCED THIS IN THE SAME CHANGE THAT FIXED THE MISSING MATERIAL, and the
   computed values did not show it — 21px/800/-.42px letter-spacing all read as
   correct. It was visible only by looking at the screen. Third instance today of
   a rule written for one ground and applied to both; the other two were a lens
   plate and a quotation mark. **A surface with its own ground is not themed by
   the world's tokens**, and this sheet is the one place in the App that is. */
.ow-map__sheet .ow-sec__t{color:rgba(255,255,255,.94);
  text-shadow:0 1px 12px rgba(0,0,0,.85)}
.ow-map__sheet .ow-sec__s{color:rgba(255,255,255,.62);
  text-shadow:0 1px 10px rgba(0,0,0,.8)}

/* ═══ GETTING THERE ═════════════════════════════════════════════════════════
   The way there is drawn in the colour of the place you are going, and what it
   costs you in distance and time is said in one line rather than a panel. */
.ow-map__route{position:absolute;left:50%;transform:translateX(-50%);
  bottom:calc(96px + env(safe-area-inset-bottom));z-index:520;
  display:flex;align-items:center;gap:12px;padding:9px 9px 9px 16px;
  border-radius:22px 18px 23px 17px;font-family:var(--ow-font);font-size:13px;
  color:var(--ow-ink);background:rgba(7,8,18,.86);
  backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.12),0 18px 40px -24px rgba(0,0,0,1);
  max-width:min(92vw,460px)}
/* SAFE MODE MUST TURN OFF BOTH SPELLINGS, OR IT IS NOT SAFE IN SAFARI.
   The rule above sets `-webkit-backdrop-filter:blur(20px)` alongside the
   unprefixed one. Clearing only the unprefixed property leaves the WebKit one
   still painting, so the person who asked for the cheap rendering path kept
   paying for a 20px backdrop blur — on the browser where safe mode matters
   most, because it is the one whose users asked for it. */
html[data-fx="safe"] .ow-map__route{background:rgba(7,8,18,.95);
  -webkit-backdrop-filter:none;backdrop-filter:none}
.ow-map[data-hour="day"] .ow-map__route{background:rgba(252,252,255,.90);color:#141828;
  box-shadow:inset 0 0 0 1px rgba(20,24,40,.12),0 18px 40px -26px rgba(20,24,40,.7)}
.ow-map__routex{border:0;cursor:pointer;padding:6px 13px;border-radius:999px;
  font-family:var(--ow-font);font-size:12px;color:var(--ow-ink);
  background:rgba(255,255,255,.10);box-shadow:inset 0 0 0 1px rgba(255,255,255,.14)}
.ow-map[data-hour="day"] .ow-map__routex{color:#141828;background:rgba(20,24,40,.07);
  box-shadow:inset 0 0 0 1px rgba(20,24,40,.14)}
.ow-map__routex:hover{background:rgba(var(--ow-h1),.30)}
/* The map's own Directions button is retired with the pin popup it lived in
   (founder/405 · 406: a pin opens the Center, not a card). Directions are on
   the Center — `.ow-where__go`, "Take me there". */
@media (min-width: 760px){
  .ow-map__route{left:calc(var(--ow-rail,0px) + var(--sheet,88px) + 26px);transform:none;
    bottom:26px}
}

/* THE MODES SIT WITH THE ANSWER — "4 min" means nothing until you know whether
   it is a drive or a walk, so the choice and its consequence share one line. */
.ow-map__modes{display:flex;gap:3px;padding:3px;border-radius:999px;
  background:rgba(255,255,255,.07);box-shadow:inset 0 0 0 1px rgba(255,255,255,.10)}
.ow-map[data-hour="day"] .ow-map__modes{background:rgba(20,24,40,.06);
  box-shadow:inset 0 0 0 1px rgba(20,24,40,.10)}
.ow-map__mode{border:0;cursor:pointer;padding:5px 12px;border-radius:999px;
  font-family:var(--ow-font);font-size:12px;color:inherit;background:none;opacity:.62;
  transition:opacity .2s,background .2s}
.ow-map__mode:hover{opacity:.9}
.ow-map__mode[aria-pressed="true"]{opacity:1;
  background:linear-gradient(140deg,rgb(var(--ow-h1)),rgb(var(--ow-h2)));color:#fff}
/* A MODE THIS ROUTER CANNOT ACTUALLY ANSWER IS MARKED, NOT HIDDEN. Hiding it
   would say the mode does not exist; this says the router cannot serve it. */
.ow-map__mode[data-blind]{opacity:.34}
.ow-map__mode[data-blind]::after{content:'·';margin-left:5px;opacity:.8}

/* ═══ FACES ON THE MAP ══════════════════════════════════════════════════════
   Founder, 2026-08-16: *"show round locations of the profile pictures for ones
   that have created centers."* ROUND, because that is what a profile picture is
   everywhere else a person has seen one, and the map should not invent a new
   shape for a thing they already recognise. The lit disc stays underneath as
   the ring, so a place with a face and a place without still read as the same
   kind of object. */
.ow-map__pin i{overflow:hidden}
.ow-map__pin i .ow-map__face-img{width:100%;height:100%;object-fit:cover;
  border-radius:50%;display:block}
/* A place with a face is bigger, because a face is worth looking at and a dot
   is not — but only slightly, so the constellation still reads as one field. */
/* ★ FOUNDER/439: the face is sized by following and posts — `--face` from
   the pin, 28px at nothing, 56px at most — and centred on its point. */
.ow-map__pin i:has(.ow-map__face-img){width:var(--face,30px);height:var(--face,30px);
  margin:calc((18px - var(--face,30px)) / 2) 0 0 calc((18px - var(--face,30px)) / 2);
  /* ★ FOUNDER/471 §5: a face on the map keeps its cut edge and its lift off
     the ground — those are what make it a pin — and loses the hue halo. It
     does not breathe either: a photograph pulsing is a glow by other means. */
  box-shadow:0 0 0 2px rgba(255,255,255,.9),0 6px 18px -6px rgba(0,0,0,.7);
  animation:none}
/* A FACE INSIDE AN ORBITING PERSON.
   `place-items:center` shrink-wraps its child, so `width:100%` resolved to 0 on
   an image that had not loaded yet — and an image with no size never triggers
   `loading="lazy"`, so it could never load. Circular, and it read as "the
   pictures do not work". Absolute-fill breaks the circle: the box exists before
   the bytes arrive. */
.ow-map__pin u{overflow:hidden;position:absolute}
.ow-map__pin u img{position:absolute;inset:0;width:16px;height:16px;
  object-fit:cover;border-radius:50%;display:block}
.ow-map__pin i{position:relative}
.ow-map__pin i .ow-map__face-img{position:absolute;inset:0;width:100%;height:100%}

/* NAMES WHEN THERE IS ROOM FOR THEM. Nine Centers in one neighbourhood stacked
   seven labels on top of each other. Pulled back, the faces and their colours
   carry the map and a tap gives the name; close in, the names are written. The
   label stays in the DOM at every zoom — hidden, never withheld — so a screen
   reader and a text search still find it. */
.ow-map[data-names="off"] .ow-map__pin b{opacity:0;pointer-events:none;
  transform:translate(-50%,-2px) scale(.94)}
.ow-map__pin b{transition:opacity .3s ease,transform .3s cubic-bezier(.22,.61,.36,1)}
/* the one you are pointing at always says who it is */
.ow-map__pin:hover b,.ow-map__pin:focus-within b{opacity:1 !important;
  transform:translate(-50%,0) scale(1) !important;z-index:9}

/* A SEARCH RESULT MUST NOT INHERIT AN INK IT CANNOT USE.
   `.ow-map__hit` paints a dark slab and took its colour from `--ow-ink`, which
   resolves DARK for a signed-out visitor — dark text on a dark row, measured at
   rgb(20,19,31) on rgba(10,12,24,.94), i.e. invisible. The row owns a fixed
   ground, so it must own its ink too. Same defect as the coloured tiles: a
   colour that assumes a context it does not control. */
.ow-map__hit{color:#f4f5fb}
.ow-map__hit b{color:#ffffff}
.ow-map__hit span{color:rgba(244,245,251,.66)}
.ow-map__hit i{color:rgba(244,245,251,.5)}
/* In daylight the slab lightens with the map, and the ink turns with it. */
.ow-map[data-hour="day"] .ow-map__hit{background:rgba(252,252,255,.94);color:#141828}
.ow-map[data-hour="day"] .ow-map__hit b{color:#0d1020}
.ow-map[data-hour="day"] .ow-map__hit span{color:rgba(20,24,40,.62)}
.ow-map[data-hour="day"] .ow-map__hit i{color:rgba(20,24,40,.45)}

/* ═══ 20 · LIGHT MODE IS THE SAME MATERIAL, NOT A PALER ONE ════════════════
   Measured on Home, 2026-08-17, in the light ground a first-time visitor
   actually lands in:

       --ow-panel-1   rgba(255,255,255,.9)     near-white
       --ow-panel-2   rgba(243,241,249,.94)    near-white
       seam           rgba(20,19,31,.2) 1.5px  effectively invisible
       card spill     rgba(95,255,92,.42)      a saturated halo on white

   So a feed card in light mode is a near-white rectangle wearing faint tints
   and a coloured glow — which reads as PAPER with a smudge, not as the lit
   pane the same component is in the dark. Everything tuned today was tuned
   dark; this is that work reaching the ground most people see first.

   THE IDENTITY DOES NOT FLIP WITH THE LIGHTS. §9.10 settled what this material
   IS — "a widget is a window a visitor looks THROUGH … hue-lit glass, something
   you look INTO." A window in a lit room is still a window: it is a touch
   DEEPER than the wall around it, its edge is legible, and the light in it is
   its own. It does not become white card stock because the room brightened.

   EVOLVE, NEVER REDESIGN (§16.7): the composition, type, spacing and spans are
   untouched. Four things change, and only under a light ground —
     · the body settles just below the page so the pane sits ON the world
       rather than dissolving into it
     · the object's own hue actually reads, instead of tinting white by 4%
     · the stitched seam becomes visible, because it is the family signature
       and an invisible signature is not one
     · the spill drops hard — a saturated halo around a bright card is not
       light, it is a rendering artifact, and it was the single loudest part
       of the wash. */
/* THE TILE PAIRS WITH THE CARD IN EVERY THEME. It first joined the WIDGET's
   light material here, which is a near-miss rather than a match: the widget
   recipe carries a third highlight gradient and slightly different alphas
   (.22/.15/.08 against the card's .20/.13). `tools/probe/material.js` caught it
   the first time it ran — dark said "one material", light said DRIFTED — which
   is precisely the inconsistency the probe exists to stop me introducing.
   Dark pairs the tile with `.po-card[data-blended]`; light pairs it here. */
html[data-theme="light"][data-theme="light"] .ow-cv-face,
html[data-theme="light"][data-theme="light"] .ow-cv-pick__row,
html[data-theme="light"][data-theme="light"] .ow-reqs,
html[data-theme="light"][data-theme="light"] .ow-msg:not([data-mine]),
html[data-theme="light"][data-theme="light"] .po-sheet,
html[data-theme="light"][data-theme="light"] .ow-cpage__ev,
html[data-theme="light"][data-theme="light"] .ow-claims__row,
html[data-theme="light"][data-theme="light"] .ow-map__tile,
html[data-theme="light"][data-theme="light"] .po-card__face{
  background:
    radial-gradient(115% 62% at 50% -10%, rgba(var(--h),.20), transparent 62%),
    radial-gradient(92% 62% at 50% 116%,  rgba(var(--h2),.13), transparent 64%),
    linear-gradient(178deg, rgba(252,251,255,.96), rgba(238,236,247,.97));
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.9),
    inset 0 -26px 46px rgba(76,66,120,.055),
    inset 0 0 0 1px rgba(20,19,31,.06),
    0 0 0 1px rgba(var(--h),.30),
    0 10px 26px -14px rgba(48,40,80,.28);
}
/* the signature, legible on a light body */
html[data-theme="light"][data-theme="light"] .po-card__face::before{
  border-color:rgba(var(--h),.34)}
/* grain is a dark-body device; on white it is dirt */
html[data-theme="light"][data-theme="light"] .po-card__face::after,
html[data-theme="light"][data-theme="light"] html[data-theme="light"][data-theme="light"] .po-sheet.is-open::after{
  opacity:.04}
/* THE HALO GOES. On a dark ground a card spilling its hue reads as light in a
   room; on a bright one the identical shadow reads as a coloured fringe. */
html[data-theme="light"][data-theme="light"] .po-card{
  box-shadow:0 0 26px -18px rgba(var(--h),.30)}
@media (hover:hover) and (pointer:fine){
  html[data-theme="light"][data-theme="light"] .po-card:hover{
    box-shadow:0 0 30px -16px rgba(var(--h),.42)}
}
/* an empty cover's band is a whisper on a light ground unless it is told not
   to be — same words, same restraint, actually visible */
html[data-theme="light"][data-theme="light"] .po-card__cover[data-empty]{
  border-color:rgba(var(--h),.34);
  background:linear-gradient(180deg,rgba(var(--h),.05),rgba(var(--h2),.04))}

/* THE PERSON ON A CARD IS A TARGET, AND IT MUST LOOK LIKE ONE.
   (founder, 2026-08-17: "If I click somebody's icon, it doesn't even open to
   their profile.") It now opens them — but a control that gives no sign it is a
   control is barely better than one that does nothing, because nobody tries it
   twice. The affordance is deliberately quiet: this sits inside a card that is
   itself pressable, so it lifts and lights rather than drawing a box. */
/* THE AFFORDANCE HAS TO COVER BOTH KINDS OF ACTOR, or a Center's face looks
   inert while behaving like a link — which is worse than either. `data-po-who`
   is a person, `data-po-center` is a place; both open something. */
.po-card__who[data-po-who],.po-card__who[data-po-center]{
  cursor:pointer;border-radius:14px 12px 15px 13px;
  transition:background .16s var(--ow-ease)}
@media (hover:hover) and (pointer:fine){
  .po-card__who[data-po-who]:hover,.po-card__who[data-po-center]:hover{background:rgba(var(--h),.10)}
  .po-card__who[data-po-who]:hover .po-card__nm,
  .po-card__who[data-po-center]:hover .po-card__nm{color:rgb(var(--h))}
  .po-card__who[data-po-who]:hover .po-card__av{opacity:.88}     /* no ring, no glow (471 §5) */
}

/* ═══ 21 · THE LIGHT-MODE INVERSION THAT MADE POPITS GENERIC ════════════════
   (founder, 2026-08-17: "the puppets [Popits] were looking less... more generic
   than they were before. It's not looking too good.")

   He was right and this is the mechanism. §9.10 decided a widget is "hue-lit
   DARK glass — something you look INTO … deeper body, so content reads", and
   built it as `linear-gradient(--ow-panel-1, --ow-panel-2)`. In the dark those
   tokens are near-black and the rule does what it says. In LIGHT they are
   `rgba(255,255,255,.9)` and `rgba(243,241,249,.94)` — so on a `#eceaf3` page
   the body comes out LIGHTER THAN THE GROUND IT SITS ON.

   Measured on a real profile: the widget face was a 46%-alpha hue wash over
   near-white, on a near-white page. The material — the depth, the edge, the
   internal light, the seam — all still declared, all invisible. A white
   rectangle with a faint tint IS generic. Nothing was removed to make it so;
   the same rule simply means the opposite when the tokens flip.

   So the intent is restored rather than reinterpreted: in a lit room the pane is
   a touch DEEPER than the wall, its edge is legible, and the light inside it is
   its own. Same three moves as the feed card above, applied to the widget body
   and the open pane, so one material family holds in both grounds.

   Composition, type, spacing and spans are untouched (§16.7 — evolve, never
   redesign). Only the body, the edge and the seam change, and only under a
   light ground. */

/* THE WIDGET BODY — this is the one the founder was looking at */
html[data-theme="light"][data-theme="light"] .po-w:not(.po--1x1) > .po-face{
  background:
    radial-gradient(115% 62% at 50% -10%, rgba(var(--h),.22), transparent 60%),
    radial-gradient(92% 68% at 50% 116%,  rgba(var(--h2),.15), transparent 64%),
    radial-gradient(70% 46% at 84% 30%,   rgba(var(--h2),.08), transparent 72%),
    linear-gradient(178deg, rgba(251,250,255,.97), rgba(235,233,246,.98));
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.95),
    inset 0 -28px 48px rgba(76,66,120,.06),
    inset 0 0 0 1px rgba(20,19,31,.07),
    0 0 0 1px rgba(var(--h),.34),
    0 10px 24px -14px rgba(48,40,80,.3);
}
/* the seam is the family signature — invisible at .2 on a pale body */
html[data-theme="light"][data-theme="light"] .po-w:not(.po--1x1) > .po-face::before{
  border-color:rgba(var(--h),.34)}
/* grain belongs on a dark body; on a pale one it is dirt */
html[data-theme="light"][data-theme="light"] .po-w:not(.po--1x1) > .po-face::after{
  opacity:.05}
/* type on a pale body needs the hue to carry, not white-on-white */
html[data-theme="light"][data-theme="light"] .po-w:not(.po--1x1) .po-w__eyebrow{
  color:rgba(var(--h),.95)}
html[data-theme="light"][data-theme="light"] .po-w:not(.po--1x1) .po-w__label{
  color:var(--ow-ink)}
/* the spill: light in a room on a dark ground, a coloured fringe on a bright one */
html[data-theme="light"][data-theme="light"] .po-w:not(.po--1x1)::before{
  opacity:.18}

/* THE OPEN PANE, for the same reason — it is the same glass.
   ── AND ITS GROUND WAS A THIRD RECIPE ─────────────────────────────────────
   ★ CAUGHT BY `tools/probe/material.js` in light, 2026-09-12, the first run
     after it learned to look at an opened Popit:

       post   115%/62% at 50% -10% .20 · 92%/62% at 50% 116% .13 · 178deg
       sheet  120%/60% at 50%  -8% .24 · 100%/70% at 50% 120% .16 · 180deg

   Close enough to look right side by side and not the same material. It is
   the drift this probe's own header describes — a Post-SHAPED rule copied
   rather than the Post's own rule shared — and it had reached the largest
   surface in the product. The ground comes from the shared declaration now;
   the SHADOW stays here, because a sheet filling the screen has no page
   around it to throw a halo onto and carries an inner glow instead. */
html[data-theme="light"][data-theme="light"] .po-sheet{
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.95),
    inset 0 -30px 54px rgba(76,66,120,.07),
    inset 0 0 0 1px rgba(20,19,31,.07),
    0 0 0 1.5px rgba(var(--h),.55), 0 0 0 5px rgba(var(--h),.10),
    0 40px 90px -34px rgba(48,40,80,.42),
    0 0 90px -34px rgba(var(--h),.5);
}
html[data-theme="light"][data-theme="light"] .po-sheet__seam{
  border-color:rgba(var(--h),.30)}

/* ═══ 22 · A PROFILE MUST SHOW THE PERSON, NOT THE FRAME AROUND THEM ════════
   (founder, 2026-08-17: "People still don't have widgets on their profiles …
   this shit's totally not working." The widgets were THERE — they were below
   the fold.)

   Measured on a real profile at a 494px viewport: the first widget began at
   400px. EIGHTY-ONE PERCENT of the screen was spent before anything the person
   had made appeared —

       banner        158px   (and it holds no photograph)
       avatar         78px
       name           28px
       Follow/Message 32px
       tab row       ~40px

   The banner is the largest single item and, right now, always empty: no Center
   and no person on this platform has uploaded one. So the surface reserves a
   photograph's worth of space, fills it with a gradient, and pushes the actual
   human being off the bottom of the screen.

   THIS IS THE SAME RULE THE EMPTY COVER ALREADY FOLLOWS. A feed card with no
   picture collapses its frame to a quiet band instead of holding a 350px void;
   a profile with no picture should not hold a 158px one. When a REAL banner
   exists it gets its full height back, unchanged — the space is earned by
   content, never reserved for its absence.

   Nothing is invented to fill it (Law 15) and no composition is redesigned
   (§16.7): the same header, tightened where it is holding nothing. */
.ow-banner:not(:has(img)){height:clamp(76px,16vw,104px)}
/* THE AVATAR OVERLAPS A SHORTER BAND BY LESS, or it hangs off the bottom of it.
   A 34px bite out of a 190px cover is a detail; the same bite out of a 76px one
   takes nearly half the band.

   ★ THIS RULE DID NOT EXIST UNTIL NOW, though it has been in the file for days.
     It was written `.ow-prof:has(.ow-banner:not(:has(img)))` — a `:has()` inside
     a `:has()`, which the spec forbids. The browser does not warn: it drops the
     whole selector as invalid and the declaration never applies to anything. It
     reads as working code and it styles nothing. `:has()` inside `:not()` at the
     top level is legal, so the test moves outward: a profile that does not
     contain a banner image. Same meaning, and it parses.
     (The `.ow-banner:not(:has(img))` rule above is fine — nothing nests there.)

   The acts DO NOT tighten with it. That companion rule was calibrated against
   the old even header, where 13px meant nothing and 8px lost nothing. The 12px
   under the counts is now load-bearing — it is what keeps "what you can do"
   attached to "what it amounts to" — and 4px of reclaimed height is not worth
   spending the one piece of grouping the header has. */
.ow-prof:not(:has(.ow-banner img)) .ow-prof__id{margin-top:-26px}

/* ON A SHORT VIEWPORT THE HEADER YIELDS FIRST. A phone in landscape, a split
   view or a small window is where an 81% header becomes 100% — the content is
   what the person came for, so the chrome is what gives way. */
@media (max-height:620px){
  .ow-banner{height:clamp(64px,12vw,88px);
    border-radius:22px 19px 21px 20px / 20px 22px 18px 21px}
  .ow-avatar{width:60px;height:60px;font-size:21px}
  .ow-prof__id{margin-top:-22px}
}

/* ── OPENING A POST. It showed three fields and no person; this is the surface
      that replaces it. Same material as everything else — hand-cut radii, hue
      from context — because a post is not a special kind of screen. ───────── */
.ow-post__who{display:flex;align-items:center;gap:10px;margin:2px 0 14px}
.ow-post__av{width:44px;height:44px;flex:none;border:0;cursor:pointer;padding:0;
  border-radius:50%;overflow:hidden;display:grid;place-items:center;
  background:linear-gradient(150deg,rgb(var(--ow-h1)),rgba(var(--ow-h2),.75));
  color:#fff;font:inherit;font-weight:650;font-size:15px}
.ow-post__av img{width:100%;height:100%;object-fit:cover}
.ow-post__nm{display:flex;flex-direction:column;gap:1px;cursor:pointer;min-width:0}
.ow-post__nm b{font-family:var(--ow-font-display);font-weight:600;font-size:15px;color:var(--ow-ink)}
.ow-post__nm span{font-size:12px;color:var(--ow-ink-3)}
.ow-post__t{font-family:var(--ow-font-display);font-weight:650;font-size:22px;
  letter-spacing:-.01em;line-height:1.18;margin:0 0 8px;color:var(--ow-ink)}
.ow-post__body p{font-size:16px;line-height:1.6;color:var(--ow-ink-2);margin:0 0 10px}
.ow-post__media{margin:12px 0;border-radius:18px 15px 19px 16px;overflow:hidden;
  background:rgba(var(--ow-h1),.10)}
.ow-post__media img,.ow-post__media video{width:100%;display:block}
.ow-post__acts{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0 4px}
.ow-post__act{font:inherit;font-size:12.5px;font-weight:560;cursor:pointer;
  padding:8px 14px;border-radius:14px 11px 13px 12px;
  border:1px solid rgba(var(--ow-h1),.28);background:rgba(var(--ow-h1),.08);
  color:var(--ow-ink)}
.ow-post__act.on{background:rgba(var(--ow-h1),.85);border-color:transparent;color:#fff}
/* ── A WORDMARK: A NAME WEARING THE FACE ITS OWNER CHOSE ──────────────────
   ★ FOUNDER, 2026-09-10: *"i want to also supoort custom wordmarks on peoples
     profiles and posts like apple music now does."*

   THE WORDS ARE ALWAYS THE DISPLAY NAME. The face is set inline from the
   resolved stack the API serves, so this rule carries no font of its own —
   a font named here would be a second place to change when the catalogue does.

   IT DOES NOT CHANGE THE SIZE OR THE WEIGHT. The only things that change are
   the face and the LINE BOX it needs.

   THE LINE BOX IS NOT OPTIONAL, AND I SHIPPED IT WRONG FIRST. Measured on a
   real profile: "Mira Seam" in Playfair Display drew 30px of glyphs into a
   23px line box. `overflow` is visible on that heading, so nothing was cut —
   the ascenders simply sat on top of the handle underneath. Faces differ in
   how much room they need above and below the baseline, and a rule that sets
   the family without giving it that room collides with whatever is beneath it
   on every face taller than the default.

   So the line box grows, and ONLY where a wordmark is worn: a person who has
   chosen nothing sees a heading identical to the one they saw before. */
.ow-wm{font-family:var(--wm, var(--ow-font-display));
  font-weight:var(--wm-w, 650);letter-spacing:-.01em;line-height:1.3}

/* ── BOOKING A THING, INLINE UNDER THE ROW THAT WAS TAPPED ────────────────
   No modal (standing UI rules). It grows out of the list item it belongs to,
   so the place being booked stays on screen above the form — a sheet that
   covers the thing you are booking makes you check the name twice. */
.ow-bk{display:flex;flex-direction:column;gap:10px;margin:2px 0 10px;
  padding:14px 14px 12px;border-radius:var(--ow-btn-r);
  background:rgba(var(--ow-h1),.07);
  box-shadow:inset 0 0 0 1px rgba(var(--ow-h1),.20)}
.ow-bk__h{font-family:var(--ow-font-display);font-weight:650;font-size:14px;
  color:var(--ow-ink)}
.ow-bk__note{margin:0;font-size:12.5px;line-height:1.5;color:var(--ow-ink-3)}
.ow-bk__f{display:flex;align-items:center;justify-content:space-between;gap:12px;
  font-size:12.5px;font-weight:560;color:var(--ow-ink-2)}
.ow-bk__in{font:inherit;font-size:13px;padding:8px 10px;
  border-radius:calc(var(--ow-btn-r) - 4px);color:var(--ow-ink);
  background:var(--ow-ground,rgba(0,0,0,.18));
  border:1px solid rgba(var(--ow-h1),.28);min-width:0;max-width:62%}
.ow-bk__in:focus{outline:none;border-color:rgba(var(--ow-h1),.60)}
.ow-bk__row{display:flex;gap:8px}
.ow-bk__why{margin:0;font-size:12.5px;line-height:1.5;color:var(--ow-ink-3)}
/* the one line that says it actually happened */
.ow-bk__ok{margin:0;font-size:13px;font-weight:560;color:var(--ow-ink)}

/* ── WHAT YOU ALREADY HOLD HERE ────────────────────────────────────────────
   The bookings a person has at this Center, above the list of what is free.
   It is a DIFFERENT KIND OF THING from the rows under it — those are offers,
   these are promises already made to this reader — so it does not borrow the
   glance row's shape. It is quieter and flatter, and it is set off by a rule
   above rather than by a slab, because two slabs stacked read as one tab that
   cannot decide what it is.

   A CANCELLED BOOKING STAYS ON THE LIST and goes grey. Removing it would be
   the surface deciding a person no longer needs to see that the thing they
   arranged is off — which is exactly the fact they came back to check. */
.ow-mine{display:flex;flex-direction:column;gap:2px;margin:0 0 6px;padding:0 0 10px;
  border-bottom:1px solid var(--ow-seam)}
.ow-mine__h{font-family:var(--ow-font-atmos);font-weight:600;font-size:10.5px;
  letter-spacing:.01em;color:var(--ow-ink-3);
  padding:2px 2px 6px}
.ow-mine__row{display:flex;align-items:baseline;gap:10px;width:100%;
  font:inherit;text-align:left;cursor:pointer;
  padding:9px 10px;border:0;border-radius:calc(var(--ow-btn-r) - 4px);
  background:transparent;color:var(--ow-ink);
  transition:background .16s ease}
.ow-mine__row:hover,.ow-mine__row:focus-visible{background:rgba(var(--ow-h1),.10)}
.ow-mine__row:focus-visible{outline:2px solid rgba(var(--ow-h1),.55);outline-offset:-2px}
.ow-mine__when{font-weight:600;font-size:13.5px}
.ow-mine__sub{font-size:12px;color:var(--ow-ink-3);margin-left:auto;
  text-align:right;flex:0 1 auto}
/* the promise that is no longer one */
.ow-mine__row[data-live="off"] .ow-mine__when{color:var(--ow-ink-3);font-weight:560}
.ow-mine__row[data-live="off"]{opacity:.72}

/* ── THE AUTHOR'S OWN CONTROLS ─────────────────────────────────────────────
   Quieter than the response row above them: Like and Comment are what a
   reader does and belong in the person's hue; Edit and Delete are what the
   WRITER does to their own thing, and a row of accented buttons for them would
   read as the loudest thing on the Post. Ghosted, on the seam, set apart by a
   rule rather than by colour.

   DELETE ARMS BEFORE IT COMMITS — no modal, no native confirm (standing UI
   rules) — and only the ARMED state earns a colour, because that is the only
   moment the next press is destructive. */
.ow-post__own{display:flex;gap:8px;flex-wrap:wrap;align-items:flex-start;
  margin:18px 0 4px;padding-top:14px;
  border-top:1px solid var(--ow-seam,rgba(255,255,255,.08))}
.ow-post__own .po-act{font-size:12.5px}
.ow-post__del.is-armed{background:rgba(255,92,108,.16);
  box-shadow:inset 0 0 0 1px rgba(255,92,108,.55);color:#ff5c6c}
/* the composer sits UNDER the row and takes the full width, so the words
   being edited are where the words were */
.ow-post__editor{flex:1 0 100%;display:flex;flex-direction:column;gap:8px;
  margin-top:4px}
.ow-post__ta{font:inherit;font-size:14.5px;line-height:1.55;min-height:96px;
  resize:vertical;padding:12px 13px;border-radius:var(--ow-btn-r);
  color:var(--ow-ink);background:rgba(var(--ow-h1),.06);
  border:1px solid rgba(var(--ow-h1),.24)}
.ow-post__ta:focus{outline:none;border-color:rgba(var(--ow-h1),.55)}
.ow-post__erow{display:flex;gap:8px}
.ow-post__ewhy{margin:0;font-size:12.5px;line-height:1.5;color:var(--ow-ink-3)}

.ow-post__c{padding:10px 0;border-top:1px solid rgba(var(--ow-h1),.14)}
.ow-post__c b{font-weight:600;font-size:13.5px;color:var(--ow-ink)}
.ow-post__c span{font-size:11.5px;color:var(--ow-ink-3);margin-left:7px}
.ow-post__c p{font-size:14.5px;line-height:1.5;color:var(--ow-ink-2);margin:3px 0 0}

/* ── COMMENTS, THE TIKTOK SHAPE ────────────────────────────────────────────
   ★ FOUNDER, 2026-08-29: *"Oneway comments should follow the clean, compact
     interaction model of TikTok — not the visually heavy/thread-heavy
     presentation of Instagram or X… Avoid: deep indented nesting · giant
     threaded boxes · excessive borders · complicated conversation trees. The
     goal is fast consumption and interaction."*

       avatar  Sarah
               This is incredible
               ♥ 12     Reply

   WHAT MAKES IT COMPACT IS WHAT IS NOT HERE. No card, no panel, no border
   between rows, no indent rail, no nesting past one level. The face is the
   left column and everything else is one text column beside it, so ten
   comments read as ten lines rather than ten objects. The previous treatment
   was `border-top` per row — a rule per comment, which is the "excessive
   borders" the ruling names, and it made a thread look like a table.

   THE REPLY BRANCH IS ONE LEVEL AND IT IS FLAT. The backend threads to any
   depth; the presentation deliberately does not, because a tree is precisely
   the thing being avoided. A branch is a smaller version of the same row, and
   it cannot open another branch inside itself. */
.ow-cmts{display:flex;flex-direction:column;gap:14px;padding:2px 0 4px}
.ow-cmts__pinhead{font-family:var(--ow-font-atmos);font-weight:600;font-size:9px;
  letter-spacing:.01em;color:var(--ow-ink-3);
  margin:14px 0 8px}
.ow-cmts--pinned{padding-bottom:14px;margin-bottom:2px;
  /* THE ONE RULE IN THE WHOLE THREAD, and it separates two LISTS rather than
     two comments — which is what a rule is for. */
  border-bottom:1px solid rgba(var(--ow-h1),.14)}

/* ── WHAT MAKES TIKTOK'S COMMENTS SIMPLE, AND WHY THIS IS SHAPED LIKE THEM ──
   ★ FOUNDER, 2026-08-30: *"have tiktok level comments research what makes them
     so simple and good."*

   Six properties do the work, and only the third is obvious:

   1 · THREE COLUMNS, NEVER MORE.  face | words | one affordance.
   2 · THE LIKE LIVES IN A FIXED RIGHT GUTTER, heart above count, vertically
       centred. This is the one people copy without understanding: it means the
       eye reads ONE column of text at a constant width, and the thumb finds
       the affordance in the SAME PLACE on every row without aiming. Put the
       heart inline under the words and both properties are lost — the text
       column ends raggedly and the target moves with the length of the comment.
   3 · WHITESPACE, NOT LINES. No card, no border, no divider. Density comes
       from tight line-height and small meta, not from boxes.
   4 · ONE LEVEL OF REPLY, EVER. `View N replies` opens a flat list and stops.
       A tree is the thing being avoided (the founder's own words).
   5 · META IS ONE SMALL GREY LINE — time, then Reply. Nothing else competes
       with what the person actually said.
   6 · COUNTS ARE ABSENT AT ZERO. A "0" beside every comment is noise about
       nothing having happened.

   The rows were `display:flex` with the actions inline under the body — right
   in every respect except 2, which is the one that makes a thread scannable. */
.ow-cmt{display:grid;grid-template-columns:32px minmax(0,1fr) 40px;
  align-items:start;gap:10px}
/* A REMOVED COMMENT IS ONE SENTENCE, SO IT GETS ONE COLUMN.
   This dropped the RAIL and kept the FACE column — but a removed row has no
   face to put there, only the sentence, and a grid puts an only child in
   column one. Measured on :3050: "This was removed." rendered as five stacked
   words in a 32px column, 200px tall, in the middle of a thread. The row keeps
   its place (that is the point of a tombstone — the reply above it still makes
   sense) and it now keeps it in one line. */
.ow-cmt[data-gone]{grid-template-columns:minmax(0,1fr)}

/* THE GUTTER. Fixed width, so every heart on every row is in one vertical
   line; centred on the first two lines of the comment rather than the whole
   row, so a long comment does not push its own control to the bottom. */
.ow-cmt__rail{display:flex;flex-direction:column;align-items:center;gap:2px;
  padding-top:3px;min-height:40px;justify-content:flex-start}
.ow-cmt__rail .ow-cmt__like{flex-direction:column;gap:2px}
.ow-cmt__rail .ow-cmt__like > b{font-size:11px;line-height:1}

/* THE CREATOR'S OWN VOICE, marked. TikTok does this because "the person who
   made this replied to you" is the single most interesting fact in a thread,
   and it costs one small chip to say. */
.ow-cmt__creator{font-family:var(--ow-font-atmos);font-weight:600;font-size:8.5px;
  letter-spacing:.01em;
  color:rgb(var(--ow-h1));padding:1px 6px;border-radius:6px;
  background:rgba(var(--ow-h1),.14)}

/* HOW THE THREAD IS ORDERED, offered rather than assumed. Relevance first —
   the best comment should be the first one read without anybody choosing. */
.ow-cmts__sort{display:flex;gap:14px;align-items:center;margin:2px 0 10px}
.ow-cmts__sort button{background:none;border:0;padding:0;cursor:pointer;
  font-family:var(--ow-font);font-weight:600;font-size:12px;
  color:var(--ow-ink-3);transition:color .16s}
.ow-cmts__sort button[aria-pressed="true"]{color:var(--ow-ink)}
.ow-cmts__sort button:hover{color:var(--ow-ink-2)}
.ow-cmt[data-gone] .ow-cmt__body{color:var(--ow-ink-3);font-style:italic}
/* the commenter's own face — their picture, or their initials in THEIR hue
   (`--h`/`--h2` set per row), cut round · rounded · square (founder/409) */
.ow-cmt__av{flex:none;width:32px;height:32px;border-radius:50%;overflow:hidden;
  display:grid;place-items:center;cursor:pointer;
  --h:var(--ow-h1);--h2:var(--ow-h2);
  font-family:var(--ow-font-display);font-weight:650;font-size:13px;color:#fff;
  background:radial-gradient(58% 46% at 32% 22%,rgba(255,255,255,.55),transparent 70%),
    linear-gradient(150deg,rgb(var(--h)),rgba(var(--h2),.72));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.4)}                  /* no glow (471 §5) */
.ow-cmt__av[data-shape="rounded"]{border-radius:11px 9px 12px 10px}
.ow-cmt__av[data-shape="square"]{border-radius:6px}
.ow-cmt__av img{width:100%;height:100%;object-fit:cover;display:block}
.ow-cmt__b{min-width:0;flex:1 1 auto}
.ow-cmt__top{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
.ow-cmt__nm{font-family:var(--ow-font);font-weight:600;font-size:13px;
  color:var(--ow-ink-3)}
.ow-cmt__when{font-size:11.5px;color:var(--ow-ink-3)}
.ow-cmt__pin{font-family:var(--ow-font-atmos);font-weight:600;font-size:8.5px;
  letter-spacing:.01em;color:rgb(var(--ow-h2))}
/* THE WORDS ARE THE LOUDEST THING IN THE ROW. On TikTok the name is small and
   grey and the comment is full-size ink — the opposite of a forum, where the
   author outranks what they said. */
.ow-cmt__body{font-size:14.5px;line-height:1.45;color:var(--ow-ink);margin:2px 0 0;
  overflow-wrap:anywhere}
.ow-cmt__acts{display:flex;align-items:center;gap:16px;margin-top:5px}
.ow-cmt__like{width:auto;height:auto;padding:0;background:none;box-shadow:none;
  border:0;border-radius:0;display:inline-flex;align-items:center;gap:5px;
  color:var(--ow-ink-3);cursor:pointer}
/* AN UNPRESSED HEART IS AN OUTLINE, A PRESSED ONE IS FILLED — the same
   grammar the Popit's rail uses, and the reason it reads at a glance. Without
   `fill:none` the material's heart came through as a solid dark blob on the
   dark ground: technically drawn, visually absent. */
.ow-cmt__like > svg{width:17px;height:17px;stroke-width:1.9;
  fill:none;stroke:currentColor}
.ow-cmt__like > b{font-size:11.5px;font-weight:600;font-variant-numeric:tabular-nums}
.ow-cmt__reply,.ow-cmt__more,.ow-cmt__own{background:none;border:0;padding:0;
  cursor:pointer;font-family:var(--ow-font);font-weight:560;font-size:12px;
  color:var(--ow-ink-3);transition:color .15s var(--ow-ease)}
.ow-cmt__reply:hover,.ow-cmt__more:hover,.ow-cmt__own:hover{color:var(--ow-ink)}
.ow-cmt__more{display:block;margin-top:7px;color:rgba(var(--ow-h2),.95)}
/* ═══ THE SUBSECTION — replies, CONTAINED ══════════════════════════════════
   ★ FOUNDER, 2026-08-31: *"replys should open subsections and should be
     contained not like thier own posts that u can click and must clearly show
     all comments and condense them try to create the best comments design
     possible."*

   WHAT "CONTAINED" HAS TO MEAN IN PIXELS. A left hairline alone does not
   contain anything — it marks an edge and leaves the other three open, so the
   replies bled into the next comment and the eye could not tell where one
   person's thread stopped. This is a REGION: a tinted ground, a rounded edge,
   the hue rail down its left, and a foot. It has a beginning and an end, and
   everything between them belongs to the comment above it.

   IT IS TINT, NOT A BORDER. §3 of the rules above says whitespace rather than
   lines, and a boxed-in reply list is precisely the "giant threaded box" the
   founder ruled against on 2026-08-29. A 3% wash reads as an inset — a
   depression in the surface the thread already occupies — where a 1px outline
   would read as a second card sitting on top of it.

   ONE RAIL, NOT ONE PER REPLY. The rail belongs to the REGION, so ten replies
   share one line instead of drawing ten. That is what stops the indentation
   accumulating, and it is why a reply can never look like a post: a post is
   never inside anything. */
/* IT REACHES BACK UNDER THE FACE AND OUT INTO THE GUTTER, and that is a
   measurement rather than a taste. `.ow-cmt__b` is column two of a
   32px|1fr|40px grid, so a subsection sitting inside it starts 42px in — and
   its own padding, its own 20px face and its own like gutter took 86px more.
   Measured on a 371px viewport: 157px of text column, and a one-sentence reply
   wrapped to FOUR LINES. Every reply was 99px tall, which is not condensed,
   it is a small post with a narrow column.

   Pulling 22px left and 8px right recovers 30px without touching the comment
   above it, which the founder has approved. The replies still begin clearly
   inside the comment — the rail and the wash say where they belong — they just
   stop paying for a second avatar gutter they never used. */
.ow-sub{margin:9px -8px 2px -22px;padding:9px 8px 4px 10px;
  border-radius:12px 10px 13px 11px;
  background:rgba(var(--ow-h1),.045);
  box-shadow:inset 2px 0 0 rgba(var(--ow-h1),.30)}
/* THE WASH IS DEEPER ON THE LIGHT GROUND, because 4.5% of a hue over white is
   invisible while over the dark sheet it is a clear depression. Written in this
   file's own two-branch form — the explicit choice, and the system default —
   rather than a third convention. */
html[data-theme="light"][data-theme="light"] .ow-sub{
  background:rgba(var(--ow-h1),.075)}
@media (prefers-color-scheme:light){
  html:not([data-theme="dark"]):not([data-theme="light"]) .ow-sub{
    background:rgba(var(--ow-h1),.075)}
}

/* ═══ ONE REPLY — CONDENSED ════════════════════════════════════════════════
   The founder's word was "condense", and the measurement is the row height. A
   reply drawn by `draw()` was 74px: a 32px face, a name line, a body line, an
   action line and a 40px rail. This is 34px, and it loses nothing a person
   reads — the name is IN the sentence, which is how talk is written down
   everywhere except forums.

   22px face · 13px text · one heart. The gap between replies is 9px against
   the thread's 14px, because inside one person's answer the lines are more
   closely related to each other than two separate comments are. */
.ow-rep{display:grid;grid-template-columns:20px minmax(0,1fr) 22px;
  align-items:start;gap:7px;padding:0 0 9px}
.ow-rep:last-of-type{padding-bottom:4px}
.ow-rep__av{width:20px;height:20px;border-radius:50%;
  display:grid;place-items:center;cursor:pointer;margin-top:1px;
  font-family:var(--ow-font-display);font-weight:650;font-size:9.5px;color:#fff;
  background:radial-gradient(58% 46% at 32% 22%,rgba(255,255,255,.55),transparent 70%),
    linear-gradient(150deg,rgb(var(--ow-h2)),rgba(var(--ow-h1),.72));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.4)}
/* NAME AND WORDS IN ONE FLOW. `.ow-rep__t` is the whole line — a bold grey
   name, then the sentence, then the time — so it wraps as one paragraph
   instead of stacking into three. This is the condensation. */
.ow-rep__t{min-width:0;font-size:13px;line-height:1.42;color:var(--ow-ink);
  overflow-wrap:anywhere}
.ow-rep__nm{font-family:var(--ow-font);font-weight:600;font-size:12.5px;
  color:var(--ow-ink-3);cursor:pointer}
/* ONE TRAILING RUN — time then controls, held together so a wrap can never
   strand "2h" on a line of its own above "Reply". `inline` rather than a flex
   row is the whole point: it sits at the end of the sentence when there is
   room for it, and only then takes a line of its own. */
.ow-rep__meta{white-space:nowrap;margin-left:7px}
.ow-rep__when{font-size:11px;color:var(--ow-ink-3)}
.ow-rep[data-gone] .ow-rep__t{color:var(--ow-ink-3);font-style:italic}
.ow-rep[data-gone]{grid-template-columns:minmax(0,1fr)}
/* THE ONLY CONTROL A REPLY CARRIES BESIDES ITS HEART. No pin (the runtime
   pins top-level comments only), no opener (aiming at a reply aims at its
   parent), nothing that could be mistaken for opening a thing. */
.ow-rep__acts{margin-left:10px}
.ow-rep__acts > button + button{margin-left:10px}
.ow-rep__reply{background:none;border:0;padding:0;cursor:pointer;
  font-family:var(--ow-font);font-weight:560;font-size:11.5px;
  color:var(--ow-ink-3);transition:color .15s var(--ow-ease)}
.ow-rep__reply:hover{color:var(--ow-ink)}
/* SMALLER THAN THE COMMENT'S, IN THE SAME COLUMN LOGIC. A reply's heart is a
   secondary act on a secondary object and it says so at 14px — but it is still
   in a fixed gutter, so the replies scan down one line exactly as the comments
   above them do. */
.ow-rep__like{width:auto;height:auto;padding:0;background:none;box-shadow:none;
  border:0;border-radius:0;display:flex;flex-direction:column;align-items:center;
  gap:1px;margin-top:2px;color:var(--ow-ink-3);cursor:pointer}
.ow-rep__like > svg{width:14px;height:14px;stroke-width:2;
  fill:none;stroke:currentColor}
.ow-rep__like > b{font-size:10px;font-weight:600;font-variant-numeric:tabular-nums}
/* THE PRESSED STATE IS NOT RESTATED HERE. This button carries `po-act` and
   `data-po-respond="like"`, so §the response rules above already paint it
   `--ow-like` with the same glow the Post and the comment get. Repeating the
   colour would create a second place the founder's *"clearly, unmistakably
   red"* could drift out of step, which is the whole reason it is a token. */

/* THE FOOT CLOSES THE REGION. Without it the subsection trails off into the
   next comment and "contained" is only half true — and it is where a person
   wants to answer, having just read the thread. */
.ow-sub__foot{display:block;width:100%;text-align:left;
  background:none;border:0;padding:4px 0 2px;cursor:pointer;
  font-family:var(--ow-font);font-weight:560;font-size:11.5px;
  color:rgba(var(--ow-h2),.95);transition:color .15s var(--ow-ease)}
.ow-sub__foot:hover{color:var(--ow-ink)}
.ow-sub__none{font-size:12.5px;color:var(--ow-ink-3);margin:0 0 4px}

/* ═══ THE PAGER — "all comments" said with a number ════════════════════════
   Full width and quiet. It is not a card and not a link: it is the bottom edge
   of the thread, and when there is nothing left it is not there at all. */
.ow-cmts__rest{display:block;width:100%;margin:4px 0 2px;padding:9px 0;
  background:none;border:0;cursor:pointer;
  font-family:var(--ow-font);font-weight:600;font-size:12.5px;
  color:rgba(var(--ow-h2),.95);
  border-top:1px solid rgba(var(--ow-h1),.12);
  transition:color .15s var(--ow-ease)}
.ow-cmts__rest:hover{color:var(--ow-ink)}
.ow-cmts__rest:disabled{color:var(--ow-ink-3);cursor:default}

/* ═══ THE AIM STRIP — the composer says what it will do ════════════════════
   A reply that does not announce itself is how this surface came to write
   top-level comments while everyone believed they were replying. The strip is
   present ONLY while aimed, so its appearance is the signal. */
.ow-cmt__aim{display:none;align-items:center;gap:10px;
  margin:8px 0 -2px;padding:6px 10px;
  border-radius:11px 9px 12px 10px;
  background:rgba(var(--ow-h1),.10)}
.ow-cmt__aim[data-on]{display:flex}
.ow-cmt__aimwho{flex:1 1 auto;min-width:0;font-size:12px;font-weight:560;
  color:var(--ow-ink-2);overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.ow-cmt__aimx{background:none;border:0;padding:0;cursor:pointer;flex:none;
  font-family:var(--ow-font);font-weight:600;font-size:11.5px;
  color:var(--ow-ink-3);transition:color .15s var(--ow-ease)}
.ow-cmt__aimx:hover{color:var(--ow-ink)}


/* ═══════════════════════════════════════════════════════════════════════════
   23 · THE NOTE — a person's smallest projection
   ═══════════════════════════════════════════════════════════════════════════
   Founder, 2026-08-17: *"a note is like a thing that appears above a profile
   picture basically everywhere where they would display it."*

   IT IS NOT INSTAGRAM'S BUBBLE, DELIBERATELY. Theirs is an opaque balloon with
   two cartoon tail dots. Copying that mark would put a borrowed thing on the
   most recognisably ONEWAY surface there is — a person's face. This world
   already holds the right idea: a Popit is a PROJECTION and the thing it came
   from is an EMITTER (§6). A face is an emitter. A note is the smallest
   projection there is, so it is the same glass as the pane, cut with the same
   asymmetric radius, tethered by the same beam — at bead scale.

   EVERY COLOUR HERE IS A TOKEN. Hardcoding `rgba(0,0,0,…)` and `#fff` is the
   exact defect that made the Popits read "more generic than they were before"
   on the light ground (§20/21): the material stopped inverting and every card
   went flat. Built from the sheet, rim and ink tokens plus the PERSON'S hue,
   this reads correctly in both grounds with no second rule.

   (And the first cut of this comment wrote those token names with a wildcard —
   `--ow-sheet-` followed by a star and a slash — which is `*` `/` and CLOSES A
   CSS COMMENT. The rule below was parsed as garbage and dropped, so the note
   rendered as bare text in the middle of the row while every OTHER rule in
   this section applied normally. It looked like a layout bug and it was a
   punctuation one.)

   A PERSON WITH NO NOTE COSTS NOTHING. The row only reserves height when a
   note is actually present (`:has`), so "everywhere" stays affordable on a
   surface that scrolls — otherwise every feed row on the platform would have
   grown by 26px to hold a thing almost nobody has set. */
.po-note{
  position:absolute;z-index:4;pointer-events:none;
  display:inline-flex;align-items:center;
  max-width:min(210px,62%);
  padding:5px 11px 6px;
  border-radius:14px 11px 13px 12px;
  background:
    linear-gradient(152deg,var(--ow-sheet-1),var(--ow-sheet-2)),
    radial-gradient(120% 160% at 18% 0%,
      rgba(var(--ch1,var(--h,var(--ow-h1))),.30),transparent 72%);
  border:1px solid var(--ow-rim);
  box-shadow:
    inset 0 1px 0 var(--ow-lift),
    0 6px 16px -8px var(--ow-drop),
    0 0 18px -10px rgba(var(--ch1,var(--h,var(--ow-h1))),.9);
  -webkit-backdrop-filter:blur(6px) saturate(1.15);
  backdrop-filter:blur(6px) saturate(1.15);
}
.po-note__t{
  font-size:12px;font-weight:500;line-height:1.25;letter-spacing:-.005em;
  color:var(--ow-ink);
  /* ONE LINE. A note that wraps to three is a post, and the founder was
     explicit that a note is not a post — the server caps the text, and this
     caps what a long name in a narrow column can still do to a row. */
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* THE TETHER — the beam at bead scale (§6), not a speech-balloon tail. It
   descends from the plate toward the face, brightest where it leaves the
   plate, exactly the way .po-beam is drawn between an emitter and its pane. */
.po-note__tie{
  position:absolute;left:13px;bottom:-7px;width:2px;height:8px;border-radius:2px;
  background:linear-gradient(to bottom,
    rgba(var(--ch1,var(--h,var(--ow-h1))),.85),
    rgba(var(--ch1,var(--h,var(--ow-h1))),0));
}

/* ── where it sits, per avatar ────────────────────────────────────────────
   Two SCALES of one design, never two designs. The small one rides a 34px row
   mark and has to earn its height; the large one rides a person's whole
   identity and can breathe. */
/* WIDE ENOUGH TO HOLD WHAT THE SERVER ALLOWS. The first cut capped this at
   180px and a 45-character note — well inside the runtime's 60-character
   limit — came out as "back from the coast, catching …". A cap the product
   advertises and the surface will not render is a broken promise in the one
   place a person's own words appear, so the plate is sized to the cap and the
   ellipsis is left as the guard it was meant to be rather than the norm. */
.po-note--sm{padding:4px 10px 5px;max-width:min(340px,74%);border-radius:12px 9px 11px 10px}
.po-note--sm .po-note__t{font-size:11px}
.po-note--sm .po-note__tie{left:12px;bottom:-6px;height:7px}

/* the feed row. `:has` is already the mechanism §22 uses for the empty banner,
   so this is the file's existing idiom rather than a new one. */
/* INSIDE THE CARD, OVER THE FACE — not stuck to the card's corner. The first
   cut put it at 8px from the row's top with 12px of inset, which on a card
   whose own top edge IS this row's top edge read as a sticker slapped across
   the rounded corner, sitting further left than the avatar it belongs to. It
   is aligned to the avatar's own left edge (the row's 15px padding) with real
   clearance above, so it reads as something resting over that face. */
.po-card__who{position:relative}
/* MEASURED: with the ring at 14px in a Scroll group, a note pinned at `top:11px`
   sat 5px ABOVE the boundary — the one real containment breach left after the
   content box landed, and the note is exactly the thing the founder asked to
   make room for, so it cannot be the thing that escapes. Both offsets now come
   off `--po-inset`, so the note follows the boundary wherever a surface puts
   it, and the row reserves the note's own height on top of that. */
.po-card__who:has(> .po-note){padding-top:calc(var(--po-inset) + 26px)}
.po-card__who > .po-note{top:var(--po-inset);left:var(--po-inset)}

/* the open post's byline (.ow-post__who is the row, .ow-post__av the face) */
.ow-post__who{position:relative}
.ow-post__who:has(> .po-note){padding-top:34px}
.ow-post__who > .po-note{top:0;left:2px}

/* THE PROFILE HEADER — the one place the note is the size the person wrote it.
   SAME GUTTER AS EVERYTHING ELSE ON THE PROFILE, for the same reason a peer
   session already gave `.ow-note-set` below: `.ow-prof__id` insets its contents
   by 18px, and an absolutely positioned child is laid out against the PADDING
   box — so `left:0` is the border edge and the avatar starts 18px further in.
   At `left:4px` the note hung off the left of the screen, fourteen pixels clear
   of the face it belongs to.

   AND IT IS SIZED TO THE RUNTIME'S CAP, not to a round number. 240px cut a
   45-character note — well inside the 60 the server allows — on the one surface
   with room to spare. A cap the product advertises and the surface refuses to
   render is a broken promise in the place a person's own words appear. */
.ow-prof__id{position:relative}
.ow-prof__id > .po-note{
  top:-30px;left:18px;max-width:min(380px,calc(100% - 36px));
}
.ow-prof__id > .po-note .po-note__t{font-size:12.5px}

/* a gallery byline and a message-thread row: both are small marks in a list.
   THE SELECTORS ARE THE ONES THE DOM ACTUALLY BUILDS. `OW.noteOn` inserts the
   note into the avatar's PARENT, which is `.ow-gal__who` and `.ow-thread` —
   the first cut of this named `.ow-gal__by` and `.ow-thread__row`, neither of
   which this App has ever rendered, so every rule here would have applied to
   nothing while the note itself still appeared, unpositioned, mid-row. */
.ow-gal__who,.ow-thread{position:relative}
.ow-gal__who:has(> .po-note){padding-top:30px}
.ow-gal__who > .po-note{top:0;left:2px}
.ow-thread:has(> .po-note){padding-top:30px}
.ow-thread > .po-note{top:5px;left:10px}

@media (prefers-reduced-motion:no-preference){
  .po-note{animation:po-note-in .34s cubic-bezier(.22,1,.36,1) both}
}
@keyframes po-note-in{
  from{opacity:0;transform:translate3d(0,4px,0) scale(.94)}
  to{opacity:1;transform:none}
}

/* ── THE COMPOSER — where a person actually sets one ──────────────────────
   A note nobody can write is a rendering exercise. This is the field, and it
   lives on the person's OWN profile, above their own face, in the place the
   note will appear — so setting it is the same gesture as seeing it. */
.ow-note-set{
  /* ON THE PAGE'S GUTTER, NOT BESIDE IT. `.ow-prof__id` insets everything by
     18px — the avatar, the name, the handle — and this sat at 4px, so the one
     control above the face was the only thing on the profile not aligned to
     the column it belongs to. It read as a pill that had slipped off the edge
     of the screen, which on a dark banner is exactly what it looked like. */
  position:absolute;top:-34px;left:18px;z-index:5;
  display:inline-flex;align-items:center;gap:7px;max-width:min(280px,86%);
  padding:5px 11px 6px;border-radius:14px 11px 13px 12px;
  /* THE EMPTY STATE OF A NOTE SHOULD LOOK LIKE A NOTE. This was a near-
     transparent tint (`--ow-field` is 4.5% ink on the light ground) sitting on
     top of a DARK banner, so the one control above the face was the least
     legible thing on the profile — dark text on a dark photograph. It wears the
     plate the note itself wears, and says it is unset with quieter ink rather
     than by disappearing.

     ★ AND THE DASHED EDGE IS GONE. Founder, 2026-08-30: *"the sitiching dotted
       lines left and ensure this does not happen again."* A dashed border is a
       convention borrowed from FORM BUILDERS — it means "drop zone" or "nothing
       here yet", and on a finished profile it reads as stitching left in the
       garment: an editor's mark that escaped into the product. Nothing else on
       this platform draws one, which is exactly why it stood out.

       The unset state is said by quieter INK and a softer plate instead. Same
       information, no borrowed vocabulary, and the control still reads as
       something to press because it wears the note's own material. */
  border:1px solid var(--ow-seam);
  background:linear-gradient(152deg,var(--ow-sheet-1),var(--ow-sheet-2));
  box-shadow:inset 0 1px 0 var(--ow-lift),0 6px 16px -8px var(--ow-drop);
  -webkit-backdrop-filter:blur(6px) saturate(1.15);
  backdrop-filter:blur(6px) saturate(1.15);
  color:var(--ow-ink-2);font:inherit;font-size:12px;font-weight:500;
  cursor:pointer;text-align:left;
}
.ow-note-set:hover{color:var(--ow-ink);border-color:rgba(var(--ow-h1),.5)}
.ow-note-set svg{width:12px;height:12px;flex:none;opacity:.75}
.ow-note-edit{
  position:absolute;top:-40px;left:18px;z-index:6;   /* the same 18px gutter */
  display:flex;align-items:center;gap:6px;
  padding:6px;border-radius:16px 13px 15px 14px;
  background:linear-gradient(152deg,var(--ow-sheet-1),var(--ow-sheet-2));
  border:1px solid var(--ow-rim);box-shadow:0 10px 26px -14px var(--ow-drop);
}
.ow-note-edit input{
  font:inherit;font-size:12.5px;font-weight:500;width:min(230px,48vw);
  padding:5px 9px;border-radius:11px 9px 10px 9px;
  border:1px solid var(--ow-field-edge);background:var(--ow-field);color:var(--ow-ink);
}
.ow-note-edit button{
  font:inherit;font-size:11.5px;font-weight:600;cursor:pointer;padding:6px 10px;
  border-radius:11px 9px 10px 9px;border:0;color:#fff;
  background:rgba(var(--ow-h1),.9);
}
.ow-note-edit button[data-act="clear"]{
  background:transparent;color:var(--ow-ink-3);border:1px solid var(--ow-rim);
}
.ow-note-edit small{font-size:10.5px;color:var(--ow-ink-3);font-weight:500;min-width:22px}


/* ── THE GUARD THESE RULES WERE MISSING ───────────────────────────────────
   MEASURED 2026-08-17, in the configuration most people are actually in.

   Every rule in §20/21 above was written twice: once for
   `html[data-theme="light"]`, and once for
   `html:not([data-theme="dark"]):not([data-theme="light"])` — which means
   "nobody has chosen a theme". The TOKEN block at the top of this file spells
   that same pair correctly, INSIDE `@media (prefers-color-scheme:light)`. This
   section copied the selector and left the media query behind.

   So "nobody has chosen" was read as "therefore light" — and a person whose
   SYSTEM is dark, with no explicit choice, is exactly that person. They got
   `--ow-ground:#000` and `--ow-ink:#fff` from the tokens, which is right, and
   then WHITE CARD STOCK from these rules, which is not. White plates, white
   text: a feed nobody could read, on the platform's first screen, in the
   default configuration. Verified in the browser at prefers-color-scheme:dark
   before and after.

   The founder's words on 2026-08-17 were *"this looks like crap"* and *"the
   Popits were looking more generic than they were before."* The light-ground
   pass those complaints produced is good work — it was simply applying in BOTH
   grounds. Nothing is redesigned here: the identical declarations now sit
   behind the query they always meant, and the `[data-theme="light"]` twin of
   each still applies unconditionally, because an explicit choice must beat the
   system on a machine set the other way.

   THE PAIR IS THE POINT, and it is why this shape keeps going wrong. A theme
   needs BOTH halves — the explicit attribute AND the system preference — and
   they take DIFFERENT guards. Write one without the other and the bug is
   invisible to whoever wrote it, because their own machine is in the mode that
   happens to look right.

   ── AND THE HALVES MUST WEIGH THE SAME, which they did not. ──────────────
   Measured the same day, and this one is OLDER than the missing media query:

       html:not([data-theme="dark"]):not([data-theme="light"]) .po-card__face
         → type + attr + attr + class          = (0,3,1)
       html[data-theme="light"] .po-card__face
         → type + attr + class                 = (0,2,1)

   `:not()` takes the specificity of its ARGUMENT, so writing the same rule two
   ways quietly wrote it at two WEIGHTS — and one component rule sits between
   them: `.po-card[data-blended] .po-card__face` at (0,3,0), which is the dark
   card stock every feed card uses, because a feed card blends the actor's hue
   with the place's.

   So the system-default half (0,3,1) beat it and looked right, while the
   EXPLICIT half (0,2,1) lost to it — meaning a person who deliberately chose
   Light got dark card stock on every blended card in their feed, and had done
   since this section was written. It survived because the person testing it
   almost certainly had no `data-theme` attribute set, which is the half that
   worked.

   `[data-theme="light"]` is therefore doubled: it costs nothing, it changes
   what MATCHES not at all, and it brings the explicit half to (0,3,1) — the
   same weight as its twin. Two spellings of one rule must weigh the same, or
   they are not one rule. */
@media (prefers-color-scheme:light){
  html:not([data-theme="dark"]):not([data-theme="light"]) .ow-cv-face,
  html:not([data-theme="dark"]):not([data-theme="light"]) .ow-cv-pick__row,
  html:not([data-theme="dark"]):not([data-theme="light"]) .ow-reqs,
  html:not([data-theme="dark"]):not([data-theme="light"]) .ow-msg:not([data-mine]),
  html:not([data-theme="dark"]):not([data-theme="light"]) .po-sheet,
  html:not([data-theme="dark"]):not([data-theme="light"]) .po-card__face{
    background:
      radial-gradient(115% 62% at 50% -10%, rgba(var(--h),.20), transparent 62%),
      radial-gradient(92% 62% at 50% 116%,  rgba(var(--h2),.13), transparent 64%),
      linear-gradient(178deg, rgba(252,251,255,.96), rgba(238,236,247,.97));
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,.9),
      inset 0 -26px 46px rgba(76,66,120,.055),
      inset 0 0 0 1px rgba(20,19,31,.06),
      0 0 0 1px rgba(var(--h),.30),
      0 10px 26px -14px rgba(48,40,80,.28);
  }
  html:not([data-theme="dark"]):not([data-theme="light"]) .po-card__face::before{
    border-color:rgba(var(--h),.34)}
  html:not([data-theme="dark"]):not([data-theme="light"]) .po-card__face::after,
  html:not([data-theme="dark"]):not([data-theme="light"])   html:not([data-theme="dark"]):not([data-theme="light"]) .po-sheet.is-open::after{
    opacity:.04}
  html:not([data-theme="dark"]):not([data-theme="light"]) .po-card{
    box-shadow:0 0 26px -18px rgba(var(--h),.30)}
  @media (hover:hover) and (pointer:fine){
  html:not([data-theme="dark"]):not([data-theme="light"]) .po-card:hover{
      box-shadow:0 0 30px -16px rgba(var(--h),.42)}
  }
  html:not([data-theme="dark"]):not([data-theme="light"]) .po-card__cover[data-empty]{
    border-color:rgba(var(--h),.34);
    background:linear-gradient(180deg,rgba(var(--h),.05),rgba(var(--h2),.04))}
  html:not([data-theme="dark"]):not([data-theme="light"]) .po-w:not(.po--1x1) > .po-face{
    background:
      radial-gradient(115% 62% at 50% -10%, rgba(var(--h),.22), transparent 60%),
      radial-gradient(92% 68% at 50% 116%,  rgba(var(--h2),.15), transparent 64%),
      radial-gradient(70% 46% at 84% 30%,   rgba(var(--h2),.08), transparent 72%),
      linear-gradient(178deg, rgba(251,250,255,.97), rgba(235,233,246,.98));
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,.95),
      inset 0 -28px 48px rgba(76,66,120,.06),
      inset 0 0 0 1px rgba(20,19,31,.07),
      0 0 0 1px rgba(var(--h),.34),
      0 10px 24px -14px rgba(48,40,80,.3);
  }
  html:not([data-theme="dark"]):not([data-theme="light"]) .po-w:not(.po--1x1) > .po-face::before{
    border-color:rgba(var(--h),.34)}
  html:not([data-theme="dark"]):not([data-theme="light"]) .po-w:not(.po--1x1) > .po-face::after{
    opacity:.05}
  html:not([data-theme="dark"]):not([data-theme="light"]) .po-w:not(.po--1x1) .po-w__eyebrow{
    color:rgba(var(--h),.95)}
  html:not([data-theme="dark"]):not([data-theme="light"]) .po-w:not(.po--1x1) .po-w__label{
    color:var(--ow-ink)}
  html:not([data-theme="dark"]):not([data-theme="light"]) .po-w:not(.po--1x1)::before{
    opacity:.18}
  html:not([data-theme="dark"]):not([data-theme="light"]) .po-sheet{
    background:
      radial-gradient(120% 60% at 50% -8%,  rgba(var(--h),.24),  transparent 58%),
      radial-gradient(100% 70% at 50% 120%, rgba(var(--h2),.16), transparent 62%),
      linear-gradient(180deg, rgba(251,250,255,.97), rgba(233,231,245,.98));
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,.95),
      inset 0 -30px 54px rgba(76,66,120,.07),
      inset 0 0 0 1px rgba(20,19,31,.07),
      0 0 0 1.5px rgba(var(--h),.55), 0 0 0 5px rgba(var(--h),.10),
      0 40px 90px -34px rgba(48,40,80,.42),
      0 0 90px -34px rgba(var(--h),.5);
  }
  html:not([data-theme="dark"]):not([data-theme="light"]) .po-sheet__seam{
    border-color:rgba(var(--h),.30)}
}

/* ═══ THE GEAR AND ITS DROPDOWN ════════════════════════════════════════════
   ★ FOUNDER, 2026-08-26: *"a settings gear in the upper right"*, *"the button
     shouldn't look ai generated nor have that border around it"*, *"settings
     should be its own page or dropdown."* */

/* NO PLATE. The header already sits on the banner; a tinted chip with a hue
   ring around it is a second surface for no reason, and it is most of what
   made the control read as generic. The mark stands on its own and earns its
   legibility from a shadow, not from a box. */
/* ── THE BELL OWNS THE CORNER; THE GEAR SITS UNDER IT ────────────────────
   ★ FOUNDER, 2026-08-26: *"notification bell cannot overlap with the settings
     gear and notification bell should dominate right corner."*

   Measured: bell at x 1065-1099 / y 10-44, gear at 1063-1097 / y 18-52 —
   stacked almost exactly, because both were placed against the same right edge
   at the same height by two components that do not know about each other.

   The header is platform furniture and reaches every surface, so the corner is
   the BELL'S. The gear belongs to one person's own Center, so it drops below
   the header band onto the banner it actually belongs to — clear of the bell,
   still top-right of its own surface, and visibly the quieter of the two. */
.ow-shell__gear{position:absolute;top:62px;right:var(--ow-gut);
  z-index:6;width:32px;height:32px;flex:none;padding:0;
  background:none;border:0;box-shadow:none;cursor:pointer;
  display:grid;place-items:center;color:var(--ow-ink);
  -webkit-tap-highlight-color:transparent;
  transition:transform .22s var(--ow-settle),opacity .18s var(--ow-ease)}
.ow-shell__gear svg{width:19px;height:19px;fill:none;stroke:currentColor;
  stroke-width:1.5;stroke-linecap:round;opacity:.7;
  filter:drop-shadow(0 1px 6px rgba(0,0,0,.55))}
@media (hover:hover){.ow-shell__gear:hover svg{opacity:1}
  .ow-shell__gear:hover{transform:rotate(38deg)}}
.ow-shell__gear:active{transform:rotate(38deg) scale(.92)}
.ow-shell__gear:focus-visible{outline:2px solid rgba(var(--ow-h1),.7);
  outline-offset:4px;border-radius:11px}

/* THE DROPDOWN IS POPIT MATERIAL, not a system menu — same body, same rim and
   the same hue-as-light the tiles are built from, so it belongs to the world
   it opens over instead of sitting on top of it. */
.ow-dd{position:absolute;z-index:40;min-width:264px;max-width:min(320px,86vw);
  padding:6px;border-radius:20px 17px 21px 18px;overflow:hidden;
  background:
    radial-gradient(120% 70% at 20% -8%,rgba(var(--ow-h1),.30),transparent 62%),
    linear-gradient(168deg,rgba(var(--ow-h1),.12),rgba(var(--ow-h2),.08) 60%,var(--ow-tile-base));
  box-shadow:inset 0 1px 0 var(--ow-lift),
             inset 0 0 0 1.5px rgba(var(--ow-h1),.26),
             0 26px 60px -22px var(--ow-drop),
             0 0 46px -18px rgba(var(--ow-h1),.5);
  -webkit-backdrop-filter:blur(18px) saturate(1.2);backdrop-filter:blur(18px) saturate(1.2);
  opacity:0;transform:translateY(-8px) scale(.97);transform-origin:top right;
  transition:opacity .16s var(--ow-ease),transform .2s var(--ow-settle)}
.ow-dd.is-in{opacity:1;transform:none}
html[data-motion="reduced"] .ow-dd{transition:none}

.ow-dd__row{display:flex;align-items:center;gap:13px;width:100%;text-align:left;
  border:0;background:none;cursor:pointer;padding:11px 12px;border-radius:14px;
  font-family:var(--ow-font);color:var(--ow-ink);
  transition:background .15s var(--ow-ease)}
@media (hover:hover){.ow-dd__row:hover{background:rgba(var(--ow-h1),.16)}}
.ow-dd__row:focus-visible{outline:0;background:rgba(var(--ow-h1),.2)}
/* THE MARK CARRIES THE ROW. Sized and weighted so the menu is legible with
   every word removed — the acceptance test the Popits are held to. */
.ow-dd__mark{width:34px;height:34px;flex:none;display:grid;place-items:center;
  border-radius:11px 9px 12px 10px;
  background:rgba(var(--ow-h1),.14);
  box-shadow:inset 0 0 0 1px rgba(var(--ow-h1),.24)}
/* THE GLOBAL PAIR, NOT THE POPIT-LOCAL ALIAS. `--h`/`--h2` are set on Popit
   subtrees; this dropdown is mounted on the shell, outside that scope, so
   `var(--h2)` resolved to nothing and every mark drew with no stroke — four
   empty plates. The world's own pair is what a shell-level surface reads. */
.ow-dd__mark svg{width:20px;height:20px;fill:none;stroke:rgba(var(--ow-h2),.96);
  stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
/* Appearance shows the person's ACTUAL colour rather than a picture of one */
.ow-dd__mark .ow-dd__disc{fill:rgba(var(--ow-h1),.85);stroke:none}
.ow-dd__txt{display:flex;flex-direction:column;gap:2px;min-width:0}
.ow-dd__t{font-size:14.5px;font-weight:560;letter-spacing:-.01em;line-height:1.15}
.ow-dd__n{font-size:11.5px;font-weight:400;color:var(--ow-ink-2);line-height:1.3;
  max-width:196px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ═══ NOTIFICATIONS ════════════════════════════════════════════════════════
   The same plate-free treatment as the menu: rows in one body, hairlines
   between, the hue carrying the unread state rather than a badge. */
.ow-notes{display:flex;flex-direction:column;border-radius:18px;overflow:hidden;
  box-shadow:inset 0 0 0 1px rgba(var(--ow-h1),.18)}
.ow-note{display:flex;align-items:flex-start;gap:11px;width:100%;text-align:left;
  border:0;border-bottom:1px solid var(--ow-rim);cursor:pointer;padding:14px 15px;
  font-family:var(--ow-font);color:var(--ow-ink);
  background:color-mix(in srgb, var(--ow-tile-base) 92%, transparent);
  transition:background .16s var(--ow-ease)}
.ow-note:last-child{border-bottom:0}
.ow-note:disabled{cursor:default}
@media (hover:hover){.ow-note:not(:disabled):hover{
  background:color-mix(in srgb, var(--ow-tile-base) 74%, transparent)}}
/* UNREAD IS THE LIGHT, NOT A LABEL — the dot lights only when it is unread,
   so the state is legible with every word stripped away. */
.ow-note__dot{width:7px;height:7px;border-radius:50%;flex:none;margin-top:6px;
  background:transparent;box-shadow:inset 0 0 0 1px rgba(255,255,255,.22)}
.ow-note[data-unread] .ow-note__dot{background:rgb(var(--ow-h2));
  box-shadow:0 0 9px rgb(var(--ow-h2)/.85)}
.ow-note__b{display:flex;flex-direction:column;gap:2px;min-width:0}
.ow-note__who{font-size:13px;font-weight:600;letter-spacing:-.01em}
.ow-note__t{font-size:13.5px;font-weight:400;color:var(--ow-ink);line-height:1.35}
.ow-note__w{font-size:11px;color:var(--ow-ink-3)}

/* ═══ THE PROFILE STRIP, AS ICONS ══════════════════════════════════════════
   ★ FOUNDER, 2026-08-26: *"I'd rather icons than text be people's profiles
     makeup."*

   The words are kept as the accessible name and the tooltip — an icon-only
   control with no name is unusable with a screen reader, and the strip-the-text
   rule is about what the EYE needs, never about deleting the label from the
   machine. */
.ow-tab[data-icon]{padding:9px 13px;min-width:44px;display:inline-grid;
  place-items:center;position:relative}
.ow-tab[data-icon] svg{width:21px;height:21px;fill:none;stroke:currentColor;
  stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;
  opacity:.62;transition:opacity .16s var(--ow-ease),transform .18s var(--ow-spring)}
.ow-tab[data-icon][aria-selected="true"] svg{opacity:1}
@media (hover:hover){.ow-tab[data-icon]:hover svg{opacity:.9;transform:translateY(-1px)}}
/* THE COUNT RIDES THE ICON rather than sitting beside a word that is no
   longer there. Anchored to the icon's centre, not the tab's edge: once the
   tabs were stretched to fill the bar (below) a 73px tab put `right:2px` a
   whole icon-width away, and the "3" floated between two tabs (C, 2026-09-30,
   the founder/573 review's Me page). */
.ow-tab[data-icon] .ow-tab__n{position:absolute;top:2px;right:auto;left:calc(50% + 5px);
  min-width:15px;height:15px;padding:0 4px;border-radius:8px;
  font-size:9.5px;font-weight:600;line-height:15px;text-align:center;
  background:rgba(var(--ow-h1),.9);color:#0b0b16}

/* the person's links — the Links tab (471 §10–11): the one link bar stood on
   end. No plate, no hairline; the mark is the platform's own, the word is
   the person's, the host is the quiet half. */
.ow-links{display:flex;flex-direction:column;padding:2px 4px}
/* ═══ THE PROFILE STRIP FILLS ITS BAR ══════════════════════════════════════
   ★ FOUNDER, 2026-08-26: *"all these buttons need to be spaced out more
     evenly. They should take up the entire bar."*

   The strip was `flex:0 0 auto` with a 2px gap — sized to its content, so five
   icons huddled at the left of a 1200px bar with a long empty run after them.
   That reads as an unfinished row rather than as navigation.

   ONLY WHEN THEY FIT. `flex:1` on a strip that overflows would squash twelve
   tabs into slivers, so the even distribution applies while the row still fits
   its container; past that the strip goes back to scrolling at a legible size.
   A Center profile uses this same shell, so it inherits this — which is what
   the founder asked for: Centers do not follow this layout elsewhere on the
   platform, but a Center's own PROFILE does. */
.ow-tabs:not(.ow-tabs--more) .ow-tab[data-icon]{flex:1 1 0;min-width:0}
.ow-tabs:not(.ow-tabs--more){gap:0}
/* the selected indicator was sized to the text; centred on the icon now */
.ow-tabs:not(.ow-tabs--more) .ow-tab[data-icon][aria-selected="true"]::after{
  left:50%;right:auto;transform:translateX(-50%);width:22px}

/* their Popits lead the Main tab; the facts follow with air between */
.ow-prof__canvas{display:block;margin:0 0 22px}
.ow-prof__canvas:empty{display:none;margin:0}

/* ═══ 29 · THE COLLECTION VIEWER ════════════════════════════════════════════
   Founder (2026-08-30): *"highlights and playlists should feel like they are
   apart of the same plane as the popits and stickers not seperate catagories
   that people can freely move around and arrange."*

   SO THE RAIL AND THE CARD GRID THAT USED TO LIVE HERE ARE GONE. A Highlight is
   a PLACEMENT on the canvas now — its tile is styled beside every other tile in
   `canvas.js`, because a tile that lives in the grid has to be measured against
   the grid, not against a stylesheet the grid cannot see.

   What stays here is the part no canvas tile can be: the full-screen sequence
   that opens when somebody taps one. Same viewer for both kinds, because a
   Story sequence and a playlist are the same interaction — a thing you move
   through and leave.
/* ── THE VIEWER ───────────────────────────────────────────────────────────
   One viewer for both, because a Story sequence and a playlist are the same
   interaction. Full screen, because that is what both are everywhere else —
   and full screen is this platform's own standard besides. */
/* OPAQUE, NOT NEARLY OPAQUE. At .94 the profile behind it stayed legible —
   high-contrast white type reads through six percent perfectly well — and a
   Story viewer with the page showing through reads as a broken overlay rather
   than as a place you have gone. There is nothing behind this worth seeing
   while it is open; the hue wash keeps it the platform's material rather than
   a flat black sheet. */
.ow-cvw{position:fixed;inset:0;z-index:2600;
  display:grid;grid-template-rows:auto auto 1fr;
  background:
    radial-gradient(120% 80% at 50% -10%,rgba(var(--ow-h1),.13),transparent 68%),
    radial-gradient(110% 70% at 50% 110%,rgba(var(--ow-h2),.09),transparent 66%),
    #08070e;
  animation:ow-cvw-in .28s cubic-bezier(.16,.9,.24,1) both}
@keyframes ow-cvw-in{from{opacity:0}to{opacity:1}}
html[data-viewer="open"]{overflow:hidden}

/* THE PROGRESS BARS SAY HOW MUCH IS LEFT, which is the one thing a Story
   viewer must never leave you guessing about. */
.ow-cvw__bars{grid-row:1;display:flex;gap:4px;
  padding:calc(12px + env(safe-area-inset-top,0px)) 14px 0}
.ow-cvw__bars i{flex:1 1 0;height:2.5px;border-radius:2px;
  background:rgba(255,255,255,.24);transition:background .3s ease}
.ow-cvw__bars i[data-on]{background:rgb(var(--ow-h1))}

.ow-cvw__head{grid-row:2;display:flex;align-items:baseline;gap:9px;
  padding:11px 14px 9px;min-height:18px;
  font-family:var(--ow-font);font-size:12.5px;color:var(--ow-ink-2)}
.ow-cvw__head b{font-weight:600;font-size:13.5px;color:#fff}

.ow-cvw__body{grid-row:3;position:relative;min-height:0;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:14px;padding:0 14px calc(18px + env(safe-area-inset-bottom,0px))}
.ow-cvw__body img,.ow-cvw__body video{
  max-width:100%;max-height:100%;min-height:0;
  border-radius:18px 13px 20px 15px;object-fit:contain;
  box-shadow:0 24px 70px -30px rgba(0,0,0,.9)}
.ow-cvw__words{max-width:640px;text-align:center}
.ow-cvw__words h2{margin:0;font-family:var(--ow-font-display);font-weight:650;
  font-size:19px;line-height:1.28;color:#fff}
.ow-cvw__words p{margin:6px 0 0;font-size:14.5px;line-height:1.6;
  color:rgba(255,255,255,.74)}

.ow-cvw__x{position:absolute;top:calc(14px + env(safe-area-inset-top,0px));
  right:12px;z-index:3;width:36px;height:36px;border:0;border-radius:50%;
  font-size:22px;line-height:1;color:#fff;cursor:pointer;
  background:rgba(255,255,255,.1);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.16)}
.ow-cvw__x:hover{background:rgba(255,255,255,.18)}

/* THE TAP TARGETS ARE INVISIBLE AND THAT IS CORRECT — every Story viewer works
   this way, so drawing arrows over somebody's photo would be explaining a thing
   they already know at the cost of the thing they came to see. Keyboard and
   screen readers get the same two controls by name. */
.ow-cvw__nav{position:absolute;top:0;bottom:0;width:32%;z-index:2;
  border:0;background:none;cursor:pointer;-webkit-tap-highlight-color:transparent}
.ow-cvw__nav--prev{left:0}
.ow-cvw__nav--next{right:0;width:68%}
.ow-cvw__nav:focus-visible{outline:2px solid rgb(var(--ow-h1));outline-offset:-4px}


/* ── NO MAP GROUND ────────────────────────────────────────────────────────
   Shown when no basemap provider is configured, or when the map could not ask.
   Deliberately quiet and deliberately PRESENT: a silent empty map reads as
   broken, and a watermarked one reads as broken AND careless. The places
   ONEWAY actually knows still draw over it, because those are real. */
/* NEITHER CENTRED NOR AT THE FOOT. Dead centre is where the map puts its own
   marker, so the notice landed on top of the very place it was saying still
   shows; anchoring it to the bottom then put it UNDER the events sheet, which
   is worse — an honest state nobody can read is not an honest state. It sits
   in the upper third: clear of the search field above, the pins at centre, and
   the sheet below. */
.ow-map__noground{position:absolute;left:50%;top:30%;transform:translateX(-50%);
  z-index:400;max-width:30ch;text-align:center;pointer-events:none;
  font-family:var(--ow-font);font-size:12px;line-height:1.5;
  color:var(--ow-ink-3);padding:12px 16px;border-radius:12px;
  background:linear-gradient(180deg,rgba(var(--ow-panel-1),.82),rgba(var(--ow-panel-2),.82));
  box-shadow:inset 0 1.5px 1px rgba(255,255,255,.12),inset 0 0 0 1px var(--ow-seam)}


/* THE CONTROL THAT MAKES "keep moving through it" TRUE WITHOUT AUTO-ADVANCING.
   ★ FOUNDER, 2026-08-31 (founder/349 §6). Quiet and full-width: it is the foot
   of the stream, not a call to action, and it disappears the moment the feed
   has genuinely ended. */
.ow-caught__more{display:block;margin:12px auto 0;padding:10px 20px;
  background:none;cursor:pointer;
  border:1px solid rgba(var(--ow-h1),.30);
  border-radius:13px 11px 14px 12px;
  font-family:var(--ow-font);font-weight:600;font-size:13px;
  color:rgba(var(--ow-h2),.95);
  transition:color .15s var(--ow-ease),border-color .15s var(--ow-ease)}
.ow-caught__more:hover{color:var(--ow-ink);border-color:rgba(var(--ow-h1),.55)}
.ow-caught__more:disabled{color:var(--ow-ink-3);cursor:default;
  border-color:rgba(var(--ow-h1),.16)}


/* THE FETCH-AHEAD SENTINEL. Zero height, no paint — it exists so an
   IntersectionObserver has something to watch two screens before the foot.
   ★ FOUNDER/350: *"fetch ahead before the user reaches the end."* */
.ow-river__ahead{height:1px;width:100%;pointer-events:none}

/* THE FOOT IS A STATE. ★ FOUNDER/350, "No false UI state": `caught up` is one
   of four things this can say and it may not be the default. */
.ow-caught[data-state="loading"] b{color:var(--ow-ink-2)}
.ow-caught[data-state="error"] b{color:var(--ow-ink)}
.ow-caught[data-state="error"] span{color:var(--ow-ink-3)}


/* ══ SMALL CHROME KEEPS A REAL TARGET ══════════════════════════════════════
   ★ FOUNDER, 2026-08-30: *"Hit targets remain large (~40-44px)."*
   ★ FOUNDER, 2026-08-31 (founder/351): *"its the little things."*

   MEASURED by `tools/probe/controls.js` on My Center, on every tab, which is
   how it was obvious these belong to the SHELL and not to any one surface:

       .ow-shell__gear   31x31   limited by nothing — simply built small
       .ow-note-set      80x27   limited by a neighbouring control

   THE SAME EXPANDER THE RESPONSE BAR USES, not a second mechanism: an
   invisible centred pseudo-element that grows the TARGET without moving a
   pixel of the design. `z-index:0` with the glyph lifted above it, so it
   catches the finger and never paints over the thing it belongs to.

   The note button's neighbour is what bounds it, so this opens the PITCH as
   well — the founder's own instruction when a target is hemmed in: grow the
   gap, not the box. */
/* ONLY WHAT IS NOT ALREADY POSITIONED. This read
   `.ow-shell__gear,.ow-note-set{position:relative}` and, appended at the end of
   the file, it OVERRODE the gear's own `position:absolute` — knocking it out of
   its upper-right placement into the flow, where half of it sat OFF-SCREEN at
   `left:-16` on a 375px viewport. An expander needs a positioned ancestor;
   `absolute` already is one, so naming the gear here bought nothing and cost
   its position. Found by the same probe that asked for the bigger target, four
   minutes after I made it.

   ★ AND THE NOTE WAS LEFT IN THE RULE — the same defect, half-fixed.
     FOUNDER, 2026-09-10, looking at his own profile. Measured on the Me page:
     `.ow-note-set` computed `position:relative`, so its own
     `top:-40px; left:18px` never applied and "Leave a note" fell into the flow
     AT (36,126) — directly on top of the avatar at (18,100,60,60). The one
     control above the face was sitting ON the face.

     The paragraph above already contains the argument: `.ow-note-set` is
     `position:absolute` at its own rule, which is a positioning context, so
     naming it here bought nothing and cost its position — word for word what
     was true of the gear. Whoever fixed the gear removed the gear and left the
     note, which is why the comment reads as resolved and the bug was still
     shipping. */
.ow-shell__gear::after,.ow-note-set::after{content:"";position:absolute;
  left:50%;top:50%;transform:translate(-50%,-50%);
  width:max(44px,calc(100% + 12px));height:max(44px,calc(100% + 14px));
  z-index:0}
.ow-shell__gear > svg,.ow-note-set > *{position:relative;z-index:1}
/* room for the bigger target, so it meets its neighbour instead of overlapping */
.ow-note-set{margin-right:10px}


/* ═══ 30 · THE OPENING PLATE ════════════════════════════════════════════════
   The object that flies from the tapped card into the surface it opens. It is
   the Popit material and nothing else — no content, no shadow of its own beyond
   the card's, and no pointer surface: everything under it stays live from the
   first frame, so the animation can be skipped or cut at any point and the
   person still has what they tapped.

   `will-change:transform` is declared because this element exists ONLY to be
   transformed and lives for about a third of a second — the case the property
   was actually designed for, rather than the blanket use that costs memory on
   things that never move. */
.ow-openflip{
  position:fixed;z-index:2400;pointer-events:none;
  border-radius:22px 19px 23px 20px;
  will-change:transform,opacity;
  background:
    radial-gradient(130% 80% at 18% -6%,  rgba(var(--h,var(--ow-h1)),.46), transparent 64%),
    radial-gradient(120% 90% at 92% 108%, rgba(var(--h2,var(--ow-h2)),.30), transparent 60%),
    linear-gradient(158deg, rgba(var(--h,var(--ow-h1)),.18),
                            rgba(var(--h3,var(--ow-h3,var(--ow-h2))),.12) 46%,
                            rgba(var(--h2,var(--ow-h2)),.10) 70%, var(--ow-tile-base));
  box-shadow:
    inset 0 2px 1px rgba(255,255,255,.2),
    inset 0 0 0 1.5px rgba(var(--h,var(--ow-h1)),.3),
    0 0 34px -10px rgba(var(--h,var(--ow-h1)),.5),
    0 18px 44px -16px rgba(0,0,0,.66);
}
/* THE SEAM COMES WITH IT. The plate is a Post in flight; a Post has a stitched
   boundary, and dropping it for the length of the animation would be a visible
   costume change at the one moment the eye is locked on the object. */
.ow-openflip::before{
  content:"";position:absolute;inset:4px;border-radius:inherit;pointer-events:none;
  border:1.5px dashed var(--ow-seam,rgba(255,255,255,.24));
  transform:rotate(.35deg);
}
/* Somebody who asked for less motion gets none of this — the surface simply
   appears, which is what `OW.originOpen` already returns early for. This rule
   is the belt to that brace, for any future caller that builds a plate itself. */
html[data-motion="reduced"] .ow-openflip{display:none}
@media (prefers-reduced-motion:reduce){.ow-openflip{display:none}}

/* ═════════════════════════════════════════════════════════════════════════
   §31  LATEST — THE POST, AT TILE SCALE
   ★ FOUNDER, 2026-08-30: *"Use the exact Post material. Don't invent a special
     'widget post card.'"*  and, 2026-08-31: *"not all have to follow exact card
     designs but should use general design motifs."*

   Nothing here restyles a Post. Every rule either SIZES the card for a tile or
   REMOVES a whole part of it, because the one thing forbidden is a part that is
   present and cut:
   ★ FOUNDER, 2026-08-28: *"i dont want to see any cut out or ... text
     anywhere."*
   That is why there is no `line-clamp` and no `text-overflow` in this section.
   A clamp would earn a line of room and pay for it with an ellipsis, which is
   the failure written down, not a way around it.
   ═════════════════════════════════════════════════════════════════════════ */
.ow-cv-posts{position:relative;z-index:1;display:flex;flex-direction:column;
  gap:6px;width:100%;height:100%;padding:9px 10px;overflow:visible;
  container-type:size}

/* the key wears the Popit label motif (§ same as .ow-mem__k) so a Latest is
   recognisably one of the family before you read a word of it */
.ow-cv-posts__k{flex:none;font-size:10px;font-weight:600;letter-spacing:.09em;
  color:var(--ow-h1-ink);line-height:1.35}
.ow-cv-posts[data-dense="s"] .ow-cv-posts__k{font-size:9.5px}

/* ── THE GLOW IS THE POST'S, AND NOTHING HERE MAY CLIP IT ────────────────
   ★ FOUNDER, 2026-09-04: *"don't have the same great glow as normal posts"* —
   and the diagnosis he offered with it was the right one.

   Measured: the shadow VALUES are already identical to a river Post's —
   `0 0 26px -18px` at alpha .3, differing only in hue, which is correct
   because a Post is lit by its own author-and-place blend. The glow was being
   CLIPPED, by three ancestors carrying `overflow:hidden` — this list, the box
   around it, and the tile face. An outer box-shadow belongs to the child, and
   any ancestor that clips takes it away.

   The card's own `contain: content` is NOT the culprit and is left alone: an
   element's own shadow is not one of its descendants, which is why a Post in a
   200-row river keeps its glow while still paying for paint containment.

   So the two INNER clips go and the tile's stays. `.ow-cv-face` is the Popit's
   own edge and still clips everything — the founder's "nothing escapes its
   tile" is untouched — but inside it a Post now lights the surface the way it
   does in the feed. The rung ladder is unaffected: `scrollHeight` reports
   content height whether or not the box clips it. */
.ow-cv-posts__list{flex:1 1 auto;display:flex;flex-direction:column;gap:7px;
  min-height:0;overflow:visible}

/* THE CARD IS THE CARD — AND IT KEEPS ITS OWN MATERIAL.
   ★ FOUNDER (group ruling), 2026-09-02: *"widgets and scroll should look
     EXACTLY EXACTLY like the posts."*

   An earlier version of this rule set `box-shadow:none` and hid
   `.po-card__face`, on the reasoning that the TILE already draws an edge. That
   was the drift, not the fix: the face IS the Post's panel and the glow is
   `0 0 42px -12px rgba(var(--h),.42)` — HUE-TINTED, so a card lit by a Center
   glows in that Center's light. A black shadow, or none, is the tell that a
   thing is not made of Post.

   So the material stays and the DOUBLE FRAME is solved at the other end: the
   tile stops painting its own panel for this kind (below), leaving exactly one
   frame on screen and it is the Post's. Only the river's reserved media height
   is dropped, because that is a scroll-performance hint, not appearance. */
/* AND IT MUST ACTUALLY RENDER. `.po-card` carries
   `content-visibility:auto; contain-intrinsic-size:auto 190px` so a river of
   them can skip work while off-screen. Inside a tile that is not an
   optimisation, it is a lie: the card is clipped by the tile, the browser
   treats it as off-screen, skips rendering it and substitutes the 190px
   PLACEHOLDER. Measured 2026-09-02 — a card whose visible content was 28px
   reported a 190px box, the ladder read a 70px overflow that did not exist,
   descended to its lightest rung to escape it, and the card's text was absent
   from `innerText` entirely because unrendered content has none.
   `.ow-fpos .po-card` already had to do this for Scroll; same reason, same fix. */
.ow-cv-posts .po-card{content-visibility:visible;contain-intrinsic-size:auto;
  flex:0 0 auto;min-height:0}

/* ONE FRAME, AND IT BELONGS TO THE POST. The Popit panel and seam would sit
   around cards that already carry both.

   SPECIFICITY, NAMED SO IT IS NOT LOST AGAIN: `.ow-cv-face` gets its ring from
   a plain class rule AND from a theme-guarded one,
   `html:not([data-theme="dark"]) .ow-cv-face`, which outranks any single class.
   Measured 2026-09-02: `.ow-cv-face--posts{box-shadow:none}` lost to it and the
   tile kept drawing an inset ring around cards that already had a frame. So the
   override carries the tile class twice and answers the theme rule in its own
   terms rather than hoping to be read last. */
/* ── AND IT IS A RESET, NOT A COLOUR — SO IT MUST NOT SPELL A THEME HALF ──
   This carried `html:not([data-theme="dark"])` purely to match the weight of
   the theme-guarded ring rule. But that spelling IS the system half — "nobody
   chose, or chose light" — and `test_architecture_invariants` is right to flag
   it: a system half outside `prefers-color-scheme` means a system-DARK reader
   is silently handed the light branch.

   Here the declaration is identical in every theme, so the rule was never
   making a light choice; it only needed the WEIGHT. Wrapping it in a media
   query would have been the obvious fix and the wrong one — system-dark would
   then lose the reset and the inset ring would come back around cards that
   already have a frame, which is the exact defect the comment above records.

   `html:root` weighs the same (0,3,1), matches every theme state including the
   one where nobody has chosen, and says nothing about light or dark. */
.ow-cv-face.ow-cv-face--posts,
html:root .ow-cv-face.ow-cv-face--posts,
html[data-theme="light"] .ow-cv-face.ow-cv-face--posts,
html[data-theme="dark"] .ow-cv-face.ow-cv-face--posts{
  background:none;box-shadow:none;border:0}
.ow-cv-face.ow-cv-face--posts::before,
.ow-cv-face.ow-cv-face--posts::after{display:none}

/* ── WHOLE PARTS, REMOVED WHOLE ────────────────────────────────────────── */
.ow-cv-posts[data-media="0"] .po-card__media,
.ow-cv-posts[data-media="0"] .po-card__cover,
.ow-cv-posts[data-media="0"] .po-shots{display:none}
.ow-cv-posts[data-body="0"]  .po-card__body,
.ow-cv-posts[data-body="0"]  .po-card__d{display:none}
.ow-cv-posts[data-foot="0"]  .po-card__foot{display:none}
/* THE LAST THING SPENT IS THE BYLINE, NOT THE SENTENCE. Only the final rung
   sets this, and only when one card with its words will not otherwise fit.
   Specificity beats `.po-card > .po-card__who{display:flex}` on purpose. */
.ow-cv-posts[data-who="0"]   .po-card__who{display:none}

/* ── TILE TYPOGRAPHY ───────────────────────────────────────────────────── */
.ow-cv-posts .po-card__av{width:22px;height:22px}
/* NO `margin:0` HERE ANY MORE. I wrote that to tighten the row and it removed
   the Post's own inset: `.po-card__who` carries `margin:14px 14px 10px` in the
   river, and that margin is what holds the avatar clear of the card's dashed
   seam. Measured 2026-09-03 inside a Latest tile — avatar left inset 14px in
   the river, 0px here, so the face sat ON the stitching.
   The base margin is already expressed in `--po-gap`, which this tile lowers
   to 7px, so leaving it alone gives a Post's proportion at a tile's scale
   rather than a number chosen twice. Only the gap between face and name is
   ours. */
.ow-cv-posts .po-card__who{gap:7px}
.ow-cv-posts .po-card__nm{font-size:11.5px}
.ow-cv-posts .po-card__when{font-size:10px}
.ow-cv-posts .po-card__t{font-size:12.5px}
.ow-cv-posts .po-card__body,
/* ── A CLAMP DEFEATS THE LADDER ────────────────────────────────────────────
   ★ FOUNDER: *"i dont want to see any cut out or ... text anywhere."*

   The shared Post description is `-webkit-line-clamp:2`, which is right in a
   feed where a card owns the page width. Inside a Latest tile it was quietly
   catastrophic — and not because two lines is too few.

   POST_RUNGS exists to make a Popit fit by REMOVING WHOLE PARTS: three posts,
   then no media, then two posts, then no foot, then one post, then no body at
   all. It descends while `fits()` says the content overflows the tile.

   A clamped element never overflows. It cuts, and reports that it fits. So the
   ladder stopped descending at the first rung that "fitted", and the tile
   showed "Doors at seven." with the rest of the sentence gone — measured, on
   two cards at once. The mechanism built to prevent cut text was being blinded
   by the mechanism that causes it.

   Unclamped here, the description tells the truth about its height, `fits()`
   sees the overflow, and the ladder drops the body as a whole part — which is
   the honest smaller answer this lane uses everywhere else. */
.ow-cv-posts .po-card__d{font-size:11.5px;line-height:1.4;margin-top:3px;
  -webkit-line-clamp:unset;display:block;overflow:visible;text-overflow:clip}
/* the two rungs above the tally: the newest Post's first lines, cut at a line
   boundary with the ellipsis as the announcement (canvas.js POST_RUNGS) */
.ow-cv-posts[data-clamp] .po-card__d{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;
  -webkit-line-clamp:var(--po-clamp,3)}
.ow-cv-posts[data-clamp="3"]{--po-clamp:3}
.ow-cv-posts[data-clamp="2"]{--po-clamp:2}
/* on those two rungs the tile is the Post's first words and nothing else: the
   "liked by" line (33px in a 116px tile, measured 2026-09-20) and the body's
   river padding go, so the words are what the room is spent on */
.ow-cv-posts[data-clamp] .po-card__known{display:none}
/* with the name row gone the mark would sit on the first line of the words */
.ow-cv-posts[data-who="0"] .po-card > .po-card__from{display:none}
.ow-cv-posts[data-clamp] .po-card__body{padding-top:4px;padding-bottom:4px}
.ow-cv-posts[data-clamp] .po-card[data-text] .po-card__body{margin-top:0}
.ow-cv-posts .po-card__foot{margin-top:3px;font-size:10.5px}
/* a river row is padded for a 600px column; inside a tile that padding is the
   difference between a sentence fitting and the ladder dropping it */
.ow-cv-posts .po-card{--po-gap:7px}
.ow-cv-posts .po-card__count{font-size:10.5px}

/* one post in a one-row tile has room to be slightly larger than one of three */
.ow-cv-posts[data-dense="s"] .po-card__nm{font-size:12px}
.ow-cv-posts[data-dense="xl"] .po-card__body{font-size:12px}

.ow-cv-posts__none{font-size:11px;font-weight:500;letter-spacing:.06em;
  color:var(--ow-ink-3);align-self:flex-start}

/* the face carries the Popit material and its dashed seam already — Latest
   must not draw a second edge inside it */
.ow-cv-face--posts{overflow:hidden}

/* ═══ 27 · SIMPLE MODE — INSTAGRAM/TWITTER DISPLAY, HOME AND SCROLL ═════════
   ★ FOUNDER, 2026-09-02: *"on simple mode we are gonna drop the popit
     presentation and ahve instagram/ twitter display"* — and, clarifying the
     scope in the next breath: *"on home and scroll but only WHEN simple mode
     is on"*.

   EVERY RULE HERE IS KEYED ON `html[data-simple="1"]`. Nothing in this block
   can reach a person who has not turned it on, and nothing above it is edited
   — which is the difference between adding a mode and taking the Popit
   presentation away from everybody. It is also the lesson from the settings
   gear: appending to this file is safe only when the appended rules cannot
   apply to the default world.

   AND IT IS SCOPED TO TWO SURFACES BY HIS OWN WORDS. `.po-river` is Home's
   stream and `.ow-fpos` is Scroll's position. A Center page, a profile and the
   Post surface keep the Popit presentation, because he named two surfaces and
   not "the App".

   WHAT "DROP THE POPIT PRESENTATION" MEANS, CONCRETELY. The Popit is a card
   with a hand-drawn asymmetric radius, a dotted seam, a hue-tinted glow and a
   panel that floats above the ground. Instagram and Twitter have none of
   those: content sits ON the ground, separated by a hairline, and the media
   runs edge to edge. So each of those four is removed rather than restyled. */

html[data-simple="1"] .po-river{gap:0}

/* THE CARD STOPS BEING AN OBJECT AND BECOMES A ROW. No frame, no float, no
   corner. The hairline is the only thing separating one Post from the next —
   Twitter's whole visual model, and the reason its feed reads as one column of
   speech rather than a stack of tiles. */
html[data-simple="1"] .po-river > .po-card,
html[data-simple="1"] .ow-fpos > .po-card{
  border-radius:0;box-shadow:none;background:none;
  border-bottom:1px solid var(--ow-rim);
  padding:14px 0 10px;margin:0}
html[data-simple="1"] .po-river > .po-card:last-child{border-bottom:0}

/* THE SEAM AND THE FACE ARE THE POPIT ITSELF, so in simple mode they are not
   restyled — they are gone. */
html[data-simple="1"] .po-river > .po-card::before,
html[data-simple="1"] .po-river > .po-card::after,
html[data-simple="1"] .ow-fpos > .po-card::before,
html[data-simple="1"] .ow-fpos > .po-card::after,
html[data-simple="1"] .po-river > .po-card > .po-card__face,
html[data-simple="1"] .ow-fpos > .po-card > .po-card__face{display:none}

/* MEDIA RUNS EDGE TO EDGE — the one thing both references share and the one
   thing a framed card cannot do. The negative margin is the card's own gutter
   given back, so the picture reaches the screen edge exactly. */
html[data-simple="1"] .po-card__cover{
  border-radius:0;
  width:calc(100% + 32px);margin-left:-16px;margin-right:-16px}
html[data-simple="1"] .po-shots{border-radius:0}

/* HOVER IS A CARD BEHAVIOUR. A row does not lift. */
@media (hover:hover) and (pointer:fine){
  html[data-simple="1"] .po-river > .po-card:hover,
  html[data-simple="1"] .ow-fpos > .po-card:hover{transform:none;box-shadow:none}
}

/* THE GROUND IS THE GROUND. Both references put content directly on it; a
   panel tint under a row is the Popit reasserting itself one layer down. */
html[data-simple="1"] .po-river > .po-card .po-card__body{background:none}

/* a rung hides whole posts with the `hidden` attribute; `.po-card` sets
   `display:block`, which beats the UA rule for `[hidden]` — so say it here */
.ow-cv-posts .po-card[hidden]{display:none}

/* ── A RIVER ROW'S HORIZONTAL `who` DOES NOT SURVIVE A TILE ──────────────
   Measured 2026-09-02, a 112px Latest on a 280px viewport: `.po-card__who` is
   an inline-flex of a 22px avatar beside the name, which in a 78px card left
   the name a 21px column — so "Loop Test Center 370201" rendered ONE CHARACTER
   PER LINE, 55px tall. That is the text-dropout the founder has called out
   twice, and no rung of the ladder fixes it because nothing about it is a
   question of how many parts are shown.

   The card is not replaced and not restyled into a "widget post card" — this
   only lets the SAME material lay out for the width it actually has: below the
   width where a name can sit beside a face, it sits under it and wraps whole.
   ★ FOUNDER, 2026-08-30: *"It should literally be a projection of the same Post
     object."* — a projection may change shape; it may not change identity. */
/* THE FIX IS `min-width`, NOT A STACK. The 21px name column was a flex default
   — a flex child's `min-width` is `auto`, so the name could be squeezed below
   its own content by the avatar beside it. Stacking the avatar above the name
   also cured it, and cost 66px of a tile that only had 86: the name wrapped to
   two lines UNDER a 20px face, and the sentence the post actually contains got
   dropped by the ladder to make room for the arrangement. Measured 2026-09-02.
   So the row stays a row, the name wraps inside it, and `who` costs ~32px. */
@container (max-width: 210px){
  /* THE ROW HAS TO FILL THE CARD BEFORE ANY OF THIS MEANS ANYTHING.
     `.po-card__who` is an inline-flex sized to its content, and `.po-card__n`
     is `flex:0 1 auto` — it may shrink, it may not grow. Measured 2026-09-02 on
     a 98px tile: card 78px, row 56px, name column 30px, and the name wrapped
     MID-WORD into an 86px stack reading "Loop / Test / Cent / er / 3702 / 01".
     Neither `min-width:0` nor stacking addresses that, because the row was
     never asking for the width in the first place. */
  .ow-cv-posts .po-card__who{display:flex;width:100%;align-items:flex-start;gap:6px}
  .ow-cv-posts .po-card__n{flex:1 1 auto}
  /* AND WORDS DO NOT BREAK. `break-word` was mine, and it turns a cramped name
     into "Cent / er" — which is the cut text the founder has forbidden twice,
     just performed by the line-breaker instead of by an ellipsis. Whole words,
     wrapped at the spaces, or nothing. */
  .ow-cv-posts .po-card__nm{white-space:normal;overflow-wrap:normal;
    word-break:normal;line-height:1.25}
  .ow-cv-posts .po-card__av{width:20px;height:20px}
}
/* BELOW THE WIDTH WHERE A FACE AND A NAME CAN SHARE A LINE, THE NAME WINS.
   A 20px avatar plus its gap is a third of a 78px card. The avatar is a
   decoration of the identity; the name IS the identity, so at this size the
   decoration goes and the name gets the whole width in whole words. */
@container (max-width: 132px){
  .ow-cv-posts .po-card__av{display:none}
  .ow-cv-posts .po-card__nm{font-size:11px}
  .ow-cv-posts .po-card__when{font-size:9.5px}
}
/* flex children default to min-width:auto, which is what let the name be
   squeezed below its own content in the first place */
.ow-cv-posts .po-card__n,
.ow-cv-posts .po-card__nm,
.ow-cv-posts .po-card__body{min-width:0}

/* ── SIMPLE MODE ────────────────────────────────────────────────────────────
   ★ FOUNDER, 2026-09-02: *"on simple mode we are gonna drop the popit
     presentation and ahve instagram/ twitter display … on home and scroll but
     only WHEN simple mode is on."*

   A's switch is `html[data-simple="1"]`, one writer, applied before paint. A
   Latest that kept its frame here would float inside a flat feed, so the tile
   surrenders its padding and the cards surrender their panel — the same
   treatment Home and Scroll take, keyed on the same attribute so the default
   is untouched. The separator is a hairline between rows, never a box. */
html[data-simple="1"] .ow-cv-posts{padding:0;gap:0}
html[data-simple="1"] .ow-cv-posts__k{padding:8px 12px 6px}
html[data-simple="1"] .ow-cv-posts__list{gap:0}
/* THE PADDING IS THE RIVER'S, TO THE PIXEL. Measured 2026-09-02 in simple
   mode: a river Post pads `14px 0 10px` — no horizontal inset at all, because
   the row IS the page width — and this was padding `10px 12px`, which put the
   widget's text on a different left edge from every Post beneath it. Radius and
   shadow already matched; the indent was the one thing that would have given it
   away as not-a-Post in a flat feed. */
html[data-simple="1"] .ow-cv-posts .po-card{border-radius:0;box-shadow:none;
  padding:14px 0 10px;border-top:1px solid var(--ow-hair,rgba(255,255,255,.10))}
html[data-simple="1"] .ow-cv-posts .po-card:first-child{border-top:0}
html[data-simple="1"] .ow-cv-posts .po-card__face{display:none}
/* and with no horizontal padding left to cancel, media is simply full width */
html[data-simple="1"] .ow-cv-posts .po-card__media,
html[data-simple="1"] .ow-cv-posts .po-card__cover{border-radius:0;
  margin-left:0;margin-right:0;width:100%}

/* ── APPEARANCE: LIGHT · DARK · DEVICE ─────────────────────────────────────
   ★ FOUNDER, 2026-09-02: *"add a device setting for dark mode."*

   A SEGMENT, NOT THREE SWITCHES. The three are mutually exclusive and always
   have been — a row of switches would let a person turn two on and would have
   to invent what that means. The pressed one is the only one that is filled,
   which is the same "one answer" grammar the reaction controls already use. */
.ow-seg{display:inline-flex;gap:2px;padding:3px;margin-top:8px;
  border-radius:14px 11px 15px 12px;
  background:linear-gradient(178deg,var(--ow-panel-1),var(--ow-panel-2));
  box-shadow:inset 0 1px 0 var(--ow-lift),inset 0 0 0 1px var(--ow-rim)}
.ow-seg__b{border:0;cursor:pointer;background:none;color:var(--ow-ink-2);
  min-height:34px;padding:0 14px;border-radius:11px 9px 12px 10px;
  font-family:var(--ow-font-display);font-weight:600;font-size:13px;
  transition:background .16s var(--ow-ease),color .16s var(--ow-ease)}
.ow-seg__b[data-act="on"]{color:#fff;
  background:linear-gradient(150deg,rgba(var(--ow-h1),.95),rgba(var(--ow-h2),.78));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.35)}
.ow-seg__b:focus-visible{outline:2px solid rgba(var(--ow-h1),.9);outline-offset:2px}
@media (hover:hover) and (pointer:fine){
  .ow-seg__b:not([data-act="on"]):hover{background:rgba(var(--ow-h1),.12)}
}
/* what "Device" currently resolves to — knowable, so it is said */
.ow-seg__note{font-size:12px;line-height:1.35;color:var(--ow-ink-3);margin:6px 0 0}
.ow-seg__note:empty{display:none}

/* ── WHERE A CENTER IS, AND THE WAY THERE ─────────────────────────────────
   ★ FOUNDER, 2026-09-02: bind Center to location for those that have one,
     with simple transit now and full navigation later.

   IT HAS NO EMPTY STATE ON PURPOSE. 116 of 196 Centers resolve no location, and
   for those this element is never created — *"these hollow projections of any
   popit and look like placeholders are gonna be systematically eliminated."*
   There is nothing here to style for absence, which is the point. */
.ow-where{flex:1 1 100%;display:flex;flex-wrap:wrap;align-items:center;gap:10px;
  margin-top:8px}
.ow-where__line{display:flex;flex-direction:column;min-width:0;flex:1 1 auto}
.ow-where__place{font-family:var(--ow-font);font-weight:560;font-size:13px;
  color:var(--ow-ink-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* the local time is what makes it read as BOUND rather than labelled */
.ow-where__time{font-family:var(--ow-font-atmos);font-weight:600;font-size:8.5px;
  letter-spacing:.01em;color:var(--ow-ink-3);margin-top:2px}
.ow-where__go{flex:none;text-decoration:none}

/* the named absence on an unbound Popit — quiet, because it is a true statement
   about this tile and not an error about the platform */
.ow-cv-none{display:block;margin-top:6px;font-size:9.5px;font-weight:600;
  letter-spacing:.01em;color:var(--ow-ink-3,rgba(255,255,255,.42))}

/* a tile still reading says so quietly — it is a statement about this moment,
   not about the Popit, so it must not carry the weight of an empty state */
.ow-cv-face.is-loading .ow-cv-none{opacity:.62}

/* ── REPORTING ────────────────────────────────────────────────────────────
   ★ FOUNDER, 2026-09-02, on Block and the same class of gap: a safety control
     the backend enforces and no person can invoke.

   IT IS A QUIET SURFACE ON PURPOSE. Reporting is a deliberate act taken about
   somebody else; it should not look like a promotion or feel like a game. No
   hue fill, no lift, no elevation — the same material as the panel it opens in
   and nothing more. */
.ow-report{flex:1 1 100%;margin-top:8px;padding:10px;
  border-radius:14px 11px 15px 12px;
  background:linear-gradient(178deg,var(--ow-panel-1),var(--ow-panel-2));
  box-shadow:inset 0 1px 0 var(--ow-lift),inset 0 0 0 1px var(--ow-rim)}
.ow-report__q{font-family:var(--ow-font-display);font-weight:600;font-size:13.5px;
  color:var(--ow-ink);margin:0 0 8px}
.ow-report__list{display:flex;flex-direction:column;gap:2px}
.ow-report__r{text-align:left;border:0;cursor:pointer;background:none;
  color:var(--ow-ink-2);min-height:36px;padding:0 10px;
  border-radius:10px 8px 11px 9px;
  font-family:var(--ow-font);font-weight:500;font-size:13px}
.ow-report__r:hover:not(:disabled){background:rgba(var(--ow-h1),.12);color:var(--ow-ink)}
.ow-report__r:disabled{opacity:.45;cursor:default}
.ow-report__r:focus-visible{outline:2px solid rgba(var(--ow-h1),.9);outline-offset:-2px}
/* the two outcomes, and neither pretends */
.ow-report__ok{font-size:12.5px;line-height:1.4;color:var(--ow-ink-2);margin:0}
.ow-report__no{font-size:12.5px;line-height:1.4;color:rgba(255,150,90,.95);margin:0}

/* ── A COMMUNITY NOTE ──────────────────────────────────────────────────────
   FOUR JUDGEMENTS, KEPT VISIBLY SEPARATE, because the contract keeps them
   separate and the whole design rests on it: what its author claims · what the
   community said · what a model said · what the platform shows. A note that
   blended them would read as the platform asserting a fact, and `status` is
   explicitly NOT a truth claim — it says whether people found it useful.

   SO IT IS NOT STYLED AS AN AUTHORITY. No seal, no tick, no green. It is a
   panel like any other, because the moment it looks official the design is
   claiming something the data does not support. */
.ow-notes{display:flex;flex-direction:column;gap:8px;margin:14px 0 4px}
.ow-note{padding:12px;border-radius:16px 13px 17px 14px;
  background:linear-gradient(178deg,var(--ow-panel-1),var(--ow-panel-2));
  box-shadow:inset 0 1px 0 var(--ow-lift),inset 0 0 0 1px var(--ow-rim)}
.ow-note__kind{display:inline-block;font-family:var(--ow-font-atmos);font-weight:600;
  font-size:12px;letter-spacing:.01em;
  color:var(--ow-ink-3);margin-bottom:5px}
.ow-note__b{font-size:14px;line-height:1.45;color:var(--ow-ink);margin:0}
.ow-note__src{display:inline-block;font-size:12.5px;margin-top:6px;
  color:rgba(var(--ow-h2),.95);text-decoration:none;
  overflow:hidden;text-overflow:ellipsis;max-width:100%;white-space:nowrap}
.ow-note__src:hover{text-decoration:underline}
/* A MODEL'S BLOCK IS QUIETER THAN THE COMMUNITY'S, deliberately — it is one
   more assessment, not an adjudication. */
.ow-note__ai{font-size:12px;line-height:1.4;color:var(--ow-ink-3);margin:8px 0 0}
.ow-note__ai b{font-weight:600;color:var(--ow-ink-2)}
.ow-note__vote{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}
.ow-note__v{display:inline-flex;align-items:center;gap:5px;min-height:32px;
  border:0;cursor:pointer;padding:0 12px;border-radius:11px 9px 12px 10px;
  font-family:var(--ow-font);font-weight:560;font-size:12.5px;color:var(--ow-ink-2);
  background:rgba(0,0,0,.07);box-shadow:inset 0 0 0 1px var(--ow-rim)}
.ow-note__v[aria-pressed="true"]{color:var(--ow-ink);
  box-shadow:inset 0 0 0 1.5px rgba(var(--ow-h1),.85)}
.ow-note__v:disabled{opacity:.5;cursor:default}
.ow-note__v b{font-family:var(--ow-font);font-weight:600;font-size:11px}
/* WHY IT IS ON SCREEN — the platform's derived answer, in the platform's own
   quiet voice. Never a verdict about the Post. */
.ow-note__why{font-size:12px;line-height:1.4;color:var(--ow-ink-3);margin:8px 0 0}

/* ── SEND THIS TO SOMEBODY ────────────────────────────────────────────────
   The ranking arrives WITH ITS REASONS and they are rendered verbatim, so a
   row can be argued with rather than trusted. An unavailable destination is
   SHOWN with its reason rather than hidden — a conversation silently missing
   from the list reads as a bug, and "you blocked each other" reads as an
   answer. */
.ow-send-to{margin-top:10px}
.ow-sendto{display:flex;flex-direction:column;gap:2px;margin-top:8px}
.ow-sendto:empty{display:none}
.ow-sendto__d{display:flex;flex-direction:column;gap:1px;text-align:left;
  border:0;cursor:pointer;background:none;min-height:44px;padding:7px 10px;
  border-radius:12px 10px 13px 11px;color:var(--ow-ink)}
.ow-sendto__d:hover:not(:disabled){background:rgba(var(--ow-h1),.12)}
.ow-sendto__d:disabled{opacity:.55;cursor:default}
.ow-sendto__d:focus-visible{outline:2px solid rgba(var(--ow-h1),.9);outline-offset:-2px}
.ow-sendto__who{font-family:var(--ow-font-display);font-weight:600;font-size:13.5px}
.ow-sendto__why{font-size:11.5px;line-height:1.3;color:var(--ow-ink-3)}
/* THE HANDLE, SHOWN ONLY WHEN TWO DESTINATIONS SHARE A NAME. It is the
   thing that is actually theirs alone, and this is the sheet where
   somebody picks who receives a private photograph. Monospaced digits so
   two similar handles are compared character by character rather than by
   overall shape. */
.ow-sendto__id{display:block;font-family:var(--ow-font-atmos);font-weight:600;
  font-size:10.5px;letter-spacing:.07em;color:rgba(var(--h),.92);
  font-variant-numeric:tabular-nums;margin-top:1px}
.ow-sendto__no{font-size:11.5px;color:rgba(255,150,90,.95)}
.ow-sendto__ok{font-size:12.5px;color:var(--ow-ink-2);margin:6px 0 0}
.ow-sendto__failed{font-size:12.5px;color:rgba(255,150,90,.95);margin:6px 0 0}

/* ═════════════════════════════════════════════════════════════════════════
   §32  WHAT'S HAPPENING — WHAT IS ON, AND NEVER WHEN
   Same motifs as Latest (§31): the family's label, a list that removes whole
   parts rather than cutting them, and no `line-clamp` or `text-overflow`
   anywhere in the section.
   ═════════════════════════════════════════════════════════════════════════ */
.ow-cv-hap{position:relative;z-index:1;display:flex;flex-direction:column;
  gap:6px;width:100%;height:100%;padding:9px 10px;overflow:hidden;
  container-type:size}
.ow-cv-hap__k{flex:none;font-size:10px;font-weight:600;letter-spacing:.09em;
  color:var(--ow-h1-ink);line-height:1.35}
.ow-cv-hap__list{flex:1 1 auto;display:flex;flex-direction:column;gap:8px;
  min-height:0;overflow:hidden}
.ow-cv-hap__e{display:flex;flex-direction:column;gap:2px;width:100%;
  text-align:left;border:0;padding:0;background:none;font:inherit;
  color:inherit;cursor:pointer}
.ow-cv-hap__e[hidden]{display:none}
.ow-cv-hap__e:not(button){cursor:default}
.ow-cv-hap__t{font-size:12.5px;font-weight:560;line-height:1.3;
  color:var(--ow-ink)}
.ow-cv-hap__p{font-size:11px;line-height:1.3;color:var(--ow-ink-2,rgba(255,255,255,.7))}
.ow-cv-hap__n{font-size:11px;line-height:1.35;color:var(--ow-ink-3,rgba(255,255,255,.5))}
.ow-cv-hap[data-place="0"] .ow-cv-hap__p{display:none}
.ow-cv-hap[data-note="0"] .ow-cv-hap__n{display:none}
.ow-cv-hap__none{font-size:11px;font-weight:500;letter-spacing:.06em;
  color:var(--ow-ink-3);align-self:flex-start}
.ow-cv-hap__e:focus-visible{outline:2px solid rgba(var(--ow-h1),.95);
  outline-offset:3px;border-radius:6px}

/* ═════════════════════════════════════════════════════════════════════════
   §33  NEXT — the countdown, and the honest absence of one
   ★ FOUNDER: "If the start time isn't known: BOARDWALK CONCERT / TIME NOT
     ANNOUNCED. Never invent a countdown."
   So the WHEN line has two appearances and they are deliberately different
   weights: a known time leads in the Center's own light, an unknown one is
   stated quietly and does not pretend to be a number.
   ═════════════════════════════════════════════════════════════════════════ */
.ow-cv-next{position:relative;z-index:1;display:flex;flex-direction:column;
  gap:5px;width:100%;height:100%;padding:9px 10px;overflow:hidden;
  container-type:size}
.ow-cv-next__k{flex:none;font-size:10px;font-weight:600;letter-spacing:.09em;
  color:var(--ow-h1-ink);line-height:1.35}
.ow-cv-next__b{flex:1 1 auto;display:flex;min-height:0;overflow:hidden}
.ow-cv-next__e{display:flex;flex-direction:column;gap:2px;width:100%;
  text-align:left;border:0;padding:0;background:none;font:inherit;color:inherit;
  cursor:pointer}
.ow-cv-next__e:not(button){cursor:default}
/* ── A PANEL IS A LIGHTER GROUND THAN THE PAGE, SO IT TAKES A STRONGER INK ─
   ★ MEASURED 2026-09-12 by tools/probe/contrast.js across dark, light and
     device at four canvas widths: `--ow-h1-ink` — the accent PREPARED to be
     read — still failed on a Popit panel. "STARTS IN 2 HOURS" at 4.47:1 and
     "AND 2 MORE" at 4.43:1, both a hair under the 4.5 small text needs, and
     both only at the wider tiles.

   THE TOKEN IS CALIBRATED FOR THE PAGE GROUND. A Popit panel is lighter than
   the page it sits on — that is what the glow IS — so an ink with just enough
   contrast against the page has just too little against the panel, and the
   wider the tile the more of the bright part of the glow sits under the words.

   SO THE PANEL CHOOSES ITS OWN INK, which is this lane's standing rule rather
   than a new one: ink and ground are chosen together or not at all. The tile
   captures the outer value into `--ow-h1-ink-src` and the face redefines
   `--ow-h1-ink` from it — a different property name, so there is no cycle, and
   the capture happens on an ancestor where the outer value is still in force.

   MIXED TOWARD `--ow-ink` RATHER THAN TOWARD A LITERAL, and that is the whole
   reason this is four lines instead of eight theme blocks: `--ow-ink` is the
   theme's own readable text colour, so the same declaration lightens the
   accent in dark and darkens it in light. It carries the hue at 76% — still
   plainly the Centre's colour, and legible on the material the Centre's
   colour is painted on. Measured after: 0 contrast findings on profile and
   Centre, in all three theme states, at 320 / 560 / 900 / 1120. */
.ow-cv-t,.po-stage{--ow-h1-ink-src:var(--ow-h1-ink);--ow-h2-ink-src:var(--ow-h2-ink)}
.ow-cv-t .ow-cv-face--po,
.po-stage .po-sheet{
  --ow-h1-ink:color-mix(in oklab,var(--ow-h1-ink-src) 76%,var(--ow-ink) 24%);
  --ow-h2-ink:color-mix(in oklab,var(--ow-h2-ink-src) 76%,var(--ow-ink) 24%);
}
/* AND THE OPENED SHEET IS THE SAME GROUND AT FULL SIZE. Measured 2026-09-12
   on an opened Clock: the fact-table key "Face" at 4.39:1 — the identical
   near-miss the tile had, for the identical reason, on a panel that is now
   the whole screen. `.po-stage` is the sheet's own parent, which is where the
   outer value can still be captured without a cycle. */

/* ── THE RAW HUE IS NOT INK ──────────────────────────────────────────────
   ★ MEASURED 2026-09-12 by tools/probe/contrast.js, on a real Centre, in BOTH
     themes: "STARTS IN 2 HOURS" at 3.09:1 in dark and 3.25:1 in light, where
     13px at weight 750 is small text and needs 4.5.

   `rgb(var(--ow-h1))` is the Centre's hue at full saturation. `--ow-h1-ink` is
   the same hue prepared to be READ — it carries a per-theme lightness clamp
   for exactly this. The declaration reached for the colour and not the ink,
   which is this lane's own standing rule broken in the one place a person is
   being told when something starts: ink and ground are chosen together or not
   at all. Measured after: passes in both themes. */
.ow-cv-next__w{font-size:13px;font-weight:600;letter-spacing:.06em;
  line-height:1.25;color:var(--ow-h1-ink)}
/* an unknown time is a fact, not a headline */
.ow-cv-next__w[data-known="0"]{font-size:10px;font-weight:560;letter-spacing:.08em;
  color:var(--ow-ink-3,rgba(255,255,255,.5))}
/* the same correction for the live state — `--ow-h2` has no prepared ink of
   its own, so the one that does is the one that is legible */
.ow-cv-next__w[data-now="1"]{color:var(--ow-h1-ink)}
.ow-cv-next__t{font-size:13px;font-weight:560;line-height:1.3;color:var(--ow-ink)}
.ow-cv-next__p{font-size:11px;line-height:1.3;color:var(--ow-ink-2,rgba(255,255,255,.7))}
.ow-cv-next__none{font-size:11px;font-weight:500;letter-spacing:.06em;
  color:var(--ow-ink-3);align-self:flex-start}
.ow-cv-next__e:focus-visible{outline:2px solid rgba(var(--ow-h1),.95);
  outline-offset:3px;border-radius:6px}
/* one frame, and it is the tile's — same reasoning as §31 */
@container (max-width: 200px){
  .ow-cv-next__t{font-size:12px}
  .ow-cv-next__p{display:none}
}

/* §34  CONNECTED LOCATION — where this Center is, and what time it is there */
.ow-cv-where{position:relative;z-index:1;display:flex;flex-direction:column;
  gap:5px;width:100%;height:100%;padding:9px 10px;overflow:hidden;
  container-type:size}
.ow-cv-where__k{flex:none;font-size:10px;font-weight:600;letter-spacing:.09em;
  color:rgb(var(--ow-h1));line-height:1.35}
.ow-cv-where__b{flex:1 1 auto;display:flex;flex-direction:column;gap:2px;
  min-height:0;overflow:hidden}
.ow-cv-where__city{font-size:15px;font-weight:600;line-height:1.25;
  color:var(--ow-ink)}
.ow-cv-where__rest{font-size:11px;line-height:1.3;
  color:var(--ow-ink-2,rgba(255,255,255,.7))}
.ow-cv-where__t{margin-top:2px;font-size:12px;font-weight:560;
  color:rgb(var(--ow-h1))}
.ow-cv-where__none{font-size:11px;font-weight:500;letter-spacing:.06em;
  color:var(--ow-ink-3);align-self:flex-start}
/* the country is the first thing to go when the tile is small: a city and a
   region already say where, and nothing is ever cut to make room */
@container (max-height: 74px){ .ow-cv-where__rest{display:none} }
@container (max-width: 150px){ .ow-cv-where__city{font-size:13px} }

/* ═════════════════════════════════════════════════════════════════════════
   §35  THE SEAM BETWEEN YOUR POPITS AND YOUR FEED
   ★ FOUNDER, 2026-09-03: *"people should be able to scroll past their popit
     blocks at top of home in one swipe should feel like a different part of
     the app."*

   Measured before writing this: Home is ONE document scroll carrying an
   `.ow-home-canvas` 1905px tall, then `.ow-feedswitch`, then a 3795px
   `.po-river`. Two and a half screens of Popits with no boundary — you arrive
   at the feed by grinding through the arrangement, and nothing tells you that
   you have crossed into a different thing.

   SNAP POINTS, NOT A REWRITE. The page becomes a snap container with exactly
   TWO stops: the top of the canvas and the top of the feed. A decisive swipe
   anywhere in the Popit block settles on the feed rather than halfway through
   somebody's arrangement, and a swipe back settles on the top of the canvas.
   Nothing inside the feed snaps — the river stays an ordinary scroll, because
   a feed that snapped every card would be a carousel.

   `proximity`, NOT `mandatory`: with only two stops on a 5,700px page,
   mandatory would keep hauling a reader back to a stop they had deliberately
   scrolled away from. Proximity snaps the gesture that is already heading
   there and leaves every other one alone.
   ═════════════════════════════════════════════════════════════════════════ */
/* ── IT PAGES; IT DOES NOT BOUND ─────────────────────────────────────────
   I tried bounding the block to one screen with its own scroll, and it was
   wrong twice over. It CLIPPED a Popit mid-tile at the region's edge — the cut
   content the founder has forbidden — and, worse, an inner scroller is the one
   thing that makes it impossible to "scroll past in one swipe": a swipe inside
   the block scrolls the block, so a reader must first reach the end of the
   arrangement before the page moves at all. `overscroll-behavior: contain`
   made that stricter still. Measured: 1905px of arrangement in a 378px window.

   So the page keeps ONE scroll and gains TWO STOPS — the top of the
   arrangement and the top of the feed — with `mandatory`. Mandatory is what
   makes it a swipe rather than a journey: the scroller must come to rest on a
   stop, so a decisive gesture from the Popits lands on the feed however tall
   the arrangement is, and a gesture back lands at the top of it. Nothing is
   clipped, nobody's layout is truncated, and the feed itself keeps ordinary
   free scrolling because nothing inside `.po-river` is a stop.

   THE COST, NAMED: a reader cannot rest halfway down a tall arrangement — the
   block is one page now. That is the trade the ruling asks for ("one swipe",
   "a different part of the app"), and it is the reason `proximity` was not
   enough: proximity only engages once you are already most of the way there,
   which is the grind the ruling is about. */
/* the switch is the top of the feed as far as a reader is concerned — it is
   the first thing that belongs to the river rather than to the arrangement */
/* ★ FOUNDER, 2026-09-03: *"natural swipe like on scroll not on feed only
     exception."*  So the values are SCROLL'S OWN, not a second set invented
   here: `.ow-fscroll` pages with `scroll-snap-type:y mandatory` and each page
   `scroll-snap-align:start`. Home's arrangement is one page by exactly that
   rule, and the feed is the named exception — nothing inside `.po-river` is a
   stop, so once you are in it you are in an ordinary scroll. */
/* NO `scroll-snap` RULES HERE, and that is a measurement rather than a
   preference. Both `proximity` and `mandatory` were tried with the arrangement
   and the feed as the two stops, and neither pages past the block: a snap area
   LARGER THAN THE SNAPPORT does not force alignment, so a 1905px arrangement in
   a 526px viewport is one oversized area the reader may rest anywhere inside.
   Leaving the rules in would be a stylesheet asserting a behaviour it does not
   produce. The gesture lives in `canvas.js` (`homeSwipePast`); what remains
   here is the part CSS can honestly do — the visible seam. */
.ow-feedswitch{scroll-margin-top:8px}

/* AND THE SEAM IS VISIBLE, because a boundary you cannot see is not a boundary.
   A hairline and a change of ground under the feed switch: the arrangement is
   yours and sits on the world; the feed is everyone's and sits on a surface. */
/* THE SEAM HANGS ON THE FEED, NOT ON THE SWITCH. `.ow-feedswitch` wraps only
   the two pills — measured 2026-09-03, it is about 100px wide — so a rule of
   `min(420px, 72%)` resolved to an 85px line sitting under one corner of the
   page. A boundary that spans a tenth of the screen is not a boundary.

   `.po-river` is the feed and is full width, so its top edge is the actual
   line between the two parts of the app, and the seam belongs there. */
.ow-feedswitch{position:relative;margin-top:10px}
.po-river{position:relative;padding-top:16px}
.po-river::before{content:"";position:absolute;left:50%;top:0;
  transform:translateX(-50%);width:min(520px,86%);height:1px;pointer-events:none;
  background:linear-gradient(90deg,transparent,
    rgba(var(--ow-h1),.34) 18%,rgba(var(--ow-h1),.34) 82%,transparent)}

/* ── AND THE TWO LINES ABOVE WERE SILENTLY UNDOING FULLSCREEN SCROLL ──────
   `.ow-fscroll` makes the river a FIXED, viewport-sized pager — that is the
   whole of Scroll. `.po-river` here sets `position:relative` so the seam's
   `::before` has something to hang on. Same specificity, declared four
   thousand lines LATER, so it won.

   WHAT THAT DID, MEASURED: Scroll's container went back to `position:relative`,
   and `height:100%` on a relative block with no sized ancestor resolved to the
   CSS ceiling — **16,777,216px**. Every card was laid out sixteen million
   pixels below the fold. Tapping Scroll showed an EMPTY SCREEN with a complete
   DOM behind it: 33 children, 269 controls, all of them off-world.

   Nothing errored. Nothing logged. The feature was simply gone, and it looked
   like a blank page rather than like a bug — which is why a suite that asserts
   on the DOM would have passed it happily.

   SPECIFICITY, NOT ORDER. `.po-river.ow-fscroll` beats any single-class rule
   wherever it is declared, so this cannot be re-broken by somebody adding a
   `.po-river` rule below. The seam is a FEED decoration and has no business in
   a fullscreen pager, so it goes too. */
.po-river.ow-fscroll{position:fixed;height:100%;padding-top:0;
  /* ── SCROLL ANCHORING WAS MOVING THE PAGER OFF ITS FIRST PAGE ──────────
     The pages are built INTO this container after it exists, and the browser's
     scroll anchoring compensates for content inserted above the current
     position by adjusting `scrollTop` to keep what you can see steady. In an
     ordinary list that is exactly right. In a snap PAGER it is a bug: the
     container arrived at page 0 and was carried to 4,061px — the end card,
     "More from your world. Keep scrolling." — so Scroll opened as though the
     person had already read everything they had just asked for.

     The tell was that the offset CHANGED with the amount of content (5,685 ->
     4,873 -> 4,061 across three builds) rather than being one remembered
     number. A remembered scroll is constant; anchoring tracks the layout.
     Resetting `scrollTop` in JS could not win, because the anchoring happens
     after each insertion. */
  overflow-anchor:none}
.po-river.ow-fscroll::before{content:none}


/* §36  COPY THIS LAYOUT — the confirm step, in the person's own terms */
/* ── THE COPY SURFACE'S TEXT, AND NOTHING ELSE ─────────────────────────────
   `.ow-cv-copysheet` and its seven children used to live here: a 380px card
   centred on a blurred scrim, painted rgba(12,13,26,.96) over rgba(6,7,16,.72).
   Two faults in one block. It was the centred modal the standing rule forbids —
   *"FULL-SCREEN, NEVER A BOX"* — and both of those colours are literals, so on
   the light theme it opened as a dark slab over a pale app.

   Both are gone rather than patched. The surface now wears `.ow-cv-pick`, the
   full-screen Add sheet, which is already tokenised and which a person has
   already met; its rows carry the destinations. What is left is the two lines
   of prose that sheet does not have a class for. */
.ow-cv-copy__p{margin:2px 0 0;font-size:13.5px;line-height:1.45;
  color:var(--ow-ink-2)}
.ow-cv-copy__n{margin:8px 0 4px;font-size:12.5px;line-height:1.4;
  color:var(--ow-ink-3)}

.ow-cv-copy{margin-top:10px}

/* ── UNDER ONE PLANE, THE SMALLEST TILES MUST STILL SAY SOMETHING WHOLE ────
   ★ FOUNDER, 2026-09-03: *"one universal plane sizes of popits and order
     shouldnt change whatsoever whenever reviewed upon."*

   The plane is now universal, so a 6-wide Popit is 152px on a 360px phone
   rather than being reflowed to full width. That is the intended trade, and it
   pushed two of my own widgets past their last rung: measured at 360px, Next's
   absence line clipped by 22px and Latest's card by 17px — clipped INSIDE the
   tile, so no bounds check saw them.

   Both take the same treatment the ladders already use: at the width where the
   full phrase cannot be whole, say the shorter true thing. "Nothing yet" is
   the platform's own absence wording and the harness recognises it, so the
   sentence gets shorter without the vocabulary changing. */
@container (max-width: 190px){
  .ow-cv-next__none,
  .ow-cv-hap__none,
  .ow-cv-posts__none{font-size:10px;letter-spacing:.05em;line-height:1.25}
  /* the second half of "nothing yet in your world" is the part that does not
     fit; the first half is the whole statement */
  .ow-cv-next__none span,
  .ow-cv-hap__none span{display:none}
}
/* and Latest gives up its timestamp before it gives up the person */
@container (max-width: 175px){
  .ow-cv-posts .po-card__when{display:none}
}

/* ── A SHORT TILE SPENDS ITS SPACE ON CONTENT, NOT ON MARGINS ─────────────
   The inset is `--po-seam + --po-gap` = 14px, which is a Post's breathing room
   and correct at a Post's size. Under one universal plane a 6x2 Popit is about
   152x70px on a 360px phone, and 14px top and bottom is 28px of a 70px tile —
   forty percent of it — which is what was still clipping Next and Latest by a
   few pixels after their ladders had already dropped everything they could.

   So the gap gives way before the content does. The seam is never touched:
   the stitching is the object's edge and it keeps its 4px at every size. This
   is the same principle as the density ladders, applied to space rather than
   to parts — at a size where something must go, it is never the words. */
@container (max-height: 96px){
  .ow-cv-next,.ow-cv-hap,.ow-cv-posts{
    padding-top:calc(var(--po-seam,4px) + 3px);
    padding-bottom:calc(var(--po-seam,4px) + 3px)}
}
@container (max-height: 76px){
  .ow-cv-next,.ow-cv-hap,.ow-cv-posts{gap:3px}
  .ow-cv-next__k,.ow-cv-hap__k,.ow-cv-posts__k{font-size:9px;line-height:1.2}
  /* the last two pixels. A Post's `who` carries a 10px bottom margin, which is
     the space before a body that a tile this short is not showing anyway —
     measured, it was the whole of the remaining 2px clip on Latest at 360px.
     Trimming a margin under a line nothing follows costs nothing; clipping the
     line does. */
  .ow-cv-posts .po-card__who{margin-bottom:2px}
}

/* ── AND IN THE NARROWEST TILE, THE WORDS STILL FIT WHOLE ─────────────────
   Under one universal plane a 3-wide Popit is about 71px on a 360px phone —
   45px of content once the seam and gap are taken — and two things did not fit
   in it. Measured: "NOTHING HERE YET" wanted 63px, and a clock's "10:17 PM"
   wanted 50px. Neither was truncated, because nothing here truncates any more;
   both simply drew past their box, which is the same fault wearing a different
   costume.

   Type gives way before words do. The absence line and the time both step down
   at the width where they stop fitting, and a clock keeps its time at ANY size
   because a clock that cannot show the time has no reason to be on the plane. */
@container (max-width: 118px){
  .ow-cv-face--po .ow-cv-none,
  .ow-cv-none{font-size:8.5px;letter-spacing:.04em;margin-top:4px}
  .ow-clk-t{font-size:clamp(13px,15cqw,20px);letter-spacing:-.01em}
  .ow-clk-z{font-size:8.5px;letter-spacing:.04em}
}
@container (max-width: 88px){
  /* the tile's own selector, because `.ow-cv-face--po .ow-cv-none` outranks a
     bare class and was quietly winning — measured 11.5px where this asked for
     7.5px, which is why "NOTHING HERE YET" still wanted 56px in a 45px box */
  .ow-cv-face--po .ow-cv-none,
  .ow-cv-none{font-size:7.5px;letter-spacing:0}
}
/* WHAT A COPY COSTS is now said in `.ow-cv-pick__blurb`, on the destination row
   it describes, so the price sits with the thing being priced. The rule that
   was here is deleted rather than kept "just in case": it styled an element
   nothing builds any more, and it painted `rgb(var(--ow-h1))` — the brand hue
   used directly as ink, which measures 2.7:1 on the light panel. The decision
   it carried still holds and is worth keeping in words: this is NEVER a colour
   of alarm, because replacing your own arrangement is a normal thing to choose
   rather than a mistake. The blurb's neutral --ow-ink-3 says exactly that. */
/* the honest floor of Latest's ladder — a count, when no post fits whole */
.ow-cv-posts__tally{font-size:11px;font-weight:600;letter-spacing:.08em;
  color:var(--ow-ink-3,rgba(255,255,255,.5));
  align-self:flex-start}
.ow-cv-posts__tally[hidden]{display:none}

/* ── A NARROW TILE RECLAIMS ITS SIDE MARGINS TOO ──────────────────────────
   The vertical half of this was fixed when short tiles were clipping; the
   horizontal half was left, and under one universal plane it bites at exactly
   the same place. A 3-wide Popit is 61px on a 320px phone, and 14px of padding
   on each side is 28px of it — forty-six percent — leaving 35px for words that
   need 37 to 39.

   Measured 2026-09-03 at 320px: "Tokyo" needs 39, "Count" 37, "NOTHING HERE
   YET" 37, all in a 35px box. Two to four pixels each, which is a whole word
   drawn past its edge.

   The inset is `--po-seam + --po-gap` and it is the GAP that gives way — the
   seam keeps its 4px at every size, because the stitching is the object's edge
   and an object whose edge moves is a different object. */
/* `.ow-cv-t` carried too, because the padding this overrides lives in the
   stylesheet `canvas.js` INJECTS at runtime — which is appended after this
   file and therefore wins every tie. Measured: a bare `.ow-cv-face--po` rule
   here changed nothing at all. Third time this cascade has caught me. */
@container (max-width: 104px){
  /* `[data-size="sm"]` carried too: the injected sheet sets `padding:11px 13px`
     through `.ow-cv-t[data-size="sm"] .ow-cv-face`, which outranks a two-class
     selector. Measured at 280px — the font step below applied and this one did
     not, leaving 25px of room for a word wanting 32. */
  .ow-cv-t .ow-cv-face--po,
  .ow-cv-t[data-size="sm"] .ow-cv-face--po,
  .ow-cv-t[data-size="md"] .ow-cv-face--po{
    padding-left:calc(var(--po-seam,4px) + 3px);
    padding-right:calc(var(--po-seam,4px) + 3px)}
}
/* a collection's title is the object's name and wraps rather than overflowing */
@container (max-width: 104px){
  .ow-cv-coll__t{font-size:9px;white-space:normal;overflow-wrap:normal;
    line-height:1.2;text-align:center}
}
/* and at the very smallest, the type steps down with it — a 51px tile has 39px
   of room and "Tokyo" wants 39 at the size above this one */
@container (max-width: 74px){
  .ow-cv-t .ow-cv-face--po .ow-cv-name,
  .ow-cv-t .ow-cv-face--po .ow-clk-city{font-size:9px;letter-spacing:-.01em}
  .ow-cv-t .ow-cv-face--po .ow-cv-none{font-size:7px;letter-spacing:0}
}

/* ── AND AT THE SMALLEST, THE ABSENCE HOLDS ONE LINE ──────────────────────
   Measured at 280px: the Next tile's body is 84x42px, which after the key
   leaves a single 14px line. "NOTHING YET" at 10px wants exactly the 64px it
   has, so it wrapped at the space and the clipped half was "YET" — an absence
   line cut in two, which is the one thing these lines exist not to be.

   Smaller, and on one line: 8px holds it in about 50px of the 64 available.
   `nowrap` is stated rather than relied on, so a longer translation shrinks
   the type instead of silently wrapping into the same clip. */
@container (max-width: 118px){
  .ow-cv-next__none,
  .ow-cv-hap__none,
  .ow-cv-posts__none,
  .ow-cv-posts__tally{font-size:8px;letter-spacing:.02em;white-space:nowrap;
    line-height:1.2}
}
/* an ended event is a fact, not an announcement — it steps back to the weight
   of the unknown state rather than leading in the Center's light */
.ow-cv-next__w[data-state="ended"]{font-size:10px;font-weight:560;
  letter-spacing:.08em;color:var(--ow-ink-3,rgba(255,255,255,.5))}

/* ── A COLLECTION IS NOT A PANEL, AND SHARING THE MATERIAL GAVE IT ONE ────
   Introduced by me, caught by the founder in light mode: *"don't have the same
   great glow"* was one observation and this was the other thing that render
   showed — opaque rectangles where the Highlights and Playlists are.

   `.ow-cv-face--coll` matches `.ow-cv-face`, so when the Post material was
   shared onto the tile face every collection got a full panel — and with
   `border-radius: 0`, because a collection deliberately has no tile radius, a
   SQUARE one. In dark the wash is subtle enough to miss; on a light ground it
   is a white box behind a circle.

   A Highlight is a ring and a Playlist is a stack: they carry their own
   presentation and the tile behind them is meant to be nothing at all. Same
   reasoning as `--posts`, which is frameless so the Post inside carries the
   only frame.

   PLACED AT THE END OF THE FILE ON PURPOSE. It first sat beside the material
   it undoes, which reads better and does not work: the light-theme and
   system-light material rules come hundreds of lines later and win the tie
   on order. Measured — the collection face stayed painted with a 0px radius
   in light. A reset has to outlive every rule it resets. */
/* Same correction as the posts face above: this is a RESET, identical in every
   theme, and `html:not([data-theme="dark"])` was carrying weight rather than
   making a choice — but it spells the system half, so a system-DARK reader was
   silently matched by the light branch. `html:root` weighs the same (0,3,1),
   matches every theme state including "nobody chose", and claims nothing. */
.ow-cv-face.ow-cv-face--coll,
html:root .ow-cv-face.ow-cv-face--coll,
html[data-theme="light"] .ow-cv-face.ow-cv-face--coll,
html[data-theme="dark"] .ow-cv-face.ow-cv-face--coll{
  background:none;box-shadow:none;border:0}
.ow-cv-face.ow-cv-face--coll::before,
.ow-cv-face.ow-cv-face--coll::after{display:none}

/* THE POST AUTHOR'S NAME IS A CONTROL, AND MUST NOT LOOK LIKE ONE.
   It opens the author, exactly as the avatar beside it does — but it is the
   same line of type it always was, so every button default is removed rather
   than restyled. Only the pointer and the pressed state say it can be used. */
.ow-post__nmb{all:unset;cursor:pointer;display:inline;font:inherit;color:inherit}
.ow-post__nmb:focus-visible{outline:2px solid rgba(var(--ow-h1),.7);outline-offset:2px;border-radius:6px}
.ow-post__nmb:active b{opacity:.66}

/* WHAT WAS ACTUALLY SAID, under the sentence that says who said it. Quieter
   than the line above it because it is context, not the news itself. */
.ow-note__p{display:block;font-size:12.5px;opacity:.62;margin-top:2px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}

/* ── THE KINDS THAT CARRY THEIR OWN CONTENT ────────────────────────────────
   ★ FOUNDER, 2026-09-05: *"popits go most need designs still"*

   Count, Since and Said. No new material, no new colour, no new shape — the
   existing panel, the existing eyebrow, the existing ink tokens, finally given
   something to hold. The standing rule is to reuse the component language and
   generate CONTENT, never a new visual grammar, and the thing that makes these
   read as designs rather than as a form is HIERARCHY: one element carries the
   tile and everything else is a label for it.

   SIZED BY THE TILE, NOT THE WINDOW — cqw/cqh, for the reason the clock
   records in its own comment: a canonical 3-wide Popit is a quarter of a phone,
   and a vw-sized number in it either overflows or disappears. */
.ow-cv-own{display:flex;flex-direction:column;height:100%;min-height:0;
  justify-content:flex-start}

/* the number is the object; it gets the room and the weight */
.ow-cv-own__n{font-family:var(--ow-font-display);font-weight:600;
  /* THE LADDER IS DIVIDED BY HOW MANY CHARACTERS ARE GOING INTO IT.
     `cqw` knows the tile's width and nothing about its contents, so a ladder
     tuned for three digits cuts the fourth — measured, a Count of 1247 drew
     "124". `--po-digits` is set by the renderer (canvas.js), and 52 / 3 is
     17.3cqw, which is the ladder this rule has always used at three digits: a
     three-digit count is pixel-identical to before, and only longer ones step
     down. The floor is 22px rather than 26 so a six-figure count still lands
     inside the tile instead of hitting the minimum and overflowing anyway. */
  font-size:clamp(22px,calc(52cqw / var(--po-digits,3)),74px);
  line-height:.96;letter-spacing:-.01em;
  color:var(--ow-ink);margin-top:auto;
  /* tabular figures so a counter does not jitter as it changes */
  font-variant-numeric:tabular-nums}

/* the unit rides with the number rather than under it — "3 DAYS" is one fact */
.ow-cv-own__u{font-size:clamp(calc(9px * var(--cv-type,1)),3.2cqw,12px);font-weight:600;
  letter-spacing:.01em;color:var(--ow-h1-ink);
  margin-top:3px}

/* the person's own word for what they counted */
.ow-cv-own__cap{font-size:clamp(calc(11px * var(--cv-type,1)),3.4cqw,14px);line-height:1.3;
  color:var(--ow-ink-2);margin-top:5px;
  /* NEVER CUT. It wraps as far as it needs and the tile gives way elsewhere,
     which is the ladder rule everywhere else in this lane. */
  white-space:normal;overflow:visible;text-overflow:clip;
  overflow-wrap:normal;word-break:normal}

/* ── COLOURS: THE CONTENT IS THE LIGHT ─────────────────────────────────────
   Not a swatch of a colour picked from a palette — the person's ACTUAL light,
   the same angle every other surface is painted from. `--po-hue` is set on the
   tile when this placement pins one; otherwise it falls through to the hue the
   person is following, so changing your colour changes this tile with it,
   which is the rule the whole platform already obeys.

   One chain, no second source: pinned -> yours -> the platform default. */
.ow-cv-own__light{flex:1 1 auto;min-height:0;margin-top:8px;
  border-radius:14px 11px 15px 12px;
  background:
    radial-gradient(120% 90% at 22% 0%,
      hsl(var(--po-hue, var(--ow-user-hue, 265)) 92% 66% / .95), transparent 62%),
    linear-gradient(150deg,
      hsl(var(--po-hue, var(--ow-user-hue, 265)) 88% 58%),
      hsl(calc(var(--po-hue, var(--ow-user-hue, 265)) + 38) 84% 52%));
  box-shadow:inset 0 0 0 1px var(--ow-rim, rgba(255,255,255,.10))}

/* the tile is the light, so the caption sits under it rather than competing */
.ow-cv-face--colours .ow-cv-own__cap{margin-top:7px}

/* ── RHYTHM: SEVEN DAYS, DRAWN ─────────────────────────────────────────────
   The track is the seam this lane already uses between rows; the bar is
   --ow-h1-ink, the accent already clamped for legibility in both themes. No
   new colour and no new material — a chart made of the same two tokens every
   list in here is made of.

   A DAY WITH NOTHING KEEPS ITS COLUMN. A week is seven days whether or not you
   were busy, and collapsing the empty ones would make a quiet week look like a
   short one. */
.ow-cv-rhythm{display:flex;align-items:flex-end;gap:3px;flex:1 1 auto;
  min-height:0;margin-top:auto;padding-top:6px}
.ow-cv-rhythm__d{flex:1 1 0;display:flex;flex-direction:column;
  align-items:stretch;justify-content:flex-end;height:100%;min-width:0;gap:3px}
.ow-cv-rhythm__b{border-radius:3px;background:var(--ow-h1-ink);
  min-height:2px;transition:height .3s cubic-bezier(.4,0,.2,1)}
/* an empty day is a track, not a gap — it says "nothing here" rather than
   leaving the eye to guess whether the day exists */
.ow-cv-rhythm__b[data-none]{background:var(--ow-seam);min-height:2px}
.ow-cv-rhythm__l{font-size:clamp(6px,2cqw,9px);font-weight:600;
  letter-spacing:.06em;color:var(--ow-ink-3);text-align:center;
  white-space:normal;overflow:visible;text-overflow:clip}
/* below this the letters are unreadable, so the shape speaks alone */
@container (max-width:132px){.ow-cv-rhythm__l{display:none}}
@container (max-height:84px){.ow-cv-rhythm__l{display:none}}

/* a sentence is not a number: it reads at reading weight, filling the tile */
.ow-cv-own__say{font-size:clamp(calc(13px * var(--cv-type,1)),4.6cqw,20px);line-height:1.32;
  font-weight:500;color:var(--ow-ink);margin-top:auto;
  letter-spacing:-.01em;
  white-space:normal;overflow:visible;text-overflow:clip;
  overflow-wrap:normal;word-break:normal}

/* ── LINKS: A SHELF OF PLACES ──────────────────────────────────────────────
   Names lead, hosts follow quietly. No new chrome — the row is the same seam
   and the same ink ramp every list in this lane uses; what changes is that a
   row is a destination, so it has a target and it leaves without a handle on
   us (rel=noopener noreferrer, set on the element). */
.ow-cv-own__links{display:flex;flex-direction:column;gap:4px;margin-top:auto;
  min-height:0;overflow:hidden}
/* ★ FOUNDER/471 §10–11: the platform's own mark leads each row; no hairline
   between rows (437) — the marks are what separate them */
.ow-cv-own__link{display:flex;align-items:center;gap:7px;min-width:0;
  text-decoration:none;color:inherit;padding:3px 0}
.ow-cv-own__linkm{flex:none;display:block;width:clamp(11px,7cqw,17px);
  aspect-ratio:1;color:var(--ow-ink)}
.ow-cv-own__linkm svg{width:100%;height:100%;display:block}
/* ── A LINK NAME IS ONE LINE ──────────────────────────────────────────────
   "The bandstand" wrapped to two lines in a 6x3 tile and the second line was
   sliced by the tile's edge — measured 2026-09-09. A shortened name is still a
   name and the full one is in the room; a name cut in half mid-word is neither.
   The row cap in canvas.js handles HOW MANY rows; this handles how tall one is,
   and both were needed because the cut here was a wrap, not a row count. */
.ow-cv-own__linkn{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  min-width:0}
/* the remainder, in the object's own light — same treatment as the guestbook's */
/* `--ow-h1-ink` rather than the raw hue: on a light ground a saturated hue is
   unreadable — measured at 1.69:1 against a 4.5 floor, and it was this exact
   rule the contrast probe caught an hour after I wrote it. The token is the
   same hue clamped to `min(l,.44)` on light and left alone on dark. */
.ow-cv-own__linkmore{display:block;margin-top:4px;font-family:var(--ow-font-atmos);
  font-weight:600;font-size:12px;letter-spacing:.01em;
  color:var(--ow-h1-ink)}
.ow-cv-own__linkn{font-size:clamp(calc(11px * var(--cv-type,1)),3.4cqw,14px);font-weight:560;
  color:var(--ow-ink);
  /* NEVER CUT — a name that ends in an ellipsis is not a place you can go */
  white-space:normal;overflow:visible;text-overflow:clip}
.ow-cv-own__linkh{font-size:clamp(calc(9px * var(--cv-type,1)),2.7cqw,11px);color:var(--ow-ink-3);
  white-space:normal;overflow:visible;text-overflow:clip}
/* the host is the first thing to go when the shelf is narrow: the name is the
   destination, the host is only reassurance about where it leads */
@container (max-width:132px){.ow-cv-own__linkh{display:none}}

/* AT THE SMALLEST TILE the caption is the part that goes. A number with no
   label is still the number; a label with no number is nothing. Removing a
   whole part, never trimming one.

   THE THRESHOLDS ARE MEASURED, AND THE FIRST PAIR WAS WRONG. I guessed
   max-width:118px, and a SIX-COLUMN Popit — half the plane, the commonest tile
   on a phone — measures 115px wide. So the caption vanished on the default
   case and "428" sat alone, which is not a Count, it is a number. The caption
   is the half that carries the meaning.

   The competition is for VERTICAL room, so height decides it: below ~76px the
   number and a line of caption genuinely cannot both sit in the tile. Width
   only decides it when the tile is too narrow for a word at all. Measured on
   the real canvas: 115x110 keeps the caption, 52x70 drops it. */
@container (max-height:76px){.ow-cv-own__cap{display:none}}
@container (max-width:84px){.ow-cv-own__cap{display:none}}


/* ══ PLACES YOU COULD KNOW — a run of Center suggestions, as one band ═══════
   ★ FOUNDER, three times: *"feed is broken again"*. Measured on his own
   account 2026-09-19: sixteen of his first twenty Home rows were `highlight`
   — the ranker offering Centers — each drawn as a full-width card of the same
   height and the same hue family. `frontend/app/home/home.js` now moves a run
   of three or more into this band; the cards are the same nodes, so every
   listener and every door comes with them.

   IT SCROLLS RATHER THAN TRUNCATES. Nothing the runtime sent is dropped: a
   feed that quietly shows twelve of fifteen offers is a feed that disagrees
   with its own payload. Three-quarter width so the next card is visibly
   there, which is the only thing that tells a thumb the row moves. */
.ow-hlband{margin:2px 0 18px}
.ow-hlband__h{font-size:17px;margin:0 0 9px}
.ow-hlband__row{display:flex;gap:10px;overflow-x:auto;overscroll-behavior-x:contain;
  scroll-snap-type:x proximity;padding-bottom:6px;
  scrollbar-width:none;-ms-overflow-style:none}
.ow-hlband__row::-webkit-scrollbar{display:none}
.ow-hlband__row>.po-card{flex:0 0 76%;max-width:320px;scroll-snap-align:start;margin:0}
/* a band on a wide screen is a row of three, not one enormous card */
@media(min-width:720px){.ow-hlband__row>.po-card{flex-basis:31%}}

/* ══ A CLUSTER — a city as one count when the read is wide ══════════════════
   ★ FOUNDER/435: the map for scale. The door answers `clusters` for a wide
   read; close in, the pins themselves are the answer. Same disc grammar as a
   pin, sized by nothing but its count, so it reads as "many here" and a tap
   flies in. */
.ow-map__cluster{width:44px;height:44px;border-radius:50%;
  background:rgba(124,45,255,.82);box-shadow:0 0 0 6px rgba(124,45,255,.22),0 6px 18px -6px rgba(0,0,0,.6);
  color:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;
  font-family:var(--ow-font-display);cursor:pointer}
.ow-map__cluster b{font-size:15px;font-weight:650;line-height:1}
.ow-map__cluster span{font-size:8.5px;opacity:.85;max-width:40px;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;margin-top:2px}

/* ══ HERE — the places in the map's own view, first in the sheet ═══════════
   ★ FOUNDER/435. Same heading, same tile array as the rest of the sheet; the
   only thing of its own is the one-line honest empty state. */
.ow-here{margin:0 0 6px}
.ow-here__none{margin:2px 0 10px;font-size:13px;line-height:1.5;color:var(--ow-ink-3)}

/* ══ ONE FACE, CUT THE WAY ITS OWNER CUT IT — on the map too ═══════════════
   ★ FOUNDER/436 + /409. A pin's face is the same picture every surface draws;
   a person is round, a Center is the shape it chose. And YOU are your own
   face where you are, with a ring that says so. */
.ow-map__pin i[data-shape="rounded"],.ow-map__pin i[data-shape="rounded"] .ow-map__face-img{border-radius:9px}
.ow-map__pin i[data-shape="square"],.ow-map__pin i[data-shape="square"] .ow-map__face-img{border-radius:3px}
.ow-map__pin i[data-shape="round"],.ow-map__pin i[data-shape="round"] .ow-map__face-img{border-radius:50%}
.ow-map__pin.is-person i:has(.ow-map__face-img){width:var(--face,34px);height:var(--face,34px);
  margin:calc((18px - var(--face,34px)) / 2) 0 0 calc((18px - var(--face,34px)) / 2)}
.ow-map__pin[data-you] i{box-shadow:0 0 0 3px #fff,0 0 0 6px rgba(var(--ow-h1,124,45,255),.55),
  0 0 28px -4px var(--pin);animation:none}
/* your own FACE says so with the ring alone — no halo on a photograph (471 §5) */
.ow-map__pin[data-you] i:has(.ow-map__face-img){box-shadow:0 0 0 3px #fff,0 0 0 6px rgba(var(--ow-h1,124,45,255),.55)}
.ow-map__pin[data-you] b{font-weight:650}
/* open now is lit; closed is dimmed; unknown is neither (founder/436) */
.ow-map__pin[data-open="0"] i{opacity:.55;animation:none;box-shadow:0 0 0 2px rgba(255,255,255,.25)}
.ow-map__pin[data-open="1"] i{box-shadow:0 0 0 3px rgba(255,255,255,.9),0 0 30px -2px var(--pin),0 0 60px -6px var(--pin)}
/* a lit FACE is lit by its edge, not by a halo (471 §5) */
.ow-map__pin[data-open="1"] i:has(.ow-map__face-img){box-shadow:0 0 0 2px rgba(255,255,255,.9),0 6px 18px -6px rgba(0,0,0,.7)}
.ow-map__pin[data-open="0"] i:has(.ow-map__face-img){box-shadow:0 0 0 2px rgba(255,255,255,.5)}

/* ══ THE PIN WEARS THE PLATFORM'S FACE (founder/571) ══════════════════════
   *"the icons and profile pictures dont look accurate and dont look good."*
   Every pin is now a face, drawn in the same grammar as `.po-card__av`: the
   picture on the platform's glass while it loads (never a block of neon hue);
   a Center with no picture is its monogram on its own hue with the same top
   light; a person with no picture is `OW.faceMark` (founder/495). No face
   glows (471 §5): a thin cut edge and a lift off the ground make it a pin. */
.ow-map__pin i[data-face]{width:var(--face,30px);height:var(--face,30px);
  margin:calc((18px - var(--face,30px)) / 2) 0 0 calc((18px - var(--face,30px)) / 2);
  display:grid;place-items:center;overflow:hidden;animation:none;
  background:radial-gradient(58% 46% at 32% 22%,rgba(255,255,255,.55),transparent 70%),
    linear-gradient(150deg,rgb(var(--ch1,124,45,255)),rgba(var(--ch2,93,226,255),.72));
  box-shadow:0 0 0 1.5px rgba(255,255,255,.92),0 6px 16px -6px rgba(0,0,0,.6)}
.ow-map__pin i[data-face="picture"]{background:var(--ow-plate,#1c2042)}
html[data-theme="light"] .ow-map__pin i[data-face="picture"]{background:#e3e0ec}
.ow-map__mono{font-family:var(--ow-font-display);font-weight:650;color:#fff;
  font-size:calc(var(--face,30px) * .36);line-height:1;letter-spacing:.01em;
  text-shadow:0 1px 3px rgba(0,0,0,.35)}
.ow-map__pin i[data-face="mark"]{color:#fff}
.ow-map__pin i[data-face="mark"] .ow-facemark{width:62%;height:62%;opacity:.9}
/* the Center's hand-cut corner, scaled to the face rather than fixed at 9px */
.ow-map__pin i[data-face][data-shape="rounded"]{border-radius:calc(var(--face,30px) * .3)}
/* THE PEOPLE AROUND A PLACE are faces only, and a face does not glow */
.ow-map__pin u{width:20px;height:20px;margin:-10px;background:var(--ow-plate,#1c2042);
  box-shadow:0 0 0 1.5px rgba(255,255,255,.92),0 3px 8px -2px rgba(0,0,0,.55)}
.ow-map__pin u img{width:20px;height:20px}
/* the name sits under the face it belongs to, whatever that face's size */
.ow-map__pin b{top:calc(9px + var(--face,18px) / 2 + 6px)}
/* YOU keep the ring that says so, pictured or not */
.ow-map__pin[data-you] i[data-face]{box-shadow:0 0 0 2px #fff,
  0 0 0 5px rgba(var(--ow-h1,124,45,255),.55),0 6px 16px -6px rgba(0,0,0,.6)}

/* ══ DISCOVERY'S SEARCH — large, fading into the map, rows over the world ═══
   ★ FOUNDER/438, 2026-09-19: *"a search bar that fades naturally into this map
     that obviously dominates the top and goes into the language of our home
     … I honestly want the search bar to be large … straight line results …
     people and profiles and posts and centers should look different."*

   The field is Home's cloth with no line: a dark wash that dissolves at its
   own bottom edge into whatever the map is doing. Under it, the kinds. When
   it opens, the results ride over the map on a veil — the same veil a Center
   uses when it is projected — so the world is still there and nothing behind
   the rows can be read as words. */
.ow-map__search{top:calc(50px + env(safe-area-inset-top))}
.ow-map__search input{height:56px;padding:0 22px;font-size:17px;font-weight:500;
  border-radius:28px 25px 29px 24px;
  background:
    radial-gradient(120% 90% at 20% -20%, rgba(var(--ow-h1),.22), transparent 60%),
    linear-gradient(180deg, rgba(10,12,24,.66), rgba(10,12,24,.42));
  box-shadow:inset 0 2px 1px rgba(255,255,255,.14),0 24px 44px -30px rgba(0,0,0,1)}
.ow-map__search input:focus{box-shadow:inset 0 2px 1px rgba(255,255,255,.2),0 0 44px -14px rgba(var(--ow-h1),.7),0 24px 44px -30px rgba(0,0,0,1)}
.ow-map__search input::placeholder{color:rgba(255,255,255,.55);font-weight:400}
/* the glass in the field, in the field's own ink (founder/573) */
.ow-map__search input{padding-left:50px}
.ow-map__glass{position:absolute;left:19px;top:16px;width:24px;height:24px;
  pointer-events:none;z-index:1;color:rgba(255,255,255,.62)}
.ow-map__glass svg{width:24px;height:24px;fill:none;stroke:currentColor;
  stroke-width:1.8;stroke-linecap:round;display:block}
.ow-map[data-hour="day"] .ow-map__glass{color:rgba(20,24,40,.5)}
.ow-map[data-hour="day"] .ow-map__search input{background:
    radial-gradient(120% 90% at 20% -20%, rgba(var(--ow-h1),.14), transparent 60%),
    linear-gradient(180deg, rgba(255,255,255,.86), rgba(255,255,255,.62));
  box-shadow:inset 0 2px 1px rgba(255,255,255,.8),0 18px 40px -26px rgba(20,24,40,.5)}
/* the kinds — quiet words, the pressed one lit */
.ow-find__bar{display:none;align-items:center;gap:8px;margin:10px 4px 0}
.ow-map__search.is-open .ow-find__bar{display:flex}
.ow-find__chips{display:flex;gap:6px;align-items:center;overflow-x:auto;scrollbar-width:none;min-width:0;flex:1 1 auto;
  -webkit-mask-image:linear-gradient(90deg,#000 88%,transparent);mask-image:linear-gradient(90deg,#000 88%,transparent)}
.ow-find__chips::-webkit-scrollbar{display:none}
.ow-find__chip,.ow-find__back{flex:none;border:0;cursor:pointer;padding:8px 13px;border-radius:999px;
  font-family:var(--ow-font);font-size:12.5px;font-weight:500;letter-spacing:.01em;
  color:rgba(255,255,255,.72);background:rgba(255,255,255,.07)}
.ow-find__chip[aria-pressed="true"]{color:#fff;background:rgba(var(--ow-h1),.55);
  box-shadow:0 0 22px -8px rgba(var(--ow-h1),.9)}
.ow-find__back{flex:none;color:#fff;background:rgba(255,255,255,.12)}
/* the plane — a veil over the world, and the rows on it */
.ow-find{display:none;position:fixed;left:0;right:0;bottom:0;
  top:calc(50px + env(safe-area-inset-top) + 56px + 44px);z-index:499;
  overflow:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;
  padding:8px 16px calc(100px + env(safe-area-inset-bottom))}
.ow-map__search.is-open .ow-find{display:block}
.ow-map[data-find="on"] .ow-map__wash{background-image:linear-gradient(180deg,rgba(3,4,10,.78),rgba(3,4,10,.86))!important;
  -webkit-backdrop-filter:blur(14px) saturate(1.06);backdrop-filter:blur(14px) saturate(1.06)}
.ow-map[data-find="on"] .ow-map__near,
.ow-map[data-find="on"] .ow-map__earth{opacity:0;pointer-events:none}
.ow-find__list{display:flex;flex-direction:column;gap:6px;max-width:640px;margin:0 auto}
.ow-find__h{margin:14px 6px 4px;font-family:var(--ow-font);font-size:13px;font-weight:600;
  letter-spacing:0;color:rgba(255,255,255,.62)}
.ow-find__none{margin:18px 6px;font-size:14px;line-height:1.5;color:rgba(255,255,255,.6)}
/* ONE ROW — its face, its name, one line; no line around it (437) */
.ow-find__row{display:flex;align-items:center;gap:13px;width:100%;text-align:left;border:0;cursor:pointer;
  padding:9px 10px;border-radius:18px 15px 19px 14px;font-family:var(--ow-font);color:#fff;
  background:
    radial-gradient(130% 80% at 18% -6%, rgba(var(--h,var(--ow-h1)),.22), transparent 64%),
    linear-gradient(0deg, var(--ow-tile-mid), var(--ow-tile-mid));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.06);
  transition:transform .18s var(--ow-spring,ease),background .2s}
.ow-find__row:hover{transform:translateX(2px)}
.ow-find__row:active{transform:scale(.99)}
.ow-find__t{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1 1 auto}
.ow-find__t b{font-size:15px;font-weight:500;line-height:1.25;letter-spacing:-.01em;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
/* a place's region wraps rather than ending in "Indon…" — a cut word is a cut
   word whichever line it is on (measured at 290px, 2026-09-21) */
.ow-find__t span{font-size:12.5px;color:rgba(255,255,255,.6);white-space:normal;overflow-wrap:anywhere;line-height:1.25}
.ow-find__k{flex:none;font-style:normal;font-size:12px;letter-spacing:.01em;
  color:rgba(255,255,255,.38);padding-right:4px}
/* the face — a person round, a Center as it chose, a post its picture, an
   event its cover or its date on the icon, a place of the world a pin */
.ow-find__pic{position:relative;flex:none;width:46px;height:46px;overflow:hidden;
  display:grid;place-items:center;border-radius:12px 10px 13px 11px;
  background:linear-gradient(146deg,rgba(var(--h,var(--ow-h1)),.9),rgba(var(--h2,var(--ow-h2)),.9));
  box-shadow:none}                                                /* no glow (471 §5) */
.ow-find__pic[data-shape="round"]{border-radius:50%}
.ow-find__pic[data-shape="square"]{border-radius:5px}
.ow-find__pic img,.ow-find__pic video{width:100%;height:100%;object-fit:cover;display:block;position:absolute;inset:0}
.ow-find__pic i{font-style:normal;font-weight:650;font-size:15px;color:#fff}
/* a post that is a picture: the picture leads; a video: its frame behind a play mark */
.ow-find__pic[data-kind="post"]{width:52px;height:52px;border-radius:10px 8px 11px 9px;background:#151828}
.ow-find__pic[data-video]::after{content:'';position:absolute;left:50%;top:50%;width:0;height:0;
  transform:translate(-40%,-50%);border-left:12px solid rgba(255,255,255,.95);
  border-top:8px solid transparent;border-bottom:8px solid transparent;
  filter:drop-shadow(0 1px 4px rgba(0,0,0,.6))}
.ow-find__pic[data-video]::before{content:'';position:absolute;inset:0;box-shadow:inset 0 0 0 1.5px rgba(255,255,255,.55);border-radius:inherit;pointer-events:none}
.ow-find__pic[data-kind="post"][data-empty]{background:linear-gradient(146deg,rgba(var(--h,var(--ow-h1)),.5),rgba(var(--h2,var(--ow-h2)),.4))}
.ow-find__pic[data-kind="post"][data-empty]::after{content:'\201C';font-family:var(--ow-font-display);font-size:26px;font-weight:650;color:rgba(255,255,255,.9);position:absolute;left:50%;top:56%;transform:translate(-50%,-50%);border:0;width:auto;height:auto}
/* the date on the icon, the way Apple Calendar does it (founder, 2026-09-10) */
.ow-find__pic[data-date]{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:0;background:#1a1d33}
.ow-find__pic[data-date] b{font-size:12px;letter-spacing:.01em;color:rgba(255,120,120,.95);line-height:1}
.ow-find__pic[data-date] em{font-style:normal;font-size:19px;font-weight:650;line-height:1.05;color:#fff}
.ow-find__pic[data-glyph="pin"]{background:rgba(255,255,255,.1)}
.ow-find__pic[data-glyph="pin"]::after{content:'';width:12px;height:12px;border-radius:50% 50% 50% 0;transform:rotate(-45deg);
  background:#fff;box-shadow:0 0 0 3px rgba(255,255,255,.25)}
/* the old dropdown is retired */
.ow-map__found{display:none!important}

/* ══ RESERVED FOR YOU — the names a person reserved, one press to take ═══════
   ★ FOUNDER/441. Home's cloth, no lines (437). */
.ow-claims{margin:6px 0 14px}
.ow-claims__h{font-size:19px;margin:0 0 4px}
.ow-claims__why{margin:0 0 10px;font-size:13.5px;line-height:1.5;color:var(--ow-ink-3)}
.ow-claims__list{display:flex;flex-direction:column;gap:8px}
.ow-claims__row{display:flex;align-items:center;gap:12px;padding:10px 12px;border-radius:18px 15px 19px 14px;
  --h:var(--ow-h1);--h2:var(--ow-h2);
  background:
    radial-gradient(130% 80% at 18% -6%, rgba(var(--h),.30), transparent 64%),
    linear-gradient(0deg, var(--ow-tile-mid), var(--ow-tile-mid));
  box-shadow:inset 0 2px 1px var(--ow-lift, rgba(255,255,255,.2)),0 0 30px -12px rgba(var(--h),.6)}
.ow-claims__row[data-claimed]{opacity:.85}
.ow-claims__face{flex:none;width:42px;height:42px;border-radius:12px 10px 13px 11px;overflow:hidden;
  display:grid;place-items:center;font-family:var(--ow-font-display);font-size:20px;font-weight:650;color:#fff;
  background:linear-gradient(146deg,rgba(var(--h),.9),rgba(var(--h2),.9))}
.ow-claims__face img{width:100%;height:100%;object-fit:cover;display:block}
.ow-claims__t{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}
.ow-claims__t b{font-size:16px;font-weight:600;letter-spacing:-.01em}
.ow-claims__t span{font-size:12.5px;color:var(--ow-ink-3)}
.ow-claims__go{flex:none}
.ow-claims__go[data-done]{opacity:.7}
.ow-claims__err{flex:1 1 100%;margin:4px 0 0;font-size:12.5px;color:#ff9a9a}

/* ══ THE ONE LINK BAR — universal on every Center and person ═════════════════
   ★ FOUNDER/442. A row of the platforms' own marks in the identity's ink,
   under the sentence, above the counts. No line around it (437); each mark
   sits on a quiet disc of the identity's hue so it reads as a control. */
/* ★ FOUNDER/447: *"i dont need big box buttons all around them so space
   them more evenly."* No plate, no ring, no glow: the mark itself, in the
   page's ink, on an even pitch — every mark 32px wide with the same 14px
   between, so a row of nine is nine marks and not nine buttons. The 32px
   box is the touch target and is invisible. */
/* ★ FOUNDER/471 §7: *"make social icons smallers."* 24px marks in 32px boxes
   on a 46px pitch read as a row of app icons; 19px in 28px on 40px reads as
   a line of marks under a sentence, which is what it is. Still even, still
   no plate: the box is the touch target and stays invisible. */
.ow-linkbar{display:flex;flex-wrap:wrap;gap:8px 12px;margin:10px 0 0;align-items:center}
.ow-linkbar__i{display:inline-flex;align-items:center;justify-content:center;gap:6px;
  width:28px;height:28px;padding:0;border:0;border-radius:7px;cursor:pointer;
  color:var(--ow-ink);opacity:.9;text-decoration:none;font-family:var(--ow-font);font-size:12px;
  background:none;box-shadow:none;
  transition:transform .18s var(--ow-spring,ease),opacity .2s}
.ow-linkbar__i svg{width:19px;height:19px;display:block;flex:none}
.ow-linkbar__i span{padding-right:6px;white-space:nowrap}
.ow-linkbar__i:hover{transform:translateY(-2px);opacity:1}
.ow-linkbar__i:active{transform:scale(.94)}
.ow-linkbar__i:focus-visible{outline:2px solid rgba(var(--ow-h1),.9);outline-offset:2px}
/* the two acts that are really links are ours: they carry the page's hue */
.ow-linkbar__i[data-platform="events"],.ow-linkbar__i[data-platform="directions"]{
  color:rgb(var(--ow-h1));opacity:1}
/* the same bar stood on end — the Links tab (471 §10–11). Scoped under the
   list so these win over the bar's own row rules above. */
.ow-linkbar.ow-linkbar--list{flex-direction:column;align-items:stretch;gap:2px;margin:0}
.ow-linkbar--list .ow-linkbar__i{width:auto;height:auto;justify-content:flex-start;gap:14px;
  padding:10px 8px;border-radius:12px;opacity:1}
.ow-linkbar--list .ow-linkbar__i svg{width:22px;height:22px}
.ow-linkbar--list .ow-linkbar__i:hover{transform:none;background:color-mix(in srgb,var(--ow-tile-base) 60%,transparent)}
.ow-linkbar__w{display:flex;flex-direction:column;gap:1px;min-width:0;text-align:left;padding:0}
.ow-linkbar__w b{font-weight:560;font-size:15px;letter-spacing:-.01em;color:var(--ow-ink);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ow-linkbar__w small{font-size:12px;color:var(--ow-ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ow-prof__handle{font-size:15px;letter-spacing:.02em;text-transform:none;font-weight:600;opacity:.92}

/* ══ THE MEDIA VIEWER — a picture or a video, full screen, the way back over it
   ★ FOUNDER/438. Black stage; the bar is a gradient the media fades into —
   the back arrow, the sender's face, their name and when. No line, no box. */
.ow-viewer{position:fixed;inset:0;z-index:1400;background:#000;display:flex;flex-direction:column;
  opacity:0;transition:opacity .22s ease}
.ow-viewer.is-in{opacity:1}
html.ow-viewer-open{overflow:hidden}
.ow-viewer__stage{position:absolute;inset:0;display:grid;place-items:center}
.ow-viewer__media{max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain;display:block}
video.ow-viewer__media{width:100%;max-height:100%}
.ow-viewer__bar{position:absolute;left:0;right:0;top:0;z-index:2;display:flex;align-items:center;gap:10px;
  padding:calc(10px + env(safe-area-inset-top)) 12px 26px;
  background:linear-gradient(180deg,rgba(0,0,0,.62),rgba(0,0,0,.28) 60%,transparent);
  color:#fff;pointer-events:none}
.ow-viewer__bar>*{pointer-events:auto}
.ow-viewer__back{width:42px;height:42px;border:0;border-radius:50%;cursor:pointer;display:grid;place-items:center;
  color:#fff;background:rgba(255,255,255,.12);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}
.ow-viewer__back svg{width:22px;height:22px}
.ow-viewer__who{display:flex;align-items:center;gap:10px;min-width:0}
.ow-viewer__who.is-door{cursor:pointer}
.ow-viewer__face{flex:none;width:36px;height:36px;border-radius:50%;overflow:hidden;display:grid;place-items:center;
  font-family:var(--ow-font-display);font-weight:650;font-size:15px;color:#fff;
  background:linear-gradient(146deg,rgba(var(--h,var(--ow-h1)),.95),rgba(var(--h2,var(--ow-h2)),.95));
  box-shadow:0 0 0 2px rgba(255,255,255,.85)}
.ow-viewer__face[data-shape="rounded"]{border-radius:11px}
.ow-viewer__face[data-shape="square"]{border-radius:5px}
.ow-viewer__face img{width:100%;height:100%;object-fit:cover;display:block}
.ow-viewer__t{display:flex;flex-direction:column;min-width:0;font-family:var(--ow-font);text-shadow:0 1px 8px rgba(0,0,0,.7)}
.ow-viewer__t b{font-size:15px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ow-viewer__t span{font-size:12px;opacity:.78}
.ow-viewer__cap{position:absolute;left:0;right:0;bottom:0;z-index:2;margin:0;
  padding:26px 16px calc(16px + env(safe-area-inset-bottom));color:#fff;font-family:var(--ow-font);font-size:14px;line-height:1.5;
  background:linear-gradient(0deg,rgba(0,0,0,.6),transparent)}
/* a photo or a video inside a message bubble — the thing, bounded, no line */
.ow-msg__media{display:block;border:0;padding:0;margin:2px 0 4px;cursor:pointer;background:none;
  max-width:min(78vw,320px);border-radius:16px 13px 17px 12px;overflow:hidden;
  box-shadow:0 8px 24px -12px rgba(0,0,0,.7)}
.ow-msg__media img,.ow-msg__media video{display:block;width:100%;height:auto;max-height:360px;object-fit:cover}
.ow-msg__media[data-video]{position:relative}
.ow-msg__media[data-video]::after{content:'';position:absolute;left:50%;top:50%;transform:translate(-40%,-50%);
  border-left:18px solid rgba(255,255,255,.95);border-top:11px solid transparent;border-bottom:11px solid transparent;
  filter:drop-shadow(0 2px 8px rgba(0,0,0,.6))}
/* the composer's attach control — the camera, beside the field */
.ow-attach{flex:none;width:40px;height:40px;border:0;border-radius:50%;cursor:pointer;display:grid;place-items:center;
  color:var(--ow-ink);background:rgba(var(--ow-h1),.14)}
.ow-attach svg{width:20px;height:20px}
.ow-attach[disabled]{opacity:.5;cursor:default}

/* ══ WHERE ELSE A POST GOES — a row of faces, not a wall of chips ════════════
   ★ FOUNDER/438 + /442. Sideways, each place as its face with its name under
   it; the pressed one is lit. No line (437). */
.ow-compose__where{flex-wrap:nowrap;align-items:flex-start}
.ow-compose__dests{display:flex;gap:12px;overflow-x:auto;scrollbar-width:none;padding:4px 2px 8px;flex:1 1 auto;min-width:0;
  -webkit-mask-image:linear-gradient(90deg,#000 92%,transparent);mask-image:linear-gradient(90deg,#000 92%,transparent)}
.ow-compose__dests::-webkit-scrollbar{display:none}
.ow-dest{flex:none;width:68px;display:flex;flex-direction:column;align-items:center;gap:6px;border:0;background:none;cursor:pointer;
  padding:0;color:var(--ow-ink);font-family:var(--ow-font);opacity:.78;transition:opacity .2s,transform .18s var(--ow-spring,ease)}
.ow-dest[aria-pressed="true"]{opacity:1}
.ow-dest:active{transform:scale(.96)}
.ow-dest__face{width:52px;height:52px;border-radius:15px 13px 16px 14px;overflow:hidden;display:grid;place-items:center;
  font-family:var(--ow-font-display);font-weight:650;font-size:15px;color:#fff;
  background:linear-gradient(146deg,rgba(var(--h,var(--ow-h1)),.9),rgba(var(--h2,var(--ow-h2)),.9));
  box-shadow:none}                                                /* no glow (471 §5) */
/* the chosen destination is the one at full light with its name in ink —
   nothing is drawn around its face (471 §5) */
.ow-dest{opacity:.55}
.ow-dest[aria-pressed="true"]{opacity:1}
.ow-dest[aria-pressed="true"] .ow-dest__n{color:var(--ow-ink);font-weight:600}
.ow-dest__face[data-shape="round"]{border-radius:50%}
.ow-dest__face[data-shape="square"]{border-radius:6px}
.ow-dest__face img{width:100%;height:100%;object-fit:cover;display:block}
.ow-dest__n{font-size:11px;line-height:1.2;text-align:center;max-width:68px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ow-post__media{position:relative}
.ow-post__full{position:absolute;right:10px;top:10px;z-index:2;border:0;cursor:pointer;padding:6px 10px;border-radius:999px;
  font-family:var(--ow-font);font-size:12px;font-weight:600;letter-spacing:.01em;
  color:#fff;background:rgba(0,0,0,.45);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}
.ow-you__links{margin-top:6px}
.ow-you__links .ow-linkbar{margin:4px 0 0}

/* ═══ THE BOTTOM NOTIFICATION BAR ═════════════════════════════════════════
   ★ FOUNDER/493: *"a bottom notification bar that can popup like all social
     medias describing briefly for a second an individual error if there is
     then fade away after a second."*

   ABOVE THE DOCK, never over it: `--ow-dock` already carries the dock's height
   on a phone and 0 on a wide screen, so the bar sits clear of the one control
   strip a person is always reaching for. Centred, narrow, and never wider than
   the column — a notice, not a screen.

   IT CANNOT BE PRESSED THROUGH AND IT CANNOT BLOCK. `pointer-events` is off on
   the bar and back on for its action button alone, so the feed underneath
   stays live while the bar is up. */
.ow-toast{position:fixed;left:50%;transform:translateX(-50%) translateY(10px);
  bottom:calc(14px + var(--ow-dock,0px) + env(safe-area-inset-bottom));
  z-index:200;display:flex;align-items:center;gap:12px;
  max-width:min(440px, calc(100vw - 32px));
  padding:11px 14px;border-radius:14px 12px 15px 13px;
  font-family:var(--ow-font);font-size:13px;line-height:1.35;
  color:#fff;background:rgba(18,17,28,.94);
  box-shadow:0 10px 30px -10px rgba(0,0,0,.55), inset 0 0 0 1px rgba(255,255,255,.1);
  opacity:0;pointer-events:none;
  transition:opacity .2s var(--ow-ease), transform .26s var(--ow-spring)}
.ow-toast[data-on]{opacity:1;transform:translateX(-50%) translateY(0)}
.ow-toast__t{flex:1 1 auto;min-width:0}
/* the hue says which kind of notice this is without a word or an icon — a
   coloured edge, because a red plate for every hiccup is a product shouting */
.ow-toast[data-tone="bad"]{box-shadow:0 10px 30px -10px rgba(0,0,0,.55),
  inset 0 0 0 1px rgba(255,255,255,.1), inset 3px 0 0 0 var(--ow-like,#ff4d6a)}
.ow-toast[data-tone="ok"]{box-shadow:0 10px 30px -10px rgba(0,0,0,.55),
  inset 0 0 0 1px rgba(255,255,255,.1), inset 3px 0 0 0 rgb(var(--ow-h1))}
.ow-toast__a{flex:none;pointer-events:auto;cursor:pointer;
  appearance:none;-webkit-appearance:none;border:0;
  padding:5px 11px 6px;border-radius:9px;
  font-family:var(--ow-font);font-size:12.5px;font-weight:600;color:#fff;
  background:rgba(255,255,255,.14)}
.ow-toast__a:hover{background:rgba(255,255,255,.22)}
@media (prefers-reduced-motion:reduce){
  .ow-toast{transition:opacity .2s linear;transform:translateX(-50%)}
  .ow-toast[data-on]{transform:translateX(-50%)}
}

/* ══ A CONVERSATION IS A ROOM ═════════════════════════════════════════════
   ★ FOUNDER/493: *"On the chat itself the bar should obviously extend to
     bottom and the service should have smooth animations like iMessage."*

   The composer was `position:sticky; bottom:0` inside the scrolling page, so
   it stopped at the bottom of the COLUMN and the dock sat on top of the last
   message — measured at 290: the bar was at y=617 with the thread running
   underneath the nav. Sticky is the right idea and the wrong containing block.

   SO THE THREAD TAKES THE SCREEN. The document says it is in a conversation;
   the composer is pinned to the viewport's bottom edge, full width, and the
   dock steps aside — which is what iMessage, Snapchat and WhatsApp all do,
   because a tab bar under a keyboard is a row of buttons nobody can reach. The
   thread pads itself by the composer's height so the newest message is never
   behind it.

   THE SIZE IS MEASURED, NOT GUESSED: 62px is the composer's real height at
   290 (a 42px field plus its padding), plus the safe area. It grows with the
   textarea, so the padding is a floor rather than an exact figure — a floor
   that is too small hides a message, a floor that is slightly large costs a
   few pixels of air. */
html[data-ow-thread] .ow-composer{
  position:fixed;left:0;right:0;bottom:0;z-index:120;
  padding:10px max(14px,env(safe-area-inset-left)) max(10px,env(safe-area-inset-bottom));
  background:linear-gradient(0deg,var(--ow-chrome) 78%,transparent)}
html[data-ow-thread] .ow-composer > *{max-width:640px}
html[data-ow-thread] .ow-composer{justify-content:center}
html[data-ow-thread] .ow-nav{display:none}
html[data-ow-thread] .ow-main{padding-bottom:calc(72px + env(safe-area-inset-bottom))}

/* ── ARRIVING LIKE iMESSAGE ───────────────────────────────────────────────
   A message that simply exists on the next paint reads as a page reload; one
   that rises and settles reads as something that just arrived. Transform and
   opacity only, one spring, 220ms — the same easing the rest of the App uses,
   so this is the product's motion rather than a message-only effect. It runs
   on ARRIVAL only: `.ow-msg--new` is set by the surface when a row is appended
   after the first paint, so opening a thread does not animate fifty rows. */
@keyframes ow-msg-in{
  from{opacity:0;transform:translateY(10px) scale(.985)}
  to{opacity:1;transform:none}}
.ow-msg--new{animation:ow-msg-in .22s var(--ow-spring) both}
@media (prefers-reduced-motion:reduce){
  .ow-msg--new{animation:none}
}

/* ══ THE CAMERA IS THE SCREEN ═════════════════════════════════════════════
   ★ FOUNDER/493: *"The camera should become the entire screen here, no matter
     the platform, the buttons along side of photo/ record button … has upload
     and import buttons next to it."*

   It was a rounded box in the middle of a page, with Upload and Import as two
   stacked cards underneath it and a destination picker and a caption field
   below those — everything on one portion, which is the other half of what he
   is describing. A camera that is a widget on a form is not a camera.

   THE STAGE TAKES THE VIEWPORT while the lens is live. Fixed, edge to edge,
   above everything, with the dock stepped aside — the controls float over the
   picture the way they do in every camera on earth. `data-ow-lens` is on the
   document because the dock is not inside this surface, and `stop()` clears
   it on every exit. */
html[data-ow-lens] .ow-compose__stage{position:fixed;inset:0;z-index:150;
  margin:0;padding:0;border-radius:0;background:#000;display:block}
html[data-ow-lens] .ow-compose__lens{position:absolute;inset:0;
  border-radius:0;margin:0;width:100%;height:100%;overflow:hidden}
html[data-ow-lens] .ow-compose__lens video{position:absolute;inset:0;
  width:100%;height:100%;object-fit:cover;display:block}
/* THE DOCK STAYS OVER THE CAMERA (founder/544): Lightbulb opens AS the camera,
   the way Snapchat's camera is a tab with its bar over the picture — so a
   person leaves it the way they leave anything else. */
html[data-ow-lens] .ow-nav{position:fixed;left:0;right:0;z-index:160;margin:0 auto}
@media (min-width:900px){
  html[data-ow-lens] .ow-nav{right:auto;margin:0}
  html[data-ow-lens] .ow-compose__stage{left:96px}
}
/* everything else on the surface is behind the lens and must not scroll under
   it — the page is the camera until the camera closes */
html[data-ow-lens],
html[data-ow-lens] body{overflow:hidden}

/* the controls, over the picture */
html[data-ow-lens] .ow-compose__bar{position:absolute;left:0;right:0;
  bottom:calc(env(safe-area-inset-bottom) + 112px);
  display:flex;align-items:center;justify-content:center;gap:34px;
  padding:0 22px;background:none;z-index:2}
html[data-ow-lens] .ow-compose__lenstop{position:absolute;left:0;right:0;
  top:calc(env(safe-area-inset-top) + 14px);
  display:flex;align-items:center;justify-content:space-between;
  padding:0 16px;z-index:2}
/* Upload and Import: a glyph and a word, small, either side of the shutter */
.ow-compose__side{display:flex;flex-direction:column;align-items:center;gap:5px;
  appearance:none;-webkit-appearance:none;border:0;background:none;cursor:pointer;
  color:#fff;font-family:var(--ow-font);font-size:11.5px;font-weight:600;
  padding:6px;min-width:58px;
  filter:drop-shadow(0 1px 8px rgba(0,0,0,.6))}
.ow-compose__side span{display:grid;place-items:center;width:34px;height:34px;
  border-radius:50%;background:rgba(255,255,255,.16)}
.ow-compose__side svg{width:19px;height:19px;stroke:#fff;fill:none;stroke-width:1.8}
.ow-compose__side:hover span{background:rgba(255,255,255,.26)}
.ow-compose__side:active{transform:scale(.96)}

/* ── THE CAMERA BEFORE THE PICTURE (founder/544–546) ──────────────────────
   What shows in the instant before the camera answers, and whenever it
   cannot: the person's own colour, deep, so the first screen of Lightbulb is
   a fresh place rather than a black hole or a page of choices. The video,
   once it arrives, covers it. */
html[data-ow-lens] .ow-compose__lens{
  background:radial-gradient(120% 70% at 50% -8%,rgba(var(--h),.55),transparent 62%),
             radial-gradient(90% 55% at 50% 112%,rgba(var(--h2,var(--h)),.42),transparent 64%),
             #07060c;
  box-shadow:none}
html[data-ow-lens] .ow-compose__lens video[data-mirror]{transform:scaleX(-1)}
.ow-compose__lensmid{position:absolute;left:0;right:0;top:0;
  /* centred in the room ABOVE the controls, never behind them — on a short
     screen (the founder's pane is 392px tall) the middle of the whole screen
     is where the shutter row already is */
  bottom:calc(env(safe-area-inset-bottom) + 186px);
  display:grid;place-items:center;
  padding:calc(env(safe-area-inset-top) + 58px) 28px 0;z-index:1;pointer-events:none}
@media (min-width:900px){ .ow-compose__lensmid{bottom:calc(env(safe-area-inset-bottom) + 136px)} }
.ow-compose__lensmid > *{pointer-events:auto}
.ow-compose__lensnote{margin:0;max-width:300px;text-align:center;color:#fff;
  font-size:14px;line-height:1.5;font-weight:500;
  text-shadow:0 1px 12px rgba(0,0,0,.5)}
.ow-compose__lenson{font:inherit;font-size:15px;font-weight:600;cursor:pointer;
  color:#111;background:#fff;border:0;border-radius:999px;padding:13px 24px;
  box-shadow:0 12px 34px -10px rgba(0,0,0,.6)}
.ow-compose__lenson:active{transform:scale(.97)}
.ow-compose__lens[data-state="off"] .ow-compose__shutter{opacity:.35;cursor:default}
.ow-compose__lens[data-state="starting"] .ow-compose__round,
.ow-compose__lens[data-state="off"] .ow-compose__round,
.ow-compose__lens[data-state="ask"] .ow-compose__round{visibility:hidden}
/* a round glass control on the picture — Switch camera */
.ow-compose__round{display:grid;place-items:center;width:40px;height:40px;
  border-radius:50%;border:0;cursor:pointer;color:#fff;
  background:rgba(0,0,0,.34);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}
.ow-compose__round svg{width:21px;height:21px;fill:none;stroke:currentColor;
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
html[data-ow-lens] .ow-compose__shutter{flex:none;width:70px;height:70px;border-width:4px}
/* at the founder's 290px the three controls and their gaps must fit one row */
@media (max-width:360px){ html[data-ow-lens] .ow-compose__bar{gap:18px;padding:0 12px} }
/* the strip under the shutter: what you are making with it */
.ow-compose__modes{position:absolute;left:0;right:0;z-index:2;
  bottom:calc(env(safe-area-inset-bottom) + 72px);
  display:flex;justify-content:center;gap:6px}
.ow-compose__mode{font:inherit;font-size:13px;font-weight:600;cursor:pointer;
  color:rgba(255,255,255,.66);background:none;border:0;padding:6px 11px;
  border-radius:999px;text-shadow:0 1px 8px rgba(0,0,0,.55)}
.ow-compose__mode[aria-pressed="true"]{color:#fff;background:rgba(255,255,255,.16)}
@media (min-width:900px){
  .ow-compose__modes{bottom:calc(env(safe-area-inset-bottom) + 22px)}
  html[data-ow-lens] .ow-compose__bar{bottom:calc(env(safe-area-inset-bottom) + 62px)}
}
/* Import, over the picture, above the shutter */
.ow-compose__lenspanel{position:absolute;left:14px;right:14px;z-index:3;
  bottom:calc(env(safe-area-inset-bottom) + 196px)}
@media (min-width:900px){ .ow-compose__lenspanel{bottom:calc(env(safe-area-inset-bottom) + 146px)} }
.ow-compose__lenspanel .ow-compose__imp{max-width:520px;margin:0 auto;padding:10px;
  border-radius:18px;background:rgba(10,10,16,.62);
  -webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}
.ow-compose__lenspanel .ow-fld input{color:#fff}
.ow-compose__impnote{flex-basis:100%;margin:0;font-size:12.5px;color:#fff}
.ow-compose__impnote:empty{display:none}
/* WRITE: the person's colour instead of a picture, the words in the middle */
html[data-ow-lens] .ow-compose__write{display:flex;align-items:center;justify-content:center;
  background:radial-gradient(110% 80% at 30% 0%,rgba(var(--h),.95),transparent 70%),
             radial-gradient(100% 70% at 80% 100%,rgba(var(--h2,var(--h)),.9),transparent 70%),
             rgb(var(--h))}
.ow-compose__words{width:min(88%,560px);min-height:30vh;max-height:46vh;resize:none;
  border:0;outline:0;background:none;color:#fff;text-align:center;
  font-family:var(--ow-font-display);font-weight:600;font-size:26px;line-height:1.3;
  text-shadow:0 1px 14px rgba(0,0,0,.25);margin-top:-80px}
.ow-compose__words::placeholder{color:rgba(255,255,255,.7)}
.ow-compose__next{width:auto;flex:none;min-width:150px;padding:13px 34px;border-radius:999px;
  font-size:14px;font-weight:600;background:#fff;color:#111}
.ow-compose__next:disabled{opacity:.5}

/* ── THE POST PAGE WEARS THE CARD'S ACTION ROW ────────────────────────────
   ★ FOUNDER/490, /488: one Post, one set of controls, the same everywhere.
   The page hands `OW.mountResponses` the card shape it reads; these two rules
   are what let the card's own `.po-card__foot .po-acts` rules apply on a
   surface that is not a card. Nothing about the row is redefined here — if it
   changes on Home it changes here, which is the point. */
.ow-post__actrow{display:block;margin:6px 0 4px}
.ow-post__actrow .po-card__foot{display:flex;align-items:center;padding:8px 0 10px}

/* ── THE DEFAULT FACE (founder/495) ───────────────────────────────────────
   One drawing for "this person has no picture", so it is the same mark at
   every size on every surface. It fills whatever face box it lands in and
   takes that box's ink, which is why it is stroked in `currentColor` rather
   than given a colour here — on a card it is dark on the hue, on a photograph
   it is white, and neither needs a rule of its own.

   62% rather than 100%: a silhouette that touches the rim of its own disc
   reads as a cropped photograph. The inset is what makes it read as a mark. */
.ow-facemark{width:62%;height:62%;display:block;opacity:.62}
.po-card__av .ow-facemark,
.ow-post__av .ow-facemark,
.ow-thread__av .ow-facemark,
.po-glance__face .ow-facemark,
.ow-prof__face .ow-facemark{margin:auto}
/* the face boxes centre their contents already; these are the ones that laid
   out for a text node and need telling that the child is now a drawing */
.po-card__av,.ow-post__av,.ow-thread__av,.po-glance__face{display:grid;place-items:center}

/* ── YOUR FACE ON THE CORNER DESTINATION (founder/495) ────────────────────
   The picture sits where the glyph is and covers it; the glyph stays under it
   as the fallback, so a picture that fails to load reveals the outline rather
   than a hole. Cut round, because a person is round everywhere else in this
   product, and sized to the glyph so the row's rhythm does not move. */
.ow-nav__b--me{position:relative}
/* z-index 2: on a phone the dock lifts every glyph to z-index 1 (above the
   active pill, oneway-app.html), which drew the outline person ON TOP of the
   picture it is meant to sit under. (C, 2026-09-30) */
.ow-nav__face{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  z-index:2;
  width:23px;height:23px;border-radius:50%;object-fit:cover;display:block;
  box-shadow:0 0 0 1.5px rgba(var(--ow-h1),.55)}
/* the label sits under the icon on a phone; the face must not cover it */
.ow-nav__b--me > span{position:relative;z-index:1}
@media (min-width:900px){
  /* ON THE RAIL THE WORD SITS UNDER THE GLYPH, so the glyph is 7px above the
     button's centre — and a face centred on the BUTTON sat 7px low, its lower
     edge over the "Me" (measured 1440px: face 602-628, label from 622). It is
     centred on the glyph it covers instead. (C, 2026-09-30, founder/573) */
  .ow-nav__face{width:26px;height:26px;top:calc(50% - 7px)}
}
/* the gear keeps the bell's exact treatment — one row, one weight */
.ow-top__gear svg{fill:none;stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round}

/* ── THE UPLOAD BAR (founder/493) ─────────────────────────────────────────
   It measures steps that really happened — one per file that is on the server,
   plus the post itself — so it never creeps to 90% and waits. A track and a
   fill, the hue, no percentage printed: the number would invite a person to
   read precision into something that is deliberately coarse. */
.ow-compose__prog{height:4px;border-radius:3px;overflow:hidden;margin:8px 0 2px;
  background:color-mix(in srgb, var(--ow-ink) 12%, transparent)}
.ow-compose__prog > i{display:block;height:100%;width:0;border-radius:inherit;
  background:linear-gradient(90deg, rgb(var(--ow-h1)), rgb(var(--ow-h2)));
  transition:width .28s var(--ow-ease)}
@media (prefers-reduced-motion:reduce){ .ow-compose__prog > i{transition:none} }

/* ── HOW YOUR LOCATION IS SHARED (founder/493) ────────────────────────────
   The same rows the audience uses, under their own quiet label — one control
   vocabulary for three questions about one subject, because "who", "when" and
   "how precisely" are the same kind of choice and should not look like three
   different features. */
.ow-loc__how{margin-top:14px}
.ow-loc__howlbl{display:block;margin:0 0 6px;
  font-family:var(--ow-font-atmos);font-weight:600;font-size:10px;
  letter-spacing:.16em;text-transform:uppercase;color:var(--ow-ink-3)}

/* ══ COLOUR LOGOS OR BLACK AND WHITE — THE PERSON'S CHOICE ════════════════
   ★ FOUNDER/496: *"users should choose if they want the color logos or the
     black and white icons on their platforms and posts."*

   The marks are silhouettes drawn in `currentColor`, so colour is one
   declaration per platform and nothing else about them changes: same path,
   different ink. `data-ow-marks` on the document decides, which is why it
   reaches the links bar AND the platform marks on a Post card in one rule —
   "on their platforms and posts" is one switch, not two features.

   MONO IS EXPLICIT, not merely the absence of colour: a favicon is a colour
   bitmap we do not control, so the black-and-white choice has to desaturate it
   rather than hope. */
html[data-ow-marks="color"] .ow-linkbar__i[data-platform],
html[data-ow-marks="color"] .po-card__from__i[data-platform]{color:var(--mk, currentColor)}
html[data-ow-marks="mono"] .ow-mk__site img,
html[data-ow-marks="mono"] .po-card__from__i img{filter:grayscale(1) contrast(1.05)}

/* a site's own icon, laid over the generic mark that is its fallback */
.ow-mk__site{position:relative;display:grid;place-items:center;width:100%;height:100%}
.ow-mk__site > svg{width:100%;height:100%}
.ow-mk__site > img{position:absolute;inset:0;width:100%;height:100%;
  object-fit:contain;border-radius:3px;background:transparent}

/* ── THE LINK EDITOR: ROWS, NOT A TEXT BLOB (founder/496) ─────────────────
   A link is a thing with a mark and a name, so it is drawn as one. The add
   box sits above the rows because that is the order of the action: paste,
   Enter, and the row appears under your hands. */
.ow-lnked__add{display:flex;gap:8px;align-items:center}
.ow-lnked__add .ow-you__in{flex:1 1 auto;min-width:0}
.ow-lnked__go{flex:none;appearance:none;-webkit-appearance:none;border:0;cursor:pointer;
  padding:10px 14px;border-radius:12px 10px 13px 11px;
  font-family:var(--ow-font);font-size:13px;font-weight:600;color:#fff;
  background:linear-gradient(160deg,rgb(var(--ow-h1)),rgb(var(--ow-h2)))}
.ow-lnked{display:flex;flex-direction:column;margin-top:10px}
.ow-lnked__r{display:flex;align-items:center;gap:11px;padding:9px 2px;
  border-top:1px solid var(--ow-hair,rgba(20,19,31,.10))}
.ow-lnked__r:first-child{border-top:0}
.ow-lnked__i{flex:none;width:22px;height:22px;display:grid;place-items:center;
  color:var(--ow-ink-2)}
.ow-lnked__i svg{width:100%;height:100%}
html[data-ow-marks="color"] .ow-lnked__i[data-platform]{color:var(--mk, currentColor)}
html[data-ow-marks="mono"] .ow-lnked__i img{filter:grayscale(1) contrast(1.05)}
.ow-lnked__t{min-width:0;flex:1 1 auto;display:flex;flex-direction:column;gap:1px}
.ow-lnked__t b{font-family:var(--ow-font-display);font-weight:600;font-size:13.5px;
  color:var(--ow-ink);line-height:1.2}
.ow-lnked__t span{font-size:11.5px;color:var(--ow-ink-3);line-height:1.2;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ow-lnked__x{flex:none;appearance:none;-webkit-appearance:none;border:0;background:none;
  cursor:pointer;width:28px;height:28px;display:grid;place-items:center;
  color:var(--ow-ink-3);border-radius:50%}
.ow-lnked__x svg{width:15px;height:15px;fill:none;stroke:currentColor;
  stroke-width:1.9;stroke-linecap:round}
.ow-lnked__x:hover{color:var(--ow-ink);background:rgba(var(--ow-h1),.10)}

/* ── A POST'S OWN FRAME WHERE THE FACE WAS (founder/491) ──────────────────
   Same height as every face in the list so the rows keep their rhythm; the
   WIDTH follows the media's aspect ratio (set inline from the media's own
   dimensions, bounded 9:16 to 1.91:1 like a Post card). A 9:16 clip becomes a
   tall slim frame, a landscape photo a wide one — recognisably what it is.
   Squared corners with a small radius, because a circle is a person's face and
   this is a thing they made. */
.ow-find__pic[data-cover]{width:auto;height:46px;aspect-ratio:4/5;border-radius:9px;
  background:rgba(0,0,0,.18)}
.ow-find__pic[data-cover] img,
.ow-find__pic[data-cover] video{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;display:block}
/* a video says it is one, the way the Post rows already do */
.ow-find__pic[data-cover="video"]::after{content:"";position:absolute;right:4px;bottom:4px;
  width:0;height:0;border-style:solid;border-width:4px 0 4px 7px;
  border-color:transparent transparent transparent #fff;
  filter:drop-shadow(0 1px 2px rgba(0,0,0,.6))}

/* ══ SETTINGS AND PRIVACY (founder/501) ═══════════════════════════════════
   TikTok's grammar, researched: one flat list, small grey section headings,
   a row per thing (icon · label · current value · chevron), each row opening
   its own page. Simpler: a search field first, and the value on every row so
   most visits end on the list. No cards, no plates, no glow — a settings
   list is read, not admired. */
.ow-set{max-width:640px;margin:0 auto;padding-bottom:40px}
.ow-set__h{font-family:var(--ow-font-display);font-weight:700;font-size:22px;
  letter-spacing:-.01em;margin:6px 2px 14px;color:var(--ow-ink)}
.ow-set__search{display:flex;align-items:center;gap:9px;margin:0 0 18px;
  padding:10px 13px;border-radius:12px 10px 13px 11px;
  background:color-mix(in srgb, var(--ow-ink) 7%, transparent)}
.ow-set__search svg{width:17px;height:17px;flex:none;fill:none;stroke:var(--ow-ink-3);stroke-width:1.8;stroke-linecap:round}
.ow-set__search input{flex:1 1 auto;min-width:0;border:0;outline:0;background:none;
  font-family:var(--ow-font);font-size:15px;color:var(--ow-ink)}
.ow-set__search input::placeholder{color:var(--ow-ink-3)}
.ow-set__sec{margin:0 0 20px}
.ow-set__sh{font-family:var(--ow-font);font-weight:600;font-size:12.5px;letter-spacing:.01em;
  color:var(--ow-ink-3);margin:0 2px 4px}
.ow-set__row{display:flex;align-items:center;gap:13px;width:100%;min-height:52px;
  padding:6px 2px;border:0;background:none;cursor:pointer;text-align:left;
  font-family:var(--ow-font);color:var(--ow-ink);text-decoration:none;
  -webkit-tap-highlight-color:transparent;position:relative}
.ow-set__row + .ow-set__row::before{content:"";position:absolute;left:37px;right:0;top:0;height:1px;
  background:var(--ow-hair,rgba(20,19,31,.10))}
html[data-theme="dark"] .ow-set__row + .ow-set__row::before{background:rgba(255,255,255,.08)}
.ow-set__row:active{background:color-mix(in srgb, var(--ow-ink) 5%, transparent)}
@media (hover:hover) and (pointer:fine){ .ow-set__row:hover{background:color-mix(in srgb, var(--ow-ink) 4%, transparent)} }
.ow-set__ic{flex:none;width:24px;height:24px;display:grid;place-items:center}
.ow-set__ic svg{width:21px;height:21px;fill:none;stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round}
.ow-set__lb{flex:1 1 auto;min-width:0;font-size:15px;font-weight:500}
.ow-set__v{flex:0 1 auto;min-width:0;max-width:46%;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;font-size:13.5px;color:var(--ow-ink-3)}
.ow-set__cv{flex:none;width:16px;height:16px;color:var(--ow-ink-3)}
.ow-set__cv svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.ow-set__row--out .ow-set__lb,.ow-set__row--out .ow-set__ic{color:#e0405a}
.ow-set__none{color:var(--ow-ink-3);font-size:14px;margin:8px 2px}

/* a row's own page slides in from the right, the way TikTok's do */
.ow-set__page.is-in{animation:ow-set-in .22s var(--ow-ease,ease) both}
@keyframes ow-set-in{from{opacity:0;transform:translateX(18px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){ .ow-set__page.is-in{animation:none} }
.ow-set__ph{display:flex;align-items:center;gap:8px;margin:2px 0 16px}
.ow-set__back{appearance:none;-webkit-appearance:none;border:0;background:none;cursor:pointer;
  width:38px;height:38px;margin-left:-8px;display:grid;place-items:center;color:var(--ow-ink);border-radius:50%}
.ow-set__back svg{width:22px;height:22px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.ow-set__back:hover{background:color-mix(in srgb, var(--ow-ink) 6%, transparent)}
.ow-set__pt{font-family:var(--ow-font-display);font-weight:700;font-size:19px;margin:0;color:var(--ow-ink)}
.ow-set__pb{display:flex;flex-direction:column;gap:12px}
.ow-set__pb > .ow-btn{align-self:flex-start}
.ow-set__lede{margin:0;font-size:14px;line-height:1.5;color:var(--ow-ink-2)}
.ow-set__big{margin:0;font-family:var(--ow-font-display);font-weight:700;font-size:20px;color:var(--ow-ink);word-break:break-all}
.ow-set__step{margin:6px 0 0;font-weight:600;font-size:14.5px;color:var(--ow-ink)}
.ow-set__f{display:flex;flex-direction:column;gap:6px}
.ow-set__fl{font-size:12.5px;font-weight:600;color:var(--ow-ink-3)}
.ow-set__at input{padding-left:26px}
.ow-set__at{position:relative}
.ow-set__at::after{content:"@";position:absolute;left:12px;bottom:11px;color:var(--ow-ink-3);font-size:15px}
.ow-set__say{margin:0;font-size:13.5px;color:var(--ow-ink-2)}
.ow-set__say[data-bad="1"]{color:#e0405a}
.ow-set__key{margin:0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:15px;
  letter-spacing:.06em;word-break:break-all;color:var(--ow-ink);
  padding:12px 14px;border-radius:12px 10px 13px 11px;background:color-mix(in srgb, var(--ow-ink) 6%, transparent)}
.ow-set__codes{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
.ow-set__codes code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:14.5px;text-align:center;
  padding:9px 6px;border-radius:10px;background:color-mix(in srgb, var(--ow-ink) 6%, transparent);color:var(--ow-ink)}
.ow-set__sess{display:flex;align-items:center;gap:13px;min-height:48px;font-size:14.5px;color:var(--ow-ink)}
/* a site you signed in to: its name, then where it lives and what it gets */
.ow-set__site{padding:6px 0}
.ow-set__site .ow-set__lb small{display:block;margin-top:2px;font-size:12.5px;font-weight:400;
  color:var(--ow-ink-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* the act sits at the row's end at its own size; .ow-btn is full-width by
   default and took half the row, cutting the address */
.ow-set__site > .ow-btn{flex:none;width:auto;padding:9px 16px}
/* the one act in Settings that cannot be undone: the Log out red, filled */
.ow-btn.ow-set__danger{background:#e0405a;color:#fff;border-color:transparent;
  box-shadow:0 8px 18px rgba(224,64,90,.28)}
.ow-btn.ow-set__danger:hover{background:#cf3550}
/* the moved sections sit in a page now; their own heading repeats the page
   title, so it steps back */
.ow-set__pb .ow-cust__row{margin:0}

/* ══ ONE POST, MANY PLATFORMS (founder/503) ══════════════════════════════════
   A platform is its own mark, in its own colour when colour logos are on. The
   Settings row and the composer's chip draw the same mark. */
.ow-reach__mk{flex:none;width:34px;height:34px;display:grid;place-items:center;border-radius:10px 9px 11px 10px;
  color:var(--ow-ink);background:color-mix(in srgb, var(--ow-ink) 6%, transparent)}
.ow-reach__mk svg{width:19px;height:19px;display:block}
.ow-reach__mk img{width:19px;height:19px}
html[data-ow-marks="color"] .ow-reach__mk[data-platform]{color:var(--mk, currentColor)}
html[data-theme="dark"][data-ow-marks="color"] .ow-reach__mk[data-platform="x"],
html[data-theme="dark"][data-ow-marks="color"] .ow-reach__mk[data-platform="threads"]{color:var(--ow-ink)}
.ow-reach__row{display:flex;align-items:center;gap:13px;min-height:56px;padding:4px 0}
.ow-reach__w{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:1px}
.ow-reach__w b{font-size:15px;font-weight:550;color:var(--ow-ink)}
.ow-reach__acct{font-size:13px;color:var(--ow-ink-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ow-reach__note{font-size:12.5px;color:var(--ow-ink-2)}
.ow-reach__note:empty{display:none}
.ow-reach__go{flex:none}
/* IMPORT EVERYTHING (founder/597) — under a connected account, aligned with
   its name (mark 34 + gap 13), so it reads as that account's own line */
.ow-reach__imp{display:flex;align-items:center;gap:12px;margin:-2px 0 10px 47px;padding:9px 0 0;
  border-top:1px solid var(--ow-line, rgba(127,127,127,.18))}
.ow-reach__imp-w{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}
.ow-reach__imp-t{font-size:13.5px;color:var(--ow-ink-2);line-height:1.35}
.ow-reach__imp-s{font-size:12.5px;color:var(--ow-ink-3);line-height:1.35}
.ow-reach__imp-s:empty{display:none}
.ow-reach__imp > .po-act{flex:none}
.ow-reach__imp[data-busy="1"] .ow-reach__imp-t{color:var(--ow-ink)}
.ow-reach__imp[data-busy="1"] .ow-reach__imp-t::after{content:"";display:inline-block;width:6px;height:6px;margin-left:8px;
  border-radius:50%;background:currentColor;vertical-align:middle;animation:ow-imp-pulse 1.2s ease-in-out infinite}
@keyframes ow-imp-pulse{0%,100%{opacity:.25}50%{opacity:1}}
@media (prefers-reduced-motion: reduce){.ow-reach__imp[data-busy="1"] .ow-reach__imp-t::after{animation:none}}
/* a narrow pane (the founder's is 290px): the words take the whole line and
   the act sits under them, rather than the words wrapping every third word */
@media (max-width: 359px){
  .ow-reach__imp{flex-wrap:wrap;row-gap:9px}
  .ow-reach__imp-w{flex-basis:100%}
}

/* the composer's row: marks you tap, lit when chosen */
.ow-reach{display:flex;flex-direction:column;gap:8px;margin:10px 0 2px}
.ow-reach__lbl{font-size:12.5px;font-weight:600;color:var(--ow-ink-3)}
.ow-reach__chips{display:flex;gap:10px;overflow-x:auto;scrollbar-width:none;padding:2px 2px 6px}
.ow-reach__chips::-webkit-scrollbar{display:none}
.ow-reach__chip{flex:none;display:flex;flex-direction:column;align-items:center;gap:5px;width:56px;border:0;background:none;
  padding:0;cursor:pointer;font-family:var(--ow-font);color:var(--ow-ink-2);opacity:.5;transition:opacity .18s}
.ow-reach__chip[aria-pressed="true"]{opacity:1;color:var(--ow-ink)}
.ow-reach__chip[aria-pressed="true"] .ow-reach__mk{box-shadow:inset 0 0 0 2px currentColor}
.ow-reach__chip[data-off="1"]{opacity:.38}
.ow-reach__chip[data-connect="1"]{opacity:.9}
.ow-reach__chip[data-connect="1"] .ow-reach__mk{position:relative}
.ow-reach__chip[data-connect="1"] .ow-reach__mk::after{content:"+";position:absolute;right:-4px;bottom:-4px;
  width:15px;height:15px;border-radius:50%;background:rgb(var(--ow-h1));color:#fff;
  font:700 12px/15px var(--ow-font);text-align:center;box-shadow:0 0 0 2px var(--ow-ground,#000)}
.ow-reach__chip span.ow-reach__n{font-size:11px;line-height:1.2;max-width:56px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ow-reach__why{margin:0;font-size:12.5px;color:var(--ow-ink-2)}
.ow-reach__why:empty{display:none}
.ow-reach__went{margin:6px 0 0;padding:0;list-style:none;display:flex;flex-direction:column;gap:6px}
.ow-reach__went li{display:flex;align-items:center;gap:10px;font-size:13.5px;color:var(--ow-ink-2)}
.ow-reach__went li .ow-reach__mk{width:26px;height:26px;border-radius:8px}
.ow-reach__went li .ow-reach__mk svg{width:15px;height:15px}
.ow-reach__went li[data-state="failed"]{color:#e0405a}
/* CONNECTING WITH YOUR OWN ACCESS (Bluesky, Mastodon, Telegram, Discord):
   the fields open under the row, aligned with its name, never over the page */
.ow-reach__form{display:flex;flex-direction:column;gap:12px;margin:2px 0 14px 47px}
.ow-reach__why .ow-reach__form,.ow-reach__slot:empty{margin-left:0}
.ow-reach__slot:empty{display:none}
.ow-reach__help{font-size:12.5px;color:var(--ow-ink-3);line-height:1.4}
.ow-reach__formrow{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.ow-reach__said{font-size:13px;color:var(--ow-ink-2);line-height:1.4}
.ow-reach__said:empty{display:none}

/* ══ THE UPLOAD SCREEN (founder/493) — TikTok's Post screen ═════════════════
   The words beside the cover, then where it goes, then one Post across the
   bottom. */
.ow-upload__top{display:flex;align-items:center;gap:6px;margin:0 0 12px}
.ow-upload__back{appearance:none;-webkit-appearance:none;border:0;background:none;cursor:pointer;width:38px;height:38px;
  margin-left:-8px;display:grid;place-items:center;color:var(--ow-ink);border-radius:50%}
.ow-upload__back svg{width:22px;height:22px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.ow-upload__h{margin:0;font-family:var(--ow-font-display);font-weight:700;font-size:19px;color:var(--ow-ink)}
.ow-upload__row{display:flex;gap:12px;align-items:stretch;margin:0 0 10px}
.ow-upload__cap{flex:1 1 auto;min-width:0;display:flex}
.ow-upload__cap textarea{flex:1 1 auto;min-height:128px;resize:vertical}
.ow-upload__cover{position:relative;flex:none;width:96px;aspect-ratio:3/4;border-radius:12px 10px 13px 11px;overflow:hidden;
  background:color-mix(in srgb, var(--ow-ink) 8%, transparent)}
.ow-upload__cover img,.ow-upload__cover video{width:100%;height:100%;object-fit:cover;display:block}
.ow-upload__n{position:absolute;right:6px;bottom:6px;min-width:20px;height:20px;padding:0 6px;border-radius:10px;
  display:grid;place-items:center;font-size:11.5px;font-weight:650;color:#fff;background:rgba(0,0,0,.55)}
.ow-upload__post{width:100%;margin-top:16px}
/* WHAT YOU MADE IS ITS OWN SCREEN (founder/493). From the first thing attached
   to the receipt, the composer's stage takes the viewport the way the lens
   does, on the world's own floor, and the dock steps aside. `data-ow-make` is
   on the document because the dock is not inside this surface; the composer
   clears it on every way out (`ow:navigate`, the camera, the receipt). */
html[data-ow-make] .ow-compose__stage{position:fixed;inset:0;z-index:150;margin:0;border-radius:0;
  overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;
  background:var(--ow-ground,#eceaf3);
  padding:calc(env(safe-area-inset-top) + 12px) max(16px,env(safe-area-inset-right))
          calc(env(safe-area-inset-bottom) + 28px) max(16px,env(safe-area-inset-left))}
html[data-ow-make] .ow-nav{display:none}
html[data-ow-make],html[data-ow-make] body{overflow:hidden}
html[data-ow-make] .ow-compose__stage > *{max-width:640px;margin-left:auto;margin-right:auto}
/* on its own screen the destination row reads as a section, label above */
.ow-upload__where .ow-compose__where,
html[data-ow-make] .ow-compose__where{flex-direction:column;align-items:stretch;gap:8px}
html[data-ow-make] .ow-compose__wherelbl{font-family:var(--ow-font);letter-spacing:0;font-size:12.5px;color:var(--ow-ink-3)}

/* ══ THE POST SCREEN'S ROWS, TIKTOK'S WAY (founder/510) ═══════════════════════
   Each decision is a labelled row with its answer and a chevron (the Settings
   row), each opening its own page; a choice among a few is a row with a check.
   Post sits across the bottom, where the thumb already is. */
.ow-upload__rows{margin:14px 0 0}
.ow-upload__bar{position:sticky;bottom:0;z-index:2;margin:18px -2px 0;
  padding:12px 2px calc(env(safe-area-inset-bottom) + 6px);
  background:linear-gradient(to bottom, transparent, var(--ow-ground,#eceaf3) 28%)}
.ow-upload__bar .ow-upload__post{margin-top:0}
.ow-setpick{min-height:56px}
.ow-setpick__w{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}
.ow-setpick__w b{font-size:15px;font-weight:550;color:var(--ow-ink)}
.ow-setpick__w span{font-size:13px;color:var(--ow-ink-3);line-height:1.35}
.ow-setpick__ok{flex:none;width:22px;height:22px;color:var(--ow-ink)}
.ow-setpick__ok svg{width:22px;height:22px;fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round}
.ow-setpick__face{width:34px;height:34px;flex:none;font-size:12px;border-radius:10px 9px 11px 10px}

/* ══ A CHAT'S SETTINGS, TIKTOK'S WAY (founder/510) ═══════════════════════════
   Behind the "…" in the chat's header: its own page with a back arrow, the
   Settings rows and sections. The chat waits behind it. */
.ow-thread-head__more{appearance:none;-webkit-appearance:none;border:0;background:none;cursor:pointer;
  flex:none;width:38px;height:38px;margin-left:auto;display:grid;place-items:center;color:var(--ow-ink);border-radius:50%}
.ow-thread-head__more svg{width:22px;height:22px;fill:currentColor}
.ow-thread-head__more:hover{background:color-mix(in srgb, var(--ow-ink) 6%, transparent)}
[data-chatset="1"] > :not(.ow-chatset){display:none !important}
.ow-chatset{max-width:640px;margin:0 auto;padding-bottom:40px}
.ow-chatset__who{display:flex;flex-direction:column;align-items:center;gap:8px;margin:4px 0 22px;text-align:center}
.ow-chatset__face{width:84px;height:84px;border-radius:50%;overflow:hidden;display:grid;place-items:center;
  background:color-mix(in srgb, var(--ow-ink) 8%, transparent);color:var(--ow-ink-2)}
.ow-chatset__face img{width:100%;height:100%;object-fit:cover}
.ow-chatset__face svg{width:52px;height:52px}
/* People and admins (founder/549): each person's face in their own colour,
   their role in words, and admins marked so the list can be read at a glance */
.ow-people__row{gap:12px;cursor:default}
.ow-people__av{width:38px;height:38px;font-size:13px}
.ow-people__av svg{width:24px;height:24px}
.ow-people__badge{flex:none;margin-left:auto;font-size:11.5px;font-weight:650;letter-spacing:.01em;
  padding:4px 10px;border-radius:999px;color:var(--ow-ink);
  background:rgba(var(--ow-h1),.18);box-shadow:inset 0 0 0 1px rgba(var(--ow-h1),.45)}
.ow-people__open{margin:2px 0 14px}
.ow-chatset__nm{display:flex;flex-direction:column;gap:2px}
.ow-chatset__nm b{font-family:var(--ow-font-display);font-size:18px;font-weight:700;color:var(--ow-ink)}
.ow-chatset__nm span{font-size:13px;color:var(--ow-ink-3)}
.ow-chatset .ow-set__row--out .ow-set__lb,.ow-chatset .ow-set__row--out .ow-set__ic{color:#e0405a}
/* the inbox's header and the New message page (founder/510) */
.ow-inbox__top{display:flex;align-items:center;gap:8px;margin:2px 0 12px}
.ow-inbox__h{margin:0;font-family:var(--ow-font-display);font-weight:700;font-size:22px;color:var(--ow-ink)}
.ow-inbox__top .ow-thread-head__more svg{fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round}
.ow-newmsg__p{min-height:58px}
.ow-newmsg__face{flex:none;width:40px;height:40px;border-radius:50%;overflow:hidden;display:grid;place-items:center;
  background:color-mix(in srgb, var(--ow-ink) 8%, transparent);color:var(--ow-ink-2)}
.ow-newmsg__face img{width:100%;height:100%;object-fit:cover}
.ow-newmsg__face svg{width:26px;height:26px}

/* THE EDIT SCREEN SHOWS WHAT YOU MADE, LARGE (founder/510-511) — TikTok's
   edit screen is the media, not a thumbnail in a corner. On the making screen
   the picks are a swipeable row of full-height media (the whole width when
   there is one), and Next / Send to a friend sit across the bottom. */
html[data-ow-make] .ow-compose__picks{display:flex;gap:10px;overflow-x:auto;scroll-snap-type:x mandatory;
  scrollbar-width:none;margin:0 -16px;padding:0 16px 2px}
html[data-ow-make] .ow-compose__picks::-webkit-scrollbar{display:none}
html[data-ow-make] .ow-compose__pick{flex:0 0 84%;aspect-ratio:auto;height:min(60vh,560px);scroll-snap-align:center;
  border-radius:16px 13px 17px 14px;background:#0b0a14}
html[data-ow-make] .ow-compose__picks[data-n="1"] .ow-compose__pick{flex-basis:100%}
html[data-ow-make] .ow-compose__pick img,
html[data-ow-make] .ow-compose__pick video{object-fit:contain}
html[data-ow-make] .ow-compose__stage > .po-act{margin-top:10px}
html[data-ow-make] .ow-compose__send{position:sticky;bottom:0;z-index:2;margin-top:14px;
  padding:12px 0 calc(env(safe-area-inset-bottom) + 6px);
  background:linear-gradient(to bottom, transparent, var(--ow-ground,#eceaf3) 28%)}
/* the act across the bottom of the making screen is AT the bottom, however
   short the page (TikTok's Next and Post), not wherever the content ends */
html[data-ow-make] .ow-compose__stage{display:flex;flex-direction:column}
html[data-ow-make] .ow-compose__stage > *{width:100%;flex:none}
html[data-ow-make] .ow-compose__send,
html[data-ow-make] .ow-upload__bar{margin-top:auto}
html[data-ow-make] .ow-upload__rows{margin-bottom:18px}
.ow-setpick[data-off="1"] .ow-setpick__w b,.ow-setpick[data-off="1"] .ow-reach__mk{opacity:.55}
.ow-setpick .ow-reach__mk.ow-setpick__face{width:34px;height:34px}
.ow-dest__face.ow-setpick__face[data-shape="round"]{border-radius:50%;background:color-mix(in srgb, var(--ow-ink) 8%, transparent);color:var(--ow-ink-2)}
.ow-dest__face.ow-setpick__face svg{width:22px;height:22px}
/* the small acts keep their own size on the making screen: only the media
   and the bar across the bottom run edge to edge */
html[data-ow-make] .ow-compose__stage > .po-act,
html[data-ow-make] .ow-compose__stage > .ow-depth__add{width:auto;align-self:flex-start}
html[data-ow-make] .ow-compose__stage .ow-compose__picks[data-n="1"] > .ow-compose__pick{flex:0 0 100%}
/* the media takes ITS OWN shape (a landscape photo is a landscape box, a
   portrait video a tall one), up to 60% of the screen, full width — no
   letterbox bars around a photo that did not need them */
html[data-ow-make] .ow-compose__stage > .ow-compose__picks{width:calc(100% + 32px);max-width:none;margin-left:-16px;margin-right:-16px;align-items:flex-start}
html[data-ow-make] .ow-compose__pick{height:auto;max-height:60vh;display:flex;align-items:center;justify-content:center}
html[data-ow-make] .ow-compose__pick img,
html[data-ow-make] .ow-compose__pick video{height:auto;max-height:60vh;width:100%}
html[data-ow-make] .ow-compose__stage > .po-act,
html[data-ow-make] .ow-compose__stage > .ow-depth__add{margin-left:0;margin-right:auto}

/* ── A NOTIFICATION LEADS WITH WHO (founder/513 tour) ─────────────────────
   The face where the eye starts; the unread light moves to the far edge,
   centred on the row, so a read row has no blank gutter. */
.ow-note{align-items:center}
.ow-note__face{flex:none;width:40px;height:40px;border-radius:50%;overflow:hidden;
  display:grid;place-items:center;background:rgba(var(--ow-h1),.12)}
.ow-note__face img{width:100%;height:100%;object-fit:cover;display:block}
.ow-note__face svg{width:26px;height:26px}
.ow-note__b{flex:1 1 auto}
.ow-note__dot{order:3;margin:0 0 0 auto;align-self:center;box-shadow:none}

/* ── A COMPOSER OUTSIDE A THREAD SITS ABOVE THE DOCK (founder/513 tour) ───
   `.ow-composer` is sticky at `bottom:0`, and below 900px the dock is a pill
   floating 10px off the bottom edge — so on Understanding the "Ask your
   Brain" field and its send button were drawn UNDER the dock, half covered.
   Inside a thread the dock is hidden and the composer owns the edge (above);
   everywhere else it stops at the dock's top: 48px of pill, its 10px lift,
   and a little air. */
@media (max-width:899px){
  html:not([data-ow-thread]) .ow-composer{
    bottom:calc(66px + env(safe-area-inset-bottom));
    background:none}
}

/* ── A TEXT POST IN A LATEST TILE IS SET FOR THE TILE (founder/513 tour) ──
   `.po-card[data-text]` sets a text Post at 16px and lifts its body 6px into
   the name row — right in a feed, where the name row is there to be lifted
   into. In a Latest tile the name row is often the first thing the ladder
   drops, and then the lift carried the first line OVER the card's edge:
   measured at 390px, the body began at 176px on a card whose face began at
   182px, and "We are on the boardwalk until six." ran four lines of 16px in a
   119px column with its first line cut. Tile-sized type, no lift, and the
   card's own inset above the words. */
.ow-cv-posts .po-card[data-text] .po-card__d{font-size:13.5px;line-height:1.38}
.ow-cv-posts .po-card[data-text] .po-card__body{margin-top:0}
.ow-cv-posts[data-who="0"] .po-card[data-text] .po-card__body{padding-top:10px}

/* ── A CENTER'S OWN SITE FILLS THE SCREEN BENEATH ITS TABS (founder/516) ──
   The frame was `min-height:72vh` — on a phone a short window inside a
   scrolling page, the site scrolling inside the Center scrolling. Beneath the
   Center's framing it is the page: the screen's height less the tab row, so
   once the tabs reach the top the site is all there is, and it scrolls as
   itself. */
#surface .ow-mirror iframe{height:calc(100dvh - 120px);min-height:480px}
/* and it carries a ground of its own there, or the list scrolls visibly
   through the field (founder/513 tour, Understanding) */
@media (max-width:899px){
  html:not([data-ow-thread]) .ow-composer{
    background:linear-gradient(0deg,var(--ow-chrome) 58%,transparent);
    padding-top:18px;margin:0 -18px;padding-left:18px;padding-right:18px}
}

/* ═══ INK ON A PERSON'S OWN LIGHT (founder/519 contrast sweep) ════════════
   Every CHOSEN thing in the App is filled with the person's (or the Center's)
   hue and written in white — "Following", the Feed/Scroll switch, For You, a
   chosen theme, the letter on a face, your own message bubble. White is right
   on a deep hue and illegible on a light one: loop.ada's cyan measured 1.2:1
   to 2.2:1 (tools/probe/contrast-sweep.cjs, 28 pages), the Test Hotel's pink
   2.1:1. The hue is the person's to choose, so the ink cannot be fixed; it is
   DERIVED from the fill — near-black when the fill is light, white when it is
   deep — by relative colour, on the element that paints, so a Center's scope
   and a tween frame get the right ink for their own hue.

   Y is the fill's luma on 0–255; above 125, white falls under 4.5:1. The
   ×100 makes the switch sharp; the floors are the App's dark ink (20,24,40). */
@supports (color: rgb(from red r g b)) {
  .ow-worlds__b.is-here,
  .ow-feedswitch button[aria-pressed="true"],
  .ow-choice .po-act[data-act="on"], .ow-choice .po-act[aria-pressed="true"],
  .ow-find__chip[aria-pressed="true"],
  .ow-compose__step[aria-current="step"]{
    color:rgb(from rgb(var(--ow-h1))
      clamp(20, (125 - (0.2126*r + 0.7152*g + 0.0722*b)) * 100, 255)
      clamp(24, (125 - (0.2126*r + 0.7152*g + 0.0722*b)) * 100, 255)
      clamp(40, (125 - (0.2126*r + 0.7152*g + 0.0722*b)) * 100, 255))}
  .po-act[data-act="on"]{
    color:rgb(from rgb(var(--h, var(--ow-h1)))
      clamp(20, (125 - (0.2126*r + 0.7152*g + 0.0722*b)) * 100, 255)
      clamp(24, (125 - (0.2126*r + 0.7152*g + 0.0722*b)) * 100, 255)
      clamp(40, (125 - (0.2126*r + 0.7152*g + 0.0722*b)) * 100, 255))}
  #surface .ow-cv-row__av, .ow-thread__av{
    color:rgb(from rgb(var(--ow-h1))
      clamp(20, (125 - (0.2126*r + 0.7152*g + 0.0722*b)) * 100, 255)
      clamp(24, (125 - (0.2126*r + 0.7152*g + 0.0722*b)) * 100, 255)
      clamp(40, (125 - (0.2126*r + 0.7152*g + 0.0722*b)) * 100, 255))}
  /* your own bubble is the hue deepened 82% toward ink; the words ride that */
  .ow-msg[data-mine]{
    color:rgb(from color-mix(in oklab, rgb(var(--ow-h1)) 82%, #14131f)
      clamp(20, (125 - (0.2126*r + 0.7152*g + 0.0722*b)) * 100, 255)
      clamp(24, (125 - (0.2126*r + 0.7152*g + 0.0722*b)) * 100, 255)
      clamp(40, (125 - (0.2126*r + 0.7152*g + 0.0722*b)) * 100, 255))}
  .ow-msg[data-mine] .ow-rx__c{color:inherit}
}
/* THE HUE AS INK, ON A LIGHT GROUND, IS THE DEEP END OF ITSELF. A pressed
   count ("1" saved) and the "Creator" mark were written in the raw hue — on
   #eceaf3 a cyan measured 1.0:1. In the light room they take the hue mixed
   halfway to ink: still unmistakably the person's colour, and readable. */
html[data-theme="light"] .po-card__foot .po-acts .po-act[aria-pressed="true"],
html[data-theme="light"] .po-acts--rail .po-act[aria-pressed="true"],
html[data-theme="light"] .ow-cmt__creator{
  color:color-mix(in oklab, rgb(var(--h, var(--ow-h1))) 48%, #14131f)}
@media (prefers-color-scheme: light){
  html:not([data-theme="dark"]) .po-card__foot .po-acts .po-act[aria-pressed="true"],
  html:not([data-theme="dark"]) .po-acts--rail .po-act[aria-pressed="true"],
  html:not([data-theme="dark"]) .ow-cmt__creator{
    color:color-mix(in oklab, rgb(var(--h, var(--ow-h1))) 48%, #14131f)}
}
/* A PRESSED ACT'S FILL IS THE HUE AT .45–.85 OVER THE GROUND, so on a light
   ground it is much lighter than the hue — the Test Hotel's deep pink fills as
   (248,142,248). The ink is derived from the fill as it lands: the hue mixed
   65% into the room's ground. */
@supports (color: rgb(from red r g b)) {
  html[data-theme="light"] .po-act[data-act="on"]{
    color:rgb(from color-mix(in srgb, rgb(var(--h, var(--ow-h1))) 65%, #eceaf3)
      clamp(20, (125 - (0.2126*r + 0.7152*g + 0.0722*b)) * 100, 255)
      clamp(24, (125 - (0.2126*r + 0.7152*g + 0.0722*b)) * 100, 255)
      clamp(40, (125 - (0.2126*r + 0.7152*g + 0.0722*b)) * 100, 255))}
  @media (prefers-color-scheme: light){
    html:not([data-theme="dark"]) .po-act[data-act="on"]{
      color:rgb(from color-mix(in srgb, rgb(var(--h, var(--ow-h1))) 65%, #eceaf3)
        clamp(20, (125 - (0.2126*r + 0.7152*g + 0.0722*b)) * 100, 255)
        clamp(24, (125 - (0.2126*r + 0.7152*g + 0.0722*b)) * 100, 255)
        clamp(40, (125 - (0.2126*r + 0.7152*g + 0.0722*b)) * 100, 255))}
  }
}
/* a REMOVED message of yours keeps the bubble's shape but not its fill, so
   the ink derived from the full bubble was dark on a dark ground in the dark
   room (2.0:1, contrast-sweep dark). What stands in its place is secondary
   text, in the room's own secondary ink, whichever room it is. */
.ow-msg[data-mine]:has(.ow-msg__gone){color:var(--ow-ink-2)}

/* ── A BLACK BRAND ON A DARK GROUND IS WHITE (founder/519) ────────────────
   X, Threads and GitHub publish BLACK as their brand colour, so in colour
   mode on the dark room their marks were black on black — the X in the Four
   Queens link row was simply missing. Those platforms show their mark white
   on dark themselves; so does this. */
html[data-theme="dark"][data-ow-marks="color"] :is(.ow-linkbar__i,.po-card__from__i,.ow-lnked__i,.ow-reach__mk):is([data-platform="x"],[data-platform="threads"],[data-platform="github"]){color:#f2f2f6}
@media (prefers-color-scheme: dark){
  html:not([data-theme="light"])[data-ow-marks="color"] :is(.ow-linkbar__i,.po-card__from__i,.ow-lnked__i,.ow-reach__mk):is([data-platform="x"],[data-platform="threads"],[data-platform="github"]){color:#f2f2f6}
}

/* ═══ THE CALENDAR (founder/520; EXPERIENCE.md "Calendar") ═══════════════
   A day heading, then its plans: the hour in its own column (the place's
   local hour), what and where beside it, the status at the edge. The rows
   are the App's panel material, one per plan. */
.ow-cal{display:flex;flex-direction:column;padding-bottom:24px}
.ow-cal__day{font-family:var(--ow-font);font-size:13px;font-weight:600;color:var(--ow-ink-2);
  margin:20px 2px 8px;letter-spacing:.005em}
.ow-cal__day:first-child{margin-top:6px}
.ow-cal__row{display:flex;align-items:center;gap:14px;width:100%;text-align:left;cursor:pointer;
  padding:13px 15px;margin:0 0 8px;border:0;border-radius:17px 14px 18px 15px;font:inherit;color:var(--ow-ink);
  background:linear-gradient(178deg,var(--ow-panel-1),var(--ow-panel-2));
  box-shadow:inset 0 1px 0 var(--ow-lift),inset 0 0 0 1px var(--ow-rim)}
.ow-cal__row:disabled{cursor:default}
.ow-cal__time{flex:none;width:64px;display:flex;flex-direction:column;gap:2px}
.ow-cal__time b{font-size:15px;font-weight:650;letter-spacing:-.01em}
.ow-cal__time span{font-size:11px;color:var(--ow-ink-3)}
.ow-cal__b{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px;
  padding-left:14px;box-shadow:inset 2px 0 0 rgba(var(--ow-h1),.55)}
.ow-cal__t{font-size:15px;font-weight:600;line-height:1.3}
.ow-cal__s{font-size:12.5px;color:var(--ow-ink-2);line-height:1.4}
.ow-cal__st{flex:none;font-size:11.5px;color:var(--ow-ink-3)}
.ow-cal__st[data-status="cancelled"]{text-decoration:line-through}
.ow-cal__past{opacity:.75}
.ow-cal__head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin:6px 2px 4px}
.ow-cal__h{font-family:var(--ow-font);font-size:28px;font-weight:650;letter-spacing:-.02em;margin:0;color:var(--ow-ink)}
.ow-cal__n{font-size:13px;color:var(--ow-ink-3)}
.ow-cal__earlier{font-family:var(--ow-font);font-size:17px;font-weight:650;margin:26px 2px 0;color:var(--ow-ink-2)}
/* "What you have coming" names the whole of it on the right: the Calendar */
.ow-cpage__all{background:none;border:0;padding:0;cursor:pointer;font:inherit;font-size:13px;font-weight:600;
  color:color-mix(in oklab, rgb(var(--ow-h1)) 55%, var(--ow-ink))}

/* the rest of a band, one press away (founder/520) */
.ow-cpage__more{display:inline-flex;align-items:center;border:0;cursor:pointer;font:inherit;font-size:13px;font-weight:600;
  padding:8px 14px;border-radius:999px;color:var(--ow-ink-2);background:rgba(var(--ow-h1),.08);
  box-shadow:inset 0 0 0 1px rgba(var(--ow-h1),.28)}

/* ═══ A GUEST'S QUESTION TO A CENTER (EXPERIENCE.md, Guest Communication) ══ */
.ow-guest{display:flex;flex-direction:column;gap:9px;margin:14px 0 4px;padding:16px;
  border-radius:18px 15px 19px 16px;background:linear-gradient(178deg,var(--ow-panel-1),var(--ow-panel-2));
  box-shadow:inset 0 1px 0 var(--ow-lift),inset 0 0 0 1px var(--ow-rim)}
.ow-guest__h{font-family:var(--ow-font);font-size:17px;font-weight:650;margin:0;color:var(--ow-ink)}
.ow-guest__p{font-size:13px;color:var(--ow-ink-2);margin:0 0 4px;line-height:1.45}
.ow-guest textarea,.ow-guest input{font:inherit;font-size:15px;color:var(--ow-ink);padding:11px 13px;border:0;border-radius:13px;
  background:color-mix(in srgb,var(--ow-tile-base) 80%,transparent);box-shadow:inset 0 0 0 1px var(--ow-rim);resize:vertical}
.ow-guest textarea:focus,.ow-guest input:focus{outline:none;box-shadow:0 0 0 1.5px rgba(var(--ow-h1),.7)}
.ow-guest__said{font-size:13px;color:var(--ow-ink-2);margin:0;min-height:0}
.ow-guest__said:empty{display:none}
.ow-guest__acts{display:flex;gap:8px}
.ow-guest__acts .ow-btn{margin-top:0}

/* the map's own credit and controls wear the platform's face too — Leaflet
   ships "Helvetica Neue, Arial" (founder/530 font audit, Discovery) */
.leaflet-container,.leaflet-container .leaflet-control-attribution{font-family:var(--ow-font)}

/* ══ CHAT RULES (founder/160, 541) — a rule with several answers: its name,
   then one pill per answer, the chosen one lit. The switches are .ow-switch. */
.ow-rulech{padding:12px 14px;border-radius:16px 13px 17px 14px;
  background:linear-gradient(178deg,var(--ow-panel-1),var(--ow-panel-2));
  box-shadow:inset 0 1px 0 var(--ow-lift),inset 0 0 0 1px var(--ow-rim)}
.ow-switch + .ow-rulech,.ow-rulech + .ow-rulech,.ow-rulech + .ow-switch{margin-top:8px}
.ow-rulech__l{display:block;font-family:var(--ow-font-display);font-weight:600;font-size:14.5px;
  letter-spacing:-.01em;color:var(--ow-ink)}
.ow-rulech__o{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}
.ow-rulech__b{font-size:13px}
.ow-rulech__b[aria-pressed="true"]{color:#fff;
  background:linear-gradient(150deg,rgb(var(--ow-h1)),rgba(var(--ow-h2),.85))}
.ow-rulech__b:disabled{opacity:.6;cursor:default}
.ow-rulech__b[aria-pressed="true"]:disabled{opacity:.85}

/* ══ THE CHAT SCREEN — founder/550, 551, 552 ═══════════════════════════════
   ★ FOUNDER/552: *"the main issue was the acutal chat screen. it needs to feel
     iconic but also simple enough and familar enough to use every day"*
   ★ FOUNDER/551: *"dont change galaxy"* — nothing below touches the world
     behind the chat, its lines, its colour or its glow; the bubbles keep their
     own materials (your hue, their hue rim and light). What changes is how the
     chat SITS on it: a bar that stays, messages grouped the way people talk,
     the time once per run, the actions behind a press, a composer that is
     part of the screen. All of it scoped to `data-ow-thread`, the App's own
     flag for "a conversation is open", so no other surface moves. */

/* the App's bar steps aside inside a chat, as the dock already does; the
   chat's own bar is the top of the screen and it stays there */
html[data-ow-thread] .ow-top{display:none}
html[data-ow-thread] .ow-thread-head{position:sticky;top:0;z-index:110;
  display:flex;align-items:center;gap:10px;
  margin:0 calc(-1 * var(--ow-gutter,18px)) 4px;
  padding:calc(env(safe-area-inset-top) + 8px) 8px 9px 6px;
  background:rgba(var(--ow-chrome-rgb),.74);
  -webkit-backdrop-filter:blur(20px) saturate(1.25);backdrop-filter:blur(20px) saturate(1.25);
  box-shadow:0 1px 0 rgba(255,255,255,.07)}
html[data-fx="safe"][data-ow-thread] .ow-thread-head{background:rgba(var(--ow-chrome-rgb),.94)}
.ow-thread-head__back{flex:none;width:40px;height:40px;border:0;background:none;cursor:pointer;
  display:grid;place-items:center;color:var(--ow-ink);border-radius:50%}
.ow-thread-head__back svg{width:24px;height:24px;fill:none;stroke:currentColor;stroke-width:2.1;
  stroke-linecap:round;stroke-linejoin:round}
.ow-thread-head__back:active{background:rgba(var(--ow-h1),.16)}
@media (hover:hover) and (pointer:fine){ .ow-thread-head__back:hover{background:rgba(var(--ow-h1),.12)} }
html[data-ow-thread] .ow-thread-head__face{width:38px;height:38px;font-size:13px}
html[data-ow-thread] .ow-thread-head__face svg{width:24px;height:24px}
html[data-ow-thread] .ow-thread-head__b{gap:0;min-width:0}
html[data-ow-thread] .ow-thread-head__nm{font-size:16.5px;line-height:1.2;font-weight:650;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
html[data-ow-thread] .ow-thread-head__pr{margin-top:1px;font-size:12px}
/* what this chat does with messages is one quiet line, not a row of pills */
html[data-ow-thread] .ow-thread-head__facts{gap:0;margin-top:1px}
html[data-ow-thread] .ow-thread-head__facts:empty{display:none}
html[data-ow-thread] .ow-thread-head .ow-fact{font-family:var(--ow-font);font-weight:500;font-size:12px;
  letter-spacing:0;text-transform:none;padding:0;background:none;box-shadow:none;border-radius:0;
  color:var(--ow-ink-3);white-space:nowrap}
html[data-ow-thread] .ow-thread-head .ow-fact + .ow-fact::before{content:"\00b7";margin:0 6px}
html[data-ow-thread] .ow-thread-head__more{width:40px;height:40px;margin-left:auto;flex:none}

/* ── THE MESSAGES SIT ON THE COMPOSER ──────────────────────────────────────
   A short chat floated at the top with the whole screen empty under it. In
   every messenger the newest message is just above where you type, and the
   conversation grows upward from there — so the column is at least the
   screen's height and its messages are pushed to its foot. */
html[data-ow-thread] .ow-msgs{gap:0;padding:6px 0 10px;justify-content:flex-end;
  min-height:calc(100dvh - 150px - env(safe-area-inset-top) - env(safe-area-inset-bottom))}
/* runs: two pixels inside one, a breath between two */
html[data-ow-thread] .ow-msgs > *{margin-top:3px}
html[data-ow-thread] .ow-msgs > [data-gap]{margin-top:12px}
html[data-ow-thread] .ow-msgs > .ow-msg-day{margin:18px 0 6px}
html[data-ow-thread] .ow-msg-day{font-family:var(--ow-font);font-weight:600;font-size:11.5px;
  letter-spacing:0;color:var(--ow-ink-3);padding:4px 11px;border-radius:999px;
  background:rgba(var(--ow-chrome-rgb),.45)}

/* ── A MESSAGE IS WORDS IN A BUBBLE ────────────────────────────────────────
   One line of text was an 87px card: words, a time, then a row of four
   buttons. It is a bubble sized to what was said now. Rounded all round, with
   the tail corner on the last of a run — the shape everybody reads as "this
   is who said it" — and a width that leaves the other side of the screen to
   the other person. */
html[data-ow-thread] .ow-msg{position:relative;padding:8px 13px 8px;border-radius:20px;
  font-size:15.5px;line-height:1.36;max-width:min(78%,520px);outline:none;
  -webkit-tap-highlight-color:transparent;cursor:default}
html[data-ow-thread] .ow-msg-line{max-width:86%;gap:7px}
html[data-ow-thread] .ow-msg-line .ow-msg{max-width:100%}
html[data-ow-thread] .ow-msg-line:not(:has(.ow-msg-face)){padding-left:35px}
html[data-ow-thread] .ow-msg:not([data-mine])[data-last]{border-bottom-left-radius:6px}
html[data-ow-thread] .ow-msg[data-mine][data-last]{border-bottom-right-radius:6px}
html[data-ow-thread] .ow-msg-face{width:28px;height:28px}
html[data-ow-thread] .ow-msg__who{margin:0 0 3px;font-size:12px;font-weight:600;
  color:rgb(var(--h,var(--ow-h1)));opacity:.95}
/* the time: on the last message of a run, small, at its foot — and on any
   message whose actions are open. A message in the middle keeps "edited". */
html[data-ow-thread] .ow-msg__t{display:flex;justify-content:flex-end;gap:6px;margin:3px -2px -1px 10px;
  font-size:11px;line-height:1.2;opacity:.62}
html[data-ow-thread] .ow-msg:not([data-mine]) .ow-msg__t{justify-content:flex-start;margin-left:-2px}
html[data-ow-thread] .ow-msg:not([data-last]):not([data-open]) .ow-msg__tt{display:none}
html[data-ow-thread] .ow-msg:not([data-last]):not([data-open]) .ow-msg__t:not(:has(.ow-msg__ed)){display:none}
html[data-ow-thread] .ow-msg__ed{margin-left:0}
/* Sent · Read — under your last message, outside it */
html[data-ow-thread] .ow-msgs > .ow-msg__st{align-self:flex-end;margin:4px 6px 0;
  font-family:var(--ow-font);font-weight:500;font-size:11.5px;letter-spacing:0;
  color:var(--ow-ink-3);opacity:1;text-align:right}
html[data-ow-thread] .ow-msgs > .ow-msg__st[data-st="read"]{color:var(--ow-ink-2)}
/* a quoted message inside a reply */
html[data-ow-thread] .ow-msg__q{margin:1px -5px 6px;padding:6px 10px;border-radius:13px;
  border-left:3px solid rgba(255,255,255,.55)}
html[data-ow-thread] .ow-msg:not([data-mine]) .ow-msg__q{border-left-color:rgba(var(--h,var(--ow-h1)),.8);
  background:rgba(var(--h,var(--ow-h1)),.10)}
html[data-ow-thread] .ow-msg__q-who{font-size:12px;font-weight:650}
html[data-ow-thread] .ow-msg__q-b{font-size:13px;line-height:1.3}
/* a photo fills its bubble to a thin edge */
html[data-ow-thread] .ow-msg:has(.ow-msg__media){padding:4px 4px 7px}
html[data-ow-thread] .ow-msg:has(.ow-msg__media) > .ow-msg__x,
html[data-ow-thread] .ow-msg:has(.ow-msg__media) > .ow-msg__who,
html[data-ow-thread] .ow-msg:has(.ow-msg__media) > .ow-msg__q{margin-left:9px;margin-right:9px}
html[data-ow-thread] .ow-msg:has(.ow-msg__media) > .ow-msg__x{display:block;padding-top:5px}
html[data-ow-thread] .ow-msg:has(.ow-msg__media) > .ow-msg__t{margin-right:8px;margin-left:10px}
html[data-ow-thread] .ow-msg__media{margin:0 0 2px;border-radius:16px;overflow:hidden;width:100%}
html[data-ow-thread] .ow-msg__media img,
html[data-ow-thread] .ow-msg__media video{max-height:320px;border-radius:16px}
html[data-ow-thread] .ow-msg:has(.ow-msg__x) .ow-msg__media{margin-top:6px}
html[data-ow-thread] .ow-rx{margin-top:5px}

/* ── ITS ACTIONS, WHEN ASKED FOR ───────────────────────────────────────────
   A tap or a long press opens them over the message, a tap anywhere else
   closes them; a mouse also shows them on hover. One row, the same material
   as the bar above, never inside the bubble — so opening them moves nothing. */
html[data-ow-thread] .ow-msg__acts{display:none;position:absolute;bottom:calc(100% + 6px);left:-4px;
  z-index:30;margin:0;padding:4px;gap:2px;flex-wrap:nowrap;opacity:1;white-space:nowrap;
  border-radius:999px;background:rgba(var(--ow-chrome-rgb),.92);
  -webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);
  box-shadow:0 10px 30px -10px rgba(0,0,0,.65),inset 0 0 0 1px rgba(255,255,255,.08)}
html[data-ow-thread] .ow-msg[data-mine] .ow-msg__acts{left:auto;right:-4px}
html[data-ow-thread] .ow-msg__acts:empty{display:none !important}
/* the gap between the message and its row is still the message, so a mouse
   can travel up to the row without it closing */
html[data-ow-thread] .ow-msg__acts::after{content:"";position:absolute;left:0;right:0;top:100%;height:8px}
html[data-ow-thread] .ow-msg[data-open] .ow-msg__acts{display:flex}
@media (hover:hover) and (pointer:fine){
  html[data-ow-thread] .ow-msg:hover .ow-msg__acts{display:flex}
}
html[data-ow-thread] .ow-msg__act{min-height:36px;padding:0 13px;border-radius:999px;
  font-family:var(--ow-font);font-weight:600;font-size:13px;letter-spacing:0;color:var(--ow-ink)}
html[data-ow-thread] .ow-msg__act:hover,
html[data-ow-thread] .ow-msg[data-mine] .ow-msg__act:hover{background:rgba(var(--ow-h1),.18)}
/* the message you are acting on is marked, with an outline so its own light is untouched */
html[data-ow-thread] .ow-msg[data-open]{outline:2px solid rgba(var(--h,var(--ow-h1)),.75);outline-offset:2px}
html[data-ow-thread] .ow-msg:focus-visible{outline:2px solid rgba(var(--ow-h1),.9);outline-offset:2px}
html[data-ow-thread] .ow-rx__pick{margin-left:2px}
html[data-ow-thread] .ow-msg__act[data-armed]{color:#fff;background:rgba(230,70,90,.9)}

/* ── THE COMPOSER IS PART OF THE SCREEN ────────────────────────────────────
   Pinned to the edge on the same frosted material as the bar at the top, so
   the screen has a top and a bottom and the galaxy between them. The camera,
   the field and send share one height and one shape — round — and every one
   of them is a thumb's width. */
html[data-ow-thread] .ow-composer{gap:8px;align-items:flex-end;
  padding:8px max(10px,env(safe-area-inset-left)) max(8px,env(safe-area-inset-bottom));
  background:rgba(var(--ow-chrome-rgb),.78);
  -webkit-backdrop-filter:blur(20px) saturate(1.25);backdrop-filter:blur(20px) saturate(1.25);
  box-shadow:0 -1px 0 rgba(255,255,255,.07)}
html[data-fx="safe"][data-ow-thread] .ow-composer{background:rgba(var(--ow-chrome-rgb),.95)}
html[data-ow-thread] .ow-composer textarea{min-height:44px;padding:11px 16px;border-radius:22px;
  font-size:15.5px;line-height:1.4}
html[data-ow-thread] .ow-attach{width:44px;height:44px}
html[data-ow-thread] .ow-send{width:44px;height:44px;border-radius:50%}
html[data-ow-thread] .ow-send:disabled{opacity:.35}
html[data-ow-thread] .ow-send svg{width:20px;height:20px;transform:translateX(1px)}
html[data-ow-thread] .ow-reply-aim{border-radius:14px}

/* ══ THE CHAT SCREEN, POLISHED AT THE COMPONENT LEVEL — founder/554 ════════
   The review: *"polish the existing system at the component/interaction level
   and stop making broad aesthetic changes."* Each rule below answers one of
   its points; the galaxy (point 4) is left exactly as it is (551). */

/* 1 · denser bubbles, the same light at a smaller radius */
html[data-ow-thread] .ow-msg{padding:7px 12px;font-size:15px;line-height:1.34}
html[data-ow-thread] .ow-msgs > [data-gap]{margin-top:10px}
html[data-ow-thread] .ow-msgs > *{margin-top:2px}
html[data-ow-thread] .ow-msgs.ow-msgs .ow-msg.ow-msg.ow-msg:not([data-mine]){
  box-shadow:inset 0 2px 1px var(--ow-lift),
    inset 0 0 0 1px rgba(var(--h),.24),
    0 0 12px -9px rgba(var(--h),.4),
    0 4px 12px -10px var(--ow-drop)}
html[data-ow-thread] .ow-msg.ow-msg[data-mine]{
  box-shadow:inset 0 1px 0 rgba(255,255,255,.34),0 3px 10px -6px rgba(var(--ow-h1),.55)}

/* 2 · a photo is the message: edge to edge at the top, its words under it,
   the time on the last line — one object, not a card holding a picture */
html[data-ow-thread] .ow-msg:has(.ow-msg__media){padding:3px 3px 6px;max-width:min(72%,420px)}
html[data-ow-thread] .ow-msg:has(.ow-msg__media) > .ow-msg__media:first-child{margin:0}
html[data-ow-thread] .ow-msg__media,
html[data-ow-thread] .ow-msg__media img,
html[data-ow-thread] .ow-msg__media video{border-radius:17px}
html[data-ow-thread] .ow-msg:has(.ow-msg__x) .ow-msg__media{margin-top:0}
html[data-ow-thread] .ow-msg:has(.ow-msg__media) > .ow-msg__x{padding-top:6px;margin:0 9px}
html[data-ow-thread] .ow-msg:has(.ow-msg__media):not(:has(.ow-msg__x)) > .ow-msg__t{margin-top:5px}

/* 3 · a cleaner bar: a smaller face with no ring, a quieter status */
html[data-ow-thread] .ow-thread-head{padding-top:calc(env(safe-area-inset-top) + 6px);padding-bottom:7px}
html[data-ow-thread] .ow-thread-head__face{width:32px;height:32px;font-size:11.5px;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.35)}
html[data-ow-thread] .ow-thread-head__face svg{width:20px;height:20px}
html[data-ow-thread] .ow-thread-head__nm{font-size:16px}
html[data-ow-thread] .ow-thread-head__pr{font-size:11.5px;color:var(--ow-ink-3)}
html[data-ow-thread] .ow-thread-head__pr[data-on="1"]{color:var(--ow-ink-3);gap:5px}
html[data-ow-thread] .ow-thread-head__pr[data-on="1"]::before{width:6px;height:6px;box-shadow:none}
/* the name is a door to the chat's controls, and says so with a small chevron */
html[data-ow-thread] .ow-thread-head__b.is-door,
html[data-ow-thread] .ow-thread-head__face[role="button"]{cursor:pointer;outline:none}
html[data-ow-thread] .ow-thread-head__b.is-door .ow-thread-head__nm::after{content:"";display:inline-block;
  width:6px;height:6px;margin:0 0 2px 7px;border-right:1.8px solid currentColor;border-bottom:1.8px solid currentColor;
  transform:rotate(-45deg);opacity:.45;vertical-align:middle}
html[data-ow-thread] .ow-thread-head__b.is-door:focus-visible .ow-thread-head__nm{text-decoration:underline}

/* 5 · a composer that takes a little less of the screen */
html[data-ow-thread] .ow-composer{padding-top:6px;padding-bottom:max(6px,env(safe-area-inset-bottom));gap:7px}
html[data-ow-thread] .ow-composer textarea{min-height:40px;padding:9px 15px;border-radius:20px;font-size:15px}
html[data-ow-thread] .ow-attach,
html[data-ow-thread] .ow-send{width:40px;height:40px}
html[data-ow-thread] .ow-send svg{width:18px;height:18px}
html[data-ow-thread] .ow-msgs{min-height:calc(100dvh - 140px - env(safe-area-inset-top) - env(safe-area-inset-bottom))}

/* 6 · the chat's controls lead with the three most used */
.ow-chatset__quick{display:flex;justify-content:center;gap:10px;margin:4px 0 22px}
.ow-chatset__q{flex:0 1 96px;display:flex;flex-direction:column;align-items:center;gap:7px;
  padding:12px 6px 10px;border:0;cursor:pointer;border-radius:18px;color:var(--ow-ink);
  font-family:var(--ow-font);background:rgba(var(--ow-h1),.10);
  box-shadow:inset 0 0 0 1px rgba(var(--ow-h1),.20)}
.ow-chatset__q span{display:grid;place-items:center;width:36px;height:36px;border-radius:50%;
  background:rgba(var(--ow-h1),.20)}
.ow-chatset__q svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round}
.ow-chatset__q b{font-size:12.5px;font-weight:600}
.ow-chatset__q[aria-pressed="true"] span{background:rgb(var(--ow-h1));color:#fff}
.ow-chatset__q:active{transform:scale(.97)}
.ow-chatset__q:disabled{opacity:.55}
@media (hover:hover) and (pointer:fine){ .ow-chatset__q:hover{background:rgba(var(--ow-h1),.16)} }
/* the big face on a chat's controls page is the same face as the bar's —
   the person's own colour, not a dark disc with a coloured ring */
.ow-chatset__face.ow-thread__av{
  background:radial-gradient(46% 36% at 28% 18%,rgba(255,255,255,.28),transparent 64%),
    linear-gradient(150deg,
      color-mix(in oklab,rgb(var(--ch1,var(--ow-h1))) 84%,#14131f),
      color-mix(in oklab,rgb(var(--ch2,var(--ow-h2))) 44%,#14131f));
  color:#fff;font-size:26px;box-shadow:inset 0 1px 0 rgba(255,255,255,.4)}
.ow-chatset__face.ow-thread__av[data-kind="center"]{border-radius:26px 22px 27px 23px}

/* ── THE ROOM SPEAKING, AND WHAT AN ADMIN CAN DO (founder/549, 556) ───────
   "Maya made Jordan an admin" is the room telling everybody in it what
   changed: centred and quiet, the way the day is said — never a bubble from
   nobody. The acts on a person sit under their row, only where the server's
   rules would say yes; the destructive one asks in place. */
.ow-msg-sys{align-self:center;max-width:86%;text-align:center;margin:10px 0 4px;
  font-family:var(--ow-font);font-size:12px;font-weight:500;line-height:1.35;color:var(--ow-ink-3)}
html[data-ow-thread] .ow-msgs > .ow-msg-sys{margin:12px 0 6px}
.ow-people__acts{display:flex;gap:8px;flex-wrap:wrap;margin:-2px 0 10px 50px}
.ow-people__act{min-height:34px;padding:0 14px;font-size:12.5px}
.ow-people__act[data-danger]{color:#ff8a9a}
.ow-people__act[data-armed]{color:#fff;background:rgba(230,70,90,.9)}
.ow-rename{display:flex;gap:8px;align-items:center;margin-top:6px}
.ow-rename__in{flex:1 1 auto;min-width:0;font:inherit;font-size:15px;color:var(--ow-ink);
  padding:11px 14px;border:0;border-radius:14px;
  background:linear-gradient(178deg,var(--ow-panel-1),var(--ow-panel-2));
  box-shadow:inset 0 1px 0 var(--ow-lift),inset 0 0 0 1px var(--ow-rim)}
.ow-rename__in:focus{outline:none;box-shadow:inset 0 1px 0 var(--ow-lift),0 0 0 1.5px rgba(var(--ow-h1),.7)}
.ow-rename .ow-btn{width:auto;flex:none;padding:12px 20px}
.ow-chatset__face.is-editable{cursor:pointer;position:relative}
.ow-chatset__face.is-editable::after{content:"";position:absolute;right:2px;bottom:2px;width:24px;height:24px;border-radius:50%;
  background:rgb(var(--ow-h1)) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 8h3l2-3h6l2 3h3v11H4z'/%3E%3Ccircle cx='12' cy='13' r='3.2'/%3E%3C/svg%3E") center/14px no-repeat;
  box-shadow:0 0 0 2px var(--ow-ground,#000)}
.ow-chatset__face[data-busy]{opacity:.6}
.ow-chatset__face{overflow:visible}
.ow-chatset__face img{border-radius:inherit}

/* ══ THE PERSON'S DISPLAY SWITCHES (founder/556) ═══════════════════════════
   Set on the document by OW.applyPrefs from Settings. Neither touches the
   galaxy, the person's colour or the glow — they are the person's choices
   too; these only make what sits on them easier to read. */
/* HIGHER CONTRAST: the secondary and tertiary ink move toward the primary,
   and the hairlines that separate things get stronger */
html[data-contrast="high"]{--ow-ink-2:rgba(255,255,255,.9);--ow-ink-3:rgba(255,255,255,.78)}
html[data-contrast="high"][data-theme="light"]{--ow-ink-2:rgba(20,19,31,.96);--ow-ink-3:rgba(20,19,31,.84)}
html[data-contrast="high"] .ow-thread + .ow-thread::before{background:rgba(255,255,255,.2)}
html[data-contrast="high"] .ow-set__row + .ow-set__row{border-color:rgba(255,255,255,.2)}
html[data-contrast="high"] .ow-msg-sys,
html[data-contrast="high"] .ow-msg__t{opacity:1}
/* LARGER TEXT: the content column is enlarged the way browser zoom enlarges a
   page — a block at auto width re-flows inside the same screen, so nothing
   runs off the side; the fixed dock and composer keep their size and place */
html[data-text="large"] .ow-main{zoom:1.12}

/* ── A REQUEST THAT SAYS SOMETHING (founder/556) ─────────────────────────── */
.ow-req__msg{display:block;margin:2px 0 1px;font-size:13.5px;line-height:1.35;color:var(--ow-ink);
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow:hidden}
.ow-reqsay{display:flex;gap:8px;align-items:center;flex-basis:100%;margin-top:8px}
.ow-reqsay__in{flex:1 1 auto;min-width:0;font:inherit;font-size:14px;color:var(--ow-ink);
  padding:10px 14px;border:0;border-radius:20px;
  background:linear-gradient(178deg,var(--ow-panel-1),var(--ow-panel-2));
  box-shadow:inset 0 1px 0 var(--ow-lift),inset 0 0 0 1px var(--ow-rim)}
.ow-reqsay__in:focus{outline:none;box-shadow:inset 0 1px 0 var(--ow-lift),0 0 0 1.5px rgba(var(--ow-h1),.7)}

/* ── WHAT A ROOM IS DECIDING (founder/556) ──────────────────────────────────
   An open proposal sits above the composer: what it would change, the count,
   and Agree / Disagree — the same card on Chat rules. */
.ow-props:empty{display:none}
.ow-props{display:flex;flex-direction:column;gap:6px;margin:0 0 6px}
.ow-prop{padding:10px 12px;border-radius:16px;
  background:rgba(var(--ow-chrome-rgb),.82);
  -webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);
  box-shadow:inset 0 0 0 1px rgba(var(--ow-h1),.35)}
.ow-prop__what{margin:0 0 3px;font-size:13.5px;line-height:1.35;color:var(--ow-ink)}
.ow-prop__n{display:block;font-size:12px;color:var(--ow-ink-3)}
.ow-prop__acts{display:flex;gap:8px;margin-top:8px}
.ow-prop__acts:empty{display:none}
.ow-prop__acts[data-busy]{opacity:.6;pointer-events:none}
.ow-prop__mine{font-size:12.5px;font-weight:600;color:var(--ow-ink-2)}
html[data-ow-thread] .ow-thread__foot .ow-props{position:fixed;left:0;right:0;z-index:119;
  bottom:calc(64px + env(safe-area-inset-bottom));padding:0 10px;margin:0 auto;max-width:640px}

/* ── A GROUP'S DOOR (founder/556) — what someone outside a group sees ──── */
.ow-door-g{display:flex;flex-direction:column;align-items:center;text-align:center;padding:4px 0 40px}
.ow-door-g .ow-set__ph{align-self:stretch}
.ow-door-g__face{margin:18px 0 12px}
.ow-door-g__t{margin:0;font-family:var(--ow-font-display);font-weight:650;font-size:22px;color:var(--ow-ink)}
.ow-door-g__n{margin:4px 0 18px;font-size:13.5px;color:var(--ow-ink-3)}
.ow-door-g__act{width:100%;max-width:380px}
.ow-door-g__go{width:auto;min-width:160px;border-radius:999px;padding:12px 26px}
.ow-door-g__act .ow-reqsay{flex-direction:column;align-items:stretch}
.ow-door-g__act .ow-reqsay .ow-door-g__go{align-self:center}
.ow-door-g__said{margin:12px 0 0;max-width:340px;font-size:13px;line-height:1.45;color:var(--ow-ink-2)}
.ow-door-g__said:empty{display:none}

/* ── MY DAY (founder/593) — the top of a field employee's Workspace: the
   shift they are on or the next, the clock, what comes after, the open shifts
   theirs to take, their time off (frontend/app/workspace/day.js) ── */
.ow-day{display:flex;flex-direction:column;gap:12px;margin:2px 0 26px}
.ow-day__now{display:flex;flex-direction:column;padding:18px 18px 16px;border-radius:24px 19px 25px 20px;color:#fff;
  background:linear-gradient(140deg,rgb(var(--ow-h1)),rgb(var(--ow-h2)));box-shadow:0 20px 44px -24px rgba(var(--ow-h1),.95)}
.ow-day__k{font:600 12.5px var(--ow-font);letter-spacing:.02em;opacity:.88}
.ow-day__t{margin:6px 0 2px;font:750 34px/1.05 var(--ow-font);letter-spacing:-.015em}
.ow-day__s{font-size:14.5px;opacity:.94}
.ow-day__clock{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:16px}
.ow-day__punch{height:48px;padding:0 26px;border:0;border-radius:24px;cursor:pointer;font:750 15.5px var(--ow-font);
  color:rgb(var(--ow-h1));background:#fff;box-shadow:0 10px 22px -12px rgba(0,0,0,.45);transition:transform .15s}
.ow-day__punch:hover{transform:translateY(-1px)}
.ow-day__punch:disabled{opacity:.6;cursor:default;transform:none}
.ow-day__punch--quiet{height:38px;padding:0 16px;font-size:13.5px;color:#fff;background:linear-gradient(140deg,rgb(var(--ow-h1)),rgb(var(--ow-h2)))}
/* on the lead card the second press is glass on the card's own light, not a second gradient */
.ow-day__now .ow-day__punch--quiet{height:44px;padding:0 18px;font-size:14.5px;background:rgba(255,255,255,.22);box-shadow:none}
.ow-day__since{font-size:13.5px;opacity:.94}
.ow-day__also{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;padding:12px 14px;
  border-radius:16px;font-size:13.5px;color:var(--ow-ink);background:rgba(var(--ow-h1),.10)}
.ow-day__said{margin:0 2px;font-size:13.5px;color:var(--ow-ink-2)}
.ow-day__h{margin:10px 2px 0;font:650 15px var(--ow-font);color:var(--ow-ink)}
.ow-day__rows{display:flex;flex-direction:column;border-radius:18px 15px 19px 16px;overflow:hidden;
  border:1px solid color-mix(in srgb,var(--ow-ink) 9%,transparent);background:color-mix(in srgb,var(--ow-ink) 3%,transparent)}
.ow-day__row{display:flex;align-items:center;gap:12px;padding:12px 14px;color:var(--ow-ink);
  border-top:1px solid color-mix(in srgb,var(--ow-ink) 7%,transparent)}
.ow-day__row:first-child{border-top:0}
.ow-day__when{flex:none;width:92px;font:650 13px var(--ow-font)}
.ow-day__what{flex:1 1 auto;min-width:0;font:600 14px var(--ow-font)}
.ow-day__what small{display:block;margin-top:1px;font:400 12.5px var(--ow-font);color:var(--ow-ink-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ow-day__take{flex:none;height:36px;padding:0 16px;border:0;border-radius:18px;cursor:pointer;
  font:700 13.5px var(--ow-font);color:#2b8a3e;background:rgba(47,158,68,.15)}
.ow-day__take:hover{background:rgba(47,158,68,.24)}
.ow-day__state{flex:none;padding:5px 11px;border-radius:11px;font:650 12px var(--ow-font)}
.ow-day__state--requested{color:#b35c00;background:rgba(245,159,0,.16)}
.ow-day__state--approved{color:#2b8a3e;background:rgba(47,158,68,.15)}
.ow-day__state--denied{color:var(--ow-ink-2);background:color-mix(in srgb,var(--ow-ink) 8%,transparent)}
.ow-day__askb{align-self:flex-start;height:40px;padding:0 18px;border-radius:20px;cursor:pointer;font:650 14px var(--ow-font);
  color:var(--ow-ink);background:none;border:1px solid rgba(var(--ow-h1),.45)}
.ow-day__askb:hover{background:rgba(var(--ow-h1),.08)}
.ow-day__ask{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;padding:14px;border-radius:18px;
  background:color-mix(in srgb,var(--ow-ink) 4%,transparent)}
.ow-day__ask label{display:flex;flex-direction:column;gap:6px;font:600 12.5px var(--ow-font);color:var(--ow-ink-2)}
.ow-day__ask label span{font-weight:400}
.ow-day__ask input,.ow-day__ask select{height:44px;padding:0 12px;border-radius:12px;font:500 14.5px var(--ow-font);color:var(--ow-ink);
  background:color-mix(in srgb,var(--ow-ink) 5%,transparent);border:1px solid color-mix(in srgb,var(--ow-ink) 14%,transparent);
  min-width:0;width:100%;box-sizing:border-box;color-scheme:light dark}
.ow-day__wide{grid-column:1/-1}
.ow-day__acts{grid-column:1/-1;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.ow-day__acts .ow-day__punch{color:#fff;background:linear-gradient(140deg,rgb(var(--ow-h1)),rgb(var(--ow-h2)))}
.ow-day__plain{height:44px;padding:0 14px;border:0;background:none;cursor:pointer;font:600 14px var(--ow-font);color:var(--ow-ink-2)}
.ow-day__btns{flex:none;display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end}
.ow-day__take--quiet{color:var(--ow-ink-2);background:color-mix(in srgb,var(--ow-ink) 8%,transparent)}
.ow-day__row--stack{flex-wrap:wrap;row-gap:8px}
.ow-day__row--done{opacity:.62}
.ow-day__row--stack .ow-day__what{flex:1 1 calc(100% - 104px)}
.ow-day__rows .ow-day__what small{white-space:normal}
.ow-day__row--stack .ow-day__btns{flex-basis:100%;justify-content:flex-start;padding-left:104px}
.ow-day__inask{flex-basis:100%;display:flex;gap:6px;flex-wrap:wrap;align-items:center;padding-left:104px;box-sizing:border-box}
.ow-day__inask input{flex:1 1 200px;min-width:0;height:40px;padding:0 12px;border-radius:12px;font:500 14.5px var(--ow-font);color:var(--ow-ink);
  background:color-mix(in srgb,var(--ow-ink) 5%,transparent);border:1px solid color-mix(in srgb,var(--ow-ink) 14%,transparent);color-scheme:light dark}
.ow-day__inask input[aria-invalid="true"]{border-color:#e8590c}
.ow-day__what small.ow-day__memory{margin-top:5px;color:var(--ow-ink);font:500 12.5px var(--ow-font);white-space:normal}
.ow-day__help{margin-top:12px;border-top:0;border-radius:16px 14px 17px 15px;background:color-mix(in srgb,var(--ow-ink) 4%,transparent)}
.ow-day__help .ow-day__btns,.ow-day__help .ow-day__inask{padding-left:0}
.ow-day__help .ow-day__what small{white-space:normal}
.ow-day__tally{margin-left:10px;font:500 12.5px var(--ow-font);color:var(--ow-ink-2)}
.ow-day__notes{display:flex;flex-direction:column;gap:8px}
.ow-day__note{padding:12px 14px;border-radius:16px 14px 17px 15px;background:color-mix(in srgb,rgb(var(--ow-h1)) 10%,transparent);
  border:1px solid color-mix(in srgb,rgb(var(--ow-h1)) 22%,transparent);color:var(--ow-ink)}
.ow-day__note.is-seen{background:color-mix(in srgb,var(--ow-ink) 3%,transparent);border-color:color-mix(in srgb,var(--ow-ink) 8%,transparent)}
.ow-day__notek{display:block;font:600 12px var(--ow-font);color:var(--ow-ink-2)}
.ow-day__note p{margin:4px 0 8px;font:500 14.5px/1.4 var(--ow-font)}
.ow-day__note.is-seen p{margin-bottom:0}
.ow-day__avail{grid-template-columns:repeat(auto-fit,minmax(130px,1fr))}
.ow-day__lic{margin:0 0 10px}
.ow-day__lic.is-urgent{background:color-mix(in srgb,#e03131 10%,transparent);border-color:color-mix(in srgb,#e03131 32%,transparent)}
.ow-day__licsub{display:block;font:400 12.5px/1.4 var(--ow-font);color:var(--ow-ink-2)}
