    /* ═══════════════════════════════════════════════════════════════
       RESET & TOKENS
     ═══════════════════════════════════════════════════════════════ */
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    /* Two themes, one set of names. `html[data-theme]` is set before first
       paint by the inline script in index.html (stored choice, else the
       browser's prefers-color-scheme) and switched live by applyTheme() in
       app.js. Night is the default so a page that never runs the script is
       still the familiar dark one.
       Contrast: --text and --text-sec hold 4.5:1 on --bg, --surface and
       --surface2 in both themes; --on-accent on --accent too. --text-dim is
       for de-emphasised labels only (≥ 4:1). */
    :root, html[data-theme="nuit"] {
      --bg:       #141618;
      --surface:  #1D2023;
      --surface2: #282C30;
      --surface3: #31363B;
      --border:   #393E44;
      --accent:   #3CCF8E;
      --accent-glow: rgba(60, 207, 142, 0.3);
      --on-accent: #08170F;
      --text:     #F3F4F5;
      --text-sec: #A2A8AF;
      --text-dim: #858C94;
      --danger:   #F0616C;
      --warn:     #FFB020;
      --voice:    #FF8A4C;
      --tesla-c:  #E31937;
      /* Channel display: dark glass in both themes — it is an object, not a
         background, so its colours do not change with the theme. */
      --display-bg:      #0B0D0E;
      --display-line:    #2C3136;
      --display-public:  #3CE0A0;
      --display-suc:     #FFC23D;
      --display-private: #7CC4FF;
      --display-muted:   #8C959D;
      /* Map: night keeps the inverted OSM tiles, so the ground is dark.
         Close to the inverted OSM land colour rather than pure black, so a
         tile still loading reads as map, not as a hole. */
      --map-bg:      #1C1E22;
      --map-label:   #F3F4F5;
      --map-halo:    #0B0D0E;
      --map-voice:   #FF8A4C;
      --marker-edge: #FFFFFF;
      --marker-me:   #0B7F51;
      /* Member colours are light pastels (server COLORS), so the letter on a
         plaque is dark. */
      --on-marker:   #101214;
      --scrim:       rgba(0, 0, 0, 0.55);
      --shadow:      rgba(0, 0, 0, 0.5);
      color-scheme: dark;
    }
    html[data-theme="jour"] {
      --bg:       #F1F2F3;
      --surface:  #FFFFFF;
      --surface2: #E6E8EA;
      --surface3: #DADDE0;
      --border:   #C9CED3;
      --accent:   #0B7F51;
      --accent-glow: rgba(11, 127, 81, 0.22);
      --on-accent: #FFFFFF;
      --text:     #15171A;
      --text-sec: #565D65;
      --text-dim: #646B73;
      --danger:   #C42638;
      --warn:     #9A5B00;
      --voice:    #C2410C;
      --display-bg:   #1B1E21;
      --display-line: #1B1E21;
      --display-muted: #9AA3AB;
      /* Map: CARTO light tiles, untouched. */
      --map-bg:      #EEF0F1;
      --map-label:   #15171A;
      --map-halo:    #FFFFFF;
      --map-voice:   #C2410C;
      --marker-me:   #0B7F51;
      --scrim:       rgba(20, 23, 26, 0.35);
      --shadow:      rgba(20, 23, 26, 0.18);
      color-scheme: light;
    }
    :root {
      --r:        12px;
      --r-sm:     6px;
      --r-ui:     10px;
      --r-display: 14px;
      --r-marker: 8px;
      --f-sans:   'Instrument Sans', -apple-system, BlinkMacSystemFont, sans-serif;
      --f-mono:   'JetBrains Mono', monospace;
      /* Was referenced by nine rules but never declared, so they fell back
         to the UA font. */
      --font:     var(--f-sans);
    }

    html, body {
      height: 100%; background: var(--bg); color: var(--text);
      font-family: var(--f-sans); font-size: 18px; line-height: 1.4;
      overflow: hidden; -webkit-tap-highlight-color: transparent;
      -webkit-font-smoothing: antialiased; user-select: none;
      -webkit-text-size-adjust: 100%;
    }
    /* Kill iOS double-tap-zoom on every interactive control. `manipulation`
       allows pan + pinch but disables the 300 ms double-tap-zoom gesture.
       The Leaflet container sets its own touch-action (none → handled by
       its own gesture code) so the map is unaffected. The chat scroller
       and panel scrollers also keep their normal scroll behaviour. */
    button, input, textarea, select, label,
    .signal-btn, .map-btn, .tab-btn, .lcd-frame, .lang-chip, .lang-pill,
    .ch-row, .mode-opt, .tesla-swatch, .tesla-model-chip,
    .pinned-add-btn, .pinned-item, .pin-sheet-btn, .pin-action-btn,
    .pin-compose-input, .qr-modal .qr-close, .code-dropdown-item,
    #qr-header-btn, #chat-toggle-btn, #quit-btn, #road-trip-btn,
    #handle-input, #code-input {
      touch-action: manipulation;
    }

    /* Subtle grain texture */
    body::before {
      content: "";
      position: fixed; inset: 0;
      background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3%3Ffilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E");
      opacity: 0.03;
      pointer-events: none;
      z-index: 9999;
    }

    /* ═══════════════════════════════════════════════════════════════
       SCREENS
    ═══════════════════════════════════════════════════════════════ */
    .screen {
      position: fixed; inset: 0; display: flex; flex-direction: column;
      transition: opacity 0.25s ease, transform 0.25s ease;
    }
    .screen.hidden { opacity: 0; pointer-events: none; transform: scale(0.98); }

    /* ═══════════════════════════════════════════════════════════════
       JOIN SCREEN
    ═══════════════════════════════════════════════════════════════ */
    #join-screen {
      align-items: center; justify-content: flex-start;
      padding: 36px 28px 24px; overflow-y: auto;
      background-image:
        linear-gradient(color-mix(in srgb, var(--bg) 72%, transparent), color-mix(in srgb, var(--bg) 88%, transparent)),
        url('/assets/background.png');
      background-size: cover;
      background-position: center center;
      background-repeat: no-repeat;
      background-attachment: fixed;
    }

    /* ── Language picker (top of join screen) ───────────────────────────── */
    /* Lang picker = single chip top-right + popover */
    .lang-picker {
      position: absolute; top: 16px; right: 16px;
      z-index: 50;
    }
    .lang-chip {
      display: inline-flex; align-items: center; gap: 8px;
      padding: 10px 14px; border-radius: var(--r-sm);
      background: var(--surface2); border: 1px solid var(--border);
      color: var(--text); font-family: var(--f-mono);
      font-size: 15px; font-weight: 700; letter-spacing: 0.5px;
      cursor: pointer; user-select: none;
      transition: background 0.15s, border-color 0.15s, transform 0.1s;
      position: relative;
    }
    .lang-chip:hover  { background: var(--surface3, #2a2c30); border-color: var(--text-sec); }
    .lang-chip:active { transform: scale(0.96); }
    .lang-chip .flag    { font-size: 22px; line-height: 1; }
    .lang-chip .chev    { font-size: 13px; opacity: 0.6; transition: transform 0.2s; }
    .lang-picker.open .lang-chip .chev { transform: rotate(180deg); opacity: 1; }
    /* Pulse hint when no language has been explicitly chosen yet */
    .lang-chip.unset::after {
      content: ''; position: absolute; right: 4px; top: 4px;
      width: 6px; height: 6px; border-radius: 50%;
      background: var(--accent); box-shadow: 0 0 6px var(--accent-glow);
      animation: lang-hint-pulse 1.6s ease-in-out infinite;
    }
    @keyframes lang-hint-pulse {
      0%, 100% { transform: scale(1);   opacity: 0.9; }
      50%      { transform: scale(1.4); opacity: 0.4; }
    }
    .lang-dropdown {
      position: absolute; right: 0; top: calc(100% + 6px);
      min-width: 100%;
      background: var(--surface2);
      border: 1px solid var(--border);
      border-radius: var(--r-sm);
      box-shadow: 0 10px 28px rgba(0,0,0,0.55);
      padding: 4px 0;
      display: flex; flex-direction: column;
      opacity: 0; transform: translateY(-4px); pointer-events: none;
      transition: opacity 0.15s, transform 0.15s;
      z-index: 200;
    }
    .lang-picker.open .lang-dropdown { opacity: 1; transform: translateY(0); pointer-events: auto; }
    .lang-pill {
      display: flex; align-items: center; gap: 10px;
      padding: 12px 16px;
      background: transparent; border: none;
      color: var(--text); font-family: var(--f-mono);
      font-size: 15px; font-weight: 700; letter-spacing: 0.5px;
      cursor: pointer; white-space: nowrap;
      transition: background 0.12s;
    }
    .lang-pill:hover  { background: var(--surface3, #2a2c30); }
    .lang-pill.active { color: var(--accent); }
    .lang-pill .flag  { font-size: 20px; line-height: 1; }

    .logo { text-align: center; margin-bottom: 28px; width: 100%; flex-shrink: 0; }
    .logo-img {
      display: block; margin: 0 auto 10px;
      width: 140px; height: 140px;
      object-fit: contain;
      filter: drop-shadow(0 0 24px color-mix(in srgb, var(--accent) 30%, transparent));
    }
    .logo-car {
      font-size: 72px; display: block; line-height: 1; margin-bottom: 10px;
      filter: drop-shadow(0 0 24px var(--accent-glow));
    }
    .logo-title  { font-size: 40px; font-weight: 900; letter-spacing: -1.5px; font-family: var(--f-sans); }
    .logo-tagline { font-size: 14px; font-weight: 700; color: var(--accent); letter-spacing: 2px; margin-top: 6px; text-transform: uppercase; font-family: var(--f-mono); }

    /* Mode toggle */
    .mode-toggle {
      display: flex;
      border: 1px solid var(--border);
      border-radius: var(--r);
      overflow: hidden;
      margin-bottom: 28px;
      width: 100%;
      max-width: 500px;
      flex-shrink: 0;
      background: var(--surface);
      box-shadow: 0 8px 24px var(--shadow);
    }
    .mode-opt {
      flex: 1;
      display: flex; flex-direction: column; align-items: center; gap: 6px;
      padding: 16px 10px;
      background: transparent; border: none;
      border-right: 1px solid var(--border);
      cursor: pointer; font-family: var(--f-sans);
      color: var(--text-sec); font-size: 20px; font-weight: 800;
      transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
    }
    .mode-opt:last-child { border-right: none; }
    .mode-opt:hover      { background: color-mix(in srgb, var(--text) 5%, transparent); color: var(--text); }
    .mode-opt:active     { transform: scale(0.98); }
    /* Both modes select the same way: the choice is where the audio comes
       from, not a brand statement. */
    .mode-opt.active.tesla,
    .mode-opt.active.standard { background: var(--surface2); color: var(--text); box-shadow: inset 0 -3px 0 var(--accent); }
    .mode-opt .mode-icon { font-size: 32px; line-height: 1; transition: transform 0.2s; }
    .mode-opt.active .mode-icon { transform: scale(1.1); }
    .mode-opt .mode-name { font-size: 17px; font-weight: 900; text-transform: uppercase; letter-spacing: 1px; }

    .join-form { width: 100%; max-width: 500px; display: flex; flex-direction: column; gap: 16px; flex-shrink: 0; }
    .field-group { display: flex; flex-direction: column; gap: 8px; }
    .field-label { font-size: 13px; font-weight: 500; color: var(--text-sec); font-family: var(--f-sans); }

    input.txt {
      background: var(--surface); border: 1px solid var(--border);
      border-radius: var(--r-ui); color: var(--text);
      font-size: 32px; font-weight: 700; font-family: var(--f-mono);
      padding: 0 24px; height: 84px; outline: none; width: 100%;
      transition: all 0.2s;
      letter-spacing: 4px; text-transform: uppercase; caret-color: var(--accent);
    }
    input.txt.handle { text-transform: none; letter-spacing: 0; font-size: 26px; font-family: var(--f-sans); }
    input.txt:focus  { border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-glow); background: var(--surface2); }
    input.txt::placeholder { color: var(--text-dim); letter-spacing: 2px; opacity: 0.5; }
    input.txt.handle::placeholder { letter-spacing: 0; }

    .hint  { font-size: 12px; color: var(--text-dim); padding-left: 2px; font-family: var(--f-mono); }
    .error-msg { color: var(--danger); font-size: 15px; font-weight: 700; text-align: center; min-height: 22px; }

    /* PWA install prompt */
    #pwa-install-wrap {
      width: 100%; max-width: 500px;
      margin-top: 16px; margin-bottom: 4px;
      display: none; flex-direction: column; align-items: stretch; gap: 8px;
    }
    #pwa-install-wrap.visible { display: flex; }
    #pwa-install-btn {
      display: flex; align-items: center; justify-content: center; gap: 10px;
      padding: 14px 20px; border-radius: var(--r);
      background: var(--surface2); border: 1px solid var(--accent);
      color: var(--accent); font-size: 15px; font-weight: 800;
      font-family: var(--font); cursor: pointer; letter-spacing: 0.5px;
      transition: background 0.15s, opacity 0.15s;
    }
    #pwa-install-btn:active { opacity: 0.75; }
    #pwa-ios-hint {
      font-size: 12px; color: var(--text-sec); text-align: center;
      line-height: 1.5; padding: 0 4px;
    }
    #pwa-ios-hint strong { color: var(--text); }

    /* ── Pairing challenge modal (join screen) ── */
    #pairing-modal, #chkey-modal {
      position: fixed; inset: 0; z-index: 500;
      background: color-mix(in srgb, var(--bg) 92%, transparent);
      display: flex; flex-direction: column; align-items: center; justify-content: center;
      padding: 28px; gap: 20px;
    }
    #pairing-modal.hidden, #chkey-modal.hidden { display: none; }
    /* Private channel key: pictures, not digits — sized to be told apart at a
       glance from the driver's seat. */
    .chkey-cell .icon-img { width: 58%; height: 58%; }
    .chkey-cell:disabled { cursor: default; }
    .chkey-note { font-size: 14px; color: var(--text-sec); text-align: center; min-height: 1.4em; }
    .chkey-note.warn { color: var(--warn); font-weight: 700; }
    .panel-chkey {
      display: flex; align-items: center; gap: 10px;
      font-size: 13px; color: var(--text-sec); margin: 0 0 12px;
    }
    .panel-chkey.hidden { display: none; }
    .panel-chkey-icon .icon-img { width: 28px; height: 28px; }
    .pairing-title {
      font-size: 20px; font-weight: 900; color: var(--text);
      text-align: center; letter-spacing: 0.5px;
    }
    .pairing-sub {
      font-size: 14px; color: var(--text-sec); text-align: center; line-height: 1.5;
    }
    .pairing-grid {
      display: grid; grid-template-columns: repeat(3, 1fr);
      gap: 12px; width: 100%; max-width: 320px;
    }
    .pairing-cell {
      aspect-ratio: 1; border-radius: var(--r);
      background: var(--surface2); border: 2px solid var(--border);
      color: var(--text); font-size: 28px; font-weight: 900;
      font-family: var(--font); cursor: pointer;
      display: flex; align-items: center; justify-content: center;
      transition: background 0.12s, border-color 0.12s, transform 0.1s;
    }
    .pairing-cell:active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); transform: scale(0.93); }
    .pairing-cancel-btn {
      font-size: 13px; color: var(--text-dim); background: none; border: none;
      cursor: pointer; letter-spacing: 0.5px; padding: 4px 8px;
    }
    .pairing-cancel-btn:active { color: var(--text-sec); }

    /* ── Pairing toast (main screen, first device) ── */
    #pairing-toast {
      position: fixed; top: 76px; left: 50%; transform: translateX(-50%);
      z-index: 400; min-width: 240px; max-width: calc(100vw - 32px);
      background: var(--surface); border: 1px solid var(--accent);
      border-radius: var(--r); padding: 14px 20px;
      display: flex; flex-direction: column; align-items: center; gap: 6px;
      box-shadow: 0 4px 24px rgba(0,0,0,0.6);
      transition: opacity 0.25s; pointer-events: none;
    }
    #pairing-toast.hidden { display: none; }
    .toast-label {
      font-size: 13px; color: var(--text-sec); font-weight: 700;
      text-transform: uppercase; letter-spacing: 1px;
    }
    .toast-code {
      font-size: 44px; font-weight: 900; letter-spacing: 8px;
      color: var(--accent); line-height: 1;
    }
    .toast-hint { font-size: 11px; color: var(--text-dim); text-align: center; }

    /* Join screen — privacy notice */
    .join-notice {
      width: 100%; max-width: 500px;
      background: color-mix(in srgb, var(--warn) 8%, var(--surface));
      border: 1px solid color-mix(in srgb, var(--warn) 45%, transparent);
      border-radius: var(--r);
      padding: 14px 16px;
      margin-top: 8px;
      color: var(--text-sec);
      font-size: 12.5px; font-weight: 600; line-height: 1.5;
    }
    .join-notice .notice-title {
      display: flex; align-items: center; gap: 8px;
      color: var(--warn); font-size: 12px; font-weight: 700;
      text-transform: uppercase; letter-spacing: 1.5px;
      margin-bottom: 8px;
    }
    .join-notice ul { margin: 0; padding-left: 18px; }
    .join-notice li { margin: 3px 0; }
    .join-notice strong { color: var(--text); font-weight: 800; }

    .btn-join {
      background: var(--accent); color: var(--on-accent); border: none; border-radius: var(--r);
      font-size: 22px; font-weight: 900; font-family: var(--font);
      height: 84px; cursor: pointer; letter-spacing: 1px;
      display: flex; align-items: center; justify-content: center; gap: 14px;
      transition: opacity 0.15s, transform 0.1s;
    }
    .btn-join:active  { transform: scale(0.97); opacity: 0.85; }
    .btn-join:disabled { opacity: 0.38; cursor: not-allowed; transform: none; }

    .btn-public {
      background: var(--accent); color: var(--on-accent);
      font-size: 20px; letter-spacing: 1px;
    }
    .btn-private {
      background: var(--surface2); color: var(--text);
      border: 2px solid var(--border); flex-shrink: 0;
      width: 84px; height: 84px; font-size: 26px; border-radius: var(--r);
      padding: 0;
    }
    .btn-private:active { transform: scale(0.95); opacity: 0.8; }

    /* ── Invite banner (shown when ?join=CODE) ───────────────────────────────── */
    .invite-banner {
      width: 100%; max-width: 500px;
      background: color-mix(in srgb, var(--accent) 7%, transparent);
      border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
      border-radius: var(--r);
      padding: 14px 16px 12px;
      display: flex; flex-direction: column; gap: 4px;
    }
    .invite-banner-title {
      color: var(--accent); font-size: 11px; font-weight: 900;
      text-transform: uppercase; letter-spacing: 2px;
      display: flex; align-items: center; gap: 6px;
    }
    .invite-banner-code {
      font-size: 30px; font-weight: 900; letter-spacing: 5px;
      color: var(--text); line-height: 1.15;
    }
    .invite-banner-sub { color: var(--text-sec); font-size: 12px; font-weight: 600; }

    /* ── Bouton Partager le lien et rejoindre ────────────────────────────────── */
    .btn-share-join {
      width: 100%; max-width: 500px;
      background: transparent; color: var(--accent);
      border: 2px solid var(--accent); border-radius: var(--r);
      font-size: 17px; font-weight: 900; font-family: var(--font);
      height: 68px; cursor: pointer; letter-spacing: 0.5px;
      display: flex; align-items: center; justify-content: center; gap: 10px;
      transition: opacity 0.15s, transform 0.1s, background 0.15s;
    }
    .btn-share-join:active   { transform: scale(0.97); opacity: 0.85; }
    .btn-share-join:disabled { opacity: 0.25; cursor: not-allowed; transform: none; }

    /* ── FAB ✉️ (main screen, position fixe) ────────────────────────────────── */
    .fab-share {
      position: fixed; bottom: 80px; right: 20px; left: auto; z-index: 200;
      background: var(--surface2); color: var(--accent);
      border: 2px solid var(--accent); border-radius: 50%;
      width: 44px; height: 44px;
      padding: 0; font-size: 20px;
      cursor: pointer;
      display: none; align-items: center; justify-content: center;
      box-shadow: 0 4px 14px rgba(0,0,0,0.5);
      transition: opacity 0.2s, transform 0.15s;
    }
    .fab-share.visible { display: flex; }
    .fab-share:active  { opacity: 0.75; transform: scale(0.95); }
    body.tesla-mode .fab-share { display: none !important; }

    /* ── Toast "Lien copié !" ────────────────────────────────────────────────── */
    .share-toast {
      position: fixed; bottom: 76px; left: 50%;
      transform: translateX(-50%) translateY(8px);
      background: var(--accent); color: var(--on-accent);
      padding: 10px 22px; border-radius: 50px;
      font-size: 14px; font-weight: 900; font-family: var(--font);
      opacity: 0; transition: opacity 0.25s, transform 0.25s;
      pointer-events: none; z-index: 9999; white-space: nowrap;
    }
    .share-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

@keyframes bounce {
      0%, 20%, 50%, 80%, 100% { transform: translateY(0); }
      40% { transform: translateY(-10px); }
      60% { transform: translateY(-5px); }
    }

    .join-divider {
      display: flex; align-items: center; gap: 12px;
      color: var(--text-dim); font-size: 12px; font-weight: 800;
      text-transform: uppercase; letter-spacing: 2px;
    }
    .join-divider::before,
    .join-divider::after {
      content: ''; flex: 1; height: 1px; background: var(--border);
    }

    .trip-state-panel {
      display: flex; flex-direction: column; align-items: center; justify-content: center; flex: 1; gap: 16px;
    }
    .trip-state-panel.hidden { display: none !important; }

    .join-private-row {
      display: flex; gap: 12px; align-items: stretch;
      position: relative;
    }
    .join-private-row .txt {
      flex: 1; font-size: 26px; letter-spacing: 6px;
    }
    /* Dropdown of previously-joined private channels (excludes CB + SuC).
       Positioned ABOVE the field so the on-screen keyboard (Tesla browser,
       mobile) doesn't hide it. */
    .code-dropdown {
      position: absolute;
      bottom: calc(100% + 6px); left: 0; right: 0;
      background: var(--surface2);
      border: 1px solid var(--accent);
      border-radius: var(--r-sm);
      max-height: 200px;
      overflow-y: auto;
      z-index: 100;
      box-shadow: 0 8px 24px rgba(0,0,0,0.6);
    }
    .code-dropdown.hidden { display: none; }
    .code-dropdown-hint {
      padding: 6px 12px; font-size: 10px; font-family: var(--f-mono);
      color: var(--text-dim); letter-spacing: 1px; text-transform: uppercase;
      border-bottom: 1px solid var(--border);
    }
    .code-dropdown-item {
      padding: 12px 14px; cursor: pointer;
      display: flex; justify-content: space-between; align-items: center;
      font-family: var(--f-mono); font-size: 18px; font-weight: 800;
      letter-spacing: 2px; color: var(--text);
      border-bottom: 1px solid var(--border);
    }
    .code-dropdown-item:last-child { border-bottom: none; }
    .code-dropdown-item:hover { background: var(--surface3); color: var(--accent); }
    .code-dropdown-item:active { transform: scale(0.98); }

    /* ═══════════════════════════════════════════════════════════════
       HEADER
    ═══════════════════════════════════════════════════════════════ */
    .header {
      display: flex; align-items: center; height: 68px; padding: 0 16px;
      background: var(--surface); border-bottom: 1px solid var(--border);
      gap: 12px; flex-shrink: 0;
      /* Above Leaflet's panes (max 700) so children that overflow below the
         header — e.g. the LCD URL badge — paint on top of the map tiles. */
      z-index: 800;
      box-shadow: 0 2px 12px var(--shadow);
      position: relative;
    }
    /* Connection indicator (left side of header) */
    .conn-status {
      display: inline-flex; align-items: center; gap: 8px;
      flex-shrink: 0; line-height: 1;
    }
    .conn-dot {
      width: 10px; height: 10px; border-radius: 50%;
      background: var(--danger); flex-shrink: 0; transition: background 0.4s;
    }
    .conn-dot.on { background: var(--accent); }
    .conn-label {
      font-family: var(--f-sans); font-size: 11px;
      letter-spacing: 0.3px; text-transform: uppercase;
      color: var(--text-sec); font-weight: 700;
      transition: color 0.3s;
    }
    .conn-dot.on + .conn-label { color: var(--text-sec); }
    /* Audit α 0.9: SuC ⚡ override removed — it doubled up with the LCD's
       own ⚡ glyph. Connection dot stays the classic online indicator. */
    /* Mobile: hide the conn status block (dot+label) AND the user-count pill.
       Connection state is signalled by greying-out the LCD when offline (see
       body.offline rule below). Tesla mode keeps both for the dashboard look. */
    body:not(.tesla-mode) .conn-status,
    body:not(.tesla-mode) .hdr-pill,
    body:not(.tesla-mode) .hdr-handle { display: none; }
    /* ── Channel display ───────────────────────────────────────────────────
       The one lit object on the screen: dark glass (same in day and night —
       it is an object, not a background), the channel name glowing in the
       colour of its kind, the logo's radio waves in front (a bolt on a
       Supercharger). It replaced a green LCD with a VT323 face and a dot
       screen. Class names stay .lcd-* so app.js and the tests are untouched.
       The glow is a text-shadow and nothing else — no filter, no blur (see
       the canvas crash in CLAUDE.md: the car dies from memory no counter
       reports). Always absolute-centred in the header, so its position never
       shifts when buttons appear or disappear with the channel. */
    .lcd-wrap {
      position: absolute; left: 50%; top: 50%;
      transform: translate(-50%, -50%);
      display: inline-flex; align-items: center;
      z-index: 1;
      pointer-events: none; /* let the inner button handle clicks itself */
    }
    .lcd-wrap > * { pointer-events: auto; }
    .lcd-frame {
      --c: var(--display-public);
      display: inline-flex; align-items: center; justify-content: center; gap: 8px;
      background: var(--display-bg);
      border: 1px solid var(--display-line);
      border-radius: var(--r-display);
      color: var(--c);
      /* Mobile defaults — narrow enough to leave room for the other controls. */
      height: 40px; padding: 0 10px; gap: 6px;
      max-width: 140px;
      cursor: pointer;
      transition: transform 0.1s;
      position: relative;
      outline: none; font: inherit;
      -webkit-appearance: none; appearance: none;
    }
    body.sc-channel .lcd-frame      { --c: var(--display-suc); }
    body.private-channel .lcd-frame { --c: var(--display-private); }
    /* Offline: the whole display dims to grey — it used to be a grayscale
       filter over the LCD. */
    body.offline .lcd-frame         { --c: var(--display-muted); }
    body.offline .lcd-frame .hdr-room { text-shadow: none; }
    .lcd-frame:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
    .lcd-frame:active { transform: scale(0.98); }
    /* The radio waves of the logo (a bolt on a Supercharger channel). */
    .lcd-frame::before {
      content: ''; flex-shrink: 0;
      width: 16px; height: 16px;
      background: currentColor;
      -webkit-mask: url('/assets/icons/ui/arcs.svg') center / contain no-repeat;
              mask: url('/assets/icons/ui/arcs.svg') center / contain no-repeat;
    }
    body.sc-channel .lcd-frame::before {
      -webkit-mask-image: url('/assets/icons/ui/bolt.svg');
              mask-image: url('/assets/icons/ui/bolt.svg');
    }
    .hdr-room {
      color: inherit;
      font-family: var(--f-mono);
      /* Mobile defaults */
      font-size: 16px; font-weight: 700; letter-spacing: 0.05em;
      text-transform: uppercase;
      line-height: 1;
      text-shadow: 0 0 14px color-mix(in srgb, currentColor 45%, transparent);
      pointer-events: none;
      user-select: none;
      white-space: nowrap;
      overflow: hidden; text-overflow: ellipsis;
      max-width: 100%;
    }
    /* Tesla mode: the display is the hero. */
    body.tesla-mode .lcd-frame {
      height: 64px; padding: 0 24px; gap: 14px;
      max-width: 380px;
    }
    body.tesla-mode .lcd-frame::before { width: 28px; height: 28px; }
    body.tesla-mode .hdr-room { font-size: 34px; }

    /* Site signature, straddling the display's bottom edge as before.
       Same glass as the display, always in the public-channel green whatever
       the channel. Non-interactive (pointer-events:none) so taps in this zone
       reach the display button beneath = open channel panel. */
    .lcd-badge {
      position: absolute;
      left: 50%;
      transform: translateX(-50%);
      background: var(--display-bg);
      border: 1px solid var(--display-line);
      border-radius: 4px;
      box-shadow: 0 2px 4px rgba(0,0,0,.35);
      font-family: var(--f-mono);
      font-weight: 700;
      text-transform: lowercase;
      color: var(--display-public);
      text-decoration: none;
      z-index: 2;
      pointer-events: none;              /* let taps reach the display button */
      white-space: nowrap;
      user-select: none;
      /* Mobile defaults */
      bottom: -9px;
      padding: 2px 7px;
      font-size: 8px;
      letter-spacing: 1.5px;
    }
    body.offline .lcd-badge { color: var(--display-muted); }
    body.tesla-mode .lcd-badge {
      bottom: -10px;
      padding: 3px 9px;
      font-size: 9px;
      letter-spacing: 2px;
    }
    .hdr-sep    { width: 1px; height: 24px; background: var(--border); flex-shrink: 0; }
    .hdr-handle { font-size: 15px; font-weight: 700; color: var(--text-sec); max-width: 130px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--f-sans); }

    /* ── Bouton Signal (filtre distance audio) ── */
    .signal-btn {
      font-family: var(--f-sans);
      font-size: 10px; font-weight: 600; letter-spacing: 0.3px;
      padding: 6px 10px; border-radius: var(--r-ui);
      border: 1px solid transparent;
      background: var(--surface2); color: var(--text);
      cursor: pointer; white-space: nowrap; flex-shrink: 0;
      transition: all 0.15s;
      display: flex; flex-direction: column; align-items: center; gap: 1px;
      line-height: 1;
    }
    .signal-btn .sig-icon { font-size: 12px; }
    .signal-btn .sig-label { font-size: 9px; letter-spacing: 0.3px; }

    /* ── Bouton retour BANDE EV ─────────────────────────────────────────────
       Visible uniquement hors CB (canal privé ou Supercharger). Il partageait
       l'allure de .signal-btn avec le bouton Signal juste à côté, au point
       qu'on ne voyait plus que c'est un RETOUR et pas un filtre. On lui donne
       donc le vert de l'afficheur LCD tel qu'il est sur la bande EV : le
       bouton devient un rappel visuel de la destination du retour.
       La classe est posée par le JS (updateSignalBtn) seulement quand le
       bouton pointe vraiment sur CB — quand il sert de retour vers un canal
       privé précédent (↩️ + nom), il garde l'aspect neutre, puisque la
       destination n'est alors pas la bande EV. */
    /* Back-to-channel button, hanging off the left of the channel display
       (inside .lcd-wrap, positioned so the display itself stays centred).
       A small piece of the display in the colour of its destination. */
    .lcd-back {
      --c: var(--display-public);
      position: absolute; right: calc(100% + 8px); top: 50%; transform: translateY(-50%);
      display: flex; align-items: center; gap: 5px;
      height: 34px; padding: 0 9px;
      border-radius: var(--r-ui);
      background: var(--display-bg); border: 1px solid var(--display-line);
      color: var(--c);
      font: 700 12px/1 var(--f-mono); letter-spacing: 0.05em; text-transform: uppercase;
      white-space: nowrap; cursor: pointer;
    }
    .lcd-back.lcd-private { --c: var(--display-private); }
    .lcd-back .sig-icon { font-size: 15px; display: flex; }
    .lcd-back:active { transform: translateY(-50%) scale(0.96); }
    body.tesla-mode .lcd-back { height: 52px; padding: 0 14px; gap: 8px; font-size: 16px; right: calc(100% + 12px); }
    body.tesla-mode .lcd-back .sig-icon { font-size: 22px; }

    /* ── Twemoji icon/flag images (replace native emoji glyphs) ──────────────
       1em by default: emoji always rendered at the ambient font-size of
       whatever wrapped them, so sizing the image the same way means every
       existing font-size rule (incl. tesla-mode overrides) keeps working
       unchanged — no per-context override list to maintain. */
    .icon-img, .flag-img {
      width: 1em; height: 1em; display: inline-block;
      vertical-align: -0.15em; object-fit: contain; user-select: none;
    }
    /* Line icons (iconImg → uiIcon in i18n.js): a mask over currentColor, so
       they follow the text colour and the theme. */
    .ui-ic {
      background-color: currentColor;
      -webkit-mask: var(--ic) center / contain no-repeat;
              mask: var(--ic) center / contain no-repeat;
    }
    .flag-img { border-radius: 2px; box-shadow: 0 0 0 1px rgba(0,0,0,0.15); }
    /* Active filter: a 3-px accent filet at the foot, not a second lit
       button — the display is the only thing on the header that glows. */
    .signal-btn.active {
      box-shadow: inset 0 -3px 0 var(--accent);
    }
    .signal-btn:active { transform: scale(0.95); }
    .signal-btn.no-pos { opacity: 0.2; cursor: not-allowed; pointer-events: none; }

    /* ── Signal zone: distance filter + language filter grouped together ── */
    .signal-group { display: flex; align-items: stretch; gap: 4px; flex-shrink: 0; }
    .signal-group .signal-btn { flex-shrink: 0; }
    /* Non-tesla (phone/desktop): the header doesn't have Tesla's width to
       spare — two buttons push into the absolutely-centred LCD channel
       display and get hidden behind it. Fold distance into the language
       panel instead (see .filter-dist-row) and keep a single button. */
    body:not(.tesla-mode) #signal-btn { display: none; }

    /* ── Language filter picker (bottom sheet, reuses .pin-sheet chrome) ── */
    .lang-filter-title {
      font-size: 15px; font-weight: 700; font-family: var(--f-sans);
      color: var(--text); text-align: center; padding: 4px 0 12px;
    }
    .lang-filter-grid {
      display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;
      margin-bottom: 8px;
    }
    .lang-filter-tile {
      display: flex; flex-direction: column; align-items: center; justify-content: center;
      gap: 4px; min-height: 72px; padding: 10px 6px;
      background: var(--surface2); color: var(--text);
      border: 1px solid var(--border); border-radius: var(--r-sm);
      cursor: pointer; font-family: var(--f-mono);
    }
    .lang-filter-tile:active { background: var(--surface3); transform: scale(0.97); }
    .lang-filter-tile.active {
      border-color: var(--accent); color: var(--accent);
      background: color-mix(in srgb, var(--accent) 8%, transparent); box-shadow: 0 0 12px var(--accent-glow);
    }
    .lang-filter-tile .lf-icon  { font-size: 32px; line-height: 1; }
    .lang-filter-tile .lf-label { font-size: 12px; font-weight: 700; letter-spacing: 0.5px; }

    /* Distance section folded into the same panel on non-tesla — Tesla
       keeps its own dedicated SIGNAL header button, so hide it there. */
    .filter-dist-title {
      font-size: 12px; font-weight: 700; letter-spacing: 1px;
      color: var(--text-sec); text-align: center; padding: 0 0 8px;
      font-family: var(--f-mono);
    }
    .filter-dist-row {
      display: flex; flex-wrap: wrap; gap: 8px; justify-content: center;
      margin-bottom: 16px; padding-bottom: 16px; border-bottom: 1px solid var(--border);
    }
    .filter-dist-chip {
      min-width: 46px; padding: 10px 6px;
      background: var(--surface2); color: var(--text);
      border: 1px solid var(--border); border-radius: var(--r-sm);
      font-family: var(--f-mono); font-size: 13px; font-weight: 700;
      cursor: pointer;
    }
    .filter-dist-chip:active { background: var(--surface3); transform: scale(0.97); }
    .filter-dist-chip.active {
      border-color: var(--accent); color: var(--accent);
      background: color-mix(in srgb, var(--accent) 8%, transparent); box-shadow: 0 0 12px var(--accent-glow);
    }
    body.tesla-mode .filter-dist-title,
    body.tesla-mode .filter-dist-row { display: none; }
    /* Tesla : panneau filtre langue ancré en haut à gauche, sous le header —
       proche du bouton LANGUE qui l'ouvre, plus facile à atteindre en
       conduisant que le centre/bas de l'écran. */
    body.tesla-mode #lang-filter-backdrop {
      align-items: flex-start;
      justify-content: flex-start;
    }
    body.tesla-mode #lang-filter-backdrop .pin-sheet {
      margin: 84px 0 0 16px;
      max-width: 420px;
      border-radius: 16px;
      box-shadow: 0 10px 30px rgba(0,0,0,0.6);
    }

    .hdr-pill {
      display: flex; align-items: center; gap: 6px;
      background: var(--surface2); border: 1px solid var(--border);
      border-radius: 20px; padding: 6px 12px;
      font-size: 14px; font-weight: 700; font-family: var(--f-sans);
      font-variant-numeric: tabular-nums;
    }
    .spacer { flex: 1; }

    .btn-quit {
      background: var(--surface2); border: 1px solid transparent; border-radius: var(--r-ui);
      color: var(--text); font-family: var(--f-sans);
      height: 36px; padding: 0 8px; cursor: pointer;
      transition: all 0.2s; flex-shrink: 0;
      display: flex; flex-direction: column; align-items: center; justify-content: center;
      gap: 1px; line-height: 1;
    }
    .btn-quit .quit-icon  { font-size: 16px; line-height: 1; }
    .btn-quit .quit-label { font-size: 9px; letter-spacing: 0.3px; font-weight: 600; text-transform: uppercase; }
    .btn-quit:hover { color: var(--danger); }
    .btn-quit:active { transform: scale(0.95); }

    /* ═══════════════════════════════════════════════════════════════
       TAB BAR
    ═══════════════════════════════════════════════════════════════ */
    .tab-bar { display: flex; background: var(--surface); border-bottom: 1px solid var(--border); flex-shrink: 0; padding: 0 8px; }
    .tab-btn {
      flex: 1; height: 56px; background: transparent; border: none;
      border-bottom: 2px solid transparent; color: var(--text-sec);
      font-size: 15px; font-weight: 800; font-family: var(--f-sans);
      cursor: pointer; display: flex; align-items: center; justify-content: center;
      gap: 8px; transition: all 0.2s; letter-spacing: 1px;
    }
    .tab-btn.active { color: var(--accent); border-bottom-color: var(--accent); background: color-mix(in srgb, var(--accent) 3%, transparent); }
    .tab-icon       { font-size: 18px; }
    .tab-badge {
      background: var(--danger); color: #fff;
      font-size: 10px; font-weight: 900; border-radius: 10px;
      padding: 2px 6px; min-width: 18px; text-align: center; line-height: 14px;
      font-family: var(--f-mono);
    }
    .tab-badge.hidden { display: none; }
    /* Notif "messages épinglés non lus" sur l'onglet chat (CB mobile) */
    .tab-btn { position: relative; }
    .tab-btn.has-pinned::after {
      content: url('/assets/icons/pushpin.svg');
      position: absolute; top: 6px; right: 10px;
      width: 12px; height: 12px;
      filter: drop-shadow(0 0 6px var(--accent-glow));
      animation: pin-tab-pulse 1.6s ease-in-out infinite;
    }
    .tab-btn.has-pinned:not(.active) {
      background: color-mix(in srgb, var(--accent) 8%, transparent);
    }
    @keyframes pin-tab-pulse {
      0%, 100% { transform: scale(1);   opacity: 0.85; }
      50%      { transform: scale(1.25); opacity: 1; }
    }

    /* ═══════════════════════════════════════════════════════════════
       VIEWS
    ═══════════════════════════════════════════════════════════════ */
    .view { flex: 1; overflow: hidden; position: relative; display: flex; flex-direction: column; }
    .view.hidden { display: none; }

    /* ═══════════════════════════════════════════════════════════════
       MAP VIEW
    ═══════════════════════════════════════════════════════════════ */
    #map { flex: 1; width: 100%; }
    /* OSM tiles inverted → dark mode (no API key, always available) */
    /* Night keeps the long-standing look: OSM tiles inverted into a dark map.
       Day shows the CARTO light tiles as they are (see applyTheme in app.js). */
    html:not([data-theme="jour"]) .leaflet-tile-pane { filter: invert(100%) hue-rotate(180deg) brightness(95%) contrast(90%); }
    .leaflet-container     { background: var(--map-bg); }
    .leaflet-popup-content-wrapper {
      background: var(--surface); color: var(--text);
      border: 1px solid var(--border); border-radius: var(--r-ui);
      font-family: var(--f-sans); font-size: 14px; font-weight: 600;
      box-shadow: 0 8px 28px var(--shadow);
    }
    .leaflet-popup-tip            { background: var(--surface); }
    .leaflet-bar a, .leaflet-bar a:hover {
      background: var(--surface); color: var(--text); border-color: var(--border);
    }
    .leaflet-container .leaflet-control-attribution {
      background: color-mix(in srgb, var(--surface) 80%, transparent); color: var(--text-sec);
    }
    .leaflet-container .leaflet-control-attribution a { color: var(--text-sec); }
    .leaflet-popup-close-button   { color: var(--text-sec) !important; font-size: 16px !important; }

    /* Members overlay */
    .members-overlay {
      position: absolute; top: 12px; left: 12px; z-index: 1000;
      background: var(--surface);
      border: 1px solid var(--border); border-radius: var(--r-ui);
      padding: 14px 16px; max-width: 230px;
      box-shadow: 0 6px 20px var(--shadow);
    }
    /* Tesla mode = 15" screen → enlarge the members overlay so it's easy
       to glance at while driving. */
    body.tesla-mode .members-overlay { padding: 18px 22px; max-width: 320px; }
    body.tesla-mode .overlay-title   { font-size: 13px; margin-bottom: 14px; }
    body.tesla-mode .member-row      { font-size: 17px; padding: 8px 0; gap: 12px; }
    body.tesla-mode .member-dot      { width: 26px; height: 26px; font-size: 13px; }
    body.tesla-mode .member-me       { font-size: 12px; }
    body.tesla-mode .member-dist,
    body.tesla-mode .member-eta      { font-size: 14px; }
    body.tesla-mode .member-voice,
    body.tesla-mode .member-hp-off   { font-size: 17px; }
    .overlay-title { font-size: 12px; font-weight: 600; color: var(--text-sec); text-transform: uppercase; letter-spacing: 0.6px; margin-bottom: 10px; font-family: var(--f-sans); }
    .member-row    { display: flex; align-items: center; gap: 10px; padding: 6px 0; font-size: 14px; font-weight: 700; transition: opacity 0.2s; font-family: var(--f-sans); }
    .member-row.dimmed { opacity: 0.3; }
    /* My own row opens the colour + model picker (data-open-tesla-picker). */
    .member-row.me, .timeline-node.me { cursor: pointer; border-radius: 8px; }
    .member-row.me:active, .timeline-node.me:active { background: var(--surface2); }
    .member-dot    {
      width: 22px; height: 22px; border-radius: 6px; flex-shrink: 0;
      background: var(--mc); color: var(--on-marker);
      display: flex; align-items: center; justify-content: center;
      font: 700 11px/1 var(--f-mono); letter-spacing: -0.02em;
    }
    .member-dot.me {
      background: var(--marker-edge); color: var(--marker-me);
      border: 2px solid var(--marker-me);
    }
    .member-name   { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
    .member-me     { font-size: 11px; font-weight: 600; color: var(--text-sec); flex-shrink: 0; text-transform: uppercase; font-family: var(--f-sans); }
    .member-hp-off { font-size: 12px; flex-shrink: 0; opacity: 0.6; }
    .members-sep   { height: 1px; background: var(--border); margin: 6px 0; }
    .member-dist   { font-size: 12px; font-weight: 500; color: var(--text-sec); flex-shrink: 0; margin-left: auto; font-family: var(--f-sans); font-variant-numeric: tabular-nums; }
    .member-eta    { font-size: 11px; font-weight: 700; color: var(--text-sec); flex-shrink: 0; font-family: var(--f-mono); padding: 2px 6px; border-radius: 4px; background: var(--surface2); }
    .member-eta.ahead   { color: var(--accent); }
    .member-eta.behind  { color: var(--warn); }
    .member-voice  { font-size: 14px; flex-shrink: 0; opacity: 0.15; transition: all 0.2s; }
    /* filter:drop-shadow (not text-shadow/color) so the glow still applies
       now that this is an <img> rather than a colorable text glyph. */
    .member-voice.active { opacity: 1; color: var(--voice); animation: micPulse 0.8s ease-in-out infinite alternate; }
    @keyframes micPulse { from { transform: scale(1); } to { transform: scale(1.25); } }

    /* Connection icon (spec: specs/connection-icons-spec.md) */
    .member-conn {
      position: relative; flex-shrink: 0; font-size: 15px; line-height: 1;
      display: inline-flex; align-items: center; user-select: none;
    }
    .member-conn.clickable { cursor: pointer; transition: transform 0.1s; }
    .member-conn.clickable:hover { transform: scale(1.15); }
    .member-conn-audio { font-size: 10px; margin-left: -1px; vertical-align: super; }
    .member-conn-tint {
      position: absolute; right: -3px; bottom: -2px;
      width: 8px; height: 8px; border-radius: 50%;
      border: 1.5px solid var(--surface2, #222);
      box-shadow: 0 1px 2px rgba(0,0,0,0.4);
      pointer-events: none;
    }
    body.tesla-mode .member-conn       { font-size: 18px; }
    body.tesla-mode .member-conn-tint  { width: 10px; height: 10px; }
    body.tesla-mode .member-conn-audio { font-size: 12px; }

    /* Tesla color picker */
    .tesla-picker {
      position: fixed; z-index: 3000;
      background: var(--surface2, #1e1e1e);
      border: 1px solid var(--border, #333);
      border-radius: var(--r, 10px);
      padding: 14px; min-width: 220px;
      box-shadow: 0 8px 28px rgba(0,0,0,0.65);
      font-family: var(--font);
    }
    .tesla-picker-title {
      font-size: 11px; font-weight: 800; color: var(--text-sec, #999);
      text-transform: uppercase; letter-spacing: 1.5px; margin-bottom: 10px;
    }
    .tesla-picker-grid {
      display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px;
    }
    .tesla-swatch {
      width: 34px; height: 34px; border-radius: 50%;
      border: 2px solid var(--border, #333);
      cursor: pointer; padding: 0; position: relative;
      transition: transform 0.1s, box-shadow 0.1s;
    }
    .tesla-swatch:hover { transform: scale(1.12); }
    .tesla-swatch.selected {
      border-color: var(--accent, #00E5A0);
      box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 35%, transparent);
    }
    .tesla-swatch.selected::after {
      content: '✓'; position: absolute; inset: 0;
      display: flex; align-items: center; justify-content: center;
      color: #fff; font-size: 15px; font-weight: 900;
      text-shadow: 0 1px 3px rgba(0,0,0,0.8);
    }
    .tesla-picker-clear {
      display: block; margin-top: 12px; width: 100%;
      background: transparent; border: 1px solid var(--border, #333);
      color: var(--text-sec, #999);
      font-family: var(--font); font-size: 11px; font-weight: 700;
      padding: 7px; border-radius: 6px; cursor: pointer;
      text-transform: uppercase; letter-spacing: 1px;
    }
    .tesla-picker-clear:hover { color: var(--text, #fff); border-color: var(--text-sec, #999); }
    /* Tesla model picker (row of 5 chips: S · 3 · X · Y · CT) */
    .tesla-model-section { margin-top: 14px; }
    .tesla-model-grid {
      display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px;
    }
    .tesla-model-chip {
      height: 34px; min-width: 34px; border-radius: 17px;
      background: var(--surface3, #2a2a2a);
      border: 2px solid var(--border, #333);
      color: var(--text, #fff);
      font-family: var(--f-mono); font-weight: 900; font-size: 14px;
      cursor: pointer; padding: 0 6px;
      transition: transform 0.1s, border-color 0.1s, background 0.15s;
    }
    .tesla-model-chip:hover { transform: scale(1.06); }
    .tesla-model-chip.selected {
      background: var(--accent, #00E5A0); color: var(--on-accent);
      border-color: var(--accent, #00E5A0);
      box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 35%, transparent);
    }

    /* Road Trip Timeline Mode */
    .members-overlay.road-trip-mode {
      height: calc(100vh - 200px);
      max-height: 600px;
      width: 390px;
      max-width: calc(100vw - 24px);
      display: flex;
      flex-direction: column;
      overflow: hidden;
      padding: 14px 16px 0 16px;
    }
    .overlay-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 12px;
    }
    .overlay-header .overlay-title { margin-bottom: 0; }

    #rt-panel {
      position: absolute;
      bottom: 20px;
      left: 12px;
      width: 390px;
      max-width: calc(100vw - 24px);
      z-index: 1000;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--r);
      padding: 14px 16px;
      box-shadow: 0 8px 32px rgba(0,0,0,0.5);
      display: none;
      flex-direction: column;
      gap: 8px;
    }
    #rt-panel:not(.hidden) {
      display: flex;
    }
    #rt-panel.in-tab {
      position: static;
      width: 100%;
      max-width: none;
      box-shadow: none;
      border: 1px solid var(--border);
      background: var(--surface2);
      backdrop-filter: none;
      padding: 16px;
      margin: 0;
    }
    /* Saisie d'adresse : le panneau vit en bas de la carte, exactement là où
       le clavier virtuel (Tesla comme mobile) vient le recouvrir — on tape
       sans voir le champ. Pendant la saisie on le remonte en haut au centre,
       hors d'atteinte du clavier. Le header étant en dehors de .view,
       top:12px se place juste dessous sans le chevaucher.
       Exclu en mode onglet (.in-tab) : le panneau y est dans le flux normal,
       en haut de l'onglet Trajet, donc déjà visible. */
    #rt-panel.search-focus:not(.in-tab) {
      top: 12px;
      bottom: auto;
      left: 50%;
      right: auto;
      transform: translateX(-50%);
    }

    #rt-search-view { display: flex; flex-direction: column; gap: 8px; }
    #rt-search-view.hidden, #rt-active-view.hidden { display: none; }
    .rt-search-row { display: flex; gap: 8px; }
    #rt-search-input {
      flex: 1; background: var(--surface2); border: 1px solid var(--border);
      color: var(--text); padding: 6px 10px; border-radius: 4px; font-size: 12px;
    }
    #rt-search-btn {
      background: var(--surface2); border: 1px solid var(--border);
      color: var(--text); padding: 6px 10px; border-radius: 4px; cursor: pointer;
    }
    
    /* Résultats de recherche : ancrés EN HAUT, pas centrés. Le clavier
       virtuel reste affiché pendant qu'on choisit une adresse et mange la
       moitié basse de l'écran — une modale centrée démarrait donc au milieu
       et se retrouvait tronquée. La hauteur max est recalculée en JS à
       l'ouverture (cf. fitSearchModalToViewport) parce que le clavier Tesla
       est un overlay système : il ne rétrécit pas forcément le viewport, donc
       ni vh ni dvh ne le voient. Le max-height ci-dessous n'est que le
       repli avant que le JS ne prenne la main. */
    #rt-search-modal {
      /* inset:0, not 100vw/100vh: a zoomed element's containing block is
         divided by the zoom, so inset tracks the viewport, while viewport
         units do not — the vw/vh form left the backdrop covering only 80 %
         of the screen once the modal started being scaled. Every other
         full-screen overlay in this file already uses inset. */
      position: fixed; inset: 0;
      background: rgba(0,0,0,0.8); z-index: 3000;
      display: flex; justify-content: center; align-items: flex-start;
      padding-top: 10px;
    }
    #rt-search-modal.hidden { display: none; }
    .rt-search-modal-content {
      background: var(--surface); border: 1px solid var(--border);
      border-radius: var(--r); width: 90%; max-width: 500px;
      max-height: 88vh; display: flex; flex-direction: column;
      box-shadow: 0 10px 40px rgba(0,0,0,0.8);
    }
    .rt-search-modal-header {
      display: flex; justify-content: space-between; align-items: center;
      padding: 16px; border-bottom: 1px solid var(--border);
    }
    .rt-search-modal-header h3 { margin: 0; font-size: 18px; color: var(--text); }
    #rt-search-modal-close {
      background: transparent; border: none; color: var(--text-sec);
      font-size: 24px; cursor: pointer;
    }
    #rt-search-modal-results {
      padding: 16px; overflow-y: auto; display: flex; flex-direction: column; gap: 12px;
    }
    .rt-modal-result-item {
      padding: 16px; background: var(--surface2); border-radius: 8px;
      cursor: pointer; font-size: 16px; border: 1px solid transparent;
      /* Flex children in a height-capped column shrink before the column
         scrolls, so a full list squashed every row and clipped its text
         instead of letting the list scroll. */
      flex-shrink: 0;
    }
    .rt-modal-result-item:hover { background: var(--surface3); border-color: var(--accent); }

    #rt-active-view { display: flex; flex-direction: column; gap: 8px; }
    .rt-dest-label { font-size: 13px; font-weight: 700; color: var(--accent); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .rt-eta-line {
      font-size: 12px; font-weight: 700; color: var(--text);
      font-family: var(--f-mono); letter-spacing: 0.3px;
      display: flex; align-items: center; gap: 6px;
    }
    .rt-eta-line.hidden { display: none; }
    .rt-eta-sep { color: var(--text-dim); }
    .rt-eta-fallback {
      display: none; color: var(--text-dim); font-size: 10px;
      border: 1px solid var(--border); padding: 0 4px; border-radius: 4px;
    }
    .rt-eta-line.approx .rt-eta-fallback { display: inline; }
    #rt-cancel-btn {
      background: var(--surface2); border: 1px solid var(--border);
      color: var(--text-sec); font-size: 10px; padding: 4px 8px;
      border-radius: 4px; cursor: pointer; text-transform: uppercase; font-weight: 800;
      align-self: flex-start;
    }

    /* Tesla mode: trip panel content is too small to read/tap while driving. */
    body.tesla-mode #rt-panel        { padding: 18px 22px; gap: 12px; }
    body.tesla-mode .rt-dest-label   { font-size: 22px; }
    body.tesla-mode .rt-eta-line     { font-size: 20px; gap: 10px; }
    body.tesla-mode .rt-eta-fallback { font-size: 14px; padding: 1px 6px; }
    body.tesla-mode #rt-cancel-btn   {
      font-size: 14px; padding: 10px 18px; border-radius: 6px;
    }
    body.tesla-mode #rt-search-input {
      font-size: 18px; padding: 12px 14px; border-radius: 6px;
    }
    body.tesla-mode #rt-search-btn   {
      font-size: 18px; padding: 12px 16px; border-radius: 6px;
    }
    body.tesla-mode .rt-modal-result-item { font-size: 20px; padding: 20px; }
    body.tesla-mode .rt-search-modal-header h3 { font-size: 22px; }

    .members-overlay.road-trip-mode #members-list {
      flex: 1;
      position: relative;
      overflow: hidden;
      margin-left: -16px;
      margin-right: -16px;
      padding-left: 16px;
      padding-right: 16px;
    }
    
    .timeline-track {
      position: absolute;
      left: 20px;
      top: 10px;
      bottom: 10px;
      width: 2px;
      background: var(--border);
      z-index: 1;
    }
    .timeline-node {
      position: absolute;
      left: 16px;
      display: flex;
      align-items: center;
      transition: top 0.5s ease;
      z-index: 2;
      width: calc(100% - 32px);
    }
    .timeline-dot {
      width: 10px;
      height: 10px;
      border-radius: 50%;
      border: 2px solid var(--surface);
      flex-shrink: 0;
      z-index: 2;
    }
    .timeline-content {
      margin-left: 12px;
      background: var(--surface2);
      color: var(--text);
      padding: 4px 8px;
      border-radius: 6px;
      display: flex;
      align-items: center;
      gap: 8px;
      flex: 1;
      border: 1px solid var(--border);
      overflow: hidden;
    }
    .timeline-node.disconnected .timeline-content {
      font-style: italic;
      opacity: 0.6;
    }

    .finish-flag { font-size: 32px; line-height: 1; filter: drop-shadow(0 2px 4px rgba(0,0,0,0.5)); }

    /* Active-state panel must stretch its children full width, otherwise the
       timeline track and rtPanel shrink to content width (align-items:center
       is inherited from .trip-state-panel and suits the empty state only). */
    #trip-active-state { align-items: stretch; justify-content: flex-start; }

    /* ── CB tips panel (bottom-left of the map, dismissable) ──────────── */
    #tips-panel {
      position: absolute; bottom: 20px; left: 20px;
      width: 320px; max-width: calc(100vw - 40px);
      background: var(--surface);
      border: 1px solid var(--accent);
      border-radius: var(--r-ui);
      padding: 14px 16px 14px;
      box-shadow: 0 6px 20px var(--shadow);
      /* Above the PTT (mic/HP) controls on mobile — sits in foreground until
         the user dismisses it. Single-shot UX (never re-shown after dismiss). */
      z-index: 1100;
    }
    #tips-panel.hidden { display: none; }
    /* Where the map is too narrow for the two side by side, the tips sit
       *above* 📻/🎙️ rather than over them. Covering them was acceptable while
       phones opened their mic on their own; now that talking starts with a
       tap on 🎙️, a tips card on top of it hides the one button that matters.
       (82 px of PTT at bottom: 24px, plus a gap.) */
    @media (max-width: 860px) {
      body:not(.tesla-mode) #tips-panel { bottom: 118px; }
    }
    /* Visible only when on CB and not dismissed. */
    body:not(.cb-channel) #tips-panel,
    body.tips-dismissed   #tips-panel { display: none; }
    /* …except while the old-Tesla audio notice is up: it is not onboarding
       chatter, it applies on every channel, and it outranks a dismissal the
       user made months ago. Kept last so it wins on equal specificity. */
    body.mic-notice #tips-panel { display: block; }

    /* Amber treatment — applied to the whole panel only while the notice is
       the slide on screen (see render() in app.js), so the ordinary tips keep
       their green. Amber, not red: nothing is broken, the car needs an
       update. */
    #tips-panel.warn { border-color: var(--warn); box-shadow: 0 10px 32px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,176,32,0.25); }
    #tips-panel.warn .tips-header { color: var(--warn); }
    #tips-panel.warn .tips-dot.active { background: var(--warn); }
    #tips-panel.warn .tips-arrow:hover { color: var(--warn); border-color: var(--warn); }
    .tip-slide.warn .tip-text { color: var(--text); }
    .tip-slide.warn .tip-text b,
    .tip-slide.warn .tip-text strong { color: var(--warn); font-weight: 900; }
    .tips-header {
      font-size: 14px; font-weight: 600; color: var(--text);
      font-family: var(--f-sans); letter-spacing: 0;
      margin-bottom: 10px; display: flex; align-items: center; gap: 6px;
      padding-right: 26px; /* room for close X */
    }
    .tips-close {
      position: absolute; top: 6px; right: 8px;
      background: transparent; border: none;
      color: var(--text-sec); font-size: 22px; line-height: 1;
      padding: 4px 8px; cursor: pointer;
      transition: color 0.15s;
    }
    .tips-close:hover { color: var(--text); }
    .tips-viewport { overflow: hidden; min-height: 78px; }
    .tips-track {
      display: flex; transition: transform 0.25s ease;
      will-change: transform;
    }
    .tip-slide {
      flex: 0 0 100%; min-width: 0;
      display: flex; gap: 10px; align-items: flex-start;
      font-size: 13px; color: var(--text-sec); line-height: 1.45;
      padding-right: 4px;
    }
    .tip-icon { font-size: 26px; flex-shrink: 0; line-height: 1; margin-top: 1px; }
    .tip-text strong { color: var(--text); font-weight: 600; }
    .tip-text em     { color: var(--accent); font-style: normal; font-weight: 700; }
    .tips-nav {
      display: flex; align-items: center; justify-content: space-between;
      margin-top: 10px; padding-top: 8px;
      border-top: 1px solid var(--border);
    }
    .tips-arrow {
      background: var(--surface2); border: 1px solid transparent;
      color: var(--text); width: 32px; height: 32px;
      border-radius: 8px; cursor: pointer;
      font-size: 18px; line-height: 1;
      display: flex; align-items: center; justify-content: center;
      transition: all 0.15s;
    }
    .tips-arrow:hover   { color: var(--accent); }
    .tips-arrow:active  { transform: scale(0.92); }
    .tips-arrow[disabled] { opacity: 0.3; cursor: default; }
    .tips-dots { display: flex; gap: 7px; }
    .tips-dot {
      width: 7px; height: 7px; border-radius: 50%;
      background: var(--surface3); cursor: pointer;
      transition: background 0.15s, transform 0.15s;
    }
    .tips-dot.active { background: var(--accent); transform: scale(1.3); }
    body.tesla-mode #tips-panel { width: 420px; padding: 20px 22px; }
    body.tesla-mode .tips-header { font-size: 15px; }
    body.tesla-mode .tip-slide   { font-size: 15px; }
    body.tesla-mode .tip-icon    { font-size: 30px; }
    body.tesla-mode .tips-close  { font-size: 26px; }
    body.tesla-mode .tips-viewport { min-height: 96px; }
    body.tesla-mode .tips-arrow  { width: 34px; height: 34px; font-size: 22px; }
    body.tesla-mode .tips-dot    { width: 9px; height: 9px; }

    #road-trip-btn { bottom: 20px; left: 20px; display: none; }
    #road-trip-btn.visible { display: flex; }
    #road-trip-btn.active { border-color: var(--accent); color: var(--accent); box-shadow: 0 0 15px var(--accent-glow); }
    /* Private-channel callout: fill the trip button green so the driver
       notices the road-trip feature (same idea as the chat-toggle-btn that
       turns green when the chat panel is hidden). Toned down once the trip
       panel is actually open / active (.active state above wins). */
    body.private-channel #road-trip-btn.visible:not(.active) {
      background: var(--accent); color: var(--on-accent);
      border-color: var(--accent);
      box-shadow: 0 0 14px var(--accent-glow);
    }

    /* GPS / Map controls */
    .gps-warning {
      position: absolute; top: 12px; left: 50%; transform: translateX(-50%);
      z-index: 1000; background: rgba(255,68,85,0.95); color: #fff;
      font-size: 12px; font-weight: 800; padding: 8px 20px; border-radius: 20px;
      display: none; white-space: nowrap; box-shadow: 0 4px 16px rgba(0,0,0,0.5);
      border: 1px solid rgba(255,255,255,0.2); font-family: var(--f-sans);
      cursor: pointer;
    }
    .gps-warning.tappable::after { content: ' ›'; opacity: 0.85; }

    /* ── GPS help modal (reuses the .pin-sheet chrome) ── */
    .gps-help-title { font-size: 16px; font-weight: 800; font-family: var(--f-sans); padding: 4px 0 10px; text-align: center; }
    .gps-help-steps {
      font-size: 14px; line-height: 1.55; color: var(--text-sec);
      background: var(--surface2); border-radius: var(--r-sm);
      padding: 12px 14px; margin-bottom: 12px; white-space: pre-line;
    }
    .map-btn {
      position: absolute; z-index: 1000;
      background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
      color: var(--text); font-size: 24px; width: 56px; height: 56px;
      display: flex; align-items: center; justify-content: center;
      cursor: pointer; box-shadow: 0 8px 24px rgba(0,0,0,0.5);
      transition: all 0.15s;
    }
    .map-btn:active { transform: scale(0.92); background: var(--surface2); }
    #locate-btn { bottom: 20px; right: 20px; }

    /* Bouton audio split HP 📻 | Mic 🎙️ — phone, et Tesla si micro navigateur dispo */
    #ptt-wrap {
      position: absolute; bottom: 24px; left: 50%; transform: translateX(-50%);
      z-index: 1000; display: none;
      flex-direction: row; gap: 4px;
      background: var(--surface); border: 1px solid var(--border); border-radius: 44px; padding: 5px;
      box-shadow: 0 8px 24px var(--shadow);
    }
    /* iOS playback unlock — see VoiceManager.primeAudio. */
    #audio-unlock-hint {
      position: absolute; top: 16px; left: 50%; transform: translateX(-50%);
      z-index: 1200; white-space: nowrap; display: flex; align-items: center; gap: 8px;
      padding: 12px 20px; border-radius: 24px; border: none; cursor: pointer;
      background: var(--accent); color: var(--on-accent); font: 800 15px var(--font);
      box-shadow: 0 8px 24px rgba(0,0,0,0.6);
    }
    #audio-unlock-hint.hidden { display: none; }
    #audio-unlock-hint .icon-img { width: 22px; height: 22px; }

    /* Car first: the device not carrying this driver's audio shows where it
       went instead of two buttons that would do nothing. */
    .audio-elsewhere {
      display: none; align-items: center; gap: 8px;
      height: 48px; padding: 0 18px; border-radius: 24px;
      background: var(--surface2); border: 1px solid transparent;
      color: var(--text-sec); font-size: 14px; font-weight: 600;
    }
    .audio-elsewhere .icon-img { width: 22px; height: 22px; }
    body.audio-on-car #hp-btn, body.audio-on-car #mic-btn,
    body.audio-on-phone #hp-btn, body.audio-on-phone #mic-btn { display: none !important; }
    body.audio-on-car .audio-elsewhere-car,
    body.audio-on-phone .audio-elsewhere-phone { display: flex; }
    /* Speaker / mic: two switches. On = accent fill. Off = neutral with a
       danger outline and a crossed icon — the state no longer rests on green
       vs red alone. The class (on/off) is all app.js sets; icons and labels
       follow from it. Labels only show on the car's wide buttons. */
    .ptt-half {
      width: 72px; height: 72px; border: none; cursor: pointer;
      display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 10px;
      font-size: 28px; transition: background 0.15s, color 0.15s, transform 0.1s; border-radius: 36px;
      position: relative; overflow: hidden;
      font-family: var(--f-sans);
    }
    .ptt-half:active { transform: scale(0.94); }
    .ptt-half.on  { background: var(--accent); color: var(--on-accent); }
    .ptt-half.off { background: var(--surface2); color: var(--danger); box-shadow: inset 0 0 0 2px var(--danger); }
    .ptt-half .ptt-ic-off, .ptt-half .ptt-lbl-off { display: none; }
    .ptt-half.off .ptt-ic-on, .ptt-half.off .ptt-lbl-on { display: none; }
    .ptt-half.off .ptt-ic-off { display: inline-block; }
    .ptt-lbl { display: none; font-size: 17px; font-weight: 600; white-space: nowrap; }
    .ptt-half.off .ptt-lbl { color: var(--text); }
    /* Tesla : boutons HP/Mic ~3x plus larges (même hauteur) pour rester
       faciles à toucher en conduisant — oblongs plutôt que ronds. */
    body.tesla-mode .ptt-half { width: 216px; border-radius: 36px; }
    body.tesla-mode .ptt-half.on  .ptt-lbl-on,
    body.tesla-mode .ptt-half.off .ptt-lbl-off { display: inline; }
    /* Élargis, les deux boutons font ~450 px : centrés, ils débordent sous le
       panneau trajet ancré en bas à gauche (12 px + 390 px de large). Quand ce
       panneau est ouvert — champ d'adresse comme destination active — on les
       décale juste à sa droite.
       La classe vient du JS (updateRoadTripUI) et non d'un sélecteur de frère :
       mapView.appendChild(rtPanel) replace le panneau APRÈS #ptt-wrap dans le
       DOM, donc #rt-panel ~ #ptt-wrap ne matcherait jamais. body.tesla-mode
       reste dans le sélecteur car hors Tesla le panneau vit dans l'onglet
       Trajet (.in-tab) et ne flotte pas au-dessus de la carte. */
    body.tesla-mode.rt-panel-open #ptt-wrap {
      /* Centré DANS l'espace restant, pas collé contre le panneau : en plein
         écran la zone libre s'élargit et les boutons doivent suivre.
         left + right bornent la zone, width:fit-content garde les boutons à
         leur taille, et margin:auto les centre dedans — le navigateur fait
         le calcul, donc rien ne dépend de la largeur des boutons (qui
         changerait si on les redimensionne un jour) ni de celle de l'écran.
           414 = 12 (gauche du panneau) + 390 (sa largeur) + 12 d'écart
            88 = 20 (droite du bouton 📍) + 56 (sa largeur) + 12 d'écart */
      left: 414px;
      right: 88px;
      transform: none;
      width: fit-content;
      margin: 0 auto;
    }

    /* Member markers — plaque + heading nub.
       The divIcon box stays 90×64 anchored at its centre (45,32), which is
       what --pin-scale's transform-origin and the Leaflet anchor assume; the
       plaque sits on that centre and the name hangs off to its right. */
    .car-pin {
      position: relative; width: 90px; height: 64px;
      --plq: 30px;
    }
    /* Filtre (distance ou langue, dans un sens ou l'autre) actif : le membre
       reste visible sur la carte mais estompé — jamais masqué, contrairement
       au chat, la position reste utile pour l'orientation générale. */
    .car-pin.filter-faded { opacity: 0.4; filter: saturate(0.25); transition: opacity 0.3s ease, filter 0.3s ease; }
    .car-pin .plq {
      position: absolute; left: 50%; top: 50%;
      width: var(--plq); height: var(--plq);
      transform: translate(-50%, -50%);
      border-radius: var(--r-marker);
      background: var(--mc);
      border: 2px solid var(--marker-edge);
      box-shadow: 0 2px 6px rgba(0,0,0,0.35);
      display: flex; align-items: center; justify-content: center;
      color: var(--on-marker);
      font-size: 16px;
      z-index: 2;
    }
    /* Mine: inverted — white plaque, green frame and letter, second white
       frame. Recognisable by shape and contrast, not by colour alone. */
    .car-pin.me .plq {
      background: var(--marker-edge);
      color: var(--marker-me);
      border: 2.5px solid var(--marker-me);
      box-shadow: 0 0 0 2px var(--marker-edge), 0 3px 8px rgba(0,0,0,0.4);
      cursor: pointer;
    }
    .plq-letter { font: 700 13px/1 var(--f-mono); letter-spacing: -0.02em; }
    /* Declared paint colour, top-right corner of the plaque (updateMarker). */
    .plq-paint {
      position: absolute; top: -7px; right: -7px;
      width: 14px; height: 14px; border-radius: 50%;
      border: 2px solid #FFFFFF;
      box-shadow: 0 1px 3px rgba(0,0,0,0.45);
      pointer-events: none;
    }
    .plq-name {
      position: absolute; top: 50%; left: calc(50% + var(--plq) / 2 + 7px);
      transform: translateY(-50%);
      font: 600 14px/1 var(--f-sans); white-space: nowrap;
      color: var(--map-label);
      text-shadow: 0 0 3px var(--map-halo), 0 0 3px var(--map-halo), 0 0 2px var(--map-halo);
      pointer-events: none;
    }
    /* Heading nub: rotated around the plaque's centre, shown only while
       moving (updateMarker decides). White rim under the member colour. */
    .plq-cap {
      position: absolute; left: calc(50% - 9px); top: calc(50% - 9px);
      width: 18px; height: 18px; z-index: 1; pointer-events: none;
    }
    .plq-cap::before, .plq-cap::after {
      content: ''; position: absolute; left: 50%;
      clip-path: polygon(50% 0, 100% 100%, 0 100%);
    }
    .plq-cap::before {
      width: 22px; height: 16px; margin-left: -11px;
      top: calc(9px - var(--plq) / 2 - 18px);
      background: var(--marker-edge);
    }
    .plq-cap::after {
      width: 15px; height: 11px; margin-left: -7.5px;
      top: calc(9px - var(--plq) / 2 - 15px);
      background: var(--mc);
    }
    .car-pin.me .plq-cap::after { background: var(--marker-me); }
    /* Speaking: the logo's radio waves, left of the plaque. */
    .plq-talk {
      position: absolute; top: 50%; width: 22px; height: 26px;
      left: calc(50% - var(--plq) / 2 - 26px);
      transform: translateY(-50%) scaleX(-1);
      background: var(--map-voice);
      -webkit-mask: url('/assets/icons/ui/arcs.svg') center / contain no-repeat;
              mask: url('/assets/icons/ui/arcs.svg') center / contain no-repeat;
      animation: plqTalk 1s ease-in-out infinite alternate;
      pointer-events: none;
    }
    @keyframes plqTalk { from { opacity: 1; } to { opacity: 0.35; } }

    /* Supercharger markers — a piece of the channel display dropped on the
       map: dark glass tile, amber bolt, code in the display face. Green bolt
       and a soft pulse when people are connected on that channel. Never a
       member colour, so a Supercharger cannot be mistaken for a car. */
    .sc-pin {
      display: flex; flex-direction: column; align-items: center; gap: 3px;
      cursor: pointer;
    }
    .sc-pin .sc-icon {
      width: 30px; height: 30px; border-radius: var(--r-marker);
      background: var(--display-bg); border: 2px solid var(--marker-edge);
      color: var(--display-suc);
      display: flex; align-items: center; justify-content: center;
      font-size: 17px; line-height: 1;
      box-shadow: 0 2px 6px rgba(0,0,0,0.35);
    }
    .sc-pin .sc-tag {
      font-size: 9px; font-weight: 700; font-family: var(--f-mono);
      padding: 2px 7px; border-radius: 4px; white-space: nowrap;
      background: var(--display-bg); color: var(--display-suc);
      border: 1px solid var(--display-line);
      box-shadow: 0 2px 4px rgba(0,0,0,0.35); letter-spacing: 0.08em;
      text-transform: uppercase;
    }
    /* Popup Supercharger */
    .sc-popup { min-width: 180px; }
    .sc-popup-name { font-size: 15px; font-weight: 700; color: var(--text); margin-bottom: 4px; }
    .sc-popup-code { font-size: 12px; color: var(--text-sec); margin-bottom: 6px; }
    .sc-popup-code strong { color: var(--text); font-family: var(--f-mono); letter-spacing: 0.08em; }
    .sc-popup-users {
      font-size: 12px; font-weight: 600; color: var(--text);
      margin-bottom: 10px; min-height: 0;
      display: flex; align-items: center; gap: 5px;
    }
    .sc-popup-users span { color: var(--accent); }
    .sc-popup-users:empty { margin-bottom: 10px; }
    .sc-join-btn {
      display: block; width: 100%;
      background: var(--accent); color: var(--on-accent); border: none; border-radius: var(--r-ui);
      font-size: 14px; font-weight: 700; font-family: var(--font);
      padding: 10px 0; cursor: pointer; letter-spacing: 0.2px;
      transition: opacity 0.15s;
    }
    .sc-join-btn:active { opacity: 0.75; }
    /* Supercharger actif (du monde connecté sur ce canal) */
    @keyframes sc-pulse {
      0%, 100% { box-shadow: 0 0 0 2px color-mix(in srgb, var(--display-public) 45%, transparent); }
      50%       { box-shadow: 0 0 0 6px color-mix(in srgb, var(--display-public) 12%, transparent); }
    }
    .sc-pin.sc-active .sc-icon {
      color: var(--display-public);
      animation: sc-pulse 2.2s ease-in-out infinite;
    }
    .sc-pin.sc-active .sc-tag { color: var(--display-public); }
    .sc-badge {
      position: absolute; top: -5px; right: -5px;
      min-width: 17px; height: 17px; border-radius: 9px;
      background: var(--marker-edge); color: #0B7F51;
      font-size: 10px; font-weight: 700; font-family: var(--f-sans);
      display: flex; align-items: center; justify-content: center;
      border: 1.5px solid var(--display-public); padding: 0 3px; line-height: 1;
      box-shadow: 0 1px 4px rgba(0,0,0,0.4);
    }

    /* ═══════════════════════════════════════════════════════════════
       CHAT VIEW
    ═══════════════════════════════════════════════════════════════ */
    #chat-view { background: var(--bg); }
    .messages {
      flex: 1; overflow-y: auto; padding: 20px 20px 12px;
      display: flex; flex-direction: column; gap: 12px; scroll-behavior: smooth;
    }
    /* Tesla : messages récents en bas */
    body.tesla-mode .messages { flex-direction: column; }
    .messages::-webkit-scrollbar       { width: 4px; }
    .messages::-webkit-scrollbar-track { background: transparent; }
    .messages::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }

    .msg        { display: flex; flex-direction: column; gap: 4px; max-width: 80%; }
    .msg.mine   { align-self: flex-end;   align-items: flex-end; }
    .msg.theirs { align-self: flex-start; align-items: flex-start; }
    .msg-meta   { font-size: 11px; font-weight: 800; opacity: 0.7; font-family: var(--f-mono); text-transform: uppercase; letter-spacing: 0.5px; }
    .msg-bubble { padding: 12px 18px; border-radius: 16px; font-size: 17px; line-height: 1.5; word-break: break-word; font-family: var(--f-sans); }
    .msg.mine   .msg-bubble { background: var(--accent); color: var(--on-accent); border-bottom-right-radius: 2px; box-shadow: 0 4px 12px var(--accent-glow); font-weight: 600; }
    .msg.theirs .msg-bubble { background: var(--surface2); color: var(--text); border-bottom-left-radius: 2px; border: 1px solid var(--border); }
    /* Filtre langue actif : masque les messages d'un membre dont la langue
       déclarée ne correspond pas au filtre choisi (reste dans le DOM, donc
       réapparaît si le filtre change ensuite). */
    .msg.lang-hidden { display: none; }
    .sys-msg { text-align: center; font-size: 12px; color: var(--text-dim); padding: 4px 0; font-family: var(--f-mono); text-transform: uppercase; letter-spacing: 1px; }

    .chat-bar {
      display: flex; align-items: center; gap: 12px; padding: 12px 16px;
      background: var(--surface); border-top: 1px solid var(--border); flex-shrink: 0;
      box-shadow: 0 -4px 20px rgba(0,0,0,0.2);
    }
    #chat-input {
      flex: 1; background: var(--surface2); border: 1px solid var(--border);
      border-radius: 24px; color: var(--text); font-size: 16px; font-family: var(--f-sans);
      padding: 0 20px; height: 48px; outline: none;
      text-transform: none; letter-spacing: 0; caret-color: var(--accent);
      transition: all 0.2s;
    }
    #chat-input:focus     { border-color: var(--accent); background: var(--surface3); box-shadow: 0 0 0 3px var(--accent-glow); }
    #chat-input::placeholder { color: var(--text-dim); opacity: 0.6; }
    .btn-send {
      background: var(--accent); border: none; border-radius: 50%;
      width: 48px; height: 48px; font-size: 20px; cursor: pointer; flex-shrink: 0;
      display: flex; align-items: center; justify-content: center;
      transition: all 0.15s; box-shadow: 0 4px 12px var(--accent-glow);
    }
    .btn-send:active { transform: scale(0.9); opacity: 0.8; }

    /* BMC / ToS footer inside chat view */
    #bmc-wrap {
      display: flex; align-items: center; justify-content: center; gap: 16px;
      padding: 5px 16px 6px;
      background: var(--surface); border-top: 1px solid var(--border);
      flex-shrink: 0;
    }
    #bmc-btn {
      display: inline-flex; align-items: center; gap: 6px;
      padding: 6px 14px; border-radius: 20px;
      background: var(--surface2); border: 1px solid var(--border);
      color: var(--text); font-size: 13px; font-weight: 700;
      text-decoration: none; letter-spacing: 0.3px;
      transition: opacity 0.2s;
    }
    #bmc-btn:active { opacity: 0.7; }
    #tos-link {
      font-size: 11px; color: var(--text-dim); text-decoration: none;
      letter-spacing: 0.3px; transition: color 0.15s;
    }
    #tos-link:active { color: var(--text-sec); }

    /* ── Pinned messages panel (public rooms only) ───────────────────── */
    #pinned-panel {
      flex-shrink: 0;
      background: linear-gradient(180deg, var(--surface) 0%, var(--surface2) 100%);
      border-bottom: 1px solid var(--border);
      max-height: 40vh; overflow-y: auto;
    }
    #pinned-panel.hidden { display: none; }
    .pinned-header {
      display: flex; align-items: center; justify-content: flex-end;
      padding: 4px 10px;
      background: transparent;
      position: sticky; top: 0; z-index: 2;
    }
    .pinned-add-btn {
      background: var(--accent); color: var(--on-accent); border: none;
      padding: 4px 10px; border-radius: 12px;
      font-size: 11px; font-weight: 900; font-family: var(--f-mono);
      letter-spacing: 0.5px; cursor: pointer;
      box-shadow: 0 2px 6px var(--accent-glow);
      transition: transform 0.1s;
    }
    .pinned-add-btn:active { transform: scale(0.94); }
    .pinned-add-btn[disabled] { opacity: 0.4; cursor: not-allowed; box-shadow: none; }
    .pinned-list {
      display: flex; flex-direction: column; gap: 6px; padding: 8px 12px 10px;
    }
    .pinned-empty {
      padding: 14px 16px; text-align: center;
      font-size: 13px; color: var(--text-dim); font-style: italic;
    }
    .pinned-item {
      display: flex; gap: 10px; align-items: flex-start;
      padding: 10px 12px;
      background: var(--surface2); border: 1px solid var(--border);
      border-left: 3px solid var(--accent);
      border-radius: var(--r-sm);
      position: relative;
    }
    .pinned-item.mine { border-left-color: var(--accent); }
    .pinned-item-body { flex: 1; min-width: 0; }
    .pinned-item-meta {
      font-size: 10px; font-weight: 800; font-family: var(--f-mono);
      text-transform: uppercase; letter-spacing: 0.5px;
      color: var(--text-sec); margin-bottom: 4px;
    }
    .pinned-item-meta .pinned-author { color: var(--text); }
    .pinned-item-text {
      font-size: 14px; line-height: 1.4; color: var(--text); word-break: break-word;
    }
    .pinned-item-actions {
      display: flex; gap: 4px; flex-shrink: 0;
    }
    .pin-action-btn {
      background: none; border: none; padding: 4px 6px;
      color: var(--text-dim); cursor: pointer; font-size: 14px;
      border-radius: 4px; line-height: 1;
    }
    .pin-action-btn:hover  { color: var(--text); background: var(--surface3); }
    .pin-action-btn:active { transform: scale(0.92); }
    .pin-action-btn.danger:hover { color: var(--danger); }
    body.tesla-mode .pinned-item-text { font-size: 16px; }
    body.tesla-mode #pinned-panel { max-height: 30vh; }

    /* ── Long-press / pin action sheet (overlay) ─────────────────────── */
    .pin-sheet-backdrop {
      position: fixed; inset: 0; background: rgba(0,0,0,0.6);
      z-index: 9000; display: flex; align-items: flex-end; justify-content: center;
      animation: fadeIn 0.15s ease-out;
    }
    .pin-sheet-backdrop.hidden { display: none; }
    .pin-sheet {
      width: 100%; max-width: 480px;
      background: var(--surface); border-top: 1px solid var(--border);
      border-radius: 16px 16px 0 0; padding: 12px;
      box-shadow: 0 -10px 30px rgba(0,0,0,0.6);
    }
    .pin-sheet-preview {
      padding: 10px 12px; margin-bottom: 10px;
      background: var(--surface2); border-radius: var(--r-sm);
      font-size: 13px; color: var(--text-sec); max-height: 80px; overflow: hidden;
    }
    .pin-sheet-btn {
      display: flex; align-items: center; gap: 10px; width: 100%;
      padding: 14px 16px; margin: 0; background: var(--surface2); color: var(--text);
      border: 1px solid var(--border); border-radius: var(--r-sm);
      font-size: 16px; font-weight: 700; font-family: var(--f-sans);
      cursor: pointer; margin-bottom: 6px;
    }
    .pin-sheet-btn:active { background: var(--surface3); }
    .pin-sheet-btn.danger { color: var(--danger); }
    .pin-sheet-btn.primary { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
    .pin-sheet-cancel { background: transparent; border: none; color: var(--text-sec); margin-top: 6px; }
    @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

    /* ── Pin compose modal (the "+ Épingler" entry point) ────────────── */
    .pin-compose-input {
      width: 100%; min-height: 80px; resize: vertical;
      padding: 10px 12px; margin-bottom: 8px;
      background: var(--surface2); color: var(--text);
      border: 1px solid var(--border); border-radius: var(--r-sm);
      font-size: 15px; font-family: var(--f-sans);
      outline: none; text-transform: none;
    }
    .pin-compose-input:focus { border-color: var(--accent); }
    .pin-compose-counter {
      text-align: right; font-size: 11px; color: var(--text-dim);
      font-family: var(--f-mono); margin-bottom: 8px;
    }
    .pin-compose-counter.over { color: var(--danger); }

    /* ── SC marker badge: pinned message count (bottom-right of icon) ── */
    .sc-pin-badge {
      position: absolute; bottom: -4px; right: -6px;
      min-width: 17px; height: 17px; border-radius: 9px;
      background: var(--accent); color: var(--on-accent);
      font-size: 10px; font-weight: 900; font-family: -apple-system, system-ui, sans-serif;
      display: flex; align-items: center; justify-content: center;
      padding: 0 4px; line-height: 1;
      box-shadow: 0 1px 4px rgba(0,0,0,0.5);
      border: 1.5px solid var(--marker-edge);
    }
    .sc-pin-badge::before {
      content: url('/assets/icons/pushpin.svg');
      display: inline-block; width: 8px; height: 8px; margin-right: 2px;
      vertical-align: -1px;
    }

    /* Footer mentions légales sur l'écran d'accueil */
    .join-footer {
      margin-top: 18px; padding: 12px 0 4px; text-align: center;
      border-top: 1px solid var(--border);
    }
    .join-footer a {
      font-size: 12px; color: var(--text-dim); text-decoration: none;
      letter-spacing: 0.3px; transition: color 0.15s;
    }
    .join-footer a:hover,
    .join-footer a:active { color: var(--text-sec); text-decoration: underline; }

    /* ═══════════════════════════════════════════════════════════════
       SWITCH CHANNEL PANEL
    ═══════════════════════════════════════════════════════════════ */
    .panel-backdrop {
      position: fixed; inset: 0; z-index: 200;
      background: var(--scrim);
    }
    .panel-backdrop.hidden { display: none; }

    /* Dropdown ancré sous le nom du canal (haut-gauche), pas en bas d'écran */
    .panel {
      position: fixed;
      top: 72px; /* juste sous le header (68px) */
      left: 50%; transform: translateX(-50%);
      width: min(420px, calc(100vw - 24px));
      z-index: 201;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--r-ui);
      padding: 16px 20px 20px;
      max-height: 52vh; overflow-y: auto;
      box-shadow: 0 12px 40px var(--shadow);
    }
    .panel.hidden { display: none; }
    /* Tesla : largeur plus généreuse sur grand écran */
    body.tesla-mode .panel { width: min(480px, 40vw); }
    /* Tesla: twice the height, so the recent channels fit without scrolling
       the list with a finger while parked. 104vh is 2 × the 52vh above; the
       second term keeps the panel on screen. Both are in the panel's own
       (zoomed) units: vh ignores the car-ui zoom while `top` does not, hence
       the division by --ui-scale — see "Viewport units do not follow zoom". */
    body.tesla-mode .panel {
      max-height: min(104vh, calc(100vh / var(--ui-scale, 1) - 88px));
    }

    /* Identity row at the top of the channel switch panel — replaces the
       handle in the header bar on mobile. */
    .panel-handle-row {
      display: flex; align-items: center; gap: 8px;
      padding: 4px 4px 12px; margin: 0 0 8px;
      border-bottom: 1px solid var(--border);
      font-family: var(--f-sans); font-size: 12px;
      color: var(--text-sec); text-transform: uppercase; letter-spacing: 0.4px;
    }
    .panel-handle-row .panel-handle-icon { font-size: 16px; }
    .panel-handle-row .panel-handle-name {
      color: var(--text); font-weight: 700; letter-spacing: 0.5px;
      text-transform: none; font-family: var(--f-sans); font-size: 15px;
    }
    /* "💡 Astuces" link on the right of the handle row — only shown when
       the user has dismissed the tips at least once. Click → re-enables;
       tips will appear the next time the user is on CB (or immediately
       if they're on CB right now and reopen the map). */
    .panel-tips-restore {
      margin-left: auto;
      background: var(--surface2);
      border: 1px solid transparent;
      color: var(--text);
      font-family: var(--f-sans); font-size: 13px; font-weight: 500;
      letter-spacing: 0;
      padding: 6px 12px; border-radius: 16px;
      cursor: pointer; white-space: nowrap;
      transition: background 0.15s, border-color 0.15s;
    }
    .panel-tips-restore:hover {
      border-color: var(--accent);
      background: color-mix(in srgb, var(--accent) 8%, transparent);
    }
    .panel-tips-restore:active { transform: scale(0.96); }
    /* Show only when on CB *and* the user has dismissed the tips. Pointless
       on private (no tips there, the trip panel takes the bottom-left) or
       SuC (tips currently CB-only). */
    body:not(.tips-dismissed) .panel-tips-restore,
    body:not(.cb-channel)     .panel-tips-restore { display: none; }
    .panel-header {
      display: flex; align-items: center; justify-content: space-between;
      margin-bottom: 14px;
    }
    .panel-title { font-size: 20px; font-weight: 600; color: var(--text); }
    .btn-panel-close {
      background: var(--surface2); border: none; border-radius: var(--r-ui);
      width: 40px; height: 40px; font-size: 18px; cursor: pointer;
      color: var(--text); display: flex; align-items: center; justify-content: center;
    }

    .panel-input-row { display: flex; gap: 12px; align-items: stretch; }
    .panel-input-row input.txt { font-size: 22px; height: 72px; flex: 1; }
    .btn-join-panel {
      background: var(--accent); color: var(--on-accent); border: none; border-radius: var(--r-ui);
      font-size: 18px; font-weight: 600; font-family: var(--font);
      padding: 0 24px; height: 72px; cursor: pointer;
      display: flex; align-items: center; gap: 8px; white-space: nowrap;
      transition: opacity 0.15s;
    }
    .btn-join-panel:active { opacity: 0.75; }

    .recents-label {
      font-size: 13px; font-weight: 500; color: var(--text-sec);
      margin: 20px 0 8px;
    }
    .recents-head { display: flex; align-items: center; justify-content: space-between; margin: 20px 0 8px; }
    .recents-head .recents-label { margin: 0; }
    .recents-clear {
      height: 32px; padding: 0 12px; border: 1px solid transparent; border-radius: 16px;
      background: var(--surface2); color: var(--text-sec);
      font: 500 13px var(--f-sans); cursor: pointer;
    }
    .recents-clear.armed { color: var(--danger); box-shadow: inset 0 0 0 1.5px var(--danger); }
    body.tesla-mode .recents-clear { height: 40px; padding: 0 16px; font-size: 14px; }

    .ch-row {
      display: flex; align-items: center; gap: 14px;
      padding: 14px 0; border-bottom: 1px solid var(--border);
      cursor: pointer; transition: opacity 0.15s;
    }
    .ch-row:last-child { border-bottom: none; }
    .ch-row:active { opacity: 0.6; }
    /* A recent channel = a small piece of the channel display, in its kind's
       colour (channelKind() in app.js). */
    .ch-code {
      --c: var(--display-private);
      display: inline-flex; align-items: center; gap: 8px;
      height: 42px; min-width: 150px; padding: 0 12px;
      border-radius: 10px; background: var(--display-bg); color: var(--c);
      font: 700 18px/1 var(--f-mono); letter-spacing: 0.06em; white-space: nowrap;
    }
    .ch-code .icon-img { font-size: 18px; }
    .ch-code.ch-public { --c: var(--display-public); }
    .ch-code.ch-suc    { --c: var(--display-suc); }
    .ch-active   { font-size: 14px; font-weight: 500; color: var(--text); display: inline-flex; align-items: center; gap: 4px; }
    .ch-live     { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); display: inline-block; margin-right: 2px; }
    .ch-inactive { font-size: 14px; font-weight: 500; color: var(--text-sec); }
    .ch-tick     { margin-left: auto; font-size: 13px; font-weight: 600; color: var(--text-sec); }

    /* Day / night switch — a three-way segmented control. */
    .theme-row { display: flex; gap: 6px; }
    .theme-opt {
      flex: 1; height: 48px; display: flex; align-items: center; justify-content: center; gap: 8px;
      border: 1px solid transparent; border-radius: var(--r-ui);
      background: var(--surface2); color: var(--text);
      font: 500 15px var(--f-sans); cursor: pointer;
    }
    .theme-opt .icon-img { font-size: 18px; }
    .theme-opt.selected { box-shadow: inset 0 0 0 2px var(--accent); font-weight: 600; }
    body.tesla-mode .theme-opt { height: 60px; font-size: 16px; }
    .ch-spacer   { flex: 1; }

    .no-recents { font-size: 15px; color: var(--text-dim); padding: 12px 0; text-align: center; }

    /* ══════════════════════════════════════════════════════════════
       TESLA SPLIT LAYOUT
    ══════════════════════════════════════════════════════════════ */
    body.tesla-mode #main-screen {
      display: grid;
      grid-template-rows: auto 1fr;
      grid-template-columns: 70fr 30fr;
    }
    body.tesla-mode .header {
      grid-column: 1 / -1;
      grid-row: 1;
      height: 72px;
    }
    body.tesla-mode .tab-bar { display: none; }

    body.tesla-mode #map-view {
      grid-column: 1;
      grid-row: 2;
      display: flex !important;
      border-right: 1px solid var(--border);
    }
    body.tesla-mode #chat-view {
      grid-column: 2;
      grid-row: 2;
      display: flex !important;
      background: var(--bg);
    }

    body.tesla-mode .messages {
      padding: 24px;
    }
    body.tesla-mode #chat-input {
      font-size: 20px;
      height: 64px;
      border-radius: 32px;
    }
    body.tesla-mode .btn-send {
      width: 64px;
      height: 64px;
      font-size: 24px;
    }
    body.tesla-mode .msg-bubble { font-size: 20px; padding: 14px 20px; }
    body.tesla-mode .msg-meta   { font-size: 12px; }

    /* Bannière audio Tesla — texte sur une ligne, CTA en dessous */
    .tesla-audio-banner {
      display: none; /* masquée par défaut et sur téléphone */
      flex-direction: column; align-items: stretch; gap: 10px;
      padding: 12px 16px;
      background: rgba(227,25,55,0.10);
      border-bottom: 1px solid rgba(227,25,55,0.25);
      font-size: 13px; font-weight: 600; color: var(--danger); line-height: 1.4;
      flex-shrink: 0;
      position: relative;
    }
    .tesla-audio-banner .ab-row { display: flex; align-items: flex-start; gap: 10px; padding-right: 24px; }
    .tesla-audio-banner .tab-icon { font-size: 18px; flex-shrink: 0; margin-top: 1px; }
    .tesla-audio-banner .ab-text { flex: 1; }
    .tesla-audio-banner .ab-cta {
      align-self: center;
      background: var(--accent); color: var(--on-accent); border: none;
      padding: 10px 18px; border-radius: 14px;
      font-size: 14px; font-weight: 900; font-family: var(--f-mono);
      letter-spacing: 0.5px; cursor: pointer;
      box-shadow: 0 2px 6px var(--accent-glow);
      transition: transform 0.1s;
      white-space: nowrap;
    }
    .tesla-audio-banner .ab-cta:active { transform: scale(0.94); }
    .tesla-audio-banner .ab-dismiss {
      position: absolute; top: 6px; right: 8px;
      background: transparent; border: none;
      color: var(--danger); font-size: 18px; line-height: 1;
      padding: 4px 6px; cursor: pointer;
      opacity: 0.6; transition: opacity 0.15s;
    }
    .tesla-audio-banner .ab-dismiss:hover { opacity: 1; }
    body.tesla-mode .tesla-audio-banner { display: flex; }
    /* Masquée dès qu'un téléphone companion est connecté ou que l'utilisateur a fermé */
    body.tesla-mode.has-companion .tesla-audio-banner,
    body.tesla-mode.has-tesla-mic .tesla-audio-banner,
    body.tesla-mode .tesla-audio-banner.dismissed { display: none; }

    /* QR modal (📱 Connecter mon mobile) */
    .qr-modal-bd {
      position: fixed; inset: 0; background: rgba(0,0,0,0.75);
      z-index: 9500; display: flex; align-items: center; justify-content: center;
      padding: 20px;
    }
    .qr-modal-bd.hidden { display: none; }
    .qr-modal {
      width: 100%; max-width: 340px;
      background: var(--surface); border: 1px solid var(--border);
      border-radius: 16px; padding: 22px;
      box-shadow: 0 16px 48px rgba(0,0,0,0.7);
      text-align: center;
    }
    .qr-modal h3 {
      font-size: 18px; font-weight: 900; color: var(--text);
      margin: 0 0 8px; font-family: var(--f-sans);
    }
    .qr-modal p {
      font-size: 13px; color: var(--text-sec); margin: 0 0 16px;
      line-height: 1.5;
    }
    .qr-modal img {
      display: block; margin: 0 auto 16px;
      width: 240px; height: 240px;
      background: #fff; padding: 8px; border-radius: 8px;
    }
    .qr-modal .qr-close {
      width: 100%; background: var(--surface2); border: 1px solid var(--border);
      color: var(--text); padding: 12px; border-radius: var(--r-sm);
      font-size: 14px; font-weight: 800; cursor: pointer;
    }
    .qr-modal .qr-close:active { transform: scale(0.98); }

    /* Message bubbles slightly larger on Tesla */
    body.tesla-mode .msg-bubble   { font-size: 22px; padding: 16px 22px; }
    body.tesla-mode .msg-meta     { font-size: 13px; }
    body.tesla-mode .sys-msg      { font-size: 14px; }

    /* ── Tesla mode specific ── */
    body.tesla-mode #main-screen {
      display: grid;
      grid-template-rows: auto 1fr;
      grid-template-columns: 1fr 380px;
      transition: grid-template-columns 0.5s cubic-bezier(0.4, 0, 0.2, 1);
    }
    body.tesla-mode.chat-hidden #main-screen {
      grid-template-columns: 1fr 0px;
    }
    body.tesla-mode #chat-view {
      grid-column: 2;
      grid-row: 2;
      display: flex !important;
      background: var(--bg);
      border-left: 1px solid var(--border);
      transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s;
      overflow: hidden;
    }
    body.tesla-mode.chat-hidden #chat-view {
      transform: translateX(100%);
      opacity: 0;
      pointer-events: none;
      border-left: none;
    }

    /* Header buttons — chat toggle + QR pairing (both Tesla-only) */
    #chat-toggle-btn, #qr-header-btn {
      background: var(--surface2); border: 1px solid transparent;
      color: var(--text);
      width: 48px; height: 48px; border-radius: var(--r-ui);
      display: flex; align-items: center; justify-content: center;
      font-size: 22px; cursor: pointer; flex-shrink: 0;
      transition: all 0.15s;
    }
    #chat-toggle-btn:active, #qr-header-btn:active { transform: scale(0.95); }
    body:not(.tesla-mode) #chat-toggle-btn,
    body:not(.tesla-mode) #qr-header-btn { display: none; }
    /* Chat-toggle gets a brief green-fill "attention" pulse for 10 s when
       the chat is hidden (initial hide) OR when a new message arrives while
       the chat stays hidden. Otherwise stays outlined-accent → less
       distracting while driving. */
    body.tesla-mode.chat-hidden #chat-toggle-btn.attention {
      background: var(--accent); color: var(--on-accent);
      box-shadow: 0 0 12px var(--accent-glow);
    }
    /* QR pairing button: hide as soon as a companion phone joins. */
    body.tesla-mode.has-companion #qr-header-btn { display: none; }

    /* ── Tesla mode: enlarge the header controls (~+25%) for in-car reach.
       LCD already has its own size override; this is for SuC/CB/Signal,
       QR, chat, quit. */
    body.tesla-mode .signal-btn {
      height: 60px; min-width: 60px; padding: 0 14px; font-size: 13px;
      justify-content: center; gap: 4px;
    }
    body.tesla-mode .signal-btn .sig-icon  { font-size: 22px; }
    body.tesla-mode .signal-btn .sig-label { font-size: 12px; letter-spacing: 0.3px; }
    /* Language filter tiles get an extra size bump on the car screen — bigger
       than the header buttons since this panel is meant to be tapped while
       actually driving, not just glanced at. */
    body.tesla-mode .lang-filter-tile { min-height: 92px; }
    body.tesla-mode .lang-filter-tile .lf-icon  { font-size: 42px; }
    body.tesla-mode .lang-filter-tile .lf-label { font-size: 15px; }
    body.tesla-mode #chat-toggle-btn,
    body.tesla-mode #qr-header-btn {
      width: 60px; height: 60px; font-size: 28px;
    }
    body.tesla-mode .btn-quit {
      height: 60px; min-width: 60px; padding: 0 12px; gap: 4px;
    }
    body.tesla-mode .btn-quit .quit-icon  { font-size: 22px; }
    body.tesla-mode .btn-quit .quit-label { font-size: 12px; }

    /* ── Rescue: Tesla mode on a header too narrow for its contents.
       This used to pin quit / chat / QR with `position: fixed` at
       right: 8 / 64 / 120 below a 900 px media query. Those offsets assumed
       48-px buttons; Tesla mode makes them 60, so the three overlapped each
       other by 4 px, the corner logo stayed in flow and landed underneath
       them, and the handle and member count vanished behind the pile — which
       is exactly what a car reporting under 900 CSS px showed.

       It was also solving a problem the UI scale had already solved: with the
       chrome zoomed, the header fits in normal flow at every car width
       measured. So nothing is pinned any more. `body.hdr-tight` is set by
       fitHeader() in app.js from a real overflow test (raw viewport width
       stopped predicting fit once the chrome started being scaled), and all
       it does is stand the decorative items down so the controls keep their
       room. Order of sacrifice: connection label, handle, member count,
       logo — quit, chat and QR are never touched. */
    body.tesla-mode.hdr-tight .conn-status,
    body.tesla-mode.hdr-tight .hdr-handle,
    body.tesla-mode.hdr-tight .hdr-pill,
    body.tesla-mode.hdr-tight .corner-logo { display: none; }

    /* Map message bubbles (Tesla mode) */
    .map-msg-bubble {
      border: 2px solid rgba(0,0,0,0.5) !important;
      border-radius: 16px !important;
      padding: 8px 14px !important;
      color: #000 !important;
      font-size: 1.5rem !important;
      font-weight: 800 !important;
      text-align: center;
      box-shadow: 0 8px 24px rgba(0,0,0,0.4) !important;
      width: max-content !important;
      max-width: 280px !important;
      white-space: normal !important;
      word-wrap: break-word !important;
      pointer-events: auto !important;
      cursor: pointer;
      /* Animation opacity-only : NE PAS animer transform, sinon écrase
         le translate3d(...) que Leaflet pose en inline pour le positionnement. */
      animation: bubbleFade 0.25s ease-out;
      transition: opacity 0.3s ease;
    }
    @keyframes bubbleFade {
      from { opacity: 0; }
      to   { opacity: 1; }
    }
    .map-msg-bubble.fade-out { opacity: 0; }
    /* Lignes empilées dans une même bulle */
    .bubble-line {
      padding: 4px 2px;
      transition: opacity 0.3s ease, max-height 0.3s ease;
      max-height: 200px;
      overflow: hidden;
    }
    .bubble-line + .bubble-line {
      border-top: 1px solid rgba(0,0,0,0.18);
      margin-top: 2px;
    }
    .bubble-line.fading { opacity: 0; max-height: 0; padding-top: 0; padding-bottom: 0; }

    .member-muted-icon {
      color: var(--danger);
      margin-left: 4px;
      display: inline-flex;
      align-items: center;
    }
    .member-row.tesla-muted .member-name {
      color: var(--text-dim);
    }

    /* ── About modal (opened by the corner-logo) ──────────────────────────── */
    #about-modal {
      position: fixed; inset: 0; z-index: 4000;
      background: color-mix(in srgb, var(--bg) 85%, transparent);
      display: flex; align-items: center; justify-content: center; padding: 24px;
    }
    #about-modal.hidden { display: none; }
    .about-card {
      background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
      padding: 32px 28px; max-width: 340px; width: 100%;
      display: flex; flex-direction: column; align-items: center; gap: 10px;
      box-shadow: 0 24px 60px rgba(0,0,0,0.7);
    }
    .about-logo { width: 64px; height: 64px; filter: drop-shadow(0 0 24px var(--accent-glow)); }
    #about-title { margin: 6px 0 0; font-size: 24px; font-weight: 900; letter-spacing: -0.5px; }
    .about-tagline {
      font-family: var(--f-mono); font-size: 11px; font-weight: 700;
      color: var(--accent); letter-spacing: 2px; text-transform: uppercase;
    }
    .about-version {
      font-family: var(--f-mono); font-size: 12px; color: var(--text-dim);
      letter-spacing: 1px; margin-top: 8px;
    }
    .about-links {
      display: flex; flex-direction: column; gap: 8px; margin-top: 14px; width: 100%;
    }
    .about-links a {
      text-align: center; padding: 10px; border-radius: var(--r-sm);
      background: var(--surface2); color: var(--text-sec);
      font-size: 13px; font-weight: 700; text-decoration: none;
      border: 1px solid var(--border); transition: all 0.15s;
    }
    .about-links a:hover { color: var(--accent); border-color: var(--accent-glow); }
    .about-close {
      margin-top: 14px; padding: 8px 18px; border-radius: var(--r-sm);
      background: transparent; border: 1px solid var(--border);
      color: var(--text-sec); font-family: var(--f-mono);
      font-size: 11px; letter-spacing: 1.5px; text-transform: uppercase;
      cursor: pointer;
    }
    .about-close:hover { color: var(--text); border-color: var(--text-sec); }

    /* ── Corner logo (right side of header) — opens the About popup ── */
    .corner-logo {
      display: inline-flex;
      align-items: center;
      padding: 4px;
      border-radius: 6px;
      background: transparent;
      border: none;
      cursor: pointer;
      opacity: 0.65;
      transition: opacity 0.15s, background 0.15s;
      flex-shrink: 0;
    }
    .corner-logo:hover  { opacity: 1; background: color-mix(in srgb, var(--text) 5%, transparent); }
    .corner-logo:active { transform: scale(0.96); }
    .corner-logo img    { width: 28px; height: 28px; display: block; }

    /* ── Small screens (iPhone SE, older Android) ── */
    @media (max-height: 680px) {
      #join-screen     { padding: 20px 20px 16px; }
      .logo            { margin-bottom: 16px; }
      .logo-img        { width: 96px; height: 96px; margin-bottom: 6px; }
      .logo-car        { font-size: 52px; margin-bottom: 6px; }
      .logo-title      { font-size: 32px; }
      .logo-tagline    { font-size: 14px; margin-top: 4px; }
      .mode-toggle     { margin-bottom: 16px; }
      .mode-opt        { padding: 10px 8px; }
      .mode-opt .mode-icon { font-size: 22px; }
      .mode-opt .mode-name { font-size: 14px; }
      .join-form       { gap: 10px; }
      input.txt        { height: 68px; font-size: 24px; }
      .btn-join        { height: 68px; font-size: 17px; }
      .btn-private     { width: 68px; height: 68px; }
      .join-private-row .txt { font-size: 22px; }
    }

    /* ══════════════════════════════════════════════════════════════
       CAR-SCREEN UI SCALE
       ──────────────────────────────────────────────────────────────
       Tesla's Summer 2026 browser lays the 1920-px-wide centre panel
       out in a much smaller CSS viewport than the old one did, so
       every size in this file — all of it tuned against the old 1:1
       rendering — is painted proportionally bigger. app.js measures
       the ratio (innerWidth / 1920) and publishes it as --ui-scale;
       these rules apply it as a plain `zoom` on the chrome.

       #map is NOT in the list, on purpose. Leaflet converts between
       screen pixels and map coordinates on every drag, every marker
       and every circle; a scaled coordinate space underneath it is
       exactly the kind of failure that cannot be debugged on a
       screen with no console. The map keeps its full area and its
       untouched pixel math — only what sits on top of it shrinks.

       Nothing here fires unless app.js adds .car-ui, which it does
       only in Tesla mode and only when the viewport is narrower than
       the width the interface was drawn for. On a phone, on a
       desktop, and on an in-car browser that reports 1920 again,
       --ui-scale is 1 and this whole block is inert.
    ══════════════════════════════════════════════════════════════ */
    body.car-ui #join-screen,
    body.car-ui .header,
    body.car-ui .tab-bar,
    body.car-ui #map-view > *:not(#map),
    /* Sibling of #map-view, not a child — it sits directly under
       #main-screen, which is why the descendant rule above missed it and it
       stayed at full size while everything around it shrank. */
    body.car-ui #rt-search-modal,
    body.car-ui #map .leaflet-control-container,
    body.car-ui #trip-view,
    body.car-ui #chat-view,
    body.car-ui #pairing-modal,
    body.car-ui #chkey-modal,
    body.car-ui #pairing-toast,
    body.car-ui .pin-sheet-backdrop,
    body.car-ui .panel,
    body.car-ui .panel-backdrop,
    body.car-ui .qr-modal-bd,
    body.car-ui #about-modal {
      zoom: var(--ui-scale, 1);
    }

    /* The Tesla split layout is a grid on #main-screen, which is not zoomed
       (zooming it would take the map with it). Its own track sizes therefore
       have to be scaled here, or the chat column would keep a physical width
       that no longer matches the shrunken text inside it. */
    body.car-ui.tesla-mode #main-screen {
      grid-template-columns: 1fr calc(380px * var(--ui-scale, 1));
    }
    body.car-ui.tesla-mode.chat-hidden #main-screen {
      grid-template-columns: 1fr 0px;
    }

    /* Viewport units do NOT follow `zoom` — Chrome resolves vh against the
       real viewport, not the zoomed coordinate space. So zooming the search
       modal shrank the box by exactly as much as the text inside it and not
       one extra result fitted: 88vh became 88 % of the screen *times* the
       scale. Dividing it back makes the box fill the same share of the screen
       it always did, while its contents are the ones that shrink — which is
       what "see more than two results" actually needs. */
    body.car-ui .rt-search-modal-content {
      /* --vvh is the height actually on screen (app.js, visualViewport), so
         this keeps the panel above the car's keyboard when the browser
         reports it. Divided by the scale because a zoomed element's lengths
         are in its own coordinate space, while the px value published from JS
         is in real ones — the same reason the vh version was wrong. */
      max-height: calc(var(--vvh, 88vh) * 0.92 / var(--ui-scale, 1));
    }

    /* The results themselves have to be compact, not just scaled: a Nominatim
       label ("Ajaccio, Corse-du-Sud, Corse, France métropolitaine, France")
       wraps to two lines, and at the in-car sizes two of those filled the
       whole panel. Trimmed to two lines with an ellipsis so one long answer
       cannot push the rest off the list. */
    body.car-ui .rt-modal-result-item {
      font-size: 15px; padding: 10px 12px; line-height: 1.3;
      display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
      overflow: hidden;
    }
    body.car-ui #rt-search-modal-results { padding: 12px; gap: 8px; }
    body.car-ui .rt-search-modal-header   { padding: 10px 12px; }
    body.car-ui .rt-search-modal-header h3 { font-size: 16px; }

    /* ── Car-screen tools in the channel panel ─────────────────────────────
       The scale row only means anything where the page is being scaled, so it
       follows .car-ui. The audio check is shown everywhere — it is just as
       useful on the phone half of a failing call. */
    .panel-car-tools {
      margin-top: 18px; padding-top: 14px;
      border-top: 1px solid var(--border);
      display: none; flex-direction: column; gap: 10px;
    }
    /* Diagnostic tools, not features — `convoix.app/#debug` only. */
    body.debug-tools .panel-car-tools { display: flex; }
    .ui-scale-block { display: none; }
    /* Deliberately NOT gated on .car-ui: that class switches off at exactly
       1.00, which hid the stepper at the very moment someone had stepped up
       to it and needed it to step back down. */
    body.debug-tools .ui-scale-block { display: block; }
    .ui-scale-row { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
    .ui-scale-btn {
      min-width: 40px; height: 36px; padding: 0 10px;
      background: var(--surface2); color: var(--text);
      border: 1px solid var(--border); border-radius: var(--r-sm);
      font: 800 17px/1 var(--f-mono); cursor: pointer;
    }
    .ui-scale-btn:active { transform: scale(0.96); }
    .ui-scale-btn.ui-scale-auto { font-size: 12px; margin-left: auto; }
    .ui-scale-value {
      min-width: 82px; text-align: center;
      font: 700 13px/1 var(--f-mono); color: var(--text-sec);
    }
    .panel-audio-diag {
      width: 100%; height: 40px;
      background: var(--surface2); color: var(--text);
      border: 1px solid var(--border); border-radius: var(--r-sm);
      font: 800 13px/1 var(--f-sans); cursor: pointer;
    }
    .panel-audio-diag:active { transform: scale(0.99); }

    /* ── Map marker scale on a car screen ──────────────────────────────────
       Markers live inside #map, which is never zoomed (Leaflet's coordinate
       math has to stay untouched), so they are pulled back with a transform
       instead. transform-origin is set to each icon's Leaflet anchor — the
       centre for a car pin (iconAnchor 45,32 of a 90×64 box), the bottom
       centre for a Supercharger and the finish flag — so the marker shrinks
       around the exact point it marks and stays on its coordinates. The CSS
       box is untouched, so iconSize/iconAnchor stay valid and the hit area
       scales with the artwork. */
    body.car-ui .car-pin     { transform: scale(var(--pin-scale, 1)); transform-origin: center center; }
    body.car-ui .sc-pin      { transform: scale(var(--pin-scale, 1)); transform-origin: bottom center; }
    body.car-ui .finish-flag { transform: scale(var(--pin-scale, 1)); transform-origin: bottom center; }
