@import url("theme.css");

/* Colours, type and radii live in theme.css, shared with admin.css. */
body{font-size:16px;line-height:1.6}
a{color:var(--accent-ink)}
code{font-family:var(--mono)}

/* ---------- auth ---------- */
.auth-body{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;padding:24px;gap:14px}
.auth{width:100%;max-width:440px;background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);padding:32px 28px}
.auth-eyebrow{margin:0 0 6px;font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--muted)}
/* Brand logo inside the auth card. The artwork is on white, matching the card,
   so it needs no badge or tile — just centered at the top. */
.auth-logo{display:flex;justify-content:center;align-items:center;margin:0 0 16px;line-height:0}
.auth-logo img{display:block;width:auto;height:auto;max-width:min(240px,72%);max-height:96px}
.auth h1{margin:0 0 8px;font-size:26px;line-height:1.15}
.auth-lede{margin:0 0 22px;color:var(--muted);font-size:15px}
.providers{display:flex;flex-direction:column;gap:10px}
.provider{display:block;width:100%;text-align:center;text-decoration:none;cursor:pointer;
  padding:14px 16px;border:1px solid var(--line);border-radius:10px;background:#fff;color:var(--ink);
  font:inherit;font-weight:500}
.provider:hover{border-color:var(--accent);background:var(--accent-soft)}
.panel{margin-top:20px;padding-top:20px;border-top:1px solid var(--line)}
.panel label{display:block;margin:12px 0 6px;font-size:14px;font-weight:500}
.panel input{width:100%;padding:12px 14px;border:1px solid var(--line);border-radius:10px;font:inherit;background:#fff}
.panel input:focus{outline:2px solid var(--accent);outline-offset:1px}
.panel button{margin-top:12px;width:100%;padding:13px;border:0;border-radius:10px;background:var(--red);color:#fff;
  font:inherit;font-weight:600;cursor:pointer}
.panel button:hover{background:var(--accent-ink)}
.msg{margin:12px 0 0;font-size:14px;color:var(--muted);min-height:1.2em}

/* ---------- access code boxes ---------- */
.pin-label{display:block;margin:12px 0 8px;font-size:14px;font-weight:500}
/* Boxes shrink to fit rather than overflow, so a longer code still sits on
   one line on a narrow phone. */
.pin{display:flex;gap:8px;justify-content:space-between}
.pin .pin-box{
  flex:1 1 0;min-width:0;width:auto;max-width:60px;aspect-ratio:1/1;padding:0;
  text-align:center;font-size:24px;font-weight:600;font-variant-numeric:tabular-nums;
  border:1px solid var(--line);border-radius:10px;background:#fff;color:var(--ink)
}
.pin .pin-box:focus{outline:2px solid var(--accent);outline-offset:1px;border-color:var(--accent)}
.pin .pin-box:disabled{background:var(--wash);color:var(--muted);opacity:1}
@media (max-width:380px){
  .pin{gap:6px}
  .pin .pin-box{font-size:20px}
}
.auth-fine{margin:22px 0 0;font-size:12.5px;color:var(--muted);line-height:1.5}
/* Prominent sign-in / forgot-password links on the auth screens — deliberately
   larger and heavier than the .auth-fine small print they sit above. */
.auth-links{margin:20px 0 4px;font-size:16px;line-height:1.9}
.auth-links a{font-weight:600}

/* Prominent call-to-action link styled as a button on the auth screens (e.g.
   "Create an account" on the sign-in page), plus a quiet back-to-home link. */
.auth-btn{display:block;width:100%;box-sizing:border-box;text-align:center;text-decoration:none;
  cursor:pointer;padding:14px 16px;border-radius:10px;background:var(--red);color:#fff;font:inherit;
  font-weight:700;font-size:16px;margin-top:10px}
.auth-btn:hover{background:var(--accent-ink)}
.auth-btn.secondary{background:#fff;color:var(--red);border:2px solid var(--red)}
.auth-btn.secondary:hover{background:var(--accent-soft)}
.auth-newacct{margin:20px 0 0;font-size:14px;color:var(--muted);text-align:center}
.auth-back{margin:24px 0 0;font-size:14px;text-align:center}
.auth-back a{font-weight:600;text-decoration:none;color:var(--muted)}
.auth-back a:hover{color:var(--accent)}

/* Separator between the registration form and the provider buttons. A rule
   either side rather than a bare word, so it reads as "these are alternatives"
   instead of as a stray label belonging to the button under it. */
.auth-or{display:flex;align-items:center;gap:12px;margin:20px 0 14px;
  font-size:12.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.06em}
.auth-or::before,.auth-or::after{content:"";flex:1;height:1px;background:var(--line)}

/* Form-level outcomes. These were inline style="color:#b91c1c" attributes
   repeated at each call site, which meant the error red and the success green
   were defined in five places and the pages could disagree about them. */
.auth-error{color:var(--danger,#b91c1c);font-weight:500}
.auth-ok{color:var(--ok,#15803d);font-weight:500}

/* Resend sits in its own <form> so Enter in the code box cannot fire it, but
   it should read as a link rather than as a second submit competing with
   "Confirm and continue". */
.auth-inline{margin:10px 0 0}
.linkbtn{background:none;border:0;padding:0;font:inherit;font-size:13px;
  color:var(--accent,#6d28d9);text-decoration:underline;cursor:pointer;width:auto}
.linkbtn:hover{text-decoration:none}

/* Correcting a mistyped address: folded away, because it is the exception and
   an open form here competes with the code box for attention. */
.auth-change{margin:18px 0 0}
.auth-change>summary{font-size:13px;color:var(--muted);cursor:pointer;
  list-style:none;display:inline-flex;align-items:center;gap:6px}
.auth-change>summary::-webkit-details-marker{display:none}
.auth-change>summary::before{content:"";width:6px;height:6px;flex:none;
  border-right:1.6px solid currentColor;border-bottom:1.6px solid currentColor;
  transform:rotate(-45deg);transition:transform .15s}
.auth-change[open]>summary::before{transform:rotate(45deg)}
.auth-change>summary:hover{color:var(--ink)}
@media (prefers-reduced-motion:reduce){.auth-change>summary::before{transition:none}}

/* ---------- chat ---------- */
.chat-body{display:block;padding:0}
.chat{width:100%;min-width:0;background:var(--card);border:1px solid var(--line);border-radius:16px;
  display:flex;flex-direction:column;height:calc(100vh - 32px);overflow:hidden}
.chat-head{display:flex;align-items:center;gap:10px;padding:12px 16px;border-bottom:1px solid var(--line)}
.chat-head-main{flex:1;min-width:0;display:flex;align-items:center;gap:6px}
.chat-head-main .chat-sub{flex:0 1 auto;min-width:0;font-size:15px;font-weight:600;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Pencil beside the room name — small, quiet, and clearly tappable. */
.roomedit{flex:none;display:inline-flex;align-items:center;justify-content:center;
  width:26px;height:26px;padding:0;border:0;border-radius:50%;background:transparent;
  color:var(--muted);cursor:pointer}
.roomedit:hover{background:var(--wash);color:var(--ink)}
.roomedit:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.roomedit svg{display:block}
/* Header people avatars. Creator is the large one beside the arrow; the admin
   and everyone else are smaller and overlap slightly. */
.chead-av{flex:none;border-radius:50%;overflow:hidden;background:var(--paper);
  display:inline-flex;align-items:center;justify-content:center;font-weight:700;line-height:1}
.chead-av img{width:100%;height:100%;object-fit:cover;display:block}
.chead-av-lg{width:36px;height:36px;font-size:15px}
.chead-av-sm{width:24px;height:24px;font-size:11px;border:2px solid #fff}
.chead-av-blank{background:#dfe3e8;color:#5b6270}
/* Person silhouette for a header avatar with no picture (e.g. the admin). */
.chead-av-person{background:#dfe3e8 no-repeat center/72%;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%235b6270'><circle cx='12' cy='8.5' r='4'/><path d='M12 14c-4.4 0-8 2.5-8 5.5V22h16v-2.5c0-3-3.6-5.5-8-5.5z'/></svg>")}
.chead-admin{background:var(--accent-soft);color:var(--accent-ink)}
.chead-people{display:flex;align-items:center}
.chead-people .chead-av-sm{margin-left:-6px}
.chead-people .chead-av-sm:first-child{margin-left:0}
/* Thicker, more visible back arrow (shown on the narrow layout). */
.chat-head .cbox-back{color:var(--ink)}
.chat-head .cbox-back svg{display:block}
.chat-title{margin:0;font-size:15px;font-weight:600}
.chat-sub{margin:0;font-size:12px;color:var(--muted)}
.chat-head-meta{display:flex;align-items:center;gap:8px}
.pill{font-size:12px;padding:4px 10px;border-radius:999px;background:var(--paper);color:var(--muted)}
.pill[data-status="active"]{background:#EAF3DE;color:#3B6D11}
.pill[data-status="full"]{background:#E6F1FB;color:#185FA5}
.pill[data-status="waiting"]{background:#FAEEDA;color:#854F0B}
.count{font-size:12px;color:var(--muted)}

.roster{display:flex;align-items:center;gap:8px;padding:10px 16px;border-bottom:1px solid var(--line);background:var(--wash)}
.seat{width:28px;height:28px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;
  font-size:11px;font-weight:600;background:var(--accent-soft);color:var(--accent-ink)}
.seat-me{background:var(--red);color:#fff}
.seat-empty{background:none;border:1px dashed var(--line)}
.seat-label{font-size:12px;color:var(--muted);margin-left:4px}

.thread{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:4px;
  scrollbar-width:none;-ms-overflow-style:none}
.thread::-webkit-scrollbar{width:0;height:0;display:none}
.sys{text-align:center;font-size:11.5px;color:var(--muted);padding:10px 0}
/* The room-entry notice, given a little more presence than a plain system
   line: a door icon, the message a size up, and the time below it. */
.sys-enter{display:flex;flex-direction:column;align-items:center;gap:6px;
  padding:18px 12px;color:var(--ink)}
.sys-enter .sys-icon{font-size:30px;line-height:1}
.sys-enter .sys-text{font-size:17px;font-weight:600;line-height:1.3;
  text-align:center;max-width:32ch}
.sys-enter .sys-time{font-size:12px;color:var(--muted)}
/* Inline "Admin has entered the room · 9:43 AM" notice, dropped into the thread
   the moment an admin joins. Meta/Messenger-style: plain centered gray text,
   no box, no bold. */
.sysline{align-self:center;margin:8px auto;max-width:92%;text-align:center;
  font-size:12.5px;font-weight:400;color:var(--muted)}
.sysline strong{font-weight:400}
/* Gentle fade-and-rise as a transcript notice appears. (Previously named
   standbyIn and shared with the removed standby banner; kept here under a
   neutral name since the sysline join notice still uses it.) */
.sysline-join{animation:entryFadeIn .35s ease both}
@keyframes entryFadeIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.sysline-join{animation:none}}
.row{display:flex;gap:8px;align-items:flex-start;margin-top:6px}
.row-me{justify-content:flex-end}
/* The avatar is dropped from the top of the stack (the sender name) down level
   with the chat bubble, so it sits beside the bubble rather than the timestamp
   or reactions that hang below it. The offset is the name line's height. */
.row-them>.avatar,.row-them>.avatarcol{margin-top:17px}
.avatar{flex:none;width:28px;height:28px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  font-size:11px;font-weight:600;background:var(--accent-soft);color:var(--accent-ink)}
.avatar-bot{background:var(--accent-soft);color:var(--accent-ink);letter-spacing:-1px}
.stack{max-width:74%}
.who{margin:0 0 3px 4px;font-size:11px;color:var(--muted)}
.tag{font-size:10px;background:var(--accent-soft);color:var(--accent-ink);padding:1px 6px;border-radius:var(--r-pill)}
.bubble{padding:8px 12px;border-radius:18px;background:var(--bubble-them);font-size:15px;
  line-height:1.35;word-wrap:break-word;overflow-wrap:anywhere;white-space:pre-wrap}
.bubble-bot{background:#E9EBEE}
.row-me .bubble{background:var(--bubble-me);color:var(--bubble-me-text)}
.row-me .bubble a{color:#fff;text-decoration:underline}
.row-me .bubble{background:var(--bubble-me);color:#fff;border-radius:18px 18px 4px 18px}
.row-me .bubble a{color:#fff;text-decoration:underline}
.bubble a{color:var(--bubble-me)}

.composer{display:flex;gap:8px;align-items:center;padding:10px 14px;border-top:1px solid var(--line)}
.composer-field{position:relative;flex:1;display:flex;align-items:center}
.composer-field input{width:100%;padding:9px 42px 9px 15px;border:0;border-radius:999px;
  font:inherit;font-size:15px;background:var(--bubble-them);color:var(--ink)}
.composer-field input:focus{outline:2px solid var(--bubble-me);outline-offset:-1px}
#imageBtn{position:absolute;right:6px;width:30px;height:30px;padding:0;border:0;background:none;
  display:flex;align-items:center;justify-content:center;color:var(--bubble-me);cursor:pointer;border-radius:50%}
#imageBtn:hover{background:var(--bubble-me-wash)}
#sendBtn{flex:none;width:36px;height:36px;padding:0;border:0;border-radius:50%;background:none;
  display:flex;align-items:center;justify-content:center;color:var(--bubble-me);cursor:pointer}
#sendBtn:hover{background:var(--bubble-me-wash)}
.composer button:disabled{opacity:.4;cursor:default}
.composer-note{margin:0;padding:0 16px 12px;font-size:12px;color:var(--muted);min-height:1em}

@media(max-width:520px){
  .chat-body{padding:0}
  .chat{height:100vh;border:0;border-radius:0;max-width:none}
  .chatwrap{max-width:none}
}

/* Image attachments in chat */
.bubble-img{display:block;line-height:0}
.bubble-img img{max-width:100%;height:auto;border-radius:10px;display:block}
.bubble-cap{margin:6px 0 0;font-size:14px;line-height:1.4}
#imageBtn{flex:none;width:40px;display:flex;align-items:center;justify-content:center;
  cursor:pointer;color:#64748b;
  border:1px solid var(--line,#d8dee8);background:#fff;border-radius:8px}
#imageBtn:hover{color:#334155;background:#f6f8fb}
#imageBtn:disabled{opacity:.5;cursor:default}

/* ---- Emoji picker in the composer ----
   Sits immediately left of the attachment button, which puts it next to Send.
   #imageBtn is absolutely positioned inside the field, so this is too rather
   than being laid out around it -- and the input's right padding has to grow
   to match, or long text runs under both buttons. */
#emojiBtn{position:absolute;right:50px;width:34px;height:30px;padding:0;
  display:flex;align-items:center;justify-content:center;cursor:pointer;color:#64748b;
  border:1px solid var(--line,#d8dee8);background:#fff;border-radius:8px}
#emojiBtn:hover{color:#334155;background:#f6f8fb}
#emojiBtn[aria-expanded="true"]{color:var(--red);border-color:var(--red)}
.composer-field input{padding-right:92px}

.emojipop{position:absolute;bottom:calc(100% + 8px);right:0;z-index:60;
  width:min(324px,calc(100vw - 28px));max-height:264px;overflow-y:auto;
  padding:8px 10px;background:#fff;border:1px solid var(--line,#d8dee8);
  border-radius:12px;box-shadow:0 10px 30px rgba(0,0,0,.16);scrollbar-width:thin}
.emojipop[hidden]{display:none}
.emojipop-head{margin:8px 2px 4px;font-size:10.5px;font-weight:700;
  letter-spacing:.05em;text-transform:uppercase;color:var(--muted)}
.emojipop-head:first-child{margin-top:0}
.emojipop-grid{display:grid;grid-template-columns:repeat(8,1fr);gap:2px}
.emojipop-btn{border:0;background:none;padding:4px 0;font-size:19px;line-height:1.25;
  border-radius:8px;cursor:pointer;transition:background .08s,transform .08s}
.emojipop-btn:hover{background:var(--wash,#F7F8FA);transform:scale(1.18)}
.emojipop-btn:focus-visible{outline:2px solid var(--red);outline-offset:-2px}
@media (prefers-reduced-motion:reduce){.emojipop-btn{transition:none}}

/* Emoji reactions */
.stack{position:relative}
/* Others' rows carry an avatar on the left, so the row gets a little extra
   room and the bubble sits clear of it. */
.row-them{padding-left:26px}
.row-them .avatar{margin-left:-26px}

/* Reaction pills sit just beneath the bubble, flush to the message's own side
   (right for your messages, left for others'), and wrap onto more rows if
   there are many. align-self pins the row to the correct edge of the stack so
   the pills always line up under the bubble's edge. */
.reacts{display:flex;flex-wrap:wrap;gap:4px;margin:3px 0 1px}
.row-me   .reacts{align-self:flex-end;justify-content:flex-end}
.row-them .reacts{align-self:flex-start;justify-content:flex-start}
.reacts:empty{display:none}
.react-pill{display:inline-flex;align-items:center;gap:3px;padding:2px 7px;
  border:1px solid var(--line,#d8dee8);background:#fff;border-radius:999px;
  font-size:12px;line-height:1.5;cursor:pointer}
.react-pill:hover{background:#f3f6fb}
.react-mine{border-color:#185FA5;background:#eaf2fb;font-weight:600}

/* Emoji picker for the long-press / right-click "React" action. Anchored to
   the stack, dropping just below the bubble on the message's side. */
.react-picker{position:absolute;top:100%;margin-top:4px;display:flex;gap:2px;
  padding:4px 6px;background:#fff;border:1px solid var(--line,#d8dee8);
  border-radius:999px;box-shadow:0 4px 14px rgba(16,24,40,.14);z-index:20}
.row-me   .react-picker{right:0;left:auto}
.row-them .react-picker{left:0;right:auto}
.react-picker button{border:0;background:none;font-size:18px;line-height:1;
  padding:2px 3px;cursor:pointer;border-radius:6px}
.react-picker button:hover{background:#f0f4fa;transform:scale(1.15)}

.bubble{position:relative}

/* Notification sound toggle */
.sound-btn{border:0;background:none;font-size:16px;line-height:1;padding:2px 4px;
  cursor:pointer;opacity:.75;border-radius:6px}
.sound-btn:hover{opacity:1;background:rgba(0,0,0,.05)}

/* FAQ quick-question buttons */
.faqbar{display:flex;flex-wrap:wrap;gap:6px;padding:8px 12px 0}
.faqbar:empty{display:none}
.faq-btn{padding:6px 12px;border:1px solid var(--line,#d8dee8);background:#fff;
  border-radius:999px;font-size:13px;line-height:1.3;cursor:pointer;
  color:var(--ink,#16324f);text-align:left}
.faq-btn:hover{background:#f0f4fa;border-color:#b9c6d8}
.faq-btn:disabled{opacity:.5;cursor:default}

/* Inline field hint (details form) */
.auth .hint{font-size:13px;line-height:1.4;color:#64748b;margin:4px 0 12px}

/* Autofill detection: Chrome exposes autofill only via :-webkit-autofill, so a
   no-op animation on it gives JS an animationstart event to listen for. */
@keyframes ccAutofill { from { opacity: 1; } to { opacity: 1; } }
input[inputmode="email"]:-webkit-autofill { animation-name: ccAutofill; animation-duration: 1ms; }

/* Typing indicator */
.typing-row{display:flex;align-items:center;gap:7px;margin:0;padding:4px 16px 0;
  font-size:13px;color:#64748b;min-height:20px}
.typing-row[hidden]{display:none}

/* The "Connecting you with an agent…" standby banner (.standbybar and its
   parts) was removed along with its markup and the script that toggled it, so
   its styles and keyframes are gone too — nothing references them any more. */
.typing-dots{display:inline-flex;gap:3px;align-items:flex-end}
.typing-dots i{width:5px;height:5px;border-radius:50%;background:#94a3b8;
  animation:ccTyping 1.25s infinite ease-in-out}
.typing-dots i:nth-child(2){animation-delay:.18s}
.typing-dots i:nth-child(3){animation-delay:.36s}
@keyframes ccTyping{
  0%,60%,100%{transform:translateY(0);opacity:.45}
  30%{transform:translateY(-4px);opacity:1}
}
@media (prefers-reduced-motion:reduce){
  .typing-dots i{animation:none;opacity:.7}
}

/* The agent "typing…" bubble is a real bot bubble holding the animated dots,
   so it lines up exactly where the message it precedes will appear. */
.bubble-typing{display:inline-flex;align-items:center;padding:11px 14px}
.bubble-typing .typing-dots i{background:#8b93a7}
.agent-typing{animation:ccTypingIn .18s ease-out}
@keyframes ccTypingIn{from{opacity:0}to{opacity:1}}
@media (prefers-reduced-motion:reduce){
  .agent-typing{animation:none}
}

/* Message timestamps — a floating label revealed on tap. It is positioned
   absolutely against the stack, so showing or hiding it never reflows the
   thread: no bubble is pushed down. It sits just beneath the message, flush to
   the message's own side, as a small rounded pill that stays readable over
   whatever is behind it. */
.bubble-time{position:absolute;top:100%;margin-top:1px;z-index:5;
  font-size:11px;line-height:1.3;white-space:nowrap;pointer-events:none;
  padding:1px 8px;border-radius:999px;color:#fff;background:rgba(15,23,42,.78);
  box-shadow:0 1px 4px rgba(16,24,40,.20)}
.row-me   .bubble-time{right:0;left:auto;text-align:right}
.row-them .bubble-time{left:0;right:auto;text-align:left}

/* Admin (human staff) messages */
.avatar-admin{background:#7C3AED;color:#fff;font-size:13px}
.bubble-admin{background:#E7F3FF}

/* An admin's avatar carries a small "admin" badge directly beneath it, with the
   assigned name beside the avatar. Admin rows opt out of the hanging-avatar
   gutter the other rows use (the badge is wider than the picture, which the
   gutter's fixed offset assumes it is not) and top-align so the badge sits under
   the picture rather than beside the bubble. */
.avatarcol{display:flex;flex-direction:column;align-items:center;gap:3px;flex:none}
.avatarcol .avatar{margin-left:0}
.row-admin{align-items:flex-start;padding-left:0}
.avatar-role{font-size:9px;line-height:1;font-weight:700;letter-spacing:.02em;
  color:#fff;background:#7C3AED;padding:2px 6px;border-radius:var(--r-pill);
  text-transform:lowercase}

/* FAQ answers — the agent's reply to a tapped FAQ button.
   Addressed to one reader rather than the room, so they are tinted apart from
   the plain agent grey (#E9EBEE). The left edge carries the weight; the fill
   stays pale so the text keeps its contrast. Placed after .bubble-bot so it
   wins on the shared .bubble-bot class.

   Scoped to .row-them: the question that precedes an answer is the reader's
   own message and sits on the other side, so this must never reach a
   right-hand bubble even if the class were applied there by mistake. */
.row-them .bubble-faq{
  background:#FFF6E5;
  box-shadow:inset 3px 0 0 0 #E8A33D;
  border-radius:18px 18px 18px 4px;
}
.row-them .bubble-faq a{color:#9A5B00}

/* Avatars: real picture, or a Meta-style blank silhouette */
.avatar{overflow:hidden;background:#E4E6EB;flex:none}
.avatar img{width:100%;height:100%;object-fit:cover;display:block}
.avatar-blank{background:#E4E6EB no-repeat center/70%;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23BCC0C4'><circle cx='12' cy='8.5' r='4'/><path d='M12 14c-4.4 0-8 2.5-8 5.5V22h16v-2.5c0-3-3.6-5.5-8-5.5z'/></svg>")}

/* Own picture button in the header */
.myavatar{width:28px;height:28px;border-radius:50%;border:0;padding:0;cursor:pointer;
  background:#E4E6EB no-repeat center/cover;flex:none;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23BCC0C4'><circle cx='12' cy='8.5' r='4'/><path d='M12 14c-4.4 0-8 2.5-8 5.5V22h16v-2.5c0-3-3.6-5.5-8-5.5z'/></svg>")}
.myavatar.hasimg{background-size:cover}

/* Attachment preview above the composer */
.imgpreview{position:relative;display:inline-block;margin:8px 14px 0}
.imgpreview[hidden]{display:none}
.imgpreview img{max-height:88px;border-radius:12px;display:block;border:1px solid var(--line,#d8dee8)}
.imgpreview-x{position:absolute;top:-7px;right:-7px;width:22px;height:22px;border-radius:50%;
  border:0;background:#111;color:#fff;font-size:15px;line-height:1;cursor:pointer}

/* Tighter bubbles — less dead space around short messages */
.bubble{padding:7px 11px}
.bubble-time{margin-top:2px}
.row{margin:0}
.thread{gap:2px}

/* Picture crop dialog */
.avmodal{position:fixed;inset:0;background:rgba(0,0,0,.45);display:flex;
  align-items:center;justify-content:center;padding:20px;z-index:60}
.avmodal[hidden]{display:none}
.avbox{background:#fff;border-radius:14px;padding:18px;max-width:340px;width:100%}
.avbox h2{margin:0 0 4px;font-size:17px}
.avhint{margin:0 0 12px;font-size:13px;color:#65676B}
.avstage{position:relative;width:256px;height:256px;margin:0 auto;border-radius:50%;
  overflow:hidden;cursor:grab;touch-action:none;background:#e4e6eb}
.avstage:active{cursor:grabbing}
.avstage canvas{display:block}
.avring{position:absolute;inset:0;border-radius:50%;box-shadow:0 0 0 2px rgba(0,0,0,.12) inset;
  pointer-events:none}
.avzoom{display:flex;align-items:center;gap:10px;margin:14px 0 4px;font-size:13px;color:#65676B}
.avzoom input{flex:1}
.avacts{display:flex;align-items:center;gap:8px;margin-top:10px}
.avspacer{flex:1}
.avghost{background:none;border:1px solid var(--line,#d8dee8);color:#111}

  background:rgba(0,0,0,.05);border-radius:8px;cursor:pointer;font-size:13px;line-height:1.3}
.row-me .quote{border-left-color:rgba(255,255,255,.55);background:rgba(255,255,255,.16)}
.quote-who{display:block;font-weight:600;font-size:12px;opacity:.85}
.quote-text{display:block;opacity:.8;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.flash{animation:ccFlash 1.2s ease}
@keyframes ccFlash{0%,100%{background:transparent}30%{background:rgba(124,58,237,.14)}}

/* Reply composer bar */
.replybar{display:flex;align-items:center;gap:10px;margin:8px 14px 0;padding:7px 10px;
  background:#F0F2F5;border-left:3px solid #7C3AED;border-radius:8px}
.replybar[hidden]{display:none}
.replybar-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.replybar-who{font-size:12px;font-weight:600;color:#5B21B6}
.replybar-text{font-size:13px;color:#65676B;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.replybar-x{flex:none;width:24px;height:24px;border:0;border-radius:50%;background:none;
  font-size:18px;line-height:1;color:#65676B;cursor:pointer}
.replybar-x:hover{background:rgba(0,0,0,.07);color:#111}

/* Timestamps hide after a few minutes; tap a bubble to bring one back. */
.bubble-time{transition:opacity .15s}
.bubble-time.is-hidden{display:none}
.bubble{cursor:pointer}

/* Seen-by avatars */
.seenby{display:flex;gap:2px;align-items:center;margin:2px 4px 0;justify-content:flex-end}
.seenby-av{width:14px;height:14px;border-radius:50%;background:#E4E6EB no-repeat center/cover;
  border:1px solid #fff;flex:none}
.seenby-blank{background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23BCC0C4'><circle cx='12' cy='8.5' r='4'/><path d='M12 14c-4.4 0-8 2.5-8 5.5V22h16v-2.5c0-3-3.6-5.5-8-5.5z'/></svg>");
  background-size:80%}

/* ---- Signup: avatar picker with camera badge ---- */
.avpick{display:flex;flex-direction:column;align-items:center;gap:6px;margin-bottom:18px}
.avpick-btn{position:relative;width:96px;height:96px;padding:0;border:0;border-radius:50%;
  background:#E4E6EB;cursor:pointer;display:flex;align-items:center;justify-content:center}
.avpick-img{width:100%;height:100%;border-radius:50%;overflow:hidden;
  display:flex;align-items:center;justify-content:center}
.avpick-img img{width:100%;height:100%;object-fit:cover}
.avpick-cam{position:absolute;right:0;bottom:2px;width:30px;height:30px;border-radius:50%;
  background:#fff;border:1px solid #d8dee8;color:#050505;
  display:flex;align-items:center;justify-content:center;box-shadow:0 1px 3px rgba(0,0,0,.12)}
.avpick-label{font-size:13px;color:#65676B}

/* ---- Chat page: profile card beside the conversation ---- */
.chatwrap{display:grid;grid-template-columns:minmax(210px,250px) minmax(0,1fr);
  gap:16px;max-width:1060px;margin:0 auto;padding:16px;align-items:stretch}
.chatwrap > .mecard{align-self:start}
@media(max-width:860px){
  .chatwrap{grid-template-columns:1fr;padding:0;gap:0}
  .mecard{order:2;margin:12px}
}
.mecard{background:#fff;border:1px solid var(--line,#E4E6EB);border-radius:14px;
  padding:18px 16px;display:flex;flex-direction:column;align-items:center;gap:4px}
.mecard-av{position:relative;width:88px;height:88px;padding:0;border:0;border-radius:50%;
  background:#E4E6EB;cursor:pointer;display:flex;align-items:center;justify-content:center}
.mecard-img{width:100%;height:100%;border-radius:50%;overflow:hidden;
  display:flex;align-items:center;justify-content:center}
.mecard-img img{width:100%;height:100%;object-fit:cover}
.mecard-cam{position:absolute;right:-2px;bottom:0;width:28px;height:28px;border-radius:50%;
  background:#fff;border:1px solid #d8dee8;color:#050505;
  display:flex;align-items:center;justify-content:center;box-shadow:0 1px 3px rgba(0,0,0,.12)}
.mecard-name{margin:10px 0 4px;font-size:16px;font-weight:600;text-align:center}
.mecard-facts{display:grid;grid-template-columns:1fr;gap:0;margin:8px 0 0;width:100%;font-size:13px}
.mecard-facts dt{color:#65676B;font-size:11.5px;text-transform:uppercase;
  letter-spacing:.04em;margin-top:10px}
.mecard-facts dd{margin:1px 0 0;overflow-wrap:anywhere}

/* ---- Profile completeness: barber-pole progress bar ---- */
.mebar{width:100%;height:14px;margin:14px 0 6px;border-radius:999px;
  background:#E4E6EB;overflow:hidden}
.mebar-fill{height:100%;border-radius:999px;background-color:#1A9E4B;
  /* Yellow diagonals over green, scrolling like a barber's pole. */
  background-image:repeating-linear-gradient(45deg,
    #FFD028 0 10px, transparent 10px 20px);
  background-size:28px 28px;
  animation:ccBarber 0.8s linear infinite;
  transition:width .5s ease}
@keyframes ccBarber{from{background-position:0 0}to{background-position:28px 0}}
@media (prefers-reduced-motion:reduce){.mebar-fill{animation:none}}
.mebar-label{margin:0 0 10px;font-size:12px;color:#65676B;font-weight:600}

/* ---- Step checklist ---- */
.mesteps{list-style:none;margin:0;padding:0;width:100%;display:flex;
  flex-direction:column;gap:9px}
.mesteps li{display:flex;gap:9px;align-items:flex-start}
.mestep-num{flex:none;width:22px;height:22px;border-radius:50%;
  background:#E4E6EB;color:#65676B;font-size:11.5px;font-weight:700;
  display:flex;align-items:center;justify-content:center;margin-top:1px}
.isdone .mestep-num{background:#1A9E4B;color:#fff}
.mestep-body{min-width:0;display:flex;flex-direction:column;gap:1px}
.mestep-label{font-size:11.5px;color:#65676B;text-transform:uppercase;
  letter-spacing:.03em;display:flex;align-items:center;gap:6px}
.mestep-count{font-size:10.5px;font-weight:700;color:#8A8D91;
  background:#F0F2F5;padding:1px 5px;border-radius:999px;letter-spacing:0}
.isdone .mestep-count{background:#E3F5E8;color:#1A7F37}
.mestep-value{font-size:13.5px;overflow-wrap:anywhere;display:flex;
  align-items:center;gap:5px;color:#050505}
.mestep-value em{color:#8A8D91;font-style:normal;font-size:13px}
.mestep-value svg{flex:none;color:#65676B}

/* Composer: the quoted message shown as a bubble for reference */
.replybar{align-items:flex-start}
.replybar-quote{display:block;margin-top:3px;padding:6px 10px;border-radius:12px;
  background:#fff;border:1px solid #dcdfe4;font-size:13px;color:#050505;
  line-height:1.35;max-height:44px;overflow:hidden}

/* Avatar dialog: remove button and clearer states */
.avdanger{background:none;border:1px solid var(--danger-line);color:var(--danger)}
.avdanger:hover{background:#fee2e2}
.avdanger[hidden]{display:none}
#avSave:disabled{opacity:.45;cursor:default}
.avacts{flex-wrap:wrap}
#avMsg{margin:10px 0 0;font-size:13px;min-height:18px}

/* ---- Quoted reply: its own faded bubble above the answer ---- */
.quotebox{align-self:flex-start;max-width:100%;margin:0 0 -8px 10px;
  padding:6px 11px 12px;border-radius:14px 14px 14px 4px;
  background:#E9EAEE;color:#65676B;font-size:13px;line-height:1.32;
  cursor:pointer;position:relative;z-index:0;opacity:.92}
.row-me .quotebox{align-self:flex-end;margin:0 10px -8px 0;
  border-radius:14px 14px 4px 14px}
.quotebox-who{display:block;font-weight:600;font-size:11.5px;color:#8A8D91;margin-bottom:1px}
.quotebox-text{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden;overflow-wrap:anywhere}
.quotebox:hover{background:#DFE1E6}

/* The reply sits over the quote, so the two read as one exchange. */
.bubble.has-quote{position:relative;z-index:1}
.stack{display:flex;flex-direction:column}
.row-me .stack{align-items:flex-end}
.row-them .stack{align-items:flex-start}

/* ---- Full-width shell ---- */
.chat{max-width:none;height:100vh;border:0;border-radius:0}
.chat-body{background:var(--paper)}
/* A cleaner, more readable typeface for the whole chat surface. Inter loads
   from the head; the system stack keeps things sharp if it does not. */
.chat-body{font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,
  'Helvetica Neue',Arial,'Apple Color Emoji','Segoe UI Emoji',sans-serif;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
.chat-body .bubble{letter-spacing:.1px}
.view{display:block;padding:16px;max-width:640px;margin:0 auto}
.view[hidden]{display:none}
.mecard-wide{align-items:stretch}

/* ---- App bar: one row above every view, at every width ---- */
/* Sticky also makes this the containing block for the dropdown, which is
   positioned absolutely just under it. */
.appbar{position:sticky;top:0;z-index:80;flex:none;
  display:flex;align-items:center;gap:10px;padding:8px 12px;
  background:#fff;border-bottom:1px solid var(--line)}
.appbar-title{flex:1;min-width:0;margin:0;font-size:15px;font-weight:600;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Placement A: the "Rewards 4 Feedbacks" logo, left-aligned on a dark strip so
   the white part of the wordmark reads (it is built for a dark background). The
   campaign name sits beside it in white as secondary context. */
.appbar-brand{background:linear-gradient(180deg,#2b2147,#211838);border-bottom-color:#17102b}
.appbar-logo{flex:none;height:30px;width:auto;display:block}
.appbar-brand .appbar-title{color:#fff;opacity:.82;font-size:13.5px;font-weight:600}

/* ---- Section menu (three dots in a grey circle, beside the bell) ---- */
.navwrap{position:relative;flex:none}
.navdots{flex:none;width:34px;height:34px;padding:0;border:0;border-radius:50%;
  background:#E4E6EB;cursor:pointer;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px}
.navdots span{display:block;width:4px;height:4px;border-radius:50%;
  background:var(--ink);transition:background .12s}
.navdots:hover{background:#D8DADF}
.navdots:focus-visible{outline:2px solid var(--red);outline-offset:2px}
.navdots[aria-expanded="true"]{background:#D8DADF}

/* Anchored to the dots and opening down-and-left, since it now sits near the
   right edge of the room header rather than the top-left of the app bar. */
.burgermenu{position:absolute;top:calc(100% + 6px);right:0;left:auto;z-index:90;
  min-width:200px;max-width:calc(100vw - 24px);padding:6px;
  background:#fff;border:1px solid var(--line);
  border-radius:12px;box-shadow:0 8px 24px rgba(0,0,0,.14);
  display:flex;flex-direction:column;gap:2px}
.burgermenu[hidden]{display:none}
/* Each item is icon + label. */
.burgermenu button{display:flex;align-items:center;gap:10px;width:100%;
  text-align:left;padding:10px 12px;border:0;border-radius:8px;
  background:none;font:inherit;font-size:14px;cursor:pointer;color:var(--ink)}
.burgermenu .navi{flex:none;color:var(--muted)}
.burgermenu button:hover{background:#F0F2F5}
.burgermenu button:hover .navi{color:var(--red)}
.burgermenu button:focus-visible{outline:2px solid var(--red);outline-offset:-2px}
.burgermenu button[aria-current="true"]{background:#F0F2F5;font-weight:600;color:var(--red)}
.burgermenu button[aria-current="true"] .navi{color:var(--red)}

/* ---- Roster: who is in the room, under the header ----
   Each person is a vertical cell: the avatar on top, their name (first name
   and last initial) beneath it. */
.roster{display:flex;flex-wrap:wrap;gap:14px 16px;padding:12px 16px;
  background:var(--paper);border-bottom:1px solid var(--line)}
.roster:empty{display:none}
.rmate{position:relative;display:flex;flex-direction:column;align-items:center;
  gap:5px;width:64px}
/* The avatar and its presence dot share this wrapper so the dot sits on the
   picture, not down beside the name. */
.rmate-avwrap{position:relative;width:60px;height:60px}
.rmate-av{display:block;width:60px;height:60px;border-radius:50%;
  background:#E4E6EB no-repeat center/cover;border:2px solid #fff;
  box-shadow:0 1px 3px rgba(16,24,40,.16)}
.rmate-blank{background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23BCC0C4'><circle cx='12' cy='8.5' r='4'/><path d='M12 14c-4.4 0-8 2.5-8 5.5V22h16v-2.5c0-3-3.6-5.5-8-5.5z'/></svg>");
  background-size:74%}
/* The name under the avatar. Clipped to the cell width with an ellipsis so a
   long name cannot stretch the row; the tooltip on .rmate carries it in full. */
.rmate-name{max-width:64px;font-size:11.5px;line-height:1.15;font-weight:600;
  color:var(--ink);text-align:center;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
/* Presence dot, bottom-right of the avatar.
   Green inside the first minute, amber for the ten after, and removed
   entirely beyond that — absence of a dot is the third state, so there is
   deliberately no grey variant. The ring is the roster's own background,
   which is what separates the dot from the avatar edge underneath it. */
.rmate-dot{position:absolute;right:1px;bottom:1px;width:14px;height:14px;
  border-radius:50%;border:2px solid var(--paper)}
.rmate-dot-on{background:#31A24C}
.rmate-dot-idle{background:#E8A33D}

/* The admin sits first in the roster and is a role rather than a person, so
   it gets a mark on a filled circle instead of a photo or an initial. */
.rmate-admin{margin-right:4px}
.rmate-adminav{background:#7C3AED;color:#fff;font-size:26px;line-height:56px;
  text-align:center;background-image:none}

/* ---- Profile edit form ---- */
.meform{width:100%;display:flex;flex-direction:column;gap:10px;margin-top:14px;
  padding-top:14px;border-top:1px solid var(--line)}
.meform label{display:flex;flex-direction:column;gap:4px;font-size:12px;
  color:var(--muted);text-transform:uppercase;letter-spacing:.03em}
.meform input{padding:11px 13px;border:1px solid var(--line);border-radius:var(--r-sm);
  font:inherit;font-size:15px;text-transform:none;color:var(--ink);background:#fff;
  transition:border-color .12s,box-shadow .12s}
.meform input:hover{border-color:#c9ccd1}
.meform input:focus,.meform select:focus{outline:none;border-color:var(--accent);
  box-shadow:0 0 0 3px var(--accent-wash)}

/* ---- Select ----------------------------------------------------------------
   The native control is kept — it gives the iOS wheel and Android list, which
   beat anything custom on a phone, and type-ahead for free on desktop. Only
   the chrome is replaced: appearance:none removes the platform arrow, and the
   wrapper draws a chevron in its place. */
.mefield{position:relative;display:block}
.meform select{-webkit-appearance:none;-moz-appearance:none;appearance:none;
  width:100%;padding:11px 38px 11px 13px;border:1px solid var(--line);
  border-radius:var(--r-sm);background:#fff;font:inherit;font-size:15px;
  color:var(--ink);text-transform:none;cursor:pointer;
  transition:border-color .12s,box-shadow .12s}
.meform select:hover{border-color:#c9ccd1}
/* Nothing chosen yet reads as placeholder text rather than a real answer.
   The class comes from chat.js; :invalid would need the field to be required. */
.meform select.is-empty{color:var(--muted)}
.mefield::after{content:"";position:absolute;right:14px;top:50%;
  width:9px;height:9px;margin-top:-6px;pointer-events:none;
  border-right:2px solid var(--muted);border-bottom:2px solid var(--muted);
  transform:rotate(45deg);transition:border-color .12s}
.mefield:hover::after{border-color:var(--ink)}
.mefield:focus-within::after{border-color:var(--accent)}
.meform optgroup{font-size:13px;color:var(--muted)}
.meform option{color:var(--ink);font-size:15px}

.meform-acts{display:flex;align-items:center;gap:12px;margin-top:8px}

/* ---- Room-create progress bar --------------------------------------------
   A staged, cosmetic progress indicator shown while a room is being made. The
   fill only reaches 100% once the server confirms success; on failure the form
   returns with the error, so it never claims "done" before the room exists. */
.rcprog{margin-top:16px}
.rcprog[hidden]{display:none}
.rcprog-track{height:10px;border-radius:999px;background:#eef0f3;overflow:hidden;position:relative}
.rcprog-fill{height:100%;width:0;border-radius:999px;position:relative;overflow:hidden;
  background:linear-gradient(90deg,#12a150,#1a7f37);
  transition:width .45s cubic-bezier(.4,.1,.2,1)}
.rcprog-fill::after{content:"";position:absolute;inset:0;
  background:linear-gradient(100deg,transparent,rgba(255,255,255,.45),transparent);
  transform:translateX(-100%);animation:rcSheen 1.1s ease-in-out infinite}
@keyframes rcSheen{to{transform:translateX(100%)}}
.rcprog-row{display:flex;align-items:center;justify-content:space-between;margin-top:12px}
.rcprog-status{display:flex;align-items:center;gap:9px;color:var(--ink);font-size:14px;font-weight:600}
.rcprog-spin{flex:none;width:16px;height:16px;border-radius:50%;
  border:2px solid rgba(26,127,55,.25);border-top-color:#1a7f37;animation:rcSpin .7s linear infinite}
.rcprog.is-done .rcprog-spin{animation:none;border-color:#1a7f37}
@keyframes rcSpin{to{transform:rotate(360deg)}}
.rcprog-pct{font:600 13px var(--mono,ui-monospace,monospace);color:var(--muted);min-width:42px;text-align:right}
@media (prefers-reduced-motion:reduce){.rcprog-fill::after,.rcprog-spin{animation:none}}

/* ---- Save ------------------------------------------------------------------
   Primary action, so it carries the accent. The pressed state moves it a pixel
   rather than changing colour, which reads as a physical press on touch where
   there is no hover to lean on.

   #createSave shares the rule rather than copying it. These are selected by id
   because that is how this file has always addressed them; the cost is that a
   new primary button is a bare browser default until it is named here, which
   is what happened to Create room. */
#profileSave,#createSave{padding:11px 22px;border:0;border-radius:var(--r-pill);
  background:var(--accent);color:#fff;font:inherit;font-size:15px;font-weight:600;
  cursor:pointer;transition:background .12s,transform .06s,box-shadow .12s;
  box-shadow:0 1px 2px rgba(124,58,237,.30)}
#profileSave:hover,#createSave:hover{background:var(--accent-ink);box-shadow:0 2px 8px rgba(124,58,237,.30)}
#profileSave:active,#createSave:active{transform:translateY(1px);box-shadow:0 1px 2px rgba(124,58,237,.30)}
#profileSave:focus-visible,#createSave:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
#profileSave:disabled,#createSave:disabled{opacity:.6;cursor:default;transform:none;box-shadow:none}
/* Confirmation reuses the button rather than only the status line, so the
   feedback lands where the person is already looking. */
#profileSave.is-saved{background:var(--ok);box-shadow:none}

#profileMsg,#createMsg{min-height:1.2em;font-size:13.5px;transition:opacity .15s}
#profileMsg.is-error,#createMsg.is-error{color:var(--danger)}
#profileMsg.is-ok,#createMsg.is-ok{color:var(--ok)}
@media (prefers-reduced-motion:reduce){
  .meform input,.meform select,#profileSave,#createSave,.mefield::after{transition:none}
  #profileSave:active,#createSave:active{transform:none}
}
.mehint{margin:0;font-size:12.5px;color:var(--muted)}
.meh{margin:16px 0 6px;font-size:15px}
.meh:first-child{margin-top:0}
.meswitch{display:flex;align-items:center;gap:9px;font-size:14px;margin:8px 0}
.meswitch input{width:16px;height:16px;flex:none}

/* "Always show message times": quiet inline stamps that always hold their own
   space, rather than floating pills — so a full thread never has labels
   overlapping one another or the messages below them. */
body.always-stamps .bubble-time{position:static;top:auto;margin-top:2px;
  padding:0;background:none;color:#94a3b8;box-shadow:none;white-space:normal;
  font-size:11.5px;line-height:1.3}
body.always-stamps .row-me   .bubble-time{text-align:right}
body.always-stamps .row-them .bubble-time{text-align:left}
body.always-stamps .bubble-time.is-hidden{display:block}

/* ---- Page layout: app bar on top, the active view fills what is left ----
   The body is the column. The bar is a fixed-height row; whichever view is
   showing takes the remaining height. min-height:0 on the flex children is
   what lets the thread scroll internally instead of stretching the page. */
.chat-body{display:flex;flex-direction:column;height:100vh;overflow:hidden}
/* dvh tracks the mobile address bar as it collapses; vh is the fallback. */
@supports (height:100dvh){
  .chat-body{height:100dvh}
}
.chat{flex:1 1 auto;height:auto;min-height:0;overflow:hidden}
/* .chat sets display:flex, which otherwise beats the browser's own rule for
   the hidden attribute and leaves the conversation on screen underneath the
   profile and settings views. */
.chat[hidden]{display:none}
.thread{min-height:0;overflow-y:auto;overflow-x:hidden}
/* Profile and settings scroll inside themselves, the same way the thread
   does, so the bar stays put no matter which section is open. */
.view{flex:1 1 auto;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch}
.chat-head{position:relative}

/* ---------- Review progress ----------
   Sits directly under the chat header's border, so it reads as part of the
   header rather than a separate dashboard component. A flat, thin, modern
   indicator: a slim track, a fill whose width is the REAL percentage, and a
   slow "lava lamp" of soft blobs drifting INSIDE the fill (clipped to it). The
   status sits below the bar as one combined phrase, "Progress: <status>". */
.cprog{padding:8px 16px 7px;border-bottom:1px solid var(--line);background:var(--wash)}
/* A simple flat channel. overflow:hidden clips the fill (and its lava) to the
   rounded ends. */
.cprog-track{position:relative;width:100%;min-width:0;height:8px;border-radius:999px;
  background:rgba(120,130,150,.16);overflow:hidden}
/* The fill: width = real progress. A base gradient carries the site's colour;
   overflow:hidden keeps the animated blobs strictly inside it, and the width
   transition still slides the bar when the percentage moves. */
.cprog-fill{position:relative;height:100%;width:0;border-radius:999px;
  /* A wide gradient that slowly drifts sideways for a little life. overflow is
     left visible so the tip glow can bleed just past the fill; the outer track
     clips everything to the pill shape. */
  background:linear-gradient(90deg,#FF6FB5,#C874E8,#5B8DEF,#38BDF8,#5B8DEF,#C874E8,#FF6FB5);
  background-size:220% 100%;
  animation:cprogFlow 9s linear infinite;
  transition:width .5s ease}
/* A small rectangular glow at the leading tip of the fill — a soft, gently
   breathing light where the progress currently reaches. */
.cprog-fill::after{content:"";position:absolute;top:-1px;bottom:-1px;right:-2px;
  width:7px;border-radius:3px;pointer-events:none;
  background:rgba(120,200,255,.95);filter:blur(2px);
  box-shadow:0 0 7px 2px rgba(56,189,248,.85),0 0 14px 4px rgba(10,124,255,.55);
  animation:cprogTip 2.4s ease-in-out infinite}
@keyframes cprogFlow{0%{background-position:0% 50%}100%{background-position:220% 50%}}
@keyframes cprogTip{0%,100%{opacity:.55}50%{opacity:1}}
/* Status line below the bar. One combined phrase; wraps only if it truly must. */
.cprog-msg{margin:5px 0 0;font-size:12px;font-weight:600;color:var(--muted);
  min-height:1em;transition:color .3s ease}
.cprog-msg:empty{display:none}
/* Full: the status takes the completed blue. */
.cprog.is-full .cprog-msg{color:#3B82F6}

/* ---------- completion celebration ----------
   Fixed overlay, pointer-events limited to the sheet itself, and removed by
   script. The banner explains what the confetti is for; on its own, falling
   paper is decoration rather than feedback. */
.ccfetti{position:fixed;inset:0;pointer-events:none;z-index:70;overflow:hidden}
.ccfetti i{position:absolute;top:-14px;width:9px;height:14px;opacity:0;
  will-change:transform,opacity;
  animation:ccFall var(--dur,2.4s) var(--delay,0s) cubic-bezier(.25,.6,.5,1) forwards}
@keyframes ccFall{
  0%{opacity:0;transform:translate3d(0,-10px,0) rotate(0deg)}
  8%{opacity:1}
  100%{opacity:0;
    transform:translate3d(var(--drift,20px),102vh,0) rotate(var(--spin,540deg))}
}
.ccbanner{position:fixed;left:50%;top:14%;transform:translateX(-50%) translateY(-8px);
  z-index:71;padding:14px 22px;border-radius:16px;text-align:center;
  background:linear-gradient(135deg,rgba(255,111,181,.94),rgba(91,141,239,.94));
  color:#fff;box-shadow:0 12px 34px rgba(16,24,40,.28);
  backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
  font-size:17px;font-weight:700;letter-spacing:.2px;
  opacity:0;animation:ccBannerIn .45s cubic-bezier(.2,1.2,.3,1) forwards;
  max-width:calc(100vw - 32px)}
.ccbanner small{display:block;margin-top:4px;font-size:11.5px;font-weight:600;opacity:.85}
@keyframes ccBannerIn{to{opacity:1;transform:translateX(-50%) translateY(0)}}
.ccbanner.is-going{animation:ccBannerOut .3s ease forwards}
@keyframes ccBannerOut{to{opacity:0;transform:translateX(-50%) translateY(-10px)}}

@media (prefers-reduced-motion:reduce){
  /* Motion is the whole point of these, so they are removed rather than
     slowed: a value that still updates is more useful than an animation.
     The banner stays, because it carries the actual message. */
  .cprog-fill{transition:none;animation:none}
  .cprog-fill::after{animation:none;opacity:.85}
  .ccfetti{display:none}
  .ccbanner{animation:none;opacity:1;transform:translateX(-50%)}
}

/* "optional" marker on a profile label, so the field doesn't read as required */
/* The label is a flex column, so a bare span stretches the full width;
   align-self keeps the pill hugging its text. */
.melabel-opt{align-self:flex-start;font-size:10px;font-weight:700;
  letter-spacing:.02em;padding:1px 7px;margin:2px 0 0;border-radius:var(--r-pill);
  background:var(--paper);color:var(--faint);text-transform:uppercase}

/* City resolved from the ZIP, shown under the field */
.mezip{display:block;margin-top:5px;font-size:12.5px;min-height:1.1em;color:var(--muted)}
.mezip.is-busy{opacity:.7}
.mezip.is-found{color:var(--ok);font-weight:600}

/* ---- Message action sheet ---------------------------------------------------
   Opened by press-and-hold on touch, or right-click with a mouse. Positioned
   in JS against the bubble, so only appearance lives here. */
.msgactions{position:absolute;z-index:120;display:flex;flex-direction:column;
  min-width:170px;padding:6px;background:var(--card);border:1px solid var(--line);
  border-radius:var(--r);box-shadow:0 12px 32px rgba(0,0,0,.18)}
.msgactions-item{display:flex;align-items:center;gap:12px;padding:11px 12px;
  border:0;border-radius:var(--r-sm);background:none;font:inherit;font-size:15px;
  color:var(--ink);text-align:left;cursor:pointer}
.msgactions-item:hover,.msgactions-item:focus-visible{background:var(--paper)}
.msgactions-icon{width:20px;text-align:center;font-size:15px;color:var(--muted)}

/* The message being acted on stays visible while the sheet covers the thread. */
.row.is-acting .bubble{box-shadow:0 0 0 2px var(--accent)}

/* ---- Brief confirmation ---- */
.chattoast{position:fixed;left:50%;bottom:88px;transform:translateX(-50%) translateY(8px);
  z-index:200;background:#0f172a;color:#fff;padding:10px 18px;border-radius:var(--r-pill);
  font-size:14px;opacity:0;pointer-events:none;transition:opacity .18s,transform .18s}
.chattoast.is-on{opacity:1;transform:translateX(-50%) translateY(0)}

/* Long-press should select the menu, not the text under it. */
.bubble{-webkit-touch-callout:none}
@media (prefers-reduced-motion:reduce){.chattoast{transition:none}}

/* ---- admin room-alarm prompt --------------------------------------------
   A small, quiet invitation shown when an admin alarms one of your OTHER
   rooms: "<admin> is alerting you to <room>" with View Room / No Thanks. It
   reads like an ordinary chat invitation, not an alarm — no warning colour,
   no flashing, no overlay. One card per room, stacked bottom-centre, clear of
   the composer. The loud in-thread alarm and this prompt are separate. */
.roomalerts{position:fixed;left:50%;bottom:88px;transform:translateX(-50%);z-index:190;
  display:flex;flex-direction:column;gap:8px;width:min(360px,calc(100vw - 24px));
  pointer-events:none}
.roomalert{pointer-events:auto;background:var(--card);color:var(--ink);
  border:1px solid var(--line);border-radius:14px;padding:12px 14px;
  box-shadow:0 8px 24px rgba(16,24,40,.16);
  display:flex;flex-direction:column;gap:10px;
  animation:roomalertIn .2s ease both}
.roomalert-text{margin:0;font-size:13.5px;line-height:1.4}
.roomalert-actions{display:flex;gap:8px;justify-content:flex-end}
.roomalert-view,.roomalert-no{appearance:none;border-radius:var(--r-sm);cursor:pointer;
  font:inherit;font-size:13px;font-weight:600;padding:6px 14px;line-height:1}
.roomalert-view{border:0;background:var(--accent);color:#fff}
.roomalert-view:hover{filter:brightness(1.05)}
.roomalert-no{border:1px solid var(--line);background:var(--card);color:var(--muted)}
.roomalert-no:hover{background:var(--wash);color:var(--ink)}
@keyframes roomalertIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.roomalert{animation:none}}

/* --------------------------------------------------------------- signing out
   The sign-out control sits at the foot of the burger menu, separated from the
   section links because it leaves the chat rather than moving around inside
   it. */
.burgermenu-out {
  margin: 4px 0 0;
  padding: 4px 0 0;
  border-top: 1px solid rgba(0, 0, 0, .08);
}
.burgermenu-danger {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  color: #d93025;
}
.burgermenu-danger .navi { color: #d93025; }

/* The confirmation shown when /logout.php is reached by GET. */
.logout-confirm      { text-align: center; }
.logout-confirm h1   { margin-bottom: 6px; }
.logout-confirm p    { color: #6b7684; font-size: 14px; }
.logout-back         { margin-top: 12px; }
.logout-back a       { color: #6b7684; font-size: 13px; }

/* 404 for an inactive campaign link (no inline styles, per the CSP). */
.notfound { font: 16px system-ui, sans-serif; padding: 2rem; }

/* Notice on the sign-in page after an admin ended someone's session. */
.auth-notice {
  max-width: 380px;
  margin: 12px auto 0;
  padding: 9px 12px;
  border-radius: 8px;
  background: #fff4e5;
  border: 1px solid #ffd8a8;
  color: #7a4b00;
  font-size: 13px;
  text-align: center;
}

/* ---------- Create a room ----------
   The share panel under the create form. It is hidden until a room exists, so
   these only ever apply to a real link. */
.roomshare{margin-top:22px;padding-top:18px;border-top:1px solid var(--line)}

/* The field and the button read as one control, so the button carries the
   left rule instead of a gap. */
.roomshare-row{display:flex;align-items:stretch;gap:0;margin:10px 0 14px;
  border:1px solid var(--line);border-radius:var(--r-sm);overflow:hidden;
  background:#fff}
.roomshare-row:focus-within{border-color:var(--accent);
  box-shadow:0 0 0 3px var(--accent-wash)}
.roomshare-row input{flex:1;min-width:0;padding:11px 13px;border:0;
  font:inherit;font-size:14px;color:var(--ink);background:none;
  text-overflow:ellipsis}
.roomshare-row input:focus{outline:none}

.roomshare-copy{flex:none;padding:0 18px;border:0;border-left:1px solid var(--line);
  background:var(--accent);color:#fff;font:inherit;font-size:14px;font-weight:600;
  cursor:pointer;transition:background .12s}
.roomshare-copy:hover{background:var(--accent-ink)}
.roomshare-copy:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}

/* Numbered because sharing a link genuinely is a sequence: copy it, send it,
   they open it. The order is the information. */
.roomshare-steps{margin:0 0 14px;padding-left:20px;font-size:13.5px;
  color:var(--muted);line-height:1.6}
.roomshare-steps li{margin:2px 0}

/* Pay-it-forward step: the "you get paid" line reads a touch stronger and gets
   a pixel-art cash bundle that gives a little bounce to nudge more referrals. */
.roomshare-paystep{color:var(--ink)}
.roomshare-paystep strong{color:#1a7f37}
/* Coin glyph used in place of the "+" on the referral buttons. */
.refer-coin{vertical-align:middle;flex:none}
.roomshare-coin{display:inline-flex;vertical-align:-7px;margin-left:5px;color:#1a7f37;
  animation:coinBounce 1.6s ease-in-out infinite}
@keyframes coinBounce{
  0%,100%{transform:translateY(0) rotate(0)}
  25%{transform:translateY(-4px) rotate(-6deg)}
  50%{transform:translateY(0) rotate(0)}
  60%{transform:translateY(-2px) rotate(6deg)}
  75%{transform:translateY(0) rotate(0)}}
@media (prefers-reduced-motion:reduce){.roomshare-coin{animation:none}}

/* Shown to someone who followed an invitation and did not end up where they
   expected — a full room, a closed one, a link that has expired. */
/* "Couldn't join that room" notice — a floating window centred over a dimmed
   page, dismissed with its Back button (or Esc). Replaces the old inline strip. */
.joinmodal{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center;
  background:rgba(0,0,0,.6);padding:24px}
.joinmodal-box{background:#fff;border-radius:16px;max-width:340px;width:100%;
  padding:26px 26px 20px;text-align:center;box-shadow:0 30px 80px rgba(0,0,0,.5)}
.joinmodal-msg{margin:0 0 20px;font-size:15px;line-height:1.5;color:var(--ink)}
.joinmodal-back{display:inline-flex;align-items:center;gap:6px;
  border:1px solid var(--line);background:#fff;color:var(--ink);
  font:inherit;font-size:14px;font-weight:600;padding:9px 18px;border-radius:999px;cursor:pointer}
.joinmodal-back:hover{background:var(--wash)}

/* The creator's way into the room they just made. Sized and coloured like the
   primary Save button rather than like the Copy button beside the link, so the
   two actions on this panel do not compete: entering is the main one, sharing
   is the one you come back for. */
.roomenter{display:inline-block;margin:4px 0 16px;padding:12px 24px;
  border-radius:var(--r-pill);background:var(--accent);color:#fff;
  font-size:15px;font-weight:600;text-decoration:none;
  box-shadow:0 1px 2px rgba(124,58,237,.30);
  transition:background .12s,transform .06s,box-shadow .12s}
.roomenter:hover{background:var(--accent-ink);color:#fff;
  box-shadow:0 2px 8px rgba(124,58,237,.30)}
.roomenter:active{transform:translateY(1px);box-shadow:0 1px 2px rgba(124,58,237,.30)}
.roomenter:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
@media (prefers-reduced-motion:reduce){
  .roomenter{transition:none}
  .roomenter:active{transform:none}
}

/* Share pop-up — raised over the room right after it is created. A dimmed
   full-page backdrop with a white card centred on it, same shape as the
   "couldn't join" notice. The copy field sits in the middle of the card and
   the numbered directions sit below it. */
.sharemodal{position:fixed;inset:0;z-index:1100;display:flex;
  align-items:center;justify-content:center;
  background:rgba(0,0,0,.6);padding:24px}
.sharemodal[hidden]{display:none}
.sharemodal-box{position:relative;background:#fff;border-radius:16px;
  max-width:440px;width:100%;max-height:calc(100vh - 48px);overflow-y:auto;
  padding:30px 28px 24px;text-align:center;
  box-shadow:0 30px 80px rgba(0,0,0,.5)}

.sharemodal-x{position:absolute;top:12px;right:12px;width:34px;height:34px;
  display:inline-flex;align-items:center;justify-content:center;
  border:0;border-radius:50%;background:none;color:var(--muted);
  cursor:pointer;transition:background .12s,color .12s}
.sharemodal-x:hover{background:var(--wash);color:var(--ink)}
.sharemodal-x:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

.sharemodal-title{margin:0 0 6px;font-size:18px;font-weight:600;color:var(--ink)}
.sharemodal-hint{margin:0 0 18px;font-size:13.5px;color:var(--muted);line-height:1.5}

/* The copy field is a .roomshare-row; here it is the centrepiece, so give it a
   little breathing room above and below and let it fill the card's width. */
.sharemodal-row{margin:0 0 18px}

/* Directions read left-aligned even though the card is centred — a numbered
   list centred on the page is hard to follow. */
.sharemodal-steps{text-align:left;margin:0 0 16px}

.sharemodal-cap{margin:0 0 20px;font-size:12.5px;color:var(--muted);line-height:1.5}

.sharemodal-done{display:inline-block;padding:11px 26px;border:0;
  border-radius:var(--r-pill);background:var(--accent);color:#fff;
  font:inherit;font-size:15px;font-weight:600;cursor:pointer;
  box-shadow:0 1px 2px rgba(124,58,237,.30);transition:background .12s}
.sharemodal-done:hover{background:var(--accent-ink)}
.sharemodal-done:focus-visible{outline:2px solid var(--accent);outline-offset:3px}

/* =====================================================================
   Chat inbox shell — conversations on the left, the open one on the right.

   The numbers are the admin console's, deliberately: 360px list, flexible
   thread, 1500px cap, 62px rows. The two sides of the product are the same
   product and were measuring differently. The admin has a third column of
   participant detail beside these two; there is nothing in it a participant
   should see about the people they are talking to, so this stops at two.

   Sized by the viewport, not by its contents. .chat-body is a flex column and
   an auto cross-axis margin on a child switches it from stretching to
   shrink-to-fit — the bug that made the admin console a different width in
   every room. width:100% and border-box keep that from happening here.
   ===================================================================== */
.chat-body{display:flex;flex-direction:column;height:100vh;height:100dvh;margin:0}
.chat-body>.appbar{flex:none}

/* ---- Single-layer chat shell -------------------------------------------
   The active conversation is the one primary interface and fills the shell:
   centred with a comfortable max width on desktop, edge-to-edge on a phone.
   The room list is no longer a column beside or behind the chat — it is a
   slide-in drawer (below), so there is never a second visible layer. */
.cbox{
  flex:1 1 auto;
  min-height:0;
  width:100%;
  max-width:1100px;
  box-sizing:border-box;
  margin:0 auto;
  display:flex;
  position:relative;
  border-left:1px solid var(--line);
  border-right:1px solid var(--line);
  overflow:hidden;
  background:var(--card)}

/* A view (create / rename / profile / settings) hides the whole shell. */
.cbox[hidden]{display:none}

/* -------------------------------------------------- conversations drawer
   The room list slides in over the chat from the left, dimming the chat with a
   backdrop. Closed, it is off-screen AND visibility:hidden so its links stay
   out of the tab order. */
.cbox-backdrop{position:fixed;inset:0;z-index:115;background:rgba(16,24,40,.42)}
.cbox-backdrop[hidden]{display:none}

.cbox-list{position:fixed;top:0;left:0;bottom:0;z-index:120;
  width:min(360px,calc(100vw - 40px));
  display:flex;flex-direction:column;min-width:0;min-height:0;
  background:var(--card);border-right:1px solid var(--line);
  box-shadow:2px 0 26px rgba(16,24,40,.20);
  transform:translateX(-100%);visibility:hidden;
  transition:transform .24s ease, visibility 0s linear .24s;
  will-change:transform}
.cbox.list-open .cbox-list{transform:translateX(0);visibility:visible;
  transition:transform .24s ease, visibility 0s}
@media (prefers-reduced-motion:reduce){
  .cbox-list,.cbox.list-open .cbox-list{transition:none}
}
/* Close (×) button in the drawer header. */
.cbox-listclose{flex:none;width:34px;height:34px;padding:0;border:0;border-radius:50%;
  background:none;color:var(--muted);cursor:pointer;display:inline-flex;
  align-items:center;justify-content:center}
.cbox-listclose:hover{background:var(--wash,#f0f4fa);color:var(--ink)}
.cbox-listclose:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* -------------------------------------------------------- list contents */
.cbox-head{display:flex;align-items:center;gap:8px;padding:12px 14px;
  border-bottom:1px solid var(--line)}
.cbox-head h2{margin:0;font-size:17px;flex:0 1 auto;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* "+" invite button, pushed to the right of the header. A soft circle with a
   person-plus glyph — "add someone to this chat". */
.cbox-add{margin-left:auto;flex:none;width:34px;height:34px;padding:0;border:0;
  border-radius:50%;background:var(--accent-soft);color:var(--accent-ink);
  cursor:pointer;display:flex;align-items:center;justify-content:center}
.cbox-add:hover{filter:brightness(.96)}
.cbox-add:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* Green "Refer" link in the Group Chats header — goes to the homepage-signups
   room. Styled as a pill; text-decoration:none keeps it from underlining. */
.cbox-refer{margin-left:auto;flex:none;display:inline-flex;align-items:center;gap:6px;
  padding:7px 14px;border:0;border-radius:999px;background:#16a34a;color:#fff;
  font:inherit;font-size:13px;font-weight:700;cursor:pointer;text-decoration:none}
.cbox-refer:hover{background:#15803d}
.cbox-refer:focus-visible{outline:2px solid #16a34a;outline-offset:2px}
.cbox-refer svg{display:block}

/* "Back to chat" control at the top of the profile / settings / create views. */
.viewback{display:inline-flex;align-items:center;gap:6px;margin:0 0 14px;
  padding:6px 11px;border:1px solid var(--line);border-radius:8px;background:#fff;
  font:inherit;font-size:13px;font-weight:600;color:var(--ink);cursor:pointer}
.viewback:hover{background:var(--wash)}
.viewback:focus-visible{outline:2px solid var(--red);outline-offset:2px}
.cbox-badge{display:inline-flex;align-items:center;justify-content:center;
  min-width:20px;height:20px;padding:0 6px;border-radius:999px;
  background:var(--danger);color:#fff;font-size:12px;font-weight:600;line-height:1}

/* scrollbar-gutter, so a list long enough to scroll is not narrower than one
   that is not. Without it every row shifts sideways as conversations arrive. */
.cbox-scroll{flex:1;overflow-y:auto;scrollbar-gutter:stable;padding:6px}

/* Fixed height, so the list keeps one rhythm however much is in a row. */
.cbox-item{
  display:flex;align-items:center;gap:11px;
  height:62px;box-sizing:border-box;padding:9px 10px;
  border-radius:10px;text-decoration:none;color:inherit;cursor:pointer}
.cbox-item:hover{background:var(--wash,#f2f4f7)}
.cbox-item.on{background:var(--accent-soft)}
/* A class rule beats the browser's own [hidden]{display:none}. */
.cbox-item[hidden]{display:none}

.cbox-av{flex:none;width:44px;height:44px;border-radius:50%;
  background:var(--accent-soft);color:var(--accent-ink);
  display:flex;align-items:center;justify-content:center;
  font-weight:600;font-size:17px}

.cbox-body{flex:1;min-width:0;display:flex;flex-direction:column;
  justify-content:center;gap:2px;overflow:hidden}
.cbox-name{font-size:14px;line-height:20px;font-weight:600;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cbox-prev{font-size:13px;line-height:20px;color:var(--muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cbox-muted{color:var(--faint,#9aa1ab)}
.cbox-unread .cbox-name{font-weight:700}
.cbox-unread .cbox-prev{color:var(--ink);font-weight:600}
/* A room that just received a message flashes once as it moves up the list,
   so the eye catches the change rather than only the reordered position. */
.cbox-bumped{animation:cboxBump 1.4s ease-out 1}
@keyframes cboxBump{
  0%{background:var(--accent-soft)}
  100%{background:transparent}
}
@media (prefers-reduced-motion:reduce){
  .cbox-bumped{animation:none}
}

.cbox-meta{flex:none;display:flex;align-items:center;gap:5px;flex-wrap:wrap;justify-content:flex-end;max-width:120px}
.cbox-pip{display:inline-flex;align-items:center;justify-content:center;
  min-width:20px;height:20px;padding:0 6px;border-radius:999px;
  background:var(--accent);color:#fff;font-size:12px;font-weight:700;line-height:1}
/* Small tags on a room tab: green "Paid" at 100%, blue "Customer support". */
.cbox-tag{display:inline-flex;align-items:center;height:18px;padding:0 7px;
  border-radius:999px;font-size:10.5px;font-weight:700;line-height:1;letter-spacing:.2px;white-space:nowrap}
.cbox-tag-paid{background:#d6f5df;color:#17663a}
.cbox-tag-support{background:#e3ecfb;color:#1d4ed8}
.cbox-empty{padding:18px 12px;color:var(--muted);font-size:13.5px;text-align:center}

/* ---- Active chat fills the shell --------------------------------------- */
.cbox>.chat{flex:1 1 auto;height:auto;min-height:0;min-width:0;width:100%;
  border:0;border-radius:0;max-width:none}

/* On a phone the shell is edge-to-edge; the drawer widens on small screens. */
@media(max-width:860px){
  .cbox{max-width:none;border-left:0;border-right:0}
}
@media(max-width:520px){
  .cbox-list{width:calc(100vw - 40px)}
}

/* ---- "Chats" button — opens the conversations drawer (all widths) ------- */
.cbox-back{display:inline-flex;align-items:center;justify-content:center;flex:none;
  width:38px;height:38px;padding:0;border:0;border-radius:50%;
  background:none;color:var(--ink);cursor:pointer}
.cbox-back:hover{background:var(--wash,#f0f4fa)}
.cbox-back:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.cbox-back svg{display:block}

/* ---- In-app notification centre ---------------------------------------
   Circular lightning button, top-right, with a red unread badge over its
   upper-right corner and a dropdown listing the rooms with unread activity
   (one row per room). The dropdown overlays the interface and never shifts
   the page. */
.notifwrap{position:relative;flex:none}
.notifbtn{position:relative;display:inline-flex;align-items:center;justify-content:center;
  width:40px;height:40px;padding:0;border:0;border-radius:50%;
  background:var(--wash,#eef1f6);color:var(--ink);cursor:pointer;transition:background .12s ease}
.notifbtn:hover{background:#e3e8f0}
.notifbtn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.notifbtn svg{display:block}
.notif-badge{position:absolute;top:-3px;right:-3px;
  display:inline-flex;align-items:center;justify-content:center;
  min-width:19px;height:19px;padding:0 5px;border-radius:999px;
  background:#e11d2a;color:#fff;font-size:11px;font-weight:800;line-height:1;
  border:2px solid var(--card,#fff);box-sizing:border-box}
.notif-badge[hidden]{display:none}

/* ---------------------------------------------------------- header crowding
   On a phone the room header has to fit the back arrow, the creator's avatar,
   the room name, the "who's here" avatar cluster and the menu on one narrow
   row. The cluster has no natural width limit, so as people join it grew
   unchecked — squeezing the room name down to a couple of letters and, with
   enough people, crowding the menu. The name is what matters here, so the
   cluster is capped to three faces (admin, if present, plus two) and the rest
   are dropped; the name takes the space that frees up. */
@media(max-width:600px){
  .chead-people{flex:none}
  .chead-people .chead-av-sm:nth-child(n+4){display:none}
}

/* ---- Service bell in the composer ----------------------------------------
   Matches the emoji/send icon buttons, tinted so it reads as "available". */
.composer .bell-btn{flex:none;width:40px;height:40px;border:0;border-radius:50%;
  display:grid;place-items:center;cursor:pointer;color:var(--accent);
  background:var(--accent-soft);transition:background .15s,transform .08s}
.composer .bell-btn:hover{background:#E4DBFB}
.composer .bell-btn:active{transform:scale(.94)}
.composer .bell-btn[data-state="ringing"]{background:var(--accent);color:#fff}
.composer .bell-btn[data-state="cooldown"]{background:var(--wash);color:var(--faint)}
.composer .bell-btn:disabled{cursor:default}

/* Attention cue: a gentle pulsing glow so people notice the bell is pressable.
   Only while it's ready (idle) — it stops the moment it's rung or on cooldown,
   and honours reduced-motion. */
.composer .bell-btn[data-state="ready"]{animation:bellNudge 2.4s ease-in-out infinite}
@keyframes bellNudge{
  0%,100%{box-shadow:0 0 0 0 rgba(124,58,237,.45);transform:scale(1)}
  8%{transform:scale(1.08)}
  16%{transform:scale(1)}
  70%{box-shadow:0 0 0 10px rgba(124,58,237,0)}
}
.composer .bell-btn:hover,
.composer .bell-btn:focus-visible{animation:none}
@media (prefers-reduced-motion:reduce){
  .composer .bell-btn[data-state="ready"]{animation:none}
}

/* Room alarm — an admin's "look now" broadcast to everyone in the room. The
   loud tone and strong vibration still fire in chat.js the moment it arrives;
   the transcript line itself is deliberately quiet, reading like an ordinary
   system/transcript entry ("Winston Room alerted  7:14 AM") rather than the
   old red boxed banner. No bell, no border, no warning colour, no pulse. */
.alarmline{display:flex;align-items:baseline;justify-content:center;gap:8px;
  margin:8px auto;max-width:92%;text-align:center;
  font-size:12.5px;color:var(--muted)}
.alarmline-text{font-weight:400;min-width:0;word-break:break-word}
.alarmline-time{font-size:.85em;opacity:.7;white-space:nowrap}

/* Push notification gate — sits above the composer so the conversation stays
   readable behind it, and is deliberately not dismissable. */
/* The gate sits in the same flex column as the thread, between it and the
   composer. Left uncapped it takes its full content height, and on a short
   screen (or in landscape) that squeezed the thread — the scrollable message
   list — down toward nothing, so the conversation the gate itself says you can
   "still read below" had no room to show. Capping the gate and letting it
   scroll inside that cap keeps the thread readable in every gate state
   (enforced or invite), on every screen. */
.pushgate{position:relative;margin:0 auto 8px;padding:0 10px;max-width:720px;width:100%;
  flex:0 1 auto;min-height:0;max-height:42vh;overflow-y:auto;-webkit-overflow-scrolling:touch}
.pushgate-card{background:#fff;border:1px solid #E4E6EB;border-radius:16px;
  box-shadow:0 8px 28px rgba(0,0,0,.10);padding:18px 18px 16px;text-align:center}
.pushgate-ico{display:inline-flex;align-items:center;justify-content:center;
  width:46px;height:46px;border-radius:50%;background:#EFEAFE;color:#5B21B6;margin-bottom:8px}
.pushgate-title{font-size:16px;margin:0 0 6px;line-height:1.3}
.pushgate-msg{font-size:13.5px;color:#65676B;margin:0 0 10px;line-height:1.45}
.pushgate-hint{font-size:12.5px;color:#65676B;background:#F7F8FA;border:1px solid #E4E6EB;
  border-radius:10px;padding:9px 11px;margin:0 0 10px;text-align:left;line-height:1.5}
.pushgate-btn{display:block;width:100%;padding:13px 16px;border:0;border-radius:12px;
  background:#7C3AED;color:#fff;font-size:15px;font-weight:600;cursor:pointer}
.pushgate-btn:hover{background:#6D28D9}
.pushgate-btn:disabled{opacity:.6;cursor:default}
.pushgate-busy{font-size:12.5px;color:#65676B;margin:8px 0 0}
/* Invitation mode (push-gate.js adds .pushgate-invite): the same card gains
   two quieter choices under Confirm. Skip is the ordinary secondary action;
   No Thanks is deliberately the quietest thing on the card. The fine print
   names Settings as the permanent off-switch, because neither button is one. */
.pushgate-alt{margin-top:8px}
.pushgate-skip{display:block;width:100%;padding:11px 16px;border:1px solid var(--line);
  border-radius:12px;background:var(--card);color:var(--ink);font-size:14px;font-weight:600;cursor:pointer}
.pushgate-skip:hover{background:var(--wash)}
.pushgate-no{display:block;width:100%;margin-top:2px;padding:9px 16px;border:0;background:none;
  color:var(--muted);font-size:13px;cursor:pointer;text-decoration:underline;text-underline-offset:2px}
.pushgate-no:hover{color:var(--ink)}
.pushgate-fine{font-size:11.5px;color:var(--faint);margin:6px 0 0}
/* The invitation must never crowd the conversation out: it shares the chat
   column with a thread that can flex down to nothing, and the full enforced
   card (icon, long copy, stacked buttons) is tall enough to do exactly that
   on a phone. In invite mode the same card compacts — icon gone, tighter
   type, Skip and No Thanks side by side — so the thread above it keeps most
   of the screen and the conversation stays readable while the card is up. */
.pushgate-invite .pushgate-ico{display:none}
.pushgate-invite .pushgate-card{padding:13px 14px 11px}
.pushgate-invite .pushgate-title{font-size:14.5px;margin-bottom:4px}
.pushgate-invite .pushgate-msg{font-size:12.5px;margin-bottom:9px}
.pushgate-invite .pushgate-btn{padding:10px 16px}
.pushgate-invite .pushgate-alt{display:grid;grid-template-columns:1fr auto;gap:2px 10px;
  align-items:center;margin-top:6px}
.pushgate-invite .pushgate-skip{padding:9px 14px;font-size:13.5px}
.pushgate-invite .pushgate-no{margin-top:0;padding:9px 4px;white-space:nowrap}
.pushgate-invite .pushgate-fine{grid-column:1 / -1;margin-top:2px}

/* ---- shared RGB highlight on a message ----------------------------------
   The ring an admin puts around one message with "Copy This" in the console;
   the poll carries the room's pointer here so every participant sees the same
   one. Drawn the same way as the console's — a conic-gradient filling a pseudo
   the bubble's own size (inset:0), masked so only a 3px band at the edge shows
   and the middle is punched out for the text to read through. This is a border,
   not a glow hanging outside: an outside ring is clipped by the thread's
   scroller and, once the bubble is its own stacking context, would paint over
   the bubble's background behind the text rather than around it. The registered
   angle lets the colours travel; without @property, or with reduced motion, the
   same ring stands still. */
@property --ahl-a{syntax:'<angle>';inherits:false;initial-value:0deg}
.bubble.bubble-highlight{position:relative}
.bubble.bubble-highlight::before{content:"";position:absolute;inset:0;
  border-radius:inherit;padding:3px;pointer-events:none;
  background:conic-gradient(from var(--ahl-a),
    #ff0040,#ff8000,#ffe000,#33dd44,#00d5ff,#2255ff,#a020f0,#ff0040);
  -webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);
          mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);
  -webkit-mask-composite:xor;mask-composite:exclude;
  animation:ahlRgb 5s linear infinite}
@keyframes ahlRgb{to{--ahl-a:360deg}}
@media (prefers-reduced-motion:reduce){
  .bubble.bubble-highlight::before{animation:none}
}

/* Composer while the gate is unresolved. */
.composer-locked{opacity:.45;pointer-events:none;filter:grayscale(1)}

@media (max-width:640px){
  .pushgate-card{padding:15px 14px 13px;border-radius:14px}
  .pushgate-title{font-size:15px}
}
