diff --git a/docs/ACCEPTANCE.md b/docs/ACCEPTANCE.md
index 02d6395..805afd7 100644
--- a/docs/ACCEPTANCE.md
+++ b/docs/ACCEPTANCE.md
@@ -413,14 +413,24 @@
- reale OBS-Browserquelle, echte YouTube-/TikTok-Wiedergabe und Safari auf einem
iOS-Gerät nicht ausgeführt; Anbieterrequests wurden lokal abgefangen
-## Verwaltungskorrektur 0.5.11
+## Kompakte Live-Regie und Verwaltungskorrektur 0.5.11
-| Kriterium | Nachweis | Stand |
-| ------------------------------------------ | ---------------------------------------------------------------------------------------- | --------------------- |
-| Werkzeugdialog ist initial geschlossen | Chromium prüft den Dialog vor jeder Bedienaktion bei 390 und 1280 Pixeln | erfüllt am 23.10.2026 |
-| Dialog öffnet nur nach bewusster Aktion | Browser legt ein Werkzeug an und öffnet ein bestehendes Werkzeug anschließend zum Ändern | erfüllt am 23.10.2026 |
-| Bestehender Werkzeugablauf bleibt erhalten | Browser prüft Icon, Format, Deaktivierung und Piece-Zuordnung nach erneutem Laden | erfüllt am 23.10.2026 |
-| Themen-Iconpicker bleibt im Viewport | Chromium prüft Öffnen und Auswahl bei 390, 960 und 1280 Pixeln | erfüllt am 23.10.2026 |
+| Kriterium | Nachweis | Stand |
+| ------------------------------------------- | ------------------------------------------------------------------------------------------------ | --------------------- |
+| Werkzeugdialog ist initial geschlossen | Chromium prüft den Dialog vor jeder Bedienaktion bei 390 und 1280 Pixeln | erfüllt am 24.10.2026 |
+| Dialog öffnet nur nach bewusster Aktion | Browser legt ein Werkzeug an und öffnet ein bestehendes Werkzeug anschließend zum Ändern | erfüllt am 24.10.2026 |
+| Bestehender Werkzeugablauf bleibt erhalten | Browser prüft Icon, Format, Deaktivierung und Piece-Zuordnung nach erneutem Laden | erfüllt am 24.10.2026 |
+| Themen-Iconpicker bleibt im Viewport | Chromium prüft Öffnen und Auswahl bei 390, 960 und 1280 Pixeln | erfüllt am 24.10.2026 |
+| Globale Navigation bleibt aus der Regie | Chromium findet weder Seitenleiste noch mobilen App-Header oder Bottomnavigation | erfüllt am 24.10.2026 |
+| Kleines Desktopfenster bleibt zweispaltig | Chromium prüft Moderation, Ablauf und feste Aktionen bei 900 × 450 Pixeln | erfüllt am 24.10.2026 |
+| Lange Inhalte scrollen innerhalb der Fläche | Synthetischer Langtext und zusätzliche Ablaufabschnitte erzeugen zwei unabhängige Scroller | erfüllt am 24.10.2026 |
+| Mobile Regie verwendet ein Ablauf-Sheet | 390 × 700 prüft Endlage, Fokus, Escape, Aktionsschließen und Wechsel auf 900 Pixel | erfüllt am 24.10.2026 |
+| Leseposition bleibt am Piece | Nach tiefem Scrollen beginnt ein direkt gewähltes anderes Piece am oberen Rand | erfüllt am 24.10.2026 |
+| Zustände sind ausgeschrieben | Ablauf prüft Kommend, Aktuell, Erledigt und Übersprungen | erfüllt am 24.10.2026 |
+| Erstes und letztes Piece direkt wählbar | Browser wechselt per stabiler Usage-ID auf Desktop und aus dem mobilen Sheet in beide Richtungen | erfüllt am 24.10.2026 |
+| Navigation veröffentlicht nicht automatisch | Öffentliche Ausgabe bleibt bei direkter Auswahl unverändert | erfüllt am 24.10.2026 |
+| Timer und Livezustand überleben Reload | Aktuelles Piece und laufender Abschnittscountdown bleiben nach Neuladen erhalten | erfüllt am 24.10.2026 |
+| Sendungsende ist eindeutig | Letztes Piece zeigt Endaktion ohne horizontalen Überlauf; danach fehlen Weiter-Aktionen | erfüllt am 24.10.2026 |
## Ausgeführte Prüfungen 0.5.11
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
index 2c37d19..68abc5d 100644
--- a/docs/ARCHITECTURE.md
+++ b/docs/ARCHITECTURE.md
@@ -283,6 +283,17 @@ Der Live-Modus ist eine eigene, fokussierte Arbeitsansicht. Die
Präsentationsausgabe verwendet eine separate, strikt reduzierte Datenansicht
ohne interne Anwendungsnavigation.
+Die private Route `/live` verwendet ebenfalls einen eigenen authentifizierten
+Layoutmodus ohne allgemeine Seitenleiste, mobilen App-Header oder
+Bottomnavigation. Ihre Regiefläche belegt die verfügbare Viewporthöhe als Raster
+aus fester Statuszeile, unabhängig scrollender Piece- und Ablaufspalte sowie
+fester Aktionszeile. Unterhalb des schmalen Breakpoints wird ausschließlich die
+Ablaufspalte als modales Sheet mit Fokusbegrenzung eingeblendet; beim Wechsel
+auf einen breiteren Viewport wird dieser Modalzustand aufgehoben. Dieser
+Layoutmodus ändert weder die Live-Actions noch die getrennte öffentliche
+`/heinzlive`-Projektion. Lokale Lesepositionen sind an die jeweilige Verwendung
+gebunden und werden nicht zwischen Pieces übertragen.
+
Icons werden aus dem lokalen Font-Awesome-Classic-Regular-Sprite unter
`static/icons` gerendert. Die Verwaltung lädt zusätzlich die durchsuchbaren
Metadaten aller 3.671 Icons, zeigt je Suchergebnis aber höchstens 120 Einträge
diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md
index 17687aa..57a8fa1 100644
--- a/docs/DECISIONS.md
+++ b/docs/DECISIONS.md
@@ -360,3 +360,17 @@ Validierung, während ein statischer SVG-Sprite über den gemeinsamen
Pfaddaten in ihren JavaScript-Chunk. Es gibt weder einen externen Laufzeitabruf
noch eine Laufzeitabhängigkeit vom benachbarten Quellverzeichnis; Herkunft und
Lizenz stehen in `THIRD_PARTY_NOTICES.md`.
+
+## D-024: Private Live-Regie als fokussierter Viewport
+
+Die authentifizierte Live-Steuerung nutzt einen eigenen Root-Layoutmodus und
+rendert die allgemeine App-Navigation nicht. Kopf- und Aktionszeile bleiben fest;
+Moderation und Ablauf sind die einzigen unabhängig scrollenden Arbeitsflächen.
+Die Umschaltung zum mobilen Sheet hängt ausschließlich von der Fensterbreite ab,
+damit ein breites, niedriges Desktopfenster weiterhin zwei Spalten verwendet.
+
+Diese Darstellung ist keine neue Live-Zustandsgrenze. Start, direkte Auswahl,
+Erledigen, Überspringen, Reserveeinfügung und Präsentation bleiben unveränderte
+serverseitige Actions mit ihren bisherigen Revisions- und Idempotenzregeln. Ein
+Piece-Wechsel veröffentlicht daher weiterhin nichts; nur die ausdrückliche
+Präsentationsaction verändert die getrennte öffentliche Ausgabe.
diff --git a/docs/RELEASE_NOTES.md b/docs/RELEASE_NOTES.md
index ad14590..1f04f82 100644
--- a/docs/RELEASE_NOTES.md
+++ b/docs/RELEASE_NOTES.md
@@ -1,17 +1,72 @@
# Release Notes
-## 0.5.11 - Verwaltungskorrektur
+## 0.5.11 - Kompakte Live-Regie
Veröffentlicht am 23. Oktober 2026
+Sendewerk 0.5.11 macht aus der privaten Live-Steuerung eine kompakte Regieansicht
+für Vollbild, Smartphone und kleine Desktopfenster neben OBS oder Firebot. Das
+aktuelle Piece, die Position im Ablauf, der Abschnittscountdown und die
+wichtigsten Aktionen bleiben gleichzeitig erreichbar. Die öffentliche
+heinzlive-Ausgabe und die gespeicherten Livezustände bleiben technisch getrennt.
+
+### Highlights
+
+- Die Live-Regie verwendet die gesamte Fensterfläche ohne normale
+ App-Seitenleiste, mobilen App-Header oder Bottomnavigation.
+- Eine kompakte Kopfzeile zeigt aktuellen Abschnitt, Abschnittscountdown und den
+ tatsächlich präsentierten Ausgabezustand.
+- Moderation und Ablauf bleiben auch bei einem nur 900 × 450 Pixel großen
+ Desktopfenster nebeneinander. Lange Inhalte scrollen innerhalb ihrer
+ Arbeitsfläche; Kopf- und Aktionszeile bleiben sichtbar.
+- Die feste Fußleiste bündelt nächstes Piece, „Präsentieren“, „Überspringen“ und
+ „Erledigen & weiter“. Beim letzten offenen Piece wird das Sendungsende
+ eindeutig benannt.
+- Auf dem Smartphone öffnet „Ablauf · Piece X/Y“ den Ablauf als separates Sheet,
+ während das aktuelle Piece die Hauptfläche bleibt. Fokusführung, Escape und
+ der Wechsel zurück auf eine breite Ansicht schließen den Modalzustand sauber.
+
+### Moderation und Materialien
+
+- Interner Titel, Einleitung, Moderationsstichpunkte und Überleitung erscheinen
+ in klarer Lesereihenfolge. Leere Bereiche werden weggelassen.
+- Recherche, Quellen, Werkzeuge und eigene Notizen sind kompakt beschriftet und
+ werden nur bei Bedarf geöffnet.
+- Die Prompter-Schriftgröße bleibt einstellbar und wird weiterhin lokal
+ gespeichert. Texte werden nicht automatisch verkleinert.
+
+### Ablauf und Liveverhalten
+
+- Abschnitte sind kompakt aufklappbar; der aktuelle Abschnitt ist geöffnet und
+ das aktive Piece bleibt in der Ablaufspalte sichtbar.
+- Pieces kennzeichnen „Aktuell“, „Erledigt“, „Übersprungen“ und „Kommend“ auch
+ textlich und lassen sich direkt auswählen, einschließlich des ersten Eintrags.
+- Die Reserve bleibt zusammengeklappt erreichbar. Dauerhafte Sortierpfeile und
+ die separate große „Als Nächstes“-Karte entfallen; Umsortieren bleibt über
+ eine kompakte Aktion am jeweiligen Eintrag erreichbar.
+- Die lokal gespeicherte Leseposition gehört zum jeweiligen Piece. Eine direkte
+ Auswahl beginnt daher oben, ohne die Position des vorherigen Textes zu erben.
+- Direkte Auswahl verändert keinen Erledigungsstatus und veröffentlicht nichts.
+ Nur „Präsentieren“ oder „Präsentation leeren“ verändert die öffentliche
+ Ausgabe.
+- Abschnittstimer, tatsächliche Laufzeiten, Livefortschritt und Wiederaufnahme
+ verwenden unverändert die vorhandenen persistenten Zustände.
+
+### Einstellungen und Verwaltung
+
+- Ausgabekanal verbinden, Schlüssel erneuern, Kanal trennen, Theme wechseln,
+ Zeiten ein-/ausblenden und „Zur Planung“ bleiben im Regiemenü erreichbar.
- Der Dialog „Werkzeug hinzufügen“ bleibt beim Aufruf der Verwaltungsseite
- wieder zuverlässig geschlossen.
-- Der Dialog erscheint erst, wenn ein neues Werkzeug angelegt oder ein
- bestehendes Werkzeug bearbeitet wird.
+ zuverlässig geschlossen und erscheint erst nach einer bewussten Aktion.
- Die Iconauswahl unter „Inhaltliche Zuordnung“ bleibt auf Desktop und
Smartphone vollständig im sichtbaren Browserbereich.
-- Die Korrektur gilt gleichermaßen für Desktop- und Smartphone-Ansichten und
- verändert keine vorhandenen Werkzeuge oder Zuordnungen.
+
+### Update und Kompatibilität
+
+- Dieses Release benötigt keine neue Datenbankmigration und keine Änderung der
+ Konfiguration.
+- Bestehende Sendungen, Livezustände, Timer, Ausgabekanäle und
+ Präsentationssnapshots bleiben erhalten.
## 0.5.10 - Editor-Erlebnis
diff --git a/docs/STATUS.md b/docs/STATUS.md
index 2d28c4c..3042530 100644
--- a/docs/STATUS.md
+++ b/docs/STATUS.md
@@ -7,14 +7,31 @@ Anmeldung, Rollen, persistente Inhaltsbearbeitung, Sendungsplanung,
Live-Steuerung, Präsentationsausgabe und lokaler Betriebsweg sind dokumentiert.
Etappe 6 wurde nicht begonnen.
-## Verwaltungskorrektur 0.5.11
+## Kompakte Live-Regie 0.5.11
-- der Werkzeugdialog bleibt nach dem Laden der Verwaltungsseite geschlossen und
- wird erst durch „Werkzeug hinzufügen“ oder die Bearbeitung eines vorhandenen
- Werkzeugs als modaler Dialog eingeblendet
-- die Iconauswahl der Themenverwaltung bleibt auch in der rechten Desktop-Spalte
- vollständig innerhalb des sichtbaren Browserbereichs
-- ein Browser-Regressionscheck sichert den geschlossenen Ausgangszustand ab
+- fokussierte private Liveansicht ohne normale App-Seitenleiste oder mobile
+ Bottomnavigation; Planung, Theme, Schriftgröße, Zeitdarstellung und
+ Ausgabekanal bleiben über das kompakte Regiemenü erreichbar
+- feste Kopfzeile mit aktuellem Abschnitt, persistentem Abschnittscountdown und
+ öffentlichem Ausgabezustand sowie feste Fußleiste mit nächstem Piece,
+ Präsentation, Überspringen und kontextgerechtem Erledigen
+- aktuelle Moderation und schmale Ablaufspalte bleiben auch bei 900 × 450 Pixeln
+ nebeneinander; beide Arbeitsflächen scrollen unabhängig, ohne die ganze Seite
+ oder einzelne Textblöcke zu verschachtelten Scrollcontainern zu machen
+- auf schmalen Ansichten bleibt das Piece die Hauptfläche und der Ablauf öffnet
+ als separates, safe-area-fähiges Sheet mit Fokusführung und Escape-Schließen
+- kompakter aufklappbarer Ablauf mit ausgeschriebenen Zuständen, direkter
+ Auswahl jedes Pieces, automatisch sichtbarem aktuellen Eintrag und
+ zusammengeklappter Reserve; Sortierung bleibt in einer kompakten Aktion
+ erreichbar, dauerhafte Sortierpfeile entfallen
+- die Leseposition wird je Piece lokal erhalten; ein erstmals gewähltes Piece
+ beginnt oben statt an der Position des vorherigen Textes
+- Recherche, Quellen, Werkzeuge und eigene Notizen erscheinen nur bei Bedarf;
+ leere Moderationsbereiche werden nicht dargestellt
+- Livefortschritt, direkte Auswahl, Abschnittstimer und öffentliche Präsentation
+ verwenden unverändert ihre getrennten persistenten Zustände
+- der Werkzeugdialog bleibt nach dem Laden der Verwaltungsseite geschlossen;
+ die Themen-Iconauswahl bleibt vollständig innerhalb des Viewports
Etappe 6 bleibt unverändert unbegonnen.
diff --git a/src/app.css b/src/app.css
index 85f4465..f68da99 100644
--- a/src/app.css
+++ b/src/app.css
@@ -980,51 +980,191 @@ textarea:focus {
font-size: 0.7rem;
}
+.live-controller-shell {
+ height: 100dvh;
+ min-height: 0;
+ margin-left: 0 !important;
+ padding: 0 !important;
+ overflow: hidden;
+}
+
.live-page {
- max-width: 88rem;
+ display: grid;
+ width: 100%;
+ height: 100dvh;
+ grid-template-rows: auto auto minmax(0, 1fr) auto;
+ gap: 0.55rem;
+ padding: max(0.65rem, env(safe-area-inset-top)) max(0.65rem, env(safe-area-inset-right))
+ max(0.65rem, env(safe-area-inset-bottom)) max(0.65rem, env(safe-area-inset-left));
}
-.live-header {
- display: flex;
+
+.live-command-header {
+ display: grid;
+ position: relative;
+ z-index: 60;
+ grid-template-columns: minmax(12rem, 1fr) auto minmax(8rem, 0.55fr) auto;
align-items: center;
- justify-content: space-between;
- gap: 1.25rem;
- margin-bottom: 1rem;
+ gap: 0.65rem;
+ min-width: 0;
+ padding: 0.55rem 0.65rem;
+ border: 1px solid color-mix(in srgb, var(--gold) 24%, var(--line));
+ border-radius: 0.9rem;
+ background: var(--glass);
+ box-shadow: var(--shadow-soft);
+ backdrop-filter: blur(20px) saturate(1.08);
}
-.live-header h1 {
- max-width: 24ch;
- margin: 0.18rem 0 0;
- font-size: clamp(1.5rem, 2.5vw, 2.1rem);
- letter-spacing: -0.035em;
- line-height: 1.08;
+
+.live-section-context,
+.live-output-state {
+ display: flex;
+ min-width: 0;
+ align-items: center;
+ gap: 0.55rem;
+}
+
+.live-section-context > div,
+.live-output-state > div {
+ display: grid;
+ min-width: 0;
+}
+
+.live-section-context small,
+.live-output-state small,
+.section-countdown span {
+ color: var(--text-faint);
+ font-size: 0.59rem;
+ font-weight: 720;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+}
+
+.live-section-context strong,
+.live-output-state strong {
+ overflow: hidden;
+ font-size: 0.75rem;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
.live-pulse {
- display: inline-block;
width: 0.48rem;
height: 0.48rem;
- margin-right: 0.35rem;
+ flex: 0 0 auto;
border-radius: 50%;
background: #d65c55;
box-shadow: 0 0 0 0.25rem color-mix(in srgb, #d65c55 15%, transparent);
}
-.live-header-actions {
- display: flex;
- align-items: center;
- gap: 0.7rem;
+.section-countdown {
+ display: grid;
+ min-width: 6.8rem;
+ gap: 0.08rem;
+ padding: 0.38rem 0.55rem;
+ border: 1px solid var(--line);
+ border-radius: 0.65rem;
+ background: var(--panel-soft);
+ text-align: center;
}
-.live-header-actions > a {
- color: var(--text-muted);
- font-size: 0.75rem;
- font-weight: 650;
+
+.section-countdown strong {
+ font-size: 0.78rem;
+ font-variant-numeric: tabular-nums;
}
+
+.section-countdown.warning {
+ border-color: #f3ba63;
+ color: #b87600;
+}
+
+.section-countdown.overrun {
+ border-color: #ff776d;
+ color: #d24d45;
+}
+
+.live-output-state > span {
+ width: 0.5rem;
+ height: 0.5rem;
+ flex: 0 0 auto;
+ border-radius: 50%;
+ background: var(--text-faint);
+}
+
+.live-output-state.active > span {
+ background: var(--accent);
+ box-shadow: 0 0 0 0.2rem color-mix(in srgb, var(--accent) 12%, transparent);
+}
+
+.live-mobile-run-button {
+ display: none;
+}
+
+.live-menu {
+ position: relative;
+ z-index: 40;
+ width: fit-content;
+ justify-self: end;
+}
+
+.live-menu > summary {
+ width: fit-content;
+ padding: 0.48rem 0.65rem;
+ border: 1px solid var(--line);
+ border-radius: 0.65rem;
+ background: var(--panel-soft);
+ cursor: pointer;
+ font-size: 0.7rem;
+ font-weight: 750;
+ list-style: none;
+}
+
+.live-menu-popover {
+ position: absolute;
+ z-index: 50;
+ top: calc(100% + 0.4rem);
+ right: 0;
+ display: grid;
+ width: min(22rem, calc(100vw - 1.3rem));
+ max-height: calc(100dvh - 5rem);
+ overflow-y: auto;
+ padding: 0.7rem;
+ border: 1px solid var(--line-strong);
+ border-radius: 0.8rem;
+ background: var(--glass-strong);
+ box-shadow: var(--shadow-large);
+ backdrop-filter: blur(24px);
+}
+
+.live-menu-popover section {
+ display: grid;
+ gap: 0.4rem;
+ padding: 0.6rem;
+}
+
+.live-menu-popover section + section {
+ border-top: 1px solid var(--line);
+}
+
+.live-menu-popover section > strong {
+ color: var(--text-faint);
+ font-size: 0.62rem;
+ letter-spacing: 0.07em;
+ text-transform: uppercase;
+}
+
+.live-menu-popover a {
+ font-size: 0.72rem;
+ font-weight: 700;
+}
+
.font-control {
display: flex;
+ width: fit-content;
padding: 0.18rem;
border: 1px solid var(--line);
border-radius: 0.65rem;
background: var(--panel);
}
+
.font-control button {
width: 2.25rem;
height: 2rem;
@@ -1035,251 +1175,124 @@ textarea:focus {
font-size: 0.75rem;
font-weight: 700;
}
+
.font-control button:hover {
background: var(--panel-soft);
}
-.live-grid {
+.live-message {
+ margin: 0;
+ padding: 0.45rem 0.65rem;
+ font-size: 0.7rem;
+}
+
+.live-messages {
display: grid;
- grid-template-columns: minmax(0, 1.65fr) minmax(16rem, 0.7fr);
- gap: 1rem;
- align-items: start;
+ gap: 0.35rem;
+}
+
+.live-messages:empty {
+ display: none;
+}
+
+.live-workspace {
+ display: grid;
+ min-width: 0;
+ min-height: 0;
+ grid-template-columns: minmax(0, 1fr) minmax(15rem, 20rem);
+ gap: 0.65rem;
+}
+
+.live-page.without-rail .live-workspace {
+ grid-template-columns: minmax(0, 1fr);
+}
+
+.live-page.without-rail .live-rail {
+ display: none;
}
.live-current {
min-width: 0;
- padding: clamp(1.25rem, 3vw, 2.25rem);
+ min-height: 0;
+ overflow: hidden;
border: 1px solid var(--line);
- border-radius: 1.8rem;
+ border-radius: 1rem;
background: var(--glass);
- box-shadow: var(--shadow);
+ box-shadow: var(--shadow-soft);
backdrop-filter: blur(20px) saturate(1.08);
}
+.live-current-scroll {
+ height: 100%;
+ overflow-y: auto;
+ padding: clamp(1rem, 2.2vw, 2rem);
+ overscroll-behavior: contain;
+ scrollbar-gutter: stable;
+}
+
+.live-piece-heading {
+ padding-bottom: 0.85rem;
+}
+
.live-kicker {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 0.75rem;
color: var(--accent);
- font-size: 0.72rem;
- font-weight: 700;
+ font-size: 0.65rem;
+ font-weight: 750;
letter-spacing: 0.055em;
text-transform: uppercase;
}
+
.live-kicker strong {
color: var(--text-muted);
+ font-variant-numeric: tabular-nums;
}
-.live-current h2 {
- max-width: 20ch;
- margin: 1rem 0 1.4rem;
- font-size: clamp(1.8rem, 3.2vw, 2.8rem);
- font-weight: 670;
- letter-spacing: -0.05em;
- line-height: 1.03;
+
+.live-current h1 {
+ max-width: 30ch;
+ margin: 0.45rem 0 0;
+ font-size: clamp(1.45rem, 2.7vw, 2.55rem);
+ font-weight: 690;
+ letter-spacing: -0.045em;
+ line-height: 1.04;
+ overflow-wrap: anywhere;
}
-.prompter-copy {
- max-width: 34ch;
+
+.live-script-section {
+ max-width: 58rem;
+ padding: 1rem 0;
+ border-top: 1px solid var(--line);
+}
+
+.live-script-section > span {
+ display: block;
+ margin-bottom: 0.45rem;
+ color: var(--accent);
+ font-size: 0.64rem;
+ font-weight: 780;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.prompter-copy,
+.live-script-section .live-markdown {
+ max-width: 45ch;
font-family: ui-serif, Georgia, serif;
font-size: var(--prompt-size);
line-height: 1.47;
}
-.moderation-card,
+.moderation-card .live-markdown {
+ font-family: inherit;
+ font-size: max(1rem, calc(var(--prompt-size) * 0.82));
+ line-height: 1.5;
+}
+
.transition-card {
- margin-top: 1.25rem;
- padding: 1rem 1.1rem;
- border-radius: 0.9rem;
- background: color-mix(in srgb, var(--panel-soft) 66%, transparent);
-}
-
-.moderation-card span,
-.transition-card span {
- color: var(--accent);
- font-size: 0.67rem;
- font-weight: 750;
- letter-spacing: 0.08em;
- text-transform: uppercase;
-}
-.moderation-card p,
-.transition-card p {
- margin: 0.4rem 0 0;
-}
-
-.research-details {
- margin-top: 0.8rem;
- padding: 0.82rem 1rem;
- border: 1px solid var(--line);
- border-radius: 0.85rem;
- color: var(--text-muted);
-}
-.research-details summary {
- cursor: pointer;
- color: var(--text);
- font-size: 0.78rem;
- font-weight: 680;
-}
-.research-details p {
- margin-bottom: 0;
- font-size: 0.78rem;
-}
-.transition-card {
- border-left: 3px solid var(--gold);
- background: color-mix(in srgb, var(--gold) 8%, var(--panel));
-}
-
-.live-rail {
- display: grid;
- gap: 0.75rem;
-}
-.next-card,
-.live-progress,
-.readonly-actions {
- padding: 1rem;
- border: 1px solid var(--line);
- border-radius: 1rem;
- background: var(--glass);
- box-shadow: var(--shadow-soft);
- backdrop-filter: blur(16px) saturate(1.06);
-}
-.next-card > span {
- color: var(--accent);
- font-size: 0.66rem;
- font-weight: 750;
- letter-spacing: 0.08em;
- text-transform: uppercase;
-}
-.next-card h3 {
- margin: 0.7rem 0 0.28rem;
- font-size: 1rem;
- line-height: 1.25;
-}
-.next-card p {
- margin: 0;
- color: var(--text-muted);
- font-size: 0.7rem;
-}
-.live-progress header {
- display: flex;
- justify-content: space-between;
- color: var(--text-muted);
- font-size: 0.7rem;
- font-weight: 680;
-}
-.progress-track {
- height: 0.24rem;
- margin: 0.7rem 0 1rem;
- overflow: hidden;
- border-radius: 1rem;
- background: var(--panel-soft);
-}
-.progress-track span {
- display: block;
- width: 45%;
- height: 100%;
- border-radius: inherit;
- background: var(--gold);
-}
-.rail-section {
- display: grid;
- grid-template-columns: auto 1fr;
- gap: 0.6rem;
- padding: 0.55rem;
- border-radius: 0.6rem;
- color: var(--text-muted);
-}
-.rail-section > span {
- font-size: 0.63rem;
- font-weight: 720;
-}
-.rail-section div {
- display: flex;
- justify-content: space-between;
- gap: 0.6rem;
-}
-.rail-section strong {
- font-size: 0.7rem;
-}
-.rail-section small {
- font-size: 0.64rem;
-}
-.rail-section.active {
- background: color-mix(in srgb, var(--gold) 10%, var(--panel));
- color: var(--text);
-}
-.readonly-actions {
- display: flex;
- justify-content: space-between;
- gap: 0.5rem;
- color: var(--text-faint);
- font-size: 0.63rem;
-}
-.readonly-actions strong {
- color: var(--text-muted);
- font-size: 0.61rem;
-}
-
-.live-state-line {
- margin: 0.4rem 0 0;
- color: var(--text-muted);
- font-size: 0.75rem;
-}
-
-.live-toolbar {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- gap: 0.55rem;
- margin-bottom: 1rem;
- padding: 0.75rem;
- border: 1px solid var(--line);
- border-radius: 1rem;
- border-color: color-mix(in srgb, var(--gold) 16%, var(--line));
- background: var(--glass);
- box-shadow: var(--shadow-soft);
- backdrop-filter: blur(20px) saturate(1.08);
-}
-
-.live-session-summary {
- display: grid;
- margin-left: auto;
- text-align: right;
-}
-
-.live-output-settings {
- position: relative;
-}
-
-.live-output-settings > summary {
- cursor: pointer;
- color: var(--text-muted);
- font-size: 0.7rem;
- font-weight: 700;
-}
-
-.live-output-settings > div {
- position: absolute;
- z-index: 4;
- top: calc(100% + 0.45rem);
- left: 0;
- display: grid;
- width: max-content;
- max-width: calc(100vw - 2rem);
- gap: 0.35rem;
- padding: 0.6rem;
- border: 1px solid var(--line-strong);
- border-radius: var(--radius-small);
- background: var(--glass-strong);
- box-shadow: var(--shadow);
-}
-
-.live-session-summary strong {
- font-size: 0.78rem;
-}
-
-.live-session-summary span {
- color: var(--text-muted);
- font-size: 0.65rem;
-}
-
-.live-grid.without-rail {
- grid-template-columns: minmax(0, 1fr);
+ border-top-color: color-mix(in srgb, var(--gold) 50%, var(--line));
}
.live-markdown > :first-child,
@@ -1292,18 +1305,193 @@ textarea:focus {
margin-bottom: 0;
}
-.live-current-actions {
+.live-support-actions {
display: flex;
- align-items: center;
+ align-items: flex-start;
flex-wrap: wrap;
- gap: 0.5rem;
- margin-top: 1.4rem;
- padding-top: 1rem;
+ gap: 0.45rem;
+ padding-top: 0.9rem;
border-top: 1px solid var(--line);
}
-.live-current-actions form:last-child {
- margin-left: auto;
+.research-details,
+.live-materials {
+ min-width: min(100%, 15rem);
+ max-width: min(100%, 34rem);
+ padding: 0.55rem 0.65rem;
+ border: 1px solid var(--line);
+ border-radius: 0.65rem;
+ background: color-mix(in srgb, var(--panel-soft) 76%, transparent);
+}
+
+.research-details summary,
+.live-materials summary {
+ cursor: pointer;
+ font-size: 0.7rem;
+ font-weight: 740;
+}
+
+.research-details .live-markdown,
+.live-materials > :not(summary) {
+ margin-top: 0.65rem;
+}
+
+.research-details .live-markdown,
+.live-materials p,
+.live-materials li {
+ font-size: 0.76rem;
+ line-height: 1.5;
+}
+
+.live-materials ul {
+ display: grid;
+ gap: 0.5rem;
+ padding: 0;
+ list-style: none;
+}
+
+.live-materials li,
+.live-materials small {
+ display: block;
+}
+
+.live-materials small {
+ color: var(--text-muted);
+}
+
+.live-empty-copy {
+ color: var(--text-muted);
+}
+
+.live-empty {
+ display: grid;
+ place-items: center;
+ padding: 1rem;
+ text-align: center;
+}
+
+.live-empty > div {
+ max-width: 34rem;
+}
+
+.live-empty h1 {
+ margin-inline: auto;
+}
+
+.live-rail {
+ display: grid;
+ min-width: 0;
+ min-height: 0;
+ grid-template-rows: auto auto minmax(0, 1fr);
+ padding: 0.65rem;
+ overflow: hidden;
+ border: 1px solid var(--line);
+ border-radius: 1rem;
+ background: var(--glass);
+ box-shadow: var(--shadow-soft);
+ backdrop-filter: blur(18px) saturate(1.06);
+}
+
+.live-rail > header,
+.live-reserve > summary {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 0.65rem;
+}
+
+.live-rail > header > div {
+ display: grid;
+}
+
+.live-rail > header strong {
+ font-size: 0.8rem;
+}
+
+.live-rail > header span {
+ color: var(--text-muted);
+ font-size: 0.62rem;
+}
+
+.live-rail > header > button {
+ display: none;
+ width: 2rem;
+ height: 2rem;
+ place-items: center;
+ padding: 0;
+}
+
+.progress-track {
+ height: 0.22rem;
+ margin: 0.55rem 0;
+ overflow: hidden;
+ border-radius: 1rem;
+ background: var(--panel-soft);
+}
+
+.progress-track span {
+ display: block;
+ height: 100%;
+ border-radius: inherit;
+ background: var(--gold);
+}
+
+.live-run-scroll {
+ min-height: 0;
+ overflow-y: auto;
+ padding-right: 0.15rem;
+ overscroll-behavior: contain;
+}
+
+.rail-section {
+ border-top: 1px solid var(--line);
+}
+
+.rail-section > summary {
+ display: grid;
+ grid-template-columns: auto minmax(0, 1fr);
+ gap: 0.55rem;
+ padding: 0.55rem 0.25rem;
+ cursor: pointer;
+ list-style: none;
+}
+
+.rail-section > summary > span {
+ padding-top: 0.08rem;
+ color: var(--text-faint);
+ font-size: 0.59rem;
+ font-weight: 760;
+}
+
+.rail-section > summary > div {
+ display: grid;
+ min-width: 0;
+}
+
+.rail-section > summary strong {
+ font-size: 0.7rem;
+ overflow-wrap: anywhere;
+}
+
+.rail-section > summary small {
+ color: var(--text-faint);
+ font-size: 0.58rem;
+}
+
+.rail-section.active > summary {
+ color: var(--gold-strong);
+}
+
+.rail-section-items {
+ display: grid;
+ gap: 0.3rem;
+ padding: 0 0 0.45rem;
+}
+
+.rail-section-items > p {
+ margin: 0 0.25rem 0.35rem;
+ color: var(--text-faint);
+ font-size: 0.64rem;
}
.live-run-item,
@@ -1311,75 +1499,379 @@ textarea:focus {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
- gap: 0.5rem;
- margin-top: 0.35rem;
- padding: 0.5rem 0.55rem;
- border-radius: 0.65rem;
- background: color-mix(in srgb, var(--panel-soft) 68%, transparent);
+ gap: 0.3rem;
+ border: 1px solid transparent;
+ border-radius: 0.6rem;
+ background: color-mix(in srgb, var(--panel-soft) 70%, transparent);
}
.live-run-item.current {
- box-shadow: inset 3px 0 var(--gold);
- background: color-mix(in srgb, var(--gold) 9%, var(--panel));
+ border-color: color-mix(in srgb, var(--gold) 55%, var(--line));
+ background: color-mix(in srgb, var(--gold) 10%, var(--panel));
}
-.live-run-item > div:first-child,
-.live-reserve-item > div:first-child {
+.live-run-item.done {
+ opacity: 0.68;
+}
+
+.live-run-item.skipped {
+ border-style: dashed;
+ opacity: 0.62;
+}
+
+.live-run-item > form:first-child {
+ min-width: 0;
+}
+
+.live-run-actions,
+.live-reserve-actions {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: 0.2rem;
+}
+
+.live-run-actions form,
+.live-reserve-actions form {
+ display: flex;
+}
+
+.live-run-order {
+ position: relative;
+}
+
+.live-run-order > summary,
+.live-run-actions > form button {
+ display: grid;
+ width: 1.8rem;
+ height: 1.8rem;
+ place-items: center;
+ padding: 0;
+ border: 1px solid transparent;
+ border-radius: 0.45rem;
+ background: transparent;
+ color: var(--text-muted);
+ cursor: pointer;
+ list-style: none;
+}
+
+.live-run-order > summary:hover,
+.live-run-order[open] > summary,
+.live-run-actions > form button:hover {
+ border-color: var(--line);
+ background: var(--panel-soft);
+ color: var(--text);
+}
+
+.live-run-order > div {
+ display: flex;
+ position: absolute;
+ z-index: 10;
+ top: calc(100% + 0.2rem);
+ right: 0;
+ gap: 0.2rem;
+ padding: 0.25rem;
+ border: 1px solid var(--line-strong);
+ border-radius: 0.55rem;
+ background: var(--glass-strong);
+ box-shadow: var(--shadow-soft);
+}
+
+.live-run-order > div button {
+ display: grid;
+ width: 1.8rem;
+ height: 1.8rem;
+ place-items: center;
+ padding: 0;
+}
+
+.live-run-select {
+ display: grid;
+ width: 100%;
+ min-width: 0;
+ padding: 0.45rem 0.5rem;
+ border: 0;
+ background: transparent;
+ color: var(--text);
+ cursor: pointer;
+ text-align: left;
+}
+
+.live-run-select:disabled {
+ cursor: default;
+ opacity: 1;
+}
+
+.live-run-select strong,
+.live-reserve-item strong {
+ font-size: 0.67rem;
+ overflow-wrap: anywhere;
+}
+
+.live-run-select small,
+.live-reserve-item small {
+ color: var(--text-faint);
+ font-size: 0.56rem;
+}
+
+.live-run-actions {
+ margin-right: 0.28rem;
+}
+
+.live-reserve {
+ margin-top: 0.45rem;
+ border-top: 1px solid color-mix(in srgb, var(--gold) 42%, var(--line));
+}
+
+.live-reserve > summary {
+ padding: 0.6rem 0.25rem;
+ cursor: pointer;
+ color: var(--gold-strong);
+ font-size: 0.68rem;
+ font-weight: 760;
+ list-style: none;
+}
+
+.live-reserve > div {
+ display: grid;
+ gap: 0.3rem;
+ padding-bottom: 0.35rem;
+}
+
+.live-reserve-item {
+ padding: 0.4rem 0.5rem;
+}
+
+.live-reserve-item > div {
display: grid;
min-width: 0;
}
-.live-run-item strong,
-.live-reserve-item strong {
- font-size: 0.7rem;
- overflow-wrap: anywhere;
+.live-reserve-item > form {
+ display: flex;
}
-.live-run-item small,
-.live-reserve-item small {
+.live-reserve-actions {
+ min-width: 0;
+}
+
+.live-reserve-item > form button {
+ font-size: 0.6rem;
+}
+
+.live-reserve p {
+ margin: 0.4rem;
color: var(--text-muted);
- font-size: 0.61rem;
+ font-size: 0.64rem;
}
-.live-run-actions {
+.live-rail-backdrop {
+ display: none;
+}
+
+.live-action-bar {
+ display: flex;
+ min-width: 0;
+ align-items: center;
+ justify-content: space-between;
+ gap: 0.8rem;
+ padding: 0.55rem 0.65rem;
+ border: 1px solid color-mix(in srgb, var(--gold) 28%, var(--line));
+ border-radius: 0.9rem;
+ background: var(--glass-strong);
+ box-shadow: var(--shadow-soft);
+ backdrop-filter: blur(22px) saturate(1.08);
+}
+
+.live-next-summary {
+ display: grid;
+ min-width: 0;
+}
+
+.live-next-summary span {
+ color: var(--accent);
+ font-size: 0.58rem;
+ font-weight: 760;
+ letter-spacing: 0.07em;
+ text-transform: uppercase;
+}
+
+.live-next-summary strong {
+ overflow: hidden;
+ font-size: 0.72rem;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.live-footer-actions {
display: flex;
align-items: center;
- gap: 0.2rem;
+ justify-content: flex-end;
+ gap: 0.4rem;
}
-.live-run-actions form {
+.live-footer-actions form {
display: flex;
}
-.live-run-actions button:not(.text-button) {
- min-width: 1.65rem;
- min-height: 1.65rem;
- padding: 0;
+.live-footer-actions button {
+ white-space: nowrap;
}
-.live-run-actions .text-button {
- padding: 0.3rem;
- font-size: 0.62rem;
+.live-finished-label {
+ color: var(--gold-strong);
+ font-size: 0.75rem;
}
-.live-reserve {
- padding: 1rem;
- border: 1px solid color-mix(in srgb, var(--gold) 34%, var(--line));
- border-radius: 1rem;
- background: color-mix(in srgb, var(--gold) 5%, var(--panel));
-}
+@media (max-width: 46rem) {
+ .live-page {
+ gap: 0.4rem;
+ padding: max(0.4rem, env(safe-area-inset-top)) max(0.4rem, env(safe-area-inset-right))
+ max(0.4rem, env(safe-area-inset-bottom)) max(0.4rem, env(safe-area-inset-left));
+ }
-.live-reserve > header {
- display: flex;
- justify-content: space-between;
- font-size: 0.7rem;
- font-weight: 720;
-}
+ .live-command-header {
+ grid-template-areas:
+ 'context menu'
+ 'countdown output'
+ 'run run';
+ grid-template-columns: minmax(0, 1fr) auto;
+ gap: 0.4rem;
+ padding: 0.45rem;
+ }
-.live-reserve > p {
- margin: 0.65rem 0 0;
- color: var(--text-muted);
- font-size: 0.68rem;
+ .live-section-context {
+ grid-area: context;
+ }
+
+ .section-countdown {
+ grid-area: countdown;
+ min-width: 0;
+ text-align: left;
+ }
+
+ .live-output-state {
+ grid-area: output;
+ min-width: 0;
+ padding-inline: 0.35rem;
+ }
+
+ .live-menu {
+ grid-area: menu;
+ }
+
+ .live-mobile-run-button {
+ display: block;
+ grid-area: run;
+ width: 100%;
+ padding: 0.42rem 0.55rem;
+ border: 1px solid var(--line);
+ border-radius: 0.6rem;
+ background: var(--panel-soft);
+ color: var(--text);
+ font-size: 0.67rem;
+ font-weight: 740;
+ }
+
+ .live-desktop-rail-toggle {
+ display: none;
+ }
+
+ .live-workspace,
+ .live-page.without-rail .live-workspace {
+ grid-template-columns: minmax(0, 1fr);
+ }
+
+ .live-page.without-rail .live-rail,
+ .live-rail {
+ display: grid;
+ position: fixed;
+ z-index: 80;
+ inset: max(0.4rem, env(safe-area-inset-top)) max(0.4rem, env(safe-area-inset-right))
+ max(0.4rem, env(safe-area-inset-bottom)) auto;
+ width: min(22rem, calc(100vw - 0.8rem - env(safe-area-inset-left)));
+ transform: translateX(calc(100% + 1rem));
+ visibility: hidden;
+ transition:
+ transform 160ms ease,
+ visibility 0s linear 160ms;
+ }
+
+ .live-rail.open {
+ transform: translateX(0);
+ visibility: visible;
+ transition-delay: 0s;
+ }
+
+ .live-rail > header > button {
+ display: grid;
+ }
+
+ .live-rail-backdrop.visible {
+ display: block;
+ position: fixed;
+ z-index: 70;
+ inset: 0;
+ border: 0;
+ background: color-mix(in srgb, var(--bg) 56%, transparent);
+ backdrop-filter: blur(3px);
+ }
+
+ .live-current-scroll {
+ padding: 0.9rem;
+ }
+
+ .live-current h1 {
+ font-size: clamp(1.35rem, 7vw, 2rem);
+ }
+
+ .live-support-actions {
+ display: grid;
+ }
+
+ .research-details,
+ .live-materials {
+ width: 100%;
+ min-width: 0;
+ }
+
+ .live-action-bar {
+ display: grid;
+ gap: 0.4rem;
+ padding: 0.45rem;
+ }
+
+ .live-next-summary strong {
+ font-size: 0.68rem;
+ }
+
+ .live-footer-actions {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 0.3rem;
+ }
+
+ .live-footer-actions form,
+ .live-footer-actions button {
+ width: 100%;
+ }
+
+ .live-footer-actions button {
+ min-height: 2.2rem;
+ padding-inline: 0.4rem;
+ white-space: normal;
+ overflow-wrap: anywhere;
+ font-size: 0.64rem;
+ }
+
+ .live-clear-form {
+ grid-column: 1 / -1;
+ }
+
+ .live-finished-label {
+ grid-column: 1 / -1;
+ text-align: center;
+ }
}
.heinzlive-output {
@@ -1451,75 +1943,6 @@ textarea:focus {
text-transform: uppercase;
}
-.section-countdown {
- display: flex;
- align-items: baseline;
- justify-content: space-between;
- gap: 1rem;
- margin-bottom: 1rem;
- padding: 0.8rem 1rem;
- border: 1px solid var(--line);
- border-radius: 0.65rem;
- background: var(--surface-raised);
-}
-
-.section-countdown strong {
- font-variant-numeric: tabular-nums;
-}
-
-.section-countdown.warning {
- border-color: #f3ba63;
- color: #f3ba63;
-}
-
-.section-countdown.overrun {
- border-color: #ff776d;
- color: #ff776d;
-}
-
-.live-materials {
- display: grid;
- grid-template-columns: minmax(9rem, 0.45fr) repeat(2, minmax(0, 1fr));
- gap: 1rem;
- margin-top: 1rem;
- padding: 1rem;
- border: 1px solid var(--line);
- border-radius: 0.7rem;
- background: var(--surface-raised);
-}
-
-.live-materials h3,
-.live-materials ul {
- margin: 0;
-}
-
-.live-materials ul {
- display: grid;
- gap: 0.45rem;
- padding: 0;
- list-style: none;
-}
-
-.live-material-group li,
-.live-material-group small {
- display: block;
-}
-
-.live-material-group small {
- color: var(--text-muted);
-}
-
-@media (max-width: 760px) {
- .live-materials {
- grid-template-columns: 1fr;
- }
-
- .section-countdown {
- align-items: flex-start;
- flex-direction: column;
- }
-}
-
.output-content h1,
.output-idle h1 {
max-width: 16ch;
@@ -5387,39 +5810,6 @@ textarea:focus {
.section-editor {
margin-left: 0;
}
- .live-header-actions {
- width: 100%;
- overflow-x: auto;
- justify-content: flex-start;
- padding-bottom: 0.2rem;
- }
- .live-header-actions > * {
- flex: 0 0 auto;
- }
- .live-grid {
- grid-template-columns: 1fr;
- }
- .live-current {
- padding: 1.3rem;
- border-radius: 1.3rem;
- }
- .live-current-actions {
- align-items: stretch;
- }
- .live-current-actions form:last-child {
- margin-left: 0;
- }
- .live-current h2 {
- margin: 1.1rem 0 1.5rem;
- }
- .live-kicker {
- align-items: flex-start;
- flex-direction: column;
- gap: 0.2rem;
- }
- .live-rail {
- grid-template-columns: 1fr;
- }
.settings-grid {
grid-template-columns: 1fr;
}
@@ -5478,19 +5868,6 @@ body:has(.heinzlive-output.empty),
padding: 0;
}
-.live-toolbar {
- position: relative;
- z-index: 10;
-}
-
-.live-output-settings[open] {
- z-index: 50;
-}
-
-.live-output-settings > div {
- z-index: 50;
-}
-
.template-popover {
width: min(64rem, calc(100vw - var(--sidebar-width) - 4rem));
max-height: calc(100dvh - 8rem);
diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts
index 6e6db09..fd46542 100644
--- a/src/routes/+layout.server.ts
+++ b/src/routes/+layout.server.ts
@@ -2,5 +2,6 @@ import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = ({ locals, url }) => ({
user: url.pathname.startsWith('/heinzlive/') ? null : locals.user,
- presentationMode: url.pathname.startsWith('/heinzlive/')
+ presentationMode: url.pathname.startsWith('/heinzlive/'),
+ liveControllerMode: url.pathname === '/live'
});
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index a9a291a..d7be73a 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -34,6 +34,8 @@
{#if data.presentationMode}
{@render children()}
+{:else if data.user && data.liveControllerMode}
+