/* HiFinder design system v8: "The Clearing".
   A forest at rest rather than a void: the background is a real dark green,
   not near-black, and every surface above it is a lighter step of the same
   hue instead of a different one. Soft glow instead of hard shadows, rounded
   shapes, sentence case. Archivo Black for headings, Inter for everything you
   read, JetBrains Mono for small meta labels. Everything self-hosted.

   Colour is still split three ways so no single hue dominates, but the
   identity and action roles are now close kin, both green, since that is the
   whole vibe: this is a place, not a control panel.
     sage   the app's resting identity (ambient marks, borders, glow)
     amber  anything you can act on (buttons, links, active states). The name
            is legacy from v7's warm accent; the token stayed so the rest of
            this file did not have to be rewritten, only its value did.
     lilac  small punctuation (chips, badges, secondary meta)
   Every text colour below was re-checked against WCAG AA on the new,
   lighter background and against --panel, not carried over by eye. */

@font-face {
  font-family: "Archivo Black";
  src: url("../fonts/archivo-black.4b6914a69e6a.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  /* Variable file: real weights from 100 to 900, no synthetic bolding. */
  font-family: "Inter";
  src: url("../fonts/inter.260c81a4759b.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("../fonts/jetbrains-mono.b058178d7f30.woff2") format("woff2");
  font-weight: 100 800;
  font-style: normal;
  font-display: swap;
}

:root {
  --ink: #070a08;
  --background: #1a3c2a;
  --panel: rgba(46, 95, 64, 0.72);
  --panel-2: rgba(60, 114, 80, 0.75);
  --bone: #e9ede9;

  /* Identity. Light enough to read as the brand colour on the new, lighter
     background, verified at 8.2:1 there and 5:1 on --panel. */
  --sage: #b7dfb2;
  --sage-soft: rgba(183, 223, 178, 0.12);
  /* Action. A shade brighter and slightly warmer than sage, so clickable
     things still separate from ambient ones, just by less than v7's amber
     did. 7.7:1 on background, 4.7:1 on panel. */
  --amber: #93e08a;
  --amber-soft: rgba(147, 224, 138, 0.12);
  /* Punctuation. Kept a distinct hue on purpose: an all-green palette still
     needs one colour that is obviously not terrain or action. */
  --lilac: #cbc2ee;

  /* --ember is the old name for the identity colour. Kept as an alias so the
     whole stylesheet did not have to be rewritten in one go. */
  --ember: var(--sage);
  --border: rgba(90, 138, 92, 0.15);
  --border-strong: rgba(90, 138, 92, 0.35);
  --muted: #a8d5a6;
  --danger: #f0876a;
  --info: #7ab8d7;
  --gold: #d9b877;
  --star-dim: #2a5138;
  --display: "Archivo Black", "Segoe UI", system-ui, sans-serif;
  --body-font: "Inter", "Segoe UI", system-ui, sans-serif;
  --mono: "JetBrains Mono", Consolas, monospace;
  --radius: 14px;
  --radius-lg: 20px;
  --radius-pill: 999px;
  /* Depth comes from soft shadow plus a faint glow, never hard offsets. */
  --shadow-brutal: 0 6px 24px rgba(0, 0, 0, 0.45);
  --shadow-brutal-lg: 0 14px 44px rgba(0, 0, 0, 0.55);
  --shadow-ember: 0 0 0 1px rgba(147, 224, 138, 0.32), 0 8px 28px rgba(147, 224, 138, 0.16);
  --glow: 0 0 24px rgba(183, 223, 178, 0.22);

  /* Motion. Built-in CSS easings are too weak to read as deliberate, and the
     bare "ease" keyword ramps in, which delays the exact moment the eye is on
     the element. These are the curves the animation audit prescribes. */
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --dur-fast: 120ms;
  --dur-base: 180ms;
  --dur-slow: 260ms;
  /* One knob for every press. Reduced motion sets it to 1, which switches off
     press feedback everywhere without a rule per element. */
  --press: 0.97;
}

* { box-sizing: border-box; }

/* The hidden attribute must always win, even over elements whose class sets a
   display value (e.g. the route bar). Otherwise those show as empty boxes. */
[hidden] { display: none !important; }

html { scrollbar-color: var(--ember) var(--ink); }

body {
  margin: 0;
  background: var(--background);
  color: var(--bone);
  font-family: var(--body-font);
  font-size: 15.75px;
  line-height: 1.6;
  letter-spacing: -0.006em;
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
}

/* The haze: three coloured clouds that drift and breathe behind everything.
   Slow enough (40s) to read as atmosphere rather than animation. This is the
   only "trippy" element; the interface on top of it stays plain. */
body::before {
  content: "";
  position: fixed;
  inset: -25%;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(38% 44% at 22% 18%, rgba(183, 223, 178, 0.13), transparent 70%),
    radial-gradient(34% 40% at 80% 26%, rgba(203, 194, 238, 0.15), transparent 70%),
    radial-gradient(44% 46% at 55% 88%, rgba(147, 224, 138, 0.09), transparent 70%);
  filter: blur(16px);
  animation: haze-drift 40s var(--ease-in-out) infinite alternate;
}
@keyframes haze-drift {
  0%   { transform: translate3d(-2%, -1%, 0) scale(1); }
  50%  { transform: translate3d(3%, 2%, 0) scale(1.08); }
  100% { transform: translate3d(-1%, 3%, 0) scale(1.03); }
}

/* A whisper of grain so the gradients do not band on cheap panels. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 999;
  pointer-events: none;
  opacity: 0.025;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

::selection { background: var(--ember); color: var(--ink); }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: var(--ember); }
::-webkit-scrollbar-track { background: var(--ink); }

a { color: var(--amber); text-decoration: none; font-weight: 600; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

h1, h2, h3 {
  font-family: var(--display);
  font-weight: 400;
  color: var(--bone);
  line-height: 1.12;
  letter-spacing: -0.025em;
}
/* Fluid type: scales smoothly with the viewport instead of jumping at
   breakpoints, so there is no awkward size between phone and desktop. */
h1 { font-size: clamp(1.42rem, 1.08rem + 1.6vw, 2.05rem); margin: 0.1em 0 0.4em; }
h2 { font-size: clamp(1.02rem, 0.94rem + 0.45vw, 1.24rem); margin: 1.7em 0 0.6em; }
h3 { font-size: clamp(0.92rem, 0.89rem + 0.16vw, 1.02rem); margin: 0 0 0.4em; }
.kicker { color: var(--muted); margin-top: -0.15rem; }
.dot-accent { color: var(--ember); }

.mono {
  font-family: var(--mono);
  letter-spacing: 0.02em;
}

.skip {
  position: absolute;
  left: -999px;
  top: 8px;
  z-index: 1001;
  background: var(--ember);
  color: var(--ink);
  padding: 0.5rem 1rem;
  font-family: var(--mono);
}
.skip:focus { left: 8px; }

.visually-hidden {
  /* !important throughout, not just on position: a plain input[type="file"]
     selector is more specific than this one class (attribute selectors
     outrank a lone class), so it was winning the width and height here and
     an outwardly 1x1px file input was actually rendering at its normal
     full size, wide enough to push a cramped mobile row past the viewport. */
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* --- Styled file input (core.widgets.StyledFileInput) --- */
.file-picker { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; }
.file-picker-btn { display: inline-flex; align-items: center; gap: 0.5rem; }
.file-picker-name {
  color: var(--muted);
  font-size: 0.82rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 220px;
}
.file-picker-current {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  flex-basis: 100%;
  font-size: 0.82rem;
  color: var(--muted);
  margin-bottom: 0.2rem;
}
.file-picker-current a { color: var(--bone); }
.file-picker-clear { display: inline-flex; align-items: center; gap: 0.3rem; cursor: pointer; }

/* --- Navigation --- */
.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  padding: 0.6rem 1.3rem;
  background: rgba(10, 18, 15, 0.72);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  border-bottom: 1px solid var(--border);
}
.brand { display: inline-flex; align-items: center; gap: 0.65rem; color: var(--bone); }
.brand:hover { text-decoration: none; }
.logo-sq {
  width: 36px;
  height: 36px;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(145deg, var(--sage), #6ba07d);
  color: var(--ink);
  border: none;
  border-radius: 12px;
  box-shadow: var(--glow);
}
.brand-block { display: flex; flex-direction: column; line-height: 1.2; }
.brand-block > span:first-child {
  font-family: var(--display);
  font-weight: 400;
  font-size: 0.98rem;
  letter-spacing: -0.015em;
}
.brand-block .slash { color: var(--ember); }
.brand-sub {
  font-size: 0.68rem;
  color: var(--muted);
}

.nav-links { display: flex; align-items: center; gap: 0.15rem; flex-wrap: wrap; }
.nav-link {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.86rem;
  font-weight: 500;
  color: var(--muted);
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;
  padding: 0.45rem 0.85rem;
  transition: background 160ms ease, color 160ms ease;
}
.nav-link svg { width: 16px; height: 16px; }
.nav-link:hover {
  background: var(--amber-soft);
  color: var(--bone);
  text-decoration: none;
}
/* The current section is marked with colour, not a filled slab. */
.nav-link.active {
  background: var(--amber-soft);
  color: var(--amber);
  box-shadow: inset 0 0 0 1px rgba(147, 224, 138,0.28);
}

/* The <details> wrapping .nav-links. Transparent on desktop: the summary
   (the burger) is hidden and .nav-links lays out exactly as it always has.
   Only the max-width: 640px block below turns this into an actual drawer. */
.nav-drawer { position: relative; }
.nav-burger {
  display: none;
  width: 40px;
  height: 40px;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-pill);
  color: var(--bone);
  cursor: pointer;
  list-style: none;
}
.nav-burger::-webkit-details-marker { display: none; }
.nav-burger::marker { content: ""; }
.nav-burger:hover { background: var(--amber-soft); }
.nav-drawer[open] .nav-burger { background: var(--amber-soft); color: var(--amber); }
.nav-burger-icon { width: 20px; height: 20px; }

.inline-form { display: inline; }
.linklike {
  display: inline-flex;
  align-items: center;
  gap: 0.42rem;
  background: none;
  border: 1px solid transparent;
  padding: 0.45rem 0.7rem;
  color: var(--muted);
  font-family: var(--mono);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  cursor: pointer;
}
.linklike svg { width: 14px; height: 14px; }
.linklike:hover { color: var(--amber); }

/* --- Layout --- */
.container { max-width: 920px; margin: 0 auto; padding: clamp(1rem, 3vw, 1.6rem) clamp(0.9rem, 3vw, 1.3rem) 2.4rem; }
.footer {
  max-width: 920px;
  margin: 2.5rem auto 0;
  padding: 1.2rem;
  color: var(--muted);
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.02em;
  border-top: 1px solid var(--border);
  text-align: center;
}
.page-head {
  display: flex;
  justify-content: space-between;
  align-items: end;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 0.6rem;
}
.back-link {
  display: inline-block;
  color: var(--muted);
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.02em;
  margin-bottom: 0.5rem;
}
.back-link:hover { color: var(--amber); text-decoration: none; }

/* --- Cards --- */
/* Cards are frosted glass over the haze, so the drift shows faintly through. */
.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.3rem 1.5rem;
  margin: 1rem 0;
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
}
.card-link {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  color: var(--bone);
  font-weight: 400;
  transition: transform 100ms var(--ease-out), box-shadow 100ms var(--ease-out), border-color 100ms var(--ease-out);
}
.card-link:hover {
  text-decoration: none;
  transform: translateY(-2px);
  border-color: var(--border-strong);
  box-shadow: var(--shadow-ember);
}
.card-link strong { font-size: 0.92rem; font-weight: 700; }
/* auto-fit, not auto-fill: with a short list, auto-fill still reserves empty
   tracks for cards that do not exist, so the real cards are shoved into the
   left part of the row and the rest sits dead. auto-fit collapses those empty
   tracks and lets the real cards stretch to fill the row instead. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 0.9rem;
}
.card-grid .card { margin: 0; }

.muted { color: var(--muted); font-size: 0.9rem; }

/* --- Chips & badges --- */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.32rem;
  padding: 0.14rem 0.55rem;
  font-family: var(--mono);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  background: var(--panel-2);
  color: var(--bone);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
}
/* currentColor, not a token: it picks up whatever .chip-<category> below has
   already set on this exact chip, so one rule works for all eight without
   naming any of them. Was missing entirely; every category dot on a chip
   rendered as a zero-size, invisible span until now. */
.chip .dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }
/* One value per category, copied from CATEGORY_COLORS in globe.js. These
   drifted apart once already: migration 0010 renamed four categories, this
   file was not updated, and half the app's categories rendered with no colour
   while the other half disagreed with their own map pin. */
.chip-beach    { color: #5aaecb; border-color: #5aaecb66; }
.chip-forest   { color: #4fa982; border-color: #4fa98266; }
.chip-park     { color: #7cbf7c; border-color: #7cbf7c66; }
.chip-mountain { color: #9aa7b8; border-color: #9aa7b866; }
.chip-city     { color: #d98a5a; border-color: #d98a5a66; }
.chip-lake     { color: #6f9fd8; border-color: #6f9fd866; }
.chip-field    { color: #c9b45e; border-color: #c9b45e66; }
.chip-camp     { color: #c9866a; border-color: #c9866a66; }
.chip-private { color: var(--danger); border-color: rgba(255, 93, 58, 0.45); }

.owner-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; margin: 0.6rem 0; }
.owner-actions form { margin: 0; }

/* Sub-tabs inside the Crew section (Find people / Friends / Messages): a
   segmented pill, not underlined text, so it reads as one control with a
   position rather than three separate links. */
.subtabs {
  display: inline-flex;
  padding: 4px;
  gap: 4px;
  border-radius: var(--radius-pill);
  background: rgba(7, 10, 8, 0.42);
  border: 1px solid var(--border);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  margin-bottom: 1.2rem;
  max-width: 100%;
  overflow-x: auto;
  scrollbar-width: none;
}
.subtabs::-webkit-scrollbar { display: none; }
.subtab {
  padding: 0.5rem 0.95rem;
  color: var(--muted);
  font-family: var(--body-font);
  font-weight: 600;
  font-size: 0.72rem;
  border-radius: var(--radius-pill);
  white-space: nowrap;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  transition: color 140ms ease;
}
.subtab:hover { color: var(--bone); text-decoration: none; }
.subtab.active { background: var(--amber); color: var(--ink); font-weight: 700; }
.crew-tab-count {
  display: inline-flex;
  margin-left: 0.45rem;
  color: var(--lilac);
  font-weight: 700;
}
.subtab.active .crew-tab-count { color: var(--ink); }
.chip-match   { background: var(--lilac); color: var(--ink); border-color: var(--border-strong); }
.chip-coords  { font-variant-numeric: tabular-nums; }

.count-badge {
  display: inline-block;
  min-width: 1.6em;
  text-align: center;
  padding: 0.05rem 0.45rem;
  background: var(--lilac);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 700;
  border-radius: var(--radius-pill);
}

/* --- Stars --- */
.stars {
  position: relative;
  display: inline-block;
  color: var(--star-dim);
  font-size: 1rem;
  line-height: 1;
  letter-spacing: 2px;
}
.stars-fill {
  position: absolute;
  left: 0;
  top: 0;
  white-space: nowrap;
  overflow: hidden;
  color: var(--gold);
}

/* --- Buttons --- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  background: linear-gradient(145deg, var(--amber), #d1863a);
  color: #06180d;
  border: none;
  border-radius: var(--radius-pill);
  padding: 0.6rem 1.3rem;
  font-family: var(--body-font);
  font-size: 0.92rem;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 4px 18px rgba(147, 224, 138,0.22);
  transition: transform 180ms var(--ease-out), box-shadow 180ms var(--ease-out), filter 180ms var(--ease-out);
}
.btn svg { width: 16px; height: 16px; }
.btn:hover {
  text-decoration: none;
  transform: translateY(-1px);
  box-shadow: 0 6px 26px rgba(147, 224, 138,0.32);
  filter: brightness(1.04);
}
.btn:active { transform: translateY(0); box-shadow: 0 2px 10px rgba(147, 224, 138,0.2); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; transform: none; box-shadow: none; }
.btn-small { padding: 0.4rem 0.85rem; font-size: 0.66rem; }
.btn-big { padding: 0.85rem 1.7rem; font-size: 0.8rem; }
.btn-wide { width: 100%; }
.btn-outline {
  background: var(--panel);
  /* Full shorthand, not border-color alone: .btn sets border: none, and a
     colour on a border-style: none box draws nothing. */
  border: 1px solid var(--border);
  color: var(--bone);
  box-shadow: var(--shadow-brutal);
}
.btn-outline:hover { border: 1px solid var(--border-strong); background: var(--panel-2); box-shadow: var(--shadow-ember); }

/* --- Social login --- */
.social-row { display: flex; flex-direction: column; gap: 0.7rem; margin: 0.9rem 0; }
.social-row form { margin: 0; }
.btn-social { width: 100%; background: var(--bone); color: var(--ink); border: 1px solid var(--border-strong); }
.btn-social:hover { box-shadow: var(--shadow-ember); }
.divider {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  color: var(--muted);
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.02em;
  margin: 1.2rem 0;
}
.divider::before, .divider::after { content: ""; flex: 1; height: 2px; background: var(--border); }

/* --- Forms --- */
/* Without margin: auto this sat flush against .container's left edge on any
   screen wider than 520px, since a block element does not center itself. */
.form-card { max-width: 520px; margin: 0 auto; }
form p { margin: 0.9rem 0; }
form label {
  display: block;
  margin-bottom: 0.3rem;
  color: var(--muted);
  font-family: var(--mono);
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}
input[type="text"], input[type="password"], input[type="email"], input[type="search"],
input[type="number"], input[type="file"], input[type="url"], textarea, select {
  width: 100%;
  background: rgba(7, 13, 11, 0.6);
  color: var(--bone);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.68rem 0.85rem;
  font: inherit;
  transition: border-color 160ms ease, box-shadow 160ms ease, background 160ms ease;
}
input::placeholder, textarea::placeholder {
  color: #6b7d70;
  font-size: 0.92rem;
}
input:focus, textarea:focus, select:focus {
  outline: none;
  border-color: var(--amber);
  box-shadow: 0 0 0 3px rgba(147, 224, 138,0.18);
}
.helptext { display: block; color: var(--muted); font-size: 0.8rem; margin-top: 0.3rem; }
.errorlist {
  color: var(--danger);
  list-style: none;
  padding: 0.45rem 0.7rem;
  margin: 0.3rem 0;
  font-size: 0.88rem;
  background: rgba(255, 93, 58, 0.08);
  border-left: 4px solid var(--danger);
}
form > ul:not(.errorlist), form li { color: var(--muted); font-size: 0.84rem; }

/* --- Flash messages --- */
/* Fixed, not relative: the map and the Lounge are position: fixed full-bleed
   layouts that start right under the nav regardless of what else is on the
   page, so a message sitting in normal flow lands in the exact same screen
   region as their own floating buttons. pointer-events: none is safe because
   a message is status text with no link or button inside it, so nothing is
   ever lost by letting a click fall through to whatever is actually under it
   rather than being swallowed by an invisible banner someone already read. */
.messages {
  position: fixed;
  top: 63px;
  left: 0;
  right: 0;
  max-width: 920px;
  margin: 0.7rem auto 0;
  padding: 0 1.3rem;
  z-index: 60;
  pointer-events: none;
}
.message {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.65rem 0.95rem;
  margin: 0.45rem 0;
  background: var(--panel);
  border: 1px solid var(--border);
  border-left: 6px solid var(--border);
  font-size: 0.93rem;
  /* Slides in, holds long enough to actually read, slides back out on its
     own. animation-fill-mode: forwards leaves it at opacity: 0 afterward
     rather than snapping back to visible; harmless to leave in the DOM at
     that point since .messages is already pointer-events: none and fixed,
     so an invisible leftover node affects nothing. Reduced motion turns
     animation off globally (see the media query below), which means this
     one stops disappearing on its own too, not just stops sliding: that is
     the right trade for anyone who finds timed, moving UI hard to track,
     not a bug to route around.  */
  animation: toast-lifecycle 6s ease forwards;
  border-radius: var(--radius);
}
.message::before { font-weight: 700; font-family: var(--mono); }
.message.success { border-left-color: var(--ember); }
.message.success::before { content: "✓"; color: var(--ember); }
.message.info { border-left-color: var(--info); }
.message.info::before { content: "i"; color: var(--info); }
.message.error { border-left-color: var(--danger); }
.message.error::before { content: "!"; color: var(--danger); }

/* A message that belongs to the page rather than to the last click. Same
   left-rule idiom as .message so the two read as one family, but it does not
   slide in and it does not go away, because the state it describes does not
   either. Used for a spot waiting on a moderator, and for a private spot. */
.notice {
  padding: 0.7rem 0.95rem;
  margin: 0.8rem 0 0;
  background: var(--panel);
  border: 1px solid var(--border);
  border-left: 6px solid var(--info);
  border-radius: var(--radius);
  font-size: 0.92rem;
  color: var(--bone);
  line-height: 1.5;
}

@keyframes slide-in {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Same entrance as slide-in above, held, then the same move in reverse.
   One animation instead of an entrance animation plus a separate JS timer
   plus a separate exit animation, because nothing here needs to run after
   the element appears: the whole lifecycle is known up front. */
@keyframes toast-lifecycle {
  0%   { opacity: 0; transform: translateY(-6px); }
  5%   { opacity: 1; transform: translateY(0); }
  90%  { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(-6px); }
}

/* --- Photos --- */
.photo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 0.7rem;
}
.photo-grid a { display: block; overflow: hidden; border: 1px solid var(--border); }
.photo-grid img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  display: block;
  transition: transform 200ms var(--ease-out);
}
.photo-grid a:hover img { transform: scale(1.04); }

/* --- Age gate --- */
.gate { max-width: 560px; margin: 7dvh auto; text-align: center; padding: 2.6rem 2.2rem; box-shadow: var(--shadow-brutal-lg); }
.gate svg { color: var(--ember); }
.gate h1 { font-size: clamp(1.7rem, 1.2rem + 2.4vw, 2.4rem); }
.gate form { margin: 1.4rem 0 0.9rem; }

/* --- Avatars, profile, people --- */
.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  background: var(--panel-2);
  border: 1px solid var(--accent, var(--border)); /* per-member accent ring */
  color: var(--accent, var(--sage));
  font-weight: 800;
  font-size: 0.95rem;
  flex: none;
  object-fit: cover;
  overflow: hidden;
  border-radius: var(--radius-pill);
}
img.avatar { border-color: var(--accent, var(--border)); }
.avatar-big { width: 4.4rem; height: 4.4rem; font-size: 1.7rem; }
.avatar-deck { width: 6rem; height: 6rem; font-size: 2.4rem; }

.profile-head { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.profile-head h1 { margin: 0; font-size: clamp(1.35rem, 1.1rem + 1.2vw, 1.75rem); }

/* Customizable profile hero: banner + accent (per-member --accent) */
.profile-hero {
  --accent: var(--ember);
  background: var(--panel);
  border: 1px solid var(--border);
  margin: 0.9rem 0;
  border-radius: var(--radius-lg);
}
.profile-banner {
  height: 150px;
  background:
    repeating-linear-gradient(0deg, rgba(0,0,0,0.18) 0 1px, transparent 1px 4px),
    linear-gradient(135deg, color-mix(in srgb, var(--accent) 38%, #0b100e), #0b100e 78%);
  background-size: cover;
  background-position: center;
  border-bottom: 1px solid var(--border);
}
.profile-id {
  /* Known stacking hazard: .profile-banner carries the banner image and this
     row overlaps it with a negative margin. Without its own position and a
     z-index above the banner's stacking context, the avatar renders under
     the banner and clips into a flat-topped half circle. */
  position: relative;
  z-index: 1;
  display: flex;
  align-items: flex-end;
  gap: 1rem;
  padding: 0 1.3rem;
  margin-top: -44px;
  flex-wrap: wrap;
}
.avatar-hero {
  width: 96px;
  height: 96px;
  font-size: 2.2rem;
  border: 3px solid var(--accent);
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.5);
  background: var(--panel-2);
}
.profile-actions { display: flex; align-items: center; gap: 0.6rem; margin-left: auto; padding-bottom: 4px; flex-wrap: wrap; }
.profile-actions .btn, .profile-actions form { white-space: nowrap; flex: none; }
.profile-edit-btn {
  background: color-mix(in oklch, var(--accent) 14%, transparent);
  border: 1px solid color-mix(in oklch, var(--accent) 50%, transparent);
  color: var(--accent);
}
.profile-edit-btn:hover { background: var(--accent); color: var(--ink); }
.profile-pending {
  font-family: var(--mono);
  font-size: 0.68rem;
  color: var(--sage);
  border: 1px dashed var(--sage);
  border-radius: var(--radius-pill);
  padding: 0.5rem 0.9rem;
}
.profile-more { position: relative; }
.profile-more-btn {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: var(--panel-2);
  color: var(--bone);
  cursor: pointer;
  font-size: 1.1rem;
}
.profile-more-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 10;
  background: var(--ink);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-brutal);
  min-width: 170px;
  padding: 0.3rem;
  display: flex;
  flex-direction: column;
}
.profile-more-item {
  background: none;
  border: none;
  text-align: left;
  padding: 0.6rem 0.7rem;
  min-height: 44px;
  color: var(--bone);
  font-size: 0.82rem;
  cursor: pointer;
  border-radius: var(--radius);
}
.profile-more-item:hover { background: var(--panel-2); }
.profile-substrip { padding: 0 1.3rem; margin: 0.4rem 0 0; font-size: 0.78rem; }
.profile-blocked-strip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
  margin: 0.7rem 1.3rem 0;
  padding: 0.7rem 0.9rem;
  background: rgba(240, 135, 106, 0.1);
  border: 1px solid rgba(240, 135, 106, 0.35);
  border-radius: var(--radius);
  font-size: 0.82rem;
  flex-wrap: wrap;
}
.profile-safety-line { padding: 0 1.3rem; margin: 0.6rem 0 0; font-size: 0.8rem; }
.profile-safety-line .linklike { font-size: inherit; }

.profile-stats-row {
  display: flex;
  align-items: baseline;
  gap: 1.1rem;
  flex-wrap: wrap;
  padding: 0.9rem 1.3rem;
  margin-top: 0.6rem;
  border-top: 1px solid var(--border);
}
.profile-stat {
  display: flex;
  align-items: baseline;
  gap: 0.35rem;
  color: inherit;
  text-decoration: none;
}
.profile-stat strong { font-family: var(--display); font-size: 1.15rem; color: var(--accent); }
.profile-stat .mono { color: var(--muted); font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.03em; }
a.profile-stat:hover strong { text-decoration: underline; }
.profile-meta-line { display: flex; gap: 0.9rem; margin-left: auto; flex-wrap: wrap; }
.profile-meta-line .mono { color: var(--muted); font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.03em; }
.profile-vibe { color: var(--accent); }
.profile-bio { padding: 0 1.3rem 1.2rem; max-width: 62ch; font-size: 0.86rem; line-height: 1.6; }

/* --- Edit panel --- */
.profile-edit-panel {
  border-radius: var(--radius-lg);
  border: 1px solid color-mix(in oklch, var(--accent) 35%, transparent);
  box-shadow: var(--shadow-brutal-lg);
  overflow: hidden;
}
.profile-edit-head {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  padding: 1rem 1.3rem;
  background: rgba(7, 10, 8, 0.35);
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}
.profile-edit-head h2 { margin: 0; flex: 1; min-width: 0; }
.profile-edit-head-actions { display: flex; gap: 0.6rem; flex: none; }
.profile-edit-panel form { padding: 0 1.3rem; }
.avatar-edit-row .avatar-edit-inner { display: flex; align-items: center; gap: 0.9rem; }
.avatar-edit-preview {
  width: 54px;
  height: 54px;
  border: 2px solid var(--accent);
  background-size: cover;
  background-position: center;
  flex: none;
}
.vibe-chips { display: flex; flex-wrap: wrap; gap: 0.5rem; }
/* top/left pin this to the container's corner rather than leaving it at
   the browser's default "static position", the same fix as .star-input's
   radios below and for the same reason: an unpinned hidden input can end up
   positioned somewhere no longer inside the visible row once its siblings
   wrap, and focusing it then scrolls an overflow: hidden ancestor sideways
   to reveal it, dragging real content out of view with it. */
.vibe-chips input[type="radio"] { position: absolute; top: 0; left: 0; opacity: 0; width: 1px; height: 1px; }
.vibe-chips label {
  min-height: 38px;
  display: inline-flex;
  align-items: center;
  padding: 0 0.9rem;
  border-radius: var(--radius-pill);
  background: rgba(46, 95, 64, 0.7);
  border: 1px solid var(--border);
  font-size: 0.78rem;
  cursor: pointer;
}
.vibe-chips input:checked + label,
.vibe-chips label:has(input:checked) {
  border-color: var(--accent);
  color: var(--accent);
}
.vibe-chips input:focus-visible + label { outline: 2px solid var(--amber); outline-offset: 2px; }
.profile-edit-note { font-size: 0.78rem; margin: 0.8rem 0; }
.profile-edit-panel .customize-actions { padding-bottom: 1.2rem; }

/* --- Spot card (profile grids) --- */
.spot-card {
  display: block;
  color: inherit;
  text-decoration: none;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  transition: transform 160ms ease, border-color 160ms ease;
}
.spot-card:hover { transform: translateY(-3px); border-color: color-mix(in oklch, var(--accent) 50%, transparent); }
.spot-card-cover { display: block; height: 104px; background: rgba(7, 10, 8, 0.4); }
.spot-card-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.spot-card-cover-empty {
  display: block;
  height: 100%;
  background: repeating-linear-gradient(135deg, rgba(183, 223, 178, 0.07) 0 10px, transparent 10px 20px);
}
.spot-card-body { display: block; padding: 0.7rem 0.75rem; }
.spot-card-title {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-family: var(--display);
  font-size: 0.8rem;
  line-height: 1.25;
  margin-bottom: 0.4rem;
}
.spot-card-chips { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-bottom: 0.4rem; }
.spot-card-meta { color: var(--muted); font-size: 0.58rem; text-transform: uppercase; letter-spacing: 0.02em; }
.profile-review-grid { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.profile-review-cover { width: 52px; height: 52px; flex: none; border-radius: 10px; }
.profile-review-card {
  display: flex;
  gap: 0.7rem;
  align-items: center;
  padding: 0.7rem;
  color: inherit;
  text-decoration: none;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  transition: transform 160ms ease, border-color 160ms ease;
}
.profile-review-card:hover { transform: translateY(-3px); border-color: color-mix(in oklch, var(--accent) 50%, transparent); }
.profile-review-card-body { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.profile-hero .avatar-big {
  width: 92px;
  height: 92px;
  font-size: 2.1rem;
  border: 3px solid var(--accent);
  box-shadow: var(--shadow-brutal);
  background: var(--panel-2);
}
.profile-id-text { padding-bottom: 0.35rem; flex: 1; min-width: 0; }
.profile-id-text h1 { margin: 0; font-size: 1.6rem; }
.profile-id .tagline { margin: 0.15rem 0 0; color: var(--accent); font-weight: 600; }
.profile-id .chip-match { align-self: center; margin-bottom: 0.4rem; }
.profile-meta { display: flex; flex-wrap: wrap; gap: 0.45rem; padding: 0.85rem 1.3rem 1.1rem; }
.vibe-badge { border-color: var(--accent); color: var(--accent); }

.customize-head { margin-bottom: 1.1rem; }
.customize-head .eyebrow {
  display: block;
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--amber);
  margin-bottom: 0.3rem;
}
.customize-head h1, .customize-head h2 { margin: 0; }
.customize-head .muted { font-size: 0.8rem; margin-top: 0.4rem; }
.field { margin: 0.9rem 0; }
.checkbox-field { display: flex; align-items: center; gap: 0.55rem; }
.checkbox-field input[type="checkbox"] { width: 1.1rem; height: 1.1rem; flex: none; }
.checkbox-field label { margin: 0; }
/* A list of "name, pick some" checkboxes: which friends were somewhere, which
   friends a private spot is shared with, which friends a post goes to.
   Written to work whether or not the checkboxes are Django's own widget
   markup, which wraps each one in its own div, or a plain loop that does not. */
.friend-checks { margin-top: 0.3rem; }
.friend-checks label { display: flex; align-items: center; gap: 0.5rem; font-weight: 400; margin: 0.3rem 0; }
.friend-checks input[type="checkbox"] { width: 1.1rem; height: 1.1rem; flex: none; }
.banner-field {
  position: relative;
  height: 200px;
  margin-bottom: 0.4rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background:
    repeating-linear-gradient(0deg, rgba(0,0,0,0.18) 0 1px, transparent 1px 4px),
    linear-gradient(135deg, color-mix(in srgb, var(--accent) 38%, #0b100e), #0b100e 78%);
  background-size: cover;
  background-position: center;
}
/* Positioned individually, not by absolutely placing .file-picker itself:
   that wrapper still contains .file-picker-current, and an absolute child
   of an absolute parent measures top/left against that parent, not against
   .banner-field. Two nested position: absolute elements is how "top: 12px;
   left: 12px" ended up next to the button instead of the opposite corner. */
.banner-field .file-picker-btn { position: absolute; top: 12px; right: 12px; }
.banner-field .file-picker-name { display: none; }
/* The plain "Currently: banners/2026/xyz.jpg" link is redundant once the
   image itself is the background here, and unreadable in dark text over an
   arbitrary photo besides. The Clear control still has to survive, as a
   small dark pill mirroring Replace banner on the opposite corner, since it
   is the only way to remove a banner without replacing it with another. */
.banner-field .file-picker-current {
  position: absolute;
  top: 12px;
  left: 12px;
  right: auto;
  margin: 0;
  flex-basis: auto;
  z-index: 1;
}
.banner-field .file-picker-current > a { display: none; }
.banner-field .file-picker-clear {
  padding: 0.5rem 0.75rem;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  border-radius: var(--radius-pill);
  color: var(--bone);
  font-size: 0.78rem;
}
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 1rem; }
.field-row .field { min-width: 0; }
.customize-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-top: 1.3rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}
.customize-actions .btn { flex: none; }
@media (max-width: 560px) {
  .field-row { grid-template-columns: 1fr; }
}

/* Accent swatch picker */
.swatch-row { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.swatch { cursor: pointer; }
.swatch input { position: absolute; opacity: 0; width: 0; height: 0; }
.swatch span {
  display: block;
  width: 34px;
  height: 34px;
  background: var(--sw);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-brutal);
  transition: transform 100ms var(--ease-out);
}
.swatch:hover span { transform: translateY(-1px); }
.swatch input:checked + span { border-color: var(--bone); outline: 2px solid var(--sw); outline-offset: 2px; }
.swatch input:focus-visible + span { outline: 2px solid var(--bone); outline-offset: 2px; }

.deck-card { max-width: 460px; margin: 1.2rem auto; text-align: center; padding: 2.2rem 1.8rem; box-shadow: var(--shadow-brutal-lg); }
.deck-card h2 { margin: 0.7rem 0 0.1rem; font-size: 1.3rem; }
.deck-card .tagline { color: var(--accent); font-weight: 600; margin: 0.1rem 0 0.5rem; }
.deck-meta { display: flex; justify-content: center; flex-wrap: wrap; gap: 0.4rem; margin: 0.5rem 0 0.2rem; }
.deck-bio { color: var(--bone); margin: 0.7rem 0 0.3rem; }
.deck-actions { display: flex; gap: 0.8rem; justify-content: center; margin-top: 1.4rem; }
.deck-actions form { margin: 0; flex: 1; max-width: 180px; }
.deck-actions .btn { width: 100%; padding: 0.7rem 1rem; }

.person { display: flex; align-items: center; gap: 0.75rem; }
.person .btn { margin-left: auto; }

/* --- Empty states --- */
.empty {
  border: 2px dashed var(--border);
  padding: 2rem 1.4rem;
  text-align: center;
  color: var(--muted);
  margin: 0.9rem 0;
  background: var(--panel);
  border-radius: var(--radius-lg);
}
.empty .big { font-size: 1.6rem; display: block; margin-bottom: 0.35rem; }
.empty .btn { margin-top: 0.9rem; }

/* --- Misc --- */
details { margin: 1rem 0; }
details summary {
  cursor: pointer;
  color: var(--muted);
  font-family: var(--mono);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  padding: 0.3rem 0;
}
details summary:hover { color: var(--amber); }
details[open] summary { color: var(--bone); margin-bottom: 0.4rem; }

.pagination { display: flex; gap: 0.8rem; justify-content: center; margin: 1.4rem 0; }
.pagination a {
  padding: 0.4rem 1rem;
  border: 1px solid var(--border);
  color: var(--bone);
  background: var(--panel);
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 700;
  border-radius: var(--radius-pill);
}
.pagination a:hover { color: var(--ink); background: var(--amber); border-color: var(--border-strong); text-decoration: none; }

@media (pointer: coarse), (max-width: 640px) {
  /* Touch targets: aim for the 44px minimum so controls are comfortable with
     a thumb rather than needing a precise tap. */
  .nav-link, .linklike { min-height: 44px; padding-top: 0.7rem; padding-bottom: 0.7rem; }
  .btn { min-height: 44px; }
  .btn-small { min-height: 38px; }
  .map-pill { min-height: 44px; }
  .spot-item { min-height: 52px; }
  .rail-btn { width: 52px; height: 52px; }
  .swatch span { width: 44px; height: 44px; }
  .filter-row select { min-height: 44px; }
  .quick-cat { min-height: 44px; padding: 0.6rem 0.9rem; }
  /* Controls that were below the 44px minimum design.md calls
     non-negotiable. .btn-small in particular is Accept, Decline, Delete spot
     and Remove friend. */
  .btn-small { min-height: 44px; }
  .spot-card-close, .route-clear, .notif-dismiss { min-width: 44px; min-height: 44px; }
  .subtab, .geo-result, .icon-btn, .like-btn, .action-link,
  details summary { min-height: 44px; display: flex; align-items: center; }
}

@media (max-width: 640px) {
  .nav { padding: 0.5rem 0.8rem; }
  .brand-sub { display: none; }
  h1 { font-size: 1.6rem; }
  .container { padding: 1.1rem 1rem; }

  /* The nav drawer. Above this width .nav-burger stays display: none from
     its base rule and .nav-links is the plain row it always was; only here
     do they swap roles. This used to instead try to fit every nav-link on
     one row as icon-only, which still did not fit and forced the whole
     page 35+px wider than the viewport, so every other centred, calc()-based
     element on the page (the map's pills, the Lounge's compose button) was
     laid out against the wrong width and sat partly off-screen. */
  .nav-burger { display: flex; width: 44px; height: 44px; }
  .nav-links {
    display: none;
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0.2rem;
    width: min(260px, calc(100vw - 1.6rem));
    max-height: calc(100vh - 76px);
    overflow-y: auto;
    padding: 0.5rem;
    background: var(--panel-2);
    backdrop-filter: blur(18px);
    -webkit-backdrop-filter: blur(18px);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-brutal-lg);
    z-index: 60;
  }
  .nav-drawer[open] .nav-links { display: flex; }
  .nav-links .nav-link,
  .nav-links .linklike,
  .nav-links .btn-small {
    width: 100%;
    justify-content: flex-start;
  }
  .nav-links .nav-link span, .nav-links .linklike span { display: inline; }
  .nav-links .install-btn { border-top: 1px solid var(--border); border-radius: 0; margin-top: 0.3rem; padding-top: 0.7rem; }

  /* Profile hero: avatar (96px, fixed) plus Edit profile / Message (fixed,
     pushed right by margin-left: auto) already claim most of a phone's
     width, and .profile-id-text's min-width: 0 means the row never had to
     wrap to fit the username, it just squeezed that flex item down to a
     sliver and let the text run over the button instead. flex-basis: 100%
     forces the actions onto their own line so the username gets the room
     the first line actually has. */
  .profile-actions { flex-basis: 100%; margin-left: 0; justify-content: flex-start; }

  /* Same shape of bug, the edit panel's avatar row: preview, file input
     button and help text in one unwrapping flex row was wider than a phone
     long before the help text had anywhere to go. */
  .avatar-edit-row .avatar-edit-inner { flex-wrap: wrap; }
  .avatar-edit-row .helptext { flex-basis: 100%; }
}

/* --- Motion: htmx swaps and page transitions --- */

/* htmx fades a fragment out before swapping in the replacement. The deck card
   uses this so a pass or like glides instead of snapping. */
.deck-swap.htmx-swapping { opacity: 0; transform: translateY(6px) scale(0.99); }
.deck-swap { transition: opacity 180ms var(--ease-out), transform 180ms var(--ease-out); }

/* A liked heart pops once when its fragment is swapped in. */
.like-form.htmx-added .like-btn.liked svg { animation: like-pop 320ms ease; }
@keyframes like-pop {
  0% { transform: scale(1); }
  45% { transform: scale(1.35); }
  100% { transform: scale(1.12); }
}

/* Any element htmx is waiting on dims slightly, so a slow network still
   feels answered rather than ignored. */
.htmx-request .btn, .btn.htmx-request { opacity: 0.65; }

/* Native cross-document view transitions: pages cross-fade instead of
   hard-cutting. Browsers without support simply navigate as before. */
@view-transition { navigation: auto; }

/* Press feedback. A phone has no hover, so until now a tap was answered with
   nothing at all: ten elements moved on :hover and exactly one moved on
   :active. --press is the single knob for all of it. */
.btn:active,
.card-link:active,
.chip:active,
.nav-link:active,
.subtab:active,
.icon-btn:active,
.linklike:active,
.action-link:active,
.rail-btn:active,
.reel-arrow:active,
.reel-compose:active,
.reel-tab:active,
.compose-close:active,
.map-pill:active,
.list-toggle:active,
.spot-card-close:active,
.swatch:active span {
  transform: scale(var(--press));
  transition-duration: var(--dur-fast);
}
/* .map-pill is centred by a translate, so a bare scale would jump it left. */
.map-pill:active { transform: translateX(-50%) scale(var(--press)); }
.btn:active { box-shadow: 0 2px 10px rgba(147, 224, 138,0.2); }

/* A touch screen fires a hover on tap and then leaves the element stuck in
   that state until you tap somewhere else, so on a phone every card you
   touched stayed lifted. Movement on hover is a desktop affordance. */
@media (hover: none), (pointer: coarse) {
  .btn:hover, .card-link:hover, .notif:hover, .swatch:hover span,
  .photo-grid a:hover img, .map-pill:hover, .list-toggle:hover,
  .spot-card-close:hover, .rail-btn:hover, .reel-arrow:hover,
  .reel-compose:hover, .compose-close:hover, .profile-stat:hover {
    transform: none;
  }
}

/* Reduced motion means gentler, not dead. The old rule was animation and
   transition none on every element, which also deleted the htmx swap feedback
   and the deck glide, so the app stopped answering clicks at all. Movement
   goes; opacity, colour and shadow stay, because those are the answer. */
@media (prefers-reduced-motion: reduce) {
  :root { --press: 1; }
  *, *::before, *::after {
    animation: none !important;
    transition-property: opacity, background-color, border-color, color, box-shadow, filter !important;
    transition-duration: 140ms !important;
  }
  /* The haze drifts forever and is pure decoration. */
  body::before { animation: none !important; }
  /* A cross-document view transition is neither an animation nor a
     transition, so the old block never touched it: the one whole-page
     movement in the app was the only one that survived it. */
  @view-transition { navigation: none; }
}

/* --- Notifications --- */
.nav-bell { position: relative; }
.bell-dot {
  position: absolute;
  top: 2px;
  right: 4px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: var(--radius-pill);
  background: var(--amber);
  color: #06180d;
  font-size: 0.62rem;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
  box-shadow: 0 0 10px rgba(147, 224, 138,0.45);
}
/* --- Alerts page --- */
.notif-page {
  position: relative;
  /* overflow: clip, not hidden: hidden still makes this a "scroll
     container" a browser can programmatically scroll to bring a focused
     descendant into view, which would reveal .notif-aurora's -20% inset
     bleed and drag real content sideways. clip has no scroll semantics at
     all, so there is nothing for a focus event to scroll. */
  overflow: clip;
  max-width: 660px;
  margin: 0 auto;
  background: var(--background);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-brutal-lg);
  padding: 1.5rem;
}
.notif-aurora {
  position: absolute;
  inset: -20%;
  pointer-events: none;
  filter: blur(38px);
  opacity: 0.4;
  background:
    radial-gradient(52% 46% at 6% 2%, rgba(183, 223, 178, 0.5), transparent 70%),
    radial-gradient(44% 40% at 94% 26%, rgba(203, 194, 238, 0.5), transparent 70%);
  animation: haze-drift 44s var(--ease-in-out) infinite alternate;
}
.notif-page-inner { position: relative; }

.notif-head-actions { display: flex; gap: 0.6rem; flex: none; }
.notif-unread-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  color: var(--amber);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: 0.3rem;
}
.notif-pulse {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--amber);
  animation: notif-pulse 2.4s ease-in-out infinite;
}
@keyframes notif-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }
.notif-mark-read {
  background: rgba(147, 224, 138, 0.12);
  border: 1px solid rgba(147, 224, 138, 0.5);
  color: var(--amber);
}
.notif-mark-read:hover { background: var(--amber); color: var(--ink); }
.notif-clear:hover { color: var(--danger); border-color: var(--danger); background: var(--panel); }

/* Kind filter chips: shown only past a handful of alerts, per notifications.js. */
.notif-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding-bottom: 0.9rem;
  margin-bottom: 0.4rem;
  border-bottom: 1px solid var(--border);
}
.notif-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  min-height: 34px;
  padding: 0 0.7rem;
  border-radius: var(--radius-pill);
  background: var(--panel);
  border: 1px solid var(--border);
  color: var(--bone);
  font-family: var(--mono);
  font-size: 0.64rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  cursor: pointer;
}
.notif-chip.is-active { background: var(--amber); color: var(--ink); border-color: var(--amber); }
.notif-chip.is-active .notif-chip-dot { display: none; }
.notif-chip-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }

.notif-day-group + .notif-day-group { margin-top: 1.4rem; }
.notif-day-label {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  color: var(--sage);
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin: 0 0 0.5rem;
}
.notif-day-label::after { content: ""; flex: 1; height: 1px; background: var(--border); }

.notif-list { display: flex; flex-direction: column; gap: 0.5rem; }
.notif {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 0.8rem 0.9rem;
  background: rgba(46, 95, 64, 0.45);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  color: var(--bone);
  font-weight: 400;
  transition: transform 140ms var(--ease-out), border-color 140ms var(--ease-out);
}
.notif:hover { transform: translateX(3px); border-color: rgba(183, 223, 178, 0.45); }
/* Unread ones carry a filled panel until dismissed or marked read, not a
   left stripe: a stripe reads as a category, and this already has one. */
.notif.is-new { background: var(--panel-2); border-color: rgba(147, 224, 138, 0.4); }

.notif-avatar { position: relative; flex: none; }
.notif-avatar-img { width: 38px; height: 38px; }
.notif-avatar-system {
  display: block;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: rgba(183, 223, 178, 0.14);
}
.notif-kind-dot {
  position: absolute;
  bottom: -2px;
  right: -2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 2px solid var(--background);
}
.notif-kind-dot.kind-match { background: var(--amber); }
.notif-kind-dot.kind-message, .notif-kind-dot.kind-vote { background: var(--lilac); }
.notif-kind-dot.kind-like, .notif-kind-dot.kind-comment, .notif-kind-dot.kind-review { background: var(--sage); }

.notif-main { display: flex; flex-direction: column; gap: 0.2rem; min-width: 0; flex: 1; }
.notif-link {
  color: var(--bone);
  font-size: 0.84rem;
  line-height: 1.4;
  overflow-wrap: anywhere;
}
.notif-link:hover { text-decoration: none; }
/* Stretched link: the row has one real <a> (this one) plus a real <button>
   (dismiss), which cannot nest inside it, so the link is extended over the
   whole row with ::after instead. The button stays position: relative so it
   sits above the overlay and keeps taking its own clicks. */
.notif-link::after { content: ""; position: absolute; inset: 0; }
.notif-meta { display: flex; align-items: center; gap: 0.6rem; }
.notif-kind {
  font-family: var(--mono);
  font-size: 0.6rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.notif-kind.kind-match { color: var(--amber); }
.notif-kind.kind-message, .notif-kind.kind-vote { color: var(--lilac); }
.notif-kind.kind-like, .notif-kind.kind-comment, .notif-kind.kind-review { color: var(--sage); }
.notif-when { color: var(--muted); font-size: 0.62rem; }

.notif-dismiss-form { position: relative; flex: none; margin: 0; }
.notif-dismiss {
  width: 30px;
  height: 30px;
  border-radius: var(--radius);
  background: transparent;
  border: 1px solid var(--border-strong);
  color: var(--muted);
  cursor: pointer;
}
.notif-dismiss:hover { color: var(--bone); }

/* Three fading dots from one element: a centre dot plus two box-shadow
   copies at falling opacity, rather than three elements for one motif. */
.notif-empty-motif {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--sage);
  box-shadow: -14px 0 0 rgba(183, 223, 178, 0.5), 14px 0 0 rgba(183, 223, 178, 0.2);
  margin-bottom: 0.9rem;
}
.notif-page .empty { border: 1px dashed rgba(183, 223, 178, 0.35); }
.notif-empty-actions { display: flex; justify-content: center; gap: 0.6rem; margin-top: 0.9rem; }

/* --- Installable app: offline card and the install invitation --- */
.offline-card { max-width: 30rem; margin: 4rem auto; text-align: center; }
.offline-mark {
  width: 64px;
  height: 64px;
  margin: 0 auto 1rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--ember);
  background: var(--sage-soft);
  border: 1px solid rgba(183, 223, 178,0.22);
}
.offline-mark svg { width: 30px; height: 30px; }
/* Quiet in the nav until the browser says the app is installable. It is a
   button, not a link, so it needs the surrounding nav styling reset. */
.install-btn { background: none; border: none; font: inherit; cursor: pointer; }
.install-btn[hidden] { display: none; }

/* --- Moderation queue --- */
/* A destructive button reads as destructive before it is pressed, not after. */
.btn-danger {
  color: var(--danger);
  border: 1px solid rgba(240, 135, 106, 0.5);
  background: rgba(255, 122, 92, 0.08);
  box-shadow: none;
}
.btn-danger:hover {
  color: #0b0f0c;
  background: var(--danger);
  border-color: var(--danger);
}
.mod-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr));
  gap: 1rem;
  margin: 1rem 0 2rem;
}
.mod-card {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  padding: 0.8rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
}
.mod-shot {
  display: block;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: var(--radius);
  background: rgba(0, 0, 0, 0.35);
}
.mod-shot img { width: 100%; height: 100%; object-fit: cover; }
.mod-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.78rem;
}
.mod-caption { color: var(--muted); font-size: 0.85rem; margin: 0; }
.mod-actions { display: flex; gap: 0.5rem; margin-top: auto; }
.mod-actions form { margin: 0; flex: 1; }
/* Approve and reject sit next to each other, so both need a thumb-sized
   target: a mis-tap here deletes somebody's photo. */
.mod-actions .btn { width: 100%; justify-content: center; min-height: 44px; }
.mod-reports { list-style: none; padding: 0; margin: 0 0 2rem; }
.mod-reports p { margin: 0.4rem 0 0; }

/* A photo of your own that is still waiting on a moderator. */
.photo-cell { position: relative; display: block; }
/* "In review". Two forms: a chip that sits in a normal row, and an overlay
   pinned to the corner of a photo. The overlay used to be the only form, so
   using it anywhere else dropped it out of the flow. */
.review-tag {
  padding: 0.15rem 0.5rem;
  border-radius: var(--radius-pill);
  background: var(--ink);
  border: 1px solid rgba(147, 224, 138,0.5);
  color: var(--amber);
  font-size: 0.68rem;
  letter-spacing: 0.02em;
}
.photo-cell .review-tag {
  position: absolute;
  left: 0.5rem;
  bottom: 0.5rem;
  background: rgba(6, 12, 9, 0.82);
}
.review-tag-float { position: absolute; left: 0.8rem; top: 0.8rem; bottom: auto; z-index: 3; }

/* An empty list is a moment to offer the way out, not just to state the
   obvious. The button clears every filter at once. */
.list-empty {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.7rem;
  padding: 0.9rem 0 0.2rem;
}
.list-empty p { margin: 0; font-size: 0.85rem; }

/* A photo the automatic check flagged. Amber, not red: it is a reason to
   look, not a verdict. The moderator still decides. */
.mod-flag {
  margin: 0;
  padding: 0.35rem 0.55rem;
  border-radius: var(--radius);
  background: var(--amber-soft);
  border: 1px solid rgba(147, 224, 138,0.3);
  color: var(--amber);
  font-family: var(--mono);
  font-size: 0.64rem;
}

/* Closing a report. Sits under the reason, not beside it, so a long reason
   never squeezes the buttons. */
.report-actions { display: flex; gap: 0.5rem; margin-top: 0.7rem; }
.report-actions form { margin: 0; }

/* Meeting-safety card. Sage rather than red: this is guidance from the app,
   not an alarm. An interface that shouts gets scrolled past. */
.safety-card {
  margin: 1rem 0 1.5rem;
  padding: 1rem 1.2rem;
  background: var(--sage-soft);
  border: 1px solid rgba(183, 223, 178,0.28);
  border-radius: var(--radius-lg);
}
.safety-card h2 {
  margin: 0 0 0.6rem;
  font-size: 0.98rem;
}
.safety-card ul { margin: 0; padding-left: 1.1rem; }
.safety-card li { margin-bottom: 0.4rem; font-size: 0.9rem; }
.safety-card li strong { color: var(--sage); }
.safety-card p { margin: 0.7rem 0 0; font-size: 0.82rem; }
.safety-line { margin-top: 0.8rem; font-size: 0.82rem; }

/* --- People and friend requests --- */
/* Replaced the swipe deck. A list you can scroll and ignore is less
   pressuring than a card that demands a decision before showing the next. */
.person-card { display: flex; flex-direction: column; gap: 0.6rem; }
.person-head {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  color: var(--bone);
}
.person-head:hover { text-decoration: none; color: var(--amber); }
.person-head span { display: flex; flex-direction: column; min-width: 0; }
.person-head .muted { font-size: 0.78rem; }
.person-meta { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.person-stats { font-size: 0.76rem; margin: 0; }
.person-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: auto; }
.person-actions form { margin: 0; }

/* --- Analytics dashboard --- */
.stat-row { display: flex; flex-wrap: wrap; gap: 1rem; }
.stat-card {
  flex: 1 1 8rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  text-align: center;
}
.stat-num {
  font-family: var(--mono);
  font-size: 1.8rem;
  font-weight: 700;
  color: var(--sage);
}
.bar-list {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 6rem;
}
.bar-list-hourly { gap: 3px; }
.bar-row { flex: 1 1 0; height: 100%; display: flex; align-items: flex-end; }
.bar-track {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: flex-end;
  border-radius: 2px;
  overflow: hidden;
  background: var(--panel-2);
}
.bar-fill {
  width: 100%;
  min-height: 2px;
  background: var(--sage);
  border-radius: 2px 2px 0 0;
}
.bar-list-labels {
  display: flex;
  justify-content: space-between;
  margin-top: 0.35rem;
  color: var(--muted);
  font-family: var(--mono);
  font-size: 0.68rem;
}
.country-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.country-list li { display: grid; grid-template-columns: 5rem 1fr 3rem; align-items: center; gap: 0.6rem; }
.country-name { font-family: var(--mono); font-size: 0.82rem; }
.bar-track-h { height: 0.55rem; }
.bar-fill-h {
  height: 100%;
  background: var(--amber);
  border-radius: var(--radius-pill);
}
.country-total {
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--muted);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* --- Spot page --- */
.spot-shell {
  position: relative;
  /* overflow: clip, not hidden: hidden still makes this a "scroll
     container" a browser can programmatically scroll to bring a focused
     descendant into view (the star rating radios, a hidden file input),
     which reveals .spot-aurora's -20% inset bleed for a moment and drags
     real content sideways with it. contain: paint alone did not stop this;
     clip has no scroll semantics at all, so there is nothing to scroll. */
  overflow: clip;
  border-radius: var(--radius-lg);
  background: var(--background);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-brutal-lg);
  padding: 1.4rem;
}
.spot-aurora {
  position: absolute;
  inset: -20%;
  pointer-events: none;
  filter: blur(38px);
  opacity: 0.4;
  background:
    radial-gradient(50% 40% at 6% 4%, rgba(183, 223, 178, 0.4), transparent 70%),
    radial-gradient(46% 38% at 92% 30%, rgba(203, 194, 238, 0.28), transparent 70%);
  animation: haze-drift 40s var(--ease-in-out) infinite alternate;
}
.spot-shell > *:not(.spot-aurora) { position: relative; }

.spot-hero {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: 1.4rem;
  align-items: start;
}
/* The spec's own "one column under 860px, photo first" was never actually
   wired up: this grid had no responsive override at all, so a phone got the
   full desktop 1.15fr/1fr split squeezed into its width, the photo reduced
   to a sliver and the title, rating and buttons crushed into what was left. */
@media (max-width: 860px) { .spot-hero { grid-template-columns: 1fr; } }
.spot-hero-photo {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--border);
}
.spot-hero-photo .photo-cell { display: block; }
.spot-hero-photo img { width: 100%; height: 320px; object-fit: cover; display: block; }
.spot-hero-photo-empty {
  height: 320px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  background: repeating-linear-gradient(135deg, rgba(183, 223, 178, 0.06) 0 10px, transparent 10px 20px);
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.spot-hero-chip {
  /* No color or border-color here: chip-{{ category }} (styles.css, "One
     value per category") supplies both, same as every other category chip
     in the app. Also gives .dot something to inherit via currentColor,
     which it had nothing to inherit before this had color: var(--bone). */
  position: absolute;
  top: 10px;
  left: 10px;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  background: rgba(7, 10, 8, 0.6);
  border-width: 1px;
  border-style: solid;
  padding: 0.28rem 0.65rem;
  font-family: var(--mono);
  font-size: 0.58rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  border-radius: var(--radius-pill);
}
.spot-hero-chip .dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.spot-hero-count {
  position: absolute;
  bottom: 10px;
  right: 10px;
  background: rgba(7, 10, 8, 0.6);
  color: var(--bone);
  padding: 0.22rem 0.55rem;
  font-size: 0.58rem;
  border-radius: var(--radius-pill);
}
.spot-hero-details h1 { text-wrap: pretty; margin-bottom: 0.5rem; }
.spot-hero-rating { display: flex; align-items: center; gap: 0.5rem; margin: 0 0 0.7rem; }
.spot-hero-rating .stars-fill { color: var(--gold); }
.spot-hero-desc { max-width: 46ch; margin: 0 0 1rem; }
.spot-owner-strip {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.6rem 0.8rem;
  margin-bottom: 0.9rem;
}
.spot-owner-date { display: block; }
.spot-hero-actions { display: flex; gap: 0.6rem; flex-wrap: wrap; margin-bottom: 0.9rem; }
.spot-owner-row {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding-top: 0.8rem;
  border-top: 1px solid var(--border);
  flex-wrap: wrap;
}
.spot-owner-edit { color: var(--amber); font-size: 0.78rem; }
.spot-owner-delete {
  background: none;
  border: none;
  padding: 0;
  color: var(--muted);
  font-size: 0.78rem;
  cursor: pointer;
  min-height: 44px;
}
.spot-owner-delete:hover { color: var(--danger); }
.spot-owner-coords { margin-left: auto; font-variant-numeric: tabular-nums; }

.notice-danger { border-left-color: var(--danger); background: rgba(240, 135, 106, 0.08); }
.notice-lilac { border-left-color: var(--lilac); background: rgba(203, 194, 238, 0.08); }

/* --- Composer --- */
.composer {
  background: rgba(7, 10, 8, 0.4);
  border: 1px solid rgba(90, 138, 92, 0.3);
  border-radius: var(--radius-lg);
  padding: 1rem;
  margin: 1.2rem 0;
}
.composer-switch {
  display: none;
  padding: 4px;
  gap: 4px;
  border-radius: var(--radius-pill);
  background: rgba(7, 10, 8, 0.5);
  border: 1px solid var(--border);
  margin-bottom: 0.9rem;
  width: fit-content;
}
.composer.js-ready .composer-switch { display: inline-flex; }
.composer-tab {
  background: none;
  border: none;
  padding: 0.5rem 0.9rem;
  border-radius: var(--radius-pill);
  color: var(--muted);
  font-family: var(--body-font);
  font-weight: 600;
  font-size: 0.72rem;
  cursor: pointer;
  min-height: 32px;
}
.composer-tab.is-active { background: var(--amber); color: var(--ink); font-weight: 700; }
.composer-note { font-size: 0.72rem; }
.composer-actions { display: flex; align-items: center; justify-content: space-between; gap: 0.8rem; margin-top: 0.7rem; flex-wrap: wrap; }
.composer-visit-cols { display: grid; grid-template-columns: 1.4fr 1fr; gap: 1rem; }
.composer-visit-row { display: flex; align-items: center; gap: 0.7rem; margin-top: 0.5rem; flex-wrap: wrap; }
.composer-visit-save { margin-left: auto; }
.composer-signedout {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.9rem 1.1rem;
  margin: 1.2rem 0;
  flex-wrap: wrap;
}
@media (max-width: 720px) { .composer-visit-cols { grid-template-columns: 1fr; } }

/* flex-wrap: wrap, not nowrap: five 44px star buttons plus the readout text
   do not fit one row under about 360px. Unwrapped, the readout shrank to a
   sliver and wrapped one word per line instead, which was still wider than
   the row had space for, and the overflow made .spot-shell itself
   horizontally scrollable, no page-level overflow, so nothing upstream
   caught it, but focusing a star scrolled the whole shell sideways to bring
   its (invisible, 1x1px) radio input into view. */
.star-input { border: none; padding: 0; margin: 0 0 0.7rem; display: flex; align-items: center; gap: 0.3rem; flex-wrap: wrap; }
.star-input-readout { flex-basis: 100%; }
.star-input input[type="radio"] { position: absolute; top: 0; left: 0; opacity: 0; width: 1px; height: 1px; }
.star-input-star {
  font-size: 1.6rem;
  line-height: 1;
  color: rgba(217, 184, 119, 0.28);
  cursor: pointer;
  min-width: 44px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.star-input-star.is-filled { color: var(--gold); }
.star-input input:focus-visible + .star-input-star { outline: 2px solid var(--amber); outline-offset: 2px; }
.star-input-readout { color: var(--muted); margin-left: 0.4rem; }

/* --- Two columns below the composer --- */
.spot-cols { display: grid; grid-template-columns: 1.55fr 1fr; gap: 1.5rem; align-items: start; margin-top: 1.4rem; }
@media (max-width: 900px) { .spot-cols { grid-template-columns: 1fr; } }

.section-head { display: flex; align-items: baseline; gap: 0.6rem; margin: 1.3rem 0 0.8rem; }
.section-head h2 { margin: 0; font-size: 1.2rem; }
.section-head-rule { flex: 1; height: 1px; background: var(--border); }

.visit-card, .review-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.9rem;
  margin-bottom: 0.65rem;
  position: relative;
  transition: border-color 140ms ease;
}
.visit-card:hover, .review-card:hover { border-color: rgba(183, 223, 178, 0.4); }
.visit-card-head, .review-card-head { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.5rem; }
.visit-card-who, .review-card-who { display: flex; flex-direction: column; gap: 0.15rem; }
.visit-card-date, .review-card-date { font-size: 0.6rem; }
.review-card-head { justify-content: space-between; }
.review-card-date { margin-left: auto; }
.visit-card-note, .review-card-text { font-size: 0.85rem; line-height: 1.55; margin: 0; }
.visit-card-photo { display: block; margin-top: 0.6rem; border-radius: var(--radius); overflow: hidden; height: 132px; }
.visit-card-photo img { width: 100%; height: 100%; object-fit: cover; }
.card-hover-action { position: absolute; top: 8px; right: 8px; }
.card-hover-btn {
  min-width: 44px;
  min-height: 44px;
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
  border-radius: var(--radius);
}
.card-hover-btn:hover { color: var(--danger); }

.spot-empty {
  background: rgba(46, 95, 64, 0.55);
  border: 1px dashed rgba(183, 223, 178, 0.35);
  border-radius: var(--radius-lg);
  padding: 1.3rem 1rem;
  text-align: center;
}
.spot-empty-small { padding: 0.9rem 0.7rem; }
.spot-empty-title { font-weight: 700; color: var(--bone); margin: 0 0 0.25rem; }
.fade-stars { display: block; margin-bottom: 0.5rem; letter-spacing: 0.2em; color: rgba(217, 184, 119, 0.35); }

/* --- Rail --- */
.spot-rail { display: flex; flex-direction: column; gap: 0.9rem; }
.rail-card { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 0.9rem; overflow: hidden; }
.rail-card-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 0.6rem; }
.rail-map { height: 150px; border-radius: var(--radius); overflow: hidden; margin: -0.9rem -0.9rem 0.7rem; background: repeating-linear-gradient(135deg, rgba(183, 223, 178, 0.06) 0 10px, transparent 10px 20px); }
.rail-map-footer { display: flex; justify-content: space-between; align-items: center; }
.rail-map-footer a { color: var(--sage); font-size: 0.66rem; }
.rail-photo-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.rail-photo-cell { position: relative; display: block; border-radius: 10px; overflow: hidden; height: 62px; }
.rail-photo-cell img { width: 100%; height: 100%; object-fit: cover; }
.rail-photo-more {
  position: absolute;
  inset: 0;
  background: rgba(7, 10, 8, 0.5);
  color: var(--bone);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.85rem;
}
.rating-bars { display: flex; flex-direction: column; gap: 0.4rem; }
.rating-bar-row { display: grid; grid-template-columns: 1rem 1fr 1.4rem; align-items: center; gap: 0.5rem; }
.rating-bar-track { height: 5px; border-radius: var(--radius-pill); background: rgba(7, 10, 8, 0.45); overflow: hidden; }
.rating-bar-fill { display: block; height: 100%; background: var(--gold); }
.rail-report {
  width: 100%;
  min-height: 40px;
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--muted);
  font-family: var(--mono);
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  cursor: pointer;
}
.rail-report:hover { color: var(--danger); }

/* --- Crew: Find people, Friends, Messages --- */
/* overflow: clip, not hidden: see .spot-shell in the spot page rules for
   why hidden alone is not enough to stop a focus-triggered scroll from
   revealing .crew-aurora's -20% inset bleed. */
.crew-page { position: relative; overflow: clip; }
.crew-aurora {
  position: absolute;
  inset: -20%;
  pointer-events: none;
  filter: blur(38px);
  opacity: 0.5;
  background:
    radial-gradient(52% 46% at 6% 2%, rgba(183, 223, 178, 0.35), transparent 70%),
    radial-gradient(44% 40% at 98% 26%, rgba(203, 194, 238, 0.28), transparent 70%),
    radial-gradient(56% 42% at 22% 100%, rgba(147, 224, 138, 0.22), transparent 70%);
  animation: haze-drift 40s var(--ease-in-out) infinite alternate;
}
.crew-page > *:not(.crew-aurora) { position: relative; }

.crew-page-head { display: flex; justify-content: space-between; align-items: flex-end; gap: 1rem; flex-wrap: wrap; margin-bottom: 1rem; }
.crew-page-head .kicker { max-width: 44ch; }
.crew-stat { text-align: right; }
.crew-stat strong { display: block; font-family: var(--display); font-size: 1.7rem; color: var(--sage); line-height: 1; }

.crew-search {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  border-radius: var(--radius-lg);
  padding: 0.85rem 1.05rem;
  background: rgba(7, 10, 8, 0.4);
  border: 1px solid var(--border-strong);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  margin-bottom: 1.2rem;
}
.crew-search:focus-within { border-color: var(--amber); }
.crew-search-label { color: var(--amber); font-weight: 700; font-size: 0.68rem; }
.crew-search input {
  flex: 1;
  min-width: 0;
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font-size: 0.86rem;
}
.crew-search-divider { width: 1px; height: 18px; background: rgba(90, 138, 92, 0.35); }

.crew-two-col { display: grid; grid-template-columns: 1.25fr 1fr; gap: 0.9rem; margin-bottom: 1.4rem; }
@media (max-width: 860px) { .crew-two-col { grid-template-columns: 1fr; } }

.crew-request-panel {
  background: var(--panel-2);
  border: 1px solid rgba(147, 224, 138, 0.45);
  box-shadow: var(--glow);
  border-radius: var(--radius-lg);
  padding: 1rem;
}
.crew-request-head { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.7rem; color: var(--amber); font-family: var(--mono); font-size: 0.7rem; }
.crew-pulse-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--amber); animation: notif-pulse 2.4s ease-in-out infinite; }
.crew-request { display: flex; flex-direction: column; gap: 0.6rem; }
.crew-request + .crew-request { margin-top: 0.6rem; padding-top: 0.6rem; border-top: 1px solid var(--border); }
.crew-request-who { display: flex; align-items: center; gap: 0.6rem; color: inherit; }
.crew-request-who span { display: flex; flex-direction: column; }
.crew-request-actions { display: flex; gap: 0.6rem; }
.crew-request-actions form { flex: 1; }
.crew-request-actions .btn { width: 100%; }

.crew-reach-panel { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 1rem; }
.crew-reach-row { margin-top: 0.6rem; }
.crew-reach-label { display: flex; justify-content: space-between; align-items: baseline; font-size: 0.8rem; margin-bottom: 0.3rem; }
.crew-reach-label strong { font-family: var(--display); font-size: 0.95rem; color: var(--bone); }
.crew-reach-track { display: block; height: 4px; border-radius: var(--radius-pill); background: rgba(7, 10, 8, 0.4); overflow: hidden; }
.crew-reach-fill { display: block; height: 100%; }
.crew-reach-fill-amber { background: var(--amber); }
.crew-reach-fill-sage { background: var(--sage); }

.crew-section-label { display: block; margin: 1.6rem 0 0.6rem; }
.crew-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 720px) { .crew-grid { grid-template-columns: 1fr; } }

.crew-card { display: flex; flex-direction: column; background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 1rem; backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); transition: transform 160ms ease, border-color 160ms ease, box-shadow 160ms ease; }
.crew-card:hover { transform: translateY(-3px); border-color: rgba(183, 223, 178, 0.5); box-shadow: 0 12px 30px rgba(0, 0, 0, 0.35); }
.crew-card-head { display: flex; align-items: center; gap: 0.7rem; color: inherit; }
.crew-card-head strong { display: block; }
.crew-card-tagline { display: block; font-size: 0.78rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.crew-card-chips { display: flex; flex-wrap: wrap; gap: 0.35rem; margin: 0.7rem 0 0.75rem; }
.crew-chip-lilac { background: rgba(203, 194, 238, 0.14); color: var(--lilac); border-color: transparent; }
.crew-chip-sage { background: rgba(183, 223, 178, 0.1); color: var(--sage); border-color: transparent; }
.crew-card-action { margin-top: auto; }
.crew-add-friend {
  width: 100%;
  min-height: 44px;
  background: rgba(147, 224, 138, 0.12);
  border: 1px solid rgba(147, 224, 138, 0.5);
  color: var(--amber);
}
.crew-add-friend:hover { background: var(--amber); color: var(--ink); }

.crew-empty { text-align: center; }
.crew-empty-wide { grid-column: 1 / -1; }
.crew-empty-dots { display: flex; justify-content: center; gap: 0.35rem; margin-bottom: 0.6rem; }
.crew-empty-dots span { width: 8px; height: 8px; border-radius: 50%; background: var(--sage); }
.crew-empty-dots span:nth-child(2) { opacity: 0.6; }
.crew-empty-dots span:nth-child(3) { opacity: 0.3; }
.crew-empty-line { display: block; width: 1px; height: 30px; margin: 0 auto 0.7rem; background: linear-gradient(var(--sage), transparent); }
.crew-empty-bars { display: flex; justify-content: center; align-items: flex-end; gap: 4px; height: 24px; margin-bottom: 0.6rem; }
.crew-empty-bars span { width: 5px; background: var(--sage); border-radius: 2px; }
.crew-empty-bars span:nth-child(1) { height: 60%; opacity: 0.8; }
.crew-empty-bars span:nth-child(2) { height: 100%; opacity: 0.5; }
.crew-empty-bars span:nth-child(3) { height: 40%; opacity: 0.3; }

.crew-incoming-line { margin: 0.4rem 0 1rem; }
.crew-incoming-line a { color: var(--amber); }

.crew-friend-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.6rem; }
@media (max-width: 720px) { .crew-friend-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 420px) { .crew-friend-grid { grid-template-columns: 1fr; } }
.crew-friend-tile {
  position: relative;
  text-align: center;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.85rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  transition: transform 160ms ease, border-color 160ms ease;
}
.crew-friend-tile:hover { transform: translateY(-3px); border-color: rgba(183, 223, 178, 0.5); }
.crew-friend-avatar { margin-bottom: 0.55rem; }
.crew-friend-name { color: var(--bone); font-weight: 700; font-size: 0.8rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
.crew-friend-count { display: block; margin: 0.15rem 0 0.7rem; }
.crew-friend-message {
  width: 100%;
  min-height: 36px;
  border-radius: var(--radius);
  background: none;
  border: 1px solid rgba(147, 224, 138, 0.45);
  color: var(--amber);
}
.crew-friend-message:hover { background: var(--amber); color: var(--ink); }
.crew-friend-remove-form { position: absolute; top: 6px; right: 6px; }
.crew-friend-remove {
  width: 44px;
  height: 44px;
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
  opacity: 0;
  transition: opacity 140ms ease, color 140ms ease;
}
.crew-friend-tile:hover .crew-friend-remove,
.crew-friend-remove:focus-visible { opacity: 1; }
.crew-friend-remove:hover { color: var(--danger); }
@media (hover: none), (pointer: coarse) {
  .crew-friend-remove { opacity: 1; }
}

/* --- Safety card: five numbered cards, horizontally scrollable --- */
.safety-card {
  background: rgba(7, 10, 8, 0.42);
  border: 1px solid rgba(183, 223, 178, 0.3);
  border-radius: var(--radius-lg);
  padding: 1.1rem;
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  margin-bottom: 1.2rem;
}
.safety-card-head { display: flex; align-items: baseline; gap: 0.6rem; margin-bottom: 0.9rem; }
.safety-card-head h2 { margin: 0; font-size: 1rem; color: var(--sage); }
.safety-card-list {
  display: flex;
  gap: 0.65rem;
  overflow-x: auto;
  padding-bottom: 0.3rem;
  margin: 0 0 0.8rem;
  list-style: none;
}
.safety-card-list li {
  flex: none;
  width: 176px;
  background: var(--panel-2);
  border: 1px solid rgba(90, 138, 92, 0.25);
  border-radius: var(--radius);
  padding: 0.8rem;
  font-size: 0.72rem;
  line-height: 1.45;
  color: var(--muted);
}
.safety-card-list li strong { display: block; margin-bottom: 0.2rem; color: var(--bone); font-size: 0.78rem; }
.safety-card-num { display: block; font-family: var(--display); font-size: 1.2rem; color: rgba(183, 223, 178, 0.45); margin-bottom: 0.3rem; }
.safety-strip {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  border-left: 3px solid rgba(183, 223, 178, 0.5);
  padding: 0.5rem 0.8rem;
  margin-bottom: 1.2rem;
  font-size: 0.8rem;
  flex-wrap: wrap;
}
