/* Vault — app shell styles. Green default; decoy stays neutral blue-grey
   so the two never read as the same product. */
:root{
  --bg:#0e1512;--panel:#151e1a;--panel2:#1c2723;--line:#253029;--line2:#1b2420;
  --ink:#e5ece7;--mute:#8ea69a;--mute2:#5f7369;
  --acc:#7fc9a0;--acc-d:#4a8a68;--on-acc:#0d2118;--amber:#c9a45e;--red:#c5726a;
  --d-bg:#eef1f6;--d-ink:#2c3440;--d-mute:#8a93a3;--d-acc:#5b7cc4;
}
*{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}
html,body{height:100%;overscroll-behavior:none}
body{font-family:Inter,system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--ink);
  -webkit-font-smoothing:antialiased;overflow:hidden}
/* Height falls through three tiers, each overriding the last where supported:
   100%    — any browser
   100dvh  — accounts for the URL bar
   --vvh   — set by the visualViewport shim, the only one that knows about the
             on-screen keyboard. top follows the viewport offset so nothing is
             ever left anchored above the glass. */
.view{position:fixed;left:0;right:0;top:var(--vvtop,0px);display:none;flex-direction:column;
  height:100%;height:100dvh;height:var(--vvh,100dvh);overscroll-behavior:none}
.view.on{display:flex}

/* ── decoy ── */

/* ── decoy skins ──────────────────────────────────────────
   The cover story should suit the phone it lives on. A player that is glaringly
   light on a phone in dark mode is itself a small oddity, so there are several
   and the admin picks one. Only the decoy changes; the app inside keeps its own
   theme entirely. */
body[data-skin="daylight"]{--d-bg:#eef1f6;--d-panel:#fff;--d-ink:#1b2430;--d-mute:#7b8798;
  --d-line:#dee3ea;--d-art1:#8fa8d8;--d-art2:#5b7cc4;--d-art3:#41608f;--d-acc:#5b7cc4}
body[data-skin="graphite"]{--d-bg:#15171a;--d-panel:#1d2024;--d-ink:#e7eaee;--d-mute:#8d95a1;
  --d-line:#2a2e34;--d-art1:#6f7f96;--d-art2:#4d5a6e;--d-art3:#333d4c;--d-acc:#8fa3bd}
body[data-skin="midnight"]{--d-bg:#0b0f18;--d-panel:#121826;--d-ink:#dfe6f2;--d-mute:#7d879b;
  --d-line:#1d2536;--d-art1:#5b6ea8;--d-art2:#3c4a7a;--d-art3:#26304f;--d-acc:#7f93cc}
body[data-skin="amber"]{--d-bg:#17130e;--d-panel:#201a13;--d-ink:#f0e7db;--d-mute:#a4968256;
  --d-mute:#a49682;--d-line:#2e261c;--d-art1:#c9924e;--d-art2:#9a6c34;--d-art3:#6d4a22;--d-acc:#d3a05c}
body[data-skin="forest"]{--d-bg:#0f1512;--d-panel:#161e19;--d-ink:#e2eae4;--d-mute:#8b9a91;
  --d-line:#222c26;--d-art1:#5f9d78;--d-art2:#417a5c;--d-art3:#2b543e;--d-acc:#6fae87}
body[data-skin="paper"]{--d-bg:#f6f3ec;--d-panel:#fffdf8;--d-ink:#2a2620;--d-mute:#877f72;
  --d-line:#e6e0d4;--d-art1:#c2a887;--d-art2:#9c7f5c;--d-art3:#6f5a3d;--d-acc:#9c7f5c}

#v-decoy{background:var(--d-bg);color:var(--d-ink);align-items:center;
  padding:calc(26px + env(safe-area-inset-top,0px)) 26px 30px;
  overflow-y:auto;overscroll-behavior:contain}
.dhead{width:100%;display:flex;align-items:center;justify-content:space-between;margin-bottom:32px}
.dhead h1{font-size:16px;font-weight:600}
.dhead .gear{font-size:15px;color:var(--d-mute);cursor:pointer;padding:6px}
.art{width:min(216px,58vw);aspect-ratio:1;border-radius:26px;margin-bottom:26px;position:relative;
  overflow:hidden;background:linear-gradient(150deg,var(--d-art1),var(--d-art2) 55%,var(--d-art3));
  box-shadow:0 16px 34px rgba(60,80,120,.28);cursor:pointer;
  display:grid;place-items:center;
  /* Without this the browser waits to see whether the touch becomes a scroll,
     and can fire pointercancel mid-press. That is what killed the long press
     on phones. */
  touch-action:none;
  user-select:none;-webkit-user-select:none;-webkit-touch-callout:none}
.art:active{transform:scale(.985)}
.art::after{content:"";position:absolute;inset:0;background:
  radial-gradient(circle at 30% 25%,rgba(255,255,255,.28),transparent 46%),
  radial-gradient(circle at 72% 78%,rgba(255,255,255,.16),transparent 42%)}
.art svg{z-index:2;opacity:.88}
.art .ring{position:absolute;inset:0;border-radius:26px;border:4px solid #fff;opacity:0;z-index:3;
  clip-path:inset(0 100% 0 0);pointer-events:none;transition:opacity .1s;
  box-shadow:0 0 18px rgba(255,255,255,.55) inset}
.art .fill{position:absolute;left:0;bottom:0;width:100%;height:0;z-index:2;
  background:rgba(255,255,255,.22);pointer-events:none;transition:height .05s linear}
.art.done{animation:flash .35s ease-out}
@keyframes flash{0%{filter:brightness(1)}40%{filter:brightness(1.9)}100%{filter:brightness(1)}}
#v-decoy h2{font-size:20px;font-weight:600;letter-spacing:-.02em}
#v-decoy .sub{font-size:13px;color:var(--d-mute);margin:5px 0 24px}
.bar{width:100%;max-width:340px;height:4px;background:#dbe1ea;border-radius:4px;overflow:hidden;margin-bottom:9px}
.bar i{display:block;height:100%;width:0;background:var(--d-acc);border-radius:4px}
.times{width:100%;max-width:340px;display:flex;justify-content:space-between;font-size:11px;
  color:var(--d-mute);font-variant-numeric:tabular-nums;margin-bottom:24px}
.ctrls{display:flex;align-items:center;gap:30px;margin-bottom:auto}
.ctrls button{background:none;border:none;color:var(--d-ink);cursor:pointer;opacity:.55;padding:6px}
.play{width:66px;height:66px;border-radius:50%;background:var(--d-acc);color:#fff!important;opacity:1!important;
  display:grid;place-items:center;box-shadow:0 8px 20px rgba(91,124,196,.4)}
.play:active{transform:scale(.95)}
.tracks{width:100%;max-width:340px;border-top:1px solid #dee3ea;padding-top:14px}
.trk{display:flex;align-items:center;gap:12px;padding:9px 2px;font-size:13px;cursor:pointer}
.trk .tn{width:30px;height:30px;border-radius:9px;background:#dde4ef;display:grid;place-items:center;
  font-size:11px;color:#6b7789;font-weight:600}
.trk .tt{flex:1}.trk .td{font-size:11px;color:var(--d-mute)}
.ver{font-size:10px;color:#b3bbc7;margin-top:14px;cursor:pointer;user-select:none;padding:6px 16px}

/* ── unlock ── */
#v-lock{justify-content:center;padding:0 34px;max-width:460px;margin:0 auto}
.back{position:fixed;top:calc(20px + env(safe-area-inset-top,0px));left:26px;color:var(--mute2);font-size:12px;cursor:pointer;padding:8px}
.mark{width:38px;height:38px;border-radius:11px;border:1.5px solid var(--acc-d);display:grid;
  place-items:center;margin-bottom:24px;color:var(--acc)}
#v-lock h2{font-size:19px;font-weight:500;letter-spacing:-.01em}
#v-lock .k{font-size:12.5px;color:var(--mute);margin:7px 0 26px;line-height:1.6}
.f{margin-bottom:14px}
.f label{display:block;font-size:11px;letter-spacing:.05em;text-transform:uppercase;
  color:var(--mute2);margin-bottom:7px}
.f input{width:100%;background:var(--panel);border:1px solid var(--line);color:var(--ink);
  padding:13px 14px;border-radius:10px;font-size:15px;font-family:inherit}
.f input:focus{outline:none;border-color:var(--acc-d);box-shadow:0 0 0 3px rgba(127,201,160,.13)}
.go{width:100%;padding:14px;border-radius:10px;background:var(--acc);color:var(--on-acc);border:none;
  font-weight:600;font-size:14px;cursor:pointer;margin-top:10px}
.go:disabled{opacity:.5}
.err{color:var(--red);font-size:12.5px;margin-top:12px;display:none}
.nostore{display:flex;gap:9px;margin-top:22px;font-size:11px;color:var(--mute2);line-height:1.6}
.nostore svg{flex:0 0 14px;margin-top:2px;color:var(--acc-d)}

/* ── app ── */
#v-app{background:var(--bg)}
/* Minimal header. Single line, so the padding and the avatar are the whole
   height. env(safe-area-inset-top) still has to be honoured or the name ends
   up under the status bar on a notched phone. */
.top{flex:0 0 auto;background:var(--panel);border-bottom:1px solid var(--line);
  padding:calc(6px + env(safe-area-inset-top,0px)) 12px 6px;
  display:flex;align-items:center;gap:9px}
.top h1{font-size:14px;font-weight:600;line-height:1.25;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.nm{flex:1;min-width:0}
/* The subtitle carried both the encryption note and the typing state. The
   line is gone; the dot below carries the first, and the typing bubble at the
   foot of the thread already carried the second. */
.sub2{display:none}
.edot{flex:0 0 7px;width:7px;height:7px;border-radius:50%;background:var(--acc);
  opacity:.8;transition:background .2s,opacity .2s}
.edot.typing{background:var(--amber);animation:edotpulse 1.1s ease-in-out infinite}
@keyframes edotpulse{0%,100%{opacity:.35}50%{opacity:1}}
.av{width:38px;height:38px;flex:0 0 38px;border-radius:12px;display:grid;place-items:center;
  background:linear-gradient(140deg,#5f9d78,#33604a);font-weight:600;font-size:14px;color:#fff}
.pill{font-size:10.5px;color:var(--amber);border:1px solid rgba(201,164,94,.35);
  background:rgba(201,164,94,.08);padding:4px 9px;border-radius:20px;font-variant-numeric:tabular-nums}
.body{flex:1;overflow-y:auto;min-height:0}
.row{display:flex;gap:12px;padding:13px 16px;border-bottom:1px solid var(--line2);
  cursor:pointer;align-items:center}
.row:hover{background:var(--panel)}
.rm{flex:1;min-width:0}.rm b{font-size:14.5px;display:block}
.rm span{font-size:12px;color:var(--mute);display:block;margin-top:3px}
.empty{text-align:center;padding:60px 32px;color:var(--mute2)}
.empty b{display:block;font-size:15px;color:var(--mute);margin-bottom:9px}
.empty p{font-size:13px;line-height:1.65}
.nav{flex:0 0 auto;display:flex;background:var(--panel);border-top:1px solid var(--line);padding:10px 0 26px}
.nav button{flex:1;background:none;border:none;color:var(--mute2);cursor:pointer;font-size:12px;
  font-family:inherit;padding:6px}
.nav button.on{color:var(--acc)}
@media(prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}

/* Masked input — looks like a password field, is not one.
   Browsers only offer to save credentials when they see input[type=password],
   so this removes the prompt entirely rather than trying to suppress it. */
.masked{
  -webkit-text-security:disc;
  text-security:disc;
  font-family:inherit;
  letter-spacing:.08em;
}
/* Fallback applied by app.js if the browser lacks text-security */
.masked.no-mask{-webkit-text-security:none;text-security:none}

/* ── thread ───────────────────────────────────────────── */
.ic{color:var(--mute);cursor:pointer;padding:5px;display:grid;place-items:center}
.av.sm{width:26px;height:26px;flex:0 0 26px;border-radius:8px;font-size:11.5px}
.av.emerald{background:linear-gradient(140deg,#5f9d78,#33604a)}
.av.violet {background:linear-gradient(140deg,#8c7ab0,#584a78)}
.av.clay   {background:linear-gradient(140deg,#b08a6a,#7a5a3f)}
.av.harbour{background:linear-gradient(140deg,#6f8fc4,#41608f)}
.av.rose   {background:linear-gradient(140deg,#b07a88,#7a4a56)}
.av.teal   {background:linear-gradient(140deg,#4f9fa2,#2d6669)}
.sub2.typing{color:var(--acc)}
.burnbar{flex:0 0 auto;background:rgba(201,164,94,.08);border-bottom:1px solid rgba(201,164,94,.2);
  color:var(--amber);font-size:10.5px;padding:6px 16px;display:flex;align-items:center;gap:7px}
/* The ONLY thing in the thread that scrolls.
   overscroll-behavior:contain is the important word: without it, hitting the
   top or bottom of this list chains the scroll out to the page and drags the
   header and composer with it. That is what made the whole screen move. */
.msgs{flex:1;overflow-y:auto;padding:16px 13px;display:flex;flex-direction:column;gap:9px;min-height:0;
  overscroll-behavior:contain;-webkit-overflow-scrolling:touch;
  background-image:var(--wall,none);background-size:var(--wsize,cover);
  background-repeat:var(--wrep,no-repeat);background-position:center}
/* Bubbles with a tail on the last message of a run, as WhatsApp does. The
   tail is a pseudo-element triangle rather than an image, so it takes the
   bubble colour from the theme automatically. */
.m{max-width:78%;padding:8px 11px 7px;border-radius:14px;
  font-size:var(--msg-size,16px);line-height:var(--msg-line,23px);
  position:relative;cursor:pointer;user-select:none;word-wrap:break-word;
  box-shadow:0 1px 1px rgba(0,0,0,.22)}
.m.them{background:var(--bub-them);align-self:flex-start;border-top-left-radius:5px}
.m.me{background:var(--bub-me);align-self:flex-end;border-top-right-radius:5px}

/* only the first bubble of a run carries the tail */
.m.tail.them::before,
.m.tail.me::before{content:"";position:absolute;top:0;width:11px;height:14px}
.m.tail.them::before{left:-8px;
  background:radial-gradient(circle at 100% 0,transparent 11px,var(--bub-them) 11px)}
.m.tail.me::before{right:-8px;
  background:radial-gradient(circle at 0 0,transparent 11px,var(--bub-me) 11px)}

/* grouped messages sit closer together */
.m.grouped{margin-top:-5px}
.m.hl{box-shadow:inset 0 0 0 1px rgba(201,164,94,.45);background:rgba(201,164,94,.14)}
.m.retained{background:rgba(201,164,94,.08);border:1px dashed rgba(201,164,94,.45)}
.m .tx{white-space:pre-wrap}
.m .ts{font-size:10px;color:var(--mute2);margin-top:2px;text-align:right;
  opacity:.8;user-select:none}
.m.them .ts{text-align:left}
.m .star{color:var(--amber);margin-right:4px}
.rtag{display:block;font-size:9px;letter-spacing:.04em;text-transform:uppercase;
  color:var(--amber);margin-bottom:5px}
.m .rep{border-left:2px solid var(--acc-d);padding:5px 8px;margin:-2px 0 6px;
  background:rgba(0,0,0,.22);border-radius:6px;font-size:12px;color:var(--mute)}
.m .rep b{display:block;color:var(--acc);font-size:10.5px;margin-bottom:2px}
.react{position:absolute;bottom:-9px;background:var(--panel2);border:1px solid var(--line);
  border-radius:20px;padding:1px 6px;font-size:11px}
.m.me .react{left:8px}.m.them .react{right:8px}
.loading{text-align:center;padding:40px;color:var(--mute2);font-size:13px}

.composer{flex:0 0 auto;border-top:1px solid var(--line);background:var(--panel);
  padding-bottom:env(safe-area-inset-bottom,0px)}
.replybar{display:none;padding:9px 13px;border-bottom:1px solid var(--line2);align-items:center;gap:10px}
.replybar.on{display:flex}
.replybar .q{flex:1;min-width:0;border-left:2px solid var(--acc);padding-left:9px}
.replybar .q b{display:block;font-size:10.5px;color:var(--acc);margin-bottom:2px}
.replybar .q span{font-size:12px;color:var(--mute);display:block;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.replybar .x{color:var(--mute2);cursor:pointer;padding:4px}
.crow{padding:9px 11px 24px;display:flex;gap:8px;align-items:center}
.crow input{flex:1;background:var(--bg);border:1px solid var(--line);color:var(--ink);
  padding:11px 14px;border-radius:22px;font-size:15px;font-family:inherit;min-width:0}
.crow input:focus{outline:none;border-color:var(--acc-d)}
.send{width:40px;height:40px;flex:0 0 40px;border-radius:50%;background:var(--acc);border:none;
  display:grid;place-items:center;color:var(--on-acc);cursor:pointer;
  user-select:none;-webkit-user-select:none;-webkit-tap-highlight-color:transparent}
.send:active{transform:scale(.93)}

/* ── PIN ──────────────────────────────────────────────── */
#v-pin{justify-content:center;align-items:center;padding:0 34px;text-align:center}
#v-pin h2{font-size:19px;font-weight:600}
#v-pin .k{font-size:13px;color:var(--mute);margin:8px 0 22px}
.pinrow{display:flex;gap:10px;justify-content:center;margin-bottom:16px}
.pinrow input{width:48px;height:58px;background:var(--panel);border:1px solid var(--line);
  border-radius:12px;color:var(--ink);font-size:24px;text-align:center;font-family:inherit}
.pinrow input:focus{outline:none;border-color:var(--acc);box-shadow:0 0 0 3px rgba(127,201,160,.14)}
.linkbtn{font-size:12.5px;color:var(--mute);cursor:pointer;padding:10px;text-decoration:underline}

/* ── admin ────────────────────────────────────────────── */
/* Seven admin tabs will not fit a phone. They scroll sideways rather than
   squashing to an unreadable width, with a fade at the edge so it is obvious
   there is more. */
.tabs{flex:0 0 auto;display:flex;background:var(--panel);border-bottom:1px solid var(--line);
  overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch;position:relative}
.tabs::-webkit-scrollbar{display:none}
.tabs button{flex:0 0 auto;padding:11px 15px;background:none;border:none;color:var(--mute2);
  font-size:12.5px;cursor:pointer;border-bottom:2px solid transparent;font-family:inherit;
  white-space:nowrap}
.tabwrap{position:relative;flex:0 0 auto}
.tabwrap::after{content:"";position:absolute;top:0;right:0;bottom:2px;width:26px;pointer-events:none;
  background:linear-gradient(90deg,transparent,var(--panel))}
.tabs button.on{color:var(--acc);border-bottom-color:var(--acc)}
.pad{padding:14px 16px}
.card{background:var(--panel);border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
.urow{display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid var(--line2);
  cursor:pointer}
.urow:last-child{border-bottom:none}
.urow:hover{background:var(--panel2)}
.tag{font-size:9.5px;padding:2px 8px;border-radius:5px;border:1px solid var(--line);color:var(--mute)}
.tag.active{color:var(--acc);border-color:var(--acc-d)}
.tag.suspended{color:var(--amber);border-color:rgba(201,164,94,.4)}
.status{margin:14px 16px;border-radius:13px;padding:14px;border:1px solid}
.status.ok{background:rgba(127,201,160,.08);border-color:rgba(127,201,160,.28)}
.status.warn{background:rgba(201,164,94,.08);border-color:rgba(201,164,94,.3)}
.status b{display:block;font-size:13.5px;margin-bottom:6px}
.status.ok b{color:var(--acc)} .status.warn b{color:var(--amber)}
.status p{font-size:12px;color:var(--mute);line-height:1.55}
.note{margin:14px 16px;padding:12px 13px;border-radius:11px;font-size:11.5px;line-height:1.6;
  background:rgba(127,201,160,.07);border:1px solid var(--acc-d);color:var(--mute)}
.btn{width:100%;padding:13px;border-radius:11px;background:var(--acc);color:var(--on-acc);
  border:none;font-weight:600;font-size:14px;cursor:pointer;font-family:inherit}
.btn.ghost{background:transparent;border:1px solid var(--line);color:var(--ink)}
.btn:disabled{opacity:.45}

/* ── sheet ────────────────────────────────────────────── */
.sheet{position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:70;display:none;align-items:flex-end}
.sheet.on{display:flex}
.sinner{width:100%;max-height:88%;overflow-y:auto;background:var(--panel);
  border-radius:20px 20px 0 0;padding:16px 0 26px;border-top:1px solid var(--line);
  animation:up .2s ease-out}
@keyframes up{from{transform:translateY(28px);opacity:0}to{transform:none;opacity:1}}
.sinner h4{font-size:15px;padding:0 18px 6px}
.sinner .sp{font-size:12.5px;color:var(--mute);padding:0 18px 14px;line-height:1.6}
.sinner .lb{display:block;font-size:11px;text-transform:uppercase;letter-spacing:.05em;
  color:var(--mute2);padding:8px 18px 6px}
.f-in{width:calc(100% - 36px);margin:0 18px;background:var(--bg);border:1px solid var(--line);
  color:var(--ink);padding:12px 13px;border-radius:10px;font-size:15px;font-family:inherit}
.f-in:focus{outline:none;border-color:var(--acc-d)}
.sinner .btn{width:calc(100% - 36px);margin:14px 18px 0}
.sinner .err{margin:10px 18px 0}
.reacts{display:flex;justify-content:space-around;padding:4px 16px 12px;border-bottom:1px solid var(--line2)}
.reacts span{font-size:25px;cursor:pointer;padding:4px}
.opt{padding:13px 20px;font-size:14.5px;cursor:pointer}
.opt:hover{background:var(--panel2)}
.opt.red{color:var(--red)}

/* ── toast ────────────────────────────────────────────── */
.toast{position:fixed;bottom:90px;left:50%;transform:translateX(-50%);background:var(--panel2);
  border:1px solid var(--line);color:var(--ink);font-size:13px;padding:11px 18px;border-radius:22px;
  z-index:90;opacity:0;pointer-events:none;transition:opacity .25s;white-space:nowrap}
.toast.on{opacity:1}

.grp h3.sect{font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;
  color:var(--mute2);padding:16px 16px 8px;margin:0}
.note code{background:rgba(0,0,0,.3);padding:1px 5px;border-radius:4px;font-size:11px;
  font-family:ui-monospace,monospace}

/* Two unlabelled doors on the unlock screen. Identical styling on purpose —
   nothing distinguishes them but the word, and only you know which is which. */
.doors{display:flex;gap:8px;margin-bottom:22px}
.doors .door{flex:1;padding:10px;border-radius:10px;background:var(--panel);
  border:1px solid var(--line);color:var(--mute);font-family:inherit;font-size:13.5px;
  cursor:pointer;transition:border-color .15s,color .15s}
.doors .door.on{border-color:var(--acc-d);color:var(--ink)}

.fixes{margin:0;padding-left:18px;list-style:disc}
.fixes li{font-size:12.5px;line-height:1.65;color:var(--mute);margin-bottom:6px}
.fixes li::marker{color:var(--acc-d)}

/* ── media ────────────────────────────────────────────── */
.cic{color:var(--mute);cursor:pointer;padding:4px;display:grid;place-items:center;flex:0 0 auto}
.cic:active{color:var(--acc)}
.cic.rec{color:var(--red)}
.recbar{display:none;align-items:center;gap:9px;padding:9px 14px;border-top:1px solid var(--line2);
  font-size:12.5px;color:var(--red)}
.recbar.on{display:flex}
.recbar .dot{width:9px;height:9px;border-radius:50%;background:var(--red);animation:pulse 1.1s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}
.recbar .rcancel{color:var(--mute);cursor:pointer;text-decoration:underline}

.m .mediabox{width:190px;height:auto;border-radius:11px;overflow:hidden;margin:-2px 0 5px;
  background:rgba(0,0,0,.25);display:block;cursor:pointer}
.m .mediabox img{width:100%;display:block}
.m .thumbph{width:190px;height:132px;display:grid;place-items:center;color:var(--mute2)}
.voice{display:flex;align-items:center;gap:10px;min-width:170px}
.voice .pl{width:30px;height:30px;flex:0 0 30px;border-radius:50%;background:var(--acc);
  color:var(--on-acc);display:grid;place-items:center;cursor:pointer}
.voice .wave{flex:1;display:flex;align-items:center;gap:2px;height:22px}
.voice .wave i{width:2.5px;background:var(--acc-d);border-radius:2px}
.voice .d{font-size:10.5px;color:var(--mute);font-variant-numeric:tabular-nums}

.viewer{position:fixed;inset:0;background:#07090a;z-index:80;display:none;flex-direction:column}
.viewer.on{display:flex}
.vtop{flex:0 0 auto;display:flex;align-items:center;gap:11px;
  padding:calc(12px + env(safe-area-inset-top,0px)) 16px 12px}
.vtop .vt{flex:1;font-size:13px;color:#cfd8d3}

/* Close — legible over any photo.
   A bare white stroke vanishes against a bright image, which is why this one
   sits on its own dimmed disc. 40px is the tap target; the glyph inside is
   20px, the proportion WhatsApp uses. */
.vx{flex:0 0 40px;width:40px;height:40px;border-radius:50%;display:grid;place-items:center;
  cursor:pointer;color:#fff;background:rgba(12,14,15,.52);
  -webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);
  border:1px solid rgba(255,255,255,.13);transition:background .14s,transform .14s}
.vx:hover{background:rgba(12,14,15,.72)}
.vx:active{transform:scale(.92);background:rgba(12,14,15,.8)}
.vstage{flex:1;display:grid;place-items:center;padding:0 14px;min-height:0;
  user-select:none;-webkit-user-select:none;-webkit-touch-callout:none}
.vstage img{max-width:100%;max-height:100%;border-radius:12px;pointer-events:none}
.vfoot{flex:0 0 auto;padding:14px 18px 30px}
.memnote{background:rgba(127,201,160,.1);border:1px solid rgba(127,201,160,.26);border-radius:11px;
  padding:12px 13px;display:flex;gap:10px;align-items:flex-start}
.memnote svg{flex:0 0 15px;color:var(--acc);margin-top:1px}
.memnote b{display:block;font-size:11.5px;color:#a9c7b6;margin-bottom:3px}
.memnote span{font-size:11px;color:#8fae9d;line-height:1.5}

/* ── device approval ──────────────────────────────────── */
#v-code{justify-content:center;align-items:center;padding:0 30px;text-align:center}
#v-code h2{font-size:19px;font-weight:600}
#v-code .k{font-size:13px;color:var(--mute);margin:8px 0 22px;line-height:1.6}
#coderow{display:flex;gap:7px;justify-content:center;margin-bottom:16px}
#coderow input{width:44px;height:56px;background:var(--panel);border:1px solid var(--line);
  border-radius:11px;color:var(--ink);font-size:22px;text-align:center;font-family:inherit;
  font-variant-numeric:tabular-nums}
#coderow input:focus{outline:none;border-color:var(--acc);box-shadow:0 0 0 3px rgba(127,201,160,.14)}
.codebig{font-family:ui-monospace,monospace;font-size:34px;font-weight:700;letter-spacing:.1em;
  color:var(--acc);text-align:center;padding:18px 0 6px}
.chal{margin:0 18px 14px;background:var(--bg);border:1px solid var(--acc-d);border-radius:12px;padding:14px}
.chal .cl{font-size:10px;letter-spacing:.09em;text-transform:uppercase;color:var(--acc);margin-bottom:8px}
.chal .cq{font-size:15px;font-weight:500;line-height:1.5;margin-bottom:12px}
.chal .ca{background:var(--panel);border:1px dashed var(--line);border-radius:9px;padding:12px;
  text-align:center;cursor:pointer}
.chal .ca b{display:none;font-family:ui-monospace,monospace;font-size:16px;color:var(--acc)}
.chal .ca span{font-size:12px;color:var(--mute2)}
.chal .ca.open b{display:block}.chal .ca.open span{display:none}
.prow{display:flex;align-items:center;gap:12px;padding:12px 18px;border-top:1px solid var(--line2)}
.prow .m{flex:1;min-width:0}
.prow .m b{font-size:13.5px;font-weight:500;display:block}
.prow .m span{font-size:11px;color:var(--mute2);display:block;margin-top:2px;line-height:1.45}
.sw{width:44px;height:26px;flex:0 0 44px;border-radius:14px;background:var(--line);position:relative;
  cursor:pointer;transition:background .18s}
.sw::after{content:"";position:absolute;top:3px;left:3px;width:20px;height:20px;border-radius:50%;
  background:#7d9187;transition:.18s}
.sw.on{background:var(--acc)}.sw.on::after{left:21px;background:var(--on-acc)}
.status.warn{background:rgba(201,164,94,.08);border-color:rgba(201,164,94,.3)}
.status.warn b{color:var(--amber)}

/* Collapsible release history inside the Update tab */
.rel{border-bottom:1px solid var(--line2);background:var(--panel)}
.relhead{display:flex;align-items:baseline;gap:9px;padding:13px 16px;cursor:pointer}
.relhead b{font-family:'Sora';font-size:13px;color:var(--acc);flex:0 0 auto}
.relhead span{flex:1;font-size:13px;min-width:0}
.relhead em{font-style:normal;font-size:10.5px;color:var(--mute2)}
.rel .fixes{display:none;padding:0 16px 14px 34px;margin:0}
.rel.open .fixes{display:block}
.rel.open .relhead b{color:var(--ink)}

/* ── theme picker ─────────────────────────────────────── */
.tgrid{display:grid;grid-template-columns:1fr 1fr;gap:11px;padding:0 14px 8px}
.tc{background:var(--panel);border:1.5px solid var(--line);border-radius:15px;padding:10px;cursor:pointer;
  transition:border-color .15s}
.tc.on{border-color:var(--acc)}
.tprev{border-radius:11px;overflow:hidden;margin-bottom:9px;height:74px;padding:9px;display:flex;
  flex-direction:column;justify-content:flex-end;gap:5px;background-position:center}
.tprev i{display:block;height:12px;border-radius:6px}
.tprev i.a{width:60%;align-self:flex-start}
.tprev i.b{width:50%;align-self:flex-end}
.tprev i.c{width:22%;height:5px;align-self:flex-start;margin-top:2px}
.tc b{font-size:13px;font-weight:500;display:block}
.tc span{font-size:10.5px;color:var(--mute2)}

/* ── emoji ────────────────────────────────────────────── */
#esheet .einner{width:100%;height:52%;background:var(--panel);border-radius:20px 20px 0 0;
  border-top:1px solid var(--line);display:flex;flex-direction:column;animation:up .2s ease-out}
.grab{width:36px;height:4px;background:var(--line);border-radius:4px;margin:8px auto;flex:0 0 auto}
.egrid{flex:1;overflow-y:auto;padding:2px 10px 8px;display:grid;grid-template-columns:repeat(8,1fr);gap:2px}
.egrid span{font-size:25px;text-align:center;padding:5px 0;cursor:pointer;border-radius:9px}
.egrid span:hover{background:var(--panel2)}
.ecats{flex:0 0 auto;display:flex;border-top:1px solid var(--line);padding:7px 6px 22px;overflow-x:auto;gap:2px}
.ecats button{background:none;border:none;font-size:19px;cursor:pointer;padding:6px 9px;border-radius:9px;
  opacity:.45;flex:0 0 auto}
.ecats button.on{opacity:1;background:var(--panel2)}
.m.big{font-size:36px;line-height:1.2;background:none!important;padding:2px 6px}

/* Testing-mode banner. Deliberately loud — a relaxed security setting you
   cannot see is one you will forget about. */
.testbar{position:fixed;top:0;left:0;right:0;z-index:200;display:none;
  align-items:center;gap:10px;padding:8px 14px;
  background:repeating-linear-gradient(45deg,#3a2c12,#3a2c12 10px,#42320f 10px,#42320f 20px);
  border-bottom:1px solid var(--amber);color:#f0d9a8;font-size:12px}
.testbar.on{display:flex}
.testbar b{font-family:'Sora';font-size:12px;color:var(--amber)}
.testbar span{flex:1;font-variant-numeric:tabular-nums;opacity:.85}
.testbar u{cursor:pointer;text-decoration:underline}
body.testing .view{padding-top:32px}
body.testing #v-decoy{padding-top:calc(58px + env(safe-area-inset-top,0px))}

/* Self-test rows */
.chk{display:flex;align-items:flex-start;gap:12px;padding:12px 16px;border-bottom:1px solid var(--line2)}
.chk:last-child{border-bottom:none}
.chk .ci{width:22px;height:22px;flex:0 0 22px;border-radius:50%;display:grid;place-items:center;
  font-size:12px;font-weight:700;margin-top:1px}
.chk.pass   .ci{background:rgba(127,201,160,.15);color:var(--acc)}
.chk.fail   .ci{background:rgba(197,114,106,.15);color:var(--red)}
.chk.warn   .ci{background:rgba(201,164,94,.15);color:var(--amber)}
.chk.manual .ci{background:var(--panel2);color:var(--mute2)}
.chk .rm b{font-size:13.5px;font-weight:500;display:block}
.chk .rm span{font-size:11.5px;color:var(--mute);display:block;margin-top:3px;line-height:1.5}
.status.bad{background:rgba(197,114,106,.08);border-color:rgba(197,114,106,.32)}
.status.bad b{color:var(--red)}

.olderbar{align-self:center;font-size:10.5px;color:var(--mute2);padding:8px 0 4px}

.whybox{display:none;text-align:left;margin:14px auto 0;max-width:330px;background:var(--panel);
  border:1px solid var(--line);border-radius:11px;padding:12px 13px;font-size:11.5px;line-height:1.7;
  color:var(--mute);font-family:ui-monospace,monospace}
.whybox.on{display:block}
.whybox b{color:var(--ink);font-family:inherit}
.whybox .no{color:var(--red)}
.whybox .yes{color:var(--acc)}

/* ── typing indicator ─────────────────────────────────────
   Three dots that rise in sequence. Deliberately understated — it should read
   as someone thinking, not as an animation demanding attention. */
.typing-bub{padding:12px 14px!important;display:inline-flex;align-items:center;
  min-height:0;width:auto;align-self:flex-start}
.dots{display:inline-flex;gap:4px;align-items:flex-end;height:9px}
.dots i{width:6px;height:6px;border-radius:50%;background:var(--mute);display:block;
  animation:typedot 1.25s infinite ease-in-out}
.dots i:nth-child(2){animation-delay:.16s}
.dots i:nth-child(3){animation-delay:.32s}
@keyframes typedot{
  0%,60%,100%{transform:translateY(0);opacity:.42}
  30%{transform:translateY(-5px);opacity:1}
}
.sub2.typing{color:var(--acc)}
@media(prefers-reduced-motion:reduce){
  .dots i{animation:none;opacity:.7}
}

/* ── unread ───────────────────────────────────────────────
   A count, not a colour wash. The row should read normally at a glance and
   only the number should draw the eye. */
.badge{flex:0 0 auto;min-width:20px;height:20px;padding:0 6px;border-radius:10px;
  background:var(--acc);color:var(--on-acc);font-size:11px;font-weight:700;
  display:grid;place-items:center;font-variant-numeric:tabular-nums}
.row.unread .rm b{font-weight:600}
.row.unread .rm span{color:var(--acc)}

/* ── attachment menu ──────────────────────────────────── */
.attgrid{display:grid;grid-template-columns:1fr 1fr;gap:11px;padding:4px 18px 6px}
.attitem{background:var(--bg);border:1px solid var(--line);border-radius:14px;padding:16px 12px;
  display:flex;flex-direction:column;align-items:center;gap:9px;cursor:pointer;text-align:center}
.attitem:hover{border-color:var(--acc-d)}
.attitem .ai{width:42px;height:42px;border-radius:50%;display:grid;place-items:center;color:#fff}
.attitem.doc .ai{background:linear-gradient(140deg,#6f8fc4,#41608f)}
.attitem.img .ai{background:linear-gradient(140deg,#8c7ab0,#584a78)}
.attitem.vid .ai{background:linear-gradient(140deg,#b07a88,#7a4a56)}
.attitem.loc .ai{background:linear-gradient(140deg,#5f9d78,#33604a)}
.attitem b{font-size:13.5px;font-weight:500}
.attitem span{font-size:10.5px;color:var(--mute2);line-height:1.4}

/* ── file and location bubbles ────────────────────────── */
.filebox{display:flex;align-items:center;gap:11px;min-width:190px;cursor:pointer}
.filebox .fi{width:38px;height:38px;flex:0 0 38px;border-radius:10px;display:grid;place-items:center;
  background:rgba(255,255,255,.09);color:var(--ink)}
.filebox .fm{min-width:0;flex:1}
.filebox .fm b{display:block;font-size:13px;font-weight:500;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.filebox .fm span{font-size:10.5px;color:var(--mute2)}
.locbox{min-width:200px;cursor:pointer}
.locmap{height:96px;border-radius:10px;margin-bottom:7px;position:relative;overflow:hidden;
  background:
    linear-gradient(0deg,rgba(127,201,160,.08) 1px,transparent 1px) 0 0/100% 16px,
    linear-gradient(90deg,rgba(127,201,160,.08) 1px,transparent 1px) 0 0/16px 100%,
    linear-gradient(140deg,#16221d,#101a16);
  display:grid;place-items:center}
.locmap svg{color:var(--acc);filter:drop-shadow(0 3px 5px rgba(0,0,0,.5))}
.loccoord{font-family:ui-monospace,monospace;font-size:11px;color:var(--mute)}

.vidbox{width:210px;border-radius:11px;overflow:hidden;position:relative;cursor:pointer;
  background:#0b100e;display:block}
.vidbox .vph{height:130px;display:grid;place-items:center;color:rgba(255,255,255,.28)}
.vidplay{position:absolute;inset:0;display:grid;place-items:center}
.vidplay i{width:44px;height:44px;border-radius:50%;background:rgba(0,0,0,.55);display:grid;
  place-items:center;color:#fff;font-style:normal}
.vstage video{max-width:100%;max-height:100%;border-radius:12px;background:#000}
.vstage iframe{width:100%;height:100%;border:0;border-radius:12px;background:#fff}

/* ── add to home screen ───────────────────────────────── */
.installcard{position:fixed;left:14px;right:14px;bottom:16px;z-index:120;display:none;
  background:var(--panel);border:1px solid var(--acc-d);border-radius:15px;padding:14px;
  box-shadow:0 14px 40px rgba(0,0,0,.55);align-items:flex-start;gap:12px;max-width:420px;margin:0 auto}
.installcard.on{display:flex}
.ic-ico{width:38px;height:38px;flex:0 0 38px;border-radius:11px;background:rgba(127,201,160,.13);
  border:1px solid var(--acc-d);display:grid;place-items:center;color:var(--acc)}
.ic-txt{flex:1;min-width:0}
.ic-txt b{display:block;font-size:13.5px;margin-bottom:3px}
.ic-txt span{font-size:11.5px;color:var(--mute);line-height:1.55;display:block}
.ic-act{display:flex;flex-direction:column;gap:7px;align-items:flex-end;flex:0 0 auto}
.ic-go{background:var(--acc);color:var(--on-acc);border:none;border-radius:9px;padding:8px 14px;
  font-family:inherit;font-weight:600;font-size:12.5px;cursor:pointer}
.ic-no{font-size:11px;color:var(--mute2);cursor:pointer}

.doctext{width:100%;max-height:100%;overflow:auto;background:#0d1310;border:1px solid var(--line);
  border-radius:12px;padding:16px;font-family:ui-monospace,monospace;font-size:12.5px;
  line-height:1.7;color:#c7d6ce;white-space:pre-wrap;word-break:break-word}

/* ── text size control ────────────────────────────────── */
.fontrow{display:flex;align-items:center;gap:14px;padding:16px 16px 10px}
.fstep{width:44px;height:44px;flex:0 0 44px;border-radius:12px;background:var(--bg);
  border:1px solid var(--line);display:grid;place-items:center;cursor:pointer;
  font-size:17px;font-weight:600;user-select:none}
.fstep:active{border-color:var(--acc)}
.fstep small{font-size:11px;margin-left:1px}
.fbar{flex:1;height:5px;background:var(--line);border-radius:5px;overflow:hidden}
.ffill{height:100%;background:var(--acc);border-radius:5px;transition:width .16s}
.fsample{margin:0 16px 8px;padding:13px 14px;background:var(--bub-them);border-radius:14px;
  border-top-left-radius:5px;color:var(--ink)}
.flabel{font-size:11px;color:var(--mute2);text-align:center;padding:0 0 16px}
.eempty{grid-column:1/-1;text-align:center;padding:40px 20px;color:var(--mute2);
  font-size:12.5px;line-height:1.6}
.ecats button{font-size:18px;padding:6px 8px}

/* ── read receipts ────────────────────────────────────────
   Grey when it arrived, blue when it was read. The shape carries the meaning
   for anyone who cannot distinguish the colours. */
.ticks{display:inline-flex;vertical-align:-2px;margin-left:3px;color:var(--mute2)}
.ticks.read{color:#4aa3f0}
.ticks svg{display:block}

/* ── in-conversation search ──────────────────────────────
   Runs entirely on messages already decrypted in memory. Nothing is sent to
   the server, so searching cannot leak what you searched for. */
/* Pinned. It sits above the message list and must stay reachable the whole
   time you are searching — never scrolled away behind the results. */
.searchbar{flex:0 0 auto;display:none;align-items:center;gap:9px;padding:9px 12px;
  background:var(--panel);border-bottom:1px solid var(--line);
  position:sticky;top:0;z-index:14;box-shadow:0 3px 10px rgba(0,0,0,.16)}
.searchbar.on{display:flex}
.searchbar input{flex:1;background:var(--bg);border:1px solid var(--line);color:var(--ink);
  padding:9px 13px;border-radius:20px;font-size:14px;font-family:inherit;min-width:0}
.searchbar input:focus{outline:none;border-color:var(--acc-d)}
.scount{font-size:11.5px;color:var(--mute2);font-variant-numeric:tabular-nums;flex:0 0 auto;
  min-width:52px;text-align:center}
.snav{color:var(--mute);cursor:pointer;padding:5px;display:grid;place-items:center;flex:0 0 auto}
.snav.off{opacity:.3;pointer-events:none}
mark{background:rgba(201,164,94,.34);color:inherit;border-radius:3px;padding:0 1px}
.m.hit{box-shadow:0 0 0 2px var(--amber)}
.m.hit.current{box-shadow:0 0 0 2px var(--acc);background:rgba(127,201,160,.14)}

/* ── suggested theme card ─────────────────────────────── */
.themecard{display:flex;gap:11px;align-items:center;min-width:212px}
.tcp{width:52px;height:52px;flex:0 0 52px;border-radius:11px;padding:7px;display:flex;
  flex-direction:column;justify-content:flex-end;gap:4px;background-position:center;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.07)}
.tcp i{display:block;height:8px;border-radius:4px;width:70%}
.tcp i.r{width:52%;align-self:flex-end}
.tcm{min-width:0;flex:1}
.tcm b{display:block;font-size:12.5px;font-weight:500;line-height:1.4}
.tcm span{font-size:10.5px;color:var(--mute2);display:block;margin-top:1px}
/* Two distinct buttons, not underlined text. An offer you can accept or
   decline should look like two things you can press, and the decline must be
   as easy to hit as the accept. */
.tcbtns{display:flex;gap:8px;margin-top:9px}
.tcb{font-size:11.5px;font-family:inherit;font-weight:500;line-height:1;
  padding:7px 13px;border-radius:14px;cursor:pointer;border:1px solid transparent;
  white-space:nowrap;transition:filter .14s,transform .12s}
.tcb:active{transform:scale(.96)}
.tcb.yes{background:var(--acc);color:#0b1210}
.tcb.yes:hover{filter:brightness(1.08)}
.tcb.no{background:transparent;color:var(--mute);border-color:var(--line)}
.tcb.no:hover{color:var(--ink);border-color:var(--mute2)}
.tcok{font-style:normal;font-size:10.5px;color:var(--acc);display:block;margin-top:5px}
.tcno{font-style:normal;font-size:10.5px;color:var(--mute2)}

/* Actions under whichever theme was just tapped */
.ctacts{grid-column:1/-1;display:flex;flex-direction:column;gap:8px;
  padding:4px 0 10px;animation:fadein .16s ease-out}
@keyframes fadein{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
.ctacts .btn{width:100%;margin:0}

/* ── decoy skin picker ────────────────────────────────── */
.skins{display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;padding:0 14px 8px}
.skin{background:var(--panel);border:1.5px solid var(--line);border-radius:13px;padding:8px;
  cursor:pointer;text-align:center}
.skin.on{border-color:var(--acc)}
.sprev{height:74px;border-radius:9px;margin-bottom:8px;position:relative;overflow:hidden;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;padding:8px}
.sart{width:30px;height:30px;border-radius:8px;display:block}
.sline{width:38px;height:4px;border-radius:3px;display:block}
.sline.sm{width:24px;height:3px}
.sbar{width:44px;height:3px;border-radius:3px;display:block;margin-top:2px}
.skin b{font-size:11.5px;font-weight:500;display:block}
.skin span{font-size:9.5px;color:var(--mute2)}
