/* ============================================================================
   AI-Chat und Data-Modus — Gruppe 2

   Diese Datei hat 2.140 Zeilen Inline-CSS aus `templates/channels/ai_chat.html`
   abgeloest (Befund C3: 93 Inline-`style=`, 39 Hex-Farben, 190 `rgba()`).
   Solange die Regeln im Template standen, lagen sie AUSSERHALB jeder Schicht
   und schlugen damit `components.css` und `tokens.css` — kein Primitiv aus
   Gruppe 0 konnte hier greifen. Deshalb steht alles hier und in `@layer
   channels`: die Schicht liegt hinter `components`, sie darf also ergaenzen,
   ohne dass es `!important` braucht.

   Reihenfolge der Abschnitte:
     1 · Lesespalte und Rahmen
     2 · Nachrichten
     3 · Fliesstext aus Markdown
     4 · Zustaende waehrend des Streams
     5 · Systemmeldungen und Hilfe
     6 · Die Eingabezeile
     7 · Menues und Slash-Liste
     8 · Anhaenge und Bildvorschau
     9 · Earnings und Reaktionstabelle
    10 · Data-Modus
    11 · Glossar
    12 · Textvorschlaege
    13 · Enge Fenster

   STIL.md geht dieser Datei vor. Keine Farbe, die nicht in `tokens.css` steht.
   ========================================================================= */

@layer channels {

    /* ====================================================================
       1 · Lesespalte und Rahmen — C1, C2

       Der Wert stand zweimal (`ai_chat.html:4760` und `:4771`) und lautete
       beide Male `900px` — gerendert 121 Zeichen je Zeile bei einer Grenze
       von 65 bis 75. Jetzt steht er einmal, als `--measure-prose`, und die
       Eingabezeile teilt ihn sich mit der Spalte.
       ==================================================================== */

    .chat-container {
        display: flex;
        flex-direction: column;
        height: 100%;
        min-height: 0;
    }

    /* Der Verlauf scrollt, die Eingabe nicht. Er beginnt oben — der
       Leerraum liegt unter dem letzten Beitrag, nicht darueber. Mit
       `justify-content: flex-end` klebte eine kurze Unterhaltung unten an
       der Eingabe, die erste Antwort stand dann in der unteren
       Bildschirmhaelfte statt oben im Verlauf. */
    .chat-messages {
        flex: 1;
        min-height: 0;
        display: flex;
        flex-direction: column;
        overflow-y: auto;
        overscroll-behavior: contain;
        padding: var(--space-4);
    }

    /* Auf schmalen Fenstern begrenzt nicht mehr `--measure-prose` die Spalte,
       sondern das Fenster: bei 1024 px bleiben der Inhaltsflaeche nach den
       Shell-Leisten 576 px, davon gingen zweimal 16 px an diesen Innenabstand.
       Er faellt auf `--space-2`, damit der Lesetext so breit wird, wie der
       Platz es zulaesst. Die verbleibende Enge liegt an der Shell, nicht
       hier — siehe die Zusammenfassung zu Gruppe 2. */
    @media (max-width: 1200px) {
        .chat-messages {
            padding-inline: var(--space-2);
        }
    }

    .chat-stream {
        width: 100%;
        /* Die Schrift steht hier, weil `70ch` an der Schrift DIESES Elements
           gemessen wird. Ohne diese Zeile erbte die Spalte 14 px aus der
           Shell, `70ch` ergab 630 px — und der Fliesstext darin steht auf
           16 px, also gerendert nur 62 Zeichen. Gemessen, nicht geschaetzt. */
        font-family: var(--font-prose);
        font-size: var(--text-base);
        line-height: var(--line-height-prose);
        max-inline-size: var(--measure-prose);
        margin-inline: auto;
        /* Die Spalte waechst nach unten; die Eingabe sitzt direkt darunter. */
        padding-block-end: var(--space-5);
    }

    .chat-input-container {
        position: relative;
        z-index: var(--z-local);
        padding-block-start: 0;
        background: transparent;
    }

    /* Dieselbe Breite wie die Lesespalte, mittig darunter — §3.2. Die
       `min-height: 56px` von frueher ist weg; die Zeile ist im Ruhezustand
       48 px hoch und waechst mit dem Text. */
    .chat-input-shell {
        position: relative;
        width: 100%;
        /* Dieselbe Schriftgroesse wie `.chat-stream`, damit `70ch` denselben
           Pixelwert ergibt. Sonst stehen Spalte und Eingabe unterschiedlich
           breit uebereinander. Die Bedienelemente darin setzen ihre eigene
           Groesse, hier wirkt es nur als Rechenbasis. */
        font-size: var(--text-base);
        max-inline-size: var(--measure-prose);
        margin-inline: auto;
        padding-block-end: var(--space-4);
    }

    /* ====================================================================
       2 · Nachrichten — §3.5

       Der Bot bekommt keinen Rahmen und kein Gruen mehr. Die Antwort steht
       frei auf der Flaeche; die Trennung leistet der Abstand, nicht die
       Linie (R3). Nur die Nutzernachricht hat einen sichtbaren Behaelter,
       weil sie rechts steht und sonst in der Spalte verschwaende.
       ==================================================================== */

    .message,
    .chat-message {
        display: flex;
        gap: var(--space-3);
        margin-block-end: var(--space-5);
    }

    /* Zwischen Nutzerfrage und der zugehoerigen Antwort steht weniger Luft
       als zwischen zwei Wechseln — sie gehoeren zusammen. */
    .message.user-message,
    .chat-message.user-message,
    .chat-message-user {
        margin-block-end: var(--space-4);
        /* STEP_02_CHAT.md §3.5: die eigene Frage steht rechts, die Antwort
           laeuft ueber die volle Spalte. Ohne das stehen beide linksbuendig
           und der Wechsel zwischen Frage und Antwort ist nur noch an der
           Flaeche zu erkennen. */
        margin-inline-start: auto;
        max-inline-size: min(36rem, 78%);
    }

    .avatar,
    .bot-msg-avatar,
    .user-msg-avatar {
        display: flex;
        flex-shrink: 0;
        align-items: center;
        justify-content: center;
        width: 32px;
        height: 32px;
        border-radius: var(--radius-chip);
        background: var(--surface-raised);
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        font-weight: var(--font-weight-medium);
        line-height: 1;
    }

    .user-msg-avatar-text {
        font-family: var(--font-ui);
        font-size: var(--text-xs);
        font-weight: var(--font-weight-medium);
        line-height: 1;
    }

    .bot-msg-content,
    .user-msg-content {
        position: relative;
        flex: 1;
        min-width: 0;
    }

    /* Der Behaelter steht schon bündig am rechten Rand der Lesespalte
       (margin-inline-start: auto auf .chat-message-user), aber die Blase
       darin ist `inline-block` — ohne diese Zeile haengt sie an der
       LINKEN Kante ihres eigenen, bereits rechtsbuendigen Behaelters und
       liess dort eine Luecke von der Kappungsbreite bis zum echten Rand. */
    .user-msg-content {
        text-align: right;
    }

    /* Der Einzug richtete frueher zwei verschiedene Nachrichtenarten
       verschieden aus. Eine Spalte, ein Einzug. */
    .bot-msg-content-intro,
    .bot-msg-content-stream {
        margin-left: 0;
    }

    .header {
        display: flex;
        align-items: baseline;
        gap: var(--space-2);
        margin-block-end: var(--space-1);
    }

    /* Kein Gruen am Bot-Namen — §3.5. Der Name ist Beschriftung, keine
       Marke. */
    .username,
    .bot-name-label {
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-sm);
        font-weight: var(--font-weight-medium);
    }


    /* Die Nutzernachricht bleibt rechts, Flaeche `--surface-raised`,
       Radius aus dem Statut. Kein Rahmen — die Flaeche traegt die Kante. */
    .user-msg-bubble {
        display: inline-block;
        max-width: 100%;
        text-align: left;
        padding-block: var(--space-2);
        padding-inline: var(--space-3);
        border-radius: var(--radius-control);
        background: var(--surface-raised);
        color: var(--ink-primary);
    }

    .text {
        color: var(--ink-primary);
        font-family: var(--font-ui);
        font-size: var(--text-base);
        font-weight: var(--font-weight-regular);
        line-height: var(--line-height-prose);
    }

    /* Zeitstempel: Mono, 12 px, gedaempft, nur beim Ueberfahren — §3.5.
       Er bewegt sich nicht, er blendet auf; `max-width` zu animieren war
       eine Layout-Animation und damit gegen STIL.md §6. */
    .timestamp,
    .hover-time-label,
    .message-time-label {
        color: var(--ink-muted);
        font-family: var(--font-data);
        font-size: var(--text-2xs);
        font-variant-numeric: tabular-nums;
        line-height: 1.2;
    }

    .hover-time-label,
    .message .timestamp {
        opacity: 0;
        transition: opacity var(--dur-fast) var(--ease-out);
    }

    .bot-msg-content > .hover-time-label {
        position: absolute;
        left: 0;
        bottom: calc(-1 * var(--space-4));
        display: block;
        pointer-events: none;
    }

    .chat-message-user .hover-time-label,
    .user-msg-content .hover-time-label {
        display: none;
    }

    @media (hover: hover) and (pointer: fine) {
        .chat-message:not(.chat-message-user):hover .hover-time-label,
        .chat-message:not(.chat-message-user):focus-within .hover-time-label,
        .message:hover .timestamp,
        .message:focus-within .timestamp {
            opacity: 1;
        }
    }

    .message-date-separator {
        display: flex;
        justify-content: center;
        margin-block: var(--space-4) var(--space-3);
    }

    .message-date-separator-label {
        color: var(--ink-muted);
        font-size: var(--text-2xs);
        user-select: none;
    }

    /* ====================================================================
       3 · Fliesstext aus Markdown

       Der Fliesstext ist 16 px Inter mit Zeilenhoehe 1,68 — §3.1. Die
       Ueberschriftengewichte 700 fielen auf 500 bzw. 600 (R4).
       ==================================================================== */

    .markdown-content {
        color: var(--ink-primary);
        font-family: var(--font-ui);
        font-size: var(--text-base);
        font-weight: var(--font-weight-regular);
        line-height: var(--line-height-prose);
    }

    .markdown-content h1,
    .markdown-content h2,
    .markdown-content h3 {
        margin-block: var(--space-4) var(--space-2);
        font-weight: var(--font-weight-medium);
        line-height: 1.3;
        text-wrap: balance;
    }

    .markdown-content h1 { font-size: var(--text-lg); }
    .markdown-content h2 { font-size: var(--text-base); }
    .markdown-content h3 { font-size: var(--text-sm); }

    .markdown-content p {
        margin-block: var(--space-2);
        text-wrap: pretty;
    }

    .markdown-content ul,
    .markdown-content ol {
        margin-block: var(--space-2) var(--space-3);
        padding-inline-start: var(--space-5);
    }

    .markdown-content li {
        margin-block: var(--space-1);
    }

    .markdown-content strong {
        color: var(--ink-primary);
        font-weight: var(--font-weight-semibold);
    }

    .markdown-content em {
        color: var(--ink-secondary);
        font-style: italic;
    }

    .markdown-content code,
    .markdown-content pre {
        font-family: var(--font-data);
        font-size: var(--text-xs);
    }

    .markdown-content pre {
        margin-block: var(--space-3);
        padding: var(--space-3);
        border-radius: var(--radius-control);
        background: var(--surface-raised);
        overflow-x: auto;
    }

    /* Kennzahlen im Fliesstext. Die Farbe steht auf dem Wert, nicht auf
       der Beschriftung — R2. */
    .metric-line {
        display: block;
        margin-block: var(--space-1);
    }

    .metric-summary {
        margin-block: var(--space-2) var(--space-3);
    }

    .metric-label {
        color: var(--ink-secondary);
        font-weight: var(--font-weight-regular);
    }

    .metric-value {
        font-family: var(--font-data);
        font-variant-numeric: tabular-nums;
        font-weight: var(--font-weight-medium);
    }

    .metric-positive { color: var(--signal-up); }
    .metric-negative { color: var(--signal-down); }
    .metric-neutral  { color: var(--signal-warn); }
}

@layer channels {

    /* ====================================================================
       4 · Zustaende waehrend des Streams

       Der Denkkasten hatte eine Rasterzeilen-Auflage, einen blinkenden
       Blockcursor und einen gruenen Rollbalken — Terminal-Kostuem ohne
       Terminal-Funktion (C5, R2). Was bleibt, ist die Funktion: ein
       Bereich, der sich auf- und zuklappen laesst.
       ==================================================================== */

    .status-line {
        display: flex;
        align-items: center;
        gap: var(--space-2);
        margin-block-end: var(--space-2);
        color: var(--ink-muted);
        font-size: var(--text-xs);
        line-height: 1.4;
    }

    /* Leer heisst still: die Zeile beginnt ohne Inhalt und darf dann auch
       keinen Abstand aufspannen. */
    .status-line:empty {
        display: none;
    }

    /* Das Wartezeichen der Antwort. Es sitzt in der Blase beim Avatar, nicht
       unten in der Eingabeleiste — die Wartezeit gehoert dorthin, wo die
       Antwort entsteht. Drei Punkte, die langsam steigen und sinken;
       animiert werden nur `opacity` und `transform` (STIL.md §6). Der
       Traeger ist `aria-busy` — damit nimmt ihn die reduced-motion-Regel in
       base.css aus: wer Bewegung reduziert, soll trotzdem sehen, dass
       gearbeitet wird. */
    .bot-typing-dots {
        display: none;
        align-items: center;
        gap: var(--space-1);
        height: 20px;
        margin-block-end: var(--space-2);
    }

    .chat-message.is-awaiting .bot-typing-dots {
        display: inline-flex;
    }

    .bot-typing-dots span {
        width: 6px;
        height: 6px;
        border-radius: var(--radius-round, 50%);
        background: var(--ink-muted);
        animation: chat-typing-dot 1.6s var(--ease-in-out) infinite;
    }

    .bot-typing-dots span:nth-child(2) { animation-delay: 0.2s; }
    .bot-typing-dots span:nth-child(3) { animation-delay: 0.4s; }

    @keyframes chat-typing-dot {
        0%, 100%  { opacity: 0.35; transform: translateY(0); }
        50%       { opacity: 1;    transform: translateY(-3px); }
    }

    .status-pills-label {
        margin-block-end: 4px;
        color: var(--ink-muted);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        font-weight: var(--font-weight-medium);
        letter-spacing: 0.02em;
    }

    .status-pills {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-1);
        margin-block-end: var(--space-2);
    }

    .stream-status-pill {
        display: inline-flex;
        align-items: center;
        height: 20px;
        padding-inline: 6px;
        border-radius: var(--radius-chip);
        background: var(--surface-raised);
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        font-weight: var(--font-weight-regular);
    }

    /* Die drei Zustandsfarben sind Zustandssignal, nicht Zier — R2 laesst
       sie hier ausdruecklich zu. */
    .stream-status-pill-info {
        background: var(--surface-selected);
        color: var(--ink-primary);
    }

    .stream-status-pill-success {
        background: var(--signal-up-surface);
        color: var(--signal-up);
    }

    .stream-status-pill-error {
        background: var(--signal-down-surface);
        color: var(--signal-down);
    }

    /* Smart Routing: die Umleitung ist Zustandssignal (Modell offline/langsam). */
    .stream-status-pill-warn {
        background: var(--signal-warn-surface);
        color: var(--signal-warn);
    }

    /* Der Textcursor waehrend des Streams. Er blinkt in der Deckkraft —
       die reduced-motion-Regel in base.css schaltet ihn still. */
    .typing-cursor {
        animation: chat-caret-blink 1s steps(1, end) infinite;
    }

    @keyframes chat-caret-blink {
        0%, 50%   { opacity: 1; }
        50.01%, 100% { opacity: 0; }
    }

    /* Der Denkabschnitt ist Text, kein Kasten (Vorbild: Claude). Eine Zeile
       mit Verb + Sekunden + Pfeil, aufklappbar zur Zeitleiste darunter. */
    .thinking-box {
        position: sticky;
        top: var(--space-3);
        z-index: var(--z-local);
        margin-block-end: var(--space-3);
    }

    /* Niemals ueber Text ueberlappen: Bei aufgeklapptem Kasten oder
       nach Abschluss des Streams (.is-done oder .chat-message:not(.bot-streaming))
       bleibt die Box immer statisch im normalen Dokumentenfluss. */
    .thinking-box.is-collapsed,
    .thinking-box.is-done,
    .thinking-box:not(.is-collapsed),
    .chat-message:not(.bot-streaming) .thinking-box {
        position: static;
    }

    .thinking-header {
        display: inline-flex;
        align-items: center;
        gap: var(--space-1);
        padding: 0;
        border: none;
        background: none;
        color: var(--ink-muted);
        font: inherit;
        font-size: var(--text-xs);
        cursor: pointer;
        user-select: none;
    }

    @media (hover: hover) and (pointer: fine) {
        .thinking-header:hover {
            color: var(--ink-secondary);
        }
    }

    .thinking-toggle-indicator {
        display: inline-flex;
        transform: rotate(0deg);
        transition: transform var(--dur-fast) var(--ease-standard);
    }

    .thinking-box.is-collapsed .thinking-toggle-indicator {
        transform: rotate(-90deg);
    }

    /* Aufklappen ohne Hoehenanimation. `max-height` zu bewegen war eine
       Layout-Animation und damit gegen STIL.md §6; jetzt blendet der
       Inhalt ueber. */
    .thinking-content {
        position: relative;
        max-height: 220px;
        margin-block-start: var(--space-2);
        padding-inline-start: 1px;
        overflow-y: auto;
        opacity: 1;
        transition: opacity var(--dur-fast) var(--ease-out);
    }

    .thinking-box.is-collapsed .thinking-content {
        display: none;
        opacity: 0;
    }

    .thinking-content::-webkit-scrollbar {
        width: 6px;
    }

    .thinking-content::-webkit-scrollbar-track {
        background: transparent;
    }

    .thinking-content::-webkit-scrollbar-thumb {
        border-radius: var(--radius-chip);
        background: var(--line-strong);
    }

    /* Zeitleiste der Denkschritte: Punkt + duenne Linie je Zeile, wie eine
       Abfolge von Notizen statt eines Fliesstexts. */
    .thinking-entries {
        display: flex;
        flex-direction: column;
    }

    .thinking-entry {
        position: relative;
        padding-inline-start: var(--space-4);
        padding-block: var(--space-1);
        color: var(--ink-muted);
        font-size: var(--text-xs);
        line-height: 1.5;
    }

    .thinking-entry::before {
        content: '';
        position: absolute;
        left: 3px;
        top: 0;
        bottom: 0;
        width: 1px;
        background: var(--line-subtle);
    }

    .thinking-entry:first-child::before {
        top: 9px;
    }

    .thinking-entry:last-child::before {
        bottom: calc(100% - 9px);
    }

    .thinking-entry::after {
        content: '';
        position: absolute;
        left: 0;
        top: 9px;
        width: 6px;
        height: 6px;
        border-radius: 50%;
        background: var(--ink-muted);
    }

    /* Reasoning als Fliesstext-Absatz statt Wort-pro-Zeile. */
    .thinking-entry.is-reasoning {
        white-space: pre-wrap;
        overflow-wrap: anywhere;
        color: var(--ink-secondary);
    }

    .thinking-entry.is-reasoning::after {
        background: var(--line-strong);
    }

    /* Werkzeugschritt: Punkt pulsiert, solange er laeuft. */
    .thinking-entry.is-step[data-state="running"]::after {
        background: var(--ink-secondary);
        animation: turn-step-pulse 1.2s var(--ease-standard) infinite;
    }

    .thinking-entry.is-step[data-state="error"] {
        color: var(--danger);
    }

    .thinking-entry.is-entering,
    .web-search-group.is-entering {
        animation: turn-enter var(--dur-fast) var(--ease-out) both;
    }

    @keyframes turn-enter {
        from { opacity: 0; transform: translateY(2px); }
        to { opacity: 1; transform: none; }
    }

    @keyframes turn-step-pulse {
        50% { opacity: 0.35; }
    }

    /* Antworttext: frisch eingeblendete Zeichen (Deckkraft setzt chat_stream_text.js). */
    .stream-fresh {
        transition: opacity 80ms linear;
    }

    @media (prefers-reduced-motion: reduce) {
        .thinking-entry.is-entering,
        .web-search-group.is-entering,
        .thinking-entry.is-step[data-state="running"]::after {
            animation: none;
        }
    }

    /* ====================================================================
       Web Search Card (Claude Style — Vorbild: Claude Web Search)
       Kompakter Kasten mit Favicon, Titel, Domain und direktem Link.
       ==================================================================== */

    .web-search-wrapper {
        margin-block-end: var(--space-3);
    }

    .web-search-block {
        display: flex;
        flex-direction: column;
        gap: var(--space-2);
    }

    .web-search-heading {
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-xs);
        font-weight: var(--font-weight-medium);
        letter-spacing: normal;
        user-select: none;
    }

    .web-search-groups {
        display: flex;
        flex-direction: column;
        gap: var(--space-2);
    }

    .web-search-group {
        display: flex;
        flex-direction: column;
        gap: 6px;
    }

    .web-search-bar {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: var(--space-2);
        padding-block: 2px;
        padding-inline: 0;
        border: none;
        background: transparent;
        color: var(--ink-primary);
        font-family: var(--font-ui);
        font-size: var(--text-xs);
        cursor: pointer;
        user-select: none;
    }

    .web-search-bar-left {
        display: flex;
        align-items: center;
        gap: 6px;
        min-width: 0;
        flex: 1;
    }

    .web-search-globe-icon {
        flex-shrink: 0;
        width: 14px;
        height: 14px;
        color: var(--ink-secondary);
    }

    .web-search-query-text {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        color: var(--ink-primary);
        font-size: var(--text-xs);
        font-weight: var(--font-weight-regular);
    }

    .web-search-bar-right {
        display: flex;
        align-items: center;
        gap: 6px;
        flex-shrink: 0;
    }

    .web-search-count {
        color: var(--ink-muted);
        font-family: var(--font-data);
        font-size: var(--text-2xs);
        font-weight: var(--font-weight-regular);
    }

    .web-search-bar .chevron-icon {
        flex-shrink: 0;
        width: 12px;
        height: 12px;
        color: var(--ink-muted);
        transform: rotate(0deg);
        transition: transform var(--dur-fast) var(--ease-standard);
    }

    .web-search-group.is-collapsed .web-search-bar .chevron-icon {
        transform: rotate(-90deg);
    }

    .web-search-results {
        max-height: 195px;
        padding: 4px;
        border-radius: var(--radius-control);
        background: var(--surface-raised);
        border: 1px solid var(--line-subtle);
        overflow-y: auto;
        display: flex;
        flex-direction: column;
        gap: 2px;
    }

    .web-search-group.is-collapsed .web-search-results {
        display: none;
    }

    /* Nach dem Stream klappt das Panel weich zur Kopfzeile zusammen. */
    .web-search-block:has(> .web-search-heading[role="button"]) {
        display: grid;
        grid-template-rows: auto 1fr;
        transition: grid-template-rows var(--dur-base) var(--ease-standard);
    }

    /* Gleiche Spezifitaet wie die :has()-Regel oben, sonst gewinnt 1fr. */
    .web-search-block.is-collapsed:has(> .web-search-heading[role="button"]) {
        grid-template-rows: auto 0fr;
    }

    .web-search-block > .web-search-groups {
        min-height: 0;
        overflow: hidden;
    }

    .web-search-heading[role="button"] {
        cursor: pointer;
    }

    .web-search-group[data-state="empty"] .web-search-query-text,
    .web-search-group[data-state="empty"] .web-search-count {
        color: var(--ink-muted);
    }

    .web-search-results::-webkit-scrollbar {
        width: 4px;
    }

    .web-search-results::-webkit-scrollbar-track {
        background: transparent;
    }

    .web-search-results::-webkit-scrollbar-thumb {
        border-radius: var(--radius-chip);
        background: var(--line-strong);
    }

    .web-search-item {
        display: flex;
        align-items: center;
        gap: 10px;
        padding-block: 5px;
        padding-inline: 8px;
        border-radius: 5px;
        background: transparent;
        color: var(--ink-primary);
        text-decoration: none;
        transition: background-color var(--dur-instant) var(--ease-standard);
        min-width: 0;
    }

    @media (hover: hover) and (pointer: fine) {
        .web-search-item:hover {
            background: var(--surface-hover);
            color: var(--ink-primary);
        }
    }

    .web-search-favicon-wrap {
        display: flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
        width: 16px;
        height: 16px;
    }

    .web-search-favicon {
        width: 16px;
        height: 16px;
        border-radius: 3px;
        object-fit: contain;
    }

    .web-search-fallback-icon {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 16px;
        height: 16px;
        color: var(--ink-muted);
    }

    .web-search-fallback-icon .icon {
        width: 13px;
        height: 13px;
    }

    .web-search-item-title {
        flex: 1;
        min-width: 0;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        font-family: var(--font-ui);
        font-size: var(--text-xs);
        color: var(--ink-primary);
    }

    .web-search-item-domain {
        flex-shrink: 0;
        margin-left: auto;
        color: var(--ink-muted);
        font-family: var(--font-data);
        font-size: var(--text-2xs);
    }

    .web-search-loading,
    .web-search-empty {
        display: flex;
        align-items: center;
        gap: var(--space-2);
        padding: var(--space-2) var(--space-3);
        color: var(--ink-muted);
        font-family: var(--font-ui);
        font-size: var(--text-xs);
    }

    .web-search-spinner {
        display: inline-block;
        width: 12px;
        height: 12px;
        border: 1.5px solid var(--line-strong);
        border-top-color: var(--ink-secondary);
        border-radius: 50%;
        animation: web-search-spin 0.8s linear infinite;
    }

    @keyframes web-search-spin {
        to { transform: rotate(360deg); }
    }

    /* Quellen. Der Chip war blau und trug einen Rahmen; jetzt ist er eine
       Flaeche wie jeder andere Chip. */
    .sources-list {
        scrollbar-width: thin;
        scrollbar-color: var(--line-strong) transparent;
    }

    .sources-list::-webkit-scrollbar {
        width: 6px;
    }

    .sources-list::-webkit-scrollbar-track {
        background: transparent;
    }

    .sources-list::-webkit-scrollbar-thumb {
        border-radius: var(--radius-chip);
        background: var(--line-strong);
    }

    .sources-popup {
        opacity: 0;
        visibility: hidden;
        pointer-events: none;
        transform: scale(0.97);
        transform-origin: bottom left;
        transition:
            opacity var(--dur-base) var(--ease-out),
            transform var(--dur-base) var(--ease-out),
            visibility var(--dur-base) var(--ease-out);
    }

    .sources-popup.is-open {
        opacity: 1;
        visibility: visible;
        pointer-events: auto;
        transform: scale(1);
    }

    .source-badge {
        cursor: pointer;
    }

    .inline-source-chip {
        display: inline-flex;
        align-items: center;
        margin-inline-start: var(--space-2);
        padding-block: 2px;
        padding-inline: var(--space-2);
        border-radius: var(--radius-chip);
        background: var(--accent-subtle);
        color: var(--ink-secondary);
        font-size: var(--text-2xs);
        font-weight: var(--font-weight-regular);
        vertical-align: baseline;
        text-decoration: none;
        transition:
            background-color var(--dur-instant) var(--ease-standard),
            color var(--dur-instant) var(--ease-standard);
    }

    @media (hover: hover) and (pointer: fine) {
        a.inline-source-chip:hover {
            background: var(--surface-hover);
            color: var(--ink-primary);
        }
    }

    /* Diagramm im Verlauf. Der Kopf trug Versalien mit Sperrung — das ist
       die Grossbuchstaben-Marke aus dem Verbot in STIL.md §7. */
    .chart-container {
        display: none;
        margin-block-end: var(--space-4);
        border-radius: var(--radius-panel);
        background: var(--surface-raised);
        overflow: hidden;
    }

    .inline-chart-title {
        display: flex;
        align-items: center;
        min-height: 36px;
        padding-inline: var(--space-3);
        border-bottom: 1px solid var(--line);
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-xs);
        font-weight: var(--font-weight-medium);
        letter-spacing: normal;
        text-transform: none;
    }

    .inline-chart-viewport {
        width: 100%;
        min-height: 320px;
    }

    .inline-chart-fallback {
        padding: var(--space-3);
        color: var(--ink-muted);
        font-size: var(--text-2xs);
    }

    /* ====================================================================
       5 · Systemmeldungen und Hilfe
       ==================================================================== */

    .system-message {
        display: flex;
        justify-content: center;
        margin-block: var(--space-2) var(--space-4);
    }

    .system-message-pill {
        display: inline-flex;
        align-items: center;
        max-width: 100%;
        padding-block: var(--space-1);
        padding-inline: var(--space-3);
        border-radius: var(--radius-chip);
        background: var(--surface-raised);
        color: var(--ink-secondary);
        font-size: var(--text-2xs);
        line-height: 1.4;
        white-space: pre-wrap;
        text-align: center;
    }

    .system-message-success .system-message-pill {
        background: var(--signal-up-surface);
        color: var(--signal-up);
    }

    .system-message-warning .system-message-pill {
        background: var(--signal-warn-surface);
        color: var(--signal-warn);
    }

    .system-message-error .system-message-pill {
        background: var(--signal-down-surface);
        color: var(--signal-down);
    }

    .system-message-help {
        margin-block: var(--space-3) var(--space-5);
    }

    /* Die Hilfekarte trug einen Verlauf, einen Schatten, eine Lichtkante
       und eine 22-px-Versalienzeile mit 0,12 em Sperrung. Ruhende Flaechen
       haben keinen Schatten (§7). */
    .system-help-card {
        position: relative;
        width: 100%;
        border-radius: var(--radius-panel);
        background: var(--surface-raised);
        padding: var(--space-3);
        overflow: hidden;
    }

    .system-help-head {
        display: flex;
        align-items: center;
        gap: var(--space-2);
        margin: calc(-1 * var(--space-3)) calc(-1 * var(--space-3)) var(--space-3);
        padding: var(--space-3);
        border-bottom: 1px solid var(--line);
    }

    .system-help-badge {
        display: inline-flex;
        align-items: center;
        height: 24px;
        padding-inline: var(--space-2);
        border-radius: var(--radius-chip);
        background: var(--accent-subtle);
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        font-weight: var(--font-weight-regular);
        letter-spacing: normal;
        text-transform: none;
    }

    .system-help-title {
        color: var(--ink-primary);
        font-family: var(--font-ui);
        font-size: var(--text-lg);
        font-weight: var(--font-weight-medium);
        letter-spacing: normal;
        text-transform: none;
        line-height: 1.25;
    }

    .system-help-list {
        display: grid;
        gap: var(--space-1);
    }

    .system-help-item {
        display: grid;
        grid-template-columns: 22px 150px minmax(0, 1fr);
        align-items: center;
        gap: var(--space-3);
        padding-block: var(--space-2);
        padding-inline: var(--space-3);
        border-radius: var(--radius-control);
        background: var(--surface-panel);
        transition: background-color var(--dur-instant) var(--ease-standard);
    }

    @media (hover: hover) and (pointer: fine) {
        .system-help-item:hover {
            background: var(--surface-hover);
        }
    }

    .system-help-icon {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 22px;
        color: var(--ink-secondary);
        font-size: var(--text-xs);
        line-height: 1;
    }

    .system-help-command {
        color: var(--ink-primary);
        font-family: var(--font-data);
        font-size: var(--text-xs);
        font-weight: var(--font-weight-medium);
        letter-spacing: normal;
        white-space: nowrap;
    }

    .system-help-desc {
        min-width: 0;
        color: var(--ink-secondary);
        font-size: var(--text-xs);
        line-height: 1.35;
    }

    .journal-notification {
        margin-block-start: var(--space-3);
        padding-block: var(--space-2);
        padding-inline: var(--space-3);
        border-radius: var(--radius-control);
        background: var(--surface-raised);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
    }

    .journal-notification-header {
        margin-block-end: var(--space-1);
        color: var(--ink-secondary);
        font-size: var(--text-2xs);
        font-weight: var(--font-weight-medium);
        letter-spacing: normal;
        text-transform: none;
    }

    .journal-notification-body {
        color: var(--ink-primary);
        line-height: 1.5;
        white-space: pre-wrap;
    }
}

@layer channels {

    /* ====================================================================
       6 · Die Eingabezeile — C2, C5, §3.2

       Vorher: eine Leiste im Industrie-Sci-Fi-Kostuem. Zwei
       Eck-Winkel als Pseudoelemente, ein Schlagschatten nach oben, ein
       gruener Fokusrand, ein `>`-Prompt-Zeichen und zwei
       Beschriftungssysteme nebeneinander — `EARNINGS` in 11 px Versalien
       neben `Live-Search` in 12 px normal.

       Jetzt: eine Flaeche. 48 px im Ruhezustand, waechst mit dem Text bis
       200 px. Eine Beschriftungsgroesse, eine Schreibung.
       ==================================================================== */

    /* Gruppe 2b, §2 (P2): ein Kasten mit zwei Zeilen innen. Rahmen `--line`,
       Flaeche `--surface-composer` — waermer als das neutrale
       `--surface-raised`, das hier vorher stand. */
    .ios-composer {
        position: relative;
        isolation: isolate;
        display: flex;
        flex-direction: column;
        gap: var(--space-1);
        padding-block: var(--space-2);
        padding-inline: var(--space-3);
        border: 1px solid var(--line);
        border-radius: var(--radius-panel);
        background: var(--surface-composer);
        overflow: visible;
        transition:
            border-color var(--dur-fast) var(--ease-out),
            box-shadow var(--dur-fast) var(--ease-out);
    }

    /* Zeile 1 · nur der Text, ueber die volle Breite. */
    .composer-text-row {
        display: flex;
        min-width: 0;
        padding-block-start: var(--space-1);
    }

    /* Der Fokus liegt auf der Leiste, nicht auf dem nackten Textfeld —
       sonst zeichnet der Ring einen Kasten mitten in die Flaeche.

       STEP_02C B.2: Bis Gruppe 2c stand hier `outline: 2px solid
       var(--line-focus)`, und `--line-focus` ist `--accent`, also Fast-Weiss.
       Beim Tippen lag damit ein greller Rahmen um die halbe Fensterbreite.
       Jetzt wird der Rahmen, den der Composer ohnehin traegt, nur heller, und
       ein einzelner Pixel Schein legt sich davor — kein zweiter Ring mit
       Abstand, der eine zweite Kante zeichnet.

       Nur hier. Knoepfe, Felder und Menueeintraege behalten den deutlichen
       `:focus-visible`-Ring aus `base.css`; Tastaturbedienung wird davon nicht
       leiser. `box-shadow` steht STIL.md §7 nicht entgegen: verboten sind
       Schatten an RUHENDEN Flaechen, das hier ist ein Zustand. */
    .ios-composer:focus-within {
        border-color: var(--line-focus-soft);
        box-shadow: 0 0 0 1px var(--line-focus-soft);
    }

    .ios-composer.is-busy {
        opacity: 0.95;
    }

    /* Zeile 2 · die Bedienung. `align-items: center` statt `flex-end`: hier
       stehen jetzt nur noch Elemente einer Hoehe nebeneinander, das Textfeld
       ist eine Zeile hoeher gezogen. */
    .hotbar-main-content {
        position: relative;
        z-index: var(--z-local);
        width: 100%;
        min-width: 0;
        display: flex;
        align-items: center;
        gap: var(--space-2);
        opacity: 1;
        transition: opacity var(--dur-fast) var(--ease-out);
    }

    /* Modellname und Kontextstand schieben sich nach rechts, Anhang und
       Senden bleiben aussen. */
    .composer-status {
        display: inline-flex;
        flex-shrink: 1;
        min-width: 0;
        align-items: center;
        margin-inline-start: auto;
    }

    /* §2: Der Name steht in der Fliessschrift auf 13 px, die Zahl in Mono auf
       12 px. Der Rahmen des Abzeichens faellt weg — im Composer steht es auf
       derselben Flaeche wie die Bedienung, ein Kasten darum waere eine zweite
       Kante ohne Grund (R3). */
    .composer-status .token-badge {
        display: inline-flex;
        align-items: baseline;
        gap: var(--space-2);
        min-width: 0;
        min-height: 0;
        margin: 0;
        padding: 0;
        border: none;
        border-radius: 0;
        background: transparent;
    }

    .composer-model-name {
        min-width: 0;
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-xs);
        font-weight: var(--font-weight-regular);
        letter-spacing: normal;
        text-transform: none;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .composer-context-count {
        flex-shrink: 0;
        color: var(--ink-muted);
        font-family: var(--font-data);
        font-size: var(--text-2xs);
        font-variant-numeric: tabular-nums;
        white-space: nowrap;
    }

    /* Knapper Kontext bleibt ein Zustandssignal (R2) — die Toenung aus
       `applyTokenBadgeTone()` faerbt jetzt die Zahl, nicht mehr den ganzen
       Traeger. */
    .composer-status .token-badge-medium .composer-context-count { color: var(--signal-warn); }
    .composer-status .token-badge-high .composer-context-count { color: var(--signal-down); }

    /* Waehrend gedacht wird, tritt die Bedienung zurueck und die Anzeige
       tritt vor. Frueher schrumpfte die ganze Leiste auf eine Pille —
       `width`, `height` und `padding` wurden animiert, was STIL.md §6
       ausschliesst, und der Rand kam als roher `rgba()`-Wert aus dem
       JavaScript. */
    .ios-composer.is-thinking .hotbar-main-content,
    .ios-composer.is-thinking .hotbar-status-line {
        opacity: 0;
        pointer-events: none;
    }

    .composer-left {
        position: relative;
        flex-shrink: 0;
    }

    .composer-center {
        min-width: 0;
        flex: 1;
        position: relative;
        display: flex;
        align-items: flex-end;
    }

    .composer-right {
        display: inline-flex;
        flex-shrink: 0;
        align-items: flex-end;
        gap: var(--space-1);
    }

    /* Ein Knopf in der Leiste. Kein Gruen beim Ueberfahren (R2), keine
       Versalien mit Sperrung (R4). */
    .hotbar-action-btn,
    .composer-mode-wrap .dropdown-btn {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        gap: var(--space-1);
        height: 32px;
        padding-inline: var(--space-2);
        border: none;
        border-radius: var(--radius-control);
        background: transparent;
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        font-weight: var(--font-weight-regular);
        letter-spacing: normal;
        text-transform: none;
        white-space: nowrap;
        cursor: pointer;
        transition:
            background-color var(--dur-instant) var(--ease-standard),
            color var(--dur-instant) var(--ease-standard),
            transform var(--dur-fast) var(--ease-out);
    }

    @media (hover: hover) and (pointer: fine) {
        .hotbar-action-btn:hover,
        .composer-mode-wrap .dropdown-btn:hover {
            background: var(--surface-hover);
            color: var(--ink-primary);
        }
    }

    .hotbar-action-btn:active,
    .composer-mode-wrap .dropdown-btn:active {
        transform: scale(0.97);
    }

    .composer-mode-wrap .dropdown-btn.active,
    #extras-plus-btn.is-open {
        background: var(--surface-selected);
        color: var(--ink-primary);
    }

    .hotbar-label,
    #current-model-text {
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        font-weight: var(--font-weight-regular);
        letter-spacing: normal;
        text-transform: none;
        line-height: 1;
    }

    /* §2: 28×28, nur Icon. Die Beschriftung „Aktionen" ist im Markup weg, der
       Knopf braucht also keine Textbreite mehr. */
    .composer-plus-btn {
        position: relative;
        width: 28px;
        min-width: 28px;
        height: 28px;
        padding: 0;
    }

    /* Der Punkt sagt: mindestens ein Modus laeuft. Ein Zeichen statt fuenf
       Beschriftungen (P1) — und das einzige sichtbare Zeichen dafuer, er
       muss also auffallen. `--accent` statt `--ink-primary`: derselbe
       Grauton wie am Senden-Knopf, nicht das reine Weiss des Fliesstexts.
       Er ist grau — der aktive Zustand eines Bedienelements traegt keine
       Signalfarbe (R2). Er blendet auf, statt zu wachsen: `transform` und
       `opacity`, nichts sonst (§6). */
    .composer-plus-dot {
        position: absolute;
        top: 3px;
        right: 3px;
        width: 5px;
        height: 5px;
        border-radius: 999px;
        background: var(--accent);
        opacity: 0;
        pointer-events: none;
        transform: scale(0.97);
        transition:
            opacity var(--dur-fast) var(--ease-out),
            transform var(--dur-fast) var(--ease-out);
    }

    #extras-plus-btn.has-active-mode .composer-plus-dot {
        opacity: 1;
        transform: scale(1);
    }

    /* Solange das Menue offen ist, steht der Zustand darin — der Punkt neben
       dem Kreuz waere doppelt. */
    #extras-plus-btn.is-open .composer-plus-dot {
        opacity: 0;
    }

    /* Das Plus dreht sich zum Kreuz. Eine Rueckmeldung, keine Vorfuehrung. */
    .extras-plus-icon-wrap {
        position: relative;
        display: inline-block;
        flex-shrink: 0;
        width: var(--icon-nav);
        height: var(--icon-nav);
    }

    /* Lauf A: die Glyphen sind jetzt Phosphor-Schriftzeichen, keine <svg>.
       Ein <i> hat keine eigene Groesse — es braucht Zeile, Schriftgrad und
       eine Mitte, sonst sitzt das Kreuz neben dem Plus statt darauf. */
    .extras-plus-glyph {
        position: absolute;
        inset: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        color: currentColor;
        font-size: var(--icon-nav);
        line-height: 1;
        transition:
            transform var(--dur-fast) var(--ease-out),
            opacity var(--dur-fast) var(--ease-out);
    }

    .extras-plus-glyph-plus {
        opacity: 1;
        transform: rotate(0deg) scale(1);
    }

    .extras-plus-glyph-x {
        opacity: 0;
        transform: rotate(-90deg) scale(0.8);
    }

    #extras-plus-btn.is-open .extras-plus-glyph-plus {
        opacity: 0;
        transform: rotate(90deg) scale(0.8);
    }

    #extras-plus-btn.is-open .extras-plus-glyph-x {
        opacity: 1;
        transform: rotate(0deg) scale(1);
    }

    /* Lauf A, A.1: Bedienleiste und Menue standen auf 16 px — das war der
       Grund fuer „zu klein". `--icon-nav` ist die Rolle fuer beides. */
    .hotbar-action-btn i,
    .composer-mode-wrap .dropdown-btn i,
    .extras-menu-item i,
    .composer-mode-wrap .dropdown-item i {
        flex-shrink: 0;
        color: currentColor;
        font-size: var(--icon-nav);
    }

    /* STEP_02C B.3: Senden trug bis Gruppe 2c die volle Akzentflaeche
       (`background: var(--accent)`, Fast-Weiss). In einer Zeile, in der sonst
       nichts eine Flaeche hat, war das der hellste Fleck der ganzen
       Oberflaeche — ein Knopf, den man hundertmal am Tag drueckt, muss nicht
       schreien, wo er ist. Jetzt nur das Icon, wie das ＋ daneben. Fluesse
       und Zustaende kommen aus `.hotbar-action-btn`; hier steht nur, was der
       Senden-Knopf anders macht. */
    .composer-send-btn {
        width: 32px;
        min-width: 32px;
        height: 32px;
        padding: 0;
    }

    /* Ohne Flaeche traegt die Farbe des Icons den Zustand allein. Deshalb
       `opacity` nicht mehr als Ausgrauen: ein halbdurchsichtiges Icon auf
       `--surface-panel` faellt unter den Lesekontrast. `--ink-muted` ist
       gemessen (4,56:1) und sagt dasselbe. */
    .composer-send-btn:disabled {
        color: var(--ink-muted);
        background: transparent;
        cursor: not-allowed;
    }

    .composer-send-btn:disabled:active {
        transform: none;
    }

    /* Diktat-Knopf (E15). Gleiche Groesse wie Senden, damit die Zeile ruhig
       bleibt. Im aktiven Zustand traegt die Farbe den Zustand — ein Punkt
       oder eine Animation waere in einer Zeile, in der sonst nichts blinkt,
       zu laut. */
    .composer-dictate-btn {
        width: 32px;
        min-width: 32px;
        height: 32px;
        padding: 0;
    }

    .composer-dictate-btn[data-active="1"] {
        color: var(--accent, var(--ink-strong));
    }

    .composer-dictate-btn[data-disabled="1"] {
        display: none;
    }

    .composer-input-shell {
        position: relative;
        flex: 1;
        min-width: 0;
        min-height: 32px;
        display: flex;
        align-items: flex-end;
        background: transparent;
    }

    /* Das `>`-Prompt-Zeichen faellt weg — Terminal-Kostuem ohne
       Terminal-Funktion (C5, R4). Die Klasse bleibt stehen, weil das
       Markup sie noch kennen darf; sie zeichnet nur nichts mehr. */
    .cmd-prompt {
        display: none;
    }

    .composer-input {
        flex: 1;
        min-width: 0;
        min-height: 32px;
        max-height: 200px;
        padding-block: var(--space-1);
        border: none;
        /* C9: Das nackte `outline: none` von vorher hatte keinen Ersatz. Hier
           hat es einen — der Ring sitzt eine Ebene hoeher auf
           `.ios-composer:focus-within`, siehe oben. Ohne diese Zeile
           zeichnete der Browser zwei Ringe uebereinander. */
        outline: none;
        resize: none;
        background: transparent;
        color: var(--ink-primary);
        font-family: var(--font-ui);
        font-size: var(--text-sm);
        font-weight: var(--font-weight-regular);
        letter-spacing: normal;
        line-height: 1.5;
        overflow-y: hidden;
        caret-color: var(--ink-primary);
        scrollbar-width: thin;
        scrollbar-color: var(--line-strong) transparent;
    }

    .composer-input::-webkit-scrollbar {
        width: 8px;
    }

    .composer-input::-webkit-scrollbar-track {
        background: transparent;
    }

    .composer-input::-webkit-scrollbar-thumb {
        border-radius: var(--radius-chip);
        background: var(--line-strong);
    }

    .composer-input::placeholder {
        color: var(--ink-muted);
        font-family: var(--font-ui);
        font-size: var(--text-sm);
        letter-spacing: normal;
        text-transform: none;
    }

    /* Gruppe 2b, P1: Die Chip-Leiste ist weg. Sie stand dauerhaft in der
       Eingabezeile — fuenf Beschriftungen, auch wenn kein Modus lief. Der
       Zustand steht jetzt im Aktionen-Menue (§3) und als Punkt am ＋.

       Die Traeger bleiben im Markup, weil `updateHotbarStatusLine()` und die
       Vertragspruefung sie unter ihren Kennungen suchen. Sie zeichnen nichts:
       das `hidden`-Attribut im Markup, diese Regel als Rueckhalt. Wer
       `hidden` entfernt, sieht die Chips NICHT wieder — dazu muss auch hier
       etwas geaendert werden, und das ist die Absicht. */
    .hotbar-status-line,
    .hotbar-status-modes,
    .hotbar-status-badge {
        display: none;
    }

    /* Die Anzeige waehrend des Denkens. Sie sitzt mittig auf der Leiste
       und blendet auf — sie wird nicht mehr aus einer schrumpfenden
       Pille geboren. */
    .hotbar-thinking-state {
        position: absolute;
        z-index: var(--z-local);
        top: 50%;
        left: 50%;
        display: inline-flex;
        align-items: center;
        gap: var(--space-2);
        max-width: calc(100% - var(--space-5));
        height: 32px;
        padding-inline: var(--space-4);
        border-radius: var(--radius-control);
        background: var(--surface-selected);
        opacity: 0;
        pointer-events: none;
        transform: translate(-50%, -50%) scale(0.97);
        transform-origin: center center;
        transition:
            opacity var(--dur-base) var(--ease-out),
            transform var(--dur-base) var(--ease-out);
    }

    .ios-composer.is-thinking .hotbar-thinking-state {
        opacity: 1;
        transform: translate(-50%, -50%) scale(1);
    }

    /* Unsichtbar (opacity 0) drehte der Spinner endlos weiter und hielt den
       Compositor in jedem Frame wach. */
    .ios-composer:not(.is-thinking) .hotbar-thinking-spinner {
        animation: none;
    }

    .hotbar-thinking-text {
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        font-weight: var(--font-weight-regular);
        letter-spacing: normal;
        text-transform: none;
        white-space: nowrap;
    }

    .hotbar-thinking-timer {
        min-width: 30px;
        color: var(--ink-muted);
        font-family: var(--font-data);
        font-size: var(--text-2xs);
        font-variant-numeric: tabular-nums;
        text-align: right;
    }

    /* ====================================================================
       7 · Menues und Slash-Liste

       Das Aussehen der Menues steht in `components.css` (Abschnitt 5).
       Hier bleibt nur, was die Anordnung im Chat betrifft.
       ==================================================================== */

    /* §3: Das Menue waechst aus dem ＋. `transform-origin: bottom left` setzt
       den Ursprung auf die untere linke Ecke — das ist die Ecke, die am
       Ausloeser liegt, weil das Menue ueber ihm aufgeht und linksbuendig zu
       ihm steht. */
    .liquid-dropdown {
        position: absolute;
        bottom: calc(100% + var(--space-2));
        left: 0;
        z-index: var(--z-dropdown);
        padding: var(--space-1);
        border-radius: var(--radius-panel);
        background: var(--surface-menu);
        opacity: 0;
        visibility: hidden;
        pointer-events: none;
        transform: scale(0.97);
        transform-origin: bottom left;
        transition:
            opacity var(--dur-base) var(--ease-out),
            transform var(--dur-base) var(--ease-out),
            visibility var(--dur-base) var(--ease-out);
    }

    .liquid-dropdown:not(.show) {
        display: none;
    }

    .liquid-dropdown.show {
        visibility: visible;
    }

    .liquid-dropdown.is-open {
        opacity: 1;
        pointer-events: auto;
        transform: scale(1);
    }

    .liquid-dropdown.is-closing {
        opacity: 0;
        transform: scale(0.97);
        transition-duration: var(--dur-fast);
    }

    /* §3: 260 px breit, Radius `--radius-panel`. Schatten kommt aus
       `components.css` (Abschnitt 5) — ein Menue schwebt, das ist die
       einzige Stelle, an der STIL.md §7 einen Schatten zulaesst. Die
       Flaeche dort ist `--surface-overlay`, das neutrale Grau der
       Ueberlagerungen; ein Menue braucht `--surface-menu`, dieselbe
       Flaeche wie beim Verlaufszeilen-Menue und der Slash-Liste — sonst
       hat der AI-Chat zwei verschiedene Menuefarben. */
    .extras-menu-panel {
        min-width: 260px;
        padding-block: var(--space-1) var(--space-2);
        background: var(--surface-menu);
    }

    .extras-menu-panel[popover] {
        position: fixed;
        inset: auto;
        margin: 0;
        border: 0;
        overflow-y: auto;
        max-width: calc(100vw - 24px);
        max-height: calc(100dvh - 24px);
    }

    .extras-menu-panel.show {
        visibility: visible;
        opacity: 0;
        pointer-events: none;
        transform: scale(0.97);
    }

    .extras-menu-panel.show.is-open {
        opacity: 1;
        pointer-events: auto;
        transform: scale(1);
    }

    .extras-menu-panel.is-closing {
        opacity: 0;
        transform: scale(0.97);
        transition-duration: var(--dur-fast);
    }

    /* Gruppe 2b, §3: Die Abschnittsmarke steht klein und normal gesetzt, in
       der Fliessschrift — nicht als Mono-Versalien. Die Haarlinie trennt
       MODI von KONTEXT; die erste Marke braucht keine, ueber ihr steht die
       Kante des Menues. */
    .extras-menu-section-label {
        padding-block: var(--space-2) var(--space-1);
        padding-inline: var(--space-3);
        color: var(--ink-muted);
        font-family: var(--font-ui);
        font-size: var(--text-xs);
        font-weight: var(--font-weight-regular);
        letter-spacing: normal;
        text-transform: none;
    }

    /* Nur zwischen den Abschnitten, nicht ueber dem ersten. Die Linie liegt
       am Rand des Menues an — sie ist die Trennung, nicht ein Rahmen um eine
       Gruppe (R3).

       STEP_02C B.5: Bis Gruppe 2c stand hier `~`, also „jede Marke, ueber der
       eine andere Marke steht". Das ging schief, sobald eine Marke
       verschwindet: im Data-Modus blendet `refreshDataModeUi()` `Modi` aus,
       `Kontext` blieb ihr Geschwister und behielt die Linie — sie lag dann
       ganz oben im Menue und trennte nichts. Mit `:first-child` haengt die
       Linie an der Stellung IM MENUE, nicht an einem Geschwister, das
       unsichtbar sein kann. Seit B.4 steht ohnehin der Anhang-Eintrag oben:
       `Modi` traegt die Linie jetzt selbst. */
    .extras-menu-section-label:not(:first-child) {
        margin-block-start: var(--space-1);
        padding-block-start: var(--space-3);
        border-top: 1px solid var(--line-subtle);
    }

    /* §3: Zeilenhoehe 32 px. `--row-h` steht auf 40 px und gilt fuer
       Tabellen- und Listenzeilen; eine Menuezeile ist enger. */
    .extras-menu-item,
    .composer-mode-wrap .dropdown-item {
        position: relative;
        display: flex;
        align-items: center;
        gap: var(--space-2);
        height: 32px;
        padding-inline: var(--space-3);
        border: none;
        border-radius: var(--radius-control);
        color: var(--ink-primary);
        font-family: var(--font-ui);
        font-size: var(--text-sm);
        font-weight: var(--font-weight-regular);
        letter-spacing: normal;
        cursor: pointer;
        transition:
            background-color var(--dur-instant) var(--ease-standard),
            color var(--dur-instant) var(--ease-standard);
    }

    @media (hover: hover) and (pointer: fine) {
        .extras-menu-item:hover,
        .composer-mode-wrap .dropdown-item:hover {
            background: var(--surface-hover);
            color: var(--ink-primary);
        }
    }

    .extras-menu-item.is-active,
    .composer-mode-wrap .dropdown-item.selected {
        background: var(--surface-selected);
        color: var(--ink-primary);
    }

    .extras-menu-text {
        display: flex;
        flex: 1;
        min-width: 0;
        align-items: center;
        gap: var(--space-2);
        white-space: nowrap;
    }

    /* §3: Der Zustand steht rechtsbuendig als Wort — `Aus` gedaempft, `An`
       hell. Vorher war er ein gefuellter Chip; ein Chip liest sich wie ein
       Knopf, hier steht aber nur eine Auskunft. Kein Gruen (R2). */
    .extras-menu-hint {
        display: inline-flex;
        flex-shrink: 0;
        align-items: center;
        gap: var(--space-1);
        margin-inline-start: auto;
        padding: 0;
        border-radius: 0;
        background: transparent;
        color: var(--ink-muted);
        font-family: var(--font-ui);
        font-size: var(--text-xs);
        font-weight: var(--font-weight-regular);
        letter-spacing: normal;
        text-transform: none;
        white-space: nowrap;
        transition: color var(--dur-instant) var(--ease-standard);
    }

    .extras-menu-item.is-active .extras-menu-hint {
        background: transparent;
        color: var(--ink-primary);
    }

    /* Das Haekchen steht als eigenes Element im Markup — Phosphor hat das
       Motiv, also wird es genommen (STIL.md §5). Es sitzt NEBEN der Wortzelle,
       nicht darin: `status.textContent = 'An'` im JavaScript wuerde ein Kind
       der Zelle wegschreiben. Sichtbar nur im aktiven Zustand. */
    .extras-menu-check {
        flex-shrink: 0;
        color: var(--ink-primary);
        font-size: var(--icon-sm);
        line-height: 1;
        opacity: 0;
        transition: opacity var(--dur-fast) var(--ease-out);
    }

    .extras-menu-item.is-active .extras-menu-check {
        opacity: 1;
    }

    .composer-mode-wrap {
        min-width: 140px;
    }

    .composer-mode-wrap .custom-dropdown {
        width: 100%;
    }

    .composer-mode-wrap .dropdown-btn {
        min-width: 140px;
        justify-content: flex-start;
    }

    .composer-mode-wrap .dropdown-btn > i:last-child {
        margin-inline-start: auto;
        opacity: 0.5;
    }

    .composer-mode-wrap .dropdown-content {
        min-width: 180px;
    }

    .composer-mode-wrap .dropdown-item span {
        line-height: 1;
    }

    .composer-mode-wrap .dropdown-item .dropdown-item-meta {
        margin-inline-start: auto;
        color: var(--ink-muted);
        font-size: var(--text-2xs);
        letter-spacing: normal;
        white-space: nowrap;
    }

    /* Die Slash-Liste sitzt direkt ueber dem Feld und wirkt wie seine
       Fortsetzung nach oben. */
    .slash-popup {
        position: absolute;
        left: 0;
        right: 0;
        bottom: calc(100% + var(--space-1));
        z-index: var(--z-dropdown);
        max-height: 260px;
        padding: var(--space-1);
        border-radius: var(--radius-panel);
        background: var(--surface-overlay);
        overflow-y: auto;
        opacity: 1;
        visibility: visible;
        pointer-events: auto;
        transform: scale(1);
        transform-origin: bottom center;
        scrollbar-width: thin;
        scrollbar-color: var(--line-strong) transparent;
        scrollbar-gutter: stable;
        transition:
            opacity var(--dur-fast) var(--ease-out),
            transform var(--dur-base) var(--ease-out);
    }

    .slash-popup::-webkit-scrollbar {
        width: 8px;
    }

    .slash-popup::-webkit-scrollbar-track,
    .slash-popup::-webkit-scrollbar-corner {
        background: transparent;
    }

    .slash-popup::-webkit-scrollbar-thumb {
        border-radius: var(--radius-chip);
        background: var(--line-strong);
    }

    .slash-popup.hidden {
        opacity: 0;
        visibility: hidden;
        pointer-events: none;
        transform: scale(0.97);
    }

    .slash-cmd {
        display: flex;
        align-items: center;
        gap: var(--space-3);
        min-height: var(--row-h);
        padding-block: var(--space-2);
        padding-inline: var(--space-3);
        border-radius: var(--radius-control);
        cursor: pointer;
        transition:
            background-color var(--dur-instant) var(--ease-standard),
            color var(--dur-instant) var(--ease-standard);
    }

    .slash-cmd.selected {
        background: var(--surface-selected);
    }

    @media (hover: hover) and (pointer: fine) {
        .slash-cmd:hover {
            background: var(--surface-hover);
        }
    }

    .slash-cmd-icon {
        width: 22px;
        color: var(--ink-secondary);
        font-size: var(--icon-sm);
        line-height: 1;
        text-align: center;
    }

    .slash-cmd-meta {
        display: flex;
        flex-direction: column;
        min-width: 0;
        gap: 1px;
    }

    .slash-cmd-name {
        color: var(--ink-primary);
        font-family: var(--font-data);
        font-size: var(--text-xs);
        font-weight: var(--font-weight-medium);
        letter-spacing: normal;
        line-height: 1.3;
    }

    .slash-cmd-desc {
        color: var(--ink-secondary);
        font-size: var(--text-xs);
        line-height: 1.35;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .composer-input-shell.is-slash-open {
        background: transparent;
    }
}

@layer channels {

    /* ====================================================================
       8 · Anhaenge und Bildvorschau

       Dazu die `.msg-*`-Klassen, die bis Gruppe 2 unbeschichtet in
       `theme.css` standen. Sie sind hier, weil sie sonst jede
       @layer-Regel schlagen — dieselbe Begruendung wie in Gruppe 1.
       ==================================================================== */

    .msg-avatar {
        display: flex;
        flex-shrink: 0;
        align-items: center;
        justify-content: center;
        width: 32px;
        height: 32px;
        margin-inline-end: var(--space-3);
        border-radius: var(--radius-chip);
        background: var(--surface-raised);
        color: var(--ink-secondary);
        font-size: var(--text-2xs);
        font-weight: var(--font-weight-medium);
    }

    .chat-message.same-author .msg-avatar {
        visibility: hidden;
    }

    .msg-content {
        flex: 1;
        min-width: 0;
    }

    .msg-header {
        display: flex;
        align-items: baseline;
        gap: var(--space-2);
        margin-block-end: var(--space-1);
    }

    /* Weder Bot noch Nutzer bekommen eine Namensfarbe. Vorher war der Bot
       smaragdgruen und der Nutzer indigo — beides Markenfarbe an einer
       Beschriftung und damit gegen R2. */
    .msg-author,
    .msg-author.bot,
    .msg-author.user {
        color: var(--ink-secondary);
        font-size: var(--text-sm);
        font-weight: var(--font-weight-medium);
    }

    .msg-timestamp {
        color: var(--ink-muted);
        font-family: var(--font-data);
        font-size: var(--text-2xs);
        font-variant-numeric: tabular-nums;
    }

    .msg-body {
        color: var(--ink-primary);
        font-family: var(--font-ui);
        font-size: var(--text-base);
        line-height: var(--line-height-prose);
        white-space: pre-wrap;
        overflow-wrap: break-word;
    }

    .msg-body strong {
        font-weight: var(--font-weight-semibold);
    }

    .msg-body code {
        padding-block: 2px;
        padding-inline: var(--space-1);
        border-radius: var(--radius-chip);
        background: var(--surface-raised);
        font-family: var(--font-data);
        font-size: var(--text-xs);
    }

    .btn-loading {
        pointer-events: none;
        opacity: 0.7;
    }

    /* Die Anhangsleiste. Sie klappte ueber `max-height`, `margin` und
       `padding` auf — drei Layout-Eigenschaften in einer Bewegung
       (STIL.md §6). Jetzt schaltet die Hoehe hart und der Inhalt blendet
       auf. */
    .chat-attachment-tray {
        display: none;
        flex-wrap: wrap;
        gap: var(--space-2);
        opacity: 0;
        pointer-events: none;
        transition: opacity var(--dur-base) var(--ease-out);
    }

    .ios-composer.has-attachments .chat-attachment-tray {
        display: flex;
        margin-block-end: var(--space-3);
        padding: var(--space-2);
        opacity: 1;
        pointer-events: auto;
    }

    .chat-attachment-card {
        position: relative;
        width: 88px;
        height: 88px;
        border-radius: var(--radius-control);
        background: var(--surface-panel);
        color: var(--ink-primary);
        overflow: hidden;
        isolation: isolate;
    }

    .chat-attachment-preview {
        width: 100%;
        height: 100%;
        padding: 0;
        border: 0;
        background: transparent;
        cursor: zoom-in;
    }

    .chat-attachment-preview img {
        display: block;
        width: 100%;
        height: 100%;
        object-fit: cover;
        transition: transform var(--dur-base) var(--ease-out);
    }

    @media (hover: hover) and (pointer: fine) {
        .chat-attachment-card:hover .chat-attachment-preview img,
        .chat-attachment-card:focus-within .chat-attachment-preview img {
            transform: scale(1.04);
        }
    }

    .chat-attachment-file {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        gap: var(--space-2);
        height: 100%;
        padding: var(--space-3);
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        font-weight: var(--font-weight-regular);
        line-height: 1.2;
        text-align: center;
    }

    .chat-attachment-file i {
        color: var(--ink-muted);
        font-size: var(--icon-xl);
    }

    .chat-attachment-file span {
        max-width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .chat-attachment-remove {
        position: absolute;
        z-index: var(--z-local);
        top: var(--space-1);
        left: var(--space-1);
        display: inline-grid;
        place-items: center;
        width: 22px;
        height: 22px;
        padding: 0;
        border: none;
        border-radius: var(--radius-chip);
        background: var(--surface-overlay);
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        line-height: 1;
        cursor: pointer;
        opacity: 0;
        transition:
            opacity var(--dur-fast) var(--ease-out),
            color var(--dur-fast) var(--ease-out);
    }

    .chat-attachment-card:hover .chat-attachment-remove,
    .chat-attachment-card:focus-within .chat-attachment-remove {
        opacity: 1;
    }

    @media (hover: hover) and (pointer: fine) {
        .chat-attachment-remove:hover {
            color: var(--signal-down);
        }
    }

    .chat-image-preview {
        position: fixed;
        z-index: var(--z-modal);
        inset: 0;
        display: grid;
        place-items: center;
        padding: var(--space-6);
        background: var(--surface-canvas);
        opacity: 0;
        pointer-events: none;
        transition: opacity var(--dur-base) var(--ease-out);
    }

    .chat-image-preview.is-open {
        opacity: 1;
        pointer-events: auto;
    }

    .chat-image-preview img {
        max-width: min(72vw, 760px);
        max-height: 72vh;
        object-fit: contain;
        border-radius: var(--radius-panel);
        cursor: zoom-out;
        transform: scale(0.97);
        transition: transform var(--dur-base) var(--ease-out);
    }

    .chat-image-preview.is-open img {
        transform: scale(1);
    }

    /* ====================================================================
       9 · Earnings und Reaktionstabelle

       Der Analyseknopf war blau gefuellt und trug weisse Schrift; der
       Chip wurde beim Ueberfahren gelb. Beides Farbe an einem
       Bedienelement ohne Finanzbedeutung (R2). Die Zahlen in der Tabelle
       behalten ihre Signalfarbe — dort steht Geld.
       ==================================================================== */

    .earnings-chip {
        display: inline-flex;
        align-items: center;
        gap: var(--space-1);
        padding-block: var(--space-1);
        padding-inline: var(--space-2);
        border-radius: var(--radius-chip);
        background: var(--accent-subtle);
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        letter-spacing: normal;
        text-transform: none;
        cursor: pointer;
        transition:
            background-color var(--dur-instant) var(--ease-standard),
            color var(--dur-instant) var(--ease-standard),
            transform var(--dur-fast) var(--ease-out);
    }

    @media (hover: hover) and (pointer: fine) {
        .earnings-chip:hover {
            background: var(--surface-hover);
            color: var(--ink-primary);
        }
    }

    .earnings-chip:active {
        transform: scale(0.97);
    }

    .earnings-chip.active {
        background: var(--surface-selected);
        color: var(--ink-primary);
    }

    .earnings-analyze-btn {
        display: inline-flex;
        align-items: center;
        gap: var(--space-2);
        padding-block: var(--space-2);
        padding-inline: var(--space-5);
        border: none;
        border-radius: var(--radius-control);
        background: var(--accent);
        color: var(--accent-ink);
        font-family: var(--font-ui);
        font-size: var(--text-sm);
        font-weight: var(--font-weight-medium);
        cursor: pointer;
        transition:
            background-color var(--dur-instant) var(--ease-standard),
            transform var(--dur-fast) var(--ease-out);
    }

    @media (hover: hover) and (pointer: fine) {
        .earnings-analyze-btn:hover {
            background: var(--accent-hover);
        }
    }

    .earnings-analyze-btn:active {
        transform: scale(0.97);
    }

    .earnings-analyze-btn:disabled {
        opacity: 0.4;
        cursor: not-allowed;
    }

    .reaction-table-wrap {
        margin-block: var(--space-3);
        border-radius: var(--radius-control);
        background: var(--surface-raised);
        overflow: hidden;
    }

    .reaction-table-header {
        display: flex;
        align-items: baseline;
        gap: var(--space-2);
        padding-block: var(--space-2);
        padding-inline: var(--space-3);
        border-bottom: 1px solid var(--line);
    }

    .reaction-ticker {
        color: var(--ink-primary);
        font-family: var(--font-data);
        font-size: var(--text-sm);
        font-weight: var(--font-weight-medium);
    }

    .reaction-name {
        color: var(--ink-muted);
        font-size: var(--text-2xs);
    }

    .reaction-table {
        width: 100%;
        border-collapse: collapse;
        font-family: var(--font-data);
        font-size: var(--text-xs);
    }

    .reaction-table th {
        padding-block: var(--space-2);
        padding-inline: var(--space-3);
        border-bottom: 1px solid var(--line);
        color: var(--ink-muted);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        font-weight: var(--font-weight-regular);
        text-align: left;
    }

    .reaction-table td {
        padding-block: var(--space-2);
        padding-inline: var(--space-3);
        border-bottom: 1px solid var(--line-subtle);
        color: var(--ink-primary);
        font-variant-numeric: tabular-nums;
    }

    .reaction-table tbody tr:last-child td {
        border-bottom: none;
    }

    @media (hover: hover) and (pointer: fine) {
        .reaction-table tbody tr:hover td {
            background: var(--surface-hover);
        }
    }

    .reaction-date-main {
        font-weight: var(--font-weight-medium);
    }

    .reaction-date-badge {
        display: inline-flex;
        align-items: center;
        margin-block-start: var(--space-1);
        padding-block: 2px;
        padding-inline: var(--space-1);
        border-radius: var(--radius-chip);
        background: var(--accent-subtle);
        color: var(--ink-muted);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        line-height: 1.3;
    }

    .reaction-date-badge.confidence-high {
        background: var(--signal-up-surface);
        color: var(--signal-up);
    }

    .reaction-date-badge.confidence-medium {
        background: var(--signal-warn-surface);
        color: var(--signal-warn);
    }

    .reaction-date-badge.confidence-low {
        background: var(--signal-down-surface);
        color: var(--signal-down);
    }

    .reaction-table td.beat,
    .reaction-table td.price-reaction.positive,
    .reaction-table td.peak-reaction.positive {
        color: var(--signal-up);
        font-weight: var(--font-weight-medium);
    }

    .reaction-table td.miss,
    .reaction-table td.price-reaction.negative,
    .reaction-table td.peak-reaction.negative {
        color: var(--signal-down);
        font-weight: var(--font-weight-medium);
    }

    .reaction-summary {
        margin-block: var(--space-3) var(--space-5);
        padding-block: var(--space-3);
        padding-inline: var(--space-3);
        border-radius: var(--radius-control);
        background: var(--surface-raised);
        color: var(--ink-primary);
        font-size: var(--text-xs);
        line-height: 1.55;
    }
}

@layer channels {

    /* ====================================================================
       10 · Data-Modus — D-2, §3.4

       Vorher standen sechs Dinge gleichrangig untereinander: Kopfzeile,
       Einordnungs-Chip, Freitext, Tabelle, AI-Einschaetzung und vier
       Absaetze Fussnoten. Nichts sagte, was zusammengehoert.

       Jetzt drei Bloecke mit drei verschiedenen Gewichten:
         1 · Was gefragt und was gerechnet wurde — eine Flaeche,
             Definitionsliste, der Chip in derselben Zeile wie die Frage
         2 · Das Ergebnis — steht frei, die Tabelle traegt ihre eigene
             Flaeche, die AI-Einschaetzung steht direkt darunter im
             selben Block
         3 · Vorbehalte — eingeklappt, eine Zeile Zusammenfassung

       Der Aufbau der Tabelle selbst ist unveraendert. Sie bekommt die
       Tokens, sonst nichts.
       ==================================================================== */

    /* `.v2-switch` und `.v2-switch-btn` stehen seit Gruppe 2b in `shell.css`
       (Abschnitt 2a) — siehe oben. */

    .v2-answer {
        display: flex;
        flex-direction: column;
        gap: var(--space-5);
        max-width: 100%;
        margin-block: var(--space-1) var(--space-1);
    }

    .v2-block {
        display: flex;
        flex-direction: column;
        gap: var(--space-3);
        min-width: 0;
    }

    /* Block 1. Die einzige Flaeche der Antwort — deshalb traegt nichts
       darin eine zweite (R3: keine verschachtelten Karten). Der
       Farbstreifen links (`border-left: 2px solid var(--accent)`) ist
       weg, §7. */
    .v2-block-frage {
        padding-block: var(--space-3);
        padding-inline: var(--space-4);
        border-radius: var(--radius-panel);
        background: var(--surface-raised);
    }

    .v2-head {
        display: grid;
        grid-template-columns: minmax(0, 8rem) minmax(0, 1fr);
        gap: var(--space-2) var(--space-4);
        margin: 0;
    }

    .v2-head-row {
        display: contents;
    }

    .v2-head-label {
        margin: 0;
        color: var(--ink-muted);
        font-family: var(--font-ui);
        font-size: var(--text-xs);
        font-weight: var(--font-weight-regular);
        letter-spacing: normal;
        text-transform: none;
    }

    .v2-head-value {
        display: flex;
        flex-wrap: wrap;
        align-items: baseline;
        gap: var(--space-2);
        margin: 0;
        min-width: 0;
        color: var(--ink-primary);
        font-family: var(--font-ui);
        font-size: var(--text-sm);
        line-height: 1.5;
        overflow-wrap: anywhere;
    }

    /* Zahlen und Kennungen in der Kopfzeile stehen in der Datenschrift —
       STIL.md §4. */
    .v2-head-value .v2-head-data {
        font-family: var(--font-data);
        font-variant-numeric: tabular-nums;
    }

    /* Der Einordnungs-Chip sitzt in derselben Zeile wie die Frage, nicht
       darunter — §3.4. */
    .v2-tags {
        display: inline-flex;
        flex-wrap: wrap;
        align-items: center;
        gap: var(--space-1);
        /* Der Sammelbehaelter ist selbst kein Chip. Die allgemeine Regel
           `[class*="-tag"]` in components.css trifft ihn wegen des Namens
           mit und legte einen zweiten Kasten hinter die Etiketten. */
        padding: 0;
        border: 0;
        border-radius: 0;
        background: none;
        min-height: 0;
    }

    .v2-tag {
        display: inline-flex;
        align-items: center;
        padding-block: 2px;
        padding-inline: var(--space-2);
        border-radius: var(--radius-chip);
        background: var(--accent-subtle);
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        font-weight: var(--font-weight-regular);
        letter-spacing: normal;
        text-transform: none;
        white-space: nowrap;
    }

    .v2-tag-secured {
        background: var(--signal-up-surface);
        color: var(--signal-up);
    }

    .v2-tag-explorative {
        background: var(--accent-subtle);
        color: var(--ink-secondary);
    }

    .v2-tag-warn,
    .v2-tag-stale {
        background: var(--signal-warn-surface);
        color: var(--signal-warn);
    }

    .v2-tag-quiet {
        background: transparent;
        color: var(--ink-muted);
    }

    .v2-robust {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: var(--space-2);
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-xs);
    }

    .v2-robust-windows {
        color: var(--ink-muted);
    }

    /* Die Tabelle. Aufbau unveraendert — Spalten, Reihenfolge, Inhalt
       stehen wie vorher. Nur die Farben, die Schrift und die Abstaende
       kommen jetzt aus Tokens. */
    .v2-table-scroll {
        max-width: 100%;
        border-radius: var(--radius-control);
        background: var(--surface-raised);
        overflow-x: auto;
        scrollbar-width: thin;
        scrollbar-color: var(--line-strong) transparent;
    }

    .v2-table {
        width: 100%;
        border-collapse: collapse;
        font-family: var(--font-data);
        font-size: var(--text-xs);
        white-space: nowrap;
    }

    .v2-table-caption {
        caption-side: top;
        padding-block: var(--space-2);
        padding-inline: var(--space-3);
        border-bottom: 1px solid var(--line);
        color: var(--ink-muted);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        text-align: left;
        white-space: normal;
    }

    .v2-table th {
        position: sticky;
        top: 0;
        padding-block: var(--space-2);
        padding-inline: var(--space-3);
        border-bottom: 1px solid var(--line);
        background: var(--surface-selected);
        color: var(--ink-muted);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        font-weight: var(--font-weight-regular);
        letter-spacing: normal;
        text-transform: none;
        text-align: right;
    }

    .v2-table th:first-child { text-align: left; }

    .v2-table td {
        padding-block: var(--space-2);
        padding-inline: var(--space-3);
        border-bottom: 1px solid var(--line-subtle);
        color: var(--ink-primary);
    }

    .v2-table tbody tr:last-child td { border-bottom: none; }

    @media (hover: hover) and (pointer: fine) {
        .v2-table tbody tr:hover td { background: var(--surface-hover); }
    }

    .v2-cell-label {
        display: flex;
        align-items: baseline;
        gap: var(--space-2);
        font-family: var(--font-ui);
        text-align: left;
    }

    .v2-cell-num {
        text-align: right;
        font-variant-numeric: tabular-nums;
    }

    .v2-cell-ci {
        color: var(--ink-secondary);
        text-align: right;
        font-variant-numeric: tabular-nums;
    }

    .v2-cell-status {
        color: var(--signal-warn);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        text-align: right;
    }

    .v2-bucket-index { color: var(--ink-primary); }
    .v2-bucket-clock { color: var(--ink-muted); font-size: var(--text-2xs); }
    .v2-bucket-note  { color: var(--ink-secondary); font-size: var(--text-2xs); }

    .v2-row-undecided td { background: var(--surface-panel); }

    .v2-chart {
        margin: 0;
        padding-block: var(--space-3) var(--space-2);
        padding-inline: var(--space-3);
        border-radius: var(--radius-control);
        background: var(--surface-raised);
    }

    .v2-chart-title {
        margin-block-end: var(--space-2);
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
    }

    .v2-chart-svg { display: block; width: 100%; height: 240px; }
    .v2-chart-grid { stroke: var(--line); stroke-width: 1; }
    .v2-chart-bar { fill: var(--ink-secondary); }
    .v2-chart-bar:hover { fill: var(--ink-primary); }
    .v2-chart-whisker { stroke: var(--ink-muted); stroke-width: 1.5; }
    .v2-chart-bar-unknown { fill-opacity: 0.5; stroke: var(--line-strong); }
    .v2-chart-hatch-line { stroke: var(--ink-muted); stroke-width: 2; }

    .v2-chart-axis,
    .v2-chart-axis-title {
        fill: var(--ink-muted);
        font-family: var(--font-data);
        font-size: 10px;
    }

    .v2-chart-unknown-label {
        fill: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: 10px;
    }

    .v2-chart-note {
        margin-block-start: var(--space-2);
        color: var(--ink-muted);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        line-height: 1.5;
    }

    /* Warnungen bleiben sichtbar und bleiben unterscheidbar. Sie wandern
       NICHT in die eingeklappten Vorbehalte — eine Sperre, die man
       aufklappen muss, ist keine Sperre. Der 3-px-Farbstreifen links ist
       durch die Flaeche ersetzt (§7). */
    .v2-warn {
        display: flex;
        flex-direction: column;
        gap: var(--space-1);
        padding-block: var(--space-2);
        padding-inline: var(--space-3);
        border-radius: var(--radius-control);
    }

    .v2-warn-orange {
        background: var(--signal-warn-surface);
    }

    .v2-warn-red {
        background: var(--signal-down-surface);
    }

    .v2-warn-head {
        display: flex;
        align-items: center;
        gap: var(--space-2);
    }

    .v2-warn-code {
        padding-block: 1px;
        padding-inline: var(--space-1);
        border-radius: var(--radius-chip);
        font-family: var(--font-data);
        font-size: var(--text-2xs);
        font-weight: var(--font-weight-medium);
        letter-spacing: normal;
    }

    .v2-warn-orange .v2-warn-code { color: var(--signal-warn); }
    .v2-warn-red .v2-warn-code { color: var(--signal-down); }

    .v2-warn-kind {
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        letter-spacing: normal;
        text-transform: none;
    }

    .v2-warn-text {
        color: var(--ink-primary);
        font-family: var(--font-ui);
        font-size: var(--text-sm);
        line-height: 1.55;
    }

    .v2-warn-quiet {
        color: var(--ink-muted);
        font-size: var(--text-xs);
    }

    /* Die AI-Einschaetzung gehoert zum Ergebnis und steht direkt darunter
       — kein eigener Kasten mehr (§3.4). Eine Haarlinie trennt sie von
       der Tabelle, weil hier eine Flaeche die Ordnung nicht traegt: sie
       laege sonst in der Flaeche der Tabelle. */
    .v2-prose {
        padding-block-start: var(--space-3);
        border-top: 1px solid var(--line-subtle);
    }

    .v2-prose-label {
        margin-block-end: var(--space-1);
        color: var(--ink-muted);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        font-weight: var(--font-weight-regular);
        letter-spacing: normal;
        text-transform: none;
    }

    .v2-prose-body {
        color: var(--ink-primary);
        font-family: var(--font-ui);
        font-size: var(--text-sm);
        line-height: var(--line-height-prose);
    }

    .v2-prose-body p:first-child { margin-block-start: 0; }
    .v2-prose-body p:last-child { margin-block-end: 0; }

    /* Block 3. Eingeklappt, mit einer Zeile Zusammenfassung — F5. Sie
       verschwinden nicht, sie draengen sich nur nicht mehr vor das
       Ergebnis. */
    .v2-vorbehalte {
        border-top: 1px solid var(--line-subtle);
    }

    .v2-vorbehalte-summary {
        display: flex;
        align-items: center;
        gap: var(--space-2);
        padding-block: var(--space-2);
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-xs);
        list-style: none;
        cursor: pointer;
        transition: color var(--dur-instant) var(--ease-standard);
    }

    .v2-vorbehalte-summary::-webkit-details-marker { display: none; }

    @media (hover: hover) and (pointer: fine) {
        .v2-vorbehalte-summary:hover { color: var(--ink-primary); }
    }

    .v2-vorbehalte-chevron {
        display: inline-flex;
        flex-shrink: 0;
        transform: rotate(-90deg);
        transition: transform var(--dur-fast) var(--ease-standard);
    }

    .v2-vorbehalte[open] .v2-vorbehalte-chevron {
        transform: rotate(0deg);
    }

    .v2-balance {
        display: flex;
        flex-direction: column;
        gap: var(--space-2);
        padding-block-end: var(--space-3);
    }

    .v2-balance-line {
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-xs);
    }

    .v2-balance-note {
        color: var(--ink-muted);
        font-family: var(--font-ui);
        font-size: var(--text-xs);
        line-height: 1.55;
    }

    .v2-empty {
        padding-block: var(--space-3);
        padding-inline: var(--space-4);
        border-radius: var(--radius-panel);
        background: var(--surface-raised);
    }

    /* Ein fehlendes Artefakt ist eine Stoerung, keine ungestellte Frage.
       Der Unterschied wird jetzt ueber die Flaeche getragen statt ueber
       einen Farbstreifen. */
    .v2-fault {
        padding-block: var(--space-3);
        padding-inline: var(--space-4);
        border-radius: var(--radius-panel);
        background: var(--signal-down-surface);
    }

    .v2-fault .v2-empty-head { color: var(--signal-down); }

    .v2-empty-head {
        margin-block-end: var(--space-1);
        color: var(--signal-warn);
        font-family: var(--font-ui);
        font-size: var(--text-sm);
        font-weight: var(--font-weight-medium);
        letter-spacing: normal;
        text-transform: none;
    }

    .v2-empty-body {
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-sm);
        line-height: 1.55;
    }

    /* ====================================================================
       11 · Glossar — D-3, §4.2

       Das Fachwort behaelt seinen Namen. Daneben steht ein Erklaerzeichen,
       die Erklaerung selbst kommt aus dem Tooltip in `components.css`.
       Der Tooltip ersetzt das Wort nicht, er steht daneben.

       Wie oft das Zeichen erscheint, entscheidet `l4.glossar.umfang` im
       localStorage: `erstes` (Vorgabe) nur beim ersten Vorkommen je
       Antwort, `alle` an jedem. Den Schalter dazu setzt Gruppe 5.
       ==================================================================== */

    .glossar-term {
        position: relative;
        /* Wort und Erklaerzeichen bleiben zusammen. Ohne das brach das `?`
           an einem Zeilenende vom Wort ab und stand allein am Anfang der
           naechsten Zeile — gemessen im Data-Modus. */
        white-space: nowrap;
        cursor: help;
    }

    /* Der Erklaertext selbst darf umbrechen; nur das Paar Wort + Zeichen
       nicht. Sonst laeuft die Sprechblase in einer Zeile aus dem Bild. */
    .glossar-term .glossar-tip {
        white-space: normal;
    }

    .glossar-mark {
        display: inline-block;
        margin-inline-start: 2px;
        color: var(--ink-muted);
        font-family: var(--font-ui);
        font-size: 0.8em;
        font-weight: var(--font-weight-regular);
        vertical-align: super;
        line-height: 1;
        user-select: none;
        transition: color var(--dur-instant) var(--ease-standard);
    }

    @media (hover: hover) and (pointer: fine) {
        .glossar-term:hover .glossar-mark { color: var(--ink-secondary); }
    }

    .glossar-tip {
        position: absolute;
        z-index: var(--z-tooltip);
        bottom: calc(100% + var(--space-1));
        left: 0;
        display: none;
        inline-size: max-content;
        max-inline-size: 20rem;
        padding-block: var(--space-1);
        padding-inline: var(--space-2);
        border-radius: var(--radius-chip);
        background: var(--surface-overlay);
        color: var(--ink-primary);
        font-family: var(--font-ui);
        font-size: var(--text-xs);
        font-weight: var(--font-weight-regular);
        line-height: 1.4;
        white-space: normal;
        text-align: start;
        transform-origin: bottom left;
    }

    .glossar-term:hover .glossar-tip,
    .glossar-term:focus-visible .glossar-tip {
        display: block;
    }

    /* ====================================================================
       12 · Enge Fenster
       ==================================================================== */

    @media (max-width: 860px) {
        .chat-messages {
            padding: var(--space-3);
        }

        .composer-mode-wrap,
        .composer-mode-wrap .dropdown-btn {
            min-width: 120px;
        }
    }

    @media (max-width: 680px) {
        .hotbar-status-line {
            flex-wrap: wrap;
            align-items: flex-start;
            row-gap: var(--space-1);
        }

        .hotbar-status-modes {
            width: 100%;
            flex-wrap: wrap;
            gap: var(--space-2);
        }

        .system-help-item {
            grid-template-columns: 18px minmax(0, 1fr);
            row-gap: var(--space-1);
        }

        .system-help-command { grid-column: 2; }

        .system-help-desc {
            grid-column: 1 / -1;
            padding-inline-start: 28px;
            white-space: normal;
        }

        .v2-head {
            grid-template-columns: minmax(0, 1fr);
            gap: var(--space-1) 0;
        }

        .v2-head-row + .v2-head-row .v2-head-label {
            margin-block-start: var(--space-2);
        }
    }
}

@layer channels {

    /* ====================================================================
       13 · Was das JavaScript baut

       Diese Bloecke entstehen zur Laufzeit in `ai_chat.html`. Sie trugen
       ihre Farben als rohe `style="…"`-Zeichenketten im JavaScript —
       201 hartkodierte Werte, die weder `tokens.css` noch diese Datei je
       erreicht haetten (STIL.md §7, letzter Punkt).
       ==================================================================== */

    /* `.chat-mode-switch-row` und `.v2-switch` sind in Gruppe 2b nach
       `shell.css` gezogen (Abschnitt 2a) — der Umschalter steht jetzt in der
       linken Leiste, nicht mehr in der Inhaltsflaeche des Kanals
       (STEP_02B_POSITIONEN.md §4). Die Klassennamen sind unveraendert. */

    .stream-error {
        color: var(--signal-down);
    }

    .response-content {
        color: var(--ink-primary);
        font-family: var(--font-ui);
        font-size: var(--text-base);
        line-height: var(--line-height-prose);
    }

    /* Tabelle aus Markdown. Aufbau unveraendert; die Zebrastreifen sind
       weg, weil die Zeilentrennung ueber eine Haarlinie laeuft — eine
       Flaeche je Zeile waere eine zweite Ebene ohne Grund (R1). */
    .table-container {
        margin-block: var(--space-4);
        border-radius: var(--radius-control);
        background: var(--surface-raised);
        overflow-x: auto;
    }

    .chat-table {
        width: 100%;
        border-collapse: collapse;
        font-family: var(--font-data);
        font-size: var(--text-xs);
    }

    .chat-table th {
        padding-block: var(--space-2);
        padding-inline: var(--space-3);
        border-bottom: 1px solid var(--line);
        color: var(--ink-muted);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        font-weight: var(--font-weight-regular);
        text-align: left;
    }

    .chat-table td {
        padding-block: var(--space-2);
        padding-inline: var(--space-3);
        border-bottom: 1px solid var(--line-subtle);
        color: var(--ink-primary);
        font-variant-numeric: tabular-nums;
    }

    .chat-table tbody tr:last-child td {
        border-bottom: none;
    }

    /* BEAT und MISS sind Finanzbefunde — sie duerfen Farbe tragen (R2). */
    .chat-table td.chat-table-up { color: var(--signal-up); }
    .chat-table td.chat-table-down { color: var(--signal-down); }

    /* Der Denksatz, wie ihn der Stream baut: Text + Sekunden + Pfeil. */
    .thinking-timer {
        font-variant-numeric: tabular-nums;
    }

    /* Quellen — die einfache Liste am Ende einer Antwort. Der blaue
       Verweis (`#8BA4FF`) ist weg; ein Verweis wird durch die
       Unterstreichung erkennbar, nicht durch eine Markenfarbe. */
    .source-fallback {
        margin-block-start: var(--space-4);
        padding-block-start: var(--space-3);
        border-top: 1px solid var(--line-subtle);
    }

    .source-fallback-label {
        margin-block-end: var(--space-1);
        color: var(--ink-muted);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        letter-spacing: normal;
        text-transform: none;
    }

    .source-link {
        display: block;
        margin-block: var(--space-1);
        color: var(--ink-secondary);
        font-size: var(--text-xs);
        text-decoration: none;
        transition: color var(--dur-instant) var(--ease-standard);
    }

    @media (hover: hover) and (pointer: fine) {
        .source-link:hover {
            color: var(--ink-primary);
            text-decoration: underline;
        }
    }

    .source-link-date {
        color: var(--ink-muted);
    }

    /* Quellen — die Sprechblase. Sie waechst aus ihrem Ausloeser
       (`transform-origin: bottom left`) und liegt auf der Menuestufe. */
    .sources-footer {
        position: relative;
        display: flex;
        align-items: center;
        gap: var(--space-3);
        margin-block-start: var(--space-5);
        padding-block-start: var(--space-4);
        border-top: 1px solid var(--line-subtle);
    }

    .sources-toggle {
        display: flex;
        align-items: center;
        gap: var(--space-1);
        padding-block: var(--space-1);
        padding-inline: var(--space-2);
        border: none;
        border-radius: var(--radius-chip);
        background: transparent;
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-xs);
        font-weight: var(--font-weight-regular);
        letter-spacing: normal;
        text-transform: none;
        cursor: pointer;
        transition:
            background-color var(--dur-instant) var(--ease-standard),
            color var(--dur-instant) var(--ease-standard),
            transform var(--dur-fast) var(--ease-out);
    }

    @media (hover: hover) and (pointer: fine) {
        .sources-toggle:hover {
            background: var(--surface-raised);
            color: var(--ink-primary);
        }
    }

    .sources-toggle:active {
        transform: scale(0.97);
    }

    .sources-toggle .chevron-icon {
        font-size: var(--icon-sm);
        transition: transform var(--dur-fast) var(--ease-standard);
    }

    .sources-footer.is-open .chevron-icon {
        transform: rotate(180deg);
    }

    .sources-popup {
        position: absolute;
        container-type: inline-size;
        bottom: calc(100% + var(--space-2));
        left: 0;
        z-index: var(--z-dropdown);
        width: min(500px, 100%);
        padding: var(--space-4);
        border-radius: var(--radius-panel);
        background: var(--surface-overlay);
    }

    .sources-popup.opens-below {
        top: calc(100% + var(--space-2));
        bottom: auto;
        transform-origin: top left;
    }

    .sources-popup-head {
        display: flex;
        align-items: center;
        gap: var(--space-2);
        margin-block-end: var(--space-3);
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-sm);
        font-weight: var(--font-weight-medium);
    }

    .sources-popup-head i {
        font-size: var(--icon-md);
    }

    /* Kennzeichnung fuer den Notfallblock: gefunden, aber nicht zitiert. */
    .sources-note {
        padding-block: 1px;
        padding-inline: var(--space-2);
        border: 1px solid var(--line-soft);
        border-radius: var(--radius-chip);
        color: var(--ink-tertiary, var(--ink-secondary));
        font-size: var(--text-xs);
        font-weight: var(--font-weight-regular, 400);
        white-space: nowrap;
    }

    .sources-list {
        max-height: 300px;
        padding-inline-end: var(--space-1);
        overflow-y: auto;
    }

    .source-row {
        display: flex;
        align-items: center;
        gap: 10px;
        margin-block-end: 2px;
        padding-block: 6px;
        padding-inline: 10px;
        border-radius: var(--radius-control);
        background: var(--surface-panel);
        color: inherit;
        text-decoration: none;
        transition: background-color var(--dur-instant) var(--ease-standard);
        min-width: 0;
    }

    .source-row.is-clickable {
        cursor: pointer;
    }

    @media (hover: hover) and (pointer: fine) {
        .source-row.is-clickable:hover {
            background: var(--surface-hover);
        }
    }

    .source-row-favicon-wrap {
        display: flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
        width: 16px;
        height: 16px;
    }

    .source-row-favicon {
        width: 16px;
        height: 16px;
        border-radius: 3px;
        object-fit: contain;
    }

    .source-row-fallback {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 16px;
        height: 16px;
        color: var(--ink-muted);
    }

    .source-row-fallback .icon {
        width: 13px;
        height: 13px;
    }

    .source-row-body {
        flex: 1;
        min-width: 0;
    }

    .source-row-title {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        color: var(--ink-primary);
        font-family: var(--font-ui);
        font-size: var(--text-xs);
        line-height: 1.4;
    }

    .source-row-domain {
        flex-shrink: 0;
        margin-left: auto;
        color: var(--ink-muted);
        font-family: var(--font-data);
        font-size: var(--text-2xs);
    }

    .source-row-out {
        flex-shrink: 0;
        width: 12px;
        height: 12px;
        color: var(--ink-muted);
    }

    @container (max-width: 320px) {
        .source-row {
            display: grid;
            grid-template-columns: 16px minmax(0, 1fr) 12px;
            gap: 4px 8px;
        }
        .source-row-favicon-wrap { grid-row: 1 / span 2; }
        .source-row-body { grid-column: 2; }
        .source-row-domain {
            grid-column: 2;
            margin-left: 0;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }
        .source-row-out { grid-column: 3; grid-row: 1 / span 2; }
    }
}

@layer channels {

    /* ====================================================================
       14 · Die serverseitig gerenderte Nachricht

       `templates/partials/chat_message.html`. Der Signalkasten trug einen
       Verlauf-Avatar, drei Emoji-Chips und die Indigo-Markenfarbe
       `#5865F2`. Die Zahlen behalten ihre Signalfarbe — dort steht Geld
       (R2); alles andere ist grau.
       ==================================================================== */

    .signal-card {
        margin-block-end: var(--space-3);
        padding: var(--space-4);
        border-radius: var(--radius-panel);
        background: var(--surface-raised);
    }

    .signal-card-head {
        display: flex;
        align-items: baseline;
        gap: var(--space-3);
        margin-block-end: var(--space-3);
    }

    .signal-ticker {
        color: var(--ink-primary);
        font-family: var(--font-data);
        font-size: var(--text-xl);
        font-weight: var(--font-weight-medium);
    }

    .signal-kind {
        padding-block: 2px;
        padding-inline: var(--space-2);
        border-radius: var(--radius-chip);
        background: var(--accent-subtle);
        color: var(--ink-secondary);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
    }

    .signal-metrics {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(6rem, 1fr));
        gap: var(--space-2);
    }

    /* Keine Karte in der Karte (R3): die Kennzahl steht auf der Flaeche
       des Kastens, getrennt durch Abstand statt durch eine zweite Ebene. */
    .signal-metric {
        text-align: center;
    }

    .signal-metric-label {
        margin-block-end: var(--space-1);
        color: var(--ink-muted);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
    }

    .signal-metric-value {
        color: var(--ink-primary);
        font-family: var(--font-data);
        font-size: var(--text-base);
        font-weight: var(--font-weight-medium);
        font-variant-numeric: tabular-nums;
    }

    .signal-metric-value.text-green  { color: var(--signal-up); }
    .signal-metric-value.text-red    { color: var(--signal-down); }
    .signal-metric-value.text-yellow { color: var(--signal-warn); }

    .stale-warning {
        margin-block-start: var(--space-4);
        padding-block: var(--space-2);
        padding-inline: var(--space-3);
        border-radius: var(--radius-control);
        background: var(--signal-warn-surface);
        color: var(--signal-warn);
        font-family: var(--font-ui);
        font-size: var(--text-2xs);
        line-height: 1.5;
    }
}

@layer channels {
    @media (max-width: 767px) {
        .chat-container { min-height: 0; }
        .chat-messages { padding-inline: 0; }
        .chat-input-container { padding: 8px 0 max(8px, env(safe-area-inset-bottom)); }
        .chat-input-shell, .chat-stream { width: 100%; max-width: 100%; min-width: 0; }
        .chat-input-container .ios-composer { padding: 10px 10px 6px; }
        .chat-input-container .hotbar-main-content { flex-wrap: nowrap; gap: 4px; }
        .chat-input-container .composer-status { flex: 1; flex-direction: column; align-items: stretch; margin-inline-start: 0; }
        .chat-input-container .composer-status .token-badge { align-self: flex-start; padding-inline-start: 8px; }
        .chat-input-container .composer-right { align-items: center; }
        .chat-input-container .composer-input { font-size: 16px; max-height: min(180px, 30dvh); }
        .chat-input-container .composer-plus-btn, .chat-input-container .composer-send-btn, .chat-input-container .hotbar-action-btn { min-width: 44px; min-height: 44px; }
        .message { max-width: 100%; min-width: 0; }
        .message .content, .message .text { min-width: 0; overflow-wrap: anywhere; }
        .message .text { font-size: 16px; }
        .message pre, .message .table-wrapper { max-width: 100%; overflow-x: auto; }
        .chat-input-container .model-picker { flex: 1; min-width: 0; }
        .chat-input-container .model-picker-trigger { max-width: 100%; min-height: 44px; }
        .chat-input-container .composer-status .model-picker-trigger { width: 100%; }
        .chat-input-container .composer-plus-dot { top: 10px; right: 10px; }
        .chat-input-container .token-badge { flex-shrink: 0; }
        .extras-menu-panel { max-width: calc(100vw - 32px); max-height: min(540px, 65dvh); overflow-y: auto; }
    }
}
