/* ============================================================================
   Mapsee — design system
   Light + dark (prefers-color-scheme), indigo/violet accent, buttery motion.
   Functionally-critical rules (map sizing, sheet flex, PIN GEOMETRY) are kept
   verbatim and flagged — do not "tidy" them.
   ============================================================================ */

:root{
  /* accent — indigo → violet */
  --accent:#6366f1; --accent-strong:#4f46e5; --accent-2:#8b5cf6;
  --grad:linear-gradient(135deg,#6366f1 0%,#8b5cf6 100%);
  --accent-soft:#eef2ff; --accent-soft-2:#e0e7ff; --accent-ring:rgba(99,102,241,.32);
  --on-accent:#fff;

  /* ink + surfaces (light) */
  --ink:#0e1424; --ink-2:#334155; --muted:#5b6678;
  --bg:#f3f4fb; --card:#ffffff; --surface-2:#eef0f7; --surface-3:#e7eaf3;
  --line:#e6e8f2; --line-2:#dfe2ee;
  --ok:#16a34a; --danger:#e11d48; --warn-bg:#fef3c7; --warn-ink:#92400e;

  /* chrome */
  --header:#0d1220; --header-2:#141a2e; --header-ink:#f4f6ff; --header-line:rgba(255,255,255,.10);
  --page-backdrop:radial-gradient(1100px 720px at 50% -8%, rgba(99,102,241,.16), #eceefa 60%, #e9ebf7);

  /* elevation */
  --shadow-1:0 1px 2px rgba(15,23,42,.06),0 1px 3px rgba(15,23,42,.07);
  --shadow-2:0 6px 18px rgba(15,23,42,.10);
  --shadow-3:0 18px 48px rgba(15,23,42,.18);
  --sheet-shadow:0 -12px 34px rgba(15,23,42,.12);

  /* shape + motion */
  --r-sm:10px; --r:14px; --r-lg:18px; --r-xl:24px; --r-full:999px;
  --ease:cubic-bezier(.22,.61,.36,1);
  --spring:cubic-bezier(.34,1.4,.5,1);
  --dur:.24s; --dur-fast:.14s;

  --safe-top:env(safe-area-inset-top,0px);
  --safe-bottom:env(safe-area-inset-bottom,0px);
  color-scheme:light;
}

@media (prefers-color-scheme:dark){
  :root{
    --accent:#818cf8; --accent-strong:#6366f1; --accent-2:#a78bfa;
    --grad:linear-gradient(135deg,#6366f1 0%,#a78bfa 100%);
    --accent-soft:#1a2140; --accent-soft-2:#232c55; --accent-ring:rgba(129,140,248,.38);
    --on-accent:#0b1020;

    --ink:#e9ecf6; --ink-2:#c4cad8; --muted:#94a0b5;
    --bg:#0a0e18; --card:#141a28; --surface-2:#1d2536; --surface-3:#232c40;
    --line:#28303f; --line-2:#30394b;
    --ok:#34d399; --danger:#fb7185; --warn-bg:#3a2f12; --warn-ink:#fcd34d;

    --header:#080b14; --header-2:#0f1524; --header-ink:#eef1fb; --header-line:rgba(255,255,255,.08);
    --page-backdrop:radial-gradient(1100px 720px at 50% -8%, rgba(99,102,241,.16), #070a12 60%, #05070d);

    --shadow-1:0 1px 2px rgba(0,0,0,.45);
    --shadow-2:0 8px 22px rgba(0,0,0,.5);
    --shadow-3:0 22px 54px rgba(0,0,0,.62);
    --sheet-shadow:0 -14px 38px rgba(0,0,0,.55);
    color-scheme:dark;
  }
}

/* ==========================================================================
   LENSES — bar.ventures / plansie.com are the same app in a different skin.
   The Worker stamps html[data-lens] before first paint (see lensShell), so
   these override the palette with no flash of the mapsee.me indigo. Only
   ACCENT/surface tokens change: every component reads them, so nothing else
   needs to know a lens exists. See src/lens.js.
   ========================================================================== */

/* bar.ventures — after-dark neon: hot magenta on deep plum, dark by default
   because a bar crawl happens at night and a white screen in a dark bar is
   hostile. */
html[data-lens="bar"]{
  color-scheme:dark;
  --accent:#f0468c; --accent-strong:#ff5c9d; --accent-2:#a855f7;
  --grad:linear-gradient(135deg,#f0468c 0%,#a855f7 100%);
  --accent-soft:#2a0f26; --accent-soft-2:#3a1533; --accent-ring:rgba(240,70,140,.38);
  --on-accent:#1a0512;
  --ink:#f6ecf4; --ink-2:#d9c6d5; --muted:#a38fa0;
  --bg:#0c0414; --card:#170a22; --surface-2:#22102f; --surface-3:#2d163d;
  --line:#33193f; --line-2:#43204f;
  --ok:#34d399; --danger:#fb7185; --warn-bg:#3a2f12; --warn-ink:#fcd34d;
  --header:#0a0310; --header-2:#170a22; --header-ink:#f8eef6; --header-line:rgba(255,255,255,.10);
  --page-backdrop:radial-gradient(1100px 720px at 50% -8%, rgba(240,70,140,.20), #0a0310 60%, #06020a);
  --shadow-1:0 1px 2px rgba(0,0,0,.5);
  --shadow-2:0 8px 22px rgba(0,0,0,.55);
  --shadow-3:0 22px 54px rgba(0,0,0,.66);
  --sheet-shadow:0 -14px 38px rgba(0,0,0,.6);
}

/* oneday.cafe — kitchen warmth: ember orange and olive on cream. Light by
   default because food photography dies on a dark chrome, with a deep-roast
   dark variant for evening service. */
html[data-lens="oneday"]{
  color-scheme:light;
  --accent:#c2410c; --accent-strong:#9a3412; --accent-2:#b45309;
  --grad:linear-gradient(135deg,#ea580c 0%,#b45309 100%);
  --accent-soft:#fff2e6; --accent-soft-2:#ffe3cc; --accent-ring:rgba(194,65,12,.28);
  --on-accent:#fff8f0;
  --ink:#2e1f14; --ink-2:#4f3a2a; --muted:#8a7362;
  --bg:#fffaf3; --card:#ffffff; --surface-2:#fdf1e3; --surface-3:#f8e6d2;
  --line:#f0dcc6; --line-2:#e3c8aa;
  --ok:#4d7c0f; --danger:#b91c1c;
  --header:#1b1108; --header-2:#2a1a0e; --header-ink:#fdf6ec; --header-line:rgba(255,255,255,.10);
  --page-backdrop:radial-gradient(1100px 720px at 50% -8%, rgba(234,88,12,.16), #fffaf3 60%, #fff);
  --shadow-1:0 1px 2px rgba(120,72,30,.16);
  --shadow-2:0 8px 22px rgba(120,72,30,.18);
  --shadow-3:0 22px 54px rgba(120,72,30,.22);
  --sheet-shadow:0 -14px 38px rgba(120,72,30,.20);
}
@media (prefers-color-scheme:dark){
  html[data-lens="oneday"]{
    color-scheme:dark;
    --accent:#fb923c; --accent-strong:#f97316; --accent-2:#fbbf24;
    --grad:linear-gradient(135deg,#fb923c 0%,#fbbf24 100%);
    --accent-soft:#33200f; --accent-soft-2:#432a14; --accent-ring:rgba(251,146,60,.34);
    --on-accent:#1c1005;
    --ink:#f7ece0; --ink-2:#dcc9b4; --muted:#a99180;
    --bg:#171009; --card:#221810; --surface-2:#2d2016; --surface-3:#38291c;
    --line:#372718; --line-2:#4a3522;
    --ok:#84cc16; --danger:#fb7185;
    --header:#120c06; --header-2:#221810; --header-ink:#faf1e6; --header-line:rgba(255,255,255,.09);
    --page-backdrop:radial-gradient(1100px 720px at 50% -8%, rgba(251,146,60,.15), #120c06 60%, #0d0804);
    --shadow-1:0 1px 2px rgba(0,0,0,.45);
    --shadow-2:0 8px 22px rgba(0,0,0,.5);
    --shadow-3:0 22px 54px rgba(0,0,0,.62);
    --sheet-shadow:0 -14px 38px rgba(0,0,0,.55);
  }
}

/* plansie.com — kawaii pastel: soft pink/lilac on near-white, rounder corners
   and a gentler shadow. Light by default; the dark block below keeps it sweet
   rather than switching to a hard charcoal. */
html[data-lens="fleabop"]{
  color-scheme:light;
  --accent:#0b7d5e; --accent-strong:#075f47; --accent-2:#65a30d;
  --grad:linear-gradient(135deg,#0d9488 0%,#65a30d 100%);
  --accent-soft:#e3f4ec; --accent-soft-2:#c9e9d9; --accent-ring:rgba(11,125,94,.32);
  --on-accent:#04291f;
  --ink:#173029; --ink-2:#375147; --muted:#748980;
  --bg:#fdf7ec; --card:#ffffff; --surface-2:#f6f1e3; --surface-3:#ece7d8;
  --line:#e6e0d0; --line-2:#d6cfbc;
  --header:#0e3228; --header-2:#09241d; --header-ink:#f2fbf6; --header-line:rgba(255,255,255,.10);
  --page-backdrop:radial-gradient(1100px 720px at 50% -8%, rgba(13,148,136,.22), #fdf7ec 60%, #fffdf8);
  --r:16px;   /* softened, but squarer than plansie - market stall, not sweet */
  --shadow-1:0 1px 2px rgba(60,90,75,.16);
  --shadow-2:0 8px 22px rgba(60,90,75,.18);
  --shadow-3:0 22px 54px rgba(60,90,75,.22);
  --sheet-shadow:0 -14px 38px rgba(60,90,75,.20);
}
@media (prefers-color-scheme:dark){
  html[data-lens="fleabop"]{
    color-scheme:dark;
    --accent:#1fb389; --accent-strong:#0f9d88; --accent-2:#84cc16;
    --grad:linear-gradient(135deg,#1fb389 0%,#84cc16 100%);
    --accent-soft:#0f2c24; --accent-soft-2:#153a30; --accent-ring:rgba(31,179,137,.34);
    --on-accent:#04291f;
    --ink:#e9f6ef; --ink-2:#c4dbd0; --muted:#93aaa0;
    --bg:#0e1a16; --card:#16241f; --surface-2:#1d2f28; --surface-3:#253b32;
    --line:#25382f; --line-2:#31483d;
    --header:#0b1713; --header-2:#16241f; --header-ink:#eefaf4; --header-line:rgba(255,255,255,.09);
    --page-backdrop:radial-gradient(1100px 720px at 50% -8%, rgba(31,179,137,.14), #0b1713 60%, #081210);
    --shadow-1:0 1px 2px rgba(0,0,0,.42);
    --shadow-2:0 8px 22px rgba(0,0,0,.48);
    --shadow-3:0 22px 54px rgba(0,0,0,.6);
    --sheet-shadow:0 -14px 38px rgba(0,0,0,.52);
  }
}

html[data-lens="plansie"]{
  color-scheme:light;
  --accent:#ff8fc0; --accent-strong:#f2669f; --accent-2:#b79cff;
  --grad:linear-gradient(135deg,#ffa5cd 0%,#b79cff 100%);
  --accent-soft:#fff0f7; --accent-soft-2:#ffe3f0; --accent-ring:rgba(255,143,192,.34);
  --on-accent:#4a1030;
  --ink:#4a3b46; --ink-2:#6b5a66; --muted:#9c8a97;
  --bg:#fff7fb; --card:#ffffff; --surface-2:#fdeef6; --surface-3:#fbe2ef;
  --line:#f6dcea; --line-2:#efc9dd;
  --header:#fff1f7; --header-2:#ffe6f1; --header-ink:#5b4453; --header-line:rgba(90,60,80,.10);
  --page-backdrop:radial-gradient(1100px 720px at 50% -8%, rgba(255,165,205,.28), #fff7fb 60%, #fff);
  --r:20px;   /* rounder everything - the single biggest "cute" signal */
  --shadow-1:0 1px 2px rgba(214,140,180,.18);
  --shadow-2:0 8px 22px rgba(214,140,180,.20);
  --shadow-3:0 22px 54px rgba(214,140,180,.24);
  --sheet-shadow:0 -14px 38px rgba(214,140,180,.22);
}
@media (prefers-color-scheme:dark){
  html[data-lens="plansie"]{
    color-scheme:dark;
    --accent:#ffa5cd; --accent-strong:#ff8fc0; --accent-2:#c9b3ff;
    --grad:linear-gradient(135deg,#ffa5cd 0%,#c9b3ff 100%);
    --accent-soft:#33202c; --accent-soft-2:#402836; --accent-ring:rgba(255,165,205,.36);
    --on-accent:#2a1220;
    --ink:#f7eaf1; --ink-2:#dcc7d5; --muted:#ab95a5;
    --bg:#1b1218; --card:#251a22; --surface-2:#30222c; --surface-3:#3b2a35;
    --line:#3a2833; --line-2:#4a3341;
    --header:#170f14; --header-2:#251a22; --header-ink:#f9edf4; --header-line:rgba(255,255,255,.09);
    --page-backdrop:radial-gradient(1100px 720px at 50% -8%, rgba(255,165,205,.16), #170f14 60%, #120b0f);
    --shadow-1:0 1px 2px rgba(0,0,0,.42);
    --shadow-2:0 8px 22px rgba(0,0,0,.48);
    --shadow-3:0 22px 54px rgba(0,0,0,.6);
    --sheet-shadow:0 -14px 38px rgba(0,0,0,.52);
  }
}

/* wegosie.com — group exercise. Green → cyan: the one corner of the wheel the
   other four leave free, and the only one that reads as "healthy" without
   tipping into fleabop's teal→lime. Kept clean and high-contrast rather than
   neon: the lens is about turning up at any pace, not about a gym. */
html[data-lens="wegosie"]{
  color-scheme:light;
  --accent:#0d9f6e; --accent-strong:#07835a; --accent-2:#06b6d4;
  --grad:linear-gradient(135deg,#22c55e 0%,#06b6d4 100%);
  --accent-soft:#ecfdf5; --accent-soft-2:#d7f7ea; --accent-ring:rgba(13,159,110,.30);
  --on-accent:#04241a;
  /* --muted is darker than it "wants" to be against this very light green bg:
     at #7d9a92 it measured 2.93:1, the only lens in the family under 3:1
     (plansie 3.07, fleabop 3.50, oneday 4.29). #6b8880 brings it to 3.7. */
  --ink:#123029; --ink-2:#3a5b51; --muted:#6b8880;
  --bg:#f4fdfa; --card:#ffffff; --surface-2:#e9faf3; --surface-3:#dcf5ec;
  --line:#d3efe5; --line-2:#b8e4d5;
  --header:#eafaf3; --header-2:#dcf5ec; --header-ink:#123029; --header-line:rgba(18,48,41,.10);
  --page-backdrop:radial-gradient(1100px 720px at 50% -8%, rgba(34,197,94,.22), #f4fdfa 60%, #fff);
  --shadow-1:0 1px 2px rgba(16,110,85,.16);
  --shadow-2:0 8px 22px rgba(16,110,85,.18);
  --shadow-3:0 22px 54px rgba(16,110,85,.22);
  --sheet-shadow:0 -14px 38px rgba(16,110,85,.20);
}
@media (prefers-color-scheme:dark){
  html[data-lens="wegosie"]{
    color-scheme:dark;
    --accent:#34d399; --accent-strong:#22c55e; --accent-2:#22d3ee;
    --grad:linear-gradient(135deg,#34d399 0%,#22d3ee 100%);
    --accent-soft:#0d2b23; --accent-soft-2:#12392e; --accent-ring:rgba(52,211,153,.34);
    --on-accent:#04140f;
    --ink:#e8fbf4; --ink-2:#bcdfd4; --muted:#88a89e;
    --bg:#071612; --card:#0e211c; --surface-2:#152c25; --surface-3:#1c382f;
    --line:#20392f; --line-2:#2c4c3f;
    --header:#051310; --header-2:#0e211c; --header-ink:#ecfdf5; --header-line:rgba(255,255,255,.09);
    --page-backdrop:radial-gradient(1100px 720px at 50% -8%, rgba(52,211,153,.14), #051310 60%, #030e0b);
    --shadow-1:0 1px 2px rgba(0,0,0,.42);
    --shadow-2:0 8px 22px rgba(0,0,0,.48);
    --shadow-3:0 22px 54px rgba(0,0,0,.6);
    --sheet-shadow:0 -14px 38px rgba(0,0,0,.52);
  }
}

/* ---- base ---------------------------------------------------------------- */
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{margin:0;height:100%;overflow:hidden}          /* CRITICAL: no page scroll */
body{
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  color:var(--ink);background:var(--bg);overscroll-behavior:none;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
  font-size:15px;line-height:1.45;letter-spacing:.1px;
}
#app{position:relative;display:flex;flex-direction:column;height:100dvh;background:var(--bg);isolation:isolate}
.hidden{display:none !important}
.muted{color:var(--muted)}
.small{font-size:12px}
button{font:inherit;cursor:pointer;border:none;background:none;color:inherit}
a{color:var(--accent-strong)}
::selection{background:var(--accent-soft-2);color:var(--ink)}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:6px}

/* refined scrollbars (desktop) */
.list,.shbody,.shpage,.form,.scrollpane,.notif-panel,#epMsgs{scrollbar-width:thin;scrollbar-color:var(--line-2) transparent}
.list::-webkit-scrollbar,.shbody::-webkit-scrollbar,.shpage::-webkit-scrollbar,.form::-webkit-scrollbar{width:9px;height:9px}
.list::-webkit-scrollbar-thumb,.shbody::-webkit-scrollbar-thumb,.shpage::-webkit-scrollbar-thumb,.form::-webkit-scrollbar-thumb{
  background:var(--line-2);border-radius:99px;border:3px solid transparent;background-clip:content-box}

/* ---- floating map chrome (the header is gone — the map runs to the top) --- */
.brand{text-decoration:none;color:inherit}
.brandmark{display:block;flex:0 0 auto;filter:drop-shadow(0 2px 4px rgba(0,0,0,.35))}
.wordmark{font-weight:800;font-size:19px;letter-spacing:-.3px;color:var(--ink)}
.wordmark .tld{color:var(--accent);font-weight:750}
.ob-brand{display:flex;align-items:center;gap:10px;margin-bottom:12px}
.ob-brand .wordmark{color:var(--ink);font-size:22px}
.ob-brand .wordmark .tld{color:var(--accent)}
/* brand pill: centered over the top of the map, glassy */
.map-brand{position:absolute;top:calc(10px + var(--safe-top));left:50%;transform:translateX(-50%);z-index:5;
  display:flex;align-items:center;gap:7px;padding:6px 13px;border-radius:999px;
  background:color-mix(in srgb, var(--card) 82%, transparent);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  border:1px solid var(--line);box-shadow:var(--shadow-2)}
.map-brand .wordmark{font-size:16.5px}
/* bell stack: top right over the map */
.map-topright{position:absolute;top:calc(10px + var(--safe-top));right:10px;z-index:5;
  display:flex;flex-direction:column;gap:8px;align-items:flex-end}
/* install CTA: top LEFT - the only corner nothing else claims (brand is centred,
   bell is right), so promoting it never crowds the other chrome. */
.map-topleft{position:absolute;top:calc(10px + var(--safe-top));inset-inline-start:10px;z-index:5;
  display:flex;flex-direction:column;gap:8px;align-items:flex-start}
.mapfab{position:relative;font:inherit;font-size:16px;padding:8px 11px;border-radius:999px;cursor:pointer;
  background:color-mix(in srgb, var(--card) 86%, transparent);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  border:1px solid var(--line);color:var(--ink);box-shadow:var(--shadow-2);
  transition:background var(--dur-fast) var(--ease),transform var(--dur-fast) var(--ease)}
.mapfab:hover{background:var(--surface-2)}
.mapfab:active{transform:scale(.92)}

.ghost{
  border:1px solid var(--line);background:var(--card);color:var(--ink);border-radius:var(--r-sm);
  padding:8px 12px;font-weight:600;transition:background var(--dur-fast),border-color var(--dur-fast),transform var(--dur-fast)}
.ghost:hover{border-color:var(--line-2);background:var(--surface-2)}
.ghost:active{transform:scale(.96)}
#installBtn{background:var(--grad);border-color:transparent;color:#fff;box-shadow:var(--shadow-2);font-weight:700;font-size:13px;
  display:flex;align-items:center;gap:6px;max-width:46vw;white-space:nowrap;overflow:hidden}
#installBtn .ib-art{font-size:12px;line-height:1;opacity:.95;flex:0 0 auto}
/* one gentle rise as it appears - it only ever shows when the browser has
   actually offered an install, so a single cue is a prompt, not nagging */
#installBtn:not(.hidden){animation:ib-in .42s var(--ease) both}
@keyframes ib-in{from{opacity:0;transform:translateY(-8px) scale(.94)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){ #installBtn:not(.hidden){animation:none} }

/* ---- views + maps -------------------------------------------------------- */
#views{position:relative;flex:1;overflow:hidden}         /* CRITICAL */
.view{position:absolute;inset:0;display:none;flex-direction:column}
.view.active{display:flex;animation:view-in var(--dur) var(--ease)}
#map,#group-map{flex:1;min-height:0;min-width:0;isolation:isolate}   /* isolate: contains pin z-index (incl. selected .sel-top:9999) so pins never paint over body-level notifications/toasts */
.maplibregl-ctrl-group{border-radius:12px !important;overflow:hidden;box-shadow:var(--shadow-2) !important;border:1px solid var(--line)}
.maplibregl-popup-content{border-radius:12px;padding:10px 12px;background:var(--card);color:var(--ink);box-shadow:var(--shadow-2)}
.maplibregl-popup-content h3{margin:0 0 4px;font-size:14px}
.maplibregl-ctrl-attrib{border-radius:8px 0 0 0 !important;font-size:10px !important;backdrop-filter:blur(4px)}

/* ---- bottom sheet -------------------------------------------------------- */
.sheet{
  background:var(--card);border-top:1px solid var(--line);max-height:42vh;      /* CRITICAL: max-height + flex */
  display:flex;flex-direction:column;overflow:hidden;
  border-radius:var(--r-xl) var(--r-xl) 0 0;box-shadow:var(--sheet-shadow);position:relative}
/* grip bar (mobile): a slim drag target — drag up/down to resize, ▾ to minimize.
   Kept compact so it doesn't eat vertical space; the pane header rows below are
   ALSO drag targets (see .head-drag), so the effective grab area stays large. */
.sheet-grip{flex:0 0 auto;position:relative;height:22px;cursor:ns-resize;
  touch-action:none;user-select:none;-webkit-user-select:none}
.grip-pill{position:absolute;top:8px;left:50%;transform:translateX(-50%);
  width:44px;height:5px;border-radius:99px;background:var(--line-2);opacity:.9;pointer-events:none}
.grip-btn{position:absolute;top:1px;right:8px;width:30px;height:20px;border:none;border-radius:8px;
  background:transparent;color:var(--muted);font-size:13px;line-height:1;cursor:pointer}
.grip-btn:hover{background:var(--surface-2)}
/* edge rail (desktop): shown only in the ≥900px sidebar layout */
.sheet-edge{display:none}
.sheet-head{display:flex;align-items:center;gap:10px;padding:6px 15px 8px;flex-wrap:wrap}
/* the pane's first header row doubles as a big mobile resize target (see
   armHeaderDrag): own vertical gestures so a drag resizes instead of scrolling,
   while taps on the controls inside still click through. */
.head-drag{touch-action:none}
.sheet-head strong{font-size:16px;letter-spacing:.2px}
.list{overflow-y:auto;padding:4px 8px 8px}               /* CRITICAL: overflow */
/* Nearby list: one shared 12px left gutter so the filter chips, event cards and
   date row all line up on the left. The list supplies the gutter and the cards
   drop their own side padding (--card-pl:0), so a card's content + its hover
   background both start at 12px — flush with everything else on the page. */
#nearbyList{padding-left:12px;padding-right:12px}
#nearbyList .card{--card-pl:0px}

/* CRITICAL — sections fill the sheet so the lists scroll internally */
#sheetHome{display:flex;flex-direction:column;min-height:0;flex:1}
#sheetHome>.sheet-head{flex:0 0 auto}
/* the four home pages (Nearby ⇄ RSVPs ⇄ People ⇄ Create) slide as one track */
.home-pages{flex:1 1 auto;min-height:0;display:flex;width:100%;will-change:transform;
  transition:transform .34s cubic-bezier(.22,.85,.25,1)}
.home-pages.dragging{transition:none}
.home-pg{flex:0 0 100%;width:100%;min-width:0;min-height:0;display:flex;flex-direction:column;
  touch-action:pan-y;overscroll-behavior:contain}
/* iOS Safari ignores an ancestor-only touch-action once a SCROLLABLE descendant
   owns the pan — it claims the gesture and pointercancels the swipe. Putting
   pan-y directly ON the scrollers (like .shpage does for Details⇄Chat) is what
   makes the home swipe follow the finger reliably on iPhone. */
.home-pg .list,#pgPeople>.people,#pgCreate>.form{touch-action:pan-y;overscroll-behavior:contain}
/* the category chip row scrolls HORIZONTALLY — it must keep its native pan
   (the swipe handler already ignores gestures that start on it) */
.home-pg .filterbar{touch-action:pan-x pan-y}
.home-pg>.sub-head,.home-pg>.rangerow,.home-pg>.filterbar{flex:0 0 auto}
.sub-head{display:flex;align-items:center;gap:8px;padding:0 12px 6px}
.home-head{flex-wrap:nowrap;gap:6px;padding:6px 10px 8px}
/* Longer translated labels (fr/de) must NOT push the ⚙ settings button (which
   holds the language switcher) off the right edge, since .sheet clips overflow.
   Let the tab labels shrink + ellipsis, and pin ⚙ so it's always reachable. */
.home-head .mtab{min-width:0;padding:7px 10px;font-size:12.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.home-head .mtab-add{flex:0 0 auto}   /* ➕ is emoji-only, never shrink it away */
.home-head #mineSettings{flex:0 0 auto}
.mtab-add{font-size:13px}
#pgPeople>.people{flex:1 1 auto;min-height:0;overflow-y:auto;padding:0 12px var(--safe-bottom)}
#pgCreate>.form{flex:1 1 auto;min-height:0;overflow-y:auto;max-height:none;max-width:none;
  padding:4px 14px calc(14px + var(--safe-bottom))}
/* "more like this nearby" rows on the event sheet */
.morelike .ml{display:block;margin-top:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#nearbyList,#mineList{flex:1 1 auto;min-height:0}
#nearbyList,#mineList{padding-bottom:var(--safe-bottom)}
#sheetPanel{display:flex;flex-direction:column;min-height:0;flex:1}
.sheet.expanded{max-height:64vh}
/* user-resized (mobile drag): explicit height beats the 42vh/64vh caps */
.sheet.resized{height:var(--sheet-h,42vh);max-height:min(85dvh,85vh)}
/* minimized: just the grip showing — the map gets (almost) everything.
   Declared AFTER .resized so it wins when both classes are present. */
.sheet.minimized{height:calc(24px + var(--safe-bottom));max-height:none}
.sheet.minimized>:not(.sheet-grip):not(.sheet-edge){display:none}
/* Details + Chat live side-by-side in a horizontally sliding track (#shPages)
   so swiping between tabs tracks the finger with BOTH panes visible. Each page
   scrolls itself vertically; the body just clips the track. */
.shbody{overflow:hidden;min-height:0;flex:1;display:flex;
  animation:panel-in var(--dur) var(--ease)}
.shpages{display:flex;width:100%;min-height:0;flex:1;will-change:transform;
  transition:transform .34s cubic-bezier(.22,.85,.25,1)}
.shpages.dragging{transition:none}
.shpage{flex:0 0 100%;min-width:0;min-height:0;display:flex;flex-direction:column;
  touch-action:pan-y;overscroll-behavior:contain}
/* bottom padding carries the safe-area inset (like People/Create/lists do) so
   the last controls (RSVP buttons, guest list) aren't stuck under the home
   indicator / sheet edge - you can always scroll clear to the bottom. */
.pg-details{overflow-y:auto;gap:10px;padding:6px 13px calc(18px + var(--safe-bottom))}
#shRsvp{scroll-margin-top:8px}   /* block:"start" reveal breathes instead of hugging the edge */
.pg-chat{overflow:hidden;padding:6px 13px 12px}
.pg-chat #shMsgs{flex:1;overflow-y:auto;min-height:110px;touch-action:pan-y}
.shbody .meta-card{padding:13px}
.shbody .msgs{padding:0}
.shbody .chatbar{padding:0;border-top:none}
#sheetPanel .chatbar{border-top:1px solid var(--line);padding:10px 12px calc(10px + var(--safe-bottom))}
#sheetPanel:not(.hidden){animation:panel-in var(--dur) var(--ease)}
#shOpen{text-decoration:none}

/* ---- cards --------------------------------------------------------------- */
/* Event cards: each row gets a color-tinted ICON TILE (the event's pin color +
   category glyph at a legible size) and a highlighted WHEN line, with soft
   dividers between rows — so long lists scan instead of blending together. */
.card{--card-pl:10px;display:flex;gap:12px;padding:12px var(--card-pl);border-radius:var(--r);cursor:pointer;position:relative;
  transition:background var(--dur-fast) var(--ease),transform var(--dur-fast) var(--ease),box-shadow var(--dur-fast)}
.card+.card::before{content:"";position:absolute;top:0;left:calc(var(--card-pl,10px) + 52px);right:10px;height:1px;background:var(--line)}
.card:hover{background:var(--surface-2)}
.card:hover::before,.card:hover+.card::before{opacity:0}
.card:active{background:var(--surface-3);transform:scale(.985)}
.card .dot{position:relative;width:40px;height:40px;border-radius:12px;margin-top:2px;flex:0 0 auto;
  background:color-mix(in srgb, var(--c, var(--accent)) 18%, transparent);
  display:flex;align-items:center;justify-content:center}
.card .dot::after{content:"";position:absolute;right:-3px;bottom:-3px;width:11px;height:11px;border-radius:50%;
  background:var(--c, var(--accent));border:2px solid var(--card)}   /* matches its map pin */
.card .body{min-width:0;flex:1}
.card .title{font-weight:650;font-size:15px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;letter-spacing:.1px}
.card .when{font-size:12.5px;font-weight:700;color:var(--accent-strong);margin-top:2px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.card .when .muted{font-weight:500}
.card .meta{color:var(--muted);font-size:12.5px;margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.card .go{align-self:center;font-size:20px;color:var(--muted);opacity:.45;transition:opacity var(--dur-fast),color var(--dur-fast)}
.card:hover .go{opacity:1;color:var(--accent-strong)}
/* one-tap "can't go" on an unanswered invite card */
.card-nogo{align-self:center;flex:0 0 auto;width:34px;height:34px;border-radius:50%;border:1px solid var(--line-2);
  background:var(--surface-2);font-size:15px;line-height:1;cursor:pointer;opacity:.7;
  transition:opacity var(--dur-fast),background var(--dur-fast),transform var(--dur-fast)}
.card-nogo:hover{opacity:1;background:var(--surface-3)}
.card-nogo:active{transform:scale(.9)}
.dot-emoji{font-size:19px;line-height:1}
.empty{padding:26px 16px;text-align:center;color:var(--muted);font-size:14px}
.mine-sec{padding:10px 12px 5px;font-size:11.5px;font-weight:700;letter-spacing:.4px;text-transform:uppercase;
  color:var(--muted);position:sticky;top:0;background:var(--card);z-index:1}
.card.past{opacity:.56}
.mine-toggle{display:block;width:calc(100% - 24px);margin:8px 12px;padding:9px 12px;border:1px dashed var(--border);
  border-radius:10px;background:transparent;color:var(--muted);font-size:12.5px;font-weight:600;
  text-align:left;cursor:pointer}
.mine-toggle:hover{color:var(--fg);border-color:var(--muted)}

/* ---- forms + buttons + inputs ------------------------------------------- */
.form{padding:16px;overflow-y:auto;display:flex;flex-direction:column;gap:13px;max-width:560px;width:100%;margin:0 auto}
.form h2{margin:2px 0 0;font-size:22px;font-weight:800;letter-spacing:-.2px}
input,textarea,select{font:inherit;color:var(--ink)}
.form input,.form textarea,.form select{
  width:100%;padding:12px 13px;border:1px solid var(--line);border-radius:var(--r);background:var(--card);
  transition:border-color var(--dur-fast),box-shadow var(--dur-fast)}
.form input::placeholder,.form textarea::placeholder{color:var(--muted)}
.form input:focus,.form textarea:focus,.form select:focus{
  outline:none;border-color:var(--accent);box-shadow:0 0 0 4px var(--accent-ring)}
.form label{display:flex;flex-direction:column;gap:6px;font-size:13px;color:var(--muted);font-weight:600}
.row2{display:grid;grid-template-columns:1fr 1fr;gap:10px}
/* create-flow sections: ghost cards (hairline, transparent) so the inputs carry
   the weight - same calm-container language as the chips/nearby redesign */
.form-head{display:flex;align-items:center;justify-content:space-between;gap:10px}
.scan-chip{font:inherit;font-size:13px;font-weight:650;padding:9px 14px;border:none;border-radius:var(--r-full);
  cursor:pointer;background:var(--accent-soft);color:var(--accent-strong);white-space:nowrap;
  transition:background var(--dur-fast),transform var(--dur-fast)}
.scan-chip:hover{background:var(--accent-soft-2)}
.scan-chip:active{transform:scale(.95)}
.quickrow{display:flex;gap:8px;flex-wrap:wrap;margin-top:-4px}
.linkrow{display:flex;gap:8px;align-items:stretch}
.linkrow input{flex:1;min-width:0}
.linkgo{padding:10px 16px;font-size:14px;flex:0 0 auto}
.fsec{display:flex;flex-direction:column;gap:10px;padding:12px;border:1px solid var(--line);border-radius:var(--r-lg)}
/* datetime-local intrinsically wants ~215px (date + time + picker icon), so
   the Starts/Ends pair goes side by side ONLY when both genuinely fit - else
   it stacks. Container-driven (auto-fit), so it's right on phones AND in the
   desktop rail at any drag width. */
.fsec .row2:has(input[type="datetime-local"]){grid-template-columns:repeat(auto-fit,minmax(205px,1fr))}
.fsec .row2>label{min-width:0}
.fsec .row2 input,.fsec .row2 select{min-width:0}
.fsec input[type="datetime-local"]{padding:12px 8px;font-size:14px}
.fsec-t{font-size:11.5px;font-weight:700;letter-spacing:.4px;text-transform:uppercase;color:var(--muted)}
details.fsec>summary{cursor:pointer;font-size:13.5px;font-weight:650;color:var(--ink-2);list-style:none;user-select:none}
details.fsec>summary::-webkit-details-marker{display:none}
details.fsec>summary::before{content:"▸";display:inline-block;margin-right:7px;color:var(--muted);
  transition:transform var(--dur-fast) var(--ease)}
details.fsec[open]>summary::before{transform:rotate(90deg)}
/* imported/scanned fields flash briefly so the user sees what was filled in */
.form .prefilled{animation:prefill-flash 1.8s var(--ease)}
@keyframes prefill-flash{0%{background:var(--accent-soft);border-color:var(--accent)}100%{background:var(--card)}}
select{padding:8px 10px;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--card)}

.primary{
  background:var(--grad);color:#fff;border-radius:var(--r);padding:14px;font-weight:750;font-size:16px;letter-spacing:.2px;
  box-shadow:var(--shadow-1),inset 0 1px 0 rgba(255,255,255,.18);
  transition:transform var(--dur-fast) var(--ease),box-shadow var(--dur-fast),filter var(--dur-fast)}
.primary:hover{box-shadow:var(--shadow-2),inset 0 1px 0 rgba(255,255,255,.2);filter:saturate(1.06)}
.primary:active{transform:scale(.97)}
.primary:disabled{opacity:.5;filter:grayscale(.3);box-shadow:none}
hr{border:none;border-top:1px solid var(--line);margin:6px 0}
.msg{font-size:14px;color:var(--ok);min-height:18px}
.msg.err{color:var(--danger)}

/* ---- detail panels + chat ----------------------------------------------- */
.scrollpane{flex:1;overflow-y:auto;display:flex;flex-direction:column}
.meta-card{background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);padding:14px;font-size:14px;line-height:1.55}
/* event description: clamped to 4 lines so imported walls of text never bury
   the facts/RSVP; "Show more" appears only when it actually overflows */
.ev-desc{margin:6px 0 2px;overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:4}
.ev-desc.open{display:block;-webkit-line-clamp:unset;overflow:visible}
.desc-more{color:var(--accent-strong);font-weight:650;font-size:13px;cursor:pointer;margin-bottom:2px}
.addr-sub{margin-top:-2px}
/* Imported-listing action pills — Tickets / Spotify / YouTube links from the
   description, promoted to tidy tappable buttons instead of raw inline URLs. */
.ext-actions{display:flex;flex-wrap:wrap;gap:8px;margin:10px 0 2px}
.ext-btn{display:inline-flex;align-items:center;gap:6px;padding:8px 14px;border-radius:999px;
  font-weight:650;font-size:13px;line-height:1;text-decoration:none;color:#fff;border:0;
  transition:transform .08s ease,filter .12s ease}
.ext-btn:hover{filter:brightness(1.07)}
.ext-btn:active{transform:translateY(1px)}
.ext-btn .ext-ico{font-size:.85em}
/* the primary source link (Tickets & info / More on this event) leads by accent
   color + first position, but stays a normal-sized pill like the others */
.ext-primary{background:var(--accent-strong)}
.ext-showinfo{background:#475569}
.ext-spotify{background:#1db954}
.ext-youtube{background:#ff0000}
.ext-bandcamp{background:#629aa9}
/* event POSTER banner: sits behind the name/icon/category at the top of the
   Details card. Bleeds to the card edges; a bottom-heavy scrim keeps the text
   readable over any image. Without a poster the wrapper is invisible. */
.ev-poster-head{border-radius:calc(var(--r-lg) - 1px) calc(var(--r-lg) - 1px) 0 0}
.ev-poster-head.has-poster{
  position:relative;overflow:hidden;
  margin:-14px -14px 10px;padding:56px 14px 10px;
  color:#fff;text-shadow:0 1px 3px rgba(0,0,0,.55)}
.ev-poster-head.has-poster .poster-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0}
.ev-poster-head.has-poster::after{content:"";position:absolute;inset:0;z-index:1;
  background:linear-gradient(to bottom, rgba(0,0,0,.10) 30%, rgba(0,0,0,.62))}
.ev-poster-head.has-poster>*:not(.poster-img){position:relative;z-index:2}
.shbody .meta-card .ev-poster-head.has-poster{margin:-13px -13px 10px}
.ev-poster-head.has-poster .muted{color:rgba(255,255,255,.85)}
.ev-poster-head.has-poster .priv-tag{filter:drop-shadow(0 1px 2px rgba(0,0,0,.5))}
.meta-card .t{font-size:19px;font-weight:800;margin-bottom:4px;letter-spacing:-.2px}
.meta-card .row{color:var(--muted);margin-top:3px}
.meta-card .row a{color:var(--accent-strong);text-decoration:none;font-weight:600}
/* secondary "Google Maps" alt link (iPhone only) — quieter than the primary 📍 */
.meta-card .row a.gmaps-link{font-size:12.5px;font-weight:600;color:var(--muted)}
.meta-card .row b{color:var(--ink)}
.series-nav a{white-space:nowrap}   /* keep "← Jul 5" / "Jul 19 →" from breaking mid-link */
.series-nav .muted{opacity:.5}
.btnrow{display:flex;gap:8px;flex-wrap:wrap}
/* flex-basis 140px (not 0) so a too-tight row WRAPS instead of squishing buttons
   until their labels bleed off-screen; min-width:0 lets a lone button shrink */
.btnrow .ghost,.btnrow .primary{flex:1 1 140px;min-width:0;text-align:center;text-decoration:none;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.rsvp-card{background:linear-gradient(180deg,var(--accent-soft),color-mix(in srgb,var(--accent-soft) 60%,var(--card)));
  border:1px solid var(--accent-soft-2);border-radius:var(--r-lg);padding:13px;display:flex;flex-direction:column;gap:10px}
/* The pre-RSVP "I'm going" button is BLACK — a clear "take this action" CTA. Its
   old purple (--grad) styling read as an already-selected state, so people
   thought they'd RSVP'd. The CONFIRMED going status shows the purple highlight
   instead (.rsvp-pill.going). */
#shGoing{background:#111827}
@media (prefers-color-scheme: dark){ #shGoing{background:#0b0f1a;box-shadow:0 0 0 1px var(--line),var(--shadow-1)} }
/* confirmed-status pill in the status switcher — going = purple highlighted */
.rsvp-pill{display:inline-block;padding:2px 11px;border-radius:var(--r-full);font-weight:750}
.rsvp-pill.going{background:var(--grad);color:#fff}
.rsvp-pill.maybe{background:var(--accent-soft);color:var(--accent-strong)}
.rsvp-pill.declined{background:var(--surface-2);color:var(--muted)}
.backbtn{padding:8px 12px;font-size:18px;line-height:1}
.pickmap{height:240px;border-radius:var(--r-lg);border:1px solid var(--line);overflow:hidden}
#epLiveMap{height:38vh;min-height:220px;border-top:1px solid var(--line)}

/* segmented control */
.seg{display:flex;background:var(--surface-2);border-radius:var(--r);padding:4px;gap:4px}
.segbtn{flex:1;padding:9px;border-radius:calc(var(--r) - 4px);font-weight:700;color:var(--muted);position:relative;
  transition:color var(--dur-fast),background var(--dur) var(--ease),box-shadow var(--dur),transform var(--dur-fast)}
/* selected tab matches the home tabs: purple gradient, white text */
.segbtn.active{background:var(--grad);border-color:transparent;color:#fff;box-shadow:var(--shadow-1)}
.segbtn:active{transform:scale(.97)}
/* event tabs sit INLINE in the header row (compact) — active tab is the purple
   gradient (matches the home tabs). Only the TITLE shrinks/ellipsizes when tight;
   the action icons stay rigid (no vertical stacking). If a rare worst case
   (long title + all four icons + tiny screen) still can't fit, the actions
   wrap to a second line rather than clipping off-screen. */
.ev-head #shTitle{min-width:44px}
.ev-head .ev-actions{flex:0 0 auto;flex-wrap:nowrap}
/* nowrap is load-bearing: a WRAP container breaks lines on natural widths —
   the shrinkable title never gets to shrink — so wrap can never save this row.
   With nowrap the title ellipsizes down to its min and everything stays put. */
.ev-head{gap:6px;flex-wrap:nowrap}
/* compact sizing is UNCONDITIONAL: the event header lives in a ~383px side
   panel on desktop and a phone-width sheet on mobile — the viewport is never
   the real constraint, the panel is, so no media query can key this */
.ev-head .backbtn{padding:8px}
/* swipe-to-back: the back arrow arms (purple, grows) as a right-swipe progresses
   on Details / a spot sheet / a DM, giving a live "you're going back" cue */
#shBack.pulling,.dm-close.pulling{background:var(--grad);color:#fff;border-color:transparent;
  transform:scale(calc(1 + var(--pull,0) * 0.22));transition:none}
.ev-head .hicon{width:32px;height:32px;font-size:15px}
#shTabs{flex:0 1 auto;min-width:0;padding:3px;gap:3px}
/* shrink the tabs (fr: Détails / Discussion) before the header clips the action
   icons on the right */
#shTabs .segbtn{flex:0 1 auto;min-width:0;padding:4px 9px;font-size:12.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#shTabChat{position:relative}

/* chat */
.chat{display:flex;flex-direction:column;border-top:1px solid var(--line);background:var(--card)}
.msgs{padding:6px 12px;display:flex;flex-direction:column;gap:9px}
.m{max-width:86%;background:var(--surface-2);border-radius:16px;padding:8px 12px;box-shadow:var(--shadow-1);
  animation:msg-in var(--dur) var(--ease)}
.m.mine{align-self:flex-end;background:var(--grad);color:#fff}
.m .a{font-size:12px;font-weight:700;color:var(--accent-strong)}
.m.mine .a{color:rgba(255,255,255,.9)}
/* plain URLs in message bodies — readable on both bubble colors */
.m .b a:not(.mention){color:var(--accent-strong);text-decoration:underline;word-break:break-all}
.m.mine .b a:not(.mention){color:#fff}
.m .b{font-size:14px;white-space:pre-wrap;word-break:break-word;line-height:1.4}
.m .t{font-size:11px;color:var(--muted);margin-top:2px}
.m.mine .t{color:rgba(255,255,255,.9)}
.m.sys{align-self:center;background:none;box-shadow:none;color:var(--muted);font-size:12px;padding:3px 8px;text-align:center;max-width:96%}
.reacts{display:flex;gap:6px;margin-top:6px;flex-wrap:wrap}
.react{border:1px solid var(--line);background:var(--card);color:var(--ink);border-radius:var(--r-full);padding:3px 9px;font-size:13px;cursor:pointer;
  transition:background var(--dur-fast),border-color var(--dur-fast),transform var(--dur-fast)}
/* on your own (indigo) bubbles the pills must NOT inherit the white text, or the
   edit/delete/reaction controls become white-on-white circles */
.m.mine .react{color:var(--ink)}
.react.mdel,.react.mrm{color:var(--danger);border-color:color-mix(in srgb,var(--danger) 35%,var(--line))}
.react:active{transform:scale(.92)}
.react.on{background:var(--accent-soft);border-color:var(--accent)}
.react.add{color:var(--muted)}
.react.medit,.react.mdel,.react.mrm{cursor:pointer}
.picker{display:flex;gap:4px;margin-top:6px}
.picker button{font-size:18px;padding:2px 6px;border-radius:8px;transition:background var(--dur-fast)}
.picker button:active{background:var(--surface-2)}
.chatbar{display:flex;flex-wrap:wrap;gap:8px;padding:10px 12px;border-top:1px solid var(--line);
  padding-bottom:calc(10px + var(--safe-bottom));background:var(--card)}
/* composer-slot nudge for non-RSVP'd readers of a public chat */
.chat-nudge{display:block;width:100%;padding:12px;border:none;border-top:1px solid var(--line);
  padding-bottom:calc(12px + var(--safe-bottom));background:var(--surface-2);color:var(--ink);
  font:inherit;font-size:13.5px;text-align:center;cursor:pointer}
.chat-nudge:active{background:var(--surface-3,rgba(0,0,0,.06))}
/* staged photo/drawing preview — sits on its own line(s) above the input row */
.attach{flex-basis:100%;display:flex;flex-direction:column;gap:8px;padding:8px;border:1px solid var(--line);
  border-radius:12px;background:var(--surface-2);animation:pop-in var(--dur-fast) var(--spring)}
.attach .at-item{display:flex;align-items:center;gap:10px;min-width:0}
.attach img{width:46px;height:46px;object-fit:cover;border-radius:8px;flex:0 0 auto}
.attach .at-draw{flex:0 0 auto;width:56px;height:40px;display:flex;align-items:center;justify-content:center;
  border-radius:8px;background:var(--card);color:var(--accent-strong)}
.attach .at-draw .draw-svg{width:52px;height:36px}
.attach .atx{display:flex;flex-direction:column;line-height:1.25;min-width:0;flex:1}
.attach .atx b{font-size:13px}
.attach .rm{flex:0 0 auto;width:30px;height:30px;border-radius:99px;border:none;cursor:pointer;
  background:var(--surface-3,rgba(0,0,0,.06));color:var(--ink);font-size:14px;line-height:1}
.attach .rm:hover{background:var(--danger);color:#fff}
.chatbar input{flex:1;font:inherit;padding:11px 13px;border:1px solid var(--line);border-radius:var(--r-full);background:var(--surface-2);
  transition:border-color var(--dur-fast),box-shadow var(--dur-fast),background var(--dur-fast)}
.chatbar input:focus{outline:none;border-color:var(--accent);background:var(--card);box-shadow:0 0 0 4px var(--accent-ring)}
.chatbar .primary{padding:11px 18px;font-size:15px}
.loadmore{text-align:center;color:var(--accent-strong);font-weight:600;font-size:13px;padding:8px;cursor:pointer}
#epMsgs{max-height:45vh;overflow-y:auto}
.editbox{display:flex;gap:6px;margin-top:4px}
.editbox input{flex:1;font:inherit;padding:8px 10px;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--card)}
.editbox .primary{padding:8px 12px;font-size:14px}

/* address autocomplete + tags */
.suggest{display:flex;flex-direction:column;gap:6px;margin-top:6px}
.sugg{background:var(--card);border:1px solid var(--line);border-radius:var(--r);padding:11px 12px;font-size:14px;cursor:pointer;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:background var(--dur-fast),border-color var(--dur-fast)}
.sugg:hover{background:var(--surface-2);border-color:var(--line-2)}
.sugg:active{background:var(--surface-3)}
.priv-tag{display:inline-block;background:var(--warn-bg);color:var(--warn-ink);border-radius:8px;padding:1px 7px;font-size:11px;font-weight:700}

/* guest-management / co-host controls */
.rmx{color:var(--danger);cursor:pointer;margin-left:4px;font-size:12px;font-weight:700}
.cox{color:var(--accent-2);cursor:pointer;margin-left:5px;font-size:11px;font-weight:700}

/* icon + filter chips */
.iconin{font-size:18px;text-align:center}
.iconpick,.avpick{display:flex;flex-wrap:wrap;gap:6px;margin:2px 0 4px}
.iconbtn,.avbtn{font-size:18px;line-height:1;padding:6px 8px;border:1px solid var(--line);background:var(--card);border-radius:var(--r-sm);cursor:pointer;
  transition:transform var(--dur-fast) var(--spring),border-color var(--dur-fast),background var(--dur-fast),box-shadow var(--dur-fast)}
.iconbtn:active,.avbtn:active{transform:scale(.9)}
.iconbtn.on,.avbtn.on{border-color:var(--accent);background:var(--accent-soft);box-shadow:0 0 0 3px var(--accent-ring);transform:scale(1.06)}
.filterbar{display:flex;gap:8px;overflow-x:auto;padding:8px 12px 4px;-webkit-overflow-scrolling:touch}
.filterbar::-webkit-scrollbar{display:none}
/* Desktop/mouse: a vertical wheel can't scroll a horizontal strip, so let the
   chips WRAP to as many rows as needed — every category stays visible and
   clickable. Touch devices keep the single-row swipe-scroll above. */
@media (hover:hover) and (pointer:fine){ .filterbar{flex-wrap:wrap;overflow-x:visible} }
/* ghost chips: hairline + transparent at rest so two stacked chip rows read as
   options, not a wall of white pills - only the SELECTED chip gets filled */
.fchip{flex:0 0 auto;font-size:13px;font-weight:600;padding:7px 12px;border:1px solid var(--line-2);background:transparent;color:var(--ink-2);
  border-radius:var(--r-full);cursor:pointer;white-space:nowrap;transition:background var(--dur-fast),color var(--dur-fast),border-color var(--dur-fast),transform var(--dur-fast)}
.fchip:hover{background:var(--surface-2)}
.fchip:active{transform:scale(.94)}
.fchip.on{background:var(--grad);border-color:transparent;color:#fff;box-shadow:var(--shadow-1);font-weight:650}

/* zip jump */
.ziprow{display:flex;align-items:center;gap:8px;padding:6px 0}
.ziprow input{flex:1;min-width:0;font:inherit;padding:9px 11px;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface-2);
  transition:border-color var(--dur-fast),box-shadow var(--dur-fast),background var(--dur-fast)}
.ziprow input:focus{outline:none;border-color:var(--accent);background:var(--card);box-shadow:0 0 0 4px var(--accent-ring)}
.ziprow .primary{padding:9px 15px;font-size:14px;flex:0 0 auto}

/* ---- floating map buttons (layers / search / compass) -------------------- */
.locate{position:absolute;top:calc(60px + var(--safe-top));right:10px;z-index:2;width:40px;height:40px;padding:0;
  border:1px solid var(--line);border-radius:12px;background:var(--card);color:var(--ink-2);
  display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:var(--shadow-2);
  transition:transform var(--dur-fast) var(--spring),color var(--dur-fast),border-color var(--dur-fast)}
.locate:hover{color:var(--accent)}
.locate:active{transform:scale(.9)}
/* .on / .locating lived here for the removed 🎯 button (lit once located, pulsed
   while fixing). Nothing sets either now — the compass has its own `.persp`
   state. The keyframes stay: #shLiveBtn.on still animates on them. */
@keyframes locate-pulse{50%{opacity:.45}}

/* ---- toast --------------------------------------------------------------- */
.toast{position:fixed;left:50%;bottom:calc(30px + var(--safe-bottom));transform:translateX(-50%) translateY(0);
  background:var(--header);color:var(--header-ink);padding:11px 18px;border-radius:var(--r-full);font-size:14px;font-weight:600;
  z-index:200;max-width:90%;box-shadow:var(--shadow-3);border:1px solid var(--header-line);
  animation:toast-in var(--dur) var(--spring)}

/* ---- notifications ------------------------------------------------------- */
.notif-dot{position:absolute;top:2px;right:4px;width:9px;height:9px;border-radius:50%;background:var(--danger);
  border:2px solid var(--card);animation:pulse-dot 1.8s ease-in-out infinite}
.notif-panel{position:absolute;top:calc(52px + var(--safe-top));right:10px;width:min(340px,calc(100vw - 20px));max-height:60vh;
  overflow-y:auto;background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--shadow-3);z-index:60;
  animation:pop-in var(--dur) var(--spring);transform-origin:top right}
.notif-panel .nitem{padding:11px 13px;border-bottom:1px solid var(--line);cursor:pointer;transition:background var(--dur-fast)}
.notif-panel .nitem:last-child{border-bottom:none}
.notif-panel .nitem:hover{background:var(--surface-2)}
.notif-panel .nitem.unread{background:var(--accent-soft)}
.notif-panel .nitem .t{font-weight:700;font-size:14px}
.notif-panel .nitem .b{font-size:13px;color:var(--ink-2)}
.notif-panel .nitem .w{margin-top:2px;color:var(--muted)}
.notif-panel .empty{padding:20px;text-align:center;color:var(--muted)}
/* what's-new hub rows (unread chats / DMs / followers) */
.ns-h{padding:9px 13px 4px;font-size:11px;font-weight:800;letter-spacing:.5px;text-transform:uppercase;color:var(--muted)}
.ns-row{display:flex;align-items:center;gap:10px}
.ns-ic{flex:0 0 auto;font-size:18px;line-height:1}
.ns-tx{flex:1;min-width:0;display:flex;flex-direction:column}
.ns-tx b{font-size:13.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ns-prev{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ns-ago{flex:0 0 auto}
.ns-x{flex:0 0 auto;width:24px;height:24px;padding:0;border:none;background:transparent;color:var(--muted);
  font-size:13px;line-height:1;border-radius:99px;cursor:pointer;opacity:.55}
.ns-x:hover{opacity:1;background:var(--surface-3);color:var(--ink)}
#notifToasts{position:absolute;top:calc(56px + var(--safe-top));right:10px;display:flex;flex-direction:column;gap:8px;z-index:70;
  width:min(320px,calc(100vw - 20px));pointer-events:none}
.ntoast{pointer-events:auto;background:var(--card);border:1px solid var(--line);border-left:4px solid var(--accent);
  border-radius:var(--r);padding:11px 13px;box-shadow:var(--shadow-3);cursor:pointer;animation:pop-in var(--dur) var(--spring)}
.ntoast .t{font-weight:700;font-size:14px}
.ntoast .b{font-size:13px;color:var(--ink-2)}
.ntoast .w{font-size:12px;color:var(--muted);margin-top:2px}
.ntoast.bye{opacity:0;transform:translateX(12px);transition:all .35s var(--ease)}
.tabdot{display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--danger);margin-left:6px;vertical-align:middle;
  animation:pulse-dot 1.8s ease-in-out infinite}

/* ---- onboarding ---------------------------------------------------------- */
.onboard{position:fixed;inset:0;background:rgba(8,11,20,.55);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
  display:flex;align-items:center;justify-content:center;z-index:100;padding:18px;animation:fade-in var(--dur) var(--ease)}
.onboard-card{background:var(--card);border-radius:var(--r-xl);padding:22px;max-width:400px;width:100%;
  box-shadow:var(--shadow-3);border:1px solid var(--line);position:relative;animation:pop-in .32s var(--spring)}
.onboard-card h2{margin:.1em 0 .3em;font-size:22px;font-weight:800;letter-spacing:-.3px}
.onboard-x{position:absolute;top:10px;right:12px;border:none;background:none;font-size:18px;color:var(--muted);cursor:pointer;
  width:30px;height:30px;border-radius:99px;transition:background var(--dur-fast)}
.onboard-x:hover{background:var(--surface-2)}
.ob-opt{display:flex;gap:12px;align-items:center;width:100%;text-align:left;background:var(--card);border:1px solid var(--line);
  border-radius:var(--r-lg);padding:13px;margin:9px 0;cursor:pointer;font:inherit;color:var(--ink);
  transition:border-color var(--dur-fast),background var(--dur-fast),transform var(--dur-fast)}
.ob-opt:hover{border-color:var(--accent);background:var(--accent-soft)}
.ob-opt:active{transform:scale(.98)}
.ob-opt.static,.ob-opt.static:hover{cursor:default;border-color:var(--line);background:var(--card);transform:none}
.ob-opt .obe{font-size:22px;line-height:1;flex:0 0 auto;width:40px;height:40px;display:flex;align-items:center;justify-content:center;
  background:var(--accent-soft);border-radius:12px}

/* ---- install hint banner ------------------------------------------------- */
.install-hint{position:fixed;left:50%;transform:translateX(-50%);bottom:calc(16px + var(--safe-bottom));
  width:min(440px,calc(100vw - 24px));background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);
  box-shadow:var(--shadow-3);z-index:210;display:flex;align-items:center;gap:12px;padding:12px 14px;
  animation:sheet-up .34s var(--spring)}
.install-hint .ic{width:42px;height:42px;flex:0 0 auto;border-radius:12px;background:var(--grad);display:flex;align-items:center;
  justify-content:center;font-size:22px;box-shadow:var(--shadow-1)}
/* showing the real app icon: drop the gradient tile, or a transparent pin
   sits on a second gradient and the mark disappears into it */
.install-hint .ic.art{background:none;box-shadow:none}
.install-hint .tx{flex:1;min-width:0}
.install-hint .tx b{font-size:14px}
.install-hint .tx span{display:block;font-size:12.5px;color:var(--muted);margin-top:1px}
.install-hint .x{color:var(--muted);font-size:18px;padding:4px;border-radius:8px;flex:0 0 auto}
/* iOS Add-to-Home-Screen walkthrough (opened from the install hint) */
.ios-guide{position:fixed;inset:0;z-index:420;background:rgba(4,6,12,.55);display:flex;align-items:flex-end;
  justify-content:center;animation:fade-in .18s ease}
.ios-guide .ig-card{width:min(480px,100vw);background:var(--card);border:1px solid var(--line);
  border-radius:var(--r-lg) var(--r-lg) 0 0;padding:20px 18px calc(18px + var(--safe-bottom));
  box-shadow:var(--shadow-3);animation:ig-up .34s var(--spring)}
/* sheet-up bakes in translateX(-50%) for the left:50% hint — this card is
   flex-centered, so it needs its own slide */
@keyframes ig-up{from{opacity:0;transform:translateY(18px)}to{opacity:1;transform:none}}
.ios-guide .ig-title{font-size:17px;font-weight:800}
.ios-guide .ig-sub{font-size:13px;color:var(--muted);margin:4px 0 14px}
.ios-guide .ig-step{display:flex;align-items:center;gap:12px;padding:10px 0}
.ios-guide .ig-step+.ig-step{border-top:1px solid var(--line)}
.ios-guide .ig-ic{width:46px;height:46px;flex:0 0 auto;border-radius:12px;background:var(--surface-2);
  display:flex;align-items:center;justify-content:center;font-size:22px;color:var(--accent-strong)}
.ios-guide .ig-step b{display:block;font-size:14.5px}
.ios-guide .ig-step span:not(.ig-ic){display:block;font-size:12.5px;color:var(--muted);margin-top:1px}
.ios-guide .ig-go{width:100%;margin-top:14px;padding:12px}

/* ---- legacy surfaces (kept for safety, themed) --------------------------- */
.group-head,.pagehead{display:flex;align-items:center;gap:10px;padding:12px 14px;
  background:linear-gradient(180deg,var(--header),var(--header-2));color:var(--header-ink)}
.pagehead strong{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:16px}
.group-head .share{margin-left:auto;font-size:12px;display:flex;align-items:center;gap:6px;color:#cbd5e1}
.group-head .ghost,.pagehead .ghost{background:rgba(255,255,255,.12);border-color:var(--header-line);color:var(--header-ink)}
.tabbar{display:flex;background:var(--card);border-top:1px solid var(--line);padding-bottom:var(--safe-bottom)}
.tabbar button{flex:1;display:flex;flex-direction:column;align-items:center;gap:2px;padding:8px 0 10px;font-size:22px;color:var(--muted)}
.tabbar button span{font-size:11px;font-weight:600}
.tabbar button.active{color:var(--accent)}

/* ---- map markers (misc, non-critical) ------------------------------------ */
.me-dot{width:16px;height:16px;border-radius:50%;background:var(--ok);border:3px solid #fff;box-shadow:0 0 0 2px color-mix(in srgb,var(--ok) 40%,transparent)}
.member-dot,.live-dot{width:15px;height:15px;border-radius:50%;background:var(--accent);border:3px solid #fff;box-shadow:0 0 0 2px var(--accent-ring)}
.live-dot.me{background:var(--ok);box-shadow:0 0 0 2px color-mix(in srgb,var(--ok) 40%,transparent)}
/* Perched above the dot, clear of the heading arrow's northward swing — `top`
   is paired with ARROW_ORBIT in app.js, which derives the clearance; change
   neither alone. The explicit line-height pins the tag's height at 14px (12+2
   padding) whatever emoji the name/mode string carries: the clearance is
   measured against that, and a taller line box would silently eat the gap. */
.live-tag{position:absolute;top:-32px;left:50%;transform:translateX(-50%);background:var(--header);color:#fff;font-size:10px;line-height:12px;padding:1px 6px;border-radius:6px;white-space:nowrap}
/* travel-direction arrow: a triangle that orbits the dot to point the way they go
   (its rotate()+offset are set inline from the reported heading) */
.live-arrow{position:absolute;left:50%;top:50%;width:0;height:0;
  border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:10px solid var(--accent);
  transform-origin:50% 50%;filter:drop-shadow(0 0 1.5px #fff) drop-shadow(0 1px 1.5px rgba(0,0,0,.4));pointer-events:none}
.live-dot.me .live-arrow,.me-dot .live-arrow{border-bottom-color:var(--ok)}
.vn-pin{width:18px;height:18px;border-radius:5px;background:var(--accent-2);border:2px solid #fff;box-shadow:0 2px 6px rgba(0,0,0,.3);cursor:pointer;transform:rotate(45deg);position:relative}

/* ==========================================================================
   PIN GEOMETRY — CRITICAL, DO NOT ALTER SIZES/POSITIONS
   ========================================================================== */
/* SAFETY NET: EVERY MapLibre marker element (event pins, the create/edit pin,
   the "you are here" dot, live-location dots) MUST be absolutely positioned, or
   it falls into normal document flow and stacks below the pins before it. This
   !important beats any stylesheet load-order accident (this is what once let
   .pin-wrap's `relative` win over MapLibre's own `absolute`). Do not remove. */
.maplibregl-marker{position:absolute !important}
.ev-pin{position:relative;width:22px;height:22px;border-radius:50% 50% 50% 0;transform:rotate(-45deg);
  border:2px solid #fff;box-shadow:0 3px 8px rgba(0,0,0,.32)}
.ev-pin.priv{border:2px dashed #fbbf24}
/* selected event: pop its OWN pin with a white gap + solid accent ring + lift so
   it's unmistakable among the other pins (we no longer move the map on select) */
.ev-pin.focus{transform:rotate(-45deg) scale(1.14);
  box-shadow:0 0 0 3px #fff,0 0 0 6px var(--accent),0 6px 16px rgba(0,0,0,.5);z-index:4}
/* springy select/deselect: the CRITICAL geometry values above are untouched,
   this only animates BETWEEN those states. `translate` is a SEPARATE property
   from `transform` and has to be named separately — it is how perspective mode
   rotate(-45deg)/scale that lives on `transform`. (The `translate` transition is
   vestigial now that pins no longer lift in perspective mode — kept because
   `translate` is still the safe channel for any future marker offset here.) */
.ev-pin{transition:transform var(--dur-fast) var(--spring),box-shadow var(--dur-fast) var(--ease),
                   translate var(--dur) var(--ease)}
.pin-emoji{position:absolute;top:50%;left:50%;transform:translate(-50%,-58%) rotate(45deg);font-size:13px;line-height:1;pointer-events:none}
.pin-badge{position:absolute;top:-12px;right:-12px;transform:rotate(45deg);font-size:11px;line-height:1;background:var(--card);
  border:1px solid var(--line);border-radius:999px;padding:2px 3px;box-shadow:0 1px 4px rgba(2,6,23,.25)}
.vn-count{position:absolute;top:-8px;right:-9px;transform:rotate(-45deg);background:var(--header);color:#fff;border-radius:999px;
  font-size:10px;font-weight:700;min-width:16px;height:16px;line-height:16px;text-align:center;padding:0 3px}
/* MUST be position:absolute + this exact size — see history in git.
   width = pin's 22px; height = pin-centre(11) + rotated tip reach(≈15.56) = 26.56 */
.pin-wrap{position:absolute;line-height:0;width:22px;height:26.56px}
/* favorited venue (pin cluster): gold ring + a star riding the pin's top-left
   (counter-rotated 45° so it reads upright on the -45° teardrop) */
.ev-pin.fav{box-shadow:0 0 0 2px #fff,0 0 0 4px #f5b301,0 3px 8px rgba(0,0,0,.32)}
.fav-star{position:absolute;top:-11px;left:-11px;transform:rotate(45deg);font-size:12px;line-height:1;
  color:#f5b301;text-shadow:0 1px 2px rgba(0,0,0,.4);pointer-events:none}

/* ==========================================================================
   PERSPECTIVE MODE — markers on the ground
   ==========================================================================
   Event pins used to LIFT 20px off their anchor with a stem drawn back down to
   it, to sell depth under a 55° pitch. It read as clutter rather than as depth:
   on a map with any density the stems are a field of vertical lines, and the
   thing they point at — the anchor — was never in doubt. Pins now sit on their
   anchor in this mode exactly as they do north-up, so nothing moves when you
   tilt in and there is one less thing between you and the street.

   The dots below still stand up. They mark WHERE PEOPLE ARE rather than where a
   thing is, their stem is 12px rather than 20px, and there are a handful of them
   on screen rather than a screenful — so the depth cue still earns its keep. */
/* Live locations (other people, and your own dot) stand up. These
   elements ARE the marker element, so `translate` is the only way to lift them:
   their `transform` is MapLibre's and is rewritten every frame.

   The geometry is fiddlier than the pins' because these markers are anchored at
   their CENTRE, not their foot, and because a `top:%` on the pseudo resolves
   against the dot's PADDING box — i.e. inside its 3px white ring. Reading it out
   for the 16px me-dot, with the ground point at P: the dot spans P-25..P-9 once
   lifted, its padding box ends at P-12, so a 12px stem from there lands exactly
   on P and the shadow needs 9px more to sit centred on it. */
#map.persp .me-dot,#map.persp .live-dot{translate:0 -17px}
#map.persp .me-dot::before,#map.persp .live-dot::before{content:"";position:absolute;left:50%;top:100%;
  width:2px;height:12px;margin-left:-1px;border-radius:2px;pointer-events:none;
  background:linear-gradient(to bottom,rgba(2,6,23,.45),rgba(2,6,23,.06));
  animation:stem-up var(--dur) var(--ease)}
#map.persp .me-dot::after,#map.persp .live-dot::after{content:"";position:absolute;left:50%;top:calc(100% + 9px);
  width:14px;height:6px;margin-left:-7px;border-radius:50%;background:rgba(2,6,23,.28);
  filter:blur(1.5px);pointer-events:none;animation:fade-in var(--dur) var(--ease)}
@keyframes stem-up{from{height:0;opacity:0}}
/* ---- the vantage point, when perspective mode isn't standing on you ----
   A dashed ring rather than a solid dot, so it never reads as another person on
   the map: it is a place the camera is standing, not somebody. Deliberately NOT
   given the stand-up lift above — this one belongs ON the ground, because what
   it marks is the ground point every range chip is measured from. */
.persp-anchor{width:28px;height:28px;border-radius:50%;pointer-events:none;
  border:2px dashed var(--accent);background:color-mix(in srgb,var(--accent) 15%,transparent);
  animation:fade-in var(--dur) var(--ease)}
.persp-anchor::after{content:"";position:absolute;left:50%;top:50%;width:7px;height:7px;
  margin:-3.5px 0 0 -3.5px;border-radius:50%;background:var(--accent);box-shadow:0 0 0 2px #fff}
.persp-anchor b{position:absolute;left:50%;top:32px;transform:translateX(-50%);white-space:nowrap;
  font-size:10px;line-height:15px;font-weight:700;letter-spacing:.2px;padding:0 7px;border-radius:999px;
  background:rgba(2,6,23,.68);color:#fff;box-shadow:0 1px 4px rgba(2,6,23,.35)}
/* the dots' lift animates, so entering and leaving the mode reads as one move
   with the camera's tilt rather than a pop */
.me-dot,.live-dot{transition:translate var(--dur) var(--ease)}
/* ---- range chip: how far that pin is from YOU (perspective mode only) ----
   Sits at the pin's anchor, reading as a label on the
   ground rather than another badge crowding the pin. Deliberately small and
   monochrome: it must be glanceable at a walking pace and invisible otherwise. */
.pin-dist{position:absolute;left:50%;top:100%;translate:-50% 5px;
  font-size:10px;line-height:14px;font-weight:700;letter-spacing:.2px;white-space:nowrap;
  padding:0 6px;border-radius:999px;background:rgba(2,6,23,.66);color:#fff;
  font-variant-numeric:tabular-nums;pointer-events:none;
  box-shadow:0 1px 4px rgba(2,6,23,.35);animation:fade-in var(--dur-fast) var(--ease)}

/* ---- popover menu -------------------------------------------------------- */
.popmenu{position:fixed;z-index:1300;background:var(--card);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--shadow-3);padding:6px;min-width:190px;max-width:78vw;display:flex;flex-direction:column;gap:2px;
  animation:pop-in var(--dur-fast) var(--spring)}
.popitem{display:block;width:100%;text-align:left;background:none;border:0;padding:11px 13px;border-radius:var(--r-sm);
  font:inherit;font-weight:600;color:var(--ink);cursor:pointer;white-space:nowrap;transition:background var(--dur-fast)}
.popitem:hover,.popitem:active{background:var(--surface-2)}
/* One-of-a-set menus (date filter, basemap): every row gets a check gutter so
   the labels stay aligned whether or not they are the current choice, and the
   selected one is marked three ways — tick, accent colour and a tinted row —
   so it reads at a glance and does not rely on colour alone. */
.popmenu.checkable .popitem{position:relative;padding-left:32px}
.popmenu.checkable .popitem.sel{color:var(--accent-strong);background:var(--accent-soft)}
.popmenu.checkable .popitem.sel::before{content:"✓";position:absolute;left:11px;top:50%;
  transform:translateY(-50%);font-weight:800}
[dir="rtl"] .popmenu.checkable .popitem{padding-left:13px;padding-right:32px}
[dir="rtl"] .popmenu.checkable .popitem.sel::before{left:auto;right:11px}

/* ---- device linking modal (🔗 Devices) ------------------------------------ */
.devmodal-ov{position:fixed;inset:0;z-index:1200;background:rgba(4,6,12,.55);
  display:flex;align-items:center;justify-content:center;padding:16px}
.devmodal{width:min(420px,94vw);background:var(--card);border:1px solid var(--line);
  border-radius:var(--r-xl);box-shadow:var(--shadow-3);padding:16px;display:flex;
  flex-direction:column;gap:12px;animation:msg-in var(--dur) var(--ease)}
.devmodal .dm-head{display:flex;align-items:center;justify-content:space-between}
.dm-tools{display:flex;gap:6px}
.dm-block:hover{background:color-mix(in srgb, var(--danger) 18%, var(--surface-2))}
.devmodal .dm-head .rm{width:30px;height:30px;border-radius:99px;border:none;cursor:pointer;
  background:var(--surface-2);color:var(--ink)}
.dm-list{display:flex;flex-direction:column;gap:6px}
.dm-fp{border-top:1px solid var(--line);padding-top:8px;margin-top:4px;font-variant-numeric:tabular-nums}
/* Notifications help/status sheet (⚙ → Notifications) */
.notifmodal-ov .devmodal{width:min(360px,94vw)}
.nm-body{display:flex;flex-direction:column;gap:10px}
.nm-state{font-weight:700;font-size:15px}
.nm-state.ok{color:var(--ok)}
.nm-body p{margin:0}
.nm-body .primary{align-self:flex-start}
.nm-tip{border-top:1px solid var(--line);padding-top:10px}
/* disconnected thread: the composer's replacement notice */
.dm-disc{flex:0 0 auto;padding:11px 14px calc(11px + var(--safe-bottom));font-size:12.5px;line-height:1.45;
  color:var(--ink-2);background:var(--surface-2);border-top:1px solid var(--line);text-align:center}
/* one-time "turn on notifications" bar inside a DM */
.dm-notice{display:flex;align-items:center;gap:8px;padding:7px 10px;
  background:var(--accent-soft);border-bottom:1px solid var(--line);font-size:12.5px}
.dm-notice span{flex:1;min-width:0}
.dm-notice .nn-go{border:none;background:var(--accent);color:#fff;padding:5px 11px;
  border-radius:99px;font-size:12px;font-weight:600;cursor:pointer;white-space:nowrap}
.dm-notice .nn-x{border:none;background:transparent;color:var(--muted);cursor:pointer;font-size:14px;padding:2px 4px}
/* ---- connections (People card in My events) + 1:1 DM thread ---- */
/* Events / People tabs live on the My-events header row (next to Devices /
   Calendar); the two panels sit in a horizontal swipe track. */
.hdr-spacer{flex:1 1 auto}
/* the CLIP must be on the stationary parent (#sheetHome), not the moving track:
   overflow on the track clips the panel translateX slides into view (People). */
#sheetHome{overflow:hidden}
/* (the old two-page mine-pages track was superseded by the four-page
   .home-pages track — see the bottom-sheet section) */
.mtab{font:inherit;font-size:13.5px;font-weight:650;padding:7px 14px;border:1px solid var(--line-2);
  background:transparent;color:var(--ink-2);border-radius:var(--r-full);cursor:pointer;
  display:inline-flex;align-items:center;gap:6px;transition:background var(--dur-fast),color var(--dur-fast),border-color var(--dur-fast)}
.mtab:hover{background:var(--surface-2)}
.mtab.active{background:var(--grad);border-color:transparent;color:#fff}
.mt-badge{background:var(--danger);color:#fff;font-size:11px;font-weight:700;min-width:16px;height:16px;
  padding:0 5px;border-radius:99px;display:inline-flex;align-items:center;justify-content:center}
.mtab.active .mt-badge{background:rgba(255,255,255,.3)}
/* People panel: a full scroll list (a swipe-track sibling of the events list) */
.people{padding:2px 10px 8px;padding-bottom:var(--safe-bottom)}
.people .ppl-t{font-weight:700;font-size:14px;margin-bottom:2px}
.ppl-row{display:flex;align-items:center;gap:8px;padding:7px 0}
.ppl-row+.ppl-row{border-top:1px solid var(--line)}
.ppl-av{font-size:18px;line-height:1}
.ppl-who{flex:1;min-width:0;display:flex;flex-direction:column}
.ppl-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600;font-size:14px}
.ppl-sub{font-size:11.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ppl-x{padding:6px 9px;font-size:12.5px;flex:0 0 auto}
.ppl-dm{padding:6px 12px;font-size:13px}
.ppl-act{padding:6px 10px;font-size:12.5px}
.ppl-ct{padding:6px 8px;font-size:14px;line-height:1;flex:0 0 auto}
.ppl-ct.linked{color:var(--accent)}
.ppl-search{width:100%;box-sizing:border-box;margin:2px 0 6px;font:inherit;font-size:13px;
  padding:8px 10px;border:1px solid var(--line);border-radius:10px;background:var(--card);color:var(--ink)}
/* People-as-messaging-list bits */
.ppl-bar{display:flex;align-items:center;gap:8px;margin:2px 0 6px}
.ppl-bar .ppl-search{flex:1;margin:0}
.ppl-newgrp{flex:0 0 auto;font-size:12.5px;padding:7px 11px;white-space:nowrap}
.ppl-row.thread{cursor:pointer;border-radius:12px;padding:8px 6px}
.ppl-row.thread:hover{background:var(--surface-2)}
.ppl-prev{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ppl-ago{flex:0 0 auto;font-size:11px}
.ppl-sec{margin:10px 2px 2px;font-size:10.5px;font-weight:800;letter-spacing:.5px}
.ppl-stale{padding:6px 8px;margin:2px 0 4px;border:1px dashed var(--line);border-radius:10px;font-size:11.5px;text-align:center}
.ppl-blocked-toggle{display:block;width:100%;text-align:left;border:none;border-top:1px solid var(--line);
  background:transparent;color:var(--muted);font:inherit;font-size:12px;padding:10px 4px;cursor:pointer;margin-top:8px}
.ppl-blocked-toggle:hover{color:var(--ink)}
/* new-group modal */
.groupmodal-ov .devmodal{width:min(360px,94vw);max-height:80vh}
/* live thread list: unread emphasis + "just got fresher" flash */
.ppl-row.thread.unread{box-shadow:inset 3px 0 0 var(--accent)}
.ppl-row.thread.unread .ppl-name{font-weight:800}
.ppl-fresh{animation:ppl-fresh 1.6s ease}
@keyframes ppl-fresh{0%{background:var(--accent-soft)}100%{background:transparent}}
.ng-list{display:flex;flex-direction:column;gap:2px;max-height:44vh;overflow-y:auto}
.ng-row{display:flex;align-items:center;gap:9px;padding:8px 6px;border-radius:10px;cursor:pointer;font-size:14px}
.ng-row:hover{background:var(--surface-2)}
.ng-row input{width:17px;height:17px;flex:0 0 auto}
/* already-invited connection in the 🎟 picker: visible but greyed + not selectable */
.ng-row.ip-invited{opacity:.55;cursor:default}
.ng-row.ip-invited:hover{background:none}
.ip-done{margin-left:auto;color:var(--accent-strong)}
/* person card: destructive actions live at the bottom, separated */
.pp-danger{display:flex;gap:8px;border-top:1px solid var(--line);padding-top:10px}
.pp-danger button{font-size:12.5px;padding:7px 11px}
.pp-block.danger{color:var(--danger)}
.react.known{border-color:var(--accent)}
/* guest chips open a card now — signal it */
.react.tappable{cursor:pointer}
.react.tappable:hover{background:var(--surface-3)}
.guestmodal-ov .devmodal{width:min(340px,94vw)}
.guestmodal-ov .pp-acts{flex-wrap:wrap}
.gc-kick.danger{color:var(--danger)}
/* tappable author lines in chats (event + group DM) */
.m > .a.tappable{cursor:pointer}
.m > .a.tappable:hover{text-decoration:underline}
/* group edit modal */
.groupeditmodal-ov .devmodal{width:min(340px,94vw)}
.ge-l{display:flex;flex-direction:column;gap:4px;font-size:12px;color:var(--muted)}
.ge-l .ppl-search{margin:0}
.ge-icon{width:90px}
.ge-swatches{display:flex;gap:8px;flex-wrap:wrap}
.ge-sw{width:26px;height:26px;border-radius:50%;border:2px solid transparent;cursor:pointer;padding:0}
.ge-sw.on{border-color:var(--ink);box-shadow:0 0 0 2px var(--card) inset}
/* member management rows (admins): name grows, actions hug the right edge */
.ge-mrow{cursor:default}
.ge-mrow b{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ge-mspace{flex:1}
.ge-mrole,.ge-mkick{font-size:12px;padding:5px 8px;flex:0 0 auto}
.ge-mkick{color:var(--danger)}
#geAddGo,#geAdd{align-self:flex-start;font-size:12.5px}
/* food menu + pickup orders (0079) */
.fd-items{display:flex;flex-direction:column;gap:4px;margin:6px 0}
.fd-row{display:flex;align-items:center;gap:8px;padding:4px 0}
.fd-name{flex:1;min-width:0;font-size:14px}
.fd-price{font-variant-numeric:tabular-nums}
.fd-qty{display:flex;align-items:center;gap:6px}
.fd-qty button{width:28px;height:28px;padding:0;border-radius:99px;font-size:16px;line-height:1}
.fd-qty b{min-width:16px;text-align:center;font-variant-numeric:tabular-nums}
.fd-del{flex:0 0 auto;color:var(--danger);padding:4px 8px}
.fd-hide{flex:0 0 auto;padding:4px 6px}
.fd-row.fd-hidden{opacity:.55}   /* a hidden menu item reads dimmer in the editor */
.fd-osum .muted{font-weight:400}
/* press-and-hold to reorder: the picked-up row lifts, the list stops scrolling.
   No text selection / iOS callout on a row, or the long-press highlights text
   instead of picking the item up (inputs stay selectable). */
.fd-row{user-select:none;-webkit-user-select:none;-webkit-touch-callout:none}
.fd-row input{user-select:text;-webkit-user-select:text}
.fd-row.fd-dragging{opacity:.9;background:var(--surface-2);border-radius:10px;box-shadow:0 3px 12px rgba(0,0,0,.22);position:relative;z-index:2}
.fd-items.fd-reordering{touch-action:none}
.fd-items.fd-reordering .fd-row{cursor:grabbing}
/* host item name + editable description stacked */
.fd-hname{display:flex;flex-direction:column;align-items:flex-start;gap:0;line-height:1.25}
.fd-iname{cursor:pointer}
.fd-idesc{cursor:pointer;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fd-idesc:empty{display:none}
/* pickup timeline strip */
.fd-timeline{display:flex;gap:6px;overflow-x:auto;-webkit-overflow-scrolling:touch;padding:6px 0 2px;margin-bottom:2px}
.fd-tl-chip{flex:0 0 auto;font-size:12px;padding:4px 9px;border-radius:999px;border:1px solid var(--line);
  background:var(--surface-2);white-space:nowrap;cursor:pointer;font-variant-numeric:tabular-nums}
.fd-tl-chip.ready{border-color:var(--accent-ring);background:var(--accent-soft)}
.fd-tl-chip .muted{font-weight:400}
.fd-order.fd-flash{animation:fd-flash 1.2s ease}
@keyframes fd-flash{0%,100%{background:transparent}25%{background:var(--accent-soft)}}
.fd-addrow{display:flex;gap:6px;margin-top:6px}
.fd-addrow input{flex:1;min-width:0;font:inherit;padding:8px 10px;border:1px solid var(--line);border-radius:10px;background:var(--surface-2)}
.fd-addrow #fdPrice{flex:0 0 84px}
.fd-addrow #fdQty{flex:0 0 64px}
.fd-rf{color:var(--danger)}
.fd-start{width:100%;justify-content:center}
.fd-recover{margin:2px 0 6px}
.fd-recover .react{cursor:pointer}
.fd-recover .btnrow{margin-top:6px}
.fd-verified{font-size:11.5px;font-weight:650;color:#059669;background:rgba(5,150,105,.12);
  border-radius:99px;padding:2px 8px;vertical-align:2px;white-space:nowrap}
/* the Stripe Payment Element can be taller than a phone viewport — the pay
   sheet must scroll or the Pay button falls off-screen */
.foodpay-ov .devmodal{max-height:88vh;overflow-y:auto;-webkit-overflow-scrolling:touch}
/* ---- accessibility ---- */
/* keyboard focus is always visible (mouse clicks stay clean via :focus-visible) */
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}
/* honor "reduce motion": kill non-essential animation/transition for vestibular
   safety — the app stays fully functional, just without the movement */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;
    transition-duration:.001ms!important;scroll-behavior:auto!important}
}
/* ---- right-to-left (Arabic) ----
   Flex/inline content mirrors automatically under dir="rtl"; the pieces that
   don't are the absolutely/fixed-anchored controls (pinned with physical
   left/right) and a few hard-coded text-align:left. Mirror just those so the
   Arabic layout reads correctly without rewriting the whole sheet. Centered
   elements (left:50% + translateX) already stay centered and need no change. */
/* The home tabs (#homePages) and the event Details⇄Chat track (.shpages) slide
   via a JS translateX that assumes LTR page order. Under dir="rtl" the flex row
   reverses, so translateX(-tab*100%) slides the WRONG pages off-screen - the
   Settings page (which holds the language switcher) vanished entirely, trapping
   Arabic users with no way back. Pin the sliding TRACKS + their tab bars to LTR
   so index→offset math stays valid, and restore rtl on each PAGE so its content
   still reads right-to-left. */
[dir="rtl"] #homePages,[dir="rtl"] #homeTabs,
[dir="rtl"] .shpages,[dir="rtl"] #shTabs{ direction:ltr }
[dir="rtl"] .home-pg,[dir="rtl"] .shpage{ direction:rtl }
/* RTL swaps the two corners: the bell moves left, so install must move right or
   the two would stack on top of each other */
[dir="rtl"] .map-topleft{ align-items:flex-end }   /* inset-inline-start already mirrors the side */
[dir="rtl"] .map-topright,
[dir="rtl"] .locate,
[dir="rtl"] .notif-panel,
[dir="rtl"] #notifToasts{ right:auto; left:10px }
[dir="rtl"] .notif-dot{ right:auto; left:4px }
[dir="rtl"] .onboard-x{ right:auto; left:12px }
[dir="rtl"] .grip-btn{ right:auto; left:8px }
[dir="rtl"] .geo-badge{ left:auto; right:8px }
[dir="rtl"] .img-expand{ right:auto; left:8px }
[dir="rtl"] .dm-thumb .rm{ right:auto; left:4px }
[dir="rtl"] .lb-x,
[dir="rtl"] .lightbox .lb-x{ right:auto; left:16px }
/* left-aligned rows should follow the reading direction */
[dir="rtl"] .ob-opt,
[dir="rtl"] .popitem,
[dir="rtl"] .ppl-blocked-toggle,
[dir="rtl"] .pp-ev,
[dir="rtl"] .poll-opt,
[dir="rtl"] .mention-opt{ text-align:right }
/* the poll fill bar grows from the start edge */
[dir="rtl"] .po-bar{ left:auto; right:0 }
/* undo button inside the toast - big tap target, clearly interactive */
.toast{display:flex;align-items:center;gap:10px}
.toast:empty{display:none}
.toast-undo{border:1px solid color-mix(in srgb, currentColor 45%, transparent);background:transparent;color:inherit;
  font:inherit;font-weight:800;padding:4px 12px;border-radius:999px;cursor:pointer;flex:0 0 auto}
.toast-undo:active{background:color-mix(in srgb, currentColor 16%, transparent)}
/* event QR modal: black-on-white card so it scans in any theme */
.qr-modal{max-width:340px;text-align:center}
.qr-box{background:#fff;padding:16px;border-radius:14px;margin:10px auto 8px;width:min(280px,72vw)}
.qr-box svg{width:100%;height:auto;display:block}
.qr-cap{margin-bottom:10px;font-weight:600}
.qr-modal .btnrow{justify-content:center}
/* language picker */
.lang-modal{max-width:340px}
.lang-list{display:flex;flex-direction:column;gap:2px;margin-top:6px;max-height:60vh;overflow-y:auto;-webkit-overflow-scrolling:touch}
.lang-opt{display:flex;align-items:center;gap:8px;width:100%;text-align:start;
  padding:11px 12px;border:0;border-radius:10px;background:transparent;color:inherit;
  font-size:16px;cursor:pointer}
.lang-opt:hover{background:rgba(127,127,127,.12)}
.lang-opt.on{background:rgba(127,127,127,.14);font-weight:600}
.lang-opt .lang-name{flex:0 0 auto}
.lang-opt .muted{margin-inline-start:auto}
.lang-opt .lang-tick{margin-inline-start:8px;color:var(--accent-strong)}
[dir="rtl"] .lang-opt{text-align:right}
/* progressive-render sentinel: invisible tripwire the IntersectionObserver
   watches to pump the next chunk of the Nearby list */
.list-sentinel{height:1px;width:100%;pointer-events:none}
/* screen-reader-only helper text */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0,0,0,0);white-space:nowrap;border:0}
/* store order actions + offline pay */
.fd-actions{display:flex;flex-direction:column;gap:6px;margin-top:4px}
.fd-actions .primary,.fd-actions .ghost{width:100%}
.fd-offline{margin:6px 0}
.fd-offline button{margin-left:6px;font-size:12px;padding:4px 8px}
.offord-note{background:var(--surface-2);border-radius:12px;padding:9px 12px;font-size:13.5px;margin:8px 0}
/* recurring-storefront series nav (prev/next occurrence) */
.series-nav{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:7px 11px;
  background:var(--surface-2);border-radius:12px;margin:2px 0}
.series-nav button{font-size:12.5px;padding:5px 9px}
.series-nav button[disabled]{opacity:.4}
/* menu item photos: thumb in the row, tap → full-size viewer */
.fd-thumb{width:44px;height:44px;object-fit:cover;border-radius:10px;flex:0 0 auto;cursor:zoom-in;background:var(--surface-2)}
.fd-imgview{position:fixed;inset:0;z-index:200;background:rgba(0,0,0,.78);display:flex;align-items:center;justify-content:center;
  padding:20px;cursor:zoom-out;animation:fade-in .15s ease}
.fd-imgview img{max-width:94vw;max-height:88vh;border-radius:14px;object-fit:contain}
/* host editor: the picture button lives on the LEFT of the row and IS the
   thumbnail (or a 📷 placeholder); a ✏️ badge shows it's editable once set */
.fd-pic{flex:0 0 auto;padding:0;border:0;background:none;cursor:pointer;position:relative;
  width:44px;height:44px;border-radius:10px;display:flex;align-items:center;justify-content:center}
.fd-pic .fd-thumb{cursor:pointer;width:44px;height:44px}
.fd-pic-add{width:44px;height:44px;display:flex;align-items:center;justify-content:center;
  border:1px dashed var(--line);border-radius:10px;background:var(--surface-2);font-size:18px;opacity:.75}
.fd-pic-edit{position:absolute;right:-4px;bottom:-4px;background:var(--card);border:1px solid var(--line);
  border-radius:99px;width:20px;height:20px;display:flex;align-items:center;justify-content:center;font-size:11px;line-height:1}
/* inline stock editor */
.fd-stock{flex:0 0 auto;display:flex;align-items:center;gap:3px}
.fd-qedit{width:46px;font:inherit;padding:5px 6px;border:1px solid var(--line);border-radius:8px;
  background:var(--surface-2);text-align:center;font-variant-numeric:tabular-nums}
.fd-qedit::-webkit-outer-spin-button,.fd-qedit::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
/* pickup-hours generator */
.ph-days{display:flex;gap:4px;flex-wrap:wrap;margin:8px 0}
.ph-day{display:flex;align-items:center;gap:4px;font-size:13px;padding:5px 8px;
  border:1px solid var(--line);border-radius:10px;cursor:pointer}
.ph-day input{width:15px;height:15px}
.pickuphours-ov label{font-size:13px;color:var(--muted);display:flex;flex-direction:column;gap:3px}
.pickuphours-ov input[type=time],.pickuphours-ov select{font:inherit;padding:7px 9px;border:1px solid var(--line);
  border-radius:10px;background:var(--surface-2);color:var(--ink)}
.fd-when{display:flex;flex-direction:column;gap:4px;margin:8px 0}
.fd-when select{font:inherit;padding:8px 10px;border:1px solid var(--line);border-radius:10px;background:var(--surface-2);color:var(--ink)}
/* host order row: head (# name + items), a meta line carrying price/time/
   status with the 💬 message button pinned to its right, then an actions row */
.fd-order{display:flex;flex-direction:column;gap:3px;padding:8px 0;border-top:1px solid var(--line)}
.fd-o-head{font-size:14px}
.fd-o-meta{display:flex;align-items:center;gap:8px}
.fd-o-meta > .muted{flex:1 1 auto;min-width:0}
.fd-o-meta .fd-dm{flex:0 0 auto;padding:3px 10px;font-size:15px;margin:0}
.fd-o-actions{display:flex;flex-wrap:wrap;gap:6px;margin-top:2px}
.fd-o-actions button{font-size:12.5px;padding:5px 9px;margin:0}
.fd-o-actions .fd-cxl{color:var(--danger)}
.fd-osearch{width:100%;font:inherit;padding:7px 10px;border:1px solid var(--line);border-radius:10px;background:var(--surface-2);color:var(--ink);margin:6px 0}
#fdPayEl{margin:10px 0}
/* buyer's "Your order" card + the shop-collapse toggle */
.fd-yours{background:var(--accent-soft);border:1px solid var(--accent-ring);border-radius:12px;padding:10px 12px;margin-bottom:8px}
.fd-yourcard{padding:6px 0;border-top:1px solid var(--line)}
.fd-yours .fd-yourcard:first-of-type{border-top:0}
.fd-yline{font-size:14px}
.fd-yourcard .fd-creq-btn,.fd-yourcard .fd-cancel-btn{font-size:12.5px;padding:5px 9px;margin-top:5px}
.fd-creq{margin-top:5px}
.fd-more{width:100%;margin-bottom:8px}
/* host: an order awaiting a cancel confirm */
.fd-order.fd-cxl-req{background:color-mix(in srgb,var(--danger) 8%,transparent);border-radius:10px;padding-left:8px;padding-right:8px}
.fd-cxl-tag{color:var(--danger)}
#shMenu #fdOrder{width:100%;margin-top:4px}

/* themed group thread: accent strip + send button take the group color */
.dm-sheet.themed{border-top:3px solid var(--dm-accent)}
.dm-sheet.themed .dm-send{background:var(--dm-accent);border-color:transparent;color:#fff}
/* ---- venue (spot) sheet: the venue lives in the back-button row; below it a
   quiet meta line + the AI one-liner about the place ---- */
.vh-blurb{padding:2px 2px 4px;font-size:12.5px;line-height:1.45;color:var(--ink-2);font-style:italic;
  opacity:0;transition:opacity .45s var(--ease)}
.vh-blurb.in{opacity:1}
/* venue sheet with no blurb: collapse the empty meta-card so there's no blank
   buffer between the title banner and the events (the old count/navigate row) */
.meta-card.spot-collapsed{display:none}
.spot-fav{font-size:17px;line-height:1;color:var(--muted)}
.spot-fav.on{color:#f5b301}
/* subtle "hide this venue from Nearby" action in the spot-sheet header */
/* each event reads as a mini flyer taped to the venue's window */
.spot-card{display:flex;align-items:center;gap:12px;padding:9px 10px;border-radius:14px;cursor:pointer}
.spot-card:hover{background:var(--surface-2)}
.spot-card:active{background:var(--surface-3);transform:scale(.985)}
.spot-card+.spot-card{margin-top:6px}
.spot-card .body{min-width:0;flex:1}
.spot-card .title{font-weight:650;font-size:15px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;letter-spacing:.1px}
.spot-card .when{font-size:12.5px;font-weight:700;color:var(--accent-strong);margin-top:2px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.spot-card .meta{margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.spot-card .go{color:var(--muted);font-size:18px;flex:0 0 auto}
.poster-thumb{flex:0 0 48px;width:48px;height:64px;border-radius:9px;overflow:hidden;position:relative;
  display:flex;align-items:center;justify-content:center;font-size:22px;
  background:linear-gradient(150deg, color-mix(in srgb, var(--pc) 45%, var(--card)), color-mix(in srgb, var(--pc) 90%, #000));
  box-shadow:0 1px 4px rgba(2,6,23,.28);border:1px solid rgba(255,255,255,.14)}
/* poster image sits ON TOP of the glyph placeholder and fades in when its
   bytes arrive — the box never changes size, so nothing jumps */
.poster-thumb .pt-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;
  opacity:0;transition:opacity .28s var(--ease, ease)}
.poster-thumb .pt-img.in{opacity:1}
/* nearby-list variant: 40px wide keeps .card's divider geometry (40 + 12 gap = 52) */
.poster-thumb.sm{flex:0 0 40px;width:40px;height:54px;border-radius:8px;font-size:19px;margin-top:2px}
/* person profile (tap a connection's name in People) */
.ppl-who.tappable{cursor:pointer}
.ppl-who.tappable:hover .ppl-name{text-decoration:underline}
.personmodal-ov .devmodal{width:min(400px,94vw);max-height:82vh;overflow-y:auto}
.pp-sub{margin-top:-6px}
.pp-acts{display:flex;flex-wrap:wrap;gap:6px}
.pp-acts button{font-size:12.5px;padding:7px 11px}
.pp-events{display:flex;flex-direction:column;gap:6px}
.pp-sec{font-size:12px;font-weight:700;color:var(--muted);text-transform:uppercase;
  letter-spacing:.4px;margin-top:6px}
.pp-ev{display:flex;align-items:center;gap:9px;width:100%;text-align:left;font:inherit;
  padding:8px 10px;border:1px solid var(--line);border-radius:12px;background:var(--surface-2);
  color:var(--ink);cursor:pointer}
.pp-ev:hover{background:var(--surface-3)}
.pp-ev-ic{font-size:17px;line-height:1;flex:0 0 auto}
.pp-ev-tx{flex:1;min-width:0;display:flex;flex-direction:column}
.pp-ev-tx b{font-size:13.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pp-ev-badges{flex:0 0 auto;font-size:13px}
/* link-a-contact modal (on-device only) */
.contactmodal-ov .devmodal{width:min(340px,94vw)}
.ct-body{display:flex;flex-direction:column;gap:10px}
.ct-body label{display:flex;flex-direction:column;gap:4px;font-size:12px;color:var(--muted)}
.ct-body input{font:inherit;padding:9px 10px;border:1px solid var(--line);border-radius:10px;background:var(--card);color:var(--ink)}
.ct-body p{margin:0}
.ct-body .btnrow{display:flex;gap:8px}
.ct-body .btnrow .primary{flex:1}
.ppl-unread{background:rgba(255,255,255,.28);padding:0 6px;border-radius:99px;font-size:11px}
.fol{cursor:pointer;color:var(--accent-strong);padding:0 2px}
/* ---- DM chat sheet: a bottom sheet over the nearby map (reuses .m/.b/.t
   message bubbles + .msgmenu picker from event chat) ---- */
.dm-ov{position:fixed;inset:0;z-index:360;display:flex;flex-direction:column;justify-content:flex-end;pointer-events:none}
.dm-sheet{pointer-events:auto;background:var(--card);border-top:1px solid var(--line);
  border-radius:var(--r-xl) var(--r-xl) 0 0;box-shadow:var(--sheet-shadow);
  height:var(--dm-h,60vh);max-height:90vh;
  display:flex;flex-direction:column;min-height:0;animation:panel-in var(--dur) var(--ease)}
/* drag grip: resize the sheet to reveal the map (pins/drawings/geo) behind it */
.dm-grip{flex:0 0 auto;position:relative;height:20px;cursor:ns-resize;touch-action:none;user-select:none;-webkit-user-select:none}
@media (min-width:900px){ .dm-sheet{width:min(460px,100%);margin:0 auto} }
.dm-head{display:flex;align-items:center;gap:10px;padding:9px 10px;border-bottom:1px solid var(--line)}
/* order-DM context strip: what this thread is about, under the title */
.dm-ordctx{display:flex;align-items:center;gap:8px;padding:7px 12px;border-bottom:1px solid var(--line);
  background:var(--accent-soft);cursor:pointer;font-size:13px;flex:0 0 auto}
.dm-ordctx .doc-ic{flex:0 0 auto}
.dm-ordctx .doc-tx{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dm-ordctx .doc-go{flex:0 0 auto;color:var(--accent-strong);font-weight:800}
.dm-ordctx:active{background:var(--accent-ring)}
.dm-close{font-size:24px;line-height:1;background:none;border:none;cursor:pointer;color:var(--ink);padding:0 6px}
.dm-peer{flex:1;min-width:0;display:flex;align-items:center;gap:8px;overflow:hidden;font-size:15px}
.dm-peer b{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dm-av{font-size:20px;line-height:1}
.dm-block{padding:6px 9px;flex:0 0 auto}
.dm-msgs{flex:1 1 auto;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:9px;padding:8px 12px}
.dm-reply{display:flex;align-items:center;gap:8px;padding:6px 12px;border-top:1px solid var(--line);background:var(--surface-2)}
.dm-reply .rp-tx{flex:1;min-width:0;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dm-reply .rm{border:none;background:none;cursor:pointer;font-size:14px;color:var(--muted)}
.dm-thumb{position:relative;padding:8px 12px 0;width:fit-content}
.dm-thumb img{max-height:90px;border-radius:10px;display:block}
.dm-att{display:inline-flex;align-items:center;gap:7px;padding:6px 10px;background:var(--surface-2);border-radius:10px;font-size:13px}
.dm-att .draw-svg{width:52px;height:26px}
.dm-thumb .rm{position:absolute;top:4px;right:4px;background:rgba(4,6,12,.72);color:#fff;border:none;border-radius:99px;width:22px;height:22px;cursor:pointer}
.dm-compose{display:flex;align-items:center;gap:6px;padding:8px 10px calc(8px + var(--safe-bottom));border-top:1px solid var(--line)}
.dm-compose .ghost{padding:8px 9px;font-size:16px;flex:0 0 auto;line-height:1}
.dm-plus{font-size:21px;font-weight:700;line-height:1}
.dm-send{flex:0 0 auto;padding:9px 15px}
.dm-in{flex:1;min-width:0;font:inherit;padding:10px;border:1px solid var(--line);border-radius:10px;
  background:var(--card);color:var(--ink)}
.dm-livedot{width:30px;height:30px;border-radius:99px;background:var(--danger);color:#fff;display:flex;
  align-items:center;justify-content:center;font-size:15px;box-shadow:0 0 0 4px rgba(225,29,72,.3),0 2px 6px rgba(0,0,0,.4)}
/* reaction chips under a bubble */
.rx{display:flex;gap:5px;margin-top:5px;flex-wrap:wrap}
.rx-chip{font:inherit;font-size:12px;padding:2px 8px;border-radius:99px;border:1px solid var(--line-2);
  background:var(--card);cursor:pointer;color:var(--ink-2)}
.rx-chip.mine{background:var(--accent-soft);border-color:var(--accent-ring);color:var(--accent-strong)}
.ep-quick{display:flex;gap:4px;padding:2px 2px 7px;border-bottom:1px solid var(--line);margin-bottom:4px;justify-content:space-around}
/* simple image lightbox for DM photos */
.lb-ov{position:fixed;inset:0;z-index:500;background:rgba(4,6,12,.92);display:flex;align-items:center;justify-content:center}
.lb-ov img{max-width:94vw;max-height:88vh;border-radius:8px}
.lb-x{position:absolute;top:14px;right:16px;background:rgba(255,255,255,.15);color:#fff;border:none;
  border-radius:99px;width:36px;height:36px;font-size:16px;cursor:pointer}
.dm-dev{display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:8px 10px;border:1px solid var(--line);border-radius:12px;font-size:14px}
.dm-you{color:var(--accent-strong);font-size:11px}
.dm-code{text-align:center;padding:10px;border:1px dashed var(--line-2);border-radius:12px}
.dm-big{font-size:30px;font-weight:800;letter-spacing:6px;margin:6px 0;font-variant-numeric:tabular-nums;user-select:all;-webkit-user-select:all}
/* code + copy button on one row; the code keeps user-select:all so a single
   tap/click still grabs the whole thing for a manual copy */
.dm-bigrow{display:flex;align-items:center;justify-content:center;gap:10px;flex-wrap:wrap}
.dm-copy{flex:0 0 auto;font-size:13px;padding:6px 10px;white-space:nowrap}
.dm-join input{flex:1;font:inherit;padding:10px 12px;border:1px solid var(--line);
  border-radius:10px;background:var(--surface-2);text-transform:uppercase;letter-spacing:2px}
/* the "no other device" way in. Full width and under the code row so it reads as
   the alternative to it, rather than competing with Link for the same glance. */
.dm-recover{width:100%;margin-top:8px}

/* ---- "who reacted" popover (long-press / right-click a reaction chip) ---- */
.reactors-pop{position:fixed;z-index:1300;background:var(--card);border:1px solid var(--line);
  border-radius:var(--r);box-shadow:var(--shadow-3);padding:6px;min-width:120px;max-width:220px;
  max-height:240px;overflow-y:auto;animation:msg-in var(--dur) var(--ease)}
.reactors-pop .rp-emoji{font-size:15px;font-weight:700;padding:3px 8px 6px;border-bottom:1px solid var(--line);margin-bottom:4px}
.reactors-pop .rp-name{font-size:14px;padding:5px 8px;border-radius:8px;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.react{cursor:pointer}

/* ---- message context menu (long-press / right-click on a chat message) ---- */
.msgmenu{position:fixed;z-index:1300;background:var(--card);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--shadow-3);padding:6px;min-width:200px;animation:msg-in var(--dur) var(--ease)}
.msgmenu .mm-emoji{display:flex;gap:2px;padding:2px 2px 7px;border-bottom:1px solid var(--line);margin-bottom:4px}
.msgmenu .mm-emoji button{border:0;background:none;font-size:20px;line-height:1;padding:6px 7px;border-radius:10px;cursor:pointer}
.msgmenu .mm-emoji button:hover{background:var(--surface-2)}
.msgmenu .mm-emoji button.on{background:var(--surface-3)}
.msgmenu .popitem{font-size:14px}
.msgmenu .popitem.danger{color:var(--danger)}
.msgmenu .mm-more{border:0;background:var(--surface-2);color:var(--muted);font-size:17px;line-height:1;
  padding:6px 8px;border-radius:10px;cursor:pointer;font-weight:700}
.msgmenu .mm-more:hover{background:var(--surface-3);color:var(--ink)}
/* full emoji picker (same popover, richer panel) */
.msgmenu.pickmode{width:min(300px,92vw);padding:8px}
.ep-head{display:flex;gap:6px;align-items:center;margin-bottom:6px}
.ep-back{flex:0 0 auto;width:30px;height:32px;border:0;border-radius:9px;background:var(--surface-2);
  color:var(--ink);font-size:18px;line-height:1;cursor:pointer;padding:0 0 2px}
.ep-back:hover{background:var(--surface-3)}
.ep-in{flex:1;min-width:0;font:inherit;font-size:14px;padding:7px 10px;border:1px solid var(--line);
  border-radius:9px;background:var(--surface-2)}
.ep-grid{display:grid;grid-template-columns:repeat(8,1fr);gap:1px;max-height:256px;overflow-y:auto;
  overscroll-behavior:contain;padding-right:2px}
.ep-cat{grid-column:1/-1;font-size:11px;font-weight:700;color:var(--muted);padding:7px 2px 2px;
  position:sticky;top:0;background:var(--card)}
.ep-e{border:0;background:none;font-size:19px;line-height:1;padding:5px 0;border-radius:8px;cursor:pointer}
.ep-e:hover{background:var(--surface-2)}
.ep-e.on{background:var(--surface-3)}
/* reply quote inside a message — tap to jump to the original */
.mq{font-size:12px;color:var(--muted);border-left:3px solid var(--accent);padding:3px 8px;margin:2px 0 4px;
  border-radius:6px;background:color-mix(in srgb,var(--accent) 8%,transparent);cursor:pointer;
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mq b{color:var(--accent-strong)}
.m.mine .mq{border-left-color:rgba(255,255,255,.7);background:rgba(255,255,255,.16);color:rgba(255,255,255,.85)}
.m.mine .mq b{color:#fff}
.mq.mq-gone{cursor:default;font-style:italic}
/* "Replying to …" bar above the composer */
.replybar{flex-basis:100%;display:flex;align-items:center;gap:8px;padding:6px 8px;
  border:1px solid var(--line);border-radius:12px;background:var(--surface-2)}
.replybar .rp-tx{flex:1;min-width:0;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.replybar .rm{flex:0 0 auto;width:26px;height:26px;border-radius:99px;border:none;cursor:pointer;
  background:var(--surface-3);color:var(--ink)}
.replybar .rm:hover{background:var(--danger);color:#fff}

/* ---- polls in chat --------------------------------------------------------- */
.poll{background:var(--card);color:var(--ink);border:1px solid var(--line);border-radius:14px;
  padding:11px;margin:4px 0;display:flex;flex-direction:column;gap:8px;min-width:min(260px,72vw)}
.m.mine .poll{margin-left:auto}          /* readable box even inside my gradient bubble */
.poll-q{font-size:14px;font-weight:700;line-height:1.3}
.poll-opts{display:flex;flex-direction:column;gap:6px}
.poll-opt{position:relative;display:block;width:100%;text-align:left;border:1px solid var(--line);
  border-radius:10px;padding:9px 11px;background:var(--surface-2);color:var(--ink);cursor:pointer;overflow:hidden;
  transition:border-color var(--dur-fast) var(--ease)}
.poll-opt:hover{border-color:var(--line-2)}
.po-bar{position:absolute;left:0;top:0;bottom:0;width:0;background:color-mix(in srgb,var(--accent) 20%,transparent);
  transition:width .35s var(--ease)}
.po-fill{position:relative;display:flex;align-items:center;justify-content:space-between;gap:10px}
.po-label{font-size:14px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.po-count{flex:0 0 auto;font-size:13px;font-weight:700;color:var(--muted);font-variant-numeric:tabular-nums}
.poll-opt.voted{border-color:var(--accent)}
.poll-opt.voted .po-label::before{content:"✓ ";color:var(--accent-strong);font-weight:800}
.poll-opt.lead .po-bar{background:color-mix(in srgb,var(--accent) 32%,transparent)}
.poll-opt.lead .po-count{color:var(--accent-strong)}
.poll-foot{padding-top:1px}

/* poll composer modal (📊 in the chat bar) */
.pollmodal-ov{position:fixed;inset:0;z-index:1200;background:rgba(4,6,12,.55);
  display:flex;align-items:center;justify-content:center;padding:16px}
.pollmodal{width:min(440px,94vw);max-height:88vh;overflow-y:auto;background:var(--card);border:1px solid var(--line);
  border-radius:var(--r-xl);box-shadow:var(--shadow-3);padding:16px;display:flex;flex-direction:column;gap:10px;
  animation:msg-in var(--dur) var(--ease)}
.pollmodal .pm-head{display:flex;align-items:center;justify-content:space-between}
.pollmodal .pm-head .rm{width:30px;height:30px;border-radius:99px;border:none;cursor:pointer;background:var(--surface-2);color:var(--ink)}
.pm-q{font:inherit;font-size:15px;font-weight:600;padding:11px 12px;border:1px solid var(--line);border-radius:12px;background:var(--surface-2)}
.pm-opts{display:flex;flex-direction:column;gap:6px}
.pm-opt{display:flex;gap:6px;align-items:center}
.pm-opt input{flex:1;min-width:0;font:inherit;padding:9px 11px;border:1px solid var(--line);border-radius:10px;background:var(--surface-2)}
.pm-opt .rm{flex:0 0 auto;width:30px;height:30px;border-radius:99px;border:none;cursor:pointer;background:var(--surface-3);color:var(--muted)}
.pm-opt .rm:hover{background:var(--danger);color:#fff}
.pm-add{align-self:flex-start;font-size:13px;padding:6px 10px}
.pm-multi{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--muted);cursor:pointer}
.pm-multi input{width:16px;height:16px}
/* long-press should open the menu, not select text or the iOS callout — touch only,
   so desktop text selection keeps working */
@media (pointer:coarse){
  #shMsgs .m,.dm-msgs .m{user-select:none;-webkit-user-select:none;-webkit-touch-callout:none}
}

/* ==========================================================================
   MOTION
   ========================================================================== */
@keyframes view-in{from{opacity:0}to{opacity:1}}
@keyframes panel-in{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
/* ---- home list ⇄ event/spot panel slide (View Transitions) --------------
   Both panes carry one name and only one is ever rendered (the other is
   display:none), so opening captures list→panel and Back captures panel→list.
   The map (root) is held still. panel-in is suppressed mid-transition so it
   doesn't double up with the slide. */
#sheetHome,#sheetPanel{view-transition-name:sheet-pane}
html[data-sheetvt]::view-transition-group(root){animation-duration:.001s}
html[data-sheetvt="fwd"]::view-transition-old(sheet-pane){animation:vt-out-left var(--dur) var(--ease) both}
html[data-sheetvt="fwd"]::view-transition-new(sheet-pane){animation:vt-in-right var(--dur) var(--ease) both}
html[data-sheetvt="back"]::view-transition-old(sheet-pane){animation:vt-out-right var(--dur) var(--ease) both}
html[data-sheetvt="back"]::view-transition-new(sheet-pane){animation:vt-in-left var(--dur) var(--ease) both}
@keyframes vt-in-right{from{transform:translateX(100%)}to{transform:translateX(0)}}
@keyframes vt-out-left{from{transform:translateX(0);opacity:1}to{transform:translateX(-22%);opacity:0}}
@keyframes vt-in-left{from{transform:translateX(-22%);opacity:0}to{transform:translateX(0);opacity:1}}
@keyframes vt-out-right{from{transform:translateX(0)}to{transform:translateX(100%)}}
/* The panes travel a FULL pane-width sideways, so on the desktop sidebar layout
   the slide would sweep straight across the map. The VT pseudos live in the top
   layer — no z-index can lift the map over them — so clip the group instead: it
   is sized to the pane's own box, and clipping it makes the panel slide out from
   under the map edge rather than over it. Harmless on mobile (full-width sheet:
   the pane was sliding off-screen there anyway). */
::view-transition-group(sheet-pane){overflow:clip}
/* Where the slide runs, panel-in must NOT also fire - that vertical rise + fade
   is the "bob and flash" on top of the horizontal slide. Kill it for the panel
   AND its body wherever View Transitions exist (they own the entrance). */
@supports (view-transition-name:none){
  #sheetPanel:not(.hidden),#sheetPanel .shbody{animation:none}
}
/* spot ⇄ event is a content change WITHIN the panel (not a home/panel swap), so
   it gets a matching directional content slide instead of the VT pane slide */
#shBody.pane-fwd{animation:pane-in-r var(--dur) var(--ease)}
#shBody.pane-back{animation:pane-in-l var(--dur) var(--ease)}
@keyframes pane-in-r{from{transform:translateX(34px);opacity:.35}to{transform:none;opacity:1}}
@keyframes pane-in-l{from{transform:translateX(-34px);opacity:.35}to{transform:none;opacity:1}}
@keyframes msg-in{from{opacity:0;transform:translateY(6px) scale(.98)}to{opacity:1;transform:none}}
@keyframes pop-in{from{opacity:0;transform:scale(.94) translateY(-4px)}to{opacity:1;transform:none}}
@keyframes fade-in{from{opacity:0}to{opacity:1}}
@keyframes toast-in{from{opacity:0;transform:translateX(-50%) translateY(14px) scale(.96)}to{opacity:1;transform:translateX(-50%) translateY(0) scale(1)}}
@keyframes sheet-up{from{opacity:0;transform:translateX(-50%) translateY(16px)}to{opacity:1;transform:translateX(-50%) translateY(0)}}
@keyframes pulse-dot{0%,100%{box-shadow:0 0 0 0 color-mix(in srgb,var(--danger) 55%,transparent)}50%{box-shadow:0 0 0 5px transparent}}

/* ==========================================================================
   RESPONSIVE — centred phone frame on wide screens
   ========================================================================== */
@media (max-width:410px){
  .brandmark{width:18px;height:21px}
  .map-brand .wordmark{font-size:15px}
  .home-head .mtab{padding:7px 8px;font-size:12px}
}
@media (max-width:340px){ .map-brand .wordmark{display:none} }   /* mark-only when truly tiny */

/* Desktop / large tablet: leverage the big screen — a full-height map with a
   docked sidebar (the mobile bottom-sheet becomes a left rail). */
@media (min-width:900px){
  #app{height:100dvh;width:100%}

  /* NEARBY: sidebar (list / event panel) at the left, big map filling the rest */
  #view-nearby{flex-direction:row}
  #view-nearby .sheet{
    order:-1;flex:0 0 auto;width:var(--panel-w,clamp(372px,30vw,448px));height:100%;max-height:none;
    border-radius:0;border-top:none;border-right:1px solid var(--line);box-shadow:10px 0 34px rgba(2,6,23,.10)}
  /* The panel takes the LEFT of the view on desktop (order:-1), so a CTA at
     left:10px lands on top of it. "Top left of the MAP" starts where the panel
     ends. --panel-w is set on #view-nearby (the shared parent) so this can read
     the dragged width; the fallback matches the panel's own default. Logical
     inset so RTL - where the flex row puts the panel on the right - mirrors it
     for free. */
  #view-nearby .map-topleft{inset-inline-start:calc(var(--panel-w,clamp(372px,30vw,448px)) + 10px)}
  /* collapsed to the 26px rail → follow it back in */
  #view-nearby:has(.sheet.minimized) .map-topleft{inset-inline-start:36px}
  #view-nearby .sheet .sheet-grip{display:none}      /* vertical grip is a mobile thing */
  #view-nearby .sheet.expanded{max-height:none}
  #view-nearby .sheet.resized{height:100%;max-height:none}   /* mobile drag-height never applies here */
  #view-nearby .sheet .sheet-head{padding-top:15px}
  /* the drag rail STRADDLES the pane/map boundary (11px each side) so there's a
     fat, forgiving hitbox — needs overflow:visible on the desktop sheet, which
     is safe here (no rounded corners to clip, minimized hides via display:none) */
  #view-nearby .sheet{overflow:visible}
  /* the drag rail sits entirely on the MAP side of the boundary (left edge flush
     with the pane border). It used to straddle 11px INTO the pane, which parked
     it on top of the RSVP/list scrollbar so you couldn't grab the thumb — the
     scrollbar owns the pane's right edge, the resize handle owns the map side. */
  #view-nearby .sheet .sheet-edge{display:block;position:absolute;top:0;right:-16px;width:16px;height:100%;
    cursor:col-resize;touch-action:none;user-select:none;-webkit-user-select:none;z-index:5}
  /* hover: a slim guide line AT the boundary (the rail's left edge) */
  #view-nearby .sheet .sheet-edge:hover{background:linear-gradient(to right,
    color-mix(in srgb,var(--accent) 45%,transparent) 0, color-mix(in srgb,var(--accent) 45%,transparent) 3px,
    transparent 3px)}
  /* collapse chevron: a tab on the boundary poking into the map, clear of the scrollbar */
  .edge-btn{position:absolute;top:50%;left:0;transform:translateY(-50%);width:20px;height:48px;
    border:1px solid var(--line);border-radius:0 10px 10px 0;background:var(--card);color:var(--muted);
    font-size:13px;line-height:1;cursor:pointer;box-shadow:var(--shadow-1);padding:0}
  .edge-btn:hover{color:var(--ink);background:var(--surface-2)}
  /* collapsed sidebar: a slim rail; the map takes the whole stage */
  #view-nearby .sheet.minimized{width:26px;height:100%}
  #view-nearby .sheet.minimized>:not(.sheet-edge){display:none}

  /* a little more room in the wider rail */
  .card{--card-pl:13px}
  .locate{top:64px}
}

/* very wide screens: cap the map's usable width so it never feels sparse */
@media (min-width:1500px){
  #view-nearby .sheet{width:var(--panel-w,440px)}
  /* keep the install CTA over the MAP, not the panel (see the 900px block) */
  #view-nearby .map-topleft{inset-inline-start:calc(var(--panel-w,440px) + 10px)}
  #view-nearby:has(.sheet.minimized) .map-topleft{inset-inline-start:36px}
}

/* respect reduced-motion */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms !important;animation-iteration-count:1 !important;transition-duration:.001ms !important;scroll-behavior:auto !important}
}

/* ==========================================================================
   PROMOTED / FEATURED EVENTS (monetization)
   ========================================================================== */
/* gilded gold pin — overrides the inline event colour */
.ev-pin.promo{
  background:linear-gradient(135deg,#fde68a 0%,#fbbf24 45%,#d97706 100%) !important;
  border-color:#fffbeb !important;
  box-shadow:0 0 0 3px rgba(245,158,11,.45),0 5px 14px rgba(180,83,9,.55) !important}
.pin-wrap.promo{z-index:6}
/* the SELECTED event's pin/cluster always stacks above every other marker (incl.
   promoted) so it can't be overlapped while open. !important beats any inline
   z-index MapLibre may set per-frame; cleared on deselect. */
.pin-wrap.sel-top{z-index:9999 !important}
.promo-spark{position:absolute;top:-12px;right:-9px;font-size:13px;line-height:1;color:#fbbf24;pointer-events:none;
  text-shadow:0 0 5px rgba(251,191,36,.9),0 1px 2px rgba(0,0,0,.5);animation:spark 2.4s ease-in-out infinite}
@keyframes spark{0%,100%{opacity:.6;transform:scale(.85) rotate(0)}50%{opacity:1;transform:scale(1.2) rotate(16deg)}}

/* featured card + tags */
.card.promo{background:linear-gradient(180deg,rgba(245,158,11,.10),transparent 72%);box-shadow:inset 3px 0 0 #f59e0b}
.card.promo:hover{background:linear-gradient(180deg,rgba(245,158,11,.17),transparent 76%)}
.feat-tag{display:inline-block;background:linear-gradient(135deg,#fbbf24,#f59e0b);color:#3d2b00;font-size:10px;font-weight:800;
  letter-spacing:.3px;padding:1px 7px;border-radius:999px;vertical-align:middle;box-shadow:0 1px 3px rgba(180,83,9,.4)}
.feat-banner{background:linear-gradient(135deg,#fbbf24,#f59e0b);color:#3d2b00;font-weight:800;font-size:12.5px;letter-spacing:.4px;
  padding:5px 11px;border-radius:10px;display:inline-block;margin-bottom:7px;box-shadow:0 2px 7px rgba(180,83,9,.35)}

/* the "Promote" host button */
.promo-btn{background:linear-gradient(135deg,#fcd34d,#f59e0b) !important;color:#3d2b00 !important;border-color:transparent !important;font-weight:800}
.promo-btn.on{background:linear-gradient(135deg,#fef3c7,#fcd34d) !important}

/* promote flow card */
.promo-card{background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);padding:14px;box-shadow:var(--shadow-1);
  animation:panel-in var(--dur) var(--ease)}
.promo-head{display:flex;align-items:center;gap:11px;margin-bottom:11px}
.promo-star{width:40px;height:40px;flex:0 0 auto;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:20px;
  color:#3d2b00;background:linear-gradient(135deg,#fcd34d,#f59e0b);box-shadow:0 3px 10px rgba(180,83,9,.4)}
.promo-h-tx{display:flex;flex-direction:column;line-height:1.3}
.promo-h-tx b{font-size:15px}
.promo-benefits{list-style:none;margin:0 0 13px;padding:0;display:flex;flex-direction:column;gap:8px}
.promo-benefits li{display:flex;gap:9px;align-items:flex-start;font-size:13.5px;color:var(--ink-2)}
.promo-benefits li span{flex:0 0 auto}
.promo-pkgs{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin:4px 0 13px}
.promo-pkg{position:relative;display:flex;flex-direction:column;align-items:center;gap:1px;padding:13px 6px 9px;border:1.5px solid var(--line);
  border-radius:var(--r);background:var(--card);cursor:pointer;transition:border-color var(--dur-fast),background var(--dur-fast),transform var(--dur-fast),box-shadow var(--dur-fast)}
.promo-pkg:active{transform:scale(.97)}
.promo-pkg b{font-size:13.5px}
.promo-pkg .promo-price{font-size:18px;font-weight:800;color:var(--ink)}
.promo-pkg.on{border-color:#f59e0b;background:linear-gradient(180deg,rgba(245,158,11,.13),transparent);box-shadow:0 0 0 3px rgba(245,158,11,.25)}
.promo-pill{position:absolute;top:-9px;left:50%;transform:translateX(-50%);white-space:nowrap;background:linear-gradient(135deg,#fbbf24,#f59e0b);
  color:#3d2b00;font-size:9px;font-weight:800;letter-spacing:.2px;padding:2px 7px;border-radius:999px;box-shadow:0 1px 3px rgba(180,83,9,.4)}
.promo-go{width:100%;background:linear-gradient(135deg,#fcd34d,#f59e0b) !important;color:#3d2b00 !important;
  box-shadow:0 4px 14px rgba(217,119,6,.4) !important}
.promo-ghost{width:100%;margin-top:8px}
.promo-demo{background:var(--accent-soft);border:1px dashed var(--accent);border-radius:var(--r);padding:11px 12px;font-size:13px;
  color:var(--ink-2);margin-bottom:12px;line-height:1.5}
/* Stripe Payment Element mounts here (its own themed iframe); just give it room */
.promo-payel{margin:6px 0 12px;min-height:44px}
.promo-payel:empty::before{content:"Loading secure checkout…";color:var(--muted);font-size:13px}

/* Top-right map button stack, 48px pitch. Layers takes the first slot — it used
   to sit under the 🎯 centre-on-me button, which is gone (the compass covers it).
   These stay explicit rather than leaning on `.locate`'s own top: `.locate.layers`
   outranks `.locate` on specificity, so a bare `.locate{top:…}` in a media query
   would never reach them. */
.locate.layers{top:calc(60px + var(--safe-top))}
@media (min-width:900px){ .locate.layers{top:64px} }
.locate.jump{top:calc(108px + var(--safe-top));font-size:16px}
@media (min-width:900px){ .locate.jump{top:112px} }
/* ---- secondary categories (0108) ----------------------------------------
   A wrapping chip row rather than a multi-select: on a phone a <select multiple>
   is a scroll trap, and the whole point is that picking two extras should feel
   as cheap as it is. Muted until chosen so the PRIMARY select above stays the
   obvious required field and these read as the optional widening they are. */
.cats2-label{margin:10px 0 5px}
.cats2{display:flex;flex-wrap:wrap;gap:6px}
.cat2{font:inherit;font-size:12.5px;line-height:1;padding:7px 10px;border-radius:999px;
  border:1px solid var(--line);background:var(--surface-2);color:var(--ink-2);
  cursor:pointer;white-space:nowrap;
  transition:transform var(--dur-fast) var(--spring),background var(--dur-fast),
             color var(--dur-fast),border-color var(--dur-fast)}
.cat2:hover{border-color:var(--accent);color:var(--accent)}
.cat2:active{transform:scale(.94)}
.cat2.on{background:var(--accent);border-color:var(--accent);color:#fff}

/* north-up / course-up compass. Lives in the MAP's bottom-right corner (JS
   re-parents it into #map — see the map init), clear of the layers/🔎 stack
   top-right and sitting just above MapLibre's attribution, which must stay
   legible for the OSM/CARTO licence. */
.locate.compass{top:auto;right:10px;
  /* clears the attribution bar, which is ~24px and can wrap to two lines on a
     narrow phone — hence 2 lines' worth rather than a snug single-line offset.
     Measured: at 32px the button clipped the attribution by 2px. */
  bottom:56px;
  /* the needle leans back in 3D under perspective mode's tilt; without a
     perspective on the parent a rotateX just squashes the disc flat instead of
     leaning it */
  perspective:130px}
@media (min-width:900px){ .locate.compass{bottom:40px} }   /* never wraps here */
/* The needle mirrors the camera: rotateZ holds true north, rotateX matches the
   map's pitch. No CSS transition — the transform is rewritten from the map's own
   `rotate`/`pitch` events, so it is already frame-accurate, and easing it would
   only make it lag behind a live gesture. */
.compass-needle{transform-origin:50% 50%}
/* perspective mode: the map is already facing the way you're facing, so the
   button stops being a compass and reads as an engaged mode. */
.locate.compass.persp{color:var(--accent);border-color:var(--accent);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent),var(--shadow-2)}
/* floating jump-to-place panel (kept out of the list — no vertical cost) */
.zip-panel{position:absolute;top:calc(54px + var(--safe-top));left:50%;transform:translateX(-50%);z-index:6;
  width:min(420px,calc(100% - 24px));padding:8px 10px;background:var(--card);
  border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--shadow-2)}
.zip-panel .ghost{flex:0 0 auto;padding:8px 10px}
/* grouped search dropdown under the panel: your events / people / nearby / places.
   Absolutely positioned so it escapes the panel's flex row and floats over the map.
   Long result sets SCROLL (up to 10 per section): tall max-height, momentum
   scrolling, sticky group headers, and one-line rows that ellipsize instead of
   wrapping — so a dense city stays readable */
.zip-suggest{position:absolute;top:calc(100% + 8px);left:0;right:0;margin-top:0;padding:0 8px 8px;
  max-height:min(70vh,520px);overflow-y:auto;-webkit-overflow-scrolling:touch;
  overscroll-behavior:contain;background:var(--card);
  border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--shadow-2)}
.zip-suggest:empty{display:none}
.zip-suggest .sugg{border:none;border-radius:var(--r-sm);padding:8px 10px;flex:0 0 auto;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.zip-suggest .sugg-h{position:sticky;top:0;z-index:1;background:var(--card);
  padding:8px 10px 4px;margin:0 -2px;font-weight:600;letter-spacing:.02em}

/* event-action icon buttons in the panel header (share / live / notify / cal / report) */
.ev-actions{margin-left:auto;display:flex;gap:6px;align-items:center;flex-wrap:wrap}
#shTitle{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.hicon{width:36px;height:36px;padding:0;display:inline-flex;align-items:center;justify-content:center;font-size:16px;line-height:1;
  border:1px solid var(--line);background:var(--card);color:var(--ink);border-radius:11px;cursor:pointer;
  transition:background var(--dur-fast),border-color var(--dur-fast),transform var(--dur-fast),color var(--dur-fast)}
.hicon:hover{background:var(--surface-2);border-color:var(--line-2)}
.hicon:active{transform:scale(.88)}
.hicon.on{background:var(--accent-soft);border-color:var(--accent);color:var(--accent-strong)}
#shLiveBtn.on{border-color:var(--ok);color:var(--ok);background:color-mix(in srgb,var(--ok) 16%,var(--card));animation:locate-pulse 1.6s ease-in-out infinite}

/* chat images */
.msg-img{display:block;width:min(240px,64vw);height:auto;border-radius:12px;margin:3px 0 4px;cursor:zoom-in;
  background:var(--surface-2);object-fit:cover;box-shadow:var(--shadow-1)}
.m.mine .msg-img{margin-left:auto}
#shImgInput,#shLibInput{display:none}
/* geo-tagged photo: image flies the map, ⤢ opens the lightbox */
.msg-imgwrap{position:relative;display:block;width:fit-content;margin:3px 0 4px}
.m.mine .msg-imgwrap{margin-left:auto}
.msg-imgwrap .msg-img{margin:0;cursor:pointer}
.geo-badge{position:absolute;left:8px;bottom:10px;display:flex;align-items:center;pointer-events:none;
  background:rgba(4,6,12,.60);color:#fff;font-size:13px;line-height:1;padding:3px 5px;border-radius:99px;box-shadow:var(--shadow-1)}
.img-expand{position:absolute;top:8px;right:8px;width:26px;height:26px;border-radius:99px;border:none;cursor:pointer;
  background:rgba(4,6,12,.6);color:#fff;font-size:13px;line-height:1;display:flex;align-items:center;justify-content:center}
.img-expand:hover{background:rgba(4,6,12,.88)}
/* a geo-tagged TEXT message: 📍 hint + tappable body */
.geo-tag{cursor:pointer}
.geo-msg .b{cursor:pointer}
/* the on-map chat bubble a geo-tagged message floats at its send spot */
.msg-bubble{position:relative;max-width:min(240px,60vw);background:var(--card);color:var(--ink);
  border:1px solid var(--line);border-radius:12px;padding:7px 10px;box-shadow:var(--shadow-3);cursor:pointer;
  font-size:13px;line-height:1.35;white-space:pre-wrap;word-break:break-word}
.msg-bubble::after{content:"";position:absolute;left:50%;bottom:-7px;transform:translateX(-50%);
  border:7px solid transparent;border-top-color:var(--card);border-bottom:0;filter:drop-shadow(0 1px 0 var(--line))}
.msg-bubble .mb-who{font-size:11px;font-weight:700;color:var(--muted);margin-bottom:2px}
.msg-bubble .mb-tx{color:var(--ink)}
.msg-bubble .mb-img{display:block;width:100%;max-height:150px;object-fit:cover;border-radius:8px;margin:2px 0}
/* geo-photo pin: a chat photo pinned on the map where it was taken */
.photo-pin{position:relative;width:46px;height:46px;border-radius:12px;overflow:hidden;cursor:pointer;
  border:2px solid #fff;background:var(--surface-2);box-shadow:var(--shadow-1);animation:msg-in var(--dur) var(--ease)}
.photo-pin img{width:100%;height:100%;object-fit:cover;display:block}
.photo-pin .pp-count{position:absolute;top:2px;right:2px;background:var(--header);color:#fff;
  font-size:10px;font-weight:700;padding:1px 5px;border-radius:99px;pointer-events:none}
/* jump-to-message highlight (tapping a photo pin) */
.m.flash{animation:msg-flash 2s var(--ease)}
@keyframes msg-flash{0%,55%{box-shadow:0 0 0 3px var(--accent)}100%{box-shadow:0 0 0 0 transparent}}
/* one-shot "here's what's new" — soft glow + a sparkle that drifts up and fades */
.m.new-glow{position:relative;animation:new-glow 2.6s var(--ease) both}
@keyframes new-glow{0%,45%{box-shadow:0 0 0 3px var(--accent-soft),0 0 14px var(--accent-soft)}100%{box-shadow:0 0 0 0 transparent}}
.m.new-glow::after{content:"✨";position:absolute;top:-6px;right:-4px;font-size:15px;pointer-events:none;
  animation:new-sparkle 2.6s var(--ease) both}
@keyframes new-sparkle{0%{opacity:0;transform:translateY(4px) scale(.6)}18%{opacity:1;transform:translateY(0) scale(1.1)}
  55%{opacity:1;transform:translateY(-4px) scale(1)}100%{opacity:0;transform:translateY(-10px) scale(.8)}}
.attach .pinnote .unpin{color:var(--accent-strong);cursor:pointer;text-decoration:underline}
.chatbar #shAttachBtn{flex:0 0 auto}
/* typing / presence line pinned under the chat scroll */
.typing-line{flex:0 0 auto;padding:4px 6px 0;font-size:12px;color:var(--muted);font-style:italic}
/* imported "around town" listings */
.src-tag{display:inline-block;margin-left:6px;padding:1px 7px;border-radius:99px;font-size:10.5px;
  font-weight:700;font-style:normal;background:var(--surface-2);color:var(--muted);vertical-align:middle}
/* follow chip active state */
.react.on{background:var(--accent-soft);color:var(--accent-strong)}
/* weekly digest + backup-nudge cards at the top of My Events */
.digest-card{display:flex;align-items:center;gap:10px;margin:4px 10px 8px;padding:11px 13px;border-radius:12px;
  background:var(--accent-soft);border:1px solid var(--accent-ring);cursor:default}
.digest-card b{font-size:13.5px;white-space:nowrap}
.digest-card span{flex:1;min-width:0;font-size:12.5px;color:var(--muted)}
.digest-card .x{flex:0 0 auto;color:var(--muted);font-size:15px;padding:2px 6px;border-radius:8px}
.digest-card.backup-nudge{cursor:pointer}
/* ---- nearby date chips (they live IN the sheet-head row) ---- */
/* 📅 custom date-range pickers (shown by the Dates chip) */
/* one line on EVERY form factor: labels share the width and the date inputs
   shrink (min-width:0), so small phones and the ~380px desktop sidebar match */
.rangerow{display:flex;gap:8px;align-items:center;flex-wrap:nowrap;padding:0 12px 8px}
.rangerow label{display:flex;align-items:center;gap:6px;font-size:12.5px;font-weight:600;color:var(--muted);
  flex:1 1 0;min-width:0}
.rangerow input{font:inherit;font-size:13px;padding:6px 8px;border:1px solid var(--line);border-radius:8px;
  background:var(--card);color:var(--ink);flex:1;min-width:0;width:100%}
.rangerow .ghost{padding:6px 10px;flex:0 0 auto}
/* weekday hint beside date inputs ("Starts · Friday" / "Fri") */
.dow{color:var(--accent-strong);font-weight:700;font-size:12px;white-space:nowrap}
/* chips right-align in the header row; the (auto-removing) map hint shares the
   auto margin until it's gone, then the chips own the right edge. Compact enough
   that "Nearby · NNN events" + all three chips fit one 375px line. */
.whenchips{display:flex;gap:4px;flex:0 0 auto;flex-wrap:wrap}
.wchip{padding:6px 10px;border-radius:99px;font-size:12px;font-weight:650;color:var(--muted);
  background:transparent;border:1px solid var(--line-2);cursor:pointer;
  transition:background var(--dur-fast),color var(--dur-fast),border-color var(--dur-fast),transform var(--dur-fast)}
.wchip:hover{background:var(--surface-2)}
.wchip:active{transform:scale(.94)}
.wchip.on{background:var(--grad);border-color:transparent;color:#fff;box-shadow:var(--shadow-1)}
/* unread-chat dot on event-list cards */
.card .card-unread{position:static;display:inline-block;margin-left:6px;vertical-align:middle}
/* ---- RSVP box: name + single tappable avatar + big buttons ---- */
.rsvp-head{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
.rsvp-head b{font-size:16px}
.rsvp-id{display:flex;gap:10px;align-items:center}
.rsvp-id #shName{flex:1;min-width:0}
.avatar-btn{width:48px;height:48px;flex:0 0 auto;border-radius:50%;font-size:24px;line-height:1;
  background:var(--surface-2);border:1.5px solid var(--line);cursor:pointer;
  display:flex;align-items:center;justify-content:center}
.avatar-btn:hover{border-color:var(--accent)}
.av-free{width:100%;margin-top:8px;font:inherit;font-size:13.5px;padding:8px 11px;
  border:1px solid var(--line);border-radius:10px;background:var(--card);color:var(--ink)}
/* attention pulse when we walk the user to the RSVP box */
.rsvp-card.attn{animation:rsvp-attn 1.9s var(--ease)}
@keyframes rsvp-attn{0%,45%{box-shadow:0 0 0 3px var(--accent-ring),0 0 18px var(--accent-ring)}
  100%{box-shadow:0 0 0 0 transparent}}
/* ---- poster field (create + edit forms) ---- */
.posterrow{display:flex;align-items:center;gap:8px;margin-top:4px}
.poster-prev{width:76px;height:44px;object-fit:cover;border-radius:8px;border:1px solid var(--line);flex:0 0 auto}
/* ---- color swatches (event pin color + drawing color) ---- */
.colorpick{display:flex;gap:7px;flex-wrap:wrap;align-items:center;margin-top:4px}
.colorpick .colorswatch{width:24px;height:24px;border-radius:50%;border:2px solid transparent;cursor:pointer;
  padding:0;box-shadow:inset 0 0 0 2px var(--card), 0 1px 2px rgba(0,0,0,.25)}
.colorpick .colorswatch.on{border-color:var(--ink);transform:scale(1.12)}
.drawbar .draw-colors{margin-top:0;gap:5px}
.drawbar .draw-colors .colorswatch{width:20px;height:20px}
/* ---- map drawing ---- */
.draw-vtx{width:14px;height:14px;border-radius:50%;background:#fff;border:2.5px solid var(--draw-c,#6366f1);
  box-shadow:0 1px 3px rgba(0,0,0,.45);cursor:grab}
.draw-vtx:active{cursor:grabbing}
/* ---- pin a spot ---- */
.pinbar{flex-direction:column;align-items:stretch;gap:6px;width:min(440px,calc(100% - 24px))}
.pinbar .pin-row{display:flex;gap:6px;align-items:center;min-width:0}
.pinbar input{font:inherit;padding:8px 10px;border:1px solid var(--line);border-radius:10px;
  background:var(--surface-2);color:var(--ink);min-width:0}
.pinbar #pinEmoji{flex:0 0 52px;text-align:center;font-size:17px;padding:8px 4px}
.pinbar #pinTitle{flex:1}
.pinbar #pinSearch{flex:1}
.pinbar .suggest{max-height:180px;overflow-y:auto;margin-top:0}
/* the pin on the map: emoji + name tag; posted pins jump to their chat message */
.chat-pin{display:flex;flex-direction:column;align-items:center;cursor:pointer;gap:1px}
.chat-pin .cp-emoji{font-size:26px;line-height:1;filter:drop-shadow(0 1px 2px rgba(0,0,0,.45))}
.chat-pin .cp-tag{max-width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:11px;font-weight:700;padding:2px 7px;border-radius:99px;background:var(--card);
  color:var(--ink);border:1px solid var(--line);box-shadow:0 1px 3px rgba(0,0,0,.25)}
.chat-pin.draft .cp-emoji{animation:pop-in var(--dur-fast) var(--spring)}
/* pin posted to chat — tap to see it on the map; ↗ opens the maps app */
.pincard{display:flex;align-items:center;gap:10px;margin:4px 0;padding:8px 10px;cursor:pointer;
  background:var(--surface-2);border:1px solid var(--line);border-radius:12px;color:var(--ink);width:fit-content;max-width:100%}
.m.mine .pincard{margin-left:auto;background:rgba(255,255,255,.16);border-color:rgba(255,255,255,.28);color:#fff}
.pincard:hover{border-color:var(--accent)}
.pincard .pc-emoji{font-size:24px;line-height:1;flex:0 0 auto}
.pincard .pc-tx{display:flex;flex-direction:column;line-height:1.3;min-width:0}
.pincard .pc-tx b{font-size:14px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pincard .pc-nav{white-space:nowrap}
.attach .at-pin{flex:0 0 auto;font-size:22px;width:46px;height:40px;display:flex;align-items:center;
  justify-content:center;border-radius:8px;background:var(--card)}
.drawbar{position:absolute;top:12px;left:50%;transform:translateX(-50%);z-index:6;display:flex;align-items:center;
  gap:6px;flex-wrap:wrap;justify-content:center;max-width:calc(100% - 24px);padding:7px 9px;
  background:var(--card);border:1px solid var(--line);border-radius:14px;box-shadow:var(--shadow-3)}
.drawbar .draw-hint{font-size:12px;color:var(--muted);margin:0 4px;white-space:nowrap}
.drawbar button{padding:6px 11px;font-size:13px}
/* drawing posted to chat — tap to draw it on the map */
.drawcard{display:flex;align-items:center;gap:10px;margin:4px 0;padding:8px 10px;cursor:pointer;
  background:var(--surface-2);border:1px solid var(--line);border-radius:12px;color:var(--accent-strong);width:fit-content;max-width:100%}
.m.mine .drawcard{margin-left:auto;background:rgba(255,255,255,.16);border-color:rgba(255,255,255,.28);color:#fff}
.drawcard:hover{border-color:var(--accent)}
.draw-svg{flex:0 0 auto;width:104px;height:52px}
.drawcard .dc-tx{display:flex;flex-direction:column;line-height:1.3}
.drawcard .dc-tx b{font-size:14px}
.lightbox{position:fixed;inset:0;background:rgba(4,6,12,.9);z-index:400;display:flex;align-items:center;justify-content:center;
  padding:16px;animation:fade-in .18s ease;cursor:zoom-out;touch-action:none}
.lightbox img{max-width:100%;max-height:100%;border-radius:12px;box-shadow:var(--shadow-3);cursor:pointer;
  transform-origin:center center;will-change:transform;-webkit-user-drag:none;user-select:none}
.lightbox.zoomed img{cursor:grab}
.lightbox.zoomed img:active{cursor:grabbing}
.lightbox .lb-hint{position:absolute;top:calc(14px + var(--safe-top));left:50%;transform:translateX(-50%);
  color:#fff;font-size:11px;font-weight:600;background:rgba(4,6,12,.55);padding:3px 10px;border-radius:99px;
  pointer-events:none;white-space:nowrap}
/* gallery navigation: ‹ › arrows + "3 / 7" counter (swipe works on touch too) */
.lightbox .lb-nav{position:absolute;top:50%;transform:translateY(-50%);width:44px;height:64px;border:none;
  border-radius:14px;background:rgba(255,255,255,.13);color:#fff;font-size:32px;line-height:1;cursor:pointer;
  display:flex;align-items:center;justify-content:center;padding:0 0 4px}
.lightbox .lb-nav:hover{background:rgba(255,255,255,.26)}
.lightbox .lb-prev{left:calc(10px + env(safe-area-inset-left,0px))}
.lightbox .lb-next{right:calc(10px + env(safe-area-inset-right,0px))}
.lightbox .lb-count{position:absolute;bottom:calc(16px + var(--safe-bottom));left:50%;transform:translateX(-50%);
  color:#fff;font-size:12px;font-weight:600;background:rgba(4,6,12,.55);padding:3px 11px;border-radius:99px;pointer-events:none}
.lightbox .lb-x{position:absolute;top:calc(12px + var(--safe-top));right:14px;width:40px;height:40px;border-radius:99px;
  background:rgba(255,255,255,.16);color:#fff;font-size:18px;display:flex;align-items:center;justify-content:center}

/* @-mentions in chat */
.mention{font-weight:650;text-decoration:none}
.mention-u,.mention-e{color:var(--accent-strong);background:var(--accent-soft);border-radius:6px;padding:0 4px}
.mention-u[data-uid],.mention-e[data-eid]{cursor:pointer}
.m.mine .mention-u,.m.mine .mention-e{color:#fff;background:rgba(255,255,255,.24)}
/* @event mention with a poster: a small thumbnail sits inside the chip */
.mention-e.has-poster{display:inline-flex;align-items:center;gap:4px;padding-left:2px;vertical-align:middle}
.mention-poster{width:18px;height:18px;border-radius:4px;object-fit:cover;flex:0 0 auto}
.mention-pop{position:fixed;z-index:1250;background:var(--card);border:1px solid var(--line);border-radius:12px;
  box-shadow:var(--shadow-3);padding:5px;max-height:210px;overflow-y:auto;animation:pop-in var(--dur-fast) var(--spring)}
.mention-opt{display:flex;align-items:center;gap:8px;width:100%;text-align:left;padding:9px 11px;border-radius:8px;
  font:inherit;color:var(--ink);background:none;cursor:pointer;white-space:nowrap}
.mention-opt:hover{background:var(--surface-2)}
.chatbar #shAttachBtn{flex:0 0 auto}

/* accessible, visually-hidden heading — announced by screen readers and read by
   search crawlers, but takes no visual space (this is NOT display:none, so it
   stays in the accessibility tree and the indexed DOM) */
.sr-only{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
/* no-JS crawlable fallback (only ever renders inside <noscript>) */
.noscript-seo{max-width:680px;margin:0 auto;padding:24px 20px;color:var(--ink);line-height:1.55}
.noscript-seo h2{margin:0 0 10px}
.noscript-seo h3{margin:18px 0 6px}
.noscript-seo h4{margin:14px 0 2px;font-size:15px}
.noscript-seo ul{padding-left:20px;margin:6px 0}
.noscript-seo a{color:var(--accent-strong)}
/* "you're not going" reveal/hide row at the foot of the Nearby list */
.decl-row{padding:11px 12px;text-align:center}
.decl-link{color:var(--accent-strong);font-weight:700;cursor:pointer;white-space:nowrap}
.decl-link:hover{text-decoration:underline}

/* ---------- live-chat pulse (pins + list tag) ---------- */
.ev-pin.live{animation:pin-throb 1.8s ease-in-out infinite}
@keyframes pin-throb{
  0%,100%{box-shadow:0 0 0 0 rgba(16,185,129,.55),0 3px 8px rgba(0,0,0,.32)}
  50%{box-shadow:0 0 0 10px rgba(16,185,129,0),0 3px 8px rgba(0,0,0,.32)}}
.live-tag{color:#059669;font-weight:700}
/* ---------- runs (Settings › Runs) ----------
   The route line itself is a map layer, not CSS — see ensureRunLayers. This is
   just the list inside the modal. */
.runs-body{padding:2px 0}
.runs-list{margin-top:12px;display:flex;flex-direction:column;gap:2px;max-height:52vh;overflow-y:auto;-webkit-overflow-scrolling:touch}
.run-row{padding:9px 2px;border-bottom:1px solid var(--line)}
.run-row:last-child{border-bottom:none}
.run-main{display:flex;align-items:baseline;gap:8px;justify-content:space-between}
.run-main b{font-size:14px}
/* stats wrap rather than truncate — on a narrow phone the pace and HR are the
   whole point of the row, so losing them to an ellipsis defeats it */
.run-stats{margin-top:2px;line-height:1.5}
.run-acts{display:flex;flex-wrap:wrap;gap:6px;margin-top:7px}
/* Live recorder. Anchored to the map's bottom edge, above the compass's row and
   clear of the home indicator. z-index sits over the map but under the sheet, so
   opening a sheet doesn't leave it floating on top of the conversation. */
.trackbar{position:absolute;left:10px;right:10px;bottom:calc(10px + var(--safe-bottom));z-index:3;
  display:flex;align-items:center;gap:10px;justify-content:space-between;
  padding:9px 12px;background:var(--card);border:1px solid var(--line);
  border-radius:var(--r-lg);box-shadow:var(--shadow-2)}
@media (min-width:900px){ .trackbar{left:auto;right:10px;width:340px} }
.trk-sport{font-size:19px;align-self:center}
.trk-stats{display:flex;gap:14px;flex:1 1 auto;min-width:0;align-items:center}
.trk-stats span{display:flex;flex-direction:column;line-height:1.15;min-width:0}
.trk-stats b{font-size:15px;font-variant-numeric:tabular-nums}   /* stops the clock jittering */
.trk-stats i{font-style:normal;font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:.03em}
.trk-btns{display:flex;gap:6px;flex:0 0 auto}
.trk-btns button{padding:7px 11px;font-size:13px}
/* ---------- workout card, as posted into a chat or DM ----------
   Rendered from the message's own snapshot, so it needs no map and no network —
   it must look right the instant the bubble paints. */
/* The card is an OPAQUE panel, so it must state its own text colour: dropped into
   a "mine" bubble it inherits that bubble's white (.m.mine{color:#fff}) and every
   number goes white-on-light-grey. .pincard and .drawcard solve the same problem
   by going translucent over the gradient; this one is a solid card with a map in
   it, so it stays a solid card and just owns its ink. */
.wk-card{margin-top:6px;border:1px solid var(--line);border-radius:var(--r-lg);
  background:var(--surface-2);color:var(--ink);overflow:hidden;cursor:pointer;max-width:280px}
.wk-card:hover{border-color:var(--accent)}
.m.mine .wk-card{margin-left:auto}
.wk-head{display:flex;align-items:center;gap:6px;padding:8px 10px 6px;font-size:14px}
.wk-head b{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* auto-fit rather than a fixed column count: a swim has no climb and a ride has
   no pace, so the number of tiles genuinely varies per card */
.wk-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(64px,1fr));
  gap:2px 8px;padding:0 10px 8px}
.wk-grid span{display:flex;flex-direction:column;line-height:1.2;min-width:0}
.wk-grid b{font-size:13px;font-variant-numeric:tabular-nums}
.wk-grid i{font-style:normal;font-size:9.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.03em}
/* ---- the route thumbnail: real basemap tiles, laid out by workoutRouteThumb ----
   The tile layer is a fixed-width strip centred in the card, so a bubble narrower
   than the mosaic crops it evenly on both sides instead of leaving a bald margin.
   Everything inside is positioned in the same pixel space the JS projected into. */
.wk-map{background:var(--surface-2);border-top:1px solid var(--line);padding:0;line-height:0}
.wk-mapbox{position:relative;overflow:hidden;background:var(--surface-2)}
/* Tiles only come at whole zoom levels, so the mosaic is laid out at the zoom
   ABOVE the ideal one and scaled back down here (see workoutRouteThumb). Origin
   0 0 so that scale-down shrinks the layer toward its own top-left, which is
   where the box's top-left is. */
.wk-tiles{position:absolute;top:0;left:50%;transform-origin:0 0}
.wk-tile{position:absolute;display:block;max-width:none;
  /* tiles fade in as they land, so a slow network reveals the map instead of
     flashing a grid of alt-boxes into place */
  animation:fade-in .35s var(--ease)}
.wk-line{position:absolute;top:0;left:0;pointer-events:none}
/* the route: a white casing under a warm line, which is what keeps it legible
   over both a pale street map and dark satellite imagery. non-scaling-stroke so
   the widths survive the layer's scale-down unchanged. */
.wk-line polyline{vector-effect:non-scaling-stroke;stroke-linejoin:round;stroke-linecap:round}
.wk-line .wk-case{stroke:rgba(255,255,255,.95);stroke-width:6}
.wk-line .wk-route{stroke:#f97316;stroke-width:3}
.wk-line circle{vector-effect:non-scaling-stroke;stroke:#fff;stroke-width:1.6}
.wk-line .wk-start{fill:#22c55e}
.wk-line .wk-end{fill:#ef4444}
/* OSM/CARTO/Esri all require the credit wherever their tiles are shown */
.wk-attrib{position:absolute;right:0;bottom:0;font-size:8px;line-height:11px;padding:0 4px;
  background:rgba(255,255,255,.72);color:#334155;border-radius:4px 0 0 0;pointer-events:none}

/* ---- route recap: the ten-second flythrough (startRecap) ---- */
.recap-dot{width:26px;height:26px;border-radius:50%;background:var(--card);
  border:2px solid #f97316;box-shadow:0 2px 8px rgba(2,6,23,.35);
  display:flex;align-items:center;justify-content:center;font-size:14px;line-height:1}
/* FIXED to the viewport and above the DM overlay (z 360), not parked inside the
   map: a recap is usually started from a chat bubble, and a peeked DM sits over
   the map's own bottom edge — which is exactly where this would otherwise hide. */
.recap-bar{position:fixed;left:10px;right:10px;bottom:calc(10px + var(--safe-bottom));z-index:370;
  display:flex;align-items:center;gap:9px;padding:8px 10px;
  background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);
  /* panel-in, not sheet-up: sheet-up bakes in a translateX(-50%) for left:50%
     elements, and this bar is pinned by its own left/right */
  box-shadow:var(--shadow-2);animation:panel-in var(--dur) var(--ease)}
@media (min-width:900px){ .recap-bar{left:auto;right:10px;width:340px} }
.recap-ico{font-size:16px;line-height:1;flex:0 0 auto}
.recap-read{font-size:13px;font-weight:700;font-variant-numeric:tabular-nums;white-space:nowrap;flex:0 0 auto}
.recap-prog{flex:1 1 auto;min-width:0;height:4px;border-radius:99px;background:var(--line-2);overflow:hidden}
.recap-prog b{display:block;height:100%;width:0;background:#f97316;border-radius:99px}
.recap-x{flex:0 0 auto;width:26px;height:26px;border:none;border-radius:99px;padding:0;
  background:var(--surface-2);color:var(--ink-2);font-size:13px;cursor:pointer}
.recap-x:hover{background:var(--line-2);color:var(--ink)}
/* The one-tap way into a recap (showRoutePreview). Deliberately the SAME box as
   .recap-bar — same edges, same z, same bottom offset — so pressing it swaps the
   panel in place instead of making the bottom of the screen jump. */
.wk-preview{position:fixed;left:10px;right:10px;bottom:calc(10px + var(--safe-bottom));z-index:370;
  display:flex;flex-direction:column;gap:8px;padding:9px 10px;
  background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);
  box-shadow:var(--shadow-2);animation:panel-in var(--dur) var(--ease)}
@media (min-width:900px){ .wk-preview{left:auto;right:10px;width:340px} }
/* the stats that used to be a toast. Tabular figures so the numbers don't
   shimmy, and it truncates rather than wrapping the panel to two lines. */
.wk-preview-sum{font-size:13px;font-weight:700;font-variant-numeric:tabular-nums;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.wk-preview-row{display:flex;align-items:center;gap:8px}
.wk-preview-go{flex:1 1 auto;min-width:0;border:none;border-radius:99px;padding:9px 12px;
  background:#f97316;color:#fff;font:inherit;font-size:13px;font-weight:700;cursor:pointer}
.wk-preview-go:hover{filter:brightness(1.06)}
.wk-preview-rm{flex:0 0 auto;border:none;border-radius:99px;padding:9px 12px;
  background:var(--surface-2);color:var(--ink-2);font:inherit;font-size:13px;font-weight:600;cursor:pointer}
.wk-preview-rm:hover{background:var(--line-2);color:var(--ink)}
/* paused reads at a glance: the numbers are no longer moving, so say so */
.trackbar.paused{border-color:var(--accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 18%,transparent),var(--shadow-2)}
.trackbar.paused .trk-stats b{opacity:.55}
/* ---------- event countdown ---------- */
#shCountdown b{color:#2563eb}
#shCountdown.live-now b{color:#dc2626}
/* ---------- RSVP confetti ---------- */
.confetti{position:fixed;inset:0;pointer-events:none;z-index:400}
.confetti i{position:absolute;width:8px;height:12px;border-radius:2px;opacity:0;
  animation:conf-pop 1.15s cubic-bezier(.15,.65,.35,1) forwards}
@keyframes conf-pop{
  0%{transform:translate(-50%,-50%) rotate(0);opacity:1}
  100%{transform:translate(calc(-50% + var(--dx)),calc(-50% + var(--dy) + 240px)) rotate(var(--rz));opacity:0}}

/* ---- Settings page (home tab 4): purpose first, then options, then legal ---- */
.setpage{flex:1 1 auto;min-height:0;overflow-y:auto;padding:12px 16px 30px;-webkit-overflow-scrolling:touch}
/* purpose card now sits BELOW the settings options (actions first, values
   after) so it needs top separation it didn't when it led the page */
.set-hero{text-align:center;padding:20px 16px 18px;margin-top:18px;border:1px solid var(--line-2);border-radius:18px;
  background:linear-gradient(160deg, color-mix(in srgb, var(--accent, #7c5cff) 12%, transparent), transparent 60%)}
.set-globe{font-size:40px;line-height:1;margin-bottom:8px}
.set-hero h2{margin:0 0 8px;font-size:18px}
.set-mission{margin:0 0 10px;font-size:15.5px;font-weight:650;line-height:1.45}
.set-creed{margin:0;font-size:13px;line-height:1.55}
.set-list{display:flex;flex-direction:column;gap:8px;margin-top:14px}
.set-row{display:flex;align-items:center;gap:12px;width:100%;padding:12px 14px;font:inherit;text-align:start;
  border:1px solid var(--line-2);border-radius:14px;background:inherit;color:inherit;cursor:pointer}
.set-row:hover{background:var(--surface-2)}
.set-emoji{font-size:20px;flex:0 0 auto}
.set-txt{display:flex;flex-direction:column;gap:2px;min-width:0}
.set-lbl{font-weight:650}
.set-sub{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.set-chev{margin-inline-start:auto;opacity:.4;font-size:18px}
.set-links{display:flex;gap:8px 18px;justify-content:center;flex-wrap:wrap;margin-top:18px}
.set-links a{color:inherit;opacity:.8;font-size:13px;text-decoration:underline;text-underline-offset:3px}
.set-foot{text-align:center;margin-top:14px}

/* ---- share popover (no-Web-Share fallback) + recap nudge card ---- */
.share-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:12px}
.share-grid a,.share-grid button{display:block;text-align:center;text-decoration:none;padding:10px 8px;
  border:1px solid var(--line-2);border-radius:12px;color:inherit;font:inherit;background:inherit;cursor:pointer}
.share-grid a:hover,.share-grid button:hover{background:var(--surface-2)}
.recapcard{border-style:dashed}
