:root {
  --blue: #3b7dd8;
  --blue-dark: #2c6bc0;
  --blue-light: #e8f0fe;
  --bg: #f5f6f8;
  --panel: #ffffff;
  --sidebar-bg: #e9ecef;
  --sidebar-text: #333;
  --sidebar-border: #d0d5dd;
  --text: #1e1e1e;
  --muted: #6c757d;
  --border: #dee2e6;
  --sent: #3b7dd8;
  --received: #ffffff;
  --danger: #dc3545;
  --green: #28a745;
  --accent: #ffcc00;
  --accent-dark: #d4aa00;
  --accent-soft: #fff6d6;
  --accent-ink: #8a6a00;
}

/* ===== Theme layout base (the new Theseus3k inherits this flat foundation) ===== */

* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
  font-family: "Inter", sans-serif;
  background: var(--bg);
  color: var(--text);
  overflow: hidden;
}
img { -webkit-user-drag: none; }

/* ===== App layout ===== */
/* 100dvh (with 100vh fallback) keeps the shell inside the visible viewport on
   mobile even when the browser URL bar is shown; otherwise the bottom (composer)
   sits below the fold of an overflow:hidden page and can never be scrolled to. */
.app { display: flex; height: 100vh; height: 100dvh; }
.sidebar {
  width: 320px;
  flex: 0 0 auto;
  position: relative;
  background: var(--sidebar-bg);
  color: var(--sidebar-text);
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--sidebar-border);
}
/* Invisible full-height drag strip on the chat list's right edge (resizable
   sidebar, desktop only). It sits just left of the 6px scrollbar column so the
   list keeps its scroll thumb; dragging right widens the list. */
.sidebar-resizer { position: absolute; right: 6px; top: 0; bottom: 0; width: 5px; cursor: col-resize; user-select: none; touch-action: none; }
.sidebar-resizer.hidden { display: none; }
.chat-area { flex: 1; display: flex; flex-direction: column; position: relative; min-width: 0; }

.sidebar-head {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 12px;
  min-height: 54px;
  border-bottom: 1px solid var(--sidebar-border);
  background: #dde1e7;
}
.sidebar-head .brand { overflow: hidden; flex: 1; min-width: 0; display: flex; align-items: center; }
.sidebar-head .brand img { /*height: 32px;*/ max-width: 100%; object-fit: contain; object-position: left center; }

.icon-btn {
  background: transparent; border: none; cursor: pointer; padding: 6px; border-radius: 4px; display: inline-flex; align-items: center; justify-content: center;
}
.sidebar-head .help-btn img { width: 18px; height: 18px; }
}
.icon-btn img { width: 16px; height: 16px; opacity: .7; }
.icon-btn:hover { background: rgba(0,0,0,.08); }
.icon-btn:hover img { opacity: 1; }

.menu-btn { display: none; }

.new-chat { padding: 10px 12px; }
.new-chat-btn {
  width: 100%; padding: 8px; border: 1px solid var(--border); border-radius: 4px;
  background: #fff; color: var(--text); font-size: 13px; cursor: pointer; font-weight: 600;
}
.new-chat-btn:hover { background: var(--blue-light); border-color: var(--blue); }

.chat-list { flex: 1; overflow-y: auto; }
.chat-list-empty { padding: 20px; text-align: center; color: var(--muted); font-size: 13px; }
.chat-item {
  display: flex; align-items: center; gap: 10px; padding: 10px 12px 10px 20px; cursor: pointer;
  border-bottom: 1px solid #dfe3ea; position: relative; transition: background .1s;
}
.chat-item:hover { background: #dfe3ea; }
.chat-item.active { background: var(--blue-light); }
/* Comic-style bubble tail on the active chat item: the wide end sits flush at
   the item's right edge (next to the message pane) and the apex points back at
   the "speaker" (the chat item), like a speech bubble's tail aimed at the mouth.
   Kept entirely inside the item so the scrollable sidebar never clips it. */
.chat-item.active::after {
  content: ""; position: absolute; right: 0; top: 50%;
  transform: translateY(-50%); border: 8px solid transparent;
  border-left-width: 0; border-right-color: var(--bg);
}
.chat-item .ci-icon { width: 36px; height: 36px; border-radius: 50%; background: #cfd4dc; display: flex; align-items: center; justify-content: center; flex-shrink: 0; position: relative; }
.chat-item .ci-icon img { width: 18px; height: 18px; }
.chat-item .ci-icon img.ci-avatar { width: 100%; height: 100%; }
.chat-item .ci-icon img.ci-lock { position: absolute; right: -2px; bottom: -2px; width: 13px; height: 13px; background: #fff; border-radius: 50%; padding: 1px; box-shadow: 0 0 0 1px rgba(0,0,0,.15); }
/* "In call" overlay badge on a chat item's icon (top-right of the avatar disc). */
.chat-item .ci-icon img.ci-call { position: absolute; right: -2px; top: -2px; width: 13px; height: 13px; background: var(--green); border-radius: 50%; padding: 1px; box-shadow: 0 0 0 1px rgba(0,0,0,.15); }
/* "@" overlay badge shown on a channel when it receives an @-mention. */
.chat-item .ci-icon img.ci-at { position: absolute; left: -2px; bottom: -2px; width: 13px; height: 13px; background: var(--blue); border-radius: 50%; padding: 1px; box-shadow: 0 0 0 1px rgba(0,0,0,.15); }
/* Pushpin badge for pinned chats (positioned upper-right of the avatar disc). */
.chat-item .ci-icon img.ci-pin { position: absolute; right: -2px; top: -2px; width: 13px; height: 13px; background: #ffb020; border-radius: 50%; padding: 1px; box-shadow: 0 0 0 1px rgba(0,0,0,.15); }
/* Archive badge for chats a teacher moved into the Archive (top-left); the
   row itself is dimmed — the chat is globally read-only. */
.chat-item .ci-icon img.ci-arch { position: absolute; left: -2px; top: -2px; width: 13px; height: 13px; background: #fff; border-radius: 50%; padding: 1px; box-shadow: 0 0 0 1px rgba(0,0,0,.15); }
.chat-item.chat-item-archived .ci-name, .chat-item.chat-item-archived .ci-last { opacity: .6; }
.chat-item .ci-name { font-weight: 600; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-item .ci-meta { margin-left: auto; font-size: 11px; color: var(--muted); flex-shrink: 0; display: flex; align-items: center; gap: 5px; }
/* Red unread badge (new-message count) shown before the time label. */
.chat-item .ci-badge {
  min-width: 16px; height: 16px; padding: 0 5px; border-radius: 9px;
  background: #e5484d; color: #fff; font-size: 10px; font-weight: 700;
  line-height: 16px; text-align: center; display: inline-block; flex-shrink: 0;
}
.chat-item.active .ci-badge { background: #fff; color: #e5484d; }
/* Three-dot button for the touch-friendly full-screen context menu: visible
   on hover for pointer devices and always on touch (no-hover) devices. */
.chat-item .ci-dots {
  display: none; flex-direction: column; gap: 2px; padding: 5px 3px;
  background: transparent; border: none; cursor: pointer; flex-shrink: 0;
}
.chat-item:hover .ci-dots { display: flex; }
.chat-item .ci-dots span { width: 3px; height: 3px; border-radius: 50%; background: var(--muted); display: block; }
@media (hover: none) {
  .chat-item .ci-dots { display: flex; }
}
/* Full-screen touch context menu: header (icon + name + close) and a scrollable
   list of actions, primarily for tablets/phones where right-click doesn't exist. */
.touch-menu { position: fixed; inset: 0; z-index: 1400; background: var(--bg); display: flex; flex-direction: column; }
.tm-head { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--border); background: var(--panel); }
.tm-ic { width: 28px; height: 28px; }
.tm-title { flex: 1; font-size: 15px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tm-close { background: transparent; border: none; padding: 6px; cursor: pointer; }
.tm-close img { width: 16px; height: 16px; display: block; }
.tm-body { flex: 1; overflow-y: auto; padding: 10px; }
.tm-item { display: flex; align-items: center; gap: 12px; width: 100%; text-align: left; padding: 14px 16px; margin-bottom: 6px; background: var(--panel); border: 1px solid var(--border); border-radius: 10px; font: inherit; font-size: 14px; color: var(--text); cursor: pointer; }
.tm-item:active { background: var(--blue-light); }
.tm-item .cm-caret { margin-left: auto; }
.tm-sep { height: 1px; margin: 6px 0; }
/* Forward picker (searchable Chat + user list). Full-screen sheet on touch
   devices; on desktop it shrinks into a centered modal (see the media query
   after .fwd-empty). */
.fwd-dlg {
  position: fixed; inset: 0; z-index: 1500; display: flex; flex-direction: column;
  background: var(--bg);
}
.fwd-box { flex: 1; min-height: 0; display: flex; flex-direction: column; background: var(--bg); }
.fwd-head { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--border); background: var(--panel); }
.fwd-title { flex: 1; font-size: 15px; font-weight: 600; }
.fwd-searchwrap { padding: 10px 14px; border-bottom: 1px solid var(--border); background: var(--bg); }
.fwd-search { width: 100%; padding: 9px 12px; border: 1px solid var(--border); border-radius: 8px; font-size: 14px; background: var(--panel); color: var(--text); box-sizing: border-box; }
.fwd-list { flex: 1; overflow-y: auto; padding: 10px; }
.fwd-item {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  padding: 12px 14px; margin-bottom: 6px; background: var(--panel);
  border: 1px solid var(--border); border-radius: 10px; font: inherit;
  font-size: 14px; color: var(--text); cursor: pointer;
}
 .fwd-item.fwd-saved { font-size: 22px; padding: 18px 20px; gap: 16px; }
 .fwd-item:active { background: var(--blue-light); }
 .fwd-item .fwd-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
 .fwd-empty { padding: 24px; text-align: center; color: var(--muted); font-size: 13px; }
 /* 'Send whiteboard' picker: same shell as the forward picker (header, search
    line, decorated rows) plus a trailing send affordance per row. */
 .wb-send-dlg { position: fixed; inset: 0; z-index: 1500; display: flex; flex-direction: column; background: var(--bg); }
 .wb-send-dlg.hidden { display: none; }
 .wb-send-box { flex: 1; min-height: 0; display: flex; flex-direction: column; background: var(--bg); }
 .wb-send-row { cursor: pointer; }
 .wb-send-row:hover { background: var(--blue-light); }
 .wb-send-go { width: 16px; height: 16px; margin-left: auto; flex: 0 0 auto; opacity: .5; }
 .wb-send-row:hover .wb-send-go { opacity: 1; }
 @media (min-width: 821px) {
   .wb-send-dlg.wb-send-dlg { align-items: center; justify-content: center; background: rgba(18,24,40,.45); }
   .wb-send-box { flex: 0 0 auto; width: min(420px, calc(100vw - 40px)); height: min(520px, calc(100vh - 88px)); border-radius: 12px; overflow: hidden; border: 1px solid var(--border); box-shadow: 0 18px 60px rgba(0,0,0,.32); }
 }
 .info-dlg { position: fixed; inset: 0; z-index: 1500; display: flex; align-items: center; justify-content: center; background: rgba(18,24,40,.45); }
 .info-box { flex: 0 0 auto; width: min(400px, calc(100vw - 40px)); background: var(--panel); border: 1px solid var(--border); border-radius: 12px; overflow: hidden; box-shadow: 0 18px 60px rgba(0,0,0,.32); }
 .info-head { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--border); background: var(--panel); }
 .info-title { flex: 1; font-size: 15px; font-weight: 600; }
 .info-body { padding: 16px; font-family: monospace; font-size: 13px; white-space: pre-wrap; color: var(--text); max-height: 60vh; overflow-y: auto; }
 /* Desktop: the picker becomes a centered modal. Both selectors carry equal
   specificity to the theme's attribute-scoped rules and this file loads last,
   so this override wins over the themable translucent fill. */
@media (min-width: 821px) {
  .fwd-dlg.fwd-dlg { align-items: center; justify-content: center; background: rgba(18,24,40,.45); }
  .fwd-box { flex: 0 0 auto; width: min(420px, calc(100vw - 40px)); height: min(520px, calc(100vh - 88px)); border-radius: 12px; overflow: hidden; border: 1px solid var(--border); box-shadow: 0 18px 60px rgba(0,0,0,.32); }
}
.chat-item .ci-body { flex: 1; min-width: 0; }
.chat-item .ci-last { font-size: 12px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.sidebar-foot { border-top: 1px solid var(--sidebar-border); padding: 8px 12px; background: #dde1e7; min-height: 52px; box-sizing: border-box; display: flex; align-items: center; }
.sidebar-foot .me { display: flex; align-items: center; gap: 8px; flex: 1; max-width: 100%; min-width: 0; }
.me-avatar {
  width: 34px; height: 34px; border-radius: 50%; background: var(--blue);
  color: #fff; display: flex; align-items: center; justify-content: center; font-weight: 700; flex-shrink: 0;
}
.me-info { flex: 1; min-width: 0; }
.me-name { font-size: 13px; font-weight: 600; color: #333; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.me-role { font-size: 11px; color: var(--muted); }

/* Role icons preceding user names */
.role-icon { width: 16px; height: 16px; vertical-align: middle; margin-right: 4px; }
.role-icon-lg { width: 34px; height: 34px; border-radius: 50%; flex-shrink: 0; }
.msg-meta .role-icon { margin-right: 3px; }
.member-row { display: flex; align-items: center; gap: 6px; padding: 6px 8px; border-radius: 4px; }
.member-row .role-icon { margin: 0; }
.member-row .mr-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.member-row .mr-role { font-size: 11px; color: var(--muted, #7a8194); text-transform: uppercase; }
.member-row .rm-member { margin-left: auto; flex-shrink: 0; }

/* Add-members dialog (member management) */
.member-add { margin-top: 14px; }
.member-add #memSearch { width: 100%; box-sizing: border-box; }
.ma-results { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; max-height: 200px; overflow-y: auto; }
.ma-result { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; padding: 8px 10px; border: 1px solid var(--border); background: #fff; border-radius: 4px; cursor: pointer; font-size: 13px; font-family: inherit; }
.ma-result:hover { background: var(--blue-light); border-color: var(--blue); }
.ma-avatar { width: 20px; height: 20px; border-radius: 50%; flex-shrink: 0; }
.ma-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ma-add { font-size: 12px; font-weight: 600; color: var(--blue); }

/* ===== No chat ===== */
.no-chat { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: var(--muted); gap: 16px; }
.nochat-logo { height: 80px; opacity: .5; }

/* ===== Chat view ===== */
/* min-height:0 lets the view shrink with the window even when its content has
   a large intrinsic size (e.g. the whiteboard canvas is sized from its width/
   height attributes, which would otherwise pin .chat's min-height:auto and
   keep #boardContainer from collapsing). */
.chat { flex: 1; display: flex; flex-direction: column; height: 100%; min-height: 0; min-width: 0; background: var(--bg); }
.chat-header {
  background: var(--panel); border-bottom: 1px solid var(--border); padding: 10px 16px;
  display: flex; align-items: center; gap: 10px; min-height: 54px;
}
.chat-titleblock { flex: 1; display: flex; flex-direction: column; justify-content: flex-start; min-width: 0; }
.chat-titleblock.has-icon { flex-direction: row; align-items: center; gap: 10px; }
.chat-header-icon { border-radius: 50%; background: #cfd4dc; display: flex; align-items: center; justify-content: center; width: 30px; height: 30px; flex-shrink: 0; }
.chat-header-icon img { width: 18px; height: 18px; }
.chat-titles { display: flex; flex-direction: column; min-width: 0; }
.chat-title { font-weight: 700; font-size: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-meta { font-size: 12px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Presence */
.presence-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: #c0c4cc; margin-right: 4px; }
.presence-dot.on { background: var(--green); }
.presence-online { color: var(--green); }
.presence-public { color: var(--muted); }
.presence-off { color: var(--muted); }

/* Chat folders */
.folders-bar { display: flex; align-items: center; gap: 2px; padding: 2px 6px; border-bottom: 1px solid var(--border); }
.chat-folder { margin: 0 0 2px; border-bottom: 1px solid #cdd8ea; }
.chat-folder .cf-body { min-height: 4px; border-radius: 4px; }
.dropzone.drop-hover { outline: 2px dashed var(--blue); outline-offset: -2px; background: rgba(47,111,237,.06); }
.chat-item[draggable="true"]:active { cursor: grab; }
.cf-head { display: flex; align-items: center; gap: 6px; padding: 5px 8px; cursor: pointer; border-radius: 4px; }
.cf-head:hover { background: #dfe3ea; }
.cf-caret { font-size: 10px; width: 10px; color: var(--muted); transition: transform .15s; transform: rotate(90deg); }
.chat-folder.collapsed .cf-caret { transform: rotate(0deg); }
.cf-icon { width: 16px; height: 16px; }
.cf-name { flex: 1; font-weight: 600; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cf-count { font-size: 11px; color: var(--muted); background: #eef0f3; border-radius: 8px; padding: 0 6px; }
.chat-folder.collapsed .cf-body { display: none; }

/* Context menu */
.ctx-menu { position: fixed; z-index: 1200; background: #fff; border: 1px solid var(--border); border-radius: 6px; box-shadow: 0 6px 20px rgba(0,0,0,.14); padding: 4px; min-width: 180px; }
.cm-item { display: flex; align-items: center; gap: 8px; width: 100%; background: none; border: 0; text-align: left; padding: 7px 9px; border-radius: 4px; cursor: pointer; font-size: 13px; color: var(--text); }
.cm-item:hover { background: #dfe3ea; }
.cm-item:focus-visible { outline: 2px solid var(--blue, #2563eb); outline-offset: -2px; }
.cm-item:focus { background: #e3e8f0; }
.cm-item:disabled { opacity: .45; cursor: default; }
.cm-item:disabled .cm-ic { opacity: .7; }
/* Sidebar footer three-dot menu button. */
.foot-dots { flex-direction: column; gap: 3px; padding: 8px 10px; }
.foot-dots span { width: 4px; height: 4px; border-radius: 50%; background: var(--muted); display: block; }
.cm-item .cm-ic { width: 16px; height: 16px; }
.cm-item .cm-caret { margin-left: auto; font-size: 11px; color: #999; }
.cm-item .cm-visbox { flex: none; display: inline-block; width: 14px; height: 14px; border: 1px solid var(--border); border-radius: 3px; text-align: center; line-height: 14px; vertical-align: middle; }
.cm-item .cm-vismark { width: 12px; height: 12px; vertical-align: middle; }
.cm-item .cm-name { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ctx-menu.cm-sub { position: absolute; }
.cm-item:hover .cm-caret { color: #fff; }
.cm-sep { height: 1px; background: var(--border); margin: 4px 6px; }
.chat-actions { margin-left: auto; display: flex; gap: 4px; align-items: center; }
/* Unified call action in the header: real green button with icon + wording.
   While a call runs the wording reads "Join call"; otherwise it reads "Call"
   (start). On small screens only the icon survives so the button stays compact. */
.chat-actions .join-call-btn {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  background: linear-gradient(180deg, #7cbf5a, #4d9b37); color: #fff;
  border: 1px solid #3f8a2f; border-radius: 7px; padding: 0 12px; height: 36px;
  font-size: 13px; font-weight: 700; line-height: 1; white-space: nowrap;
  box-shadow: 0 1px 2px rgba(0,0,0,.15); font-family: inherit;
}
.chat-actions .join-call-btn:hover { filter: brightness(1.06); }
.chat-actions .join-call-btn img { width: 16px; height: 16px; }
[data-theme="theseus3k"] .chat-actions .join-call-btn img { filter: brightness(0) invert(1); }
@media (max-width: 820px) {
  .chat-actions .join-call-btn { width: 44px; padding: 0; justify-content: center; }
  .chat-actions .join-call-btn .jcb-txt { display: none; }
}
/* Three-dot "more" button in the chat header (mobile): same dot pattern as the
   chat-item overflow dots, sized for the 54px header. */
 .icon-btn.more-btn { display: inline-flex; flex-direction: column; gap: 2px; padding: 10px; min-width: 44px; min-height: 44px; justify-content: center; }
 .icon-btn.more-btn span { width: 4px; height: 4px; border-radius: 50%; background: var(--muted); display: block; }

.message-list { flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 6px; }
.message-list.drag-over { outline: 2px dashed #5a8dee; outline-offset: -4px; background-color: #eaf1fe; }

/* Pinned messages strip: chat-scoped bar above the message list. Each chip
   carries a pin icon, author and a snippet; clicking jumps to the message. */
.pinned-bar {
   display: flex; align-items: center; gap: 6px; padding: 5px 12px; flex-shrink: 0;
   background: var(--panel); border-bottom: 1px solid var(--border);
   overflow-x: auto; min-height: 34px;
 }
 .pinned-bar.hidden { display: none; }
 .pin-item {
   display: flex; align-items: center; gap: 6px; cursor: pointer;
   background: #fff; border: 1px solid var(--border); border-radius: 8px;
   padding: 6px 12px; max-width: 360px; min-width: 220px; flex-shrink: 0; transition: background .1s;
 }
 .pin-item:hover { background: #eef1f5; }
 .pin-avatar { width: 18px; height: 18px; border-radius: 50%; flex-shrink: 0; object-fit: cover; }
 .pin-avatar.pin-role-icon { border-radius: 3px; object-fit: contain; }
 .pin-sender { font-size: 13px; font-weight: 600; white-space: nowrap; flex-shrink: 0; }
 .pin-body { font-size: 13px; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
 .pin-unpin-btn {
   border: none; background: transparent; cursor: pointer; padding: 0; flex-shrink: 0;
   width: 16px; height: 16px; display: flex; align-items: center; justify-content: center;
   opacity: .55; transition: opacity .1s;
 }
 .pin-unpin-btn:hover { opacity: 1; }
 .pin-unpin-btn img { width: 14px; height: 14px; }
.msg { max-width: 75%; display: flex; flex-direction: column; }
.msg.own { align-self: flex-end; align-items: flex-end; }
.msg.other { align-self: flex-start; align-items: flex-start; }
.msg-bubble { padding: 8px 12px; border-radius: 6px; font-size: 14px; line-height: 1.45; word-break: break-word; white-space: pre-wrap; }
.msg.own .msg-bubble { background: var(--sent); color: #fff; border-bottom-right-radius: 2px; }
.msg.other .msg-bubble { background: var(--received); color: var(--text); border-bottom-left-radius: 2px; border: 1px solid var(--border); }
/* Links inside your own message bubbles stay white in every theme. */
.msg.own .msg-bubble a, .msg-own a { color: #fff; }
.msg-meta { font-size: 11px; color: var(--muted); padding: 2px 4px; }
.msg-meta.msg-avatar-row { padding: 0; }
.msg.own .msg-meta { text-align: right; }
/* Time sits beside the bubble (left for own, right for others), shown on hover. */
.msg-row { display: flex; align-items: center; position: relative; }
.msg-time {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  font-size: 11px;
  color: var(--muted);
  white-space: nowrap;
  opacity: 0;
  transition: opacity .12s;
  pointer-events: none;
}
.msg.own .msg-time { right: calc(100% + 6px); }
.msg.other .msg-time { left: calc(100% + 6px); }
.msg:hover .msg-time { opacity: 1; }
/* "Discussion started" separator shown once at the top of the chat. */
.msg-sep { align-self: center; margin: 12px 0 4px; }
.msg-sep span { display: inline-block; background: #eef1f5; color: var(--muted); font-size: 11px; border-radius: 10px; padding: 2px 10px; border: 1px solid var(--border); }

.msg.system { align-self: center; max-width: 90%; }
.msg.system .msg-bubble { background: #f0f0f0; color: var(--muted); font-size: 12px; border-radius: 4px; padding: 4px 12px; border: 1px solid var(--border); }
.msg.system.k-call .msg-bubble { text-align: center; }
.msg.system.k-call .call-sum-head { font-weight: 700; cursor: pointer; user-select: none; }
.msg.system.k-call .call-sum-head .mcs-arr { display: inline-block; margin-left: 3px; font-size: 11px; }
.msg.system.k-call .call-group { display: none; margin-top: 5px; padding-top: 4px; border-top: 1px solid rgba(0,0,0,.1); }
.msg.system.k-call .call-group .call-group-item { padding: 2px 0 0; line-height: 1.5; }
.msg.system.k-call.open .call-group { display: block; }
.msg-hidden { display: none !important; }
/* Reply quote inside a bubble */
.reply-quote { display: block; border-left: 3px solid #b7c4d6; padding: 2px 6px; margin-bottom: 4px; background: rgba(255,255,255,.5); border-radius: 3px; font-size: 12px; color: #555; }
.msg.own .reply-quote { background: rgba(255,255,255,.18); border-left-color: #fff; color: #eef; }
.rq-name { font-weight: 700; }
/* "Forwarded from <chat>" label at the top of a message bubble. */
.msg-forward { display: flex; align-items: center; gap: 5px; font-size: 11px; color: var(--muted); margin-bottom: 4px; }
.msg-forward img { width: 12px; height: 12px; opacity: .6; }
.msg.own .msg-forward { color: #dfe8f5; }
.msg.own .msg-forward img { opacity: .8; }
.rq-body { opacity: .85; word-break: break-word; }

/* Deleted (undone) message sentinel */
.msg-bubble.deleted-bubble { background: #f0f0f0; color: var(--muted); font-style: italic; font-size: 13px; border: 1px solid var(--border); cursor: default; }
.msg.own .msg-bubble.deleted-bubble { background: #e4e6ea; color: #777; }
.del-text { display: block; word-break: break-word; }
.mod-deleted-actions { display: inline-flex; gap: 6px; margin-top: 6px; vertical-align: middle; }

/* Edit-in-place */
.edit-wrap { display: flex; flex-direction: column; gap: 6px; width: 100%; }
.msg-bubble.editing { position: relative; }
.msg-bubble.editing .edit-wrap { width: 100%; }
.edit-input { width: 100%; min-width: 0; box-sizing: border-box; border: 1px solid var(--border); border-radius: 4px; padding: 6px 8px; font-size: 13px; font-family: inherit; resize: vertical; overflow-y: auto; background: #fff; color: var(--text); }
.edit-actions { display: flex; justify-content: flex-end; gap: 6px; }

/* Emoticon-only message image */
.emoji-msg { width: 32px; height: 32px; }
/* Emoticon shown inline inside a longer text message */
.emoji-inline { width: 17px; height: 17px; vertical-align: -3px; margin: 0 1px; }

/* Inline / small buttons used in message controls */
.btn-xs { padding: 2px 8px; font-size: 11px; border-radius: 3px; }

/* Reply bar above composer */
.reply-bar { display: flex; align-items: center; gap: 8px; padding: 6px 16px; background: var(--blue-light); border-top: 1px solid var(--border); font-size: 12px; }
.reply-bar .rb-tag { font-weight: 700; color: var(--blue); white-space: nowrap; }
.reply-bar .rb-snippet { flex: 1; color: #555; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-style: italic; }
.reply-bar .rb-cancel { width: auto; padding: 2px 4px; }
.reply-bar .rb-cancel img { width: 14px; height: 14px; }

/* Upload progress panel above the composer. */
.upload-progress {
  display: flex; align-items: center; gap: 8px; padding: 8px 16px;
  background: var(--panel); border-top: 1px solid var(--border); font-size: 12px;
}
.upload-progress .up-ic { width: 16px; height: 16px; flex-shrink: 0; }
.upload-progress .up-name { flex: 0 1 auto; min-width: 60px; max-width: 40%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 600; color: var(--text); }
.upload-progress .up-pct { flex-shrink: 0; color: var(--muted); }
.upload-progress .up-track { flex: 1; height: 6px; border-radius: 3px; background: #e3e7ee; overflow: hidden; }
.upload-progress .up-fill { height: 100%; width: 0%; background: linear-gradient(#5b9ce4, #2f6fc8); border-radius: 3px; transition: width .15s linear; }
.upload-progress .up-cancel { flex: 0 0 auto; padding: 2px; opacity: .65; }
.upload-progress .up-cancel:hover { opacity: 1; }
.upload-progress .up-cancel img { width: 12px; height: 12px; }

/* Link preview */
.link-preview { margin-top: 6px; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; background: #f8f9fa; }
.link-preview img { width: 100%; max-height: 120px; object-fit: cover; display: block; }
.link-preview .lp-body { padding: 6px 8px; font-size: 12px; }
.link-preview .lp-title { font-weight: 700; color: var(--text); }
.link-preview.wb-preview { display: flex; align-items: center; gap: 8px; padding: 8px 10px; }
.link-preview.wb-preview .lp-ic { width: 32px; height: 32px; flex-shrink: 0; display: block; }
.link-preview.wb-preview .lp-desc { color: var(--muted); }
/* Tombstone for a whiteboard link whose board was deleted. */
.link-preview.wb-preview.deleted { border-style: dashed; opacity: .8; background: #f2f4f8; cursor: default; }
.link-preview.wb-preview.deleted .lp-title { color: var(--muted); font-weight: 600; }
.link-preview .lp-del-btn { font-size: 11px; padding: 2px 9px; border: 1px solid #c4d1e6; border-radius: 4px; background: #fff; color: #c62828; cursor: pointer; margin-top: 2px; }
.link-preview .lp-del-btn:hover { background: #ffe9e9; border-color: #e2a5a5; }

/* ---- Chat media attachments (inline image / file card) ---- */
.chat-media-img { display: block; }
.chat-media-img img { display: block; max-width: 320px; max-height: 240px; border-radius: 6px; object-fit: cover; }
.msg-bubble.chat-media-file-bubble { padding: 6px 8px; }
.chat-media-file { display: flex; align-items: center; gap: 10px; color: inherit; text-decoration: none; min-width: 180px; max-width: 280px; }
.chat-media-file:hover { color: var(--blue); }
.chat-media-file .cf-ic { width: 32px; height: 32px; flex-shrink: 0; }
.chat-media-file .cf-info { display: flex; flex-direction: column; min-width: 0; }
.chat-media-file .cf-name { font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-media-file .cf-size { font-size: 11px; color: var(--muted); }
.msg.own .chat-media-file .cf-size { color: rgba(255,255,255,.85); }

.typing-indicator { padding: 0 16px 4px; font-size: 12px; background-color: #f2f6fb; color: var(--muted); /*min-height: 18px;*/ }

.composer {
  background: var(--panel); border-top: 1px solid var(--border); display: flex; align-items: center; gap: 8px;
  padding: 8px 12px; position: relative; min-height: 52px; box-sizing: border-box;
}
.composer textarea {
  flex: 1; border: 1px solid var(--border); border-radius: 4px; padding: 8px 12px; font-size: 14px;
  resize: none; outline: none; max-height: 120px; background: #fff; font-family: inherit;
  min-height: 32px; box-sizing: border-box;
}
.composer textarea:focus { border-color: var(--blue); }
.composer #emojiBtn { width: 32px; height: 32px; box-sizing: border-box; flex-shrink: 0; }

/* Read-only notice shown in place of the composer for chats moved into the
   teacher's Archive: the composer is hidden end-to-end and the user is left
   with an explanation plus a one-click restore (visible to teachers only). */
.archived-bar {
  display: flex; align-items: center; gap: 10px; padding: 10px 14px; min-height: 52px; box-sizing: border-box;
  background: var(--panel); border-top: 1px solid var(--border); font-size: 13px; color: var(--muted);
}
.archived-bar.hidden { display: none; }
.archived-bar .ab-ic { width: 16px; height: 16px; flex: 0 0 auto; opacity: .75; }
.archived-bar .ab-txt { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.archived-bar .ab-unarchive {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; padding: 5px 10px; cursor: pointer;
  border: 1px solid var(--border); border-radius: 4px; background: var(--blue); color: #fff; font-size: 12px;
}
.archived-bar .ab-unarchive:hover { background: var(--blue-dark); }
.archived-bar .ab-unarchive-ic { width: 14px; height: 14px; filter: brightness(0) invert(1); }
.archived-bar .ab-unarchive:disabled { opacity: .5; cursor: default; }
/* Settings block list: avatar + name rows with an Unblock button each. */
.blocked-list { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.blocked-row { display: flex; align-items: center; gap: 12px; padding: 8px 12px; background: var(--panel); border: 1px solid var(--border); border-radius: 10px; }
.blocked-avatar { width: 32px; height: 32px; flex: 0 0 auto; }
.blocked-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; font-weight: 600; }
/* P2P invitation bar (replaces the composer while an invite is pending or was
   declined) and the invite dialog / service-bubble extras. Theme vars only. */
.invite-bar { background: var(--blue-light); border-top: 1px solid var(--blue); color: var(--text); }
.invite-bar-btns { display: inline-flex; gap: 8px; flex: 0 0 auto; }
.invite-target { display: flex; align-items: center; gap: 12px; margin-bottom: 4px; }
.invite-avatar { width: 44px; height: 44px; flex: 0 0 auto; }
.invite-name { font-size: 15px; font-weight: 700; }
.invite-quote { margin-top: 6px; padding: 6px 10px; border-left: 2px solid var(--blue); background: var(--blue-light); border-radius: 0 6px 6px 0; font-style: italic; overflow-wrap: anywhere; }
.invite-actions { display: flex; gap: 8px; margin-top: 8px; }
.modal-card textarea.dlg-input { width: 100%; padding: 9px; border: 1px solid var(--border); border-radius: 4px; font-size: 14px; font-family: inherit; resize: vertical; box-sizing: border-box; }
.dlg-msg { font-size: 13px; color: var(--muted); margin: 0 0 8px; }
.dlg-lbl { display: block; font-size: 12px; font-weight: 600; color: var(--muted); margin: 8px 0 4px; }

.send-btn { width: 32px; height: 32px; border-radius: 4px; background: var(--blue); border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.send-btn img { width: 16px; height: 16px; filter: brightness(0) invert(1); }
.send-btn:hover { background: var(--blue-dark); }

.emoji-panel {
  position: absolute; bottom: 60px; left: 12px; background: var(--panel); border: 1px solid var(--border);
  border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,.12); padding: 8px; width: 245px; height: 210px;
  z-index: 20;
}
.emoji-picker { display: flex; flex-direction: column; gap: 6px; height: 100%; min-height: 0; }
.emoji-search input {
  width: 100%; box-sizing: border-box; border: 1px solid var(--border); border-radius: 4px;
  padding: 4px 8px; font-size: 12px; background: #fff; color: var(--text); outline: none; font-family: inherit;
}
.emoji-search input:focus { border-color: var(--blue); }
.emoji-grid {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; display: grid;
  grid-template-columns: repeat(8, 1fr); gap: 2px; align-content: start;
}
.emoji-grid img { width: 22px; height: 22px; cursor: pointer; padding: 2px; border-radius: 4px; display: block; }
.emoji-panel img:hover { background: #e9ecef; }

/* @mention highlight inside messages */
.mention { color: white; background: rgb(255 255 255 / 28%); border-radius: 3px; padding: 0 2px; font-weight: 600; }

/* @mention member picker (above composer) */
.mention-list { position: absolute; z-index: 1300; background: var(--panel, #fff); border: 1px solid var(--border); border-radius: 6px; box-shadow: 0 6px 20px rgba(0,0,0,.14); padding: 4px; max-height: 200px; overflow-y: auto; }
.mention-item { display: block; width: 100%; text-align: left; padding: 6px 10px; border: none; background: none; cursor: pointer; font-size: 13px; color: var(--text); border-radius: 4px; font-family: inherit; }
.mention-item:hover { background: #e9ecef; }
.mention-item.active { background: var(--blue, #2563eb); color: #fff; }

/* Read-only (locked) field: greyed-out input with a padlock overlaid at its
   end, carrying a tooltip that the value cannot be changed. */
.field-lock { position: relative; display: block; }
.field-lock input {
  width: 100%; padding: 9px; border: 1px solid var(--border, #d0d5dd); border-radius: 4px;
  font-size: 14px; background: #eef0f3; color: #7a7f86; padding-right: 34px; box-sizing: border-box;
}
.field-lock-ic { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); pointer-events: none; }
.field-lock-ic img { width: 14px; height: 14px; opacity: .55; display: block; }

/* 'Send...' attachment menu inside the composer. */
.send-menu {
  position: absolute; bottom: 56px; left: 12px; background: var(--panel, #fff); border: 1px solid var(--border);
  border-radius: 6px; box-shadow: 0 6px 20px rgba(0,0,0,.14); padding: 4px; z-index: 25; min-width: 180px;
}
.send-menu-item {
  position: relative; display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; padding: 8px 10px;
  border: none; background: none; cursor: pointer; font-size: 13px; color: var(--text); border-radius: 4px;
  font-family: inherit;
}
.send-menu-item:hover { background: #e9ecef; }
.send-menu-item img { width: 16px; height: 16px; }
/* The real <input type=file> sits invisibly over the whole item so the user's
   genuine click activates it natively (no synthetic input.click()). */
.send-menu-item .send-file-input {
  position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer;
}

/* Reply-quote jump: clicking a quote highlights the target message briefly. */
.msg-highlight { animation: msgFlash 1s ease; }
@keyframes msgFlash { 0%,100% { background: transparent; } 25%,65% { background: rgba(250,204,21,.18); } }
.msg-highlight .msg-bubble { animation: msgFlashBubble 1s ease; position: relative; }
@keyframes msgFlashBubble {
  0%,100% { box-shadow: 0 0 0 0 rgba(250,204,21,0); }
  25%,65% { box-shadow: 0 0 0 3px rgba(250,204,21,.85), 0 0 14px rgba(250,204,21,.45); }
}
.msg-loading { display: flex; align-items: center; justify-content: center; gap: 8px; text-align: center; color: var(--muted); font-size: 12px; padding: 6px; }
.msg-loading .call-spinner { width: 20px; height: 20px; border-width: 3px; margin: 0; }
/* While a pinned/reply navigation pages older messages in, the list is
   non-interactive: progress cursor + clicks disabled so a user action cannot
   race the incremental load (or re-enter it). */
.message-list.goto-loading { cursor: progress; }
.message-list.goto-loading .msg { pointer-events: none; }

.hidden { display: none !important; }

/* ===== Login overlay ===== */
.login-overlay {
  position: fixed; inset: 0; background: #f0f2f5;
  display: flex; align-items: center; justify-content: center; padding: 20px; z-index: 100;
}
.login-card { background: #fff; border-radius: 8px; padding: 32px; width: 100%; max-width: 360px; border: 1px solid var(--border); box-shadow: 0 2px 8px rgba(0,0,0,.08); }
.login-logo { max-height: 80px; max-width: 100%; height: auto; display: block; margin: 0 auto 12px; }
.login-card h1 { font-size: 17px; text-align: center; color: var(--text); margin: 0 0 16px; line-height: 1.3; }
.auth-form { display: flex; flex-direction: column; gap: 10px; }
.auth-form input { padding: 10px 12px; border: 1px solid var(--border); border-radius: 4px; font-size: 14px; }
.auth-form input:focus { outline: none; border-color: var(--blue); }
.auth-form button { padding: 10px; border: none; border-radius: 4px; background: var(--blue); color: #fff; font-size: 14px; font-weight: 600; cursor: pointer; }
.auth-form button:hover { background: var(--blue-dark); }
.guest-note { font-size: 13px; color: var(--muted); text-align: center; margin: 0 0 8px; }
/* Password field with a show/hide "eye" toggle pinned inside it. Generic so it
   works in login forms, settings, and JS-built dialogs alike. The button must
   defeat the .auth-form button blue fill above. */
.pw-wrap { position: relative; display: block; }
.pw-wrap input[type="password"], .pw-wrap input[type="text"] { width: 100%; padding-right: 38px; }
/* Repeat the rule span-qualified: admin.php's page <style> input rules share
   the plain rule's specificity but load after app.css, so the extra element in
   the selector is what keeps the reserved padding on its modal fields. The
   settings-page wrapper is width-capped below so the eye tracks the field. */
span.pw-wrap input[type="password"], span.pw-wrap input[type="text"] { width: 100%; padding-right: 38px; }
button.pw-eye { position: absolute; top: 50%; right: 2px; transform: translateY(-50%); display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; padding: 0; border: none; border-radius: 4px; background: transparent; box-shadow: none; cursor: pointer; }
button.pw-eye:hover { background: rgba(15,23,42,.06); }
button.pw-eye img { width: 16px; height: 16px; opacity: .55; }
button.pw-eye:hover img, button.pw-eye[aria-pressed="true"] img { opacity: .9; }
.form-msg { font-size: 13px; color: var(--danger); min-height: 16px; }
/* Guest prompt: "or" divider + account-login switch */
.auth-divider { display: flex; align-items: center; gap: 10px; margin: 16px 0 0; color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: .05em; }
.auth-divider::before, .auth-divider::after { content: ''; flex: 1; height: 1px; background: var(--border); }
.auth-switch { display: block; width: 100%; margin-top: 14px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 4px; background: transparent; color: var(--text); font-size: 14px; font-weight: 600; cursor: pointer; }
.auth-switch:hover { background: #f3f5f9; }
.auth-back { display: block; width: 100%; margin-top: 4px; padding: 8px; border: none; background: transparent !important; color: var(--muted) !important; font-size: 13px !important; font-weight: 400 !important; cursor: pointer; }
.auth-back:hover { color: var(--text) !important; text-decoration: underline; }

/* ===== Modal ===== */
/* z-index sits above the full-view image viewer (.imgview, z=900) so modal
   confirms (e.g. "Discard unsaved annotations?") layer over it, but stays
   below transient popups such as context menus / the authors window (z=1200). */
.modal { position: fixed; inset: 0; background: rgba(0,0,0,.35); display: flex; align-items: center; justify-content: center; z-index: 950; padding: 20px; }
.modal-card { background: #fff; border-radius: 8px; padding: 24px; width: 100%; max-width: 420px; max-height: 85vh; overflow-y: auto; border: 1px solid var(--border); box-shadow: 0 4px 16px rgba(0,0,0,.12); }
/* Large variant (dialog({size:'lg'})): the exercise editor's sheet. Same caps
   as KUTEACH — window-relative so the card can never leave the screen, with a
   drawn resize grip because the native handle is easy to miss this big. */
.modal-card.dlg-lg {
  width: min(1000px, calc(100vw - 40px)); min-width: 50%; max-width: calc(100vw - 40px);
  height: 75vh; min-height: 75vh; max-height: calc(100vh - 40px); overflow: hidden;
  display: flex; flex-direction: column; resize: both; position: relative;
}
.modal-card.dlg-lg .dlg-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; }
.modal-card.dlg-lg .dlg-title,
.modal-card.dlg-lg .dlg-actions { flex: 0 0 auto; }
.modal-card.dlg-lg::after {
  content: ''; position: absolute; right: 4px; bottom: 4px; width: 14px; height: 14px;
  background: linear-gradient(135deg, transparent 0 45%, var(--border) 45% 55%, transparent 55% 70%,
              var(--border) 70% 80%, transparent 80%);
  pointer-events: none;
}
/* Full-viewport variant (dialog({size:'full'})): the large dialog pinned to the
   whole screen. `.modal` drops its 20px padding so the 100vw/100vh card reaches
   the edges, and the card loses its resize grip and rounded corners. It also
   carries `dlg-lg`, so the flex-column body rules above and the diary fill rules
   (`dlg-lg .hw-ed`, `dlg-lg .hw-ed-panes`, ...) apply unchanged. */
.modal.modal-full { padding: 0; }
.modal-card.dlg-full {
  width: 100vw; max-width: 100vw; min-width: 0;
  height: 100vh; min-height: 0; max-height: 100vh;
  border: 0; border-radius: 0; resize: none;
}
.modal-card.dlg-full::after { display: none; }
.modal-card h3 { margin: 0 0 16px; font-size: 115%; }
.modal-card label { display: block; font-size: 13px; color: var(--muted); margin: 0; }
.modal-card input, .modal-card select { width: 100%; padding: 9px; border: 1px solid var(--border); border-radius: 4px; font-size: 14px; }
.modal-card .theme-pick { display: flex; align-items: center; gap: 8px; }
.modal-card .theme-pick select { flex: 1; }
.modal-card .theme-pick .theme-preview { margin-left: auto; white-space: nowrap; }
.modal-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 20px; }
/* Whiteboard picker list (Load whiteboard dialogs): default (modern) styling,
   so the list does not fall back to raw <ul> under Theseus3k. */
.dlg-blist { list-style: none; margin: 0 0 10px; padding: 0; max-height: 220px; overflow-y: auto; border: 1px solid var(--border); border-radius: 8px; background: var(--panel, #f6f8fb); }
.dlg-blist li { padding: 9px 12px; cursor: pointer; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 8px; font-size: 14px; color: var(--text); }
.dlg-blist li:last-child { border-bottom: 0; }
.dlg-blist .wb-ico { width: 16px; height: 16px; flex: 0 0 auto; opacity: .8; }
.dlg-blist .wb-ico + .cf-name { min-width: 0; }
.dlg-blist li:hover { background: rgba(37,99,235,.06); }
.dlg-blist li.selected { background: rgba(37,99,235,.12); font-weight: 600; }
.dlg-chk { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--muted); cursor: pointer; }
.dlg-chk input[type="checkbox"] { width: auto; }
.modal-actions button { padding: 8px 16px; border-radius: 4px; border: 1px solid var(--border); cursor: pointer; font-size: 13px; font-weight: 600; }

/* Avatar crop modal: image stage, dimmed outside the selection square */
.avatar-crop-modal .crop-card { max-width: 520px; }
.crop-stage { position: relative; overflow: hidden; border: 1px solid var(--border); border-radius: 8px; background: #111; margin: 0 auto; }
.crop-stage img { position: absolute; left: 0; top: 0; display: block; user-select: none; -webkit-user-drag: none; }
.crop-box {
  position: absolute; z-index: 2; box-sizing: border-box;
  border: 1px solid rgba(255,255,255,.9);
  box-shadow: 0 0 0 9999px rgba(0,0,0,.55);
  cursor: move; user-select: none; -webkit-user-select: none; touch-action: none;
}
.crop-box::before, .crop-box::after {
  content: ""; position: absolute; background: rgba(255,255,255,.5); pointer-events: none;
}
.crop-box::before { left: 0; right: 0; top: 33.333%; height: 1px; }
.crop-box::after { top: 0; bottom: 0; left: 33.333%; width: 1px; }
.crop-handle {
  position: absolute; right: -8px; bottom: -8px;
  width: 16px; height: 16px; box-sizing: border-box;
  border: 2px solid #fff; border-radius: 50%; background: #2563eb; cursor: nwse-resize;
}
.crop-hint { font-size: 12px; color: var(--muted); margin: 8px auto 0; text-align: center; }

/* Settings vertical tabs */
.settings-layout { display:flex; gap:16px; min-height:260px; }
.settings-tabs-nav { display:flex; flex-direction:column; gap:4px; min-width:120px; border-right:1px solid var(--border); padding-right:12px; flex-shrink:0; }
.settings-tab-btn { text-align:left; padding:8px 10px; border:none; background:transparent; border-radius:4px; cursor:pointer; font-size:13px; color:var(--muted); }
.settings-tab-btn.active { background:var(--blue-light); color:var(--blue); font-weight:600; }
.settings-tab-btn:hover { background:rgba(0,0,0,.05); }
.settings-tab-body { flex:1; min-width:0; overflow-y:auto; max-height:50vh; }
.settings-tab-pane { display:none; }
.settings-tab-pane.active { display:block !important; }
.settings-page { flex:1; display:flex; flex-direction:column; padding:16px; overflow-y:auto; }
.settings-page-head { display:flex; align-items:center; justify-content:space-between; margin-bottom:16px; }
.settings-page-head h2 { margin:0; font-size:18px; }
.settings-page-actions { display:flex; gap:8px; }
.settings-page-nav { display:flex; flex-direction:column; gap:4px; min-width:160px; }
.admin-main .settings-tab-pane { display:none; }
.admin-main .settings-tab-pane.active { display:block !important; }
.settings-avatar-row img.settings-avatar-preview { width:64px; height:64px; border-radius:50%; object-fit:cover; border:1px solid var(--border); }
.settings-avatar-placeholder { width:64px; height:64px; border-radius:50%; background:#dde1e7; display:flex; align-items:center; justify-content:center; font-size:22px; color:var(--muted); }
/* Keyboard-shortcuts table (help + edit dialogs) */
.settings-actions { margin-top: 14px; display: flex; gap: 8px; }
.sc-table { margin-top: 4px; display: flex; flex-direction: column; gap: 4px; max-height: 46vh; overflow-y: auto; }
.sc-row { display: flex; align-items: center; gap: 10px; border-bottom: 1px solid var(--border); padding: 7px 2px; }
.sc-row.recording { background: var(--blue-light); border-radius: 6px; padding-left: 8px; }
.sc-desc { flex: 1; min-width: 0; font-size: 13px; color: var(--text); }
.sc-key code { display: inline-block; font-family: inherit; font-size: 12px; font-weight: 700; color: var(--text); background: var(--panel); border: 1px solid var(--border); border-radius: 4px; padding: 3px 8px; min-width: 64px; text-align: center; box-sizing: border-box; }
.sc-row.recording .sc-key code { border-color: var(--blue); color: var(--blue); animation: sc-pulse 1.1s ease-in-out infinite; }
@keyframes sc-pulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(85,159,247,.35); } 50% { box-shadow: 0 0 0 4px rgba(85,159,247,0); } }
.sc-rec { flex-shrink: 0; }
.sc-actions { display: flex; gap: 8px; padding-top: 12px; }
.cs-section { margin-top: 14px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 6px; background: var(--panel); }
.cs-section .cs-sec-title { font-weight: 700; font-size: 13px; margin-bottom: 6px; color: var(--text); }
.cs-section label { display: block; font-size: 13px; margin: 6px 0; color: var(--muted); }
.cs-section label.cs-inline { display: flex; align-items: center; gap: 6px; }
.cs-section input[type=number] { width: 90px; padding: 4px 6px; border: 1px solid var(--border); border-radius: 4px; }
.cs-section input[type=checkbox], .modal-card input[type=checkbox] { width: auto; height: auto; padding: 0; margin: 0 4px 0 0; vertical-align: middle; box-shadow: none; }
.cs-section input[type=checkbox] { margin-right: 4px; }
/* "Danger zone": destructive chat actions, visually distinct from the regular controls. */
.cs-section.cs-danger { border-color: var(--danger, #d9534f); background: #fdf6f5; }
.cs-section.cs-danger .cs-sec-title { color: var(--danger, #d9534f); }
/* Per-chat permission toggles (group member editor): stacked checkbox rows that
   a group owner / grant-holding delegate uses to hand members rights. */
.cp-list { margin-top: 8px; }
.cp-list .cp-row { display: block; font-size: 13px; margin: 8px 0; color: var(--text); cursor: pointer; }
.cp-list .cp-row input[type=checkbox] { width: auto; height: auto; padding: 0; margin: 0 6px 0 0; vertical-align: middle; box-shadow: none; }
.cp-list .cp-row input[type=checkbox]:disabled { opacity: .55; cursor: not-allowed; }
/* Composer locked for members whose per-chat 'write' right was revoked. */
.composer.readonly { opacity: .55; }
.composer.readonly textarea, .composer.readonly button { pointer-events: none; }
.composer.readonly textarea { caret-color: transparent; }
 .cs-danger-btns { display: flex; flex-wrap: wrap; gap: 8px; }
 .cs-image-row { display: flex; align-items: flex-start; gap: 12px; }
 .cs-image-preview { width: 64px; height: 64px; border-radius: 50%; border: 1px solid var(--border); display: flex; align-items: center; justify-content: center; overflow: hidden; flex-shrink: 0; background: var(--bg); }
 .cs-image-preview img { width: 100%; height: 100%; object-fit: cover; }
 .cs-image-placeholder { width: 64px; height: 64px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: var(--muted); color: #fff; font-weight: 700; font-size: 22px; }
 .cs-image-preview-img { width: 100%; height: 100%; object-fit: cover; }
 .btn-primary { background: var(--blue); color: #fff; border-color: var(--blue); }
.btn-ghost { background: #f5f6f8; color: var(--text); }
.btn-danger { background: var(--danger); color: #fff; border-color: var(--danger); }

/* ===== Device settings dialog ===== */
.dev-settings { max-width: 780px; }
.dev-setup { display: grid; grid-template-columns: 1.1fr 1fr; gap: 20px; align-items: stretch; }
.dev-setup-main { min-width: 0; display: flex; flex-direction: column; }
.dev-setup-bg { min-width: 0; display: flex; flex-direction: column; }
.dev-bg-sect { flex: 1; margin-top: 0; border: 1px solid var(--border); border-radius: 8px; padding: 12px; background: var(--panel, #fff); }
.dev-preview { border: 1px solid var(--border); border-radius: 8px; padding: 10px; background: #fafbfd; }
.dev-cam { position: relative; width: 100%; aspect-ratio: 16/9; background: #101419; border-radius: 6px; overflow: hidden; }
.dev-cam video { width: 100%; height: 100%; object-fit: cover; }
.dev-cam-empty { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: #79818b; font-size: 13px; pointer-events: none; }
.dev-cam video:not([srcobject=""]) { display: block; }
.dev-cam video { color: transparent; }
.dev-row { display: flex; align-items: center; gap: 10px; margin-top: 12px; }
.dev-level { position: relative; flex: 1; height: 12px; background: #e9edf3; border-radius: 6px; overflow: hidden; border: 1px solid var(--border); }
.dev-level-body { height: 100%; width: 0%; background: linear-gradient(90deg, #2f6fed, #19c37d); border-radius: 6px; transition: width 60ms linear; }
#devSpeakerTest { white-space: nowrap; }

/* ===== Virtual background picker ===== */
@media (max-width: 640px) {
  .dev-settings.dev-settings { max-width: 100%; }
  .dev-setup { grid-template-columns: 1fr; gap: 14px; }
}
.set-sect { margin-top: 14px; border-top: 1px solid var(--border); padding-top: 12px; }
.set-sect-title { font-size: 13px; font-weight: 600; color: var(--text); margin-bottom: 8px; }
.bg-manager { display: flex; flex-direction: column; gap: 8px; }
.bg-warn { border: 1px solid #e7c269; background: #fdf6e3; color: #6b5510; border-radius: 8px; padding: 8px 10px; font-size: 12.5px; line-height: 1.45; }
/* Live video preview: camera feed with selected background applied */
.bg-video-preview { display: flex; flex-direction: column; gap: 4px; margin: 4px 0; }
.bg-video-preview .bg-preview-body { height: 200px; border-radius: 8px; position: relative; overflow: hidden; display: block; border: 1px solid var(--border); }
.bg-video-preview .bg-preview-body.bg-wide { height: auto; aspect-ratio: 16/9; }
.bg-video-preview .bg-preview-body video,
.bg-video-preview .bg-preview-body canvas { display: none; position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; }
.bg-video-preview .bg-preview-kicker { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .5px; }
/* Current-selection preview strip */
.bg-preview { display: flex; flex-direction: column; gap: 4px; }
.bg-preview-kicker {
  font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .5px;
}
.bg-preview-body {
  position: relative; height: 58px; border-radius: 10px; overflow: hidden;
  border: 1px solid var(--border); background-color: #fff; background-position: center;
  background-size: cover; display: flex; align-items: flex-end;
}
.bg-preview-body.mode-off { background-image: repeating-linear-gradient(45deg, #f3f4f6 0 12px, #e6e8eb 12px 24px); }
.bg-preview-body.mode-blur { background-image: linear-gradient(135deg, #eef4fb 0%, #cfe3f5 48%, #e6edf5 100%); }
.bg-preview-body.mode-custom { background-color: #f3f4f6; }
.bg-video-preview .bg-preview-name { z-index: 2; background: rgba(0,0,0,.55); }
.bg-preview-name {
   position: relative; z-index: 1; width: 100%; padding: 4px 8px; font-size: 11px; color: #fff;
  background: rgba(0,0,0,.5); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Segmented mode control */
.bg-modes { display: flex; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.bg-mode-btn {
  flex: 1; border: 0; border-right: 1px solid var(--border); background: #fff; color: var(--text);
  padding: 6px 4px; font-size: 13px; cursor: pointer; white-space: nowrap;
}
.bg-mode-btn:last-child { border-right: 0; }
.bg-mode-btn:hover { background: var(--blue-light); }
.bg-mode-btn.active { background: linear-gradient(180deg, #5b9bff, #3b82f6); color: #fff; }
.bg-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(92px, 1fr)); gap: 8px; }
.bg-grid-note { font-size: 12px; color: #79818b; padding: 6px 2px; }
.bg-thumb {
  position: relative; border: 1px solid var(--border); border-radius: 8px; padding: 0; overflow: hidden;
  background: #fff; cursor: pointer; text-align: left; display: block; aspect-ratio: 16/10;
}
.bg-thumb img[alt=""], .bg-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.bg-thumb-name {
  position: absolute; left: 0; right: 0; bottom: 0; background: linear-gradient(transparent, rgba(0,0,0,.65));
  color: #fff; font-size: 11px; padding: 14px 6px 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.bg-thumb-check {
  position: absolute; top: 4px; left: 4px; width: 18px; height: 18px; display: none;
}
.bg-thumb-check img { width: 18px; height: 18px; display: block; }
.bg-thumb.active { border-color: #2d6fe0; box-shadow: 0 0 0 2px rgba(47,111,237,.35); }
.bg-thumb.active .bg-thumb-check { display: block; }
.bg-thumb-del {
  position: absolute; top: 4px; right: 4px; width: 22px; height: 22px; border-radius: 50%;
  background: rgba(0,0,0,.55); display: flex; align-items: center; justify-content: center; cursor: pointer;
  opacity: 0; transition: opacity .15s ease;
}
.bg-thumb:hover .bg-thumb-del { opacity: 1; }
.bg-thumb-del img { width: 12px; height: 12px; }
.bg-thumb-del:hover { background: rgba(200,40,40,.85); }
/* Upload tile: dashed + tile, mirrors a gallery entry */
.bg-add {
  border-style: dashed; background: #fafbfc; display: flex; align-items: center;
  justify-content: center; color: var(--muted);
}
.bg-add:hover { border-color: var(--blue); background: var(--blue-light); }
.bg-add:disabled { cursor: default; opacity: .5; }
.bg-add-icon { width: 20px; height: 20px; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
.bg-add-icon img { width: 20px; height: 20px; }
.bg-add .bg-thumb-name { position: static; background: none; color: var(--muted); padding: 34px 6px 6px; text-align: center; }
.bg-add:hover .bg-thumb-name { color: var(--blue); }
.bg-hint { font-size: 12px; color: #79818b; }
.bg-note { font-size: 13px; color: var(--danger); background: #fff0f0; border: 1px solid #f3c1c1; border-radius: 6px; padding: 8px 10px; }
.bg-mgr-actions { display: flex; gap: 8px; }
.bg-up.busy { opacity: .7; cursor: default; }
/* Simplified background picker: one horizontal strip + a status line */
.bg-strip {
  display: flex; gap: 8px; overflow-x: auto; padding: 2px 0 6px;
  -webkit-overflow-scrolling: touch;
}
.bg-strip .bg-thumb { flex: 0 0 auto; width: 96px; aspect-ratio: 16/10; }
.bg-strip .bg-add { width: 96px; }
.bg-status { font-size: 12px; color: #79818b; display: flex; align-items: center; gap: 6px; min-height: 16px; }
.bg-status .bg-spin { width: 12px; height: 12px; flex: 0 0 auto; }
.bg-hint-err { color: var(--danger); }
.bg-add.busy { opacity: .55; cursor: default; }
.bg-add.busy .bg-add-icon { display: none; }
/* Spinner */
.bg-spin {
  display: inline-block; width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid rgba(120,130,140,.35); border-top-color: #3b82f6;
  -webkit-animation: bg-spin .8s linear infinite; animation: bg-spin .8s linear infinite;
}
@-webkit-keyframes bg-spin { to { transform: rotate(360deg); } }
@keyframes bg-spin { to { transform: rotate(360deg); } }
/* Loading badge on the local call tile while the background is prepared */
.bg-loading {
  position: absolute; inset: 0; z-index: 3; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 8px; pointer-events: none;
  background: rgba(16,20,24,.42); color: #fff; font-size: 13px;
}
.bg-loading .bg-spin { width: 22px; height: 22px; border-width: 3px; }
/* Loading overlay in the device-settings camera preview */
.dev-cam-loading {
  position: absolute; inset: 0; z-index: 2; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 8px; pointer-events: none;
  background: rgba(0,0,0,.35); color: #fff; font-size: 13px;
}

/* ===== User search + member pick list (item 38) ===== */
.user-search-results, #csResults, #p2pResults { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; max-height: 200px; overflow-y: auto; }
.user-search-results button, #csResults button, #p2pResults button { text-align: left; padding: 8px 10px; border: 1px solid var(--border); background: #fff; border-radius: 4px; cursor: pointer; font-size: 13px; }
.user-search-results button:hover, #csResults button:hover, #p2pResults button:hover { background: var(--blue-light); border-color: var(--blue); }
#csPicks { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.cs-pick { display: inline-flex; align-items: center; gap: 6px; background: var(--blue-light); border: 1px solid var(--blue); color: var(--text); padding: 3px 8px; border-radius: 12px; font-size: 12px; }
.cs-pick .cs-rm { border: none; background: transparent; color: var(--danger); cursor: pointer; font-size: 14px; line-height: 1; }

/* ===== Toast ===== */
.toast {
  position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background: #333; color: #fff;
  padding: 10px 18px; border-radius: 6px; font-size: 13px; opacity: 0; pointer-events: none; transition: opacity .3s; z-index: 200;
}
.toast.show { opacity: 1; }

/* Connection status pill: removed — errors are shown via toasts instead. */

/* ===== Responsive ===== */
@media (max-width: 820px) {
  .sidebar { position: fixed; left: 0; top: 0; bottom: 0; z-index: 30; transform: translateX(-100%); transition: transform .25s; width: 100% !important; max-width: 100%; min-width: 0; }
  .sidebar-resizer { display: none; }
  .sidebar.open { transform: translateX(0); box-shadow: 0 0 20px rgba(0,0,0,.15); }
  .menu-btn { display: inline-flex; }
  .chat-area { width: 100%; }
  .back-btn { display: inline-flex !important; }
  .msg { max-width: 85%; }
  /* On small screens the chat list is a full-screen home view whenever no chat
     or board is open, so the empty-state splash is never shown in the pane. */
  .no-chat { display: none; }
}
.back-btn { display: none; }

/* Call overlay */
/* The call interface reuses the app's late-2000s messenger look (light blue-grey
   panels, glossy round buttons) instead of a dark "modal" style, so starting a
   call feels like staying in the same chat window. */
.call-overlay {
  position: fixed; inset: 0; z-index: 60; display: flex; flex-direction: column;
  padding: 14px; gap: 12px;
  background: linear-gradient(180deg, #eef2f8 0%, #e1e8f3 100%);
}
.call-videos { flex: 1; display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); grid-auto-rows: minmax(0, 1fr); align-content: center; justify-content: center; justify-items: center; overflow: hidden; padding: 4px; min-height: 0; }
.call-video { position: relative; background: #101418; border: 2px solid #fff; border-radius: 8px; overflow: hidden; width: 100%; aspect-ratio: 16 / 9; max-width: 640px; max-height: 100%; min-width: 0; height: auto; box-shadow: 0 2px 6px rgba(0,0,0,.18); align-self: center; }
@media (max-width: 640px) {
  .call-videos { grid-template-columns: 1fr; }
  .call-video { max-width: 100%; }
}
.call-video video { background: #ffffff; width: 100%; height: 100%; object-fit: cover; }
/* Camera tiles (local selfie view *and* remote participants) and device/
   background previews render mirrored; screen shares are never flipped. */
.call-video:not([data-scr="1"]) video,
#devCamPreview,
.bg-preview-body video { transform: scaleX(-1); }
/* Full-stage tiles (click-to-focus + screen share) must never crop the source:
   contain so the whole frame is visible, letterboxed on non-matching aspect. */
.call-overlay.focus-mode .call-video.focused video { object-fit: contain; }
.call-overlay .call-video[data-scr="1"] video { object-fit: contain; max-height: 100dvh; max-width: 100dvw; }
.call-video.no-video video { visibility: hidden; }
.call-video.no-video::after { content: ''; position: absolute; inset: 0; background: #101418; z-index: 1; }
/* Centered "camera off" glyph for tiles whose video is disabled/missing. */
.call-video .cv-camoff { display: none; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 2; width: 64px; height: 64px; align-items: center; justify-content: center; border-radius: 50%; border: 1px dashed rgba(255,255,255,.22); background: rgba(255,255,255,.06); }
.call-video .cv-camoff img { width: 40px; height: 40px; filter: brightness(0) invert(1); opacity: .85; }
.call-video.no-video .cv-camoff { display: flex; }
/* Centered spinner + label while a tile is still waiting for its stream. */
.call-video .cv-loading { display: none; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); flex-direction: column; align-items: center; gap: 12px; z-index: 2; }
.call-video.loading .cv-loading { display: flex; }
.call-video .cv-loading-spin { width: 40px; height: 40px; border-radius: 50%; border: 4px solid rgba(37,99,235,.18); border-top-color: #2563eb; animation: callspin .9s linear infinite; }
.call-video .cv-loading-txt { color: #8b93a7; font-size: 12px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; }
.call-video .cv-name { position: absolute; bottom: 6px; left: 8px; background: rgba(0,0,0,.55); color: #fff; padding: 2px 8px; border-radius: 4px; font-size: 12px; font-weight: 600; z-index: 2; display: flex; align-items: center; gap: 4px; max-width: calc(100% - 16px); }
.call-video .cv-name .cv-role { width: 14px; height: 14px; border-radius: 50%; flex: 0 0 auto; }
.call-video .cv-name .cv-name-txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The flat 3k icons are pure black glyphs and would vanish on the dark name
   backdrop, so only they are recoloured white. The glossy 2k set is already
   coloured and keeps its skeuomorphic look, so the filter is theme-scoped. */
[data-theme="theseus3k"] .call-video .cv-name .cv-role { filter: brightness(0) invert(1); }
/* Profile-picture role badges are photos, never recoloured. */
[data-theme="theseus3k"] .call-video .cv-name .cv-role.cv-avatar { filter: none; }
.call-video .cv-quality { position: absolute; top: 6px; left: 8px; color: #fff; padding: 2px 6px; border-radius: 10px; line-height: 0; background: rgba(0,0,0,.5); z-index: 2; }
.call-video .cv-quality img { width: 13px; height: 13px; vertical-align: middle; }
.call-video .cv-quality.q-good { background: #1a7f37; }       /* excellent */
.call-video .cv-quality.q-fair { background: #b26a00; }       /* fair / connecting */
.call-video .cv-quality.q-poor { background: #c62828; }       /* poor */
.call-video .cv-quality.q-offline { background: #555; }       /* offline */

/* "Connecting…" indicator shown while the call is being joined but no video
   tiles exist yet (media acquisition / ICE negotiation still in flight). A
   compact pill pinned to the top edge so it never covers the (centered) video
   grid once the first tiles appear. */
.call-connecting {
  position: absolute; top: 12px; left: 50%; transform: translateX(-50%);
  display: flex; flex-direction: row; align-items: center; gap: 8px;
  padding: 7px 14px; border-radius: 999px;
  background: rgba(255,255,255,.82); border: 1px solid rgba(0,0,0,.1);
  box-shadow: 0 2px 10px rgba(0,0,0,.12);
  color: #56607a; font-size: 13px; font-weight: 600;
  white-space: nowrap; pointer-events: none; z-index: 5;
}
.call-connecting.hidden { display: none; }
.call-spinner {
  width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid rgba(37,99,235,.18); border-top-color: #2563eb;
  animation: callspin .9s linear infinite;
}
@keyframes callspin { to { transform: rotate(360deg); } }

/* Breakout room badge on each participant tile (room membership is read-only
   for everyone; moderators manage rooms from the left-side panel). */
.call-video .cv-name .cv-room { margin-left: auto; padding: 0 5px; border-radius: 3px; background: rgba(255,255,255,.22); font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; }

/* "You are in room X" banner, top-left of the call screen. It moves right when
   the breakout management panel is open so the two never overlap. */
.breakout-banner { position: absolute; top: 14px; left: 14px; z-index: 30; background: rgba(37,99,235,.10); color: #1e3a8a; border: 1px solid #93c5fd; border-radius: 8px; padding: 6px 12px; font-size: 12px; font-weight: 600; box-shadow: 0 2px 8px rgba(0,0,0,.08); pointer-events: none; max-width: calc(100% - 320px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.breakout-banner.hidden { display: none; }
.call-overlay.has-breakout .breakout-banner { left: 290px; }

/* Breakout management panel (moderator-only, opened from the call bar). */
.breakout-panel { position: absolute; top: 14px; left: 14px; bottom: 82px; width: 268px; z-index: 32; background: #fff; border: 1px solid #dde5f2; border-radius: 10px; box-shadow: 0 6px 24px rgba(0,0,0,.18); display: flex; flex-direction: column; overflow: hidden; }
.breakout-panel.hidden { display: none; }
.brk-head { display: flex; align-items: center; gap: 6px; padding: 10px 12px; border-bottom: 1px solid #dde5f2; background: linear-gradient(180deg, #fff, #eef3fa); }
.brk-title { flex: 1; font-weight: 700; font-size: 13px; }
.brk-head .icon-btn { padding: 4px; }
.brk-head .icon-btn img { width: 12px; height: 12px; opacity: .6; }
.brk-head .icon-btn:hover img { opacity: 1; }
.brk-actions { display: flex; flex-wrap: wrap; gap: 6px; padding: 10px 12px 2px; }
.brk-actions .btn-primary, .brk-actions .btn-ghost { font-size: 12px; padding: 5px 9px; }
.brk-hint { padding: 8px 12px 0; font-size: 11px; color: #7a8194; }
.brk-list { flex: 1; overflow-y: auto; padding: 10px 12px; display: flex; flex-direction: column; gap: 10px; }
.brk-empty { font-size: 12px; color: #7a8194; padding: 14px 2px; }
.brk-room { border: 1px solid #e3e8f2; border-radius: 8px; overflow: hidden; }
.brk-rm-name { display: flex; align-items: center; gap: 4px; padding: 6px 8px; background: #f2f6fc; font-weight: 600; font-size: 12px; }
.brk-main .brk-rm-name { background: #eef6ee; }
.brk-rm-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.brk-ren, .brk-del { padding: 2px; }
.brk-ren img, .brk-del img { width: 12px; height: 12px; opacity: .6; }
.brk-ren:hover img, .brk-del:hover img { opacity: 1; }
.brk-rm-members { padding: 4px 6px; display: flex; flex-direction: column; gap: 2px; }
.brk-member { display: flex; align-items: center; gap: 6px; padding: 3px 4px; border-radius: 6px; }
.brk-member:hover { background: rgba(0,0,0,.04); }
.brk-role { width: 15px; height: 15px; border-radius: 50%; flex: 0 0 auto; }
.brk-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
.brk-move { font-size: 11px; max-width: 96px; padding: 1px 2px; border: 1px solid #d3dbea; border-radius: 4px; background: #fff; }
.brk-rm-empty { font-size: 11px; color: #a3aab8; padding: 2px 4px; }
.brk-foot { display: flex; gap: 6px; padding: 4px 12px 12px; }
.brk-foot .btn-ghost, .brk-foot .btn-danger { font-size: 12px; padding: 5px 9px; }

/* On phones the panel becomes a bottom sheet and the banner keeps full width. */
@media (max-width: 560px) {
  .breakout-banner, .call-overlay.has-breakout .breakout-banner { left: 14px; max-width: calc(100% - 28px); }
  .breakout-panel { top: auto; bottom: 72px; left: 8px; right: 8px; width: auto; }
}

/* Member sidebar (item 41) */
.chat-body { display: flex; flex: 1 1 auto; min-height: 0; }
.chat-main { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; min-height: 0; }
.member-sidebar { width: 220px; flex: 0 0 auto; position: relative; background: var(--sidebar-bg, #f5f7fb); border-left: 1px solid var(--border, #e3e6ef); overflow-y: auto; padding: 12px 10px; }
.member-sidebar.hidden { display: none; }
/* Invisible full-height drag strip on the member sidebar's left edge: the whole
   border is draggable, the boundary tracks the cursor (drag right narrows). */
.ms-resizer { position: absolute; left: 0; top: 0; bottom: 0; width: 6px; cursor: col-resize; user-select: none; touch-action: none; z-index: 3; }
.ms-resizer.hidden { display: none; }
body.rs-resizing { cursor: col-resize; user-select: none; }
.member-sidebar-head { font-weight: 600; margin-bottom: 10px; display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.member-sidebar-head .ms-back { display: none; padding: 2px; flex: 0 0 auto; }
.member-sidebar-head .ms-back img { width: 16px; height: 16px; }
.ms-add-link { font-size: 12px; font-weight: 500; color: var(--blue, #2563eb); cursor: pointer; text-decoration: none; }
.ms-add-link:hover { text-decoration: underline; }
.member-sidebar-group { font-size: 11px; text-transform: uppercase; color: var(--muted, #7a8194); margin: 10px 0 4px; }
.ms-label { letter-spacing: .03em; }
.ms-row { display: flex; align-items: center; gap: 8px; padding: 5px 6px; border-radius: 6px; user-select: none; }
.ms-open-p2p { cursor: pointer; }
.ms-row:hover { background: rgba(0,0,0,.04); }
.ms-avatar { width: 20px; height: 20px; border-radius: 50%; flex: 0 0 auto; }
.ms-avatar-wrap { position: relative; display: inline-flex; flex: 0 0 auto; }
.ms-online { position: absolute; right: -2px; bottom: -2px; width: 8px; height: 8px; border-radius: 50%; background: #34a853; border: 1.5px solid var(--sidebar-bg, #f5f7fb); }
.ms-name { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ms-role { font-size: 11px; color: var(--muted, #7a8194); }
.ms-empty { font-size: 12px; color: var(--muted, #7a8194); padding: 4px 6px; }
.ms-guest .ms-name { color: #666; }
#chatActions .icon-btn.active { background: var(--blue, #3b82f6); border-radius: 6px; }
.call-video .hand-reaction { position: absolute; top: 8px; right: 8px; z-index: 2; }
/* Flat 3k glyphs only — the glossy 2k hand icon keeps its colour. */
[data-theme="theseus3k"] .call-video .hand-reaction img { filter: brightness(0) invert(1); }
.call-video .cv-media-state { position: absolute; bottom: 6px; right: 8px; display: flex; gap: 4px; z-index: 2; }
.call-video .cv-media-state .cv-state-ic { width: 20px; height: 20px; padding: 2px; background: rgba(0,0,0,.55); border-radius: 3px; }
.call-video .cv-media-state .cv-state-ic img { width: 16px; height: 16px; display: block; }
/* Same rule: the flat 3k mic/cam state glyphs get recoloured white, the glossy
   2k ones (already coloured) render as-is on the dark backdrop. */
[data-theme="theseus3k"] .call-video .cv-media-state .cv-state-ic img { filter: brightness(0) invert(1); }
.call-video.large { max-width: 100%; height: 50%; }

/* Click-to-focus: one stream fills the overlay, the rest are hidden; clicking
   the focused stream again restores the normal grid. See the relocated block at
   the end of the call layout styles (must beat the screen/board-shared rules). */

/* Screen sharing, like a call whiteboard, takes over the whole overlay: the
   shared screen fills the main area while the participant videos collapse into
   a compact strip at the bottom above the control bar. The screen tile is a
   direct overlay child, so it is positioned against the overlay (inset:0) —
   never against the collapsed strip container. */
.call-overlay .call-video[data-scr="1"] {
  position: absolute; inset: 0; width: auto; min-width: 0; max-width: none;
  height: auto; min-height: 0; aspect-ratio: auto; border: none; border-radius: 0;
  box-shadow: 0 2px 18px rgba(0,0,0,.35); z-index: 5;
}
/* While a shared screen's stream is still arriving the tile shows only the
   loading spinner — never a blank full-stage box behind it. The <video>
   element itself (white until its first frame) must be hidden too, otherwise
   the transparency is masked by it. */
.call-overlay .call-video[data-scr="1"].loading { background: transparent; border: none; box-shadow: none; }
.call-overlay .call-video[data-scr="1"].loading video { visibility: hidden; }
.call-overlay.screen-shared .call-videos {
  position: absolute; left: 10px; right: 10px; bottom: 84px; z-index: 6;
  display: flex; flex-wrap: nowrap; gap: 6px; align-items: flex-end;
  justify-content: center; overflow-x: auto; padding: 4px;
  pointer-events: auto; margin: 0;
  touch-action: none; user-select: none;
}
/* The participant strip can be dragged by grabbing any tile; it snaps to the
   centre of the nearest screen edge (top/right/bottom/left) and re-orients
   itself (row on top/bottom, column on left/right). */
.call-overlay.screen-shared.strip-tc .call-videos,
.call-overlay.screen-shared.strip-bc .call-videos {
  left: 50%; right: auto; top: 10px; bottom: auto;
  transform: translateX(-50%); flex-direction: row;
  justify-content: center; align-items: center;
  overflow-x: auto; overflow-y: visible;
}
.call-overlay.screen-shared.strip-bc .call-videos { top: auto; bottom: 10px; }
.call-overlay.screen-shared.strip-lc .call-videos,
.call-overlay.screen-shared.strip-rc .call-videos {
  top: 50%; bottom: auto; left: 10px; right: auto;
  transform: translateY(-50%); flex-direction: column;
  justify-content: center; align-items: center;
  overflow-y: auto; overflow-x: visible;
}
.call-overlay.screen-shared.strip-rc .call-videos { left: auto; right: 10px; }
/* While actively being dragged the strip floats freely at inline left/top. */
.call-overlay.screen-shared.strip-drag .call-videos { left: auto; right: auto; top: auto; bottom: auto; transform: none; }
/* During a drag the screen edge the strip will snap to is highlighted as a thin
   accent line, so the dock target is obvious before the pointer is released. */
.call-overlay.strip-drag::after {
  content: ''; position: absolute; z-index: 30; pointer-events: none;
  background: var(--blue); box-shadow: 0 0 12px var(--blue);
  border-radius: 2px; transition: none;
}
.call-overlay.strip-drag[data-strip-best="tc"]::after { top: 0; left: 0; right: 0; height: 4px; }
.call-overlay.strip-drag[data-strip-best="bc"]::after { bottom: 0; left: 0; right: 0; height: 4px; }
.call-overlay.strip-drag[data-strip-best="lc"]::after { left: 0; top: 0; bottom: 0; width: 4px; }
.call-overlay.strip-drag[data-strip-best="rc"]::after { right: 0; top: 0; bottom: 0; width: 4px; }
 .call-overlay.screen-shared .call-video:not([data-scr="1"]) {
    flex: 0 0 auto; width: 200px; min-width: 200px; max-width: 200px; height: 112px; min-height: 112px;
    box-shadow: 0 2px 10px rgba(0,0,0,.35);
  }
  @media (max-width: 1366px) {
    .call-overlay.screen-shared .call-videos {
      left: 50%; right: auto; transform: translateX(-50%);
      justify-content: flex-start; max-width: calc(100vw - 20px);
    }
    .call-overlay.screen-shared .call-video:not([data-scr="1"]) { width: 160px; min-width: 160px; max-width: 160px; height: 90px; min-height: 90px; }
  }
.call-overlay.screen-shared .call-video:not([data-scr="1"]) .cv-name { font-size: 11px; padding: 1px 6px; }
.call-overlay.screen-shared .call-video:not([data-scr="1"]) .cv-camoff { width: 44px; height: 44px; }
.call-overlay.screen-shared .call-video:not([data-scr="1"]) .cv-camoff img { width: 28px; height: 28px; }
.call-overlay.screen-shared .call-video:not([data-scr="1"]) .cv-loading-spin { width: 28px; height: 28px; border-width: 3px; }
.call-overlay.screen-shared .call-video:not([data-scr="1"]) .cv-loading-txt { display: none; }
 .call-overlay.screen-shared .call-bar { position: relative; z-index: 8; margin-top: auto; flex: 0 0 auto; }

 /* Screen annotation toolbar: appears on the tile when screen sharing
    is active. Disabled by default; the sharer toggles it on. */
 .call-overlay .screen-annot-toolbar { display: none; }
 .call-overlay .screen-annotate-canvas { pointer-events: none; }
 .call-overlay .screen-annotate-canvas.active { pointer-events: auto; cursor: crosshair; }
 /* Colorful remote cursors on screen tiles */
 .call-overlay .screen-annot-cursor { position: absolute; pointer-events: none; z-index: 5; display: none; }
 .call-overlay .screen-annot-cursor .sac-arrow { width: 0; height: 0; border-left: 8px solid transparent; border-right: 8px solid transparent; border-top: 12px solid currentColor; margin-left: -4px; filter: drop-shadow(0 1px 2px rgba(0,0,0,.3)); }
 .call-overlay .screen-annot-cursor .sac-dot { width: 8px; height: 8px; border-radius: 50%; border: 1px solid #fff; margin-top: -4px; }
/* Screen annotation color popup */
  .screen-annot-color-pop { z-index: 50; }
  /* Screen-share annotation toolbar: buttons re-styled to read on the dark
     floating bar. Theme icons are monochrome glyphs; the filter renders them
     as white silhouettes so the whole toolset stays visible. */
  .call-overlay .screen-annot-toolbar button { background: none; border: none; padding: 3px; border-radius: 4px; cursor: pointer; color: #fff; }
  .call-overlay .screen-annot-toolbar button:hover { background: rgba(255,255,255,.18); }
  .call-overlay .screen-annot-toolbar button.active { outline: 1px solid #fff; outline-offset: -1px; }
  .call-overlay .screen-annot-toolbar button:disabled { opacity: .35; cursor: default; }
  .call-overlay .screen-annot-toolbar img { display: block; width: 16px; height: 16px; filter: brightness(0) invert(1); }
  .call-overlay .screen-annot-toolbar .screen-ann-toggle { display: inline-flex; align-items: center; gap: 4px; }
  .call-overlay .screen-annot-toolbar .screen-ann-toggle span,
  .call-overlay .screen-annot-toolbar button.screen-ann-width span { font-size: 11px; line-height: 16px; color: #fff; }
 /* Mobile: smaller toolbar */
 @media (max-width: 640px) {
   .call-overlay .screen-annot-toolbar { padding: 3px 6px; gap: 3px; }
   .call-overlay .screen-annot-toolbar .screen-ann-color span { width: 14px; height: 14px; }
 }

/* Reactions: a big emoticon briefly bursting over the reacting participant's
   tile (fades out; removed from the DOM after 3 s). */
.call-video .reaction-burst { position: absolute; left: 50%; top: 46%; transform: translate(-50%, -50%); z-index: 3; pointer-events: none; animation: reactionPop 3s ease forwards; }
.call-video .reaction-burst img { width: 64px; height: 64px; filter: drop-shadow(0 2px 6px rgba(0,0,0,.5)); }
@keyframes reactionPop {
  0%   { transform: translate(-50%, -50%) scale(0.3); opacity: 0; }
  12%  { transform: translate(-50%, -62%) scale(1.15); opacity: 1; }
  20%  { transform: translate(-50%, -55%) scale(1); }
  75%  { opacity: 1; }
  100% { transform: translate(-50%, -85%) scale(0.85); opacity: 0; }
}

/* Reaction picker above the call toolbar (styled like the composer's panel). */
.react-menu {
  position: fixed; z-index: 70; transform: translateX(-50%);
  display: flex; flex-direction: column; gap: 6px; padding: 8px; width: 244px; max-height: 320px;
  background: linear-gradient(#fbfcfe,#e9eff7); border: 1px solid #9fb4d4;
  box-shadow: 0 4px 14px rgba(20,40,70,.28); border-radius: 6px;
}
.react-menu.hidden { display: none; }
.react-menu .emoji-picker { height: auto; }
.react-menu .emoji-grid { height: 250px; max-height: 250px; flex: 0 0 auto; }
.react-menu img:hover { background: linear-gradient(#5b9ce4,#2f6fc8); }
.react-menu img:active { transform: scale(.92); }
.call-bar {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  background: linear-gradient(180deg, #ffffff, #e9eff8);
  border: 1px solid #d4dcea; border-radius: 12px; padding: 10px 14px; flex-wrap: wrap;
  position: relative; z-index: 6; box-shadow: 0 2px 8px rgba(20,40,90,.12);
}
/* Persistent yellow "mesh connection" warning in the call toolbar. The CSS
   triangle-'!' needs no theme icon asset, so it renders identically in both
   themes. Hidden unless the call is actually running over the P2P mesh. */
.call-mesh-warn { display: none; align-items: center; gap: 6px; margin-right: 2px; padding: 4px 10px 4px 8px; border-radius: 20px; background: #fff7d6; border: 1px solid #f2c94c; color: #6b5300; font-size: 11px; font-weight: 700; line-height: 1; white-space: nowrap; cursor: default; user-select: none; }
.call-overlay.mesh-mode .call-mesh-warn { display: inline-flex; }
.call-mesh-warn .wm-icon { position: relative; width: 14px; height: 13px; flex: 0 0 auto; }
.call-mesh-warn .wm-icon::before { content: ''; position: absolute; left: 50%; top: 0; transform: translateX(-50%); border-left: 7px solid transparent; border-right: 7px solid transparent; border-bottom: 12px solid #f5b301; }
.call-mesh-warn .wm-icon::after { content: '!'; position: absolute; left: 50%; top: 1px; transform: translateX(-50%); font-size: 9px; font-weight: 800; color: #6b4a00; line-height: 1; }
/* Persistent red "WebRTC UDP disabled" warning in the call toolbar. Raised by
   the UDP probe when the browser only gathers TCP candidates, which makes both
   the SFU and mesh transports unable to connect (blank remote tiles). */
.call-udp-warn { display: none; align-items: center; gap: 6px; margin-right: 2px; padding: 4px 10px 4px 8px; border-radius: 20px; background: #fdecec; border: 1px solid #eb5757; color: #7a1f1f; font-size: 11px; font-weight: 700; line-height: 1; white-space: nowrap; cursor: default; user-select: none; }
.call-overlay.no-udp .call-udp-warn { display: inline-flex; }
.call-udp-warn .wm-icon { position: relative; width: 14px; height: 13px; flex: 0 0 auto; }
.call-udp-warn .wm-icon::before { content: ''; position: absolute; left: 50%; top: 0; transform: translateX(-50%); border-left: 7px solid transparent; border-right: 7px solid transparent; border-bottom: 12px solid #eb5757; }
.call-udp-warn .wm-icon::after { content: '!'; position: absolute; left: 50%; top: 1px; transform: translateX(-50%); font-size: 9px; font-weight: 800; color: #fff; line-height: 1; }
/* Glossy late-2000s disc buttons: white-blue gradient, bevel highlight, crisp 1px rim. */
.call-btn {
  width: 46px; height: 46px; border-radius: 50%; border: 1px solid #b6c3da; cursor: pointer;
  background: linear-gradient(180deg, #ffffff, #d6dfee);
  display: flex; align-items: center; justify-content: center;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.8), 0 1px 2px rgba(0,0,0,.12);
}
.call-btn img { width: 22px; height: 22px; }
.call-btn:hover { background: linear-gradient(180deg, #ffffff, #cbd6e8); }
.call-btn.active { background: linear-gradient(180deg, #5b9bff, #3b82f6); border-color: #2d6fe0; }
.call-btn.danger { background: linear-gradient(180deg, #ffabab, #e69996); border-color: #bd7976; }
.call-btn.danger:hover { background: linear-gradient(180deg, #ffabab, #d38080); }
.call-btn[disabled] { opacity: .5; cursor: progress; }
/* Call event toasts (hand raises, reactions) — top-right of the call screen,
   stacked below the badge column, fading out after a few seconds. */
.call-toasts { position: absolute; top: 14px; right: 14px; z-index: 40; display: flex; flex-direction: column; gap: 8px; pointer-events: none; }
.call-overlay.has-sidebar .call-toasts { right: 264px; }
.call-toast {
  display: flex; align-items: center; gap: 8px; max-width: 260px;
  background: rgba(20,26,38,.88); color: #fff; border-radius: 8px;
  padding: 8px 12px; font-size: 13px; box-shadow: 0 4px 14px rgba(0,0,0,.3);
  transition: opacity .4s, transform .4s;
}
.call-toast.hide { opacity: 0; transform: translateY(-8px); }
.call-toast .ct-ico { width: 18px; height: 18px; flex: 0 0 auto; }
.call-toast .ct-emoji img { width: 22px; height: 22px; display: block; }
.call-toast .ct-txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.call-toast .ct-txt b { font-weight: 600; }

/* Participants sidebar within the call overlay. */
.call-sidebar {
  position: absolute; top: 14px; right: 14px; width: 240px; z-index: 20;
  height: auto; max-height: calc(100% - 100px);
  background: linear-gradient(180deg, #fbfcfe, #eef3fa); border: 1px solid #c5d2e6;
  border-radius: 10px; box-shadow: 0 4px 16px rgba(20,40,90,.2);
  display: flex; flex-direction: column; overflow: hidden;
}
.call-sidebar.hidden { display: none; }
.call-sidebar-head { font-weight: 700; font-size: 13px; padding: 10px 12px; border-bottom: 1px solid #dde5f2; background: linear-gradient(180deg, #ffffff, #eef3fa); display: flex; align-items: center; gap: 6px; cursor: move; user-select: none; }
.call-sidebar-head .csh-title { flex: 1; }
.call-sidebar-head .csh-close { padding: 4px; }
.call-sidebar-head .csh-close img { width: 12px; height: 12px; opacity: .6; }
.call-sidebar-head .csh-close:hover img { opacity: 1; }
.call-sidebar-list { overflow-y: auto; padding: 6px 8px; display: flex; flex-direction: column; gap: 2px; }
.cs-breakouts { padding: 2px 0 6px; border-bottom: 1px solid #e3e9f2; margin-bottom: 4px; }
.cs-breakouts-btn {
  width: 100%; display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  cursor: pointer; padding: 8px 10px; border-radius: 6px; border: 1px dashed #b9c6da;
  background: transparent; color: #4b6a94; font-size: 13px; font-weight: 600;
}
.cs-breakouts-btn:hover { background: rgba(85,159,247,.09); border-color: var(--blue); color: var(--blue); }
.cs-breakouts-btn img { width: 15px; height: 15px; }
.cs-breakouts.active .cs-breakouts-btn { border-style: solid; background: var(--blue); border-color: var(--blue); color: #fff; }
.cs-row { display: flex; align-items: center; gap: 8px; padding: 6px; border-radius: 6px; }
.cs-row:hover { background: rgba(0,0,0,.04); }
.cs-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }
.cs-row .cv-quality { position: static; }
.cs-mod { width: 16px; height: 16px; flex: 0 0 auto; }
.cs-mod img { width: 15px; height: 15px; display: block; }

/* Pre-call permission gate: overlay card explaining a hard-denied mic/camera and
   letting the user re-check or continue anyway. */
.perm-gate { position: absolute; inset: 0; z-index: 50; display: flex; align-items: center; justify-content: center; background: rgba(15,22,38,.45); }
.perm-gate-card {
  width: min(440px, 92%); background: linear-gradient(180deg, #ffffff, #eef3fa);
  border: 1px solid #c5d2e6; border-radius: 12px; padding: 20px 22px;
  box-shadow: 0 10px 30px rgba(0,0,0,.3);
}
.perm-gate-card h3 { margin: 0 0 6px; font-size: 16px; }
.pg-desc { margin: 0 0 12px; font-size: 13px; color: var(--muted, #5b6472); }
.pg-rows { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.pg-row { display: flex; align-items: center; justify-content: space-between; padding: 7px 10px; border: 1px solid var(--border, #dde3ee); border-radius: 6px; background: #fff; font-size: 13px; }
.pg-row b { font-weight: 600; }
.pg-row.p-granted b { color: #1a7f37; }
.pg-row.p-denied b { color: #c62828; }
.pg-row.p-prompt b { color: #b26a00; }
.pg-hints-head { font-size: 12px; color: var(--muted, #5b6472); margin-bottom: 8px; }
.pg-hints { display: flex; flex-direction: column; gap: 5px; margin-bottom: 12px; border-top: 1px solid var(--border, #dde3ee); padding-top: 8px; }
.pg-acc {
  width: 100%; text-align: left; padding: 7px 10px; font-size: 13px; font-weight: 600;
  background: #f2f5fb; color: var(--text); border: 1px solid var(--border, #dde3ee);
  border-radius: 6px; cursor: pointer; display: flex; align-items: center; gap: 8px;
}
.pg-acc::before { content: '▸'; color: var(--muted, #7a8194); font-size: 11px; transition: transform .15s; }
.pg-acc.open::before { transform: rotate(90deg); }
.pg-acc.open { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.pg-acc-body { border: 1px solid var(--border, #dde3ee); border-top: none; border-radius: 0 0 6px 6px; background: #fff; }
.pg-acc-body.hidden { display: none; }
.pg-acc-body p { margin: 0; font-size: 12px; color: var(--muted, #5b6472); padding: 8px 10px; line-height: 1.45; }
.pg-never { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted, #5b6472); margin-bottom: 12px; cursor: pointer; }
.pg-actions { display: flex; justify-content: flex-end; gap: 8px; }
.pg-actions button { padding: 8px 16px; border-radius: 4px; border: 1px solid var(--border); cursor: pointer; font-size: 13px; font-weight: 600; }
.pg-actions .btn-primary { color: #fff; }

/* Whiteboard */
.board-wrap { position: relative; flex: 1; display: flex; flex-direction: column; min-height: 0; min-width: 0; background: #fff; }
/* Second toolbar line shown above the drawing tools only in the call board:
   load/save icons, transient board name, page controls, presence and the
   "stop sharing" button all live here. */
.board-topbar { display: flex; gap: 6px; padding: 6px 8px; background: #eef1f6; border-bottom: 1px solid var(--border); align-items: center; }
.board-topbar-group { display: inline-flex; align-items: center; gap: 6px; flex: 1 1 0; min-width: 0; }
.board-topbar-l { justify-content: flex-start; }
.board-topbar-r { justify-content: flex-end; flex-wrap: wrap; }
/* Board action buttons share one look whether they sit in the call board's top
   row (.board-topbar), in the toolbar under the canvas (.board-toolbar) or,
   for normal whiteboards, docked in the page header (.board-header-controls).
   Page + zoom controls live only in the toolbar, right-aligned next to each
   other, so no page-control styling is needed in the topbar/header variants. */
.board-topbar .board-x, .board-header-controls .board-x, .board-toolbar .board-x { padding: 4px 8px; border: 1px solid var(--border); background: #fff; border-radius: 4px; cursor: pointer; display: inline-flex; align-items: center; gap: 4px; }
.board-topbar .board-x img, .board-header-controls .board-x img, .board-toolbar .board-x img { width: 16px; height: 16px; }
.board-topbar button:disabled, .board-header-controls button:disabled { opacity: .4; cursor: default; }
.board-topbar .board-x[data-x="cease"], .board-header-controls .board-x[data-x="cease"] { border-color: #c62828; color: #c62828; }
.board-topbar .board-x[data-x="cease"] img, .board-header-controls .board-x[data-x="cease"] img { filter: none; }
.board-topbar .board-x.active, .board-header-controls .board-x.active, .board-toolbar .board-x.active { background: var(--blue, #3b82f6); border-color: var(--blue, #3b82f6); }
.board-topbar .board-x.active img, .board-header-controls .board-x.active img, .board-toolbar .board-x.active img { filter: brightness(0) invert(1); }
.board-toolbar .board-pages { margin-left: auto; }
/* Match the pages control's height to the adjacent zoom widget (~26px): it must
   not inherit the toolbar buttons' taller 44px min-height. The widget also hugs
   its content (never stretches to fill the row) and caps the page counter, so
   it stays compact next to the zoom control instead of ballooning wide. */
.board-toolbar .board-pages { height: 26px; padding: 0 1px; box-sizing: border-box; gap: 1px; flex: 0 0 auto; max-width: 50%; overflow: hidden; }
.board-toolbar .board-pages button { height: 100%; padding: 0 3px; min-width: 0; min-height: 0; border: none; line-height: 1; }
.board-toolbar .board-page-label { font-size: 12px; font-weight: 600; color: var(--text); padding: 0 2px; min-width: 0; max-width: 44px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.board-toolbar-zoom { display: inline-flex; align-items: center; gap: 6px; }
.board-toolbar .board-zoom-label { font-size: 12px; font-weight: 600; color: var(--text); min-width: 34px; text-align: center; }
.board-topbar-name { font-size: 13px; font-weight: 600; color: var(--text); padding: 0 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; flex: 0 1 auto; text-align: left; max-width: 260px; }
/* Normal whiteboard: the page controls sit centered in the outer page header
   via an absolutely positioned middle column, with the back button/title on
   the left and the share/open/rename/delete actions flush right. */
.board-header-controls { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; }
#boardHeader { position: relative; }
#boardHeader .board-header-controls { position: absolute; left: 50%; transform: translateX(-50%); margin-left: 0; }
#boardHeader .chat-actions { margin-left: auto; }
.board-topbar .board-pages, .board-header-controls .board-pages { margin-left: 0; background: #fff; border: 1px solid var(--border); border-radius: 4px; padding: 2px; display: inline-flex; align-items: center; gap: 2px; }
.board-topbar .board-pages button, .board-header-controls .board-pages button { padding: 3px 8px; border: none; background: transparent; cursor: pointer; }
.board-topbar .board-pages button.disabled, .board-header-controls .board-pages button.disabled { opacity: .4; cursor: not-allowed; pointer-events: none; }
.board-topbar .board-zoom-label, .board-header-controls .board-zoom-label { font-size: 12px; font-weight: 600; color: var(--text); min-width: 34px; text-align: center; }
.board-topbar .board-zoom-in, .board-topbar .board-zoom-out, .board-header-controls .board-zoom-in, .board-header-controls .board-zoom-out { cursor: pointer; }
.board-toolbar { display: flex; gap: 6px; padding: 8px; background: #f5f6f8; align-items: center; flex-wrap: wrap; overflow-x: auto; -webkit-overflow-scrolling: touch; }
.board-toolbar button { padding: 6px 10px; border: 1px solid var(--border); background: #fff; border-radius: 4px; cursor: pointer; font-size: 13px; display: inline-flex; align-items: center; gap: 4px; min-height: 44px; }
.board-toolbar button img { width: 16px; height: 16px; }
/* Pages sidebar toggle — compact, height-matched to the pages/zoom widgets. */
.board-toolbar .board-pages-toggle { height: 26px; min-height: 0; padding: 0 2px; border: 1px solid var(--border); background: #fff; border-radius: 4px; cursor: pointer; display: inline-flex; align-items: center; flex: 0 0 auto; box-sizing: border-box; }
.board-toolbar .board-pages-toggle img { width: 16px; height: 16px; }
.board-toolbar .board-pages-toggle.active { background: var(--blue, #3b82f6); border-color: var(--blue, #3b82f6); }
.board-toolbar .board-pages-toggle.active img { filter: brightness(0) invert(1); }
/* The board body lays the resizable pages sidebar against the right edge. */
.board-body { flex: 1; display: flex; flex-direction: row; align-items: stretch; min-height: 0; min-width: 0; position: relative; overflow: hidden; }
.board-body .board-canvas { min-width: 0; flex: 1 1 auto; }
.board-pages-sidebar { flex: 0 0 auto; position: relative; background: #f6f7f9; border-left: 1px solid var(--border); display: flex; flex-direction: column; min-height: 0; min-width: 0; box-sizing: border-box; overflow: hidden; }
.board-pages-resizer { position: absolute; left: 0; top: 0; bottom: 0; width: 6px; cursor: col-resize; user-select: none; touch-action: none; z-index: 3; background: transparent; }
.board-pages-resizer:hover, .board-pages-resizer.rs-active { background: var(--blue, #3b82f6); opacity: .35; }
.board-pages-head { display: flex; align-items: center; justify-content: space-between; gap: 6px; padding: 6px 8px 6px 10px; border-bottom: 1px solid var(--border); font-size: 11px; font-weight: 600; color: var(--muted, #666); text-transform: uppercase; letter-spacing: .04em; }
.board-pages-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.board-pages-close { border: none; background: transparent; cursor: pointer; padding: 2px; display: inline-flex; align-items: center; justify-content: center; color: var(--muted, #7a8194); border-radius: 3px; flex: 0 0 auto; }
.board-pages-close img { width: 12px; height: 12px; }
.board-pages-close:hover { background: rgba(0, 0, 0, .07); }
.board-pages-list { flex: 1; overflow-y: auto; padding: 6px; display: flex; flex-direction: column; gap: 6px; -webkit-overflow-scrolling: touch; }
.board-pages-list.drop-end { box-shadow: inset 0 -2px 0 0 var(--blue, #3b82f6); }
.board-page-item { position: relative; cursor: pointer; user-select: none; -webkit-user-select: none; flex: 0 0 auto; }
.board-page-thumb { display: block; width: 100%; aspect-ratio: 16 / 10; background: #fff; border: 1px solid var(--border, #d6dbe4); border-radius: 4px; box-sizing: border-box; pointer-events: none; }
.board-page-item.active .board-page-thumb { border-color: var(--blue, #3b82f6); box-shadow: 0 0 0 1px var(--blue, #3b82f6); }
.board-page-no { position: absolute; top: 3px; left: 5px; font-size: 10px; font-weight: 600; color: #fff; background: rgba(0, 0, 0, .55); border-radius: 3px; padding: 0 4px; line-height: 1.6; pointer-events: none; }
.board-page-item.dragging { opacity: .35; }
.board-page-item.drop-before .board-page-thumb { box-shadow: 0 -2px 0 0 var(--blue, #3b82f6); }
.board-page-item.drop-after .board-page-thumb { box-shadow: 0 2px 0 0 var(--blue, #3b82f6); }
/* Mobile: board toolbar scrollable horizontally if needed */
@media (max-width: 640px) {
  .board-toolbar { gap: 4px; padding: 6px; }
  .board-toolbar button { padding: 6px 8px; min-height: 44px; min-width: 36px; }
  .board-toolbar button img { width: 18px; height: 18px; }
  .board-toolbar-zoom { display: none; }
}
.board-toolbar button:disabled, .board-toolbar button.disabled-tool { opacity: .35; cursor: not-allowed; pointer-events: none; filter: grayscale(0.6); }
.board-toolbar button.active { background: var(--blue); color: #fff; border-color: var(--blue); }
.board-tool-allow-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; max-height: 260px; overflow-y: auto; }
.board-tool-allow-item { display: flex; align-items: center; gap: 6px; font-size: 13px; padding: 4px 6px; border: 1px solid var(--border); border-radius: 6px; background: #fff; }
.board-remote-ctl { display: flex; flex-direction: column; gap: 6px; }
.board-remote-ctl > label { display: block; font-size: 12px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; margin-top: 6px; }
.board-remote-ctl > label:first-child { margin-top: 0; }
.board-remote-select { width: 100%; padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px; font-size: 13px; background: #fff; color: var(--text); }
.board-remote-style { display: grid; grid-template-columns: 1fr 120px; column-gap: 12px; row-gap: 10px; align-items: center; }
.board-remote-style label { font-size: 12px; color: var(--muted); }
.board-remote-style input[type="range"] { width: 100%; margin: 0; }
.board-remote-style .cp-toggle { width: 30px; height: 26px; }
.board-toolbar .board-x.btn-primary { border-color: var(--blue); }
.board-toolbar select { padding: 5px 6px; border: 1px solid var(--border); border-radius: 4px; font-size: 13px; background: #fff; color: var(--text); }
.board-toolbar .board-color, .board-toolbar .board-fill { width: 30px; height: 26px; padding: 1px; border: 1px solid var(--border); border-radius: 4px; background: #fff; cursor: pointer; }
.board-toolbar label { font-size: 12px; color: var(--muted); }
/* Drawing style controls live on their own bar under the tool row. */
 .board-stylebar { display: flex; gap: 6px; padding: 6px 8px; background: #eef0f4; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); align-items: center; flex-wrap: wrap; overflow-x: auto; -webkit-overflow-scrolling: touch; }
 .board-stylebar button { padding: 4px 8px; border: 1px solid var(--border); background: #fff; border-radius: 4px; cursor: pointer; font-size: 13px; display: inline-flex; align-items: center; gap: 4px; min-height: 44px; }
 .board-stylebar button img { width: 14px; height: 14px; }
 .board-stylebar button:disabled { opacity: .4; cursor: default; }
 .board-stylebar button.active { background: var(--blue-light, #e8f0fe); border-color: var(--blue); }
.board-stylebar .board-undo { margin-left: auto; }
 /* Mobile: board stylebar scrollable */
 @media (max-width: 640px) {
   .board-stylebar { gap: 4px; padding: 4px 6px; }
   .board-stylebar button { padding: 4px 6px; min-height: 44px; min-width: 36px; }
   .board-stylebar button img { width: 16px; height: 16px; }
 }
.board-presence { font-size: 12px; color: var(--muted); background: #eef1f5; padding: 4px 8px; border-radius: 10px; white-space: nowrap; }
.board-presence.has { color: #1a7f37; background: #e6f4ea; }
.board-pages { display: inline-flex; align-items: center; gap: 4px; margin-left: 6px; background: #fff; border: 1px solid var(--border); border-radius: 4px; padding: 2px; }
.board-pages button { padding: 3px 8px; border: none; background: transparent; }
.board-pages button.disabled { opacity: .4; cursor: not-allowed; pointer-events: none; }
.board-pages .board-page-label { font-size: 13px; min-width: 34px; text-align: center; color: var(--text); }
/* Per-author visibility picker (board header "Authors" button). The shell rule
   provides the fixed window so the picker shows up in every theme; theme
   stylesheets are free to restyle surfaces/colors on top. */
.board-authors-window { position: fixed; z-index: 1200; min-width: 230px; max-width: 300px; background: #fff; border: 1px solid var(--border); border-radius: 10px; box-shadow: 0 8px 24px rgba(20, 30, 60, .18); overflow: hidden; font-size: 13px; color: var(--text); }
.board-authors-window .ba-head { display: flex; align-items: center; justify-content: space-between; padding: 8px 10px; background: #f5f7fb; border-bottom: 1px solid var(--border); }
.board-authors-window .ba-title { font-size: 13px; font-weight: 600; color: var(--text); }
.board-authors-window .ba-close { padding: 3px; }
.board-authors-window .ba-close img { width: 12px; height: 12px; opacity: .6; }
.board-authors-window .ba-close:hover img { opacity: 1; }
.board-authors-window .ba-list { max-height: 260px; overflow-y: auto; }
.board-authors-window .ba-item { display: flex; align-items: center; gap: 8px; padding: 7px 10px; cursor: pointer; font-size: 13px; color: var(--text); }
.board-authors-window .ba-item:hover { background: #dfe3ea; }
.board-authors-window .ba-item.active { background: var(--blue-light, #e8f1fe); }
.board-authors-window .ba-dot { width: 22px; height: 22px; border-radius: 50%; background: var(--blue, #3b82f6); color: #fff; font-size: 12px; font-weight: 600; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; }
.board-authors-window .ba-dot.ba-all { background: #64748b; }
.board-authors-window .ba-name { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.board-authors-window .ba-check { width: 15px; height: 15px; border-radius: 50%; border: 2px solid #c3ccdc; flex: 0 0 auto; }
.board-authors-window .ba-item.active .ba-check { border-color: var(--blue, #3b82f6); background: radial-gradient(circle, var(--blue, #3b82f6) 0 4px, #fff 5px); }
.board-authors-window .ba-remote, .board-authors-window .ba-tool-allow { padding: 3px; flex: 0 0 auto; }
.board-authors-window .ba-remote img, .board-authors-window .ba-tool-allow img { width: 14px; height: 14px; opacity: .7; }
.board-authors-window .ba-remote:hover img, .board-authors-window .ba-tool-allow:hover img { opacity: 1; }
 .board-canvas { flex: 1; cursor: crosshair; touch-action: none; min-height: 0; }
 /* Mobile: sidebar toggle button to show/hide participant videos */
 .board-sidebar-toggle {
   display: none; position: fixed; right: 8px; bottom: 80px; z-index: 10;
   width: 44px; height: 44px; border-radius: 50%; border: 1px solid var(--border);
   background: var(--panel); color: var(--text); cursor: pointer;
   align-items: center; justify-content: center; box-shadow: 0 2px 8px rgba(0,0,0,.2);
 }
 .board-sidebar-toggle img { width: 20px; height: 20px; }
 /* Hide participant videos behind sidebar on mobile when board is shared */
  @media (max-width: 768px) {
    .call-overlay.board-shared .call-videos { left: -110%; transition: left .3s ease; }
    .call-overlay.board-shared .call-videos.videos-visible { left: 10px; }
    .board-sidebar-toggle { display: flex; }
  }
  /* Mobile: prevent any overflow in call overlay */
  .call-overlay { overflow: hidden; }
  .call-bar { gap: 6px; padding: 8px 10px; }
  .call-btn { width: 40px; height: 40px; }
  .call-btn img { width: 18px; height: 18px; }
  /* Mobile: board toolbar wraps more compactly, buttons stay tappable */
  .board-toolbar { gap: 4px; padding: 6px; overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .board-toolbar button { padding: 6px 8px; min-width: 36px; min-height: 36px; }
  .board-stylebar button { min-height: 36px; }
  .board-toolbar button img { width: 18px; height: 18px; }
  @media (max-width: 820px) {
    /* Mobile: context menus are full-width sheets for easy access */
    .ctx-menu { position: fixed !important; bottom: 0; left: 0; right: 0; top: auto !important;
      max-height: 70vh; overflow-y: auto; border-radius: 12px 12px 0 0 !important;
      margin: 0 !important; min-width: 0 !important; }
    .cm-item { padding: 12px 16px !important; min-height: 44px; }
    .cm-item .cm-ic { width: 20px; height: 20px; }
    /* Mobile submenus expand inline below the parent item */
    .ctx-menu .cm-sub { position: relative !important; left: auto !important; top: auto !important;
      border-radius: 0 0 12px 12px; margin-left: 16px; }
    /* Mobile: board style popups should not overflow */
    .board-styles { left: 8px; right: 8px; min-width: 0; max-height: 50vh; overflow-y: auto; }
    .board-styles .cp-popup, .board-remote-ctl .cp-popup { left: 8px; right: 8px; min-width: 0; }
  }
   /* Mobile: ensure the text editor doesn't overflow */
  .board-text-edit { font-size: 16px; max-width: calc(100% - 16px); }
  /* Mobile: improve touch targets across the app */
  .board-opt { min-height: 44px; }
  .board-opt-pop { padding: 12px; }
  /* Mobile: prevent the video sidebar from blocking content */
  .call-overlay.board-shared .call-sidebar { width: 220px; }
  /* Mobile: board canvas needs proper sizing */
  .board-canvas { touch-action: none; overscroll-behavior: contain; }
  .board-wrap { overflow: hidden; }
  /* Mobile: ensure call video tiles don't overflow the grid */
  .call-video { max-width: 100%; box-sizing: border-box; }
  /* Mobile: break-out banner and panel adjustments */
  .breakout-banner { max-width: calc(100% - 28px); font-size: 11px; }
  .breakout-panel { width: calc(100% - 16px); left: 8px; right: 8px; }
  /* Mobile: ensure call-toasts don't overflow */
  .call-toasts { max-width: calc(100% - 28px); }
  /* Mobile: board hint should not block content */
  .board-hint { font-size: 11px; bottom: 4px; }
  /* Mobile: improve zoom label readability */
  .board-zoom-label { font-size: 11px; }
  /* Mobile: stylebar should be scrollable horizontally */
  .board-stylebar { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  /* Mobile: ensure context menu items are tappable */
  .call-sidebar-list .cs-row { min-height: 44px; }
  /* Mobile: prevent member sidebar from overflowing */
  .member-sidebar { max-width: 100%; }
  /* Mobile: board canvas touch-action: none prevents scroll during drawing */
  .board-canvas { -webkit-overflow-scrolling: touch; }
  /* In-place whiteboard text editor (contenteditable overlay) — sits exactly
     where the caption will render and inherits its zoomed size/color. */
 .board-text-edit {
  position: absolute; z-index: 30; background: rgba(255,255,255,.35);
  outline: 1px dashed #2f6fed; outline-offset: 3px; padding: 0;
  font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
  line-height: 1.25; white-space: pre-wrap; word-break: break-word;
  box-sizing: content-box; min-height: 1.6em;
}
.board-text-edit:focus { background: rgba(255,255,255,.6); outline-color: #2f6fed; }
/* Item 37 (TODO 14): muted desktop-only hint shown while a drawing tool is active. */
.board-hint { position: absolute; left: 8px; bottom: 8px; font-size: 12px; color: var(--muted); pointer-events: none; z-index: 5; }
.board-hint.hidden { display: none; }
/* Item 12: whiteboard style options live in a popup panel opened by the style button. */
.board-styles { position: absolute; top: 48px; left: 12px; z-index: 20; display: none; flex-direction: column; gap: 9px; background: #fff; border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 6px 20px rgba(0,0,0,.14); padding: 12px; min-width: 230px; }
.board-styles.show { display: flex; }
.board-styles .bs-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.board-styles .bs-label { font-size: 12px; color: var(--muted); min-width: 96px; }
.board-styles .board-color, .board-styles .board-fill { width: 34px; height: 26px; padding: 1px; border: 1px solid var(--border); border-radius: 4px; background: #fff; cursor: pointer; }
.board-styles select { padding: 5px 6px; border: 1px solid var(--border); border-radius: 4px; font-size: 13px; background: #fff; color: var(--text); }
.board-styles .board-width, .board-styles .board-opacity { width: 90px; flex: 0 1 auto; margin: 0; }
.board-styles .bs-value { min-width: 34px; text-align: right; font-size: 12px; color: var(--text); }
/* Shape fill mode: how rectangles/ellipses are drawn (border / border+fill / fill). */
.board-styles .bs-fillmode { display: inline-flex; gap: 4px; }
.board-styles .fm-btn { width: 28px; height: 24px; padding: 3px; display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--border); border-radius: 4px; background: #fff; cursor: pointer; }
.board-styles .fm-btn.active { border-color: var(--blue); background: rgba(47,111,237,.08); }
.board-styles .fm-ico { width: 14px; height: 14px; box-sizing: border-box; display: block; }
.board-styles .fm-border { border: 2px solid #4a4a4a; background: transparent; }
.board-styles .fm-both { border: 2px solid #8a8a8a; background: #4a4a4a; }
.board-styles .fm-fill { border: 2px solid #4a4a4a; background: #4a4a4a; }
/* Base color-selector widget: swatch button that opens a popup with the native
   picker and quick preset swatches. Shared by the stroke-color and fill-color rows. */
.board-styles .color-pick, .board-remote-ctl .color-pick { position: relative; display: inline-flex; }
.board-styles .cp-toggle, .board-remote-ctl .cp-toggle { width: 26px; height: 26px; padding: 2px; border: 1px solid var(--border); border-radius: 4px; background-color: #fff; box-shadow: inset 0 0 0 1px rgba(0,0,0,.06); cursor: pointer; }
.board-styles .cp-toggle.active, .board-remote-ctl .cp-toggle.active { border-color: var(--blue); }
 .board-styles .cp-popup, .board-remote-ctl .cp-popup { position: absolute; top: calc(100% + 6px); left: 0; right: auto; z-index: 30; display: none; flex-direction: column; gap: 8px; background: #fff; border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 6px 20px rgba(0,0,0,.16); padding: 10px; min-width: 230px; }
 /* In the remote-control dialog the swatch sits in the right grid column;
    anchor its popup to the swatch's right edge so it stays inside the card.
    These come after the earlier (max-width: 820px) mobile block, so both the
    desktop right-anchor and the mobile full-width stretch are re-asserted here. */
  @media (min-width: 821px) {
    .board-remote-ctl .cp-popup { left: auto; right: 0; }
  }
  @media (max-width: 820px) {
    .board-remote-ctl .cp-popup { left: 8px; right: 8px; min-width: 0; }
  }
 .board-styles .cp-popup.open, .board-remote-ctl .cp-popup.open { display: flex; }
.board-styles .cp-native, .board-remote-ctl .cp-native { display: none; }
.board-styles .cp-body, .board-remote-ctl .cp-body { display: flex; flex-direction: column; gap: 8px; }
.board-styles .cp-sv, .board-remote-ctl .cp-sv { width: 100%; height: 110px; border: 1px solid var(--border); border-radius: 6px; cursor: crosshair; touch-action: none; display: block; }
.board-styles .cp-hue, .board-remote-ctl .cp-hue { width: 100%; height: 14px; border: 1px solid var(--border); border-radius: 7px; cursor: pointer; touch-action: none; display: block; }
.board-styles .cp-presets, .board-remote-ctl .cp-presets { display: grid; grid-template-columns: repeat(7, 1fr); gap: 5px; border-top: 1px solid var(--border); padding-top: 8px; }
.board-styles .cp-swatch, .board-remote-ctl .cp-swatch { width: 100%; height: 20px; padding: 0; border: 1px solid rgba(0,0,0,.18); border-radius: 4px; cursor: pointer; }
.board-styles .cp-swatch.active, .board-remote-ctl .cp-swatch.active { outline: 2px solid var(--blue); outline-offset: 1px; }
.board-styles .cp-swatch.cp-none, .board-remote-ctl .cp-swatch.cp-none { background: repeating-linear-gradient(45deg, #fff 0 3px, #e3e6ea 3px 6px); border-style: dashed; }
/* Per-option toolbar buttons (drawing style controls inline on the toolbar). */
.board-opt { position: relative; padding: 3px 6px !important; min-width: 0; gap: 4px; }
.board-opt.active { background: var(--blue-light); border-color: var(--blue) !important; }
.board-opt .opt-ico { width: 14px; height: 14px; }
.board-opt .opt-swatch { display: inline-block; width: 14px; height: 14px; border: 1px solid var(--border); border-radius: 3px; background: transparent; vertical-align: middle; }
.board-opt .opt-swatch-fill { background: repeating-linear-gradient(45deg, #fff 0 2px, #e3e6ea 2px 4px); }
.board-opt .opt-swatch-fill.opt-none { background: repeating-linear-gradient(45deg, #fff 0 2px, #e3e6ea 2px 4px); border-style: dashed; }
.board-opt .opt-bar { vertical-align: middle; image-rendering: pixelated; }
.board-opt .opt-tsize { font-size: 13px; font-weight: 600; line-height: 1; display: inline-flex; align-items: baseline; gap: 2px; vertical-align: middle; }
.board-opt .opt-tsize-val { font-size: 10px; font-weight: 400; }
.board-opt .fm-ico { width: 14px; height: 14px; vertical-align: middle; }
 /* Per-option popup panels (compact, single-row controls inside board-wrap). */
 .board-opt-pop { position: absolute; }
 .board-opt-pop.board-styles { min-width: 200px; padding: 10px; border-radius: 8px; }
.board-opt-pop.board-styles .bs-row { justify-content: flex-start; gap: 8px; }
.board-opt-pop.board-styles .bs-label { display: none; }
.board-opt-pop.board-styles .board-width,
.board-opt-pop.board-styles .board-textsize,
.board-opt-pop.board-styles .board-opacity { width: 110px; }
/* Full-view image viewer (click an inline chat image): dark overlay, top
   action bar, and an optional annotate mode overlaid on the image. */
.chat-media-img { cursor: zoom-in; }
.imgview { position: fixed; inset: 0; z-index: 900; background: rgba(8,10,16,.82); display: flex; flex-direction: column; }
.imgview-top { display: flex; align-items: center; gap: 10px; padding: 10px 14px; color: #fff; }
.imgview-name { font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.imgview-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.iv-btn { padding: 6px 12px; border: 1px solid rgba(255,255,255,.28); border-radius: 6px; background: rgba(255,255,255,.1); color: #fff; font-size: 13px; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; }
.iv-btn:hover { background: rgba(255,255,255,.2); }
.iv-btn.iv-annotate.active { background: var(--blue); border-color: var(--blue); }
.iv-btn.iv-close { padding: 6px 8px; }
.iv-btn.iv-close img { width: 14px; height: 14px; filter: brightness(0) invert(1); }
.imgview-stage { flex: 1; display: flex; align-items: center; justify-content: center; min-height: 0; padding: 24px 28px 30px; position: relative; }
.iv-loading { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; z-index: 4; }
.iv-loading-spin { width: 42px; height: 42px; border-radius: 50%; border: 4px solid rgba(255,255,255,.18); border-top-color: #fff; animation: callspin .9s linear infinite; }
.imgview-frame { position: relative; display: inline-block; min-width: 0; min-height: 0; max-width: 100%; max-height: 100%; }
.imgview-frame img, .imgview-frame canvas { max-width: 100%; max-height: 100%; display: block; }
.imgview-img { max-width: 100%; max-height: 100%; width: auto; height: auto; border-radius: 4px; box-shadow: 0 10px 40px rgba(0,0,0,.5); }
.iv-canvas { position: absolute; inset: 0; width: 100%; height: 100%; touch-action: none; cursor: crosshair; display: none; }
.iv-tools { position: absolute; top: 10px; left: 50%; transform: translateX(-50%); z-index: 10; display: inline-flex; align-items: center; gap: 6px; background: rgba(255,255,255,.96); border: 1px solid var(--border); border-radius: 10px; box-shadow: 0 6px 24px rgba(0,0,0,.25); padding: 6px; white-space: nowrap; }
.iv-tool { width: 30px; height: 30px; padding: 5px; border: 1px solid transparent; border-radius: 6px; background: transparent; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; position: relative; }
.iv-tool.active { background: var(--blue-light); border-color: var(--blue); }
.iv-tool img { width: 16px; height: 16px; }
.iv-tool:disabled { opacity: .4; cursor: default; }
.iv-swatch { width: 22px; height: 22px; border: 1px solid var(--border); border-radius: 6px; box-shadow: inset 0 0 0 1px rgba(0,0,0,.08); cursor: pointer; }
.iv-width { border: 1px solid var(--border); border-radius: 6px; background: #fff; cursor: pointer; padding: 2px 4px; display: inline-flex; align-items: center; }
.iv-pop { position: absolute; top: calc(100% + 8px); left: 0; z-index: 15; display: inline-flex; gap: 4px; flex-wrap: wrap; background: #fff; border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 6px 20px rgba(0,0,0,.2); padding: 8px; max-width: 230px; }
.iv-colorpop { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; width: 196px; max-width: none; }
.iv-csw { height: 20px; border: 1px solid rgba(0,0,0,.2); border-radius: 4px; cursor: pointer; }
.iv-cnative { width: 100%; grid-column: 1 / -1; height: 26px; padding: 0; border: 1px solid var(--border); border-radius: 4px; background: #fff; cursor: pointer; }
.iv-widthpop { align-items: center; gap: 8px; }
.iv-wrange { width: 110px; }
.iv-wval { font-size: 12px; font-weight: 600; min-width: 18px; text-align: right; }
.iv-tools.hidden, .iv-pop.hidden, .iv-send.hidden { display: none; }
.iv-btn.iv-send[disabled] { opacity: .45; cursor: default; }
.iv-btn.iv-send[disabled]:hover { background: rgba(255,255,255,.1); }
/* Main-area board container (normal whiteboards) */
.board-container { flex: 1; display: flex; flex-direction: column; min-height: 0; min-width: 0; }
/* Ephemeral whiteboard shared inside a live call: covers the video area but
   stays beneath the call control bar so call controls remain usable. */
.call-whiteboard { position: absolute; top: 0; left: 0; right: 0; bottom: 0; z-index: 5; background: #fff; display: flex; flex-direction: column; }

/* When a call whiteboard is shared, keep the participant videos visible as a
   compact strip at the bottom, above the control bar, instead of the full-size
   grid that the board covers underneath. */
.call-overlay.board-shared .call-videos {
  position: absolute; left: 10px; right: 10px; bottom: 84px; z-index: 6;
  display: flex; flex-wrap: nowrap; gap: 6px; align-items: flex-end;
  justify-content: center; overflow-x: auto; padding: 4px;
  pointer-events: auto; margin: 0;
}
.call-overlay.board-shared .call-video {
  flex: 0 0 auto; width: 128px; min-width: 128px; max-width: 128px; height: 74px; min-height: 74px;
  box-shadow: 0 2px 10px rgba(0,0,0,.35);
}
.call-overlay.board-shared .call-video .cv-name { font-size: 11px; padding: 1px 6px; }
.call-overlay.board-shared .call-video .cv-quality { top: 4px; left: 4px; font-size: 10px; padding: 0 6px; }
.call-overlay.board-shared .call-video .cv-camoff { width: 32px; height: 32px; }
.call-overlay.board-shared .call-video .cv-camoff img { width: 20px; height: 20px; }
.call-overlay.board-shared .call-video .cv-loading-spin { width: 22px; height: 22px; border-width: 3px; }
.call-overlay.board-shared .call-video .cv-loading-txt { display: none; }
/* Keep the call controls anchored at the bottom above the shared board: the
   bar is an opaque, flex-last child with a high z-index so the board (which
   fills the overlay behind it) can never push it away or hide it, even when
   the buttons wrap onto several lines. */
.call-overlay.board-shared .call-bar { position: relative; z-index: 8; margin-top: auto; flex: 0 0 auto; }
/* The call participants sidebar sits below the board topbar + toolbar so the
   two toolbars remain fully visible.  An approximate offset (topbar ≈ 42px +
   toolbar ≈ 50px) covers both at all reasonable sizes. */
.call-overlay.board-shared .call-sidebar { top: 92px; }

/* Click-to-focus: one stream fills the overlay, the rest are hidden; clicking
   the focused stream again restores the normal grid. position:fixed positions
   the focused tile against the viewport, NOT against #callVideos — which in
   board/screen-shared layouts is an absolute strip whose height collapses (an
   absolute tile focused from inside it would collapse to a sliver). These rules
   live AFTER the screen/board-shared blocks on purpose: with equal specificity
   the later source wins, so a focused strip tile is never re-sized to the small
   strip dimensions by a shared-layout rule. */
.call-video { cursor: pointer; }
.call-overlay.focus-mode { padding: 0; }
.call-overlay.focus-mode .call-video { display: none; }
.call-overlay.focus-mode .call-video.focused {
  display: block; position: fixed; left: 0; top: 0; right: 0; bottom: 0;
  width: auto; max-width: none; height: auto; min-width: 0;
  min-height: 0; border: 0; border-radius: 0; box-shadow: none; background: none;
  z-index: 9;
}
.call-overlay.focus-mode .call-bar { position: relative; z-index: 10; margin-top: auto; flex: 0 0 auto; }
/* The screen-share participant strip centres itself with translateX/Y centering
   transforms. A transformed ancestor becomes the containing block for position:
   fixed, so a focused tile inside the strip would resolve against the strip
   (narrow, off-centre) instead of the viewport. In focus mode the strip is
   hidden anyway, so dropping the transform is harmless and the focused tile
   spans the full stage as intended. */
.call-overlay.focus-mode .call-videos { transform: none; }
/* Close button on the focused tile: a prominent X that restores the grid. */
.cv-close-focus { display: none; }
.call-overlay.focus-mode .call-video.focused .cv-close-focus {
  display: flex; position: absolute; top: 14px; right: 14px; z-index: 5;
  width: 40px; height: 40px; border-radius: 50%; cursor: pointer;
  align-items: center; justify-content: center; background: rgba(0,0,0,.6);
  border: 1px solid rgba(255,255,255,.35); box-shadow: 0 2px 10px rgba(0,0,0,.4);
}
.call-overlay.focus-mode .call-video.focused .cv-close-focus:hover { background: rgba(0,0,0,.85); transform: scale(1.05); }
.call-overlay.focus-mode .call-video.focused .cv-close-focus img { width: 20px; height: 20px; display: block; }
[data-theme="theseus3k"] .call-overlay.focus-mode .call-video.focused .cv-close-focus img { filter: brightness(0) invert(1); }
/* Focus mode shows just the stream: every tile decoration (name, quality,
   media-state icons, camera-off placeholder, loading spinner, pin badge) is
   hidden so the stage is a clean full-bleed video plus the close button. */
.call-overlay.focus-mode .call-video.focused .cv-name,
.call-overlay.focus-mode .call-video.focused .cv-quality,
.call-overlay.focus-mode .call-video.focused .cv-media-state,
.call-overlay.focus-mode .call-video.focused .cv-camoff,
.call-overlay.focus-mode .call-video.focused .cv-loading,
.call-overlay.focus-mode .call-video.focused .cv-pin { display: none; }

/* Moderator "pin": on non-moderator viewers the pinned participant's camera
   tile is shown enlarged by default (teacher-focused layout) with everyone else
   placed in a single row beneath it. This is deliberately NOT focus mode —
   clicking any tile still triggers the normal focus overlay, and screen/board
   shares keep taking the full stage. The rules are scoped away from those
   layouts so the pin grid never fights them; the pinned-mode class on the
   overlay simply re-asserts itself when the stage is freed again. */
.call-video .cv-pin {
  position: absolute; top: 30px; left: 8px; z-index: 3;
  width: 20px; height: 20px; border-radius: 50%;
  background: rgba(0,0,0,.55); display: flex;
  align-items: center; justify-content: center; pointer-events: none;
}
.call-video .cv-pin img { width: 14px; height: 14px; filter: brightness(0) invert(1); display: block; }
.call-overlay:not(.screen-shared):not(.board-shared):not(.focus-mode).pinned-mode .call-videos {
  display: flex; flex-direction: column; gap: 12px;
}
/* align-self: stretch is load-bearing, not cosmetic. The base .call-video rule
   sets align-self: center for grid tiles; inside the flex .pin-row that leaves
   the enlarged tile at its content height, so the video (height: 100%) resolves
   against an indefinite parent, the tile collapses to roughly the stream's
   intrinsic pixel height, and it overflows the row — where #callVideos'
   overflow: hidden crops the frame exactly like object-fit: cover. Stretching
   makes the tile take the row's height; object-fit: contain then letterboxes
   instead of cutting the picture. */
.call-overlay:not(.screen-shared):not(.board-shared):not(.focus-mode).pinned-mode .call-video.pinned {
  flex: 1 1 auto; min-width: 0; min-height: 0; width: 100%; max-width: 100%;
  max-height: 100%; aspect-ratio: auto; align-self: stretch;
}
.call-overlay:not(.screen-shared):not(.board-shared):not(.focus-mode).pinned-mode .call-video.pinned video { object-fit: contain; }
/* Up to two pinned tiles live in a .pin-row (see applyPinLayout): a horizontal
   band that shares the stage with the .pin-strip beneath it. A single pinned
   tile fills the whole row exactly like the classic layout did. */
.call-overlay:not(.screen-shared):not(.board-shared):not(.focus-mode).pinned-mode .pin-row {
  flex: 1 1 auto; min-height: 0; display: flex; flex-direction: row;
  flex-wrap: nowrap; gap: 12px; min-width: 0; max-height: 100%;
}
.call-overlay:not(.screen-shared):not(.board-shared):not(.focus-mode).pinned-mode .pin-row .call-video.pinned {
  flex: 1 1 0; min-width: 0; min-height: 0; width: 100%; max-width: 100%;
  max-height: 100%; aspect-ratio: auto; align-self: stretch;
}
.call-overlay:not(.screen-shared):not(.board-shared):not(.focus-mode).pinned-mode .pin-row .call-video.pinned video { object-fit: contain; }
@media (max-width: 920px) {
  .call-overlay:not(.screen-shared):not(.board-shared):not(.focus-mode).pinned-mode .pin-row {
    flex-direction: column;
  }
}
/* Everyone else is wrapped in a .pin-strip row (see applyPinLayout) so the whole
   strip under the pinned tile centres as one unit — a flex row, not grid tracks
   that could leave the band offset on wide stages. */
.call-overlay:not(.screen-shared):not(.board-shared):not(.focus-mode).pinned-mode .pin-strip {
  flex: 0 0 auto; display: flex; flex-wrap: wrap; justify-content: center;
  align-content: center; gap: 12px; min-width: 0;
}
.call-overlay:not(.screen-shared):not(.board-shared):not(.focus-mode).pinned-mode .pin-strip .call-video {
  flex: 0 1 165px; min-width: 0; max-width: 165px; height: auto; max-height: 110px;
}
@media (max-width: 920px) {
  .call-overlay:not(.screen-shared):not(.board-shared):not(.focus-mode).pinned-mode .pin-strip .call-video {
    flex: 0 1 105px; max-width: 105px;
  }
}
/* Small strip tiles sit tighter against their edges: the normal 6–8px offsets
   were sized for big tiles and look detached on a ~165px-wide form. */
.call-overlay.pinned-mode .pin-strip .call-video .cv-name { bottom: 2px; left: 3px; padding: 1px 5px; font-size: 10px; }
.call-overlay.pinned-mode .pin-strip .call-video .cv-quality { top: 2px; left: 3px; }
.call-overlay.pinned-mode .pin-strip .call-video .cv-media-state { bottom: 2px; right: 3px; gap: 2px; }
.call-overlay.pinned-mode .pin-strip .call-video .cv-media-state .cv-state-ic { width: 16px; height: 16px; padding: 1px; }
.call-overlay.pinned-mode .pin-strip .call-video .cv-camoff { width: 32px; height: 32px; }
.call-overlay.pinned-mode .pin-strip .call-video .cv-camoff img { width: 20px; height: 20px; }
.call-overlay.pinned-mode .pin-strip .call-video .cv-loading-spin { width: 22px; height: 22px; }
.call-overlay.pinned-mode .pin-strip .call-video .cv-loading-txt { display: none; }
.call-overlay.pinned-mode .pin-strip .call-video .hand-reaction { top: 3px; right: 3px; }
.call-overlay.pinned-mode .pin-strip .call-video .cv-pin { top: 18px; left: 3px; }

/* Admin */
.admin-404 { text-align: center; padding-top: 60px; color: var(--muted); }

/* Admin dashboard: loud Mediasoup SFU availability banner. Mesh is only ever a
   fallback, so an SFU that is unconfigured or down deserves a prominent
   callout right on the dashboard rather than a quiet log line. */
#adminMain .sfu-alert { border-radius: 10px; padding: 14px 16px; margin-bottom: 14px; font-size: 13px; line-height: 1.5; border: 1px solid; }
#adminMain .sfu-alert b { display: block; margin-bottom: 3px; font-size: 15px; }
#adminMain .sfu-alert.sfu-down { background: #fdecea; border-color: #e57373; color: #7f1d1d; }
#adminMain .sfu-alert.sfu-warn { background: #fff8e1; border-color: #f2c94c; color: #6b5300; }
#adminMain .sfu-alert.sfu-ok { background: #e8f5e9; border-color: #81c784; color: #1b5e20; }
@media (max-width: 640px) {
  #adminMain .sfu-alert { font-size: 12px; }
}

/* Scrollbars */
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-thumb { background: #b0b5bc; border-radius: 6px; }
::-webkit-scrollbar-thumb:hover { background: #8a9099; }


/* ---- Scrollbars ---- */
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-thumb { background: rgba(15,23,42,.22); border-radius: 6px; }
::-webkit-scrollbar-thumb:hover { background: rgba(15,23,42,.34); }

/* ==============================================================
   MOBILE FULL-SCREEN SIDEBAR — usability pass for the chat list
   (kept after the theme block so these win over the glossy rules)
   ============================================================== */
@media (max-width: 820px) {
  /* Bigger, centered logo in the full-screen chat list head. */
  .sidebar-head .brand { justify-content: center; }
  .sidebar-head .brand img { max-height: 5rem; max-width: 100%; }

  /* The drawer-toggle hides on mobile: the chat list IS the home screen. */
  .menu-btn { display: none !important; }

  /* Fatter touch targets for the new-chat/folder action row. */
  .folders-bar { gap: 4px; padding: 6px 8px; }
  .folders-bar .icon-btn { padding: 6px; }
  .folders-bar .icon-btn img { width: 16px; height: 16px; }

  /* Bigger folder rows so headers are easy to tap. */
  .cf-head { padding: 12px 10px; }
  .cf-caret { font-size: 12px; width: 12px; }
  .cf-icon { width: 20px; height: 20px; }
  .cf-name { font-size: 14px; }
  .cf-count { font-size: 12px; padding: 2px 8px; }

  /* Active chat item keeps the plain look: no bubble tail, no blue fill. */
  .chat-item.active::after { display: none; }
  .chat-item.active,
  .chat-item.active:hover { background: transparent; box-shadow: none; }
  .chat-item.active .ci-name,
  .chat-item.active .ci-meta,
  .chat-item.active .ci-last { color: var(--text); text-shadow: none; }
  .chat-item.active .ci-icon { background: #cfd4dc; }
  /* The classic theme's active-row rules are attribute-scoped (higher
     specificity), so repeat the mobile plain-look reset for theseus2k. */
  [data-theme="theseus2k"] .chat-item.active::after { display: none; }
  [data-theme="theseus2k"] .chat-item.active,
  [data-theme="theseus2k"] .chat-item.active:hover { background: transparent; box-shadow: none; }
  [data-theme="theseus2k"] .chat-item.active .ci-name,
  [data-theme="theseus2k"] .chat-item.active .ci-meta,
  [data-theme="theseus2k"] .chat-item.active .ci-last { color: var(--text); text-shadow: none; }
  [data-theme="theseus2k"] .chat-item.active .ci-icon { background: #cfd4dc; }

  /* The participants sidebar becomes a whole-screen sheet on mobile (the chat
     list already spawns a full-screen home view): it slides in from the right
     and shows a back button in its head. `.hidden` turns into the off-canvas
     translate state so the slide transition still runs (display:none cannot).
     Above the theme rules' specificity so the sheet background stays opaque. */
  .member-sidebar.member-sidebar {
    position: fixed; top: 0; right: 0; bottom: 0; left: 0;
    width: 100% !important; max-width: 100%; min-width: 0;
    z-index: 45; display: flex; flex-direction: column;
    background: var(--bg); padding: 0; overflow: hidden;
    border-left: none;
    transform: translateX(0); transition: transform .25s ease;
  }
  .member-sidebar.hidden { display: flex; transform: translateX(100%); }
  .member-sidebar-head .ms-back { display: inline-flex; }
  .member-sidebar .member-sidebar-head {
    flex: 0 0 auto; padding: 12px 14px; margin-bottom: 0;
    border-bottom: 1px solid var(--border); background: var(--panel);
  }
  .member-sidebar-head { justify-content: flex-start; }
   .member-sidebar-head .ms-add-link { margin-left: auto; }
   .member-sidebar .member-sidebar-list { flex: 1; overflow-y: auto; padding: 12px 10px; }
   .member-sidebar .ms-resizer { display: none; }
   .board-pages-sidebar .board-pages-resizer { display: none; }
}

/* ===== Settings page layout improvements ===== */
.admin-card {
   background: var(--panel);
   border: 1px solid var(--border);
   border-radius: 10px;
   padding: 22px 24px;
   margin-bottom: 14px;
   max-width: 820px;
}
.admin-card h3 {
   margin: 0 0 16px;
   font-size: 115%;
   color: var(--text);
}
.admin-card > label {
   display: block;
   font-size: 11px;
   color: var(--muted);
   text-transform: uppercase;
   letter-spacing: .05em;
   margin: 16px 0 5px;
}
.admin-card > label:first-child { margin-top: 0; }
.admin-card input[type=text],
.admin-card input[type=password],
.admin-card select {
   width: 100%;
   max-width: 360px;
   padding: 9px 12px;
   border: 1px solid var(--border);
   border-radius: 6px;
   font-size: 14px;
   background: var(--panel);
   color: var(--text);
   box-sizing: border-box;
}
.admin-card input[type=text]:focus,
.admin-card input[type=password]:focus,
.admin-card select:focus {
   outline: none;
   border-color: var(--blue);
   box-shadow: 0 0 0 3px rgba(59,125,216,.15);
}
.admin-card input[type=password] { max-width: 300px; }
.admin-card .field-lock { max-width: 360px; }
.admin-card .field-lock input { width: 100%; }
/* Cap the wrapper to the password field it decorates (that field's input is
   already capped at 300px just above); otherwise the block wrapper spans the
   whole card and the absolutely-positioned eye floats past the field's right
   edge. The padding override beats the compact settings/admin rules further
   down. */
#settingsMain .admin-card .pw-wrap,
#adminMain .admin-card .pw-wrap { max-width: 300px; }
#settingsMain .admin-card .pw-wrap input[type=password],
#adminMain .admin-card .pw-wrap input[type=password] { padding-right: 38px; }

/* Settings-avatar row: circular preview plus vertical button stack */
.settings-avatar-row {
   display: flex;
   align-items: center;
   gap: 16px;
   margin: 4px 0 8px;
}
.settings-avatar-row img.settings-avatar-preview {
   width: 64px; height: 64px;
   border-radius: 50%;
   object-fit: cover;
   border: 2px solid var(--border);
   flex-shrink: 0;
}
.settings-avatar-placeholder {
   width: 64px; height: 64px;
   border-radius: 50%;
   background: var(--bg);
   border: 2px solid var(--border);
   display: flex; align-items: center; justify-content: center;
   font-size: 22px; color: var(--muted);
   flex-shrink: 0;
}
.settings-avatar-row > div { display: flex; flex-direction: column; gap: 6px; }

/* Device settings preview */
.dev-preview {
   border: 1px solid var(--border);
   border-radius: 8px;
   padding: 12px;
   background: #fafbfd;
   margin-bottom: 12px;
}
.dev-cam {
   position: relative;
   width: 100%;
   aspect-ratio: 16 / 9;
   background: #101419;
   border-radius: 6px;
   overflow: hidden;
}
.dev-cam video {
   width: 100%; height: 100%;
   object-fit: cover;
}
.dev-cam-empty {
   position: absolute; inset: 0;
   display: flex; align-items: center; justify-content: center;
   color: #79818b; font-size: 13px; pointer-events: none;
}
.dev-row { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
.dev-level {
   position: relative; flex: 1; height: 10px;
   background: #e9edf3; border-radius: 5px; overflow: hidden; border: 1px solid var(--border);
}
.dev-level-body {
   height: 100%; width: 0%;
   background: linear-gradient(90deg, #2f6fed, #19c37d);
   border-radius: 5px; transition: width 60ms linear;
}

/* Theme picker */
.admin-card .theme-pick { display: flex; align-items: center; gap: 10px; }
.admin-card .theme-pick select { flex: 1; max-width: 280px; }
.admin-card .theme-pick .theme-preview { margin-left: auto; white-space: nowrap; }

/* Save row */
.settings-save-row {
   display: flex; gap: 8px; justify-content: flex-end;
   max-width: 820px; margin-top: 8px; padding-top: 16px;
   border-top: 1px solid var(--border);
}
.settings-save-row .btn { min-width: 100px; }

/* Section divider within cards */
.admin-card .set-sect { margin-top: 18px; border-top: 1px solid var(--border); padding-top: 14px; }
.admin-card .set-sect:first-child { margin-top: 0; border-top: none; padding-top: 0; }

/* Hint text */
.admin-card .hint { font-size: 12px; color: var(--muted); margin: 6px 0 0; }

/* Devices tab: stacked select layout */
.dev-selects { display: flex; flex-direction: column; gap: 12px; max-width: 400px; }
.dev-selects label { display: block; font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; margin: 0 0 3px; }
.dev-selects select { width: 100%; padding: 9px 12px; border: 1px solid var(--border); border-radius: 6px; font-size: 14px; background: var(--panel); color: var(--text); box-sizing: border-box; }

/* Admin main content area */
.admin-main {
   flex: 1;
   padding: 28px 32px;
   overflow-x: auto;
   background: var(--bg);
}
@media (max-width: 820px) {
   .admin-main { padding: 20px 16px; padding-top: 62px; }
   .admin-card { padding: 16px; }
   .settings-save-row { justify-content: stretch; }
   .settings-save-row .btn { flex: 1; }
}

/* Settings + admin wide-screen forms
   The settings page and the admin panel share the .admin-card look. On wide
   screens the cards use the available width; the admin panel groups fields
   into a responsive .set-grid, the settings Devices pane puts the camera
   preview next to the selects, and every form control is kept compact. */
#adminMain .set-field > label {
   display: block;
   font-size: 12px;
   color: var(--muted);
   text-transform: uppercase;
   letter-spacing: .05em;
   margin: 14px 0 4px;
}

/* Compact dropdowns / inputs on the settings + admin forms. */
#settingsMain .admin-card input[type=text],
#settingsMain .admin-card input[type=password],
#settingsMain .admin-card select,
#adminMain .admin-card input[type=text],
#adminMain .admin-card input[type=password],
#adminMain .admin-card select {
   padding: 7px 10px;
   font-size: 13px;
}

@media (min-width: 1080px) {
   #settingsMain .settings-tab-pane,
   #adminMain .admin-card {
      max-width: 1120px;
      margin-left: auto;
      margin-right: auto;
   }
   #settingsMain .settings-save-row {
      max-width: 1120px;
      margin-left: auto;
      margin-right: auto;
   }
   #adminMain .set-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
      gap: 0 32px;
      align-items: start;
   }
   #adminMain .set-grid > * { min-width: 0; }
   #adminMain .set-grid > .set-hint { grid-column: 1 / -1; }
   #settingsMain .dev-grid {
      display: grid;
      grid-template-columns: minmax(0, 5fr) minmax(0, 4fr);
      gap: 0 28px;
      align-items: start;
   }
   #settingsMain .dev-selects { max-width: none; }
}

/* Admin app-settings fine-tuning. The save row lives inside the card, so it
   must span the card content width instead of the .settings-save-row cap. */
#adminMain .settings-save-row { max-width: none; }

/* Interface tab: one full-width row per option, checkbox beside its select. */
#adminMain .set-pane[data-set-pane=iface] .set-grid {
   display: flex;
   flex-direction: column;
   gap: 14px;
   align-items: stretch;
}
#adminMain .iface-row { display: flex; gap: 20px; align-items: flex-end; }
#adminMain .iface-row .set-field { flex: 1; min-width: 0; }
#adminMain .iface-row label.chk { flex-shrink: 0; margin: 0 0 3px; white-space: normal; }

/* Restrictions tab: keep the three policy columns, but each checkbox gets its
   own line (input rows must not swallow the next checkbox). */
#adminMain .set-col label.chk {
   display: flex;
   align-items: center;
   gap: 6px;
   margin: 14px 0 4px;
   white-space: normal;
}

/* ---------- New-version reload banner ---------- */
/* A new deploy pushes its build tag over WebSocket; clients serving an older
   build show this fixed top-center pill and let the user reload in their own
   time (mid-call reloads are deferred until the call overlay closes). */
.version-bar {
  position: fixed; top: 14px; left: 50%; transform: translateX(-50%);
  z-index: 200; display: flex; align-items: center; gap: 10px;
  padding: 8px 12px 8px 14px; border-radius: 999px;
  background: var(--blue); color: #fff;
  box-shadow: 0 4px 18px rgba(20,40,90,.35);
  font-size: 13px; font-weight: 600; max-width: calc(100vw - 32px);
}
.version-bar.hidden { display: none; }
.version-bar .vb-ico { width: 16px; height: 16px; flex: 0 0 auto; display: block; }
[data-theme="theseus3k"] .version-bar .vb-ico { filter: brightness(0) invert(1); }
.version-bar .vb-txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.version-bar .vb-reload {
  flex: 0 0 auto; border: none; cursor: pointer; padding: 5px 12px;
  border-radius: 999px; background: #fff; color: var(--blue-dark);
  font-size: 12px; font-weight: 700;
}
.version-bar .vb-reload:hover { background: var(--blue-light); }
.version-bar .vb-close {
  flex: 0 0 auto; border: none; cursor: pointer; background: none; color: #fff;
  font-size: 16px; line-height: 16px; padding: 2px 4px; opacity: .75;
}
.version-bar .vb-close:hover { opacity: 1; }
.call-chat {
  position: absolute; top: 14px; right: 14px; width: 380px; z-index: 20;
  height: auto; max-height: calc(100% - 100px);
  background: linear-gradient(180deg, #fbfcfe, #eef3fa); border: 1px solid #c5d2e6;
  border-radius: 10px; box-shadow: 0 4px 16px rgba(20,40,90,.2);
  display: flex; flex-direction: column; overflow: hidden;
}
.call-chat.hidden { display: none; }
.call-chat-body { flex: 1; overflow: hidden; display: flex; flex-direction: column; min-height: 0; }
#callChatBody #chatHeader { display: none; }
.call-overlay.has-chat .call-toasts { right: 410px; }
/* Messages <-> Дневник two-position switch. Both ends carry their own icon so
   the control shows what it can become, and the pill slides behind them; the
   icons sit above the pill and the inactive one dims. The checkbox stays in the
   DOM (visually hidden, not display:none) so it keeps focus + Space/Enter. */
.vs-switch {
  position: relative; display: inline-flex; align-items: center; flex: 0 0 auto;
  width: 74px; height: 30px; border-radius: 15px; cursor: pointer;
  background: rgba(0,0,0,.08); border: 1px solid var(--border);
  margin: 0 2px;
}
.vs-switch input {
  position: absolute; width: 1px; height: 1px; margin: 0; padding: 0;
  opacity: 0; border: 0; clip: rect(0 0 0 0); clip-path: inset(50%);
}
.vs-slider {
  position: absolute; top: 2px; left: 4px; width: 30px; height: 26px;
  border-radius: 15px; background: var(--blue); pointer-events: none;
  transition: transform .18s ease;
}
.vs-switch input:checked ~ .vs-slider { transform: translateX(36px); }
.vs-switch input:focus-visible ~ .vs-slider { outline: 2px solid var(--blue); outline-offset: 1px; }
.vs-ic {
  position: relative; z-index: 1; width: 16px; height: 16px; margin: 0 10px;
  opacity: .45; pointer-events: none; transition: opacity .18s ease;
}
.vs-switch input:not(:checked) ~ .vs-ic-a,
.vs-switch input:checked ~ .vs-ic-b { opacity: 1; }
/* ===== Дневник: per-group homework diary / gradebook =====
   The pane replaces the message list and the composer; it scrolls on its own
   and keeps the same flat foundation as the rest of the app. */
.homework-view { flex: 1; min-height: 0; display: flex; flex-direction: column; }

/* ---- the diary's toolbars ----
   View-level controls live in two strips, in the shape the rest of the app uses
   (flat panel strips with bordered buttons, cf. .board-toolbar and the chat
   header). The top row is navigation only — the tab switch on the left, the day
   on the right; the strip beneath it carries the actions of the tab on screen.
   Before this the same controls were spread over a tab strip, a date bar and a
   button trailing the bottom of whatever list was on screen, so the eye had to
   hunt for them on every switch.

   The per-row controls (a card's edit/delete/deadline, a roster row's edit/remove)
   deliberately stay on their row — they act on that row and nowhere else, which is
   what the rest of the UI does too. */
.hw-toolbar {
  display: flex; align-items: center; gap: 10px; flex-shrink: 0;
  padding: 8px 14px; border-bottom: 1px solid var(--border); background: var(--panel);
  flex-wrap: wrap; row-gap: 8px;
}
/* The day's navigation is pushed to the right of the top row, as one bordered
   widget, so the date stays between its own arrows. */
.hw-toolbar-nav .icon-btn:disabled, .hw-toolbar-nav .hw-date:disabled { opacity: .4; }
.hw-date-toggle { display: inline-flex; align-items: center; gap: 6px; margin-right: 6px; }
.hw-date-toggle-lbl { font-size: 12px; color: var(--muted); white-space: nowrap; }
.hw-toolbar-nav {
  display: inline-flex; align-items: center; justify-content: center; gap: 1px;
  margin-left: auto; flex: 0 0 auto;
}
/* The day's arrows are bare glyphs around the date — no button chrome in
   either theme (same treatment as the month-dialog arrows). */
.hw-toolbar-nav .icon-btn {
  border: none; background: none; box-shadow: none; border-radius: 8px;
  flex: 0 0 auto;
}
.hw-toolbar-nav .icon-btn:hover { background: rgba(0,0,0,.06); border: none; box-shadow: none; }
.hw-toolbar-nav .icon-btn { padding: 5px; }
.hw-toolbar-nav .icon-btn img { width: 18px; height: 18px; }
.hw-toolbar-nav .icon-btn:hover { background: rgba(0,0,0,.06); border-color: var(--border); }
/* The day on screen is the picker (it opens the month dialog), so it is a
   button — but one that carries the date the way it always did: no fill and no
   border until it is hovered, the way the tab buttons give up their border too.
   The calendar glyph sits inside it at half strength, which is what the removed
   standalone button was for. */
.hw-date {
  display: inline-flex; align-items: center; gap: 7px;
  font-family: inherit; font-weight: 600; font-size: 16px; color: var(--text);
  min-width: 0; white-space: nowrap; cursor: pointer; flex: 0 0 auto;
  padding: 6px 10px; border: 1px solid transparent; border-radius: 8px; background: none;
}
.hw-date:hover { background: rgba(0,0,0,.06); border-color: var(--border); }
.hw-date .hw-date-cal { width: 15px; height: 15px; opacity: .5; flex: 0 0 auto; }
.hw-today { font-size: 13px; padding: 8px 14px; white-space: nowrap; }
/* "Today" is drawn in the calendar's own footer, not in the date bar: it is a
   date pick like every other control in that dialog, and the same footer serves
   the editor's date field, where it fills the field rather than the diary. */
.hw-cal-foot { display: flex; justify-content: center; margin-top: 10px; }
.hw-cal-foot .hw-today { width: 100%; }
/* Second row: the tab's actions. Hidden outright when there are none (Grades,
   plain students, archived chats) rather than left as an empty strip. */
.hw-toolbar-sub { gap: 8px; }
.hw-toolbar-acts { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }

/* ---- teacher tab switch (Students / Grades / Homework) ----
   Only a diary manager ever sees it; a student keeps the navigation alone. It is
   a segmented control inside the toolbar, not a strip of its own. */
.hw-tabs { display: inline-flex; align-items: center; gap: 3px; flex: 0 0 auto; }
.hw-tab-btn {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 14px; border: 1px solid transparent; border-radius: 8px;
  background: none; color: var(--muted); font-size: 14px; cursor: pointer; white-space: nowrap;
}
.hw-tab-btn img { width: 16px; height: 16px; opacity: .55; }
.hw-tab-btn:hover { color: var(--text); background: rgba(0,0,0,.05); }
/* The diary's toolbar has to hold the tabs and the day's navigation on one line,
   and the diary is not the window — it sits in the chat column, so how much room
   the toolbar has is a question about its container rather than the viewport (the
   sidebar alone can take half of it). When the two do not fit side by side the
   tabs keep their icons and drop their words, exactly as the icon-only buttons
   elsewhere in the app do: the title still says what the button is. Below 820px
   of viewport the toolbar wraps the tabs onto a row of their own instead, which is
   room enough for the words, so this only applies above that width. */
.homework-view { container-type: inline-size; }
@media (min-width: 821px) {
  @container (max-width: 740px) {
    .hw-tab-lbl { display: none; }
    .hw-tab-btn { padding: 8px 11px; }
  }
}
.hw-tab-btn.active {
  color: var(--text); font-weight: 600; background: rgba(0,0,0,.07);
  border-color: transparent;
}
.hw-tab-btn.active img { opacity: .9; }

/* Month picker — a modal dialog, so the grid is sized by its own box rather
   than by the space left under the toolbar. */
.hw-cal-dlg { width: 300px; max-width: 100%; }
.hw-cal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
/* Month navigation is a pair of bare arrows around the label — no button
   chrome in either theme (the 2k glossy icon-btn border is suppressed here by
   later cascade position). */
.hw-cal-head .icon-btn { border: none; background: none; box-shadow: none; padding: 6px; }
.hw-cal-head .icon-btn:hover { background: rgba(0,0,0,.06); border: none; box-shadow: none; }
.hw-cal-label { font-weight: 600; font-size: 14px; text-transform: capitalize; }
.hw-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.hw-cal-pad { display: block; }
.hw-cal-day {
  border: 1px solid transparent; background: none; cursor: pointer; padding: 8px 0;
  border-radius: 6px; font-size: 13px; color: var(--text); position: relative;
}
.hw-cal-day:hover { background: rgba(0,0,0,.06); }
.hw-cal-day.today { font-weight: 700; color: var(--text); }
.hw-cal-day.cur { background: var(--blue); border-color: var(--blue-dark); color: #fff; font-weight: 700; }
/* a date that already carries homework gets a dot under the number */
.hw-cal-day.has::after {
  content: ''; position: absolute; left: 50%; bottom: 3px; width: 5px; height: 5px;
  margin-left: -2px; border-radius: 50%; background: var(--blue);
}
.hw-cal-day.has.assign::after { background: var(--blue); }
.hw-cal-day.has.due::after { background: var(--danger); }
.hw-cal-day.cur.has::after { background: #fff; }
/* In the dialog the days can be as big as they like. */
.hw-cal-dlg .hw-cal-day { padding: 11px 0; font-size: 14px; }

.hw-content { flex: 1; min-height: 0; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 16px; }

/* ---- roster tab ----
   The rows are group members (role='member', i.e. the gradebook's roster) and
   the buttons are the group's own membership controls, gated on the same
   per-chat rights as the members sidebar. The tally repeats the gradebook's
   cell states for the day on screen: graded / pending / missed / untouched. */
/* The Students tab lists staff and students as two lists; this heads the second
   one when there is a first, and is absent for a group with no teachers. */
.hw-stu-sec {
  display: flex; align-items: center; gap: 9px;
  font-size: 12px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); font-weight: 700; margin: 20px 0 10px;
}
.hw-stu-sec:first-child { margin-top: 4px; }
.hw-stu-list { display: flex; flex-direction: column; gap: 8px; }
.hw-stu-row {
  display: flex; align-items: center; gap: 12px; padding: 10px 14px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 1px 2px rgba(15,23,42,.04);
  transition: box-shadow .14s ease, transform .14s ease;
}
.hw-stu-row:hover { box-shadow: 0 5px 16px rgba(15,23,42,.10); }
.hw-stu-av-wrap { position: relative; flex: 0 0 auto; }
.hw-stu-av {
  width: 38px; height: 38px; border-radius: 50%; object-fit: cover; display: block;
  border: 1px solid var(--border); background: var(--bg);
}
.hw-stu-on {
  position: absolute; right: 0; bottom: 0; width: 10px; height: 10px;
  border-radius: 50%; background: var(--green); border: 2px solid var(--panel);
}
.hw-stu-nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; font-weight: 600; color: var(--text); }
.hw-stu-tally { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
/* The student's own grades tab: progress bar on top, then one compact row
   per exercise per day (number, instruction, mark-or-state, comment). */
.hw-mygrades { display: flex; flex-direction: column; gap: 12px; }
.hw-my-tally { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 6px 0 2px; }
.hw-my-tally-head { font-size: 13px; font-weight: 700; color: var(--text); }
.hw-my-tally-row { display: flex; justify-content: center; }
.hw-my-tally-sub { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; }
.hw-mygrades .hw-ex-list { margin-top: 12px; }
.hw-my-tally .hw-tally-bar { width: 220px; }
.hw-my-seg {
  display: inline-flex; align-items: center; gap: 5px; padding: 3px 10px;
  border-radius: 999px; font-size: 12px; font-weight: 600; color: var(--text);
  white-space: nowrap;
}
button.hw-my-seg { cursor: pointer; border: 1px solid transparent; font-family: inherit; }
button.hw-my-seg:hover { border-color: currentColor; }
button.hw-my-seg.on { outline: 2px solid currentColor; outline-offset: 1px; }
.hw-my-seg img { width: 12px; height: 12px; }
.hw-my-seg-n { font-variant-numeric: tabular-nums; }
.hw-my-dash { font-weight: 700; line-height: 1; }
.hw-my-seg-graded { background: rgba(31,157,58,.16); }
.hw-my-seg-pending { background: rgba(255,204,0,.35); }
.hw-my-seg-missed { background: rgba(224,45,45,.14); }
.hw-my-seg-unsubmitted { background: rgba(15,23,42,.07); }
/* Rows and details never widen their column: long prompts, wide marking rows
   and media all stay inside, wrapping or clipping instead of stretching. */
.hw-my-row, .hw-my-detail { min-width: 0; max-width: 100%; }
.hw-my-detail { overflow: hidden; overflow-wrap: break-word; }
.hw-my-detail img, .hw-my-detail audio, .hw-my-detail video, .hw-my-detail iframe { max-width: 100%; }
.hw-my-detail .hw-gr-blank { min-width: 0; }
.hw-my-detail .hw-gr-av { max-width: 100%; }
.hw-my-inst { flex: 1 1 auto; min-width: 0; overflow-wrap: break-word; }
.hw-my-fb { flex: 1 1 100%; font-size: 13px; color: var(--muted); white-space: pre-wrap; overflow-wrap: break-word; }
.hw-ex-list { min-width: 0; }
.hw-my-row {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 12px 14px; border: 1px solid var(--border); border-radius: 10px;
  background: var(--panel); cursor: pointer;
}
.hw-my-row:hover { border-color: var(--accent-dark); }
.hw-my-row.open { border-color: var(--accent-dark); }
/* State color-coding: the row's left edge and wash carry the verdict, so a
   glance down the day tells graded/pending/missed/todo apart. */
.hw-my-row.hw-my-graded { border-left: 3px solid var(--green); background: linear-gradient(rgba(31,157,58,.10), rgba(31,157,58,.10)), var(--panel); }
.hw-my-row.hw-my-pending { border-left: 3px solid var(--accent-dark, var(--accent)); background: linear-gradient(rgba(255,204,0,.10), rgba(255,204,0,.10)), var(--panel); }
.hw-my-row.hw-my-missed { border-left: 3px solid var(--danger); background: linear-gradient(rgba(224,45,45,.08), rgba(224,45,45,.08)), var(--panel); }
.hw-my-row.hw-my-unsubmitted { border-left: 3px solid var(--border); }
.hw-my-row .hw-grade { font-size: 16px; }
.hw-my-chev { flex: 0 0 auto; width: 12px; height: 12px; position: relative; opacity: .6; }
.hw-my-chev::before {
  content: ''; position: absolute; left: 2px; top: 1px; width: 7px; height: 7px;
  border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted);
  transform: rotate(-45deg); transition: transform .15s ease;
}
.hw-my-row.open .hw-my-chev::before { transform: rotate(45deg); }
.hw-my-detail {
  margin: -6px 0 6px; padding: 12px 14px;
  border: 1px solid var(--border); border-top: none; border-radius: 0 0 10px 10px;
  background: var(--bg);
}
.hw-my-detail .hw-ex-prompt { margin-bottom: 8px; }
.hw-my-detail .hw-gr-text, .hw-my-detail .hw-gr-file { margin: 4px 0; }

.hw-my-state { font-size: 12px; }
/* Drag-reorder handle on manager exercise cards: a CSS dot grip (no asset),
   plus the drop indicator (accent rule above/below the target card). */
.hw-ex-drag {
  flex: 0 0 auto; width: 14px; align-self: stretch; cursor: grab; opacity: .55;
  background-image: radial-gradient(circle, var(--muted) 1.1px, transparent 1.3px);
  background-size: 6px 6px; background-position: center; background-repeat: repeat;
  border-radius: 4px;
}
.hw-ex-drag:hover { opacity: 1; }
.hw-ex-drag:active { cursor: grabbing; }
.hw-ex.hw-drag-src { opacity: .45; }
.hw-ex.hw-drag-over { box-shadow: 0 -2px 0 0 var(--blue); }
.hw-ex.hw-drag-over.hw-drag-after { box-shadow: 0 2px 0 0 var(--blue); }
.hw-answered { display: flex; align-items: center; gap: 8px; width: 100%; box-sizing: border-box; margin: 10px 0 0; padding: 0; background: transparent; border: none; cursor: pointer; font: inherit; color: var(--muted); font-size: 12px; text-align: left; }
.hw-answered:hover .hw-answered-lbl { color: var(--text); }
.hw-av-stack { display: flex; align-items: center; }
.hw-av-stack img, .hw-av-stack .hw-av-fb { width: 24px; height: 24px; border-radius: 50%; object-fit: cover; border: 2px solid var(--panel); margin-left: -8px; flex-shrink: 0; }
.hw-av-stack img:first-child, .hw-av-stack .hw-av-fb:first-child { margin-left: 0; }
.hw-av-stack .hw-av-fb { display: inline-flex; align-items: center; justify-content: center; background: var(--blue); color: #fff; font-size: 11px; font-weight: 700; }
.hw-av-more { font-size: 11px; color: var(--muted); margin-left: 2px; white-space: nowrap; }
.hw-solvers { display: flex; flex-direction: column; gap: 6px; max-height: 320px; overflow-y: auto; }
.hw-solver-row { display: flex; align-items: center; gap: 10px; width: 100%; box-sizing: border-box; text-align: left; background: var(--panel); border: 1px solid var(--border); border-radius: 8px; padding: 7px 10px; cursor: pointer; color: var(--text); font: inherit; box-shadow: 0 1px 2px rgba(15,23,42,.06); }
.hw-solver-row:hover { border-color: var(--blue); box-shadow: 0 2px 6px rgba(15,23,42,.10); }
.hw-sol-grade { padding: 5px 10px; min-width: 0; flex-shrink: 0; display: inline-flex; flex-direction: column; align-items: center; gap: 1px; }
.hw-sol-grade .hw-grade-big { font-size: 125%; }
.hw-sol-grade .hw-cell-ic { width: 14px; height: 14px; }
.hw-sol-ic { width: 28px; height: 28px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
span.hw-sol-ic.hw-sol-fb { display: inline-flex; align-items: center; justify-content: center; background: var(--blue); color: #fff; font-size: 13px; font-weight: 700; }
.hw-sol-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.hw-sol-name { font-weight: 600; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hw-sol-sub { font-size: 11px; color: var(--muted); white-space: nowrap; }
.hw-tally-bar { display: flex; width: 96px; height: 8px; border-radius: 999px; overflow: hidden; background: var(--border); }
.hw-tally-seg { display: block; height: 100%; }
.hw-seg-graded { background: var(--green); }
.hw-seg-pending { background: var(--blue); }
.hw-seg-missed { background: var(--danger); }
.hw-seg-unsubmitted { background: var(--muted); opacity: .4; }
.hw-tally-x { font-weight: 700; color: var(--text); font-size: 12px; min-width: 34px; text-align: right; font-variant-numeric: tabular-nums; }
.hw-stu-acts { display: flex; align-items: center; gap: 4px; flex: 0 0 auto; }
.hw-stu-acts .icon-btn { padding: 7px; border-radius: 8px; }
.hw-stu-acts .icon-btn img { width: 16px; height: 16px; opacity: .55; }
.hw-stu-acts .icon-btn:hover img { opacity: 1; }

/* ---- exercises (homework and exercise are one entity, so there is no
        enclosing sheet: each exercise is its own card) ---- */
/* The two halves of a date: what was assigned on it, and what was assigned
   earlier and is still open. A heading per list, so it is always clear which
   day a card under it belongs to. */
.hw-sec + .hw-sec { margin-top: 28px; }
.hw-sec-h {
  display: flex; align-items: center; gap: 9px;
  margin: 0 0 12px; font-size: 12px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
}
.hw-sec-t { flex: 0 0 auto; }
.hw-sec-c {
  flex: 0 0 auto; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 10px;
  background: var(--bg); color: var(--muted); border: 1px solid var(--border);
  font-size: 11px; font-weight: 800; letter-spacing: 0;
  display: inline-flex; align-items: center; justify-content: center;
}
.hw-sec-line { flex: 1 1 auto; height: 1px; background: var(--border); }
.hw-ex-list { display: flex; flex-direction: column; gap: 14px; }
.hw-ex-empty { font-size: 13px; color: var(--muted); font-style: italic; padding: 4px 0; }

/* An exercise the teacher switched off: still here, still editable, still with
   everything it collected — just visibly out of the class's way. */
.hw-tag-off {
  color: var(--muted); background: var(--bg); border: 1px solid var(--border);
  border-radius: 999px; padding: 3px 9px; font-weight: 600;
}
/* Which day a carried-over exercise was handed out on. */
.hw-tag-when {
  background: var(--bg); border: 1px solid var(--border);
  border-radius: 999px; padding: 3px 9px; margin-left: auto;
}

/* A card. The left stripe carries its state — accent while live, muted while
   switched off — and the hover lift makes the whole card read as the unit the
   actions act on. */
.hw-ex {
  position: relative; overflow: hidden;
  background: var(--panel); border: 1px solid var(--border); border-radius: 14px;
  padding: 15px 17px 15px 21px;
  box-shadow: 0 1px 2px rgba(15,23,42,.05);
  transition: box-shadow .15s ease, transform .15s ease;
}
.hw-ex::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
  background: var(--blue);
}
.hw-ex:hover { box-shadow: 0 6px 20px rgba(15,23,42,.10); }
.hw-ex-off { opacity: .62; }
.hw-ex-off::before { background: var(--muted); }
.hw-ex-off:hover { opacity: 1; }
/* A student's card paints its own state on the stripe and the number badge:
   gold while it is still to do, red once its deadline has passed unanswered,
   green once it is handed in. Teacher cards keep the plain accent stripe. */
.hw-ex-student.hw-st-over::before { background: var(--danger); }
.hw-ex-student.hw-st-done::before { background: var(--green); }
.hw-st-over .hw-ex-n { background: var(--danger); border-color: var(--danger); color: #fff; }
.hw-st-done .hw-ex-n { background: var(--green); border-color: var(--green); color: #fff; }
.hw-ex-head { display: flex; align-items: center; gap: 10px; margin-bottom: 11px; flex-wrap: wrap; }
.hw-ex-n {
  flex: 0 0 auto; min-width: 26px; height: 26px; padding: 0 8px; border-radius: 8px;
  background: var(--blue); color: #fff; font-size: 13px; font-weight: 800;
  border: 1px solid var(--blue-dark);
  display: inline-flex; align-items: center; justify-content: center;
}
.hw-ex-inst {
  flex: 0 0 auto; font-size: 13px; font-weight: 600; color: var(--text);
}
/* The deadline sits with its own exercise, so it must not be pushed to the far
   edge by .hw-ex-acts' margin-left:auto — it belongs next to the number. */
.hw-ex-due { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--muted); margin-left: auto; }
.hw-ex-due-spacer { flex: 1 1 auto; }
.hw-ex-due img { width: 16px; height: 16px; opacity: .7; }
/* A deadline past today, on both the teacher's card and the student's. */
.hw-ex-due-late { color: var(--danger); }
.hw-ex-due-late img { opacity: .9; }
/* The chip doubles as the way into the exercise editor, where the deadline is
   edited, so it reads as text until hovered. */
.hw-due-btn {
  font: inherit; color: inherit; background: none; border: 0; padding: 0;
  cursor: pointer; border-bottom: 1px dashed transparent;
}
.hw-due-btn:hover { border-bottom-color: currentColor; }
.hw-ex-acts { margin-left: auto; display: flex; align-items: center; gap: 4px; }
.hw-ex-acts .icon-btn { padding: 7px; border-radius: 8px; }
.hw-ex-acts .icon-btn img { width: 16px; height: 16px; }
.hw-ex-scale { font-size: 12px; color: var(--muted); }
/* The exercise text itself is the diary's main content, so it gets the app's
   full body size rather than a UI label size. */
.hw-ex-prompt iframe, .hw-ed-area iframe, .hw-gr-prompt iframe, #hwPrevPrompt iframe {
  width: 100%; max-width: 560px; aspect-ratio: 16 / 9; height: auto;
  border: 0; border-radius: 8px; margin: 6px 0;
}
/* Consent facade: nothing third-party loads until the button is pressed. */
.hw-embed {
  display: flex; align-items: center; gap: 10px; margin: 8px 0; padding: 12px 14px;
  max-width: 560px; border: 1px dashed var(--border-dark, var(--border)); border-radius: 10px;
  background: var(--bg);
}
.hw-embed-load {
  display: inline-flex; align-items: center; gap: 8px; flex: 0 0 auto;
  border: 1px solid var(--blue); border-radius: 8px;
  background: var(--blue-light); color: var(--text);
  font-size: 14px; font-weight: 700; padding: 8px 14px; cursor: pointer; font-family: inherit;
}
.hw-embed-load:hover { border-color: var(--blue); }
.hw-embed-play {
  width: 0; height: 0; border-left: 9px solid currentColor;
  border-top: 6px solid transparent; border-bottom: 6px solid transparent; display: block;
}
.hw-embed-link {
  flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 12px; color: var(--muted);
}
.hw-ex-prompt {
  font-size: 16px; line-height: 2; overflow-wrap: anywhere;
  padding: 14px 16px; background: var(--panel-solid, #fff); border: 1px solid var(--border);
  border-radius: 10px; box-shadow: 0 1px 2px rgba(15,23,42,.05);
}
.hw-ex-prompt ul, .hw-ex-prompt ol { margin: 4px 0; padding-left: 26px; }
.hw-ex-state { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
/* Metadata (assigned / deadline / scale) is reference, not the exercise, so it
   stays folded behind a Details toggle and the card leads with the work. */
.hw-ex-details { margin-top: 12px; }
.hw-ex-more {
  display: inline-flex; align-items: center; gap: 6px; width: max-content;
  font-size: 12px; font-weight: 600; color: var(--muted);
  cursor: pointer; list-style: none; user-select: none;
}
.hw-ex-more::-webkit-details-marker { display: none; }
.hw-ex-more::marker { content: ''; }
.hw-ex-more:hover { color: var(--text); }
.hw-ex-more-c { font-size: 10px; line-height: 1; transition: transform .15s ease; }
.hw-ex-details[open] .hw-ex-more { color: var(--text); }
.hw-ex-details[open] .hw-ex-more-c { transform: rotate(90deg); }
.hw-ex-details[open] .hw-ex-meta { margin-top: 10px; }
/* The bordered container under the text: the day it was assigned, its own
   deadline and the scale it is marked against, as labelled fields. */
.hw-ex-meta {
  display: flex; flex-wrap: wrap; gap: 0; margin-top: 12px;
  padding: 10px 14px; border: 1px solid var(--border); border-radius: 10px; background: var(--bg);
}
.hw-ex-meta-i {
  display: flex; flex-direction: column; gap: 3px; font-size: 14px;
  padding: 0 18px; border-left: 1px solid var(--border);
}
.hw-ex-meta-i:first-child { padding-left: 0; border-left: 0; }
.hw-ex-meta-i label {
  font-size: 10px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); font-weight: 700;
}
.hw-ex-answer { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 10px; }

/* Hand-in kinds: free text, file chip, recorder. */
.hw-text-ed {
  width: 100%; min-height: 90px; padding: 8px 10px;
  border: 1px solid #ced4da; border-radius: 8px;
  background: #fff; color: var(--text); font: inherit;
}
.hw-text-ed:focus { outline: none; border-color: var(--blue); }
.hw-text-ed:empty::before { content: attr(data-ph); color: var(--muted); }
.hw-text-tools { width: 100%; }
.hw-text-done { width: 100%; }
.hw-file-chip { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.hw-file-link { font-weight: 600; }
.hw-file-size { color: var(--muted); font-size: 12px; }
.hw-file-pick input[type="file"] { display: none; }
.hw-file-picklbl {
  display: inline-block; padding: 6px 12px; cursor: pointer;
  border: 1px dashed #9aa0aa; border-radius: 8px; color: var(--muted);
}
.hw-file-picklbl:hover { color: var(--text); border-color: var(--accent-dark); }
.hw-rec { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.hw-rec-timer { font-variant-numeric: tabular-nums; font-weight: 700; }
.hw-rec-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--danger); animation: hw-rec-blink 1s infinite; }
@keyframes hw-rec-blink { 50% { opacity: .25; } }
.hw-rec-hint { color: var(--muted); font-size: 12px; }
.hw-gr-text { white-space: pre-wrap; }
.hw-gr-file { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.hw-ed-confrow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 8px 0; }
.hw-ed-conf { width: 90px; }
.hw-ed-confhint { color: var(--muted); font-size: 12px; }
.hw-ed-allowed { display: inline-flex; gap: 4px 12px; flex-wrap: wrap; }
/* Solver file drop zone: dashed area doubling as the browse button, with an
   inline upload progress bar underneath while the file flies. */
.hw-file-area { display: flex; flex-direction: column; gap: 6px; width: 100%; }
.hw-file-drop {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 12px 14px; border: 1px dashed #9aa0aa;
  border-radius: 10px; color: var(--muted); font-size: 13px; font-weight: 600;
  cursor: pointer; background: transparent;
}
.hw-file-drop img { width: 16px; height: 16px; opacity: .7; }
.hw-file-drop:hover { border-color: var(--accent-dark); color: var(--text); }
.hw-file-drop.hw-drop-on { border-color: var(--accent-dark); color: var(--text); background: var(--accent-soft); }
.hw-file-zonemain { display: contents; }
.hw-file-prog { height: 6px; border-radius: 999px; background: var(--border); overflow: hidden; }
.hw-file-prog i { display: block; height: 100%; width: 0; background: var(--accent-dark, var(--accent)); border-radius: 999px; transition: width .15s linear; }
/* Add-exercise drop-add area closing the homework list. */
.hw-drop-add { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; box-sizing: border-box; padding: 13px 14px; background: transparent; border: 1px dashed #9aa0aa; border-radius: 10px; color: var(--muted); font-size: 13px; font-weight: 600; cursor: pointer; }
.hw-drop-add img { width: 16px; height: 16px; opacity: .7; }
.hw-drop-add:hover { border-color: var(--accent-dark); color: var(--text); }
.hw-drop-add:hover img { opacity: 1; }
.hw-stu-row.hw-drop-add { box-shadow: none; padding: 13px 14px; gap: 8px; border-radius: 10px; background: transparent; border: 1px dashed #9aa0aa; }
/* On/off switch state label: always names the state the exercise is in now. */
.hw-ex-switch-lbl { font-size: 12px; font-weight: 600; color: var(--muted); margin-right: 2px; }
.hw-ex-switch-lbl.off { color: var(--danger); }
/* In-text upload progress chip at the caret. */
.hw-upload-chip { display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px; border: 1px dashed #9aa0aa; border-radius: 8px; color: var(--muted); font-size: 12px; }

/* Blanks: the author writes "____" markers, the student gets an input there. */
.hw-blank-in {
  width: 108px; padding: 4px 8px; margin: 0 2px; font-size: 16px; font-family: inherit;
  border: none; border-bottom: 1px solid var(--blue); background: transparent;
  color: var(--text); border-radius: 3px 3px 0 0;
}
.hw-blank-in:focus { outline: none; background: rgba(0,0,0,.04); }
  width: auto; max-width: 240px; margin: 0 2px; padding: 5px 8px 5px 10px; font-size: 15px; font-family: inherit;
  border: 1px solid var(--border-dark, var(--border)); border-radius: 6px; vertical-align: baseline;
  background-color: var(--bg); color: var(--text);
}
  flex: 1 1 auto; min-width: 60px; width: 130px; border: 0; background: transparent;
  font-size: inherit; font-family: inherit; color: inherit; padding: 0; outline: none;
}
  flex: 0 0 auto; width: 22px; border: 0; background: transparent; cursor: pointer; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
}
  width: 0; height: 0; border-left: 5px solid transparent; border-right: 5px solid transparent;
  border-top: 6px solid currentColor; opacity: .65; display: block;
}
  position: absolute; top: calc(100% + 4px); left: -1px; z-index: 120; min-width: calc(100% + 2px); max-width: 280px;
  max-height: 220px; overflow-y: auto; display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,.18); padding: 4px;
}
  border: 0; background: none; text-align: left; font-size: 14px; color: var(--text);
  padding: 7px 9px; border-radius: 5px; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Read-only blanks (grading, review) fit their text: content-based sizing where
   supported, the size attribute everywhere else (set in hwFillPrompt). */
.hw-blank-in[readonly] { field-sizing: content; width: auto; min-width: 4em; }
.hw-blank-ro { border-bottom-color: var(--border); color: var(--text); }
.hw-readonly { font-size: 16px; color: var(--text); }

.hw-grade { font-size: 14px; font-weight: 700; padding: 3px 10px; border-radius: 12px; background: var(--bg); color: var(--text); border: 1px solid var(--border); }
.hw-tag { font-size: 12px; color: var(--muted); }
.hw-tag-late { color: var(--danger); }
/* Autograding: a small tag on the student's exercise and a dashed outline on a
   mark the machine assigned, so "graded by hand" and "graded automatically"
   are distinguishable at a glance. */
.hw-tag-auto {
  color: var(--muted); background: var(--bg); border: 1px solid var(--border);
  border-radius: 10px; padding: 2px 8px; font-weight: 600;
}
.hw-grade-auto { outline: 1px dashed var(--blue); outline-offset: 1px; }
.hw-tag-fb { overflow-wrap: anywhere; }
.hw-feedback { font-size: 13px; color: var(--muted); font-style: italic; }

/* The one primary action of the current view, right-aligned in the toolbar.
   A solid button, so it is distinguishable from the navigation widgets next to
   it — the app's .btn-primary, sized like a toolbar button. */
.hw-toolbar-acts .hw-add-ex {
  align-self: auto; display: inline-flex; align-items: center; gap: 8px;
  border: 1px solid var(--blue-dark); background: var(--blue); color: #fff;
  padding: 9px 16px; border-radius: 8px; font-size: 13px; font-weight: 600; cursor: pointer;
  white-space: nowrap;
}
.hw-toolbar-acts .hw-add-ex:hover { filter: brightness(1.06); }
.hw-toolbar-acts .hw-add-ex img { width: 16px; height: 16px; opacity: .85; }

/* ---- students x exercises matrix ---- */
.hw-legend { display: flex; flex-wrap: wrap; gap: 6px 16px; margin-bottom: 12px; font-size: 12px; color: var(--muted); }
.hw-legend-i { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; }
.hw-legend-sw { width: 14px; height: 14px; border-radius: 4px; border: 1px solid rgba(15,23,42,.14); flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; }
.hw-legend-sw img { width: 10px; height: 10px; opacity: .78; }
.hw-legend-graded { background: color-mix(in srgb, var(--green) 30%, transparent); }
.hw-legend-pending { background: color-mix(in srgb, var(--blue) 55%, transparent); }
.hw-legend-missed { background: color-mix(in srgb, var(--danger) 28%, transparent); }
.hw-legend-unsubmitted { background: color-mix(in srgb, var(--text) 7%, transparent); }

.hw-table-wrap {
  overflow-x: auto; background: var(--panel); border: 1px solid var(--border);
  border-radius: 14px; box-shadow: 0 1px 2px rgba(15,23,42,.05);
}
.hw-table { border-collapse: collapse; width: 100%; font-size: 13px; }
.hw-th {
  padding: 11px 12px; text-align: center; font-weight: 700; color: var(--muted);
  border-bottom: 1px solid var(--border); white-space: nowrap;
  background: var(--panel-solid, var(--panel));
}
.hw-th-name {
  text-align: left; position: sticky; left: 0; z-index: 2;
  background: var(--panel-solid, var(--panel)); box-shadow: 1px 0 0 var(--border);
}
/* Exercise columns hug their content and the student-name column takes the
   slack, so a short gradebook does not stretch into a grid of wide empty cells. */
.hw-table .hw-th:not(.hw-th-name),
.hw-table .hw-cell { width: 1%; }
.hw-table .hw-th-name { width: 100%; }
.hw-stu-name { font-weight: 600; color: var(--text); }
/* A column for work handed out earlier and still open: its number alone could
   repeat one already on screen, so it carries its assign date beneath. */
.hw-th-carry { color: var(--text); }
.hw-th-when {
  display: block; margin-top: 2px; font-size: 10px; font-weight: 600;
  letter-spacing: .02em; color: var(--muted);
}
.hw-cell {
  position: relative; text-align: center; padding: 11px 12px; border: 1px solid var(--border);
  min-width: 44px; cursor: default;
}
.hw-cell[data-open] { cursor: pointer; }
.hw-cell[data-open]:hover { filter: brightness(.94); }
.hw-cell-v { font-weight: 700; }
.hw-cell-ic { width: 16px; height: 16px; opacity: .7; vertical-align: middle; }
.hw-cell-graded { background: rgba(31,157,58,.20); }
.hw-cell-pending { background: rgba(255,204,0,.32); }
.hw-cell-unsubmitted { background: rgba(15,23,42,.05); }
.hw-cell-missed { background: rgba(224,45,45,.16); }
/* a late submission gets a corner marker on top of the cell colour */
.hw-cell-late { position: absolute; top: 3px; right: 3px; width: 6px; height: 6px; border-radius: 50%; background: var(--danger); }
/* a mark autograding assigned: a small ring in the free corner, so a teacher can
   see at a glance which cells were never looked at by a human. The cell stays
   fully clickable — reviewing and re-marking are the point. */
.hw-cell-auto::after {
  content: ''; position: absolute; bottom: 3px; right: 3px; width: 6px; height: 6px;
  border-radius: 50%; border: 2px solid var(--blue);
}

.hw-empty { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; color: var(--muted); font-size: 14px; padding: 28px 16px; }
.hw-empty-badge {
  width: 76px; height: 76px; border-radius: 22px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--bg); border: 1px solid var(--border);
}
.hw-empty-ic { width: 34px; height: 34px; opacity: .55; }
.hw-empty-t { margin: 0; font-size: 15px; font-weight: 600; color: var(--text); }
.hw-empty-row { flex: 0 0 auto; padding: 10px 0; font-style: italic; }

/* ---- dialogs ---- */
/* Exercise type menu: shown before the editor, types grouped by category. */
.hw-types { display: flex; flex-direction: column; gap: 16px; min-width: 320px; }
.hw-tg { display: flex; flex-direction: column; gap: 6px; }
.hw-tg-t {
  font-size: 12px; text-transform: uppercase; letter-spacing: .5px;
  color: var(--muted); font-weight: 600;
}
.hw-type {
  display: flex; align-items: center; gap: 12px; text-align: left; width: 100%;
  border: 1px solid var(--border); background: var(--bg); border-radius: 9px;
  padding: 11px 13px; cursor: pointer; color: var(--text); font-size: 14px;
}
.hw-type:hover { border-color: var(--border); background: rgba(0,0,0,.04); }
.hw-type-ic { width: 24px; height: 24px; flex: 0 0 24px; opacity: .75; }
.hw-type-b { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.hw-type-n { font-weight: 600; }
.hw-type-h { font-size: 12px; color: var(--muted); }
.hw-form { display: flex; flex-direction: column; gap: 6px; }
.hw-form label { font-size: 13px; color: var(--muted); margin-top: 6px; }
.hw-ed { display: flex; flex-direction: column; gap: 8px; }
/* The format toolbar sits directly under the "Exercise text" label and grows
   into the top edge of the prompt pane, like one proper toolbar: the rows and
   the pane share a single border, and every toolbutton is borderless (hover
   alone shows the hit area), in both themes. */
.hw-ed-tools { display: flex; align-items: center; gap: 2px; flex-wrap: wrap; }
.hw-ed-lbl + .hw-ed-tools {
  border: 1px solid var(--border); border-bottom: none; border-radius: 8px 8px 0 0;
  background: var(--bg); padding: 5px 7px; position: relative; margin-top: -4px;
}
.hw-ed-lbl + .hw-ed-tools + .hw-ed-tools-media,
.hw-ed-tools-media {
  border: 1px solid var(--border); border-bottom: none; border-top: 1px dashed var(--border);
  border-radius: 0; background: var(--bg); padding: 5px 7px; position: relative;
}
.hw-ed-lbl + .hw-ed-tools + .hw-ed-tools-media + .hw-ed-area,
.hw-ed-tools-media + .hw-ed-area { border-radius: 0 0 8px 8px; }
.hw-ed-tools button { border: none; background: none; box-shadow: none; }
.hw-ed-tools button:hover { background: rgba(0,0,0,.07); border: none; box-shadow: none; }
.hw-ed-tools .hw-ed-blank { padding: 5px 10px; }
.hw-ed-b, .hw-ed-i, .hw-ed-u, .hw-ed-s {
  width: 32px; height: 32px; border: 1px solid var(--border); background: var(--bg);
  border-radius: 7px; cursor: pointer; color: var(--text); font-size: 15px;
}
.hw-ed-blank { border: 1px solid var(--border); background: var(--bg); border-radius: 7px; padding: 7px 12px; font-size: 13px; cursor: pointer; color: var(--text); }
.hw-ed-blank-short, .hw-ed-blank-wide { font-family: monospace; }
.hw-ed-media { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: 1px solid var(--border); background: var(--bg); border-radius: 7px; cursor: pointer; }
.hw-ed-media img { width: 16px; height: 16px; opacity: .75; }
.hw-ed-media:hover img { opacity: 1; }
/* Exercise-embedded media scale to the prompt column, never past it. */
.hw-ex-prompt img, .hw-ed-area img { max-width: 100%; height: auto; border-radius: 6px; }
.hw-ex-prompt audio, .hw-ex-prompt video, .hw-ed-area audio, .hw-ed-area video { max-width: 100%; }
/* Wide blanks fill their line. Only top-level paragraphs/divs turn flex, so
   nested structures keep rendering; anywhere else the min-width still reads
   as a long blank. A wide blank sitting directly in the area (the first thing
   typed — no paragraph wraps it) becomes a full-width block itself. */
.hw-has-wide > p, .hw-has-wide > div { display: flex; flex-wrap: wrap; align-items: baseline; }
.hw-has-wide .hw-blank-in.hw-wide, .hw-has-wide .hw-blank[data-wide] { flex: 1 1 10em; min-width: 10em; }
.hw-has-wide > .hw-blank[data-wide]:first-child,
.hw-has-wide > .hw-blank-in.hw-wide:first-child { display: block; width: 100%; box-sizing: border-box; }
/* Resize dot on a short blank's bottom border, at its right end: the only
   visible chrome of the width drag, deliberately quiet. The JS hit zone around
   it (18x20px) is larger than the dot itself so the grip is easy to hit. */
.hw-ed-area .hw-blank { position: relative; }
.hw-ed-area .hw-blank:not([data-wide])::after {
  content: ''; position: absolute; right: -3px; bottom: -4px; width: 8px; height: 8px;
  border-radius: 50%; background: var(--muted); opacity: .65; pointer-events: none;
}
/* Inline unsaved-changes confirm in the editor's action row (no nested dialog:
   the modal is a singleton). */
.hw-discard-msg { flex: 1 1 auto; font-size: 13px; color: var(--text); }
/* Inline unsaved-changes confirm needs a primary small button next to ghost. */
.dlg-actions .btn-xs { padding: 6px 12px; font-size: 13px; }
.hw-ed-align { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: 1px solid var(--border); background: var(--bg); border-radius: 7px; cursor: pointer; }
.hw-ed-align img { width: 16px; height: 16px; opacity: .75; }
.hw-ed-align:hover img { opacity: 1; }
.cp-scope { position: relative; display: inline-flex; }
.cp-scope .cp-toggle { width: 32px; height: 32px; padding: 2px; border: 1px solid var(--border); border-radius: 7px; background-color: var(--bg); box-shadow: inset 0 0 0 1px rgba(0,0,0,.06); cursor: pointer; }
.cp-scope .cp-toggle.active { border-color: var(--blue); }
.cp-scope .cp-popup { position: absolute; top: calc(100% + 6px); left: 0; z-index: 70; display: none; flex-direction: column; gap: 8px; background: var(--panel); border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 6px 20px rgba(0,0,0,.16); padding: 10px; min-width: 190px; }
.cp-scope .cp-popup.open { display: flex; }
.cp-scope .cp-native { display: none; }
.cp-scope .cp-body { display: flex; flex-direction: column; gap: 8px; }
.cp-scope .cp-sv { width: 100%; height: 110px; border: 1px solid var(--border); border-radius: 6px; cursor: crosshair; touch-action: none; display: block; }
.cp-scope .cp-hue { width: 100%; height: 14px; border: 1px solid var(--border); border-radius: 7px; cursor: pointer; touch-action: none; display: block; }
.cp-scope .cp-presets { display: grid; grid-template-columns: repeat(7, 1fr); gap: 5px; border-top: 1px solid var(--border); padding-top: 8px; }
.cp-scope .cp-swatch { width: 100%; height: 20px; padding: 0; border: 1px solid rgba(0,0,0,.18); border-radius: 4px; cursor: pointer; }
.cp-scope .cp-swatch.active { outline: 2px solid var(--blue); outline-offset: 1px; }
.hw-ed-area {
  min-height: 7em; max-height: 320px; overflow-y: auto; padding: 11px 13px; font-size: 15px;
  line-height: 2; border: 1px solid var(--border); border-radius: 8px; background: var(--panel);
  resize: vertical;
}
/* The edited text breathes: a full blank line between paragraphs. */
.hw-ed-area p { margin: 1em 0; }
.hw-ed-area > :first-child { margin-top: 0; }
.hw-ed-area > :last-child { margin-bottom: 0; }
.hw-ed-area:focus { outline: none; border-color: var(--blue); }
/* Editor blanks carry their 1-based number in document order (the same order
   the server renumbers them into on save). A pseudo-element, so it is never
   editable and never stored — only rendered. */
.hw-ed-area { counter-reset: hwblank; }
.hw-ed-area .hw-blank { counter-increment: hwblank; }
.hw-ed-area .hw-blank::before {
  content: counter(hwblank); width: 20px; height: 20px; border-radius: 50%;
  background: var(--blue); color: #fff;
  font-size: 12px; font-weight: 700; display: inline-flex; align-items: center; justify-content: center;
  margin-right: 5px; vertical-align: 1px;
  user-select: none; -webkit-user-select: none;
}
/* Grading blanks carry the same numbered circle, read off their marker index. */
.hw-gr-blank::before {
  content: attr(data-i); min-width: 20px; height: 20px; border-radius: 10px; padding: 0 4px;
  background: var(--blue); color: #fff;
  font-size: 12px; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
}
/* Inside the large dialog the exercise is written in a pane, not in a fixed strip:
   the area takes all the height the dialog has left over, so growing the dialog
   grows the sheet with it, and the cap above stops applying. min-height stays
   below zero-free so the pane never collapses if the dialog is dragged small. */
.dlg-lg .dlg-body > .hw-ed { flex: 1 1 auto; min-height: 0; }
/* Only the prompt pane takes the slack; the dates, the toolbar, the autograde row
   and the scale row keep their own height (they would otherwise be the first
   things the flex layout squashes in a short dialog). */
.dlg-lg .hw-ed-area { flex: 1 1 auto; min-height: 7em; max-height: none; }
.hw-ed-tools { position: relative; }
.hw-ed-zoom { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: none; background: transparent; border-radius: 4px; cursor: pointer; padding: 6px; }
.hw-ed-zoom img { width: 16px; height: 16px; opacity: .7; }
.hw-ed-zoom:hover img { opacity: 1; }
.hw-ed-zoom.on { background: var(--blue-light); }
.hw-ed-zoom.on img { opacity: 1; }
.hw-ed-conv { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: 1px solid var(--border); background: var(--bg); border-radius: 7px; cursor: pointer; }
.hw-ed-conv img { width: 16px; height: 16px; opacity: .55; }
.hw-ed-conv.on { background: var(--blue-light); }
.hw-ed-conv.on img { opacity: 1; }
/* Zoomed writing: only the toolbar and the textarea stay; the dates, the
   instruction, the chips, the help, the autograde row and the scale row hide
   (they keep their values), so the textarea takes the whole dialog. */
.hw-ed.zoomed #hwEdPane > .hw-ed-dates,
.hw-ed.zoomed #hwEdPane > .hw-ed-lbl,
.hw-ed.zoomed #hwEdPane > .hw-ed-inst,
.hw-ed.zoomed #hwEdPane > .hw-ed-chipfield,
.hw-ed.zoomed .hw-ed-helpbody,
.hw-ed.zoomed #hwEdPane > .hw-ed-autorow,
.hw-ed.zoomed #hwEdPane > .hw-ed-scalerow { display: none; }
/* The two modes of the exercise editor: write it, or look at it as a student will.
   One segmented switch rather than two dialogs, because a teacher checking a blank
   wants to do it against the text they are still writing, not from a copy of it. */
.hw-ed-tabs { display: inline-flex; align-items: center; gap: 3px; align-self: flex-start; flex: 0 0 auto; margin-bottom: 4px; }
.hw-ed-tab {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 14px; border: 1px solid transparent; border-radius: 8px;
  background: none; color: var(--muted); font-size: 14px; cursor: pointer; white-space: nowrap;
}
.hw-ed-tab img { width: 16px; height: 16px; opacity: .55; }
.hw-ed-tab:hover { color: var(--text); background: rgba(0,0,0,.05); }
.hw-ed-tab.active {
  color: var(--text); font-weight: 600; background: var(--blue-light);
  border-color: var(--blue);
}
.hw-ed-tab.active img { opacity: .9; }
.hw-ed-panes { display: flex; flex-direction: column; min-height: 0; }
/* Exercise-type indicator atop the editor panes: the kind being edited, so a
   reopened dialog never reads as generic. */
.hw-ed-type { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--muted); padding: 2px 2px 6px; }
.hw-ed-type img { width: 16px; height: 16px; opacity: .7; }
.hw-ed-pane { display: flex; flex-direction: column; gap: 8px; min-height: 0; }
/* Off rather than gone: the pane keeps its markup and its state (what the teacher
   has written, what they tried in the preview) and only stops being shown. */
.hw-ed-pane.off { display: none; }
.dlg-lg .hw-ed-panes { flex: 1 1 auto; min-height: 0; }
.dlg-lg .hw-ed-pane { flex: 1 1 auto; }
.dlg-lg .hw-ed-pane > *:not(.hw-ed-area) { flex: 0 0 auto; }
/* The student's view of the exercise. The caption sits above the card because it is
   about the whole pane: nothing typed below it is kept or sent anywhere. */
.hw-ed-prevnote { font-size: 13px; color: var(--muted); }
.hw-ed-prev .hw-ex { background: var(--bg); }
/* The preview's verdict on a blank. A checked answer is not a mark — nothing was
   submitted — so it is the underline of the input that says so, in the two colours
   the app already uses for right and wrong. */
.hw-blank-in-ok { border-bottom-color: var(--green); }
.hw-blank-in-bad { border-bottom-color: var(--danger); }
/* The assignment date and the exercise's own deadline, side by side. */
.hw-ed-dates { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 8px; }
.hw-ed-date { display: flex; flex-direction: column; gap: 3px; flex: 1; min-width: 150px; }
.hw-ed-date label { font-size: 13px; color: var(--muted); }
/* A date field: a button carrying the wording used everywhere else, not a native
   <input type="date"> whose popup is drawn by the OS in the OS theme and the
   browser's language. The click opens the app's own month grid in a panel of its
   own, anchored to the field and outside the dialog (hwFieldPick). */
.hw-ed-datebtn {
  display: flex; align-items: center; gap: 7px; text-align: left;
  font-size: 14px; color: var(--text); background: var(--bg);
  border: 1px solid var(--border); border-radius: 8px; padding: 9px 11px; cursor: pointer;
}
.hw-ed-datebtn:hover { background: var(--blue-light); border-color: var(--blue); }
.hw-ed-datebtn img { width: 16px; height: 16px; opacity: .75; flex: 0 0 auto; }
/* The date itself. min-width:0 + ellipsis so a long localized date ("Thursday,
   1 October 2026") cannot stretch the field; the text is also the button's
   title, so the full value is still readable on hover. */
.hw-ed-dateval { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hw-ed-clear {
  align-self: flex-start; font-size: 12px; color: var(--muted);
  background: none; border: 0; padding: 0; cursor: pointer;
}
.hw-ed-clear:hover { color: var(--danger); text-decoration: underline; }
/* The grid's own panel, floating above the dialog. Separate from the dialog on
   purpose: dialog() reuses one #modalDialog element, so a second dialog() would
   replace the editor that opened it, and an inline grid pushed the form around.
   Fixed to the viewport and positioned by script (hwPlaceFieldPick).
   Opaque (--panel-solid, not the theme's translucent --panel): it floats over the
   modal's dark backdrop, and a translucent surface let that overlay bleed through,
   which read as a washed-out grey calendar. */
.hw-dpick {
  position: fixed; z-index: 1000; width: 288px;
  background: var(--panel-solid, var(--panel));
  border: 1px solid var(--border); border-radius: 10px;
  box-shadow: 0 8px 28px rgba(0,0,0,.22); padding: 10px;
}
/* Comfortable cells in the panel, as in the calendar dialog, where there is room
   for them; the inline version had to keep the compact default. */
.hw-dpick .hw-cal-day { padding: 9px 0; font-size: 14px; }
.hw-dpick .hw-cal-label { font-size: 14px; }
/* The scale editor carries a whole mark list, so it needs more room than a month
   grid. */
.hw-dpick-scale { width: 340px; }
.hw-ed-lbl { font-size: 13px; color: var(--muted); }
/* Autograding row of the exercise editor: the switch plus a plain sentence of
   what it currently means, so the state is readable without hovering. */
.hw-ed-autorow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.hw-ed-autohint { font-size: 13px; color: var(--muted); flex: 1 1 auto; min-width: 0; }
.hw-ed-auto {
  position: relative; flex: 0 0 auto; width: 42px; height: 24px; padding: 0;
  border-radius: 12px; border: 1px solid var(--border-dark, var(--border));
  background: var(--bg); cursor: pointer;
}
.hw-ed-auto .hw-ed-autoknob {
  position: absolute; top: 2px; left: 2px; width: 18px; height: 18px;
  border-radius: 50%; background: var(--muted); transition: left .14s ease;
}
.hw-ed-auto.on { background: var(--blue-light); border-color: var(--blue); }
.hw-ed-auto.on .hw-ed-autoknob { left: 20px; background: var(--blue); }
/* A blank in an exercise that is graded by hand holds no answer at all: it is
   shown as a plain gap and cannot be typed into. */
.hw-ed-area .hw-blank-locked {
  display: inline-block; min-width: 68px; border-bottom: 1px dashed var(--border-dark, var(--border));
  cursor: default;
}
/* Grading scale in the editor: a read-only label of the current scale plus the
   button that opens the scale editor in its own window. The editable list is not
   in the form at all. */
.hw-ed-scalerow { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.hw-ed-scaleval {
  flex: 1 1 auto; min-width: 0; font-size: 14px; color: var(--text);
  background: var(--bg); border: 1px solid var(--border); border-radius: 8px;
  padding: 9px 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.hw-ed-scalebtn {
  display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto;
  font-size: 13px; padding: 9px 12px; border-radius: 8px;
}
.hw-ed-scalebtn img { width: 14px; height: 14px; opacity: .75; }
/* The scale editor's own window: presets, the list itself, and "make default".
   An action on the teacher's settings, not a property of the exercise, hence a
   button here rather than a checkbox on the exercise editor. */
/* The scale editor's own modal dialog over the exercise editor: the templates
   stay small and muted, the chips field is the prominent main control, and the
   instruction sits right below it. Above the app modal (z 950), below nothing
   that matters. */
.hw-scale-dlg { position: fixed; inset: 0; background: rgba(0,0,0,.55); display: flex; align-items: center; justify-content: center; z-index: 960; padding: 20px; }
.hw-scale-card { width: min(430px, calc(100vw - 40px)); max-height: calc(100vh - 60px); overflow-y: auto; background: var(--bg); border: 1px solid var(--border); border-radius: 12px; padding: 18px; display: flex; flex-direction: column; gap: 12px; box-shadow: 0 12px 40px rgba(0,0,0,.45); }
.hw-scale-head { display: flex; align-items: center; justify-content: space-between; }
.hw-scale-title { font-size: 16px; font-weight: 700; }
.hw-scale-presets { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.hw-scale-presets-lbl { font-size: 11px; color: var(--muted); }
.hw-scale-preset {
  font-size: 11px; padding: 4px 9px; border-radius: 7px; cursor: pointer;
  border: 1px solid var(--border); background: none; color: var(--muted);
}
.hw-scale-preset:hover { color: var(--text); border-color: var(--blue); }
.hw-scale-chips {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  min-height: 52px; padding: 9px 11px; border-radius: 9px;
  border: 2px solid var(--blue); background: var(--bg);
}
.hw-scale-chips:focus-within { outline: 2px solid var(--blue); outline-offset: 1px; }
.hw-scale-chip {
  display: inline-flex; align-items: center; gap: 5px; padding: 5px 6px 5px 11px;
  border-radius: 16px; background: var(--blue-light); color: var(--text); font-size: 14px;
}
.hw-scale-chipx { border: 0; background: none; cursor: pointer; color: var(--muted); font-size: 15px; line-height: 1; padding: 0 2px; }
.hw-scale-chipx:hover { color: var(--danger); }
.hw-scale-chipin { flex: 1 1 90px; min-width: 90px; border: 0; background: none; outline: none; font-size: 15px; color: var(--text); }
.hw-scale-hint { font-size: 13px; color: var(--text); opacity: .85; line-height: 1.4; }
.hw-scale-modes { display: flex; flex-direction: column; gap: 10px; }
.hw-scale-mode {
  display: flex; align-items: flex-start; gap: 8px; flex: 1 1 0;
  font-size: 15px; cursor: pointer; padding: 9px 11px;
  border: 1px solid var(--border); border-radius: 9px; background: var(--bg);
}
.hw-scale-mode input { accent-color: var(--blue); cursor: pointer; width: 17px; height: 17px; margin-top: 1px; flex: 0 0 auto; }
.hw-scale-mode:has(input:checked) { border-color: var(--blue); box-shadow: 0 0 0 1px var(--blue); }
.hw-scale-modetx { display: flex; flex-direction: column; gap: 1px; }
.hw-scale-modet { font-weight: 700; }
.hw-scale-modeh { font-size: 12px; font-weight: 400; color: var(--muted); line-height: 1.35; }
.hw-scale-same { display: flex; align-items: center; gap: 7px; font-size: 13px; cursor: pointer; }
.hw-scale-same input { accent-color: var(--blue); cursor: pointer; }
.hw-scale-samerow { display: flex; align-items: center; gap: 8px; font-size: 13px; margin-top: 8px; }
#hwScaleAuto { display: flex; flex-direction: column; gap: 8px; }
#hwScaleCustom { display: flex; flex-direction: column; gap: 8px; }
#hwScalePerRows { display: flex; flex-direction: column; gap: 6px; max-height: 180px; overflow-y: auto; }
.hw-scale-ptrow { display: flex; align-items: center; gap: 8px; }
.hw-scale-ptnum {
  width: 20px; height: 20px; border-radius: 50%; background: var(--blue); color: #fff;
  font-size: 12px; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
}
.hw-scale-ptrow .hw-scale-pointsin { flex: 0 1 90px; }
.hw-scale-preview { display: flex; flex-wrap: wrap; gap: 6px; min-height: 30px; padding: 8px; border: 1px dashed var(--border); border-radius: 8px; }
.hw-scale-preview:empty { display: none; }
.hw-scale-pointsin {
  width: 64px; font-size: 14px; padding: 6px 8px; border-radius: 8px;
  border: 1px solid var(--border); background: var(--bg); color: var(--text);
}
.board-tool-defrow { display: flex; align-items: center; gap: 10px; margin-top: 10px; font-size: 13px; color: var(--muted); }
.board-tool-defrow .btn-ghost { margin-left: auto; }
.hw-scale-def { align-self: flex-start; font-size: 13px; padding: 7px 12px; border-radius: 8px; }
.hw-scale-def:disabled { opacity: .6; cursor: default; }
.hw-scale-note { font-size: 12px; color: var(--muted); line-height: 1.35; }
.hw-scale-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 2px; }
.hw-gr { display: flex; flex-direction: column; gap: 8px; flex: 1 1 auto; min-height: 0; }
.hw-gr label { font-size: 13px; color: var(--muted); }
/* The submitted sheet at the head of the grading dialog: the exercise text with
   the student's own answers in the blanks, so the teacher marks what is in front
   of them. Capped and scrolled so a long sheet cannot push the scale off screen. */
.hw-gr-review { display: flex; flex-direction: column; gap: 6px; flex: 1 1 auto; min-height: 0; }
.hw-gr-rh {
  font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted);
}
.hw-gr-prompt { flex: 1 1 auto; min-height: 120px; max-height: none; overflow: auto; }
.hw-gr-scale { display: flex; gap: 6px; flex-wrap: wrap; }
.hw-gr-none { font-size: 13px; color: var(--muted); font-style: italic; }
.hw-g {
  min-width: 40px; padding: 9px 14px; border: 1px solid var(--border); background: var(--bg);
  border-radius: 7px; cursor: pointer; font-size: 15px; font-weight: 600; color: var(--text);
}
.hw-g.on { background: var(--blue); border-color: var(--blue-dark); color: #fff; }
.hw-gr-when { font-size: 12px; color: var(--muted); }
.hw-gr-blank { display: inline-flex; align-items: center; gap: 4px; vertical-align: baseline; min-width: min-content; }
.hw-gr-blank .hw-gr-av { width: 108px; min-width: min-content; flex: 0 1 auto; }
.hw-gr-awrap { display: inline-flex; align-items: center; min-width: min-content; }
.hw-gr-blank .hw-gr-ok {
  margin-left: 8px; width: 20px; height: 20px; padding: 0; box-sizing: border-box; flex: 0 0 auto;
  border: 1px solid var(--border); background: var(--bg); border-radius: 50%; cursor: pointer; color: var(--muted);
  display: inline-flex; align-items: center; justify-content: center;
}
.hw-gr-blank .hw-blank-in-bad, .hw-gr-blank .hw-gr-chip.hw-blank-in-bad {
  color: var(--muted); text-decoration: line-through;
}
.hw-gr-blank .hw-gr-ok img { width: 12px; height: 12px; }
.hw-gr-blank .hw-gr-ok.on { color: var(--text); }
.hw-gr-blank .hw-gr-ok.ok { background: rgba(31,157,58,.16); border-color: var(--green); }
.hw-gr-blank .hw-gr-ok.bad { background: rgba(224,45,45,.12); border-color: var(--danger); }
.hw-empty-page { flex: 1 1 auto; min-height: 46vh; justify-content: center; font-style: normal; }
.hw-ed-area .hw-blank { vertical-align: baseline; }
.hw-ed-area select.hw-blank { border-bottom: 0; min-width: 130px; }
.hw-ed-area .hw-blank::selection { background: var(--blue-light); }
.hw-ed-area .hw-blank::-moz-selection { background: var(--blue-light); }
.hw-ex-switch {
  position: relative; flex: 0 0 auto; width: 42px; height: 24px; padding: 0;
  border-radius: 12px; border: 1px solid var(--border-dark, var(--border));
  background: var(--bg); cursor: pointer;
}
.hw-ex-switch .hw-ex-knob {
  position: absolute; top: 2px; left: 2px; width: 18px; height: 18px;
  border-radius: 50%; background: var(--muted); transition: left .14s ease;
}
.hw-ex-switch.on { background: var(--blue-light); border-color: var(--blue); }
.hw-ex-switch.on .hw-ex-knob { left: 20px; background: var(--blue); }
.hw-ed-tools .hw-ed-help { margin-left: auto; background: transparent; border: none; cursor: pointer; padding: 6px; border-radius: 4px; display: inline-flex; align-items: center; justify-content: center; }
.hw-ed-tools .hw-ed-help img { width: 16px; height: 16px; opacity: .7; }
.hw-ed-tools .hw-ed-help:hover img { opacity: 1; }
.hw-ed-tools .hw-ed-help.on img { opacity: 1; }
.hw-ed-helpbody {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 60; width: min(340px, calc(100vw - 48px));
  border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; font-size: 13px;
  background: var(--panel); box-shadow: 0 10px 30px rgba(0,0,0,.2);
}
.hw-ed-helpbody p { margin: 6px 0; }
.hw-ed-helpbody ul { margin: 6px 0 6px 18px; padding: 0; }
.hw-ed-inst { width: 100%; border: 1px solid var(--border); border-radius: 6px; padding: 8px 10px; font-size: 14px; font-family: inherit; box-sizing: border-box; background: #fff; color: var(--text); }
.hw-ed-nocopy { display: flex; align-items: center; gap: 8px; cursor: pointer; }
.hw-ed-nocopy input { width: auto; }
.hw-ed-chipfield {
  display: flex; flex-wrap: wrap; gap: 6px; align-content: flex-start; align-items: center;
  border: 1px solid var(--border); border-radius: 6px; padding: 7px 10px; height: min-content; min-height: 44px;
  overflow-y: auto; background: #fff; box-sizing: border-box; cursor: text;
}
.hw-ed-chipinput {
  flex: 1 1 40px; min-width: 40px; border: none !important; outline: none !important; box-shadow: none !important;
  padding: 4px 2px; font-size: 14px; font-family: inherit; background: transparent; color: var(--text);
}
.hw-ed-check { display: flex; align-items: center; gap: 7px; font-size: 13px; color: var(--text); cursor: pointer; }
.hw-draft-bar {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 9px 12px; margin-bottom: 8px; font-size: 13px;
  background: var(--blue-light); border: 1px solid var(--blue); border-radius: 8px;
}
.hw-draft-bar .btn-xs { margin-left: auto; }
.hw-draft-bar .btn-xs + .btn-xs { margin-left: 0; }
.hw-ed-check input { width: 15px; height: 15px; accent-color: var(--blue); cursor: pointer; }
.hw-drag-bank { display: flex; flex-wrap: wrap; gap: 6px; padding: 8px; margin-bottom: 8px; border: 1px dashed var(--border); border-radius: 8px; min-height: 40px; }
.hw-chip { display: inline-flex; align-items: center; gap: 6px; padding: 4px 12px; border: 1px solid var(--blue); background: var(--blue-light); border-radius: 14px; font-size: 14px; cursor: grab; user-select: none; }
.hw-chip.armed { outline: 2px solid var(--blue); }
.hw-chip-x { border: none; background: transparent; cursor: pointer; color: var(--muted); font-size: 14px; padding: 0 0 0 2px; }
.hw-blank-drop {
  display: inline-flex; align-items: center; justify-content: center; min-width: 92px; min-height: 28px;
  padding: 0 4px; margin: 0 2px; box-sizing: border-box; border: 1px dashed var(--border-dark, var(--border));
  border-radius: 14px; vertical-align: -3px; cursor: pointer;
}
.hw-blank-drop.over { background: var(--blue-light); border-style: solid; }
.hw-blank-drop.hw-drop-filled { border-color: transparent; padding: 0; min-height: 0; min-width: 0; }
.hw-blank-drop.hw-drop-filled .hw-chip { white-space: nowrap; }
.hw-blank-drop.hw-blank-in-ok { border-color: var(--green); border-style: solid; }
.hw-blank-drop.hw-blank-in-bad { border-color: var(--danger); border-style: solid; }
.hw-blank-drop .hw-chip { margin: 0; }
.hw-blank-drop .hw-chip-in.hw-chip-ok { background: #e7f4ea; border-color: var(--green); }
.hw-blank-drop .hw-chip-in.hw-chip-wrong { background: #fdeeee; border-color: var(--danger); }
.hw-blank-drop .hw-chip-in .hw-was { color: var(--muted); text-decoration: line-through; font-weight: 400; }
.hw-blank-drop .hw-chip-in .hw-now { color: var(--danger); font-weight: 700; margin-left: 6px; }
.hw-ex-prompt .hw-chip { max-height: 1.55em; overflow: hidden; }
.hw-blank .over { background: var(--blue-light); }
.hw-gr-chip { display: inline-block; padding: 4px 12px; border: 1px solid var(--border); border-radius: 14px; font-size: 14px; background: var(--bg); min-width: 60px; min-height: 1.2em; }
.hw-gr-chip.hw-blank-in-ok { border-bottom-color: var(--green); }
.hw-gr-chip.hw-blank-in-bad { border-bottom-color: var(--danger); }
.hw-ex-nocopy .hw-ex-prompt { user-select: none; -webkit-user-select: none; }
.hw-ex-nocopy-prompt { user-select: none; -webkit-user-select: none; }
.hw-gr-blank .hw-gr-correct {
  width: 110px; flex: 0 1 auto; margin-left: 8px; border: 1px solid var(--border); border-radius: 4px; padding: 4px 8px;
  font-size: 14px; background: #fff; font-family: inherit; box-sizing: border-box;
}
.hw-gr-bottom { margin-top: auto; padding-top: 10px; display: flex; flex-direction: column; gap: 8px; flex: 1 1 auto; min-height: 0; }
.hw-gr-scale { display: flex; align-items: center; flex-wrap: wrap; gap: 4px; }
.hw-gr-scale .hw-gr-clear { margin-left: 10px; }
.hw-gr-when-inline { margin-right: auto; align-self: center; font-size: 12px; color: var(--muted); }
#hwGrFb {
  width: 100%; min-height: 70px; flex: 1 1 auto; resize: vertical; border: 1px solid var(--border); border-radius: 6px;
  padding: 8px 10px; font-size: 14px; font-family: inherit; box-sizing: border-box; background: #fff; color: var(--text);
}
#hwGrFb:focus { outline: none; border-color: var(--accent); }
.hw-gr-clear { align-self: flex-start; font-size: 13px; padding: 7px 14px; }

/* Дневник on small screens: the toolbar stays one row, the exercise padding and
   the table cells shrink, and the matrix keeps its name column pinned. */
@media (max-width: 820px) {
  .hw-toolbar { padding: 8px; gap: 8px; row-gap: 8px; }
  .hw-tabs { order: 1; flex: 1 0 100%; justify-content: space-between; }
  .hw-toolbar-nav { order: 2; margin-left: auto; }
  .hw-toolbar-sub .hw-toolbar-acts { flex: 1 0 100%; }
  .hw-toolbar-acts .hw-add-ex { width: 100%; justify-content: center; }
  .hw-tab-btn { padding: 7px 10px; font-size: 13px; gap: 6px; }
  .hw-toolbar-nav .icon-btn { padding: 8px; }
  .hw-today { padding: 7px 12px; }
  .hw-content { padding: 12px; gap: 12px; }
  .hw-ex { padding: 12px; border-radius: 10px; }
  .hw-ex-head { gap: 7px; }
  .hw-ex-prompt { font-size: 15px; }
  .hw-blank-in { width: 90px; }
  .hw-th, .hw-cell { padding: 7px 6px; min-width: 34px; }
  .hw-table { font-size: 12px; }
  .hw-cal-day { padding: 9px 0; }
}

/* The blank marker as it appears inside the teacher's editor: the author types
   around it, so it is styled in place rather than swapped for an input (which
   is what the student gets). It starts empty — the underline and the min-width
   are what make it visible — and any text typed inside it is the default answer
   the student starts from, so it is drawn in the normal text colour. */
.hw-ed-area .hw-blank {
  display: inline-block; min-width: 92px; padding: 0 4px; margin: 0 2px;
  border-bottom: 1px solid var(--blue); color: var(--text);
}
/* Autograding off: the answers are still in the prompt, just not shown. Only the
   colour is taken away, so the blank keeps its width and the prose around it does
   not reflow as the toggle is flipped. */
.hw-ed-area.hw-ed-hideanswers .hw-blank { color: transparent; }
.hw-submitted { color: var(--muted); font-size: 13px; margin-top: 6px; opacity: .9; }
