/* human problems — "Dusk" chat UI.
   WhatsApp iOS bubble geometry (18px radius, grouped stacks, timestamp tucked in
   the corner — tailless) in a palette of muted, darkened pastels.
   Adapted from the Dusk Bubble Kit. Dark-first: :root is the primary look,
   light is a remap. Type: Inter for message content, Space Mono for chrome.

   The random feed has no "me", so every message is an incoming (left) bubble —
   except ones you posted from this browser, which come back as outgoing (right)
   bubbles with a sent tick (app.js tracks their ids in localStorage). */

:root {
  --panel:   #17141b;   /* page ground — bubbles sit directly on this */
  --raised:  #2b2530;   /* composer field, record-preview player */
  --hair:    rgba(255, 255, 255, 0.09);
  --hair-2:  rgba(255, 255, 255, 0.14);

  --in:      #433b4e;   /* incoming bubble — desaturated lilac, dropped to dusk */
  --out:     #33474a;   /* outgoing bubble — desaturated eucalyptus, darkened */
  --ink-in:  #ece6ef;
  --ink-out: #e4efec;
  --meta-in: rgba(236, 230, 239, 0.5);
  --meta-out:rgba(228, 239, 236, 0.52);

  --tick:    #9db0d6;   /* accent — periwinkle; play buttons, focus, send */
  --live:    #cf9f83;   /* recording state only — dusk clay */
  --error:   #d98c6f;

  --l-gold:  #c7b17c;
  --l-rose:  #cf9aa8;
  --l-sage:  #a2bf9c;
  --l-sky:   #93b6cf;
  --l-clay:  #d19a86;

  --text:    #e7e2ea;
  --text-dim:#9a91a3;
  --text-mut:#6f6779;

  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --mono: "Space Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  color-scheme: dark;
}

@media (prefers-color-scheme: light) {
  :root {
    --panel:   #efeaec;
    --raised:  #ded7dc;
    --hair:    rgba(30, 22, 34, 0.10);
    --hair-2:  rgba(30, 22, 34, 0.17);

    --in:      #d6cbe2;
    --out:     #c2dad7;
    --ink-in:  #322b3a;
    --ink-out: #223330;
    --meta-in: rgba(50, 43, 58, 0.55);
    --meta-out:rgba(34, 51, 48, 0.55);

    --tick:    #4f71a6;
    --live:    #a2604a;
    --error:   #a3341a;

    --l-gold:  #866c2f;
    --l-rose:  #a25c6d;
    --l-sage:  #567a4f;
    --l-sky:   #45719c;
    --l-clay:  #9c5b45;

    --text:    #2a2431;
    --text-dim:#5f5768;
    --text-mut:#8a8194;

    color-scheme: light;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--panel);
  color: var(--text);
  font-family: var(--sans);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

.app {
  max-width: 600px;
  margin: 0 auto;
  padding: 0 18px 80px;
}

/* ---- composer: pinned to the top, never scrolls away ---- */
.composer {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--panel);
  padding: 16px 0 12px;
  margin-bottom: 8px;
  border-bottom: 1px solid var(--hair);
}

.bar { display: flex; align-items: center; gap: 10px; }

.bar__field {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
}
.bar__input {
  flex: 1;
  min-width: 0;
  font-family: var(--sans);
  font-size: 1rem;
  color: var(--text);
  background: var(--raised);
  border: 1px solid var(--hair-2);
  border-radius: 22px;
  padding: 10px 16px;
}
.bar__input::placeholder { color: var(--text-mut); }
.bar__input:focus { outline: none; border-color: var(--tick); }

.bar__timer {
  flex: none;
  padding: 10px 4px 10px 14px;
  font-family: var(--mono);
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
  color: var(--live);
}
.bar__timer::before { content: "REC "; letter-spacing: 0.08em; }

.bar__preview { flex: 1; min-width: 0; }
.bar__preview .player {
  background: var(--raised);
  border: 1px solid var(--hair-2);
  border-radius: 18px;
  padding: 6px 12px;
}

/* the "leave a contact to get an answer" field — slides out below the bar once
   the message clears the minimum (app.js: refreshComposer). */
.composer__contact {
  width: 100%;
  margin-top: 8px;
  font-family: var(--sans);
  font-size: 0.95rem;
  color: var(--text);
  background: var(--raised);
  border: 1px solid var(--hair-2);
  border-radius: 18px;
  padding: 9px 16px;
  animation: slideDown 0.24s ease both;
}
.composer__contact::placeholder { color: var(--text-mut); }
.composer__contact:focus { outline: none; border-color: var(--tick); }
.composer__contact[hidden] { display: none; }
@keyframes slideDown {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* live recording trace — a row of bars driven by the mic's actual level
   (app.js: startWave), jumping like a meter. Bars scale on the Y axis only
   (transform, no layout) so it's cheap to animate every frame. */
.wave {
  flex: 1;
  min-width: 0;
  align-self: stretch;
  display: flex;
  align-items: center;
  gap: 2px;
  min-height: 34px;
  margin-right: 6px;
  overflow: hidden;
}
/* `display: flex` above would otherwise beat the UA's [hidden] { display: none }
   (same specificity, later rule wins), leaving the bars in the row next to the
   text input when idle. */
.wave[hidden] { display: none; }
.wave__bar {
  flex: 1;
  min-width: 0;
  height: 70%;
  border-radius: 2px;
  background: var(--live);
  transform: scaleY(0.08);
  transform-origin: center;
}

/* one round action button, WhatsApp-composer style */
.iconbtn {
  flex: none;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid var(--hair-2);
  background: var(--raised);
  color: var(--text);
  cursor: pointer;
  transition: transform 0.1s, background 0.15s, opacity 0.15s;
}
.iconbtn:active { transform: scale(0.94); }
.iconbtn:disabled { opacity: 0.45; cursor: not-allowed; }
.iconbtn svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.iconbtn svg.fill { fill: currentColor; stroke: none; }
.iconbtn--primary { background: var(--tick); color: var(--panel); border-color: transparent; }
.iconbtn--danger { background: var(--live); color: var(--panel); border-color: transparent; }

/* ---- audio player: feed voice bubbles + record preview ---- */
.player {
  display: flex;
  align-items: center;
  gap: 10px;
}
.player__toggle {
  flex: none;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: var(--tick);
  color: var(--panel);
  cursor: pointer;
}
.player__toggle svg { width: 14px; height: 14px; fill: currentColor; }
.player__toggle:disabled { opacity: 0.5; cursor: not-allowed; }

.player__seek {
  flex: 1;
  min-width: 0;
  -webkit-appearance: none;
  appearance: none;
  height: 20px;
  background: transparent;
  cursor: pointer;
}
.player__seek::-webkit-slider-runnable-track {
  height: 3px;
  border-radius: 2px;
  background: linear-gradient(var(--tick), var(--tick)) 0 / var(--pos, 0%) 100% no-repeat, var(--hair-2);
}
.player__seek::-moz-range-track { height: 3px; border-radius: 2px; background: var(--hair-2); }
.player__seek::-moz-range-progress { height: 3px; border-radius: 2px; background: var(--tick); }
.player__seek::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 11px;
  height: 11px;
  margin-top: -4px;
  border: 0;
  border-radius: 50%;
  background: var(--tick);
}
.player__seek::-moz-range-thumb {
  width: 11px;
  height: 11px;
  border: 0;
  border-radius: 50%;
  background: var(--tick);
}
.player__seek:disabled { opacity: 0.5; cursor: progress; }

.player__time {
  flex: none;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.7rem;
  color: var(--meta-in);
}
.card--out .player__time { color: var(--meta-out); }

.player__discard {
  flex: none;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--text-mut);
  cursor: pointer;
}
.player__discard:hover { color: var(--live); }
.player__discard svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* focus — one treatment */
.bar__input:focus-visible,
.iconbtn:focus-visible,
.player__toggle:focus-visible,
.player__seek:focus-visible,
.player__discard:focus-visible {
  outline: 2px solid var(--tick);
  outline-offset: 2px;
}

.status {
  margin: 10px 4px 0;
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--text-dim);
}
.status.is-ok { color: var(--l-sage); }
.status.is-warn { color: var(--live); }
.status.is-error { color: var(--error); }

/* ---- feed ---- */
.cards {
  list-style: none;
  margin: 0;
  padding: 8px 0 0;
  display: flex;
  flex-direction: column;
}

/* every .card is a bubble */
.card {
  position: relative;
  max-width: 82%;
  margin-top: 10px;
  padding: 7px 11px 20px;
  border-radius: 18px;
  font-size: 0.975rem;
  line-height: 1.34;
  overflow-wrap: break-word;
  word-break: break-word;
  animation: rise 0.22s ease both;
}
@keyframes rise { from { opacity: 0; transform: translateY(4px); } }

/* ambient arrivals surface slowly out of nothing — a long fade up from full
   transparency, with a blur that resolves, like a memory coming back. opacity
   and filter only (no layout properties), so the card's height is final the
   instant it's inserted and prependCard's scroll compensation stays exact. */
.card--fresh { animation: emerge 3.6s ease-out both; }
@keyframes emerge {
  from { opacity: 0; filter: blur(7px); }
  to   { opacity: 1; filter: blur(0); }
}

.card--in  { align-self: flex-start; background: var(--in);  color: var(--ink-in); }
.card--out { align-self: flex-end;   background: var(--out); color: var(--ink-out); }

/* room for the lightbulb reaction chip that hangs off the bottom of the bubble;
   z-index keeps the chip above the next card should they ever graze */
.card--seeking { margin-bottom: 20px; z-index: 1; }

/* stacked follow-ups: sender-side corners tighten to a 6px column, tucked close */
.card.is-stack { margin-top: 3px; }
.card--in.is-stack  { border-top-left-radius: 6px;  border-bottom-left-radius: 6px; }
.card--out.is-stack { border-top-right-radius: 6px; border-bottom-right-radius: 6px; }

.card__text { margin: 0; white-space: pre-wrap; padding-right: 22px; }

/* ---- ⋮ per-message menu (top-right of every bubble) ---- */
/* button: muted into the bubble, inset far enough to clear the 18px corner
   radius and sit balanced (not jammed in the corner). The popup is a small dark
   card. app.js: renderCardMenu / toggleCardMenu — one menu open at a time,
   closes on outside click or Escape. */
.card__menu { position: absolute; top: 5px; right: 6px; z-index: 2; }
.card__menu-btn {
  width: 18px;
  height: 18px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--meta-in);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  opacity: 0.5;
  transition: opacity 0.12s;
}
.card--out .card__menu-btn { color: var(--meta-out); }
.card__menu-btn:hover,
.card__menu.is-open .card__menu-btn { opacity: 1; }
.card .player { padding-right: 18px; }

.card__menu-list {
  position: absolute;
  top: 22px;
  right: 0;
  min-width: 156px;
  display: flex;
  flex-direction: column;
  padding: 3px;
  border-radius: 9px;
  background: var(--raised);
  border: 1px solid var(--hair-2);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.3);
  animation: rise 0.12s ease both;
}
.card__menu-list[hidden] { display: none; }
.card__menu-item {
  display: flex;
  align-items: center;
  text-align: left;
  white-space: nowrap;
  font-family: var(--mono);
  font-size: 0.72rem;
  line-height: 1.2;
  color: var(--text);
  background: none;
  border: 0;
  border-radius: 6px;
  padding: 5px 8px;
  cursor: pointer;
}
.card__menu-item:hover { background: var(--hair); }
.card__menu-item:focus-visible { outline: 2px solid var(--tick); outline-offset: -2px; }
/* how many readers have already tapped "Никому не показывать" — right-aligned
   count, only present when > 0 (app.js sets data-count). */
.card__menu-item[data-count]::after {
  content: attr(data-count);
  margin-left: auto;
  padding-left: 12px;
  font-size: 0.62rem;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
/* last line of the menu: view count, not interactive */
.card__menu-note {
  margin-top: 2px;
  padding: 4px 8px 2px;
  border-top: 1px solid var(--hair);
  font-family: var(--mono);
  font-size: 0.66rem;
  color: var(--text-dim);
}

/* the bubble whose menu is open floats above its neighbours so the popup wins */
.card.menu-open { z-index: 30; }

.card__time {
  position: absolute;
  right: 11px;
  bottom: 6px;
  display: flex;
  align-items: center;
  gap: 3px;
  font-family: var(--mono);
  font-size: 0.6875rem;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: var(--meta-in);
}
.card--out .card__time { color: var(--meta-out); }
.tick { width: 15px; height: 10px; display: block; }
.tick path { fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }

/* ---- lightbulb reaction (messages that are seeking a response) ---- */
/* An oval chip hanging off the bottom-left of the bubble (opposite the timestamp
   + tick, which stay bottom-right, so they never touch). Its generous vertical
   padding makes the pill rounder, and it laps back onto the bubble by roughly
   that padding plus a sliver of the bulb, so it reads as fixed to the message.
   Off = light not switched on (grey, dim); on (>= 1 reader contact) = lit, with
   a soft glow that eases in. app.js: renderBulb / openBulbModal. */
.bulb {
  position: absolute;
  left: 6px;
  top: calc(100% - 8px);
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 4px 9px;
  border-radius: 999px;
  background: var(--raised);
  border: 1px solid var(--hair-2);
  line-height: 1;
}
.bulb__btn {
  border: 0;
  padding: 0;
  background: none;
  cursor: pointer;
  font-size: 0.85rem;
  line-height: 1;
  /* off: the light isn't on */
  filter: grayscale(1) brightness(0.7) opacity(0.55);
  transition: filter 0.6s ease, text-shadow 0.6s ease;
}
.bulb--on .bulb__btn,
.bulb--lighting .bulb__btn {
  filter: none;
  text-shadow: 0 0 6px rgba(255, 206, 120, 0.9), 0 0 14px rgba(255, 190, 90, 0.6);
}
.bulb--lighting .bulb__btn { animation: bulbOn 0.6s ease both; }
@keyframes bulbOn {
  from { filter: grayscale(1) brightness(0.7) opacity(0.55); text-shadow: none; }
  60%  { filter: none; text-shadow: 0 0 10px rgba(255, 214, 140, 1), 0 0 22px rgba(255, 190, 90, 0.8); }
  to   { filter: none; text-shadow: 0 0 6px rgba(255, 206, 120, 0.9), 0 0 14px rgba(255, 190, 90, 0.6); }
}
.bulb__count {
  font-family: var(--mono);
  font-size: 0.68rem;
  font-variant-numeric: tabular-nums;
  color: var(--l-gold);
}
.bulb__count:empty { display: none; }

/* ---- modal: "leave a contact to give an answer" ---- */
.modal {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.modal[hidden] { display: none; }
.modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
}
.modal__card {
  position: relative;
  width: 100%;
  max-width: 340px;
  background: var(--raised);
  border: 1px solid var(--hair-2);
  border-radius: 16px;
  padding: 18px;
  animation: rise 0.18s ease both;
}
.modal__label {
  margin: 0 0 12px;
  font-family: var(--mono);
  font-size: 0.85rem;
  color: var(--text);
}
.modal__input {
  width: 100%;
  font-family: var(--sans);
  font-size: 0.95rem;
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--hair-2);
  border-radius: 12px;
  padding: 9px 14px;
}
.modal__input:focus { outline: none; border-color: var(--tick); }
.modal__actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 14px;
}
.modal__btn {
  font-family: var(--mono);
  font-size: 0.8rem;
  color: var(--text);
  background: none;
  border: 1px solid var(--hair-2);
  border-radius: 10px;
  padding: 8px 14px;
  cursor: pointer;
}
.modal__btn--primary { background: var(--tick); color: var(--panel); border-color: transparent; }
.modal__btn:disabled { opacity: 0.45; cursor: not-allowed; }

.empty {
  text-align: center;
  color: var(--text-dim);
  font-family: var(--mono);
  font-size: 0.8rem;
  padding: 24px 12px;
}

/* ---- single-message view (/m/{id}) ---- */
/* Composer + infinite feed are hidden (app.js: bootSingle); the one bubble sits
   in the same feed column, and this is the "back to everything" link under it. */
.single-nav { text-align: center; padding: 22px 12px 48px; }
.single-nav[hidden] { display: none; }
.single-nav__btn {
  display: inline-block;
  font-family: var(--mono);
  font-size: 0.82rem;
  color: var(--text);
  text-decoration: none;
  background: var(--raised);
  border: 1px solid var(--hair-2);
  border-radius: 999px;
  padding: 10px 22px;
  transition: border-color 0.12s;
}
.single-nav__btn:hover { border-color: var(--tick); }
.single-nav__btn:focus-visible { outline: 2px solid var(--tick); outline-offset: 2px; }

.sentinel { min-height: 1px; }
.sentinel .skeleton { margin-top: 12px; }
.sentinel .empty { padding: 16px 12px; }

.skeleton {
  height: 52px;
  max-width: 60%;
  border-radius: 18px;
  background: var(--raised);
  animation: pulse 1.4s ease-in-out infinite;
}
@keyframes pulse { 50% { opacity: 0.5; } }

@media (prefers-reduced-motion: reduce) {
  .card, .card--fresh, .skeleton, .card__menu-list,
  .composer__contact, .modal__card, .bulb--lighting .bulb__btn { animation: none; }
}
