/* HiFinder design system v7: "Dusk Haze".
   Minimal and calm up front, with a slow drift underneath: a breathing aurora
   behind everything, 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 split three ways so no single hue dominates:
     sage   the app's resting identity (ambient marks, borders, glow)
     amber  anything you can act on (buttons, links, active states)
     lilac  small punctuation (chips, badges, secondary meta)
   The old neon green is gone. Sage carries the plant association without
   shouting it. */

@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: #0b100e;
  --panel: rgba(23, 31, 27, 0.72);
  --panel-2: rgba(32, 42, 36, 0.75);
  --bone: #e9ede9;

  /* Identity. Dusty enough to sit under text without vibrating. */
  --sage: #8fbf9f;
  --sage-soft: rgba(143, 191, 159, 0.12);
  /* Action. Warm, so clickable things separate from ambient ones by hue,
     not just by brightness. */
  --amber: #e8a44c;
  --amber-soft: rgba(232, 164, 76, 0.12);
  /* Punctuation. */
  --lilac: #9b8fd4;

  /* --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(143, 191, 159, 0.15);
  --border-strong: rgba(143, 191, 159, 0.28);
  --muted: #8f9c93;
  --danger: #f0876a;
  --info: #7ab8d7;
  --gold: #d9b877;
  --star-dim: #2b342e;
  --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(232, 164, 76, 0.32), 0 8px 28px rgba(232, 164, 76, 0.16);
  --glow: 0 0 24px rgba(143, 191, 159, 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(143, 191, 159, 0.13), transparent 70%),
    radial-gradient(34% 40% at 80% 26%, rgba(155, 143, 212, 0.15), transparent 70%),
    radial-gradient(44% 46% at 55% 88%, rgba(232, 164, 76, 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 {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* --- 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(232, 164, 76, 0.28);
}

.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; }
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, 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);
}
/* 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 (Discover / Matches / Messages). */
.subtabs {
  display: flex;
  gap: 0.4rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 1.2rem;
}
.subtab {
  padding: 0.5rem 0.9rem;
  color: var(--muted);
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  border-bottom: 3px solid transparent;
  margin-bottom: -2px;
}
.subtab:hover { color: var(--bone); text-decoration: none; }
.subtab.active { color: var(--amber); border-bottom-color: var(--amber); }
.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(232, 164, 76, 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(232, 164, 76, 0.32);
  filter: brightness(1.04);
}
.btn:active { transform: translateY(0); box-shadow: 0 2px 10px rgba(232, 164, 76, 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 --- */
.form-card { max-width: 520px; }
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(232, 164, 76, 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 --- */
.messages {
  max-width: 920px;
  margin: 0.7rem auto 0;
  padding: 0 1.3rem;
  position: relative;
  z-index: 60;
}
.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;
  animation: slide-in 200ms ease;
  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); }
}

/* --- 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 {
  display: flex;
  align-items: flex-end;
  gap: 1rem;
  padding: 0 1.3rem;
  margin-top: -44px;
}
.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); }

/* Accent swatch picker */
.customize { margin-top: 1rem; }
.customize .field { margin: 0.9rem 0; }
.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; }

.stat-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.8rem; margin: 1.1rem 0; }
.stat-tile {
  background: var(--panel-2);
  border: 1px solid var(--border);
  padding: 0.9rem 0.6rem;
  text-align: center;
  border-radius: var(--radius);
}
.stat-tile svg { width: 16px; height: 16px; color: var(--ember); }
.stat-tile .num { font-family: var(--display); font-size: 1.4rem; display: block; line-height: 1.2; }
.stat-tile .lbl {
  color: var(--muted);
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.02em;
}

.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 { 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; }
  .nav-links { flex-wrap: nowrap; gap: 0.25rem; }
  .nav-link span, .linklike span { display: none; }
  .nav-link, .linklike { padding: 0.4rem 0.5rem; }
  .nav-link svg, .linklike svg { width: 16px; height: 16px; }
  .brand-sub { display: none; }
  h1 { font-size: 1.6rem; }
  .container { padding: 1.1rem 1rem; }
  .stat-grid { gap: 0.5rem; }
}

/* --- 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(232, 164, 76, 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 {
    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(232, 164, 76, 0.45);
}
.notif-list { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 0.8rem; }
.notif {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.8rem 1rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--bone);
  font-weight: 400;
  transition: background 140ms var(--ease-out), transform 140ms var(--ease-out);
}
.notif:hover { background: var(--panel-2); text-decoration: none; transform: translateY(-1px); }
/* Unread ones carry a soft green edge until you open the page. */
.notif.is-new { border-color: rgba(232, 164, 76, 0.3); box-shadow: inset 3px 0 0 var(--amber); }
.notif-main { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.notif-text { overflow-wrap: anywhere; }
.notif-when { color: var(--muted); font-size: 0.72rem; }

/* --- 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(143, 191, 159, 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(232, 164, 76, 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(232, 164, 76, 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(143, 191, 159, 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; }
