/* Nearby Right Now — responsive styles. Mobile-first, no framework. */

:root {
  --bg: #0f1115;
  --panel: #171a21;
  --panel-2: #1e222b;
  --line: #2a2f3a;
  --text: #e7e9ee;
  --muted: #9aa3b2;
  --accent: #4ade80;
  --accent-dim: #22c55e;
  --danger: #f87171;
  --warn: #fbbf24;
  --radius: 12px;
  --shadow: 0 6px 24px rgba(0, 0, 0, .35);
}

* { box-sizing: border-box; }

/* The `hidden` attribute must beat any class that sets `display`.

   `.stack` (0,1,0) and the UA's `[hidden]` (0,1,0) have identical
   specificity, and our author sheet wins the tie — so `.stack { display: flex }`
   silently defeated `hidden` on the sign-in code form. Both auth steps then
   rendered at once and the code field was submittable before any address was
   set, producing a bare "That request could not be processed." The reset below
   is the standard normalize.css rule; every toggle in app.js reveals an element
   with `node.hidden = false`, which removes the attribute, so nothing that
   should reappear is affected. */
[hidden] { display: none !important; }

.grid-notice {
  margin: 12px 0;
  padding: 14px 16px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
h1, h2, h3 { line-height: 1.25; margin: 0 0 .5rem; }
h1 { font-size: 1.6rem; }
h2 { font-size: 1.15rem; }
h3 { font-size: 1rem; }

/* ---------------------------------------------------------------- chrome */

.topbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
  padding: .75rem 1rem;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 20;
}
.brand { font-weight: 700; font-size: 1.1rem; color: var(--text); }
.brand span { color: var(--accent); }
.topnav { display: flex; align-items: center; gap: .9rem; flex-wrap: wrap; font-size: .92rem; }
.topnav a { color: var(--muted); }
.topnav a:hover { color: var(--text); }
.topnav .admin-link { color: var(--warn); }

.shell { max-width: 1200px; margin: 0 auto; padding: 1rem; }
.footer {
  max-width: 1200px; margin: 2rem auto 3rem; padding: 1rem;
  color: var(--muted); font-size: .8rem;
  display: flex; gap: 1rem; align-items: center; flex-wrap: wrap;
}

/* ---------------------------------------------------------------- bits */

.cta {
  display: inline-block;
  background: var(--accent); color: #06240f;
  border: 0; border-radius: 9px;
  padding: .6rem 1rem; font: inherit; font-weight: 600;
  cursor: pointer;
}
.cta:hover { background: var(--accent-dim); text-decoration: none; }
.cta:disabled { opacity: .5; cursor: not-allowed; }
.cta.ghost { background: transparent; color: var(--accent); border: 1px solid var(--line); }
.cta.ghost:hover { background: var(--panel-2); }
/* A destructive confirm must not wear the same green as "Continue". */
.cta.danger { background: var(--danger); color: #2a0b0b; }
.cta.danger:hover { background: #ef4444; }
.cta.big { padding: .8rem 1.4rem; font-size: 1.05rem; }
.cta.small { padding: .35rem .7rem; font-size: .85rem; }
.cta.full { display: block; width: 100%; text-align: center; margin-bottom: .4rem; }

.linkbtn {
  background: none; border: 0; color: var(--muted);
  font: inherit; cursor: pointer; padding: .25rem 0; text-align: left;
}
.linkbtn:hover { color: var(--text); text-decoration: underline; }
.linkbtn.danger { color: var(--danger); }

.muted { color: var(--muted); }
.small { font-size: .85rem; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.warn-text { color: var(--warn); font-size: .9rem; }
.dot { color: var(--line); }

.badge {
  display: inline-block; background: var(--panel-2); color: var(--text);
  border-radius: 999px; padding: .1rem .5rem; font-size: .75rem;
}
.badge.business { background: #3b2f10; color: var(--warn); }
.badge.warn { background: #4a2f0d; color: var(--warn); }
.badge.verified { background: #123524; color: var(--accent); font-weight: 600; display: inline-flex; align-items: center; gap: 0.25rem; }
.badge.phone-verified { background: #1a2a3a; color: #60a5fa; font-weight: 600; display: inline-flex; align-items: center; gap: 0.25rem; }
.warn-pill {
  background: #4a2f0d; color: var(--warn); padding: .2rem .6rem;
  border-radius: 999px; font-weight: 600;
}

/* The legal links.  In the footer rather than only on one page, because the terms
   and the privacy policy have to be reachable from the sign-in page: notice that
   exists only behind the account it governs is not notice. */
.footer-links { display: flex; gap: .75rem; align-items: center; }

/* A legal document is read, not skimmed, so it gets a narrower measure than the
   grid pages and paragraph spacing rather than the list styling used elsewhere. */
.legal-doc { max-width: 46rem; margin: 1.5rem auto 0; }
.legal-doc .article-body { line-height: 1.65; }
.legal-doc .article-body h2 {
  font-size: 1.05rem; margin: 1.8rem 0 .5rem; padding-top: 1rem;
  border-top: 1px solid var(--line);
}
.legal-doc .article-body h2:first-of-type { border-top: 0; padding-top: 0; }
.legal-doc .article-body p { margin: .7rem 0; }
.legal-doc .article-body table { border-collapse: collapse; width: 100%; margin: .8rem 0; }
.legal-doc .article-body th,
.legal-doc .article-body td {
  border: 1px solid var(--line); padding: .45rem .6rem; text-align: left;
  vertical-align: top; font-size: .9rem;
}
.legal-doc .article-body th { background: var(--panel-2); }
.legal-nav { margin-top: 2rem; display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }

/* The consent line on the sign-in card.  Below the form rather than above it: it
   has to be present and readable at the point of signing in, but it is not the
   thing the user came to do. */
.legal-consent { margin-top: 1.2rem; line-height: 1.5; }

/* The public-beta notice.  Deliberately legible rather than subtle: it is the
   one element on the page whose job is to be read, and a beta notice nobody
   notices is decoration.  Sits below the sticky topbar, which has z-index 20,
   so it scrolls away rather than covering content on every screen. */
.beta-banner {
  display: flex; align-items: center; justify-content: center; gap: .75rem;
  flex-wrap: wrap; padding: .5rem 1rem;
  background: #3b3210; border-bottom: 1px solid var(--line);
  font-size: .85rem; color: var(--warn);
}
.beta-banner .beta-tag { font-weight: 700; letter-spacing: .02em; }
.beta-banner a { color: var(--text); text-decoration: underline; }
.beta-banner a:hover { color: var(--warn); }

.state-pill {
  display: inline-block; border-radius: 999px; padding: .15rem .6rem;
  font-size: .78rem; background: var(--panel-2); color: var(--muted);
}
.state-pill.live, .state-pill.settled, .state-pill.confirmed { background: #123524; color: var(--accent); }
.state-pill.off, .state-pill.expired, .state-pill.void, .state-pill.invalid { background: #3a1f1f; color: var(--danger); }
.state-pill.qualified-no, .state-pill.pending, .state-pill.new { background: #3b3210; color: var(--warn); }

.flash {
  display: none; position: fixed; top: 3.4rem; left: 50%; transform: translateX(-50%);
  background: var(--panel-2); border: 1px solid var(--line); border-left: 3px solid var(--accent);
  padding: .6rem 1rem; border-radius: 8px; z-index: 60; box-shadow: var(--shadow);
  max-width: min(90vw, 40rem);
}
.flash.visible { display: block; }
.flash.error { border-left-color: var(--danger); }

/* ---------------------------------------------------------------- forms */

.field { display: flex; flex-direction: column; gap: .3rem; }
.field.grow { flex: 1 1 12rem; }
.field label { font-size: .82rem; color: var(--muted); }
input, select, textarea {
  background: var(--panel-2); border: 1px solid var(--line); color: var(--text);
  border-radius: 8px; padding: .5rem .6rem; font: inherit; width: 100%;
}
input:focus, select:focus, textarea:focus { outline: 2px solid var(--accent-dim); outline-offset: 1px; }
.stack { display: flex; flex-direction: column; gap: .8rem; }
.form-actions { display: flex; gap: .8rem; align-items: center; margin-top: .6rem; }
.field.checkbox label { flex-direction: row; display: flex; gap: .5rem; align-items: flex-start; color: var(--text); }
.field.checkbox input { width: auto; }

/* Settings → Location: Update + show-distance on one row; checkbox not full-width */
.location-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.1rem;
  align-items: center;
}
.location-row .check-row {
  display: inline-flex;
  gap: 0.5rem;
  align-items: flex-start;
  margin: 0;
  max-width: min(100%, 28rem);
}
.location-row .check-row input[type="checkbox"] {
  width: auto;
  flex: 0 0 auto;
  margin-top: 0.2rem;
  accent-color: var(--accent);
}
.location-row .check-row-text { min-width: 0; }
.location-status { margin-top: 0.55rem; min-height: 1.2em; }
.location-status.is-ok { color: var(--accent); }
.location-status.is-error { color: var(--danger); }
button.danger {
  background: transparent; color: var(--danger); border: 1px solid var(--danger);
  border-radius: 8px; padding: .55rem .9rem; font: inherit; cursor: pointer;
}
button.danger:hover { background: var(--danger); color: #1a0a0a; }
.account-notice { margin: 1rem 0; }

/* ---------------------------------------------------------------- pages */

.hero { padding: 2.5rem 0 1.5rem; max-width: 46rem; }
.hero h1 { font-size: clamp(1.7rem, 4vw, 2.6rem); }
.hero em { color: var(--accent); font-style: normal; }
.lede { color: var(--muted); font-size: 1.05rem; }
.hero-actions { margin-top: 1.4rem; }

.panels { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); }
.panels.two { grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); }
.panel {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1rem; margin-bottom: 1rem;
}
.panel h2 { margin-top: 0; }

.auth-card {
  max-width: 26rem; margin: 3rem auto; background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--radius); padding: 1.5rem;
}
.devhint {
  margin-top: 1.2rem; padding-top: 1rem; border-top: 1px solid var(--line);
  color: var(--muted); font-size: .82rem;
}

.page-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; margin-bottom: 1rem; flex-wrap: wrap;
}

/* ------------------------------------------------------------ grid page */

.grid-controls {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: .8rem; margin-bottom: 1rem;
}
.control-row { display: flex; gap: .7rem; align-items: flex-end; flex-wrap: wrap; }
.control-row.secondary { margin-top: .6rem; align-items: center; font-size: .85rem; }

/* Browse tabs.  A section is a top-level category flagged `show_as_tab`, so
   this bar is a rendering of the tree rather than a second hierarchy -- which
   is what stops it drifting from the category dropdown it sits above. */
.section-tabs {
  display: flex; gap: .4rem; flex-wrap: wrap; margin-bottom: .8rem;
  border-bottom: 1px solid var(--line); padding-bottom: .5rem;
}
.section-tab {
  background: transparent; color: var(--muted); border: 1px solid transparent;
  border-radius: var(--radius); padding: .45rem .9rem; cursor: pointer;
  font: inherit; font-weight: 600;
}
.section-tab:hover { color: var(--text); background: var(--panel-2); }
.section-tab.active { color: var(--accent); border-color: var(--accent-dim); background: var(--panel); }

/* Attribute facets sit inside the controls panel, below the ordinary filters. */
.attr-facets {
  margin-top: .6rem; padding-top: .6rem; border-top: 1px solid var(--line);
}
.attr-fields { display: flex; gap: .7rem; flex-wrap: wrap; align-items: flex-end; }
.attr-fields .field { flex: 0 1 11rem; }

.grid {
  display: grid; gap: .9rem;
  grid-template-columns: repeat(auto-fill, minmax(10.5rem, 1fr));
}
.grid[aria-busy="true"] { opacity: .5; }

.tile {
  display: flex; flex-direction: column; background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; color: var(--text);
}
.tile:hover { border-color: var(--accent-dim); text-decoration: none; }
.tile-wrap { position: relative; display: flex; }
/* The tile is no longer the grid item, so it has to be told to fill the cell.
   Without this the badge row (now a sibling) would leave every tile shorter
   than its neighbours. */
.tile-wrap > .tile { flex: 1 1 auto; min-width: 0; }
.tile-media { position: relative; aspect-ratio: 4 / 3; background: var(--panel-2); }

/* Shine — a bright, colour-cycling outline on a promoted card.

   The animation is the product, so it is the one place motion is essential
   rather than decorative.  ``prefers-reduced-motion`` therefore does not remove
   the outline; it makes it *static* -- a fixed bright ring, which still says
   "this card is promoted" without cycling.  Dropping the outline entirely would
   remove the disclosure that keeps a paid boost honest, and a reduced-motion
   user is exactly as entitled to know a listing is promoted as anyone else. */
.tile-wrap.shining > .tile,
.card-shining {
  animation: shine-cycle 4s linear infinite;
}

.tile-wrap.shining > .tile {
  border-width: 2px;
}

@keyframes shine-cycle {
  0%   { border-color: #4ade80; box-shadow: 0 0 12px rgba(74, 222, 128, .55); }
  25%  { border-color: #22d3ee; box-shadow: 0 0 14px rgba(34, 211, 238, .60); }
  50%  { border-color: #a78bfa; box-shadow: 0 0 14px rgba(167, 139, 250, .60); }
  75%  { border-color: #fbbf24; box-shadow: 0 0 14px rgba(251, 191, 36, .55); }
  100% { border-color: #4ade80; box-shadow: 0 0 12px rgba(74, 222, 128, .55); }
}

@media (prefers-reduced-motion: reduce) {
  .tile-wrap.shining > .tile,
  .card-shining {
    animation: none;
    border-color: #4ade80;
    box-shadow: 0 0 10px rgba(74, 222, 128, .5);
  }
}

/* The badge that discloses the shine.  Named, not just a colour, so the
   promotion is announced to a screen reader as well as seen. */
.badge.shining {
  background: #14324a;
  color: #7dd3fc;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
}
.tile-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.tile-placeholder {
  width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
  padding: 0 .8rem; text-align: center;
  color: var(--muted); font-weight: 600; font-size: .88rem; letter-spacing: .01em;
}
.tile-placeholder.large { min-height: 14rem; }
.tile-badges { position: absolute; top: .4rem; left: .4rem; display: flex; gap: .3rem; flex-wrap: wrap; z-index: 1; }
/* A badge that carries a destination must not look like the static pills it
   sits beside, or nobody will try clicking it. */
.badge-link { cursor: pointer; }
.badge-link:hover { text-decoration: underline; }
.tile-body { padding: .6rem; display: flex; flex-direction: column; gap: .2rem; }
.tile-body h3 { font-size: .95rem; margin: 0; }
.tile-category { margin: 0; font-size: .78rem; color: var(--muted); }
.tile-price { margin: 0; font-weight: 600; color: var(--accent); font-size: .9rem; }
.tile-meta { display: flex; gap: .35rem; font-size: .75rem; color: var(--muted); flex-wrap: wrap; margin-top: .2rem; }

.grid-footer { display: flex; flex-direction: column; align-items: center; gap: .6rem; padding: 1.5rem 0; }

/* -------------------------------------------------------- card / profile */

.card-detail {
  display: grid; gap: 1.2rem; grid-template-columns: 1fr;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1rem;
}
@media (min-width: 720px) { .card-detail { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); } }
/* The main image only.  A descendant selector here matched the thumbnails
   below as well, stretching each one to the full column width -- the strip
   rendered as a stack of full-size images.  Same failure mode as the sign-in
   steps that once drew on top of each other: a rule that out-specifies what
   it meant to target.  ``>`` says exactly which image is meant. */
.card-detail-media > img { width: 100%; border-radius: 10px; display: block; }

/* The thumbnail row.  Without it a card carrying four photos looked exactly
   like a card carrying one, which is what the user reported. */
.thumb-strip { display: flex; gap: .4rem; margin-top: .5rem; flex-wrap: wrap; }
.thumb {
  padding: 0; border: 2px solid transparent; border-radius: 8px;
  background: none; cursor: pointer; line-height: 0;
  width: 3.6rem; height: 3.6rem; overflow: hidden; flex: 0 0 auto;
}
.thumb img { width: 100%; height: 100%; object-fit: cover; border-radius: 0; display: block; }
.thumb.active { border-color: var(--accent); }
.thumb:hover { border-color: var(--muted); }
.thumb:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.card-detail-body { display: flex; flex-direction: column; gap: .6rem; }
.detail-head { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }
.detail-head h1 { margin: 0; }
.detail-price { font-size: 1.25rem; color: var(--accent); font-weight: 700; margin: 0; }
.detail-text { color: var(--text); white-space: pre-wrap; }

.card-attrs {
  display: grid;
  gap: .35rem .9rem;
  margin: .2rem 0 .4rem;
  padding: .55rem .7rem;
  background: var(--panel-2, var(--panel));
  border: 1px solid var(--line);
  border-radius: 10px;
}
.card-attr { display: grid; grid-template-columns: minmax(6rem, 9rem) 1fr; gap: .4rem; align-items: baseline; }
.card-attr dt { margin: 0; font-size: .78rem; color: var(--muted); font-weight: 600; }
.card-attr dd { margin: 0; font-size: .9rem; color: var(--text); }
.tile-attrs { display: flex; flex-wrap: wrap; gap: .25rem; margin-top: .15rem; }
.tile-attrs .chip { font-size: .7rem; padding: .1rem .4rem; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }

.offers-list ul { padding-left: 1.1rem; margin: .3rem 0; }
.offers-list li { margin-bottom: .5rem; }
.offer-price { margin-left: .5rem; color: var(--accent); }
.owner-strip { border-top: 1px solid var(--line); padding-top: .7rem; }
.owner-link { display: flex; gap: .6rem; align-items: center; color: var(--text); }
.owner-badges { display: flex; gap: .35rem; flex-wrap: wrap; margin-top: .45rem; }
.action-bar { display: flex; gap: .5rem; flex-wrap: wrap; margin-top: .5rem; }

.avatar {
  width: 2.2rem; height: 2.2rem; border-radius: 50%;
  object-fit: cover; background: var(--panel-2);
}
.avatar.large { width: 4.5rem; height: 4.5rem; }
.avatar.placeholder { display: inline-block; }

.profile-head { display: flex; gap: 1rem; align-items: flex-start; margin-bottom: 1.5rem; flex-wrap: wrap; }
.profile-head h1 { margin: 0 0 .2rem; }

/* -------------------------------------------------------------- my cards */

.slots-panel {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1rem; margin-bottom: 1rem;
}
.slot-strip { display: flex; gap: .5rem; flex-wrap: wrap; margin: .7rem 0; }
.slot {
  border: 1px dashed var(--line); border-radius: 9px; padding: .5rem .7rem;
  display: flex; flex-direction: column; gap: .15rem; min-width: 9rem; font-size: .85rem;
}
.slot.filled { border-style: solid; border-color: var(--accent-dim); background: #10241a; }
.slot-index { font-size: .72rem; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }

.card-form {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1rem; margin-bottom: 1rem;
  display: flex; flex-direction: column; gap: .8rem;
}
.offers { border: 1px solid var(--line); border-radius: 9px; padding: .7rem; }
.offers legend { font-size: .85rem; color: var(--muted); padding: 0 .3rem; }

.card-images { border: 1px solid var(--line); border-radius: 9px; padding: .7rem; }
.card-images legend { font-size: .85rem; color: var(--muted); padding: 0 .3rem; }
.image-strip { display: flex; flex-wrap: wrap; gap: .6rem; margin-bottom: .6rem; }
.image-thumb {
  width: 108px; display: flex; flex-direction: column; gap: .25rem;
  border: 1px solid var(--line); border-radius: 8px; padding: .3rem;
}
.image-thumb img { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; border-radius: 5px; display: block; }
.image-thumb.pending { border-style: dashed; }
.offer-row { display: flex; gap: .4rem; margin-bottom: .4rem; align-items: flex-end; }
.offer-row .field { flex: 1 1 0; }
.offer-row .field.wide { flex: 2 1 0; }

.cards-list { display: flex; flex-direction: column; gap: .8rem; }
.owner-card {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1rem;
}
.owner-card-head { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }
.owner-card-head h3 { margin: 0; }
.owner-card-actions { display: flex; gap: .8rem; flex-wrap: wrap; margin-top: .6rem; }
.offer-summary { margin: .5rem 0 0; padding-left: 1.1rem; color: var(--muted); font-size: .88rem; }

/* ------------------------------------------------------------- messages */


/* Explicit return to discovery from card detail / messages.  Accent green so it
   reads as a primary navigation affordance on the dark shell, including on
   narrow widths where the topbar Nearby link is easy to miss above the fold. */
.back-to-results {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  margin: 0 0 .85rem;
  color: var(--accent);
  font-weight: 600;
  font-size: .95rem;
  text-decoration: none;
}
.back-to-results:hover { color: var(--text); text-decoration: underline; }
.messages-nav {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
  align-items: center;
  margin: 0 0 .35rem;
  /* Prepended into .messages-layout's grid — must span both columns so it
     sits above Conversations + thread instead of taking the first cell and
     shoving the thread down under the list. */
  grid-column: 1 / -1;
}
.messages-nav .back-to-results { margin-bottom: 0; }
.thread-head .thread-leave { margin-right: .25rem; }

.messages-layout { display: grid; gap: 1rem; grid-template-columns: 1fr; }
@media (min-width: 800px) { .messages-layout { grid-template-columns: 17rem 1fr; } }
.conversation-list {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: .8rem;
}
.conversation-list h2 { font-size: .95rem; color: var(--muted); }
.conversation-list ul { list-style: none; margin: 0; padding: 0; }
.conversation-list a {
  display: flex; align-items: center; gap: .4rem; padding: .5rem;
  border-radius: 8px; color: var(--text); flex-wrap: wrap;
}
.conversation-list a:hover { background: var(--panel-2); text-decoration: none; }
.conversation-list a.active { background: var(--panel-2); border-left: 2px solid var(--accent); }
.conv-name { font-weight: 600; }
.conv-preview { flex-basis: 100%; }

.thread {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: .8rem; display: flex; flex-direction: column;
  min-height: 24rem;
}
.thread-head {
  display: flex; gap: .8rem; align-items: center; flex-wrap: wrap;
  border-bottom: 1px solid var(--line); padding-bottom: .6rem; margin-bottom: .6rem;
}
.message-scroll { flex: 1; overflow-y: auto; max-height: 55vh; display: flex; flex-direction: column; gap: .4rem; }
.bubble {
  max-width: 75%; padding: .45rem .7rem; border-radius: 12px;
  display: flex; flex-direction: column; gap: .15rem; word-break: break-word;
}
.bubble.mine { align-self: flex-end; background: #16351f; }
.bubble.theirs { align-self: flex-start; background: var(--panel-2); }
.bubble time { font-size: .68rem; color: var(--muted); }
.composer { display: flex; gap: .5rem; margin-top: .6rem; flex-wrap: wrap; }
/* The file input is driven by the "Photo" button, so it must not draw.  Done
   with clip rather than display:none or the `hidden` attribute: an element with
   display:none is skipped by some assistive tech, and a file input that cannot
   be focused is a control a keyboard user cannot reach if the button ever fails
   to wire up.  The `hidden` attribute in particular loses to any author rule
   that matches the element -- that collision shipped a bug here before. */
.composer-attach-input {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.composer .attach { align-self: center; }
.composer-attachments { display: flex; gap: .4rem; flex-wrap: wrap; width: 100%; }
.attachment-chip { position: relative; display: flex; align-items: center; gap: .3rem; }
.attachment-chip img {
  width: 3rem; height: 3rem; object-fit: cover; border-radius: 6px;
  border: 1px solid var(--line);
}
/* Photos sit above the caption inside a bubble. */
.bubble-images { display: flex; gap: .3rem; flex-wrap: wrap; margin-bottom: .2rem; }
.bubble-images img {
  max-width: 11rem; max-height: 11rem; border-radius: 8px; display: block;
  border: 1px solid var(--line);
}

/* View-once photos.
   No image is rendered until someone chooses to open one, because drawing it
   would spend the single view.  The button is therefore the whole affordance,
   and "spent" has to look deliberate rather than like a broken image. */
.view-once.open {
  background: var(--panel-2); color: var(--text); border: 1px dashed var(--line);
  border-radius: 8px; padding: .45rem .7rem; font: inherit; font-size: .82rem;
  cursor: pointer;
}
.view-once.open:hover { border-color: var(--accent); }
.view-once.open:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.view-once.spent {
  color: var(--muted); font-size: .8rem; font-style: italic;
}
.bubble .view-once.spent-note {
  display: block; color: var(--muted); font-size: .78rem; margin-bottom: .2rem;
}
.view-once-modal { max-width: min(90vw, 40rem); }
.view-once-stage img.view-once-image {
  max-width: 100%; max-height: 70vh; border-radius: 8px; display: block;
}
.empty-thread { color: var(--muted); }

/* -------------------------------------------------------------- billing */

.table { width: 100%; border-collapse: collapse; font-size: .9rem; }
.table th, .table td { text-align: left; padding: .45rem .5rem; border-bottom: 1px solid var(--line); }
.table th { color: var(--muted); font-weight: 600; font-size: .8rem; }
.table tr.current { background: #10241a; }
.table tr.ok td:first-child { color: var(--accent); }
.table tr.fail td:first-child { color: var(--danger); }

.invoice {
  border: 1px solid var(--line); border-radius: 9px;
  padding: .7rem; margin-bottom: .6rem; background: var(--panel-2);
}
.invoice-head { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }
.invoice-actions { display: flex; gap: .8rem; margin-top: .4rem; flex-wrap: wrap; }

/* ---------------------------------------------------------------- modal */

.modal-backdrop {
  position: fixed; inset: 0; background: rgba(0, 0, 0, .65);
  display: flex; align-items: center; justify-content: center; padding: 1rem; z-index: 80;
}
.modal {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 1.2rem; max-width: 34rem; width: 100%; max-height: 85vh; overflow-y: auto;
}
.occupant-list { margin: .5rem 0 1rem; }
/* What a block will do, enumerated before it happens.  Readable rather than
   shouty: the point is that the person can check each line, not that they get
   startled into cancelling. */
.consequence-list {
  margin: .2rem 0 .6rem; padding-left: 1.1rem;
  color: var(--text); font-size: .92rem; line-height: 1.5;
}
.consequence-list li { margin-bottom: .25rem; }
.modal-actions { display: flex; gap: .8rem; align-items: center; margin-top: .9rem; }
.modal .field { margin-bottom: .7rem; }
.blocked-notice {
  margin-top: .6rem; padding: .6rem .8rem; border-radius: 8px;
  background: var(--panel-2); border: 1px solid var(--line);
  border-left: 3px solid var(--danger); color: var(--muted); font-size: .88rem;
}

/* ----------------------------------------------------------------- admin */

.admin-body { background: #0b0d11; }
.admin-nav {
  display: flex; gap: 1rem; flex-wrap: wrap; padding: .6rem 1rem;
  background: #121620; border-bottom: 1px solid var(--line); font-size: .9rem;
}
.admin-nav a { color: var(--muted); }
.admin-nav a.active { color: var(--accent); }
.admin-nav .spacer { flex: 1; }

.stat-grid { display: grid; gap: .7rem; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); }
.stat {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 10px; padding: .8rem;
}
.stat .value { font-size: 1.5rem; font-weight: 700; }
.stat .label { color: var(--muted); font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; }

.event-row { font-size: .85rem; }
.event-row .severity { font-weight: 700; text-transform: uppercase; font-size: .7rem; }
.severity.info { color: var(--muted); }
.severity.warning { color: var(--warn); }
.severity.error { color: var(--danger); }
.severity.audit { color: var(--accent); }

.diagnostic {
  border: 1px solid var(--line); border-radius: 10px; padding: .9rem; background: var(--panel-2);
}
.diagnostic .verdict { font-size: 1.1rem; font-weight: 700; margin-bottom: .5rem; }
.diagnostic .verdict.yes { color: var(--accent); }
.diagnostic .verdict.no { color: var(--danger); }

@media (max-width: 620px) {
  .tile-body h3 { font-size: .88rem; }
  .control-row { align-items: stretch; }
  .control-row .field { flex-basis: 100%; }
  .dating-chrome .age-filter { flex-basis: 100%; flex-wrap: wrap; }
}

/* ------------------------------------------------------------------ help */

/* The search field and its button sit on one line; the button must not stretch
   to fill the row, which is what a bare flex child does by default. */
.help-search { display: flex; gap: .5rem; align-items: center; margin: 1rem 0 1.25rem; flex-wrap: wrap; }
.help-search input[type="search"] {
  flex: 1 1 20rem; min-width: 12rem; padding: .6rem .75rem;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); color: var(--text); font: inherit;
}
.help-search input[type="search"]:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

.help-categories { display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: 1.25rem; }
.help-cat {
  display: inline-block; padding: .3rem .6rem;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 999px; font-size: .9rem;
}
.help-cat.active { background: var(--accent-dim); border-color: var(--accent); color: #06120a; }
/* Nesting is shown by indent as well as by pill order, so a child never reads
   as a sibling when the pills happen to wrap onto the same line. */
.help-cat.depth-1 { margin-left: 1rem; }
.help-cat.depth-2 { margin-left: 2rem; }
.help-cat.depth-3 { margin-left: 3rem; }

.help-list { list-style: none; padding: 0; margin: 0; }
.help-list li { padding: .9rem 0; border-bottom: 1px solid var(--line); }
.help-list li:last-child { border-bottom: 0; }
.help-list a { font-size: 1.05rem; }
.help-list p { margin: .25rem 0 0; }

.help-meta { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .5rem; }
.chip {
  display: inline-block; padding: .15rem .5rem; font-size: .78rem;
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: 999px; color: var(--muted);
}
a.chip:hover { color: var(--text); }

.breadcrumb { margin-bottom: .75rem; font-size: .9rem; color: var(--muted); }
.breadcrumb a { color: var(--muted); }
.breadcrumb span { margin: 0 .3rem; }

.lede { font-size: 1.05rem; color: var(--muted); margin-top: -.25rem; }

/* Article body: the only place authored HTML is rendered. Bounded so a huge
   image cannot break the layout, and so tables stay readable on mobile. */
.article-body { margin: 1rem 0; line-height: 1.65; }
.article-body img { max-width: 100%; height: auto; border-radius: var(--radius); }
.article-body figure { margin: 1rem 0; }
.article-body figcaption { font-size: .85rem; color: var(--muted); margin-top: .35rem; }
.article-body table { width: 100%; border-collapse: collapse; margin: 1rem 0; }
.article-body th, .article-body td { border: 1px solid var(--line); padding: .45rem .6rem; text-align: left; }
.article-body blockquote {
  margin: 1rem 0; padding: .25rem 1rem; border-left: 3px solid var(--line); color: var(--muted);
}
.article-body pre {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: .75rem; overflow-x: auto;
}
.article-body code { background: var(--panel-2); border-radius: 4px; padding: .1rem .3rem; font-size: .92em; }
.article-body pre code { background: none; padding: 0; }

/* Available to a screen reader, not to the eye. Used for the search label,
   which has a placeholder for sighted users but still needs an accessible
   name -- a placeholder is not one, and it disappears as soon as anything is
   typed. Clipped rather than display:none, because display:none removes it
   from the accessibility tree entirely and would defeat the purpose. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* Category checkboxes in the console: a column that wraps, so a deep tree does
   not push the form off the side of the page. */
.checkbox-grid { display: flex; flex-wrap: wrap; gap: .35rem 1.25rem; padding: .35rem 0; }
.checkbox-grid .checkbox { display: inline-flex; align-items: center; gap: .4rem; font-size: .9rem; }

/* Trust and ratings.

   The breakdown is styled as a plain list rather than a chart: the point is that
   a person can read exactly which inputs produced their score and how much each
   one moved it, and a list of "component → points (weight)" says that more
   directly than any graphic would. */
.panel.trust .trust-score {
  display: flex;
  align-items: baseline;
  gap: .4rem;
  margin: .2rem 0 .4rem;
}
.panel.trust .trust-number {
  font-size: 2rem;
  font-weight: 700;
  color: var(--accent);
  line-height: 1;
}
.trust-breakdown {
  list-style: none;
  margin: .4rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: .2rem;
}
.trust-row {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  border-bottom: 1px solid var(--line);
  padding: .2rem 0;
  font-size: .85rem;
}
.trust-label { color: var(--muted); }
.trust-value { font-variant-numeric: tabular-nums; }

.rating {
  border-bottom: 1px solid var(--line);
  padding: .4rem 0;
}
.rating-head { margin: 0 0 .2rem; display: flex; align-items: center; gap: .3rem; flex-wrap: wrap; }
.rating-stars { color: var(--warn); letter-spacing: .08em; }
.rating-form { display: flex; gap: .5rem; flex-wrap: wrap; align-items: flex-end; margin-top: .4rem; }
.badge.verified.small { font-size: .7rem; padding: .05rem .3rem; }


/* Dating discovery chrome — acting-as + seeking/for/age bar. */
.dating-chrome {
  margin-top: .6rem; padding-top: .6rem; border-top: 1px solid var(--line);
  display: flex; gap: .7rem; flex-wrap: wrap; align-items: flex-end;
}
.dating-chrome .field { flex: 0 1 10rem; }
.dating-chrome .age-filter {
  display: flex; align-items: flex-end; gap: .7rem; flex: 0 1 auto;
}
.dating-chrome .age-filter .field { flex: 0 0 auto; }
.dating-chrome .age-omitted {
  display: inline-flex; align-items: center; gap: .4rem;
  margin: 0 0 .35rem; color: var(--text); font-size: .82rem; white-space: nowrap;
}
.dating-chrome .age-omitted input { width: auto; flex: 0 0 auto; accent-color: var(--accent); }
.dating-chrome input[type="number"] {
  width: 5.5rem; padding: .45rem .55rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); color: var(--text);
}
.dating-more-filters {
  margin-top: .4rem; padding: .7rem; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--panel-2);
  display: flex; gap: .7rem; flex-wrap: wrap; align-items: flex-start;
}
.dating-more-filters .field { flex: 0 1 12rem; }
.dating-more-filters select[multiple] { min-height: 6rem; }
