/* We.Cam V2 — one stylesheet, themed entirely through CSS variables (skins.js).
   Signature: the privacy dial. Key material renders in --mono as identity jewelry. */

:root {
  --bg: #12141c; --bg2: #181b25; --bg3: #202433;
  --fg: #e8e6df; --fg-dim: #9c9b93;
  --accent: #e0a458; --accent-fg: #1a1206;
  --danger: #e5484d; --ok: #4cc38a;
  --radius: 10px; --density: 1; --font-scale: 1;
  --sans: system-ui, "Segoe UI", sans-serif;
  --mono: ui-monospace, "Cascadia Code", Consolas, monospace;
  --line: color-mix(in srgb, var(--fg) 9%, transparent);
}

* { box-sizing: border-box; }
html { font-size: calc(16px * var(--font-scale)); }
body { margin: 0; background: var(--bg); color: var(--fg); font-family: var(--sans); line-height: 1.55; }
/* The shell sizes itself to the viewport and scrolls internally, so the document behind it
   must not scroll: any document scroll is the UA panning the page to reveal a focused input
   (the mobile-keyboard jump). ui.js mount() sets .app-shell only while a shell is mounted —
   the landing/onboarding/docs pages keep normal page scrolling. */
html.app-shell, html.app-shell body { height: 100%; overflow: hidden; overscroll-behavior: none; }
/* Text controls never render below 16px on a phone: iOS Safari auto-zooms the whole page on
   focus below that threshold, which reads as a violent jump the moment the keyboard opens.
   The Terminal skin ships --font-scale .95 (15.2px), so this is reachable in stock config.
   max() keeps larger scales and larger explicit sizes intact. */
@media (max-width: 700px) {
  input, textarea, select { font-size: max(16px, 1em); }
}
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; }
}

h1 { font-size: 1.55rem; font-weight: 650; margin: 0 0 .25rem; letter-spacing: -.01em; }
h2 { font-size: 1.05rem; font-weight: 600; margin: 0 0 .5rem; }
p { margin: .25rem 0; }
.dim { color: var(--fg-dim); }
.eyebrow { font-family: var(--mono); font-size: .68rem; letter-spacing: .16em; text-transform: uppercase; color: var(--fg-dim); margin: 0 0 .5rem; }
/* Spec §2.3 gives every screen an <h1>, and on the screens whose title IS the eyebrow label
   (feed, settings) the heading wears .eyebrow. The class already outranks h1's type selector
   on font-size, letter-spacing and margin — font-weight is the ONE h1 declaration .eyebrow
   does not answer, so without this the label would render at 650 instead of inheriting 400.
   Measured, not assumed. h2.eyebrow (docs.js section headings) is deliberately left alone. */
h1.eyebrow { font-weight: inherit; }
/* Community's <h1> is the community name, which sits in the list pane at h2 size by design —
   the chat pane's channel name is the h2 under it. Same metrics as h2, different level. */
.pane-title { font-size: 1.05rem; font-weight: 600; letter-spacing: 0; }

.brand { font-family: var(--mono); font-weight: 600; letter-spacing: .04em; font-size: 1.05rem; }
.brand .dot { color: var(--accent); }
.boot { min-height: 100vh; min-height: 100dvh; display: grid; place-items: center; }

/* controls */
.btn { font: inherit; color: var(--fg); background: var(--bg3); border: 1px solid var(--line);
  border-radius: var(--radius); padding: calc(.55rem * var(--density)) calc(1rem * var(--density));
  cursor: pointer; transition: filter .15s ease; }
.btn:hover { filter: brightness(1.15); }
.btn:disabled { opacity: .45; cursor: default; }
.btn-primary { background: var(--accent); color: var(--accent-fg); border-color: transparent; font-weight: 600; }
.btn-danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, transparent); background: transparent; }
.btn-ghost { background: transparent; border-color: transparent; color: var(--fg-dim); }
.btn-ghost:hover { color: var(--fg); }

.input { font: inherit; color: var(--fg); background: var(--bg); border: 1px solid var(--line);
  border-radius: var(--radius); padding: calc(.55rem * var(--density)) calc(.8rem * var(--density)); width: 100%; }
.input:focus { border-color: var(--accent); outline: none; }
.field { display: grid; gap: .3rem; }
.field label { font-size: .8rem; color: var(--fg-dim); }
form.stack, .stack { display: grid; gap: .9rem; }
.row { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }

.card { background: var(--bg2); border: 1px solid var(--line); border-radius: var(--radius);
  padding: calc(1.25rem * var(--density)); }

/* friend code — identity jewelry */
.code-chip { font-family: var(--mono); font-size: 1.1rem; letter-spacing: .05em; color: var(--accent);
  background: var(--bg3); border: 1px solid var(--line); padding: .55rem .85rem; border-radius: var(--radius);
  display: inline-flex; gap: .5rem; align-items: baseline; cursor: pointer; }
.code-chip small { color: var(--fg-dim); font-family: var(--sans); font-size: .72rem; }

/* the privacy dial (signature component) */
.dial { display: grid; gap: .4rem; }
.dial-stops { display: grid; grid-template-columns: repeat(3, 1fr); gap: .5rem; }
.dial-stop { font: inherit; text-align: left; color: var(--fg); background: var(--bg2);
  border: 1px solid var(--line); border-radius: var(--radius); padding: .7rem .8rem; cursor: pointer; }
.dial-stop[aria-pressed="true"] { border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, var(--bg2)); }
.dial-stop-name { font-weight: 650; }
.dial-stop-blurb { font-size: .74rem; color: var(--fg-dim); margin-top: .15rem; }
.dial-track { position: relative; height: 6px; background: var(--bg3); border-radius: 3px; margin: .3rem 8px 0; }
.dial-thumb { position: absolute; top: -5px; width: 16px; height: 16px; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 14px color-mix(in srgb, var(--accent) 55%, transparent);
  left: calc(var(--dial-pos, 0) * (100% - 16px)); transition: left .25s ease; }
.dial-label { min-height: 2.2em; font-size: .85rem; color: var(--fg-dim); font-style: italic; }

/* onboarding */
.onb { min-height: 100vh; min-height: 100dvh; display: grid; place-items: center; padding: 1.5rem; }
.onb-card { width: min(580px, 100%); display: grid; gap: 1.1rem; }

/* ---- landing (Round 6): the settings panel became the poster ---- */
.landing { min-height: 100vh; min-height: 100dvh; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 1.1rem; padding: 2.5rem 1.5rem; text-align: center;
  max-width: 860px; margin: 0 auto; }
.landing-brand { font-size: 1.3rem; }
.landing-h1 { font-family: var(--mono); font-size: clamp(1.6rem, 5vw, 2.6rem);
  font-weight: 650; letter-spacing: -.01em; margin: 0; }
.landing-h1::after { content: ''; } /* no decoration — the dial is the show */
.landing-sub { max-width: 560px; color: var(--fg-dim); margin: 0; }
.landing-dial { position: relative; width: min(560px, 100%); margin: 1rem 0; }
.landing-dial::before { content: ''; position: absolute; inset: -18%; z-index: -1;
  background: radial-gradient(closest-side, color-mix(in srgb, var(--accent) 20%, transparent), transparent 70%); }
.landing-dial .dial-label { font-family: var(--mono); font-style: normal; color: var(--accent); }
.landing-cta { display: grid; gap: .4rem; justify-items: center; }
.landing-go { font-size: 1.05rem; padding: .7rem 1.6rem; }
.landing-invite { font-size: .8rem; margin: 0; }
.landing-features { display: grid; grid-template-columns: repeat(3, 1fr); gap: .8rem;
  width: 100%; margin-top: 1.2rem; text-align: left; }
.landing-card p { color: var(--fg-dim); font-size: .85rem; }
.landing-footer { font-family: var(--mono); font-size: .7rem; color: var(--fg-dim);
  letter-spacing: .1em; margin-top: 1.5rem; }
.landing-footer-block { display: grid; gap: .4rem; justify-items: center; margin-top: 1.5rem; }
.landing-footer-block .landing-footer { margin-top: 0; }
.landing-links { font-family: var(--mono); font-size: .72rem; color: var(--fg-dim); margin: 0; }
.landing-links a { color: var(--fg-dim); }
@media (max-width: 700px) { .landing-features { grid-template-columns: 1fr; } }

/* onboarding consent line */
.onb-consent { font-size: .74rem; }
.onb-consent a { color: var(--fg-dim); }

/* ---- public doc pages (Round 7) ---- */
.docpage { max-width: 640px; margin: 0 auto; padding: 3rem 1.5rem 4rem; }
.docpage-back { font-family: var(--mono); font-size: .8rem; color: var(--fg-dim); text-decoration: none; }
.docpage-h1 { font-family: var(--mono); font-size: 1.6rem; margin: 1rem 0 .2rem; }
.docpage-meta { font-size: .78rem; margin-bottom: 1.5rem; }
.docpage-section { margin: 1.4rem 0; }
.docpage-section p { color: var(--fg); font-size: .92rem; line-height: 1.65; }
.docpage-list { padding-left: 1.1rem; }
.docpage-list li { margin: .35rem 0; color: var(--fg); font-size: .92rem; line-height: 1.65; }

/* app shell — three panes.
   Height cascade (last supported wins): 100vh is the floor for old engines; 100dvh tracks
   the shrinking layout viewport where interactive-widget=resizes-content applies (Android);
   --app-vh is the visual-viewport height published by js/viewport.js, and is the only one of
   the three that reflects an iOS soft keyboard. .has-vv gates the last step so a browser
   without visualViewport (or a failed module load) never lands on an empty var(). */
.shell { display: grid; grid-template-columns: 64px 236px 1fr; height: 100vh; height: 100dvh;
  padding-bottom: env(safe-area-inset-bottom); padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right); }
html.has-vv .shell { height: var(--app-vh); }
.rail { display: flex; flex-direction: column; align-items: center; gap: .5rem; padding: .8rem 0;
  border-right: 1px solid var(--line); }
.rail .brand { writing-mode: vertical-rl; margin-bottom: .5rem; font-size: .85rem; }
.rail-btn { width: 42px; height: 42px; border-radius: var(--radius); background: var(--bg3);
  border: 1px solid var(--line); color: var(--fg); font-size: 1.1rem; cursor: pointer;
  transition: color .14s ease, border-color .14s ease, background-color .14s ease; }
.rail-btn[aria-current="true"] { border-color: var(--accent); color: var(--accent); }
.rail-spacer { flex: 1; }
.pane-list { background: var(--bg2); border-right: 1px solid var(--line); padding: 1rem;
  overflow: auto; overscroll-behavior: contain; }
.pane-main { padding: 2rem; overflow: auto; overscroll-behavior: contain; }
/* Entry fade on a real view change only (ui.js gates this on the pane's data-view key), so
   the constant message-driven repaints of the same conversation never strobe. */
.pane-main.view-enter { animation: viewIn .16s ease both; }
@keyframes viewIn { from { opacity: 0; } }
.empty { border: 1px dashed var(--line); border-radius: var(--radius); padding: 1rem;
  color: var(--fg-dim); font-size: .85rem; }

/* skin gallery */
.skin-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: .7rem; }
.skin-tile { font: inherit; text-align: left; background: var(--bg2); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius); padding: .7rem; cursor: pointer; }
.skin-tile[aria-pressed="true"] { border-color: var(--accent); }
.skin-swatches { display: flex; gap: .3rem; margin-top: .45rem; }
.skin-swatches i { width: 18px; height: 18px; border-radius: 50%; border: 1px solid var(--line); }

.badge { font-family: var(--mono); font-size: .7rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent); border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  border-radius: 999px; padding: .15rem .6rem; }
.badge.mine { outline: 1px solid var(--accent); }

section.settings-block { margin-bottom: 2rem; max-width: 640px; }

/* toasts */
#toasts { position: fixed; bottom: 1rem; left: 50%; transform: translateX(-50%);
  display: grid; gap: .5rem; z-index: 99; }
.toast { background: var(--bg3); border: 1px solid var(--line); border-radius: var(--radius);
  padding: .55rem 1rem; animation: rise .18s ease; }
.toast.danger { border-color: var(--danger); }
.toast.ok { border-color: var(--ok); }
@keyframes rise { from { opacity: 0; transform: translateY(6px); } }

.rail-menu { display: none; } /* desktop: full three-pane, no drawer toggle */

@media (max-width: 700px) {
  .shell { grid-template-columns: 56px 1fr; }
  .rail-menu { display: block; }
  /* above the backdrop, so the rail stays usable with the drawer open */
  .rail { position: relative; z-index: 41; }
  .pane-list { position: fixed; top: 0; left: 56px; bottom: 0; width: min(80vw, 300px);
    transform: translateX(-115%); transition: transform .2s ease; z-index: 42; box-shadow: 0 0 30px rgba(0,0,0,.4); }
  .shell.drawer-open .pane-list { transform: translateX(0); }
  /* Dimmed backdrop: gives the open drawer a way out other than picking something. The click
     lands on .shell itself, which is what shell.js's dismiss handler listens for. */
  .shell.drawer-open::before { content: ''; position: fixed; inset: 0; z-index: 40;
    background: rgba(0, 0, 0, .45); animation: fadeIn .2s ease; }
  .pane-main { padding: 1.2rem; }
}
@keyframes fadeIn { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .pane-list { transition: none; } }

/* conversations */
.friend-item { display: flex; align-items: center; gap: .5rem; width: 100%; text-align: left;
  font: inherit; color: var(--fg); background: transparent; border: 1px solid transparent;
  border-radius: var(--radius); padding: .5rem .6rem; cursor: pointer;
  transition: background-color .14s ease, border-color .14s ease, color .14s ease; }
.friend-item:hover { background: var(--bg3); }
.friend-item[aria-current="true"] { background: var(--bg3); border-color: var(--line); }
.friend-name { flex: 1; min-width: 3rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.friend-code-short { font-family: var(--mono); font-size: .68rem; color: var(--fg-dim); }
.verified-mark { color: var(--ok); font-size: .8rem; }
.convo { display: flex; flex-direction: column; height: 100%; }
.convo-head { display: flex; align-items: center; gap: .6rem; padding-bottom: .8rem;
  border-bottom: 1px solid var(--line); flex-wrap: wrap; }
.composer { display: flex; gap: .6rem; padding-top: .8rem; border-top: 1px solid var(--line); }
.composer .input { flex: 1; }

/* mobile: the composer reads as one full-width input bar with the icons inside */
@media (max-width: 700px) {
  .composer { gap: .15rem; align-items: center; background: var(--bg3);
    border: 1px solid var(--line); border-radius: var(--radius);
    padding: .2rem .3rem; border-top: 1px solid var(--line); }
  .composer:focus-within { border-color: var(--accent); }
  .composer .input { border: 0; background: transparent; min-width: 0; }
  .composer .input:focus { outline: none; }
  .composer .btn { padding: .25rem .5rem; }
}
.safety { background: var(--bg2); border: 1px solid var(--line); border-radius: var(--radius);
  padding: .8rem 1rem; margin-top: .8rem; }
.safety-number { font-family: var(--mono); font-size: 1.05rem; letter-spacing: .08em; color: var(--accent); }

/* community rail + channels */
.rail-comm { width: 42px; height: 42px; border-radius: 50%; background: var(--bg3);
  border: 1px solid var(--line); color: var(--fg); font-weight: 650; cursor: pointer;
  transition: color .14s ease, border-color .14s ease, background-color .14s ease; }
.rail-comm[aria-current="true"] { border-color: var(--accent); color: var(--accent); }
.chan-item { display: flex; gap: .4rem; width: 100%; text-align: left; font: inherit; color: var(--fg-dim);
  background: transparent; border: 1px solid transparent; border-radius: var(--radius);
  padding: .35rem .6rem; cursor: pointer;
  transition: background-color .14s ease, border-color .14s ease, color .14s ease; }
.chan-item:hover { color: var(--fg); background: var(--bg3); }
.chan-item[aria-current="true"] { color: var(--fg); background: var(--bg3); border-color: var(--line); }
.chan-hash { color: var(--fg-dim); font-family: var(--mono); }
.member-row { display: flex; gap: .5rem; align-items: center; font-size: .8rem; padding: .25rem 0; }

/* ephemeral reactions */
.react-btn { font: inherit; background: transparent; border: 1px solid var(--line);
  border-radius: 999px; padding: 0 .35rem; cursor: pointer; font-size: .8rem; }
.react-btn:hover { background: var(--bg3); }

a, a:visited { color: var(--accent); }

/* emoji picker */
.emoji-wrap { position: relative; display: inline-flex; }
.emoji-grid { position: absolute; bottom: 118%; right: 0; display: flex; flex-wrap: wrap; gap: .2rem;
  width: 232px; padding: .4rem; background: var(--bg2); border: 1px solid var(--line);
  border-radius: var(--radius); z-index: 30; }
.emoji-btn { background: transparent; border: 0; font-size: 1.25rem; cursor: pointer; line-height: 1.4; }
.emoji-btn:hover { transform: scale(1.15); }

/* combined emoji/GIF picker (Klipy) — .expr-panel mirrors .emoji-grid's popover box so the
   emoji-only case (GIF picker off / unconfigured) looks pixel-identical to the old picker */
.expr-panel { position: absolute; bottom: 118%; right: 0; width: 232px; max-height: 320px;
  overflow: hidden; display: flex; flex-direction: column; background: var(--bg2);
  border: 1px solid var(--line); border-radius: var(--radius); z-index: 30; }
.expr-panel .emoji-grid { position: static; width: auto; max-height: 260px; overflow: auto;
  border: 0; border-radius: 0; background: transparent; z-index: auto; }
.expr-tabs { display: flex; gap: .3rem; padding: .4rem .4rem 0; }
.expr-tab { background: transparent; border: 1px solid var(--line); border-radius: 999px;
  padding: .1rem .6rem; font-size: .72rem; cursor: pointer; color: var(--fg-dim); }
.expr-tab[aria-pressed="true"] { border-color: var(--accent); color: var(--fg); font-weight: 600;
  background: color-mix(in srgb, var(--accent) 14%, var(--bg3)); }
.gif-pane { display: flex; flex-direction: column; gap: .35rem; padding: .4rem; overflow: hidden; }
.gif-search { font-size: .8rem; }
.gif-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: .3rem; max-height: 200px; overflow: auto; }
.gif-cell { padding: 0; border: 0; background: transparent; cursor: pointer; border-radius: 4px; overflow: hidden; line-height: 0; }
.gif-thumb { width: 100%; height: 72px; object-fit: cover; display: block; }
.gif-ad { position: relative; }
.gif-ad-chip { position: absolute; top: 2px; left: 2px; font-size: .55rem; background: var(--bg2);
  border: 1px solid var(--line); border-radius: 4px; padding: 0 .25rem; color: var(--fg-dim); }
.gif-status { margin: 0; font-size: .75rem; text-align: center; min-height: 1em; }
.gif-more { align-self: center; }

/* selected state for generic toggle buttons (community mode, restore tabs, etc.) */
.btn[aria-pressed="true"] { border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, var(--bg3)); color: var(--fg); font-weight: 600; }

/* Discord-style chat (single left-aligned column) */
/* overflow-anchor: none is load-bearing, not a tweak. chatAnchor.js does the scroll
   anchoring for this list deliberately (pin to newest, or hold a specific message in
   place). Chrome's BUILT-IN scroll anchoring was doing its own competing version: as
   lazy <img> embeds loaded above the viewport it silently rewrote scrollTop and fired a
   scroll event carrying a position the app never set, which chatAnchor could only read
   as "the user scrolled here" — un-pinning the conversation and persisting a bogus
   mid-history anchor. Measured on a 60-image channel: opened 6124px above the newest
   message and stayed there on every revisit; 0px with the browser's version disabled. */
.chat { flex: 1; overflow: auto; overscroll-behavior: contain; overflow-anchor: none;
  display: flex; flex-direction: column; padding: .5rem 0; gap: 0; }
.chat-head { display: flex; align-items: baseline; gap: .5rem; margin-top: .85rem; }
.chat-head .avatar { align-self: center; }
/* stable click/hover target wrapping avatarNode's swappable tile-or-img (Round 11
   self-nav / profile-card wiring) — see messageList.js */
.chat-avatar-wrap { display: inline-flex; align-self: center; cursor: pointer; }
.avatar { width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center;
  color: #fff; font-weight: 700; font-size: .9rem; flex: none; }
.avatar-img { object-fit: cover; padding: 0; }
.friend-item .avatar { width: 1.6rem; height: 1.6rem; font-size: .7rem; }
.chat-author { font-weight: 650; }
.chat-time { color: var(--fg-dim); font-size: .68rem; }
.chat-line { margin-left: 44px; padding: .12rem .5rem .12rem 0; overflow-wrap: anywhere;
  position: relative; border-radius: 4px; }
.chat-line:hover { background: color-mix(in srgb, var(--fg) 5%, transparent); }
.chat-line.deleted { font-style: italic; color: var(--fg-dim); }
/* State + actions are hidden until you hover (desktop), tap the line (mobile), or focus it
   (keyboard). The :focus-within arm is load-bearing: display:none removes an element from the
   tab order entirely, so without it these buttons are literally unreachable without a mouse.
   rovingFocus.js makes the line itself focusable so there is something to focus. */
.chat-line .line-state { color: var(--fg-dim); font-family: var(--mono); font-size: .7rem;
  margin-left: .4rem; display: none; }
.chat-line:hover .line-state, .chat-line.tap .line-state,
.chat-line:focus-within .line-state { display: inline; }
.chat-line .line-del { position: absolute; top: 0; right: .3rem; display: none;
  border: 0; background: transparent; color: var(--fg-dim); cursor: pointer; font-size: .75rem; }
.chat-line:hover .line-del, .chat-line.tap .line-del,
.chat-line:focus-within .line-del { display: block; }
/* reaction picker floats out of flow (Discord-style pill) so hover never reflows the chat */
.chat-line .react-row { display: none; gap: .2rem; position: absolute; top: -1.1rem; right: 2rem;
  background: var(--bg2); border: 1px solid var(--line); border-radius: 999px; padding: .1rem .3rem; z-index: 2; }
.chat-line:hover .react-row, .chat-line.tap .react-row,
.chat-line:focus-within .react-row { display: inline-flex; }
/* The focused message reads as selected. :focus (not :focus-visible) because arrow-key
   navigation must always show where you are — :focus-visible is suppressed after a pointer
   interaction, and a click DOES focus the line (it carries a tabindex), so :focus-visible
   alone would leave the roving position invisible for the rest of that session. The outline
   below is inset so the ring never overlaps the neighbouring rows. */
.chat-line:focus { background: color-mix(in srgb, var(--fg) 7%, transparent); outline: none; }
.chat-line:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ---- calls (Round 2a) ---- */
.call-banner { position: fixed; top: .8rem; left: 50%; transform: translateX(-50%); z-index: 60;
  display: flex; gap: .6rem; align-items: center; padding: .6rem .9rem; border-radius: 12px;
  background: var(--panel, #1c1c22); box-shadow: 0 6px 24px rgba(0,0,0,.45); }
.call-banner-text { font-weight: 600; }
.call-overlay { position: fixed; inset: 0; z-index: 55; display: flex; flex-direction: column;
  background: #0b0b0f; }
.call-videos { position: relative; flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center; }
.call-remote { width: 100%; height: 100%; object-fit: contain; background: #000; }
.call-local { position: absolute; right: 1rem; bottom: 1rem; width: min(26vw, 200px);
  border-radius: 10px; border: 1px solid rgba(255,255,255,.25); background: #000; }
.call-voice-tile { display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; }
.avatar-big { width: 96px; height: 96px; font-size: 2.4rem; display: inline-flex; align-items: center;
  justify-content: center; border-radius: 50%; }
.call-status { display: flex; gap: .8rem; align-items: baseline; justify-content: center; padding: .5rem; color: #fff; }
.call-name { font-weight: 700; }
.call-dur { font-family: var(--mono); }
.call-controls { display: flex; gap: .9rem; justify-content: center; padding: .8rem 0 1.2rem; }
.call-btn { font-size: 1.25rem; padding: .55rem .9rem; border-radius: 999px; }
.call-btn.off { opacity: .45; text-decoration: line-through; }
.friend-actions { margin-left: auto; display: none; gap: .25rem; }
.friend-item:hover .friend-actions, .friend-item:focus-within .friend-actions { display: flex; }
.friend-actions .btn { padding: .15rem .45rem; }
.chat-sys { text-align: center; font-size: .75rem; opacity: .65; margin: .4rem 0; }

/* ---- embeds (Round 2b) ---- */
.embeds { margin-top: .35rem; display: flex; flex-direction: column; gap: .4rem; }
.embed-frame { width: 100%; max-width: 560px; aspect-ratio: 16 / 9; border: 0; border-radius: 10px; background: #000; }
.embed-frame.tall { max-width: 340px; aspect-ratio: 9 / 16; }
.embed-media { display: block; max-width: min(100%, 420px); max-height: 340px; border-radius: 10px; }
.embed-load { display: inline-flex; align-items: center; gap: .4rem; padding: .45rem .7rem; border-radius: 10px;
  border: 1px dashed rgba(128,128,128,.5); background: transparent; color: inherit; cursor: pointer; font-size: .8rem; }
.embed-load:hover { border-style: solid; }
.embed-card { display: inline-flex; flex-direction: column; gap: .15rem; padding: .5rem .7rem; border-radius: 10px;
  border: 1px solid rgba(128,128,128,.35); text-decoration: none; max-width: 420px; overflow-wrap: anywhere; }
.embed-card-host { font-size: .72rem; opacity: .7; text-transform: uppercase; letter-spacing: .04em; }
.embed-card-url { font-size: .82rem; }

/* ---- attachments (Round 2c) ---- */
.att { margin-top: .35rem; }
.att-img { display: block; max-width: min(100%, 380px); max-height: 320px; border-radius: 10px; cursor: zoom-in; }
.att-video { display: block; max-width: min(100%, 420px); max-height: 340px; border-radius: 10px; }
.att-file { display: inline-flex; align-items: baseline; gap: .3rem; padding: .5rem .7rem; border-radius: 10px;
  border: 1px solid rgba(128,128,128,.35); text-decoration: none; }
.att-note { font-size: .78rem; opacity: .7; }
.convo.droppable { outline: 2px dashed rgba(128,128,128,.7); outline-offset: -6px; }
/* ---- native <dialog> modals (Round 12) ---- */
.wc-dialog { background: var(--bg2); color: var(--fg); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1.2rem 1.4rem; max-width: min(440px, 92vw); }
.wc-dialog::backdrop { background: rgba(0, 0, 0, .55); }
.wc-dialog h2 { margin-bottom: .4rem; }
.wc-dialog-actions { justify-content: flex-end; margin-top: 1.1rem; }
.wc-lightbox { border: 0; padding: 0; background: transparent; max-width: 94vw; max-height: 94vh;
  cursor: zoom-out; overflow: hidden; }
.wc-lightbox::backdrop { background: rgba(0, 0, 0, .85); }
.wc-lightbox img { display: block; max-width: 94vw; max-height: 94vh; border-radius: 8px; }

/* ---- profile hover card + visitable profile page (Round 4 Task 4) ---- */
.profile-card { position: fixed; z-index: 85; width: 260px; background: var(--bg2);
  border: 1px solid var(--line); border-radius: var(--radius); padding: 1rem;
  box-shadow: 0 12px 32px rgba(0,0,0,.5); display: flex; flex-direction: column; }
.profile-header { height: var(--profile-header-h, 140px); border-radius: var(--radius); }
.profile-header.h1, .header-swatch.h1 { background: linear-gradient(135deg, #e0a458, #7a4318); }
.profile-header.h2, .header-swatch.h2 { background: linear-gradient(120deg, #d9646f, #7a2f52); }
.profile-header.h3, .header-swatch.h3 { background: linear-gradient(150deg, #35a08f, #123c42); }
.profile-header.h4, .header-swatch.h4 { background: linear-gradient(160deg, #7469d6, #241a4f); }
.profile-header.h5, .header-swatch.h5 { background: linear-gradient(140deg, #4f9d63, #1c3626); }
.profile-header.h6, .header-swatch.h6 { background: linear-gradient(130deg, #4a5c9c, #12141c); }
/* Overlap the avatar onto the header banner. Derived from the two sizes rather than a magic
   number, so changing either the banner height or the avatar size keeps the overlap correct. */
.profile-avatar-slot { margin-left: 1.2rem;
  margin-top: calc((var(--avatar-xl-size, 192px) / -2) + 40px); }
.avatar-xl { width: var(--avatar-xl-size, 192px); height: var(--avatar-xl-size, 192px);
  border-radius: 50%; border: 3px solid var(--bg); }
/* Settings Profile block: header-color picker (Round 4 Task 5) — same skin-tile
   aria-pressed idiom as .skin-tile, sized as a small swatch instead of a full tile. */
.header-swatches { display: flex; gap: .5rem; margin: .5rem 0; }
.header-swatch { width: 56px; height: 36px; border-radius: 6px; border: 2px solid var(--line); cursor: pointer; padding: 0; }
.header-swatch[aria-pressed="true"] { border-color: var(--accent); }
/* size variants: avatarNode's async image swap replaces the .avatar element wholesale, so the
   size class lives on a wrapper span instead of the (possibly swapped) node itself. */
.avatar-lg, .avatar-xl { display: inline-flex; flex: none; }
.avatar-lg { width: 96px; height: 96px; }
.avatar-lg .avatar, .avatar-xl .avatar { width: 100%; height: 100%; font-size: 2.4rem; }
.avatar-xl .avatar { font-size: 4.8rem; }

/* ---- public posts + follows (Round 9) ---- */
.vis-switch { display: inline-flex; gap: .3rem; }
.vis-btn { font: inherit; font-size: .8rem; color: var(--fg-dim); background: var(--bg3);
  border: 1px solid var(--line); border-radius: 999px; padding: .3rem .8rem; cursor: pointer; }
.vis-btn[aria-pressed="true"] { color: var(--fg); border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, var(--bg3)); font-weight: 600; }
.pill-row { display: flex; gap: .4rem; margin: .8rem 0 .3rem; }
.pill { font: inherit; font-size: .8rem; color: var(--fg-dim); background: transparent;
  border: 1px solid var(--line); border-radius: 999px; padding: .3rem .9rem; cursor: pointer; }
.pill[aria-pressed="true"] { color: var(--accent-fg); border-color: var(--accent); background: var(--accent); font-weight: 600; }
.post-card { margin-top: .8rem; }
.post-actions { margin-top: .5rem; justify-content: flex-end; }

/* ---- boot watchdog retry (R7.1) ---- */
.boot-retry { gap: 1rem; } /* .boot is grid; auto-placement stacks, gap spaces */
.boot-retry-msg { color: var(--fg-dim); font-size: .9rem; max-width: 26rem; text-align: center; }
.boot-retry-btn { font: inherit; padding: .5rem 1.4rem; border-radius: var(--radius); border: 1px solid var(--line); background: var(--bg2); color: var(--fg); cursor: pointer; }

/* ---- TOS consent gate (Round 7b) ---- */
/* Now a native <dialog>: the top layer handles stacking, so the z-index (previously 80, to
   clear .call-overlay at 55) is no longer needed — a modal dialog is above everything. */
.consent-overlay { border: 0; background: transparent; padding: 1rem; max-width: none;
  max-height: none; width: 100%; height: 100%; display: grid; place-items: center; }
.consent-overlay::backdrop { background: color-mix(in srgb, #000 60%, transparent); }
.consent-card { background: var(--bg2); border: 1px solid var(--line); border-radius: var(--radius);
  width: min(680px, 100%); max-height: 90vh; display: flex; flex-direction: column; padding: 1.2rem 1.4rem; gap: .7rem; }
.consent-notice { font-size: .72rem; margin: 0; }
.consent-lead { font-weight: 650; font-size: .9rem; margin: 0; }
.consent-terms { overflow-y: auto; border: 1px solid var(--line); border-radius: var(--radius);
  padding: 1rem 1.2rem; background: var(--bg); flex: 1; min-height: 0; }
.consent-count { font-size: .78rem; margin: 0; }
.consent-check { font-size: .82rem; display: flex; gap: .5rem; align-items: baseline; cursor: pointer; }
.consent-actions { justify-content: flex-end; }

/* Skip link — off-screen until focused, then pinned top-left over the rail. */
.skip-link { position: absolute; left: -9999px; z-index: 100;
  background: var(--bg3); color: var(--fg); border: 1px solid var(--accent);
  border-radius: var(--radius); padding: .5rem .9rem; text-decoration: none; }
.skip-link:focus { left: .5rem; top: .5rem; }
.pane-main:focus { outline: none; } /* focused programmatically by the skip link, not by the user */

/* Visually hidden, still announced. Not display:none and not visibility:hidden — both
   remove the element from the accessibility tree, which defeats the entire purpose. */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

.field-label { display: block; font-size: .8rem; color: var(--fg-dim); margin-bottom: .4rem; }

/* Round 12 fix: .input sets `font: inherit` (line 56), which resolves font-size to inherit
   and — being a class selector — outranks the plain-type `input { font-size: max(16px, 1em) }`
   mobile clamp above (line 28), silently defeating it for every .input element (nearly all
   text inputs in the app). Same specificity as line 56, placed after it, so this wins the
   cascade tie without touching font-family/weight, which still come from `font: inherit`. */
@media (max-width: 700px) {
  .input { font-size: max(16px, 1em); }
}

/* Settings: index + panel. Below 700px they swap full-screen; above it they sit side by
   side. 700px is the breakpoint the shell already uses — do not introduce a second. */
.settings-layout { display: grid; gap: 1.5rem; }
.settings-index ul { list-style: none; margin: 0; padding: 0; }
.settings-index-link { display: block; padding: .6rem .8rem; border-radius: var(--radius);
  text-decoration: none; color: var(--fg); }
.settings-index-link:hover { background: var(--bg3); }
.settings-index-link.is-active { background: var(--bg3); font-weight: 600; }
.settings-back { display: inline-block; margin-bottom: 1rem; font-size: .9rem; }
@media (min-width: 701px) {
  .settings-layout.has-panel { grid-template-columns: 200px minmax(0, 1fr); align-items: start; }
}
