    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
    /* Design language ported from the /grok app (public/grok/style.css):
       deep-navy radial glow, soft radius system (cards 18 / buttons 14 /
       inner 10-12 / pills 999), blue gradient primary, system font stack. */
    :root {
      --bg:#0b1020; --surface:#161c30; --surface-hi:#1b2540; --border:#243049;
      --accent:#4f8cff; --accent-dim:#35578f; --en:#5b93ff; --vi:#f0a030;
      --text:#e6e9f2; --muted:#9aa3bd; --danger:#f87171; --ready:#34d399;
    }
    html, body {
      height:100%; background:var(--bg); color:var(--text);
      font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-size:16px;
      overflow:hidden; -webkit-tap-highlight-color:transparent; touch-action:manipulation;
    }
    /* Pinned, not merely sized. A phone browser can scroll its visible window
       away from the top of the page even when nothing is scrollable, and the
       app then sits with its head under the address bar and a dead band along
       the bottom until a rotation resets it. A fixed body cannot be scrolled
       away from, so there is nothing to reset. */
    body {
      position:fixed; top:0; left:0; right:0;
      width:100%; overscroll-behavior:none;
    }
    /* Older phones measure 100% before the address bar settles, so the layout
       is built for a screen taller than what is visible and a dead band sits
       at the bottom until a rotation forces a re-measure. --app-h is set from
       the real inner height by shared script (re-measured after load and on
       every resize); dvh replaces it where the browser understands it. */
    /* svh is the viewport with the browser's own bars showing, which is the
       one that is always really there. The measured height replaces it as
       soon as script runs. */
    body { height: var(--app-h, 100svh); }
    @supports not (height: 100svh) { body { height: var(--app-h, 100%); } }

    /* Screens stack on top of each other so incoming/outgoing can animate
       past one another instead of just popping. Direction-aware: forward
       nav slides from the right, back nav slides from the left. */
    .screen {
      position:absolute; inset:0;
      display:flex; flex-direction:column; align-items:center;
      /* Centred while it fits, scrollable the moment it does not. Plain
         `center` would put the overflow above the top edge where no amount of
         scrolling reaches it, which is how a screen ends up with its title
         cut off and no way to get at it. `safe` gives up centring instead. */
      justify-content:safe center;
      overflow-y:auto; overscroll-behavior:contain; -webkit-overflow-scrolling:touch;
      padding:24px 20px; gap:18px;
      background:radial-gradient(1200px 600px at 50% -10%, #16203c 0%, var(--bg) 55%);
      visibility:hidden; pointer-events:none;
      transform:translateX(0) scale(1); opacity:1;
      transition:transform .36s cubic-bezier(.16,1,.3,1), opacity .3s ease;
    }
    .screen.active { visibility:visible; pointer-events:auto; }
    .screen.no-anim { transition:none !important; }
    .screen.pos-right { transform:translateX(7%) scale(.96); opacity:0; }
    .screen.pos-left  { transform:translateX(-7%) scale(.96); opacity:0; }

    @keyframes room-live-pulse {
      0%   { box-shadow:0 0 0 0 rgba(0,212,170,.55); }
      70%  { box-shadow:0 0 0 18px rgba(0,212,170,0); }
      100% { box-shadow:0 0 0 0 rgba(0,212,170,0); }
    }
    .pulse-live { animation:room-live-pulse .9s cubic-bezier(.16,1,.3,1); border-radius:14px; }
    .logo { font-size:12px; font-weight:700; letter-spacing:.22em; color:var(--muted); text-transform:uppercase; }
    .logo span { color:var(--accent); }
    .logo em { font-style:normal; color:var(--vi); font-size:9px; vertical-align:super; }

    .title { font-size:32px; font-weight:800; line-height:1.1; text-align:center; letter-spacing:.02em; }
    .title em { font-style:normal; color:var(--accent); }
    .sub { font-size:14px; color:var(--muted); text-align:center; max-width:320px; line-height:1.5; }

    .btn-primary { width:100%; max-width:320px; padding:17px 24px; background:linear-gradient(180deg,#5b93ff,#3b74e6); color:#fff; border:none; border-radius:14px; font-size:17px; font-weight:700; cursor:pointer; transition:transform .05s ease, opacity .15s; }
    .btn-primary:active { opacity:.9; transform:scale(.99); }
    /* The two doors, told apart by colour: green to open a room, blue to walk
       into one somebody already opened. Scoped to these two buttons so the
       rest of the app keeps one accent. */
    #btn-two-host { background:linear-gradient(180deg,#3ecf8e,#1f9d65); }
    #btn-have-code { background:linear-gradient(180deg,#5b93ff,#3b74e6); color:#fff; border:none;
      padding:17px 24px; border-radius:14px; font-size:17px; }
    .btn-secondary { width:100%; max-width:320px; padding:14px 24px; background:var(--surface-hi); color:var(--text); border:1px solid var(--border); border-radius:12px; font-size:15px; font-weight:700; cursor:pointer; }
    .divider { display:flex; align-items:center; gap:12px; width:100%; max-width:320px; }
    .divider::before,.divider::after { content:''; flex:1; height:1px; background:var(--border); }
    .divider span { font-size:12px; color:var(--muted); letter-spacing:.12em; }
    .input-row { display:flex; gap:8px; width:100%; max-width:320px; }
    .code-input { flex:1; padding:14px; background:var(--surface-hi); border:1px solid var(--border); border-radius:12px; color:var(--text); font-size:17px; letter-spacing:.25em; text-align:center; font-variant-numeric:tabular-nums; outline:none; }
    .code-input:focus { border-color:var(--accent); }
    .btn-go { padding:14px 22px; background:var(--surface-hi); border:1px solid var(--border); border-radius:12px; color:var(--accent); font-size:17px; font-weight:700; cursor:pointer; }

    .status-row { display:flex; align-items:center; gap:8px; font-size:12px; letter-spacing:.1em; text-transform:uppercase; font-weight:600; color:var(--muted); }
    .dot { width:9px; height:9px; border-radius:50%; background:var(--muted); flex-shrink:0; }
    .dot.waiting { background:var(--en); animation:pulse 1.2s infinite; }
    .dot.live    { background:var(--ready); animation:pulse 1.5s infinite; }
    .dot.error   { background:var(--danger); }
    .dot.muted   { background:var(--vi); }
    @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:.25} }

    /* Full-screen OK-to-talk light (ported from the Grok app) — green while
       the mic is actively listening, red while gated (TTS playing / off). */
    #screen-active.talk-go   { background: radial-gradient(circle at 50% 22%, #18c574 0%, #0a7a45 78%); transition: background .3s ease; }
    #screen-active.talk-wait { background: radial-gradient(circle at 50% 22%, #ef5a56 0%, #9e1b1b 78%); transition: background .3s ease; }

    /* UI language: a flag in the top-right corner of every screen. A flag is
       readable without already knowing the interface language, which a text
       label ("UI language") is not — that was the whole problem. The native
       <select> sits invisible on top so tapping opens the OS picker, where
       each language is listed in its own script. */
    .ui-lang-corner { position:fixed; top:10px; right:10px; z-index:500; }
    .ui-lang-flag { display:flex; align-items:center; gap:5px; background:var(--surface-hi); border:1px solid var(--border); border-radius:999px; padding:6px 11px; font-size:17px; line-height:1; box-shadow:0 4px 14px rgba(0,0,0,.3); }
    .ui-lang-flag .chev { font-size:10px; color:var(--muted); }
    .ui-lang-corner select { position:absolute; inset:0; width:100%; height:100%; opacity:0; cursor:pointer; }
    html[dir="rtl"] .ui-lang-corner { right:auto; left:10px; }
    .room-controls { display:flex; align-items:center; gap:6px; }
    .mode-switch-btn { color:var(--accent); border-color:var(--accent-dim); }
    .btn-leave-compact { width:auto; max-width:none; align-self:center; padding:7px 14px; font-size:12px; flex:0 0 auto; }

    /* LOBBY */
    #qr-box { background:#fff; padding:12px; border-radius:12px; }
    #qr-box img, #qr-box canvas { display:block; }
    .room-code-value { font-size:28px; font-weight:800; color:var(--accent); letter-spacing:.22em; font-variant-numeric:tabular-nums; }
    .earbud-hint { display:flex; gap:10px; align-items:center; background:var(--surface); border:1px solid var(--border); border-radius:12px; padding:12px 16px; max-width:320px; font-size:13px; color:var(--muted); line-height:1.45; }
    .earbud-hint b { color:var(--text); font-weight:600; }

    /* Same-room push-to-talk toggle, on the two-phone setup screens */
    .room-toggle { display:flex; align-items:center; gap:10px; width:100%; max-width:320px; background:var(--surface); border:1px solid var(--border); border-radius:12px; padding:12px 16px; cursor:pointer; }
    .room-toggle input { width:19px; height:19px; accent-color:var(--accent); flex-shrink:0; cursor:pointer; }
    .room-toggle span { font-size:13px; color:var(--text); line-height:1.4; }

    /* Push-to-talk button on the active screen — replaces the sensitivity
       slider (which has nothing to gate against once PTT overrides it). */
    .ptt-row { width:100%; }
    .ptt-button { width:100%; height:100%; padding:14px 24px; border-radius:12px; background:var(--surface-hi); border:1px solid var(--border); color:var(--text); font-size:15px; font-weight:700; cursor:pointer; -webkit-user-select:none; user-select:none; touch-action:manipulation; transition:background .1s,border-color .1s,transform .05s; }
    .ptt-button.held { background:linear-gradient(180deg,#3ddfa4,#1fae76); border-color:#1fae76; color:#052e1e; transform:scale(.98); }
    .lang-block { width:100%; max-width:320px; display:flex; flex-direction:column; gap:6px; }
    .lang-block label { font-size:12px; letter-spacing:.08em; text-transform:uppercase; font-weight:600; color:var(--muted); text-align:center; }
    .lang-select { width:100%; padding:13px; background:var(--surface-hi); border:1px solid var(--border); border-radius:12px; color:var(--text); font-size:16px; font-weight:600; outline:none; appearance:none; text-align:center; text-align-last:center; cursor:pointer; }
    .lang-select:focus { border-color:var(--accent); }

    /* ACTIVE — conversation canvas */
    #screen-active { justify-content:flex-start; padding:12px; gap:8px; }
    .session-header { display:flex; align-items:center; justify-content:space-between; width:100%; padding:0 4px; }
    .lang-pair { font-size:13px; font-weight:700; display:flex; gap:6px; align-items:center; }
    .lang-pair .arrow { color:var(--muted); font-size:10px; }
    .room-tag { font-size:11px; color:var(--muted); letter-spacing:.12em; font-variant-numeric:tabular-nums; }
    .lang-switch-btn { padding:6px 12px; background:var(--surface-hi); border:1px solid var(--border); border-radius:999px; color:var(--muted); font-size:11px; font-weight:600; cursor:pointer; }
    .lang-switch-btn:active { border-color:var(--accent); color:var(--accent); }

    .banner { display:none; width:100%; padding:10px 14px; border-radius:12px; text-align:center; font-size:13px; font-weight:700; }
    .banner.show { display:block; }
    .banner.error { background:rgba(248,113,113,.12); border:1px solid var(--danger); color:var(--danger); }

    /* Single running column, no matter one phone or two: translation big,
       original small + subtler underneath. No left/right "mine vs theirs"
       split — .mine/.theirs classes are still added for internal bookkeeping
       but render identically. */
    #canvas { flex:1; width:100%; overflow-y:auto; display:flex; flex-direction:column; gap:8px; padding:6px 2px; -webkit-overflow-scrolling:touch; }
    .bubble { width:100%; padding:12px 16px; border-radius:14px; line-height:1.35; background:var(--surface); border:1px solid var(--border); box-shadow:0 6px 20px rgba(0,0,0,.25); }
    .bubble .primary { font-size:18px; }
    .bubble .secondary { font-size:14px; color:var(--text); text-align:right; margin-top:6px; padding-top:6px; border-top:1px solid var(--border); }
    .replay-row { margin-top:8px; display:flex; justify-content:flex-end; gap:6px; }
    .replay-btn { background:var(--surface-hi); border:1px solid var(--border); border-radius:999px; color:var(--accent); font-size:12px; font-weight:700; padding:5px 14px; cursor:pointer; }
    .replay-btn:active { opacity:.6; }
    /* "What did they actually mean?" — the thing a word-for-word translator
       cannot do. Sits beside PLAY on every bubble. */
    .explain-btn { background:var(--surface-hi); border:1px solid var(--border); border-radius:999px; color:var(--vi); font-size:13px; font-weight:700; padding:5px 13px; cursor:pointer; }
    .explain-btn:active { opacity:.6; }
    .explain-note { margin-top:8px; padding:10px 12px; border-radius:12px; background:rgba(240,160,48,.10); border:1px solid rgba(240,160,48,.35); color:var(--text); font-size:14px; line-height:1.45; }
    .explain-note .explain-head { display:block; font-size:11px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--vi); margin-bottom:3px; }
    .bubble.pending .primary { color:var(--muted); }
    .tone-badge { display:flex; align-items:center; gap:5px; margin-top:6px; padding:4px 11px; border-radius:999px; font-size:11px; font-weight:600; width:fit-content; }
    .tone-badge.good { background:rgba(52,211,153,.12); border:1px solid rgba(52,211,153,.35); color:var(--ready); }
    .tone-badge.warn { background:rgba(240,160,48,.12); border:1px solid rgba(240,160,48,.35); color:var(--vi); }
    .tone-badge.bad  { background:rgba(248,113,113,.12); border:1px solid rgba(248,113,113,.35); color:var(--danger); }
    .tone-note { color:var(--muted); font-style:italic; margin-left:3px; text-transform:none; font-weight:400; }

    /* TEXT MODE — two persistent columns */
    #screen-text-solo, #screen-text-room { justify-content:flex-start; padding-top:28px; gap:12px; }
    .text-lang-select { padding:10px 6px; font-size:13px; }
    .text-columns-head { display:grid; grid-template-columns:1fr 1fr; gap:8px; width:100%; max-width:480px; }
    .text-columns-head select, .text-columns-head span { text-align:center; }
    .text-columns-head span { font-size:12px; letter-spacing:.08em; font-weight:600; color:var(--muted); text-transform:uppercase; padding:8px 0; }
    .text-columns-body { flex:1; width:100%; max-width:480px; overflow-y:auto; display:flex; flex-direction:column; gap:6px; padding:2px; -webkit-overflow-scrolling:touch; }
    .text-row { display:grid; grid-template-columns:1fr 1fr; gap:8px; }
    .text-cell { padding:10px 12px; background:var(--surface); border:1px solid var(--border); border-radius:12px; font-size:15px; line-height:1.35; word-break:break-word; }
    .text-cell.pending { color:var(--muted); font-style:italic; }
    .text-cell.col-a { border-left:3px solid var(--en); }
    .text-cell.col-b { border-left:3px solid var(--vi); }
    .text-cell .tone-badge { margin-top:5px; }
    .text-col-input-row { display:flex; gap:8px; width:100%; max-width:480px; }
    .chat-input { flex:1; padding:11px 14px; background:var(--surface-hi); border:1px solid var(--border); border-radius:12px; color:var(--text); font-size:15px; outline:none; }
    .chat-input:focus { border-color:var(--accent); }
    .chat-send-btn { padding:11px 18px; background:var(--surface-hi); border:1px solid var(--border); border-radius:12px; color:var(--accent); font-size:14px; font-weight:700; cursor:pointer; }
    .chat-send-btn:active { border-color:var(--accent); }

    /* Voice-forward toggle */
    #btn-voice-forward.off { color:var(--muted); border-color:var(--border); opacity:.6; }
    #btn-voice-forward.on { color:var(--accent); border-color:var(--accent-dim); }

    /* Pause toggle — big and obvious, sits right under the VAD meter. Shares
       .action-row with the push-to-talk button when PTT is on, so the two
       sit side by side instead of stacking and eating vertical space. */
    .btn-pause { flex:0 0 auto; width:44px; height:36px; padding:0; background:var(--surface-hi); color:var(--text); border:1px solid var(--border); border-radius:10px; font-size:15px; font-weight:700; cursor:pointer; }
    .btn-pause.paused { background:var(--vi); border-color:var(--vi); color:#241503; }
    /* Hard 50/50 split via a fixed flex-basis, not flex-grow. flex:1 with
       min-width:0 on both still rendered unequal (measured 320px vs 370px
       for identical flex-grow/shrink/min-width) — a <button> as a direct
       flex item and a <div> wrapping a <button> apparently don't get the
       same automatic minimum-size treatment. flex-grow:0/flex-shrink:0
       with a fixed basis sidesteps that entirely: each is exactly half the
       row, full stop, regardless of content. */
    .action-row { display:flex; gap:8px; width:100%; align-items:stretch; }
    .action-row .ptt-row { flex:0 0 calc(50% - 4px); width:auto; }
    .action-row .btn-pause { flex:0 0 calc(50% - 4px); width:auto; }

    /* Typed-message row on the voice active screen (chat-input/-send-btn shared with text mode, defined above) */
    .chat-input-row { display:flex; gap:8px; width:100%; padding-top:4px; }
    #live-partial { align-self:flex-end; max-width:86%; font-size:14px; color:var(--muted); font-style:italic; padding:0 6px; display:none; }

    .vad-area { width:100%; display:flex; flex-direction:column; align-items:center; gap:8px; padding:6px 0 2px; }
    .vad-bars { display:flex; align-items:center; gap:4px; height:34px; }
    .vad-bar { width:5px; border-radius:3px; background:var(--muted); transition:height .08s ease, background .15s; min-height:4px; }
    .vad-bar.active { background:var(--ready); }
    .vad-status { font-size:12px; letter-spacing:.1em; font-weight:600; text-transform:uppercase; color:var(--muted); display:flex; align-items:center; gap:8px; }
    .vad-status.speaking { color:var(--ready); }
    .vad-status.muted { color:var(--vi); }

    /* Mic sensitivity — drag until room noise stops lighting the meter above */
    /* One line along the bottom: the microphone slider, and pause beside it.
       A label over the slider and a full width button under it were three
       stacked rows doing the work of one, on the screen with the least room
       to spare. */
    .bottom-row { width:100%; display:flex; align-items:center; gap:10px; padding:2px 2px 6px; }
    .sens-icon { font-size:13px; flex:0 0 auto; }
    .bottom-row input[type=range] { flex:1; min-width:0; height:22px; background:transparent; appearance:none; cursor:pointer; }
    .bottom-row input[type=range]::-webkit-slider-runnable-track { height:4px; border-radius:999px; background:var(--border); }
    .bottom-row input[type=range]::-webkit-slider-thumb { appearance:none; width:18px; height:18px; margin-top:-7px; border-radius:50%; background:var(--accent); border:2px solid var(--bg); }
    .bottom-row input[type=range]::-moz-range-track { height:4px; border-radius:999px; background:var(--border); }
    .bottom-row input[type=range]::-moz-range-thumb { width:16px; height:16px; border-radius:50%; background:var(--accent); border:2px solid var(--bg); }

    .toast { position:fixed; bottom:24px; left:50%; transform:translateX(-50%); background:var(--danger); color:#fff; padding:11px 22px; border-radius:999px; font-size:13px; font-weight:600; opacity:0; pointer-events:none; transition:opacity .3s; z-index:999; max-width:90vw; text-align:center; box-shadow:0 8px 24px rgba(0,0,0,.35); }
    .toast.info { background:var(--accent); color:#fff; }
    .toast.show { opacity:1; }
    /* HOME — grouped sections */
    .home-group { width:100%; max-width:320px; display:flex; flex-direction:column; align-items:center; gap:10px; }
    .home-group-label { font-size:12px; letter-spacing:.14em; font-weight:700; color:var(--muted); text-transform:uppercase; }
    .home-group-row { display:flex; gap:10px; width:100%; }
    .home-group-row .btn-primary { width:auto; flex:1; max-width:none; }
    .btn-link { display:block; text-align:center; text-decoration:none; }

    /* QR SCAN */
    .qr-scan-viewport { position:relative; width:100%; max-width:320px; aspect-ratio:1/1; background:#000; border-radius:14px; overflow:hidden; }
    #qr-scan-video { width:100%; height:100%; object-fit:cover; }
    .qr-scan-frame { position:absolute; inset:12%; border:2px solid var(--accent); border-radius:14px; box-shadow:0 0 0 999px rgba(0,0,0,.35); pointer-events:none; }

    /* Correct-and-redo — editable "original heard" text under own bubbles */
    .secondary.editable { display:flex; align-items:center; justify-content:space-between; gap:8px; }
    .secondary.editable .secondary-text { flex:1; }
    .edit-btn { background:none; border:none; color:var(--muted); font-size:12px; line-height:1; cursor:pointer; padding:2px 4px; flex-shrink:0; }
    .edit-btn:active { opacity:.6; }
    .secondary.editing { display:flex; gap:6px; align-items:center; }
    .edit-input { flex:1; min-width:0; padding:7px 10px; background:var(--bg); border:1px solid var(--accent-dim); border-radius:10px; color:var(--text); font-size:13px; outline:none; }
    .edit-input:focus { border-color:var(--accent); }
    .edit-save-btn, .edit-cancel-btn { background:var(--surface-hi); border:1px solid var(--border); border-radius:10px; font-size:12px; padding:6px 10px; cursor:pointer; flex-shrink:0; }
    .edit-save-btn { color:var(--ready); }
    .edit-cancel-btn { color:var(--danger); }

    /* Liveness check — "are you still there" safety net for voice sessions */
    #liveness-check { position:fixed; inset:0; background:rgba(11,16,32,.93); display:none; align-items:center; justify-content:center; z-index:1000; padding:24px; }
    #liveness-check.show { display:flex; }
    .liveness-card { width:100%; max-width:320px; display:flex; flex-direction:column; align-items:center; gap:16px; text-align:center; }
    .liveness-card .title { font-size:26px; }
    .liveness-countdown { font-size:13px; letter-spacing:.1em; font-weight:700; color:var(--vi); text-transform:uppercase; }
    #btn-liveness-confirm { background:linear-gradient(180deg,#3ddfa4,#1fae76); color:#052e1e; }

    @media (max-width:360px){ .title{font-size:30px} .room-code-value{font-size:22px} }

    /* Cultural review — held between the writer tapping SEND and the message
       actually going out, so it sits directly above the input it belongs to. */
    .review-panel { display:none; margin:0 0 8px; padding:12px 14px; border-radius:14px;
      background:rgba(240,160,48,.10); border:1px solid rgba(240,160,48,.40); }
    .review-panel.show { display:block; }
    .review-head { display:block; font-size:11px; font-weight:700; letter-spacing:.08em;
      text-transform:uppercase; color:var(--vi); margin-bottom:4px; }
    .review-note { font-size:14px; line-height:1.45; color:var(--text); }
    .review-draft { margin-top:8px; padding:8px 10px; border-radius:10px; background:var(--surface);
      border:1px solid var(--border); font-size:14px; color:var(--muted); word-break:break-word; }
    .review-actions { margin-top:10px; display:flex; gap:8px; }
    .review-actions button { flex:1; border-radius:999px; padding:9px 12px; font-size:13px;
      font-weight:700; letter-spacing:.04em; cursor:pointer; border:1px solid var(--border); }
    .btn-review-edit { background:var(--surface-hi); color:var(--text); }
    .btn-review-send { background:rgba(240,160,48,.22); border-color:rgba(240,160,48,.55); color:var(--text); }
    .review-actions button:active { opacity:.6; }
    /* The check runs on the send tap, so the input needs to show it is busy. */
    .chat-send-btn.checking { opacity:.55; pointer-events:none; }
    /* "?" on a text cell sits under the text, same idea as the voice bubbles. */
    .text-cell .replay-row { margin-top:6px; justify-content:flex-start; }

    /* The link a host sends to a partner in another city. Shown above the QR,
       which only helps when the two phones are in the same room. */
    .lobby-link { display:flex; flex-direction:column; align-items:center; gap:10px; width:100%; max-width:320px; }
    .room-link { width:100%; font-size:13px; word-break:break-all; color:var(--muted); padding:9px 12px;
      background:var(--surface); border:1px solid var(--border); border-radius:10px; text-align:center; }
    .lobby-link .home-group-row { width:100%; }

    /* A typed code on the home screen joins by itself at the sixth digit. */
    .home-code { width:100%; max-width:320px; flex:none; margin-top:4px; font-size:15px; letter-spacing:.18em; }
    /* Voice only: browsers refuse to open a microphone or play sound without
       a tap, so while sound is locked the whole screen is the tap target. */
    .tap-overlay { position:fixed; inset:0; z-index:1000; display:flex; align-items:center; justify-content:center;
      padding:32px; background:rgba(11,16,32,.94); cursor:pointer; }
    .tap-overlay-text { font-size:24px; font-weight:800; text-align:center; line-height:1.3; color:var(--text);
      max-width:420px; animation:tap-pulse 1.6s ease-in-out infinite; }
    @keyframes tap-pulse { 0%,100% { opacity:.75 } 50% { opacity:1 } }

    /* Video call: a square picture at the top, and everything that was said
       underneath it. Subtitles used to sit over the face and fade after a few
       seconds, which meant one line at a time and no way back: miss it and it
       was gone. The conversation now stacks up in its own box and scrolls, so
       you can look back at what was actually said. */
    /* align-items:stretch matters: the screens are centring columns, and left
       to that the transcript would sit in a narrow strip down the middle. */
    .video-screen { padding:0; justify-content:flex-start; background:var(--bg);
      display:flex; flex-direction:column; align-items:stretch;
      /* The screen itself must not scroll: the transcript inside it does. Two
         nested scrollers fight each other, and the outer one was also taking
         a gutter out of the width. */
      overflow:hidden; }
    /* Square. It keeps the camera's full width, which is what two faces need,
       and gives the words the room the picture was wasting. */
    .video-frame { position:relative; width:100%; aspect-ratio:1/1; flex:0 0 auto;
      overflow:hidden; background:#0d1220; }
    #remote-video { width:100%; height:100%; object-fit:cover; display:block; background:#0d1220; }
    #local-video { position:absolute; top:max(12px, env(safe-area-inset-top)); right:12px; width:26vw; max-width:140px; aspect-ratio:1/1;
      object-fit:cover; border-radius:12px; border:2px solid rgba(255,255,255,.35); background:#111; z-index:2;
      cursor:grab; touch-action:none; transition:left .18s ease, top .18s ease; }
    #local-video:active { cursor:grabbing; }
    .video-top { position:absolute; top:max(12px, env(safe-area-inset-top)); left:12px; z-index:2; display:flex; align-items:center; gap:10px;
      padding:6px 10px; border-radius:999px; background:rgba(0,0,0,.45); }

    /* The transcript. Newest at the bottom, the way a conversation reads. */
    .video-subs { width:100%; flex:1 1 auto; min-height:0; overflow-y:auto; -webkit-overflow-scrolling:touch;
      padding:12px 14px 4px; display:flex; flex-direction:column; gap:10px; background:var(--bg); }
    .sub-line { max-width:100%; }
    .sub-line .who { font-size:11px; font-weight:800; letter-spacing:.08em; text-transform:uppercase; color:var(--muted); }
    .sub-line .what { font-size:18px; line-height:1.35; color:var(--text); }
    /* My own words are secondary: they are there so I can see what the phone
       heard, not to be read. Theirs is the thing being read. */
    .sub-line.mine .what { font-size:15px; color:var(--muted); }
    .sub-line.provisional .what { opacity:.65; font-style:italic; }
    .video-subs-empty { color:var(--muted); font-size:14px; text-align:center; padding:18px 8px; }

    .video-actions { flex:0 0 auto; padding:10px 0 max(12px, env(safe-area-inset-bottom));
      display:flex; justify-content:center; gap:14px; background:var(--bg); }
    .video-btn { width:52px; height:52px; border-radius:50%; border:1px solid rgba(255,255,255,.3); background:rgba(255,255,255,.08);
      color:#fff; font-size:20px; cursor:pointer; }
    .video-btn.off { background:var(--danger); }
    .video-btn.leave { width:auto; padding:0 22px; border-radius:999px; font-size:14px; font-weight:800; letter-spacing:.06em; background:var(--danger); }

    /* A subtitle still being spoken: same place, visibly not final yet. */
    .caption-main.provisional { opacity:.72; font-style:italic; }

    /* Faces beside the conversation. No audio anywhere in this app.
       Portrait: two small thumbnails, deliberately modest, because the
       conversation is the point and a phone held upright has little height
       to spare. Landscape: the picture moves to a column down the side, so
       it costs the messages nothing at all. */
    .text-video { position:relative; display:flex; gap:8px; align-items:center; justify-content:center;
      width:100%; margin:4px auto 2px; flex:0 0 auto; }
    .text-video video { height:96px; aspect-ratio:3/4; width:auto; object-fit:cover; border-radius:10px;
      background:#0d1220; border:1px solid var(--border); }
    /* Keyboard up: smaller again, so the messages never get pushed away. */
    .text-video.compact video { height:60px; }
    .text-video-note { position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
      text-align:center; font-size:13px; color:var(--muted); pointer-events:none; padding:0 12px; }
    .lang-switch-btn.on { border-color:var(--accent); color:var(--accent); }

    @media (orientation: landscape) {
      /* Sideways there is width to spare and no height. The video becomes a
         column on the left and the conversation keeps its full height. */
      #screen-text-room.has-video { padding-left:132px; }
      #screen-text-room.has-video .text-video { position:fixed; left:10px; top:10px; bottom:10px; width:112px;
        margin:0; flex-direction:column; justify-content:center; gap:10px; z-index:3; }
      #screen-text-room.has-video .text-video video { width:112px; height:auto; aspect-ratio:3/4; }
      #screen-text-room.has-video .text-video.compact video { height:auto; }
    }

    /* Leaving is a small control in the header, not a big target beside SEND. */
    .lang-switch-btn.leave { color:var(--danger); border-color:rgba(248,113,113,.4); font-weight:700; }

    /* AI Assist inside a two-phone room. */
    .chat-send-btn.assist { flex:0 0 auto; padding:0 14px; }
    .compose-card { border:1px solid var(--vi); border-radius:12px; background:var(--surface); padding:12px; margin:0 auto 8px;
      width:100%; max-width:520px; max-height:46vh; overflow-y:auto; }
    .compose-head { font-size:11px; font-weight:800; letter-spacing:.14em; color:var(--vi); margin-bottom:6px; }
    .candidate { border:1px solid var(--border); border-radius:10px; padding:10px; margin-top:8px; background:var(--bg); }
    .candidate .register { font-size:10px; font-weight:700; letter-spacing:.12em; color:var(--vi); text-transform:uppercase; }
    .candidate .back { font-size:16px; margin:4px 0 6px; }
    .candidate .target { font-size:13px; color:var(--muted); border-top:1px solid var(--border); padding-top:5px; }
    .candidate .note { font-size:12px; color:var(--muted); margin-top:5px; font-style:italic; }
    .candidate .btn-primary { margin-top:8px; width:100%; max-width:none; padding:10px; font-size:13px; }
    .aa-chip { margin-top:6px; padding:1px 6px; border:1px solid var(--vi); border-radius:4px; background:transparent;
      color:var(--vi); font-size:9px; font-weight:700; letter-spacing:.1em; cursor:pointer; }
    .aa-note { margin-top:6px; padding:8px 10px; border-radius:8px; background:rgba(240,160,48,.10);
      border:1px solid rgba(240,160,48,.35); font-size:13px; line-height:1.45; grid-column:1 / span 2; }

    /* One phone: leaving is a small control up top, like the two-phone room. */
    .solo-head { display:flex; justify-content:flex-end; margin-bottom:6px; }

    /* The AI Assist panel starts closed; this page has no global .hidden. */
    .compose-card.hidden { display:none; }
    /* The language picker floats over the top-right corner, so the room header
       keeps its controls clear of it. */
    #screen-text-solo .solo-head { padding-right:96px; }

    /* Help: a quiet way in, and a page that reads like a page. */
    .btn-help { display:block; margin:14px auto 0; padding:8px 16px; background:transparent;
      border:1px solid var(--border); border-radius:999px; color:var(--muted);
      font-size:11px; font-weight:700; letter-spacing:.14em; cursor:pointer; }
    .help-doc { width:100%; max-width:560px; margin:6px auto 18px; text-align:left; }
    .help-title { font-size:17px; font-weight:800; letter-spacing:.1em; color:var(--accent); margin-bottom:10px; }
    .help-intro { font-size:15px; line-height:1.55; margin-bottom:18px; }
    .help-heading { font-size:13px; font-weight:800; letter-spacing:.1em; text-transform:uppercase;
      color:var(--vi); margin:18px 0 7px; }
    .help-line { font-size:15px; line-height:1.55; color:var(--text); margin-bottom:7px; }

    /* The hidden attribute has to win. Author rules that set display (like
       .home-group) quietly beat the browser's own [hidden] rule, which is how
       a section marked hidden stays on screen. */
    [hidden] { display:none !important; }

    /* Room header: status on the left, VIDEO in the middle where nobody has to
       hunt for it, room code and the way out together on the right. */
    #text-room-header { display:grid; grid-template-columns:1fr auto 1fr; gap:8px; align-items:center; }
    #text-room-header .status-row { min-width:0; }
    #text-room-header .header-right { display:flex; align-items:center; justify-content:flex-end; gap:8px; }
    #text-room-header #btn-video-toggle { white-space:nowrap; }

    /* The home screen leads with one action. Everything else is quieter. */
    .btn-start { font-size:17px; padding:16px 20px; }
    .home-foot { margin-top:auto; padding-top:18px; display:flex; flex-direction:column;
      align-items:center; gap:7px; width:100%; }
    .home-foot-label { font-size:12px; color:var(--muted); }
    .btn-quiet { padding:8px 16px; background:transparent; border:1px solid var(--border);
      border-radius:999px; color:var(--muted); font-size:11px; font-weight:700;
      letter-spacing:.12em; cursor:pointer; }
    .btn-quiet:active { border-color:var(--accent); color:var(--accent); }
    #screen-home { justify-content:flex-start; padding-top:34px; }

    /* Which line is which. Without this, your own message coming back in the
       other person's language looks like the app translated the wrong way. */
    .cand-tag { font:600 9px 'IBM Plex Mono', ui-monospace, monospace; letter-spacing:.12em;
      color:var(--muted); margin-top:6px; }
    .candidate .back-tag { margin-top:4px; }

    /* Every screen is a fixed panel over a body that never scrolls, which is
       right for the app and wrong for a document. Help is longer than a phone
       screen, so it scrolls inside itself and starts at the top. */
    #screen-help { justify-content:flex-start; }
    #screen-help .help-doc { flex:0 0 auto; }
    #screen-help #btn-help-back { flex:0 0 auto; margin-bottom:8px; }

    /* Three ways to invite someone, one on screen at a time. */
    .invite-tabs { display:flex; gap:6px; width:100%; max-width:340px; }
    .invite-tab { flex:1; padding:9px 4px; background:transparent; border:1px solid var(--border);
      border-radius:999px; color:var(--muted); font-size:10px; font-weight:700;
      letter-spacing:.1em; cursor:pointer; }
    .invite-tab.on { border-color:var(--accent); color:var(--accent); background:var(--surface-hi); }
    .invite-pane { display:flex; flex-direction:column; align-items:center; gap:10px;
      width:100%; max-width:340px; }
    #pane-code .room-code-value { display:block; }

    /* The pronoun question, only on screen when Vietnamese is in the room. */
    .persona-block { width:100%; max-width:320px; display:flex; flex-direction:column; gap:8px;
      padding:12px; border:1px solid var(--border); border-radius:12px; background:var(--surface); }
    .persona-why { font-size:12px; color:var(--muted); line-height:1.45; }
    .persona-row { display:flex; align-items:center; gap:8px; }
    .persona-row label { font-size:12px; color:var(--muted); flex:0 0 74px; text-align:left; }
    .persona-row .lang-select { flex:1; }

    /* Three ways to invite someone, and nothing else until one is chosen. */
    .invite-choice { display:flex; flex-direction:column; gap:12px; width:100%; max-width:340px; }
    .btn-invite { padding:22px 16px; background:var(--surface-hi); border:1px solid var(--border);
      border-radius:14px; color:var(--text); font-size:15px; font-weight:800;
      letter-spacing:.12em; cursor:pointer; }
    .btn-invite:active { border-color:var(--accent); color:var(--accent); }

    /* Browsers without `safe` alignment, which includes the Safari on a lot of
       phones. This used to centre with auto margins, and that is the very trap
       `safe` exists to avoid: when the content is taller than the screen, an
       auto top margin pushes the first line above the top edge where no amount
       of scrolling reaches it. The page opens with its heading already gone.
       Start at the top instead. Everything stays reachable. */
    @supports not (justify-content: safe center) {
      .screen { justify-content:flex-start; }
    }
    /* The room lays itself out top to bottom and manages its own scrolling. */
    #screen-text-room, #screen-text-solo { justify-content:flex-start; overflow:hidden; }

    /* Going back into a room you just left is a recovery, not the main road. */
    .home-foot #btn-reenter { margin-bottom:6px; }

    /* The message box grows with the message. */
    /* The writing box is the point of this app, so it gets the whole width and
       the buttons sit under it rather than stealing a third of the line. */
    .chat-input { resize:none; font-family:inherit; line-height:1.35; max-height:38vh; overflow-y:auto; }
    .chat-input-row { flex-direction:column; align-items:stretch; gap:8px; }
    .chat-send-row { display:flex; gap:8px; justify-content:flex-end; }
    .chat-send-row .chat-send-btn { flex:0 0 auto; min-width:96px; }
    .chat-send-row .chat-send-btn.assist { min-width:56px; padding:0 14px; }

    /* Idle in a room: dimmed, never dark, never off. Still perfectly readable
       from across a table, and one touch away from full brightness. */
    body.screen-dim { filter:brightness(0.35); transition:filter 1.2s ease; }
    body { transition:filter .25s ease; }

    /* What he is about to send her, so he can read it first. */
    .invite-preview { width:100%; max-width:340px; margin:2px auto 0; padding:10px 12px;
      border:1px solid var(--border); border-radius:10px; background:var(--surface);
      color:var(--muted); font-size:12px; line-height:1.5; white-space:pre-wrap; text-align:left; }

    /* The bare code page: six boxes and nothing to distract from them. Big
       enough to hit with a thumb and to read at arm's length, and they stay
       on one line on the narrowest phone. */
    .code-only { gap:22px; }
    .code-only-prompt { color:var(--muted); font-size:15px; text-align:center; margin:0; max-width:300px; }
    .code-boxes { display:flex; gap:8px; justify-content:center; width:100%; max-width:330px; }
    .code-box { flex:1 1 0; min-width:0; aspect-ratio:3/4; max-height:74px; text-align:center;
      font-size:30px; font-weight:800; font-variant-numeric:tabular-nums; color:var(--text);
      background:var(--surface-hi); border:1px solid var(--border); border-radius:12px; }
    .code-box:focus { outline:none; border-color:var(--accent); box-shadow:0 0 0 2px rgba(79,140,255,.25); }
    .code-box:disabled { opacity:.6; }
    .code-only-status { color:var(--muted); font-size:14px; min-height:20px; margin:0; }

    /* The voice room's picture. Square, because that is what the camera is
       asked for, and sized so the conversation underneath still has room. */
    .voice-video { position:relative; width:100%; max-width:360px; margin:0 auto; flex:0 0 auto;
      aspect-ratio:1/1; border-radius:14px; overflow:hidden; background:#0d1220;
      border:1px solid var(--border); }
    .voice-video[hidden] { display:none !important; }
    #voice-remote-video { width:100%; height:100%; object-fit:cover; display:block; background:#0d1220; }
    #voice-local-video { position:absolute; right:8px; bottom:8px; width:28%; aspect-ratio:1/1; object-fit:cover;
      border-radius:10px; border:2px solid rgba(255,255,255,.35); background:#111; z-index:2;
      cursor:grab; touch-action:none; transition:left .18s ease, top .18s ease; }
    #voice-local-video:active { cursor:grabbing; }
    /* Only my own camera is on: my picture takes the whole frame instead of
       sitting in the corner of a black square. */
    .voice-video.mine-only #voice-local-video { position:static; width:100%; border:none; border-radius:0; }
    .voice-video-note { position:absolute; left:0; right:0; bottom:10px; margin:0; text-align:center;
      font-size:12px; color:#cfd6ea; text-shadow:0 1px 2px #000; pointer-events:none; }
    #btn-voice-camera.on { border-color:var(--accent); color:var(--accent); }
