/* heydok Videosprechstunde — OFFICIAL brand theme.
   Source: "Brand- & Styleguide heydok" (January 2026 PDF, repo root):
     #002d50 dark blue (primary color + text) · #911818 dark red (accents/CTAs)
     #d6e4ef light blue (background elements) · #c93c3c light red (gradient end)
     gradient: linear-gradient(135deg,#911818,#c93c3c)
     font: Inter Tight (self-hosted, assets/fonts) — 400 body / 500 buttons /
     600 highlights / 700 headings · pill buttons & badges · cards w/ subtle
     shadows · clean, breathable, accessible.
   Buttons follow the guide's examples: primary CTA is red and turns NAVY on
   hover; secondary is navy text/outline and fills navy on hover.
   Class contract (selectors/ids) is unchanged — only the visual language. */

@font-face{
  font-family:"Inter Tight";
  font-style:normal;
  font-weight:100 900;
  font-display:swap;
  src:url("/assets/fonts/inter-tight-latin.woff2") format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:"Inter Tight";
  font-style:normal;
  font-weight:100 900;
  font-display:swap;
  src:url("/assets/fonts/inter-tight-latin-ext.woff2") format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

:root{
  --navy:#002d50;--red:#911818;--red-light:#c93c3c;--blue-bg:#d6e4ef;
  --bg:#fbfdff;--panel:#ffffff;--border:#dbe7f1;--divider:#e6eef6;
  --text:#002d50;--muted:#51708c;
  --grad:linear-gradient(135deg,#911818 0%,#c93c3c 100%);
}
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:"Inter Tight",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial,sans-serif;
  font-weight:400;background:var(--bg);color:var(--text);line-height:1.55;min-height:100vh;position:relative}
/* Brand background (styleguide) — applies to EVERY page (body-level, fixed):
   ::before = ECG graph paper (fine 9px + bold 45px red grid, like an EKG printout)
   over soft brand-blue washes; ::after = gradient "snake" ribbons + a ring accent.
   All low-contrast so text/cards stay perfectly legible. */
body::before{content:"";position:fixed;inset:0;z-index:-2;pointer-events:none;
  background:
    linear-gradient(rgba(145,24,24,.05) 1px,transparent 1px) 0 0 / 9px 9px,
    linear-gradient(90deg,rgba(145,24,24,.05) 1px,transparent 1px) 0 0 / 9px 9px,
    linear-gradient(rgba(145,24,24,.09) 1px,transparent 1px) 0 0 / 45px 45px,
    linear-gradient(90deg,rgba(145,24,24,.09) 1px,transparent 1px) 0 0 / 45px 45px,
    radial-gradient(1100px 540px at 90% -8%,rgba(214,228,239,.5),transparent 60%),
    radial-gradient(960px 560px at -10% 112%,rgba(214,228,239,.42),transparent 60%),
    radial-gradient(560px 560px at 6% 4%,rgba(214,228,239,.3),transparent 62%),
    linear-gradient(180deg,rgba(255,247,247,.5),rgba(251,253,255,.5))}
/* ::after = gradient "snake" ribbons (styleguide). opacity keeps them a soft watermark. */
body::after{content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;opacity:.14;
  background-repeat:no-repeat,no-repeat,no-repeat;
  background-position:right -50px top -34px,left -74px bottom -30px,left 34px top 56%;
  background-size:410px 410px,350px 350px,250px 250px;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23911818'/%3E%3Cstop offset='1' stop-color='%23c93c3c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M20 300 C 140 180 260 360 380 220' fill='none' stroke='url(%23g)' stroke-width='34' stroke-linecap='round'/%3E%3Cpath d='M40 360 C 160 250 280 410 390 300' fill='none' stroke='url(%23g)' stroke-width='18' stroke-linecap='round' opacity='0.6'/%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cdefs%3E%3ClinearGradient id='b' x1='0' y1='1' x2='1' y2='0'%3E%3Cstop offset='0' stop-color='%23002d50'/%3E%3Cstop offset='1' stop-color='%234a90e2'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M10 240 C 120 110 300 300 390 130' fill='none' stroke='url(%23b)' stroke-width='30' stroke-linecap='round'/%3E%3Cpath d='M0 300 C 120 200 260 360 380 250' fill='none' stroke='url(%23b)' stroke-width='14' stroke-linecap='round' opacity='0.55'/%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cdefs%3E%3ClinearGradient id='g2' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23911818'/%3E%3Cstop offset='1' stop-color='%23c93c3c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M40 120 C 160 40 260 260 370 170' fill='none' stroke='url(%23g2)' stroke-width='22' stroke-linecap='round'/%3E%3C/svg%3E")}
[dir="rtl"] body::after{transform:scaleX(-1)}

/* brand header — white bar, navy wordmark, red gradient accent line (styleguide "snake" gradient) */
.topbar{background:#fff;border-bottom:1px solid var(--divider);position:relative}
.topbar::after{content:"";display:block;height:3px;background:var(--grad)}
.topbar-in{max-width:1000px;margin:0 auto;padding:12px 20px;display:flex;align-items:center;gap:14px}
.brand-logo{height:40px;width:auto;display:block}
/* "Videosprechstunde" styled as part of the heydok wordmark: navy, heading weight,
   tight tracking, with the brand's red EKG pulse as accent (like the logo's "o") */
.brand-sub{display:inline-flex;align-items:center;gap:9px;color:var(--navy);
  font-size:19px;font-weight:700;letter-spacing:-.02em;
  border-left:1px solid var(--blue-bg);padding-left:14px;line-height:1}
[dir="rtl"] .brand-sub{border-left:none;border-right:1px solid var(--blue-bg);padding-left:0;padding-right:14px}
.brand-sub::after{content:"";width:26px;height:14px;flex-shrink:0;
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 26 14'%3E%3Cdefs%3E%3ClinearGradient id='p' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23911818'/%3E%3Cstop offset='1' stop-color='%23c93c3c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M0 8h6l3-6 4 11 3-8 2 3h8' fill='none' stroke='url(%23p)' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat center/contain}
@media(max-width:480px){.brand-sub{display:none}}
/* language switcher on the far right of the brand bar */
.lang-mount{margin-left:auto}
[dir="rtl"] .lang-mount{margin-left:0;margin-right:auto}
.lang-select{font-family:inherit;font-size:13px;font-weight:600;color:var(--navy);background:#fff;
  border:1.5px solid var(--blue-bg);border-radius:999px;padding:6px 12px;cursor:pointer}
.lang-select:focus{outline:none;border-color:var(--navy);box-shadow:0 0 0 3px rgba(0,45,80,.14)}
/* build-version tag right of the language switcher (filled from /api/config) */
.ver{font-size:11px;font-weight:600;color:var(--muted);letter-spacing:.02em;white-space:nowrap}

/* title with hover-info icon (login) + E2EE lock (room). Native title = tooltip. */
.title-row{display:flex;align-items:center;gap:10px}
.info-dot{display:inline-flex;align-items:center;justify-content:center;cursor:help;color:var(--navy);opacity:.65}
.info-dot svg{width:19px;height:19px}
.info-dot:hover,.info-dot:focus{opacity:1;outline:none}
.e2ee-lock{display:inline-flex;align-items:center;justify-content:center;vertical-align:middle;cursor:help;
  width:30px;height:30px;border-radius:50%;background:#eef2f6;color:var(--muted);transition:.15s;margin-left:6px}
.e2ee-lock svg{width:16px;height:16px}
.e2ee-lock[data-state="active"]{background:rgba(30,94,58,.13);color:#1e5e3a}
.e2ee-lock[data-state="error"]{background:rgba(145,24,24,.13);color:var(--red)}
.e2ee-lock:focus{outline:none;box-shadow:0 0 0 3px rgba(0,45,80,.14)}

.wrap{max-width:760px;margin:0 auto;padding:30px 20px 64px}
.center{max-width:470px}
h1{font-size:24px;font-weight:700;letter-spacing:-.01em;margin-bottom:4px;color:var(--navy)}
h2{font-size:16.5px;font-weight:700;margin:26px 0 10px;color:var(--navy)}
p.muted{color:var(--muted);font-size:14px}
b{font-weight:600}
.card{background:var(--panel);border:1px solid var(--border);border-radius:18px;padding:26px;
  margin-top:18px;box-shadow:0 6px 18px rgba(0,45,80,.06)}
label{display:block;font-size:13px;color:var(--muted);margin:12px 0 5px;font-weight:600}
input{width:100%;background:#fff;border:1px solid #c3d4e3;border-radius:10px;
  color:var(--text);padding:11px 13px;font-size:14px;font-family:inherit}
input::placeholder{color:#8aa2b8}
input:focus{outline:none;border-color:var(--navy);box-shadow:0 0 0 3px rgba(0,45,80,.14)}

/* pill buttons (styleguide): primary red -> navy on hover; secondary fills navy */
button{background:var(--red);color:#fff;border:none;border-radius:999px;padding:11px 22px;
  font-size:14px;font-weight:500;font-family:inherit;cursor:pointer;margin-top:16px;
  transition:background .15s,color .15s,box-shadow .15s}
button:hover:not(:disabled){background:var(--navy);box-shadow:0 4px 14px rgba(0,45,80,.22)}
button.sec{background:#fff;color:var(--navy);border:1.5px solid var(--navy)}
button.sec:hover:not(:disabled){background:var(--navy);color:#fff;box-shadow:none}
button.danger{background:var(--red)}
button.danger:hover:not(:disabled){background:#6d1212;box-shadow:0 4px 14px rgba(145,24,24,.28)}
button:disabled{opacity:.5;cursor:not-allowed}
button:focus-visible{outline:2px solid var(--navy);outline-offset:2px}
.row{display:flex;gap:10px;flex-wrap:wrap}
.row button{flex:1;margin-top:0}
a{color:var(--navy);font-weight:500}
.msg{font-size:13px;margin-top:12px;padding:10px 14px;border-radius:12px;display:none}
.login-hint{margin:6px 0 2px;font-size:13.5px}
.msg.err{display:block;background:rgba(145,24,24,.07);color:var(--red);border:1px solid rgba(145,24,24,.3)}
.msg.ok{display:block;background:var(--blue-bg);color:var(--navy);border:1px solid #bcd3e6}

/* pill badges (styleguide) — E2EE indicator: trust-blue active, red error */
.tag{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:700;text-transform:uppercase;
  letter-spacing:.04em;padding:0;border-radius:0;background:transparent;color:var(--navy);border:none}
.tag.off{background:transparent;color:var(--muted)}
.tag.err{background:transparent;color:var(--red)}
.tag.live{color:#1e5e3a}
.tag.live::before{content:"";width:6px;height:6px;border-radius:50%;background:#1e5e3a;
  box-shadow:0 0 0 3px rgba(30,94,58,.16);flex-shrink:0}

.link-box{background:var(--blue-bg);border:1px solid #bcd3e6;border-radius:10px;padding:10px 12px;
  font-size:12.5px;word-break:break-all;color:var(--navy);margin-top:8px;
  font-family:ui-monospace,Menlo,Consolas,monospace}
ul.mt{list-style:none;margin-top:8px}
ul.mt li{padding:10px 14px;border:1px solid var(--divider);border-radius:12px;margin-top:8px;font-size:13.5px;
  display:flex;justify-content:space-between;align-items:center;gap:10px;background:#fff}
/* Consent text shows in full — no inner scroll box, so the patient sees the
   whole Einwilligung at once before checking the box. */
.consent{font-size:13px;color:var(--muted);background:#f3f8fc;border:1px solid var(--divider);
  border-radius:12px;padding:13px;margin-top:8px}
.checkrow{display:flex;align-items:flex-start;gap:10px;margin-top:14px;font-size:13.5px}
.checkrow input{width:auto;margin-top:3px;accent-color:var(--navy)}

/* ===== meeting room: remote big + own PiP + controls-on-video + sidebar =====
   The room uses the FULL browser window: full width, stage sized to the viewport. */
.room{max-width:none;padding:14px 18px 22px}
.room h1{font-size:20px;margin-bottom:0}
/* patient step 1 (name + consent) and step 2 (Techniktest) stay narrow centered cards */
.room #joinStep,.room #techStep{max-width:470px;margin:0 auto}
/* the dock is its own row BELOW this one now, so its height has to come out of
   the reservation too — otherwise the dock is clipped off the bottom */
.room-wrap{display:flex;gap:14px;margin-top:12px;align-items:stretch;
  height:calc(100vh - 275px);min-height:360px}
@media(max-width:860px){.room-wrap{flex-direction:column;height:auto}}
/* browser-native fullscreen (Vollbild button) */
.room-wrap:fullscreen{background:var(--bg);padding:14px;height:100%;min-height:0}

/* Every video is its own equal-size "window": the own camera (.rtile.self) is a
   tile exactly like the remote ones; tiles are separate rounded cards with gaps,
   laid out in parallel with the chat sidebar. */
/* Column, on every screen size: the video area, then the controls UNDER it.
   `.tiles` is created by JS and APPENDED to .stage, so it comes after the static
   .stage-controls in DOM order — explicit `order` is what decides this, not the
   markup (getting that wrong put the dock ABOVE the video, 2026-08-31). */
.stage{position:relative;flex:1;min-width:0;display:flex}
.stage > .tiles{flex:1;min-height:0}
@media(max-width:860px){.stage{min-height:52vh}}
.stage .tiles{display:flex;flex:1;min-width:0;min-height:0;gap:14px}
/* 3+ windows: equal-size grid, columns from the window count (--cols set by
   livekit-room.js: 2 up to 4 windows, 3 up to 9, 4 up to 16 — a 12-party DMP
   training is 4x3). The bottom padding keeps the last row clear of the
   overlaid controls so no face is hidden behind the buttons. */
.stage.multi .tiles{display:grid;grid-template-columns:repeat(var(--cols,2),minmax(0,1fr));
  grid-auto-rows:minmax(0,1fr);align-content:stretch}
.stage.multi .rtile{flex:none;min-width:0;min-height:0;width:auto;height:auto}
.stage.multi .rname{bottom:12px}
/* narrow screens (phones/tablets): max 2 columns, every window at least 120px
   tall, the grid scrolls — a 12-party group is 6 rows you swipe through
   (adaptiveStream pauses off-screen windows, so this also saves bandwidth) */
@media(max-width:860px){
  .stage.multi .tiles{grid-template-columns:repeat(var(--cols-sm,2),minmax(0,1fr));
    grid-auto-rows:minmax(120px,1fr);overflow-y:auto;max-height:calc(100vh - 120px)}
  .stage.multi .rtile{min-height:120px}
  .stage.has-pin:not(.has-screen):not(.wb-open) .tiles{
    grid-template-rows:minmax(200px,1fr) repeat(var(--strip-rows,1),minmax(90px,auto))}
}
/* dense (7+ windows): compact badges so the small cards stay readable */
.stage.dense .rname{left:8px;bottom:8px;font-size:11px;padding:2px 9px;max-width:80%}
.stage.dense .rtile .net-bars{top:6px;right:6px;transform:scale(.85);transform-origin:top right}
.stage.dense .pin-btn{top:6px;left:6px;width:28px;height:28px}
.stage.dense .pin-btn svg{width:14px;height:14px}
.stage.dense .rtile.cam-off .coff{font-size:11.5px;gap:5px}
.stage.dense .rtile.cam-off .coff svg{width:24px;height:24px}
[dir="rtl"] .stage.dense .rname{left:auto;right:8px}
[dir="rtl"] .stage.dense .rtile .net-bars{right:auto;left:6px}
[dir="rtl"] .stage.dense .pin-btn{left:auto;right:6px}
.stage .rtile{position:relative;flex:1 1 0;min-width:0;border-radius:22px;overflow:hidden;
  background:linear-gradient(165deg,#1a3a5c,#0e2338);box-shadow:0 14px 34px rgba(0,45,80,.22)}
/* the soft centre highlight from the draft, so an empty card is not a flat block */
.stage .rtile::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(460px 380px at 50% 58%,rgba(214,228,239,.26),transparent 72%)}
.stage .rtile > *{position:relative;z-index:1}
.stage .rtile video{width:100%;height:100%;object-fit:cover;background:#0a2038;display:block}
/* own camera is always mirrored (like a mirror — matches every meeting tool);
   only the LOCAL preview flips, the encoded stream others see is unchanged */
video#local{transform:scaleX(-1)}
.stage .rtile audio{display:none}
/* camera off: solid placeholder instead of a frozen last frame */
.stage .rtile .coff{display:none}
.stage .rtile.cam-off video{visibility:hidden}
.stage .rtile.cam-off .coff{position:absolute;inset:0;display:flex;flex-direction:column;gap:9px;
  align-items:center;justify-content:center;color:rgba(255,255,255,.78);font-size:13.5px;
  font-weight:600;letter-spacing:.02em;
  background:radial-gradient(ellipse at center,#123049 0%,#0a2038 78%)}
.stage .rtile.cam-off .coff svg{width:34px;height:34px;opacity:.8}
/* the audio-only overlay covers the same ground — it wins over cam-off */
.stage.audio-only .rtile .coff{display:none}
/* the draft's .name-chip: a mic glyph before the name, so a muted participant is
   readable at a glance */
.stage .rname{position:absolute;left:12px;bottom:64px;background:rgba(0,45,80,.72);padding:3px 12px;
  display:inline-flex;align-items:center;gap:7px;
  border-radius:999px;font-size:12.5px;font-weight:500;color:#fff;max-width:70%;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.stage .rtile.blocked{outline:2px solid var(--red);outline-offset:-2px}
/* screen share = full-screen presentation: the shared screen fills the viewport,
   the camera windows float INSIDE it as equal-size cards (no extra strip section),
   controls stay at the bottom. The whiteboard wins if both are open. */
.stage .screen{position:relative;flex:1;min-width:0;background:#06131f}
.stage .screen video{width:100%;height:100%;object-fit:contain;background:#06131f}
.stage.has-screen:not(.wb-open){position:fixed;inset:0;z-index:60;display:block;background:#06131f;border-radius:0}
.stage.has-screen:not(.wb-open) .screen{position:absolute;inset:0}
.stage.has-screen:not(.wb-open) .screen .rname{bottom:auto;top:14px}
.stage.has-screen:not(.wb-open) .tiles{position:absolute;left:auto;top:auto;right:16px;bottom:84px;
  height:auto;flex:none;gap:10px;display:flex;flex-wrap:nowrap;padding:0;z-index:5;
  max-width:calc(100% - 32px);overflow-x:auto}
[dir="rtl"] .stage.has-screen:not(.wb-open) .tiles{right:auto;left:16px}
.stage.has-screen:not(.wb-open) .rtile{flex:0 0 176px;width:176px;height:112px;min-width:0;max-width:none;
  border-radius:12px;border:1px solid rgba(255,255,255,.22);box-shadow:0 6px 16px rgba(0,0,0,.45)}
.stage.has-screen:not(.wb-open) .rtile .rname{bottom:6px;font-size:11px;padding:2px 9px}
/* The share view is a FIXED full-viewport layer (z-index 60). Since the drafts
   moved the dock out of the stage into its own row (.dockwrap, a sibling of
   .room-wrap), it would otherwise sit UNDERNEATH that layer — the shared screen
   swallowed every click, so the doctor could not even stop sharing. Lift it over
   the presentation; the floating camera windows above it keep their 84px offset. */
.room-wrap:has(> .stage.has-screen:not(.wb-open)) ~ .dockwrap{
  position:fixed;left:0;right:0;bottom:14px;z-index:61;margin:0}
/* the page header (incl. the E2EE lock) is covered by the share view — show the
   in-stage mirror lock so the mandatory E2EE indicator stays visible */
.stage .stage-lock{display:none;position:absolute;top:14px;right:16px;z-index:6;width:32px;height:32px;
  border-radius:999px;background:rgba(255,255,255,.92);color:var(--navy);box-shadow:0 4px 12px rgba(0,0,0,.35)}
.stage .stage-lock[data-state="active"]{color:#1e5e3a}
.stage .stage-lock[data-state="error"]{color:var(--red)}
.stage.has-screen:not(.wb-open) .stage-lock{display:inline-flex}
/* NOT duplicated on the stage in immersive mode: the room header (with the lock
   and the not-recorded marker) stays on screen there, so a second lock would just
   be noise. The mirror below is for screen share, where the header IS covered. */
[dir="rtl"] .stage .stage-lock{right:auto;left:16px}
/* pin/spotlight (local layout only): pinned tile owns the stage, the rest shrink
   to a strip below — same geometry as has-screen. Screen share and whiteboard win
   over the pin (their strip layouts already apply); the pin re-applies after. */
/* top-LEFT, as in the drafts (.tile .pin): the quality bars own the top-right
   corner, and before this the two sat on the same spot and overlapped in a group
   call. */
.stage .pin-btn{position:absolute;top:10px;left:10px;z-index:3;display:none;width:32px;height:32px;
  padding:0;margin:0;border-radius:50%;border:0;cursor:pointer;align-items:center;justify-content:center;
  background:rgba(6,19,31,.6);color:#dbe7f2;box-shadow:0 3px 10px rgba(0,0,0,.3)}
.stage .pin-btn svg{width:14px;height:14px}
.stage .pin-btn:hover{background:rgba(0,45,80,.85);color:#fff}
.stage .pin-btn.on{background:var(--navy);color:#fff;outline:2px solid rgba(255,255,255,.75)}
[dir="rtl"] .stage .pin-btn{left:auto;right:10px}
/* pinning is pointless with one remote tile (already full-size) and has no
   visible effect while screen share / whiteboard strips are active — hide it there */
.stage.multi:not(.wb-open):not(.has-screen) .pin-btn{display:inline-flex}
/* grid: row 1 = the pinned window (spans all columns), the others form a strip
   of up to 6 per row below (--strip-cols/--strip-rows from livekit-room.js) —
   11 thumbnails = 6 + 5, so a 12-party group keeps everyone visible */
.stage.has-pin:not(.has-screen):not(.wb-open) .tiles{display:grid;gap:10px;
  grid-template-columns:repeat(var(--strip-cols,1),minmax(0,1fr));
  grid-template-rows:minmax(0,1fr) repeat(var(--strip-rows,1),minmax(0,22%));align-content:stretch}
.stage.has-pin:not(.has-screen):not(.wb-open) .rtile{flex:none;min-width:0;min-height:0;max-width:none;width:auto;height:auto}
.stage.has-pin:not(.has-screen):not(.wb-open) .rtile.pinned{grid-column:1/-1;grid-row:1}
.stage.has-pin:not(.has-screen):not(.wb-open) .rtile:not(.pinned) .rname{bottom:8px;font-size:11px;padding:2px 9px}
/* whiteboard open: the board is its OWN window (.wb-window) with its own toolbar;
   the video windows become a narrow column of equal cards beside it, controls
   move below the column, the chat stays parallel on the right */
.room-wrap.wb-open .stage{flex:0 0 250px;max-width:250px;flex-direction:column;gap:10px}
.room-wrap.wb-open .stage .tiles{display:flex;flex-direction:column;flex-wrap:nowrap;gap:10px;padding:0;overflow-y:auto}
.room-wrap.wb-open .stage .rtile{flex:0 0 150px;min-width:0;max-width:none;width:100%;height:150px}
.room-wrap.wb-open .stage .rname{bottom:8px;font-size:11px;padding:2px 9px}
.room-wrap.wb-open .stage .rtile .net-bars{display:none}
.room-wrap.wb-open .stage-controls{position:static;flex-wrap:wrap;gap:6px;padding:0}
.room-wrap.wb-open .ctl{width:38px;height:38px}
.room-wrap.wb-open .ctl svg{width:17px;height:17px}
.stage.wb-open .screen{display:none}
@media(max-width:860px){
  .room-wrap.wb-open .stage{flex:0 0 auto;max-width:none;min-height:0}
  .room-wrap.wb-open .stage .tiles{flex-direction:row;overflow-x:auto;overflow-y:hidden}
  .room-wrap.wb-open .stage .rtile{flex:0 0 140px;height:100px}
}

/* whiteboard window: separate card with its own toolbar (tools live here only) */
.wb-window{display:none;flex:1 1 0;min-width:0;background:#fff;border:1px solid var(--border);
  border-radius:18px;box-shadow:0 8px 22px rgba(0,45,80,.16);flex-direction:column;overflow:hidden}
.room-wrap.wb-open .wb-window{display:flex}
@media(max-width:860px){.room-wrap.wb-open .wb-window{min-height:52vh}}
.wb-toolbar{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;
  padding:8px 14px;border-bottom:1px solid var(--border);background:var(--panel)}
.wb-toolbar h3{font-size:12.5px;font-weight:700;color:var(--navy);text-transform:uppercase;
  letter-spacing:.03em;margin:0}
/* 5-colour picker: each swatch shows its ink via --sw; the active one gets a ring */
.wb-colors{display:flex;gap:7px;align-items:center;margin-left:auto}
.wb-swatch{width:22px;height:22px;padding:0;border-radius:50%;background:var(--sw);cursor:pointer;
  border:2px solid #fff;box-shadow:0 0 0 1px var(--border)}
.wb-swatch:hover{box-shadow:0 0 0 1px var(--navy)}
.wb-swatch.on{box-shadow:0 0 0 2px var(--navy)}
.wb-tools{display:flex;gap:8px}
.wb-tools .ctl{width:38px;height:38px;background:#fff;border:1px solid var(--border);box-shadow:none}
.wb-tools .ctl svg{width:17px;height:17px}
.wb-tools .ctl:hover:not(:disabled){background:#eef4fa;color:var(--navy);box-shadow:none}
.wb-tools .ctl.on{background:var(--navy);color:#fff;border-color:var(--navy)}
.wb-tools .ctl.on:hover:not(:disabled){background:var(--navy);color:#fff}
.wb-tools .ctl.wb-text{width:auto;padding:0 16px;border-radius:999px;font-size:12.5px;font-weight:600}
.wb-window .wb{display:block;flex:1;min-height:0;width:100%;touch-action:none;background:#fbfdff}

/* ---- 1:1 call: PHONE ONLY gets the speaker view ----------------------------
   The approved set (00-overview.html, screen 5p) lists "doctor full-screen + own
   view PiP" under `mobile:` — on a laptop or monitor the two windows stay equal
   and side by side, which is what 05g/05h/05i show. Applying the phone layout to
   the desktop was rejected by the user (2026-08-31).
   `data-tiles` is set by relayout() and counts every window including your own,
   so ="2" is the 1:1 case; screen share, whiteboard and pin mode keep their own
   geometry and groups of 3+ keep the equal grid. */
@media(max-width:720px){
  .stage[data-tiles="2"]:not(.has-screen):not(.wb-open):not(.has-pin) > .tiles{
    display:block;position:relative}
  /* inset:0 rather than height:100% — the tiles box takes its height from flex and
     percentage heights against it did not resolve at phone widths (the remote
     collapsed to 150px inside a 373px box). */
  .stage[data-tiles="2"]:not(.has-screen):not(.wb-open):not(.has-pin) .rtile:not(.self){
    position:absolute;inset:0;width:auto;height:auto;min-height:0;flex:none}
  /* a phone is held portrait and the front camera is portrait */
  .stage[data-tiles="2"]:not(.has-screen):not(.wb-open):not(.has-pin) .rtile.self{
    position:absolute;bottom:10px;right:10px;z-index:5;
    width:30%;min-width:92px;max-width:200px;aspect-ratio:3/4;height:auto;
    border:2px solid rgba(255,255,255,.85);border-radius:12px;overflow:hidden;
    box-shadow:0 10px 28px rgba(0,0,0,.45)}
  /* the small window cannot hold the full "Kamera aus" placeholder */
  .stage[data-tiles="2"]:not(.has-screen):not(.wb-open):not(.has-pin) .rtile.self .coff span{display:none}
  .stage[data-tiles="2"]:not(.has-screen):not(.wb-open):not(.has-pin) .rtile.self .coff svg{width:22px;height:22px}
  .stage[data-tiles="2"]:not(.has-screen):not(.wb-open):not(.has-pin) .rtile.self .rname{
    font-size:10px;padding:2px 7px;bottom:6px;left:6px}
}

/* Vollbild is NOT in the approved dock (05g/05h/05i show exactly Mic · Camera ·
   Share · Board · Phone · Leave). It stays reachable from "Mehr" on a phone,
   where the user asked for a full-screen call, and is hidden everywhere else. */
.dk:has(> #fsBtn){display:none}

/* Send is the primary action in the chat card, so it is the filled gradient
   button in the drafts — ours was the secondary outline style. */
#chatSend{background:var(--grad);color:#fff;border:none;font-weight:700}
#chatSend:hover:not(:disabled){filter:brightness(1.06);color:#fff}

/* ---- in-call header strip (approved drafts 05g/05h .strip) ----------------
   One row: the logo in a white chip, the room title with the practice line under
   it, the E2EE status as PLAIN TEXT (status, not a control — no pill outline),
   then the Identity check button, the timer and the participant count. */
h1.strip{display:flex;align-items:center;gap:12px;margin:0 0 10px;font-size:15px;
  font-weight:700;letter-spacing:-.01em;flex-wrap:wrap}
h1.strip .logo-chip{background:#fff;border:1px solid var(--border);border-radius:12px;
  padding:6px 12px;display:inline-flex;align-items:center;box-shadow:0 4px 14px rgba(0,45,80,.06)}
h1.strip .logo-chip img{height:26px;display:block}
h1.strip .room-name{display:block;line-height:1.25}
h1.strip .room-name .sub{font-size:11.5px;font-weight:500;color:var(--muted);
  display:block;letter-spacing:.02em}
h1.strip .room-name .sub:empty{display:none}
/* in the header the lock is STATUS TEXT, not a pill: the drafts draw the icon
   and the label as one green unit (.chip.e2ee), no circular chip behind it. The
   data-state colours stay — this is the live fail-closed indicator. */
h1.strip .e2ee-lock,h1.strip .e2ee-lock[data-state]{width:auto;height:auto;border-radius:0;
  background:transparent;margin:0 -5px 0 0}
h1.strip .e2ee-lock svg{width:13px;height:13px}
h1.strip .e2ee-text{font-size:12px;font-weight:600;color:#1e5e3a}
.strip-right{display:flex;align-items:center;gap:10px}
h1.strip .strip-right{margin-left:auto}   /* patient page: the cluster ends the row */
.strip-right .chip.timer,.strip-right .chip.pcount{font-variant-numeric:tabular-nums;font-size:13px;
  font-weight:700;color:var(--navy);background:transparent;border:none;padding:2px 0}
.strip-right .chip.pcount:empty{display:none}
/* the Identity check button keeps its outline — that one IS clickable */
h1.strip .idbtn,.room-head .idbtn{margin-left:auto}
@media(max-width:720px){
  h1.strip{gap:8px;font-size:14px}
  h1.strip .logo-chip{display:none}   /* the page topbar already carries the logo */
  h1.strip .e2ee-text{display:none}   /* the lock alone, to keep the row on one line */
}

/* The floating dock from the approved drafts (.dockwrap + .dock): a white rounded
   bar centred across the full width, in its own row BELOW the video and the panels
   — not inside the video column, which is what kept shortening the video. */
.dockwrap{display:flex;justify-content:center;margin:8px 0 9px;position:relative}
.stage-controls{position:static;z-index:6;display:flex;gap:4px;align-items:flex-start;
  justify-content:center;pointer-events:none;flex-wrap:wrap;padding:7px 10px 5px;
  background:rgba(255,255,255,.92);backdrop-filter:blur(10px);
  border:1px solid var(--border);border-radius:18px;
  box-shadow:0 10px 26px rgba(0,45,80,.14)}
.ctl{pointer-events:auto;margin-top:0;width:36px;height:36px;padding:0;border-radius:50%;
  background:#eef2f6;color:var(--navy);display:inline-flex;align-items:center;justify-content:center;
  box-shadow:none;border:none;transition:background .15s}
.ctl svg{width:17px;height:17px}
.ctl:hover:not(:disabled){background:var(--blue-bg);color:var(--navy);box-shadow:none}
/* active/off states must survive :hover (higher specificity), otherwise the
   state color is invisible right after the click while the cursor still hovers */
.ctl.off{background:var(--red);color:#fff}
.ctl.off:hover:not(:disabled){background:#6d1212;color:#fff}
.ctl svg .slash{display:none}
.ctl.off svg .slash{display:inline}
.ctl.on{background:var(--navy);color:#fff;box-shadow:0 0 0 2.5px #c93c3c,0 4px 14px rgba(0,0,0,.28)}
.ctl.on:hover:not(:disabled){background:#00243f;color:#fff;box-shadow:0 0 0 2.5px #c93c3c,0 6px 18px rgba(0,0,0,.32)}
/* Leave/End is the one emphasised control: a gradient pill, wider than the
   round buttons, with a red caption (draft .dk.leave) */
.ctl.danger{background:var(--grad);color:#fff;width:48px;border-radius:999px}
.dk:has(> .ctl.danger) .dk-label{color:var(--red)}
.ctl.danger:hover:not(:disabled){background:var(--grad);color:#fff;filter:brightness(1.06)}
.ctl:disabled{opacity:.45;cursor:not-allowed;pointer-events:auto}

/* connection-quality bars, one per video window — own line on the own window,
   remote lines on theirs (approved drafts 05g/05h `.tile .q`). They used to sit
   in the header for the own line; the drafts put every line's bars on its own
   window, which also keeps the header to one clean row. */
.net-bars{display:inline-flex;align-items:flex-end;gap:2.5px;cursor:help}
.net-bars i{display:block;width:4px;border-radius:1.5px;background:#c9d6e2}
.net-bars i:nth-child(1){height:6px}
.net-bars i:nth-child(2){height:10px}
.net-bars i:nth-child(3){height:14px}
.net-bars[data-q="excellent"] i{background:#1e5e3a}
.net-bars[data-q="good"] i:nth-child(-n+2){background:#1e5e3a}
.net-bars[data-q="poor"] i:nth-child(1){background:var(--red)}
.net-bars[data-q="lost"] i{background:rgba(145,24,24,.4);animation:netblink 1.1s ease-in-out infinite}
@keyframes netblink{50%{opacity:.35}}
.stage .rtile .net-bars{position:absolute;top:14px;right:14px;background:rgba(6,19,31,.6);
  border-radius:10px;padding:6px 8px 7px;z-index:2}
[dir="rtl"] .stage .rtile .net-bars{right:auto;left:14px}
.stage .rtile .net-bars i{background:rgba(255,255,255,.35)}
.stage .rtile .net-bars[data-q="excellent"] i{background:#6fd39a}
.stage .rtile .net-bars[data-q="good"] i:nth-child(-n+2){background:#6fd39a}
.stage .rtile .net-bars[data-q="poor"] i:nth-child(1){background:#ff8a8a}
.stage .rtile .net-bars[data-q="lost"] i{background:rgba(255,138,138,.55)}
/* audio-only mode ("Video pausieren"): remote video is UNSUBSCRIBED (downlink
   relief); every video window (incl. the own one) shows a label instead */
.stage.audio-only .rtile video{visibility:hidden}
.stage .rtile .aonly{display:none}
.stage.audio-only .rtile .aonly{display:flex;position:absolute;inset:0;align-items:center;
  justify-content:center;color:rgba(255,255,255,.8);font-size:14px;font-weight:600;
  letter-spacing:.02em;background:radial-gradient(ellipse at center,#123049 0%,#0a2038 78%)}

/* end-of-call overlay (doctor ended / participant left / connection lost) */
.end-overlay{position:absolute;inset:0;z-index:9;display:flex;align-items:center;justify-content:center;
  background:linear-gradient(160deg,#0a2038,#123049);border-radius:18px}
.stage.has-screen .end-overlay{border-radius:0}
.end-overlay .wait-card .row{justify-content:center;margin-top:18px}
.end-overlay button{margin-top:0}
.end-overlay button.sec{background:transparent;color:#fff;border-color:rgba(255,255,255,.75)}
.end-overlay button.sec:hover:not(:disabled){background:#fff;color:var(--navy)}

/* waiting overlay (patient) */
.wait-overlay{position:absolute;inset:0;z-index:7;display:flex;align-items:center;justify-content:center;
  /* the waiting room is dark by design (draft 04-waiting-room-v7): a photo of the
     practice behind a navy scrim. The CALL page is light — dark video cards on the
     ECG background — so this is deliberately not the same surface. */
  background:linear-gradient(160deg,#0a2038,#123049);border-radius:18px}
.wait-card{text-align:center;color:#fff;max-width:340px;padding:24px}
.wait-card h2{color:#fff;margin:14px 0 6px}
.wait-card .muted{color:#b9cbe0}

/* ================= patient waiting room ("Warteraum") =================
   A calm holding screen while the doctor finishes up: a doctor-presence
   medallion, an honest (non-progress-bar) 3-step rail, a greeting and gently
   rotating non-medical tips over a drifting on-brand backdrop. All movement is
   CSS + module JS; degrades to a static still under prefers-reduced-motion. */
.wait-overlay{overflow:hidden}                 /* clip the drifting layers to the rounded stage */
/* 820px so the three chevrons form ONE pointing row; they stack below 640px */
.wait-overlay .wait-card{max-width:820px;position:relative;z-index:1;padding:26px 24px 86px}

/* ambient backdrop plane (behind the card) */
.wait-bg{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}

/* ECG graph paper, recoloured white-on-navy, ultra-low opacity, drifting exactly
   one bold cell (45px) → seamless loop. transform-only = GPU-composited. */
.wait-grid{position:absolute;inset:-60px;
  background:
    linear-gradient(rgba(255,255,255,.05) 1px,transparent 1px) 0 0 / 9px 9px,
    linear-gradient(90deg,rgba(255,255,255,.05) 1px,transparent 1px) 0 0 / 9px 9px,
    linear-gradient(rgba(255,255,255,.08) 1px,transparent 1px) 0 0 / 45px 45px,
    linear-gradient(90deg,rgba(255,255,255,.08) 1px,transparent 1px) 0 0 / 45px 45px;
  animation:waitGridDrift 42s linear infinite}
@keyframes waitGridDrift{to{transform:translate3d(-45px,-45px,0)}}

/* styleguide gradient "snake" ribbons — the SAME two data-URIs the global
   background already ships (style.css body::after: the blue + red ribbons),
   here on two ultra-slow, non-syncing drifts; NO filter:blur (cheap on GPUs). */
.wait-ribbon{position:absolute;width:460px;height:460px;background-repeat:no-repeat;
  background-size:contain;opacity:.13;will-change:transform}
.wait-ribbon-a{top:-96px;right:-128px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cdefs%3E%3ClinearGradient id='b' x1='0' y1='1' x2='1' y2='0'%3E%3Cstop offset='0' stop-color='%23002d50'/%3E%3Cstop offset='1' stop-color='%234a90e2'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M10 240 C 120 110 300 300 390 130' fill='none' stroke='url(%23b)' stroke-width='30' stroke-linecap='round'/%3E%3Cpath d='M0 300 C 120 200 260 360 380 250' fill='none' stroke='url(%23b)' stroke-width='14' stroke-linecap='round' opacity='0.55'/%3E%3C/svg%3E");
  animation:waitRibbonA 52s ease-in-out infinite alternate}
.wait-ribbon-b{bottom:-112px;left:-128px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23911818'/%3E%3Cstop offset='1' stop-color='%23c93c3c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M20 300 C 140 180 260 360 380 220' fill='none' stroke='url(%23g)' stroke-width='34' stroke-linecap='round'/%3E%3Cpath d='M40 360 C 160 250 280 410 390 300' fill='none' stroke='url(%23g)' stroke-width='18' stroke-linecap='round' opacity='0.6'/%3E%3C/svg%3E");
  animation:waitRibbonB 66s ease-in-out infinite alternate}
@keyframes waitRibbonA{from{transform:translate3d(0,0,0) rotate(0deg)}to{transform:translate3d(-26px,18px,0) rotate(3deg)}}
@keyframes waitRibbonB{from{transform:translate3d(0,0,0) rotate(0deg)}to{transform:translate3d(22px,-16px,0) rotate(-3deg)}}
[dir="rtl"] .wait-ribbon{transform:scaleX(-1)}

/* --- the practice's own waiting-room photo, full bleed and held back ---------
   Approved draft 04-waiting-room-v7. Opacity + a slight blur and desaturation so
   it reads as ambience rather than a picture to look at; the scrim below keeps the
   white text legible over the bright areas (windows, floor). */
.wait-photo{position:absolute;inset:0;
  background-image:url("/assets/waiting-room.webp");
  background-size:cover;background-position:center 62%;
  opacity:.62;filter:saturate(.82) blur(1.5px)}
/* darkest right behind the card, lighter towards the edges so the room stays
   recognisable */
.wait-bg::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:
    radial-gradient(closest-side at 50% 48%,rgba(8,26,46,.90),rgba(8,26,46,.66) 52%,rgba(8,26,46,.40) 78%),
    linear-gradient(180deg,rgba(8,26,46,.55),rgba(8,26,46,.28) 42%,rgba(8,26,46,.62))}

/* the greeting is a quiet kicker above the hourglass, not a heading */
.wait-greet{color:#9fb6d0;font-size:12.5px;font-weight:600;letter-spacing:.14em;
  text-transform:uppercase;margin:0 0 14px}

/* --- hourglass: an ever-moving "time is passing" cue, so the screen never reads
   as frozen. Decorative; the real status is the heading and the chevrons. --- */
.wait-hourglass{display:block;width:52px;height:64px;margin:2px auto 16px;color:#dbe9f8;
  transform-origin:50% 50%;animation:waitHgFlip 9s cubic-bezier(.7,0,.3,1) infinite}
.wait-hourglass svg{display:block;width:100%;height:100%;overflow:visible}
.hg-sand-top,.hg-sand-bot{fill:#e4cfa3}
.hg-fall{stroke:#e4cfa3;stroke-dasharray:1.4 3;animation:waitHgFall .8s linear infinite}
@keyframes waitHgFlip{0%,38%{transform:rotate(0deg)}50%,88%{transform:rotate(180deg)}100%{transform:rotate(360deg)}}
@keyframes waitHgFall{to{stroke-dashoffset:-8.8}}

/* --- doctor status: PLAIN TEXT, never a box -------------------------------
   Any filled, bordered rectangle reads as a control, round or square, and
   patients tried to press the old medallion (feedback 2026-08-26). A live dot
   plus a sentence can only be read. */
.wait-doc{display:inline-flex;align-items:center;gap:9px;margin:0 auto}
.wait-doc .live{width:7px;height:7px;border-radius:50%;background:#5fd39a;flex:0 0 auto;
  box-shadow:0 0 0 0 rgba(95,211,154,.55);animation:docLive 2.8s ease-out infinite}
@keyframes docLive{0%{box-shadow:0 0 0 0 rgba(95,211,154,.5)}70%{box-shadow:0 0 0 7px rgba(95,211,154,0)}100%{box-shadow:0 0 0 0 rgba(95,211,154,0)}}
.wait-doc b{color:#fff;font-weight:600;font-size:14.5px}
.wait-doc span.st{color:#bcd2e8;font-size:14px}

/* the patient's own name leads the heading — they are the one waiting */
.wait-card h2{color:#fff;font-size:19px;font-weight:700;margin:14px 0 8px;line-height:1.35}
.wait-card h2 .pat{color:#fff}

/* --- horizontal chevron progress ------------------------------------------
   box 1 points at box 2, box 2 at box 3, box 3 is the destination.
   done = green, current = amber. Deliberately NOT a progress bar: nothing here
   promises a waiting time. */
.wsteps{display:flex;justify-content:center;align-items:stretch;gap:4px;
  margin:16px auto 6px;flex-wrap:wrap}
.wstep{--notch:15px;
  position:relative;display:flex;align-items:center;gap:8px;
  padding:11px 18px;font-size:13.5px;font-weight:600;letter-spacing:.01em;
  color:#dbe8f5;background:rgba(255,255,255,.10);border:1px solid rgba(255,255,255,.16);
  white-space:nowrap}
.wstep.has-point{padding-right:calc(18px + var(--notch));
  clip-path:polygon(0 0,calc(100% - var(--notch)) 0,100% 50%,calc(100% - var(--notch)) 100%,0 100%)}
.wstep.has-notch{padding-left:calc(18px + var(--notch));
  clip-path:polygon(0 0,calc(100% - var(--notch)) 0,100% 50%,calc(100% - var(--notch)) 100%,0 100%,var(--notch) 50%)}
.wstep.has-notch.is-last{clip-path:polygon(0 0,100% 0,100% 100%,0 100%,var(--notch) 50%)}
.wstep .wico{width:16px;height:16px;flex:0 0 auto;display:block}
.wstep.is-done{background:rgba(38,150,94,.30);border-color:rgba(74,199,138,.55);color:#d8f5e6}
.wstep.is-done .wico{color:#63d69f}
.wstep.is-current{background:rgba(214,161,45,.30);border-color:rgba(240,196,90,.62);color:#ffeec4}
.wstep.is-current .wico{color:#ffd66b}
.wstep.is-current::after{content:"";position:absolute;inset:0;
  background:rgba(255,214,107,.16);animation:wstepPulse 2.6s ease-in-out infinite}
@keyframes wstepPulse{0%,100%{opacity:0}50%{opacity:1}}
@media(max-width:640px){
  .wsteps{flex-direction:column;align-items:stretch;gap:6px}
  .wstep,.wstep.has-point,.wstep.has-notch,.wstep.has-notch.is-last{
    clip-path:none;padding:11px 16px;border-radius:10px;justify-content:center}
}

/* --- the waiting room FILLS THE SCREEN (approved draft 04-waiting-room-v7) ---
   Waiting is not a panel beside a chat: it is the whole screen until the doctor
   lets you in. body.waiting is set by patient.js while #waitOverlay is shown, so
   the overlay covers the sidebar and the dock as well.
   The IN-CALL header is hidden here — draft 04 shows the waiting room with the
   page topbar only, and the room title, practice line, E2EE text and the
   not-recorded chip floating over the waiting card was our addition, not the
   design. The mandatory E2EE indicator stays visible the way the draft solves
   it: a lock in the TOPBAR (.topbar-lock). "Keine Aufzeichnung" is not lost
   either — the patient accepted it in the consent text before joining
   (patient.consentText) and sees it again in the call header once admitted. */
body.waiting{overflow:hidden}
body.waiting .topbar{position:fixed;top:0;left:0;right:0;z-index:50;
  background:rgba(255,255,255,.94);backdrop-filter:blur(6px)}
body.waiting .wait-overlay{position:fixed;inset:0;border-radius:0;z-index:40}
body.waiting .wait-overlay .wait-card{padding-top:96px}
body.waiting .room h1.strip{display:none}
/* the topbar lock exists only for this state (draft 04); in the call the header
   strip carries the lock, and two of them would just be noise. It takes over the
   margin-left:auto so the right-hand group reads lock · language · version, as
   in the draft. */
.topbar-lock{display:none;width:34px;height:34px;margin:0}
body.waiting .topbar-lock{display:inline-flex;margin-left:auto}
body.waiting .lang-mount{margin-left:0}
[dir="rtl"] body.waiting .topbar-lock{margin-left:0;margin-right:auto}
[dir="rtl"] body.waiting .lang-mount{margin-right:0}
@media(max-width:720px){
  /* The overlay centres its children as a flex ROW on a desktop; on a phone the
     card is taller than the screen, so the card and the help strip have to stack.
     Do NOT try to do this with `display:block`: livekit-room.js shows the overlay
     with an INLINE style.display = "flex", and an inline style beats any rule
     here. Changing the flex DIRECTION works regardless of that. */
  body.waiting .wait-overlay{flex-direction:column;align-items:stretch;
    justify-content:flex-start;overflow-y:auto;-webkit-overflow-scrolling:touch}
  body.waiting .wait-overlay .wait-card{max-width:none;padding:124px 20px 18px}
}

/* --- help strip: why you would switch device, and that nothing is lost ----- */
.help-strip{position:absolute;left:0;right:0;bottom:0;z-index:2;
  display:flex;align-items:center;justify-content:center;gap:14px;flex-wrap:wrap;
  padding:9px 20px;background:rgba(6,20,36,.62);
  border-top:1px solid rgba(255,255,255,.12);backdrop-filter:blur(3px)}
.help-ico{width:20px;height:20px;flex:0 0 auto;color:#bcd2e8;opacity:.85}
.help-txt{flex:1 1 auto;min-width:0;text-align:left}
.help-txt b{color:#fff;font-size:13.5px;font-weight:700;margin-right:6px}
.help-txt span{color:#b9cbe0;font-size:13.5px;line-height:1.5}
/* the action phrase INSIDE the sentence is the QR button (2026-09-02): a
   link-styled control that keeps the sentence reading as one line of text */
.help-link{display:inline;padding:0;margin:0;border:0;background:none;cursor:pointer;
  color:#fff;font:inherit;font-size:13.5px;font-weight:700;
  text-decoration:underline;text-underline-offset:3px;text-decoration-thickness:1.5px}
.help-link:hover{color:#dceaf7;text-decoration-thickness:2px}
.help-link:focus-visible{outline:2px solid #fff;outline-offset:2px;border-radius:3px}
@media(max-width:720px){
  /* on a phone the card is taller than the reserved space, so a pinned strip
     covered the CURRENT step — let it flow after the content instead */
  .help-strip{position:static;padding:14px 16px;gap:10px;
    flex-direction:column;align-items:flex-start;text-align:left}
  .help-txt b{display:block;margin-bottom:2px}
  .help-link{font-size:14px}
}

/* single rotating tip line — reserved height prevents layout shift */
.wait-tip{color:#9fb6d0;font-size:13px;min-height:38px;margin:8px auto 2px;max-width:320px;
  display:flex;align-items:center;justify-content:center;text-align:center;transition:opacity .32s ease}
.wait-tip.is-fading{opacity:0}

/* visually-hidden helper (step-state text for screen readers) */
.sr-only{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}

/* ---- reduced motion: freeze every animation, keep a complete calm still ---- */
@media (prefers-reduced-motion: reduce){
  .wait-grid,.wait-ribbon-a,.wait-ribbon-b,.wait-doc .live,
  .wait-hourglass,.hg-fall,.wstep.is-current::after{animation:none}
  .wstep.is-current::after{opacity:.5}                  /* static amber wash */
  .wait-hourglass{transform:none}                       /* upright, still */
  .hg-fall{stroke-dasharray:none}                       /* solid static stream */
  .wait-tip{transition:none}                            /* instant tip swap */
}

/* right sidebar: chat (top) + waiting room (bottom) */
.sidebar{width:320px;flex-shrink:0;display:flex;flex-direction:column;gap:12px;min-height:0}
@media(max-width:860px){.sidebar{width:auto}}
.panel{background:var(--panel);border:1px solid var(--border);border-radius:20px;padding:14px;
  display:flex;flex-direction:column;min-height:0;box-shadow:0 10px 28px rgba(0,45,80,.12)}
.panel h3{font-size:12.5px;font-weight:700;color:var(--navy);text-transform:uppercase;letter-spacing:.03em;margin-bottom:8px}
.chat-panel{flex:1 1 auto;min-height:130px}
.chat-log{list-style:none;flex:1;overflow-y:auto;display:flex;flex-direction:column;gap:8px;padding-right:4px;min-height:84px}
.chat-msg{font-size:13px;background:#f3f8fc;border:1px solid var(--divider);border-radius:12px;padding:7px 11px;max-width:90%}
.chat-msg.self{align-self:flex-end;background:var(--blue-bg);border-color:#bcd3e6}
.chat-msg .who{display:block;font-size:11px;font-weight:700;color:var(--muted);margin-bottom:2px}
.chat-msg .body{color:var(--navy);word-break:break-word}
.chat-input{display:flex;gap:8px;margin-top:10px}
.chat-input input{flex:1}
.chat-input button{margin-top:0;padding:9px 14px;font-size:13px;flex-shrink:0}
/* waiting room shows ~5 waiting patients at once and scrolls beyond (cap = doctor + 11,
   e.g. a DMP training group arriving together) */
.waiting-panel{flex:0 0 auto}
/* content-sized, like the draft's waiting .dcard (122px with one row). The old
   min-height:176px forced the card to ~233px even when empty and squeezed the chat
   to 290px, where the draft gives chat 435px. */
.waiting-list{list-style:none;overflow-y:auto;display:flex;flex-direction:column;gap:6px;
  min-height:0;max-height:238px}
.wait-row{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:13px;
  background:#f3f8fc;border:1px solid var(--divider);border-radius:10px;padding:7px 10px}
.wait-row .admit-btn{margin-top:0;padding:5px 12px;font-size:12px}
.wait-empty{font-size:12.5px;padding:4px 2px}

.status{margin-top:10px;font-size:12.5px;color:var(--muted);display:flex;align-items:center;gap:10px;flex-wrap:wrap}

/* Anlage 4b identity-check — a dropdown in the room header, beside the title, so
   the doctor reaches it in one click and it is never hidden below the stage. */
.room-head{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.room-head h1{flex:1 1 auto;min-width:0}
details.idmenu{position:relative;flex:0 0 auto}
details.idmenu>summary{list-style:none;cursor:pointer;display:inline-flex;align-items:center;gap:7px;
  background:#fff;border:1px solid var(--red);color:var(--red);font-weight:600;font-size:13px;
  padding:8px 14px;border-radius:999px;white-space:nowrap;box-shadow:0 2px 8px rgba(0,45,80,.08)}
details.idmenu>summary::-webkit-details-marker{display:none}
details.idmenu>summary:hover,details.idmenu[open]>summary{background:var(--red);color:#fff}
/* the open form floats as a right-aligned popover over the stage (z above stage,
   below the QR overlay); scrolls internally if it gets tall. */
.idmenu-panel{position:absolute;right:0;top:calc(100% + 8px);z-index:30;width:min(600px,92vw);
  text-align:left;background:var(--panel);border:1px solid var(--border);border-radius:14px;
  box-shadow:0 18px 46px rgba(0,45,80,.24);padding:16px 16px 18px;max-height:min(72vh,640px);overflow:auto}
.idmenu-intro{margin-bottom:6px}
@media(max-width:520px){.idmenu-panel{position:fixed;left:12px;right:12px;top:62px;width:auto}}
.idgrid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:0 14px;margin-top:4px}
@media(max-width:760px){.idgrid{grid-template-columns:1fr}}
.idgrid select{width:100%;background:#fff;border:1px solid #c3d4e3;border-radius:10px;
  color:var(--text);padding:11px 13px;font-size:14px;font-family:inherit}
.idgrid select:focus{outline:none;border-color:var(--navy);box-shadow:0 0 0 3px rgba(0,45,80,.14)}
/* plausibility rules (Geburtsdatum / PLZ / Versichertennummer): red field + message */
.idgrid input.invalid{border-color:var(--red);box-shadow:0 0 0 3px rgba(145,24,24,.12)}
.ferr{display:block;color:var(--red);font-size:12px;line-height:1.35;margin:-6px 0 10px}
.ferr[hidden]{display:none}   /* author display:block must not defeat the hidden attribute */
.idout{width:100%;margin-top:12px;background:#f3f8fc;border:1px solid var(--divider);border-radius:12px;
  padding:12px;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12.5px;color:var(--navy);resize:vertical}

/* Techniktest (pre-call device & connection check) */
.check-list{list-style:none}
.check-row{display:flex;align-items:flex-start;gap:12px;padding:13px 2px;border-bottom:1px solid var(--divider)}
.check-row:last-child{border-bottom:none}
.check-dot{flex-shrink:0;width:22px;height:22px;border-radius:50%;margin-top:1px;position:relative;
  background:#eef2f6;border:1.5px solid #dbe3ea}
.check-dot::after{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  font-size:12px;font-weight:700;color:#fff}
.check-dot[data-state="running"]{border-color:var(--navy);border-top-color:transparent;
  animation:spin 1s linear infinite;background:#fff}
.check-dot[data-state="ok"]{background:#1e5e3a;border-color:#1e5e3a}
.check-dot[data-state="ok"]::after{content:"✓"}
.check-dot[data-state="warn"]{background:#b07a1e;border-color:#b07a1e}
.check-dot[data-state="warn"]::after{content:"!"}
.check-dot[data-state="fail"]{background:var(--red);border-color:var(--red)}
.check-dot[data-state="fail"]::after{content:"✕"}
.check-main{flex:1;min-width:0}
.check-name{display:block;font-size:14.5px;font-weight:600;color:var(--navy)}
.check-detail{display:block;font-size:12.5px;color:var(--muted);margin-top:2px;word-break:break-word}
.cam-preview{width:min(100%,240px);aspect-ratio:4/3;object-fit:cover;border-radius:12px;
  margin-top:8px;background:#0a2038;border:1px solid var(--border);transform:scaleX(-1)}
.meter{height:10px;border-radius:999px;background:#eef2f6;border:1px solid #dbe3ea;
  margin-top:8px;overflow:hidden;max-width:240px}
.meter-fill{height:100%;width:0;border-radius:999px;background:var(--grad);transition:width .1s linear}
.spk-confirm{margin-top:8px}
.spk-confirm button{flex:0 0 auto;margin-top:0;padding:7px 16px;font-size:12.5px}
.msg.warn{display:block;background:rgba(176,122,30,.08);color:#7a5514;border:1px solid rgba(176,122,30,.4)}
.tt-privacy{margin-top:16px;font-size:12px}
.techtest-hint{display:block;margin-top:14px;font-size:13px}

/* ===== doctor dashboard ===== */
.dash-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
.dash-head h2{margin:0}
.dash-head p.muted{margin-top:3px;font-size:13.5px}
button.linklike{background:none;border:none;color:var(--muted);font-size:13px;font-weight:500;
  margin:0;padding:4px 2px;text-decoration:underline;border-radius:4px;flex-shrink:0}
button.linklike:hover:not(:disabled){background:none;color:var(--red);box-shadow:none}
.dash-new{width:100%;margin-top:18px}
.dash-divider{height:1px;background:var(--divider);margin:22px 0 18px}
.dash-h3{font-size:12.5px;font-weight:700;color:var(--navy);text-transform:uppercase;
  letter-spacing:.04em;margin:0 0 6px}
.dash-actions{margin-top:14px}
.dash-actions button{flex:1 1 auto;white-space:nowrap}
/* recent-consultation rows: name · status/E2EE badges · date */
li.hist-row{justify-content:flex-start}
.hist-name{font-weight:600;color:var(--navy);flex:1 1 auto;min-width:0;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.hist-name.none{color:var(--muted);font-weight:400}
.hist-tags{display:inline-flex;gap:5px;flex-shrink:0}
.hist-tags .tag{padding:2px 9px}
.hist-date{flex-shrink:0;font-size:11.5px;color:var(--muted);font-variant-numeric:tabular-nums}
.tag.ok{background:rgba(30,94,58,.1);color:#1e5e3a;border-color:rgba(30,94,58,.3)}
li.hist-empty{color:var(--muted);justify-content:center;border-style:dashed}

/* link blocks: one titled box per audience (patient / doctor), each with a
   plain-language instruction so the user knows what to DO with the link */
.link-block{border:1px solid var(--divider);border-radius:14px;background:#f8fbfe;
  padding:14px 14px 12px;margin-top:12px}
.lb-head{display:flex;align-items:center;gap:9px}
.lb-head b{font-size:14px;color:var(--navy)}
.lb-ico{flex-shrink:0;width:30px;height:30px;border-radius:50%;background:var(--blue-bg);
  color:var(--navy);display:inline-flex;align-items:center;justify-content:center}
.lb-ico svg{width:15px;height:15px}
.lb-hint{font-size:12.5px;color:var(--muted);margin:6px 0 0;line-height:1.45}
.link-block ul.mt{margin-top:8px}

/* dashboard participant-link rows */
li.link-row{flex-direction:row;align-items:center;background:#fff}
li.link-row .link-box{flex:1;min-width:0;margin-top:0;border:none;padding:2px 4px;background:transparent;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;word-break:normal}
li.link-row button{margin-top:0;padding:7px 16px;font-size:12.5px;flex-shrink:0}

/* device handoff — QR "continue on your phone" (qr.js). Fixed full-page overlay
   (works from the pre-join card AND above the in-call/waiting overlays); the
   white box gives the code the quiet zone scanners need. */
.qr-overlay{position:fixed;inset:0;z-index:40;display:flex;align-items:center;justify-content:center;
  background:rgba(6,19,31,.9)}
.qr-card .qr-box{background:#fff;border-radius:14px;padding:16px;display:inline-block;margin-top:10px}
.qr-card .qr-box svg{display:block;width:min(64vw,240px);height:min(64vw,240px)}
.qr-card .row{justify-content:center;margin-top:16px}
.qr-card button{margin-top:0}
.qr-card button.sec{background:transparent;color:#fff;border-color:rgba(255,255,255,.75)}
.qr-card button.sec:hover:not(:disabled){background:#fff;color:var(--navy)}
.qr-pre-btn{display:block;background:none;border:none;color:var(--navy);font-weight:500;
  font-size:13px;padding:0;margin-top:14px;text-decoration:underline;border-radius:4px}
.qr-pre-btn:hover:not(:disabled){background:none;color:var(--red);box-shadow:none}

/* ============================================================================
   2026-08-24 REDESIGN LAYER (approved draft set, ui-review/Draft-New/pages).
   Cascade overrides on top of the base rules above — the id/class contract and
   the room geometry systems (multi-grid, pin strip, screen share, whiteboard)
   are untouched. Design tokens stay the official styleguide values.
   ========================================================================== */

/* ---- topbar: variant-D wordmark + bordered version pill ---- */
.topbar-in{max-width:1240px;padding:12px 24px;gap:16px}
.brand-sub{font-size:21px;font-weight:800;letter-spacing:-.02em;gap:0}
.brand-sub::after{margin-left:9px}
[dir="rtl"] .brand-sub::after{margin-left:0;margin-right:9px}
.brand-sub .w1{color:var(--navy)}
.brand-sub .w2{background:var(--grad);-webkit-background-clip:text;background-clip:text;
  color:transparent;white-space:pre}
.ver{display:inline-flex;align-items:center;gap:7px;font-size:12px;font-weight:600;
  color:var(--muted);border:1.5px solid var(--blue-bg);border-radius:999px;padding:6px 13px;
  background:#fff;font-variant-numeric:tabular-nums}
.ver:empty{display:none}
.ver::before{content:"";width:7px;height:7px;border-radius:50%;background:#1e5e3a;
  box-shadow:0 0 0 3px rgba(30,94,58,.14)}

/* ---- button system: gradient primary pill / outline secondary ---- */
button{background:var(--grad);font-weight:600;font-size:14.5px;padding:12px 24px;
  box-shadow:0 6px 16px rgba(145,24,24,.22)}
button:hover:not(:disabled){background:var(--navy);box-shadow:0 8px 20px rgba(0,45,80,.3)}
button.sec{box-shadow:none}
button.linklike,.qr-pre-btn{box-shadow:none;background:none}
button.linklike:hover:not(:disabled){box-shadow:none}
.ctl,.wb-swatch,.wb-tools .ctl{box-shadow:0 4px 14px rgba(0,0,0,.28)}
.wb-swatch{box-shadow:0 0 0 1px var(--border)}
.wb-tools .ctl{box-shadow:none}

/* ---- cards ---- */
.card{border-radius:18px;box-shadow:0 6px 18px rgba(0,45,80,.06)}

/* ---- trust chips (login, dashboard, consent) ---- */
/* The three items are one statement and belong on ONE line (user, 2026-08-31).
   The draft fits them at 12px because the English labels are short; German needs
   426px against the draft's 358px, so the row needs help as the hero column
   narrows: between the stacking breakpoint and 1200px it drops to 11px with
   tighter gaps, which carries German down to where the hero stacks and the row
   has the full width again. flex-wrap stays as a safety net — a wrapped line is
   ugly, an overflowing one is broken. */
.trust-row{display:flex;gap:8px;flex-wrap:wrap;margin-top:18px}
/* status, not controls (2026-08-27): a bordered rounded box reads as "click me".
   Real buttons keep the pill; these are icon + label only. */
.trust{display:inline-flex;align-items:center;gap:7px;font-size:12px;font-weight:600;color:var(--navy);
  background:transparent;border:none;border-radius:0;padding:2px 0}
.trust svg{width:13px;height:13px;color:#1e5e3a;flex-shrink:0}
.trust.neutral svg{color:var(--navy)}
@media(max-width:1200px){
  .trust-row{gap:6px}
  .trust{font-size:11px;gap:6px}
  .trust svg{width:12px;height:12px}
}

/* ---- patient flow stepper (consent → tech check → consultation) ---- */
.steps{display:flex;align-items:center;gap:10px;justify-content:center;margin:0 0 20px;flex-wrap:wrap}
.step{display:inline-flex;align-items:center;gap:8px;font-size:12.5px;font-weight:600;color:var(--muted)}
.step .n{width:24px;height:24px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;
  font-size:12px;font-weight:700;background:#eef2f6;color:var(--muted);border:1.5px solid #dbe3ea}
.step.done .n{background:#1e5e3a;border-color:#1e5e3a;color:#fff}
.step.act{color:var(--navy)}
.step.act .n{background:var(--navy);border-color:var(--navy);color:#fff}
.step-sep{width:26px;height:1.5px;background:var(--divider)}

/* ---- login split hero (index.html) ---- */
.wrap.login-wrap{max-width:1240px}
.hero{background:#fff;border:1px solid var(--border);border-radius:24px;overflow:hidden;
  box-shadow:0 14px 38px rgba(0,45,80,.1);
  display:grid;grid-template-columns:minmax(0,46%) minmax(0,54%);align-items:stretch}
/* stacks at 1040 rather than 920: below that the left column is too narrow for
   the German trust line to stay on one row, and a stacked hero (which the
   draft already defines) reads better than a broken row. */
@media(max-width:1080px){.hero{grid-template-columns:1fr}}
.hero-left{padding:44px 24px 40px 52px;display:flex;flex-direction:column;justify-content:center}
@media(max-width:1080px){.hero-left{padding:32px 24px 8px}}
.hero-title{font-size:34px;font-weight:800;letter-spacing:-.025em;line-height:1.18;color:var(--navy);margin-bottom:0}
.hero-title .accent{background:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent}
.hero-sub{color:var(--muted);font-size:15px;margin:12px 0 6px;max-width:44ch}
.hero-left .card{border:none;box-shadow:none;padding:0;margin-top:10px;max-width:410px;overflow:visible}
.hero-left .card h2{margin-top:0}
.or-row{display:flex;align-items:center;gap:12px;max-width:410px;margin:20px 0 0;
  color:#8aa2b8;font-size:11px;font-weight:700;letter-spacing:.14em}
.or-row::before,.or-row::after{content:"";flex:1;height:1px;background:var(--divider)}
.prov-note{max-width:410px;font-size:12.5px;color:var(--muted);margin-top:12px}
.hero-right{position:relative;min-height:100%;background:#fff}
.hero-right{overflow:hidden}
.hero-right img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;object-position:center}
.hero-right::before{content:"";position:absolute;inset:0 auto 0 0;width:90px;z-index:1;
  background:linear-gradient(90deg,#fff,rgba(255,255,255,0))}
@media(max-width:1080px){.hero-right{min-height:280px}.hero-right::before{display:none}}
[dir="rtl"] .hero-right img{object-position:center}
.login-foot{margin-top:22px;text-align:center}

/* ---- dashboard: page head + user chip + two-column grid ---- */
/* wider on large screens (2026-10-07, draft 01b-option2): the cards use the free space at the sides;
   the top bar follows on the dashboard only, so the logo stays in line with the cards */
.wrap.dash-wrap{max-width:1480px}
body.dash-page .topbar-in{max-width:1480px}
.page-head{display:flex;align-items:flex-end;justify-content:space-between;gap:18px;flex-wrap:wrap;margin-bottom:20px}
.page-head .sub{color:var(--muted);font-size:14px;margin-top:2px}
.user-chip{display:flex;align-items:center;gap:12px;background:#fff;border:1px solid var(--border);
  border-radius:999px;padding:7px 10px 7px 8px;box-shadow:0 4px 14px rgba(0,45,80,.06)}
.avatar{width:38px;height:38px;border-radius:50%;background:linear-gradient(150deg,#0e3a63,var(--navy));
  color:#fff;font-weight:700;font-size:14px;display:flex;align-items:center;justify-content:center;
  letter-spacing:.03em;flex-shrink:0}
.user-chip .u-meta{line-height:1.25;padding-right:6px}
.user-chip .u-name{font-size:13.5px;font-weight:700;color:var(--navy);white-space:nowrap}
.user-chip .u-role{font-size:11px;color:var(--muted);font-weight:500;letter-spacing:.03em;text-transform:uppercase}
.user-chip .sep{width:1px;height:24px;background:var(--divider)}
.user-chip button.linklike{text-decoration:none;font-weight:600;margin:0}
[dir="rtl"] .user-chip .u-meta{padding-right:0;padding-left:6px}
.dash-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:22px;align-items:start}
@media(max-width:920px){.dash-grid{grid-template-columns:1fr}}
.dash-grid .card{margin-top:0;min-width:0}   /* long one-time links must never widen the column */
.card-head{display:flex;align-items:center;gap:11px;padding:0 0 14px;border-bottom:1px solid var(--divider);
  margin-bottom:16px}
.card-head .c-ico{width:36px;height:36px;border-radius:12px;background:var(--blue-bg);color:var(--navy);
  display:flex;align-items:center;justify-content:center;flex-shrink:0}
.card-head .c-ico svg{width:18px;height:18px}
.card-head h2,.card-head .dash-h3{margin:0}
.card-head .c-sub{font-size:12.5px;color:var(--muted);margin-top:1px}
.card-head .c-right{margin-left:auto}
[dir="rtl"] .card-head .c-right{margin-left:0;margin-right:auto}
.count-pill{font-size:11.5px;font-weight:700;color:var(--muted);background:transparent;
  border:none;border-radius:0;padding:0}

/* ---- in-call: room-head chips + timer ---- */
.room-head h1{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.chip{display:inline-flex;align-items:center;gap:7px;font-size:12px;font-weight:600;
  background:transparent;border:none;border-radius:0;padding:2px 0;color:var(--muted)}
.chip svg{width:13px;height:13px;flex-shrink:0}
.chip.timer{font-variant-numeric:tabular-nums;font-weight:700;color:var(--navy)}
.chip.timer:empty{display:none}

/* ---- in-call: floating labeled dock (.stage-controls stays overlaid) ---- */
.stage-controls{bottom:12px;gap:4px;align-items:flex-start}
.stage-controls .dk{pointer-events:auto;display:flex;flex-direction:column;align-items:center;gap:4px;
  min-width:58px;padding:0 2px}
.stage-controls .dk .ctl{margin:0}
/* No text-shadow: the drafts' .dk span has none (verified: computed
   text-shadow "none"). Ours carried one from the days when the dock floated over
   the dark video; on the white dock it only smudges the small grey caption. */
.dk-label{font-size:9.5px;font-weight:600;color:var(--muted);letter-spacing:.01em;white-space:nowrap;
  pointer-events:none}
.stage.multi .tiles{padding-bottom:92px}
.stage.has-pin:not(.has-screen):not(.wb-open) .tiles{padding-bottom:92px}
/* wb-open: compact column layout — icons only */
.room-wrap.wb-open .dk-label{display:none}
.room-wrap.wb-open .stage-controls .dk{min-width:0}
/* a hidden control hides its whole dock slot (label included) */
.stage-controls .dk:has(> .ctl[style*="none"]){display:none}
/* labels over the white end-overlay/waiting backgrounds stay readable (dark stages) */


/* ---- dashboard: today's consultations as a real table (2026-08-27) ----
   Replaces the unlabelled history list: every column has a header, all four
   sort (one active column at a time), and the identity check carries the three
   Anlage 4b outcomes - checked / not required / not checked. */
.tbl .thead,.tbl .trow{display:grid;gap:12px;align-items:center;padding:11px 22px}
/* ONE grid for the header and every row (2026-10-07, draft 01b-option2). The header and each row
   used to be separate grids; an `fr` column never shrinks below its content, so every grid sized
   its columns from its own text and the headings drifted up to 56px away from their values.
   With subgrid each column is measured across the header AND all rows: the name takes the rest
   ("…" when too long), the other three are as wide as their longest entry. Desktop only — the
   phone layout below (two lines per row, no header) is unchanged. */
@media(min-width:721px){
  .tbl{display:grid;grid-template-columns:minmax(0,1fr) auto auto auto;column-gap:12px}
  .tbl .thead,.tbl #meetingList,.tbl .trow{grid-column:1 / -1;display:grid;grid-template-columns:subgrid}
  .tbl .hist-empty{grid-column:1 / -1}
}
.tbl .thead{background:#f7fafd;border-bottom:1px solid var(--divider)}
.tbl .trow{border-bottom:1px solid var(--divider)}
.tbl .trow:last-child{border-bottom:none}
.th{display:inline-flex;align-items:center;gap:5px;border:none;background:transparent;padding:0;
  border-radius:0;box-shadow:none;
  font:inherit;font-size:10px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;
  color:var(--muted);cursor:pointer;white-space:nowrap;font-family:inherit}
/* Hover shows the sort arrow, it does not darken the label: the heading is not
   a piece of text that changes, and a colour jump on four headers at once read
   as the table flickering (review 2026-08-27). The ACTIVE column stays navy.
   SPECIFICITY, not taste: the global `button:hover:not(:disabled)` (0,2,1) drops
   a `0 8px 20px rgba(0,45,80,.3)` shadow behind every button, and the old plain
   `.th:hover` (0,2,0) lost to it — so pointing at a column header put a dark
   shadow behind the label that the draft does not have. These selectors outrank
   it and also cover :focus/:active, which the hover rule never addressed. They
   set no colour, so the active column keeps its navy from .th[aria-sort]. */
.tbl .thead .th,
.tbl .thead .th:hover:not(:disabled),
.tbl .thead .th:focus,
.tbl .thead .th:active{background:transparent;box-shadow:none}
.th.r{justify-content:flex-end;width:100%}
.th svg{width:11px;height:11px;opacity:0;transition:opacity .12s,transform .12s;flex-shrink:0}
.th:hover svg{opacity:.55}
.th[aria-sort]{color:var(--navy)}
.th[aria-sort] svg{opacity:1}
.th[aria-sort="descending"] svg{transform:rotate(180deg)}
.t-name{font-size:13.5px;font-weight:600;color:var(--navy);min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.t-name.none{color:var(--muted);font-weight:400}
.t-when{text-align:right;font-size:11.5px;color:var(--muted);font-variant-numeric:tabular-nums;line-height:1.35}
.t-when b{display:block;font-weight:600;font-size:12.5px;color:var(--navy)}
[dir="rtl"] .t-when,[dir="rtl"] .th.r{text-align:left;justify-content:flex-start}
/* Identity check: only the FACT of the check is ever stored, never eGK data. */
.t-idcheck{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;font-weight:600;
  color:var(--muted);white-space:nowrap;min-width:0}
.t-idcheck > span:last-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.t-idcheck .ic{width:20px;height:20px;border-radius:50%;flex-shrink:0;display:inline-flex;
  align-items:center;justify-content:center;background:#eef2f6;color:#8aa2b8}
.t-idcheck .ic svg{width:12px;height:12px}
.t-idcheck.done{color:#1e5e3a}
.t-idcheck.done .ic{background:rgba(30,94,58,.12);color:#1e5e3a}
.t-idcheck.na{color:#93a9bd;font-weight:500}
.t-idcheck.na .ic{background:transparent;border:1.5px dashed #cfdbe6;color:#a8bccd}

/* ---- "patient is known to the practice" (Anlage 4b declaration) ----
   The app cannot know this - the practice declares it, and we record who and
   when. Never a default: unticked leaves the consultation as "not checked". */
.known-row{display:flex;align-items:center;gap:11px;margin:0 0 12px;padding:11px 14px;
  background:#f7fafd;border:1px solid var(--divider);border-radius:14px;cursor:pointer}
.known-row input{width:18px;height:18px;flex-shrink:0;accent-color:var(--navy);cursor:pointer}
.known-row b{font-size:13px;font-weight:600;color:var(--navy)}
.known-row small{font-size:11.5px;color:var(--muted);margin-left:7px}

@media(max-width:720px){
  /* two lines per row - column headers make no sense at this width */
  .tbl .thead{display:none}
  .tbl .trow{grid-template-columns:1fr auto;row-gap:5px;padding:12px 14px}
  .tbl .trow .t-name{grid-column:1}
  .tbl .trow .t-status{grid-column:2;justify-self:end}
  .tbl .trow .t-idcheck{grid-column:1}
  .tbl .trow .t-when{grid-column:2}
  .known-row{align-items:flex-start}
  .known-row b,.known-row small{display:block;margin-left:0}
}


/* ---- in-call: slimmer dock (2026-08-27) ----
   The old dock ate ~110px of stage height. Smaller circles and labels give that
   back to the video; the labels stay, older patients navigate by them. */
.ctl{width:38px;height:38px}
.ctl svg{width:17px;height:17px}
.stage-controls{bottom:8px;gap:2px}
.stage-controls .dk{min-width:50px;gap:3px}
.dk-label{font-size:9.5px}



/* ---- Anlage 4b: "not required" sits with the eGK check ---- */
.idnotreq{margin-top:8px;width:100%}
.idnotreq-hint{font-size:11.5px;margin:6px 2px 0}


/* ---- in-call on a phone (2026-08-27) ----
   A 360px row cannot hold Mic/Camera/Share/Board/QR/End plus the panels. So on
   phones: Share + Whiteboard (+ the Anlage 4b check) fold into "Mehr", the QR
   handoff disappears (pointless when you ARE on the phone), and the sidebar
   becomes a sheet opened from Wartezimmer / Chat. Desktop is untouched. */
.dk.mob-only{display:none}
.more-sheet{display:none}
.more-sheet[hidden]{display:none}
@media(max-width:720px){
  .dk.mob-only{display:flex}
  /* moved into "Mehr" */
  /* Share and Board move into "Mehr"; the QR handoff is meaningless on a phone.
     The selector list MUST end here — leaving a trailing comma silently merged it
     into the next rule and none of these were hidden (2026-08-31). */
  .dk:has(> #screenBtn),.dk:has(> #wbBtn),.dk:has(> #qrBtn){display:none}
  /* the phone only tightens the dock spacing; placement is a base rule now */
  .stage-controls{flex-wrap:nowrap;padding:8px 6px 2px}
  .stage-controls .dk{min-width:44px}
  .more-sheet:not([hidden]){display:flex;flex-direction:column;gap:2px;
    position:absolute;left:10px;right:10px;bottom:76px;z-index:41;
    background:#fff;border:1px solid var(--border);border-radius:18px;padding:7px;
    box-shadow:0 16px 40px rgba(0,45,80,.28)}
  .sh-item{display:flex;align-items:center;gap:11px;width:100%;border:none;background:transparent;
    cursor:pointer;font-family:inherit;font-size:13.5px;font-weight:600;color:var(--navy);
    text-align:left;padding:9px 12px;border-radius:12px;box-shadow:none}
  .sh-item:hover{background:#f1f7fc;box-shadow:none}
  .sh-item .si{flex-shrink:0;width:32px;height:32px;border-radius:10px;background:#eef2f6;
    color:var(--navy);display:inline-flex;align-items:center;justify-content:center}
  .sh-item .si svg{width:16px;height:16px}
  /* the sidebar becomes a bottom sheet, opened from the dock */
  .room-wrap .sidebar{display:none}
  .room-wrap.sheet-open .sidebar{display:flex;position:fixed;left:0;right:0;bottom:0;z-index:40;
    max-height:64vh;overflow-y:auto;background:#fff;border-radius:20px 20px 0 0;
    padding:10px 10px 14px;box-shadow:0 -12px 34px rgba(0,45,80,.3)}
  .room-wrap.sheet-chat .waiting-panel,.room-wrap.sheet-wait .chat-panel{display:none}
  /* unread / waiting counter on the dock button */
  .dk{position:relative}
  .dk .badge{position:absolute;top:-3px;right:6px;min-width:16px;height:16px;padding:0 4px;
    border-radius:999px;background:var(--red);color:#fff;font-size:10px;font-weight:800;
    line-height:16px;text-align:center;border:2px solid #fff;pointer-events:none}
  .dk .badge:empty{display:none}
}

/* ---- sidebar panels: rounded drawer look ---- */
.panel{border-radius:20px;box-shadow:0 10px 28px rgba(0,45,80,.12)}
/* ✕ in the panel header — the bottom sheet's only visible exit on a phone
   (2026-09-02: patients found no way to close chat/waiting). Desktop keeps the
   plain sidebar, where a close control would be noise, so it stays hidden. */
.panel-close{display:none;margin-left:auto;width:30px;height:30px;padding:0;border-radius:50%;
  border:1px solid #c3d4e3;background:#fff;color:var(--navy);box-shadow:none;
  align-items:center;justify-content:center;flex-shrink:0;cursor:pointer}
.panel-close svg{width:15px;height:15px}
.panel-close:hover{background:#eef4fa;box-shadow:none}
@media(max-width:720px){.room-wrap.sheet-open .panel-close{display:inline-flex}}
/* plain uppercase heading, no leading dot — the drafts (05g/05h .dhead) do not
   have one, and a coloured dot reads as a status indicator that means nothing */
.panel h3{display:flex;align-items:center;gap:8px}

/* ---- chat: paperclip + file bubbles (file sharing) ---- */
.chat-input{align-items:center}
.clip-btn{flex-shrink:0;width:38px;height:38px;margin-top:0;padding:0;border-radius:50%;
  border:1px solid #c3d4e3;background:#fff;color:var(--navy);box-shadow:none;
  display:inline-flex;align-items:center;justify-content:center}
.clip-btn:hover:not(:disabled){background:#eef4fa;border-color:var(--navy);color:var(--navy);box-shadow:none}
.clip-btn svg{width:21px;height:21px} /* glyph fills the 38px circle better (2026-09-02) */
.chat-msg.file{display:flex;align-items:center;gap:10px;padding:9px 11px;max-width:96%}
.f-ico{flex-shrink:0;width:38px;height:38px;border-radius:12px;background:var(--blue-bg);color:var(--navy);
  display:inline-flex;align-items:center;justify-content:center}
.f-ico svg{width:17px;height:17px}
.f-meta{flex:1;min-width:0}
.f-name{display:block;font-size:12.5px;font-weight:700;color:var(--navy);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.f-size{display:block;font-size:11px;color:var(--muted)}
.f-save{flex-shrink:0;margin-top:0;padding:6px 14px;font-size:12px;font-weight:700;
  background:#fff;color:var(--navy);border:1.5px solid var(--navy);box-shadow:none;border-radius:999px}
.f-save:hover:not(:disabled){background:var(--navy);color:#fff;box-shadow:none}
.f-note{display:flex;align-items:flex-start;gap:7px;font-size:10.5px;color:#1e5e3a;font-weight:600;
  margin:2px 0 0 4px;line-height:1.4}
.f-note svg{width:11px;height:11px;flex-shrink:0;margin-top:1px}
.f-bar{height:5px;border-radius:999px;background:var(--divider);overflow:hidden;margin-top:5px}
.f-bar i{display:block;height:100%;width:0;border-radius:999px;background:var(--grad);transition:width .2s}


/* ---- immersive ("Vollbild") on a phone (2026-08-31) ----
   iOS Safari implements the Fullscreen API for <video> elements only, never for a
   container, so requestFullscreen() on the room can never work there. This is a
   CSS immersive mode instead: the call fills the viewport, the page chrome goes
   away, and the browser's own bars are all that remain. Where the real API exists
   (Android, desktop) the JS asks for it as well, so the browser chrome goes too.
   100dvh, not 100vh: on iOS 100vh is the tallest viewport and the bottom of the
   call would sit behind Safari's toolbar.
   The E2EE lock and the not-recorded marker stay visible — they are patient-facing
   duties (Anlage 31b §3/§4(2)) and a compliance indicator that disappears in a
   display mode is worse than no display mode. */
/* Phones only — belt and braces. Desktop uses the plain Fullscreen API on
   #roomWrap, which keeps the chat and waiting-room sidebar; making desktop
   immersive hid both and lost the panels mid-consultation (v0.1.44). Even if
   the class were ever set on a wide screen, none of this would apply. */
@media(max-width:720px){
  body.immersive{overflow:hidden}
  body.immersive .topbar,
  body.immersive .status,
  body.immersive .sidebar{display:none}
  /* both pages wrap the call in .room (patient: .wrap.center.room, doctor:
     .wrap.room), and the header <h1> with the lock, the not-recorded marker and the
     timer lives INSIDE it — so making .room the immersive container keeps those
     visible instead of hiding them behind a fixed .room-wrap. */
  body.immersive .room{position:fixed;inset:0;z-index:60;max-width:none;padding:0;
    height:100dvh;display:flex;flex-direction:column;background:var(--bg)}
  body.immersive .room h1{flex:none;margin:0;gap:8px;font-size:16px;color:#fff;
    padding:max(6px,env(safe-area-inset-top)) 12px 6px}
  body.immersive .room .room-head{flex:none;padding:0;margin:0}
  /* the patient page wraps the call in #callStep, the doctor page puts .room-wrap
     directly in .room — the flex chain has to reach the stage in BOTH, or the video
     keeps its 52vh minimum and leaves a dead area under the dock. */
  body.immersive #callStep{display:flex;flex-direction:column;flex:1;min-height:0}
  body.immersive .room-wrap{flex:1;min-height:0;height:auto;gap:0}
  body.immersive .stage{border-radius:0;padding:0 0 4px;flex:1;min-height:0}
  body.immersive .stage-controls{padding-bottom:max(8px,env(safe-area-inset-bottom))}
  /* the header sits on the dark stage now — on the doctor page the timer and the
     participant count live in .room-head next to the Identity button, on the
     patient page inside the <h1>, so both containers have to be covered */
  body.immersive .room .room-head{padding-right:12px}
  body.immersive .room :is(h1,.room-head) .chip{background:transparent;color:#dce8f3;border-color:transparent;padding-left:0}
  body.immersive .room h1 .e2ee-lock[data-state="checking"]{color:#cfe0ee}
}
