:root{
  --ctl:30px;            /* shared height for pill-shaped bar controls */
  --bg:#0b0f17; --panel:#141b28; --panel2:#1b2434; --line:#26324a;
  --fg:#e8eef8; --dim:#93a3bd; --faint:#5f708c;
  --accent:#57c9ff; --accent2:#7dffb0; --warn:#ffc65c; --bad:#ff7a6a;
  --radius:14px;
}
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{height:100%}
body{
  background:var(--bg); color:var(--fg);
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"PingFang SC","Microsoft YaHei",sans-serif;
  line-height:1.55; overflow-x:hidden;
}
.rtl{
  direction:rtl; text-align:right;
  font-family:"Noto Naskh Arabic","Vazirmatn","Geeza Pro","Iranian Sans",Tahoma,serif;
}
.wrap{max-width:1180px;margin:0 auto;padding:0 16px 60px}
h1{font-size:20px;font-weight:800;letter-spacing:.02em}
h2{font-size:13px;font-weight:700;color:var(--dim);letter-spacing:.14em;text-transform:uppercase;margin-bottom:10px}
.sub{color:var(--dim);font-size:13px}

/* Sticky app bar.
   `viewport-fit=cover` + black-translucent status bar means an installed web
   app paints UNDER the iOS status bar, so without env(safe-area-inset-top) the
   title sits behind the clock and notch — the bar looked simply missing.
   The negative margin lets it span the full width inside .wrap's padding. */
.topbar{
  position:sticky; top:0; z-index:20;
  display:flex; align-items:center; gap:10px 12px; flex-wrap:wrap;
  margin:0 -16px 18px; padding:calc(12px + env(safe-area-inset-top)) 16px 12px;
  min-height:34px;
  background:rgba(11,15,23,.9); backdrop-filter:blur(10px);
  border-bottom:1px solid var(--line);
}
.brand{display:flex;align-items:center;gap:9px}
/* h1 and the status pill are different type sizes, so equal line boxes are what
   actually make them look aligned — centring alone leaves the pill sitting low. */
.brand h1{line-height:1.2}
.dot{width:10px;height:10px;border-radius:50%;background:var(--faint);flex:none}
.dot.on{background:var(--accent2);box-shadow:0 0 12px var(--accent2)}
.dot.bad{background:var(--bad)}
.spacer{flex:1}

.panel{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:16px;margin-bottom:14px}
.grid{display:grid;gap:12px}
@media(min-width:860px){ .grid.two{grid-template-columns:1fr 1fr} }

label{display:block;font-size:12px;color:var(--dim);margin-bottom:5px;font-weight:600}
input,textarea,select{
  width:100%;background:var(--panel2);border:1px solid var(--line);color:var(--fg);
  border-radius:10px;padding:10px 12px;font-size:14px;font-family:inherit;
}
/* Removing the outline entirely left keyboard users with only a border-colour
   change — invisible to anyone who can't distinguish it. :focus-visible shows a
   real ring for keyboard users without putting one on every mouse click. */
input:focus,textarea:focus,select:focus{outline:none;border-color:var(--accent)}
:where(button,a,input,textarea,select,summary,[tabindex]):focus-visible{
  outline:2px solid var(--accent); outline-offset:2px; border-radius:8px;
}
textarea{resize:vertical;min-height:74px;line-height:1.5}
.field{margin-bottom:12px}
.hint{font-size:11.5px;color:var(--faint);margin-top:4px;line-height:1.5}

button{
  background:var(--panel2);border:1px solid var(--line);color:var(--fg);
  border-radius:11px;padding:11px 18px;font-size:14px;font-weight:700;cursor:pointer;
  font-family:inherit;transition:.15s;
}
button:hover{border-color:var(--accent)}
button:disabled{opacity:.45;cursor:not-allowed}
.primary{background:linear-gradient(180deg,#4fc4ff,#2a86d8);border-color:transparent;color:#04121f}
.danger{background:linear-gradient(180deg,#ff8a76,#d9503c);border-color:transparent;color:#210806}
.row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}

.code{
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:30px;font-weight:800;
  letter-spacing:.22em;color:var(--accent2);
}
.joinbox{display:flex;gap:14px;align-items:center;flex-wrap:wrap}
.qr{background:#fff;padding:8px;border-radius:10px;line-height:0}
.qr img,.qr canvas{display:block;width:120px;height:120px}

.meter{height:6px;background:var(--panel2);border-radius:4px;overflow:hidden;margin:10px 0 4px}
.meter i{display:block;height:100%;width:0;background:linear-gradient(90deg,var(--accent2),var(--accent));transition:width .1s}

.lines{display:flex;flex-direction:column;gap:12px}
.line{border-left:3px solid var(--line);padding:2px 0 2px 12px;animation:in .25s ease both}
.line.pending{opacity:.62;border-left-color:var(--warn)}
.line.failed{border-left-color:var(--bad)}
.line .dari{font-size:20px;line-height:1.75}
.line .src{font-size:12.5px;color:var(--dim);margin-top:3px}
/* What the recogniser heard, shown only where the corrector changed words. */
.line .heard{font-size:11.5px;color:var(--warn);opacity:.85;margin-top:2px}
@keyframes in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}

.draft{color:var(--faint);font-style:italic;min-height:22px;font-size:14px;margin-top:8px}
.err{color:var(--bad);font-size:12px;margin-top:8px;word-break:break-word}
.pill{
  display:inline-flex; align-items:center; height:var(--ctl); padding:0 12px;
  font-size:11.5px; font-weight:600; color:var(--dim);
  border:1px solid var(--line); border-radius:999px; line-height:1; white-space:nowrap;
}
.pill.live{color:var(--accent2); border-color:rgba(125,255,176,.35); background:rgba(125,255,176,.08)}
.pill.ended{color:var(--faint)}

/* Recovery banner after the console was reopened mid-session. Deliberately the
   loudest thing on the panel: the room is reading a transcript that has stopped
   growing, and the only person who can fix that is looking at this. */
.resumebar{
  display:flex; align-items:center; gap:14px; flex-wrap:wrap;
  margin:0 0 14px; padding:12px 14px;
  border:1px solid rgba(255,198,92,.38); border-radius:var(--radius);
  background:rgba(255,198,92,.09);
}
.resumebar strong{color:var(--warn); font-size:14px}
.resumebar .sub{margin-top:2px}
.resumebar > div:first-child{flex:1 1 220px}
.resumebar button{flex:0 0 auto}
/* `display:flex` above outranks the UA rule behind the `hidden` attribute, so this
   banner — "Session interrupted… Press Resume" — never went away, even mid-service
   with Stop showing. Reported 2026-10-11/13 as the console looking interrupted while
   it was live. Every other hidden-toggled box here has its own [hidden] rule; this
   one was missing it. */
.resumebar[hidden]{display:none}
.ui-rtl .resumebar{flex-direction:row-reverse}

/* ── audience view ── */
.stage{min-height:100vh;display:flex;flex-direction:column;padding:0}
.stagebar{
  display:flex;align-items:center;gap:10px;
  padding:calc(10px + env(safe-area-inset-top)) 14px 10px;
  border-bottom:1px solid var(--line);background:rgba(11,15,23,.92);
  position:sticky;top:0;z-index:30;backdrop-filter:blur(8px);
}
/* The space under the newest line is there so the reader's eye rests above
   the bottom edge instead of chasing it. At 40vh it was paying for that with
   half the screen: measured on a 1366x768 room monitor, 48% of the display was
   blank and only three lines were readable at once — which is also why a long
   line felt like it vanished so fast, since the next one pushed it out of a
   three-line window. A phone keeps a real gap; a monitor at the front of a
   room, where the whole point is that many people read it from a distance,
   keeps only enough to breathe. */
.feed{flex:1;padding:20px 18px 20vh;max-width:1000px;margin:0 auto;width:100%}
@media (min-width:900px) and (orientation:landscape){
  .feed{padding-bottom:8vh}
}
.entry{margin-bottom:26px;animation:in .3s ease both}
.entry .dari{font-size:var(--dsize,30px);line-height:1.85;font-weight:500}
.entry .src{font-size:calc(var(--dsize,30px) * .44);color:var(--faint);margin-top:8px;line-height:1.5}
.entry.pending .dari{opacity:.55}
.entry.live{border-left:3px solid var(--warn);padding-left:14px}
.iconbtn{padding:8px 12px;font-size:13px}
/* Top-bar controls share the pill height so the bar reads as one row. */
.topbar .iconbtn{
  display:inline-flex; align-items:center; justify-content:center;
  height:var(--ctl); padding:0 12px; font-size:14px; line-height:1;
}
.topnav{display:flex; align-items:center; gap:8px; margin-left:auto}
.ui-rtl .topnav{margin-left:0; margin-right:auto}
/* A long session title must never push the language pills off the bar. */
.stagebar .big{
  flex:1 1 auto; min-width:0; font-size:15px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.big{font-size:15px}

/* ── audience display menu ────────────────────────────────────────────
   Everything an audience member sets once lives here, with words rather
   than bare glyphs — "⛶" and "A−" mean nothing to most readers. */
.menuwrap{position:relative; flex:none}
.menubtn{font-size:18px; line-height:1; letter-spacing:.06em}
.menu{
  position:absolute; top:calc(100% + 8px); inset-inline-end:0; z-index:40;
  min-width:min(304px, calc(100vw - 28px));
  background:var(--panel); border:1px solid var(--line); border-radius:14px;
  padding:6px; box-shadow:0 18px 44px rgba(0,0,0,.55);
}
.menu[hidden]{display:none}
.menuitem{
  display:flex; align-items:center; justify-content:space-between; gap:12px;
  width:100%; min-height:44px; padding:8px 12px;
  background:transparent; border:none; border-radius:10px;
  font-size:14px; font-weight:600; color:var(--fg); text-align:start;
}
button.menuitem:hover{background:var(--panel2); border:none}
.menuitem.static{cursor:default}
/* Item labels sit opposite a control; letting them wrap made the row two lines
   tall and knocked the menu out of rhythm. */
.menuitem > span:first-child{white-space:nowrap}
.menuitem .tick{color:var(--accent2); font-size:13px; opacity:0; transition:opacity .12s}
.menuitem[aria-checked="true"] .tick{opacity:1}
.sizectl{display:inline-flex; align-items:center; gap:8px}
.sizectl button{
  min-width:38px; min-height:38px; padding:0; font-size:13px; font-weight:700;
  background:var(--panel2); border:1px solid var(--line); border-radius:10px; color:var(--fg);
}
.sizectl b{min-width:26px; text-align:center; font-size:12.5px; color:var(--dim); font-weight:700}
.menu .uisel{--collapsible:0}
.menu .uisel .uibtn{display:inline-flex}
.center{text-align:center}
.empty{color:var(--faint);text-align:center;padding:60px 20px;font-size:15px}

/* ── audience language selector (top-right) ── */
.langsel{display:flex;gap:4px;background:var(--panel2);border:1px solid var(--line);
  border-radius:999px;padding:3px}
/* A two-character CJK label wrapped to two lines and stretched the whole
   pill group; nothing in a language switch should ever wrap. */
.langsel .lang{white-space:nowrap;border:none;background:transparent;border-radius:999px;
  padding:7px 13px;font-size:13.5px;font-weight:700;color:var(--dim)}
.langsel .lang.on{background:linear-gradient(180deg,#4fc4ff,#2a86d8);color:#04121f}
.langsel .lang:hover{border:none;color:var(--fg)}

/* ── simplified presenter ── */
.big-start{font-size:19px;padding:15px 46px;border-radius:14px}
details.adv{padding:0}
details.adv summary{
  cursor:pointer;padding:15px 16px;font-size:13.5px;font-weight:700;color:var(--dim);
  list-style:none;user-select:none;
}
details.adv summary::-webkit-details-marker{display:none}
details.adv summary::before{content:"▸ ";color:var(--accent)}
details.adv[open] summary::before{content:"▾ "}
details.adv summary:hover{color:var(--fg)}
details.adv > *:not(summary){margin-left:16px;margin-right:16px}
details.adv > *:last-child{margin-bottom:16px}

/* ── target-language picker ── */
.langpick{display:flex;flex-wrap:wrap;gap:7px}
.langpick button{
  display:inline-flex; align-items:center; justify-content:center; white-space:nowrap;
  height:38px; padding:0 14px; font-size:13px; border-radius:999px;
  font-weight:600; color:var(--dim); line-height:1;
}
/* The tick is always in the box, just invisible when off. Revealing it on
   selection used to add ~2px of line box and a few px of width, so every
   toggle nudged the whole wrapped grid. Reserving the space fixes both. */
.langpick button::before{
  content:"✓"; display:inline-block; width:1em; margin-right:5px;
  font-size:11px; text-align:center; opacity:0; transition:opacity .15s;
}
.langpick button.on::before{opacity:1}
.langpick button.on{color:#04121f;background:linear-gradient(180deg,#7dffb0,#2fc088);border-color:transparent}

/* ── UI language switch ── */
.uisel{display:inline-flex;gap:2px;background:var(--panel2);border:1px solid var(--line);
  border-radius:999px;padding:2px;--collapsible:0}
.uisel .uibtn{display:inline-flex;align-items:center;justify-content:center;white-space:nowrap;
  border:none;background:transparent;border-radius:999px;height:calc(var(--ctl) - 6px);
  padding:0 11px;font-size:12.5px;font-weight:700;color:var(--dim);line-height:1}
.uisel .uibtn.on{background:linear-gradient(180deg,#4fc4ff,#2a86d8);color:#04121f}
.uisel .uibtn:hover{border:none;color:var(--fg)}

/* ── editable session code ── */
.codeinput{
  width:auto;min-width:200px;background:transparent;border:1px dashed var(--line);
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:30px;font-weight:800;
  letter-spacing:.22em;color:var(--accent2);padding:4px 10px;border-radius:10px;
}
.coderow{display:flex; align-items:center; gap:8px}
.newcode{
  flex:none; width:44px; height:44px; padding:0; font-size:17px; line-height:1;
  display:inline-flex; align-items:center; justify-content:center;
}
.newcode:disabled{opacity:.4}
.codeinput:hover{border-color:var(--accent)}
.codeinput:disabled{opacity:.7;border-style:solid;cursor:not-allowed}

/* ── RTL operator console ── */
.ui-rtl .wrap{direction:rtl}
.ui-rtl .line{border-left:none;border-right:3px solid var(--line);padding:2px 12px 2px 0}
.ui-rtl .line.pending{border-right-color:var(--warn)}
.ui-rtl .line.failed{border-right-color:var(--bad)}
.ui-rtl .code, .ui-rtl #joinUrl{direction:ltr;text-align:left}
.ui-rtl .langpick, .ui-rtl .row{direction:rtl}
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Noto Naskh Arabic","PingFang SC","Microsoft YaHei",sans-serif}

/* ═══════════════════════════════════════════════════════════════
   Responsive / overflow hardening
   Flex children default to min-width:auto, so a wide child (the code
   input, a long URL) pushes past its card instead of shrinking. This
   block makes every container able to shrink, then tunes for phones.
   ═══════════════════════════════════════════════════════════════ */
.panel, .field, .row, .joinbox, .joinbox > *, .brand, .topbar { min-width: 0; }
.panel { overflow: hidden; }
input, textarea, select, .codeinput { max-width: 100%; box-sizing: border-box; }

.codeinput{
  /* Sized to its content (12 chars max) rather than stretching the full panel —
     a 6-character code in a 960px box read as a broken input. */
  width: 100%; max-width: 9.5em; min-width: 0;
  font-size: clamp(22px, 7vw, 30px);
  letter-spacing: clamp(.10em, 3vw, .22em);
  text-align: center;
}
.ui-rtl .codeinput{ margin-left:auto }
#joinUrl { overflow-wrap: anywhere; }

.joinbox { align-items: flex-start; }
.joinbox > div:last-child { display:flex; flex-direction:column; align-items:flex-start; }
.joinbox > div:last-child { flex: 1 1 240px; }
.qr img, .qr canvas { width: clamp(96px, 28vw, 120px); height: clamp(96px, 28vw, 120px); }

/* language pills: cap the height on phones so the Start button stays reachable */
.langpick { max-height: 168px; overflow-y: auto; padding-right: 2px; }
/* Capping mid-row looked like a rendering fault. Fade the cut edge so a
   half-visible row reads as "more below" — applied only when it overflows. */
.langpick.scrollable{
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 26px), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 26px), transparent);
}
.langpick::-webkit-scrollbar { width: 4px; }
.langpick::-webkit-scrollbar-thumb { background: var(--line); border-radius: 4px; }

@media (max-width: 640px){
  .wrap { padding: 12px 12px 48px; }
  .panel { padding: 14px; border-radius: 12px; }
  h1 { font-size: 18px; }
  .topbar { gap: 8px; padding-left: 12px; padding-right: 12px; margin: 0 -12px 14px; }
  /* The tagline is decorative; on a phone it cost a whole row above the fold. */
  .topbar .sub { display: none; }
  .topbar h1 { font-size: 17px; }
  .topnav { gap: 6px; }
  .uisel .uibtn { padding: 0 9px; }
  /* Collapsed: show only the active language, with a caret to say it opens. */
  .uisel.collapsible { --collapsible: 1; }
  .uisel.collapsible:not(.open) .uibtn:not(.on) { display: none; }
  .uisel.collapsible:not(.open) .uibtn.on::after { content: " ⌄"; opacity: .7; font-size: 10px; }
  .big-start { width: 100%; text-align: center; padding: 15px 20px; }
  .row > button { flex: 1 1 auto; }
  .langpick button { padding: 8px 12px; font-size: 12.5px; }
  .grid.two { grid-template-columns: 1fr; }
  .joinbox { gap: 12px; }
  .stagebar { gap: 6px; padding: 8px 10px; }
  .langsel .lang, .uisel .uibtn { padding: 7px 10px; font-size: 12.5px; }
}
@media (max-width: 380px){
  .code, .codeinput { letter-spacing: .06em; }
  .iconbtn { padding: 7px 9px; }
}

/* Respect users who ask for less motion. */
@media (prefers-reduced-motion: reduce){
  .start-btn { animation: none; }
  * { transition-duration: .01ms !important; }
}

/* Key material must never render in the clear — the other key fields are
   password inputs, and a textarea has no equivalent, so mask it in CSS. */
textarea.secret {
  -webkit-text-security: disc;
  text-security: disc;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .04em;
}

/* ── site footer ───────────────────────────────────────────────────────
   Deliberately quiet: it sits under a working tool, so it identifies the
   product and offers a contact route without competing with the content. */
.sitefoot{
  margin-top:34px; padding-top:20px; border-top:1px solid var(--line);
  color:var(--faint); font-size:12.5px;
}
.footinner{
  display:flex; align-items:center; gap:18px 24px; flex-wrap:wrap;
  justify-content:space-between;
}
.footbrand{display:flex; align-items:center; gap:11px; min-width:0}
.footbrand img{border-radius:7px; opacity:.9; flex:none}
.footbrand strong{
  display:block; color:var(--dim); font-size:13px;
  font-weight:700; letter-spacing:.02em; line-height:1.3;
}
.footbrand span{display:block; font-size:11.5px; color:var(--faint); line-height:1.4}
.footcontact{display:flex; align-items:center; gap:7px; flex-wrap:wrap; min-width:0}
.sitefoot a{
  color:var(--accent); text-decoration:none;
  border-bottom:1px solid transparent; transition:.15s; word-break:break-word;
}
.sitefoot a:hover{border-bottom-color:var(--accent)}
.footlegal{
  margin-top:16px; padding-top:14px; border-top:1px solid var(--line);
  display:flex; gap:8px 18px; flex-wrap:wrap; justify-content:space-between;
  font-size:11.5px; color:var(--faint);
}

/* Audience view: one quiet line, so it never competes with the transcript. */
.stagefoot{
  flex:none; padding:10px 16px calc(10px + env(safe-area-inset-bottom));
  border-top:1px solid var(--line); background:rgba(11,15,23,.92);
  display:flex; align-items:center; gap:6px 14px; flex-wrap:wrap;
  justify-content:center; font-size:11.5px; color:var(--faint); text-align:center;
}
.stagefoot a{color:var(--accent); text-decoration:none}
.stagefoot a:hover{text-decoration:underline}
.stagefoot .sep{opacity:.45}
@media(max-width:520px){
  .footinner{gap:14px}
  .stagefoot .sep{display:none}
  .stagefoot{flex-direction:column; gap:3px}
}

/* ── toast ────────────────────────────────────────────────────────────
   Anchored to the viewport, because the labels it replaces could be
   scrolled off-screen at the moment they had something to say. */
.toast{
  position:fixed; left:50%; bottom:calc(22px + env(safe-area-inset-bottom));
  transform:translate(-50%,14px);
  max-width:min(520px,calc(100vw - 32px));
  padding:11px 16px; border-radius:12px;
  background:var(--panel2); border:1px solid var(--line); color:var(--fg);
  font-size:13.5px; line-height:1.45; text-align:center;
  box-shadow:0 10px 30px rgba(0,0,0,.45);
  opacity:0; pointer-events:none; z-index:100;
  transition:opacity .18s ease, transform .18s ease;
}
.toast.show{opacity:1; transform:translate(-50%,0)}
.toast[data-kind="bad"]{border-color:rgba(255,122,106,.5); color:#ffd9d3}

/* ── offline banner ───────────────────────────────────────────────────
   A live session that has quietly stopped reaching the relay looks
   identical to one that is simply between sentences. Say so. */
body.offline .topbar::after,
body.offline .stagebar::after{
  content:attr(data-offline); position:absolute; left:0; right:0; top:100%;
  padding:5px 12px; font-size:11.5px; font-weight:600; text-align:center;
  background:var(--bad); color:#210806;
}
.topbar,.stagebar{position:sticky}
/* The banner is absolutely positioned, so it takes no space and would sit on
   top of the first panel. Grow the bar's margin to make room for it. */
body.offline .topbar{margin-bottom:calc(18px + 26px)}
body.offline .stagebar{margin-bottom:26px}

/* ── touch targets ────────────────────────────────────────────────────
   Anything tappable wants ~44px on its short axis. Grow the control itself
   rather than overlaying a pseudo-element, which would collide with the
   caret that .uibtn.on::after already draws. */
@media (pointer:coarse){
  .topbar .iconbtn,.stagebar .iconbtn{min-height:40px;min-width:40px}
  .uisel{padding:4px}
  .uisel .uibtn{min-height:34px;padding:0 13px}
  .langsel .lang{min-height:34px}
  .langpick button{height:42px}
}

/* Motion is subtle, but honour the preference everywhere. */
@media (prefers-reduced-motion: reduce){
  .toast{transition:none}
}

/* The top bar collapses the interface-language switch because it is cramped.
   Inside the display menu there is room for all three, and collapsing there
   left only one language reachable — so undo it, after the media query that
   sets it (equal specificity, so source order decides). */
.menu .uisel.collapsible{--collapsible:0}
/* Must out-specify `.uisel.collapsible:not(.open) .uibtn:not(.on)` — :not()
   contributes its argument's specificity, so that selector scores 5 classes
   and a plainer override silently loses. */
.menu .uisel.collapsible:not(.open) .uibtn:not(.on){display:inline-flex}
.menu .uisel.collapsible:not(.open) .uibtn.on::after{content:none}

/* ── settings panel ───────────────────────────────────────────────────
   Glossary, source language, context and keys previously ran together as
   one undifferentiated column of fields. Grouping them makes the panel
   scannable and gives the key section somewhere to state hosted mode. */
.settings-group{padding-top:16px}
.settings-group + .settings-group{margin-top:6px; border-top:1px solid var(--line)}
.settings-group > h2{margin-bottom:12px}
.settings-group .row{margin-bottom:10px}
textarea.short{min-height:52px}
.subfold > summary{cursor:pointer; padding:8px 0; font-weight:600}
.subfold > summary:hover{color:var(--fg)}
.subfold .field, .subfold .grid{margin-top:10px}

/* Says what is true on a hosted deployment: no key required. Without it the
   panel asks for a key it does not need, which reads as a broken setup. */
.notice{
  margin:0 0 14px; padding:11px 13px; border-radius:11px;
  background:rgba(87,201,255,.09); border:1px solid rgba(87,201,255,.28);
  color:var(--dim); font-size:12.5px; line-height:1.55;
}
.notice[hidden]{display:none}

/* ── interface-language dropdown ──────────────────────────────────────
   Replaces an inline expansion that grew the bar on every tap while
   showing a caret that promised a popover. Same interaction model as the
   audience "⋯" menu, so both surfaces behave alike. */
.uidrop{position:relative; display:inline-flex; flex:none}
.uidrop-trigger{
  display:inline-flex; align-items:center; gap:5px;
  height:var(--ctl); padding:0 11px;
  background:var(--panel2); border:1px solid var(--line); border-radius:999px;
  font-size:12.5px; font-weight:700; color:var(--fg); line-height:1; white-space:nowrap;
}
.uidrop-trigger .caret{font-size:10px; opacity:.7; transition:transform .15s}
.uidrop.open .uidrop-trigger{border-color:var(--accent)}
.uidrop.open .uidrop-trigger .caret{transform:rotate(180deg)}
.uidrop-menu{
  position:absolute; top:calc(100% + 8px); inset-inline-end:0; z-index:60;
  min-width:172px; padding:6px;
  background:var(--panel); border:1px solid var(--line); border-radius:13px;
  box-shadow:0 16px 40px rgba(0,0,0,.55);
}
.uidrop-menu[hidden]{display:none}
.uidrop-menu button{
  display:flex; align-items:center; justify-content:space-between; gap:12px;
  width:100%; min-height:42px; padding:8px 11px;
  background:transparent; border:none; border-radius:9px;
  font-size:14px; font-weight:600; color:var(--fg); text-align:start; white-space:nowrap;
}
.uidrop-menu button:hover{background:var(--panel2); border:none}
.uidrop-menu .tick{color:var(--accent2); font-size:12px; opacity:0}
.uidrop-menu button[aria-selected="true"] .tick{opacity:1}
.uidrop-menu button[aria-selected="true"]{color:var(--accent)}

/* A connection gap in the presenter's own log. Deliberately quiet — it is a
   note to whoever edits the recording, not an alarm mid-service. */
.line.mark{border-left-color:var(--warn);opacity:.75}
.line.mark .src{font-style:italic;text-align:center}
