/* AIKYA v2 — app styles. Colour only via tokens.css. */

/* ---- reset ---- */
*,*::before,*::after{box-sizing:border-box}
/* No visible scrollbars anywhere. This is a phone app that also runs in a
   desktop browser; the gutter track looked like chrome bolted onto the
   column. Scrolling itself is untouched — wheel, trackpad, touch, keyboard
   and programmatic scrollIntoView all behave exactly as before. */
*{scrollbar-width:none}
*::-webkit-scrollbar{width:0;height:0;display:none}
/* Native-app feel: no text selection and no iOS long-press callout anywhere.
   Copy stays possible only through the app's own Copy buttons (share.copy);
   inputs/textareas below opt back in or typing and the execCommand fallback
   would break. Decided 2026-08-30 — includes post bodies, on purpose. */
html{-webkit-text-size-adjust:100%;height:100%;overscroll-behavior:none;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}
body{margin:0;min-height:100%;background:var(--sand);color:var(--ink);font:400 var(--fs-base)/var(--lh) var(--font-sans);-webkit-font-smoothing:antialiased;-webkit-tap-highlight-color:transparent;overscroll-behavior-y:contain;overscroll-behavior-x:none;touch-action:manipulation}
input,textarea,[contenteditable]{-webkit-user-select:text;user-select:text}
h1,h2,h3,h4,p{margin:0}
button,input,select,textarea{font:inherit;color:inherit}
button{background:none;border:0;padding:0;cursor:pointer;touch-action:manipulation}
a{color:inherit;text-decoration:none}
img,svg{display:block;max-width:100%}
ul,ol{margin:0;padding:0;list-style:none}
[hidden]{display:none!important}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}
.sr{position:absolute!important;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.mono{font-family:var(--font-mono);font-variant-numeric:tabular-nums}
.muted{color:var(--ink-soft)}
.faint{color:var(--ink-faint)}
.small{font-size:var(--fs-sm)}
.xs{font-size:var(--fs-xs)}
.b{font-weight:600}
.center{text-align:center}
.row{display:flex;align-items:center;gap:var(--s2)}
.col{display:flex;flex-direction:column;gap:var(--s2)}
.grow{flex:1;min-width:0}
.ell{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.gap1{gap:var(--s1)}.gap2{gap:var(--s2)}.gap3{gap:var(--s3)}.gap4{gap:var(--s4)}
.mt1{margin-top:var(--s1)}.mt2{margin-top:var(--s2)}.mt3{margin-top:var(--s3)}.mt4{margin-top:var(--s4)}.mt6{margin-top:var(--s6)}
.mb2{margin-bottom:var(--s2)}.mb3{margin-bottom:var(--s3)}.mb4{margin-bottom:var(--s4)}
.i{width:20px;height:20px;flex:0 0 auto;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.i.s{width:16px;height:16px}.i.l{width:24px;height:24px}.i.xl{width:32px;height:32px}

/* ---- jaali (light mode only) ----
   A Jaipur lattice over a warm wash: square grid, inscribed diamond, circles on
   every intersection — the geometry a jaali is cut from (~700 bytes as a data
   URI, no request).

   24px tile, halved twice from the original 96px on 2026-08-22 (96 -> 48 -> 24).
   At 96 only about four tiles spanned a phone and the lattice read as a drawn
   graphic behind the content; at 24 it is ~16 across and reads as grain.

   Stroke and opacity come down at every step, and NOT by the same factor as the
   tile. Halving the tile doubles line length per unit area, so scaling the
   stroke alone would make each smaller mesh visibly darker than the one it
   replaced. These weights were measured rather than guessed: each tile was
   rasterised over a fixed 192px square and its mean ink matched to the previous
   step (48px at .8/.15 = 1.53; 24px at .6/.11 = 1.54).

   The Wall of Praise board carries the same tile at the same 24px so the two
   weaves match — with its own opacities, because white on deep rose needs less
   than brown on cream to read at the same weight.

   The gradient is why this works. On the old cool sand the same lattice looked
   like a printing fault; over peach-to-blush it reads as textile. Both layers
   are painted on `body` (and `.panel`, which would otherwise cover them with
   flat sand) and the `--sand` token is left alone, because the
   header and tab bar blend against it (`color-mix` + backdrop blur) and dark
   mode must keep the flat dark ground.

   Light only — on the dark sand this lattice reads as smudges. Cards are opaque
   `--surface`, so the pattern never sits under body text: contrast is untouched
   and the app stays AA. */
:root[data-theme="light"] body,
:root[data-theme="light"] .splash,
:root[data-theme="light"] .panel{
  background-color:#FAF1E6;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cg fill='none' stroke='%239A7B4F' stroke-opacity='.11' stroke-width='.6'%3E%3Cpath d='M0 0h24v24H0z'/%3E%3Cpath d='M12 0 24 12 12 24 0 12Z'/%3E%3Cpath d='M12 4.5 19.5 12 12 19.5 4.5 12Z'/%3E%3Ccircle cx='0' cy='0' r='1.75'/%3E%3Ccircle cx='24' cy='0' r='1.75'/%3E%3Ccircle cx='0' cy='24' r='1.75'/%3E%3Ccircle cx='24' cy='24' r='1.75'/%3E%3Ccircle cx='12' cy='12' r='1.75'/%3E%3C/g%3E%3C/svg%3E"),
    linear-gradient(163deg,#FBE7D2 0%,#FCEFE6 28%,#F9E2EB 54%,#FBEFE2 78%,#FAF1E6 100%);
  background-size:24px 24px,cover;
  background-repeat:repeat,no-repeat;
  background-attachment:fixed,fixed;
}

/* ---- shell ---- */
#app{min-height:100dvh;display:flex;flex-direction:column}
.screen{flex:1;display:flex;flex-direction:column;min-height:0}
/* The bars share the content column rather than spanning the viewport: on a
   desktop a full-width header above a 540px feed reads as a broken website
   instead of a phone-shaped app. No media query needed — below 540px the
   max-width never binds, so phones are untouched. `width:100%` is load-bearing:
   #app is a flex column, and margin-inline:auto on a stretched item collapses it
   to its content width — the same trap #urgent hit in 0.7.3. */
.hdr{width:100%;max-width:var(--content-max);margin-inline:auto;position:sticky;top:0;z-index:20;height:calc(var(--header-h) + var(--safe-t));padding-top:var(--safe-t);background:linear-gradient(120deg,color-mix(in srgb,var(--accent-wash) 55%,transparent),color-mix(in srgb,var(--sand) 88%,transparent) 45%);backdrop-filter:saturate(1.4) blur(14px);-webkit-backdrop-filter:saturate(1.4) blur(14px);border-bottom:1px solid var(--hairline-soft)}
/* repeat(3,max-content), not 44px 44px 44px: the third slot is the group-chat
   icon, hidden unless the network has chat on — a max-content column collapses
   to nothing when its button is [hidden]. */
/* Columns: brand | network pill | praise | chat | bell | you. The count is
   explicit, so ADDING A HEADER BUTTON MEANS BUMPING THE repeat() — an extra
   child with no column of its own wraps onto a second row and drops out of the
   56px header instead of overflowing, which looks like a broken avatar rather
   than a layout that ran out of room. Hidden buttons (praise and chat both
   disappear when their feature is off) collapse to 0 in a max-content column. */
.hdr-in{height:var(--header-h);max-width:var(--content-max);margin:0 auto;padding:0 var(--s2);display:grid;grid-template-columns:40px minmax(0,1fr) repeat(4,max-content);align-items:center;gap:2px}
/* Four action buttons plus the brand only fit a 375px header at 40px, not 44.
   Scoped to the header so the 44px touch target is untouched everywhere else;
   40px still clears the 40px minimum and the row is 56px tall, so the real
   tappable area stays comfortable. Without this the network name is squeezed
   to ~40px and shows a single letter. */
.hdr-in > .icobtn{width:40px;height:40px}
/* Below ~360px even 40px buttons leave the network name about 23px — a sliver
   of one letter, which is worse than no name. Drop it there and let the pill be
   its avatar and chevron: still obviously the network, still the switcher, and
   the name is the first thing inside the sheet it opens. */
@media (max-width:359px){ .hdr-in .netpill .nm{display:none} }
.hdr-in.simple{grid-template-columns:44px minmax(0,1fr) 44px}
.hdr .title{font-size:var(--fs-lg);font-weight:600;text-align:center}
.icobtn{width:44px;height:44px;display:inline-flex;align-items:center;justify-content:center;border-radius:var(--r-pill);color:var(--ink);position:relative}
.icobtn:active{background:var(--surface-sunken)}
/* Header actions carry a resting surface so the 44px tap target is visible
   rather than implied by a floating 20px glyph — same chip treatment as the
   network pill beside them, so the header reads as one row of controls. */
.hdr-in > .icobtn{background:var(--surface);border:1px solid var(--card-edge);box-shadow:var(--shadow-sm)}
.hdr-in > .icobtn:active{background:var(--surface-sunken)}
/* Two exceptions: the avatar is already a filled circle, and the brand mark is
   the logo rather than an action — both need the hit area, not a chip round it. */
.hdr-in > .icobtn:has(.av),.hdr-in > .icobtn:has(img){border-color:transparent;background:transparent;box-shadow:none}
/* HEADER avatar = the same chip as the heart and bell beside it (2026-08-29).
   It used to be a hue-washed circle with a pink "this is you" ring — the only
   pink ring in the app — but three different treatments in one row of three
   40px circles read as clutter, so the avatar now wears the sibling chip:
   surface fill, card edge, ink initials, small shadow. A photo still fills the
   circle edge-to-edge; only the initials fallback goes monochrome, and ONLY
   here — hue washes and per-hue rings everywhere else are untouched.

   40px exactly, matching `.hdr-in > .icobtn`: the border is inset (border-box
   app-wide) so nothing draws outside the box and nothing can bleed into the
   bell's 2px grid gap, which the old outside-the-box ring had to be measured
   against every resize.

   The specificity is deliberate: `.av[data-hue=N]` fills are (0,2,0), and this
   is (0,5,0) counting the :has() and :not() arguments, so surface/ink win here
   without !important. The trailing :not(.sk) also keeps it above the generic
   hue-ring rule `.av:not(.net):not(.sq):not(.sk)` further down — a tie would
   hand the win to that later rule and quietly re-ring the header avatar (this
   exact collision shipped in 0.7.175). */
/* One brand touch kept (settled 2026-08-29 after a full-pink fill was rejected
   on sight): the chip stays EXACTLY the siblings' — same surface, border,
   shadow, ink — and the background carries only a minimal gradient tint of the
   brand pink, blended into that same surface so all three circles still read
   as one row. The tint is color-mixed from --accent rather than --accent-wash
   because the wash is a standalone colour that drifts from the surface;
   mixing keeps it literally the siblings' background plus a breath of pink. */
/* Superseded 2026-09-23: the header avatar now carries the member's duotone
   like every other person avatar (option G), so background and colour are no
   longer set here. Size, border and shadow stay the siblings'. */
.hdr-in > .icobtn:has(.av) .av:not(.sk){width:40px;height:40px;font-size:12px;border:1px solid var(--card-edge);box-shadow:var(--shadow-sm)}
.dot{position:absolute;top:10px;right:10px;width:9px;height:9px;border-radius:50%;background:var(--accent);border:2px solid var(--sand)}
.netpill{display:inline-flex;align-items:center;gap:6px;max-width:100%;margin:0 auto;padding:3px 10px 3px 3px;border-radius:var(--r-pill);background:var(--surface);border:1px solid var(--card-edge);box-shadow:var(--shadow-sm)}
.netpill .av{width:24px;height:24px;font-size:9.5px}
.netpill .nm{font-weight:600;font-size:var(--fs-md);max-width:44vw}
/* Horizontal padding raised 12px -> 16px (2026-08-27): field labels and card
   text sat close enough to the screen edge to read as touching it, reported on
   the Events tab. Vertical is unchanged — only the sides moved. */
.main{flex:1;width:100%;max-width:var(--content-max);margin:0 auto;overflow-x:clip;padding:var(--s3) var(--s4) calc(var(--tabbar-h) + var(--safe-b) + var(--s6))}
.main.noTabs{padding-bottom:calc(var(--safe-b) + var(--s6))}
.tabbar{position:fixed;left:50%;right:auto;transform:translateX(-50%);width:100%;max-width:var(--content-max);bottom:0;z-index:20;height:calc(var(--tabbar-h) + var(--safe-b));padding-bottom:var(--safe-b);background:linear-gradient(300deg,color-mix(in srgb,var(--accent-wash) 45%,transparent),color-mix(in srgb,var(--surface) 92%,transparent) 45%);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border-top:1px solid var(--hairline)}
.tabbar-in{height:var(--tabbar-h);max-width:var(--content-max);margin:0 auto;display:grid;grid-auto-flow:column;grid-auto-columns:1fr}
.tab{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;color:var(--ink-faint);font-size:var(--fs-xs);font-weight:500;position:relative}
.tab .i{width:24px;height:24px}
.tab .i[data-run]{width:28px;height:28px;margin:-2px 0}
.tab[aria-current="page"]{color:var(--ink)}
.tab[aria-current="page"]::after{content:"";position:absolute;bottom:6px;width:18px;height:3px;border-radius:2px;background:var(--accent)}
.tab .bdg{position:absolute;top:8px;left:calc(50% + 6px);min-width:16px;height:16px;padding:0 4px;border-radius:var(--r-pill);background:var(--accent);color:#fff;font-size:10px;font-weight:600;display:flex;align-items:center;justify-content:center;font-family:var(--font-mono)}
.tab .bdg.gold{background:var(--gold);color:var(--accent-ink)}
.fab{position:fixed;right:max(var(--s4),calc(50% - var(--content-max)/2 + var(--s4)));bottom:calc(var(--tabbar-h) + var(--safe-b) + var(--s4));width:54px;height:54px;border-radius:var(--r-pill);background:var(--accent);color:var(--accent-ink);display:flex;align-items:center;justify-content:center;box-shadow:var(--shadow-md);z-index:15;transition:transform var(--dur) var(--ease),opacity var(--dur)}
.fab.hide{transform:translateY(120px);opacity:0;pointer-events:none}
/* Room for the FAB (2026-08-26). `.main`'s bottom padding clears the tab bar
   but NOT the floating button above it: the FAB's top edge sits
   `tabbar + safe + 16 + 54` from the viewport bottom, and the padding stopped
   at `tabbar + safe + 24` — so the last card on any tab with a FAB sat under
   it. Reported on the Ended challenges row, but Celebrate and Events have the
   same button and the same gap.

   Keyed on `.fab` existing, deliberately NOT on `:not(.hide)` — Celebrate
   hides its FAB while the compose card is on screen, and recomputing the
   padding as it hides and shows would shift the whole column mid-scroll.
   Reserve the space for as long as the button belongs to the view. */
body:has(.fab) .main{padding-bottom:calc(var(--tabbar-h) + var(--safe-b) + 92px)}

/* ---- surfaces ---- */
.card{background:var(--surface);border:1px solid var(--card-edge);border-radius:var(--r-lg);padding:var(--s4);box-shadow:var(--shadow-sm)}
.card.tight{padding:var(--s3)}
/* Opaque on purpose (2026-08-21): transparent let the body lattice show
   through the action cards, which read as noise behind their labels. */
/* Accent at full strength read as a validation/drop-zone outline and fought
   the card's own content (2026-09-12) — --accent is the primary-action and
   active-nav colour only. Mixed down to 40% it still marks the card as the
   invitation on the tab without competing with the label beside it. */
.card.dashed{border-style:dashed;border-color:color-mix(in srgb,var(--accent) 40%,transparent);background:var(--surface);box-shadow:none}
.card + .card{margin-top:var(--s3)}
.list{background:var(--surface);border:1px solid var(--card-edge);border-radius:var(--r-lg);overflow:hidden}
.li{display:flex;align-items:center;gap:var(--s3);padding:12px var(--s4);min-height:56px;border-top:1px solid var(--hairline-soft);text-align:left;width:100%;color:inherit}
.li:first-child{border-top:0}
.li:active{background:var(--surface-sunken)}
.li .t{font-weight:500}
.li .s{font-size:var(--fs-sm);color:var(--ink-soft)}
/* `.s` is overloaded: it is this row's subtitle AND icon()'s small-size
   modifier, so `.li .s` also matched any 16px icon inside a row and pinned it
   to --ink-soft — beating the colour inherited from its own parent, because a
   rule on the element beats inheritance. The current-network tick was
   therefore grey #8B949E on pink #FF3D9E and effectively invisible (found
   2026-09-12 while adding the Manage cog beside it). Scoped to this badge on
   purpose: every other small icon in a list row wants --ink-soft and has been
   getting it for months. */
.li .netcheck .i{color:inherit;stroke:currentColor}
.li .chev{color:var(--ink-muted);margin-left:auto}
.li .lead{width:36px;height:36px;border-radius:var(--r-sm);display:flex;align-items:center;justify-content:center;background:var(--surface-sunken);color:var(--ink-soft);flex:0 0 auto}
.sect{font-size:var(--fs-xs);font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint);margin:var(--s5) var(--s1) var(--s2)}
.sect:first-child{margin-top:var(--s1)}
.divider{height:1px;background:var(--hairline);margin:var(--s4) 0}
.pill{display:inline-flex;align-items:center;gap:4px;padding:2px 9px;border-radius:var(--r-pill);font-size:var(--fs-xs);font-weight:600;line-height:1.6;background:var(--surface-sunken);color:var(--ink-soft)}
.pill.accent{background:var(--accent-wash);color:var(--accent-text)}
.pill.gold{background:var(--gold);color:var(--accent-ink)}
.pill.error{background:var(--error-wash);color:var(--error)}
.pill.ok{background:var(--ok-wash);color:var(--ok)}
.pill.info{background:var(--info-wash);color:var(--info)}
.pill.violet{background:var(--violet-wash);color:var(--violet)}
.pill.teal{background:var(--teal-wash);color:var(--teal)}
.pill.amber{background:var(--amber-wash);color:var(--amber)}
.pill.pink{background:var(--pink-wash);color:var(--pink)}
.chip{display:inline-flex;align-items:center;gap:6px;padding:7px 12px;border-radius:var(--r-pill);border:1px solid var(--hairline-strong);background:var(--surface);font-size:var(--fs-sm);font-weight:500;color:var(--ink-soft);white-space:nowrap}
.chip[aria-pressed="true"],.chip.on{background:var(--ink);color:var(--sand);border-color:var(--ink)}
.chips{display:flex;gap:var(--s2);overflow-x:auto;overscroll-behavior-x:contain;scrollbar-width:none;padding:2px 2px 6px;margin:0 -2px}
.chips::-webkit-scrollbar{display:none}
.banner{display:flex;gap:var(--s3);align-items:flex-start;padding:var(--s3) var(--s4);border-radius:var(--r-md);background:var(--info-wash);color:var(--ink);font-size:var(--fs-md)}
.banner.error{background:var(--error-wash)}.banner.gold{background:var(--gold-wash)}.banner.ok{background:var(--ok-wash)}
.strip{display:flex;align-items:center;gap:var(--s2);padding:8px var(--s3);border-radius:var(--r-md);background:var(--error-wash);color:var(--error);font-size:var(--fs-sm);font-weight:500;margin-bottom:var(--s3)}

/* ---- avatar ---- */
.av{width:40px;height:40px;border-radius:50%;background:var(--surface-sunken);color:var(--ink);display:inline-flex;align-items:center;justify-content:center;font-weight:600;font-size:var(--fs-sm);flex:0 0 auto;overflow:hidden;position:relative}
.av img{width:100%;height:100%;object-fit:cover}
.av.s{width:28px;height:28px;font-size:10.5px}.av.m{width:48px;height:48px}.av.l{width:72px;height:72px;font-size:var(--fs-xl)}.av.xl{width:96px;height:96px;font-size:var(--fs-2xl)}
.av.sq{border-radius:var(--r-sm)}
/* Ring on every person avatar (2026-08-27, asked). Same recipe as the header —
   a 1px surface gap so it reads as a ring rather than an outline painted on
   the face, then a 0.5px hairline — but in the avatar's OWN colour, not the
   brand pink. Pink is reserved for the header avatar, where it means "this is
   you"; on a member list it would have said that about everybody.

   `currentColor` does it with no extra rules: .av[data-hue=N] already sets
   `color` to that hue's ink, and avatar() always assigns a hue from a hash of
   the id — so every face rings itself and two people never collide by
   accident. It follows the hue into dark mode for free too.

   box-shadow costs no layout, so nothing anywhere shifts.

   THREE EXCLUSIONS, on purpose:
   - `.sk` is the LOADING SKELETON. skeletonList() borrows `.av` purely for the
     40px circle shape, so the ring landed on every placeholder on every tab —
     and with no data-hue to colour it, currentColor fell back to --ink and drew
     a hard dark outline. Reported as "why is a round circle showing, it was not
     there earlier". A class that means "shape" and a class that means "this is
     a person" were the same class; excluded here rather than renamed, because
     `.av` sizing is what skeletonList actually wants.
   - `.net` / `.sq` are NETWORK avatars (avatar() switches its initials rule on
     exactly these). A network logo is not a person and ringing it in the
     member-brand pink says something untrue.
   - `.avstack .av` already carries a 2px surface BORDER, which is what makes
     overlapping faces legible. A ring on top of that is two separators doing
     one job, and on a tight stack the rings collide.

   The gap is --surface, so on a surface-coloured background it disappears as
   intended. On a strongly tinted panel it shows as a thin light halo — visible
   on the praise note sheets, and accepted as the cost of one recipe
   everywhere rather than a per-context variant. */
/* RING RETIRED 2026-09-23. Person avatars are a duotone gradient now (asked:
   "G, Duotone gradient" from the avatar directions canvas): the member's hue
   ink to the ink three steps round the wheel, at an angle avatar() sets from
   the same hash (--av-angle), white initials. The three exclusions above still
   hold: skeletons, and network avatars, keep their wash. Specificity (0,3,0)
   beats the .av[data-hue] wash fills (0,2,0) below, which now also carry the
   two gradient stops as custom properties. */
.av:not(.net):not(.sq):not(.sk){background:linear-gradient(var(--av-angle,135deg),var(--av-ga,var(--av-g0)) 0%,var(--av-gb,var(--av-g3)) 100%);color:#fff;font-weight:700;text-shadow:0 1px 0 rgba(0,0,0,.18)}
/* Must out-specify the ring rule above (0,3,0) or it silently loses — which
   it did on the first attempt, and the stacked faces came out ringed and
   colliding. Same trap as `.rlist` vs `.rlist-sheet` earlier today. */
.av[data-hue="0"]{background:var(--teal-wash);color:var(--teal);--av-ga:var(--av-g0);--av-gb:var(--av-g3)}.av[data-hue="1"]{background:var(--violet-wash);color:var(--violet);--av-ga:var(--av-g1);--av-gb:var(--av-g4)}.av[data-hue="2"]{background:var(--amber-wash);color:var(--amber);--av-ga:var(--av-g2);--av-gb:var(--av-g5)}.av[data-hue="3"]{background:var(--pink-wash);color:var(--pink);--av-ga:var(--av-g3);--av-gb:var(--av-g6)}.av[data-hue="4"]{background:var(--info-wash);color:var(--info);--av-ga:var(--av-g4);--av-gb:var(--av-g0)}.av[data-hue="5"]{background:var(--ok-wash);color:var(--ok);--av-ga:var(--av-g5);--av-gb:var(--av-g1)}.av[data-hue="6"]{background:var(--gold-wash);color:var(--amber);--av-ga:var(--av-g6);--av-gb:var(--av-g2)}
.avstack{display:inline-flex}.avstack .av{margin-left:-8px;border:2px solid var(--surface)}.avstack .av:first-child{margin-left:0}

/* ---- buttons ---- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--s2);min-height:44px;padding:0 var(--s5);border-radius:var(--r-md);font-weight:600;font-size:var(--fs-base);border:1px solid transparent;transition:transform var(--dur-fast),background var(--dur-fast);user-select:none;white-space:nowrap}
.btn:active{transform:scale(.98)}
.btn:disabled{opacity:.5;pointer-events:none}
.btn.primary{background:var(--accent);color:var(--accent-ink)}
.btn.primary:active{background:var(--accent-hover)}
/* .btn:disabled's flat opacity:.5 fades the WHOLE button — background and
   dark ink text alike — toward the page behind it. On a light ghost/secondary
   button that just looks quieter; on primary's dark ink over vivid pink it
   washes the text out to an illegible grey (reported 2026-09-18: "Share text
   is grey" while an upload was still in flight, i.e. legitimately disabled/
   is-waiting — the STATE was right, the rendering wasn't). Keep the ink at
   full strength and lighten the fill instead, so "not ready yet" reads as a
   paler button, not a smudged one. */
.btn.primary:disabled,.btn.primary.is-waiting{opacity:1;background:color-mix(in srgb,var(--accent) 45%,var(--surface-sunken))}
.btn.dark{background:var(--ink);color:var(--sand)}
.btn.ghost{background:var(--surface);border-color:var(--hairline-strong);color:var(--ink)}
.btn.soft{background:var(--surface-sunken);color:var(--ink)}
.btn.danger{background:var(--error-wash);color:var(--error)}
.btn.link{background:none;color:var(--pink);min-height:36px;padding:0 var(--s2)} /* text-safe pink (7:1); --accent is 3.3:1 as text */
.btn.sm{min-height:36px;padding:0 var(--s3);font-size:var(--fs-md);border-radius:var(--r-sm)}
.btn.block{width:100%}
.btn .spin{width:18px;height:18px;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:spin .7s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.seg{display:flex;background:var(--surface-sunken);border-radius:var(--r-md);padding:3px;gap:2px}
.seg button{flex:1;min-height:36px;border-radius:9px;font-size:var(--fs-md);font-weight:500;color:var(--ink-soft)}
.seg button[aria-pressed="true"]{background:var(--surface);color:var(--ink);font-weight:600;box-shadow:var(--shadow-sm)}
/* The bottom nav's pink line under the selected pill too (asked 2026-09-24:
   "On each Tab pill, add pink line which already added in Bottom Nav Tabs"):
   the same 18 x 3 bar in the same accent, centred at the pill's foot. Every
   .seg row and the journey's Today · Whole route · Ladder tabs. */
.seg button,.jnt-z{position:relative}
.seg button[aria-pressed="true"]::after,.jnt-z[aria-selected="true"]::after{content:"";position:absolute;left:50%;bottom:3px;width:18px;height:3px;margin-left:-9px;border-radius:2px;background:var(--accent)}
.switch{position:relative;width:46px;height:28px;border-radius:var(--r-pill);background:var(--ink-muted);transition:background var(--dur);flex:0 0 auto}
.switch::after{content:"";position:absolute;top:3px;left:3px;width:22px;height:22px;border-radius:50%;background:#fff;box-shadow:var(--shadow-sm);transition:transform var(--dur) var(--ease)}
.switch[aria-checked="true"]{background:var(--accent)}
.switch[aria-checked="true"]::after{transform:translateX(18px)}
.switch:disabled{opacity:.45}

/* ---- forms ---- */
.field{display:flex;flex-direction:column;gap:6px;margin-bottom:var(--s4)}
.field label,.lbl{font-size:var(--fs-sm);font-weight:600;color:var(--ink-soft)}
.field .hint{font-size:var(--fs-xs);color:var(--ink-faint)}
.field .err{font-size:var(--fs-sm);color:var(--error)}
/* 16px is a hard minimum on these three, not a style choice: iOS Safari zooms
   the whole page whenever a text control under 16px is focused, and the page
   never zooms back. That is what made every compose sheet — celebration,
   challenge, every Circle form — appear to shift its margins the moment you
   started typing. They were inheriting the 15px body size. */
.input,.select,.textarea{width:100%;min-height:46px;padding:10px 14px;font-size:16px;border-radius:var(--r-md);border:1px solid var(--hairline-strong);background:var(--surface);color:var(--ink);outline:none;transition:border-color var(--dur-fast),box-shadow var(--dur-fast)}
.input:focus,.select:focus,.textarea:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-wash)}
.input::placeholder,.textarea::placeholder{color:var(--ink-muted)}
.input[aria-invalid="true"]{border-color:var(--error)}
.textarea{min-height:96px;resize:vertical;line-height:1.5}
/* Formatted fields (2026-09-25, parts/rich.js): a contenteditable box that
   looks like the input or textarea it replaces. The placeholder is drawn
   from data-placeholder while the box is empty. */
.rich{white-space:pre-wrap;word-break:break-word;overflow-y:auto;cursor:text;-webkit-user-select:text;user-select:text}
.rich.rich-1{white-space:nowrap;overflow-x:auto;overflow-y:hidden;line-height:24px;scrollbar-width:none}
.rich.rich-1::-webkit-scrollbar{display:none}
.rich.textarea{max-height:50vh}
.rich.empty::before{content:attr(data-placeholder);color:var(--ink-muted);pointer-events:none}
.rich b{font-weight:600}
/* The B / I pop-up over selected text. Above sheets and panels. */
.richpop{position:fixed;z-index:9000;display:flex;gap:2px;padding:3px;border-radius:var(--r-md);background:var(--ink);box-shadow:var(--shadow-sm)}
.richpop[hidden]{display:none}
.richpop button{min-width:40px;height:36px;border:0;border-radius:var(--r-sm);background:transparent;color:var(--sand);font-size:var(--fs-md)}
.richpop button:active{background:color-mix(in srgb,var(--sand) 18%,transparent)}
/* Links in posts become tappable when shown; the stored text is the plain URL. */
.rlink{color:var(--pink);text-decoration:underline;word-break:break-all}
/* A title is already semi-bold, and Mukta ships no heavier face, so a bold
   word in it would not show. Only when a title HAS bold words, the rest drop
   to 500 and the bold ones stay 600. Titles without formatting are unchanged. */
.post .body h3:has(b),.post.quiet .qrow .body h3:has(b),.post.quiet .qmedia h3:has(b){font-weight:500}
.post .body h3 b,.post.quiet .qrow .body h3 b,.post.quiet .qmedia h3 b{font-weight:700}
.select{appearance:none;background-image:linear-gradient(45deg,transparent 50%,var(--ink-faint) 50%),linear-gradient(135deg,var(--ink-faint) 50%,transparent 50%);background-position:calc(100% - 20px) 20px,calc(100% - 15px) 20px;background-size:5px 5px;background-repeat:no-repeat;padding-right:36px}
.grid2{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--s3)}
.otp{display:flex;gap:8px;justify-content:center}
.otp input{width:44px;height:54px;text-align:center;font:600 var(--fs-2xl) var(--font-mono);border-radius:var(--r-md);border:1px solid var(--hairline-strong);background:var(--surface);color:var(--ink);caret-color:var(--accent)}
.otp input:focus{border-color:var(--accent);outline:none;box-shadow:0 0 0 3px var(--accent-wash)}
.otp.shake{animation:shake .4s}
@keyframes shake{20%,60%{transform:translateX(-6px)}40%,80%{transform:translateX(6px)}}
.photo-pick{width:96px;height:96px;border-radius:50%;background:var(--surface-sunken);border:1px dashed var(--hairline-strong);display:flex;align-items:center;justify-content:center;color:var(--ink-faint);overflow:hidden;position:relative;margin:0 auto}
.photo-pick img{width:100%;height:100%;object-fit:cover}
.photo-pick.wide{width:100%;aspect-ratio:1.6;height:auto;border-radius:var(--r-md)} /* same frame as the celebration hero */
.photo-wrap{position:relative}
/* Per-photo actions (2026-08-26). Wraps rather than shrinking: three 32px
   buttons fit one line at 360px and drop to two lines below that, which is
   better than three unreadable ones. */
.photo-acts{display:flex;flex-wrap:wrap;justify-content:center;gap:var(--s2);margin-top:var(--s3)}
.photo-acts .btn.sm{padding:0 12px}
.photo-acts .danger-link{color:var(--error)}
.photo-acts .danger-link .i{color:inherit}
/* ---- Manage network: identity row + plan card (2026-08-26) ---- */
/* The logo is now a button (it opens the photo/name sheet) but must look
   exactly like the avatar it wraps — no button chrome, no focus box drawn
   around a circle it does not fit. */
.netlogo-btn{padding:0;border:0;background:none;flex:0 0 auto;line-height:0;border-radius:var(--r-md)}
.netname-edit{min-width:0}
.netname-view{display:flex;align-items:center;gap:6px;max-width:100%;padding:0;background:none;border:0;color:inherit;text-align:left;font:inherit}
/* The pencil is the affordance; it stays quiet until the row is touched or
   focused, so the card reads as a name and not as a form. */
.netname-view .i{width:15px;height:15px;flex:0 0 auto;color:var(--ink-faint);opacity:.6;transition:opacity var(--dur-fast)}
.netname-view:hover .i,.netname-view:focus-visible .i,.netname-view:active .i{opacity:1;color:var(--accent-text)}
.netname-field{display:flex;align-items:center;gap:var(--s2);margin:0}
.netname-input{font-weight:600;height:38px;padding:0 10px}
.netname-field .icobtn{width:34px;height:34px;flex:0 0 auto;color:var(--ok)}

/* Plan card. A tinted, bordered panel rather than another white card — an
   owner should be able to find it without reading, and it is the one row on
   this hub that is about the product rather than about a setting. */
.plancard{display:block;width:100%;text-align:left;color:inherit;padding:var(--s3);border-radius:var(--r-lg);
  border:1px solid var(--hairline);background:linear-gradient(150deg,var(--accent-wash) 0%,var(--surface-raised) 62%);
  margin-bottom:var(--s3)}
.plancard.premium{background:linear-gradient(150deg,var(--gold-wash) 0%,var(--surface-raised) 62%);border-color:var(--gold)}
.planbadge{display:inline-flex;align-items:center;gap:5px;padding:3px 10px;border-radius:var(--r-pill);
  background:var(--surface-raised);border:1px solid var(--hairline);font-size:var(--fs-xs);font-weight:600;color:var(--ink-soft)}
.planbadge .i{width:13px;height:13px}
.plancard.premium .planbadge{color:var(--gold-text);border-color:var(--gold)}
.planfoot{display:flex;align-items:center;gap:6px;margin-top:var(--s3);color:var(--ink-faint)}
.planfoot .xs{flex:1;min-width:0}
.planfoot .warn{color:var(--gold-text);font-weight:500}
.photo-wrap .edit{position:absolute;top:8px;right:8px;padding:6px 10px;border-radius:var(--r-pill);background:color-mix(in srgb,var(--ink) 70%,transparent);color:var(--sand);font-size:var(--fs-xs);font-weight:600;display:inline-flex;align-items:center;gap:4px;backdrop-filter:blur(6px)}
/* crop sheet */
.crop-frame{position:relative;width:100%;overflow:hidden;border-radius:var(--r-md);background:var(--ink);touch-action:none;cursor:grab;user-select:none}
.crop-frame:active{cursor:grabbing}
.crop-img{position:absolute;left:0;top:0;transform-origin:0 0;max-width:none;will-change:transform;pointer-events:none}
.crop-grid{position:absolute;inset:0;pointer-events:none;background:linear-gradient(to right,transparent 33.3%,rgba(255,255,255,.35) 33.3%,rgba(255,255,255,.35) calc(33.3% + 1px),transparent calc(33.3% + 1px),transparent 66.6%,rgba(255,255,255,.35) 66.6%,rgba(255,255,255,.35) calc(66.6% + 1px),transparent calc(66.6% + 1px)),linear-gradient(to bottom,transparent 33.3%,rgba(255,255,255,.35) 33.3%,rgba(255,255,255,.35) calc(33.3% + 1px),transparent calc(33.3% + 1px),transparent 66.6%,rgba(255,255,255,.35) 66.6%,rgba(255,255,255,.35) calc(66.6% + 1px),transparent calc(66.6% + 1px));box-shadow:inset 0 0 0 1px rgba(255,255,255,.5)}
.crop-zoom{flex:1;accent-color:var(--accent)}
/* Circle channel disclaimer — on every card and above every compose form. Quiet
   by design: present at the moment of acting, never louder than the content. */
.chnote{display:flex;gap:8px;align-items:flex-start;margin:0 16px 12px;padding:8px 10px;border-radius:var(--r-sm);background:var(--surface-sunken);color:var(--ink-soft);font-size:var(--fs-xs);line-height:1.5}
.chnote.compact{margin:0 0 12px}
.chnote .i{color:var(--ink-faint);margin-top:1px}
.chnote .btn.link{font-size:var(--fs-xs);font-weight:600;white-space:nowrap}
.photo-pick .badge{position:absolute;right:0;bottom:0;width:30px;height:30px;border-radius:50%;background:var(--accent);color:var(--accent-ink);display:flex;align-items:center;justify-content:center;border:2px solid var(--surface)}
.steps{display:flex;gap:6px;justify-content:center;margin-bottom:var(--s5)}
.steps i{width:8px;height:8px;border-radius:50%;background:var(--ink-muted)}
.steps i.on{background:var(--accent);width:22px;border-radius:4px}
.steps i.done{background:var(--ink-soft)}

/* ---- sheets, modals ---- */
.scrim{position:fixed;inset:0;background:var(--scrim);z-index:40;opacity:0;transition:opacity var(--dur) var(--ease)}
.scrim.in{opacity:1}
/* Sheet height (2026-08-27). Two caps, and the smaller wins.

   The ceiling is the viewport minus the header strip (2026-09-21: "increase
   height of bottom sheet till highlighted for long sheet" — the highlight ran
   from the header's bottom edge to the screen's). It was `80svh` before
   that, a fifth of the screen clear; and `calc(100svh - 40px)` before THAT,
   which ran a tall form into the status bar — subtracting --safe-t keeps the
   status bar clear now. A SHORT sheet is untouched: this is a ceiling, not a
   height, and anything that fits under it still sizes to its content.

   `var(--sheet-vh)` is the keyboard. app.html sets
   `interactive-widget=resizes-content`, so opening the keyboard SHRINKS the
   layout viewport — but `svh` is the small viewport height and deliberately
   ignores the keyboard, so an 80svh sheet became taller than what was left
   visible and its title was pushed off the top. ui.js publishes the real
   available height as `--sheet-vh` while the keyboard is up, and the `min()`
   makes the sheet fit above it instead of jumping.

   NOT `dvh` for either job: `dvh` also tracks the URL bar collapsing on
   scroll, so the sheet would grow and shrink under the finger mid-scroll —
   the exact bug the comments sheet already fixed once, documented below. */
.sheet{position:fixed;left:0;right:0;bottom:var(--kb-inset, 0px);z-index:41;max-width:var(--content-max);margin:0 auto;background:var(--surface-raised);border-radius:var(--r-xl) var(--r-xl) 0 0;box-shadow:var(--shadow-lg);transform:translateY(100%);transition:transform var(--dur-slow) var(--ease);max-height:min(calc(100svh - var(--safe-t) - var(--header-h)), calc(var(--sheet-vh, 100svh) - 8px));display:flex;flex-direction:column;padding-bottom:max(0px, calc(var(--safe-b) - var(--kb-inset, 0px)));will-change:transform;touch-action:none}
.sheet.in{transform:translateY(0)}
.sheet.dragging{transition:none}
.sheet .grab{width:40px;height:4px;border-radius:2px;background:var(--ink-muted);margin:10px auto 4px;flex:0 0 auto}
.sheet .sh{display:flex;align-items:center;gap:var(--s2);padding:var(--s2) var(--s5) var(--s3);flex:0 0 auto}
.sheet .sh h2{font-size:var(--fs-lg);font-weight:600;flex:1;text-align:center}
/* Sides 16px -> 20px (2026-08-27), same report: a sheet is the full width of
   the screen, so its labels are the closest thing in the app to the edge. */
.sheet .sb{overflow-y:auto;overscroll-behavior:contain;padding:0 var(--s5) var(--s4);flex:1;min-height:0;touch-action:pan-y}
.sheet .sf{padding:var(--s3) var(--s5);border-top:1px solid var(--hairline);display:flex;gap:var(--s2);flex:0 0 auto}
.sheet .sf .btn{flex:1}
/* Comments sheet (2026-08-26, resized the same day).

   `svh`, not `dvh`. `dvh` is the DYNAMIC viewport height and changes as the
   browser's URL bar collapses and expands on scroll — on phones that do that,
   a sheet sized in `dvh` grows and shrinks under the finger and leaves blank
   space at its foot mid-scroll. `svh` is the small viewport height: it assumes
   the chrome is showing and never moves, which is what a fixed overlay wants.

   The `min-height:46dvh` that used to sit here is gone for a second, unrelated
   reason: it reserved half the screen whether or not there was anything to put
   in it, so a two-comment thread opened with an empty half-sheet under it. The
   body sizes to its content now and stops at the cap.

   82%, not "almost the whole screen": the brief for this sheet is that the
   post stays VISIBLE behind it, so a long thread has to stop short rather than
   growing until it has covered the thing being commented on.

   The footer is the composer, and it is not a button row — `.sheet .sf` is a
   flex line that stretches its children and `.sheet .sf .btn{flex:1}` would
   stretch the composer's send button with it. Both are undone here rather than
   by special-casing sheet(), which every other sheet depends on. */
/* Height: no longer set here. Both this and .praisesheet carried their own
   82svh cap; the base .sheet rule now caps every sheet at 80svh, so three
   numbers became one. The svh-not-dvh reasoning above still applies. */
.sheet.comments .sb{padding-top:var(--s1)}
.sheet.comments .sf{display:block;padding:var(--s3) var(--s5) var(--s2)}
.sheet.comments .sf .btn{flex:0 0 auto}
/* Wall of Praise note, opened in a sheet (2026-08-26, replacing .wmodal).
   Same sizing rules as the comment sheet — `svh` for the reason documented
   there, and no min-height so a note with no replies does not open with an
   empty half-screen under it.

   Named `.praisesheet`, NOT `.wnote`: `.wnote` is already the corkboard note
   itself, `{position:absolute;width:40%}`. The first cut used it as the
   sheet's class and the sheet inherited that 40% — it rendered as a 150px
   column with one word per line. Second time this exact mistake has been made
   in this file's neighbourhood (see the .hbjourney note); grep before naming. */
/* Height: see the note on .sheet.comments — capped by the base rule now. */
.sheet.praisesheet .sb{padding-top:var(--s1)}
/* Opening a sticker (batch 5). The stock sheet slides up from off-screen,
   which reads as a panel arriving over the board; the note should read as
   growing into the sheet. Deliberately the same curve and the same
   overshoot-and-settle shape as `wland` — the animation a note already uses
   when it lands on the wall — so the wall has ONE motion idiom rather than
   two that nearly match.
   No fill-mode on purpose: `.sheet` resets `transform` to none once `.in`
   lands, and drag-to-dismiss writes an inline transform. `both` would pin the
   final keyframe and win over that inline style, freezing the drag. Without
   it the element falls back to the class's own transform the moment the
   440ms is up, long before a finger can reach it. */
@keyframes psheetgrow{
  0%{transform:translateY(9%) scale(.86) rotate(-2.5deg);opacity:0}
  62%{transform:translateY(0) scale(1.014) rotate(0deg);opacity:1}
  100%{transform:none;opacity:1}
}
.sheet.praisesheet.in{animation:psheetgrow .44s cubic-bezier(.2,.8,.3,1.1)}
@media (prefers-reduced-motion: reduce){.sheet.praisesheet.in{animation:none}}
/* The note keeps its paper colour and ink but loses the corkboard's absolute
   positioning and tilt: it is being read here, not pinned. */
/* The sheet wears the note's paper (2026-08-26). Opening a sticker should
   feel like the sticker growing, not like a grey panel sliding over the
   board — so the sheet takes the same --note-N/--note-N-ink pair the note
   itself uses, picked by noteHue() off the same hash. */
.sheet.praisesheet.pc1{background:var(--note-1);color:var(--note-1-ink)}
.sheet.praisesheet.pc2{background:var(--note-2);color:var(--note-2-ink)}
.sheet.praisesheet.pc3{background:var(--note-3);color:var(--note-3-ink)}
.sheet.praisesheet.pc4{background:var(--note-4);color:var(--note-4-ink)}
.sheet.praisesheet.pc5{background:var(--note-5);color:var(--note-5-ink)}
.sheet.praisesheet.pc6{background:var(--note-6);color:var(--note-6-ink)}
.sheet.praisesheet.pc7{background:var(--note-7);color:var(--note-7-ink)}
.sheet.praisesheet.pc8{background:var(--note-8);color:var(--note-8-ink)}
/* …and the note inside then drops its own paper, or it would sit as a second
   rectangle of the identical colour with a shadow around it. It keeps only
   its type and spacing. */
.sheet.praisesheet .note{background:none;color:inherit;box-shadow:none;padding-left:0;padding-right:0}
.sheet.praisesheet .note .pin{display:none}
/* Chrome inherits the note's ink rather than the app's. `currentColor` at low
   alpha keeps the grab handle, hairlines and the composer legible on all eight
   papers without eight more rules each. */
.sheet.praisesheet .grab{background:color-mix(in srgb,currentColor 35%,transparent)}
.sheet.praisesheet .sh h2,.sheet.praisesheet .icobtn{color:inherit}
.sheet.praisesheet .note-thread{border-top-color:color-mix(in srgb,currentColor 22%,transparent)}
.sheet.praisesheet .reply .bub{background:color-mix(in srgb,currentColor 13%,transparent);color:inherit}
.sheet.praisesheet .reply .who{color:inherit;opacity:.72}
.sheet.praisesheet .wnav{border-top-color:color-mix(in srgb,currentColor 20%,transparent)}
.sheet.praisesheet .wnav-n{color:inherit;opacity:.7}
.sheet.praisesheet .cbox .cfieldwrap{background:color-mix(in srgb,currentColor 12%,transparent)}
.sheet.praisesheet .cbox .cbox-ph{color:inherit;opacity:.65}
.psheet-body .note,.psheet-body .wnote{position:static;width:auto;transform:none;margin:0}
/* The inline thread, under the note's own footer. Hairline above it so the
   praise and the replies read as two things, and the replies inherit the
   note's ink the same way the reaction row does. */
.note-thread{border-top:1px solid color-mix(in srgb,currentColor 18%,transparent);margin-top:var(--s3);padding-top:var(--s3)}
.note-thread .rlist{max-height:none;overflow:visible}
.note-count{opacity:.75}
/* Prev / next between notes. Sits under the note inside the sheet body, so it
   scrolls with the content rather than floating over the thread. */
.wnav{display:flex;align-items:center;justify-content:space-between;gap:var(--s3);margin-top:var(--s4);padding-top:var(--s3);
  border-top:1px solid var(--hairline-soft)}
.wnav-n{font-size:var(--fs-sm);color:var(--ink-faint);font-variant-numeric:tabular-nums}
.wnav .icobtn[disabled]{opacity:.3;pointer-events:none}
.psheet-body{transition:transform var(--dur-fast) var(--ease)}
/* The 330px cap belongs to the inline drawer this replaced; inside the sheet
   the sheet body is already the scroller and a second one nested in it traps
   the drag-to-dismiss gesture.

   DOUBLED CLASS ON PURPOSE (2026-08-27). As `.rlist-sheet` this rule lost to
   `.rlist` on every property it names: equal specificity, and `.rlist` is
   declared ~190 lines further down, so source order handed it the win. The
   override has therefore never once applied. `.sb` never scrolled, its
   scrollTop was structurally pinned at 0, and BOTH of sheet()'s dismiss
   guards read that scrollTop — so every downward swipe on a comment thread
   was taken as pull-to-dismiss and closed the sheet (reported 2026-08-27).
   `.rlist.rlist-sheet` wins on specificity instead of position, so reordering
   this file cannot silently break it again. */
.rlist.rlist-sheet{max-height:none;overflow:visible;padding-right:0}
.actions{padding:var(--s2) var(--s3) var(--s3)}
.actions .act{width:100%;min-height:52px;display:flex;align-items:center;gap:var(--s3);padding:0 var(--s4);border-radius:var(--r-md);font-size:var(--fs-base);font-weight:500;color:var(--ink)}
.actions .act:active{background:var(--surface-sunken)}
.actions .act.danger{color:var(--error)}
.actions .act.cancel{justify-content:center;font-weight:600;color:var(--ink-soft);margin-top:var(--s2);background:var(--surface-sunken)}
.center-modal{position:fixed;inset:0;z-index:41;display:flex;align-items:center;justify-content:center;padding:var(--s5)}
.center-modal .box{width:100%;max-width:360px;background:var(--surface-raised);border-radius:var(--r-lg);padding:var(--s5);box-shadow:var(--shadow-md);transform:scale(.96);opacity:0;transition:all var(--dur) var(--ease)}
.center-modal.in .box{transform:none;opacity:1}

/* ---- toast ---- */
.toasts{position:fixed;left:0;right:0;bottom:calc(var(--tabbar-h) + var(--safe-b) + var(--s3));z-index:60;display:flex;flex-direction:column;align-items:center;gap:var(--s2);pointer-events:none;padding:0 var(--s4)}
.toast{pointer-events:auto;max-width:420px;padding:10px 16px;border-radius:var(--r-md);background:var(--ink);color:var(--sand);font-size:var(--fs-md);font-weight:500;box-shadow:var(--shadow-md);display:flex;align-items:center;gap:var(--s3);animation:toastin var(--dur-slow) var(--ease)}
.toast.error{background:var(--error);color:#fff}
.toast.ok{background:var(--ok);color:#fff}
.toast .btn{min-height:30px;padding:0 10px;font-size:var(--fs-sm);background:rgba(255,255,255,.18);color:inherit;border-radius:var(--r-sm)}
@keyframes toastin{from{transform:translateY(12px);opacity:0}}

/* ---- skeleton / empty / splash ---- */
.sk{background:linear-gradient(90deg,var(--surface-sunken) 25%,var(--sand-deep) 37%,var(--surface-sunken) 63%);background-size:400% 100%;animation:sk 1.3s ease infinite;border-radius:var(--r-sm)}
@keyframes sk{0%{background-position:100% 0}100%{background-position:0 0}}
.sk.line{height:12px;margin:6px 0}.sk.line.w60{width:60%}.sk.line.w40{width:40%}.sk.av{width:40px;height:40px;border-radius:50%}
.empty{text-align:center;padding:var(--s8) var(--s5);color:var(--ink-soft)}
.empty .i{width:40px;height:40px;margin:0 auto var(--s3);color:var(--ink-muted)}
.empty h3{color:var(--ink);font-size:var(--fs-lg);margin-bottom:var(--s1)}
.splash{position:fixed;inset:0;z-index:100;background:var(--sand);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--s4);transition:opacity var(--dur-slow)}
.splash.out{opacity:0;pointer-events:none}
.splash img{width:88px;height:88px}
.dots{display:flex;gap:6px}.dots i{width:7px;height:7px;border-radius:50%;background:var(--ink-muted);animation:bounce 1s infinite}.dots i:nth-child(2){animation-delay:.15s}.dots i:nth-child(3){animation-delay:.3s}
@keyframes bounce{0%,80%,100%{transform:translateY(0)}40%{transform:translateY(-6px)}}
.ptr{height:0;overflow:hidden;display:flex;align-items:center;justify-content:center;color:var(--ink-faint);font-size:var(--fs-sm);transition:height var(--dur)}
.ptr.show{height:40px}

/* ---- panels (You sub-screens, full-screen pushes) ---- */
.panel{position:fixed;inset:0;z-index:30;background:var(--sand);display:flex;flex-direction:column;transform:translateX(100%);transition:transform var(--dur-slow) var(--ease);will-change:transform}
.panel.in{transform:none}
.panel .pb{flex:1;overflow-y:auto;overflow-x:clip;overscroll-behavior:contain;-webkit-overflow-scrolling:touch}
.panel .pb-in{max-width:var(--content-max);margin:0 auto;padding:var(--s3) var(--s3) calc(var(--safe-b) + var(--s8))}

/* ---- auth / onboarding ---- */
.auth{min-height:100dvh;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--s6) var(--s5) calc(var(--safe-b) + var(--s6));max-width:440px;margin:0 auto}
.auth .logo{width:72px;height:72px;margin-bottom:var(--s4)}
/* Theme-swapped art: both files ship (each ~300 bytes) and CSS chooses, so no
   render-order or listener can leave the wrong one showing. */
.only-dark{display:none}
:root[data-theme="dark"] .only-light{display:none}
:root[data-theme="dark"] .only-dark{display:block}
.auth h1{font-size:var(--fs-2xl);font-weight:600;letter-spacing:-.01em;text-align:center;text-wrap:balance}
.auth .sub{color:var(--ink-soft);text-align:center;margin:var(--s2) 0 var(--s6);max-width:32ch}
.auth .tagline{color:var(--ink-soft);text-align:center;font-size:var(--fs-sm);font-weight:600;letter-spacing:.06em;text-transform:uppercase;margin:calc(var(--s3) * -1) 0 var(--s4)}
/* Devanagari sits a touch lower and lighter than the Latin it follows; the
   separator carries the optical gap so the two names read as one lockup. */
.brand-dev{color:var(--ink-soft);font-weight:600}
/* ---- sign-in, direction B "Stage" (2026-09-23) ----
   An ink stage holding the mark, over a sheet holding the form. The stage is
   --stage in BOTH themes; the sheet is --surface, so it is what carries the
   theme. Asked for with no scrollbars either way: the layout fits 100dvh on a
   phone (the stage is the part that gives), sideways overflow is clipped, and
   the bar itself is hidden on this page only - a short screen or an open
   keyboard can still scroll, it just never shows a bar. The body behind is
   split stage/surface so an iOS rubber-band never shows the wrong colour. */
:root:has(.auth.stage){scrollbar-width:none;overflow-x:clip}
:root:has(.auth.stage)::-webkit-scrollbar,:root:has(.auth.stage) body::-webkit-scrollbar{display:none;width:0;height:0}
:root:has(.auth.stage) body{background:linear-gradient(var(--stage) 50%,var(--surface) 50%);background-attachment:scroll;overflow-x:clip}
.auth.stage{max-width:none;min-height:100dvh;padding:0;background:var(--stage);justify-content:flex-start;align-items:stretch;overflow-x:clip}
.stage-top{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--s4);
  padding:calc(var(--safe-t) + var(--s6)) var(--s5) var(--s7);background:var(--stage);color:var(--stage-ink);text-align:center}
.stage-top .logo{width:60px;height:60px;margin:0}
.stage-word{font-size:26px;font-weight:700;line-height:1.1;letter-spacing:.22em;padding-left:.22em}
.stage-dev{margin-top:4px;font-size:20px;font-weight:600;line-height:1.3;color:var(--stage-soft)}
.stage-top .tagline{margin:0;color:var(--gold);font-size:var(--fs-xs);letter-spacing:.14em}
.stage-sheet{flex:0 0 auto;width:100%;max-width:520px;margin:0 auto;display:flex;flex-direction:column;
  padding:var(--s7) 28px calc(var(--safe-b) + var(--s6));background:var(--surface);color:var(--ink);border-radius:28px 28px 0 0}
.stage-sheet h1{text-align:left;font-size:var(--fs-2xl);line-height:1.15}
.stage-sheet .sub{text-align:left;max-width:none;margin:6px 0 var(--s6);font-size:var(--fs-md)}
.stage-sheet .invitecard{margin:0 0 var(--s5)}
.stage-sheet .field{margin-bottom:var(--s4)}
.stage-sheet .input{min-height:54px;border-radius:14px;background:var(--surface-sunken);border-color:transparent}
.stage-sheet .otp{justify-content:space-between;gap:var(--s2);margin-bottom:var(--s4)}
.stage-sheet .otp input{flex:1 1 0;min-width:0;max-width:52px;height:58px;border-radius:12px;background:var(--surface-sunken);border-color:transparent}
:root[data-theme="dark"] .stage-sheet .input,:root[data-theme="dark"] .stage-sheet .otp input{border-color:var(--hairline-strong)}
.stage-sheet .input:focus,.stage-sheet .otp input:focus,
:root[data-theme="dark"] .stage-sheet .input:focus,:root[data-theme="dark"] .stage-sheet .otp input:focus{border-color:var(--accent);background:var(--surface)}
.stage-sheet .input[aria-invalid="true"]{border-color:var(--error)}
.stage-sheet .btn.primary{min-height:54px;border-radius:14px}
.stage-fill{flex:1 1 auto;min-height:var(--s6)}
.stage-lock{display:flex;align-items:center;justify-content:center;gap:6px;font-size:var(--fs-xs);font-weight:600;color:var(--ink-soft)}
.stage-lock .i{width:12px;height:12px;color:var(--ok);flex:0 0 auto}
.stage-sheet .legal{margin-top:var(--s3)}
/* Short screens: the stage gives up the Devanagari line and the tagline
   first, then its padding, so the sheet never has to scroll on a phone. */
@media (max-height:720px){.stage-dev,.stage-top .tagline{display:none}.stage-top{gap:var(--s3)}.stage-top .logo{width:48px;height:48px}}
@media (max-height:600px){.stage-top{padding-top:calc(var(--safe-t) + var(--s4));padding-bottom:var(--s5)}.stage-word{font-size:20px}.stage-sheet{padding-top:var(--s6)}}
.auth form{width:100%}
.legal{font-size:var(--fs-xs);color:var(--ink-soft);text-align:center;margin-top:var(--s6)}
.legal a{text-decoration:underline}
/* Invite preview card on the sign-in page. */
.invitecard{display:flex;align-items:center;gap:var(--s3);width:100%;margin:calc(var(--s4) * -1) 0 var(--s5);
  padding:var(--s3);border:1px solid var(--hairline);border-radius:var(--r-lg);background:var(--surface-raised)}
.invitelogo{width:36px;height:36px;border-radius:10px;flex:0 0 auto;display:flex;align-items:center;justify-content:center;
  background:var(--surface-sunken);color:var(--ink-soft);overflow:hidden}
.invitelogo img{width:100%;height:100%;object-fit:cover}
.forkcard{display:flex;align-items:center;gap:var(--s4);padding:var(--s4);border-radius:var(--r-lg);background:var(--surface);border:1px solid var(--card-edge);width:100%;text-align:left}
.forkcard .lead{width:48px;height:48px;border-radius:var(--r-md);background:var(--accent-wash);color:var(--accent-text);display:flex;align-items:center;justify-content:center}
.forkcard h3{font-size:var(--fs-base);font-weight:600}
.forkcard p{font-size:var(--fs-sm);color:var(--ink-soft)}
.code{font:600 var(--fs-3xl) var(--font-mono);letter-spacing:.18em;text-align:center;padding:var(--s4);border-radius:var(--r-lg);background:var(--surface-sunken)}
/* App icon picker (You > Preferences > App Icon) */
.appicon-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s3)}
.appicon-grid button{display:flex;flex-direction:column;align-items:center;gap:var(--s2);padding:var(--s3);border-radius:var(--r-md);border:2px solid transparent}
.appicon-grid button[aria-pressed="true"]{border-color:var(--accent);background:var(--accent-wash)}
.appicon-grid img{width:64px;height:64px;border-radius:16px}
.hero{display:flex;flex-direction:column;align-items:center;text-align:center;gap:var(--s2);padding:var(--s5) 0}
.hero h2{font-size:var(--fs-xl);font-weight:600}
.role{font-size:var(--fs-xs);font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--gold-text)}
.menu{position:absolute;top:calc(100% + 6px);left:50%;transform:translateX(-50%);min-width:280px;max-width:calc(100vw - 24px);background:var(--surface-raised);border:1px solid var(--card-edge);border-radius:var(--r-lg);box-shadow:var(--shadow-md);z-index:30;overflow:hidden}
.menu .li{min-height:52px}
.menu .foot{display:flex;gap:var(--s2);padding:var(--s2);border-top:1px solid var(--hairline)}
.menu .foot .btn{flex:1}
.howto{list-style:decimal;padding-left:22px;color:var(--ink-soft);font-size:var(--fs-md)}.howto li{margin:6px 0}
/* install-gate steps that carry a real Share / Add-to-Home-Screen glyph */
.howto.icon{list-style:none;padding-left:0}
.howto.icon li{display:flex;align-items:center;gap:var(--s3);margin:var(--s2) 0}
.howto-ic{flex:0 0 auto;width:36px;height:36px;border-radius:10px;background:var(--surface-sunken);display:inline-flex;align-items:center;justify-content:center;color:var(--ink)}
.offline{position:fixed;top:calc(var(--safe-t) + var(--header-h));left:0;right:0;z-index:19;text-align:center;font-size:var(--fs-xs);font-weight:600;padding:4px;background:var(--gold);color:var(--accent-ink)}
.stag{position:fixed;top:calc(var(--safe-t) + 6px);left:50%;transform:translateX(-50%);z-index:21;font:600 9px var(--font-mono);letter-spacing:.14em;padding:2px 8px;border-radius:var(--r-pill);background:var(--gold);color:var(--accent-ink);pointer-events:none;opacity:.9}
@media (min-width:700px){.sheet{border-radius:var(--r-xl);bottom:calc(24px + var(--kb-inset, 0px));max-height:min(80svh, calc(var(--sheet-vh, 100svh) - 80px))}.tabbar-in,.hdr-in{padding-left:0;padding-right:0}}
::view-transition-old(root),::view-transition-new(root){animation-duration:var(--dur)}

/* ---- legal ---- */
.legal-body h4{font-size:var(--fs-md);font-weight:600;margin:var(--s4) 0 var(--s1)}
.legal-body p,.legal-body li{font-size:var(--fs-md);color:var(--ink-soft);line-height:1.55;margin-bottom:var(--s2)}
.legal-body ul{padding-left:18px;list-style:disc}
.legal-body strong{color:var(--ink);font-weight:600}

/* ---- posts ---- */
.post{background:var(--surface);border:1px solid var(--card-edge);border-radius:var(--r-lg);overflow:hidden;box-shadow:var(--shadow-sm);margin-bottom:var(--s3)}
.post.pending{outline:2px dashed var(--gold)}
.post .ph{display:flex;align-items:center;gap:var(--s3);padding:var(--s3) var(--s4) var(--s2)}
.post .ph .nm{font-weight:600;font-size:var(--fs-md)}
.post .ph .mt{font-size:var(--fs-xs);color:var(--ink-faint)}
.post .body{padding:0 var(--s4) var(--s3);font-size:var(--fs-base);line-height:1.5;white-space:pre-wrap;word-break:break-word}
.post .body h3{font-size:var(--fs-lg);font-weight:600;margin-bottom:var(--s1)}
/* "....see more" after a body cut at 700 characters (post-card.js clipBody). Inline, so it follows the last word. */
.post .body .seemore{display:inline;background:none;border:0;padding:0 0 0 2px;font:inherit;font-weight:600;color:var(--accent-text);cursor:pointer;white-space:nowrap}
.post .foot{display:flex;align-items:center;gap:var(--s2);padding:var(--s2) var(--s3) var(--s3);border-top:1px solid var(--hairline-soft)}
.react{display:inline-flex;align-items:center;gap:6px;min-height:40px;min-width:44px;padding:0 12px;border-radius:var(--r-pill);border:1px solid var(--hairline-strong);background:var(--surface);font-size:var(--fs-sm);font-weight:600;color:var(--ink-soft);transition:transform var(--dur-fast)}
/* ---- C2, the quiet member card (2026-09-23, "AIKYA Home — almanac
   directions", card C2) ---- Only .post.quiet, which post-card.js sets on an
   ordinary member post in the feed. The caption is one grey 12px line with
   the name at full contrast; the words and one 88px thumbnail share a row;
   the foot is a sentence — the existing reaction glyph, flat, then the counts
   in grey — with no border above it. */
.post.quiet .ph.quiet{gap:0;padding:10px 8px 0 16px;font-size:var(--fs-xs);color:var(--ink-faint);min-width:0;line-height:1.3}
.post.quiet .ph.quiet .av{margin-right:8px}
/* Two lines since 2026-09-23 (asked): name over department · time. */
.post.quiet .ph.quiet .qwho{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:1px;line-height:1.25}
.post.quiet .ph.quiet .nm{font-weight:700;font-size:var(--fs-xs);color:var(--ink);display:block;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.post.quiet .ph.quiet .mt{display:block;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:var(--fs-xs)}
.post.quiet .ph.quiet .icobtn{flex:0 0 auto;width:36px;height:36px;min-height:36px;margin:-4px 0 -4px 0}
.post.quiet .qrow{display:flex;gap:12px;align-items:flex-start;padding:8px var(--s4) 0}
.post.quiet .qrow .body{flex:1 1 auto;min-width:0;padding:0;font-size:var(--fs-md);line-height:1.45}
.post.quiet .qrow .body h3{font-size:17px;font-weight:700;line-height:1.25;margin-bottom:4px}
/* E3 (2026-09-23): the kind chip on the caption line, and the photo at full
   width with the title on it. Chip text uses the text-safe tokens (--pink,
   --gold-text), not --accent, which is 3.3:1 as text. */
.post.quiet .kchip{flex:0 0 auto;height:20px;padding:0 8px;margin-left:8px;border-radius:var(--r-pill);font-size:12px;font-weight:700;line-height:1;display:inline-flex;align-items:center;white-space:nowrap}
.kchip.k-celebration{background:color-mix(in srgb,var(--pink) 16%,transparent);color:var(--pink)}
.kchip.k-fitness{background:color-mix(in srgb,var(--teal) 16%,transparent);color:var(--teal)}
.kchip.k-general{background:color-mix(in srgb,var(--info) 16%,transparent);color:var(--info)}
.kchip.k-event{background:var(--gold-wash);color:var(--gold-text)}
.post.quiet .qmedia{position:relative;display:block;width:calc(100% - 24px);height:180px;margin:10px 12px 0;border-radius:var(--r-md);overflow:hidden;padding:0;border:0;background:color-mix(in srgb,var(--ink) 7%,var(--surface));cursor:pointer}
.post.quiet .qmedia img,.post.quiet .qmedia video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.post.quiet .qmedia .vwrap{position:absolute;inset:0}
/* Stronger since 2026-09-23 (asked): the title must read on a white or busy
   photo too. Its own class, .qscrim: as .scrim it inherited the sheet
   backdrop's opacity:0 and was never visible at all. The dark starts higher and is near-solid behind the text line;
   the text-shadow catches any bright pixel that still shows through. */
.post.quiet .qmedia .qscrim{position:absolute;inset:0;background:linear-gradient(180deg,rgba(0,0,0,0) 30%,rgba(0,0,0,.38) 55%,rgba(0,0,0,.72) 78%,rgba(0,0,0,.86) 100%);pointer-events:none}
.post.quiet .qmedia h3{position:absolute;left:14px;right:14px;bottom:12px;margin:0;font-size:17px;font-weight:800;line-height:1.2;color:#fff;text-align:left;text-shadow:0 1px 3px rgba(0,0,0,.65),0 0 12px rgba(0,0,0,.35)}
.post.quiet .qmedia .n{position:absolute;top:10px;right:10px;height:22px;padding:0 8px;border-radius:var(--r-pill);background:rgba(16,20,24,.72);color:#fff;font-size:11px;font-weight:700;line-height:1;display:flex;align-items:center;gap:4px}
.post.quiet .qmedia .n .i{width:12px;height:12px}
.post.quiet .qbody{padding:8px var(--s4) 0;font-size:var(--fs-md);line-height:1.45}
/* The quick filter above the feed: All and one pill per kind, its colour as
   a dot. Scrolls sideways if a phone is narrower than the four. */
.kfilter{display:flex;gap:8px;margin-bottom:12px;padding:2px 0;overflow-x:auto;scrollbar-width:none}
.kfilter::-webkit-scrollbar{display:none}
.kfilter .kf{flex:0 0 auto;height:34px;padding:0 12px;border-radius:var(--r-pill);border:1px solid var(--hairline-strong);background:transparent;color:var(--ink-soft);font-family:inherit;font-size:var(--fs-sm);font-weight:600;display:inline-flex;align-items:center;gap:7px;cursor:pointer}
.kfilter .kf[aria-pressed="true"]{background:var(--ink);color:var(--surface);border-color:var(--ink);font-weight:700}
.kfilter .kf .dot{width:9px;height:9px;border-radius:50%}
.kfilter .kf.k-celebration .dot{background:var(--pink)}
.kfilter .kf.k-fitness .dot{background:var(--teal)}
.kfilter .kf.k-general .dot{background:var(--info)}
.kfilter .kf.k-event .dot{background:var(--gold)}
.kempty{padding:var(--s3) 0}
.post.quiet .foot{border-top:0;gap:0;padding:4px var(--s4) 10px;font-size:var(--fs-sm);color:var(--ink-faint)}
.post.quiet .foot .react{min-width:0;padding:0 5px 0 0;border:0;border-radius:0;background:transparent;margin-right:0;justify-content:flex-start}
.post.quiet .foot .react[aria-pressed="true"]{background:transparent}
.post.quiet .foot .rfaces.txt{display:inline-flex;align-items:center;min-height:40px;min-width:24px;padding:0;border:0;background:transparent;color:var(--ink-faint);font-size:var(--fs-sm);font-weight:400}
/* The comments count leads with the chat glyph (asked 2026-09-23, mirroring
   the reaction glyph), so the two groups separate themselves and the mock's
   "·" between them is not drawn. */
.post.quiet .foot .btn.link.sm{display:inline-flex;align-items:center;gap:5px;min-height:40px;padding:0;margin-left:14px;color:var(--ink-faint);font-weight:400;font-size:var(--fs-sm)}
.post.quiet .foot .btn.link.sm .i{color:var(--ink-soft)}
.post.quiet .foot .react+.btn.link.sm{margin-left:4px}
/* urgent card on Home (2026-08-21) — the strip everywhere else. Blood: wash
   ground, red badge carrying the group; emergency: red border, alert badge. */
/* Home alert cards (2026-08-22): emergency, notice, announcement and blood in
   ONE shape - a 40px tone block, an uppercase kick, the title, a chevron. The
   base is the notice/blood tone (error wash); .em fills solid, .ann goes gold
   with the same accent-ink inversion the gold header band uses. */
.ucard{display:flex;align-items:center;gap:12px;width:100%;margin:var(--s2) 0 0;
  text-align:left;padding:12px;border-radius:var(--r-lg);background:var(--surface);
  border:1px solid var(--card-edge);border-left:4px solid var(--error)}
.ucard .ub{width:40px;height:40px;border-radius:11px;display:flex;align-items:center;justify-content:center;
  flex:0 0 auto;font-weight:800;font-size:14px;background:var(--error);color:#fff}
.ucard .uk{font-size:12px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--error)}
.ucard .ut{font-weight:600;font-size:14.5px;line-height:1.3;color:var(--ink)}
.ucard > .i{color:var(--ink-faint);flex:0 0 auto}
.ucard.em{background:var(--error);border:0;border-radius:var(--r-lg);padding:12px 14px}
.ucard.em .ut{color:#fff;font-weight:700;font-size:15px}
.ucard.em .us{color:rgba(255,255,255,.85);font-size:13px;margin-top:1px}
.ucard.em > .i{color:rgba(255,255,255,.85);flex:0 0 auto}
.ucard.em > .i:last-child{color:rgba(255,255,255,.7)}
.ucard.ann{border-color:var(--card-edge);border-left-color:var(--gold)}
.ucard.ann .ub{background:var(--gold);color:var(--accent-ink)}
.ucard.ann .uk{color:var(--gold-text)}

/* who-reacted: "+N" hugging the reaction pill, tappable */
.rwho{display:inline-flex;align-items:center;min-height:40px;margin-left:calc(var(--s2) * -1 + 4px);padding:0 4px}
.react[aria-pressed="true"]{background:var(--accent-wash);color:var(--accent-text);border-color:transparent}
.react:active{transform:scale(.94)}
.react .i{width:22px;height:22px}
.react.pop .i{animation:pop .35s var(--ease)}
@keyframes pop{40%{transform:scale(1.5)}}
.hero{position:relative;min-height:220px;display:flex;flex-direction:column;justify-content:flex-end;padding:var(--s4);color:#fff;background:linear-gradient(135deg,var(--violet) 0%,var(--accent) 60%,var(--gold) 120%);isolation:isolate}
.hero.anniv{background:linear-gradient(135deg,var(--info) 0%,var(--teal) 55%,var(--gold) 120%)}
.hero.occ{background:linear-gradient(135deg,var(--amber) 0%,var(--accent) 60%,var(--violet) 120%)}
.hero.welcome{background:linear-gradient(135deg,var(--teal) 0%,var(--info) 55%,var(--violet) 120%)}
/* ---- Daily Move recap (Option B, 2026-09-10) -------------------------------
   Rides the steps slab's colour scheme, variables and all: gold with dark ink
   in light mode, deep teal with cream in dark. The slab learnt that the hard
   way — a dark-teal card in light mode read as a dark-mode leak — so this does
   not repeat the lesson. Top-aligned and content-sized, unlike the celebration
   heroes, which bottom-align over a photo. */
.hero.recap{--slab-ink:var(--accent-ink);--slab-soft:rgba(20,40,45,.74);--slab-fill:#14282D;
  min-height:0;justify-content:flex-start;align-items:flex-start;text-align:left;padding:44px var(--s4) var(--s4);
  background:linear-gradient(135deg,var(--gold),var(--accent));color:var(--slab-ink)}
:root[data-theme="dark"] .hero.recap{--slab-ink:#F5EDE1;--slab-soft:rgba(245,237,225,.7);--slab-fill:var(--gold);background:linear-gradient(120deg,#14282D,#1E3A41)}
.hero.recap::after{display:none}
.hero.recap .chip{background:color-mix(in srgb,var(--slab-ink) 14%,transparent);color:var(--slab-ink);backdrop-filter:none}
.hero.recap .meta{position:absolute;top:var(--s3);right:var(--s4);font-size:var(--fs-xs);color:var(--slab-soft);opacity:1}
.hero.recap h2{font-size:20px;font-weight:600;line-height:1.25;text-shadow:none;margin:0;color:var(--slab-ink)}
.rstats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s2);width:100%;margin-top:var(--s3)}
.rstats b{display:block;font-family:var(--font-mono);font-size:22px;font-weight:600;line-height:1.1;color:var(--slab-ink)}
.rstats b.gold{color:var(--slab-fill)}
.rstats b small{font-family:var(--font-sans);font-size:12.5px;font-weight:500;color:var(--slab-soft)}
.rstats span{display:block;font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--slab-soft);margin-top:2px}
.post .rwho{display:flex;align-items:center;gap:10px;padding:var(--s3) var(--s4) 0}
.post .rwho .av{border-color:var(--surface)}
.post .rline{padding:var(--s2) var(--s4) 0;color:var(--ink-soft)}
.post .rbadges{display:flex;flex-wrap:wrap;gap:6px;padding:var(--s2) var(--s4) 0}
.post .rbadges .pill.gold{background:var(--gold-wash);color:var(--gold-text)}
.post .rbody{padding-top:var(--s3)}
.post .rcta{padding:var(--s3) var(--s4) var(--s4)}
.post .rcta .btn .i{margin-left:2px}
/* soft: a faint wash instead of the full gradient, so ink and .muted read as normal text (onboarding "waiting for approval") */
.hero.soft{background:linear-gradient(180deg,color-mix(in srgb,var(--accent-wash) 45%,var(--surface)) 0%,var(--surface) 100%);color:var(--ink);align-items:center;text-align:center;justify-content:center;border:1px solid var(--card-edge);border-radius:var(--r-lg)}
.hero.soft::after{display:none}
.hero.soft h2{text-shadow:none}
.hero.soft .av{border-color:var(--surface);box-shadow:0 2px 8px rgba(0,0,0,.08)}
.hero img.cover{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:-1}
.hero::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgba(0,0,0,.05) 0%,rgba(0,0,0,.55) 100%);z-index:-1}
.hero .chip{position:absolute;top:var(--s3);left:var(--s3);padding:4px 10px;border-radius:var(--r-pill);background:rgba(255,255,255,.22);backdrop-filter:blur(8px);font-size:var(--fs-xs);font-weight:600;color:#fff;letter-spacing:.04em;border:0}
.hero .av{border:3px solid rgba(255,255,255,.9);box-shadow:0 4px 16px rgba(0,0,0,.25)}
.hero .more{position:absolute;top:var(--s3);right:var(--s3);width:36px;height:36px;color:#fff;z-index:1}
.hero h2{font-size:var(--fs-xl);font-weight:600;margin-top:var(--s2);text-shadow:0 1px 8px rgba(0,0,0,.3)}
.hero .meta{font-size:var(--fs-sm);opacity:.9}
/* The honoree's name is a link to their profile (2026-09-12). On a photo hero
   a colour change would be invisible against whatever the cover happens to be,
   so it is marked with an underline and full opacity against the .9 the rest
   of the meta line carries — readable on a gradient and on a cover alike. */
.hero .meta .who-link{opacity:1;font-weight:600;text-decoration:underline;text-underline-offset:2px;text-decoration-thickness:1px}
.conf{position:absolute;inset:0;pointer-events:none;overflow:hidden;z-index:-1}
.conf i{position:absolute;width:6px;height:10px;border-radius:2px;background:#fff;opacity:.55;animation:fall 6s linear infinite}
@keyframes fall{0%{transform:translateY(-20px) rotate(0)}100%{transform:translateY(260px) rotate(300deg)}}
/* The celebration message sits directly on the card (0.7.34). It used to be a
   tinted, shadowed panel, which read as a second card nested inside the
   first — the hero above it is already doing the visual work. Horizontal
   margin matches .post .body so every card's text shares one left edge. */
.sticker{margin:var(--s3) var(--s4) var(--s4);color:var(--ink);font-family:var(--font-sans);font-size:var(--fs-base);font-weight:500;line-height:1.6}
.media{display:grid;gap:3px;background:var(--surface-sunken)}
.media.n1{grid-template-columns:minmax(0,1fr)}.media.n2{grid-template-columns:repeat(2,minmax(0,1fr))}.media.n3{grid-template-columns:minmax(0,2fr) minmax(0,1fr);grid-template-rows:1fr 1fr}.media.n3 button:first-child{grid-row:span 2}.media.n4,.media.n5,.media.n6{grid-template-columns:repeat(2,minmax(0,1fr))}
.media button{position:relative;aspect-ratio:1;overflow:hidden;background:var(--surface-sunken)}
/* A single photo is 4:3 landscape, capped and centred (0.7.36).
   History, so nobody reverts a step of it: it was full-bleed and free-height,
   which let a portrait shot run most of a screen and pushed the reactions
   below the fold; 0.7.1 forced 1:1, which fixed the height but threw away the
   sides of the event and group photos that make up most posts, and was still
   as tall as the card is wide. 4:3 keeps the shape people actually framed and
   is shorter than either — 263px on a phone against 351 square. A fixed ratio
   also means the height is known before the image loads, so there is no layout
   shift and no need for the API's real dimensions here. Inset, it needs its own
   corners; full-bleed it borrowed the card's. Multi-photo grids stay square. */
.media.n1{max-width:420px;margin-inline:auto;border-radius:var(--r-md);overflow:hidden}
.media.n1 button{aspect-ratio:4/3}
.media img{width:100%;height:100%;object-fit:cover;display:block}
.media .vwrap{width:100%;height:100%;position:relative;background:var(--surface-sunken)}
.media .vwrap video,.media .vwrap img{width:100%;height:100%;object-fit:cover;display:block}
/* Inline playback in the post sheet (2026-09-18). A lone video takes its own
   aspect ratio (set inline from the recorded dimensions) instead of the 4:3
   photo cell, capped so a portrait clip never fills the sheet; letterboxed
   on black rather than cropped. In a multi-cell grid it stays a cover crop
   like everything else. */
/* Instagram feed rule (asked 2026-09-19, replacing the letterbox): the
   cell's ratio is the clip's own, clamped in inlineVideo() to 4:5 through
   1.91:1, and the picture FILLS it. A 9:16 phone clip loses a little top and
   bottom instead of sitting as a narrow strip in a wide black box, which is
   what the screenshot showed. The 70svh cap is a safety net for a short
   viewport; cover keeps it filled when it bites. */
.media.n1 .vcell{max-height:70svh;background:#000}
.media.n1 .vcell .vwrap.inline{background:#000}
.media.n1 .vcell .vwrap.inline video{object-fit:cover}
/* Carousel (2026-09-18): several photos as one swipeable SQUARE strip. The
   track fills it and scrolls sideways with mandatory snap points, one slide
   per swipe. Dots and the counter sit over the picture; the arrows exist
   only where a mouse does. */
.media.carousel{display:block;position:relative;aspect-ratio:1;max-width:420px;margin-inline:auto;border-radius:var(--r-md);overflow:hidden;background:#000}
.media.carousel .track{position:absolute;inset:0;display:flex;overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain;scrollbar-width:none}
.media.carousel .track::-webkit-scrollbar{display:none}
.media.carousel .slide{flex:0 0 100%;height:100%;scroll-snap-align:start;scroll-snap-stop:always}
.media.carousel .slide > button{width:100%;height:100%;aspect-ratio:auto;max-height:none;display:block;background:#000}
.media.carousel .vwrap.inline{background:#000}
.media.carousel .vwrap.inline video{object-fit:contain}
.media.carousel .count{position:absolute;top:10px;right:10px;padding:2px 8px;border-radius:var(--r-pill);background:rgba(0,0,0,.55);color:#fff;font-size:var(--fs-xs);font-weight:600;line-height:1.6;pointer-events:none}
.media.carousel .dots{position:absolute;left:0;right:0;bottom:8px;display:flex;justify-content:center;gap:5px;pointer-events:none}
/* Static dots (asked 2026-09-19): no transition, no scale — the current one
   is simply the solid one, the way Instagram draws them. `animation:none`
   is the actual fix: the generic `.dots i` loading indicator (bounce 1s
   infinite, line ~594) shares the class name and was making these bob. */
.media.carousel .dots i{width:6px;height:6px;border-radius:50%;background:rgba(255,255,255,.5);animation:none}
.media.carousel .dots i.on{background:#fff}
.media.carousel .nav{position:absolute;top:50%;transform:translateY(-50%);width:32px;height:32px;border-radius:50%;background:rgba(255,255,255,.88);color:#14181d;display:none;align-items:center;justify-content:center;box-shadow:0 1px 6px rgba(0,0,0,.3);aspect-ratio:auto}
.media.carousel .nav.prev{left:8px}
.media.carousel .nav.next{right:8px}
@media (hover:hover){.media.carousel:hover .nav:not([hidden]){display:flex}}
/* Full-bleed (decided 2026-09-19, after a 16px inset was tried and shown):
   the picture touches the card's border on both sides with square corners,
   the card's own rounding clips it at the top and bottom. The 420px cap and
   the centring that .media.n1 / .media.carousel carry are dropped INSIDE a
   card for the same reason - a capped strip on a wide card would leave a
   dark margin either side, which is exactly not touching the border. */
.post > .media,.post > .media.n1,.post > .media.carousel{margin:0;max-width:none;width:100%;border-radius:0}
/* The post sheet at 4:5 (asked 2026-09-24, mockup option B in
   aikya-backend/docs/mockups/post-media-portrait-zoom-2026-09-24.html):
   one portrait frame for everything the sheet shows - a lone photo (4:3
   above), the strip (1:1 above) and a lone video (its own ratio, and capped
   to fit under the header since 2026-09-21; that cap is gone, the caption
   and thread scroll below the frame now). Everything FILLS the frame, so a
   landscape photo or a 16:9 clip crops its sides here and shows whole in
   the viewer. The feed card is untouched - .sheetmedia is only set by
   mediaGrid's inline (post sheet) mode.
   The width cap only bites on a short, wide screen (a desktop sheet), where
   a full-width 4:5 frame would be taller than the sheet; a phone never hits
   it. touch-action keeps both scrolls and hands pinches to pinchInPlace(). */
.post > .media.sheetmedia,.media.sheetmedia{display:block;position:relative;aspect-ratio:4/5;max-width:min(100%, calc((100svh - 220px) * .8));margin-inline:auto;background:#000;touch-action:pan-x pan-y}
.media.sheetmedia.n1 > button{width:100%;height:100%;aspect-ratio:auto;max-height:none;display:block}
.media.sheetmedia .vwrap.inline video{object-fit:cover}
/* mute / unmute on the sheet's inline video (2026-09-24): a corner of the
   clip, bottom right; the carousel's dots are centred and its count is top right */
.media .vwrap .vmute{position:absolute;right:10px;bottom:10px;z-index:2;width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.55);color:#fff;cursor:pointer;-webkit-tap-highlight-color:transparent}
.media .vwrap .vmute svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.media.sheetmedia button{touch-action:pan-x pan-y}
/* The lifted copy a pinch drags around (pinchInPlace). Above the sheet (41)
   and below the viewer (80); the photo's own cover crop comes with it. */
.pinchzoom{position:fixed;inset:0;z-index:60;pointer-events:none}
.pinchzoom .pzdim{position:absolute;inset:0;background:#000;opacity:0;transition:opacity var(--dur) var(--ease)}
.pinchzoom img{position:fixed;object-fit:cover;transform-origin:50% 50%;display:block}
.pinchzoom img.back{transition:transform var(--dur) var(--ease)}
/* The play badge sits on top of whatever the tile shows — a poster, or the
   bare sunken tile when no poster exists. It used to be a translucent BLACK
   disc, which on the dark theme's near-black sunken tile simply vanished
   (reported 2026-09-18 as "black thumbnail, no play button"). A light disc
   with a dark triangle and a soft drop shadow reads on a black tile, a white
   tile and any poster alike — the same badge every video platform uses. */
.playdot{position:absolute;top:50%;left:50%;width:44px;height:44px;transform:translate(-50%,-50%);border-radius:50%;background:rgba(255,255,255,.92);box-shadow:0 2px 10px rgba(0,0,0,.35);pointer-events:none;z-index:1}
.playdot::after{content:'';position:absolute;top:50%;left:55%;width:0;height:0;transform:translate(-50%,-50%);border-style:solid;border-width:8px 0 8px 13px;border-color:transparent transparent transparent #14181d}
.media .more{position:absolute;inset:0;background:rgba(0,0,0,.45);color:#fff;display:flex;align-items:center;justify-content:center;font-size:var(--fs-2xl);font-weight:600}
.drawer{border-top:1px solid var(--hairline-soft);padding:var(--s3) var(--s4)}
.reply{display:flex;gap:var(--s2);align-items:flex-start;margin-bottom:var(--s3)}
/* The replies list: about five text rows, then its own scrollbar - visible
   on purpose, so a long thread announces itself. Newest render first. */
/* overscroll-behavior is deliberately `auto`, NOT `contain`. With `contain`,
   reaching the end of a long thread swallowed the remaining scroll delta
   instead of passing it to the page, and because html/body also suppress the
   overscroll bounce there was no feedback either - so it read as the whole
   page freezing rather than an inner box ending (reported 2026-08-24). */
.rlist{max-height:330px;overflow-y:auto;overscroll-behavior:auto;padding-right:4px;scrollbar-width:thin}
.rlist::-webkit-scrollbar{width:4px}
.rlist::-webkit-scrollbar-thumb{background:var(--ink-faint);border-radius:2px}
/* The one-box composer: an input-looking field that only the pickers can
   fill. The three icons live inside the field, WhatsApp-style. */
/* Composer, rearranged 2026-09-12: hint line, then a centred row of three
   large pickers, then the box + send. The pickers were 34px and tucked inside
   the field beside the text; at 52px in their own row they are the first
   thing the thumb finds, which is the point - the box is built FROM them. */
.cwrap{display:flex;flex-direction:column;gap:10px;margin-top:8px}
/* Inline tray composer (2026-09-21, option C of the comments mockups): the
   box on top, a Lines / Emoji / Stickers segment under it, the choices of the
   active segment drawn inline. The .ctools icon row below is what it
   replaced; its rules stay because the CSS is versioned with the JS. */
.cseg{display:flex;gap:4px;padding:4px;background:var(--surface-sunken);border-radius:12px}
.cseg .csegb{flex:1 1 auto;min-width:0;white-space:nowrap;padding:0 8px;display:flex;align-items:center;justify-content:center;gap:5px;height:36px;border:0;border-radius:9px;background:transparent;color:var(--ink-soft);font-family:inherit;font-size:var(--fs-sm);font-weight:600;cursor:pointer}
.cseg .csegb .i{width:15px;height:15px;flex:0 0 auto}
.cseg .csegb[aria-selected="true"]{background:var(--surface-raised);color:var(--ink);font-weight:700;box-shadow:var(--shadow-sm)}
.ctray{min-height:52px}
.clines{display:flex;flex-wrap:wrap;gap:8px}
.clines .chip{font-family:var(--font-sans);font-weight:500;font-size:var(--fs-md);padding:8px 14px;background:transparent;color:var(--ink);white-space:normal;text-align:left;border:1px solid var(--hairline-strong)}
.clines .chip[aria-pressed="true"]{background:color-mix(in srgb,var(--accent) 14%,transparent);border:2px solid var(--accent);color:var(--ink);padding:7px 13px}
.ctray .stkgrid .stkbtn[aria-pressed="true"]{outline:2px solid var(--accent);outline-offset:2px;border-radius:12px}
.cwrap.tray .chint{color:var(--ink-muted)}

.chint{font-size:var(--fs-sm);color:var(--ink-faint);text-align:center}
.ctools{display:flex;justify-content:center;gap:18px}
.ctools .cico{width:52px;height:52px;font-size:26px;background:var(--surface-sunken);color:var(--ink);border:1px solid var(--card-edge)}
.ctools .cico .i{width:26px;height:26px}
.cbox{display:flex;gap:8px;align-items:flex-end;margin-top:0}
.cfieldwrap{flex:1;display:flex;align-items:center;gap:2px;min-height:44px;
  background:var(--surface-sunken);border-radius:22px;padding:5px 6px 5px 14px}
.cfield{flex:1;min-width:0;display:flex;align-items:center;gap:6px;flex-wrap:wrap;padding:4px 0}
.cbox-ph{color:var(--ink-faint);font-size:14px}
.cbox-t{font-size:14.5px;line-height:1.35}
.cbox-e{font-size:21px;line-height:1.2;overflow-wrap:anywhere}
img.cbox-stk{width:34px;height:34px;flex:0 0 auto}
.cbox-x{width:28px;height:28px;flex:0 0 auto;color:var(--ink-faint)}
.cico{width:34px;height:34px;flex:0 0 auto;border:none;background:none;border-radius:50%;
  display:flex;align-items:center;justify-content:center;font-size:19px;color:var(--ink-soft);cursor:pointer}
.cico:active{transform:scale(.88)}
/* The send pill lived scoped under .replybox, which the composer never uses,
   so Post rendered as a naked chevron (screenshot, 2026-08-22). */
.cbox .send{width:44px;height:44px;border-radius:50%;background:var(--accent);color:var(--accent-ink);
  display:flex;align-items:center;justify-content:center;flex:0 0 auto;border:none}
.cbox .send:disabled{opacity:.4}
/* Tapping the count inside the post sheet lands on the reply box and rings
   it once (asked 2026-09-19), so the eye finds where it was sent. */
.cwrap.pulse .cfieldwrap{animation:cpulse .9s var(--ease)}
@keyframes cpulse{0%,100%{box-shadow:0 0 0 0 transparent}40%{box-shadow:0 0 0 3px var(--accent)}}
/* "Post in" collapsed (asked 2026-09-19): one row names the current
   network; the chevron turns and the other networks' switches drop down. */
.netpick .sum .chev{transition:transform var(--dur) var(--ease)}
.netpick.open .sum .chev{transform:rotate(180deg)}
/* New Celebration, option B (2026-09-19): Title and details in ONE bordered
   box, a hairline between them; the box takes the focus ring as a whole so
   the two halves read as one field. */
.cmerge{border:1px solid var(--hairline-strong);border-radius:var(--r-md);background:var(--surface);overflow:hidden;transition:border-color var(--dur-fast),box-shadow var(--dur-fast)}
.cmerge:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-wash)}
.cmerge .input,.cmerge .textarea{border:0;border-radius:0;background:transparent;box-shadow:none}
.cmerge .input{font-weight:600;border-bottom:1px solid var(--hairline)}
.cmerge .textarea{min-height:88px;resize:none}
.sheet .sh .discard{min-width:44px;padding:0 var(--s2);font-weight:600;color:var(--ink-soft)}
/* Card foot, option B (2026-09-19): the count lives in the pill, the faces
   of who reacted sit beside it and open the list. Hidden at zero. */
.foot .rfaces{align-items:center;padding:2px}
.foot .rfaces[hidden]{display:none}
.foot .rfaces .b{font-weight:700;color:var(--pink)}
/* picker sheets */
/* Compact pass (asked 2026-08-24): all three pickers were tall enough to fill
   the sheet. The emoji names came off - twenty labelled cells at 4 across ran
   five rows and ~400px, and the label was never what anyone read; the name
   survives as the button's aria-label, so screen readers lose nothing. Six
   across keeps all twenty in four rows at ~215px, and the cell is still ~54px
   wide on a 360px phone, so nothing got fiddly to tap. */
.emgrid{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:6px}
.emcell{display:flex;align-items:center;justify-content:center;padding:8px 2px;
  border:1px solid transparent;border-radius:var(--r-md);background:none}
.emcell .e{font-size:28px;line-height:1.15}
.emcell:active{transform:scale(.9)}
/* Four across at 68px: ten stickers in three rows (~220px) instead of four
   rows at 88px (~382px). 68px is still well clear of a comfortable tap. */
.stkgrid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;justify-items:center}
.stkgrid .stkbtn{width:68px;height:68px;flex:none}
.stkgrid img.stk-pick{width:68px;height:68px}
/* .remoji: an emoji standing where an icon would (who-reacted rows). */
.remoji{font-size:17px;line-height:1}
.emreply{font-size:34px;line-height:1.15;padding:2px 0}
/* Sticker replies (2026-08-22): a horizontal picker above the preset chips,
   and a posted sticker rendered plain - no bubble, the die-cut art carries
   its own white edge and shadow. */
.stkbtn{flex:0 0 56px;width:56px;height:56px;border:none;background:none;padding:0;cursor:pointer}
.stkbtn:active{transform:scale(.88)}
img.stk-pick{width:56px;height:56px;display:block}
img.stk-reply{width:88px;height:88px;display:block;margin:-4px 0}
/* Readability pass (asked 2026-08-22): body-size text for both, and the wish
   bubble drops Pacifico - the script face was charming and unreadable in
   equal measure. The accent border and tilt keep a wish looking like one. */
.reply .bub{background:var(--surface-sunken);border-radius:var(--r-md);padding:8px 12px;font-size:var(--fs-base);line-height:1.45;max-width:100%}
.reply .bub.wish{font-size:var(--fs-base);background:var(--surface-sunken);color:var(--ink);border-left:3px solid var(--accent);transform:rotate(-.4deg)}
.reply .who{font-size:var(--fs-sm);color:var(--ink-muted);margin:2px 0 0 4px}
.replybox{display:flex;gap:var(--s2);align-items:flex-end;margin-top:var(--s2)}
.replybox .textarea{min-height:44px;max-height:120px;padding:10px 14px;border-radius:22px}
.replybox .send{width:44px;height:44px;border-radius:50%;background:var(--accent);color:var(--accent-ink);display:flex;align-items:center;justify-content:center;flex:0 0 auto}
.replybox .send:disabled{opacity:.4}
.presets{display:flex;gap:6px;overflow-x:auto;overscroll-behavior-x:contain;scrollbar-width:none;padding-bottom:6px;margin-bottom:6px}
.presets::-webkit-scrollbar{display:none}
/* The wish chips were the last place Pacifico survived: 12.5px of script face
   was the least readable text in the app, and it did not even match what got
   posted - the wish bubble dropped Pacifico back on 2026-08-22 (see .bub.wish
   above). Sans at 14px, and the line is allowed to wrap instead of running off
   as one nowrap chip, so a long wish reads as two tidy lines. */
.presets .chip{font-family:var(--font-sans);font-weight:400;font-size:var(--fs-md);
  line-height:1.35;padding:6px 11px;white-space:normal;max-width:100%;text-align:left}
.today{display:flex;flex-direction:column;gap:var(--s2)}
.today .tc{display:flex;align-items:center;gap:var(--s3);width:100%;text-align:left;padding:10px var(--s3);border-radius:var(--r-md);background:var(--surface-sunken)}
.today .tc .n{font-size:var(--fs-md);font-weight:600}
.today .tc .k{font-size:var(--fs-xs);color:var(--gold-text);font-weight:600}
.today .tc .go{margin-left:auto;font-size:var(--fs-sm);font-weight:600;color:var(--accent-text);white-space:nowrap}
/* identity line: department · company · city, so a half-remembered name resolves */
.today .tc .w{font-size:var(--fs-xs);color:var(--ink-soft);margin-top:1px}
/* "Live in Circle" — the accent hairline marks it as coming from another
   tab, so it does not read as one more celebration card. */
.circle-live{border-left:3px solid var(--accent)}
.circle-live-hd{font-size:var(--fs-xs);font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--accent-text);font-family:var(--font-mono)}
.min0{min-width:0}
/* overflow row: a few faces, overlapped, so the scale reads at a glance */
.today .stack{display:flex;align-items:center;flex:0 0 auto}
.today .stack .av{margin-left:-10px;border:2px solid var(--surface)}
.today .stack .av:first-child{margin-left:0}
.grouphead{font-size:var(--fs-xs);font-weight:600;color:var(--ink-soft);text-transform:uppercase;letter-spacing:.04em;margin:var(--s4) 0 var(--s2)}
.grouphead:first-child{margin-top:var(--s1)}
.viewer{position:fixed;inset:0;z-index:80;background:#000;display:flex;align-items:center;justify-content:center;touch-action:none;overscroll-behavior:contain;user-select:none;-webkit-user-select:none}
/* Zoom (2026-09-24): the <img> carries the transform; it eases on double-tap
   and release and follows the fingers with no transition in between. */
.viewer .stage img{transition:transform .22s var(--ease);-webkit-user-drag:none}
.viewer.zoomed{cursor:move}
.viewer .stage{max-width:100%;max-height:100%;display:flex}
.viewer .stage img,.viewer .stage video{max-width:100%;max-height:100%;object-fit:contain}
.viewer .x{position:absolute;top:calc(var(--safe-t) + 8px);right:8px;color:#fff}
.viewer .n{position:absolute;bottom:calc(var(--safe-b) + 14px);left:0;right:0;text-align:center;color:#fff;font:600 12px var(--font-mono);opacity:.8}
.compose-thumbs{display:flex;gap:var(--s2);overflow-x:auto;overscroll-behavior-x:contain;padding:4px var(--s4);margin:0 calc(-1 * var(--s4));scrollbar-width:none;scroll-padding:0 var(--s4)}
.compose-thumbs::-webkit-scrollbar{display:none}
.sheet .sf .btn.ghost{flex:0 0 auto}
.compose-thumbs .th{position:relative;flex:0 0 auto;width:84px;height:84px;border-radius:14px;overflow:hidden;background:var(--surface-sunken)}
.compose-thumbs .th img,.compose-thumbs .th video{width:100%;height:100%;object-fit:cover}
.compose-thumbs .th button{position:absolute;top:4px;right:4px;width:22px;height:22px;border-radius:50%;background:rgba(0,0,0,.6);color:#fff;display:flex;align-items:center;justify-content:center}
/* Filmstrip, refined (2026-09-19): the first slot is what the feed grid and
   every notification actually leads with, and a video used to look no
   different from a photo until it half-loaded — both badges answer that
   before anything even has to decode. */
.compose-thumbs .th .covertag{position:absolute;top:4px;left:4px;background:var(--accent);color:var(--accent-ink);font:600 9px/1 var(--font-sans);letter-spacing:.03em;text-transform:uppercase;padding:3px 6px;border-radius:var(--r-pill)}
.compose-thumbs .th .durtag{position:absolute;bottom:4px;left:4px;background:rgba(0,0,0,.6);color:#fff;font:600 9px/1 var(--font-mono,var(--font-sans));padding:3px 5px;border-radius:5px}
.compose-thumbs .add{width:84px;height:84px;border-radius:14px;border:1.5px dashed color-mix(in srgb,var(--accent) 45%,transparent);background:color-mix(in srgb,var(--accent) 6%,transparent);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;color:var(--accent-text);flex:0 0 auto}
.compose-thumbs .add .addlbl{font:600 10px/1.2 var(--font-sans);text-align:center}
/* Use the full row (asked 2026-09-23, "use full space"): the Add tile grows into what the thumbnails leave,
   and with nothing picked yet it is one wide drop zone with its label on a single line. */
.compose-thumbs .add{flex:1 0 84px;width:auto}
.compose-thumbs .add:only-child{height:96px;flex-direction:row;gap:8px}
.compose-thumbs .add:only-child .addlbl{font-size:13px}
.compose-thumbs .add:only-child .addlbl br{display:none}
/* The paste hint shows only where a clipboard paste is a habit (2026-09-19). */
.paste-hint{display:none;margin-top:6px}
@media (hover:hover){.paste-hint{display:block}}
/* Per-photo upload state. The overlay is pointer-events:none so the remove
   button on top of it still takes the tap while a photo is going up. */
.compose-thumbs .th img,.compose-thumbs .th video{transition:opacity var(--dur) var(--ease)}
.compose-thumbs .th:has(.up) img,.compose-thumbs .th.bad img,.compose-thumbs .th:has(.up) video,.compose-thumbs .th.bad video{opacity:.45}
.compose-thumbs .th .up{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--ink) 28%,transparent);pointer-events:none}
/* A ring reads at a glance without parsing a number — the number stays too
   (centred over it) for anyone who wants the precision. -90deg start so
   progress begins at 12 o'clock, same convention as every other progress
   ring in the app. */
.compose-thumbs .th .ringsvg{position:absolute;inset:0;width:100%;height:100%;transform:rotate(-90deg)}
.compose-thumbs .th .ringsvg circle{fill:none;stroke-width:4}
.compose-thumbs .th .ringsvg circle.track{stroke:rgba(255,255,255,.3)}
.compose-thumbs .th .ringsvg circle:not(.track){stroke:var(--gold);stroke-linecap:round;transition:stroke-dashoffset .18s linear}
.compose-thumbs .th .up .n{position:relative;font:600 var(--fs-xs)/1 var(--font-mono,var(--font-sans));color:#fff;text-shadow:0 1px 2px rgba(0,0,0,.45)}
.compose-thumbs .th.bad{outline:2px solid var(--error);outline-offset:-2px}
.compose-thumbs .th .retry{position:absolute;right:auto;left:50%;top:50%;transform:translate(-50%,-50%);width:30px;height:30px;background:var(--error);color:#fff}
.btn.is-waiting{opacity:.6}
/* Shell upload bar — under the header, only while photos are in flight. */
.upbar{position:fixed;left:0;right:0;top:calc(var(--header-h) + var(--safe-t));height:3px;z-index:21;background:transparent;opacity:0;transition:opacity var(--dur) var(--ease);pointer-events:none}
.upbar.on{opacity:1;background:var(--accent-wash)}
.upbar i{display:block;height:100%;width:0;background:var(--accent);transition:width .18s linear}
.notif{display:flex;gap:var(--s3);align-items:flex-start;padding:12px var(--s4);border-top:1px solid var(--hairline-soft);text-align:left;width:100%}
.notif:first-child{border-top:0}
.notif.unread{background:color-mix(in srgb,var(--accent-wash) 40%,var(--surface))}
.notif .lead{width:36px;height:36px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:var(--surface-sunken);color:var(--ink-soft);flex:0 0 auto}
.notif .t{font-size:var(--fs-md)}
.notif .s{font-size:var(--fs-xs);color:var(--ink-faint);margin-top:2px}

/* ---- circle ---- */
/* Channel-list layouts behind config circle_layout: board tiles, asking rows,
   feed chips + cards. 'list' reuses the shared .list/.li rows above. */
.cboard{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
.ctile{background:var(--surface);border:1px solid var(--card-edge);border-radius:var(--r-lg);padding:14px 12px 12px;box-shadow:var(--shadow-sm);display:flex;flex-direction:column;align-items:flex-start;gap:8px;min-height:118px;position:relative;text-align:left;color:inherit}
.ctile:active{background:var(--surface-sunken)}
.ctile .lead{width:40px;height:40px;border-radius:12px;display:flex;align-items:center;justify-content:center;flex:0 0 auto}
.ctile .t{font-weight:600}
.ctile .s{font-size:var(--fs-sm);color:var(--ink-soft);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.ctile .when{font-size:var(--fs-xs);color:var(--ink-faint);margin-top:auto}
.ctile.dashed{border-style:dashed;background:color-mix(in srgb,var(--surface) 60%,transparent);box-shadow:none}
.ctile-bdg{position:absolute;top:10px;right:10px}
.cask{display:flex;align-items:center;gap:var(--s3);background:var(--surface);border:1px solid var(--card-edge);border-radius:var(--r-lg);padding:12px;box-shadow:var(--shadow-sm);text-align:left;width:100%;color:inherit}
.cask:active{background:var(--surface-sunken)}
.cask + .cask{margin-top:var(--s2)}
.cask .lead{width:40px;height:40px;border-radius:12px;display:flex;align-items:center;justify-content:center;flex:0 0 auto}
.cask .btn.sm{border-radius:var(--r-pill)}
/* margin-top, not more padding (2026-09-12): the 2px here is clearance so a
   chip's border and focus ring are not clipped by the horizontal scroller, and
   growing it would just move the chips inside their own scroll box. The row
   sat flush against the Members card above it — measured 0px between the two
   element boxes, with only that 2px showing. --s3 is the same rhythm
   `.card + .card` uses everywhere else. */
/* Profile completeness bar (2026-09-12). A bar, not a ring: it sits beside the
   avatar in a row, where a ring would compete with the avatar for the same
   circular read. */
/* The rule beside a fun fact. Three prose cards stacked read as one block of
   text with headings floating over it; this gives the middle one an edge. */
/* ---- profile: the D3 + C3 hybrid (2026-09-12) ----
   Cover first, a flat card riding up over its bottom edge, three stats inside
   the card; then interests as tab-chips and the three prose fields on the
   Wall of Praise's own paper (--note-N + --font-hand). One header builder,
   profileHeader() in profile.js, draws it for the member sheet AND the You
   tab so one person is never rendered two ways. */
/* ---- You tab, two segments (2026-09-16, mockup you-tab.html option A) ---- */
.you-seg{margin-bottom:var(--s3)}
.you-seg button{display:inline-flex;align-items:center;justify-content:center;gap:6px}
.you-seg-bdg,.you-tile-bdg{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;border-radius:99px;
  background:var(--accent);color:var(--accent-ink);font-size:10.5px;font-weight:800;line-height:1}
.you-pane{display:flex;flex-direction:column}
.you-pane > .sect{margin-top:var(--s4)}
.you-pane > .list + .sect{margin-top:var(--s4)}

/* The hero: cover band, then the avatar in its completion ring riding up over
   it, centred. Same cover gradient as the member sheet's .pcover, so a member
   with no cover photo gets the same fallback in both places. */
/* The profile card, with the cover photo across the whole of it.

   Colours here are deliberately NOT theme tokens, and that is the exception to
   the tokens rule rather than a lapse: this card is a photo under a dark scrim
   in BOTH themes, so its text is light in both themes. A light-theme ink would
   put dark grey on a darkened photo. The celebration card (.hero) does the same
   for the same reason.

   Roughly square, because that is the shape a full-bleed photo reads best at on
   a phone; it grows if the text needs more room rather than clipping it. */
.yh{position:relative;isolation:isolate;overflow:hidden;border-radius:var(--r-lg);border:1px solid var(--card-edge);box-shadow:var(--shadow-sm);
  display:flex;flex-direction:column;justify-content:flex-end;aspect-ratio:1 / 1.06;background:#12161A;color:#fff}
.yh-cover{position:absolute;inset:0;z-index:-2;background:linear-gradient(135deg,var(--violet) 0%,var(--accent) 60%,var(--gold) 120%)}
.yh-cover img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center}
/* A faint band at the very top keeps the Edit pill legible on a white sky; clear
   through the upper-middle so the subject shows; then near-black under the text. */
.yh-scrim{position:absolute;inset:0;z-index:-1;pointer-events:none;background:linear-gradient(180deg,
  rgba(8,10,14,.28) 0%,rgba(8,10,14,0) 18%,rgba(8,10,14,0) 34%,rgba(8,10,14,.45) 52%,rgba(8,10,14,.80) 70%,rgba(8,10,14,.93) 100%)}
/* .56, not lighter: measured on a near-white cover, .46 gave the Edit label 4.98:1 —
   a hair over the 4.5:1 bar, with nothing to spare for a whiter photo. */
.yh-edit{position:absolute;top:10px;right:10px;z-index:1;display:inline-flex;align-items:center;gap:5px;height:30px;padding:0 12px;border-radius:99px;
  background:rgba(10,12,16,.56);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);border:1px solid rgba(255,255,255,.28);color:#fff;font-size:var(--fs-sm);font-weight:700}
.yh-edit .i{width:14px;height:14px}
.yh-in{position:relative;display:flex;flex-direction:column;align-items:flex-start;text-align:left;gap:2px;padding:40px var(--s4) var(--s4)}
.yh-top{display:flex;align-items:center;gap:12px;width:100%}
.yh-id{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
/* 104px ring around a 92px avatar. Its empty track is translucent white so it
   reads as a ring over dark and light photos alike. */
.yh-ring{position:relative;width:78px;height:78px;border-radius:50%;padding:0;border:0;flex:0 0 auto;
  background:conic-gradient(var(--accent) var(--p,0%),rgba(255,255,255,.30) 0);display:flex;align-items:center;justify-content:center;box-shadow:0 6px 20px rgba(0,0,0,.35)}
.yh-ring .av.xl{width:68px;height:68px;font-size:var(--fs-xl);box-shadow:0 0 0 3px rgba(12,14,18,.85)}
.yh-pct{position:absolute;bottom:-7px;left:50%;transform:translateX(-50%);font-family:var(--font-mono);font-size:12px;font-weight:600;line-height:1;
  padding:3px 7px;border-radius:99px;background:var(--accent);color:var(--accent-ink);box-shadow:0 0 0 2px rgba(12,14,18,.85);white-space:nowrap}
.yh-name{display:flex;align-items:center;gap:7px;max-width:100%;font-size:var(--fs-lg);font-weight:800;letter-spacing:-.01em;line-height:1.2;
  color:#fff;text-shadow:0 1px 12px rgba(0,0,0,.45)}
.yh-role{flex:0 0 auto;font-size:12px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;padding:2px 8px;border-radius:99px;background:rgba(255,255,255,.20);color:#fff;text-shadow:none}
.yh-role.lead{background:var(--accent);color:#fff}
.yh-pos{font-size:var(--fs-md);color:rgba(255,255,255,.93);text-shadow:0 1px 8px rgba(0,0,0,.4)}
.yh-sub{font-size:var(--fs-xs);color:rgba(255,255,255,.74)}
.yh-bio{margin-top:10px;width:100%}
/* Three lines at most: an uncapped 600-character bio would push the card to twice
   its height and turn a photo card back into a text card. The whole bio is still
   in Edit profile and on the member sheet. */
.yh-bio p{margin:0;max-width:none !important;font-style:italic;color:rgba(255,255,255,.84) !important;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.yh-bio a{color:#FFA3CE}
.yh-next{display:inline-flex;align-items:center;gap:5px;margin-top:8px;padding:0;border:0;background:none;color:#FF9FCB;font-size:var(--fs-sm);font-weight:700}
.yh-next .i{width:14px;height:14px}

/* Stats: three tiles, the most personal numbers on the tab at a size that says so. */
.ys{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s2);margin-top:var(--s3)}
.ys-t{background:var(--surface);border:1px solid var(--card-edge);border-radius:var(--r-md);padding:10px 6px;text-align:center;box-shadow:var(--shadow-sm)}
.ys-t b{display:block;font-family:var(--font-mono);font-size:22px;font-weight:600;line-height:1.1;letter-spacing:-.02em;color:var(--ink)}
.ys-t b.gold{color:var(--gold-text)}
.ys-t b.accent{color:var(--accent-text)}
.ys-t span{font-size:var(--fs-xs);font-weight:600;color:var(--ink-faint)}

/* Facts: what teammates see about you, as chips. */
.yf{display:flex;flex-wrap:wrap;gap:6px;margin-top:var(--s3)}
.yf-c{display:inline-flex;align-items:center;gap:6px;font-size:var(--fs-sm);font-weight:600;color:var(--ink-soft);
  background:var(--surface);border:1px solid var(--card-edge);border-radius:99px;padding:4px 11px 4px 9px}
.yf-c .i{width:14px;height:14px;color:var(--ink-faint)}

/* Option J (2026-09-19): the cover carries on. .yj-band tucks under the
   hero's bottom corners and runs down into the page, fading to nothing;
   the stats and fact chips sit on it as glass. Redrawn the same day as
   option B of docs/mockups/profile-band-dark-2026-09-19.html: the band was
   the brand gradient (pink into violet) and in the dark theme it was the
   only bright pink surface on the tab. Now it is the page's sunken surface
   (near-black in dark, light grey in light) with ONE soft pink glow at the
   seam with the hero — the memory of the old gradient at a tenth of the
   strength — and the numbers carry the brand pink instead of the band.
   Everything is tokens, so the light theme gets the same shape in its own
   greys without a second rule. */
.yj{position:relative}
.yj > .yh{position:relative;z-index:1}
.yj-band{position:relative;z-index:0;margin:-18px var(--s1) 0;padding:30px var(--s3) var(--s3);border-radius:0 0 var(--r-lg) var(--r-lg);
  background:
    radial-gradient(70% 90% at 50% 0%,color-mix(in srgb,var(--accent) 30%,transparent),transparent 70%),
    linear-gradient(180deg,var(--surface-sunken) 0%,var(--surface-sunken) 60%,transparent 100%)}
.yj-band .ys{margin-top:0}
.yj-band .ys-t{background:color-mix(in srgb,var(--ink) 5%,transparent);border-color:color-mix(in srgb,var(--ink) 10%,transparent);box-shadow:none}
.yj-band .ys-t b,.yj-band .ys-t b.gold,.yj-band .ys-t b.accent{color:var(--accent-text)}
.yj-band .ys-t span{color:var(--ink-faint)}
.yj-band .yf{justify-content:center;margin-top:10px}
.yj-band .yf-c{background:color-mix(in srgb,var(--ink) 5%,transparent);border-color:color-mix(in srgb,var(--ink) 10%,transparent);color:var(--ink-soft)}
.yj-band .yf-c .i{color:var(--ink-faint)}
.yj-band .ys[hidden]{display:none}
.yj + .pledger{margin-top:var(--s2)}

/* Manage on the You tab (2026-09-25): each section is one .list card of the
   same .li rows as Your preferences (H2, "the same cards, joined per
   section"). Only the count needs a rule: it hides until something waits. */
.ym-list .you-tile-bdg[hidden]{display:none}
/* The admin invite well (2026-09-25): code, then Copy and a green WhatsApp. */
.you-invwell{display:flex;align-items:center;gap:10px}
.you-invbtn{display:flex;flex-direction:column;align-items:center;gap:2px;background:none;border:0;padding:0;color:inherit;flex:0 0 auto;min-width:44px}
.you-invbtn small{font-size:12px;font-weight:700;color:var(--ink-faint)}
.you-invbtn.wa .icoround{background:var(--whatsapp);border-color:var(--whatsapp);color:var(--on-whatsapp)}
/* Moderation: change the type before approving (2026-09-25). */
.modtype{padding:4px 12px 0}
.modtypes{flex-wrap:wrap;gap:6px}
.modtype-ev{margin-top:6px}
/* The rename pencil beside the network name: small, quiet, still a 32px target. */
.you-rename{width:32px;height:32px;min-height:32px;flex:0 0 auto;color:var(--ink-faint);background:transparent;border:0}
/* The plan line in the owner's network card: tier, a thin bar, the count. */
.you-plan{display:flex;align-items:center;gap:8px;width:100%;margin:0 0 6px;padding:6px 10px;border-radius:var(--r-sm);background:var(--accent-wash);color:inherit;text-align:left}
.you-plan-t{display:inline-flex;align-items:center;gap:4px;font-size:var(--fs-xs);font-weight:700;color:var(--accent-text);white-space:nowrap}
.you-plan-bar{flex:1;height:6px;border-radius:3px;background:color-mix(in srgb,var(--accent) 18%,transparent);overflow:hidden}
.you-plan-bar i{display:block;height:100%;background:var(--accent);border-radius:3px}
.you-plan .chev{color:var(--ink-faint)}
.you-netpend{margin-left:auto}

/* Foot: Log out as plain accent text, no card, directly above Terms. */
.you-foot{display:flex;flex-direction:column;align-items:center;gap:var(--s2);margin-top:var(--s6);padding-bottom:var(--s2)}
.you-logout{padding:8px 16px;border:0;background:none;color:var(--accent-text);font-size:var(--fs-md);font-weight:700;letter-spacing:.01em;border-radius:99px}
.you-logout:active{background:var(--accent-wash)}
.you-foot p{margin:0}

.pcover{height:220px;position:relative;overflow:hidden;border-radius:var(--r-lg);background:linear-gradient(135deg,var(--violet) 0%,var(--accent) 60%,var(--gold) 120%)}
.phead.bleed .pcover{margin:calc(-1*var(--s3)) calc(-1*var(--s4)) 0;border-radius:0;height:236px}
.pcover img{width:100%;height:100%;object-fit:cover;display:block}
.pcover .fade{position:absolute;inset:0;background:linear-gradient(180deg,color-mix(in srgb,var(--sand) 30%,transparent) 0%,transparent 35%,color-mix(in srgb,var(--sand) 60%,transparent) 100%)}
.pcard{position:relative;margin:-64px var(--s2) 0;background:var(--surface);border:1px solid var(--card-edge);border-radius:20px;box-shadow:var(--shadow-md);overflow:hidden}
.pcard .edge{position:absolute;left:0;top:0;bottom:0;width:6px;background:linear-gradient(180deg,var(--accent),var(--gold))}
.pcard .pin{padding:18px 18px 16px 24px}
.pcard .who{display:flex;gap:14px;align-items:center}
.pcard .pav{display:block;border-radius:20px;overflow:hidden;flex:0 0 auto;border:1px solid var(--hairline-strong)}
.pcard .pav .av{border-radius:0}
.pcard .pname{font-size:var(--fs-xl);font-weight:800;line-height:1.05;letter-spacing:-.01em}
.pcard .pbio{font-size:var(--fs-sm);line-height:1.45;margin-top:10px;color:var(--ink)}
.pstats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin-top:16px}
.pstat{background:var(--surface-sunken);border-radius:12px;padding:10px 8px;text-align:center}
.pstat b{display:block;font-size:18px;font-weight:800;line-height:1;font-variant-numeric:tabular-nums}
.pstat .k{font-size:12px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-faint);margin-top:4px}
/* The about-you ledger (option J, 2026-09-19), one card for both profile
   screens: the three notes as question-and-answer with only the question
   coloured, then Into / Work / Studied as label-value rows. Replaced the
   tab-chips, the paper notes (.pnote, 2026-09-14) and the work list. */
.pledger{margin-top:var(--s3)}
.pledger > *{border-top:1px solid var(--hairline-soft)}
.pledger > :first-child{border-top:0}
.pl-qa{padding:12px 16px}
.pl-q{font-size:var(--fs-xs);font-weight:700;letter-spacing:.06em;text-transform:uppercase;margin-bottom:3px}
.pl-q.c1{color:var(--gold-text)}.pl-q.c2{color:var(--teal)}.pl-q.c3{color:var(--pink)}
.pl-a{font-size:var(--fs-base);line-height:1.45;white-space:pre-line;word-break:break-word}
.pl-row{display:flex;gap:12px;padding:12px 16px}
.pl-k{flex:0 0 74px;font-size:var(--fs-xs);font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-faint);padding-top:4px}
.pl-v{flex:1;min-width:0;line-height:1.45;font-size:var(--fs-base);word-break:break-word}
.pl-v b{font-weight:700}
.pl-s{color:var(--ink-soft);font-size:var(--fs-sm)}
.pl-pills{display:flex;flex-wrap:wrap;gap:6px}
.pl-pills span{padding:4px 11px;border-radius:99px;background:var(--surface-sunken);font-size:var(--fs-sm);font-weight:600}
.prows{display:flex;flex-direction:column;gap:8px}
.prow{display:flex;gap:12px;align-items:center;background:var(--surface);border:1px solid var(--card-edge);border-radius:14px;padding:10px;width:100%;text-align:left;color:inherit}
.prow .th{width:52px;height:52px;border-radius:10px;flex:0 0 auto;overflow:hidden;display:flex;align-items:center;justify-content:center;font-size:24px;background:linear-gradient(140deg,var(--accent-wash),var(--surface-sunken))}
.prow .th img{width:100%;height:100%;object-fit:cover;display:block}
.prow .t{font-weight:600;font-size:var(--fs-md);line-height:1.3}
.prow .s{font-size:var(--fs-xs);color:var(--ink-faint);margin-top:3px}
.quoterule{width:3px;align-self:stretch;border-radius:var(--r-pill);background:var(--gold);flex:0 0 auto}
.pbar{height:6px;border-radius:var(--r-pill);background:var(--surface-sunken);margin:7px 0 5px;overflow:hidden}
.pbar > i{display:block;height:100%;background:var(--accent);border-radius:var(--r-pill);transition:width var(--dur) var(--ease)}
/* Interest chips wrap; .cchips above is a horizontal SCROLLER for the Explore
   filters and would hide the overflow rather than wrap it. */
.ichips{display:flex;flex-wrap:wrap;gap:6px;margin-top:2px}
.ichips + .ichips{margin-top:8px}
.ichips .cchip.on{background:var(--accent-wash);border-color:var(--accent);color:var(--accent-text)}
.ichips .cchip[disabled]{opacity:.42}
/* The field a "Add <thing>" button sent you to. */
.field.focusme > .row > label{color:var(--accent-text)}
.cchips{display:flex;gap:6px;overflow-x:auto;overscroll-behavior-x:contain;padding:2px 2px var(--s3);margin-top:var(--s3)}
.cchip{flex:0 0 auto;height:32px;padding:0 14px;border-radius:var(--r-pill);background:var(--surface);border:1px solid var(--card-edge);font-size:var(--fs-md);font-weight:500;color:var(--ink);display:inline-flex;align-items:center}
.cchip.on{background:var(--ink);color:var(--surface);border-color:var(--ink);font-weight:600}
/* circle 'shop' hub (2026-09-02): marketplace strip + classified row thumbs */
.cstrip{display:flex;gap:8px;overflow-x:auto;overscroll-behavior-x:contain;padding:2px 2px var(--s3);scrollbar-width:none}
.cstrip::-webkit-scrollbar{display:none}
.cshopcard{flex:0 0 116px;background:var(--surface);border:1px solid var(--card-edge);border-radius:var(--r-md);overflow:hidden;text-align:left;color:inherit;box-shadow:var(--shadow-sm)}
.cshopcard:active{background:var(--surface-sunken)}
.cshopcard .cshop-ph{display:block;height:76px;background:var(--surface-sunken)}
.cshopcard .cshop-ph img{width:100%;height:100%;object-fit:cover;display:block}
.cshopcard .nm{font-size:var(--fs-xs);font-weight:600;padding:6px 8px 0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cshopcard .pr{font-size:var(--fs-sm);font-weight:700;padding:1px 8px 7px}
.crow-ph{width:46px;height:46px;border-radius:var(--r-sm);overflow:hidden;flex:0 0 auto;background:var(--surface-sunken)}
.crow-ph img{width:100%;height:100%;object-fit:cover;display:block}
.crow-pr{font-weight:700;color:var(--ink)}
.crow[hidden]{display:none}
.cquiet{opacity:.55}
.cpost{display:flex;flex-direction:column;align-items:stretch;gap:6px;background:var(--surface);border:1px solid var(--card-edge);border-radius:var(--r-lg);padding:12px;box-shadow:var(--shadow-sm);text-align:left;width:100%;color:inherit}
.cpost:active{background:var(--surface-sunken)}
.cpost + .cpost{margin-top:var(--s2)}
.cpost .t{font-weight:500}
.cpost .s{font-size:var(--fs-sm);color:var(--ink-soft)}
.cpost.urgent{background:var(--error-wash);border-color:color-mix(in srgb,var(--error) 25%,transparent)}
/* Author face + name on the stream rows and the ticket detail line (2026-09-19). */
.cpost .cmeta{display:flex;align-items:center;gap:6px;min-width:0}
.cpost .cmeta > .ell{min-width:0}

/* The Explore feed row, one grammar for every channel (2026-09-22, "E1").
   Four slots left to right: thumb, main (title + detail), value. The row is
   a single grid so the thumb column never goes ragged between a post with a
   photo and one without — the icon tile is the same 56px box.

   Density: 12px padding and a 56px thumb puts eight rows on a 390x844 screen
   where the old card put five. The card look is kept (surface, radius,
   hairline) because Explore is still browsed, not scanned — the dense list
   was the other option on the canvas and was not chosen. */
.crow2{display:flex;align-items:center;gap:12px;width:100%;text-align:left;color:inherit;background:var(--surface);border:1px solid var(--card-edge);border-radius:var(--r-lg);padding:10px;box-shadow:var(--shadow-sm);min-height:76px}
.crow2 + .crow2{margin-top:var(--s2)}
.crow2:active{background:var(--surface-sunken)}
/* Only an ACTIVE blood request gets an edge. One red thing per screen. */
.crow2.urgent{border-color:color-mix(in srgb,var(--error) 45%,transparent);box-shadow:inset 3px 0 0 var(--error),var(--shadow-sm)}
.crow2-th{width:56px;height:56px;border-radius:var(--r-sm);overflow:hidden;flex:0 0 auto;background:var(--surface-sunken);display:flex;align-items:center;justify-content:center}
.crow2-th img{width:100%;height:100%;object-fit:cover;display:block}
.crow2-th.ic .i{width:26px;height:26px}
.crow2-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
/* Two lines, never one: a title is the whole reason the row exists, and the
   old row clipped it to one line at 30 characters. */
.crow2-t{font-weight:600;font-size:var(--fs-lg);line-height:1.3;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.crow2-s{display:flex;align-items:baseline;min-width:0;font-size:var(--fs-sm);color:var(--ink-soft)}
.crow2-s > b{flex:0 0 auto;font-weight:600}
.crow2-s > .ell{min-width:0}
.crow2-v{flex:0 0 auto;display:flex;align-items:center;justify-content:flex-end;max-width:33%}
.crow2-pr{font-weight:700;font-size:var(--fs-lg);white-space:nowrap}
/* A price that is a word, not a number: it cannot compete with the title. */
.crow2-pw{font-size:var(--fs-sm);font-weight:600;color:var(--ink-soft);max-width:92px}
/* The count that rides a channel chip. */
.cchip-n{margin-left:6px;font-size:var(--fs-xs);font-weight:700;opacity:.62}
/* 320px: a 56px thumb, a 12px gap and a state pill left the detail line a
   few pixels short and it clipped mid-word. The thumb gives them back —
   the same step-down the steps tiles took at 360 in the native round. */
@media (max-width:359px){.crow2{gap:10px;padding:9px}.crow2-th{width:48px;height:48px}.crow2-th.ic .i{width:23px;height:23px}}
.cchip.on .cchip-n{opacity:.8}
.cauth{display:inline-flex;align-items:center;gap:6px;font-weight:600;color:var(--ink);flex:0 1 auto;min-width:0}
.cauth .av{width:22px;height:22px;font-size:9px;flex:0 0 auto}
.cauth + .ell::before{content:"· ";color:var(--ink-faint)}
.tkt-who{display:flex;align-items:center;gap:6px}
.tkt-who .av{width:20px;height:20px;font-size:8.5px;flex:0 0 auto}
.tkt-who .ell{min-width:0}

/* ---- Explore: Members card (M2, 2026-09-19) + the masonry members sheet
   (2026-09-10, restored 2026-09-19) ----
   The card is a plain button now — five fixed faces, nothing scrolls inside
   it, so no pointer-moved guard is needed the way the old strip needed one. */
.people-card{display:block;width:100%;text-align:left;color:inherit;cursor:pointer;margin-bottom:var(--s3)}
.people-card .row{align-items:center;gap:8px}
.people-card .chev-r{margin-left:auto;color:var(--ink-faint);display:inline-flex}
.people-card .mcount{background:var(--accent-wash);color:var(--accent-text);font-family:var(--font-mono)}
.mfaces{display:flex;justify-content:space-between;gap:4px;margin-top:12px}
.mf{display:flex;flex-direction:column;align-items:center;gap:6px;flex:1 1 0;min-width:0}
/* Bigger faces, full names (asked 2026-09-19 after the first look): 56px,
   the name on up to two centred lines rather than a truncated first name. */
.mf .av{width:56px;height:56px;font-size:var(--fs-md)}
.mf .nm{font-size:12.5px;font-weight:600;line-height:1.2;color:var(--ink);text-align:center;max-width:100%;overflow:hidden;overflow-wrap:anywhere;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}

/* The masonry wall. CSS columns, not grid: the whole point is tiles of
   different heights packing without gaps, and `break-inside:avoid` is what
   keeps a card whole across a column boundary. */
.mgrid{columns:2;column-gap:8px}
.mcard{break-inside:avoid;margin:0 0 8px;display:block;width:100%;padding:0;text-align:left;font:inherit;color:inherit;
  background:var(--surface);border:1px solid var(--card-edge);border-radius:var(--r-md);overflow:hidden;cursor:pointer;position:relative}
.mcard:active{transform:scale(.985)}
/* The tile reuses .av, so it inherits the hue fills, the initials and the
   img object-fit rule - one avatar system, not two. Only the geometry is
   overridden: a rectangle of a per-member aspect ratio instead of a circle. */
.mcard .mph{width:100%;height:auto;aspect-ratio:var(--ar, 1 / 1);border-radius:0;font-size:var(--fs-xl);display:block}
/* Name and meta sit ON the photo (2026-09-10). The gradient is deliberately
   heavy at the base: it has to hold white text over a BRIGHT photo, which is
   where a gentle overlay fails and where member photos frequently land. The
   text-shadow is the second line of defence for a blown-out highlight sitting
   exactly behind a letter. Ends fully transparent so it never reads as a bar. */
.mscrim{position:absolute;left:0;right:0;bottom:0;padding:22px 10px 9px;pointer-events:none;
  background:linear-gradient(to top, rgba(8,11,14,.86) 0%, rgba(8,11,14,.55) 45%, rgba(8,11,14,0) 100%)}
.mscrim .nm{color:#fff;font-weight:600;font-size:var(--fs-sm);line-height:1.25;text-shadow:0 1px 3px rgba(0,0,0,.55)}
.mscrim .dp{color:rgba(255,255,255,.78);font-size:11px;margin-top:1px;text-shadow:0 1px 3px rgba(0,0,0,.5)}
.members-sheet .sb{padding-bottom:var(--s4)}
/* The tile must not take the avatar ring: the 2026-09-17 removal found that
   a `:not(.mph)` on the ring rule itself out-ranked the stacked-avatar rule
   (qa.html A4), so the exception lives here, on the tile, at equal
   specificity and later in the file. */
.members-sheet .mgrid .mcard .av.mph{box-shadow:none}   /* (0,5,0) beats the ring's (0,4,0) */
.pick{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
.pick > div{border:1px solid var(--card-edge);border-radius:12px;padding:12px;display:flex;gap:10px;align-items:center;font-size:var(--fs-md);font-weight:500;background:var(--surface);cursor:pointer}
.pick > div:active{background:var(--surface-sunken)}
.strip{border:0}
.sheet.dark{background:var(--sand-deep)}

.hero-card{background:linear-gradient(180deg,color-mix(in srgb,var(--accent-wash) 35%,var(--surface)) 0%,var(--surface) 60%)}
/* Brand pink for a name sitting inside a longer label ("Manage <network>"),
   where the name is the part you are actually scanning for.

   This is --accent, the same pink as the primary button fill, chosen
   deliberately by Prakash on 2026-08-20 after seeing the measurements: as TEXT
   it is 4.96:1 on the dark surface (AA) but 3.27:1 on the light one, which
   clears the 3:1 bar for large text and UI objects but is under the 4.5:1 bar
   for small body text. It is a brand-recognition call, not an oversight, and
   it matches `.pill.accent`, which already paints --accent as text.
   --pink (7:1 both themes) stays the token for text that must meet AA. */
.netname{color:var(--accent-text);font-weight:600}
.brandlink{color:var(--ink-faint);text-decoration:none}

/* Profile cover: a full-bleed band at the top of the You card. The negative
   margins cancel .card's padding so it reaches the edges, .has-cover clips it
   to the card radius, and .on-cover lifts the avatar to straddle the seam. */
/* Taller band, and anchored to the TOP of the image: object-fit:cover centres
   by default, which trimmed the top of the photo — the part people frame
   their subject in. At a phone width this now shows very nearly the whole
   stored 16:10 crop, and only ever loses the bottom edge, which the avatar
   overlaps anyway. */
.you-cover{margin:calc(var(--s4) * -1) calc(var(--s4) * -1) 0;aspect-ratio:1.85;position:relative;background:var(--surface-sunken)}
/* aspect-ratio, not a fixed height: the card is 349px wide on a phone and up
   to 514px on a desktop, so a fixed 190px band showed 87% of the photo on a
   phone but only 59% on a desktop. Scaling the band keeps the same crop at
   every width. The img is absolutely positioned so its height does not
   depend on percentage-resolving against an aspect-ratio box. */
.you-cover img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center top;display:block}
.you-cover::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgba(0,0,0,0) 45%,rgba(0,0,0,.30) 100%)}
.card.hero-card.has-cover{overflow:hidden}
/* Edit, as a corner control (0.7.33). 44px tap target per the app's own floor.
   Two treatments because the backdrop differs: over a cover photo it needs its
   own scrim to survive an arbitrary image; on a plain card a dark scrim would
   look pasted on, so it borrows the standard sunken-surface chip. */
.card.hero-card{position:relative}
.you-edit{position:absolute;top:var(--s2);right:var(--s2);width:44px;height:44px;display:inline-flex;align-items:center;justify-content:center;border-radius:var(--r-pill);z-index:2;color:#fff;background:rgba(0,0,0,.42);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}
.you-edit:active{background:rgba(0,0,0,.60)}
.card.hero-card:not(.has-cover) .you-edit{color:var(--ink);background:var(--surface-sunken);backdrop-filter:none;-webkit-backdrop-filter:none}
.card.hero-card:not(.has-cover) .you-edit:active{background:var(--sand-deep)}
/* Compacted only when a cover is present, so the taller band does not push
   the card off-screen; a card without a cover keeps its original spacing. */
.you-hero.on-cover{position:relative;margin-top:-48px;gap:var(--s1);padding:0 0 var(--s1)}
.you-hero.on-cover h2{margin-top:var(--s1)}
.you-hero.on-cover .av{border:3px solid var(--surface);box-shadow:0 0 0 1px var(--surface), 0 0 0 1.5px currentColor}
.you-hero{display:flex;flex-direction:column;align-items:center;text-align:center;gap:var(--s2);padding:var(--s2) 0}
/* the network card (2026-09-02): header row + invite/members wells */
.you-netcard{margin-top:var(--s3);padding:var(--s3)}
.you-nethead{display:flex;align-items:center;gap:var(--s3);width:100%;text-align:left;padding:var(--s1)}
.you-nethead .t{font-weight:600}
.you-nethead .s{font-size:var(--fs-sm);color:var(--ink-soft);margin-top:1px}
.you-wells{display:flex;gap:var(--s2);margin-top:var(--s2)}
.you-well{flex:1;min-width:0;background:var(--surface-sunken);border-radius:var(--r-md);padding:10px 12px;text-align:left}
.you-well .wl{font-size:var(--fs-xs);letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint);font-weight:600;margin-bottom:6px}
.you-well .wcode{font:600 var(--fs-lg) var(--font-mono);letter-spacing:.14em;display:flex;align-items:center;gap:var(--s2)}
.you-well .wcode .i{color:var(--ink-faint)}
.you-well .avstack .av{border-color:var(--surface-sunken)}
/* Owner footnote. Padded to 4px so it lines up with .you-nethead's own padding
   rather than the wells' 12px inset, and empty when you ARE the owner — the
   container then renders nothing and the margin must not show. */
.you-netfoot:not(:empty){margin-top:var(--s2);padding:0 var(--s1)}

/* ---- Option D (2026-09-09): one well, and it carries its own actions -------
   The invite well stops being a button — it holds two of them — so the copy
   and share targets are the buttons, not the box. Both are 40px, which is
   under the 44px floor the rest of the app keeps: they sit inside a 62px-tall
   well with 10px of padding around them, so the comfortable strike area is the
   full well height and the 40px is the painted circle. */
.you-well.wide{display:flex;align-items:center;gap:10px;padding:10px 10px 10px 12px}
.you-well .wcode2{font:600 var(--fs-base) var(--font-mono);letter-spacing:.12em;color:var(--ink);margin-top:3px}
.icoround{width:40px;height:40px;border-radius:var(--r-pill);display:inline-flex;align-items:center;justify-content:center;
  border:1px solid var(--hairline-strong);color:var(--ink);flex:0 0 auto;transition:transform var(--dur-fast)}
.icoround:active{transform:scale(.94)}
.icoround.accent{background:var(--accent);border-color:transparent;color:var(--accent-ink)}
/* The three faces that moved up into the subtitle. Smaller than .av.s's 28px,
   which would set the header's height instead of the 13.5px text doing it —
   but 24px, not the 20px this started at: at 20px with a 2px border the inner
   circle is 16px and three-letter initials ran together, so "PM" read as "PN"
   on a real handset. 24px carries them and still costs the header only 3px. */
.you-nethead .avstack.tiny{margin-right:6px;vertical-align:middle}
/* -6px, not the stack's usual -8px: that overlap is tuned to 28px faces, and
   at 24px it covered the second letter of each pair of initials — "PM" read
   as "PN". Same proportion of the circle, one letter still legible. */
.you-nethead .avstack.tiny .av{width:24px;height:24px;font-size:10px;margin-left:-6px;border-width:2px}
.you-nethead .avstack.tiny .av:first-child{margin-left:0}
.you-nethead .s{display:flex;align-items:center;min-width:0}
.you-hero h2{font-size:var(--fs-xl);font-weight:600}

/* ---- Circle card, graphic-led (0.7.44) --------------------------------------
   Every channel used to render identically: a flat row of same-weight .pill and
   a button row, with colour the only difference between an emergency and a book
   giveaway. Now the channel's one important fact leads the card, in a gradient
   header or over the photo, and --tone (set per channel from CH[].tone) carries
   the identity so the chips below can all go quiet.
   The gradient is the .hero recipe at card-header scale: the tone run into a
   darkened mix of itself, not a new colour. */
.post.circ{--tone:var(--ink-soft);--tone-wash:var(--surface-sunken)}
.post.circ::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--tone);z-index:2}
.post.circ{position:relative}
.chdr{position:relative;padding:var(--s4);color:#fff;
  background:linear-gradient(118deg,var(--tone) 0%,color-mix(in srgb,var(--tone) 62%,#101418) 100%)}
.chdr h3{font-size:19px;font-weight:600;line-height:1.25;margin-top:10px;text-wrap:pretty}
.chdr .sub{font-size:var(--fs-xs);color:rgba(255,255,255,.78);margin-top:6px}
.chdr-row{display:flex;align-items:flex-end;gap:var(--s3)}
.chdr .lede{font-family:var(--font-mono);font-weight:600;letter-spacing:-.02em;line-height:.92;color:#fff}
.chdr .lbl{font-size:var(--fs-xs);color:rgba(255,255,255,.78);font-weight:600;
  text-transform:uppercase;letter-spacing:.07em}
.chdr .kick{display:flex;align-items:center;gap:7px;font-size:12px;font-weight:700;
  letter-spacing:.05em;text-transform:uppercase}
.chdr .kick .i{width:16px;height:16px}
/* photo-led: the fact sits on the image under a scrim */
.cshot{position:relative;display:block;width:100%}
.cshot img{width:100%;display:block;aspect-ratio:4/3;object-fit:cover;background:var(--surface-sunken)}
.cscrim{position:absolute;inset:auto 0 0 0;padding:14px var(--s4) var(--s3);color:#fff;
  background:linear-gradient(to top,rgba(10,14,18,.88) 0%,rgba(10,14,18,.55) 45%,transparent 100%)}
.cscrim h3{font-size:18px;font-weight:600;line-height:1.25;text-wrap:pretty}
.cscrim .lede{font-family:var(--font-mono);font-size:30px;font-weight:600;letter-spacing:-.02em;line-height:1}
.cscrim .sub{font-size:var(--fs-sm);opacity:.85;margin-top:3px}
.cflag{position:absolute;top:12px;left:12px;display:inline-flex;align-items:center;gap:5px;
  padding:4px 10px;border-radius:var(--r-pill);font-size:var(--fs-xs);font-weight:700;
  letter-spacing:.05em;text-transform:uppercase;color:#fff;
  background:linear-gradient(118deg,var(--tone) 0%,color-mix(in srgb,var(--tone) 60%,#101418) 100%)}
.cflag .i{width:12px;height:12px}
/* author demoted below the hero */
.post.circ .ph.by{padding:var(--s3) var(--s4) 10px}
.post.circ .ph.by .av{width:28px;height:28px}
.post.circ .ph.by .nm{font-size:13px}
/* meta: uniform and quiet, since --tone now carries the channel */
.post.circ .pill{background:var(--surface-sunken);color:var(--ink-faint)}
/* the disclaimer, demoted from a bordered panel to one line on the card */
.post.circ .chnote{border:0;background:none;padding:0 var(--s4) var(--s3);
  font-size:var(--fs-xs);line-height:1.45;color:var(--ink-muted)}
.post.circ .chnote .btn.link{color:var(--ink-faint);text-decoration:underline;min-height:0;padding:0}
.cseats{height:5px;border-radius:var(--r-pill);background:var(--surface-sunken);overflow:hidden;margin-top:6px}
.cseats i{display:block;height:100%;background:var(--tone);border-radius:var(--r-pill)}
.cseats-row{display:flex;justify-content:space-between;font-size:var(--fs-xs);
  font-weight:600;color:var(--ink-faint)}
/* A gold band is a LIGHT fill in light theme too, so white on it lands near
   1.6:1. .ink asks for the same inversion the dark rule below makes for every
   tone: --accent-ink on a gradient that mixes toward white, not toward black.
   Used by announcements in the alerts channel (0.7.77). */
.chdr.ink{color:var(--accent-ink);
  background:linear-gradient(118deg,var(--tone) 0%,color-mix(in srgb,var(--tone) 72%,#FFFFFF) 100%)}
.chdr.ink .lbl,
.chdr.ink .sub{color:color-mix(in srgb,var(--accent-ink) 78%,transparent)}
/* Dark theme inverts the fill relationship. --tone in dark is a LIGHT colour
   (--amber is #FAC775) because the palette is built for text on dark ground, so
   white on it lands near 1.5:1. Filled tone surfaces take --accent-ink in dark,
   exactly as .pill.gold already does in both themes, and the gradient's second
   stop mixes toward white instead of #101418 so the band stays light. */
:root[data-theme="dark"] .chdr,
:root[data-theme="dark"] .cflag{color:var(--accent-ink);
  background:linear-gradient(118deg,var(--tone) 0%,color-mix(in srgb,var(--tone) 72%,#FFFFFF) 100%)}
:root[data-theme="dark"] .chdr .lbl,
:root[data-theme="dark"] .chdr .sub{color:color-mix(in srgb,var(--accent-ink) 78%,transparent)}

/* ---- Move tab (0.7.47) -----------------------------------------------------
   The tab used to be a big number, seven unlabelled 48px bars, a dropdown and a
   plain list. These carry the averages, the insights, the multi-challenge
   standings and a leaderboard you can find yourself in. */
.card.flush{padding:0;overflow:hidden}
/* One vertical rhythm for the whole tab. Before this the gaps between stacked
   blocks measured 0, 4, 8, 12, 14 and 20px depending on which element happened
   to carry its own margin — including a -4px note overlapping the card above it
   and a podium sitting flush against the filter chips. .card carries no margin
   of its own by design, so the container sets it once. */
/* .hd-slab joined this list on 2026-09-10 when the Move tab's steps card became
   the slab: it is not a .card, so it got no space beneath it and "Last 7 days"
   sat flush against its bottom edge (reported from staging the same day). */
.mvsec > .card,.mvsec > .hd-slab,.mvsec > .chips,.mvsec > p{margin-bottom:var(--s3)}
.mvsec > .sect:first-child{margin-top:0}
.mvnote{margin:0 var(--s1) var(--s3);font-size:var(--fs-xs);line-height:1.45;color:var(--ink-faint)}
.mvbar{height:10px;border-radius:var(--r-pill);background:var(--surface-sunken);overflow:hidden}
.mvbar.thin{height:6px}
.mvbar i{display:block;height:100%;border-radius:var(--r-pill);background:var(--accent);transition:width var(--dur) var(--ease)}
/* 7-day chart: values above, bars, day names below. */
.mvchart{display:flex;align-items:flex-end;gap:6px;height:130px}
.mvcol{flex:1;display:flex;flex-direction:column;align-items:center;gap:6px;justify-content:flex-end}
.mvval{font-size:10px;font-weight:600;font-family:var(--font-mono);color:var(--ink-muted)}
.mvval.on{color:var(--gold-text)}
.mvbarv{width:100%;border-radius:6px 6px 3px 3px;
  background:linear-gradient(180deg,var(--accent) 0%,color-mix(in srgb,var(--accent) 30%,var(--surface)) 100%)}
.mvbarv.on{background:linear-gradient(180deg,var(--gold) 0%,color-mix(in srgb,var(--gold) 55%,var(--surface)) 100%)}
.mvday{font-size:10px;font-weight:600;color:var(--ink-faint)}
.mvkey{display:inline-flex;align-items:center;gap:5px;margin-right:14px}
.mvkey i{width:9px;height:9px;border-radius:3px;display:block}
/* averages: four cells, each with its own trend against the period before */
.mvavg{display:grid;grid-template-columns:repeat(4,minmax(0,1fr))}
/* Last 30 days (2026-09-18) shows three figures, not four, and its day
   list is grouped into weeks: a header carrying the week's total, and
   today's row marked so the top group has an anchor. */
.mvavg3{grid-template-columns:repeat(3,minmax(0,1fr))}
.mvavg4{grid-template-columns:repeat(4,minmax(0,1fr))}
/* Workout sessions (2026-09-18, mockup move-activity-sessions.html option A).
   One row per walk, run, ride or gym visit: the kind'''s icon, what it was and
   when, and a value that falls through distance -> calories -> minutes,
   because a gym session has no distance. */
.mvses{gap:11px}
.mvses .mvhi{width:34px;height:34px;border-radius:11px;flex:0 0 auto}
.mvses .t{font-size:14.5px;font-weight:700;display:flex;align-items:center;gap:6px;min-width:0}
.mvses-v{font-size:14px;color:var(--ink);text-align:right;flex:0 0 auto;margin-left:10px}
.mvses-v small{display:block;font-family:var(--font-sans);font-size:12px;font-weight:600;color:var(--ink-soft);letter-spacing:0;margin-top:1px}
/* Two records of one session: labelled, never resolved away in silence. */
.mvses-dup{background:var(--gold-wash);color:var(--gold-text);flex:0 0 auto}
/* A month's total runs to six figures with Indian grouping (3,19,000),
   which overflowed the cell at 320px. The figure gives way instead. */
.mvavg3 .mvavg-n{font-size:clamp(14px,4.6vw,19px)}
.mvavg3 .mvavg-cell{padding-inline:4px}
.mvwk-h{display:flex;align-items:baseline;gap:var(--s2)}
.mvwk-n{font-family:var(--font-mono);font-weight:600;font-size:12px;color:var(--ink-soft);letter-spacing:0}
.mvwk-n small{font-family:var(--font-sans,inherit);font-weight:600;font-size:12px;color:var(--ink-faint)}
.mvwk-n.none{font-family:var(--font-sans,inherit);font-weight:500;color:var(--ink-faint);text-transform:none;letter-spacing:0}
.mvwk-today{background:var(--accent-wash)}
.mvwk-today .t{font-weight:600}
.mvavg-cell{padding:14px 6px;text-align:center;border-right:1px solid var(--hairline-soft)}
.mvavg-cell:last-child{border-right:0}
.mvavg-n{font-family:var(--font-mono);font-weight:600;font-size:19px;letter-spacing:-.02em}
.mvavg-n.now{color:var(--accent-text)}
/* Last night by stage (2026-09-14): one split bar, minutes keyed beneath */
.mvsleep-bar{display:flex;height:12px;border-radius:999px;overflow:hidden;margin-top:14px;background:var(--surface-sunken);gap:2px}
.mvsleep-bar i{display:block;height:100%}
.mvsleep-key{display:flex;flex-wrap:wrap;gap:6px 14px;margin-top:10px;font-size:var(--fs-xs);color:var(--ink-faint)}
.mvsleep-key span{display:inline-flex;align-items:center;gap:5px}
.mvsleep-key i{width:9px;height:9px;border-radius:3px;display:block}
.mvsleep-key b{color:var(--ink);font-weight:600}
/* Health cards, version C (2026-09-14). Three headed sections under the steps
   slab — Today's activity, Heart, Last night's sleep — each with a History
   link into the Week / Month charts. Hues come from the --hl-* tokens; the
   tint behind an icon is mixed from the same hue so one token drives both. */
.mvsec > .mvhd,.mvsleepboard > .mvhd{display:flex;align-items:center;gap:8px}
.mvhd .grow{flex:1}
.mvhd-link{font-size:var(--fs-xs);letter-spacing:0;text-transform:none;font-weight:700;color:var(--accent-text);padding:6px 2px;margin:-6px 0}
.mvhi{width:24px;height:24px;border-radius:8px;display:flex;align-items:center;justify-content:center;flex:0 0 auto;
  color:var(--hc);background:color-mix(in srgb,var(--hc) 14%,transparent)}
.mvhi svg{width:14px;height:14px}
.mvstrip{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));width:100%;text-align:center}
.mvstrip.two{grid-template-columns:repeat(2,minmax(0,1fr));text-align:left}
.mvstrip-c{padding:10px 6px 9px;display:flex;flex-direction:column;align-items:center;gap:5px;min-width:0;border-right:1px solid var(--hairline-soft)}
.mvstrip-c:last-child{border-right:0}
.mvstrip.two .mvstrip-c{flex-direction:row;justify-content:flex-start;gap:9px;padding:10px 12px}
.mvstrip-t{display:flex;flex-direction:column;gap:3px;min-width:0}
.mvstrip-n{font-family:var(--font-mono);font-weight:600;font-size:17px;line-height:1;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.mvstrip-n.none{color:var(--ink-muted)}
.mvstrip-n small{font-family:var(--font-sans,inherit);font-size:12px;color:var(--ink-soft);font-weight:600;letter-spacing:0}
.mvstrip-k{font-size:12px;color:var(--ink-soft);font-weight:600;line-height:1.15;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
/* The night sky. Its own ink throughout, because the ground is dark in both
   themes and --ink would vanish on it in light mode. */
.mvnight{display:block;width:100%;text-align:left;position:relative;overflow:hidden;border-radius:20px;padding:16px;color:var(--night-ink);
  background:radial-gradient(120% 90% at 90% 0%,var(--night-1) 0%,var(--night-2) 45%,var(--night-3) 100%);border:1px solid rgba(167,139,250,.18)}
.mvnight .stars{position:absolute;right:0;top:0;width:60%;height:90px;pointer-events:none;opacity:.55}
.mvnight-k{font-size:var(--fs-xs);font-weight:600;color:var(--night-soft)}
.mvnight-big{font-family:var(--font-mono);font-weight:600;font-size:34px;line-height:1;letter-spacing:-.02em;margin-top:4px}
.mvnight-s{font-size:13.5px;color:var(--night-soft);margin-top:4px}
.mvnight .mvsleep-bar{background:rgba(10,10,30,.35);height:10px}
.mvnight .mvsleep-key{color:var(--night-soft)}
.mvnight .mvsleep-key b{color:var(--night-ink);font-family:var(--font-mono)}
.mvnight-during{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:14px}
.mvnight-during > button{width:100%;text-align:left;color:inherit;background:rgba(10,10,30,.35);border:1px solid rgba(183,166,255,.14);border-radius:12px;padding:10px 11px;display:flex;flex-direction:column;gap:3px}
.mvnight-during .n{font-family:var(--font-mono);font-weight:600;font-size:18px;line-height:1.1}
.mvnight-during .n small{font-family:var(--font-sans,inherit);font-size:12px;color:var(--night-soft);font-weight:600;margin-left:2px}
.mvnight-during .n.none{color:var(--night-faint)}
.mvnight-f{font-size:var(--fs-xs);color:var(--night-faint)}
/* Sleep card, version C: the AIKYA score and its word, hours beside it, one
   sentence, the parts as bars, tonight's bedtime, why it matters. */
.mvstrip-c{width:100%;color:inherit}
.mvstrip.two .mvstrip-c{text-align:left}
.mvsec > .mvnight{margin-bottom:var(--s3)}
.mvstrip-c:focus-visible,.mvnight-during > button:focus-visible,.mvnight-more:focus-visible{outline:2px solid var(--accent);outline-offset:-2px;border-radius:12px}
.mvnight-top{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}
.mvnight-word{font-size:19px;font-weight:800;letter-spacing:-.01em}
.mvnight-side{text-align:right;display:flex;flex-direction:column;align-items:flex-end;gap:2px}
.mvnight-hm{font-family:var(--font-mono);font-weight:600;font-size:22px;line-height:1.2;letter-spacing:-.02em}
.mvnight-say{margin:12px 0 0;font-size:13.5px;line-height:1.45;color:var(--night-soft)}
.mvfactors{display:flex;flex-direction:column;gap:7px;margin-top:12px}
.mvfactor{display:grid;grid-template-columns:1fr auto;gap:3px 10px;font-size:12.5px;font-weight:600}
.mvfactor .mono{font-family:var(--font-mono);color:var(--night-soft);font-weight:600}
.mvfactor .bar{grid-column:1/-1;height:5px;border-radius:9px;background:rgba(10,10,30,.45);overflow:hidden}
.mvfactor .bar i{display:block;height:100%;border-radius:9px;background:var(--night-soft)}
.mvfactor .bar i.warn{background:var(--hl-awake)}
.mvnight-tonight{display:flex;align-items:center;gap:10px;margin-top:12px;padding:10px 12px;border-radius:12px;font-size:13.5px;line-height:1.4;
  background:color-mix(in srgb,var(--hl-awake) 12%,transparent);border:1px solid color-mix(in srgb,var(--hl-awake) 30%,transparent)}
.mvnight-tonight b{font-family:var(--font-mono);color:var(--hl-awake)}
.mvnight-tonight .mvhi{background:none;width:20px;height:20px}
.mvnight-tonight .mvhi svg{width:18px;height:18px}
.mvnight-why{margin-top:10px;padding:11px 12px;border-radius:12px;background:rgba(10,10,30,.35);border:1px solid rgba(183,166,255,.14)}
.mvnight-why b{font-size:13px}
.mvnight-why p{margin:3px 0 0;font-size:12.5px;line-height:1.45;color:var(--night-soft)}
.mvnight-more{display:flex;align-items:center;gap:4px;margin:12px 0 -4px;padding:6px 0;font-size:var(--fs-xs);font-weight:700;color:var(--night-soft)}
.mvnight-more .i{width:14px;height:14px}
/* Sleep card version A: the ring and a reason under every part */
.mvnight-a{display:flex;align-items:center;gap:16px}
/* Simple sleep card (2026-09-15): hours big, a verdict pill, one sentence */
.mvsimple-top{display:flex;align-items:flex-start;gap:12px}
.mvsimple-top .mvnight-big{margin-top:4px}
.mvsimple-top .mvnight-s{margin-top:4px}
.mvverdict{display:inline-flex;align-items:center;gap:6px;flex:0 0 auto;font-size:13px;font-weight:700;border-radius:99px;padding:3px 11px;white-space:nowrap}
.mvverdict i{width:9px;height:9px;border-radius:50%;background:currentColor}
.mvverdict.good{color:#5DCAA5;background:rgba(93,202,165,.16)}
.mvverdict.ok{color:#F2B751;background:rgba(242,183,81,.16)}
.mvverdict.low{color:#FF8C80;background:rgba(255,107,94,.16)}
.mvsimple-say{margin:14px 0 0;font-size:14.5px;line-height:1.45;color:var(--night-ink)}
/* Rank tiles (2026-09-15): three across, more scroll sideways */
.mvtiles{display:grid;grid-auto-flow:column;grid-auto-columns:calc((100% - 16px) / 3);gap:8px;overflow-x:auto;scroll-snap-type:x mandatory;margin-bottom:var(--s3);padding-bottom:2px}
.mvtile{scroll-snap-align:start;display:flex;flex-direction:column;align-items:flex-start;gap:4px;min-height:132px;padding:10px 10px 11px;text-align:left;color:inherit;border-radius:16px;background:var(--surface);border:1px solid var(--card-edge)}
.mvtile.first{background:linear-gradient(165deg,color-mix(in srgb,var(--gold) 20%,var(--surface)),var(--surface) 60%);border-color:color-mix(in srgb,var(--gold) 40%,transparent)}
.mvtile .mvhi{width:30px;height:30px;border-radius:9px}
.mvtile .mvhi svg,.mvtile .mvhi .i{width:16px;height:16px}
.mvhi.award{color:var(--accent-text);background:color-mix(in srgb,var(--accent) 14%,transparent)}
.mvtile-r{font-family:var(--font-mono);font-weight:600;font-size:28px;line-height:1;letter-spacing:-.03em;color:var(--ink)}
.mvtile-r.gold{color:var(--gold-text)}
.mvtile-r small{font-family:var(--font-sans);font-size:12px;font-weight:600;letter-spacing:0;color:var(--ink-soft);margin-left:2px}
.mvtile-t{font-size:12.5px;font-weight:700;line-height:1.2;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.mvtile-s{margin-top:auto;font-size:var(--fs-xs);color:var(--ink-soft);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
.mvtile-s.good{color:var(--ok);font-weight:600}
.mvring{position:relative;width:96px;height:96px;flex:0 0 auto}
.mvring svg{width:96px;height:96px;transform:rotate(-90deg);display:block}
.mvring .c{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center}
.mvring .c .n{font-family:var(--font-mono);font-weight:600;font-size:30px;line-height:1;letter-spacing:-.02em}
.mvring .c span:last-child{font-size:12px;font-weight:600;color:var(--night-soft)}
.mvnight-verdict{font-size:22px;font-weight:800;letter-spacing:-.01em;line-height:1.15}
.mvfactors.a{gap:11px;margin-top:14px}
.mvfactors.a .mvfactor{font-size:13.5px}
.mvfactor .mono.faint{color:var(--night-faint)}
.mvfactor .why{grid-column:1/-1;font-size:12px;font-weight:400;line-height:1.4;color:var(--night-faint)}
/* Sleep leaderboard */
.mvhd-count{display:inline-flex;align-items:center;gap:4px;letter-spacing:0;text-transform:none;font-weight:600;color:var(--ink-soft)}
.mvhd-count .i{width:12px;height:12px}
.mvsleepboard > .card,.mvsleepboard > .mvjoin{margin-bottom:var(--s3)}
/* Move, simple cards (2026-09-15): six cards, each one question, one big
   number, one plain sentence; tap for more */
.mvc-hd{display:flex;flex-direction:column;gap:2px;padding:2px var(--s1) var(--s2)}
.mvc-date{font-size:17px;font-weight:800;letter-spacing:-.01em}
.mvc-hd .mvslab-sync{display:flex;align-items:center;gap:5px;font-size:var(--fs-xs);color:var(--ink-soft)}
.mvc-hd .mvslab-sync .i{width:13px;height:13px;flex:0 0 auto}
.mvc-hd .mvslab-sync-btn{color:var(--ink-soft);width:26px;height:26px;margin-left:auto}
.mvc-hd .mvslab-sync .spin{width:12px;height:12px;border:2px solid var(--ink-soft);border-top-color:transparent;border-radius:50%;animation:spin .8s linear infinite}
/* The Move segment picker (2026-09-16). Sits under the date and the connect
   banner, above whichever segment body is showing. */
.mvseg{margin-bottom:var(--s3)}
/* Connect Health before anything else: the whole app (2026-09-24, option A) */
.hgate{position:fixed;inset:0;z-index:55;background:var(--sand);overflow-y:auto;padding:calc(var(--safe-t, 0px) + 24px) 20px calc(var(--safe-b, 0px) + 20px);display:flex}
.hgate-in{width:100%;max-width:420px;margin:auto;display:flex;flex-direction:column;gap:14px}
.hgate-i{width:72px;height:72px;border-radius:22px;margin:0 auto;display:flex;align-items:center;justify-content:center;color:#fff}
.hgate-i .i{width:38px;height:38px}
.hgate-i.ios{background:linear-gradient(135deg,var(--accent),color-mix(in srgb,var(--accent) 60%,#FF2D55))}
.hgate-i.and{background:linear-gradient(135deg,var(--teal),var(--info))}
.hgate-t{margin:0;text-align:center;font-size:var(--fs-xl);font-weight:800;line-height:1.15}
.hgate-s{margin:0;text-align:center;color:var(--ink-soft);font-size:var(--fs-sm)}
.hgate-why{list-style:none;margin:6px 0 10px;padding:0;display:flex;flex-direction:column;gap:10px}
.hgate-why li{display:flex;gap:10px;align-items:flex-start;font-size:var(--fs-sm)}
.hgate-why i{flex:0 0 auto;width:24px;height:24px;border-radius:8px;background:var(--accent-wash);color:var(--accent-text);display:flex;align-items:center;justify-content:center;font-style:normal;font-weight:700;font-size:12px}
.hgate-go{min-height:50px;font-size:var(--fs-md)}
.hgate-p{margin:0;text-align:center;color:var(--ink-faint);font-size:var(--fs-xs)}
.hgate-why.steps li b{font-weight:700}
.hgate-again{min-height:44px}
/* Four segments (2026-09-19): Inspire · Leaderboard · Challenges · Health. */
.mvseg button{font-size:var(--fs-sm);padding:0 4px;white-space:nowrap}
/* H1: Health carries Body's cards under one divider; hidden when there is
   nothing under it yet (the sleep/heart host fills after the health read). */
.mvdiv{display:flex;align-items:center;gap:10px;margin:var(--s4) 0 var(--s2);font-size:var(--fs-xs);font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint)}
.mvdiv::before,.mvdiv::after{content:"";flex:1;height:1px;background:var(--hairline)}
.mvdiv:has(+ .mvsec:empty){display:none}
/* Health › no wearable data: the Body cards stay drawn (dashed) under a
   frosted overlay with one panel — what the cards need, and the Connect
   wearable button (2026-09-20). The panel is sticky so it rides along as the
   well scrolls beneath it. */
.mvsec:has(> .mvwell-ov){position:relative}
.mvwell-ov{position:absolute;inset:0;z-index:2;display:flex;justify-content:center;align-items:flex-start;padding:24px 12px;border-radius:var(--r-lg);
  background:color-mix(in srgb,var(--sand) 50%,transparent);backdrop-filter:blur(5px);-webkit-backdrop-filter:blur(5px)}
.mvwell-ovc{position:sticky;top:calc(var(--header-h) + var(--safe-t) + 16px);width:100%;max-width:360px;background:var(--surface);border:1px solid var(--card-edge);border-radius:var(--r-lg);box-shadow:var(--shadow-md,var(--shadow-sm));padding:18px 16px 16px;text-align:center;display:flex;flex-direction:column;align-items:center;gap:8px}
.mvwell-ovc .b{font-size:var(--fs-lg)}
.mvwell-ovc p{margin:0;line-height:1.45}
.mvwell-ovc .btn{width:100%;margin-top:6px}
.mvwell-ovi{width:44px;height:44px;border-radius:50%;background:var(--accent-wash);color:var(--accent-text);display:flex;align-items:center;justify-content:center}
.mvwell-ovi .i{width:22px;height:22px}
/* Inspire, V2 Trail: green → teal → blue as a 6px band on each post, on the
   FAB and on the compose card's icon (the icon is inline in celebrate.js). */
.post.inspire{position:relative}
/* The Inspire compose sheet and its compose card take the trail, not the
   pink accent (asked 2026-09-20): the Share button, the field outline on
   focus, the add-photos tile and the card's dashed border. Each rule is one
   class deeper than the pink rule it replaces, so it wins on specificity
   without touching the originals. */
/* The post type row at the top of the compose sheet (2026-09-23): two
   radios, nothing checked until the person picks; the checked one wears
   its kind - pink, or the trail. */
.ptype-l{display:flex;justify-content:space-between;align-items:baseline;font-size:12px;font-weight:800;letter-spacing:.5px;text-transform:uppercase;color:var(--ink-faint);margin-bottom:6px}
.ptype-l .req{color:var(--pink);text-transform:none;letter-spacing:0;font-size:12px;font-weight:700}
.ptype.picked .req{display:none}
/* Pills in one row (asked 2026-09-23, over the 58px tiles): icon and label
   side by side; the row scrolls sideways if a narrow phone cannot fit four. */
.ptype-seg{display:flex;flex-wrap:nowrap;gap:5px;overflow-x:auto;scrollbar-width:none}
.ptype-seg::-webkit-scrollbar{display:none}
.ptype-seg button{flex:1 1 auto;justify-content:center;height:34px;padding:0 8px;border-radius:var(--r-pill);border:1.5px solid var(--hairline-strong);background:transparent;color:var(--ink-soft);font-family:inherit;font-size:12.5px;font-weight:700;display:inline-flex;align-items:center;gap:4px;white-space:nowrap;cursor:pointer}
.ptype-seg button .i{width:14px;height:14px}
@media (max-width:379px){.ptype-seg button .i{display:none}.ptype-seg button{padding:0 6px}}
.ptype-seg button[data-k="celebration"][aria-checked="true"]{background:var(--accent);border-color:var(--accent);color:#fff}
/* G4: nothing under the type row is live until a type is picked; the
   title row carries the suggestion's Use and next; the chips sit under. */
.sheet.notype .field:not(.ptype){opacity:.38;pointer-events:none}
.cmerge .trow{display:flex;align-items:center;gap:4px;padding-right:6px;border-bottom:1px solid var(--hairline)}
.cmerge .trow .input{flex:1 1 auto;min-width:0;border-bottom:0}
.cmerge .trow .tuse{flex:0 0 auto;height:26px;padding:0 10px;border:0;border-radius:var(--r-pill);background:color-mix(in srgb,var(--accent) 15%,transparent);color:var(--pink);font-family:inherit;font-size:12px;font-weight:700;cursor:pointer}
.cmerge .trow .tnext{flex:0 0 auto;width:32px;height:32px;border:0;border-radius:50%;background:transparent;color:var(--pink);display:flex;align-items:center;justify-content:center;cursor:pointer}
.inspire-sheet .cmerge .trow .tuse{background:color-mix(in srgb,var(--teal) 16%,transparent);color:var(--teal)}
.inspire-sheet .cmerge .trow .tnext{color:var(--teal)}
.sheet .field.tsugg{display:flex;flex-direction:row;flex-wrap:nowrap;align-items:center;justify-content:flex-start;gap:6px;margin-top:-6px;overflow-x:auto;scrollbar-width:none}
.tsugg::-webkit-scrollbar{display:none}
.tsugg .tsugg-l{font-size:12px;color:var(--ink-faint);flex:0 0 auto;margin-right:2px}
.sheet .field.tsugg .chip{flex:0 0 auto;width:auto;height:30px;font-size:12.5px;white-space:nowrap}
.ptype-seg button[data-k="inspire"][aria-checked="true"]{background:linear-gradient(135deg,var(--ok),var(--teal) 55%,var(--info));border-color:transparent;color:#fff}
.ptype-seg button[data-k="general"][aria-checked="true"]{background:var(--info);border-color:var(--info);color:#fff}
.ptype-seg button[data-k="event"][aria-checked="true"]{background:var(--gold);border-color:var(--gold);color:#101418}
.evpick.req .li{outline:1.5px solid var(--gold);outline-offset:-1.5px;border-radius:var(--r-md)}
.evpick.req .li .t{color:var(--gold-text)}
.inspire-sheet .btn.primary{background:linear-gradient(135deg,var(--ok),var(--teal) 55%,var(--info));color:#fff}
/* The label is a .lbl, which the generic field-label rule paints in soft ink —
   invisible on this dark gradient (reported 2026-09-20, "share text
   missing"). On the pink button that same ink is what the design wants. */
.inspire-sheet .btn.primary .lbl{color:#fff}
.inspire-sheet .btn.primary:active{filter:brightness(.92)}
.inspire-sheet .btn.primary:disabled,.inspire-sheet .btn.primary.is-waiting{opacity:1;background:linear-gradient(135deg,color-mix(in srgb,var(--ok) 45%,var(--surface-sunken)),color-mix(in srgb,var(--teal) 45%,var(--surface-sunken)) 55%,color-mix(in srgb,var(--info) 45%,var(--surface-sunken)))}
.inspire-sheet .cmerge:focus-within{border-color:var(--teal);box-shadow:0 0 0 3px color-mix(in srgb,var(--teal) 18%,transparent)}
.inspire-sheet .compose-thumbs .add{border-color:color-mix(in srgb,var(--teal) 55%,transparent);background:color-mix(in srgb,var(--teal) 8%,transparent);color:var(--teal)}
.mvcards{display:flex;flex-direction:column;gap:var(--s3);padding-bottom:var(--s4)}
.mvcards > div:empty{display:none}
/* The column gap spaces these; `.card + .card` would double it between the two week cards. */
.mvcards > .card + .card{margin-top:0}
.mvcards .mvsec > .mvc{margin-bottom:0}
/* Steps & activity sits inline in the card column since 2026-09-18: the
   column's own gap spaces it, so the section's last child must not add its
   own margin on top of that. */
.mvcards > .mvsec > :last-child{margin-bottom:0}
.mvc{display:flex;flex-direction:column;gap:6px;width:100%;text-align:left;color:inherit;padding:14px 16px;border-radius:18px}
.mvc-k{display:flex;align-items:center;gap:8px;font-size:var(--fs-sm);font-weight:700;color:var(--ink-soft)}
.mvc-k .i{width:16px;height:16px;color:var(--ink-faint)}
.mvc-dot{width:9px;height:9px;border-radius:50%;flex:0 0 auto}
.mvc-big{font-family:var(--font-mono);font-weight:600;font-size:34px;line-height:1.05;letter-spacing:-.02em;color:var(--ink)}
.mvc-big.gold{color:var(--gold-text)}
.mvc-big small{font-family:var(--font-sans);font-size:14px;font-weight:600;letter-spacing:0;color:var(--ink-soft);margin-left:5px}
.mvc-line{display:flex;align-items:center;gap:10px}
.mvc-line .mvverdict.good,.mvc-k .mvverdict.good{color:var(--ok);background:var(--ok-wash)}
.mvc-line .mvverdict.ok,.mvc-k .mvverdict.ok{color:var(--gold-text);background:var(--gold-wash)}
.mvc-line .mvverdict.low,.mvc-k .mvverdict.low{color:var(--error);background:var(--error-wash)}
.mvc-k .mvverdict{font-size:12px;padding:2px 9px}
.mvc-bar{height:8px;border-radius:999px;background:var(--surface-sunken);overflow:hidden;margin:2px 0}
.mvc-bar i{display:block;height:100%;border-radius:999px;background:var(--accent)}
.mvc-say{font-size:14px;line-height:1.45;color:var(--ink-soft)}
/* "Steps this week" and "Streaks" (2026-09-17, option B), replacing the
   three-lane card. Bars are sized as a share of the week's top value (or the
   goal); the dashed line is the goal at the same scale. */
.mvc-sub{font-weight:500;color:var(--ink-faint);font-size:var(--fs-xs)}
.mvwb{position:relative;display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:8px;height:96px;margin:18px 0 0}
.mvwb-goal{position:absolute;left:0;right:0;height:0;border-top:1px dashed var(--hairline-strong);pointer-events:none;z-index:1}
.mvwb-goal span{position:absolute;right:0;bottom:2px;font:600 9px var(--font-mono);font-style:normal;color:var(--ink-faint);background:var(--surface);padding-left:3px}
.mvwb-col{display:flex;align-items:flex-end;min-width:0;height:100%}
.mvwb-bar{position:relative;display:block;width:100%;border-radius:7px 7px 3px 3px;background:color-mix(in srgb,var(--accent) 38%,var(--surface-sunken))}
.mvwb-bar.hit{background:linear-gradient(180deg,var(--ok),color-mix(in srgb,var(--ok) 55%,var(--surface-sunken)))}
/* Today is still running: hatched, outlined, never judged. */
.mvwb-bar.today:not(.hit){background:repeating-linear-gradient(135deg,color-mix(in srgb,var(--accent) 42%,var(--surface-sunken)) 0 6px,color-mix(in srgb,var(--accent) 20%,var(--surface-sunken)) 6px 12px);box-shadow:0 0 0 1.5px var(--accent)}
/* Nothing synced: an empty dashed slot the full height, never a zero bar. */
.mvwb-bar.none{background:transparent;border:1px dashed var(--hairline-strong);border-radius:7px}
.mvwb-bar > i{position:absolute;bottom:100%;left:50%;transform:translateX(-50%);margin-bottom:3px;font:600 9.5px var(--font-mono);font-style:normal;letter-spacing:-.03em;color:var(--ink-soft);white-space:nowrap}
.mvwb-bar.hit > i{color:var(--ok)}
.mvwb-days{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:8px;margin-top:4px}
.mvwb-days span{font:600 10.5px var(--font-mono);color:var(--ink-faint);text-align:center}
.mvwb-days span.today{color:var(--ink)}
.mvst-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:2px}
.mvst{display:flex;flex-direction:column;align-items:stretch;text-align:left;color:inherit;background:var(--surface-sunken);border:0;border-radius:14px;padding:11px 11px 12px;min-width:0;cursor:pointer;font:inherit}
.mvst:active{transform:scale(.98)}
.mvst-k{font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint)}
.mvst-big{font-family:var(--font-mono);font-size:26px;font-weight:600;line-height:1.1;color:var(--ink)}
.mvst-big small{font-family:var(--font-sans);font-size:13px;font-weight:500;color:var(--ink-faint)}
.mvst-streak{font-size:12px;line-height:1.3;font-weight:600;color:var(--ink-soft);margin-top:2px}
.mvst-streak.hot{color:var(--gold-text)}
.mvst-days{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:3px;margin-top:auto;padding-top:9px}
.mvst-days span{height:22px;border-radius:6px;display:grid;place-items:center;font:600 9px var(--font-mono);color:var(--ink-faint);min-width:0}
.mvst-days span.on.habit{background:var(--violet);color:var(--surface)}
.mvst-days span.on.move{background:var(--teal);color:var(--surface)}
.mvst-days span.miss{border:1.5px solid var(--hairline-strong)}
.mvst-days span.open{border:1.5px dashed var(--ink-faint)}
.mvst-days span.none{opacity:.55}
.mvst-days span.today{box-shadow:0 0 0 1.5px var(--ink);color:var(--ink)}
.mvst-days span.on.today{color:var(--surface)}
/* 320px phones: tighter gaps before anything shrinks. */
@media (max-width:340px){
  .mvwb,.mvwb-days{gap:5px}
  .mvst-grid{gap:8px}
  .mvst{padding:10px 8px}
  .mvst-days{gap:2px}
}
.mvc .hb2info{margin-left:auto}
.mvc-row{display:flex;align-items:center;gap:12px;padding:9px 0;border-top:1px solid var(--hairline-soft);cursor:pointer}
.mvc-row:first-of-type{border-top:0}
.mvc-row-t{font-size:15px;font-weight:600;line-height:1.3}
.mvc-row-s{font-size:var(--fs-xs);color:var(--ink-soft);margin-top:1px}
.mvc-row .hblate{margin-right:2px}
.mvc-tick{width:34px;height:34px;border-radius:50%;flex:0 0 auto;display:flex;align-items:center;justify-content:center;border:2px solid var(--hairline-strong);color:transparent}
.mvc-tick .i{width:17px;height:17px}
.mvc-tick.done{background:var(--ok);border-color:var(--ok);color:var(--surface)}
.mvc-row .chev .i{width:16px;height:16px;color:var(--ink-faint)}
.mvnew{min-height:30px;padding:0 10px;gap:4px;margin-left:8px}
.mvnew .i{width:13px;height:13px}
/* Move tab version C (2026-09-14): Activity · Wellbeing · Team */
.mvweek-hd{width:100%;text-align:left;margin-bottom:10px;color:inherit}
.mvweek-seg{margin-bottom:12px}
.mvweek-seg button{min-height:30px;font-size:var(--fs-sm)}
.mvweek .mvavg-cell{padding:8px 4px}
.mvlb-seg{margin-bottom:var(--s2)}
.mvsec > .mvlb-seg + .chips{margin-top:0}
.mvsleep-count{display:flex;align-items:center;gap:4px;margin:0 var(--s1) var(--s2);font-size:var(--fs-xs);font-weight:600;color:var(--ink-soft)}
.mvsleep-count .i{width:12px;height:12px}
.mvwell-empty .mvhi{width:34px;height:34px;border-radius:11px}
.mvwell-empty .mvhi svg{width:18px;height:18px}
.mvsec > .list.card{margin-bottom:var(--s3)}
.mvsec .list .mvhi{margin-right:10px}
/* The admin's floating + must never cover the last leaderboard row. */
body:has(.fab:not(.hide)) .mvteam{padding-bottom:84px}
/* A segment that is hidden must not keep stray margins from its sections. */
.mvsec:empty{display:none}
.mvjoin .mvnight-verdict{font-size:21px;max-width:18ch}
.mvjoin-mini{width:100%;text-align:left;gap:12px;margin-bottom:var(--s3)}
.mvjoin-mini .mvhi{width:34px;height:34px;border-radius:11px}
.mvjoin-mini .mvhi svg{width:18px;height:18px}
.mvbadge{display:inline-block;margin-left:6px;padding:0 7px;border-radius:99px;font-size:10.5px;font-weight:700;vertical-align:1px;
  color:var(--hl-hrv);background:color-mix(in srgb,var(--hl-hrv) 14%,transparent)}
.mvscore{text-align:right;flex:0 0 auto}
.mvscore .mono{font-size:16px;font-weight:600;line-height:1.1}
.mvpod.me .b{color:var(--hl-hrv)}
.mvleave{display:block;margin:0 var(--s1) var(--s3);padding:4px 0;font-size:var(--fs-xs);font-weight:600;color:var(--ink-faint);text-decoration:underline;text-underline-offset:2px}
/* One metric, one screen */
/* The panel header keeps a 44px right slot for an icon; the EN | हिं switch
   needs its own width, or it squeezes to 44px and clips. */
.panel.mvxp .hdr-in.simple{grid-template-columns:44px minmax(0,1fr) max-content}
.mvlang{padding:2px;flex:0 0 auto}
.mvlang button{flex:0 0 auto;min-width:40px}
.mvlang button{min-height:30px;padding:0 10px;font-size:13px}
.mvx{display:flex;flex-direction:column}
.mvx > .card{margin-bottom:var(--s3)}
.mvx-head{padding:14px}
.mvx-v{font-family:var(--font-mono);font-weight:600;font-size:30px;line-height:1;letter-spacing:-.02em}
.mvx-v.none{color:var(--ink-muted)}
.mvx-v small{font-family:var(--font-sans);font-size:13px;color:var(--ink-soft);font-weight:600;letter-spacing:0}
.mvx-chip{font-size:12px;font-weight:700;border-radius:99px;padding:1px 8px;background:var(--surface-sunken);color:var(--ink-soft);white-space:nowrap}
.mvx-chip.good{color:var(--ok);background:var(--ok-wash)}
.mvx-chip.low{color:var(--gold-text);background:var(--gold-wash)}
.mvx-scale{margin-top:14px}
.mvx-track{position:relative;height:10px;border-radius:9px;background:var(--surface-sunken)}
.mvx-track .zone{position:absolute;top:0;bottom:0;border-radius:9px;background:color-mix(in srgb,var(--ok) 35%,transparent)}
.mvx-track .usual{position:absolute;top:-4px;bottom:-4px;border:1.5px dashed var(--ink-soft);border-radius:6px}
.mvx-track .dot{position:absolute;top:50%;width:16px;height:16px;border-radius:50%;background:var(--ink);border:3px solid var(--surface);transform:translate(-50%,-50%)}
.mvx-ticks{display:flex;justify-content:space-between;margin-top:6px;font-family:var(--font-mono);font-size:10.5px;color:var(--ink-faint)}
.mvx-score{padding:14px}
.mvfactor.x{font-size:13.5px;margin-top:10px}
.mvfactor.x .mono{color:var(--ink-soft)}
.mvfactor.x .bar{background:var(--surface-sunken)}
.mvfactor.x .bar i{background:var(--hl-hrv)}
.mvfactor.x .why{grid-column:1/-1;font-size:12.5px;font-weight:400;color:var(--ink-soft);line-height:1.4}
.mvx-kv{display:flex;flex-direction:column;gap:14px;margin:var(--s2) var(--s1) 0}
.mvx-kv h4{margin:0 0 3px;font-size:var(--fs-xs);font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-faint)}
.mvx-kv p{margin:0;font-size:14.5px;line-height:1.55;max-width:62ch}
.mvx-helps{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:6px}
.mvx-helps li{display:flex;gap:8px;font-size:14px;line-height:1.45}
.mvx-helps li::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--hl-dist);margin-top:8px;flex:0 0 auto}
.mvx-disc{margin:var(--s4) var(--s1) var(--s3)}
/* History: one chart card per metric */
.mvhist-seg{margin-bottom:var(--s2)}
.mvchartc{padding:12px 12px 8px}
.mvchartc-hd{display:flex;align-items:center;gap:8px}
.mvchartc-t{font-size:14px;font-weight:600;flex:1}
.mvchartc-v{font-family:var(--font-mono);font-weight:600;font-size:15px}
.mvchartc-v small{font-family:var(--font-sans,inherit);font-size:12px;color:var(--ink-soft);font-weight:600;margin-left:2px}
.mvchartc-sub{font-size:12px;color:var(--ink-soft);margin:2px 0 4px 32px}
.mvchartc-sub .up{color:var(--ok)}.mvchartc-sub .down{color:var(--error)}
.mvchartc svg{width:100%;height:auto;display:block}
.mvchartc svg text{fill:var(--ink-faint);font-size:10px;font-family:var(--font-mono)}
.mvchartc svg .grid{stroke:var(--hairline)}
.mvchartc svg .goal{stroke:var(--ink);stroke-opacity:.35;stroke-dasharray:4 4}
.mvdelta{font-size:12px;font-weight:600;margin-top:2px}
.mvdelta.up{color:var(--ok)}.mvdelta.down{color:var(--error)}
/* insight rows */
.mvtip{display:flex;align-items:flex-start;gap:10px;padding:14px}
.mvtip-i{width:32px;height:32px;border-radius:10px;display:flex;align-items:center;justify-content:center;flex:0 0 auto}
/* Health connection card — the first thing on the Move tab, above the
   segments, because the connection applies to both of them and connecting is
   a find-it-once action. */
.mvhealth{display:flex;align-items:center;gap:var(--s3);margin-bottom:var(--s3)}
.mvhealth.compact{gap:10px}
.mvhealth.compact .mvhealth-i{width:32px;height:32px;border-radius:10px}
.mvhealth.compact .b{font-size:var(--fs-sm)}
.mvhealth.compact .small{font-size:var(--fs-xs)}
.mvhealth-i{width:40px;height:40px;border-radius:12px;flex:0 0 auto;display:flex;align-items:center;
  justify-content:center;background:var(--surface-sunken);color:var(--ink-faint)}
.mvhealth-i.on{background:var(--ok-wash);color:var(--ok)}
.mvhealth-i.off{background:var(--surface-sunken);color:var(--ink-muted)}
.mvhealth-ok{display:flex;align-items:center;gap:5px;color:var(--ok);font-weight:500;margin-top:1px}
.mvhealth-ok .i{width:13px;height:13px;flex:0 0 auto}
/* Sync status shown on the card instead of reloading the page (2026-08-27).
   .spin was scoped to `.btn .spin` and drew nothing outside a button. */
.mvhealth-err{display:flex;align-items:center;gap:5px;color:var(--error);font-weight:500;margin-top:1px}
.mvhealth-err .i{width:13px;height:13px;flex:0 0 auto}
.mvhealth .spin{width:12px;height:12px;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:spin .7s linear infinite;flex:0 0 auto;display:inline-block}
.mvhealth .small{display:flex;align-items:center;gap:5px}
/* standings: one row per joined challenge */
.mvstand{display:flex;align-items:flex-start;gap:12px;width:100%;text-align:left;padding:14px;color:inherit}
.mvstand-rank{width:44px;flex:0 0 auto;text-align:center}
.mvstand-rank .mono{font-family:var(--font-mono);font-weight:600;color:var(--ink-soft)}
.mvstand-rank .mono.gold{color:var(--gold-text)}
/* podium: 2-1-3 so the winner is centre and tallest */
.mvpodium{display:flex;align-items:flex-end;justify-content:center;gap:10px;padding:18px 12px 14px}
.mvpod{flex:1;text-align:center;display:flex;flex-direction:column;align-items:center;gap:4px}
.mvpod .av{margin-bottom:2px}
/* Second and third used --surface-sunken, which in DARK is #101418 — darker
   than the card it sits on (#1A2128) — so both steps read as black holes and
   only the winner had a colour. Each place gets its own, mixed against the card
   surface so it works in both themes. */
.mvpod-step{width:100%;height:44px;margin-top:6px;border-radius:8px 8px 0 0;
  background:color-mix(in srgb,var(--ink-soft) 26%,var(--surface));
  color:var(--ink);font-weight:700;font-size:13px;display:flex;align-items:flex-start;justify-content:center;padding-top:5px}
.mvpod.first .mvpod-step{height:66px;background:var(--gold);color:var(--accent-ink)}
/* --accent on --accent-wash measures 2.73:1 in light — the same trap tokens.css
   already warns about ("--accent is 3.3:1 as text"). Third place uses the
   text-safe pair the app uses for .btn.link. */
.mvpod.third .mvpod-step{height:34px;background:var(--pink-wash);color:var(--pink)}
/* leaderboard rows */
.mvrow{display:flex;align-items:center;gap:10px;padding:10px 12px}
.mvrow.me{background:var(--accent-wash);border-radius:var(--r-md)}
.mvrank{width:26px;text-align:center;flex:0 0 auto;font-family:var(--font-mono);font-weight:600;
  font-size:13px;color:var(--ink-soft)}
.mvsep{height:1px;background:var(--hairline-soft);margin:4px 12px}
/* challenge cards */
.mvchal-foot{display:flex;align-items:center;gap:8px;padding:10px 12px 10px 16px;border-top:1px solid var(--hairline-soft)}
/* The ring card (2026-09-18, mockup challenge-cards.html option D). The ring
   reports how far through; the chips beside it carry rank, goal-met and time
   left, which used to compete with the title at the same weight. */
.mvchal-hd{display:flex;align-items:flex-start;gap:var(--s3);padding:14px 16px 12px}
.mvchal-t{font-size:16px;letter-spacing:-.01em;line-height:1.25}
.mvchring{width:58px;height:58px;flex:0 0 auto}
.mvchring svg{width:58px;height:58px;display:block;overflow:visible}
.mvchring .trk{stroke:var(--surface-sunken)}
.mvchring .val{stroke:var(--accent)}
.mvchring.done .val{stroke:var(--ok)}
.mvchring text{text-anchor:middle;fill:var(--ink);font-family:var(--font-mono);font-weight:600;font-size:15px;letter-spacing:-.02em}
.mvchring .u{fill:var(--ink-soft);font-family:var(--font-sans);font-weight:700;font-size:9.5px;letter-spacing:.02em}
.mvchring.done .n{fill:var(--ok);font-size:21px}
/* Not joined: the award icon keeps the ring's place so a list of joined and
   unjoined challenges still lines up down the left edge. */
.mvchal-ic{width:58px;height:58px;border-radius:16px;background:var(--gold-wash);color:var(--gold-text)}
.mvchal-ic svg,.mvchal-ic .i{width:22px;height:22px}
.mvchips{display:flex;flex-wrap:wrap;gap:6px;margin-top:9px}
.mvchips .pill{gap:5px}
/* Preset challenges + How it works (2026-09-14) */
.mvhow-link{background:none;border:0;padding:2px 0 0;font:inherit;font-size:var(--fs-xs);font-weight:700;color:var(--accent-text);cursor:pointer}
.mvhow-link:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}
.mvhow-box{background:var(--surface-sunken);border-radius:14px;padding:12px 14px;display:flex;flex-direction:column;gap:8px;margin-bottom:var(--s3)}
.mvhow-box .k,.mvhow .k{font-size:12px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--teal)}
.mvhow{display:flex;flex-direction:column;gap:14px}
.mvhow-steps{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:10px}
.mvhow-steps li{display:grid;grid-template-columns:26px 1fr;gap:10px;font-size:var(--fs-md);line-height:1.45}
.mvhow-steps.compact li{font-size:var(--fs-sm);gap:8px;grid-template-columns:22px 1fr}
.mvhow-steps .n{width:26px;height:26px;border-radius:9px;background:var(--surface-sunken);color:var(--teal);display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700}
.mvhow-steps.compact .n{width:22px;height:22px;border-radius:7px;background:var(--surface)}
.mvhow-details{background:var(--surface-sunken);border-radius:12px;padding:12px 14px;display:flex;flex-direction:column;gap:8px;font-size:var(--fs-sm);line-height:1.5}
.mvhow-reward{display:flex;gap:8px;align-items:flex-start;font-size:var(--fs-sm);color:var(--ink)}
.mvhow-reward .i{color:var(--gold);flex:0 0 auto;margin-top:2px}
.mvhow-rules{margin:0;padding-left:18px;display:flex;flex-direction:column;gap:6px;font-size:var(--fs-sm);color:var(--ink-soft)}
.pill.mvtier.easy{background:var(--teal-wash);color:var(--teal)}
.pill.mvtier.medium{background:var(--amber-wash);color:var(--amber)}
.pill.mvtier.hard{background:var(--pink-wash);color:var(--pink)}
/* Ended challenges, collapsed (2026-08-26). The toggle looks like the other
   challenge cards (same .mvtip-i lead icon) so it reads as part of the same
   list rather than as a settings row that wandered in. */
/* `.card.mvended-toggle`, not `.mvended-toggle` — the element used to also
   carry `.flush`, whose `.card.flush{padding:0}` (0,2,0) out-specifies a bare
   class selector (0,1,0), so this padding never applied and the row sat
   jammed against the card edges. `.flush` was wrong here anyway: it exists for
   cards whose children run edge to edge, and this one is an ordinary padded
   row. Qualified with `.card` so it wins on specificity rather than on source
   order. */
.card.mvended-toggle{display:flex;align-items:center;gap:12px;width:100%;text-align:left;padding:14px 16px;margin-bottom:12px}
.card.mvended-toggle>.i:last-child{width:16px;height:16px;flex:0 0 auto;color:var(--ink-faint);transition:transform var(--dur-fast) var(--ease)}
.card.mvended-toggle[aria-expanded="true"]>.i:last-child{transform:rotate(180deg)}
.mvended-body{max-height:0;overflow:hidden;transition:max-height var(--dur) var(--ease)}
.mvended-body.open{max-height:4000px}
.mvended-body .mvchal{opacity:.8;margin-bottom:var(--s3)}
.mvended-body .mvchal:last-child{margin-bottom:0}
.mvended-body{padding-top:var(--s1)}


/* ---- Events tab: Agenda (2026-09-15, option B of events-tab-mockup.html) ----
   A hero for the next event, then compact month-grouped rows; the detail is a
   tall sheet (.evsheet) so the list keeps its place behind it. */
.evchips{margin-top:0}
.evpast{opacity:.66}
.evhero{position:relative;display:block;width:100%;height:196px;border-radius:var(--r-xl);overflow:hidden;margin:var(--s1) 0 var(--s2);text-align:left;color:#fff;
  background:linear-gradient(135deg,var(--violet),var(--accent))}
.evhero-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.evhero::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgba(0,0,0,0) 28%,rgba(0,0,0,.74))}
.evhero-txt{position:absolute;left:14px;right:14px;bottom:12px;z-index:1}
.evhero-kick{font:600 11px var(--font-mono);letter-spacing:.07em;text-transform:uppercase;opacity:.88}
.evhero-t{font-size:var(--fs-xl);font-weight:600;line-height:1.2;margin:2px 0 5px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;text-shadow:0 1px 8px rgba(0,0,0,.3)}
.evhero-m{display:flex;flex-wrap:wrap;gap:2px 12px;font-size:var(--fs-sm);opacity:.92}
.evcd{position:absolute;right:12px;top:12px;z-index:1;display:flex;gap:4px}
.evcd span{min-width:42px;padding:4px 7px;border-radius:10px;background:rgba(10,14,18,.55);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);text-align:center;line-height:1.1}
.evcd b{display:block;font:600 16px var(--font-mono)}
.evcd small{font-size:12px;opacity:.8}
.evcd .evcd-now{font-size:var(--fs-xs);font-weight:600;padding:6px 10px;background:var(--accent);color:var(--accent-ink)}
.evmonth{display:flex;align-items:baseline;gap:8px;margin:var(--s5) var(--s1) var(--s2)}
.evmonth b{font-size:var(--fs-md);font-weight:600}
.evmonth span{font-size:var(--fs-xs);color:var(--ink-faint)}
.evrow{display:flex;gap:10px;align-items:flex-start;margin-bottom:var(--s2)}
.evrow-day{width:38px;flex:0 0 auto;text-align:center;padding-top:10px}
.evrow-day small{display:block;font:600 10px var(--font-mono);letter-spacing:.05em;color:var(--ink-faint)}
.evrow-day b{display:block;font-size:21px;font-weight:600;line-height:1.1}
.evrow-day.today b{color:var(--accent-text)}
.evrow-item{flex:1;min-width:0;display:flex;gap:10px;align-items:center;padding:10px;border-radius:var(--r-lg);background:var(--surface);border:1px solid var(--card-edge);cursor:pointer}
.evrow-item:active{transform:scale(.99)}
.evrow-thumb{width:56px;height:56px;border-radius:12px;object-fit:cover;flex:0 0 auto;background:var(--surface-sunken)}
.evrow-thumb.blank{display:flex;align-items:center;justify-content:center;color:var(--ink-faint)}
.evrow-t{font-size:var(--fs-md);font-weight:600;line-height:1.25;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.evrow-m,.evrow-m2{display:flex;gap:2px 8px;font-size:var(--fs-xs);color:var(--ink-soft);margin-top:3px;min-width:0}
.evrow-m span{white-space:nowrap}
.evrow-m span + span::before{content:"\00B7\00A0"}
.evrow-m2{color:var(--ink-faint)}
.evperk{display:inline-flex;align-items:center;gap:4px;color:var(--gold-text);min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.evperk .i{width:12px;height:12px;flex:0 0 auto}
.evperk .ell{min-width:0}
/* A 44px hit area around a 32px ring, so the tick is easy to hit without
   reading as a big button on every row. */
.evtick{position:relative;width:44px;height:44px;margin:-6px -6px -6px 0;flex:0 0 auto;display:flex;align-items:center;justify-content:center;color:var(--ink-faint)}
.evtick::before{content:"";position:absolute;width:32px;height:32px;border-radius:50%;border:1.5px solid var(--hairline-strong)}
.evtick .i{width:15px;height:15px;position:relative;stroke-width:2.6}
.evtick.on{color:var(--surface)}
.evtick.on::before{background:var(--ok);border-color:var(--ok)}
/* the detail sheet */
.sheet.evsheet{height:min(88svh, calc(var(--sheet-vh, 100svh) - 8px));max-height:none}
.evsheet .sh{position:absolute;top:0;left:0;right:0;z-index:2;padding:var(--s3) var(--s3) 0;pointer-events:none}
.evsheet .sh h2{visibility:hidden}
.evsheet .sh .icobtn{pointer-events:auto;background:rgba(10,14,18,.5);color:#fff;border-radius:50%;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}
.evsheet.nocover .sh .icobtn{background:var(--surface-sunken);color:var(--ink)}
.evsheet .grab{position:absolute;left:50%;top:0;transform:translateX(-50%);z-index:3;background:rgba(255,255,255,.7)}
.evsheet.nocover .grab{background:var(--ink-muted)}
.evsheet .sb{padding:0}
.evsheet.nocover .evs-in{padding-top:52px}
.evs-cover{height:180px;background:var(--surface-sunken)}
.evs-cover img{width:100%;height:100%;object-fit:cover;display:block}
.evs-in{padding:var(--s4) var(--s5) var(--s6)}
.evs-pills{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:6px}
.evs-pills:empty{display:none}
.evs-pills .pill .i{width:12px;height:12px}
.evs-title{font-size:var(--fs-2xl);font-weight:600;line-height:1.2;margin:0}
.evs-meta{display:flex;flex-direction:column;gap:3px;margin-top:8px;font-size:var(--fs-sm);color:var(--ink-soft)}
.evs-meta span{display:flex;align-items:center;gap:6px}
.evs-meta .i{width:14px;height:14px;color:var(--ink-faint);flex:0 0 auto}
.evs-rsvp{display:grid;grid-template-columns:repeat(3,1fr);gap:4px;padding:4px;margin-top:var(--s4);border-radius:14px;background:var(--surface-sunken)}
.evs-rsvp button{min-height:42px;border-radius:11px;font-weight:600;font-size:var(--fs-md);color:var(--ink-soft)}
.evs-rsvp button[aria-pressed="true"]{background:var(--accent);color:var(--accent-ink)}
.evs-rsvp button:last-child[aria-pressed="true"]{background:var(--surface-raised);color:var(--ink);box-shadow:var(--shadow-sm)}
.evs-rsvp button:disabled{opacity:.6}
.evs-actions{display:flex;gap:var(--s2);margin-top:var(--s2)}
.evs-actions .btn{flex:1;padding:0 8px}
.evs-actions .btn .i{width:15px;height:15px}
.evs-tabs{display:flex;gap:var(--s5);border-bottom:1px solid var(--hairline);margin-top:var(--s4)}
.evs-tabs button{padding:10px 0 8px;font-size:var(--fs-md);font-weight:600;color:var(--ink-faint);border-bottom:2px solid transparent;margin-bottom:-1px}
.evs-tabs button[aria-pressed="true"]{color:var(--ink);border-bottom-color:var(--accent)}
.evs-pane{padding-top:var(--s3);min-height:120px}
.evs-desc{font-size:var(--fs-md);line-height:1.55;color:var(--ink-soft);white-space:pre-wrap;margin:0 0 var(--s3)}
.evs-irow{display:flex;align-items:center;gap:12px;width:100%;padding:10px 0;border-top:1px solid var(--hairline);text-align:left;color:inherit}
.evs-irow .t{font-size:var(--fs-md);font-weight:600}
.evs-irow .s{font-size:var(--fs-xs);color:var(--ink-faint)}
.evs-ic{width:38px;height:38px;border-radius:12px;display:flex;align-items:center;justify-content:center;background:var(--surface-sunken);color:var(--ink-soft);flex:0 0 auto}
.evs-act{font-size:var(--fs-sm);font-weight:600;color:var(--accent-text)}
.evs-by{font-size:var(--fs-xs);color:var(--ink-faint);padding-top:10px;border-top:1px solid var(--hairline)}
.evs-by b{color:var(--ink-soft);font-weight:600}
.evs-sub{font:600 11px var(--font-mono);letter-spacing:.07em;text-transform:uppercase;color:var(--ink-faint);margin:var(--s2) 0 2px}
.evs-person{display:flex;align-items:center;gap:10px;padding:7px 0}
.evs-person .t{font-size:var(--fs-md);font-weight:600}
.evs-person .s{font-size:var(--fs-xs);color:var(--ink-faint)}
.evs-none{padding:var(--s3) 0}
.evs-admin{display:flex;gap:var(--s2);margin-top:var(--s5);padding-top:var(--s4);border-top:1px solid var(--hairline)}
.evs-admin .btn{flex:1}

/* ---- Home tab (0.7.50) -----------------------------------------------------
   Built from the components the rest of the app already uses — .list, .li,
   .lead, .sect — so it reads as AIKYA rather than as a dashboard template. */
/* Each section lives in its own container so it can redraw independently,
   which makes its .sect a :first-child — and .sect:first-child exists to stop
   the FIRST heading on a page floating, at 4px. Every heading was getting that
   4px. Same specificity, declared later, so this wins it back. */
.hsec > .sect{margin-top:var(--s5)}
.hcta{min-height:32px;padding:0 var(--s3);font-size:var(--fs-md);flex:0 0 auto}
.hkv{display:flex;justify-content:space-between;align-items:center;padding:11px var(--s4);
  border-top:1px solid var(--hairline-soft);font-size:var(--fs-md)}
.hkv:first-child{border-top:0}
.hkv b{font-family:var(--font-mono);font-weight:600}
/* the unread count on a Circle row — the bell's badge is inline-styled in
   shell.js, so this is the first time it needed a class */
.bdg{min-width:20px;height:20px;padding:0 6px;border-radius:var(--r-pill);background:var(--accent);
  color:#fff;font-size:var(--fs-xs);font-weight:600;display:flex;align-items:center;
  justify-content:center;font-family:var(--font-mono);flex:0 0 auto}
/* The bell badge was inline-styled inside a header that rebuilt itself. Now the
   header is patched in place, so the badge needs a class of its own. */
.hdr-bdg{position:absolute;top:4px;right:2px;min-width:18px;height:18px;padding:0 5px;
  border:2px solid var(--sand)}
/* "New" on a Circle row: a dot, not a count. The row is one item, so a number
   would always read 1. */
/* Home 2026-08-22: date line, step tiles, what's-new tiles, slim rows. */
.hdate{text-align:center;font-size:13px;font-weight:600;color:var(--ink-muted);
  letter-spacing:.01em;margin:2px 0 12px}
.daygrid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
.daycell{padding:12px 8px;border-radius:var(--r-md);background:var(--surface);border:1px solid var(--card-edge);text-align:center}
.daycell b{display:block;font-family:var(--font-mono);font-size:21px;font-weight:600;letter-spacing:-.02em}
.daycell span{display:block;font-size:11px;color:var(--ink-muted);margin-top:3px}
.newgrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
.newcell{display:flex;align-items:center;gap:10px;padding:11px 12px;border-radius:var(--r-md);
  background:var(--surface);border:1px solid var(--card-edge);text-align:left}
.newcell .t{font-weight:600;font-size:13.5px}
.newcell .lead{width:32px;height:32px;border-radius:9px;display:flex;align-items:center;justify-content:center;flex:0 0 auto}
.newcell .lead .i{width:18px;height:18px}
.newcell .n{margin-left:auto;min-width:22px;height:22px;border-radius:11px;background:var(--accent);color:#fff;
  font-size:12px;font-weight:700;display:flex;align-items:center;justify-content:center;padding:0 6px;flex:0 0 auto}
.hslim{margin-top:8px}
.hslim .li{min-height:46px}
.hslim .lead{width:30px;height:30px}
.hslim .lead .i{width:16px;height:16px}
.hslim .t{font-size:13.5px}
.hsect{font-size:var(--fs-md);font-weight:600;color:var(--ink);margin:var(--s5) var(--s1) 9px;letter-spacing:-.01em}
.hsec > .hsect{margin-top:var(--s5)}
.hsect .n{color:var(--ink-faint);font-weight:500}

/* ---- group chat (js/views/chat.js) ---------------------------------------
   The panel is a column: scroller grows, composer sits on the bottom edge.
   Bubbles reuse the token palette — own messages accent-wash, others surface. */
/* The chat header had the full accent gradient for one day (asked 2026-08-21,
   softened the same day): now it is the app's minimal-gradient header with a
   stronger accent-wash tint, so the room still reads as its own place without
   a full-bleed pink bar. Title and buttons are back on the default ink. */
.chat-panel > .hdr{background:linear-gradient(120deg,color-mix(in srgb,var(--accent-wash) 85%,transparent),color-mix(in srgb,var(--sand) 88%,transparent) 70%)}
.chat-panel .pb{overflow:hidden;display:flex}
.chat-panel .pb-in{flex:1;display:flex;flex-direction:column;width:100%;max-width:var(--content-max);padding:0;min-height:0}
.chat-scroll{flex:1;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;padding:var(--s3);display:flex;flex-direction:column}
.chat-list{display:flex;flex-direction:column;gap:2px;margin-top:auto}
.chat-day{display:flex;justify-content:center;margin:var(--s3) 0 var(--s2)}
.chat-day span{font-size:var(--fs-xs,11px);color:var(--ink-faint);background:var(--surface-sunken);border-radius:var(--r-pill);padding:2px 10px}
.chat-row{display:flex;align-items:flex-end;gap:var(--s2);max-width:86%}
.chat-row.mine{align-self:flex-end;flex-direction:row-reverse}
.chat-row:not(.grouped){margin-top:var(--s2)}
.chat-avspace{width:28px;flex:0 0 auto}
.chat-row .av.s{width:28px;height:28px;flex:0 0 auto}
.chat-bubble{position:relative;background:var(--surface);border:1px solid var(--card-edge);border-radius:var(--r-lg,14px);padding:6px 10px 4px;box-shadow:var(--shadow-sm);min-width:0;overflow-wrap:anywhere}
.chat-row.mine .chat-bubble{background:var(--accent-wash);border-color:transparent}
.chat-who{font-size:var(--fs-xs,11px);font-weight:600;color:var(--ink-soft);margin-bottom:1px}
.chat-text{white-space:pre-wrap}
.chat-text.big{font-size:32px;line-height:1.2;display:inline-block;padding:2px 0}
.chat-text.gone{color:var(--ink-faint);font-style:italic}
.chat-text a{color:var(--info);text-decoration:underline}
.chat-when{display:inline-block;font-size:12px;color:var(--ink-faint);margin-left:8px;float:right;padding-top:6px;font-family:var(--font-mono)}
.chat-older{align-self:center;margin-bottom:var(--s2)}
.chat-composer{flex:0 0 auto;border-top:1px solid var(--hairline);background:var(--surface);padding:var(--s2) var(--s3) calc(var(--safe-b) + var(--s2))}
.chat-bar{display:flex;align-items:flex-end;gap:var(--s2)}
.chat-input{flex:1;resize:none;min-height:44px;max-height:120px;line-height:1.4}
.chat-send{background:var(--accent);color:var(--accent-ink);flex:0 0 auto}
.chat-send:disabled{opacity:.5}
.chat-emoji{display:grid;grid-template-columns:repeat(8,minmax(0,1fr));gap:2px;padding-bottom:var(--s2)}
.chat-emo{font-size:22px;padding:6px 0;border-radius:var(--r-md,10px)}
.chat-emo:active{background:var(--surface-sunken)}
.chat-locked{display:flex;align-items:center;justify-content:center;gap:var(--s2);color:var(--ink-faint);padding:var(--s2) 0}

/* ---- Wall of Praise (js/views/praise.js, js/parts/praise.js) ---------------
   A praise post renders as a pinned sticky note rather than a card: the paper
   colour IS the background, so every note carries its own ink token — using
   --ink here fails on three of the four papers. Colours cycle off the post id
   so the same note is the same colour on every device and every reload; the
   author never picks one.
   Rotation is decorative only: it is dropped under prefers-reduced-motion and
   never applied to the compose form, where a tilted input reads as a bug. */
.note{position:relative;border-radius:3px;padding:var(--s4) var(--s4) var(--s3);margin:0 0 var(--s5);
  background:var(--note-1);color:var(--note-1-ink);
  box-shadow:0 10px 18px -8px rgba(0,0,0,.30), 0 2px 4px rgba(0,0,0,.10)}
.note.c2{background:var(--note-2);color:var(--note-2-ink)}
.note.c3{background:var(--note-3);color:var(--note-3-ink)}
.note.c4{background:var(--note-4);color:var(--note-4-ink)}
.note.c5{background:var(--note-5);color:var(--note-5-ink)}
.note.c6{background:var(--note-6);color:var(--note-6-ink)}
.note.c7{background:var(--note-7);color:var(--note-7-ink)}
.note.c8{background:var(--note-8);color:var(--note-8-ink)}
.note.r1{transform:rotate(-1.6deg)}
.note.r2{transform:rotate(1.3deg)}
.note.r3{transform:rotate(-.7deg)}
.note.r4{transform:rotate(.9deg)}
@media (prefers-reduced-motion: reduce){.note.r1,.note.r2,.note.r3,.note.r4{transform:none}}
.note .pin{position:absolute;top:-8px;left:50%;transform:translateX(-50%);width:15px;height:15px;border-radius:50%;
  background:radial-gradient(circle at 35% 30%, #fff, var(--note-pin) 46%, rgba(0,0,0,.45) 100%);
  box-shadow:0 3px 4px rgba(0,0,0,.35)}
.note-who{font-family:var(--font-mono);font-size:12px;font-weight:600;letter-spacing:.02em;
  display:flex;align-items:center;gap:5px;flex-wrap:wrap;margin-bottom:var(--s2);opacity:.85}
.note-who .arw{opacity:.6}
.note-msg{font-family:var(--font-hand);font-size:20px;line-height:1.35;margin:0;overflow-wrap:anywhere;white-space:pre-wrap}
.note-foot{display:flex;align-items:center;gap:var(--s3);margin-top:var(--s3);padding-top:var(--s2);
  border-top:1px dashed currentColor;font-family:var(--font-mono);font-size:12px}
.note-foot .when{opacity:.7}
/* The reaction and replies controls inherit the note's ink instead of the card
   palette — .react/.btn.link are styled for a --surface ground. */
.note-foot .react,.note-foot .btn.link{color:inherit;opacity:.85;font-family:var(--font-mono);font-size:12px}
.note-foot .react[aria-pressed="true"]{opacity:1;font-weight:700}
.note-foot .grow{flex:1}
/* Dark mode (2026-08-23). Everything nested inside a note was styled from the
   PAGE tokens - --surface-sunken for the reply bubbles and the composer field,
   --ink-muted for the author line - and those assume the near-black page
   ground. On a note the ground is the PAPER, so in dark mode the bubbles and
   the field read as black holes punched into it, and the author line fell to
   1.05:1 (--ink-muted #484F58 on --note-8 #4A5218: two near-identical
   luminances). Inside a note, surfaces and inks are mixed from currentColor -
   the paper's own ink - so they track all eight papers and both themes with
   no per-paper values to keep in sync. */
.note .reply .bub,.note .reply .bub.wish{background:color-mix(in srgb,currentColor 13%,transparent);color:inherit}
.note .reply .who{color:inherit;opacity:.72}
.note .cfieldwrap{background:color-mix(in srgb,currentColor 13%,transparent)}
.note .cbox-ph,.note .cbox-x{color:inherit;opacity:.62}
.note .cico{color:inherit;opacity:.78}
.note .rlist::-webkit-scrollbar-thumb{background:color-mix(in srgb,currentColor 35%,transparent)}
.note .react{background:color-mix(in srgb,currentColor 13%,transparent);
  border-color:color-mix(in srgb,currentColor 26%,transparent);color:inherit}
/* restated AFTER the line above, which would otherwise beat it on source order */
.note .react[aria-pressed="true"]{background:var(--accent-wash);color:var(--accent-text);border-color:transparent}
.note .icobtn{color:inherit;opacity:.7;position:absolute;top:4px;right:4px;width:30px;height:30px}
.note .pending-chip{margin-left:auto;font-weight:700;letter-spacing:.04em;text-transform:uppercase;opacity:.9}
.note .drawer{border-top:1px dashed currentColor;margin-top:var(--s2)}

/* Corkboard: the Wall's own screen. The grid goes two-up only when there is
   room — on a narrow phone one column keeps the notes readable. */
/* The board carries the app's OWN jaali — the identical geometry to the one on
   `body` and on the sign-in screen: tile square, inscribed diamond, inner
   diamond, circles on the intersections. Nothing new is drawn.
   The one change is SCALE. The body wears it at 96px across a whole viewport,
   where it reads as fine texture; the board is only ~327px wide, so at 96px
   barely three tiles span it and the same motif reads as a large graphic parked
   behind the notes. Halved to 48px it is ~7 across — the density the sign-in
   screen has, which is what makes it read as cut stone rather than as drawing.
   Stroke and opacity come down with the scale so the finer mesh does not get
   busier than the coarse one it replaces.
   Faint on purpose: the notes are opaque and sit on top, so the lattice never
   runs under body text and contrast is untouched. Unlike the body jaali this
   one is painted in BOTH themes, because the ground is dark in both. */
.cork{position:relative;overflow:hidden;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cg fill='none' stroke='%23FFFFFF' stroke-opacity='.08' stroke-width='.6'%3E%3Cpath d='M0 0h24v24H0z'/%3E%3Cpath d='M12 0 24 12 12 24 0 12Z'/%3E%3Cpath d='M12 4.5 19.5 12 12 19.5 4.5 12Z'/%3E%3Ccircle cx='0' cy='0' r='1.75'/%3E%3Ccircle cx='24' cy='0' r='1.75'/%3E%3Ccircle cx='0' cy='24' r='1.75'/%3E%3Ccircle cx='24' cy='24' r='1.75'/%3E%3Ccircle cx='12' cy='12' r='1.75'/%3E%3C/g%3E%3C/svg%3E"),
    linear-gradient(158deg,var(--board-1) 0%,var(--board-2) 55%,var(--board-3) 100%);
  background-size:24px 24px,cover;background-repeat:repeat,no-repeat;
  border-radius:var(--r-lg);padding:var(--s5) var(--s3) var(--s3);margin-bottom:var(--s4)}
/* Sheen at the crown, so the board has a light source and the title sits in it.
   ::after would cover the notes, so the children are lifted above it. */
.cork::after{content:'';position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(120% 70% at 50% -10%,rgba(255,255,255,.16),transparent 60%)}
.cork > *{position:relative;z-index:1}
:root[data-theme="dark"] .cork{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cg fill='none' stroke='%23FFFFFF' stroke-opacity='.06' stroke-width='.6'%3E%3Cpath d='M0 0h24v24H0z'/%3E%3Cpath d='M12 0 24 12 12 24 0 12Z'/%3E%3Cpath d='M12 4.5 19.5 12 12 19.5 4.5 12Z'/%3E%3Ccircle cx='0' cy='0' r='1.75'/%3E%3Ccircle cx='24' cy='0' r='1.75'/%3E%3Ccircle cx='24' cy='24' r='1.75'/%3E%3Ccircle cx='0' cy='24' r='1.75'/%3E%3Ccircle cx='12' cy='12' r='1.75'/%3E%3C/g%3E%3C/svg%3E"),
    linear-gradient(158deg,var(--board-1) 0%,var(--board-2) 55%,var(--board-3) 100%)}
.cork-title{font-family:var(--font-script);font-size:26px;color:#fff;text-align:center;
  text-shadow:0 2px 4px rgba(60,10,30,.5);margin:0 0 var(--s4)}
/* The tidy wall (2026-09-01, replacing the 2026-08-24 measure-and-pack
   scatter): two masonry columns, no overlap, the board scrolls with the page.
   Tilt and paper are still hashed from the post id in praise.js —
   deterministic, so the wall is identical on every device — but tilt is
   capped at ±2°, texture rather than chaos. break-inside keeps a note whole;
   the top padding is headroom for the first row's pins, which sit above
   their note's box. */
.wall-canvas{columns:2;column-gap:9px;padding-top:8px}
.wnote{position:relative;display:block;width:100%;break-inside:avoid;margin:0 0 10px;
  min-height:88px;border-radius:3px;padding:10px 11px 8px;
  text-align:left;box-shadow:0 10px 18px -8px rgba(0,0,0,.35),0 2px 4px rgba(0,0,0,.12);
  background:var(--note-1);color:var(--note-1-ink)}
.wnote.c2{background:var(--note-2);color:var(--note-2-ink)}
.wnote.c3{background:var(--note-3);color:var(--note-3-ink)}
.wnote.c4{background:var(--note-4);color:var(--note-4-ink)}
.wnote.c5{background:var(--note-5);color:var(--note-5-ink)}
.wnote.c6{background:var(--note-6);color:var(--note-6-ink)}
.wnote.c7{background:var(--note-7);color:var(--note-7-ink)}
.wnote.c8{background:var(--note-8);color:var(--note-8-ink)}
.wnote .pin{position:absolute;top:-7px;left:50%;transform:translateX(-50%);width:13px;height:13px;border-radius:50%;
  background:radial-gradient(circle at 35% 30%,#fff,var(--note-pin) 46%,rgba(0,0,0,.45) 100%);box-shadow:0 3px 4px rgba(0,0,0,.35)}
.wnote .who{font-family:var(--font-mono);font-size:12px;font-weight:600;letter-spacing:.02em;opacity:.85;margin-bottom:4px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wnote .msg{font-family:var(--font-hand);font-size:14px;line-height:1.3;overflow-wrap:anywhere;
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.wnote .when{font-family:var(--font-mono);font-size:12px;opacity:.6;margin-top:5px}
/* Footer: reaction and reply counts (only when non-zero) and the day. Same
   ink as the paper, dimmed — the message stays the loudest thing on a note. */
.wnote .wfoot{display:flex;align-items:center;gap:8px;margin-top:6px;
  font-family:var(--font-mono);font-size:12px;font-weight:600;opacity:.75}
.wnote .wfoot .wc{display:inline-flex;align-items:center;gap:3px}
.wnote .wfoot .i{width:11px;height:11px}
.wnote .wfoot .grow{flex:1}
.wnote .wfoot .wdate{opacity:.9}
/* Age still recedes, but gently: on a board with no overlap every note has
   to stay readable, so the old 50% floor (right for a buried pile) becomes
   88%. Recency is mostly carried by position now — newest is top of wall. */
.wnote[data-age="1"]{opacity:.96}
.wnote[data-age="2"]{opacity:.92}
.wnote[data-age="3"]{opacity:.88}
.wnote.newest{box-shadow:0 14px 26px -8px rgba(0,0,0,.5),0 3px 6px rgba(0,0,0,.18)}
.wnote.newest .pin{width:15px;height:15px}
/* Praise you gave or received: the accent ring, so your notes are findable
   at a glance without touching the filters. */
.wnote.mine{outline:2px solid var(--accent);outline-offset:2px}
@keyframes wland{0%{transform:scale(1.6) rotate(0deg);opacity:0}70%{transform:scale(.97) rotate(var(--tilt,0deg));opacity:1}100%{transform:scale(1) rotate(var(--tilt,0deg))}}
.wnote.landing{animation:wland .5s cubic-bezier(.2,.8,.3,1.1) both}
@media (prefers-reduced-motion: reduce){.wnote.landing{animation:none}}
/* `.wmodal` — the centred overlay a note used to open into — was deleted on
   2026-08-26 when the note moved to a bottom sheet (`.sheet.praisesheet`
   above). Its z-index note is worth keeping though, because the new sheet
   inherits the same problem it solved: the composer's pickers, the ⋯ menu and
   the report form all open as sheets FROM the note, so whatever carries the
   note must sit BELOW them, not above. sheet() stacks by DOM order, so that
   now happens for free — but do not "fix" it with a z-index. */
/* Praise in the Celebrate feed: a post-it (2026-08-22). Started square
   (aspect 1.15); widened to 92% and centred on 23 Aug, then halved in
   height (aspect 2.3 — the ratio is width/height, so doubling it halves
   the paper at a fixed width). At that height only two lines of Kalam
   fit, hence the clamp; a dotted tear-off rule separates the footer. The
   .post base gives the surface reset; everything note-ish is set here. */
.sqnote{aspect-ratio:2.3;width:92%;position:relative;display:flex;flex-direction:column;cursor:pointer;
  margin:18px auto;padding:16px 14px 12px;border-radius:2px;overflow:hidden;
  background:var(--note-1);color:var(--note-1-ink);
  box-shadow:0 14px 24px -10px rgba(0,0,0,.38),0 3px 6px rgba(0,0,0,.12)}
.sqnote.c2{background:var(--note-2);color:var(--note-2-ink)}
.sqnote.c3{background:var(--note-3);color:var(--note-3-ink)}
.sqnote.c4{background:var(--note-4);color:var(--note-4-ink)}
.sqnote.c5{background:var(--note-5);color:var(--note-5-ink)}
.sqnote.c6{background:var(--note-6);color:var(--note-6-ink)}
.sqnote.c7{background:var(--note-7);color:var(--note-7-ink)}
.sqnote.c8{background:var(--note-8);color:var(--note-8-ink)}
.sqnote.t1{transform:rotate(-2deg)}
.sqnote.t2{transform:rotate(1.7deg)}
@media (prefers-reduced-motion: reduce){.sqnote.t1,.sqnote.t2{transform:none}}
.sqnote .pin{position:absolute;top:-8px;left:50%;transform:translateX(-50%);width:15px;height:15px;border-radius:50%;
  background:radial-gradient(circle at 35% 30%,#fff,var(--note-pin) 46%,rgba(0,0,0,.45) 100%);box-shadow:0 3px 4px rgba(0,0,0,.35)}
.sqnote .who{font-family:var(--font-mono);font-size:12px;font-weight:600;opacity:.85;margin-bottom:8px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* message: hand-written (Kalam), vertically centred in the paper */
.sqnote .msg{flex:1;display:flex;align-items:center;overflow:hidden}
.sqnote .msg > span{font-family:var(--font-hand);font-size:19px;line-height:1.32;overflow-wrap:anywhere;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.sqnote .ft{display:flex;gap:10px;align-items:center;font-family:var(--font-mono);font-size:12px;opacity:.85;
  margin-top:8px;padding-top:var(--s2);border-top:1px dotted currentColor}
.sqnote .wcount{display:inline-flex;align-items:center;gap:3px}
.sqnote .wcount .i{width:13px;height:13px}
.sqnote .wgo{font-weight:700}
.sqnote .pending-chip{font-weight:700;letter-spacing:.04em;text-transform:uppercase}
/* the classic folded bottom-right corner */
.sqnote::after{content:'';position:absolute;right:0;bottom:0;width:26px;height:26px;
  background:linear-gradient(315deg,var(--note-cut) 47%,rgba(0,0,0,.16) 50%,rgba(255,255,255,.35) 56%,transparent 68%)}
/* praise note as a doorway in the Celebrate feed: whole card taps through */
.note.golink{cursor:pointer}
.note.golink .wcount{display:inline-flex;align-items:center;gap:3px;opacity:.85}
.note.golink .wcount .i{width:13px;height:13px}
.note.golink .wgo{font-weight:700;opacity:.9}
.cork .chips{display:flex;gap:var(--s2);overflow-x:auto;overscroll-behavior-x:contain;padding-bottom:var(--s3);margin-bottom:var(--s2)}
.cork .chips .chip{flex:0 0 auto;border-color:rgba(255,255,255,.45);color:#fff;background:rgba(255,255,255,.14)}
/* The active chip is white-on-board, so its label takes the board's own rose
   rather than --ink: on the dark ground --ink is near-white in dark mode and
   the chip would read as blank. */
.cork .chips .chip[aria-pressed="true"]{background:#fff;color:var(--board-2);border-color:#fff;font-weight:700}
.cork-empty{color:#fff;text-align:center;padding:var(--s6) var(--s4);opacity:.9}

/* The wall's header figures, on the board itself so they read as part of it.
   Mono for the numerals, matching every other figure in the app (.chdr .lede,
   the Move tab). White with a shadow because the ground is dark in both themes
   - the same reason .cork-title carries one. */
.cork-stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--s2);
  margin:calc(var(--s2) * -1) 0 var(--s3);text-align:center;color:#fff}
.cork-stats .cstat{min-width:0}
.cork-stats b{display:block;font-family:var(--font-mono);font-size:26px;font-weight:600;
  line-height:1.05;letter-spacing:-.02em;text-shadow:0 2px 4px rgba(60,10,30,.45)}
.cork-stats span{display:block;font-size:var(--fs-xs);line-height:1.25;margin-top:3px;
  color:rgba(255,255,255,.82);text-wrap:balance}
/* Four figures on one row on every phone (2026-09-10, reported from staging:
   the fourth wrapped onto its own line). The grid went 3 -> 4 columns above;
   these scale the type with the viewport so 320px still fits four numbers
   side by side, and let a two-word label break onto two lines INSIDE its
   column rather than push the row. clamp() upper bounds are the old sizes,
   so nothing changes on a wide screen. */
.cork-stats b{font-size:clamp(19px,6vw,26px)}
.cork-stats span{font-size:clamp(10px,2.9vw,12px);overflow-wrap:anywhere}
.cork-stats .cstat{text-align:center;padding:0 2px}
.cork-nudge{text-align:center;font-size:var(--fs-sm);font-weight:600;color:#fff;
  margin:0 auto var(--s4);padding:5px var(--s3);border-radius:var(--r-pill);
  background:rgba(255,255,255,.16);width:fit-content;max-width:100%;text-wrap:balance}
/* "Wall is full" — under the board, quieter than the nudge above it. The
   board is a fixed 480px and has a real capacity; when praise outruns it the
   overflow is said out loud rather than silently dropped. */
.cork-full{text-align:center;font-size:var(--fs-xs);color:rgba(255,255,255,.72);
  margin:var(--s3) auto 0;max-width:100%;text-wrap:balance}

/* ---- Home: Option D "hero + digest" (2026-08-25) ---- */
.hgreet{display:flex;align-items:baseline;gap:8px;padding:2px 2px 10px}
.hgreet .t{font-size:20px;font-weight:700;line-height:1.2}
.hgreet .d{font-size:12.5px;color:var(--ink-faint)}
/* The warm slab hero (2026-08-25). WARM means warm in both themes now:
   light mode wears gold with dark ink (the dark-teal slab read as a dark-mode
   leak — walk feedback the same day); dark mode keeps the deep teal with
   cream. All colour comes through the --slab-* vars so the JS ghost mark
   follows the theme too. */
.hd-slab{--slab-ink:var(--accent-ink);--slab-soft:rgba(20,40,45,.74);--slab-track:rgba(20,40,45,.22);--slab-fill:#14282D;position:relative;overflow:hidden;display:block;width:100%;text-align:left;border-radius:var(--r-xl);background:linear-gradient(135deg,var(--gold),var(--accent));padding:20px 22px;box-shadow:var(--shadow-sm)}
:root[data-theme="dark"] .hd-slab{--slab-ink:#F5EDE1;--slab-soft:rgba(245,237,225,.7);--slab-track:rgba(245,237,225,.15);--slab-fill:#F2B751;background:linear-gradient(120deg,#14282D,#1E3A41)}
.hd-slab .ghost{position:absolute;right:-28px;top:-28px;opacity:.12;pointer-events:none;color:var(--slab-ink)}
.hd-slab .k{font-size:13.5px;color:var(--slab-soft)}
.hd-slab .n{font-family:var(--font-mono);font-size:44px;font-weight:600;letter-spacing:-.02em;line-height:1.1;color:var(--slab-ink)}
.hd-slab .pc{font-family:var(--font-mono);font-size:14px;color:var(--slab-ink);opacity:.75}
.hd-slab .bar{height:5px;border-radius:999px;background:var(--slab-track);margin-top:14px;overflow:hidden}
.hd-slab .bar i{display:block;height:5px;border-radius:999px;background:var(--slab-fill)}
.hd-slab .ctx{margin-top:10px;font-size:13.5px;color:var(--slab-soft)}
.hd-slab .ctx b{font-family:var(--font-mono);color:var(--slab-ink);font-weight:600}
/* The sync line on the Move tab's slab (2026-09-10), where the health card's
   status went once that card hides on connect. Inherits .ctx's colour; the
   refresh button borrows the slab ink so it reads on both gradients. */
.hd-slab .mvslab-sync{display:flex;align-items:center;gap:6px;margin-top:8px}
.hd-slab .mvslab-sync .i{width:14px;height:14px;flex:0 0 auto}
.hd-slab .mvslab-sync-btn{width:30px;height:30px;color:var(--slab-ink);margin-left:auto;flex:0 0 auto}
/* Self-contained: .spin is only ever defined scoped under .btn and .mvhealth,
   so a bare colour override here would leave a spinner with no border, no
   radius and no animation — invisible while a sync runs. */
.hd-slab .mvslab-sync .spin{width:14px;height:14px;border:2px solid var(--slab-soft);border-top-color:var(--slab-ink);border-radius:50%;animation:spin .7s linear infinite;display:inline-block;flex:0 0 auto}
/* Older praise fold under the wall (2026-09-22): a pill centred on a hairline
   rule, replacing the .card.mvended-toggle slab. 32px instead of ~64px, and
   it closes the board rather than competing with it. The opened notes sit at
   .82 so the fresh board still leads the eye. */
.cork-older{margin-top:var(--s4)}
.cork-older-rule{display:flex;align-items:center;gap:10px}
/* Colours are white-on-board, not --ink-*: the cork is dark in BOTH themes
   (same reason .cork .chips overrides the chip palette), so a token-driven
   pill would vanish in light mode. */
.cork-older-rule>i{flex:1;height:1px;background:rgba(255,255,255,.18)}
.cork-older-btn{position:relative;display:inline-flex;align-items:center;gap:7px;flex:0 0 auto;
  padding:6px 13px;border-radius:var(--r-pill);border:1px solid rgba(255,255,255,.28);
  font-size:12.5px;font-weight:600;color:rgba(255,255,255,.88)}
/* The pill is 32px tall by design; the hit area is not. */
.cork-older-btn::after{content:'';position:absolute;inset:-8px -12px}
.cork-older-btn:active{background:rgba(255,255,255,.12)}
.cork-older-btn .i{width:14px;height:14px;flex:0 0 auto;transition:transform var(--dur-fast) var(--ease)}
.cork-older-btn[aria-expanded="true"] .i{transform:rotate(180deg)}
.cork-older .wall-canvas{margin-top:var(--s3);opacity:.82}
/* The two streak cards above the steps slab (2026-08-29, restyled to "mini
   slabs" the same day — chosen from eight mockups): Habit Streak and Daily
   Move, half-width each, wearing the SAME gradient family and --slab-* vars
   as .hd-slab below so the hero region reads as one set of three slabs. */
.hd-duo{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:12px}
.hd-mini{--slab-ink:var(--accent-ink);--slab-soft:rgba(20,40,45,.74);--slab-track:rgba(20,40,45,.22);--slab-fill:#14282D;
  position:relative;overflow:hidden;display:flex;flex-direction:column;align-items:flex-start;gap:4px;text-align:left;
  border-radius:var(--r-lg);padding:12px 14px;box-shadow:var(--shadow-sm);
  background:linear-gradient(135deg,var(--gold),var(--accent));color:var(--slab-ink)}
:root[data-theme="dark"] .hd-mini{--slab-ink:#F5EDE1;--slab-soft:rgba(245,237,225,.7);--slab-track:rgba(245,237,225,.15);--slab-fill:#F2B751;background:linear-gradient(120deg,#14282D,#1E3A41)}
.hd-mini:active{filter:brightness(1.08)}
.hd-mini .k{font-size:12px;font-weight:700;color:var(--slab-soft);letter-spacing:.06em}
.hd-mini-n{font-family:var(--font-mono);font-size:22px;font-weight:600;line-height:1.1;color:var(--slab-ink)}
.hd-mini-n .of{font-family:var(--font);font-size:12.5px;font-weight:500;color:var(--slab-soft)}
.hd-mini-s{font-size:12px;color:var(--slab-soft)}
.hd-mini-s.warn{color:var(--slab-fill);font-weight:600}
/* Segment bars. `.fill` is habit progress (gold on the dark slab, deep ink on
   the light gradient); the week strip keeps semantic green/red, with today an
   outlined empty segment. box-sizing is border-box app-wide, so the outlined
   segment stays the same 6px as its filled neighbours. */
.hd-seg{display:flex;gap:3px;width:100%;margin:4px 0 2px}
.hd-seg i{flex:1;height:6px;border-radius:3px;background:var(--slab-track)}
.hd-seg i.fill{background:var(--slab-fill)}
.hd-seg i.on{background:var(--ok)}
.hd-seg i.off{background:var(--error)}
.hd-seg i.today{background:transparent;border:1.5px solid var(--slab-ink)}
.hd-minibar{width:100%;height:6px;border-radius:999px;background:var(--slab-track);margin:4px 0 2px;overflow:hidden}
.hd-minibar i{display:block;height:6px;border-radius:999px;background:var(--slab-fill)}
/* margin-BOTTOM as well as top (2026-09-12): this card and the "Share a
   celebration" action below it live in different containers on Celebrate
   (todayEl vs top), so `.card + .card` cannot reach between them and the two
   sat flush against each other. Symmetric with its own margin-top. */
.hd-card{display:flex;align-items:center;gap:12px;width:100%;text-align:left;background:var(--surface);border:1px solid var(--card-edge);border-radius:var(--r-lg);box-shadow:var(--shadow-sm);padding:12px 14px;margin-top:12px;margin-bottom:12px}
.hdrow .hd-card{margin-top:0}
.hd-card .lead{width:34px;height:34px;border-radius:10px;display:flex;align-items:center;justify-content:center;flex:0 0 auto}
.hd-card .k{font-size:13px;font-weight:700;color:var(--ink-soft);letter-spacing:.02em}
.hd-card .t{font-size:15px;font-weight:600;line-height:1.35}
/* The Tomorrow line under today's celebrations card (2026-09-17). */
.tmrw{display:flex;align-items:flex-start;gap:8px;margin-top:10px;padding-top:10px;border-top:1px solid var(--hairline-soft);font-size:var(--fs-sm);line-height:1.4;color:var(--ink-soft)}
.tmrw .i{flex:0 0 auto;margin-top:2px;color:var(--gold-text)}
.tmrw b{color:var(--ink);font-weight:600}
.hd-card.tmrw-card .s{font-size:14.5px;font-weight:600;color:var(--ink);line-height:1.35}
.hd-card .s{font-size:13.5px;color:var(--ink-faint);margin-top:1px}
.hd-ann{border-left:4px solid var(--gold)}
.hd-sect{display:flex;align-items:center;font-size:13px;font-weight:700;color:var(--ink-soft);letter-spacing:.02em;margin:16px 2px 2px}
.hd-hint{display:flex;align-items:center;gap:5px;font-size:12px;font-weight:500;color:var(--ink-muted)}
.hd-hint .i{width:14px;height:14px}
.hdrow{position:relative}
.hd-clear{position:absolute;top:6px;right:0;bottom:6px;width:76px;border-radius:12px;background:var(--error-wash);color:var(--error);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;font-size:12px;font-weight:700;opacity:0;pointer-events:none}
.hd-clear .i{width:16px;height:16px}
.hd-item{position:relative;display:flex;align-items:center;gap:12px;width:100%;text-align:left;padding:11px 2px;border-bottom:1px solid var(--hairline-soft)}
.hd-item .lead{width:36px;height:36px;border-radius:999px;display:flex;align-items:center;justify-content:center;flex:0 0 auto}
.hd-item .t{font-size:15px;line-height:1.4;font-weight:500}
.hd-item .chev{color:var(--ink-muted);margin-left:auto;flex:0 0 auto}
/* The gap beneath the digest (2026-09-10, reported from staging: the queue
   card sat flush against Celebrate's compose card). On Home the "you're caught
   up" line's padding did this job; the line is gone, so the container says it
   itself — and only when it holds something, or a non-admin's empty digest
   would push Celebrate's cards down by 12px for nothing. */
.digest:not(:empty){margin-bottom:var(--s3)}   /* queue rows open a panel; say so (2026-09-10) */
.hd-item .s{font-size:13px;color:var(--ink-faint);margin-top:1px}
.hd-caught{text-align:center;font-size:14px;color:var(--ink-faint);padding:14px 0 4px}

/* Home digest rows on a card (2026-08-25) */
.hd-list{margin-top:6px}
.hd-list .hd-item{padding:11px 14px;background:var(--surface)}
.hd-list .hdrow{border-top:1px solid var(--hairline-soft)}
.hd-list .hdrow:first-child{border-top:0}
.hd-list .hd-item{border-bottom:0}
.hd-list .hd-clear{top:4px;bottom:4px;right:4px}

/* ---- Daily Streaks (0.7.146) ------------------------------------------------
   Two tracks in one card: the habit tile carries --accent, the move tile
   --teal, and every state below is drawn from tokens so both themes work
   without a second palette. The tomorrow row is deliberately a SUNKEN strip
   with a lock and no check control — there must be nothing on it that looks
   tappable, because it cannot be marked until the day turns. */
.hbcard{overflow:hidden}
.hbtile{padding:var(--s4)}
.hbrow{display:flex;align-items:center;gap:12px}
.hbico{width:36px;height:36px;border-radius:10px;display:flex;align-items:center;justify-content:center;flex:0 0 auto}
.hbico[data-kind="habit"]{background:var(--accent-wash);color:var(--pink)}
.hbico[data-kind="move"]{background:var(--teal-wash);color:var(--teal)}
.hbico .i{width:20px;height:20px}
.hbtitle{font-weight:600;font-size:var(--fs-base);overflow-wrap:anywhere}
/* No strikethrough (2026-09-07): a struck title beside a filled check under
   a "Done today" line was one fact three times. Fading is enough. */
.hbtitle.done{color:var(--ink-soft)}
.hbsub{font-size:var(--fs-sm);color:var(--ink-soft);margin-top:3px}
.hbsub.ok{color:var(--teal);font-weight:500}
/* 44px, always: this is the one control the whole feature depends on. */
.hbcheck{width:44px;height:44px;border-radius:50%;flex:0 0 auto;display:flex;align-items:center;justify-content:center;
  border:2px dashed var(--accent);color:var(--accent-text);background:transparent;transition:transform var(--dur-fast),background var(--dur-fast)}
.hbtile[data-kind="move"] .hbcheck{border-color:var(--teal);color:var(--teal)}
.hbcheck .i{width:20px;height:20px}
.hbcheck:active{transform:scale(.92)}
.hbcheck.on{border-style:solid;background:var(--teal);border-color:var(--teal);color:#fff}
/* Both `.on` rules have to out-specify `.hbtile[data-kind="..."] .hbcheck`
   (0,3,0) above, which sets `color` to the track colour. `.hbcheck.on` is
   only (0,2,0), so the unqualified rule lost every time and the move tile
   painted a teal tick on a teal disc — a filled green circle with nothing
   in it, which is exactly how it was reported (2026-08-26). */
.hbtile[data-kind="move"] .hbcheck.on{background:var(--teal);border-color:var(--teal);color:#fff}
.hbtile[data-kind="habit"] .hbcheck.on{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}
.hbcheck.on .i{stroke-width:3}
.hbprog{display:flex;align-items:center;gap:10px;padding:8px 0 0 48px}
.hbprog .small{color:var(--ink-faint);white-space:nowrap}
.hbbar{flex:1;height:6px;border-radius:var(--r-pill);background:var(--surface-sunken);overflow:hidden;max-width:190px}
.hbbar i{display:block;height:100%;border-radius:var(--r-pill);background:var(--accent);transition:width var(--dur) var(--ease)}
.hbsep{height:1px;background:var(--hairline-soft);margin:0 16px}
.hbnext{display:flex;align-items:center;gap:8px;padding:10px 16px;background:var(--surface-sunken);
  font-size:var(--fs-sm);color:var(--ink-soft)}
.hbnext .i{width:14px;height:14px;flex:0 0 auto;color:var(--ink-faint)}
/* Track kicker on each Today tile — names which of the two things this is.
   Uppercased here rather than in the string, so the source stays sentence
   case (same convention as .sect and the Home kickers). Colour matches the
   tile's own .hbico so the label and its icon read as one unit. */
.hblabel{font-size:12px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  line-height:1.3;margin-bottom:2px}
.hblabel[data-kind="habit"]{color:var(--pink)}
.hblabel[data-kind="move"]{color:var(--teal)}
.hbguide{font-size:var(--fs-xs);color:var(--ink-faint);margin-top:2px;line-height:1.35}
/* The meta line under a tile title (Option A, 2026-09-07). Bold runs are
   the numbers; .hblate is the evening nudge in the accent, because after
   7 pm the time left IS the headline. */
.hbsub b{font-weight:600;color:var(--ink)}
.hblate{display:inline-flex;align-items:center;gap:3px;color:var(--accent-text);font-weight:600}
.hblate .i{width:12px;height:12px}
/* The foot row: seven dots, the count, an optional gold note, the link. One
   band in place of three. Whole row is the button. */
.hb2foot2{display:flex;align-items:center;gap:10px;width:100%;padding:11px var(--s4);border-top:1px solid var(--hairline);
  font-size:var(--fs-sm);color:var(--ink-faint);text-align:left}
.hb2foot2 b{font-weight:600;color:var(--ink-soft);white-space:nowrap}
.hb2foot2 .grow{flex:1}
.hb2foot2 .hb2link{display:inline-flex;align-items:center;gap:4px;white-space:nowrap;flex:0 0 auto}
.hb2foot2 .i{width:14px;height:14px;flex:0 0 auto}
/* The note is the one thing allowed to give: it ellipsizes before the
   count wraps or the chevron clips. */
.hb2pw{color:var(--gold-text);font-weight:600;white-space:nowrap;min-width:0;overflow:hidden;text-overflow:ellipsis}
/* A note ("2 to a Perfect Week") takes its own line under the dots, so the
   History & badges label always fits beside them at 375px. */
.hb2foot2:has(.hb2pw){flex-wrap:wrap;row-gap:4px}
.hb2foot2 .hb2pw{order:5;flex-basis:100%;padding-left:106px}
.hb2dots{display:flex;gap:2px;width:96px;flex:0 0 auto}
.hb2dots i{flex:1;height:8px;border-radius:3px;background:var(--surface-sunken)}
.hb2dots i.on{background:var(--ok)}
.hb2dots i.off{background:var(--error-wash)}
.hb2dots i.frz{background:var(--info-wash)}
.hb2dots i.today{background:transparent;border:1.5px solid var(--accent)}
/* Streak complete (addition 5): the card turns gold for the rest of that
   day and grows one row - the unlock. The gradient is the plan card's, so
   "something was earned" reads the same everywhere. */
.hbcard.win{background:linear-gradient(150deg,var(--gold-wash) 0%,var(--surface) 62%);border-color:var(--gold)}
.hbcard.win .hbico[data-kind="habit"]{background:var(--gold-wash);color:var(--gold-text)}
.hbwin{display:flex;align-items:center;gap:8px;width:100%;padding:10px 16px;border-top:1px solid var(--gold);
  font-size:var(--fs-sm);font-weight:600;color:var(--gold-text);text-align:left}
.hbwin .i{width:15px;height:15px;flex:0 0 auto}
.hbwin .grow{flex:1;min-width:0}
/* Footer row on the "Your progress" stats card — the way into the journey
   sheet (2026-08-26). It replaced a standalone .hbjourneylink card, which was
   one card too many on a screen that already stacks four and repeated the two
   numbers the stats grid states directly above it.

   NOTE for whoever adds the next hb* class: journeySheet()'s own body wrapper
   in habits.js uses the plain class `hbjourney` with no CSS attached. A rule
   named `.hbjourney` here lands on that sheet body — a `display:flex` did
   exactly that once and squeezed the hero, ladder and collection into one
   cut-off row. Grep before naming. */
.hbstats-foot{display:flex;align-items:center;gap:6px;padding:11px 16px;
  border-top:1px solid var(--hairline-soft);background:var(--surface-sunken);
  font-size:var(--fs-sm);font-weight:500;color:var(--ink-soft)}
.hbstats-foot .i{width:15px;height:15px;flex:0 0 auto;color:var(--ink-faint)}

/* Tomorrow, promoted (2026-08-26). While anything is still open today the
   preview stays a quiet strip at the foot of the Today card. Once every
   track is ticked there is nothing left to do on that card, so the same
   task is lifted out under its own TOMORROW heading and given a card of its
   own — still locked, still not markable. */
.hbtom{display:flex;align-items:center;gap:12px;padding:14px 16px}
.hbtom .hbico{opacity:.75}
.hbtom .hbtitle{color:var(--ink-soft)}
.hbtom .hblock{width:32px;height:32px;border-radius:50%;flex:0 0 auto;display:flex;align-items:center;
  justify-content:center;background:var(--surface-sunken);color:var(--ink-faint)}
.hbtom .hblock .i{width:15px;height:15px}
/* enrolment card: the three numbered lines ARE the explanation, so nobody has
   to be told how the streak works. */
.hbstart{padding:20px 18px;text-align:center}
.hbstart .hbico{margin:0 auto 10px;width:44px;height:44px;border-radius:12px}
.hbstart b{display:block;font-size:var(--fs-lg)}
.hbstart p{color:var(--ink-soft);margin:2px 0 14px}
.hbsteps{text-align:left;margin:0 0 16px;padding-left:22px;font-size:var(--fs-md);color:var(--ink);display:flex;flex-direction:column;gap:6px}
.hbsteps li::marker{font-family:var(--font-mono);font-weight:600;color:var(--ink-faint);font-size:var(--fs-sm)}
.hbstats{display:block;width:100%;padding:0;text-align:left;color:inherit;overflow:hidden}
/* ---- chain calendar: each cell splits habit (top) / move (bottom) ---- */
/* ---- the two-track layout (2026-08-29 rebuild: A+C combo of 3 mockups) ---- */
/* Section kickers: each track names itself and its audience, with a colour
   identity — violet for the personal habit, teal for the communal move. */
/* 16/8, and it went to 8/4 for one build (1.0.22) on a misread: "looks good
   but little spacious" meant a little MORE room, not less. Restored. */
.hb2kick{display:flex;align-items:center;gap:9px;margin:var(--s4) 0 var(--s2)}
.hb2kick b{font-size:var(--fs-lg);font-weight:700;letter-spacing:-.01em}
.hb2kick .s{font-size:12px;font-weight:600;color:var(--ink-faint);background:var(--surface-sunken);border:1px solid var(--hairline);padding:2px 9px;border-radius:var(--r-pill)}
.hb2ico{width:30px;height:30px;border-radius:10px;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto}
.hb2ico .i{width:17px;height:17px}
.hb2kick[data-kind="habit"] .hb2ico{background:linear-gradient(135deg,var(--violet-wash),var(--surface));color:var(--violet);border:1px solid var(--hairline)}
.hb2kick[data-kind="move"] .hb2ico{background:linear-gradient(135deg,var(--teal-wash),var(--surface));color:var(--teal);border:1px solid var(--hairline)}
.hb2kick .grow{flex:1}
/* "How It Works" is words, not a glyph — the ⓘ icon was reported invisible
   twice in one day (thin outline, 16px, dark chip). */
.hb2info{font-size:12px;font-weight:700;letter-spacing:.02em;color:var(--accent-text);background:var(--surface-sunken);border:1px solid var(--hairline);padding:4px 10px;border-radius:var(--r-pill);flex:0 0 auto}
.hb2info:active{background:var(--surface)}
/* History panels: .card has no margin of its own, so the stacked cards need
   the wrapper to space them; .sect keeps only a small lead-in inside it. */
.hb2hist{display:flex;flex-direction:column;gap:10px}
.hb2hist .sect{margin:6px var(--s1) 0}
.hb2sub{padding:10px 16px;border-top:1px solid var(--hairline)}
.xs2{font-size:12px;font-weight:700;letter-spacing:.05em;color:var(--ink-soft);text-transform:uppercase}
.hb2foot{display:flex;align-items:center;gap:6px;width:100%;padding:11px 16px;border-top:1px solid var(--hairline);font-size:var(--fs-sm);color:var(--ink-faint);text-align:left}
.hb2foot .i{width:15px;height:15px;flex:0 0 auto}
/* The 14-day strip: flat cells, semantic colours, today outlined. */
.hb2strip{display:flex;gap:3px;margin-top:6px}
.hb2strip i{flex:1;height:16px;border-radius:4px;display:inline-flex;align-items:center;justify-content:center;background:var(--surface-sunken);font-style:normal;font-size:9px}
.hb2strip i.on{background:var(--ok-wash);color:var(--ok)}
.hb2strip i.off{background:var(--error-wash);color:var(--error)}
.hb2strip i.frz{background:var(--info-wash);color:var(--info)}
.hb2strip i.today{background:transparent;border:1.5px solid var(--accent)}
.hb2strip .i{width:11px;height:11px}
/* The ladder path, with each rung's badge name under its node. */
.hb2lad{position:relative;display:flex;justify-content:space-between;margin-top:8px}
.hb2line{position:absolute;left:12px;right:12px;top:11px;height:2px;background:var(--hairline)}
.hb2line i{display:block;height:2px;background:var(--accent)}
.hb2rung{position:relative;display:flex;flex-direction:column;align-items:center;gap:3px;min-width:0}
.hb2rung .nd{width:24px;height:24px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font:600 9.5px var(--font-mono);background:var(--surface-sunken);color:var(--ink-faint);border:1px solid var(--hairline)}
.hb2rung[data-state="done"] .nd{background:var(--ok-wash);color:var(--ok);border-color:transparent}
.hb2rung[data-state="now"] .nd{background:var(--accent);color:var(--accent-ink);border-color:transparent;box-shadow:0 0 0 3px var(--accent-wash)}
.hb2rung b{font-size:8.5px;font-weight:600;color:var(--ink-faint);max-width:48px;text-align:center;line-height:1.15}
.hb2rung[data-state="done"] b{color:var(--ok)}
.hb2rung[data-state="now"] b{color:var(--accent-text)}
.hb2pulse{margin-top:8px}
.hb2pulse .hbbar{margin-bottom:3px}
/* The compact per-track month calendar in the history panels. */
.hb2grid{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:3px}
.hb2cell{height:26px;border-radius:6px;background:var(--surface-sunken);display:inline-flex;align-items:center;justify-content:center;border:0;font-size:9px}
.hb2cell .i{width:12px;height:12px}
.hb2cell.on{background:var(--ok-wash);color:var(--ok)}
.hb2cell.off{background:var(--error-wash);color:var(--error)}
.hb2cell.frz{background:var(--info-wash);color:var(--info)}
.hb2cell.today{outline:1.5px solid var(--accent);outline-offset:1px}
.hb2cell.future{background:transparent;border:1px dashed var(--hairline)}
.hb2cell .dnum{font:500 9px var(--font-mono);color:var(--ink-faint);opacity:.7}
.hbgrid{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:5px}
.hbdow{font-family:var(--font-mono);font-size:10px;font-weight:600;color:var(--ink-faint);text-align:center;padding-bottom:2px}
.hbcell{aspect-ratio:1;border-radius:7px;background:var(--surface-sunken);display:flex;flex-direction:column;gap:2px;padding:3px;border:0}
.hbcell.future{background:transparent;border:1px dashed var(--hairline)}
.hbcell.today{outline:2px solid var(--ink);outline-offset:1px}
.hbcell i{flex:1;border-radius:3px;display:block}
.hbcell .hbh.on{background:var(--accent)}
.hbcell .hbm.on{background:var(--teal)}
.hbcell .hbh.off,.hbcell .hbm.off{border:1.5px solid var(--hairline-strong)}
.hbcell .hbh.frz{background:repeating-linear-gradient(45deg,var(--accent-wash),var(--accent-wash) 3px,var(--accent) 3px,var(--accent) 4.5px)}
.hbcell .none{opacity:0}
.hbkey{display:flex;gap:12px;flex-wrap:wrap;margin-top:10px;font-size:var(--fs-xs);color:var(--ink-soft)}
.hbkey span{display:inline-flex;align-items:center;gap:5px}
.hbkey .k{width:10px;height:10px;border-radius:3px;display:block;background:var(--accent)}
.hbkey .k.mv{background:var(--teal)}
.hbkey .k.on{background:var(--ok-wash);border:1px solid var(--ok)}
.hbkey .k.off{background:var(--error-wash);border:1px solid var(--error)}
.hbkey .k.frz{background:var(--info-wash);border:1px solid var(--info)}
/* ---- Circle compact rows (2026-08-27) ----
   Deals-shaped: 48px tinted glyph, title, one line of facts, chevron. No
   photo — the picture is in the sheet the row opens. */
.crow{width:100%;text-align:left;margin-bottom:8px;align-items:center}
/* ---- Explore: bento home + Ticket cards (2026-09-17) ----------------------
   Tile colour comes from --bx (the channel tone), ticket colour from --tk.
   Both mix toward a darker shade of the same tone so white text stays legible
   on every tone in both themes. */
/* Sizes raised 2026-09-17 ("increase the explore tab card sizes"): rows 74 -> 96px, larger type. */
.bento{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));grid-auto-rows:96px;grid-auto-flow:row dense;gap:10px;margin-bottom:var(--s4)}
.bx{position:relative;overflow:hidden;border:0;border-radius:20px;padding:14px;display:flex;flex-direction:column;justify-content:space-between;align-items:flex-start;text-align:left;color:#fff;min-width:0;
  background:linear-gradient(140deg,color-mix(in srgb,var(--bx) 58%,#101418),color-mix(in srgb,var(--bx) 88%,#101418))}
.bx:active{transform:scale(.98)}
.bx-top{display:flex;flex-direction:column;gap:3px;min-width:0;max-width:100%;position:relative;z-index:1}
.bx-t{font-weight:800;font-size:17px;line-height:1.15;display:flex;align-items:center;gap:6px}
.bx-s{font-size:13px;line-height:1.3;opacity:.9;position:relative;z-index:1;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bx-latest{white-space:normal;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;opacity:.8}
.bx-emo{position:absolute;right:-8px;bottom:-10px;opacity:.22;pointer-events:none}
.bx-emo .i{width:84px;height:84px}
.bx-n{position:absolute;right:9px;top:9px;z-index:2;background:#fff;color:#101418;font-size:12px;font-weight:800;border-radius:10px;padding:2px 7px}
.bx-narrow{padding:12px 10px}.bx-narrow .bx-t{font-size:14.5px}.bx-narrow .bx-s{white-space:normal;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.bx-narrow .bx-emo .i{width:58px;height:58px}
.bx-photo .bx-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.bx-photo::before{content:"";position:absolute;inset:0;z-index:1;background:linear-gradient(180deg,rgba(0,0,0,.45),rgba(0,0,0,.1) 40%,rgba(0,0,0,.7))}
.bx-photo .bx-emo{display:none}
.bx-urgent{grid-column:1/-1;width:100%;min-height:82px;margin-bottom:10px;--bx:var(--error)}
@media (max-width:340px){.bento{gap:8px;grid-auto-rows:84px}.bx-t{font-size:15.5px}.bx-narrow .bx-t{font-size:13px}.bx-s{font-size:12px}}

.tkt{display:flex;align-items:stretch;width:100%;text-align:left;color:inherit;background:var(--surface);border:1px solid var(--card-edge);border-radius:18px;padding:0;margin-bottom:10px;position:relative;overflow:hidden;min-height:98px}
.tkt::before,.tkt::after{content:"";position:absolute;left:97px;width:16px;height:16px;border-radius:50%;background:var(--sand);border:1px solid var(--card-edge);z-index:1}
.tkt::before{top:-9px}.tkt::after{bottom:-9px}
.tkt-stub{width:105px;flex:0 0 auto;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;padding:10px 6px;color:#fff;text-align:center;font-weight:900;
  background:linear-gradient(160deg,color-mix(in srgb,var(--tk) 70%,#101418),color-mix(in srgb,var(--tk) 92%,#101418))}
/* Light theme tones are already dark (amber is #854F0B), so the dark-theme
   mix toward near-black turned them muddy. Here the tone itself is the base. */
:root[data-theme="light"] .tkt-stub{background:linear-gradient(160deg,color-mix(in srgb,var(--tk) 82%,#fff),var(--tk))}
:root[data-theme="light"] .bx{background:linear-gradient(140deg,color-mix(in srgb,var(--bx) 80%,#fff),var(--bx))}
.tkt-stub small{font-size:12px;font-weight:700;letter-spacing:.06em;opacity:.9;max-width:100%;line-height:1.2}
.tkt-big{line-height:1.05;letter-spacing:-.01em;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tkt-big .i{width:32px;height:32px}
.tkt-main{flex:1;min-width:0;display:flex;flex-direction:column;justify-content:center;gap:3px;padding:12px 8px 12px 16px;border-left:2px dashed var(--hairline-strong)}
.tkt-t{font-weight:700;font-size:18.5px;line-height:1.28;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.tkt-s{font-size:14.5px;color:var(--ink-soft);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tkt-meter{display:block;height:5px;border-radius:3px;background:var(--surface-sunken);overflow:hidden;margin-top:2px}.tkt-meter i{display:block;height:100%;background:var(--tk)}
.tkt > .i{align-self:center;margin-right:10px;color:var(--ink-faint);flex:0 0 auto}
.tkt-urgent{border-color:color-mix(in srgb,var(--error) 55%,transparent)}
.tkt-done{opacity:.62}.tkt-done .tkt-stub{filter:grayscale(.85)}
.tkt[hidden]{display:none}
@media (max-width:340px){.tkt-stub{width:90px}.tkt::before,.tkt::after{left:82px}.tkt-main{padding-left:11px}.tkt-t{font-size:16.5px}.tkt-s{font-size:13.5px}}
.crow-i{width:48px;height:48px;border-radius:10px;display:flex;align-items:center;justify-content:center;flex:0 0 auto}
.crow .b{font-size:var(--fs-base)}
/* ---- Circle detail sheet, "immersive" (2026-08-27, chosen from three) ----

   The sheet shows the REAL circleCard so every handler is the same one the
   list uses. What changes here is only presentation:

   1. Card chrome is flattened — inside a sheet it was a card drawn on a card.
   2. The hero runs edge to edge and gets real height, so the channel colour
      (or the photo) owns the top and carries the status and title. That band
      already existed; it was just boxed in and short.
   3. The pills become quiet fact ROWS. As pills the location, the ask count
      and the paid flag read as three tappable chips when none of them are.
   4. The action is lifted into the pinned footer by openCirclePost(), so it
      is full width and cannot be pushed below the fold by a long description.

   Scoped to .circlesheet throughout — the list card is untouched. */
.sheet.circlesheet .sb{padding-left:0;padding-right:0;padding-top:0}
.sheet.circlesheet .sb .post.circ{border:none;border-radius:0;margin:0;background:transparent;box-shadow:none}
.sheet.circlesheet .sb .chdr{border-radius:0;min-height:132px;display:flex;flex-direction:column;justify-content:flex-end;padding:var(--s4)}
.sheet.circlesheet .sb .chdr h3{font-size:var(--fs-xl);line-height:1.25}
.sheet.circlesheet .sb .cshot{border-radius:0;min-height:180px}
.sheet.circlesheet .sb .body{padding:var(--s4) var(--s5) 0;color:var(--ink-soft)}
/* facts, not chips */
.sheet.circlesheet .cfacts{flex-direction:column;align-items:stretch;gap:0 !important;padding:var(--s3) var(--s5) var(--s2) !important}
.sheet.circlesheet .cfacts .pill{background:none;border:none;padding:3px 0;color:var(--ink-soft);font-size:var(--fs-sm);border-radius:0}
.sheet.circlesheet .ph{padding:var(--s3) var(--s5) var(--s2)}
.sheet.circlesheet .foot{padding-left:var(--s5);padding-right:var(--s5)}
/* pinned action: full width, pill, primary first */
.sheet.circlesheet .sf{gap:var(--s2)}
.sheet.circlesheet .sf .cactions{flex:1;padding:0 !important;gap:var(--s2)}
.sheet.circlesheet .sf .btn{flex:1;min-height:46px;border-radius:var(--r-pill)}
.sheet.circlesheet .sf .btn.cmain{background:var(--accent);color:var(--accent-ink);border-color:transparent}
.sheet.circlesheet .sf .btn:not(.cmain){background:var(--surface-sunken);color:var(--ink);border-color:var(--hairline)}
/* The 3px tone strip is how a card is identified in a MIXED list. In the
   sheet the hero above it is already the channel colour at full width, so the
   strip is the same information twice — and it ran the whole height of the
   sheet, which read as a stray line rather than a label. */
.sheet.circlesheet .sb .post.circ::before{display:none}
.sheet.circlesheet .sf .small{flex:1 0 100%;text-align:center}

/* ---- journey sheet ---- */
.hbhero{text-align:center;padding:4px 0 18px}
.hbhero h3{font-size:var(--fs-xl);margin:12px 0 2px}
.hbherosub{color:var(--ink-soft);margin:0}
.hbringwrap{position:relative;width:132px;height:132px;margin:0 auto}
.hbring{position:absolute;inset:0;transform:rotate(-90deg)}
.hbring-bg{fill:none;stroke:var(--surface-sunken);stroke-width:10}
.hbring-fg{fill:none;stroke:var(--accent);stroke-width:10;stroke-linecap:round;transition:stroke-dashoffset var(--dur-slow) var(--ease)}
.hbringin{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center}
.hbringn{font-family:var(--font-mono);font-weight:600;font-size:34px;line-height:1;letter-spacing:-.02em}
.hbringd{font-size:16px;color:var(--ink-faint)}
.hbringlbl{font-size:var(--fs-xs);font-weight:600;color:var(--pink);letter-spacing:.06em;text-transform:uppercase;margin-top:2px}
.hbchips{display:flex;gap:8px;justify-content:center;flex-wrap:wrap;margin-top:12px}
/* The path: the connector carries the progress, so the line is the story and
   the nodes are only its punctuation. */
.hbpath{position:relative;padding-left:4px}
.hbstep{display:flex;align-items:center;gap:14px;position:relative;padding:9px 0}
.hbstep::before{content:"";position:absolute;left:21px;top:0;bottom:0;width:3px;border-radius:2px;background:var(--hairline)}
.hbstep:first-child::before{top:50%}
.hbstep:last-child::before{bottom:50%}
.hbstep[data-state="done"]::before{background:var(--gold)}
.hbstep[data-state="now"]::before{background:linear-gradient(180deg,var(--gold),var(--accent) 50%,var(--hairline) 50%)}
.hbnode{width:44px;height:44px;border-radius:50%;flex:0 0 auto;display:flex;align-items:center;justify-content:center;
  position:relative;z-index:1;background:var(--surface);border:2px dashed var(--hairline-strong);color:var(--ink-faint)}
.hbnode .i{width:20px;height:20px}
.hbnode .mono{font-family:var(--font-mono);font-weight:600;font-size:13px}
.hbnode[data-state="done"]{background:var(--gold);border:0;color:var(--accent-ink);box-shadow:var(--shadow-sm)}
.hbnode[data-state="now"]{background:var(--accent);border:0;color:var(--accent-ink);box-shadow:0 0 0 6px var(--accent-wash)}
.hbnode[data-state="open"]{border-style:solid;border-color:var(--hairline-strong);color:var(--ink-soft)}
.hbstep[data-state="locked"]{opacity:.62}
.hbsteph{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.hbstepsub{font-size:var(--fs-xs);color:var(--ink-faint);margin-top:2px}
.hbstepsub.now{color:var(--pink);font-weight:500}
.hbstep .hbbar{margin:6px 0 3px}
.hbcollect{display:flex;gap:8px;flex-wrap:wrap}
.hbchip{display:inline-flex;align-items:center;gap:6px;padding:7px 12px;border-radius:var(--r-pill);
  border:1px solid var(--hairline-strong);background:var(--surface);font-size:var(--fs-sm);font-weight:500;color:var(--ink-faint)}
.hbchip .i{width:14px;height:14px}
.hbchip.on{background:var(--gold-wash);border-color:var(--gold);color:var(--gold-text);font-weight:600}
.hbmedal{width:88px;height:88px;border-radius:50%;margin:0 auto;display:flex;align-items:center;justify-content:center;
  background:var(--gold-wash);border:3px solid var(--gold);color:var(--gold-text)}
.hbmedal .i{width:40px;height:40px}

/* ---- Event photos (2026-09-21, docs/mockups/event-photos-2026-09-21.html) ----
   A celebration post attached to an event: the chip on the card, the "Add to
   an event" row and picker in compose, the Photos tab on the event sheet,
   the albums on the Events tab's Photos segment, and the Celebrate card. */
.evseg{margin-bottom:var(--s3)}
.post .evchip{display:inline-flex;align-items:center;gap:5px;margin:0 var(--s4) var(--s2);height:24px;padding:0 10px;border-radius:var(--r-pill);background:var(--gold-wash);color:var(--gold-text);font-size:var(--fs-xs);font-weight:600;max-width:calc(100% - 2 * var(--s4))}
.post .evchip .i{width:13px;height:13px;flex:0 0 auto}
.post .evchip .ell{min-width:0}
.evpick .li .lead{background:var(--gold-wash);color:var(--gold-text)}
.evpick .li .t{font-weight:600}
.evpick .li .s{font-size:var(--fs-xs)}
.evpick .li .chev{margin-left:auto;font-size:var(--fs-sm);font-weight:600;color:var(--accent-text)}
.evpick.on{border-color:var(--gold)}
.li.evopt .tick{margin-left:auto;width:20px;height:20px;border-radius:50%;border:2px solid var(--ink-muted);flex:0 0 auto}
.li.evopt.on .tick{border-color:var(--accent);background:var(--accent)}
.evs-phead{display:flex;align-items:center;justify-content:space-between;gap:var(--s2);margin-bottom:var(--s3)}
.evs-phead .t{font-size:var(--fs-md);font-weight:600}
.evs-phead .s{font-size:var(--fs-xs);color:var(--ink-faint)}
.evs-phead .btn{flex:0 0 auto}
.evs-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:3px;border-radius:12px;overflow:hidden;background:var(--surface-sunken)}
.evs-grid button{position:relative;aspect-ratio:1;background:var(--surface-sunken);overflow:hidden;display:block}
.evs-grid img{width:100%;height:100%;object-fit:cover;display:block}
.evs-grid .vbadge{position:absolute;right:6px;top:6px;width:22px;height:22px;border-radius:50%;background:rgba(0,0,0,.55);color:#fff;display:flex;align-items:center;justify-content:center}
.evs-grid .vbadge .i{width:12px;height:12px}
.alb{display:flex;gap:10px;width:100%;text-align:left;padding:8px;margin-bottom:var(--s2);border-radius:var(--r-lg);background:var(--surface);border:1px solid var(--card-edge);color:inherit}
.alb:active{transform:scale(.99)}
.alb-col{width:96px;height:96px;flex:0 0 auto;display:grid;grid-template-columns:1fr 1fr;gap:2px;border-radius:12px;overflow:hidden;background:var(--surface-sunken)}
.alb-col.one{grid-template-columns:1fr}
.alb-col img{width:100%;height:100%;object-fit:cover;display:block}
.alb-in{display:flex;flex-direction:column;justify-content:center;gap:2px;min-width:0}
.alb-t{font-size:var(--fs-md);font-weight:600;line-height:1.2;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.alb-m{font-size:var(--fs-xs);color:var(--ink-soft)}
.alb-c{font-size:var(--fs-xs);color:var(--ink-faint);margin-top:2px}
.alb-yr{display:flex;align-items:center;gap:10px;font:600 11px var(--font-mono);letter-spacing:.07em;color:var(--ink-faint);margin:var(--s4) 0 var(--s2)}
.alb-yr::after{content:"";flex:1;height:1px;background:var(--hairline)}
.evspot{display:block;width:100%;text-align:left;border-radius:var(--r-lg);overflow:hidden;background:var(--surface);border:1px solid var(--card-edge);box-shadow:var(--shadow-sm);margin-bottom:12px;color:inherit;cursor:pointer}
/* Option C (2026-09-24): no cover, a 5px edge in the state's colour, text
   actions. The cover band, body and full-width button rules of the first
   card went with it. */
.evspot.c{position:relative;padding:10px 12px 10px 17px;overflow:hidden}
.evspot.c::before{content:"";position:absolute;left:0;top:0;bottom:0;width:5px;background:var(--evc)}
.evspot.c.soon{--evc:var(--accent)}.evspot.c.live{--evc:var(--teal)}.evspot.c.past{--evc:var(--gold)}
.evspot-k{font-size:var(--fs-xs);font-weight:700;color:var(--evc);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.evspot.c.past .evspot-k{color:var(--gold-text)}
.evspot-t{line-height:1.2}
.evspot.c .evspot-t{font-size:var(--fs-md);font-weight:800;margin-top:1px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.evspot.c .evspot-acts{display:flex;align-items:center;gap:16px;margin-top:6px;flex-wrap:nowrap;min-width:0}
.evlink{display:inline-flex;align-items:center;gap:4px;min-height:28px;padding:0;font-size:var(--fs-sm);font-weight:700;color:var(--ink-soft);white-space:nowrap}
.evlink .i{width:15px;height:15px}
.evlink.acc{color:var(--accent-text)}.evlink.on{color:var(--ok)}
.evlink-none{font-size:var(--fs-sm);color:var(--ink-faint)}

/* ---- bug report sheet (2026-09-23) ---- */
.bugstrip{display:grid;grid-template-columns:repeat(auto-fill,minmax(72px,1fr));gap:8px}
.bugth,.bugadd{position:relative;width:100%;aspect-ratio:1;border-radius:var(--r-md);overflow:hidden}
/* Nothing picked yet: the add tile is a full-width drop zone (asked: "use full space"). */
.bugadd:only-child{grid-column:1/-1;aspect-ratio:auto;height:96px;flex-direction:row;gap:8px;font-size:13px}
.bugth{background:var(--surface-sunken)}
.bugth img{width:100%;height:100%;object-fit:cover;display:block}
.bugth.err{outline:2px solid var(--error);outline-offset:-2px}
.bugvid{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:var(--ink-soft)}
.bugvid .i{width:26px;height:26px}
.bugpct{position:absolute;left:0;right:0;bottom:0;padding:3px 0;background:rgba(16,20,24,.7);color:#fff;font-size:12px;font-weight:700;text-align:center}
.bugx{position:absolute;top:4px;right:4px;width:24px;height:24px;border:0;border-radius:50%;background:rgba(16,20,24,.72);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;padding:0}
.bugx .i{width:12px;height:12px}
.bugadd{border:1.5px dashed color-mix(in srgb,var(--accent) 55%,transparent);background:transparent;color:var(--pink);font-family:inherit;font-size:12px;font-weight:700;line-height:1.15;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;padding:4px;text-align:center;cursor:pointer}

/* ---- Steps Journey (2026-09-23) — js/parts/journey.js, Move's first segment,
   the card above the Home feed and the tile on a journey post. ---- */
.jn{display:flex;flex-direction:column;gap:var(--s3)}
.jn-err{display:flex;flex-direction:column;gap:6px;align-items:flex-start}
.jn-kicker{font-size:var(--fs-xs);font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--gold-text)}
.jn-h{font-size:var(--fs-2xl);font-weight:800;line-height:1.12;margin:0}
.jn-lede{color:var(--ink-soft);font-size:var(--fs-md);line-height:1.45;margin:0}
.jn-hero{display:flex;flex-direction:column;gap:12px;padding:18px}
.jn-strip{display:grid;grid-template-columns:auto 1fr auto;gap:10px;align-items:center;margin-top:4px}
.jn-strip-a,.jn-strip-b{display:flex;flex-direction:column;font-size:var(--fs-sm);line-height:1.2}
.jn-strip-b{text-align:right}.jn-strip-a span,.jn-strip-b span{color:var(--ink-soft)}
.jn-strip-line{position:relative;height:16px;display:flex;justify-content:space-between;align-items:center}
.jn-strip-line::before{content:"";position:absolute;left:0;right:0;top:7px;border-top:3px dotted var(--hairline-strong)}
.jn-strip-line i{position:relative;width:9px;height:9px;border-radius:50%;background:var(--surface);border:2px solid var(--ink-faint)}
.jn-strip-line i.on{width:14px;height:14px;background:var(--accent);border-color:var(--accent)}
.jn-strip-line i.end{width:14px;height:14px;background:var(--gold);border-color:var(--gold)}
.jn-stats3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
.jn-stats3>div{display:flex;flex-direction:column;padding:10px;border-radius:var(--r-md);background:var(--surface-sunken);min-width:0}
.jn-stats3.flat>div{background:none;padding:0}
.jn-stats3 b{font-size:var(--fs-lg);font-weight:800;line-height:1.15}.jn-stats3 b.gold{color:var(--gold-text)}
.jn-stats3 span{font-size:var(--fs-xs);color:var(--ink-soft)}
.jn-how{display:flex;flex-direction:column;gap:12px;padding:2px 2px 0}
.jn-howrow{display:flex;gap:12px;align-items:flex-start}
.jn-howic,.jn-nic{width:34px;height:34px;flex:0 0 auto;border-radius:10px;display:flex;align-items:center;justify-content:center;background:var(--surface-sunken)}
.t-accent{color:var(--accent-text)}.t-gold{color:var(--gold-text)}.t-teal{color:var(--teal)}.t-pink{color:var(--pink)}
.jn-start{min-height:50px;font-size:var(--fs-base)}
.jn-nudges{display:flex;gap:10px;overflow-x:auto;scroll-snap-type:x mandatory;scroll-padding:0 var(--s4);margin:0 calc(-1 * var(--s4));padding:2px var(--s4);scrollbar-width:none}
.jn-nudges::-webkit-scrollbar{display:none}
.jn-nudge{flex:0 0 250px;scroll-snap-align:start;display:flex;gap:12px;align-items:flex-start;padding:12px 14px;border-radius:var(--r-lg);background:var(--surface);border:1px solid var(--hairline)}
.jn-nt{font-weight:700;font-size:var(--fs-md);line-height:1.25}.jn-ns{font-size:var(--fs-sm);color:var(--ink-soft);line-height:1.35}
.jn-head{display:flex;justify-content:space-between;align-items:center;font-size:var(--fs-xs);color:var(--ink-soft)}
.jn-head span:first-child{font-weight:700;letter-spacing:.06em;text-transform:uppercase}
.jn-map{position:relative;width:100%;aspect-ratio:358/440;border-radius:var(--r-lg);background:var(--surface-sunken);border:1px solid var(--hairline);overflow:hidden}
.jn-region{position:absolute;font-size:12px;font-weight:700;letter-spacing:.3em;color:var(--hairline-strong);pointer-events:none}
.jn-lines{position:absolute;inset:0;width:100%;height:100%}
.jn-lines polyline{fill:none;stroke-linecap:round;stroke-linejoin:round;vector-effect:non-scaling-stroke}
.jn-todo{stroke:var(--ink-faint);stroke-width:3;stroke-dasharray:1 7}
.jn-done{stroke:var(--accent);stroke-width:4}
.jn-stop{position:absolute;width:30px;height:30px;margin:-15px 0 0 -15px;display:flex;align-items:center;justify-content:center;padding:0}
.jn-stop span{width:10px;height:10px;border-radius:50%;background:var(--surface-sunken);border:2px solid var(--ink-faint);box-sizing:border-box}
.jn-stop.big span{width:13px;height:13px}
.jn-stop.on span{background:var(--accent);border-color:var(--accent)}
.jn-stop.nx span{border-color:var(--gold)}
.jn-stop.sel span{width:16px;height:16px;border-color:var(--ink)}
.jn-lab{position:absolute;margin:-7px 0 0 14px;width:110px;font-size:12px;line-height:14px;color:var(--ink-soft);pointer-events:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.jn-lab.l{margin-left:-124px;text-align:right}
.jn-lab.on{color:var(--ink);font-weight:700}.jn-lab.nx{color:var(--gold-text);font-weight:700}
.jn-mate{position:absolute;display:flex;padding:4px;margin:-15px 0 0 4px;border-radius:50%}.jn-mate.w{margin-left:-36px}
.jn-mate .av{width:22px;height:22px;font-size:9px;border:2px solid var(--surface-sunken)}
.jn-me{position:absolute;display:flex;flex-direction:column;align-items:center;transform:translate(-50%,-100%);margin-top:-4px;z-index:2}
.jn-me .av{border:2px solid var(--accent);box-shadow:var(--shadow-sm)}
.jn-me i{width:0;height:0;border-left:6px solid transparent;border-right:6px solid transparent;border-top:8px solid var(--accent)}
/* Near view + overview inset (2026-09-23, option B). Placed elements carry
   .off when they fall outside the near window; regions show on the whole
   route only, ticks / distance labels on the near view only. */
.jn-map .off{visibility:hidden}
.jn-map.near .jn-region{display:none}
.jn-tick{position:absolute;width:6px;height:6px;margin:-3px 0 0 -3px;border-radius:50%;background:var(--ink-faint);pointer-events:none;display:none}
.jn-tick.on{background:var(--accent)}
.jn-tick i{position:absolute;left:8px;top:-13px;font-style:normal;font-size:9px;line-height:12px;color:var(--ink-faint);white-space:nowrap}
.jn-map.near .jn-tick{display:block}
.jn-matelab{position:absolute;margin:-7px 0 0 24px;font-size:12px;line-height:14px;color:var(--ink-soft);white-space:nowrap;pointer-events:none;display:none}
.jn-matelab.w{margin-left:-24px;transform:translateX(-100%)}
.jn-map.near .jn-matelab{display:block}
.jn-melab{position:absolute;transform:translate(-50%,-58px);font-size:12px;line-height:14px;font-weight:700;color:var(--ink);white-space:nowrap;pointer-events:none;display:none;z-index:2}
.jn-map.near .jn-melab{display:block}
.jn-inset{position:absolute;right:10px;top:10px;width:92px;height:116px;border-radius:12px;border:1px solid var(--hairline);background:var(--surface);box-shadow:var(--shadow-sm);padding:4px 4px 0;overflow:hidden;z-index:3;display:flex;flex-direction:column;gap:2px}
.jn-inset svg{flex:1;width:100%;min-height:0}
.jn-inset polyline{fill:none;stroke-linecap:round;stroke-linejoin:round}
.jn-itodo{stroke:var(--ink-faint);stroke-width:6;stroke-dasharray:6 10}
.jn-idone{stroke:var(--accent);stroke-width:9}
.jn-idot{fill:var(--surface);stroke:var(--ink-faint);stroke-width:4}.jn-idot.on{fill:var(--accent);stroke:var(--accent)}
.jn-ibox{fill:none;stroke:var(--gold);stroke-width:6;rx:10}
.jn-ime{fill:var(--accent);stroke:var(--surface);stroke-width:4}
.jn-inset span{font-size:10px;line-height:12px;color:var(--ink-soft);text-align:center;padding-bottom:4px}
.jn-mapfoot{position:absolute;right:10px;bottom:10px;background:var(--surface);border:1px solid var(--hairline);border-radius:10px;padding:5px 10px;font-size:var(--fs-xs);color:var(--ink-soft);z-index:3;box-shadow:var(--shadow-sm)}
.jn-mapfoot b{color:var(--ink)}
.jn-sel{display:flex;gap:12px;align-items:center;text-align:left;width:100%;color:var(--ink)}
.jn-selart,.jn-rowart{flex:0 0 auto;width:44px;height:44px;border-radius:12px;display:flex;align-items:center;justify-content:center;background:var(--pink-wash);color:var(--pink)}
.jn-rowart{width:36px;height:36px;border-radius:10px}
.jn-selline{font-size:var(--fs-xs);font-weight:600;color:var(--gold-text)}
.jn-prog{display:flex;flex-direction:column;gap:10px}
.jn-bar{height:8px;border-radius:var(--r-pill);background:var(--surface-sunken);overflow:hidden}
.jn-bar i{display:block;height:100%;border-radius:var(--r-pill);background:var(--accent)}
.jn-sect{font-size:var(--fs-xs);font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-soft);margin:6px 2px -4px}
.jn-list{padding:0;overflow:hidden}
.jn-row{display:flex;gap:12px;align-items:center;padding:12px 14px;width:100%;text-align:left;color:var(--ink)}
.jn-list .jn-row+.jn-row{border-top:1px solid var(--hairline)}
.jn-rowkm{display:flex;flex-direction:column;align-items:flex-end;flex:0 0 auto}
.jn-rowkm span{font-size:var(--fs-xs);color:var(--ink-soft)}
.jn-foot{margin-top:4px}
.jn-leave{align-self:center;min-height:44px;padding:0 12px;color:var(--ink-soft);font-size:var(--fs-sm);text-decoration:underline}
.jn-stopsheet{display:flex;flex-direction:column;gap:12px}
.jn-art{height:150px;border-radius:var(--r-lg);background:var(--pink-wash);color:var(--pink);display:flex;align-items:center;justify-content:center}
.jn-chip{padding:3px 10px;border-radius:var(--r-pill);background:var(--gold-wash);color:var(--gold-text);font-size:var(--fs-xs);font-weight:700;white-space:nowrap}
.jn-chip.ok{background:var(--accent-wash);color:var(--accent-text)}
.jn-fact{margin:0;color:var(--ink-soft);line-height:1.45}
.jn-there{display:flex;flex-direction:column;gap:2px}.jn-there .jn-row{padding:8px 0}
.jn-reach{position:relative;display:flex;flex-direction:column;align-items:center;text-align:center;gap:6px;padding-top:8px}
.jn-confetti{position:absolute;inset:0 0 auto;height:180px;pointer-events:none}
.jn-confetti i{position:absolute;width:10px;height:4px;border-radius:2px}
.jn-confetti i.c0{background:var(--accent)}.jn-confetti i.c1{background:var(--gold)}.jn-confetti i.c2{background:var(--teal)}.jn-confetti i.c3{background:var(--pink)}
.jn-confetti i:nth-child(1){left:8%;top:20px;transform:rotate(30deg)}.jn-confetti i:nth-child(2){left:22%;top:90px;transform:rotate(-20deg)}
.jn-confetti i:nth-child(3){left:84%;top:14px;transform:rotate(-30deg)}.jn-confetti i:nth-child(4){left:90%;top:100px;transform:rotate(50deg)}
.jn-confetti i:nth-child(5){left:14%;top:150px;transform:rotate(70deg)}.jn-confetti i:nth-child(6){left:78%;top:160px;transform:rotate(-60deg)}
.jn-confetti i:nth-child(7){left:48%;top:0;transform:rotate(10deg)}.jn-confetti i:nth-child(8){left:34%;top:40px;transform:rotate(80deg)}
.jn-confetti i:nth-child(9){left:66%;top:60px;transform:rotate(-80deg)}
.jn-badge{width:120px;height:120px;border-radius:50%;border:3px solid var(--gold);background:var(--surface-sunken);color:var(--gold-text);display:flex;align-items:center;justify-content:center;margin:14px 0 8px}
.jn-rk{font-weight:600;color:var(--ink-soft)}
.jn-rname{font-size:40px;font-weight:800;line-height:1.05}
.jn-rline{font-size:var(--fs-sm);font-weight:600;color:var(--gold-text)}
.jn-rfact{margin:4px 0 8px;color:var(--ink-soft);line-height:1.45;max-width:320px}
.jn-reach .jn-stats3{width:100%}
.jn-nextcard{display:flex;gap:12px;align-items:center;text-align:left;width:100%;margin-top:6px}
.jn-posted{display:flex;gap:6px;align-items:center;justify-content:center;font-size:var(--fs-sm);font-weight:600;color:var(--ok);margin-top:6px}
.sheet.jn-reached .sh h2{visibility:hidden}
.jn-fin{display:flex;flex-direction:column;align-items:center;gap:8px;padding:22px 18px;border:2px solid var(--gold)}
.jn-finic{width:80px;height:80px;border-radius:50%;background:var(--gold-wash);color:var(--gold-text);display:flex;align-items:center;justify-content:center}
.jn-fin .jn-stats3{width:100%;margin-top:8px}
.jn-nextroute{display:flex;gap:12px;align-items:center}
/* Home card, option G (2026-09-23): destination on top, the road, today's ring under a rule. */
.jn-home{display:flex;flex-direction:column;gap:10px;width:100%;text-align:left;margin-bottom:12px;color:var(--ink);border-color:color-mix(in srgb,var(--accent) 30%,var(--hairline))}
.jn-homehd{display:flex;align-items:center;gap:14px}
.jn-homeart{width:56px;height:56px;flex:0 0 auto;border-radius:16px;background:var(--gold-wash);color:var(--gold-text);display:flex;align-items:center;justify-content:center}
.jn-home .jn-kicker{margin:0}
.jn-homet{font-size:var(--fs-xl);font-weight:800;line-height:1.15}
.jn-homebar{position:relative;height:14px;margin:2px 6px 0}
.jn-homebar::before{content:"";position:absolute;left:0;right:0;top:5px;height:4px;border-radius:2px;background:var(--surface-sunken)}
.jn-homefill{position:absolute;left:0;top:5px;height:4px;border-radius:2px;background:var(--accent)}
.jn-homebar i{position:absolute;top:2px;width:10px;height:10px;margin-left:-5px;border-radius:50%;background:var(--surface);border:2px solid var(--ink-faint);box-sizing:border-box}
.jn-homebar i.on{background:var(--accent);border-color:var(--accent)}.jn-homebar i.nx{border-color:var(--gold)}
/* the compact running card, option B (2026-09-24): ~120px, was ~200 */
.jn-home.cmp{gap:9px;padding:12px 14px}
/* pink side stripe like the event card's (2026-09-25); the card is display:flex, so the stripe is absolute */
.jn-home.cmp{position:relative;overflow:hidden;padding-left:19px}
.jn-home.cmp::before{content:"";position:absolute;left:0;top:0;bottom:0;width:5px;background:var(--accent)}
.jn-home.cmp .jn-homehd{gap:10px}
.jn-home.cmp .jn-homeart{width:40px;height:40px;border-radius:12px}
/* 12px since the 2026-09-25 readability audit (was 10.5px). One line no
   longer fits "Walking to India Gate · Day 2" under 390px, so it may take two
   lines rather than lose the day number to an ellipsis. */
.jn-home.cmp .jn-kicker{font-size:12px;line-height:1.25;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.jn-home.cmp .jn-homet{font-size:var(--fs-lg);line-height:1.15}
.jn-home.cmp .jn-homet span{font-size:var(--fs-sm);font-weight:600;color:var(--ink-soft)}
.jn-hometd{display:flex;flex-direction:column;align-items:flex-end;flex:0 0 auto;line-height:1.2}
.jn-hometd b{color:var(--accent-text);font-size:var(--fs-base);font-weight:800}
.jn-hometd span{font-size:var(--fs-xs);color:var(--ink-soft)}
.jn-home.cmp .jn-homebar{margin:0 6px}
.jn-home.cmp .jn-homelabs{margin-top:-2px}
/* the start card (2026-09-24): the running card's frame, big stops ringed gold */
.jn-start{cursor:pointer}
.jn-start .jn-homehd{align-items:flex-start}
.jn-startx{width:32px;height:32px;margin:-6px -6px 0 0;flex:0 0 auto}
.jn-startp{margin:0;color:var(--ink-soft)}
.jn-startft{display:flex;align-items:center;gap:10px}
.jn-startft .btn{display:inline-flex;align-items:center;gap:2px;pointer-events:none}
.jn-homebar i.me{top:0;width:14px;height:14px;margin-left:-7px;background:var(--surface);border:3px solid var(--accent)}
.jn-homelabs{display:flex;justify-content:space-between;gap:8px;margin-top:-4px;font-size:var(--fs-xs);color:var(--ink-soft)}
.jn-homelabs .nx{color:var(--gold-text);font-weight:700;text-align:center;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* ==== Steps Journey, version 2 (2026-09-23): pass, footprints, trail, stamps, passport ==== */
/* the boarding pass */
.jnp{position:relative;background:var(--surface);border:1px solid var(--hairline);border-radius:22px;overflow:hidden;box-shadow:var(--shadow-sm)}
.jnp-top{padding:18px;display:flex;flex-direction:column;gap:14px}
.jnp-ends{display:flex;justify-content:space-between;align-items:flex-end;gap:8px}
.jnp-ends .r{text-align:right}
.jnp-l{font-size:12px;letter-spacing:.08em;color:var(--ink-soft)}
.jnp-code{font-size:30px;font-weight:800;line-height:1}
.jnp-city{font-size:var(--fs-sm);color:var(--ink-soft)}
.jnp-dash{flex:1;display:flex;align-items:center;gap:6px;padding-bottom:18px;color:var(--accent-text)}
.jnp-dash span{flex:1;border-top:2px dashed var(--hairline-strong)}
.jnp-facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
.jnp-v{font-size:var(--fs-md);font-weight:800}
.jnp-stub{position:relative;border-top:2px dashed var(--hairline);padding:16px 18px;display:flex;flex-direction:column;gap:10px;background:var(--paper)}
.jnp-stub::before,.jnp-stub::after{content:"";position:absolute;top:-12px;width:24px;height:24px;border-radius:50%;background:var(--sand)}
.jnp-stub::before{left:-12px}.jnp-stub::after{right:-12px}
.jnp-paces{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
.jnp-pace{min-height:52px;border-radius:14px;border:2px solid var(--hairline);background:var(--surface);color:var(--ink);display:flex;flex-direction:column;align-items:center;justify-content:center;line-height:1.15}
.jnp-pace b{font-size:var(--fs-md)}.jnp-pace span{font-size:12px;color:var(--ink-soft)}
.jnp-pace[aria-checked="true"]{border-color:var(--accent);background:var(--accent-wash)}
.jnp-plan{font-size:var(--fs-sm);color:var(--ink-soft);line-height:1.4}
.jnp-boarded{position:absolute;right:14px;top:62px;color:var(--stamp);animation:jnStamp .45s cubic-bezier(.2,1.6,.4,1) both;pointer-events:none}
@keyframes jnStamp{from{transform:scale(1.8);opacity:0}to{transform:scale(1);opacity:1}}
/* a stamp */
.jn-stampsvg{color:var(--stamp);display:block}
.jn-stampsvg circle{fill:none;stroke:currentColor}
.jn-stampsvg .r1{stroke-width:3}.jn-stampsvg .r2{stroke-width:1.5;stroke-dasharray:3 3}
.jn-stampsvg text{fill:currentColor;font-family:inherit}
.jn-stampsvg.faded{opacity:.3;color:var(--ink-faint)}
.jn-stampsvg.dest{color:var(--accent-text)}
.jnf{display:flex;flex-direction:column;gap:12px}
.jnf-hd{display:flex;justify-content:space-between;align-items:center;gap:8px}
/* Steps Today on the journey tab, option G (2026-09-23): numbers, ten clean blocks, the km toward the next stop */
.jnf-pct{font-size:var(--fs-xs);font-weight:700;color:var(--pink)}
.jnf-row{display:flex;justify-content:space-between;align-items:flex-end;gap:10px}
.jnf-num{display:flex;align-items:baseline;gap:2px;color:var(--ink-soft);font-size:var(--fs-md);font-weight:600}
.jnf-num b{font-size:44px;font-weight:800;line-height:.95;color:var(--ink)}
.jnf-go{display:flex;flex-direction:column;align-items:flex-end;text-align:right;line-height:1.15}
.jnf-go b{font-size:var(--fs-xl);font-weight:800;color:var(--gold-text)}.jnf-go span{font-size:var(--fs-xs);color:var(--ink-soft)}
.jnf-blocks{display:grid;gap:4px}
.jnf-block{position:relative;height:10px;border-radius:5px;background:var(--surface-sunken);overflow:hidden}
.jnf-block.on{background:var(--accent)}
.jnf-block i{position:absolute;left:0;top:0;bottom:0;background:var(--accent);border-radius:5px}
.jnf-toward{display:flex;align-items:center;gap:8px;font-size:var(--fs-sm);color:var(--ink-soft)}
.jnf-toward b{color:var(--ink)}
.jnf-art{display:inline-flex;color:var(--gold-text)}
/* the trail */
.jnt-zoom{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:4px;background:var(--surface-sunken);border-radius:14px;padding:4px}
.jnt-z{min-height:40px;border-radius:10px;color:var(--ink-soft);font-size:var(--fs-sm);font-weight:700}
.jnt-z[aria-selected="true"]{background:var(--surface);color:var(--ink);box-shadow:var(--shadow-sm)}
.jnt{position:relative;width:100%;aspect-ratio:358/440;border-radius:22px;background:linear-gradient(180deg,var(--paper) 0%,var(--surface) 100%);border:1px solid var(--hairline);overflow:hidden}
.jnt-svg{position:absolute;inset:0;width:100%;height:100%}
.jnt-svg polyline{fill:none;stroke-linecap:round;stroke-linejoin:round;vector-effect:non-scaling-stroke}
.jnt-road{stroke:color-mix(in srgb,var(--gold) 28%,var(--surface-sunken));stroke-width:22}
.jnt-mid{stroke:var(--surface);stroke-width:2;stroke-dasharray:6 10}
.jnt-done{stroke:var(--accent);stroke-width:8}
.jnt-tick{position:absolute;margin:-7px 0 0 18px;font-size:10px;color:var(--ink-faint);white-space:nowrap;pointer-events:none}
/* Whole route: every stop without a pill is a dot on the road (2026-09-24) */
.jnt-stop{position:absolute;width:24px;height:24px;margin:-12px 0 0 -12px;display:flex;align-items:center;justify-content:center;border-radius:50%;z-index:1}
.jnt-stop i{width:9px;height:9px;border-radius:50%;background:var(--surface);border:2px solid var(--ink-faint)}
.jnt-stop.on i{background:var(--accent);border-color:var(--accent)}
.jnt-stopl{position:absolute;margin:-8px 0 0 12px;font-size:12px;line-height:16px;color:var(--ink-soft);white-space:nowrap;pointer-events:none}
.jnt-stopl.l{transform:translateX(-100%);margin-left:-12px}
.jnt-stopl.on{color:var(--accent-text)}
.jnt-stop.big i{width:12px;height:12px;border-color:var(--ink)}
.jnt-stop.big.on i{border-color:var(--accent)}
.jnt-stopl.big{font-weight:800;color:var(--ink)}
.jnt-stopl.big.on{color:var(--accent-text)}
/* a milestone: the compact chip, option A of 2026-09-24 (was a 40px pill) */
.jnt-post{position:absolute;display:flex;align-items:center;gap:5px;height:22px;margin:-11px 0 0 12px;padding:0 8px 0 6px;border-radius:8px;border:1px solid var(--hairline);background:var(--surface);color:var(--ink);font-size:12px;font-weight:700;white-space:nowrap;z-index:2;box-shadow:var(--shadow-sm)}
.jnt-post.l{transform:translateX(-100%);margin-left:-12px}
.jnt-post i{width:6px;height:6px;border-radius:50%;background:var(--ink-faint);flex:0 0 auto}
.jnt-post em{font-style:normal;font-weight:600;opacity:.85}
/* Full strength on the pink next-stop chip (2026-09-25 audit): at .85 the
   dark ink fell to 4.1:1 on --accent; at 1 it is 4.7:1. */
.jnt-post.nx em{opacity:1}
.jnt-post.on{color:var(--accent-text)}.jnt-post.on i{background:var(--accent)}
.jnt-post.nx{background:var(--accent);border-color:var(--accent);color:var(--accent-ink);z-index:3}.jnt-post.nx i{background:var(--accent-ink)}
.jnt-post.sel:not(.nx){border-color:var(--gold);box-shadow:0 0 0 2px color-mix(in srgb,var(--gold) 35%,transparent)}
.jnt-post.nx.sel{box-shadow:0 0 0 2px color-mix(in srgb,var(--accent) 35%,transparent)}
.jnt-tick.l{transform:translateX(-100%);margin-left:-18px}
.jnt-mate{position:absolute;display:flex;padding:4px;margin:-15px 0 0 -52px;border-radius:50%;z-index:1}
.jnt-mate.r{margin-left:22px}
.jnt-mate .av{width:26px;height:26px;font-size:10px;border:2px solid var(--surface)}
.jnt-me{position:absolute;display:flex;margin:-24px 0 0 -24px;padding:2px;border-radius:50%;z-index:2}
.jnt-me .av{width:44px;height:44px;font-size:16px;box-shadow:0 0 0 3px var(--surface),0 0 0 6px var(--accent),var(--shadow-sm)}
.jnt-win,.jnt-km{position:absolute;background:color-mix(in srgb,var(--surface) 90%,transparent);border:1px solid var(--hairline);border-radius:10px;padding:5px 10px;font-size:var(--fs-xs);color:var(--ink-soft);z-index:3}
.jnt-win{left:10px;top:10px}.jnt-km{right:10px;bottom:10px}.jnt-km b{color:var(--ink)}
.jnt-sel{display:flex;gap:14px;align-items:center;text-align:left;width:100%;color:var(--ink)}
.jnt-selstamp{flex:0 0 auto;display:flex}
.jnt-selk{font-size:12px;letter-spacing:.08em;font-weight:700;text-transform:uppercase;color:var(--gold-text)}
.jnt-selk.ok{color:var(--stamp)}
.jnt-pass{display:flex;gap:8px;align-items:center;justify-content:center}
/* the passport */
.jnpp-body{display:flex;flex-direction:column;gap:12px}
.jnpp-hd{display:flex;justify-content:space-between;align-items:center}
.jnpp-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;background:var(--paper);border:1px solid var(--hairline);border-radius:20px;padding:14px}
.jnpp-slot{aspect-ratio:1;border-radius:14px;border:2px dashed var(--hairline);display:flex;align-items:center;justify-content:center;padding:2px;color:var(--ink-faint)}
.jnpp-slot.on{border:2px solid transparent}
.jnpp-slot.nx{border-color:var(--gold);background:var(--gold-wash);color:var(--gold-text)}
.jnpp-slot.sel{border-color:var(--accent);border-style:solid}
.jnpp-slot .jn-stampsvg{width:100%;height:100%}
.jnpp-empty{display:flex;flex-direction:column;align-items:center;gap:2px;font-size:12px;text-transform:uppercase;letter-spacing:.06em;text-align:center}
.jnpp-empty b{font-size:12px}
.jnpp-detail{display:flex;flex-direction:column;gap:4px}
.jnpp-name{font-size:var(--fs-xl);font-weight:800}
.jnpp-diary p{margin:6px 0 0;line-height:1.45}
.jnpp-all{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:4px;background:var(--paper)}
.jnpp-all span{display:flex;justify-content:center}
.jn-mapview{display:flex;flex-direction:column;gap:inherit}
/* the Ladder: one line to scale, names in one column, teammates on the right */
.jnl{position:relative;border-radius:22px;background:linear-gradient(180deg,var(--paper) 0%,var(--surface) 100%);border:1px solid var(--hairline);overflow:hidden}
/* overscroll auto, not contain (2026-09-24, "When the user reaches the bottom
   of the Ladder card and continues scrolling down, the entire page should
   continue scrolling"): at either end the scroll hands on to the page */
.jnl-box{height:min(470px,62svh);overflow-y:auto;overscroll-behavior-y:auto;overscroll-behavior-x:contain;scrollbar-width:none}
.jnl-box::-webkit-scrollbar{display:none}
.jnl-in{position:relative}
.jnl-svg{position:absolute;left:0;top:0;overflow:visible}
.jnl-tick{stroke:var(--hairline);stroke-width:1}
.jnl-kml{font:500 9px var(--font-mono);fill:var(--ink-faint)}.jnl-kml.odd{opacity:.55}
.jnl-ahead{stroke:var(--ink-faint);stroke-width:3;stroke-dasharray:1 6;stroke-linecap:round}
.jnl-done{stroke:var(--accent);stroke-width:4;stroke-linecap:round}
.jnl-dot{fill:var(--surface);stroke:var(--ink-faint);stroke-width:1.8}
.jnl-dot.big{stroke:var(--ink);stroke-width:2.5}
.jnl-dot.on{fill:var(--accent);stroke:var(--accent)}
.jnl-dot.nx{stroke:var(--accent);stroke-dasharray:2.5 2}
.jnl-dot.sel{stroke:var(--gold);stroke-width:3;stroke-dasharray:none}
.jnl-lead{stroke:var(--hairline);stroke-width:1;fill:none}.jnl-lead.me{stroke:var(--accent);stroke-width:1.2}
.jnl-sidestub{stroke:var(--gold);stroke-width:1.5;stroke-dasharray:2 2;fill:none;opacity:.75}
.jnl-diamond{fill:var(--surface);stroke:var(--gold);stroke-width:1.3}
.jnl-mateline{stroke:var(--hairline);stroke-width:1;stroke-dasharray:2 4}
.jnl-name,.jnl-tag{position:absolute;left:80px;transform:translateY(-50%);white-space:nowrap;max-width:calc(100% - 172px);overflow:hidden;text-overflow:ellipsis;line-height:1.15;text-align:left}
.jnl-name{padding:2px 4px;border-radius:6px;font-size:12.5px;color:var(--ink-soft)}
.jnl-name i{font-style:normal;font-size:12px;font-weight:700;color:var(--accent-text);margin-left:5px}
.jnl-name.big{font-size:14px;font-weight:800;color:var(--ink)}
.jnl-name.on,.jnl-name.big.on{color:var(--accent-text)}
.jnl-name.nx{color:var(--ink);font-weight:700}
.jnl-name.sel{background:var(--gold-wash);color:var(--gold-text)}
.jnl-name.side{font-size:12px;font-style:italic;color:var(--gold-text)}
.jnl-name.side i{color:inherit;opacity:.75}
.jnl-tag{left:84px;padding:2px 8px;border-radius:var(--r-pill);background:var(--accent);color:var(--accent-ink);font-size:12px;font-weight:700}
.jnl-tag i{font-style:normal;font-weight:600;opacity:.85;margin-left:5px}
.jnl-mate{position:absolute;right:10px;transform:translateY(-50%);display:flex;align-items:center;gap:6px;max-width:92px;font-size:12px;color:var(--ink-soft)}
.jnl-mate > span:not(.av){overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.jnl-mate .av{width:24px;height:24px;font-size:9.5px;flex:0 0 auto;box-shadow:0 0 0 2px var(--surface)}
.jnl-me{position:absolute;transform:translate(-50%,-50%);display:flex;border-radius:50%;z-index:2}
.jnl-me .av{width:32px;height:32px;font-size:11px;box-shadow:0 0 0 3px var(--surface),0 0 0 5px var(--accent)}
.jnl-fade{position:absolute;left:0;right:0;height:24px;pointer-events:none;z-index:3}
.jnl-fade.t{top:0;background:linear-gradient(var(--paper),transparent)}
.jnl-fade.b{bottom:0;background:linear-gradient(transparent,var(--surface))}
/* the passport strip under the map (P2) */
.jnps{display:flex;flex-direction:column;gap:10px}
.jnps-hd{display:flex;justify-content:space-between;align-items:center;gap:8px}
.jnps-all{display:inline-flex;align-items:center;gap:2px;min-height:32px;font-size:var(--fs-sm);color:var(--ink-soft)}
.jnps-all span{color:var(--accent-text);font-weight:600}
.jnps-strip{position:relative;display:flex;gap:8px;overflow-x:auto;scroll-snap-type:x proximity;scrollbar-width:none;overscroll-behavior-x:contain;margin:0 -4px;padding:2px 4px}
.jnps-strip::-webkit-scrollbar{display:none}
.jnps-item{flex:0 0 68px;display:flex;flex-direction:column;align-items:center;gap:4px;scroll-snap-align:center}
.jnps-disc,.jnps-empty{width:62px;height:62px;border-radius:50%;display:flex;align-items:center;justify-content:center}
.jnps-disc{background:var(--pp-paper);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--pp-rule) 40%,transparent)}
.jnps-empty{border:1.5px dashed var(--hairline);color:var(--ink-faint)}
.jnps-item.nx .jnps-empty{border-color:var(--accent);color:var(--accent-text);background:color-mix(in srgb,var(--accent) 8%,transparent)}
.jnps-l{max-width:68px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;color:var(--ink-soft)}
.jnps-item.nx .jnps-l{color:var(--accent-text);font-weight:700}
/* an ink stamp: its own colour and one of four shapes */
.jn-ink{display:block;font-family:var(--font-mono);opacity:.92}
.jn-ink .o,.jn-ink .i{fill:none;stroke:currentColor}
.jn-ink .o{stroke-width:3}.jn-ink .i{stroke-width:1.2}.jn-ink .dash{stroke-dasharray:2 2.5}
.jn-ink .ic{fill:none;stroke:currentColor;stroke-width:3.2;stroke-linecap:round;stroke-linejoin:round}
.jn-ink text{fill:currentColor;letter-spacing:.5px}
.jn-ink.k0{color:var(--pp-ink-0)}.jn-ink.k1{color:var(--pp-ink-1)}.jn-ink.k2{color:var(--pp-ink-2)}
.jn-ink.k3{color:var(--pp-ink-3)}.jn-ink.k4{color:var(--pp-ink-4)}.jn-ink.k5{color:var(--pp-ink-5)}
/* the daily passport stamps card on Home (option B, 2026-09-24): a slice of
   the passport page, cream in both themes like the booklet */
.jsd{padding:12px;display:flex;flex-direction:column;gap:12px}
.jsd-page{position:relative;border-radius:14px;padding:12px 14px 12px;background-color:var(--pp-paper);color:var(--pp-text);
  background-image:repeating-linear-gradient(0deg,transparent 0 23px,color-mix(in srgb,var(--pp-rule) 16%,transparent) 23px 24px)}
.jsd-k{display:flex;align-items:center;gap:8px;font:600 9.5px var(--font-mono);letter-spacing:.14em;text-transform:uppercase;color:var(--pp-rule)}
.jsd-k span:first-child{flex:1;min-width:0}
.jsd-more{width:28px;height:28px;margin:-6px -8px -6px 0;color:var(--pp-rule)}
.jsd-t{margin:4px 0 10px;font-size:var(--fs-lg);font-weight:800;line-height:1.2;color:var(--pp-text)}
.jsd-stamps{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px 6px}
.jsd-st{display:flex;flex-direction:column;align-items:center;gap:4px;min-width:0}
.jsd-ink{display:block;width:84px;height:84px}.jsd-ink .jn-ink{width:100%;height:100%}
.jsd-who{display:flex;align-items:center;gap:5px;max-width:100%;font-size:var(--fs-xs);color:var(--pp-text)}
.jsd-who .av{width:22px;height:22px;font-size:9px;flex:0 0 auto}
.jsd-who b{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.jsd-who em{font-style:normal;font-weight:600;color:var(--pp-rule)}
.jsd-more-l{position:relative;margin:10px 0 0;font-size:var(--fs-sm);color:color-mix(in srgb,var(--pp-text) 80%,transparent)}
.jsd-cta .btn{display:flex;align-items:center;justify-content:center;gap:4px}
/* option B (2026-09-25): two columns, one big centred stamp for one walker,
   the place on the route in mono, and an outline button */
.jsd-stamps{grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 8px;padding-top:2px}
.jsd-where{max-width:100%;font-size:var(--fs-xs);line-height:1.25;text-align:center;color:color-mix(in srgb,var(--pp-text) 72%,transparent);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.jsd-solo{display:flex;flex-direction:column;align-items:center;gap:6px;padding:6px 0 2px}
.jsd-ink.big{width:124px;height:124px}
.jsd-who.big{font-size:var(--fs-md);gap:8px}.jsd-who.big .av{width:28px;height:28px;font-size:11px}
.jsd-at{font:600 11px var(--font-mono);letter-spacing:.08em;text-transform:uppercase;color:color-mix(in srgb,var(--pp-text) 72%,transparent)}
.jsd.solo .jsd-t{text-align:center}
.jsd-open{background:transparent;border:1.5px solid var(--accent);color:var(--pink)}
.jsd-open:active{background:color-mix(in srgb,var(--accent) 12%,transparent)}
/* the booklet (P3) */
.jnpb{position:relative;border-radius:12px;overflow:hidden;box-shadow:var(--shadow-md)}
.jnpb-pages{display:flex;overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:none;overscroll-behavior-x:contain}
.jnpb-pages::-webkit-scrollbar{display:none}
.jnpb-page{flex:0 0 100%;scroll-snap-align:start;scroll-snap-stop:always;min-height:380px;padding:12px 12px 26px;background-color:var(--pp-paper);color:var(--pp-text);
  background-image:repeating-radial-gradient(circle at 120% -20%,color-mix(in srgb,var(--pp-cover) 7%,transparent) 0 1px,transparent 1px 9px)}
.jnpb-ph{display:flex;justify-content:space-between;gap:8px;margin-bottom:10px;padding-bottom:6px;border-bottom:1px solid color-mix(in srgb,var(--pp-rule) 40%,transparent);font:600 9.5px var(--font-mono);letter-spacing:.14em;color:var(--pp-rule)}
.jnpb-holder{display:flex;gap:10px;align-items:center;margin-bottom:10px}
.jnpb-holder .av{width:48px;height:58px;border-radius:6px;flex:0 0 auto}
.jnpb-f{display:flex;flex-direction:column;min-width:0;font:500 10px/1.5 var(--font-mono);color:var(--pp-rule)}
.jnpb-f b{font:800 13px var(--font-sans);letter-spacing:.02em;color:var(--pp-text)}
.jnpb-slots{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px 4px}
.jnpb-slot{aspect-ratio:1;display:flex;align-items:center;justify-content:center;border-radius:10px;border:2px solid transparent}
.jnpb-slot .jn-ink{width:94%;height:94%}
.jnpb-slot.sel{border-color:color-mix(in srgb,var(--pp-accent) 55%,transparent)}
.jnpb-empty{width:84%;aspect-ratio:1;border-radius:50%;border:1.5px dashed color-mix(in srgb,var(--pp-rule) 50%,transparent);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;padding:6px;overflow:hidden;text-align:center;font:700 8.5px/1.25 var(--font-mono);letter-spacing:.03em;color:var(--pp-rule)}
.jnpb-empty b{font-weight:700;overflow-wrap:anywhere}
.jnpb-empty i{font-style:normal;font-weight:500}
.jnpb-slot.big .jnpb-empty{border-color:color-mix(in srgb,var(--gold) 75%,var(--pp-rule))}
.jnpb-slot.nx .jnpb-empty{border:2px dashed var(--pp-accent);color:var(--pp-accent);animation:jnBreathe 2.4s ease-in-out infinite}
@keyframes jnBreathe{50%{transform:scale(1.05)}}
@media (prefers-reduced-motion: reduce){.jnpb-slot.nx .jnpb-empty{animation:none}}
.jnpb-dots{position:absolute;left:0;right:0;bottom:9px;display:flex;justify-content:center;gap:5px;pointer-events:none}
.jnpb-dots i{width:6px;height:6px;border-radius:50%;background:color-mix(in srgb,var(--pp-rule) 35%,transparent);animation:none}
.jnpb-dots i.on{background:var(--pp-cover)}
.jnpb-detail{display:flex;gap:12px;align-items:center}
.jnpb-dart{width:44px;height:44px;border-radius:12px;flex:0 0 auto;display:flex;align-items:center;justify-content:center;background:var(--surface-sunken);color:var(--ink-soft)}
.jnpb-dart.on{background:var(--accent);color:var(--accent-ink)}
.jn-fincard{display:flex;gap:14px;align-items:center;border-color:var(--gold)}
.jn-selart.gold{background:var(--gold-wash);color:var(--gold-text);width:60px;height:60px;border-radius:16px}
/* a stamp collected */
.jnr-page{position:relative;width:240px;height:230px;background:var(--paper);border:1px solid var(--hairline);border-radius:14px;box-shadow:var(--shadow-sm);display:flex;align-items:center;justify-content:center;margin:8px 0}
.jnr-pl{position:absolute;left:14px;top:10px;font-size:12px;letter-spacing:.1em;color:var(--ink-faint)}
.jnr-in{animation:jnStamp .5s cubic-bezier(.2,1.6,.4,1) .15s both}
.jnr-dots{display:flex;gap:6px;justify-content:center;margin:6px 0}
.jnr-dots i{width:14px;height:14px;border-radius:50%;border:2px solid var(--hairline)}
.jnr-dots i.on{background:var(--stamp);border-color:var(--stamp)}
.jn-reach .jn-rname{font-size:28px}
@media (prefers-reduced-motion: reduce){.jnp-boarded,.jnr-in{animation:none}}
.jn-tile{position:relative;margin:0 var(--s4) 4px;height:150px;border-radius:var(--r-lg);background:var(--pink-wash);color:var(--pink);display:flex;align-items:center;justify-content:center}
.jn-tile.fin{background:var(--gold-wash);color:var(--gold-text)}
.jn-tilechip{position:absolute;left:12px;bottom:12px;padding:3px 9px;border-radius:var(--r-pill);background:var(--surface);color:var(--gold-text);font-size:var(--fs-xs);font-weight:700}
/* Profile editor: used / allowed and what is left under a long text box
   (2026-09-25). Right-aligned so it reads as a note on the box, not a field. */
.charcount{margin-top:4px;text-align:right;font-size:var(--fs-xs);color:var(--ink-faint);font-variant-numeric:tabular-nums}
.charcount.low{color:var(--gold-text);font-weight:700}
/* WhatsApp buttons for the poster (2026-09-25): Classifieds / Lost / Found
   lists and the Pass It On "goes to" sheet. WhatsApp's own green, so it reads
   as "this opens WhatsApp" before the icon is recognised. */
.btn.wa{background:var(--whatsapp);color:var(--on-whatsapp);border-color:transparent;font-weight:700}
.btn.wa:active{filter:brightness(.92)}
.btn.ico{width:36px;min-width:36px;padding:0;justify-content:center}
.pcontact{flex:0 0 auto}
.given{display:flex;flex-direction:column;align-items:center;text-align:center;gap:8px;padding:4px 0 8px}
.given .btn.block{width:100%;margin-top:4px}
.given-ic{width:52px;height:52px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:var(--surface-sunken);color:var(--accent)}
.given-ic .i{width:26px;height:26px}
.given-t{font-size:var(--fs-lg);font-weight:800;line-height:1.25}
/* Home Journey card, option I "Next stamp" (2026-09-25): three short lines
   and the next stop as an empty stamp; the pink side stripe from .cmp. */
.jn-home.nxs{flex-direction:row;align-items:center;gap:12px;padding:10px 12px 10px 19px}
.jn-home.nxs .jn-kicker{font-size:11px;line-height:1.25;-webkit-line-clamp:1}
.nxs-t{font-size:var(--fs-base);font-weight:800;line-height:1.25;margin-top:1px}
.nxs-s{font-size:var(--fs-xs);color:var(--ink-soft);line-height:1.35;margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.nxs-s b{color:var(--accent-text);font-weight:700}
.nxs-st{flex:0 0 auto;display:block;width:58px;height:58px;color:var(--gold-text);transform:rotate(-8deg)}
.nxs-st svg{display:block;width:100%;height:100%}
.nxs-st .o{fill:none;stroke:currentColor;stroke-width:3;stroke-dasharray:6 5}
.nxs-st .i{fill:none;stroke:currentColor;stroke-width:1.4;opacity:.55}
.nxs-st .ic{fill:none;stroke:currentColor;stroke-width:4;stroke-linecap:round;stroke-linejoin:round}
.nxs-st text{fill:currentColor;font-family:var(--font-mono);letter-spacing:.04em}
