/* ForeverTrail — v0 prototype styles
   Palette: gentle, quiet, nature-rooted. Warm paper, moss green, soft amber. */

:root{
  --paper:#f6f3ea;
  --surface:#ffffff;
  --surface-moss:#eef1e6;
  --ink:#2b2e26;
  --ink-dim:#5e6356; /* was #6d7264; darkened 2026-10-10 so captions pass 4.5:1 on paper, moss and amber tints too */
  --line:#dcd8c6;
  --moss:#57735a;
  --moss-dark:#3f5642;
  --amber:#b9884c;
  --amber-dark:#8a5f28;
  --amber-soft:#f1e3c9;
  --shadow: 0 1px 2px rgba(43,46,38,0.06), 0 10px 28px -14px rgba(43,46,38,0.28);
  --shadow-lift: 0 4px 10px -4px rgba(43,46,38,0.22), 0 14px 34px -18px rgba(43,46,38,0.32);
  --radius-sm:8px;
  --radius-md:10px;
  --radius-lg:14px;
  --ease: cubic-bezier(.22,.72,.32,1);
  /* Kept in sync with the header's real rendered height by
     updateHeaderHeightVar() in app.js - this fallback only matters
     for the instant before that first runs. */
  --header-h: 70px;
  --tabs-h: 0px; /* measured from the real bottom tab bar - see updateTabsHeightVar() in app.js */
}

*{ box-sizing:border-box; }
/* Belt-and-suspenders: several elements that get toggled with the
   `hidden` attribute (e.g. #modTools) also have an author `display`
   rule (`.action-group{ display:flex; }`), which otherwise wins over
   the browser's own `[hidden]{ display:none }` default and leaves an
   empty, still-visible box behind. This makes `hidden` always win. */
[hidden]{ display:none !important; }
/* Hover styles are wrapped in @media (hover:hover) throughout this file
   (2026-10-10): iPhone applies :hover on a tap and leaves it stuck until
   you tap elsewhere, so a tapped tile or tab stayed highlighted. Touch
   screens get :active press feedback instead - see "press feedback"
   at the end of this file. */
/* 100vh (and percentage heights chained from it) is measured against
   the LARGEST possible mobile-browser viewport, as if its address bar
   were hidden -- when that bar is actually showing, the page renders
   taller than what's really visible, pushing anything anchored to the
   bottom (like the app shell's bottom-tabs bar, see .app-shell/main
   below) off the edge of the screen and forcing a page scroll to
   reach it. 100dvh tracks the address bar's actual current state
   instead, so this always matches the real visible height; 100vh
   stays first as a fallback for older browsers that don't know dvh. */
html,body{ height:100vh; height:100dvh; margin:0; }
body{
  font-family:'Public Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  color:var(--ink);
  background:
    radial-gradient(1100px 620px at 8% -12%, rgba(185,136,76,0.07), transparent 60%),
    radial-gradient(900px 560px at 100% 0%, rgba(87,115,90,0.06), transparent 55%),
    var(--paper);
  display:flex;
  flex-direction:column;
}

h1,h2,h3{ font-family:'Fraunces', Georgia, serif; font-weight:600; margin:0; }

::selection{ background:var(--amber-soft); color:var(--ink); }

/* a calm, theme-matched scrollbar for panels (Firefox + WebKit) —
   also used by the left rail's "Your memorials" list, which scrolls
   the same way a panel does. */
.panel, .rail-memorials{ scrollbar-width:thin; scrollbar-color:var(--line) transparent; }
.panel::-webkit-scrollbar, .rail-memorials::-webkit-scrollbar{ width:9px; }
.panel::-webkit-scrollbar-track, .rail-memorials::-webkit-scrollbar-track{ background:transparent; }
.panel::-webkit-scrollbar-thumb, .rail-memorials::-webkit-scrollbar-thumb{ background:var(--line); border-radius:99px; border:2px solid var(--surface); }
.panel::-webkit-scrollbar-thumb:hover, .rail-memorials::-webkit-scrollbar-thumb:hover{ background:var(--ink-dim); }

/* a visible, on-brand focus ring everywhere, instead of the browser
   default — this matters more than usual here, since ForeverTrail is
   used by people in all kinds of emotional states and on all kinds
   of devices, and a clear focus ring is part of that care. */
a:focus-visible, button:focus-visible, input:focus-visible,
select:focus-visible, textarea:focus-visible, [tabindex]:focus-visible{
  outline:2px solid var(--moss);
  outline-offset:2px;
  border-radius:6px;
}

/* Line-icon system - hand-authored inline SVG (see the <symbol> sprite
   at the top of app.html), used via <svg class="icon"><use href="#icon-
   NAME"></use></svg> everywhere the UI used to reach for an emoji as a
   stand-in icon. Sized to the surrounding text so it drops into a
   button label or a status line without extra layout work. */
.icon{
  width:1em;
  height:1em;
  display:inline-block;
  vertical-align:-0.15em;
  flex:none;
  fill:none;
  stroke:currentColor;
  stroke-width:2;
  stroke-linecap:round;
  stroke-linejoin:round;
}
/* The follow-toggle star is the one icon with a filled/outline state
   instead of two separate symbols - .is-following (already added to
   #followMemorialBtn in app.js) switches it from outline to solid. */
.btn.is-following .icon{ fill:currentColor; }

/* load-failure banner */
.load-error{
  display:flex;
  gap:0.7rem;
  align-items:flex-start;
  background:var(--amber-soft);
  border-bottom:1px solid var(--amber);
  color:var(--ink);
  padding:0.8rem 1.4rem;
  font-size:0.88rem;
  line-height:1.5;
}
.load-error .icon{ width:1.2em; height:1.2em; margin-top:0.15em; color:var(--amber-dark); }
.load-error strong{ display:block; margin-bottom:0.2rem; font-family:'Fraunces', Georgia, serif; font-size:1rem; }

/* ---------------------------------------------------------------
   Private-beta access gate — see js/beta-gate.js. A full-screen
   overlay that sits above everything else (even the site header) until
   the shared beta code is entered once in this browser. */
.beta-gate-overlay{
  position:fixed;
  inset:0;
  z-index:9999;
  background:var(--paper);
  display:flex;
  align-items:center;
  justify-content:center;
  padding:1.4rem;
}
.beta-gate-card{
  width:min(380px, 100%);
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--radius-lg);
  box-shadow:var(--shadow-lift);
  padding:2rem 1.8rem 1.8rem;
  text-align:left;
  animation:hubCardIn .28s var(--ease) both;
}
.beta-gate-card h2{ font-size:1.25rem; margin:0.2rem 0 0.6rem; }
.beta-gate-card label{
  display:flex; flex-direction:column; gap:0.35rem;
  font-size:0.85rem; font-weight:600; color:var(--ink);
  margin-top:0.8rem;
}
.beta-gate-card input{
  font-family:'Public Sans', sans-serif;
  font-size:0.95rem;
  padding:0.6rem 0.75rem;
  border-radius:var(--radius-sm);
  border:1px solid var(--line);
  background:var(--paper);
  color:var(--ink);
  font-weight:400;
  letter-spacing:0.04em;
  transition:border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.beta-gate-card input:focus-visible{ border-color:var(--moss); outline:none; box-shadow:0 0 0 3px var(--surface-moss); }
.beta-gate-card .btn{ width:100%; margin-top:1.1rem; }
.beta-gate-card .auth-status{ margin-top:0.7rem; }

/* header */
.site-header{
  display:flex;
  align-items:center;
  gap:1.4rem;
  row-gap:0.6rem;
  /* Top padding includes the iPhone status bar / camera cutout
     (env(safe-area-inset-top), 0 on desktop and in Safari's normal
     view) - inside the iPhone app the page draws right up to the top
     edge, and without this the logo/search/profile sat under the
     clock. Added 2026-10-10. --header-h is measured from the real
     height (app.js), so panels move down with it. */
  padding:calc(0.85rem + env(safe-area-inset-top, 0px)) 1.4rem 0.85rem;
  background:var(--surface);
  border-bottom:1px solid var(--line);
  box-shadow:0 1px 0 rgba(43,46,38,0.02);
  flex-wrap:wrap;
  position:relative;
  z-index:1100;
}
.brand{ display:flex; align-items:center; gap:0.65rem; flex:none; }
/* The logo is a link home (2026-10-09) - keep it looking like a logo. */
a.brand-home-link{ color:inherit; text-decoration:none; border-radius:var(--radius-sm); -webkit-tap-highlight-color:transparent; }
@media (hover:hover){ a.brand-home-link:hover{ opacity:0.85; } }

/* Full logo (mountains + ribbon + wordmark, the actual brand file) —
   shown whenever the header has room for it. Swaps out for the
   compact FT monogram below the 640px breakpoint, where the header
   gets tight and the full logo would start to get cut off/crowd the
   search + account controls. */
.brand-logo-full{
  height:42px;
  width:auto;
  display:block;
  flex:none;
}

/* Compact logo tile — a rounded 32x32 crop of the real favicon
   artwork (twin peaks + sunrise), used only as the narrow-header
   fallback for the full logo above. Hidden by default; the
   max-width:640px block below turns it on. Used to show a separate
   hand-drawn "FT" mark instead of the actual favicon image - the
   rounding/crop here is unchanged, only what's inside it changed. */
.brand-mark-ft{
  display:none;
  align-items:center;
  justify-content:center;
  position:relative;
  width:32px; height:32px;
  border-radius:var(--radius-sm);
  overflow:hidden;
  background:#2b2e26;
  flex:none;
}
.brand-mark-ft img{
  position:absolute;
  inset:0;
  width:100%; height:100%;
  object-fit:cover;
}

.brand-text{ display:flex; flex-direction:column; gap:0.05rem; line-height:1.2; }
/* The full logo image already carries the "ForeverTrail" wordmark, so
   the text name stays hidden until the FT monogram (icon-only) takes
   over on narrow screens — see max-width:640px below. */
.brand-name{ display:none; font-family:'Fraunces', Georgia, serif; font-weight:600; font-size:1.28rem; letter-spacing:-0.01em; }
.tagline{ color:var(--ink-dim); font-size:0.82rem; margin:0; }
/* Pinned to the header's own top-right corner (.site-header is
   position:relative, see above) instead of flowing after the brand
   and search icon. It used to just float right *within that same
   row* (margin-left:auto), which meant the moment the row ran out of
   width, it got pushed down onto a whole separate line under the
   logo instead of staying in the corner - exactly what was happening
   on mobile. Taking it out of the normal flex flow entirely means it
   always sits in the corner at any width, and the logo/search icon on
   the left never have to share a row with it. flex-wrap/max-width
   here are just a safety net for the rare case it's too wide to fit
   in the corner alone (e.g. a very narrow phone, signed out, showing
   both "My profile" and "Sign in") - it wraps to a second short line
   within the corner rather than overflowing sideways. */
.header-actions{
  position:absolute;
  /* centred in the visible part of the header, below the status bar */
  top:calc(50% + env(safe-area-inset-top, 0px) / 2);
  right:1.4rem;
  transform:translateY(-50%);
  display:flex;
  align-items:center;
  justify-content:flex-end;
  gap:0.9rem;
  row-gap:0.5rem;
  flex-wrap:wrap;
  max-width:calc(100vw - 8rem);
}
.action-group{ display:flex; align-items:center; gap:0.5rem; flex-wrap:wrap; }
.action-group + .action-group{ padding-left:0.9rem; border-left:1px solid var(--line); }

/* "Find someone by name" collapses to an icon button next to the logo
   now that Home/Map are separate tabs — clicking it drops down the
   same #searchInput/#searchResults this always had (see
   .search-results below), instead of an always-visible inline box.
   The in-person filter moved out entirely, to the Map tab's own
   toolbar — see .map-toolbar further down. */
.icon-btn{
  width:36px; height:36px; flex:none;
  border-radius:50%;
  border:1px solid var(--line);
  background:var(--surface);
  display:flex; align-items:center; justify-content:center;
  font-size:1.05rem;
  color:var(--ink-dim);
  cursor:pointer;
  transition:border-color .15s var(--ease), box-shadow .15s var(--ease), color .15s var(--ease);
}
@media (hover:hover){ .icon-btn:hover{ border-color:var(--moss); color:var(--ink); box-shadow:var(--shadow); } }
/* ---- The search box (2026-10-10): memorials by name, and places. Sits
   at the top of the Home tab (in place of "Share a memory today…") and
   of the Map tab's toolbar - one element moved between the two (see
   placeSearchBox() in app.js). Replaced the header's magnifying-glass
   popover. 16px text so iPhone doesn't zoom when it's tapped. ---- */
.search-slot{ flex:1; min-width:0; }
.map-search-slot{ flex:1 1 100%; }
.search-box{ position:relative; }
.search-box-icon{
  position:absolute; left:0.85rem; top:50%; transform:translateY(-50%);
  width:16px; height:16px; color:var(--ink-dim); pointer-events:none;
}
.search-box input{
  width:100%;
  padding:0.6rem 2.4rem 0.6rem 2.3rem;
  border-radius:20px;
  border:1px solid transparent;
  background:var(--surface-moss);
  font-family:'Public Sans', sans-serif;
  font-size:16px;
  color:var(--ink);
  transition:background-color .15s var(--ease), border-color .15s var(--ease), box-shadow .15s var(--ease);
  -webkit-appearance:none; appearance:none; /* type="search": no iOS/Safari pill styling */
}
.search-box input::placeholder{ color:var(--ink-dim); }
@media (hover:hover){ .search-box input:hover{ background:var(--line); } }
.search-box input:focus{ outline:none; background:var(--surface); border-color:var(--moss); box-shadow:0 0 0 3px var(--surface-moss); }
.search-box input::-webkit-search-cancel-button{ -webkit-appearance:none; appearance:none; display:none; }
.search-clear{
  position:absolute; right:0.35rem; top:50%; transform:translateY(-50%);
  width:32px; height:32px; border:none; border-radius:50%;
  background:none; color:var(--ink-dim); font-size:1.2rem; line-height:1; cursor:pointer;
}
@media (hover:hover){ .search-clear:hover{ color:var(--ink); background:var(--line); } }
.search-clear[hidden]{ display:none; }
.search-box .search-results{
  position:absolute; top:calc(100% + 0.4rem); left:0; right:0; margin-top:0;
  z-index:1200;
  max-height:min(60vh, 420px); overflow-y:auto;
}
/* Results open over the feed / the map. */
.composer{ position:relative; z-index:5; }
.map-toolbar{ position:relative; z-index:1000; }

.btn{
  font-family:'Public Sans', sans-serif;
  font-size:0.9rem;
  font-weight:600;
  border-radius:9px;
  padding:0.55rem 1rem;
  border:1px solid var(--line);
  background:var(--surface);
  color:var(--ink);
  cursor:pointer;
  transition:background-color .15s var(--ease), border-color .15s var(--ease),
             color .15s var(--ease), box-shadow .15s var(--ease), transform .08s var(--ease);
}
@media (hover:hover){ .btn:hover{ border-color:var(--moss); box-shadow:var(--shadow); } }
.btn:active{ transform:translateY(1px); }
.btn:disabled{ opacity:0.55; cursor:not-allowed; box-shadow:none; transform:none; }
.btn-primary{
  background:var(--moss);
  border-color:var(--moss);
  color:#fbfaf5;
  box-shadow:0 1px 2px rgba(63,86,66,0.28);
}
@media (hover:hover){ .btn-primary:hover{ background:var(--moss-dark); border-color:var(--moss-dark); box-shadow:0 6px 16px -8px rgba(63,86,66,0.55); transform:translateY(-1px); } }
.btn-primary:active{ transform:translateY(0); }

.count-badge{
  display:inline-flex; align-items:center; justify-content:center;
  min-width:18px; height:18px;
  padding:0 5px;
  border-radius:9px;
  background:var(--amber);
  color:#fff;
  font-family:'IBM Plex Mono', monospace;
  font-size:0.72rem;
  font-weight:600;
  margin-left:0.35rem;
  box-shadow:0 0 0 2px var(--surface);
}

/* search results dropdown — anchored under .header-search now (see
   above); this part (background/border/animation) is unchanged from
   when it hung off the old floating .search-bar. */
.search-results{
  margin-top:0.4rem;
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--radius-md);
  box-shadow:var(--shadow-lift);
  overflow:hidden;
  animation:dropIn .16s var(--ease) both;
}
.search-results button{
  display:block;
  width:100%;
  text-align:left;
  padding:0.6rem 0.9rem;
  border:none;
  background:none;
  font-family:'Public Sans', sans-serif;
  font-size:0.9rem;
  color:var(--ink);
  cursor:pointer;
  border-bottom:1px solid var(--line);
  transition:background-color .12s var(--ease);
}
.search-results button:last-child{ border-bottom:none; }
.search-results button:focus-visible{ background:var(--surface-moss); }
@media (hover:hover){ .search-results button:hover{ background:var(--surface-moss); } }
/* Memorials + Places sections in the header search (2026-10-09). */
.search-results .search-section{
  padding:0.55rem 0.9rem 0.25rem;
  font-family:'IBM Plex Mono', ui-monospace, monospace;
  font-size:0.68rem; letter-spacing:0.08em; text-transform:uppercase;
  color:var(--ink-dim);
  background:var(--paper);
  border-bottom:1px solid var(--line);
}
.search-results .search-note{ padding:0.6rem 0.9rem; font-size:0.85rem; color:var(--ink-dim); border-bottom:1px solid var(--line); }
.search-results .search-note.is-error{ color:var(--amber-dark); }
.search-results button .icon{ width:14px; height:14px; vertical-align:-2px; color:var(--moss); }
.search-results button.is-current{ background:var(--surface-moss); }
.search-showing{ display:inline-block; margin-left:0.35rem; padding:0 0.4rem; border-radius:99px; background:var(--moss); color:#fff; font-size:0.7rem; font-weight:600; vertical-align:1px; }

/* left panel: moderator tools on top (moved here from the header —
   see below), the site's own quick actions next, "Your memorials"
   (what you follow) last — sits beside the map, same idea as the
   header used to hold the site's own buttons, just given room to also
   show a list. See the @media rules near the bottom for how this
   collapses on narrow screens. */
.app-shell{ display:flex; flex:1; min-height:0; }
main{ position:relative; flex:1; min-height:0; }

/* ---- the two tabs (Home feed / Map) that share <main> now, toggled
   by switchTab() in app.js — each fills main exactly the way #map
   alone used to. ---- */
.tab-view{ position:absolute; inset:0; }
/* Switching tabs used to be a hard cut — the only navigation moment in
   the app with no transition at all, entrance or exit, while every
   panel/sheet at least fades or slides. A quick cross-fade instead;
   switchTab() adds/removes these two classes and skips them entirely
   under prefers-reduced-motion. */
.tab-view.tab-fade-in{ animation:tabFadeIn .15s ease both; z-index:2; }
.tab-view.tab-fade-out{ animation:tabFadeOut .15s ease both; }
@keyframes tabFadeIn{ from{ opacity:0; } to{ opacity:1; } }
@keyframes tabFadeOut{ from{ opacity:1; } to{ opacity:0; } }

/* ---- Home tab: composer + feed ---- */
#homeView{ overflow-y:auto; background:var(--paper); }
.composer{
  display:flex; align-items:center; gap:0.7rem;
  padding:0.85rem 1.4rem;
  background:var(--surface);
  border-bottom:1px solid var(--line);
}
.composer-input{
  flex:1; text-align:left;
  background:var(--surface-moss);
  border:none; border-radius:20px;
  padding:0.6rem 1rem;
  color:var(--ink-dim);
  font-family:'Public Sans', sans-serif;
  font-size:0.9rem;
  cursor:pointer;
  transition:background-color .15s var(--ease);
}
@media (hover:hover){ .composer-input:hover{ background:var(--line); color:var(--ink); } }
/* The composer's own avatar bubble opens "My profile" now (see the
   delegated click handler on .composer in app.js) - scoped to just
   this one avatar, not the shared .avatar class used elsewhere for
   purely decorative avatars (feed cards, comments). */
.composer .avatar{ cursor:pointer; transition:box-shadow .15s var(--ease); }
.composer .avatar:focus-visible{ box-shadow:0 0 0 3px var(--surface-moss); }
@media (hover:hover){ .composer .avatar:hover{ box-shadow:0 0 0 3px var(--surface-moss); } }
.composer .avatar:focus-visible{ outline:2px solid var(--moss); outline-offset:2px; }
.feed-list{
  max-width:640px;
  margin:0 auto;
  padding:1.1rem 1.4rem 2.4rem;
  display:flex;
  flex-direction:column;
  gap:1.1rem;
}
.feed-empty{ text-align:center; padding:3rem 1.5rem; color:var(--ink-dim); }
.feed-empty h3{ margin-bottom:0.5rem; }

/* a memory's feed card is the same .post-card used for its own detail
   popup (see openMemoryPanel() in app.js and .post-card below) — just
   dropped into a plain list instead of a centered overlay, and given
   one small addition: a pill saying *why* it's in your feed. */
.feed-list .post-card{ width:100%; cursor:pointer; }
.origin-badge{
  display:inline-flex; align-items:center; gap:0.3rem; flex:none;
  font-family:'IBM Plex Mono', monospace; font-size:0.68rem; font-weight:600;
  letter-spacing:0.02em; border-radius:20px; padding:0.28rem 0.6rem;
  white-space:nowrap;
}
.origin-badge.following{ background:var(--surface-moss); color:var(--moss-dark); border:1px solid var(--moss); }
.origin-badge.nearby{ background:var(--amber-soft); color:var(--amber-dark); border:1px solid var(--amber); }

/* activity cards (comments/replies on things you posted) — a lighter
   format, no origin pill, since it's a notification about your own
   content rather than someone else's memorial. Shared with the Alerts
   panel — see .alert-item below. */
.activity-card{ padding:1.2rem 1.3rem; display:flex; flex-direction:column; gap:0.6rem; }
.activity-quote{
  margin:0 0 0 2.9rem; padding:0.6rem 0.85rem;
  background:var(--surface-moss); border-radius:var(--radius-md);
  font-size:0.88rem; color:var(--ink); font-style:italic;
}
.activity-actions{ display:flex; gap:1.2rem; margin-left:2.9rem; }
.activity-actions button{
  background:none; border:none; padding:0; cursor:pointer;
  font-family:'Public Sans', sans-serif; font-weight:600; font-size:0.84rem; color:var(--moss-dark);
}
@media (hover:hover){ .activity-actions button:hover{ text-decoration:underline; } }

/* ---- Map tab (2026-10-21): the map fills the tab; the search box, a row
   of chips and the Filters panel float over its top edge. ---- */
#mapView{ display:flex; flex-direction:column; }
.map-stage{ position:relative; flex:1; min-height:0; }
#map{ position:absolute; inset:0; background:#f4f0e4; }
.map-toolbar{
  position:absolute; top:0; left:0; right:0;
  padding:0.75rem 0.75rem 0;
  display:flex; flex-direction:column; gap:0.5rem;
  pointer-events:none; /* the map stays draggable between the chips */
}
.map-toolbar > *{ pointer-events:auto; }
.map-search-slot{ flex:none; }
.map-search-slot .search-box input{
  background:rgba(255,255,255,0.97);
  border-color:rgba(220,216,198,0.9);
  padding-top:0.75rem; padding-bottom:0.75rem;
  border-radius:999px;
  box-shadow:0 2px 6px rgba(43,46,38,0.12), 0 10px 24px -12px rgba(43,46,38,0.35);
}
@media (hover:hover){ .map-search-slot .search-box input:hover{ background:#fff; } }
.map-chips{
  display:flex; gap:0.35rem;
  overflow-x:auto; overflow-y:visible; scrollbar-width:none; -webkit-overflow-scrolling:touch;
  margin:0 -0.75rem; padding:0 0.75rem 0.4rem;
  pointer-events:none;
}
.map-chips::-webkit-scrollbar{ display:none; }
.map-chip{
  pointer-events:auto;
  flex:none;
  display:inline-flex; align-items:center; gap:0.35rem;
  min-height:38px; padding:0 0.7rem;
  border-radius:999px;
  border:1px solid rgba(220,216,198,0.9);
  background:rgba(255,255,255,0.97);
  box-shadow:0 1px 3px rgba(43,46,38,0.14), 0 6px 14px -8px rgba(43,46,38,0.3);
  font-family:'Public Sans', sans-serif; font-size:0.85rem; font-weight:600; color:var(--ink);
  white-space:nowrap;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  transition:border-color .15s var(--ease), background-color .15s var(--ease);
}
.map-chip .icon{ font-size:0.95rem; color:var(--moss-dark); }
@media (hover:hover){ .map-chip:hover{ border-color:var(--moss); } }
.map-chip:active{ background:var(--surface-moss); transition-duration:0s; }
.map-chip:focus-visible{ outline:2px solid var(--moss); outline-offset:2px; }
.map-chip:disabled{ opacity:0.7; cursor:default; }
.map-chip.is-active{ border-color:var(--moss); background:var(--surface-moss); }
/* Filters: icon only, so all four chips fit across a phone. */
.map-chip-icon{ position:relative; width:38px; padding:0; justify-content:center; }
.map-chip-icon .icon{ font-size:1.1rem; }
.map-chip-icon .map-chip-count{ position:absolute; top:-5px; right:-5px; }
.map-chip-count{
  min-width:18px; height:18px; padding:0 5px;
  border-radius:9px; background:var(--moss-dark); color:#fff;
  font-size:0.72rem; font-weight:700; line-height:18px; text-align:center;
}
.map-filters-panel{
  align-self:flex-start;
  width:min(320px, 100%);
  padding:0.8rem 0.95rem;
  border:1px solid var(--line);
  border-radius:var(--radius-lg);
  background:var(--surface);
  box-shadow:var(--shadow-lift);
  display:flex; flex-direction:column; gap:0.65rem;
  animation:mapFiltersIn .16s var(--ease) both;
}
@keyframes mapFiltersIn{ from{ opacity:0; transform:translateY(-4px); } to{ opacity:1; transform:none; } }
.map-filter-toggle{
  display:flex; align-items:center; gap:0.6rem;
  min-height:32px;
  font-size:0.9rem; color:var(--ink);
  cursor:pointer;
}
.map-filter-toggle input{ accent-color:var(--moss); width:18px; height:18px; margin:0; }
.map-legend{
  margin:0.1rem 0 0; padding-top:0.6rem; border-top:1px solid var(--line);
  display:flex; flex-wrap:wrap; gap:0.4rem 1rem;
  font-size:0.8rem; color:var(--ink-dim);
}
.map-legend-item{ display:inline-flex; align-items:center; gap:0.4rem; }
.map-legend .ft-marker{ width:12px; height:12px; border-width:2px; box-shadow:none; }
/* Narrow phones: Browse and Near me drop their icons so all four fit. */
@media (max-width:370px){
  #browseAllBtn .icon, #nearbyMemorialsBtn .icon{ display:none; }
}
/* While placing a memory's pin, only the pin and its bar matter. */
body.pin-adjusting .map-toolbar{ display:none; }
@media (min-width:641px){
  .map-toolbar{ right:auto; width:min(560px, 100%); padding:1rem 1rem 0; }
  .map-chips{ margin:0 -1rem; padding:0 1rem 0.4rem; }
}

/* Fixes a well-known Leaflet rendering quirk on high-DPI phone screens:
   hairline gaps between adjacent map tiles, visible as thin lines
   wherever tiles meet (most noticeable zoomed out, where several tiles
   are on screen at once) -- caused by the browser rounding each tile's
   sub-pixel position slightly differently. An outline doesn't take up
   layout space, so this extends each tile's painted edge just enough
   to cover the gap without shifting anything. */
.leaflet-tile{ outline:1px solid transparent; }

.leaflet-popup-content-wrapper{
  border-radius:var(--radius-md);
  font-family:'Public Sans', sans-serif;
  box-shadow:var(--shadow-lift);
}
.leaflet-popup-content{ margin:0.85rem 1rem; font-size:0.9rem; line-height:1.5; }

/* custom cluster + marker (2026-10-21: round amber dots and dark moss
   groups, to sit on the "Paper" map style) */
.ft-cluster-wrap{ background:none; border:none; }
.ft-cluster{
  width:36px; height:36px;
  border-radius:50%;
  background:var(--moss-dark);
  color:#fff;
  border:2.5px solid #f6f3ea;
  box-shadow:0 0 0 5px rgba(87,115,90,0.18), 0 2px 6px rgba(43,46,38,0.3);
  display:flex; align-items:center; justify-content:center;
  font-family:'Public Sans', sans-serif;
  font-size:0.82rem;
  font-weight:700;
  transition:transform .15s var(--ease), box-shadow .15s var(--ease);
}
@media (hover:hover){ .ft-cluster:hover{ transform:scale(1.08); } }

.ft-marker{
  width:18px; height:18px;
  border-radius:50%;
  background:var(--amber);
  border:2.5px solid #fff;
  box-shadow:0 0 0 5px rgba(185,136,76,0.22), 0 1px 3px rgba(43,46,38,0.35);
  transition:transform .15s var(--ease);
}
@media (hover:hover){ .ft-marker:hover{ transform:scale(1.2); } }
/* Memories posted in person (from the poster's own phone, standing at
   the spot) get the solid dot above. Memories added from afar - typed
   in, remembered, dragged into place later - get this hollow twin
   instead, so the map itself shows the difference at a glance. */
.ft-marker.is-manual{
  background:#fff;
  border:3px solid var(--amber);
}

/* Your own private memories ("Only me") - slate instead of amber, so
   they stand apart on your map. Only their author ever sees these. */
.ft-marker.is-private{ background:var(--ink-dim); box-shadow:0 0 0 5px rgba(94,99,86,0.2), 0 1px 3px rgba(43,46,38,0.35); }
.ft-marker.is-private.is-manual{ background:#fff; border:3px dashed var(--ink-dim); }
/* An included public memorial's memory that isn't counted in a private
   memorial's totals (2026-10-29): lighter and smaller, on the flat map
   behind the globe. */
.ft-marker.is-uncounted{ width:14px; height:14px; opacity:0.5; box-shadow:0 1px 2px rgba(43,46,38,0.25); }
/* Home on the flat map: the globe's dark green home pin. Smaller for a
   counted public memorial's own home. */
.ft-home-marker{
  width:16px; height:16px; border-radius:50%;
  background:var(--moss-dark); border:2.5px solid #fff;
  box-shadow:0 0 0 1.5px var(--moss-dark), 0 1px 3px rgba(43,46,38,0.35);
}
.ft-home-marker.is-small{ width:12px; height:12px; border-width:2px; box-shadow:0 1px 2px rgba(43,46,38,0.3); }

/* the temporary, draggable pin shown while placing a new memory — a
   soft pulse marks it as provisional and grabbable, not yet saved */
.ft-pending-marker{
  width:20px; height:20px;
  border-radius:50% 50% 50% 0;
  transform:rotate(-45deg);
  background:var(--amber);
  border:2px solid #fbfaf5;
  box-shadow:0 2px 6px rgba(0,0,0,0.4);
  cursor:grab;
  animation:pendingPulse 1.8s ease-in-out infinite;
}
.ft-pending-marker:active{ cursor:grabbing; }
@keyframes pendingPulse{
  0%, 100%{ box-shadow:0 2px 6px rgba(0,0,0,0.4), 0 0 0 0 rgba(185,136,76,0.35); }
  50%{ box-shadow:0 2px 6px rgba(0,0,0,0.4), 0 0 0 7px rgba(185,136,76,0); }
}

/* Zoom in / zoom out / whole map - one rounded stack in the bottom-right
   corner (MapControls in app.js). */
.ft-map-controls{
  display:flex; flex-direction:column;
  border-radius:14px; overflow:hidden;
  background:rgba(255,255,255,0.97);
  border:1px solid rgba(220,216,198,0.9);
  box-shadow:0 2px 6px rgba(43,46,38,0.12), 0 10px 24px -12px rgba(43,46,38,0.35);
}
.leaflet-bottom.leaflet-right .ft-map-controls{ margin:0 0.75rem 0.6rem 0; }
.ft-map-ctl{
  width:44px; height:44px;
  display:flex; align-items:center; justify-content:center;
  border:0; background:transparent;
  color:var(--moss-dark); font-size:1.15rem;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.ft-map-ctl + .ft-map-ctl{ border-top:1px solid #e6e1d0; }
@media (hover:hover){ .ft-map-ctl:hover{ background:var(--surface-moss); } }
.ft-map-ctl:active{ background:var(--line); }
.ft-map-ctl:focus-visible{ outline:2px solid var(--moss); outline-offset:-3px; }
/* Map credits: small and quiet, but always there (required). */
.leaflet-container .leaflet-control-attribution{
  background:rgba(246,243,234,0.8);
  font-size:10px; color:#8a8d80;
  border-radius:6px 0 0 0; padding:0 6px;
}
.leaflet-control-attribution a{ color:inherit; }
/* Only if the map style can't run and the plain tiles come back: soften
   them toward the palette. */
.ft-raster-tiles{ filter:sepia(0.18) saturate(0.7) brightness(1.03); }
/* Keep the corner control clear of the "Adjust pin" bar on phones. */
@media (max-width:640px){
  body.pin-adjusting .leaflet-bottom{ bottom:150px; }
}

/* panels (memory detail / person hub) */
.panel{
  position:absolute;
  z-index:1000;
  top:0; right:0; bottom:0;
  width:min(380px, 92vw);
  background:var(--surface);
  border-left:1px solid var(--line);
  box-shadow:-8px 0 24px -16px rgba(43,46,38,0.4), var(--shadow-lift);
  padding:2.6rem 1.6rem 1.6rem;
  overflow-y:auto;
}
/* Panels are toggled with the `hidden` attribute rather than a class,
   so this animation is defined unconditionally — the browser restarts
   it automatically each time an element goes from display:none back
   to visible, which is exactly when a panel opens. */
.panel:not([hidden]){ animation:panelIn .26s var(--ease) both; }
@keyframes panelIn{
  from{ opacity:0; transform:translateX(14px); }
  to{ opacity:1; transform:translateX(0); }
}
/* the matching close — every panel here used to vanish via `hidden`
   with no exit transition at all, the one place the app broke its own
   "enter and exit along the same path" rule. closePanel() in app.js
   adds this class, waits for it to finish, then sets `hidden`. */
.panel.is-closing{ animation:panelOut .18s var(--ease) both; }
@keyframes panelOut{
  from{ opacity:1; transform:translateX(0); }
  to{ opacity:0; transform:translateX(14px); }
}
@media (prefers-reduced-motion:reduce){
  .panel:not([hidden]), .search-results, .ft-pending-marker{ animation:none !important; }
  .beta-gate-card{ animation:none !important; }
  .panel-hub:not([hidden]), .panel-hub .hub-card{ animation:none !important; }
  #memoryPanel.panel-post:not([hidden]), #memoryPanel.panel-post .post-card{ animation:none !important; }
  .photo-strip{ scroll-behavior:auto; }
  .photo-banner .photo-dots span{ transition:none; }
  /* (The globe's slow drift checks reduced motion in JS; dragging it still works.) */
  /* First-login welcome popup's flower bed: drop the growth/sway
     transforms (real movement, the kind reduced-motion is meant to
     stop) but keep a quiet fade-in instead of a hard, instant cut —
     "reduce," not "remove entirely." See the flower-bed rules below. */
  .fl-bud-sway{ animation:none !important; }
  .fl-stem, .fl-leaf, .fl-bloom{
    animation:flReducedFade 260ms ease both !important;
    animation-delay:var(--delay, 0ms) !important;
    transform:none !important;
  }
}

/* The rest of the app's translucent chrome (the
   add-photo chip, the verify-badge link) leans on backdrop-filter —
   go near-solid and drop the blur when the system asks for less
   transparency, same spirit as the reduced-motion block above. */
@media (prefers-reduced-transparency:reduce){
  .add-photo-chip{ background:rgba(43,46,38,0.86); backdrop-filter:none; -webkit-backdrop-filter:none; }
  .verify-badge-link{ background:rgba(43,46,38,0.6); backdrop-filter:none; -webkit-backdrop-filter:none; }
}
@media (prefers-contrast:more){
  :root{ --line:#8a8a78; }
  .btn, .icon-btn, .panel, .hub-card, .post-card, .text-input,
  #addMemoryForm input, #addMemoryForm select, #addMemoryForm textarea,
  .auth-form input, .auth-form textarea{ border-width:1.5px; }
}

/* the "hub" treatment — a wide, centered overlay instead of the narrow
   side drawer every other .panel uses (see the markup note in
   app.html). Shared by the memorial page and, in the same visual
   language, the user profile page. More specific than .panel above,
   so this replaces its positioning and slide-in animation without
   touching any other panel. */
.panel-hub{
  position:fixed;
  inset:0;
  top:0; right:0; bottom:0; left:0;
  width:100%; height:100%;
  background:rgba(43,46,38,0.44);
  display:flex;
  align-items:flex-start;
  justify-content:center;
  /* The header stays visible on top of this overlay (it isn't part of
     it), so the card itself has to start below the header's real
     height, not just some fixed guess - see --header-h in :root and
     updateHeaderHeightVar() in app.js. Without this, the card's own
     top edge (title, close button) ended up hidden behind the header
     banner. */
  /* bottom: room for the bottom tab bar, which sits on top of these
     overlays (and is taller on an iPhone, where it clears the home bar). */
  padding:calc(var(--header-h) + 1.2rem) 1.4rem calc(var(--tabs-h) + 3rem);
  overflow-y:auto;
  border-left:none;
  box-shadow:none;
  animation:hubBackdropIn .22s var(--ease) both;
}
@keyframes hubBackdropIn{ from{ opacity:0; } to{ opacity:1; } }
.hub-card{
  position:relative;
  width:min(1040px, 100%);
  background:var(--surface);
  border-radius:var(--radius-lg);
  box-shadow:var(--shadow-lift);
  padding:2.4rem 2.6rem 2.8rem;
  flex:none;
  animation:hubCardIn .28s var(--ease) both;
  /* so the person hub's full-bleed photo banner (negative-margined out
     to the card's edges — see .photo-banner below) stays clipped to
     these rounded corners instead of spilling past them. */
  overflow:hidden;
}
@keyframes hubCardIn{
  from{ opacity:0; transform:translateY(10px); }
  to{ opacity:1; transform:translateY(0); }
}
/* matching close for both the hub overlay and the memory-detail overlay
   below (#memoryPanel.panel-post reuses these same keyframes) — see the
   .panel.is-closing note above. */
.panel-hub.is-closing{ animation:hubBackdropOut .16s var(--ease) both; }
.panel-hub.is-closing .hub-card{ animation:hubCardOut .16s var(--ease) both; }
@keyframes hubBackdropOut{ from{ opacity:1; } to{ opacity:0; } }
/* A tile's list (profile or memorial) is a narrower hub card, one level
   deeper than the page it came from - see hubNavigate() in app.js. While
   moving between the two, the backdrop doesn't fade (only the arriving
   page draws it), and the cards slide: deeper = in from the right, the
   old page out to the left; back = the exact reverse. */
.panel-hub-list .hub-card{ width:min(640px, 100%); }
.panel-hub-list.is-map-list .hub-card{ width:min(1040px, 100%); }
/* Shown underneath during the iPhone back swipe (wireIosBackSwipe() in
   app.js): appears as it was, without replaying its opening animation.
   Cleared the next time it moves or hides. */
.panel.panel-hub.hub-no-enter:not(.is-closing), .panel-hub.hub-no-enter:not(.is-closing) .hub-card{ animation:none; }
.panel.panel-hub.hub-enter-forward:not([hidden]), .panel.panel-hub.hub-enter-back:not([hidden]),
.panel.panel-hub.hub-leave-forward:not([hidden]), .panel.panel-hub.hub-leave-back:not([hidden]){ animation:none; }
/* Only one backdrop at a time: the owner page (profile/memorial, earlier
   in the DOM, so underneath) keeps its own; the list on top is
   see-through while it slides in or out. On phones that backdrop is the
   solid page background (see the max-width:640px block). */
.panel.panel-hub-list.hub-enter-forward, .panel.panel-hub-list.hub-leave-back{ background:transparent; }
.panel.panel-hub.hub-leave-forward, .panel.panel-hub.hub-leave-back{ pointer-events:none; }
.panel-hub.hub-enter-forward .hub-card{ animation:hubCardFromRight .3s var(--ease) both; }
.panel-hub.hub-leave-forward .hub-card{ animation:hubCardToLeft .26s var(--ease) both; }
.panel-hub.hub-enter-back .hub-card{ animation:hubCardFromLeft .3s var(--ease) both; }
.panel-hub.hub-leave-back .hub-card{ animation:hubCardToRight .26s var(--ease) both; }
@keyframes hubCardFromRight{ from{ opacity:0; transform:translateX(48px); } to{ opacity:1; transform:translateX(0); } }
@keyframes hubCardToLeft{ from{ opacity:1; transform:translateX(0); } to{ opacity:0; transform:translateX(-48px); } }
@keyframes hubCardFromLeft{ from{ opacity:0; transform:translateX(-48px); } to{ opacity:1; transform:translateX(0); } }
@keyframes hubCardToRight{ from{ opacity:1; transform:translateX(0); } to{ opacity:0; transform:translateX(48px); } }

@keyframes hubCardOut{
  from{ opacity:1; transform:translateY(0); }
  to{ opacity:0; transform:translateY(8px); }
}
.hub-card .panel-close{ position:absolute; top:1.5rem; right:1.6rem; }
.panel-close{
  position:absolute;
  top:0.9rem; right:1rem;
  width:30px; height:30px;
  border-radius:50%;
  border:1px solid var(--line);
  background:var(--surface);
  font-size:1.1rem;
  line-height:1;
  cursor:pointer;
  color:var(--ink-dim);
  transition:color .15s var(--ease), border-color .15s var(--ease), background-color .15s var(--ease), transform .1s var(--ease);
}
@media (hover:hover){ .panel-close:hover{ color:var(--ink); border-color:var(--moss); background:var(--surface-moss); transform:rotate(90deg); } }

.eyebrow{
  font-family:'IBM Plex Mono', monospace;
  font-size:0.68rem;
  letter-spacing:0.1em;
  text-transform:uppercase;
  color:var(--moss);
  margin-bottom:0.4rem;
}
.photo-placeholder{
  width:100%;
  aspect-ratio: 4/3;
  border-radius:var(--radius-md);
  background: linear-gradient(135deg, var(--amber-soft), var(--surface-moss));
  display:flex; align-items:center; justify-content:center;
  color:var(--ink-dim);
  font-size:0.8rem;
  margin:0.8rem 0;
  border:1px solid var(--line);
}

/* photo grid — used both for picking photos in the add-memory form and
   for showing them on a memory's detail view */
.photo-grid{
  display:grid;
  grid-template-columns:repeat(3, 1fr);
  gap:0.5rem;
  margin:0.6rem 0;
}
.photo-grid img{
  width:100%;
  aspect-ratio:1;
  object-fit:cover;
  border-radius:var(--radius-sm);
  border:1px solid var(--line);
  display:block;
  transition:transform .2s var(--ease), box-shadow .2s var(--ease);
}
.photo-grid a{ display:block; border-radius:var(--radius-sm); overflow:hidden; }
@media (hover:hover){ .photo-grid a:hover img{ transform:scale(1.06); box-shadow:var(--shadow); } }
.photo-thumb{ position:relative; }
.photo-remove-btn{
  position:absolute;
  top:4px; right:4px;
  width:22px; height:22px;
  border-radius:50%;
  background:rgba(43,46,38,0.72);
  color:#fbfaf5;
  border:none;
  font-size:0.85rem;
  line-height:1;
  cursor:pointer;
  transition:background-color .15s var(--ease), transform .1s var(--ease);
}
@media (hover:hover){ .photo-remove-btn:hover{ background:rgba(138,68,40,0.92); transform:scale(1.08); } }
.story-text{ line-height:1.65; color:var(--ink); margin:0.6rem 0 1rem; white-space:pre-wrap; }
.meta-row{
  display:flex; align-items:center; gap:0.55rem;
  font-size:0.85rem; color:var(--ink-dim);
  margin-top:0.9rem;
}
.avatar{
  width:26px; height:26px; border-radius:50%;
  background:var(--moss);
  color:#fbfaf5;
  display:flex; align-items:center; justify-content:center;
  font-family:'IBM Plex Mono', monospace;
  font-size:0.7rem; font-weight:600;
  flex:none;
}
.link-row a, .poster-link{
  color:var(--moss-dark);
  font-weight:600;
  text-decoration:none;
  border-bottom:1px solid var(--moss);
  transition:color .15s var(--ease), border-color .15s var(--ease);
}
@media (hover:hover){ .link-row a:hover, .poster-link:hover{ color:var(--moss); border-color:var(--moss-dark); } }

/* "posted in person" badge on a memory's detail panel — quiet, not a
   verification stamp, just an observation. Memories added from afar
   simply don't get one, rather than being marked as lesser. */
.visit-badge{
  display:inline-flex; align-items:center; gap:0.35rem;
  font-size:0.78rem; color:var(--amber-dark);
  background:var(--amber-soft);
  border:1px solid var(--line);
  border-radius:20px;
  padding:0.25rem 0.7rem;
  margin:0.5rem 0 0;
}
.locate-status-note{ display:inline-block; margin-top:0.2rem; font-weight:400; color:var(--ink-dim); }

/* memory detail popup — a wide, centered overlay (same treatment as
   the person hub below) instead of the old narrow side drawer, styled
   after a familiar social-post layout: poster header, story body,
   photo, comment count, then an action row. Same moss/paper palette
   as the rest of the app throughout — never a borrowed color scheme.
   See openMemoryPanel() in app.js. */
#memoryPanel.panel-post{
  position:fixed;
  inset:0;
  top:0; right:0; bottom:0; left:0;
  width:100%; height:100%;
  background:rgba(43,46,38,0.44);
  display:flex;
  align-items:flex-start;
  justify-content:center;
  /* Same reasoning as .panel-hub above - clears the header's real
     height (--header-h) instead of a fixed guess, so the close button
     and header of the post card aren't hidden behind the header banner. */
  /* bottom: room for the bottom tab bar, which sits on top of these
     overlays (and is taller on an iPhone, where it clears the home bar). */
  padding:calc(var(--header-h) + 1.2rem) 1.4rem calc(var(--tabs-h) + 3rem);
  overflow-y:auto;
  border-left:none;
  box-shadow:none;
  animation:hubBackdropIn .22s var(--ease) both;
}
.post-card{
  position:relative;
  width:min(560px, 100%);
  background:var(--surface);
  border-radius:var(--radius-lg);
  box-shadow:var(--shadow-lift);
  overflow:hidden;
  flex:none;
  padding-bottom:0.3rem;
  animation:hubCardIn .28s var(--ease) both;
}
.post-card .panel-close{ position:absolute; top:1.1rem; right:1.2rem; z-index:3; }
#memoryPanel.panel-post.is-closing{ animation:hubBackdropOut .16s var(--ease) both; }
#memoryPanel.panel-post.is-closing .post-card{ animation:hubCardOut .16s var(--ease) both; }

.post-header{ display:flex; align-items:flex-start; gap:0.7rem; padding:1.3rem 3rem 0.9rem 1.3rem; }
.avatar-lg{ width:42px; height:42px; font-size:0.85rem; }
.post-header .who{ flex:1; min-width:0; }
.poster-name{ font-weight:700; font-size:0.98rem; color:var(--ink); text-decoration:none; border-bottom:none; }
@media (hover:hover){ .poster-name:hover{ text-decoration:underline; color:var(--moss-dark); } }
.post-sub{ font-size:0.8rem; color:var(--ink-dim); margin-top:0.15rem; }
.post-sub a{ color:var(--ink-dim); font-weight:600; text-decoration:none; border-bottom:none; }
@media (hover:hover){ .post-sub a:hover{ color:var(--moss-dark); text-decoration:underline; } }

.post-menu-wrap{ position:relative; }
.post-menu-btn{ background:none; border:none; color:var(--ink-dim); font-size:1.3rem; line-height:1; cursor:pointer; padding:0.25rem 0.5rem; border-radius:6px; }
@media (hover:hover){ .post-menu-btn:hover{ background:var(--surface-moss); color:var(--ink); } }

.post-body{ padding:0 1.3rem 0.9rem; }
.post-title{ font-size:1.1rem; margin-bottom:0.35rem; }
.post-story{ font-size:0.92rem; line-height:1.6; color:var(--ink); margin:0; white-space:pre-wrap; }

/* the photo (or placeholder) sits edge-to-edge with its own borders,
   the way a social post's photo would — not padded like the rest of
   the card. A single photo shows full-bleed; more than one falls back
   to the existing small grid (also used when picking photos on the
   add-memory form). */
.post-media{ border-top:1px solid var(--line); border-bottom:1px solid var(--line); background:var(--surface-moss); }
.post-media .photo-placeholder{ margin:0; border:none; border-radius:0; }
.post-media .photo-grid{ margin:0; padding:0.6rem; }
.post-photo-single{ display:block; }
.post-photo-single img{ width:100%; aspect-ratio:4/3; object-fit:cover; display:block; }

.post-summary{ display:flex; align-items:center; justify-content:flex-end; padding:0.6rem 1.3rem; font-size:0.82rem; color:var(--ink-dim); }

/* Like/Comment/Share, reinterpreted for what you can actually do with
   a memory: jump to the comment box, jump to its memorial, or flag it. */
.post-actions{ display:flex; border-top:1px solid var(--line); padding:0.3rem 0.6rem; }
.post-action-btn{
  flex:1; display:flex; align-items:center; justify-content:center; gap:0.5rem;
  background:none; border:none; border-radius:var(--radius-sm);
  padding:0.6rem 0.5rem;
  font-family:'Public Sans', sans-serif; font-weight:600; font-size:0.86rem;
  color:var(--ink-dim); cursor:pointer;
  transition:background-color .12s var(--ease), color .12s var(--ease);
}
@media (hover:hover){ .post-action-btn:hover:not(:disabled){ background:var(--surface-moss); color:var(--ink); } }
.post-action-btn:disabled{ cursor:default; color:var(--moss-dark); }

.post-comments{ padding:1rem 1.3rem 1.2rem; border-top:1px solid var(--line); background:var(--paper); }

/* person hub — "memorial at a glance" stat tiles (memories added,
   miles traveled, following, countries visited). Following and
   Countries visited start as "…" and fill in once their async lookups
   (a follower-count RPC, reverse-geocoding) resolve — see
   openPersonPanel() in app.js. */
.hub-stats-grid{ display:flex; gap:1rem; flex-wrap:wrap; margin:1.6rem 0 2.2rem; }
.stat-tile{
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--radius-lg);
  box-shadow:var(--shadow);
  padding:1.1rem 1.2rem 1.2rem;
  flex:1 1 190px;
}
.stat-figure{
  font-family:'Fraunces', Georgia, serif;
  font-weight:600;
  font-size:2.1rem;
  color:var(--ink);
  line-height:1.1;
  margin-top:0.3rem;
}
.stat-caption{
  font-size:0.82rem;
  color:var(--ink-dim);
  margin-top:0.35rem;
  line-height:1.4;
}

/* person hub — "where they're remembered" (the globe) and "top
   places" side by side, since they're really two views of the same
   list of memories. Each column is free to be a different natural
   height (the globe is a little smaller in its column - see
   .hub-map-col .globe-canvas below). Stacks back to
   one column on narrow screens (see the max-width:640px block near
   the bottom). */
.hub-map-places-row{
  display:flex;
  align-items:flex-start;
  gap:1.2rem;
  margin-bottom:1.6rem;
}
.hub-map-col, .hub-places-col{ flex:1 1 0; min-width:0; }

/* person hub — "where they're remembered" / profile "where you've added
   memories": a real globe drawn on a canvas (initGlobe() in app.js,
   2026-10-21; replaced the CSS ball with oval "continents"). The profile
   page reuses the same .hub-map-panel full-width; the memorial page puts
   it in a column beside "Top places" (.hub-map-col). */
.hub-map-panel{
  background:linear-gradient(180deg, #f3f1e8, #eceee2);
  border:1px solid var(--line);
  border-radius:var(--radius-lg);
  overflow:hidden;
  margin-bottom:1.6rem;
}
.globe-mount{
  width:100%;
  display:flex; flex-direction:column; align-items:center;
  gap:0.45rem;
  padding:1rem 0.8rem 0.85rem;
}
.hub-map-col .hub-map-panel{ margin-bottom:0; }
.globe-canvas{
  display:block;
  width:min(250px, 82%); aspect-ratio:1 / 1;
  cursor:grab;
  touch-action:pan-y; /* sideways drag spins it; up/down still scrolls the page */
  -webkit-tap-highlight-color:transparent;
}
.globe-canvas:active{ cursor:grabbing; }
.hub-map-col .globe-canvas{ width:min(210px, 86%); }
.globe-legend{
  display:flex; flex-wrap:wrap; justify-content:center; gap:0.3rem 1rem;
  font-size:0.78rem; color:var(--ink-dim);
}
.globe-key{ display:inline-flex; align-items:center; gap:0.4rem; }
.globe-key-dot{
  width:9px; height:9px; border-radius:50%;
  background:var(--amber); box-shadow:0 0 0 2px #fff;
}
.globe-key-dot.is-home{ background:var(--moss-dark); box-shadow:0 0 0 2px #fff, 0 0 0 3px var(--moss-dark); }
.globe-key-dot.is-uncounted{ opacity:0.45; }
.globe-hint{ font-size:0.72rem; color:var(--ink-dim); margin:0; text-align:center; }
/* The flat map a double-click / double-tap on a globe opens (2026-10-29):
   as tall as the screen allows below the header, above the tab bar. */
.flat-map{
  width:100%;
  height:calc(100vh - var(--header-h) - var(--tabs-h) - 15rem);
  height:calc(100dvh - var(--header-h) - var(--tabs-h) - 15rem);
  min-height:320px;
  margin:0.8rem 0 0.7rem;
  border-radius:var(--radius-md);
  overflow:hidden;
  background:#e9e4d6;
}
.flat-map-legend{ margin-bottom:0.3rem; }

/* person hub — top 5 places, ranked by memory count */
.top-places-list{
  border:1px solid var(--line);
  border-radius:var(--radius-md);
  background:var(--surface);
  padding:0 1rem;
  margin-bottom:1.6rem;
}
.hub-places-col .top-places-list{ margin-bottom:0; }
.top-place-row{
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:1rem;
  padding:0.55rem 0;
  border-bottom:1px solid var(--line);
  font-size:0.92rem;
}
.top-place-row:last-child{ border-bottom:none; }
.top-place-name{ font-weight:600; color:var(--ink); }
/* On the profile page's "memorials you follow" list, this name is a
   link to that memorial rather than plain text. */
.top-place-name a{ color:var(--ink); text-decoration:none; }
@media (hover:hover){ .top-place-name a:hover{ color:var(--moss-dark); text-decoration:underline; } }
.top-place-count{
  font-family:'IBM Plex Mono', monospace;
  font-size:0.78rem;
  color:var(--moss-dark);
  white-space:nowrap;
}

.hub-dates{ color:var(--ink-dim); font-size:0.9rem; margin:0.15rem 0 0.9rem; }

/* person hub — the photo banner across the top of the card, replacing
   the old plain gradient-circle placeholder. Real photos pulled from
   the memories people have added to this memorial, crossfading slowly
   with a light Ken Burns settle; falls back to a small set of muted
   stock scenes when nothing's been posted yet. The crossfade/dot
   keyframes depend on how many photos are showing, so they're
   generated fresh into a <style id="photoBannerKeyframes"> each time
   a banner is built rather than fixed here — see
   injectPhotoBannerKeyframes() and buildPhotoBannerHtml() in app.js.
   (.hub-card above gets overflow:hidden so this bleeds to the card's
   rounded corners instead of spilling past them.) */
.photo-banner{
  position:relative;
  margin:-2.4rem -2.6rem 1.7rem;
  height:320px;
  background:var(--surface-moss);
}
.photo-banner .photo-strip{ position:absolute; inset:0; }
.photo-banner .photo-strip-slide img{ height:100%; }
.photo-banner .scrim{
  position:absolute; inset:0;
  background:linear-gradient(to top, rgba(28,30,24,0.86) 0%, rgba(28,30,24,0.42) 44%, rgba(28,30,24,0) 74%);
  pointer-events:none;
}
.photo-banner .photo-dots{
  position:absolute; right:1.5rem; bottom:1.6rem; z-index:3;
  display:flex; gap:0.4rem;
  pointer-events:none;
}
.photo-banner .photo-dots span{
  width:6px; height:6px; border-radius:50%;
  background:rgba(255,255,255,0.4);
  transition:background-color .2s var(--ease), transform .2s var(--ease);
}
.photo-banner .photo-dots span.is-active{ background:#fff; transform:scale(1.25); }

/* a quiet nudge shown only when there are no real photos yet */
.add-photo-chip{
  position:absolute; top:1.5rem; left:1.6rem; z-index:3;
  display:inline-flex; align-items:center; gap:0.4rem;
  font-family:'IBM Plex Mono', monospace;
  font-size:0.68rem; letter-spacing:0.06em; text-transform:uppercase;
  color:#fff;
  background:rgba(43,46,38,0.4);
  border:1px solid rgba(255,255,255,0.3);
  border-radius:20px;
  padding:0.4rem 0.8rem 0.4rem 0.6rem;
  backdrop-filter:blur(4px);
  -webkit-backdrop-filter:blur(4px);
}
.add-photo-chip svg{ width:14px; height:14px; flex:none; }

.banner-content{
  position:absolute; left:0; right:0; bottom:0; z-index:2;
  padding:1.5rem 2.6rem 1.4rem;
  pointer-events:none; /* swipes and taps reach the photos underneath... */
}
.banner-content a{ pointer-events:auto; } /* ...except the obituary link */
.banner-content h3{ font-size:2.1rem; letter-spacing:-0.015em; color:#fbfaf5; text-shadow:0 1px 4px rgba(0,0,0,0.22); }
.banner-content .hub-dates{ color:rgba(255,255,255,0.82); margin:0.2rem 0 0.85rem; }

/* the verified-obituary badge is the link now — tapping it opens the
   published obituary — instead of a separate "View the published
   obituary" line underneath it. */
.verify-badge-link{
  display:inline-flex; align-items:center; gap:0.45rem;
  font-family:'Public Sans', sans-serif;
  font-size:0.82rem; font-weight:600;
  color:#fff;
  background:rgba(255,255,255,0.16);
  border:1px solid rgba(255,255,255,0.42);
  border-radius:20px;
  padding:0.34rem 0.75rem 0.34rem 0.6rem;
  backdrop-filter:blur(6px) saturate(140%);
  -webkit-backdrop-filter:blur(6px) saturate(140%);
  text-decoration:none;
  transition:background-color .15s var(--ease), border-color .15s var(--ease), transform .1s var(--ease);
}
@media (hover:hover){ .verify-badge-link:hover{ background:rgba(255,255,255,0.28); border-color:rgba(255,255,255,0.62); color:#fff; transform:translateY(-1px); } }
.verify-badge-link svg{ width:14px; height:14px; flex:none; display:block; }
.verify-badge-link .ext-icon{ opacity:0.85; }

/* the panel-close × sits directly over the photo banner now, so it
   needs its own light-on-dark treatment instead of the default
   .panel-close style. */
#personPanel .panel-close{
  /* .photo-slide/.scrim/etc. inside #personPanelContent are positioned
     too, and — since #personPanelContent comes after this button in the
     markup — would otherwise paint on top of it at the same implied
     stacking order; this just needs to out-rank all of them (highest
     among them is .photo-dots/.add-photo-chip at z-index:3). */
  z-index:5;
  border-color:rgba(255,255,255,0.55);
  background:rgba(43,46,38,0.28);
  color:#fff;
}
@media (hover:hover){ #personPanel .panel-close:hover{ background:rgba(43,46,38,0.45); border-color:#fff; color:#fff; transform:rotate(90deg); } }

/* user profile */
.profile-avatar{
  width:72px; height:72px; border-radius:50%;
  background: radial-gradient(circle at 35% 30%, var(--amber) 0%, var(--moss) 75%);
  color:#fbfaf5;
  display:flex; align-items:center; justify-content:center;
  font-family:'IBM Plex Mono', monospace;
  font-size:1.35rem; font-weight:600;
  box-shadow:0 0 0 4px var(--surface-moss);
  margin-bottom:0.8rem;
}
.profile-bio{ color:var(--ink-dim); font-size:0.92rem; font-style:italic; margin:0.2rem 0 1.1rem; line-height:1.5; }
/* "Edit profile" + "Sign out", together at the top of your own "My
   profile" panel — see the isOwnProfile branch of openUserProfile()
   in app.js. Sign out moved here from the header, so this is where
   both account-level actions live now. */
.profile-account-actions{ display:flex; align-items:center; flex-wrap:wrap; gap:0.5rem 1.1rem; margin-bottom:0.9rem; }
.verify-badge{
  display:inline-flex; align-items:center; gap:0.35rem;
  font-size:0.78rem; color:var(--moss-dark);
  background:var(--surface-moss);
  border:1px solid var(--line);
  border-radius:20px;
  padding:0.25rem 0.7rem;
  margin-bottom:1.2rem;
}

.hub-memory-list{ list-style:none; margin:0.6rem 0 0; padding:0; display:flex; flex-direction:column; gap:0.6rem; }
.hub-memory-list li{
  border:1px solid var(--line);
  border-radius:9px;
  padding:0.65rem 0.8rem;
  cursor:pointer;
  background:var(--surface);
  transition:border-color .15s var(--ease), box-shadow .15s var(--ease), transform .1s var(--ease);
}
@media (hover:hover){ .hub-memory-list li:hover{ border-color:var(--moss); box-shadow:var(--shadow); transform:translateY(-1px); } }
.hub-memory-list .memory-title{ font-weight:600; font-size:0.9rem; }
.hub-memory-list .snippet{ font-size:0.84rem; color:var(--ink-dim); margin-top:0.15rem; }

/* comments on a memory — chat-bubble styled, same slot Facebook's
   comment thread would occupy under the post-actions row */
.comment-list{
  list-style:none; margin:0.6rem 0 0; padding:0;
  display:flex; flex-direction:column; gap:0.7rem;
}
.comment-item{ display:flex; gap:0.55rem; align-items:flex-start; }
.comment-item .avatar{ margin-top:0.1rem; }
.comment-body{ flex:1; min-width:0; }
.comment-bubble{
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--radius-lg);
  padding:0.5rem 0.8rem;
}
.comment-meta a{
  font-weight:700; font-size:0.85rem;
  color:var(--ink); text-decoration:none;
  border-bottom:none;
}
@media (hover:hover){ .comment-meta a:hover{ color:var(--moss-dark); text-decoration:underline; } }
.comment-date{ font-size:0.76rem; color:var(--ink-dim); margin-left:0.4rem; }
.comment-text{ margin:0.15rem 0 0; font-size:0.86rem; line-height:1.45; color:var(--ink); white-space:pre-wrap; }
.comment-foot{
  display:flex; align-items:center; gap:0.9rem;
  margin:0.3rem 0 0 0.8rem;
  font-size:0.74rem; color:var(--ink-dim);
}

.comment-report-btn{
  margin-top:0.35rem;
  margin-right:0.9rem;
  background:none; border:none; padding:0;
  font-family:'Public Sans', sans-serif;
  font-size:0.76rem;
  color:var(--ink-dim);
  cursor:pointer;
  transition:color .15s var(--ease);
}
@media (hover:hover){ .comment-report-btn:hover:not(:disabled){ color:var(--amber-dark); text-decoration:underline; } }
.comment-report-btn:disabled{ cursor:default; color:var(--moss-dark); }

/* follow a memorial — sits right under the obituary link, before the
   miles-traveled figure. Plain .btn when not following; filled like
   .btn-primary once you are, so the state reads at a glance. */
.follow-row{ display:flex; gap:0.6rem; flex-wrap:wrap; margin:0.9rem 0 0; }
.follow-row .btn{ flex:1; min-width:150px; }
.follow-row .btn[data-menu-trigger]{ flex:none; min-width:0; }

/* the small "copy link / share via… / email / Facebook" popover
   opened from a 🔗 Share button — see openShareMenu() in app.js.
   Positioned fixed from the trigger button's own coordinates (not a
   normal child), so it always floats correctly above any panel,
   including the person hub's full-viewport overlay. */
.share-menu{
  position:fixed;
  z-index:1300;
  width:206px;
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--radius-md);
  box-shadow:var(--shadow-lift);
  overflow:hidden;
  animation:dropIn .16s var(--ease) both;
}
.share-menu button{
  display:block;
  width:100%;
  text-align:left;
  padding:0.6rem 0.9rem;
  border:none;
  background:none;
  font-family:'Public Sans', sans-serif;
  font-size:0.88rem;
  color:var(--ink);
  cursor:pointer;
  border-bottom:1px solid var(--line);
  transition:background-color .12s var(--ease);
}
.share-menu button:last-child{ border-bottom:none; }
.share-menu button:focus-visible{ background:var(--surface-moss); }
@media (hover:hover){ .share-menu button:hover{ background:var(--surface-moss); } }
.share-menu.is-closing{ animation:dropOut .14s var(--ease) both; }
@keyframes dropOut{
  from{ opacity:1; transform:translateY(0); }
  to{ opacity:0; transform:translateY(-4px); }
}

/* person hub actions (report / moderator delete) */
.hub-actions{ display:flex; align-items:center; gap:1rem; flex-wrap:wrap; margin-top:1.4rem; }
.hub-actions .comment-report-btn{ margin-top:0; }
.hub-actions .btn{ flex:none; }

/* reported-comments queue */
.report-reasons{
  margin:0.3rem 0 0.7rem;
  padding-left:1.1rem;
  font-size:0.85rem;
  color:var(--ink-dim);
  line-height:1.5;
}

.comment-form{ margin-top:1rem; display:flex; flex-direction:column; gap:0.5rem; }
.comment-form textarea{
  font-family:'Public Sans', sans-serif;
  font-size:0.88rem;
  padding:0.55rem 0.7rem;
  border-radius:var(--radius-sm);
  border:1px solid var(--line);
  background:var(--paper);
  color:var(--ink);
  resize:vertical;
  transition:border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.comment-form textarea:focus-visible{ border-color:var(--moss); box-shadow:0 0 0 3px var(--surface-moss); }
.comment-form-row{ display:flex; gap:0.5rem; }
.comment-form-row input{
  flex:1; min-width:0;
  font-family:'Public Sans', sans-serif;
  font-size:0.85rem;
  padding:0.5rem 0.65rem;
  border-radius:var(--radius-sm);
  border:1px solid var(--line);
  background:var(--paper);
  color:var(--ink);
  transition:border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.comment-form-row input:focus-visible{ border-color:var(--moss); box-shadow:0 0 0 3px var(--surface-moss); }
.comment-form-row .btn{ flex:none; }

/* review queue */
.review-list{ list-style:none; margin:0.6rem 0 0; padding:0; display:flex; flex-direction:column; gap:0.9rem; }
.review-item{
  border:1px solid var(--line);
  border-radius:var(--radius-md);
  padding:0.85rem 0.9rem;
  background:var(--surface);
  transition:box-shadow .15s var(--ease);
}
@media (hover:hover){ .review-item:hover{ box-shadow:var(--shadow); } }
.review-item h4{
  font-family:'Fraunces', Georgia, serif;
  font-weight:600;
  font-size:1.05rem;
  margin:0 0 0.15rem;
}
.review-item .hub-dates{ margin:0 0 0.5rem; }
.review-item .submitted-by{ font-size:0.8rem; color:var(--ink-dim); margin:0.4rem 0 0.7rem; }
.review-actions{ display:flex; gap:0.5rem; margin-top:0.6rem; }
.review-actions .btn{ flex:1; }

/* browse-all memorials */
.text-input{
  width:100%;
  padding:0.6rem 0.9rem;
  border-radius:var(--radius-md);
  border:1px solid var(--line);
  background:var(--paper);
  color:var(--ink);
  font-size:0.92rem;
  font-family:'Public Sans', sans-serif;
  margin:0.6rem 0 0.4rem;
  transition:border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.text-input:focus-visible{ border-color:var(--moss); box-shadow:0 0 0 3px var(--surface-moss); }
.directory-list{ list-style:none; margin:0.6rem 0 0; padding:0; display:flex; flex-direction:column; gap:0.9rem; }
.directory-item{
  border:1px solid var(--line);
  border-radius:var(--radius-md);
  padding:0.85rem 0.9rem;
  background:var(--surface);
  transition:box-shadow .15s var(--ease);
}
@media (hover:hover){ .directory-item:hover{ box-shadow:var(--shadow); } }
.directory-item h4{
  font-family:'Fraunces', Georgia, serif;
  font-weight:600;
  font-size:1.05rem;
  margin:0 0 0.15rem;
}
.directory-item h4 a{ color:var(--ink); text-decoration:none; }
@media (hover:hover){ .directory-item h4 a:hover{ color:var(--moss-dark); text-decoration:underline; } }
.directory-item .hub-dates{ margin:0 0 0.4rem; }
.btn-reject{ color:var(--amber-dark); border-color:var(--amber); }
@media (hover:hover){ .btn-reject:hover{ background:var(--amber-soft); border-color:var(--amber-dark); } }
.empty-note{ color:var(--ink-dim); font-size:0.9rem; margin-top:0.8rem; }
.empty-note::before{ content:'🌿 '; }

/* add-a-memory panel (docked to the side, map stays clickable) */
.add-panel h2{ font-size:1.3rem; margin-bottom:0.5rem; }
.modal-hint{ font-size:0.85rem; color:var(--ink-dim); margin:0.3rem 0 1rem; line-height:1.5; }
.auth-tagline{ font-style:italic; }

/* post-signup welcome panel — two full-width next steps, not a row of
   equal-width buttons, since one (create a memorial) is the far more
   likely first move than the other */
.welcome-option{ margin-top:1.5rem; }
.welcome-option:first-of-type{ margin-top:1.1rem; }
.welcome-option .btn{ width:100%; }
.welcome-option .modal-hint{ margin:0.6rem 0 0; }

/* ---------------------------------------------------------------
   First-login welcome popup (community guidelines) — see
   openFirstLoginWelcomeModal() in app.js and
   supabase-migration-add-welcome-popup.sql. Reuses the same
   .panel-hub centered-backdrop treatment the memorial/profile pages
   use (so it gets the dim overlay, entrance fade, and click-outside-
   to-close behavior for free), just with a much narrower card and
   overflow left open so the flower bed can bloom past its edges. */
#firstLoginWelcomeModal{ align-items:center; }
.hub-card.welcome-guidelines-card{
  width:min(480px, 100%);
  padding:2.8rem 2.2rem 2.2rem;
  overflow:visible;
  text-align:center;
}
.welcome-guidelines-card h2{ font-size:1.35rem; margin-bottom:0.3rem; }
.fl-subline{ text-align:center; font-style:italic; }
.fl-guidelines{
  text-align:left;
  background:var(--surface-moss);
  border:1px solid var(--line);
  border-radius:var(--radius-md);
  padding:1rem 1.2rem;
  margin:0 0 1.4rem;
  font-size:0.88rem;
  line-height:1.6;
}
.fl-guidelines p{ margin:0 0 0.55rem; }
.fl-guidelines p:last-child{ margin-bottom:0; }
.welcome-guidelines-card > #firstLoginWelcomeContent > .btn{ width:auto; padding-left:1.8rem; padding-right:1.8rem; }

/* the flower bed itself — a ring of small SVG wildflowers "growing"
   up from around the card's lower edge and sides, staggered so they
   sprout one after another rather than all at once. Rare, first-time
   moment (onboarding) — the extra flourish here is earned in a way it
   wouldn't be on something seen every day. Purely decorative, so it's
   marked aria-hidden in the markup. */
.fl-bed{
  position:absolute;
  inset:-46px -30px -30px;
  --bed-inset-x:30px; /* how far left/right this bed extends past the card — side-anchored flowers in buildFirstLoginFlowerBedHtml() peg to this so they sit right at the card's real seam */
  pointer-events:none;
  z-index:1;
}
#firstLoginWelcomeContent{ position:relative; }
#firstLoginWelcomeContent > *:not(.fl-bed){ position:relative; z-index:2; }
.fl-flower{
  position:absolute;
  transform-origin:50% 100%;
  filter:drop-shadow(0 2px 3px rgba(43,46,38,0.14));
}
.fl-flower svg{ display:block; overflow:visible; }

.fl-stem{
  transform-origin:50% 100%;
  transform:scaleY(0);
  animation:flStemGrow 620ms var(--ease) both;
  animation-delay:var(--delay, 0ms);
}
.fl-leaf{
  transform-origin:50% 100%;
  transform:scale(0);
  animation:flLeafPop 480ms var(--ease) both;
  animation-delay:calc(var(--delay, 0ms) + 260ms);
}
.fl-bloom{
  transform-origin:center;
  transform:scale(.15) rotate(var(--tilt, 0deg));
  opacity:0;
  animation:flBloomOpen 640ms cubic-bezier(.31,1.42,.6,1) both;
  animation-delay:calc(var(--delay, 0ms) + 520ms);
}
.fl-bud-sway{
  transform-origin:50% 92%;
  animation:flSway 4.5s ease-in-out calc(var(--delay, 0ms) + 1100ms) infinite;
}
@keyframes flStemGrow{ from{ transform:scaleY(0); } to{ transform:scaleY(1); } }
@keyframes flLeafPop{
  from{ transform:scale(0) rotate(-8deg); opacity:0; }
  to{ transform:scale(1) rotate(0deg); opacity:1; }
}
@keyframes flBloomOpen{
  0%{ transform:scale(.15) rotate(var(--tilt, 0deg)); opacity:0; }
  55%{ opacity:1; }
  100%{ transform:scale(1) rotate(var(--tilt, 0deg)); opacity:1; }
}
@keyframes flSway{ 0%, 100%{ transform:rotate(0deg); } 50%{ transform:rotate(2.2deg); } }
@keyframes flReducedFade{ from{ opacity:0; } to{ opacity:1; } }

@media (max-width:640px){
  .fl-bed{ inset:-30px -14px -22px; --bed-inset-x:14px; }
}
#pinHint{
  background:var(--surface-moss);
  border:1px solid var(--line);
  border-radius:var(--radius-sm);
  padding:0.55rem 0.7rem;
}
.place-row{ display:flex; gap:0.5rem; }
.place-row input{ flex:1; min-width:0; }
.place-row .btn{ flex:none; padding:0.55rem 0.9rem; }
.locate-status{
  font-size:0.83rem;
  color:var(--moss-dark);
  margin:-0.5rem 0 0;
}
.locate-status::before{ content:'✓ '; }
.locate-status.is-error{ color:var(--amber-dark); }
.locate-status.is-error::before{ content:'⚠ '; }
.locate-status.is-pending{ color:var(--ink-dim); font-style:italic; }
.locate-status.is-pending::before{ content:'⏳ '; font-style:normal; }
.char-count{
  font-size:0.78rem;
  color:var(--ink-dim);
  text-align:right;
  margin:-0.5rem 0 0;
}
#addMemoryForm{ display:flex; flex-direction:column; gap:1rem; }
#addMemoryForm label{
  display:flex; flex-direction:column; gap:0.35rem;
  font-size:0.85rem; font-weight:600; color:var(--ink);
}
#addMemoryForm input, #addMemoryForm select, #addMemoryForm textarea{
  font-family:'Public Sans', sans-serif;
  font-size:0.92rem;
  padding:0.55rem 0.7rem;
  border-radius:var(--radius-sm);
  border:1px solid var(--line);
  background:var(--paper);
  color:var(--ink);
  font-weight:400;
  transition:border-color .15s var(--ease), box-shadow .15s var(--ease);
}
#addMemoryForm input:focus-visible, #addMemoryForm select:focus-visible, #addMemoryForm textarea:focus-visible{
  border-color:var(--moss); box-shadow:0 0 0 3px var(--surface-moss);
}
#addMemoryForm textarea{ resize:vertical; }
#pinStatus{ color:var(--moss-dark); }

/* "Who can see this memory?" - Everyone / Only me, top of Add a memory
   (2026-10-19). Beats "#addMemoryForm label" / "#addMemoryForm input". */
.privacy-choice{ border:0; margin:0; padding:0; min-width:0; }
.privacy-choice legend{ font-size:0.85rem; font-weight:600; color:var(--ink); padding:0; margin-bottom:0.4rem; }
.privacy-options{ display:grid; grid-template-columns:1fr 1fr; gap:0.5rem; }
#addMemoryForm label.privacy-option{
  flex-direction:row; align-items:flex-start; gap:0.5rem;
  padding:0.6rem 0.7rem; min-height:44px;
  border:1px solid var(--line); border-radius:var(--radius-sm);
  background:var(--paper); cursor:pointer;
  transition:border-color .15s var(--ease), background .15s var(--ease);
}
#addMemoryForm label.privacy-option input{
  width:18px; height:18px; flex:none; margin:0.1rem 0 0; padding:0;
  accent-color:var(--moss); cursor:pointer;
}
#addMemoryForm label.privacy-option:has(input:checked){ border-color:var(--moss); background:var(--surface-moss); box-shadow:inset 0 0 0 1px var(--moss); }
#addMemoryForm label.privacy-option:has(input:focus-visible){ box-shadow:0 0 0 3px var(--surface-moss), inset 0 0 0 1px var(--moss); }
.privacy-option-body{ display:flex; flex-direction:column; gap:0.15rem; min-width:0; }
.privacy-option-title{ display:inline-flex; align-items:center; gap:0.35rem; font-size:0.9rem; font-weight:600; color:var(--ink); }
.privacy-option-hint{ font-size:0.78rem; font-weight:400; color:var(--ink-dim); line-height:1.3; }
.privacy-note{ font-size:0.8rem; color:var(--ink-dim); margin:0.5rem 0 0; }

/* Private memories: a quiet "Only you can see this" note on the memory
   and a lock tag in lists; their map pins are slate instead of moss. */
.private-badge{
  display:inline-flex; align-items:center; gap:0.35rem;
  margin-top:0.6rem; padding:0.3rem 0.6rem;
  border-radius:999px; background:var(--paper); border:1px solid var(--line);
  font-size:0.8rem; color:var(--ink-dim);
}
.private-tag{ display:inline-flex; align-items:center; gap:0.2rem; color:var(--ink-dim); white-space:nowrap; }
.private-tag .icon{ width:12px; height:12px; }

/* every other add/create-style form (create-memorial, edit-profile)
   shares the same input treatment as #addMemoryForm above */
#createMemorialForm{ display:flex; flex-direction:column; gap:1rem; }
#createMemorialForm label{
  display:flex; flex-direction:column; gap:0.35rem;
  font-size:0.85rem; font-weight:600; color:var(--ink);
}
#createMemorialForm input, #createMemorialForm select{
  font-family:'Public Sans', sans-serif;
  font-size:0.92rem;
  padding:0.55rem 0.7rem;
  border-radius:var(--radius-sm);
  border:1px solid var(--line);
  background:var(--paper);
  color:var(--ink);
  font-weight:400;
  transition:border-color .15s var(--ease), box-shadow .15s var(--ease);
}
#createMemorialForm input:focus-visible, #createMemorialForm select:focus-visible{
  border-color:var(--moss); box-shadow:0 0 0 3px var(--surface-moss);
}

/* sign in / sign up */
.auth-area{ display:inline-flex; align-items:center; gap:0.6rem; margin-left:0.4rem; font-size:0.88rem; }
.auth-area .user-name{ font-weight:600; }
.btn-link{
  background:none; border:none; padding:0; margin:0;
  color:var(--moss-dark); font-weight:600; font-size:inherit;
  cursor:pointer; text-decoration:underline;
  transition:color .15s var(--ease);
}
@media (hover:hover){ .btn-link:hover{ color:var(--moss); } }
.auth-tabs{ display:flex; gap:0.4rem; margin-bottom:1rem; }
.auth-tabs .btn{ flex:1; }
.auth-tabs .btn.is-active{ background:var(--surface-moss); border-color:var(--moss); }
.auth-form{ display:flex; flex-direction:column; gap:1rem; }
.auth-form label{
  display:flex; flex-direction:column; gap:0.35rem;
  font-size:0.85rem; font-weight:600; color:var(--ink);
}
.auth-form input, .auth-form textarea{
  font-family:'Public Sans', sans-serif;
  font-size:0.92rem;
  padding:0.55rem 0.7rem;
  border-radius:var(--radius-sm);
  border:1px solid var(--line);
  background:var(--paper);
  color:var(--ink);
  font-weight:400;
  transition:border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.auth-form input:focus-visible, .auth-form textarea:focus-visible{
  border-color:var(--moss); box-shadow:0 0 0 3px var(--surface-moss);
}
.auth-form textarea{ resize:vertical; }
.auth-status{ font-size:0.85rem; margin:-0.5rem 0 0; color:var(--moss-dark); }
.auth-status::before{ content:'✓ '; }
.auth-status.is-error{ color:var(--amber-dark); }
.auth-status.is-error::before{ content:'⚠ '; }
.auth-status.is-pending{ color:var(--ink-dim); font-style:italic; }
.auth-status.is-pending::before{ content:'⏳ '; font-style:normal; }

/* social sign-in (Google/Apple) — see beginOAuthSignIn() in app.js */
.auth-oauth{ display:flex; flex-direction:column; gap:0.6rem; margin-bottom:1.1rem; }
.btn-oauth{ display:flex; align-items:center; justify-content:center; gap:0.6rem; width:100%; }
.btn-oauth .icon-brand{ width:18px; height:18px; flex:none; }
.auth-divider{ display:flex; align-items:center; gap:0.7rem; margin:0 0 1.1rem; font-size:0.78rem; color:var(--ink-dim); }
.auth-divider::before, .auth-divider::after{ content:''; flex:1; height:1px; background:var(--line); }

/* 18+ checkbox - the sign-up form and the one-time confirm step
   (openAgeConfirmModal() in app.js, 2026-10-19). Overrides the stacked
   column layout .auth-form label uses for text fields. */
.auth-form label.age-check{
  flex-direction:row; align-items:center; gap:0.6rem;
  font-size:0.92rem; min-height:44px; cursor:pointer;
}
.auth-form label.age-check input{
  width:22px; height:22px; flex:none; margin:0; padding:0;
  accent-color:var(--moss); cursor:pointer;
}
.age-check-note{ font-size:0.8rem; color:var(--ink-dim); margin:-0.6rem 0 0; }
.age-confirm-card{ max-width:440px; }
.age-under{ margin:0.9rem 0 0; font-size:0.88rem; }
.age-under .btn-link{ min-height:36px; }

/* "Email notifications" - openNotificationSettings() in app.js (2026-10-20) */
.notify-card{ max-width:480px; }
.notify-card h2{ padding-right:2.8rem; }
.notify-list{ list-style:none; margin:1rem 0 0; padding:0; display:flex; flex-direction:column; }
.notify-row{
  display:flex; align-items:flex-start; gap:0.75rem;
  padding:0.8rem 0; border-top:1px solid var(--line); cursor:pointer; min-height:44px;
}
.notify-row:first-child{ border-top:0; }
.notify-row input{ width:22px; height:22px; flex:none; margin:0.05rem 0 0; accent-color:var(--moss); cursor:pointer; }
.notify-row-text{ display:flex; flex-direction:column; gap:0.15rem; min-width:0; }
.notify-row-title{ font-size:0.95rem; font-weight:600; color:var(--ink); }
.notify-row-hint{ font-size:0.82rem; color:var(--ink-dim); line-height:1.35; }
.notify-footnote{ font-size:0.8rem; color:var(--ink-dim); margin:0.6rem 0 1rem; }
.notify-actions{ display:flex; gap:0.6rem; flex-wrap:wrap; }
.notify-actions .btn{ flex:1 1 140px; }

/* choose-a-real-username prompt, shown once right after a first-time
   Google/Apple sign-in — see chooseUsernameModal in app.html */
.choose-username-suggestion{ font-size:0.8rem; color:var(--ink-dim); margin:0.3rem 0 0; }

/* editing your own profile */
.avatar-edit-row{ display:flex; align-items:center; gap:0.9rem; margin-bottom:0.2rem; }
.avatar-edit-actions{ display:flex; flex-direction:column; align-items:flex-start; gap:0.35rem; }
img.avatar, img.profile-avatar{ object-fit:cover; }
.signed-in-note{
  background:var(--surface-moss);
  border:1px solid var(--line);
  border-radius:var(--radius-sm);
  padding:0.6rem 0.8rem;
  font-size:0.88rem;
}

@keyframes dropIn{
  from{ opacity:0; transform:translateY(-4px); }
  to{ opacity:1; transform:translateY(0); }
}
/* search results fade back up the way they dropped in (hideSearchResults() in app.js) */
.search-results.is-closing{ animation:dropOut .14s var(--ease) both; pointer-events:none; }
@keyframes dropOut{
  from{ opacity:1; transform:translateY(0); }
  to{ opacity:0; transform:translateY(-4px); }
}

/* ---- bottom tab bar: Home and Map are real tabs (see switchTab() in
   app.js); Add/Alerts/Profile just open an overlay on top of whichever
   tab is active, so they never carry the is-active state. ---- */
.bottom-tabs{
  flex:none;
  display:flex;
  align-items:stretch;
  background:var(--surface);
  border-top:1px solid var(--line);
  box-shadow:0 -1px 0 rgba(43,46,38,0.02);
  position:relative;
  z-index:1100;
  /* Clear the iPhone home indicator (app.html uses viewport-fit=cover,
     so the page runs under it). 0 on the website and on Android, so
     nothing changes there. The + action sheet already sits at
     64px + this same inset. */
  padding-bottom:env(safe-area-inset-bottom, 0px);
}
.tab-item{
  flex:1;
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  gap:0.2rem;
  position:relative;
  padding:0.5rem 0 0.55rem;
  background:none;
  border:none;
  color:var(--ink-dim);
  font-family:'Public Sans', sans-serif;
  font-size:0.68rem;
  font-weight:600;
  cursor:pointer;
  transition:color .15s var(--ease);
}
@media (hover:hover){ .tab-item:hover{ color:var(--ink); } }
.tab-item.is-active{ color:var(--moss-dark); }
.tab-icon{ font-size:1.25rem; line-height:1; }
.tab-count-badge{
  position:absolute;
  top:2px;
  left:50%;
  margin-left:0.4rem;
  box-shadow:0 0 0 2px var(--surface);
}

/* ---- the + tab's action sheet ---- */
.action-sheet{ position:fixed; inset:0; z-index:1400; }
.action-sheet-backdrop{ position:absolute; inset:0; background:rgba(43,46,38,0.44); animation:fadeIn .18s var(--ease) both; }
.action-sheet-card{
  position:absolute;
  left:50%;
  bottom:calc(64px + env(safe-area-inset-bottom, 0px));
  transform:translateX(-50%);
  width:min(360px, 92vw);
  background:var(--surface);
  border-radius:var(--radius-lg);
  box-shadow:var(--shadow-lift);
  padding:0.7rem 1.2rem 1.3rem;
  display:flex;
  flex-direction:column;
  gap:0.6rem;
  animation:sheetUp .2s var(--ease) both;
}
/* a grab handle, the same affordance a native iOS sheet gives you —
   drag it down to dismiss (see initActionSheetDrag() in app.js), with
   rubber-band resistance if you drag back up past rest and a
   velocity-aware decision on release, not just a fixed distance. */
.action-sheet-handle{
  width:36px; height:5px;
  border-radius:99px;
  background:var(--line);
  margin:0 auto 0.5rem;
  cursor:grab;
  touch-action:none;
  flex:none;
}
.action-sheet-handle:active{ cursor:grabbing; }
.action-sheet-card .eyebrow{ text-align:center; margin-bottom:0.2rem; }
.action-sheet-cancel{ margin-top:0.2rem; text-align:center; }
@keyframes fadeIn{ from{ opacity:0; } to{ opacity:1; } }
@keyframes fadeOut{ from{ opacity:1; } to{ opacity:0; } }
@keyframes sheetUp{ from{ opacity:0; transform:translate(-50%, 12px); } to{ opacity:1; transform:translate(-50%, 0); } }
/* matching close for a tap-outside/Cancel dismissal; a drag-dismissal
   instead carries the sheet onward with inline styles from wherever
   the finger let go (see closeActionSheet(fromDrag) in app.js) rather
   than replaying this from the top. */
.action-sheet-backdrop.is-closing{ animation:fadeOut .16s var(--ease) both; }
.action-sheet-card.is-closing{ animation:sheetDown .18s var(--ease) both; }
@keyframes sheetDown{
  from{ opacity:1; transform:translate(-50%, 0); }
  to{ opacity:0; transform:translate(-50%, 12px); }
}

/* ---- Alerts panel — a simple list, each row either a "new memory"
   notice or the same activity (comment) format used in the Home
   feed. ---- */
.alert-list{ list-style:none; margin:1rem 0 0; padding:0; display:flex; flex-direction:column; gap:0.7rem; }
.alert-item{
  display:flex; align-items:flex-start; gap:0.65rem;
  padding:0.8rem 0.9rem;
  border:1px solid var(--line);
  border-radius:var(--radius-md);
  background:var(--surface);
  cursor:pointer;
  transition:border-color .15s var(--ease), box-shadow .15s var(--ease);
}
@media (hover:hover){ .alert-item:hover{ border-color:var(--moss); box-shadow:var(--shadow); } }
.alert-item .who{ flex:1; min-width:0; }
.alert-item .alert-text{ font-size:0.9rem; line-height:1.45; }
.alert-item .alert-text a{ color:var(--moss-dark); font-weight:600; text-decoration:none; border-bottom:1px solid var(--moss); }
.alert-item .post-sub{ font-size:0.78rem; color:var(--ink-dim); margin-top:0.2rem; }
.alert-item .activity-quote{ margin:0.5rem 0 0; }

@media (max-width:900px){
  .action-group + .action-group{ border-left:none; padding-left:0; }

  /* moderator tools now live inside the profile panel (see
     .rail-mod-tools below the panel styles) instead of a sidebar, so
     there's nothing left here to collapse into a strip — the map
     toolbar's own buttons already wrap via flex-wrap at any width. */
}

@media (max-width:640px){
  .site-header{ gap:0.8rem; }
  .tagline{ display:none; }
  /* Header's too tight for the full logo here — swap to the compact
     FT monogram + the "ForeverTrail" text name instead. */
  .brand-logo-full{ display:none; }
  .brand-mark-ft{ display:flex; }
  .brand-name{ display:inline; }
  /* "My profile" collapses to a round icon-only button here, same
     treatment as the search icon right next to it - it's pinned to
     the header's top-right corner at every width now (see
     .header-actions above), and there isn't room next to the search
     icon for its full text label on a phone-width header. */
  #myProfileBtn{
    width:36px; height:36px; padding:0; border-radius:50%;
    display:flex; align-items:center; justify-content:center;
  }
  #myProfileBtn .btn-label{ display:none; }
  .panel{ width:100%; }

  .composer{ padding:0.65rem 0.85rem; }
  .feed-list{ padding:0.9rem 0.7rem 2rem; }

  .action-sheet-card{ width:calc(100vw - 1.6rem); }

  /* Same header-clearance reasoning as the base rule above - phones
     are exactly where this was cropping the card's top edge worst,
     since the fixed 1.6rem here used to be smaller than the header
     itself. */
  .panel-hub{ padding:calc(var(--header-h) + 0.9rem) 0.7rem calc(var(--tabs-h) + 2rem); }
  .hub-card{ padding:1.6rem 1.2rem 2rem; }
  .photo-banner{ margin:-1.6rem -1.2rem 1.3rem; height:220px; }
  .banner-content{ padding:1.1rem 1.2rem 1rem; }
  .banner-content h3{ font-size:1.6rem; }
  .hub-stats-grid{ gap:0.7rem; margin:1.2rem 0 1.8rem; }
  .stat-tile{ flex:1 1 130px; padding:0.85rem 0.95rem 0.95rem; }
  .stat-figure{ font-size:1.65rem; }
  /* Same specificity (two class selectors) as the .hub-map-col
     overrides above, but later in the cascade, so this wins for the
     memorial page's paired column too once it's stacked full-width —
     mirrors how the old flat-map mobile override worked. */
  .hub-map-col .globe-canvas, .hub-map-panel .globe-canvas{ width:min(240px, 78%); }
  /* stretch, not the base row's flex-start: stacked in a column,
     flex-start shrank the globe box and the followed-memorials list to
     their content width, so they didn't line up with the tiles above. */
  .hub-map-places-row{ flex-direction:column; align-items:stretch; }

  #memoryPanel.panel-post{ padding:calc(var(--header-h) + 0.9rem) 0.6rem calc(var(--tabs-h) + 2rem); }

  /* ---- phones: profile, memorial, list and memory pages are full
     screens, not a card floating on the map (2026-10-10, impeccable
     audit #4). The floating card left a strip of map down both sides
     and made every tile a card inside a card. Here the page fills the
     width under the header, its own background reaching the tab bar;
     tiles go flat (1px line, no shadow) so they read as parts of the
     page. Desktop keeps the floating card. */
  .panel-hub, #memoryPanel.panel-post{
    padding:var(--header-h) 0 0;
    background:var(--surface);
  }
  .panel-hub .hub-card, #memoryPanel.panel-post .post-card{
    width:100%;
    min-height:100%;
    border-radius:0;
    box-shadow:none;
  }
  .panel-hub .hub-card{ padding:1.6rem 1.2rem calc(var(--tabs-h) + 2rem); }
  #memoryPanel.panel-post .post-card{ padding-bottom:calc(var(--tabs-h) + 1rem); border:none; }
  .stat-tile{ box-shadow:none; }
  .photo-banner{ border-radius:0; }
  .post-header{ padding:1.1rem 2.6rem 0.8rem 1.1rem; }
  .post-body{ padding:0 1.1rem 0.8rem; }
  .post-summary{ padding:0.55rem 1.1rem; }
  .post-comments{ padding:0.9rem 1.1rem 1.1rem; }
}

/* ---- offline memory queue -------------------------------------------
   A memory created with no connection saves to the device first, shows
   up right away marked pending, and uploads for real (photos and all)
   the next time the app is open with a connection — see "Offline
   memory creation" in spec.md, and the matching section of app.js
   (updatePendingSyncBar(), syncPendingMemories(), openMemoryPanel()'s
   pending branch). Same amber "heads up, not an error" language the
   rest of the app already uses for a quiet status note (.visit-badge,
   .origin-badge, .locate-status) — a pending memory is normal, not a
   problem. */

.offline-queue-bar{
  display:flex; align-items:center; justify-content:center; gap:0.9rem;
  background:var(--amber-soft);
  border-bottom:1px solid var(--amber);
  color:var(--amber-dark);
  padding:0.6rem 1.4rem;
  font-size:0.85rem;
  text-align:center;
}
.offline-queue-bar strong{ color:var(--ink); }

/* Bars that sit above the header (load errors, "You're offline", the
   upload queue) - in the apps the page draws under the status bar, so
   whichever bar is first takes the status-bar inset that the header
   normally adds, and the header (and any later bar) drop it. Found
   2026-10-11: the offline bar's first line sat under the clock. */
.load-error:not([hidden]){ padding-top:calc(0.8rem + env(safe-area-inset-top, 0px)); }
.offline-queue-bar:not([hidden]){ padding-top:calc(0.6rem + env(safe-area-inset-top, 0px)); }
:is(.load-error, .offline-queue-bar):not([hidden]) ~ .load-error:not([hidden]){ padding-top:0.8rem; }
:is(.load-error, .offline-queue-bar):not([hidden]) ~ .offline-queue-bar:not([hidden]){ padding-top:0.6rem; }
:is(.load-error, .offline-queue-bar):not([hidden]) ~ .site-header{ padding-top:0.85rem; }

/* the pill on a feed/list card — same shape as .origin-badge's
   following/nearby pills, since it's really the same kind of thing:
   a quiet note on *why* this card looks the way it does. */
.origin-badge.pending{ background:var(--amber-soft); color:var(--amber-dark); border:1px solid var(--amber); }

/* the fuller banner inside a pending memory's own detail view */
.pending-banner{
  display:flex; align-items:center; justify-content:space-between; gap:0.8rem;
  font-size:0.82rem; color:var(--amber-dark);
  background:var(--amber-soft);
  border:1px solid var(--amber);
  border-radius:var(--radius-md);
  padding:0.6rem 0.95rem;
  margin:0 1.3rem 0.9rem;
}
.pending-banner .btn-link{ white-space:nowrap; flex:none; }

/* ---- site footer (index.html, app.html, and the legal pages) ---- */
.site-footer{
  flex:none;
  border-top:1px solid var(--line);
  background:var(--surface);
  padding:1.3rem 1.5rem;
  display:flex;
  flex-wrap:wrap;
  gap:0.4rem 1.3rem;
  align-items:center;
  justify-content:center;
  font-size:0.82rem;
  color:var(--ink-dim);
  text-align:center;
}
.site-footer a{ color:var(--ink-dim); text-decoration:none; }
@media (hover:hover){ .site-footer a:hover{ color:var(--ink); text-decoration:underline; } }
.site-footer .footer-sep{ opacity:0.45; }
/* phones: the links wrap onto centered lines, so the dots would end up
   dangling at line ends; the gap does the separating instead */
@media (max-width:640px){
  .site-footer .footer-sep{ display:none; }
  .site-footer{ gap:0.45rem 1.1rem; }
}

/* ---- full-screen photo viewer (see "photo viewer" in app.js) ---- */
.photo-viewer{
  position:fixed;
  inset:0;
  z-index:1500; /* above every panel, overlay and the + action sheet */
  background:rgba(20,22,18,0.94);
  display:flex;
  align-items:center;
  justify-content:center;
  animation:fadeIn .18s var(--ease) both;
  touch-action:none;
}
.photo-viewer[hidden]{ display:none; }
.photo-viewer-stage{
  position:absolute;
  inset:3.5rem 0 3rem;
  display:flex;
  align-items:center;
  justify-content:center;
}
.photo-viewer-img{
  max-width:100%;
  max-height:100%;
  object-fit:contain;
  user-select:none;
  -webkit-user-drag:none;
  /* Moved by JS (springTo) frame by frame - a CSS transition here would
     lag behind the finger. */
  transform-origin:50% 50%;
  will-change:transform;
}
/* the neighbouring photo that slides in alongside while paging */
.photo-viewer-peek{ position:absolute; pointer-events:none; }
.photo-viewer.is-closing{ animation:fadeOut .16s var(--ease) both; }
/* close button / "2 of 5" / arrows fade as the photo is pulled down */
.photo-viewer-close, .photo-viewer-count, .photo-viewer-nav{ opacity:var(--pv-chrome, 1); }
.photo-viewer-close{
  position:absolute;
  top:max(0.8rem, env(safe-area-inset-top, 0px));
  right:0.9rem;
  z-index:2;
  width:44px; height:44px;
  border:none;
  border-radius:50%;
  background:rgba(255,255,255,0.14);
  color:#fff;
  font-size:1.6rem;
  line-height:1;
  cursor:pointer;
}
.photo-viewer-count{
  position:absolute;
  top:max(1.35rem, calc(env(safe-area-inset-top, 0px) + 0.55rem));
  left:50%;
  transform:translateX(-50%);
  z-index:2;
  color:rgba(255,255,255,0.85);
  font-family:'IBM Plex Mono', monospace;
  font-size:0.8rem;
  letter-spacing:0.04em;
}
.photo-viewer-nav{
  position:absolute;
  top:50%;
  transform:translateY(-50%);
  z-index:2;
  width:44px; height:64px;
  border:none;
  border-radius:var(--radius-md);
  background:rgba(255,255,255,0.12);
  color:#fff;
  font-size:2rem;
  line-height:1;
  cursor:pointer;
}
.photo-viewer-nav.prev{ left:0.6rem; }
.photo-viewer-nav.next{ right:0.6rem; }
@media (hover:hover){ .photo-viewer-close:hover, .photo-viewer-nav:hover{ background:rgba(255,255,255,0.22); } }
/* On touch screens swiping does this job - keep the photo unobstructed. */
@media (hover:none) and (pointer:coarse){
  .photo-viewer-nav{ display:none; }
}
@media (prefers-reduced-motion:reduce){
  .photo-viewer, .photo-viewer-img{ animation:none !important; transition:none !important; }
}
/* "1 / 6" chip on a Home-feed photo that has more behind it */
.post-media{ position:relative; }
.photo-count-chip{
  position:absolute;
  right:0.7rem;
  bottom:0.7rem;
  padding:0.2rem 0.55rem;
  border-radius:999px;
  background:rgba(20,22,18,0.62);
  color:#fff;
  font-size:0.75rem;
  font-weight:600;
  pointer-events:none;
}
.post-media [data-photo-index]{ cursor:zoom-in; }

/* ---- swipeable photo strips (Home feed cards, memorial banner) ----
   See "swipeable photo strips" in app.js. The phone's own scrolling does
   the swipe; scroll-snap lands each photo square in the frame. */
.photo-strip{
  display:flex;
  overflow-x:auto;
  overflow-y:hidden;
  scroll-snap-type:x mandatory;
  overscroll-behavior-x:contain;
  -webkit-overflow-scrolling:touch;
  scrollbar-width:none;
}
.photo-strip::-webkit-scrollbar{ display:none; }
.photo-strip-slide{
  flex:0 0 100%;
  scroll-snap-align:start;
  scroll-snap-stop:always;
  display:block;
}
.photo-strip-slide img{ width:100%; display:block; object-fit:cover; -webkit-user-drag:none; user-select:none; }
.post-media .photo-strip-slide img{ aspect-ratio:4/3; }
.photo-strip-arrow{
  display:none;
  position:absolute; top:50%; z-index:4;
  width:34px; height:34px; margin-top:-17px;
  align-items:center; justify-content:center;
  border:none; border-radius:50%;
  background:rgba(20,22,18,0.55);
  color:#fff;
  cursor:pointer;
}
.photo-strip-arrow svg{ width:18px; height:18px; }
.photo-strip-arrow.is-prev{ left:0.6rem; }
.photo-strip-arrow.is-next{ right:0.6rem; }
.photo-banner .photo-strip-arrow{ top:42%; }
@media (hover:hover) and (pointer:fine){
  .post-media:hover .photo-strip-arrow,
  .photo-banner:hover .photo-strip-arrow{ display:flex; }
  .photo-strip-arrow:hover{ background:rgba(20,22,18,0.78); }
}

/* ---- Add a memory: "Take a photo" / "Choose from photos" ---- */
.photo-field{ display:flex; flex-direction:column; gap:0.45rem; }
.photo-field-label{ font-size:0.85rem; font-weight:600; color:var(--ink); }
.photo-source-row{ display:flex; gap:0.5rem; flex-wrap:wrap; }
/* Beats "#addMemoryForm label" (a stacked, bold form-field label). */
#addMemoryForm label.photo-source-btn{
  flex:1 1 140px;
  flex-direction:row;
  align-items:center;
  justify-content:center;
  gap:0.4rem;
  font-size:0.88rem;
  cursor:pointer;
}
/* "Also save photos I take to my phone's photos" - only shown inside the
   Android/iPhone app (app.js unhides it). Beats "#addMemoryForm label"
   and "#addMemoryForm input" (stacked bold field label, padded text box). */
#addMemoryForm label.save-to-photos-toggle{
  flex-direction:row;
  align-items:center;
  gap:0.5rem;
  font-size:0.82rem;
  font-weight:400;
  color:var(--ink);
  cursor:pointer;
}
#addMemoryForm label.save-to-photos-toggle[hidden]{ display:none; }
/* Website only: "Save a copy of the photo you just took" (2026-10-10). */
.web-save-photo-row{ margin-top:0.5rem; }
.web-save-photo-row[hidden]{ display:none; }
.web-save-photo-row .btn-link{ display:inline-flex; align-items:center; gap:0.35rem; min-height:36px; font-size:0.88rem; text-decoration:none; }
@media (hover:hover){ .web-save-photo-row .btn-link:hover{ text-decoration:underline; } }
#addMemoryForm .save-to-photos-toggle input{
  accent-color:var(--moss);
  width:18px; height:18px;
  margin:0; padding:0;
  flex:none;
}
/* The real file inputs stay in the page (so the labels above can open
   them) but out of sight. */
.photo-input-hidden{
  position:absolute;
  width:1px; height:1px;
  opacity:0;
  overflow:hidden;
  pointer-events:none;
}
/* A mouse-and-keyboard computer usually has no camera to open this way
   (desktop browsers ignore capture= and show a file picker anyway), so
   only offer "Take a photo" on touch devices. */
@media (hover:hover) and (pointer:fine){
  #addMemoryForm label.photo-source-camera{ display:none; }
}

/* ---- Add a memory: "Adjust pin on map" ---- */
#addMemoryForm .adjust-pin-btn{ align-self:stretch; justify-content:center; gap:0.4rem; }
.pin-adjust-bar{
  position:fixed;
  left:0; right:0; bottom:0;
  z-index:1150; /* over the bottom tab bar (1100), under full-screen overlays (1200) */
  background:var(--surface);
  border-top:1px solid var(--line);
  border-radius:var(--radius-lg) var(--radius-lg) 0 0;
  box-shadow:0 -8px 24px -12px rgba(43,46,38,0.35);
  padding:0.9rem 1rem calc(0.9rem + env(safe-area-inset-bottom, 0px));
  display:flex;
  flex-direction:column;
  gap:0.7rem;
  animation:pinBarUp .22s var(--ease) both;
}
@keyframes pinBarUp{ from{ opacity:0; transform:translateY(16px); } to{ opacity:1; transform:translateY(0); } }
.pin-adjust-bar[hidden]{ display:none; }
.pin-adjust-hint{ margin:0; font-size:0.9rem; color:var(--ink); text-align:center; }
.pin-adjust-actions{ display:flex; gap:0.6rem; }
.pin-adjust-actions .btn{ flex:1; justify-content:center; }
.pin-adjust-actions .btn-primary{ flex:2; }
.pin-adjust-actions .btn:disabled{ opacity:0.5; cursor:not-allowed; }
@media (min-width:641px){
  .pin-adjust-bar{ left:50%; right:auto; width:min(460px, 100vw); transform:translateX(-50%); bottom:1rem; border-radius:var(--radius-lg); border:1px solid var(--line); animation:none; }
}
@media (prefers-reduced-motion:reduce){
  .pin-adjust-bar{ animation:none !important; }
}
/* While placing a pin, other memories' pins and clusters aren't tappable -
   a stray tap on one would open that memory and lose the form. The amber
   pending pin (.ft-pending-marker) stays draggable. */
body.pin-adjusting .leaflet-marker-icon:not(:has(.ft-pending-marker)){ pointer-events:none; }

/* ---- My posts (2026-10-09) — your own memories and comments, each with
   Edit and Delete. Same card language as the Alerts list. Also used,
   compact, for the "Your posts" section on your own profile. ---- */
.mypost-tabs{ margin:1rem 0 0; }
.mypost-list{ list-style:none; margin:1rem 0 0; padding:0; display:flex; flex-direction:column; gap:0.7rem; }
.mypost-list-compact{ margin-top:0; gap:0.5rem; }
.mypost-item{
  display:flex; align-items:flex-start; gap:0.75rem;
  padding:0.75rem 0.85rem;
  border:1px solid var(--line);
  border-radius:var(--radius-md);
  background:var(--surface);
  cursor:pointer;
  transition:border-color .15s var(--ease), box-shadow .15s var(--ease);
}
@media (hover:hover){ .mypost-item:hover{ border-color:var(--moss); box-shadow:var(--shadow); } }
.mypost-thumb{
  flex:none; width:56px; height:56px;
  border-radius:var(--radius-sm);
  background:var(--surface-moss);
  overflow:hidden;
  display:flex; align-items:center; justify-content:center;
  color:var(--moss);
}
.mypost-item.is-compact .mypost-thumb{ width:44px; height:44px; }
.mypost-thumb img{ width:100%; height:100%; object-fit:cover; display:block; }
.mypost-main{ flex:1; min-width:0; }
.mypost-title{
  font-weight:600; font-size:0.93rem; line-height:1.35; color:var(--ink);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.mypost-main .post-sub{ font-size:0.78rem; color:var(--ink-dim); margin-top:0.15rem; }
.mypost-quote{ margin:0 0 0.35rem; }
.mypost-actions{ display:flex; flex-wrap:wrap; gap:1.1rem; margin-top:0.45rem; font-size:0.82rem; }
.mypost-actions .btn-link{ text-decoration:none; display:inline-flex; align-items:center; gap:0.3rem; min-height:28px; }
@media (hover:hover){ .mypost-actions .btn-link:hover{ text-decoration:underline; } }
.mypost-actions .mypost-delete{ color:var(--amber-dark); }
.mypost-pending{ color:var(--ink-dim); font-style:italic; }
.mypost-see-all{ margin-top:0.8rem; }
.stat-tile-link{ cursor:pointer; transition:border-color .15s var(--ease), box-shadow .15s var(--ease); }
@media (hover:hover){ .stat-tile-link:hover{ border-color:var(--moss); box-shadow:var(--shadow-lift); } }
.stat-tile-link:focus-visible{ outline:2px solid var(--moss); outline-offset:2px; }

/* ---- Profile tile lists (2026-10-10) - what each profile stat tile
   counts: memories, trips (miles by memorial), countries, comments. ---- */
.profile-list-back{ display:inline-flex; align-items:center; min-height:32px; margin-bottom:0.4rem; text-decoration:none; font-size:0.88rem; }
@media (hover:hover){ .profile-list-back:hover{ text-decoration:underline; } }
.profile-list-total{ margin:0.6rem 0 0; color:var(--ink); line-height:1.5; }
.profile-list-note{ margin-top:0.9rem; }
.trip-group{ margin-top:1.3rem; }
.trip-group-head{
  display:flex; align-items:flex-end; justify-content:space-between; gap:0.8rem;
  padding-bottom:0.45rem; border-bottom:1px solid var(--line);
}
.trip-group-name{ font-weight:600; color:var(--ink); text-decoration:none; }
@media (hover:hover){ a.trip-group-name:hover{ color:var(--moss-dark); text-decoration:underline; } }
.trip-group-head .post-sub{ font-size:0.78rem; color:var(--ink-dim); margin-top:0.1rem; }
.trip-list{ margin-top:0.6rem; gap:0.5rem; }
.trip-row{ align-items:center; }
.trip-miles{ flex:none; font-weight:600; font-variant-numeric:tabular-nums; color:var(--moss-dark); white-space:nowrap; }
.trip-miles-total{ font-size:1.05rem; }
.state-group{ margin-top:0.8rem; padding-left:0.75rem; border-left:2px solid var(--line); }
.state-group-head{ display:flex; align-items:baseline; justify-content:space-between; gap:0.8rem; }
.state-name{ font-weight:600; font-size:0.9rem; color:var(--ink); }
.state-group .trip-list{ margin-top:0.45rem; }
.mypost-see-all-solo{ margin-top:1.6rem; }
.follower-list{ list-style:none; margin:1rem 0 0; padding:0; }
.follower-row{ display:flex; align-items:center; justify-content:space-between; gap:0.8rem; padding:0.6rem 0; border-bottom:1px solid var(--line); }
.follower-row:last-child{ border-bottom:none; }
.follower-link{ display:flex; align-items:center; gap:0.6rem; min-width:0; color:var(--ink); text-decoration:none; font-weight:600; }
@media (hover:hover){ .follower-link:hover .follower-name{ text-decoration:underline; } }
.follower-name{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.follower-you{ flex:none; font-weight:400; color:var(--ink-dim); margin-left:-0.3rem; }
.follower-row .post-sub{ flex:none; font-size:0.78rem; color:var(--ink-dim); }

/* "Memorials near me" distance picker (2026-10-10). 16px input so iPhone
   doesn't zoom on focus. */
.nearby-radius-form{ display:flex; align-items:center; flex-wrap:wrap; gap:0.5rem; margin-top:0.8rem; }
.nearby-radius-form label, .nearby-radius-form span{ font-size:0.92rem; color:var(--ink); }
.nearby-radius-form input{
  width:5.5rem; min-height:40px; padding:0.35rem 0.6rem; font:inherit; font-size:16px;
  border:1px solid var(--line); border-radius:var(--radius-sm); background:var(--surface); color:var(--ink);
}
.nearby-radius-form input:focus-visible{ outline:2px solid var(--moss); outline-offset:1px; }
.nearby-radius-form .btn{ min-height:40px; }
.nearby-radius-hint{ margin-top:0.35rem; }
.nearby-radius-hint.is-error{ color:var(--amber-dark); }

/* "· edited" after a date, and a comment being edited in place. */
.edited-note{ color:var(--ink-dim); font-style:italic; white-space:nowrap; }
.comment-edit-form{ margin-top:0.4rem; }
.comment-edit-form .comment-form-row{ justify-content:flex-end; }
.comment-item.is-highlighted .comment-bubble{ box-shadow:0 0 0 2px var(--amber-soft); transition:box-shadow .4s var(--ease); }

/* ---- press feedback (2026-10-10) ----
   Apple's fluid-interfaces rule: respond the instant a finger touches,
   not when the next screen appears. Hover can't do this on a phone (and
   is now limited to @media (hover:hover), see the top of this file), so
   everything tappable gets an :active state. The press shows at once
   (no transition in) and eases back out when released. iOS's own grey
   tap flash is turned off on these so the two don't double up. Rows that
   contain their own buttons (Edit / Delete, Like…) don't light up as a
   whole when one of those buttons is the thing being pressed. */
.stat-tile-link, .tab-item, .search-results button, .share-menu button,
.mypost-item, .hub-memory-list li, .alert-item, .post-card[data-view-memory],
.review-item, .directory-item, .follower-link, .icon-btn, .panel-close,
.post-menu-btn, .post-action-btn, .composer-input, .composer .avatar,
.search-clear, .photo-viewer-close, .profile-list-back, .btn, .btn-link,
.map-chip, .ft-map-ctl, a.brand-home-link{
  -webkit-tap-highlight-color:transparent;
}
.stat-tile-link{ transition:border-color .15s var(--ease), box-shadow .15s var(--ease), transform .18s var(--ease), background-color .18s var(--ease); }
.stat-tile-link:active{
  transform:scale(0.97);
  border-color:var(--moss);
  background-color:var(--surface-moss);
  transition-duration:0s;
}
.tab-item .tab-icon{ display:inline-block; transition:transform .18s var(--ease); }
.tab-item:active{ color:var(--ink); }
.tab-item:active .tab-icon{ transform:scale(0.86); transition-duration:0s; }
.mypost-item, .hub-memory-list li, .alert-item, .post-card[data-view-memory],
.review-item, .directory-item{
  transition:background-color .2s var(--ease), border-color .2s var(--ease), box-shadow .2s var(--ease), transform .2s var(--ease);
}
.mypost-item:active:not(:has(button:active, a:active)),
.hub-memory-list li:active:not(:has(button:active, a:active)),
.alert-item:active:not(:has(button:active, a:active)),
.post-card[data-view-memory]:active:not(:has(button:active, a:active)),
.review-item:active:not(:has(button:active, a:active)),
.directory-item:active:not(:has(button:active, a:active)){
  background-color:var(--surface-moss);
  border-color:var(--moss);
  transform:scale(0.99);
  transition-duration:0s;
}
.search-results button:active, .share-menu button:active,
.post-menu-btn:active, .post-action-btn:active:not(:disabled),
.search-clear:active, .composer-input:active{
  background:var(--surface-moss);
  transition-duration:0s;
}
.icon-btn:active, .panel-close:active{
  background:var(--surface-moss);
  border-color:var(--moss);
  transform:scale(0.92);
  transition-duration:0s;
}
#personPanel .panel-close:active{ background:rgba(43,46,38,0.55); border-color:#fff; }
.photo-viewer-close:active{ background:rgba(255,255,255,0.28); transform:scale(0.92); }
.follower-link:active .follower-name, .profile-list-back:active, .btn-link:active{ opacity:0.6; }
.composer .avatar:active, a.brand-home-link:active{ opacity:0.7; }
.btn:active{ background-color:var(--surface-moss); transition-duration:0s; }
.btn-primary:active{ background:var(--moss-dark); border-color:var(--moss-dark); }
@media (prefers-reduced-motion:reduce){
  .stat-tile-link:active, .tab-item:active .tab-icon, .icon-btn:active, .panel-close:active,
  .photo-viewer-close:active, .mypost-item:active, .hub-memory-list li:active, .alert-item:active,
  .post-card[data-view-memory]:active, .review-item:active, .directory-item:active{ transform:none; }
}

/* ---- in-app dialogs (2026-10-10, impeccable audit #1) ----
   ftConfirm() / ftPrompt() / ftNotice() in app.js - replace the
   browser's own alert/confirm/prompt boxes. Above everything (the
   photo viewer is 1500). Buttons name their action; a destructive one
   is red. */
.ft-dialog{ position:fixed; inset:0; z-index:2000; display:flex; align-items:center; justify-content:center; padding:1.2rem; }
.ft-dialog-backdrop{ position:absolute; inset:0; background:rgba(43,46,38,0.5); animation:fadeIn .16s var(--ease) both; }
.ft-dialog-card{
  position:relative;
  width:min(400px, 100%);
  background:var(--surface);
  border-radius:var(--radius-lg);
  box-shadow:0 18px 48px -16px rgba(43,46,38,0.45), 0 2px 6px rgba(43,46,38,0.12);
  padding:1.4rem 1.4rem 1.2rem;
  animation:ftDialogIn .2s var(--ease) both;
}
.ft-dialog-title{ font-size:1.2rem; line-height:1.3; color:var(--ink); margin:0 0 0.5rem; text-wrap:balance; }
.ft-dialog-message{ margin:0; color:var(--ink-dim); font-size:0.95rem; line-height:1.5; }
.ft-dialog-title[hidden] + .ft-dialog-message{ color:var(--ink); }
.ft-dialog-field{ display:flex; flex-direction:column; gap:0.4rem; margin-top:1rem; font-size:0.85rem; font-weight:600; color:var(--ink); }
/* optional tick box in a dialog (e.g. "It shows a private home" when reporting) */
.ft-dialog-check:not([hidden]){ display:flex; align-items:center; gap:0.55rem; margin-top:0.85rem; font-size:0.9rem; color:var(--ink); cursor:pointer; }
.ft-dialog-check input{ width:1.15rem; height:1.15rem; accent-color:var(--moss); margin:0; flex:none; }
.ft-dialog-input{
  font:inherit; font-weight:400; font-size:16px; /* 16px: no iPhone zoom */
  padding:0.6rem 0.75rem;
  border:1px solid var(--line); border-radius:var(--radius-sm);
  background:var(--surface); color:var(--ink);
}
.ft-dialog-input:focus{ outline:none; border-color:var(--moss); box-shadow:0 0 0 3px var(--surface-moss); }
.ft-dialog-actions{ display:flex; justify-content:flex-end; gap:0.6rem; margin-top:1.3rem; flex-wrap:wrap; }
.ft-dialog-actions .btn{ min-width:6.5rem; justify-content:center; }
.btn.btn-danger{ background:#9a3b2a; border-color:#9a3b2a; color:#fff; }
.btn.btn-danger:active{ background:#7e2f21; border-color:#7e2f21; }
@media (hover:hover){ .btn.btn-danger:hover{ background:#7e2f21; border-color:#7e2f21; box-shadow:0 6px 16px -8px rgba(126,47,33,0.55); } }
.btn.btn-danger:disabled{ background:#c9a39b; border-color:#c9a39b; opacity:1; }
.ft-dialog.is-closing .ft-dialog-backdrop{ animation:fadeOut .15s var(--ease) both; }
.ft-dialog.is-closing .ft-dialog-card{ animation:ftDialogOut .15s var(--ease) both; }
@keyframes ftDialogIn{ from{ opacity:0; transform:translateY(8px) scale(.97); } to{ opacity:1; transform:none; } }
@keyframes ftDialogOut{ from{ opacity:1; transform:none; } to{ opacity:0; transform:translateY(4px) scale(.98); } }
@media (max-width:640px){
  /* phones: sits at the bottom like an iPhone action sheet, with
     full-width buttons and Cancel last (nearest the thumb, the safe
     choice), the action above it */
  .ft-dialog{ align-items:flex-end; padding:0 0.6rem calc(0.6rem + env(safe-area-inset-bottom, 0px)); }
  .ft-dialog-card{ width:100%; }
  .ft-dialog-actions{ flex-direction:column-reverse; }
  .ft-dialog-actions .btn{ width:100%; padding:0.75rem 1rem; font-size:0.95rem; }
}
@media (prefers-reduced-motion:reduce){
  .ft-dialog-backdrop, .ft-dialog-card{ animation:none !important; }
}

/* ---- Home feed loading placeholders (2026-10-10, impeccable audit #3) ----
   In app.html's #homeFeedContent until the memories arrive; the same
   shape as a feed card, in the page's own tints, breathing gently. */
.visually-hidden{
  position:absolute !important; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}
.feed-skeleton{
  display:grid;
  grid-template-columns:44px 1fr;
  column-gap:0.8rem;
  row-gap:0.5rem;
  align-items:center;
  background:var(--surface);
  border-radius:var(--radius-lg);
  box-shadow:var(--shadow);
  padding:1.1rem 1.2rem 1.2rem;
  animation:skeletonBreathe 1.6s ease-in-out infinite;
}
.feed-skeleton:nth-child(3){ animation-delay:.2s; }
.feed-skeleton:nth-child(4){ animation-delay:.4s; }
.sk-avatar{ grid-row:span 2; width:44px; height:44px; border-radius:50%; background:var(--surface-moss); }
.sk-line{ display:block; height:0.7rem; border-radius:99px; background:var(--surface-moss); }
.sk-w60{ width:60%; }
.sk-w40{ width:40%; }
.sk-w85{ width:85%; grid-column:1 / -1; }
.sk-gap{ margin-top:0.6rem; }
.sk-block{ grid-column:1 / -1; height:150px; margin:0.6rem -1.2rem -1.2rem; background:var(--surface-moss); border-radius:0 0 var(--radius-lg) var(--radius-lg); }
@keyframes skeletonBreathe{ 0%,100%{ opacity:1; } 50%{ opacity:0.55; } }
@media (prefers-reduced-motion:reduce){ .feed-skeleton{ animation:none; } }

/* ---- small fixes (2026-10-10, impeccable audit #6) ---- */
/* signed-out composer avatar: a person icon in a quiet tint */
.avatar.avatar-signed-out{ background:var(--surface-moss); color:var(--moss-dark); box-shadow:inset 0 0 0 1px var(--line); }
.avatar.avatar-signed-out .icon{ width:55%; height:55%; }
/* a small, quiet text button - reads as an action, not a link
   ("Unfollow" on your profile's followed memorials) */
.btn-small{
  font:inherit; font-size:0.78rem; font-weight:600;
  color:var(--ink-dim);
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:99px;
  padding:0.3rem 0.75rem;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  transition:color .15s var(--ease), border-color .15s var(--ease), background-color .15s var(--ease);
}
@media (hover:hover){ .btn-small:hover{ color:var(--ink); border-color:var(--moss); } }
.btn-small:active{ background:var(--surface-moss); transition-duration:0s; }
.btn-small:focus-visible{ outline:2px solid var(--moss); outline-offset:2px; }
.btn-small:disabled{ opacity:0.55; cursor:default; }

/* ---- section titles (2026-10-10, impeccable audit #5) ----
   Titles of the parts of a profile or memorial page ("Where they're
   remembered", "Top places") are real headings in the body face, in
   sentence case, with more room above than below. The small uppercase
   mono label (.eyebrow) is kept for what is actually data - the tile
   labels over their numbers, counts - and for the one kicker above each
   panel's title. */
.section-title{
  font-family:'Public Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  font-size:1rem;
  font-weight:700;
  line-height:1.3;
  letter-spacing:-0.005em;
  color:var(--ink);
  margin:1.9rem 0 0.65rem;
}
.section-title.is-flush{ margin-top:0; }
.section-title + .hub-stats-grid{ margin-top:0.2rem; }

/* ---- side menu (2026-10-21) ----
   The three lines at the left of the header open a menu that slides in
   from the left over everything (header and bottom tabs included), the
   way a phone app's side menu does. Your account card on top, then the
   main items, then fold-open sections at the bottom. See "side menu" in
   app.js. */
.menu-btn{
  width:40px; height:40px; flex:none;
  margin:0 -0.75rem 0 -0.5rem; /* sits close to the logo, like one unit */
  display:flex; align-items:center; justify-content:center;
  border:0; border-radius:50%;
  background:transparent;
  color:var(--ink);
  font-size:1.55rem;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  transition:background-color .15s var(--ease);
}
.menu-btn .icon{ stroke-width:2; }
@media (hover:hover){ .menu-btn:hover{ background:var(--surface-moss); } }
.menu-btn:active{ background:var(--surface-moss); transition-duration:0s; }
.menu-btn:focus-visible{ outline:2px solid var(--moss); outline-offset:2px; }
@media (max-width:640px){
  .menu-btn{ margin:0 -0.4rem 0 -0.55rem; }
}

.side-menu{ position:fixed; inset:0; z-index:1450; /* over the header, tabs and + sheet; under the photo viewer and dialogs */ }
.side-menu-backdrop{
  position:absolute; inset:0;
  background:rgba(43,46,38,0.38);
  animation:sideMenuFadeIn .24s var(--ease) both;
}
.side-menu-sheet{
  position:absolute; top:0; bottom:0; left:0;
  width:min(88vw, 380px);
  display:flex; flex-direction:column;
  overflow-y:auto; overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;
  padding:calc(1rem + env(safe-area-inset-top, 0px)) 1rem calc(1.2rem + env(safe-area-inset-bottom, 0px)) calc(1rem + env(safe-area-inset-left, 0px));
  background:var(--paper);
  box-shadow:6px 0 30px -12px rgba(43,46,38,0.45);
  animation:sideMenuIn .3s var(--ease) both;
  outline:none;
  touch-action:pan-y;
}
.side-menu.is-dragging .side-menu-sheet{ animation:none; transition:none; }
.side-menu.is-closing .side-menu-backdrop{ animation:sideMenuFadeOut .22s var(--ease) both; }
.side-menu.is-closing .side-menu-sheet{ animation:sideMenuOut .22s var(--ease) both; }
@keyframes sideMenuIn{ from{ transform:translateX(-100%); } to{ transform:none; } }
@keyframes sideMenuOut{ from{ transform:translateX(var(--drag-x, 0px)); } to{ transform:translateX(-100%); } }
@keyframes sideMenuFadeIn{ from{ opacity:0; } to{ opacity:1; } }
@keyframes sideMenuFadeOut{ from{ opacity:var(--backdrop-o, 1); } to{ opacity:0; } }
@media (prefers-reduced-motion:reduce){
  .side-menu-backdrop, .side-menu-sheet{ animation:none !important; }
}

/* Your account - the card at the top. */
.side-menu-account{
  display:flex; align-items:center; gap:0.85rem;
  width:100%; min-height:72px;
  padding:0.8rem 1rem;
  border:1px solid var(--line);
  border-radius:var(--radius-lg);
  background:var(--surface);
  box-shadow:var(--shadow);
  color:var(--ink);
  font:inherit; text-align:left;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  transition:border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.side-menu-account .avatar{ width:48px; height:48px; flex:none; font-size:1.05rem; }
.side-menu-account .side-menu-avatar-empty{
  width:48px; height:48px; flex:none; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  background:var(--surface-moss); color:var(--moss-dark); font-size:1.4rem;
}
.side-menu-account-text{ display:flex; flex-direction:column; gap:0.1rem; min-width:0; }
.side-menu-account-name{
  font-family:'Fraunces', Georgia, serif; font-weight:600; font-size:1.18rem; line-height:1.2;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.side-menu-account-hint{ font-size:0.85rem; color:var(--ink-dim); }
@media (hover:hover){ .side-menu-account:hover{ border-color:var(--moss); box-shadow:var(--shadow-lift); } }
.side-menu-account:active{ background:var(--surface-moss); transition-duration:0s; }

/* The main items. */
.side-menu-list{ list-style:none; margin:1.1rem 0 0; padding:0; display:flex; flex-direction:column; gap:0.15rem; }
.side-menu-item, .side-menu-section-btn{
  display:flex; align-items:center; gap:0.9rem;
  width:100%; min-height:52px;
  padding:0.55rem 0.6rem;
  border:0; border-radius:var(--radius-md);
  background:transparent;
  color:var(--ink);
  font:inherit; font-size:1.06rem; font-weight:500; text-align:left;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  transition:background-color .15s var(--ease);
}
.side-menu-item > .icon, .side-menu-section-btn > .icon:first-child{ font-size:1.5rem; color:var(--moss-dark); }
.side-menu-item > span:first-of-type, .side-menu-section-btn > span{ flex:1; min-width:0; }
.side-menu-soon{
  flex:none;
  font-size:0.72rem; font-weight:600; letter-spacing:0.02em;
  color:var(--amber-dark); background:var(--amber-soft);
  border-radius:99px; padding:0.2rem 0.55rem;
}
@media (hover:hover){ .side-menu-item:hover, .side-menu-section-btn:hover{ background:var(--surface-moss); } }
.side-menu-item:active, .side-menu-section-btn:active{ background:var(--line); transition-duration:0s; }
.side-menu-item:focus-visible, .side-menu-section-btn:focus-visible, .side-menu-account:focus-visible, .side-menu-subitem:focus-visible{
  outline:2px solid var(--moss); outline-offset:-2px;
}

/* The fold-open sections at the bottom. */
.side-menu-sections{ margin-top:auto; padding-top:1.4rem; }
.side-menu-section{ border-top:1px solid var(--line); padding:0.25rem 0; }
.side-menu-section:last-child{ border-bottom:1px solid var(--line); }
.side-menu-chevron{ font-size:1.3rem; color:var(--ink-dim); transition:transform .2s var(--ease); }
.side-menu-section-btn[aria-expanded="true"] .side-menu-chevron{ transform:rotate(180deg); }
.side-menu-sub{ display:flex; flex-direction:column; gap:0.1rem; padding:0 0 0.4rem 0; animation:sideMenuSubIn .2s var(--ease) both; }
@keyframes sideMenuSubIn{ from{ opacity:0; transform:translateY(-4px); } to{ opacity:1; transform:none; } }
.side-menu-subitem{
  display:flex; align-items:center; gap:0.8rem;
  min-height:46px; width:100%;
  padding:0.5rem 0.6rem 0.5rem 2.95rem;
  border:0; border-radius:var(--radius-md);
  background:transparent;
  color:var(--ink);
  font:inherit; font-size:0.97rem; text-align:left; text-decoration:none;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.side-menu-subitem .icon{ font-size:1.15rem; color:var(--ink-dim); margin-left:-2.1rem; margin-right:0.25rem; }
@media (hover:hover){ .side-menu-subitem:hover{ background:var(--surface-moss); } }
.side-menu-subitem:active{ background:var(--line); transition-duration:0s; }
.side-menu-note{ margin:0.1rem 0.6rem 0.5rem 2.95rem; font-size:0.92rem; line-height:1.45; color:var(--ink-dim); }

/* ---- Getting started / Our story / Grief support pages (2026-10-21) ---- */
.info-card{ max-width:620px; }
.info-card h2{ font-size:1.6rem; margin-top:0.2rem; }
.info-lede{ font-size:1.02rem; line-height:1.55; color:var(--ink); margin:0.7rem 0 0; }
.info-list{ margin:0; padding-left:1.15rem; display:flex; flex-direction:column; gap:0.55rem; line-height:1.5; }
.info-fine{ font-size:0.86rem; line-height:1.5; color:var(--ink-dim); margin:1.4rem 0 0; }
.info-fine a, .info-resources a{ color:var(--moss-dark); font-weight:600; }
.info-crisis{
  margin-top:1.3rem;
  padding:1rem 1.1rem;
  border:1px solid var(--amber);
  border-radius:var(--radius-lg);
  background:var(--amber-soft);
}
.info-crisis p{ margin:0; line-height:1.5; }
.info-crisis .info-fine{ margin-top:0.85rem; color:var(--ink); }
.info-crisis-actions{ display:flex; flex-wrap:wrap; gap:0.5rem; margin-top:0.85rem; }
.info-crisis-actions .btn{ text-decoration:none; min-height:44px; display:inline-flex; align-items:center; gap:0.4rem; }
.info-resources{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:0.6rem; }
.info-resources li{
  display:flex; flex-direction:column; gap:0.2rem;
  padding:0.8rem 0.95rem;
  border:1px solid var(--line); border-radius:var(--radius-md);
  background:var(--surface);
}
.info-resources span{ font-size:0.93rem; line-height:1.45; color:var(--ink-dim); }

/* Narrow phones (2026-10-21): with the camera button in the header next to
   "Sign in", the ForeverTrail name gets a little smaller so they don't
   touch, and on the very narrowest screens only the logo tile shows. */
@media (max-width:380px){
  .site-header .brand{ gap:0.5rem; }
  .brand-name{ font-size:1.08rem; }
  .header-actions{ right:1rem; }
  .account-group{ gap:0.4rem; }
}
@media (max-width:340px){
  .brand-name{ display:none; }
}

/* Upgrades page (2026-10-30): one card per private memorial size. */
.plan-list{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:0.75rem; }
.plan-card{
  display:flex; flex-direction:column; gap:0.45rem;
  padding:1rem 1.05rem 1.05rem;
  border:1px solid var(--line); border-radius:var(--radius-lg);
  background:var(--surface);
}
.plan-card-keepsake{ border-color:var(--amber); }
.plan-head{ display:flex; align-items:baseline; justify-content:space-between; gap:0.75rem; }
.plan-name{ font-family:'Fraunces', Georgia, serif; font-weight:600; font-size:1.2rem; line-height:1.2; }
.plan-price{ font-weight:700; font-size:1.15rem; color:var(--moss-dark); white-space:nowrap; }
.plan-card-keepsake .plan-price{ color:var(--amber-dark); }
.plan-size{ margin:0; font-size:0.95rem; color:var(--ink-dim); }
.plan-points{ margin:0.1rem 0 0.35rem; padding-left:1.15rem; display:flex; flex-direction:column; gap:0.3rem; font-size:0.95rem; line-height:1.45; }
.plan-card .btn{ align-self:flex-start; min-height:44px; }

/* Moderator tools in the side menu (2026-10-21) - moderators only. */
.side-menu-mod{ margin-top:1rem; padding-top:0.25rem; border-top:1px solid var(--line); }
/* Folds open like Help and support (2026-10-30). */
.side-menu-mod .side-menu-section-btn > .icon:first-child{ color:var(--amber-dark); }
.side-menu-mod .side-menu-section-btn .count-badge{ flex:none; }
.side-menu-mod .side-menu-section-btn[aria-expanded="true"] .side-menu-mod-total{ display:none; }
.side-menu-mod #sideMenuModList{ padding-bottom:0.4rem; animation:sideMenuSubIn .2s var(--ease) both; }
.side-menu-mod #sideMenuModList .side-menu-item{ min-height:46px; padding-left:1.4rem; font-size:0.99rem; }
.side-menu-group-title{
  margin:0 0 0.3rem 0.6rem;
  font-family:'IBM Plex Mono', monospace; font-size:0.72rem; letter-spacing:0.08em; text-transform:uppercase;
  color:var(--amber-dark);
}
.side-menu-mod .side-menu-list{ margin-top:0; }
.side-menu-mod .side-menu-item > .icon{ color:var(--amber-dark); }
.side-menu-item .count-badge{ flex:none; }
/* the ☰ button's "something is waiting" dot (moderators) */
.menu-btn{ position:relative; }
.menu-btn-dot{
  position:absolute; top:7px; right:6px;
  width:9px; height:9px; border-radius:50%;
  background:var(--amber); box-shadow:0 0 0 2px var(--surface);
}

/* ---- passwords (2026-10-21) ---- */
.auth-forgot{ align-self:flex-end; margin-top:-0.35rem; font-size:0.85rem; }
.auth-back-row{ margin:1rem 0 0; }
.password-card{ max-width:480px; }
.password-card .auth-form{ margin-top:1rem; }

/* ---- private memorials (Keepsake, 2026-10-26) ----
   "Who can see this memorial?" on Create a memorial (same look as the
   memory privacy choice), the lock line on a private memorial, the
   Members page and invitations, the Join page, and the moderators'
   Private memorials list. */
#createMemorialForm label.privacy-option{
  flex-direction:row; align-items:flex-start; gap:0.5rem;
  padding:0.6rem 0.7rem; min-height:44px;
  border:1px solid var(--line); border-radius:var(--radius-sm);
  background:var(--paper); cursor:pointer;
  transition:border-color .15s var(--ease), background .15s var(--ease);
}
#createMemorialForm label.privacy-option input{
  width:18px; height:18px; flex:none; margin:0.1rem 0 0; padding:0;
  accent-color:var(--moss); cursor:pointer;
}
#createMemorialForm label.privacy-option:has(input:checked){ border-color:var(--moss); background:var(--surface-moss); box-shadow:inset 0 0 0 1px var(--moss); }
#createMemorialForm label.privacy-option:has(input:focus-visible){ box-shadow:0 0 0 3px var(--surface-moss), inset 0 0 0 1px var(--moss); }
#memorialVisibilityNote{ font-size:0.85rem; line-height:1.45; color:var(--ink); background:var(--surface-moss); border-radius:var(--radius-sm); padding:0.7rem 0.8rem; margin-top:0.6rem; }
#createMemorialForm .keepsake-size{ display:flex; flex-direction:row; align-items:center; gap:0.5rem; margin-top:0.5rem; font-weight:600; }
#createMemorialForm .keepsake-size select{ width:auto; }
#createMemorialSubmitBtn:disabled{ opacity:0.55; cursor:not-allowed; }

.private-memorial-banner{ margin:0.9rem 0 0.2rem; }
.private-memorial-line{ display:flex; align-items:baseline; gap:0.4rem; margin:0; font-size:0.9rem; font-weight:600; color:var(--ink); }
.private-memorial-line .icon{ width:15px; height:15px; color:var(--moss-dark); flex:none; align-self:center; }
.private-memorial-note{ display:flex; align-items:center; gap:0.35rem; margin:0.2rem 0 0.6rem; font-size:0.82rem; color:var(--ink-dim); }
.private-memorial-banner .follow-row{ margin-top:0.4rem; }

.members-actions{ display:flex; flex-wrap:wrap; gap:0.5rem; margin:0.9rem 0 0.4rem; }
.member-row .follower-link{ flex:1 1 auto; }
.member-meta{ display:flex; flex-direction:column; align-items:flex-end; gap:0.15rem; flex:none; }
.member-role{ font-size:0.72rem; font-weight:700; letter-spacing:0.02em; padding:0.1rem 0.45rem; border-radius:999px; background:var(--surface-moss); color:var(--moss-dark); }
.member-role-owner{ background:var(--amber-soft); color:var(--amber-dark); }
.member-menu-btn{ flex:none; }
.invite-row{ flex-wrap:nowrap; }
.invite-who{ display:flex; align-items:center; gap:0.35rem; min-width:0; flex:1 1 0; font-size:0.9rem; }
.invite-who-text{ min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.invite-row .btn-small{ flex:none; }
.invite-who .icon{ width:13px; height:13px; color:var(--ink-dim); }

.invite-form{ display:flex; flex-direction:column; gap:0.7rem; margin:0.6rem 0 1rem; padding:0.9rem; border:1px solid var(--line); border-radius:var(--radius-md); background:var(--paper); }
.invite-form label{ display:flex; flex-direction:column; gap:0.35rem; font-size:0.85rem; font-weight:600; color:var(--ink); }
.invite-form input, .invite-form textarea, .invite-form select{
  font:inherit; font-size:16px; font-weight:400; color:var(--ink);
  padding:0.6rem 0.7rem; border:1px solid var(--line); border-radius:var(--radius-sm); background:var(--surface);
}
.invite-form input:focus-visible, .invite-form textarea:focus-visible, .invite-form select:focus-visible{ outline:none; border-color:var(--moss); box-shadow:0 0 0 3px var(--surface-moss); }
.invite-form textarea{ resize:vertical; }
.invite-form .title-count{ margin:-0.4rem 0 0; text-align:right; font-size:0.75rem; color:var(--ink-dim); }
.invite-form .modal-hint{ margin:0; }
.invite-form-buttons{ display:flex; flex-wrap:wrap; gap:0.5rem; }
.label-optional{ font-weight:400; color:var(--ink-dim); }

.join-message-from{ margin:1rem 0 0.3rem; font-size:0.9rem; color:var(--ink-dim); }
.join-message{ margin:0 0 1rem; padding:0.9rem 1rem; border-radius:var(--radius-md); background:var(--paper); border-left:3px solid var(--moss); font-family:inherit; font-style:italic; line-height:1.5; overflow-wrap:anywhere; }
#joinMemorialContent h2{ margin-top:0.2rem; }
#joinMemorialContent .eyebrow{ display:inline-flex; align-items:center; gap:0.3rem; }
#joinMemorialContent .eyebrow .icon{ width:12px; height:12px; }

#privateMemorialsContent .invite-form{ margin-top:0.4rem; }
.mod-private-row{ display:flex; align-items:center; justify-content:space-between; gap:0.8rem; padding:0.7rem 0; border-bottom:1px solid var(--line); }
.mod-private-row .rail-memorial-name{ display:flex; align-items:center; gap:0.3rem; flex-wrap:wrap; }
.mod-private-row .rail-memorial-name .icon{ width:13px; height:13px; }

/* Messages in the in-app dialogs can have paragraphs (and long links). */
.ft-dialog-message{ white-space:pre-line; overflow-wrap:anywhere; }
#privateMemorialsContent .rail-memorial-list{ list-style:none; margin:0.4rem 0 0; padding:0; }
#privateMemorialsContent .rail-memorial-name{ font-weight:600; color:var(--ink); }
#privateMemorialsContent .rail-memorial-meta{ font-size:0.8rem; color:var(--ink-dim); margin-top:0.15rem; }

/* Private memorial group name (2026-10-26) and Add a memory on memorial pages. */
.private-memorial-group{ display:flex; align-items:baseline; flex-wrap:wrap; gap:0.6rem; margin:0 0 0.35rem; font-family:"Fraunces", Georgia, serif; font-size:1.15rem; font-weight:600; color:var(--ink); }
.private-memorial-group .btn-link{ font-family:inherit; font-size:0.85rem; font-weight:600; }
#personPanelContent .private-memorial-group .btn-link{ font-family:"Public Sans", system-ui, sans-serif; }
.memorial-add-row{ margin:0.9rem 0 0.5rem; }
.memorial-add-row .btn{ width:100%; justify-content:center; display:inline-flex; align-items:center; gap:0.4rem; }
.private-memorial-banner .memorial-add-row{ margin-top:0.5rem; }
#createMemorialForm #memorialGroupWrap > span{ font-weight:600; }

/* Private memorials step 3: public memorials included in a private one (2026-10-26). */
.private-memorial-links{ display:flex; align-items:center; gap:0.35rem; margin:0 0 0.4rem; font-size:0.85rem; }
.private-memorial-links .icon{ width:14px; height:14px; color:var(--moss-dark); }
.match-card{ display:flex; align-items:center; justify-content:space-between; gap:0.6rem; margin:0.3rem 0 0.4rem; padding:0.6rem 0.75rem; border-radius:var(--radius-sm); background:var(--amber-soft); color:var(--ink); font-size:0.85rem; }
.match-card .btn-link{ flex:none; font-weight:700; }
.link-card-list{ list-style:none; margin:0.5rem 0 0; padding:0; }
.link-card{ display:flex; align-items:center; justify-content:space-between; gap:0.7rem; padding:0.7rem 0; border-bottom:1px solid var(--line); }
.link-card:last-child{ border-bottom:none; }
.link-card.is-declined{ opacity:0.75; }
.link-card-main{ min-width:0; flex:1 1 auto; }
.link-card-name{ font-weight:600; color:var(--ink); }
.link-card-name a{ color:inherit; }
.link-card .post-sub a{ color:var(--moss-dark); }
.link-why{ margin-top:0.2rem; font-size:0.78rem; color:var(--moss-dark); }
.link-card-actions{ display:flex; flex-direction:column; align-items:flex-end; gap:0.3rem; flex:none; }
.links-search input{ width:100%; font:inherit; font-size:16px; padding:0.6rem 0.75rem; border:1px solid var(--line); border-radius:var(--radius-sm); background:var(--paper); color:var(--ink); }
.links-search input:focus-visible{ outline:none; border-color:var(--moss); box-shadow:0 0 0 3px var(--surface-moss); }
.linked-from{ margin-top:0.3rem; font-size:0.78rem; color:var(--ink-dim); display:flex; flex-wrap:wrap; align-items:center; gap:0.2rem 0.6rem; }
.linked-from a{ color:var(--moss-dark); }
.linked-from .btn-link{ font-size:0.78rem; }
.link-request-list{ list-style:none; margin:0.8rem 0 0; padding:0; }
.link-request{ padding:0.9rem 0; border-bottom:1px solid var(--line); }
.link-request-sides{ display:grid; grid-template-columns:1fr 1fr; gap:0.7rem; }
.link-side{ padding:0.6rem 0.7rem; border:1px solid var(--line); border-radius:var(--radius-sm); background:var(--paper); min-width:0; overflow-wrap:anywhere; }
.link-request .link-card-actions{ flex-direction:row; justify-content:flex-start; margin-top:0.6rem; }
.alert-icon{ display:inline-flex; align-items:center; justify-content:center; background:var(--surface-moss); color:var(--moss-dark); }
.alert-icon .icon{ width:18px; height:18px; }

/* Free private memorials (2026-10-29) */
.mod-private-actions{ display:flex; flex-wrap:wrap; gap:0.35rem; justify-content:flex-end; align-items:center; }
#createMemorialForm .keepsake-free-note{ margin:0.5rem 0 0; font-size:0.82rem; opacity:0.85; }

/* Create a private memorial: matching public memorials to include (2026-10-29) */
.private-match-list{ margin:0.4rem 0 0.9rem; padding:0.75rem 0.85rem; border:1px solid var(--line, rgba(0,0,0,.12)); border-radius:var(--radius-sm); background:var(--paper); }
.private-match-title{ margin:0 0 0.45rem; font-weight:600; font-size:0.9rem; }
.private-match-items{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:0.45rem; }
#createMemorialForm label.private-match-item{ display:flex; flex-direction:row; align-items:flex-start; gap:0.6rem; margin:0; font-weight:400; cursor:pointer; }
#createMemorialForm label.private-match-item input{ width:18px; height:18px; flex:none; margin:0.15rem 0 0; padding:0; accent-color:var(--moss); }
#createMemorialForm label.private-match-item input:disabled{ cursor:not-allowed; }
/* "Add its stats to this memorial's totals", under each Include box. */
#createMemorialForm label.private-match-item.private-match-stats{ margin:0.3rem 0 0 1.9rem; font-size:0.86rem; color:var(--ink-dim); }
#createMemorialForm label.private-match-item.private-match-stats input{ width:16px; height:16px; }
.link-stats-toggle{ display:flex; align-items:center; gap:0.5rem; margin-top:0.4rem; font-size:0.86rem; font-weight:400; color:var(--ink); cursor:pointer; }
.link-stats-toggle input{ width:16px; height:16px; margin:0; flex:none; accent-color:var(--moss); }
.private-match-name{ display:block; font-weight:600; }
.private-match-meta{ display:block; font-size:0.82rem; opacity:0.8; }
.private-match-list .modal-hint{ margin:0.55rem 0 0; }
