/* 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:#6d7264;
  --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);
}

*{ 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; }
html,body{ height:100%; 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;
  padding:0.85rem 1.4rem;
  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; }

/* 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;
}

/* FT monogram — a small self-contained mark (dark tile + amber sun
   peeking from behind the ridge + "FT" in Abril Fatface), used only
   as the narrow-header fallback for the full logo above. Hidden by
   default; the max-width:640px block below turns it on. */
.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 svg{
  position:absolute;
  inset:0;
  width:100%; height:100%;
}
.brand-mark-ft-letters{
  position:relative;
  font-family:'Abril Fatface', Georgia, serif;
  font-weight:400;
  font-size:15px;
  letter-spacing:0.3px;
  color:#f6f3ea;
}

.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; }
.header-actions{
  margin-left:auto;
  display:flex;
  align-items:center;
  gap:0.9rem;
  row-gap:0.5rem;
  flex-wrap:wrap;
}
.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);
}
.icon-btn:hover{ border-color:var(--moss); color:var(--ink); box-shadow:var(--shadow); }
.header-search-wrap{ position:relative; flex:none; margin-left:1.4rem; }
.header-search-popover{
  position:absolute;
  top:calc(100% + 0.6rem);
  left:0;
  width:min(320px, 80vw);
}
.header-search-popover input{
  width:100%;
  padding:0.55rem 0.85rem;
  border-radius:9px;
  border:1px solid var(--line);
  background:var(--surface);
  box-shadow:var(--shadow-lift);
  font-size:0.88rem;
  font-family:'Public Sans', sans-serif;
  color:var(--ink);
  transition:box-shadow .15s var(--ease), border-color .15s var(--ease);
}
.header-search-popover input:hover{ border-color:var(--moss); }
.header-search-popover input:focus-visible{ border-color:var(--moss); outline:none; }
.header-search-popover .search-results{ position:absolute; top:calc(100% + 0.4rem); left:0; right:0; margin-top:0; }
.header-search-popover.is-closing{ animation:fadeOut .12s ease both; }

.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);
}
.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);
}
.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:hover, .search-results button:focus-visible{ background:var(--surface-moss); }

/* 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; }

/* moderator-only tools — kept the same amber "not for everyone"
   tinting they always had. Now lives on the moderator's own profile
   page (see the static markup inside #userProfilePanel in app.html)
   instead of a sidebar that no longer exists — openUserProfile() in
   app.js shows/hides this whole block depending on whether you're
   looking at your own profile and are signed in as a moderator. */
.rail-mod-tools{
  flex:none;
  display:flex;
  flex-direction:column;
  gap:0.5rem;
  padding:1rem 1.1rem 1.1rem;
  margin:0 0 1.4rem;
  background:var(--amber-soft);
  border:1px solid var(--line);
  border-radius:var(--radius-lg);
}
.rail-mod-tools .eyebrow{ color:var(--amber-dark); margin-bottom:0.2rem; }
.rail-mod-tools .btn{
  width:100%;
  display:flex;
  align-items:center;
  justify-content:space-between;
  background:transparent;
  border-color:rgba(138,95,40,0.32);
}
.rail-mod-tools .btn:hover{ background:var(--surface); border-color:var(--amber); }

/* ---- 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);
}
.composer-input:hover{ background:var(--line); color:var(--ink); }
.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);
}
.activity-actions button:hover{ text-decoration:underline; }

/* ---- Map tab: the toolbar that replaces the old rail-actions, plus
   the map itself ---- */
.map-toolbar{
  display:flex; align-items:center; flex-wrap:wrap;
  gap:0.7rem;
  padding:0.85rem 1.4rem;
  background:var(--surface);
  border-bottom:1px solid var(--line);
}
.map-toolbar .btn{ flex:none; }
.map-filter-group{
  display:flex; flex-direction:column; gap:0.4rem;
  margin-left:auto;
}
.map-filter-toggle{
  display:flex; align-items:center; gap:0.5rem;
  font-size:0.82rem; color:var(--ink);
  white-space:nowrap; cursor:pointer;
}
.map-filter-toggle input{ accent-color:var(--moss); width:15px; height:15px; }
.map-stage{ position:relative; flex:1; min-height:0; }
#mapView{ display:flex; flex-direction:column; }
#map{ position:absolute; inset:0; background:var(--surface-moss); }

.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 */
.ft-cluster-wrap{ background:none; border:none; }
.ft-cluster{
  width:34px; height:34px;
  border-radius:50%;
  background:var(--moss);
  color:#fbfaf5;
  border:2px solid #fbfaf5;
  box-shadow:var(--shadow);
  display:flex; align-items:center; justify-content:center;
  font-family:'IBM Plex Mono', monospace;
  font-size:0.8rem;
  font-weight:600;
  transition:transform .15s var(--ease), box-shadow .15s var(--ease);
}
.ft-cluster:hover{ transform:scale(1.08); box-shadow:var(--shadow-lift); }

.ft-marker{
  width:16px; height:16px;
  border-radius:50% 50% 50% 0;
  transform:rotate(-45deg);
  background:var(--moss);
  border:2px solid #fbfaf5;
  box-shadow:0 1px 3px rgba(0,0,0,0.35);
  transition:transform .15s var(--ease), box-shadow .15s var(--ease);
}
.ft-marker:hover{ transform:rotate(-45deg) scale(1.25); box-shadow:0 3px 8px rgba(0,0,0,0.4); }
/* Memories posted in person (from the poster's own phone, standing at
   the spot) get the solid pin 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:var(--paper);
  border:2px solid var(--moss);
  box-shadow:0 1px 3px rgba(0,0,0,0.25);
}

/* 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 out to the whole world" button, near the map's own zoom control */
.map-reset-btn{
  position:absolute;
  z-index:1000;
  top:90px;
  left:14px;
  background:rgba(255,255,255,0.92);
  backdrop-filter:saturate(160%) blur(6px);
  -webkit-backdrop-filter:saturate(160%) blur(6px);
  border:1px solid var(--line);
  border-radius:var(--radius-md);
  box-shadow:var(--shadow);
  padding:0.55rem 0.8rem;
  font-size:0.82rem;
  font-weight:600;
  font-family:'Public Sans', sans-serif;
  color:var(--ink);
  cursor:pointer;
  transition:background-color .15s var(--ease), border-color .15s var(--ease), box-shadow .15s var(--ease), transform .1s var(--ease);
}
.map-reset-btn:hover{ background:var(--surface); border-color:var(--moss); box-shadow:var(--shadow-lift); transform:translateY(-1px); }
.map-reset-btn:active{ transform:translateY(0); }

/* 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-banner .photo-slide, .photo-banner .photo-dots span{ animation:none !important; }
  .photo-banner .photo-slide:first-child{ opacity:1 !important; }
  .photo-banner .photo-dots span:first-child{ background:#fff !important; }
  /* The globe's own auto-spin already checks this in JS (drag still
     works); this just quiets the pins' pulsing glow too. */
  .globe-pin::after{ animation:none !important; }
  /* 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 (map-reset-btn, 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){
  .map-reset-btn{ background:var(--surface); backdrop-filter:none; -webkit-backdrop-filter:none; }
  .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;
  padding:3.2rem 1.4rem 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; } }
@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);
}
.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; }
.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);
}
.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);
}
.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;
}

/* 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;
  padding:3.2rem 1.4rem 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; }
.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; }
.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; }
.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);
}
.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 — only the globe panel's own height is fixed (see
   .hub-map-col .globe-mount below), scaled down from its old
   full-width size now that it's got half the room. 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": a small, rotating globe
   (plain CSS/JS orthographic projection — no map library, no tiles)
   with a pin for every memory's location. Replaced the old flat
   Leaflet mini map. The profile page reuses this same
   .hub-map-panel/.globe-mount pair full-width (it has no "top
   places" list to sit beside), so the shorter height below is
   scoped to .hub-map-col rather than changed here. */
.hub-map-panel{
  background:var(--surface-moss);
  border:1px solid var(--line);
  border-radius:var(--radius-lg);
  overflow:hidden;
  margin-bottom:1.6rem;
}
.globe-mount{
  width:100%; height:340px;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:0.55rem;
}
.hub-map-col .hub-map-panel{ margin-bottom:0; }
.hub-map-col .globe-mount{ height:260px; }

.globe-stage{ position:relative; width:230px; height:230px; }
.hub-map-col .globe-stage{ width:172px; height:172px; }
.globe-shadow-ellipse{
  position:absolute; left:50%; bottom:-9px; width:52%; height:20px;
  transform:translateX(-50%);
  background:radial-gradient(closest-side, rgba(43,46,38,0.24), transparent 75%);
}
.globe-wrap{ position:absolute; inset:0; border-radius:50%; cursor:grab; touch-action:none; user-select:none; }
.globe-wrap:active{ cursor:grabbing; }
.globe-sphere{
  position:absolute; inset:0; border-radius:50%;
  background:radial-gradient(circle at 32% 26%, var(--surface-moss) 0%, var(--moss) 52%, var(--moss-dark) 100%);
  box-shadow:inset -13px -10px 26px rgba(28,30,24,0.4), inset 6px 6px 14px rgba(255,255,255,0.18);
  overflow:hidden;
}
.globe-features{ position:absolute; inset:0; }
.globe-continent{
  position:absolute; left:50%; top:50%;
  background:rgba(63,86,66,0.72);
  border-radius:43% 57% 61% 39% / 51% 44% 56% 49%;
  will-change:transform;
}
.globe-pin{
  position:absolute; left:50%; top:50%; border-radius:50%;
  background:var(--amber); box-shadow:0 0 0 2px rgba(185,136,76,0.3);
  will-change:transform, opacity;
}
.globe-pin::after{
  content:""; position:absolute; inset:-4px; border-radius:50%;
  box-shadow:0 0 0 0 rgba(185,136,76,0.45);
  animation:globePinPulse 2.6s ease-out infinite;
}
.globe-pin:nth-child(2n)::after{ animation-delay:.6s; }
.globe-pin:nth-child(3n)::after{ animation-delay:1.2s; }
@keyframes globePinPulse{
  0%{ box-shadow:0 0 0 0 rgba(185,136,76,0.45); }
  70%{ box-shadow:0 0 0 9px rgba(185,136,76,0); }
  100%{ box-shadow:0 0 0 9px rgba(185,136,76,0); }
}
.globe-rim{ position:absolute; inset:0; border-radius:50%; box-shadow:inset 0 0 0 1px rgba(43,46,38,0.12); pointer-events:none; }
.globe-hint{ font-size:0.72rem; color:var(--ink-dim); margin:0; text-align:center; }

/* 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; }
.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-slide{
  position:absolute; inset:0;
  opacity:0;
}
.photo-banner .photo-slide img{ width:100%; height:100%; object-fit:cover; display:block; }
.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;
}
.photo-banner .photo-dots span{
  width:6px; height:6px; border-radius:50%;
  background:rgba(255,255,255,0.4);
}

/* 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;
}
.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);
}
.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;
}
#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; }
.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);
}
.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;
}
.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);
}
.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:hover, .share-menu button:focus-visible{ 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);
}
.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);
}
.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; }
.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); }
.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); }

/* 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);
}
.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; }

/* 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); }
}

/* ---- 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;
}
.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);
}
.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);
}
.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. */
  .map-toolbar .btn{ min-width:150px; }
}

@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; }
  .header-search-popover{ left:auto; right:0; width:min(300px, calc(100vw - 1.6rem)); }
  .header-actions{ width:100%; margin-left:0; }
  .panel{ width:100%; }

  .composer{ padding:0.65rem 0.85rem; }
  .feed-list{ padding:0.9rem 0.7rem 2rem; }
  .map-toolbar{ padding:0.6rem 0.7rem; }
  .map-toolbar .btn{ min-width:130px; font-size:0.78rem; }
  .map-filter-group{ flex:1 1 100%; margin-left:0; gap:0.5rem; }
  .map-filter-toggle{ justify-content:flex-start; }

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

  .panel-hub{ padding:1.6rem 0.7rem 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-panel .globe-mount{ height:220px; }
  .hub-map-panel .globe-stage{ width:150px; height:150px; }
  .hub-map-places-row{ flex-direction:column; }

  #memoryPanel.panel-post{ padding:1.6rem 0.6rem 2rem; }
  .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); }

/* 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; }
.site-footer a:hover{ color:var(--ink); text-decoration:underline; }
.site-footer .footer-sep{ opacity:0.45; }
