/*
 * App-specific Styles
 *
 * This file contains app-specific CSS rules and custom classes.
 * Bootstrap overrides belong in css/bootstrap.css.
 */

html {
    /* Base font size for accessibility - all rem values scale from this */
    font-size: 1.2rem;
    /* Reserve the scrollbar gutter so transient height changes (e.g. survey-runner
       flip animations) cannot toggle the scrollbar and shift the layout. */
    scrollbar-gutter: stable;
}

/* While a survey-runner flip plays, paint the scrollbar thumb and track transparent
   without removing the scrollbar from the box model. That keeps the scroll container
   intact, so `scrollbar-gutter: stable` continues to reserve the gutter (no horizontal
   jump), while the user sees no thumb blinking in/out as the wrapper height fluctuates
   mid-rotation. `scrollbar-width: none` and `::-webkit-scrollbar { width: 0 }` would
   shrink the scrollbar away and lose the gutter reservation in most browsers — using
   transparent colors avoids that. */
html.runner-flip-active {
    scrollbar-color: transparent transparent;
}

html.runner-flip-active::-webkit-scrollbar-thumb,
html.runner-flip-active::-webkit-scrollbar-track {
    background-color: transparent;
}

/* Asset-sharing access codes (rendered via RenderTreeBuilder, so styles must be global) */
.access-codes-block {
    background-color: var(--primary-light);
    border-radius: 0.5rem;
    padding: 0.75rem 1rem;
}

.access-codes-block .access-codes-table {
    border-collapse: collapse;
    table-layout: fixed;
    background-color: transparent;
    width: 100%;
}

.access-codes-block .access-codes-cell {
    padding: 0.75rem 0.75rem;
    vertical-align: middle;
    width: 33.3333%;
    background-color: transparent;
}

.access-codes-block .access-codes-cell + .access-codes-cell {
    border-left: 1px solid rgba(0, 0, 0, 0.08);
}

.access-codes-block .access-codes-label {
    font-size: 0.95rem;
    margin-bottom: 0.25rem;
}

.access-codes-block .access-code-value {
    font-size: 1.6rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    line-height: 1.1;
    margin-bottom: 0.5rem;
}

.access-codes-block .access-codes-actions {
    margin-top: 0.25rem;
}


body {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

/* Material Symbols in table cells auto vertical alignment */
td .material-symbols-outlined {
    vertical-align: middle;
}

/* Filled icon class for Material Symbols */
.material-symbols-outlined.filled {
    font-variation-settings: 'FILL' 1;
}

.content {
    padding-top: 1rem;
    flex: 1;
}

@media (max-width: 767.98px) {
    .content {
        padding-top: 0;
    }
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid var(--bs-success);
}

.invalid {
    outline: 1px solid var(--bs-danger);
}

/* Inputs with `text-uppercase` should still show their placeholder in normal case —
   uppercasing the hint text makes labels like "TEAM-CODE ..." look shouty and breaks
   readability without adding information. */
input.text-uppercase::placeholder,
textarea.text-uppercase::placeholder {
    text-transform: none;
}

.validation-message {
    color: var(--bs-danger);
}

/* Note: SVG in data-URLs cannot use CSS variables - color #FFE500 for warning icon, #b32121 for background */
.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

.blazor-error-boundary::after {
    content: "An error has occurred."
}

/* ==========================================================================
   Icon Colors
   ========================================================================== */

.icon-color-primary { color: var(--icon-color-primary); }
.icon-color-primary-light { color: var(--icon-color-primary-light); }
.icon-color-primary-pale { color: var(--icon-color-primary-pale); }
.icon-color-red { color: var(--icon-color-red); }
.icon-color-crimson { color: var(--icon-color-crimson); }
.icon-color-orange { color: var(--icon-color-orange); }
.icon-color-amber { color: var(--icon-color-amber); }
.icon-color-yellow { color: var(--icon-color-yellow); }
.icon-color-lime { color: var(--icon-color-lime); }
.icon-color-green { color: var(--icon-color-green); }
.icon-color-teal { color: var(--icon-color-teal); }
.icon-color-cyan { color: var(--icon-color-cyan); }
.icon-color-blue { color: var(--icon-color-blue); }
.icon-color-indigo { color: var(--icon-color-indigo); }
.icon-color-purple { color: var(--icon-color-purple); }
.icon-color-violet { color: var(--icon-color-violet); }
.icon-color-pink { color: var(--icon-color-pink); }
.icon-color-brown { color: var(--icon-color-brown); }
.icon-color-gold { color: var(--icon-color-gold); }
.icon-color-mint { color: var(--icon-color-mint); }
.icon-color-navy { color: var(--icon-color-navy); }
.icon-color-magenta { color: var(--icon-color-magenta); }
.icon-color-gray { color: var(--icon-color-gray); }
.icon-color-black { color: var(--icon-color-black); }

/* Color swatch — quadratisch für den ColorPickerModal-Grid. Globale Klasse, damit
   sie auch außerhalb scoped CSS (z. B. in den Table-Editoren) greift.
   Größe: 3rem statt 4rem, damit mindestens 6 Swatches pro Reihe Platz haben. */
.icon-color-swatch {
    display: inline-block;
    width: 3rem;
    height: 3rem;
    border: 0.125rem solid var(--bs-border-color);
    border-radius: 0.25rem;
    cursor: pointer;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
    padding: 0;
}

.icon-color-swatch:hover {
    transform: scale(1.1);
}

.icon-color-swatch.active {
    border-color: var(--bs-dark);
    border-width: 0.2rem;
    box-shadow: 0 0 0 0.15rem rgba(0, 0, 0, 0.15);
}

/* In-table-Variante: so groß wie ein Material-Symbols-Icon (1.5rem-Default).
   Kein Hover-Effekt, weil der Cursor-Pointer schon vom td gesetzt wird. */
.icon-color-swatch-sm {
    display: inline-block;
    width: 1.5rem;
    height: 1.5rem;
    border: 0.0625rem solid var(--bs-border-color);
    border-radius: 0.2rem;
    padding: 0;
}

/* Color-Family-Vorschau als zusammenhängender Gradient-Balken statt vieler einzelner
   Quadrate. Vorteil: die Preview-Breite bleibt unabhängig von der Skala (3..7) konstant —
   die Spaltenbreite ändert sich nie. Wird in den Vorlagen-Boxen verwendet. */
.color-family-preview {
    display: block;
    width: 100%;
    /* Volle Höhe des umgebenden Buttons (via .family-presets-grid min-height
       gesteuert). align-self: stretch sorgt dafür, dass die Center-Ausrichtung
       des Buttons den Span nicht auf seine Intrinsic-Höhe schrumpft. */
    height: auto;
    align-self: stretch;
    /* Transparente Border statt entfernter Border — der Platz bleibt im Layout
       reserviert, damit Höhe und Breite beim Umschalten "mit/ohne Rahmen" exakt
       gleich bleiben. */
    border: 0.0625rem solid transparent;
    border-radius: 0.2rem;
    padding: 0;
}

/* Color-Family-Button: der Button selbst soll keinen sichtbaren Rahmen und kein
   Padding um den Gradient haben — visuell sind nur die Gradient-Leisten zu sehen.
   Der `--bs-btn-*`-Override hält die Breite/Höhe konstant (Border bleibt 0.0625rem
   aber transparent), nur die Farbe ändert sich beim Selektieren auf Primary, damit
   die ausgewählte Familie erkennbar bleibt. */
.color-family-btn {
    --bs-btn-padding-y: 0;
    --bs-btn-padding-x: 0;
    --bs-btn-border-color: transparent;
    --bs-btn-bg: transparent;
    --bs-btn-hover-border-color: var(--bs-primary);
    --bs-btn-hover-bg: transparent;
    --bs-btn-active-border-color: var(--bs-primary);
    --bs-btn-active-bg: transparent;
}

.color-family-btn.btn-primary {
    --bs-btn-border-color: var(--bs-primary);
    --bs-btn-bg: transparent;
    --bs-btn-color: var(--bs-primary);
    --bs-btn-hover-bg: transparent;
    --bs-btn-hover-color: var(--bs-primary);
    --bs-btn-active-bg: transparent;
    --bs-btn-active-color: var(--bs-primary);
    /* Permanenter Primary-Ring um die selektierte Farbfamilie. Box-Shadow statt
       Border, damit der Ring nicht im Layout zählt (Höhe/Breite bleiben gleich)
       und stabil sichtbar bleibt, auch wenn der Button den Fokus verliert. Der
       !important überschreibt Bootstraps eigene Focus-Shadow, die sonst die
       statische Markierung wieder wegnimmt. */
    box-shadow: 0 0 0 0.1875rem var(--bs-primary) !important;
}

/* Family-Presets-Grid: gemeinsamer min-height für die drei Spalten Text / Icons /
   Farben — damit die Buttons nicht je nach Inhalt unterschiedlich hoch werden. Der
   Wert orientiert sich an der natürlichen Höhe der Text-Buttons. Mit min-height
   bleiben die Buttons mindestens so hoch, schrumpfen aber nicht, wenn der Inhalt
   (Gradient-Balken oder Icons) kleiner wäre. */
.family-presets-grid .btn-sm {
    min-height: 2.5rem;
}

/* Gauge-Chart-Legende — unter dem SVG-Halbkreis. Wrap-fähig + zentriert,
   damit lange Beschriftungen sauber umbrechen. */
.gauge-legend {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.5rem 1rem;
    margin-top: 0.5rem;
    font-size: 0.875rem;
}

.gauge-legend-item {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
}

.gauge-legend-swatch {
    display: inline-block;
    width: 0.85rem;
    height: 0.85rem;
    border-radius: 50%;
}

/* Klickbare Bootstrap-Karte (z. B. Item-Typ-Präferenzen). Wird mit `card clickable-card`
   kombiniert und reagiert auf Hover mit subtiler Hervorhebung. Interaktive Kind-Elemente
   wie Reset-Buttons müssen @onclick:stopPropagation setzen, damit der Klick nicht zum
   Card-Handler propagiert. */
.clickable-card {
    cursor: pointer;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.clickable-card:hover {
    border-color: var(--bs-primary);
    /* Inset shadow verdickt visuell den Rahmen auf ca. 2px, ohne dass das Layout springt. */
    box-shadow: inset 0 0 0 1px var(--bs-primary);
}

/* Icon picker grid buttons */
.icon-grid-btn {
    width: 3rem;
    height: 3rem;
    padding: 0;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--radius-sm);
    background: var(--bs-white);
    cursor: pointer;
    transition: var(--transition-base);
}

.icon-grid-btn:hover {
    border-color: var(--bs-primary);
    background-color: var(--selected-bg);
}

.icon-grid-btn.active {
    border: 0.25rem solid var(--bs-primary);
    box-shadow: 0 0 0 0.15rem rgba(var(--primary-rgb), 0.3);
    background-color: var(--selected-bg);
}

/* ==========================================================================
   Clickable Elements
   ========================================================================== */

.clickable,
.cursor-pointer {
    cursor: pointer;
}

/* Selectable cards (e.g. mode selection, creation mode) */
.card-selectable {
    display: block;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--radius-md);
    padding: 1.5rem;
    height: 100%;
    cursor: pointer;
    background-color: var(--bs-white);
    transition: var(--transition-colors), box-shadow var(--duration-base) var(--ease-out),
                transform var(--duration-fast) var(--ease-out);
}

.card-selectable:hover,
.card-selectable:focus {
    background-color: var(--bs-light);
    border-color: var(--bs-primary);
    box-shadow: var(--shadow-md);
    outline: none;
}

.card-selectable:active {
    transform: scale(0.99);
}

.card-selectable.active {
    border-color: var(--bs-primary);
    background-color: var(--selected-bg);
    box-shadow: var(--shadow-sm);
}

/* ==========================================================================
   Tutorial Modal
   ========================================================================== */

.tutorial-icon {
    font-size: 5rem;
}

.tutorial-title {
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--bs-primary);
}

.tutorial-page-indicator {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    border: 0.125rem solid var(--bs-border-color);
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

.tutorial-page-indicator.active {
    background-color: var(--bs-primary);
    border-color: var(--bs-primary);
    color: #fff;
}

.tutorial-close {
    position: absolute;
    top: 0.75rem;
    right: 0.75rem;
    z-index: 10;
}

/* Setup wizard */
.setup-wizard-container {
    max-width: 56rem;
    margin: 0 auto;
}

.setup-wizard-icon {
    font-size: 4rem;
}

.setup-wizard-title {
    font-size: 1.75rem;
    font-weight: 700;
    color: var(--bs-primary);
}

.setup-wizard-step {
    padding: 1.5rem 0;
}

.setup-wizard-intro-body {
    max-width: 40rem;
    margin: 0 auto;
    font-size: 1.1rem;
    line-height: 1.6;
}

.setup-wizard-intro-body p {
    margin-bottom: 1rem;
}

.setup-wizard-progress {
    display: flex;
    justify-content: center;
    gap: 0.5rem;
    margin-bottom: 1.5rem;
}

.setup-wizard-progress-indicator {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    border: 0.125rem solid var(--bs-border-color);
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

.setup-wizard-progress-indicator.active {
    background-color: var(--bs-primary);
    border-color: var(--bs-primary);
    color: #fff;
}

.setup-wizard-progress-indicator.completed {
    background-color: var(--bs-success);
    border-color: var(--bs-success);
    color: #fff;
}

/* Survey status alerts — dedicated, do not reuse alert-primary */
.alert-survey-online {
    color: var(--white);
    background-color: var(--bs-success);
    border-color: var(--bs-success);
}

.alert-survey-online .material-symbols-outlined {
    color: var(--white);
}

.alert-survey-offline {
    color: var(--bs-body-color);
    background-color: #e9ecef;
    border-color: #dee2e6;
}

.alert-survey-offline .material-symbols-outlined {
    color: var(--bs-body-color);
}

/* Paper-Backup Tutorial — page layout helpers */
.tutorial-step-badge {
    display: inline-block;
    padding: 0.25rem 0.75rem;
    border-radius: 999px;
    background-color: var(--primary-bg-subtle);
    color: var(--primary-dark);
    font-size: 0.85rem;
    font-weight: 600;
    letter-spacing: 0.02rem;
}

.tutorial-step-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 4rem;
    height: 4rem;
    border-radius: 50%;
    background-color: var(--bs-primary);
    color: #fff;
    margin: 0 auto;
}

.tutorial-step-icon .material-symbols-outlined {
    font-size: 2.25rem;
}

.tutorial-step-title {
    font-size: 1.35rem;
    font-weight: 600;
    text-align: center;
    margin-bottom: 0.75rem;
}

.tutorial-step-body {
    font-size: 1rem;
    line-height: 1.55;
    text-align: center;
    color: var(--bs-body-color);
}

.tutorial-option-card {
    display: flex;
    align-items: flex-start;
    gap: 0.85rem;
    padding: 0.85rem 1rem;
    border-radius: var(--radius-md);
    background-color: var(--primary-bg-subtle);
    border: 1px solid rgba(var(--primary-rgb), 0.2);
}

.tutorial-option-card .material-symbols-outlined {
    color: var(--bs-primary);
    flex-shrink: 0;
    margin-top: 0.1rem;
}

.tutorial-option-card-text {
    text-align: left;
    line-height: 1.45;
}

/* ==========================================================================
   Selected Table Rows
   Same primary background as hover for consistent highlighting.
   ========================================================================== */

.table .selected-row > td {
    background-color: var(--selected-bg);
}

/* ==========================================================================
   Active filter/search indicator
   Applied to form-select and form-control elements when a filter is active.
   ========================================================================== */

.filter-active {
    background-color: var(--selected-bg);
}

/* ==========================================================================
   Search-term highlight inside the item-browser result list
   Bootstrap's default <mark> uses a yellow background; we override here so the
   match stands out in primary green (matches the rest of the app palette).
   No padding so the highlight sits flush against the surrounding text.
   ========================================================================== */

.search-match {
    background-color: var(--bs-primary);
    color: var(--bs-white);
    padding: 0;
}

/* ==========================================================================
   Item-browser modal — collapsible topic drawer
   In topic-search mode the modal body is a flex row: drawer on the left, result
   list on the right. The drawer can be collapsed to a thin strip so the result
   list takes the full modal width when the user wants to focus on browsing hits.
   ========================================================================== */

.item-browser-drawer {
    width: 22rem;
    flex-shrink: 0;
}

.item-browser-drawer-strip {
    width: 2.5rem;
    flex-shrink: 0;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding-top: 0.5rem;
    border-right: 1px solid var(--bs-border-color);
    cursor: pointer;
    color: var(--bs-primary);
}

.item-browser-drawer-strip:hover {
    background-color: var(--selected-bg);
}

/* Bootstrap doesn't ship a min-w-0 utility; we need it on flex children that
   should be allowed to shrink narrower than their natural content width — without
   it, the result-list column refuses to give space back when the drawer expands. */
.min-w-0 {
    min-width: 0;
}


/* ==========================================================================
   Homepage: four crossed-out subjunctive verbs (hätte/müsste/sollte/könnte)
   Stacked, large, with a bold yellow strikethrough — sets up the "stop wishing,
   start doing" tone for the schoolAIssistant landing page.
   ========================================================================== */

.home-stricken-words {
    font-size: 3rem;
    font-weight: 500;
    line-height: 1.2;
    margin: 1.5rem 0;
    color: var(--white);
}

/* The line itself is a pseudo-element so it can extend past the word ends —
   text-decoration: line-through would be glued to the glyph width and look
   timid. The inner <span> anchors the pseudo to the word's natural box; the
   surrounding <li> keeps the centring via the parent's text-center. */
.home-stricken-word {
    position: relative;
    display: inline-block;
}

.home-stricken-word::after {
    content: "";
    position: absolute;
    top: 58%;
    left: -3rem;
    right: -3rem;
    height: 0.3rem;
    background-color: var(--bs-warning);
    transform: translateY(-50%);
}

/* "Answer" below the crossed-out subjunctives — the same scale, but in the
   yellow that the strikethrough lines use. Slightly heavier weight so it reads
   as the punchline, not as one more wishful entry. */
.home-stricken-answer {
    font-size: 3rem;
    font-weight: 700;
    line-height: 1.2;
    color: var(--bs-warning);
    margin-bottom: 1.5rem;
}

/* ==========================================================================
   Secondary perspective marker
   Used in Test contexts to signal that a perspective (parents, teachers,
   self-assessment) is typically less relevant than learner perspectives
   (pupils, students, trainees). The entry stays fully active and selectable;
   it is NOT muted (muted would signal disabled). Italic + a slightly lighter
   weight achieves "less central" without "unavailable".
   ========================================================================== */

.perspective-secondary {
    font-style: italic;
    font-weight: 400;
}

/* ==========================================================================
   Empty State Icons
   ========================================================================== */

.empty-state-icon {
    font-size: 4rem;
}

/* ==========================================================================
   Empty Layout shell (used by /login, /register, /agb, /datenschutz, /impressum, etc.)
   Sticky footer holding the three legal links. The shell is flex-column so the
   footer always sits at the bottom of the viewport even when the page content
   is short.
   ========================================================================== */

.empty-layout-shell {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

.empty-layout-shell > *:not(.empty-layout-footer) {
    flex: 1 0 auto;
    min-height: 0;
}

.empty-layout-footer {
    flex: 0 0 auto;
    padding: 0.75rem 1rem;
    text-align: center;
    font-size: 0.85rem;
    color: var(--bs-secondary-color);
    background: var(--white);
}

.empty-layout-footer a {
    color: var(--bs-secondary-color);
    text-decoration: none;
    cursor: pointer;
}

.empty-layout-footer a:hover {
    color: var(--bs-primary);
}

/* ==========================================================================
   Full Page (Home, Login)
   ========================================================================== */

/* The 100vh min-height is now provided by .empty-layout-shell; here we just
   stretch to fill whatever the shell allocates above the footer. */
.fullpage-container {
    flex: 1 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
    padding: 3.125rem 1rem 1rem;
}

.fullpage-body {
    width: 100%;
    max-width: 28rem;
}


.fullpage-body .form-label {
    color: var(--white);
}

.fullpage-body .form-control {
    background-color: rgba(255, 255, 255, 0.15);
    border: none;
    color: var(--white);
}

.fullpage-body .form-control::placeholder {
    color: rgba(255, 255, 255, 0.5);
}

.fullpage-body .form-control:focus {
    background-color: rgba(255, 255, 255, 0.25);
    color: var(--white);
    box-shadow: 0 0 0 0.25rem rgba(255, 255, 255, 0.15);
}

.fullpage-body .input-group .btn-outline-secondary {
    background-color: rgba(255, 255, 255, 0.15);
    border: none;
    color: var(--white);
}

.fullpage-body .input-group .btn-outline-secondary:hover {
    background-color: rgba(255, 255, 255, 0.25);
    color: var(--white);
}

.fullpage-body .form-check-input {
    background-color: rgba(255, 255, 255, 0.15);
    border-color: rgba(255, 255, 255, 0.3);
}

.fullpage-body .form-check-input:checked {
    background-color: var(--white);
    border-color: var(--white);
    /* Bobble in primary color — SVG can't use CSS variables (see CLAUDE.md §4.4) */
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%231e5631'/%3e%3c/svg%3e");
}

.fullpage-body .form-check-label {
    color: var(--white);
}

.fullpage-body .form-text {
    color: rgba(255, 255, 255, 0.6);
}

.fullpage-body .btn-primary {
    background-color: var(--white);
    border-color: var(--white);
    color: var(--primary);
    font-weight: 600;
}

.fullpage-body .btn-primary:hover {
    background-color: rgba(255, 255, 255, 0.9);
    border-color: rgba(255, 255, 255, 0.9);
    color: var(--primary);
}

.progress-indicator-preview {
    padding: 0.6rem 0.75rem;
    background: var(--selected-bg);
    border-radius: 0.375rem;
    display: flex;
    align-items: center;
    min-height: 2.4rem;
}

.fullpage-body .progress {
    background-color: rgba(255, 255, 255, 0.2);
}

.fullpage-body .progress-bar {
    background-color: rgba(255, 255, 255, 0.7);
}

.fullpage-hint {
    color: rgba(255, 255, 255, 0.7);
}

.fullpage-icon-unchecked {
    color: rgba(255, 255, 255, 0.15);
}

.fullpage-top-left {
    position: fixed;
    top: 1rem;
    left: 1rem;
    z-index: 1000;
}

.fullpage-top-left a {
    color: var(--white);
    text-decoration: none;
    display: flex;
    align-items: center;
}

.fullpage-top-left a:hover {
    color: rgba(255, 255, 255, 0.8);
}

.fullpage-top-right {
    position: fixed;
    top: 1rem;
    right: 1rem;
    z-index: 1000;
}

a.fullpage-link,
a.fullpage-link:visited,
a.fullpage-link:active {
    color: var(--white);
    text-decoration: none;
    opacity: 0.85;
}

a.fullpage-link:hover {
    color: var(--white);
    opacity: 1;
}

a.fullpage-link.fullpage-link-prominent,
a.fullpage-link.fullpage-link-prominent:visited,
a.fullpage-link.fullpage-link-prominent:active {
    color: var(--warning);
    opacity: 1;
}

a.fullpage-link.fullpage-link-prominent:hover {
    color: var(--warning-hover);
}

/* ==========================================================================
   App Brand
   ========================================================================== */

.app-brand {
    display: inline-flex;
    align-items: center;
    line-height: 1;
    color: var(--white);
    text-decoration: none;
}

.app-brand-logo {
    flex-shrink: 0;
    display: block;
    height: auto;
    width: auto;
}

.app-brand-title {
    display: inline-flex;
    align-items: center;
    font-family: 'Courier New', Courier, 'Nimbus Mono L', 'Liberation Mono', monospace;
    font-weight: 700;
    letter-spacing: -0.01em;
    white-space: nowrap;
    line-height: 1;
}

.app-brand-text {
    display: inline-block;
}

.app-brand-ai {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.55em;
    height: 1.55em;
    border-radius: 50%;
    background-color: var(--secondary);
    color: var(--white);
    line-height: 1;
    margin: 0 0 0 3px;
    padding: 0 0 0 0.15em;
    text-align: center;
    box-sizing: border-box;
    letter-spacing: 0;
}

/* Hero variant: logo stacked above title, used on full-page layouts */
.app-brand-hero {
    flex-direction: column;
    gap: 1.25rem;
    margin: 0 0 2rem 0;
}

.app-brand-hero .app-brand-logo {
    display: none;
}

.app-brand-hero .app-brand-title {
    font-size: clamp(2rem, 6.5vw, 4.5rem);
}

/* Compact variant: logo left of title, used in the top bar */
.app-brand-compact {
    flex-direction: row;
    gap: 0.6rem;
}

.app-brand-compact .app-brand-logo {
    display: none;
}

.app-brand-compact .app-brand-title {
    font-size: 1.4rem;
}

@media (max-width: 991.98px) {
    .app-brand-hero {
        gap: 1rem;
        margin-bottom: 1.75rem;
    }

    .app-brand-hero .app-brand-title {
        font-size: clamp(1.85rem, 8vw, 3.25rem);
    }
}

@media (max-width: 575.98px) {
    .app-brand-hero {
        gap: 0.85rem;
        margin-bottom: 1.5rem;
    }

    .app-brand-hero .app-brand-title {
        font-size: clamp(1.6rem, 9vw, 2.5rem);
    }

    .app-brand-compact .app-brand-title {
        font-size: 1.1rem;
    }
}

/* ==========================================================================
   Page Title
   ========================================================================== */

.page-title {
    color: var(--bs-primary);
    font-size: 2rem;
    font-weight: 600;
    line-height: 1;
    margin-bottom: 1rem;
}

/* Info icon button placed next to a .page-title. Sized to visually match
   the 2rem title so flexbox center alignment also reads as centered. */
.page-title-info-btn {
    color: var(--bs-primary);
    line-height: 1;
}

.page-title-info-btn .material-symbols-outlined {
    font-size: 2rem;
}

.page-subtitle {
    color: var(--bs-secondary);
    font-size: 1.7rem;
    font-weight: 500;
}

/* ==========================================================================
   Customer Type Badges
   All badges derive from the primary color at different lightness levels.
   School = 100% primary (most important customer type).
   ========================================================================== */

.customer-type-badge {
    display: inline-block;
    width: 7rem;
    text-align: left;
}

.customer-type-school,
.customer-type-carrier,
.customer-type-organization,
.customer-type-individual {
    background-color: rgb(var(--bs-primary-rgb)) !important;
    color: white;
}

/* ==========================================================================
   Truncated Table Cells
   Prevents line breaks and clips overflow with ellipsis.
   Requires table-layout: fixed on the table.
   ========================================================================== */

.text-truncate-cell {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 10rem;
}

/* Column hover highlighting for tables with class 'column-hover' */
.column-hover-active {
    background-color: var(--selected-bg) !important;
}

/* Service-period range: two DatePickers in an input-group, minimum width */
.service-period-group {
    width: auto;
}

.service-period-group .form-control {
    width: 7rem;
    flex: 0 0 auto;
}

/* Flatpickr custom footer with OK/Cancel buttons */
.flatpickr-footer {
    display: flex;
    gap: 0.5rem;
    padding: 0.5rem;
    border-top: 1px solid var(--bs-border-color);
}

.flatpickr-footer-btn {
    flex: 1;
    padding: 0.4rem 0.75rem;
    border: none;
    border-radius: 0.25rem;
    font-size: 0.875rem;
    cursor: pointer;
}

.flatpickr-delete {
    background-color: var(--bs-danger);
    color: var(--bs-white);
}

.flatpickr-delete:hover {
    opacity: 0.85;
}

.flatpickr-cancel {
    background-color: var(--bs-dark);
    color: var(--bs-white);
}

.flatpickr-cancel:hover {
    opacity: 0.85;
}

.flatpickr-ok {
    background-color: var(--bs-primary);
    color: var(--bs-white);
}

.flatpickr-ok:hover {
    opacity: 0.85;
}

/* ============================================================
 * Survey Item Components (si-*)
 * Shared styles for SurveyItemCard and individual item type views.
 * Used in preview, card view, and production surveys.
 * ============================================================ */

/* Likert / Choice options */
.si-likert {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
}

.si-likert.si-likert-tiles {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0.5rem;
    align-items: stretch;
}

@media (min-width: 576px) {
    .si-likert.si-likert-tiles {
        grid-template-columns: repeat(auto-fit, minmax(calc(33.3333% - 0.5rem), 1fr));
    }
}

@media (min-width: 992px) {
    .si-likert.si-likert-tiles {
        grid-template-columns: repeat(auto-fit, minmax(calc(20% - 0.5rem), 1fr));
    }
}

.si-likert.si-likert-tiles .si-likert-option {
    flex-direction: column;
    justify-content: flex-start;
    text-align: center;
    padding: 0.75rem 0.5rem;
}

.si-likert.si-likert-tiles .si-likert-icon {
    font-size: 2rem;
    height: 2.5rem;
    display: flex;
    align-items: center;
    justify-content: center;
}

.si-likert.si-likert-tiles .si-likert-text {
    margin-top: 0.125rem;
}

.si-likert-option {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    padding: 0.5rem 0.75rem;
    border-radius: 0.5rem;
    cursor: pointer;
    transition: all 0.15s ease;
    border: 1px solid var(--bs-border-color);
    background: var(--bs-white);
}

/* ---- Strukturmerkmal-Item runner view ---- */

.si-traits {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.si-trait-option {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    padding: 0.5rem 0.75rem;
    border-radius: 0.5rem;
    border: 1px solid var(--bs-border-color);
    background: var(--bs-white);
    cursor: pointer;
    transition: all 0.15s ease;
    margin: 0;
}

.si-trait-option:hover {
    background: var(--bs-light);
    border-color: var(--bs-primary);
}

.si-trait-option.selected {
    background: var(--selected-bg);
    border-color: var(--bs-primary);
}

.si-trait-option.locked {
    cursor: default;
    background: var(--bs-light);
    color: var(--bs-secondary-color);
}

.si-trait-option.locked:hover {
    border-color: var(--bs-border-color);
}

.si-trait-option input[type="checkbox"],
.si-trait-option input[type="radio"] {
    margin: 0;
    flex: 0 0 auto;
}

.si-trait-label {
    flex: 1 1 auto;
}

.si-trait-lock-icon {
    flex: 0 0 auto;
    font-size: 1rem;
    color: var(--bs-secondary-color);
}

.si-likert-option:hover {
    background: var(--bs-light);
    border-color: var(--bs-primary);
}

/* Reflection-locked state: the answer is final and the participant is reading the
   correctness feedback. Suppress hover effects, pointer cursor and click events on
   the answer options so they do not look re-editable. */
.si-locked .si-likert-option,
.si-locked .si-binary-option,
.si-locked .si-rating-icon {
    cursor: default;
    pointer-events: none;
}

.si-locked .si-likert-option:hover,
.si-locked .si-binary-option:hover {
    background: var(--bs-white);
    border-color: var(--bs-border-color);
}

.si-locked .si-likert-option.selected:hover,
.si-locked .si-binary-option.selected:hover {
    background: var(--bs-primary);
    border-color: var(--bs-primary);
}

.si-locked .si-likert-option.si-correct:hover,
.si-locked .si-binary-option.si-correct:hover {
    background: var(--bs-success);
    border-color: var(--bs-success);
}

.si-locked .si-likert-option.si-incorrect:hover,
.si-locked .si-binary-option.si-incorrect:hover {
    background: var(--bs-danger);
    border-color: var(--bs-danger);
}

.si-likert-option.selected {
    background: var(--bs-primary);
    color: var(--bs-white);
    border-color: var(--bs-primary);
}

.si-likert-option.si-correct {
    background: var(--bs-success);
    border-color: var(--bs-success);
}

.si-likert-option.si-incorrect {
    background: var(--bs-danger);
    border-color: var(--bs-danger);
}

/* Reflection box shown after the participant answers (test mode, ShowCorrectAnswerAfterResponse) */
.si-reflection {
    margin-top: 1rem;
    padding: 0.75rem 1rem;
    background: var(--bs-light, #f8f9fa);
    border-left: 3px solid var(--bs-primary);
    border-radius: 0.25rem;
    font-size: 0.95rem;
    line-height: 1.5;
}

.si-reflection-correct {
    border-left-color: var(--bs-success);
    background: rgba(var(--bs-success-rgb), 0.08);
}

.si-reflection-incorrect {
    border-left-color: var(--bs-danger);
    background: rgba(var(--bs-danger-rgb), 0.08);
}

/* Missed-correct: a multiple-choice option that was correct but the participant did
   not pick it. Distinct from "incorrect" (= picked the wrong one) so the learner
   sees at a glance which options they would still have needed for full marks. */
.si-reflection-missed {
    border-left-color: var(--bs-warning);
    background: rgba(var(--bs-warning-rgb), 0.10);
}

.si-reflection + .si-reflection {
    margin-top: 1rem;
}

/* Inside the reflection panel the first reflection block must not have any top
   margin so its top edge aligns with the top of the item card. We can't use
   :first-child because the panel's first child is .si-reflection-panel-handle
   (the mobile drag handle, present on every viewport — display:none on desktop).
   Adjacent-sibling from the handle to the first reflection works at all widths
   and only this pair (subsequent blocks keep the .si-reflection + .si-reflection
   inter-block spacing). */
.si-reflection-panel > .si-reflection-panel-handle + .si-reflection {
    margin-top: 0;
}

.si-reflection-label {
    font-weight: 600;
    margin-right: 0.35rem;
}

.si-reflection-editable {
    cursor: pointer;
    transition: filter 0.15s ease, transform 0.15s ease;
}

.si-reflection-editable:hover {
    filter: brightness(0.97);
}

/* Reflection panel — wraps one or more .si-reflection blocks.
   Layout depends on viewport:
   • Wide desktop (≥1200px): floats to the right of the centered item card.
   • Narrow desktop / tablet (768–1199px): sits inline below the card (default flow).
   • Mobile (<768px): renders as a bottom-sheet with a drag handle, collapsed by
     default — tap the handle to expand. */
.si-reflection-panel {
    margin-top: 1rem;
}

.si-reflection-panel-handle {
    display: none;
}

@media (min-width: 1200px) {
    .si-reflection-panel {
        position: absolute;
        left: calc(100% + 2rem);
        /* The .runner-item-wrapper top sits where the numbered badge floats; the
           actual card top is 1.25rem lower (margin-top of .si-card-numbered).
           Align the panel with the card's visual top edge. */
        top: 1.25rem;
        width: 16rem;
        max-height: calc(100% - 1.25rem);
        overflow-y: auto;
        margin-top: 0;
    }
}

@media (max-width: 767.98px) {
    .si-reflection-panel {
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        margin-top: 0;
        max-height: 5.5rem;
        overflow: hidden;
        background: var(--bs-white);
        border-top: 1px solid var(--bs-border-color);
        border-radius: 1rem 1rem 0 0;
        box-shadow: 0 -0.5rem 1rem -0.25rem rgba(0, 0, 0, 0.15);
        padding: 0.25rem 1rem 1rem;
        z-index: 1050;
        transition: max-height 0.3s ease;
    }

    .si-reflection-panel.expanded {
        max-height: 75vh;
        overflow-y: auto;
    }

    .si-reflection-panel-handle {
        display: block;
        width: 3rem;
        height: 0.25rem;
        background: var(--bs-secondary);
        border-radius: 0.125rem;
        margin: 0.25rem auto 0.5rem;
        opacity: 0.4;
        cursor: pointer;
    }

    /* Reserve space at the bottom so the answer card is not hidden behind the
       collapsed bottom-sheet. */
    .runner-card-flip-back .si-card,
    .runner-card-flip-front .si-card {
        margin-bottom: 6rem;
    }
}

.si-likert-option.selected .si-likert-icon,
.si-binary-option.selected .si-binary-icon {
    color: var(--bs-white) !important;
}

.si-likert-icon {
    font-size: 1.5rem;
    flex-shrink: 0;
}

.si-likert-text {
    font-size: 0.875rem;
    line-height: 1.3;
}

/* Single vs. Multiple choice — visual distinction.
   Three layered signals make the mode obvious even to readers who skip the text:
   1. .si-mode-hint (pictogram + label above the options)
   2. .si-choice-indicator (radio circle vs. checkbox inside each option)
   3. The selection behavior itself (single replaces, multi toggles) */
.si-mode-hint {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    color: var(--bs-secondary);
    font-size: 0.875rem;
    margin-bottom: 0.5rem;
}

.si-mode-hint .material-symbols-outlined {
    font-size: 1.125rem;
}

.si-choice-indicator {
    font-size: 1.5rem;
    flex-shrink: 0;
    color: var(--bs-secondary);
    transition: color 0.15s ease;
}

.si-likert-option.selected .si-choice-indicator,
.si-likert-option.si-correct .si-choice-indicator,
.si-likert-option.si-incorrect .si-choice-indicator {
    color: var(--bs-white);
}

/* Flip card — answer phase on the front, reflection phase on the back.
   Used by SurveyRunner to give the participant a clear "commit, then learn"
   moment. Both faces share the same grid cell so the container takes the
   height of whichever face is taller (back tends to be taller because of
   the reflection text). */
.si-flip {
    perspective: 1500px;
}

.si-flip-inner {
    display: grid;
    grid-template-areas: "stack";
    transform-style: preserve-3d;
    transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}

.si-flip-front,
.si-flip-back {
    grid-area: stack;
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
}

.si-flip-back {
    transform: rotateY(180deg);
}

.si-flip.si-flip-flipped .si-flip-inner {
    transform: rotateY(180deg);
}

@media (prefers-reduced-motion: reduce) {
    .si-flip-inner {
        transition: none;
    }
}

/* Reflection face is read-only — kill hover/cursor on the answer options so the
   participant doesn't get a misleading hint that they can still pick something. */
.si-flip-back .si-likert-option,
.si-flip-back .si-binary-option,
.si-flip-back .si-rating-icon,
.si-flip-back .si-rating-point {
    pointer-events: none;
}

/* Rating */
.si-rating {
    display: flex;
    justify-content: center;
    gap: 0.375rem;
    flex-wrap: wrap;
}

.si-rating-icon {
    cursor: pointer;
}

.si-rating-icon .material-symbols-outlined {
    font-size: 3rem;
}

.si-rating-point {
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    border: 0.125rem solid var(--bs-border-color);
    cursor: pointer;
    transition: all 0.15s ease;
    background: var(--bs-white);
}

.si-rating-point:hover {
    border-color: var(--bs-primary);
    transform: scale(1.1);
}

.si-rating-point.selected {
    background: var(--bs-primary);
    border-color: var(--bs-primary);
    color: var(--bs-white);
}

/* Slider */
.si-slider {
    padding: 0.5rem 0;
}

.si-slider-value {
    text-align: center;
    font-size: 2rem;
    font-weight: 700;
    color: var(--bs-primary);
    margin-top: 0.25rem;
}

.si-range-lg::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 50%;
    background: var(--bs-primary);
    border: none;
    margin-top: -0.375rem;
    cursor: pointer;
}

.si-range-lg::-moz-range-thumb {
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 50%;
    background: var(--bs-primary);
    border: none;
    cursor: pointer;
}

.si-range-lg::-webkit-slider-runnable-track {
    height: 0.75rem;
    border-radius: 0.375rem;
    background: var(--bs-white);
    border: 1px solid var(--bs-border-color);
}

.si-range-lg::-moz-range-track {
    height: 0.75rem;
    border-radius: 0.375rem;
    background: var(--bs-white);
    border: 1px solid var(--bs-border-color);
}

/* Binary choice */
.si-binary {
    display: flex;
    gap: 0.75rem;
}

.si-binary-option {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    padding: 1.25rem 0.75rem;
    border: 0.125rem solid var(--bs-border-color);
    border-radius: 0.75rem;
    cursor: pointer;
    transition: all 0.15s ease;
    background: var(--bs-white);
    text-align: center;
    font-weight: 500;
}

.si-binary-option:hover {
    border-color: var(--bs-primary);
    box-shadow: 0 0.125rem 0.5rem rgba(0, 0, 0, 0.08);
}

.si-binary-option.selected {
    border-color: var(--bs-primary);
    background: var(--bs-primary);
    color: var(--bs-white);
}

.si-binary-option.si-correct {
    border-color: var(--bs-success);
    background: var(--bs-success-bg-subtle);
}

.si-binary-option.si-incorrect {
    border-color: var(--bs-danger);
    background: var(--bs-danger-bg-subtle);
}

.si-binary-icon {
    font-size: 2.5rem;
}

/* Short answer — correctness reflection on the input field */
input.form-control.si-correct {
    border-color: var(--bs-success);
    background-color: var(--bs-success-bg-subtle);
}

input.form-control.si-incorrect {
    border-color: var(--bs-danger);
    background-color: var(--bs-danger-bg-subtle);
}

.si-correct-answer-reveal {
    color: var(--bs-success);
    font-size: 0.95rem;
}

/* Free text */
.si-freetext {
    border: 0.125rem solid var(--bs-border-color);
    border-radius: 0.5rem;
    resize: none;
}

/* Survey Items preview */
.preview-wrapper {
    display: flex;
    flex-direction: column;
    height: calc(100vh - 16rem);
    border: 1px solid var(--bs-border-color);
    border-radius: 0.75rem;
    background: var(--bs-gray-200);
    overflow: hidden;
}

.preview-toolbar {
    display: flex;
    justify-content: center;
    padding: 0.75rem;
    background: var(--bs-gray-200);
    border-bottom: 1px solid var(--bs-border-color);
    flex-shrink: 0;
}

.preview-scroll {
    flex: 1;
    overflow-y: auto;
    padding: 2rem 1rem;
    display: flex;
    justify-content: center;
}

.preview-device {
    background: var(--bs-white);
    border: 1px solid var(--bs-border-color);
    border-radius: 0.5rem;
    padding: 2rem;
    width: 100%;
    height: fit-content;
    box-shadow: 0 0.25rem 1rem rgba(0, 0, 0, 0.08);
    transition: max-width 0.3s ease;
}

.preview-desktop {
    max-width: 100%;
}

.preview-tablet {
    max-width: 48rem;
}

.preview-mobile {
    max-width: 24rem;
    padding: 1rem;
}

.preview-section-header {
    font-size: 1.4rem;
    font-weight: 600;
    margin-top: 3rem;
    margin-bottom: 2.5rem;
    padding-bottom: 0.5rem;
    border-bottom: 2px solid var(--bs-primary);
}

.preview-section-header:first-child,
.preview-device > .preview-section-header:first-of-type {
    margin-top: 0;
}

.preview-item {
    margin-bottom: 1.5rem;
}

.preview-item:last-child {
    margin-bottom: 0;
}

/* App footer (e.g. legal links such as Impressum) */
.app-footer {
    padding: 0.5rem 1rem;
    text-align: center;
    font-size: 0.85rem;
    color: var(--bs-secondary-color);
}

.app-footer a {
    color: var(--bs-secondary-color);
}

.app-footer a:hover {
    color: var(--bs-primary);
}

/* Imprint page content */
.imprint-content h4 {
    margin-top: 1.75rem;
    font-size: 1.2rem;
    font-weight: 600;
}

.imprint-content h4:first-child {
    margin-top: 0;
}

.imprint-content p {
    margin-bottom: 0.75rem;
}

/* ==========================================================================
   Scroll-to-Top Button
   ========================================================================== */

.scroll-to-top {
    position: fixed;
    bottom: 2rem;
    right: 2rem;
    z-index: 1000;
    width: 3rem;
    height: 3rem;
    border-radius: 50%;
    border: none;
    background-color: var(--bs-primary);
    color: var(--bs-white);
    cursor: pointer;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s ease, visibility 0.3s ease;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.2);
}

.scroll-to-top:hover {
    opacity: 0.85 !important;
}

.scroll-to-top.visible {
    opacity: 1;
    visibility: visible;
}

/* Add additional app-specific styles below */

/* Inline language flag (size matches surrounding text, circular crop) */
.lang-flag-inline {
    width: 1em;
    height: 1em;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}

/* Horizontally scrollable filter grid in the surveys list modal */
.filter-grid {
    gap: 0;
}

.filter-column {
    flex: 0 0 14rem;
    display: flex;
    flex-direction: column;
    border-right: 1px solid var(--bs-border-color);
    min-height: 0;
}

.filter-column:last-child {
    border-right: 0;
}

.filter-column-header {
    font-weight: 600;
    padding: 0.75rem 1rem;
    border-bottom: 1px solid var(--bs-border-color);
    background-color: var(--bs-light);
    position: sticky;
    top: 0;
    z-index: 1;
}

.filter-column-body {
    overflow-y: auto;
    flex: 1 1 auto;
}

.filter-value {
    padding: 0.5rem 1rem;
    cursor: pointer;
    user-select: none;
    border-bottom: 1px solid var(--bs-border-color);
}

.filter-value:hover {
    background-color: var(--bs-tertiary-bg);
}

.filter-value.selected {
    background-color: var(--bs-primary);
    color: var(--bs-white);
    font-weight: 500;
}

.filter-value.selected:hover {
    background-color: var(--bs-primary);
    color: var(--bs-white);
    opacity: 0.9;
}

/* Authentication method picker cards */
.method-card {
    border: 1px solid var(--bs-border-color);
    border-radius: var(--radius-md);
    padding: 1.5rem;
    cursor: pointer;
    background-color: var(--bs-white);
    transition: var(--transition-colors), box-shadow var(--duration-base) var(--ease-out);
}

.method-card:hover {
    background-color: var(--bs-light);
    border-color: var(--bs-primary);
    box-shadow: var(--shadow-md);
}

.method-card.selected {
    border-color: var(--bs-primary);
    background-color: var(--selected-bg);
    box-shadow: var(--shadow-sm);
}

/* Spinning icon used for in-progress paper import analyses. */
.icon-spin {
    animation: icon-spin-rotation 1.6s linear infinite;
    display: inline-block;
}

@keyframes icon-spin-rotation {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

/* Read-only wrapper used in Surveys/Edit.razor and Surveys/Templates/Edit.razor.
   <fieldset disabled> would otherwise add the browser's default fieldset chrome (border,
   padding, margin) and indent the content — we want it to be transparent layout-wise
   and only contribute the disabled-state semantics to its descendants. */
.ro-fieldset {
    border: 0;
    padding: 0;
    margin: 0;
    min-width: 0;
}

/* Bootstrap defines `fieldset:disabled .btn { pointer-events: none; opacity: ... }`
   which targets the `.btn` CLASS, not the form-listed-element semantics. That kills
   our anchor-based close/cancel buttons (<a class="btn btn-close"> etc.) too — even
   though they are intentionally NOT form-listed-elements so they survive the HTML
   `<fieldset disabled>` cascade. Re-enable interaction for anchor-based buttons inside
   a read-only fieldset; real <button> descendants stay disabled by the HTML cascade. */
.ro-fieldset[disabled] a.btn,
.ro-fieldset[disabled] a.btn-close {
    pointer-events: auto;
    opacity: 1;
}

/* Helper class for non-button list items (<div role="button">) whose mutation handler
   we've gated server-side. Removes the pointer-cursor + Bootstrap hover-highlight so
   the UI doesn't suggest clickability the user no longer has. The Bootstrap
   .list-group-item-action:hover selector is high-specificity and uses CSS variables;
   we mirror those structures here so the override actually wins. */
.ro-list-item {
    cursor: default;
}

.ro-list-item.list-group-item:hover,
.ro-list-item.list-group-item:focus {
    background-color: var(--bs-list-group-bg) !important;
    color: var(--bs-list-group-color) !important;
    z-index: auto !important;
}

/* Active items must keep their active-state colours, even on hover. */
.ro-list-item.list-group-item.active:hover,
.ro-list-item.list-group-item.active:focus {
    background-color: var(--bs-list-group-active-bg) !important;
    color: var(--bs-list-group-active-color) !important;
    border-color: var(--bs-list-group-active-border-color) !important;
}

/* Read-only Quill replacement: identical layout to the active editor, but no toolbar
   and the content is just rendered HTML (not contenteditable). */
.rich-text-editor-readonly {
    border: 1px solid var(--bs-border-color);
    border-radius: 0.375rem;
    background-color: var(--bs-body-bg);
    overflow: auto;
}

/* Editor on the AGB/Datenschutz/Impressum tabs: cap the height so the
   action buttons below and the page footer stay visible without scrolling.
   Budget accounts for: topbar (~3.5rem), article padding, page-title row
   with Versionen button (~4.5rem), info alert (~5rem), Quill toolbar (~2.5rem),
   button row (~3rem) and app-footer (~2rem). */
.terms-editor-viewport .ql-editor {
    max-height: calc(100vh - 28rem);
    min-height: 12rem;
}

/* Inline preview shown next to the editor. Same scroll budget so the two
   columns line up. */
.legal-split .legal-preview {
    border: 1px solid var(--bs-border-color);
    border-radius: 0.375rem;
    padding: 0.75rem 1rem;
    max-height: calc(100vh - 28rem);
    min-height: 12rem;
    overflow-y: auto;
    background-color: var(--bs-body-bg);
}

/* AIssistant path picker: two large clickable cards inside the choice modal. */
.ai-path-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    text-align: center;
    padding: 1.5rem 1rem;
    background-color: var(--bs-body-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: var(--transition-colors), box-shadow var(--duration-base) var(--ease-out),
                transform var(--duration-fast) var(--ease-out);
}

.ai-path-card:hover,
.ai-path-card:focus {
    border-color: var(--bs-primary);
    background-color: var(--selected-bg);
    box-shadow: var(--shadow-md);
    outline: none;
}

.ai-path-card:active {
    transform: scale(0.99);
}

.ai-path-card-icon {
    width: 3.5rem;
    height: 3.5rem;
    border-radius: 50%;
    background-color: var(--bs-primary);
    color: var(--white);
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 0.75rem;
}

.ai-path-card-icon .material-symbols-outlined {
    font-size: 2rem;
}

.ai-path-card-title {
    font-weight: 600;
    font-size: 1.1rem;
    margin-bottom: 0.5rem;
}

.ai-path-card-description {
    color: var(--bs-secondary-color);
    font-size: 0.95rem;
    line-height: 1.4;
}

/* Revise-mode item picker: list of existing items with checkboxes. Scrolling is
   handled by the outer right panel — no inner scrollbar. */
.revise-item-list {
    border: 1px solid var(--bs-border-color);
    border-radius: 0.375rem;
    padding: 0.5rem;
    background-color: var(--bs-body-bg);
}

.revise-item-row {
    display: flex;
    align-items: flex-start;
    padding: 0.25rem 0;
    gap: 0.5rem;
    cursor: pointer;
    padding-left: 0;
    margin-bottom: 0;
    min-height: auto;
}

.revise-item-row .form-check-input {
    float: none;
    margin: 0.2rem 0 0 0;
    flex-shrink: 0;
}

.revise-item-row + .revise-item-row {
    border-top: 1px solid var(--bs-border-color);
    margin-top: 0.25rem;
    padding-top: 0.5rem;
}

.revise-item-number {
    font-weight: 600;
    margin-right: 0.25rem;
}

.revise-item-preview {
    color: var(--bs-body-color);
    font-size: 0.9rem;
    line-height: 1.3;
}

/* =================================================================================
   Tree components — shared chrome for TreeEditor (management) and TreePicker (asset
   assignment). Connectors, toggle, indent and node-name layout live here so both
   trees look identical across the app. Component-specific state styles (selected,
   assigned, drag-over, grouping, readonly) stay in the scoped *.razor.css files.
   ================================================================================= */

.tree-node {
    cursor: pointer;
}

.tree-node-header {
    display: flex;
    align-items: center;
    padding: 0.1rem 0;
    gap: 0.1rem;
}

.tree-node-header:hover .tree-node-name {
    background-color: var(--bs-light);
}

.tree-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.4rem;
    height: 1.4rem;
    cursor: pointer;
    flex-shrink: 0;
    /* Mask the spine where it would otherwise cross the visible glyph: the spine line
       runs through the toggle column (Windows-Explorer-style) and the white background
       hides it inside the toggle so only the parts above and below remain visible. */
    background-color: var(--bs-white);
    z-index: 1;
    position: relative;
}

.tree-toggle i {
    font-size: 0.9rem;
}

.tree-toggle-placeholder {
    display: inline-block;
    width: 1.4rem;
    height: 1.4rem;
    flex-shrink: 0;
}

.tree-node-name {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    padding: 0.225rem 0.4rem;
    border-radius: 0.25rem;
    transition: background-color 0.15s;
}

/* Tree connectors — Windows-Explorer-style. Each child sits inside a `.tree-connector`
   wrapper. The wrapper holds an "indent strip" on its left (`padding-left`) where the
   spine and the horizontal branch are drawn; the node content (toggle + name) flows
   to the right of that strip, so the branch visibly meets the toggle's left edge
   instead of being painted underneath it. */

.tree-children {
    /* Each nesting level adds this padding on top of the parent's content, so every
       deeper child sits one indent step further right without needing per-level
       overrides for spine/toggle/branch positions. */
    padding-left: 1.75rem;
}

.tree-connector {
    position: relative;
}

/* Vertical spine: shared between all siblings, drawn in the connector's indent strip.
   The negative `top` pulls the spine of the first sibling up into the parent's header
   area so it visibly connects to the bottom of the parent's toggle. For non-first
   siblings the same offset just overlaps the previous sibling's spine — both lines
   share the same X column, so the overlap is invisible. */
.tree-connector::after {
    content: '';
    position: absolute;
    /* Centered on the toggle: toggle sits at the connector's left edge (0) and is
       1.4rem wide, so the toggle's horizontal center — and the spine — is at 0.7rem. */
    left: 0.7rem;
    top: -0.4rem;
    bottom: 0;
    border-left: 1px solid var(--bs-secondary);
}


/* Horizontal branch from the spine to where the visible content starts. For a node
   with a toggle, the toggle span is 1.4rem wide but the [+]/[-] glyph itself is only
   ~0.9rem and centered inside that span, so the visible glyph sits ~0.25rem in from
   the span's left edge. The branch extends into the toggle span up to the glyph's
   left edge instead of stopping at the (invisible) span edge — otherwise it looks
   like the line and toggle are floating apart. The toggle has no background, so the
   line shows through the empty padding next to the glyph. */
/* Non-leaf children: the toggle is pulled onto the spine (see the .tree-toggle rule
   further down) so the toggle itself is the visible connection point — no horizontal
   branch needed. */
.tree-connector::before {
    content: none;
}

/* Leaf children have no toggle on the spine, so a horizontal branch still reaches
   from the spine to the icon. The branch sits a touch lower because there's no
   toggle box to anchor against on this row. */
.tree-connector-leaf::before {
    content: '';
    position: absolute;
    /* Starts on the spine line so the L-shape stays clean. */
    left: 0.7rem;
    top: 1.4rem;
    width: 1.2rem;
    border-top: 1px solid var(--bs-secondary);
}

/* TreePicker rows are shorter than TreeEditor rows (smaller font, no permission
   icon in the name), so the leaf branch needs to sit higher and end earlier to
   land at the row's visible content. */
.tree-picker-scroll .tree-connector-leaf::before {
    top: 1rem;
    width: 0.9rem;
}

/* Consumer-side indent: in the Topics/StructuralTraits column layout the badge sits
   directly above the tree. Indenting the tree by ~0.75rem nudges the root spine
   (at 0.7rem inside the tree) to roughly the horizontal centre of the badge above.
   The tree component itself stays at zero outer padding — the indent comes from
   the page's container. */
.topics-overview-col .tree-view,
.trait-picker-col .tree-picker-scroll {
    padding-left: 0.25rem;
}



/* Last sibling: spine stops at the branch level so siblings below don't get a
   stray vertical line. */
.tree-connector-last::after {
    bottom: auto;
    /* Spine starts at top: -0.4rem and must reach the branch at top: 1.25rem, so the
       total height is 1.25rem + 0.4rem. */
    height: 1.65rem;
}

/* Leaf branches sit a touch lower (top: 1.4rem); the last-sibling spine for a leaf
   needs to reach that lower branch level. */
.tree-connector-last.tree-connector-leaf::after {
    height: 1.8rem;
}

/* =================================================================================
   Paper-import review modal — redesigned 2-column layout
   Left column: vertical list of SurveyItemCards (each pre-filled with the recognized
   or teacher-corrected answer; teacher corrections happen directly in the card).
   Right column: parallel list of crops from the stitched sheet image (one per item),
   so the teacher sees the original handwritten/drawn answer next to the rendered card.
   Both columns scroll independently but are kept in sync via initPaperReviewSyncScroll.
   ================================================================================= */

.paper-review-body {
    /* Single scroll container; one block per survey item flows vertically inside.
       Each block has its own coloured header bar above two side-by-side cards
       (rendered item left, original-paper crop right). */
    overflow-y: auto;
    min-height: 0;
}

/* Outer scroll container; one row per survey item flows vertically inside. */
.paper-review-list {
    padding: 0.5rem;
}

/* Per-item row: 2-column grid where the header label ("A1", "A2") and the
   optional uncertainty note span both columns above the cards. The left-side
   SurveyItemCard and the right-side original-paper card sit as siblings in the
   *card row only*, so align-items:stretch makes them exactly the same height
   and they start at the same Y — the label/note above doesn't count toward
   card height. */
.paper-review-item-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: 1rem;
    row-gap: 0.5rem;
    align-items: stretch;
    margin-bottom: 1.5rem;
}

.paper-review-card-header,
.paper-review-card-note {
    grid-column: 1 / -1;
}

/* Wrapper card on the right column that mirrors the SurveyItemCard look-and-feel
   on the left. The crop image sits inside this card so the original-paper region
   visually reads as the right-column counterpart to the rendered item card. */
.paper-review-original-card {
    background-color: var(--bs-white);
    border: 1px solid var(--bs-border-color);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
    /* Crop fills the card edge to edge on left/right/bottom; 24px top padding
       gives the original a comfortable breathing space so the printed content
       doesn't kiss the upper card border. */
    padding: 24px 0 0 0;
    overflow: hidden;
    /* Top-aligned so the original-paper region starts at exactly the same Y as
       the rendered question on the left card. If the left card is taller, the
       extra space appears below the crop, not above it. */
    display: flex;
    align-items: flex-start;
    justify-content: center;
}

.paper-review-card-wrap {
    /* Sits in the left column of the per-item grid row; align-items:stretch on
       the row makes this card match the original-paper card's height. */
    display: flex;
    flex-direction: column;
}

.paper-review-card-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    background-color: var(--primary-light);
    border-radius: var(--radius-sm);
    font-weight: 600;
    color: var(--primary-dark);
}

.paper-review-card-header .paper-review-status {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.25rem;
    height: 1.25rem;
    border-radius: 50%;
}

.paper-review-card-header .paper-review-status .material-symbols-outlined {
    font-size: 0.9rem;
}

.paper-review-card-note {
    font-size: 0.85rem;
    color: var(--bs-secondary-color);
    margin-bottom: 0.5rem;
}

/* Right column: one tile per item, each showing a Y-window of the stitched sheet PNG.
   Height + image offset are set by initPaperReviewCrops after the image has loaded. */
.paper-review-crop {
    /* Inner crop window — the SVG-like clipping for the stitched-sheet PNG.
       Background and border now live on the wrapper card (.paper-review-original-card),
       so the crop itself is just a transparent positioning context. */
    position: relative;
    width: 100%;
    overflow: hidden;
    scroll-margin-top: 0.5rem;
    /* Default placeholder height while the layout map + image load — gives the
       spinner overlay something to fill. Removed once the crop is .loaded so the
       computed image height takes over (which may be smaller than this). */
    min-height: 6rem;
}

.paper-review-crop.loaded {
    min-height: 0;
}

.paper-review-crop img {
    /* Positioned by JS after load — fall-back values keep the layout stable
       while the image is still being fetched. */
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: auto;
    user-select: none;
    pointer-events: none;
}

/* Loading overlay: full-card white surface with a centered spinner. Visible
   until initPaperReviewCrops has positioned and sized the image, at which
   point it adds .loaded to the crop and the spinner fades out. */
.paper-review-crop-spinner {
    position: absolute;
    inset: 0;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--bs-white);
    transition: opacity 0.15s ease-out;
}

.paper-review-crop.loaded .paper-review-crop-spinner {
    opacity: 0;
    pointer-events: none;
}

.paper-review-empty {
    color: var(--bs-secondary-color);
    font-style: italic;
    padding: 1rem;
}

/* Conversation stream — every bubble is left-aligned and (on tablet+) caps at 50%
   of the row. Incoming and outgoing only differ by background colour so the agent
   can still tell direction at a glance. */
.conversation-stream {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding: 0.5rem 0;
}

.conversation-row {
    display: flex;
    width: 100%;
    justify-content: flex-start;
}

.conversation-bubble {
    width: 100%;
    padding: 0.6rem 0.9rem 0.7rem 0.9rem;
    border-radius: 0.9rem;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
    color: var(--bs-body-color);
}

/* Desktop + Tablet: cap at 50% so long bodies don't span the whole content column.
   On mobile (< 768px) the bubble fills the row. */
@media (min-width: 768px) {
    .conversation-bubble {
        width: 50%;
    }
}

.conversation-row.in .conversation-bubble {
    background-color: #f1f3f5;
}

.conversation-row.out .conversation-bubble {
    background-color: #e6f0ea;
}

.conversation-meta {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.8rem;
    color: var(--bs-secondary-color);
    margin-bottom: 0.35rem;
}

.conversation-from {
    font-weight: 600;
    color: var(--bs-body-color);
}

.conversation-time {
    flex-grow: 1;
}

.conversation-bubble--clickable {
    cursor: pointer;
    transition: box-shadow 0.15s ease, transform 0.05s ease;
}

.conversation-bubble--clickable:hover {
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
}

.conversation-bubble--clickable:active {
    transform: translateY(1px);
}

.conversation-editor-original {
    max-height: 30vh;
    overflow-y: auto;
    padding: 0.5rem 0.75rem;
    background-color: #f8f9fa;
    border-radius: 0.5rem;
}

.conversation-body {
    word-wrap: break-word;
    overflow-wrap: anywhere;
}

.conversation-attachments {
    margin-top: 0.5rem;
    padding-top: 0.5rem;
    border-top: 1px solid rgba(0, 0, 0, 0.08);
}

/* Modal viewport clamp — keep every modal smaller than the screen so the footer
   (Cancel/Save) is always reachable. Header + footer stay pinned; only the body
   scrolls when content overflows. Per CLAUDE.md §5.6 modals sit at the top, not
   centered, so we anchor the top margin and let the available height drive the
   layout. */
.modal-dialog {
    max-height: calc(100vh - 2rem);
    margin-top: 1rem;
    margin-bottom: 1rem;
    display: flex;
    flex-direction: column;
}

.modal-dialog .modal-content {
    max-height: 100%;
    overflow: hidden;
}

.modal-dialog .modal-body {
    overflow-y: auto;
}

/* ==========================================================================
   Toast
   ========================================================================== */

#app-toast-container {
    position: fixed;
    bottom: 1.5rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 2000;
    display: flex;
    flex-direction: column-reverse;
    align-items: center;
    gap: 0.5rem;
    pointer-events: none;
}

.app-toast {
    background-color: var(--bs-dark);
    color: var(--white);
    padding: 0.5rem 1rem;
    border-radius: 0.375rem;
    font-size: 0.9rem;
    box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.2);
    opacity: 0;
    transform: translateY(0.5rem);
    transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;
}

.app-toast-visible {
    opacity: 1;
    transform: translateY(0);
}
