/* ──────────────────────────────────────────────────────────────
   Wizard – der Eingang des Besuchers.
   Setzt base.css und theme-light.css voraus.
   ────────────────────────────────────────────────────────────── */

.layout-wizard { overflow: hidden; }

.scene {
    position: relative;
    display: grid;
    place-items: center;
    min-height: 100svh;
    padding: 32px 20px 68px;
    overflow: hidden;
    perspective: 1100px;
}

/* ── Hintergrund: weiche Farbwolken ──────────────────────────── */

.aura { position: absolute; inset: -18%; pointer-events: none; }

.blob {
    position: absolute;
    border-radius: 50%;
    filter: blur(74px);
    transition: transform 1.4s cubic-bezier(.2, .8, .2, 1);
    will-change: transform;
}

.blob-1 {
    width: 620px; height: 620px; top: 4%; left: 6%;
    background: radial-gradient(circle at 34% 32%, oklch(0.90 0.085 352), transparent 68%);
    animation: blob-drift-1 26s ease-in-out infinite;
    transform: translate3d(calc(var(--mx, 0) * 78px), calc(var(--my, 0) * 58px), 0);
}

.blob-2 {
    width: 540px; height: 540px; bottom: 2%; right: 8%;
    background: radial-gradient(circle at 62% 44%, oklch(0.92 0.070 38), transparent 66%);
    animation: blob-drift-2 31s ease-in-out infinite;
    transform: translate3d(calc(var(--mx, 0) * -58px), calc(var(--my, 0) * -44px), 0);
}

.blob-3 {
    width: 400px; height: 400px; top: 34%; left: 46%;
    background: radial-gradient(circle at 50% 50%, oklch(0.91 0.060 312), transparent 70%);
    animation: blob-drift-3 22s ease-in-out infinite;
    transform: translate3d(calc(var(--mx, 0) * 38px), calc(var(--my, 0) * -50px), 0);
}

@keyframes blob-drift-1 { 0%, 100% { margin: 0 } 50% { margin: -34px 0 0 26px } }
@keyframes blob-drift-2 { 0%, 100% { margin: 0 } 50% { margin: 30px 0 0 -22px } }
@keyframes blob-drift-3 { 0%, 100% { margin: 0 } 50% { margin: -18px 0 0 -30px } }

/* Feines Korn nimmt den Verläufen das Digitale */
.grain {
    position: absolute;
    inset: 0;
    pointer-events: none;
    opacity: .30;
    mix-blend-mode: multiply;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}

/* ── Karte ───────────────────────────────────────────────────── */

.pane {
    position: relative;
    width: min(560px, 100%);
    padding: 58px 64px 50px;
    border-radius: var(--r-xl);
    background: var(--glass);
    border: 1px solid var(--glass-edge);
    backdrop-filter: blur(30px) saturate(155%);
    -webkit-backdrop-filter: blur(30px) saturate(155%);
    box-shadow: var(--lift);
    transform:
        translate3d(calc(var(--mx, 0) * -18px), calc(var(--my, 0) * -13px), 0)
        rotateY(calc(var(--mx, 0) * 9deg))
        rotateX(calc(var(--my, 0) * -9deg));
    transition: transform .5s cubic-bezier(.2, .8, .2, 1);
}

.mark {
    display: grid;
    place-items: center;
    width: 40px; height: 40px;
    margin: 0 auto 30px;
    border-radius: 13px;
    background: var(--blush);
    color: var(--blush-ink);
    font-size: 17px;
    box-shadow: 0 10px 24px -8px rgba(150, 40, 90, .45);
    transform:
        perspective(420px)
        translate3d(calc(var(--mx, 0) * -32px), calc(var(--my, 0) * -24px), 0)
        rotateY(calc(var(--mx, 0) * 18deg))
        rotateX(calc(var(--my, 0) * -18deg));
    transition: transform .5s cubic-bezier(.2, .8, .2, 1);
}

.intro {
    margin-bottom: 26px;
    text-align: center;
    font-size: 15px;
    font-weight: 300;
    line-height: 1.65;
    color: var(--text-dim);
    text-wrap: pretty;
    white-space: pre-line;
}

.question {
    margin-bottom: 30px;
    text-align: center;
    font-size: 34px;
    font-weight: 200;
    line-height: 1.22;
    letter-spacing: -.026em;
    text-wrap: balance;
}

/* ── Eingabe: ein Feld je Zeichen ────────────────────────────── */
/* Die Felder teilen sich die verfügbare Breite auf – dadurch bricht
   die Reihe nie um, egal wie viele Zeichen die Antwort hat. */

.slots {
    display: flex;
    flex-wrap: nowrap;
    justify-content: center;
    gap: 12px;
}

.slot {
    flex: 1 1 0;
    min-width: 0;
    max-width: 80px;
    height: 88px;
    padding: 0;
    border: 1px solid rgba(255, 255, 255, .9);
    border-radius: 18px;
    background: rgba(255, 255, 255, .76);
    box-shadow: inset 0 1px 3px rgba(96, 32, 60, .05);
    color: var(--text);
    font-size: 30px;
    font-weight: 300;
    text-align: center;
    text-transform: uppercase;
    caret-color: var(--blush);
    transition: border-color .2s, background .2s, box-shadow .2s;
}

.slot:focus {
    outline: 0;
    border-color: var(--blush-soft);
    background: rgba(255, 255, 255, .92);
    box-shadow: 0 0 0 4px var(--blush-veil);
}

/* Mehr Zeichen: flacher, kleiner gesetzt, engerer Abstand */
.slots[data-count="5"], .slots[data-count="6"] { gap: 10px; }
.slots[data-count="5"] .slot,
.slots[data-count="6"] .slot { height: 70px; font-size: 25px; border-radius: 15px; }

.slots[data-count="7"], .slots[data-count="8"] { gap: 9px; }
.slots[data-count="7"] .slot,
.slots[data-count="8"] .slot { height: 62px; font-size: 21px; border-radius: 13px; }

.slots[data-count="9"], .slots[data-count="10"] { gap: 7px; }
.slots[data-count="9"] .slot,
.slots[data-count="10"] .slot { height: 54px; font-size: 17px; border-radius: 11px; }

/* Platzhalter zeigen die Form der Antwort, ohne sie laut zu sagen. */
.slot::placeholder,
.line::placeholder { color: var(--text-faint); opacity: .45; }
.slot:focus::placeholder,
.line:focus::placeholder { opacity: .2; }

.hint {
    margin-top: 16px;
    text-align: center;
    font-size: 12.5px;
    color: var(--text-faint);
}

/* ── Aktionen ────────────────────────────────────────────────── */

.go {
    display: block;
    width: 100%;
    height: 52px;
    margin-top: 32px;
    border: 0;
    border-radius: var(--r-lg);
    background: var(--blush);
    color: var(--blush-ink);
    font-size: 15px;
    letter-spacing: .01em;
    cursor: pointer;
    box-shadow: 0 14px 30px -12px rgba(150, 40, 90, .55);
    transition: filter .2s;
}

.go:hover { filter: brightness(1.05); }
.go:disabled { opacity: .45; cursor: default; box-shadow: none; }

.quiet {
    border: 0;
    background: none;
    color: var(--text-faint);
    font-size: 13px;
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
}

.quiet:hover { color: var(--text-dim); }

.steps { display: flex; justify-content: center; gap: 7px; margin-top: 26px; }

.step { width: 5px; height: 5px; border-radius: 50%; background: var(--text-faint); opacity: .35; }
.step.is-done { opacity: .7; }
.step.is-current { width: 20px; border-radius: 3px; background: var(--blush); opacity: 1; }

.sig {
    position: absolute;
    left: 0; right: 0; bottom: 30px;
    text-align: center;
    font-size: 11px;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--text-faint);
}

.sig a { color: var(--text-faint); text-decoration: none; }
.sig a:hover { color: var(--blush); }
.sig-sep { margin: 0 .5em; opacity: .5; }

/* ── Meldung ─────────────────────────────────────────────────── */

.alert {
    margin-bottom: 24px;
    padding: 13px 16px;
    border-radius: var(--r);
    background: var(--danger-veil);
    color: var(--danger);
    font-size: 13.5px;
    line-height: 1.5;
    text-align: center;
    text-wrap: pretty;
}

/* ── Passwort-Schritt ────────────────────────────────────────── */

.fields { display: flex; flex-direction: column; gap: 14px; }

.fields label { display: block; margin-bottom: 6px; font-size: 12.5px; color: var(--text-dim); }

.line {
    width: 100%;
    height: 50px;
    padding: 0 16px;
    border: 1px solid rgba(255, 255, 255, .9);
    border-radius: var(--r-lg);
    background: rgba(255, 255, 255, .76);
    color: var(--text);
    font-size: 15px;
}

.line:focus {
    outline: 0;
    border-color: var(--blush-soft);
    background: rgba(255, 255, 255, .92);
    box-shadow: 0 0 0 4px var(--blush-veil);
}

/* ── Handy ───────────────────────────────────────────────────── */

@media (max-width: 560px) {
    .scene { padding: 28px 18px 56px; }

    .pane { padding: 38px 26px 32px; border-radius: 26px; }

    .mark { width: 36px; height: 36px; margin-bottom: 24px; border-radius: 12px; font-size: 15px; }

    .intro { margin-bottom: 20px; font-size: 14.5px; }

    .question { margin-bottom: 26px; font-size: 25px; }

    .slots { gap: 9px; }
    .slot { height: 70px; font-size: 27px; border-radius: 16px; }
    .slots[data-count="5"] .slot,
    .slots[data-count="6"] .slot { height: 62px; font-size: 22px; }
    .slots[data-count="7"] .slot,
    .slots[data-count="8"] .slot { height: 54px; font-size: 18px; border-radius: 12px; }
    .slots[data-count="9"] .slot,
    .slots[data-count="10"] .slot { height: 46px; font-size: 15px; border-radius: 10px; }

    .go { height: 50px; margin-top: 26px; }

    /* Auf kleinen Geräten kostet der grosse Weichzeichner spürbar Leistung */
    .blob { filter: blur(56px); }
    .blob-1 { width: 420px; height: 420px; }
    .blob-2 { width: 380px; height: 380px; }
    .blob-3 { width: 300px; height: 300px; }
}

@media (prefers-reduced-motion: reduce) {
    .blob, .pane, .mark { animation: none; transition: none; }
}

/* ──────────────────────────────────────────────────────────────
   Angemeldete Ansicht: Sidebar und Inhalt füllen das Fenster.
   Bewusst ohne Maus-Parallax – der bleibt dem Zugang vorbehalten.
   ────────────────────────────────────────────────────────────── */

.scene-site {
    display: block;
    place-items: initial;
    padding: 30px;
    overflow: visible;
    /* Die Tiefe gehört dem Zugang. Hier stört sie sogar: Perspektive macht
       die Bühne zum Bezugsrahmen für alles Fixierte – das Menü wäre dann
       so hoch wie die Seite statt wie das Fenster. */
    perspective: none;
}

/* Der Zugang steht still, die angemeldete Ansicht scrollt. Wolken und Korn
   hängen darum am Fenster statt an der Seite – sonst wandern sie mit.
   Beide sind fixiert und damit positioniert, also braucht der Inhalt eine
   eigene Ebene darüber. */
.scene-site > .aura,
.scene-site > .grain { position: fixed; z-index: 0; }
.scene-site > .shell { position: relative; z-index: 1; }

.shell {
    display: grid;
    grid-template-columns: 300px minmax(0, 1fr);
    gap: 30px;
    align-items: start;
    width: 100%;
    max-width: none;
    margin: 0;
}

.side {
    position: sticky;
    top: 30px;
    display: flex;
    flex-direction: column;
    gap: 34px;
    min-height: calc(100svh - 60px);
    padding: 34px 24px;
    border-radius: var(--r-xl);
    background: var(--glass);
    border: 1px solid var(--glass-edge);
    backdrop-filter: blur(30px) saturate(155%);
    -webkit-backdrop-filter: blur(30px) saturate(155%);
    box-shadow: var(--lift);
}

.side-brand { display: flex; align-items: center; gap: 12px; padding: 0 10px; color: inherit; }
.side-brand:hover { color: inherit; }

.side-mark {
    display: grid;
    place-items: center;
    flex: none;
    width: 36px; height: 36px;
    border-radius: 12px;
    background: var(--blush);
    color: var(--blush-ink);
    font-size: 16px;
    box-shadow: 0 10px 22px -10px rgba(150, 40, 90, .55);
}

.side-name { display: block; font-size: 15px; font-weight: 400; letter-spacing: -.005em; }
.side-who { display: block; margin-top: 1px; font-size: 11.5px; color: var(--text-faint); }

.side-nav { display: flex; flex-direction: column; gap: 4px; }

.side-item {
    display: flex;
    align-items: center;
    gap: 14px;
    height: 46px;
    padding: 0 15px;
    border-radius: var(--r-lg);
    color: var(--text-dim);
    font-size: 12px;
    letter-spacing: 1px;
    text-transform: uppercase;
}

.side-item i { flex: none; width: 18px; font-size: 15px; letter-spacing: 0; text-align: center; opacity: .75; }
.side-item:hover { background: rgba(255, 255, 255, .55); color: var(--text); }
.side-item.is-active { background: var(--blush-veil); color: var(--blush); }
.side-item.is-active i { opacity: 1; }

.side-foot { margin-top: auto; display: flex; flex-direction: column; gap: 4px; }

/* ── Schmale Fenster: Kopfzeile und ganzseitiges Menü ────────────
   Beides liegt still, bis das Skript den Schalter freigibt – ohne
   Skript bleibt die Spalte, wie sie ist.
   ────────────────────────────────────────────────────────────── */

.topbar { display: none; }

.topbar-brand { display: flex; align-items: center; gap: 12px; color: inherit; }
.topbar-brand:hover { color: inherit; }

/* Das Guthaben im schmalen Kopf – kurz, weil daneben nur der Schalter
   Platz hat. Die ganze Karte steht dann nicht mehr über dem Inhalt. */
.topbar-tally {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
    height: 38px;
    padding: 0 15px;
    border-radius: 99px;
    background: var(--blush-veil);
    color: var(--blush);
    font-family: 'Roboto Condensed', sans-serif;
    font-size: 15px;
    letter-spacing: .02em;
    white-space: nowrap;
}

.topbar-tally i { font-size: 13px; }
.topbar-tally b { font-weight: 500; }

.burger {
    display: grid;
    place-items: center;
    flex: none;
    width: 46px; height: 46px;
    padding: 0;
    border: 1px solid rgba(255, 255, 255, .75);
    border-radius: var(--r-lg);
    background: rgba(255, 255, 255, .55);
    color: var(--text);
    font-size: 17px;
    cursor: pointer;
}

.burger:hover { background: rgba(255, 255, 255, .8); }
.burger-close { display: none; }
.is-nav-open .burger-open { display: none; }
.is-nav-open .burger-close { display: block; }

.sheetnav {
    position: fixed;
    inset: 0;
    z-index: 40;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 44px;
    /* Unten bleibt Platz für den Fuss, der dort festsitzt. */
    padding: 100px 26px 150px;
    background: rgba(255, 255, 255, .76);
    backdrop-filter: blur(30px) saturate(150%);
    -webkit-backdrop-filter: blur(30px) saturate(150%);
    opacity: 0;
    visibility: hidden;
    transition: opacity .24s ease, visibility .24s;
}

.is-nav-open .sheetnav { opacity: 1; visibility: visible; }

/* Im breiten Fenster gibt es das Blatt gar nicht – auch dann nicht, wenn
   es beim Verkleinern offen war. */
@media (min-width: 1001px) {
    .sheetnav { display: none; }
}

/* Solange das Blatt offen ist, scrollt dahinter nichts. */
.is-nav-open { overflow: hidden; }

.sheetnav-list,
.sheetnav-foot { display: flex; flex-direction: column; width: 100%; max-width: 330px; }
.sheetnav-list { gap: 8px; }

/* Der Fuss sitzt am unteren Rand, die Einträge bleiben in der Mitte. */
.sheetnav-foot {
    position: absolute;
    left: 26px;
    right: 26px;
    bottom: 34px;
    margin: 0 auto;
    gap: 6px;
    align-items: center;
}

.sheetnav-foot form { width: 100%; }

.sheet-item {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 14px;
    width: 100%;
    height: 64px;
    padding: 0 20px;
    border: 0;
    border-radius: var(--r-lg);
    background: rgba(255, 255, 255, .5);
    color: var(--text-dim);
    font: inherit;
    font-size: 14px;
    letter-spacing: 1.5px;
    text-transform: uppercase;
    cursor: pointer;
}

.sheet-item i { font-size: 19px; opacity: .7; }
.sheet-item.is-active { background: var(--blush); color: var(--blush-ink); }
.sheet-item.is-active i { opacity: 1; }

.sheet-item.is-quiet { height: 52px; background: none; font-size: 12px; color: var(--text-faint); }
.sheet-item.is-quiet:hover { background: rgba(255, 255, 255, .45); color: var(--text-dim); }

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

/* ── Fortschritt ─────────────────────────────────────────────────
   Steht in der rechten Spalte, oben bündig mit der Sidebar, und bleibt
   beim Scrollen auf derselben Höhe kleben wie diese.
   ────────────────────────────────────────────────────────────── */

/* Leiste und Zähler stehen nebeneinander und kleben gemeinsam oben. */
.topline {
    position: sticky;
    top: 30px;
    z-index: 3;
    display: flex;
    align-items: stretch;
    gap: 22px;
    margin-bottom: 30px;
}

.xpbar {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 26px;
    padding: 15px 26px;
    border-radius: var(--r-lg);
    background: var(--glass);
    border: 1px solid var(--glass-edge);
    backdrop-filter: blur(30px) saturate(155%);
    -webkit-backdrop-filter: blur(30px) saturate(155%);
    box-shadow: var(--lift);
}

.xpbar-who { flex: none; display: flex; align-items: center; gap: 12px; }

.xpbar-level {
    display: inline-flex;
    align-items: center;
    height: 26px;
    padding: 0 12px;
    border-radius: 99px;
    background: var(--blush);
    color: var(--blush-ink);
    font-family: 'Roboto Condensed', sans-serif;
    font-size: 10px;
    letter-spacing: .13em;
    text-transform: uppercase;
    white-space: nowrap;
}

.xpbar-name { font-size: 14px; color: var(--text); white-space: nowrap; }
.xpbar-emoji { margin-right: 6px; font-size: 14px; line-height: 1; }

.xpbar-track {
    flex: 1 1 auto;
    min-width: 90px;
    height: 8px;
    border-radius: 99px;
    background: rgba(255, 255, 255, .55);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .8);
    overflow: hidden;
}

.xpbar-fill {
    position: relative;
    height: 100%;
    border-radius: 99px;
    background: linear-gradient(90deg, var(--blush-soft), var(--blush));
    overflow: hidden;
    transition: width .7s cubic-bezier(.2, .8, .2, 1);
}

/* Der Augenblick, in dem die Leiste voll ist: ein heller Strich läuft
   einmal hindurch, und die Leiste selbst atmet kurz auf. */
.xpbar { transition: box-shadow .5s ease; }

.xpbar.is-levelup {
    box-shadow: var(--lift), 0 0 0 1px var(--blush-soft), 0 0 50px -8px var(--blush);
}

.xpbar.is-levelup .xpbar-fill::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(100deg, transparent 12%, rgba(255, 255, 255, .95) 50%, transparent 88%);
    animation: xp-sweep .76s ease-out;
}

@keyframes xp-sweep {
    from { transform: translateX(-100%); }
    to   { transform: translateX(100%); }
}

.xpbar-count {
    flex: none;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 1px;
    text-align: right;
}

.xpbar-xp {
    font-family: 'Roboto Condensed', sans-serif;
    font-size: 12px;
    letter-spacing: .07em;
    color: var(--text-dim);
    white-space: nowrap;
}

.xpbar-next { font-size: 11.5px; color: var(--text-faint); white-space: nowrap; }

@media (prefers-reduced-motion: reduce) {
    .xpbar-fill { transition: none; }
}

/* ── Zähler neben der Leiste ─────────────────────────────────── */

.tally {
    position: relative;
    flex: none;
    display: flex;
    align-items: center;
    gap: 15px;
    padding: 15px 24px;
    border-radius: var(--r-lg);
    background: var(--glass);
    border: 1px solid var(--glass-edge);
    backdrop-filter: blur(30px) saturate(155%);
    -webkit-backdrop-filter: blur(30px) saturate(155%);
    box-shadow: var(--lift);
}

.tally-mark {
    display: grid;
    place-items: center;
    flex: none;
    width: 38px; height: 38px;
    border-radius: 50%;
    background: var(--blush-veil);
    color: var(--blush);
    font-size: 16px;
}

.tally-body { display: flex; align-items: center; gap: 11px; }

.tally-count {
    font-size: 26px;
    font-weight: 200;
    line-height: 1;
    letter-spacing: -.03em;
    color: var(--text);
}

/* Bleibt der Zähler nach einem Lauf stehen, leuchtet er kurz auf – sonst
   wäre nicht zu sehen, dass die Zahl gerade gewachsen ist und nicht bloss
   dasteht. */
.tally.is-gain { animation: tally-gain 1.2s ease-out; }
.tally.is-gain .tally-count { animation: tally-count 1.2s ease-out; }

/* Der Ring kommt zum Schatten dazu und ersetzt ihn nicht – sonst fiele
   die Fläche für die Dauer des Aufleuchtens in sich zusammen. */
@keyframes tally-gain {
    0%   { box-shadow: var(--lift), 0 0 0 0 var(--blush-veil); }
    25%  { box-shadow: var(--lift), 0 0 0 7px var(--blush-veil); }
    100% { box-shadow: var(--lift), 0 0 0 0 rgba(0, 0, 0, 0); }
}

@keyframes tally-count {
    0%   { color: var(--text); }
    25%  { color: var(--blush); }
    100% { color: var(--text); }
}

.tally-label {
    font-family: 'Roboto Condensed', sans-serif;
    font-size: 10px;
    line-height: 1;
    letter-spacing: .15em;
    text-transform: uppercase;
    color: var(--text-faint);
}

/* Eigene Sprechblase statt des Browser-Kastens: sie kommt sofort,
   trägt dieselbe Schrift und verschwindet mit dem Zeiger. */
.tally::before,
.tally::after {
    position: absolute;
    top: calc(100% + 9px);
    right: 14px;
    opacity: 0;
    transform: translateY(-5px);
    pointer-events: none;
    transition: opacity .18s ease, transform .18s ease;
}

.tally::after {
    content: attr(data-tip);
    padding: 8px 13px;
    border-radius: var(--r);
    background: var(--text);
    color: var(--bg);
    font-size: 12px;
    line-height: 1.3;
    white-space: nowrap;
    box-shadow: 0 16px 30px -14px rgba(96, 32, 60, .55);
}

/* Das Zipfelchen zur Karte hinauf */
.tally::before {
    content: "";
    top: calc(100% + 4px);
    right: 28px;
    width: 10px; height: 10px;
    border-radius: 2px;
    background: var(--text);
    transform: translateY(-5px) rotate(45deg);
}

.tally:hover::before,
.tally:hover::after,
.tally:focus-visible::before,
.tally:focus-visible::after { opacity: 1; }

.tally:hover::after,
.tally:focus-visible::after { transform: none; }
.tally:hover::before,
.tally:focus-visible::before { transform: rotate(45deg); }

.tally:focus { outline: 0; }
.tally:focus-visible { outline: 2px solid var(--blush); outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
    .tally::before, .tally::after { transition: none; }
    .tally.is-gain, .tally.is-gain .tally-count { animation: none; }
}

/* ── Inhaltsfläche ───────────────────────────────────────────── */

.sheet {
    padding: 68px 76px 72px;
    border-radius: var(--r-xl);
    background: var(--glass);
    border: 1px solid var(--glass-edge);
    backdrop-filter: blur(30px) saturate(155%);
    -webkit-backdrop-filter: blur(30px) saturate(155%);
    box-shadow: var(--lift);
}

.sheet + .sheet { margin-top: 30px; }

.eyebrow {
    margin-bottom: 18px;
    font-size: 11px;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--text-faint);
}

.headline {
    margin-bottom: 22px;
    font-size: 46px;
    font-weight: 200;
    line-height: 1.12;
    letter-spacing: -.028em;
    text-wrap: balance;
}

.lede { max-width: 73ch; color: var(--text-dim); font-size: 17px; font-weight: 300; line-height: 1.7; }
.prose { max-width: 79ch; color: var(--text-dim); font-size: 16px; line-height: 1.8; }
.prose p + p { margin-top: 18px; }
.prose strong { color: var(--text); font-weight: 500; }
.prose em { font-style: italic; }

.prose blockquote {
    margin: 18px 0;
    padding-left: 20px;
    border-left: 2px solid var(--blush-soft);
    color: var(--text);
}

.prose hr { width: 42%; height: 1px; margin: 30px auto; border: 0; background: var(--line); }

.subhead { font-size: 26px; font-weight: 200; line-height: 1.2; letter-spacing: -.02em; }

/* Ein Blatt, auf dem alles zur Mitte steht – dort gehört auch die
   Überschrift hin. Sonst begänne die Seite links und liefe erst danach
   in die Mitte. */
.sheet.is-middle > .eyebrow,
.sheet.is-middle > .subhead,
.sheet.is-middle > .headline,
.sheet.is-middle > .lede { text-align: center; }

.sheet.is-middle > .lede { margin-inline: auto; }

/* Der Untertitel unter der Überschrift eines Abschnitts */
.shop-note { margin-top: 14px; font-size: 15px; }

/* Eine Notiz über dem Inhalt – was gerade geschehen ist, oder was nicht ging. */
.note {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 28px;
    padding: 14px 18px;
    border-radius: var(--r);
    font-size: 13.5px;
}

.note.is-good { background: var(--blush-veil); color: var(--blush); }
.note.is-bad  { background: var(--danger-veil); color: var(--danger); }

/* Weder gut noch schlecht – bloss gesagt. */
.note.is-quiet { background: var(--panel-2); color: var(--text-dim); }

/* ── Bereiche ────────────────────────────────────────────────────
   Jede Karte zeigt dasselbe, ob offen oder zu: Icon, Titel, ein paar
   Zeilen. Nur der Fuss unterscheidet sich – dort steht, was noch fehlt.
   ────────────────────────────────────────────────────────────── */

.unlocks { display: grid; grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); gap: 22px; margin-top: 36px; }

/* Auf breiten Fenstern darf die Karte mitwachsen. Fünf nebeneinander
   sind eine Reihe Briefmarken – der Teaser bricht dann auf sieben Zeilen,
   und der Bereich sieht kleiner aus, als er ist. Ab hier lieber eine
   weniger und dafür jede mit Platz. */
@media (min-width: 1700px) {
    .unlocks { grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); }
}

/* Die Karten liegen auf dem hellen Blatt und müssen sich davon absetzen:
   deshalb eine Fläche, die dunkler ist als das Glas darunter, und eine
   Kante, die man auch sieht. Weiss auf Weiss verschwand. */
.unlock {
    display: flex;
    flex-direction: column;
    padding: 26px 28px 24px;
    border-radius: var(--r-lg);
    background: var(--panel-2);
    border: 1px solid var(--line);
    box-shadow: 0 14px 30px -24px rgba(96, 32, 60, .55);
}

.unlock-top { display: flex; align-items: center; justify-content: space-between; gap: 14px; }

.unlock-mark {
    display: grid;
    place-items: center;
    flex: none;
    width: 44px; height: 44px;
    border-radius: 14px;
    background: var(--blush-veil);
    color: var(--blush);
    font-size: 19px;
}

.unlock-cost {
    font-family: 'Roboto Condensed', sans-serif;
    font-size: 16px;
    letter-spacing: .04em;
    color: var(--text-dim);
    white-space: nowrap;
}

.unlock-cost small {
    margin-left: 3px;
    font-size: 9.5px;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--text-faint);
}

.unlock h3 { margin: 20px 0 8px; font-size: 17px; font-weight: 400; }
.unlock p { color: var(--text-faint); font-size: 13.5px; line-height: 1.65; }

/* Der Fuss sitzt unten, egal wie lang der Text darüber ist. */
.unlock-foot { margin-top: auto; padding-top: 20px; }

.unlock-state { display: inline-flex; align-items: center; gap: 9px; font-size: 12.5px; color: var(--text-faint); }
.unlock-state i { font-size: 13px; }

.unlock-track {
    display: block;
    height: 5px;
    margin-top: 13px;
    border-radius: 99px;
    background: #fff;
    box-shadow: inset 0 0 0 1px var(--line);
    overflow: hidden;
}

.unlock-fill {
    display: block;
    height: 100%;
    border-radius: 99px;
    background: linear-gradient(90deg, var(--blush-soft), var(--blush));
}

.unlock-btn {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    height: 42px;
    padding: 0 20px;
    border: 1px solid var(--line);
    border-radius: 99px;
    background: #fff;
    color: var(--text);
    font: inherit;
    font-size: 11px;
    letter-spacing: .13em;
    text-transform: uppercase;
    cursor: pointer;
}

.unlock-btn:hover { border-color: var(--blush-soft); color: var(--blush); }
.unlock-btn i { font-size: 13px; letter-spacing: 0; }

.unlock-btn.is-key {
    border-color: transparent;
    background: var(--blush);
    color: var(--blush-ink);
    box-shadow: 0 16px 32px -16px var(--blush);
}

.unlock-btn.is-key:hover { border-color: transparent; color: var(--blush-ink); filter: brightness(1.06); }

/* Was nicht geht, tritt zurück; was geht, tritt hervor – aber alle vier
   Zustände bleiben als Karte lesbar. */
.unlock.is-wartet,
.unlock.is-gesperrt,
.unlock.is-teuer { background: var(--panel-3); }

.unlock.is-wartet .unlock-mark,
.unlock.is-gesperrt .unlock-mark { background: #fff; color: var(--text-faint); }
.unlock.is-wartet h3,
.unlock.is-gesperrt h3,
.unlock.is-teuer h3 { color: var(--text-dim); }

/* Was noch nicht freigegeben ist, nennt auch seinen Preis leiser: er
   gilt ja noch nicht. */
.unlock.is-wartet .unlock-cost { opacity: .5; }

/* Und es trägt eine Schraffur. Sie sagt auf einen Blick und ohne Text,
   dass hier gerade gearbeitet wird – wie das Band vor einer frisch
   gestrichenen Tür. Sie liegt über der Karte, nimmt aber keine Klicks
   an, damit der Knopf darunter erreichbar bliebe, wenn je einer
   dazukommt. */
.unlock.is-wartet { position: relative; overflow: hidden; }

.unlock.is-wartet::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    border-radius: inherit;
    background: repeating-linear-gradient(
        -45deg,
        var(--blush-veil) 0 8px,
        transparent 8px 20px
    );
    opacity: .7;
}

.unlock.is-bereit {
    background: #fff;
    border-color: var(--blush-soft);
    box-shadow: 0 24px 50px -28px var(--blush);
}

/* ── Schon offen ─────────────────────────────────────────────────
   Was ihr gehört, muss nicht mehr werben: eine Zeile statt einer
   Karte, und der Blush als Fläche statt als Akzent – damit sich der
   Abschnitt auf einen Blick von der Auslage darüber unterscheidet.
   ────────────────────────────────────────────────────────────── */

.opened-head {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 46px;
    padding-top: 28px;
    border-top: 1px solid rgba(255, 255, 255, .85);
}

.opened-title {
    font-size: 11px;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--text-faint);
}

.opened-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 21px;
    height: 21px;
    padding: 0 7px;
    border-radius: 99px;
    background: var(--blush-veil);
    color: var(--blush);
    font-family: 'Roboto Condensed', sans-serif;
    font-size: 11px;
    letter-spacing: .04em;
}

.opened { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 12px; margin-top: 22px; }

.opened-card {
    display: flex;
    align-items: center;
    gap: 13px;
    padding: 13px 15px;
    border-radius: var(--r-lg);
    background: var(--blush-veil);
    border: 1px solid var(--blush-soft);
    color: var(--text);
}

.opened-card.is-link:hover { background: var(--blush-soft); color: var(--text); }

/* Gefüllt statt umrissen: das dunkle Feld mit hellem Icon sagt schon
   von weitem, dass dieser Bereich offen steht. */
.opened-mark {
    display: grid;
    place-items: center;
    flex: none;
    width: 36px; height: 36px;
    border-radius: 11px;
    background: var(--blush);
    color: var(--blush-ink);
    font-size: 15px;
}

.opened-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.opened-name { font-size: 14px; }
.opened-note { font-size: 11px; color: var(--text-faint); }

.opened-tick {
    display: grid;
    place-items: center;
    flex: none;
    margin-left: auto;
    width: 22px; height: 22px;
    border-radius: 50%;
    background: var(--blush);
    color: var(--blush-ink);
    font-size: 9px;
}

/* ── Wenn nichts dasteht ─────────────────────────────────────────
   Dieselbe Sprache wie im Backend: ein Zeichen im Kreis, ein weicher
   Schimmer dahinter, zwei Zeilen. Die leere Stelle soll nicht leer
   wirken, sondern wartend.

   Der Name .empty gibt es auch im Backend – die beiden Stylesheets
   liegen nie zusammen auf einer Seite, und dasselbe Wort für dieselbe
   Sache ist mehr wert als ein zweites.
   ────────────────────────────────────────────────────────────── */

.empty {
    position: relative;
    isolation: isolate;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 15px;
    padding: 52px 30px 46px;
    text-align: center;
}

.empty::before {
    content: "";
    position: absolute;
    z-index: -1;
    top: -150px;
    left: 50%;
    width: 440px; height: 440px;
    transform: translateX(-50%);
    border-radius: 50%;
    background: radial-gradient(circle, var(--blush-veil) 0%, transparent 70%);
    filter: blur(40px);
    pointer-events: none;
}

.empty > i {
    display: grid;
    place-items: center;
    width: 78px; height: 78px;
    border-radius: 50%;
    background: var(--blush-veil);
    color: var(--blush);
    font-size: 29px;
}

.empty-title {
    font-size: 27px;
    font-weight: 200;
    line-height: 1.2;
    letter-spacing: -.022em;
    color: var(--text);
    text-wrap: balance;
}

.empty-note {
    max-width: 44ch;
    color: var(--text-faint);
    font-size: 14.5px;
    line-height: 1.7;
}

/* Steht der Leerzustand nicht allein, sondern zwischen Überschrift und
   Liste, reisst die zentrierte Fassung die Struktur auf. Dann tritt er
   als Karte auf, links bündig wie alles andere in dieser Spalte – der
   gestrichelte Rand sagt, dass hier etwas fehlt und nicht etwas ist. */
.empty-card {
    display: flex;
    align-items: center;
    gap: 18px;
    margin-top: 36px;
    padding: 22px 26px;
    border: 1px dashed var(--line);
    border-radius: var(--r-lg);
    background: var(--panel-2);
}

.empty-card-mark {
    display: grid;
    place-items: center;
    flex: none;
    width: 46px; height: 46px;
    border-radius: 14px;
    background: var(--blush-veil);
    color: var(--blush);
    font-size: 19px;
}

.empty-card-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.empty-card-title { font-size: 16px; color: var(--text); }
.empty-card-note { font-size: 13.5px; line-height: 1.6; color: var(--text-faint); }

@media (max-width: 620px) {
    .empty { padding: 38px 12px 32px; }
    .empty-title { font-size: 23px; }
    .empty-card { flex-direction: column; align-items: flex-start; gap: 14px; padding: 20px; }
}

/* ── Geschichten ─────────────────────────────────────────────────
   Was heute offensteht, bekommt die grosse Karte; was sie schon kennt,
   steht als Reihe darunter. Vom Text der neuen Geschichte ist absichtlich
   nichts zu sehen – das Aufschlagen soll etwas wert sein.
   ────────────────────────────────────────────────────────────── */

.feature {
    display: flex;
    align-items: stretch;
    gap: 0;
    margin-top: 36px;
    border-radius: var(--r-lg);
    border: 1px solid var(--blush-soft);
    background: #fff;
    box-shadow: 0 24px 50px -30px var(--blush);
    overflow: hidden;
    color: var(--text);
}

.feature:hover { color: var(--text); box-shadow: 0 30px 60px -28px var(--blush); }

.feature-shot { flex: none; width: 42%; max-width: 300px; }

.feature-shot img {
    display: block;
    width: 100%; height: 100%;
    max-width: none;
    object-fit: cover;
}

.feature-body {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 9px;
    padding: 30px 32px 28px;
}

.feature-tag {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 26px;
    padding: 0 13px;
    border-radius: 99px;
    background: var(--blush);
    color: var(--blush-ink);
    font-family: 'Roboto Condensed', sans-serif;
    font-size: 10px;
    letter-spacing: .13em;
    text-transform: uppercase;
}

.feature-tag i { font-size: 11px; }

.feature-title {
    font-size: 27px;
    font-weight: 200;
    line-height: 1.2;
    letter-spacing: -.022em;
    text-wrap: balance;
}

.feature-date { font-size: 12.5px; color: var(--text-faint); }

.feature-go {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    margin-top: 10px;
    font-size: 11px;
    letter-spacing: .13em;
    text-transform: uppercase;
    color: var(--blush);
}

.feature-go i { transition: transform .18s ease; }
.feature:hover .feature-go i { transform: translateX(4px); }

/* Das Regal */
.shelf { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 12px; margin-top: 22px; }

.shelf-card {
    display: flex;
    align-items: center;
    gap: 13px;
    padding: 12px 15px 12px 12px;
    border-radius: var(--r-lg);
    background: var(--panel-2);
    border: 1px solid var(--line);
    color: var(--text);
}

.shelf-card:hover { background: #fff; border-color: var(--blush-soft); color: var(--text); }

.shelf-shot {
    display: block;
    flex: none;
    width: 44px; height: 44px;
    max-width: none;
    border-radius: 10px;
    object-fit: cover;
    background: var(--panel-3);
}

.shelf-shot.is-blank { display: grid; place-items: center; color: var(--text-faint); font-size: 15px; }

.shelf-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.shelf-title { font-size: 14px; }
.shelf-date { font-size: 11px; color: var(--text-faint); }

/* Auf der Seite eines Moments trägt das Bild die Seite – es steht
   deshalb über dem Titel und nicht darunter. */
.moment-shot { margin: 0 0 30px; }

.moment-shot img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: var(--r-lg);
}

/* ── Die B-Side ──────────────────────────────────────────────────
   Ein einziges Stück, und die ganze Seite lebt davon. Deshalb keine
   Liste, sondern eine Bühne: Cover, Titel, Spieler, und darunter der
   Text, der mitläuft.
   ────────────────────────────────────────────────────────────── */

.bside { margin-top: 30px; }

.bside-stage { display: flex; align-items: flex-end; gap: 26px; }

.bside-cover {
    display: block;
    flex: none;
    width: 210px; height: 210px;
    max-width: none;
    border-radius: var(--r-lg);
    object-fit: cover;
    background: var(--panel-3);
    box-shadow: 0 30px 60px -30px var(--blush);
}

.bside-cover.is-blank { display: grid; place-items: center; color: var(--text-faint); font-size: 40px; }

.bside-meta { display: flex; flex-direction: column; gap: 6px; min-width: 0; padding-bottom: 6px; }

.bside-title {
    font-size: 40px;
    font-weight: 200;
    line-height: 1.1;
    letter-spacing: -.028em;
    text-wrap: balance;
}

.bside-artist { font-size: 15px; color: var(--text-dim); }

.bside-length {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-top: 4px;
    font-size: 12px;
    color: var(--text-faint);
    font-variant-numeric: tabular-nums;
}

.bside-intro { margin-top: 28px; max-width: 68ch; }

/* Der Spieler des Browsers – er steht da, bis das Skript ihn ablöst. */
.bside-native { display: block; width: 100%; margin-top: 28px; }
.bside-native.is-hidden { display: none; }

/* ── Der eigene Spieler ─────────────────────────────────────── */

.bside-player {
    display: flex;
    align-items: center;
    gap: 18px;
    margin-top: 28px;
    padding: 16px 22px 16px 16px;
    border-radius: 99px;
    border: 1px solid var(--line);
    background: var(--panel-2);
}

.bside-play {
    display: grid;
    place-items: center;
    flex: none;
    width: 52px; height: 52px;
    border: 0;
    border-radius: 50%;
    background: var(--blush);
    color: var(--blush-ink);
    font-size: 16px;
    cursor: pointer;
    box-shadow: 0 14px 28px -14px var(--blush);
    transition: transform .16s ease, filter .16s ease;
}

.bside-play:hover { transform: scale(1.05); filter: brightness(1.05); }

/* Das Rad: eine Haarlinie, die sich füllt. Der Griff erscheint erst
   unter dem Zeiger – solange niemand hinlangt, ist es eine Anzeige. */
.bside-seek { flex: 1 1 auto; min-width: 0; padding: 12px 0; cursor: pointer; }

.bside-rail {
    display: block;
    position: relative;
    height: 4px;
    border-radius: 99px;
    background: var(--line);
    overflow: visible;
}

.bside-fill {
    display: block;
    height: 100%;
    width: 0;
    border-radius: 99px;
    background: var(--blush);
}

.bside-fill::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 100%;
    width: 12px; height: 12px;
    transform: translate(-50%, -50%) scale(0);
    border-radius: 50%;
    background: var(--blush);
    transition: transform .16s ease;
}

.bside-seek:hover .bside-fill::after,
.bside-seek:focus-visible .bside-fill::after { transform: translate(-50%, -50%) scale(1); }

.bside-clock {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: 'Roboto Mono', monospace;
    font-size: 11.5px;
    color: var(--text-faint);
    font-variant-numeric: tabular-nums;
}

.bside-clock b { color: var(--text); font-weight: 400; }
.bside-slash { opacity: .45; }

/* ── Die Lyrics ─────────────────────────────────────────────── */

/* Eine Karte, die sich auf- und zuklappen lässt. Offen ist sie der
   Normalfall: der Text gehört zum Stück. */
.bside-words {
    margin-top: 34px;
    border-radius: var(--r-lg);
    border: 1px solid var(--line);
    background: var(--panel-2);
    overflow: hidden;
}

.bside-words-head {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 15px 20px;
    border-bottom: 1px solid var(--line);
}

/* Zugeklappt ist der Kopf der ganze Kasten – dann trennt er nichts. */
.bside-words.is-hidden .bside-words-head { border-bottom: 0; }

.bside-words-title {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    font-size: 11px;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--text-faint);
}

.bside-toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
    padding: 6px 12px;
    border: 0;
    border-radius: 99px;
    background: none;
    color: var(--text-faint);
    font: inherit;
    font-size: 11px;
    letter-spacing: .1em;
    text-transform: uppercase;
    cursor: pointer;
}

.bside-toggle:hover { background: var(--bg); color: var(--blush); }

/* Das Fenster. Es zeigt fünf Zeilen und sonst nichts: keine Rollleiste,
   nichts von Hand zu bewegen – was oben und unten hinausläuft, ist weg.

   Die Höhe steht als Vielfaches einer Zeile, und eine Zeile rechnet
   sich aus ihrer eigenen Schriftgrösse: `em` allein ginge nicht, das
   bezöge sich auf den Kasten und nicht auf den Text darin. */
.lyrics {
    --lyric-size: 19px;
    --lyric-line: calc(var(--lyric-size) * 1.5 + 14px);

    position: relative;
    padding: 18px 26px 20px;
    scroll-behavior: smooth;
}

/* Erst wenn der Text der Zeit folgt, wird er zum Fenster. Ohne Skript
   und ohne Zeitmarken bleibt er eine gewöhnliche Liste – sonst wäre er
   in einem Kasten eingesperrt, den niemand bewegen kann.

   `content-box` und kein senkrechter Rand: die Höhe soll genau fünf
   Zeilen meinen. Ein Rand zählte zum Fenster dazu und liesse mehr
   sehen, als abgemacht ist. */
.bside.is-synced .lyrics {
    box-sizing: content-box;
    height: calc(5 * var(--lyric-line));
    padding: 0 26px;
    overflow: hidden;
}

/* Vorlauf und Nachlauf sind kein Rand, sondern Weg: die erste und die
   letzte Zeile sollen bis in die Mitte des Fensters wandern können, und
   dafür muss über und unter ihnen etwas zu scrollen sein. Als Inhalt
   statt als Rand, damit das Fenster seine fünf Zeilen behält. */
.bside.is-synced .lyrics::before,
.bside.is-synced .lyrics::after {
    content: "";
    display: block;
    height: calc(3 * var(--lyric-line));
}

.bside-words.is-hidden .lyrics { display: none; }

/* Den Abstand nach unten trägt die Karte – das Fenster selbst muss
   randlos bleiben. */
.bside.is-synced .bside-words { padding-bottom: 20px; }

/* Der Text steht in der Mitte und ist leicht gesetzt; die Zeile, die
   dran ist, wird schwer und eine Spur grösser. Beides läuft weich
   ineinander – Roboto ist eine Variable Font, das Gewicht lässt sich
   also wirklich animieren und schaltet nicht um.

   Grösser wird die Zeile über `transform` und nicht über `font-size`:
   so bleibt ihr Kasten, wie er ist. Die Nachbarn rücken damit nicht
   beiseite, und die Rechnung fürs Scrollen bleibt gültig. */
.lyric {
    padding: 7px 0;
    text-align: center;
    text-wrap: balance;
    font-size: var(--lyric-size, 19px);
    font-weight: 300;
    line-height: 1.5;
    letter-spacing: -.012em;
    color: var(--text);
    transition: opacity .45s ease, color .45s ease,
                font-weight .45s ease, transform .45s ease;
}

.lyric-text { display: inline-block; position: relative; }

.lyric.is-gap { padding: 0; font-size: 10px; }

/* ── Der Refrain ─────────────────────────────────────────────────
   Er ist die Stelle, auf die das Stück zuläuft, und darf das auch
   zeigen: eine Spur wärmer als der Rest, und wenn er dran ist, steigen
   zwei Funken daneben auf – dieselben, die in der Inbox ein Wort
   glitzern lassen (`fx-twinkle` in effects.css). Nichts davon nimmt
   Platz ein, also springt auch nichts.

   Im .lrc steht er als `>` am Anfang der Zeile – dasselbe Zeichen, mit
   dem der Fliesstext ein Zitat absetzt.
   ────────────────────────────────────────────────────────────── */

.lyric.is-chorus { color: var(--blush); font-weight: 400; }

.bside.is-synced .lyric.is-chorus[data-side="jetzt"] {
    text-shadow: 0 0 26px var(--blush-soft);
}

/* Ein Korn. Wo es sitzt, wie gross es ist, wie schnell und wann es
   aufsteigt, sagt das Skript – CSS kann nicht würfeln, und regelmässige
   Funken sähen aus wie eine Maschine. Hier steht nur, was für alle
   gleich ist: die Form und der Takt selbst (`fx-twinkle` aus
   effects.css, derselbe wie bei einem Wort in der Inbox). */
.spark {
    position: absolute;
    top: var(--y);
    left: var(--x);
    width: var(--size);
    height: var(--size);
    background: var(--blush);
    clip-path: polygon(50% 0%, 59% 41%, 100% 50%, 59% 59%, 50% 100%, 41% 59%, 0% 50%, 41% 41%);
    opacity: 0;
    pointer-events: none;
    animation: fx-twinkle var(--beat) ease-in-out var(--off) infinite;
}

/* Im Fenster tritt zurück, was nicht dran ist – von Anfang an. Vor dem
   ersten Ton ist die erste Zeile das Kommende und sonst nichts zu
   sehen; auch das ist die Regel und keine Ausnahme davon. */
.bside.is-synced .lyric { opacity: .16; }

.bside.is-synced .lyric[data-side="jetzt"] {
    opacity: 1;
    color: var(--blush);
    font-weight: 700;
    transform: scale(1.09);
}
.bside.is-synced .lyric[data-side="gleich"] { opacity: .34; }

/* Vom Gewesenen bleibt sichtbar, was nah ist; weiter oben verblasst es. */
.bside.is-synced .lyric[data-side="vorbei"][data-away="1"] { opacity: .5; }
.bside.is-synced .lyric[data-side="vorbei"][data-away="2"] { opacity: .28; }
.bside.is-synced .lyric[data-side="vorbei"][data-away="3"] { opacity: .14; }

/* Was noch kommt, bleibt verdeckt – bis auf die eine Zeile darüber.
   Vorauslesen soll man den Text nicht. */
.bside.is-synced .lyric[data-side="spaeter"] { opacity: 0; visibility: hidden; }

/* Oben und unten läuft der Text aus, statt an einer Kante
   abzuschneiden – die aktive Zeile steht ja in der Mitte. */
.bside.is-synced .lyrics {
    -webkit-mask-image: linear-gradient(transparent, #000 30%, #000 70%, transparent);
    mask-image: linear-gradient(transparent, #000 30%, #000 70%, transparent);
}

@media (prefers-reduced-motion: reduce) {
    .bside-play { transition: none; }
    .bside-play:hover { transform: none; }
    .lyric { transition: none; }
    .lyric[data-side="jetzt"] { transform: none; }
    .spark { display: none; }
    .lyrics { scroll-behavior: auto; }
}

@media (max-width: 760px) {
    .bside-stage { flex-direction: column; align-items: flex-start; gap: 20px; }
    .bside-cover { width: 100%; height: auto; aspect-ratio: 1; }
    .bside-title { font-size: 30px; }
    .bside-player { gap: 13px; padding: 12px 18px 12px 12px; }
    .bside-play { width: 46px; height: 46px; }
    .lyrics { --lyric-size: 17px; }
}

/* ── Das Raster zum Aufdecken ────────────────────────────────────
   Alle Stücke liegen auf einmal da, jedes an seinem festen Platz –
   ein bisschen wie ein Memory. Was offen ist, zeigt sein Bild; was zu
   ist, zeigt die Rückseite. Der Platz bleibt derselbe, egal wie oft
   sie wiederkommt: daran erkennt sie ihren eigenen Stand.
   ────────────────────────────────────────────────────────────── */

.deck-head {
    display: flex;
    align-items: center;
    gap: 14px;
    margin-top: 34px;
    padding: 14px 18px;
    border-radius: var(--r-lg);
    background: var(--panel-2);
    border: 1px solid var(--line);
}

/* Der Tag als Punkte: was noch geht, ist gefüllt. Die Zahl daneben
   sagt dasselbe in Worten – die Punkte sind für den Blick, der Satz
   für alle anderen. */
.deck-dots { display: inline-flex; align-items: center; gap: 6px; flex: none; }

.deck-dot {
    width: 9px; height: 9px;
    border-radius: 50%;
    background: var(--blush);
    box-shadow: 0 0 0 3px var(--blush-veil);
}

.deck-dot.is-used { background: transparent; border: 1px solid var(--line); box-shadow: none; }

.deck-says { font-size: 13.5px; color: var(--text-dim); }

.deck-count {
    margin-left: auto;
    flex: none;
    font-family: 'Roboto Condensed', sans-serif;
    font-size: 12px;
    letter-spacing: .06em;
    color: var(--text-faint);
    font-variant-numeric: tabular-nums;
}

.deck {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 14px;
    margin-top: 16px;
}

.deck-card {
    display: flex;
    align-items: center;
    gap: 15px;
    padding: 14px 18px 14px 14px;
    border-radius: var(--r-lg);
    border: 1px solid var(--line);
    background: var(--panel-2);
    color: var(--text);
    transition: transform .16s ease, border-color .16s ease, background .16s ease;
}

.deck-face { flex: none; }

/* Das Feld links trägt entweder das Bild des Stücks oder – solange es
   zugedeckt ist – das Zeichen des Bereichs. Beides im selben Rahmen,
   damit die Karten in einer Reihe stehen und nicht in zwei. */
.deck-shot {
    display: block;
    width: 58px; height: 58px;
    max-width: none;
    border-radius: 13px;
    object-fit: cover;
    background: var(--panel-3);
}

.deck-shot.is-blank { display: grid; place-items: center; color: var(--text-faint); font-size: 17px; }

.deck-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }

.deck-title {
    font-size: 15px;
    line-height: 1.35;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

.deck-note { font-size: 11.5px; color: var(--text-faint); }

.deck-go {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    margin-top: 3px;
    font-size: 10px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--text-faint);
}

.deck-go i { font-size: 10px; }

/* Aufgedeckt: die Karte steht im Licht und lässt sich wieder öffnen. */
.deck-card.is-open { background: #fff; border-color: var(--blush-soft); }
.deck-card.is-open:hover { color: var(--text); transform: translateY(-2px); box-shadow: 0 18px 34px -26px var(--blush); }
.deck-card.is-open .deck-go { color: var(--blush); }

/* Bereit: die Rückseite, aber sie lässt sich umdrehen. */
.deck-card.is-ready { border-style: dashed; }
.deck-card.is-ready:hover { color: var(--text); background: #fff; border-color: var(--blush); transform: translateY(-2px); }
.deck-card.is-ready .deck-shot.is-blank { background: var(--blush-veil); color: var(--blush); }
.deck-card.is-ready .deck-go { color: var(--blush); }

/* Zu: heute nicht mehr. Sie bleibt lesbar, nimmt aber keinen Klick an. */
.deck-card.is-shut { border-style: dashed; opacity: .55; cursor: default; }
.deck-card.is-shut .deck-title { color: var(--text-dim); }

@media (prefers-reduced-motion: reduce) {
    .deck-card { transition: none; }
    .deck-card:hover { transform: none; }
}

@media (max-width: 620px) {
    .deck-head { flex-wrap: wrap; gap: 10px 12px; }
    .deck-count { margin-left: 0; }
}

/* Der Weg zurück, über allem anderen auf der Seite einer Geschichte */
.back {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 26px;
    font-size: 11px;
    letter-spacing: .13em;
    text-transform: uppercase;
    color: var(--text-faint);
}

.back:hover { color: var(--blush); }

.story-shot { margin: 34px 0; }

.story-shot img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: var(--r-lg);
}

.story-text { max-width: 79ch; font-size: 16.5px; }

/* Ein Tagebucheintrag trägt mehr als nur ein Datum: Ort und Wetter
   standen damals mit da und gehören deshalb unter den Titel. */
.entry-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 20px;
    margin-top: 14px;
    color: var(--text-faint);
    font-size: 13px;
}

.entry-meta span { display: inline-flex; align-items: center; gap: 8px; }
.entry-star { margin-left: 8px; color: var(--blush); font-size: .6em; vertical-align: middle; }

@media (prefers-reduced-motion: reduce) {
    .feature-go i { transition: none; }
}

@media (max-width: 760px) {
    .feature { flex-direction: column; }
    .feature-shot { width: 100%; max-width: none; height: 180px; }
    .feature-body { padding: 24px 24px 22px; }
    .feature-title { font-size: 23px; }
}

/* ── Eine Frage ──────────────────────────────────────────────────
   Die Seite hat nur einen Gedanken: oben die Frage, darunter die Wege
   hinaus. Jede Antwort ist ein Knopf – ein Klick, und die nächste Frage
   rückt nach.
   ────────────────────────────────────────────────────────────── */

.ask { margin-top: 40px; }

.choices { display: flex; flex-direction: column; gap: 12px; max-width: 720px; }
.choices.is-pair { flex-direction: row; }
.choices.is-pair .choice { flex: 1 1 0; }

.choice {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 17px 22px;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: var(--panel-2);
    color: var(--text);
    font: inherit;
    font-size: 15px;
    text-align: left;
    cursor: pointer;
    transition: border-color .16s ease, background .16s ease, transform .16s ease;
}

.choice:hover {
    border-color: var(--blush-soft);
    background: #fff;
    color: var(--text);
    transform: translateX(3px);
}

.choice-dot {
    flex: none;
    width: 13px; height: 13px;
    border-radius: 50%;
    background: #fff;
    box-shadow: inset 0 0 0 1px var(--line);
    transition: box-shadow .16s ease, background .16s ease;
}

.choice:hover .choice-dot { background: var(--blush); box-shadow: inset 0 0 0 1px var(--blush); }

.ask-text {
    display: block;
    width: 100%;
    max-width: 720px;
    padding: 16px 18px;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: var(--panel-2);
    color: var(--text);
    font: inherit;
    font-size: 15px;
    line-height: 1.7;
    resize: vertical;
}

.ask-text:focus { outline: 0; border-color: var(--blush); background: #fff; }
.ask-text::placeholder { color: var(--text-faint); }

.ask-end { margin-top: 16px; }

@media (prefers-reduced-motion: reduce) {
    .choice, .choice-dot { transition: none; }
    .choice:hover { transform: none; }
}

/* ── Die Bewertung unter einem Inhalt ────────────────────────────
   Dieselbe Fläche unter jedem Stück: eine Frage, drei Wege hinaus, eine
   Zeile darunter, die sagt, woran man gerade ist. Sie steht bewusst
   abgesetzt vom Text – erst endet der Inhalt, dann kommt die Rückfrage.

   Drei Zustände, alle am Formular: nichts gewählt, frisch bewertet
   (die Frist läuft als Strich darunter ab) und gelocked.
   ────────────────────────────────────────────────────────────── */

.rate {
    position: relative;
    isolation: isolate;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 20px;
    margin-top: 58px;
    padding: 40px 36px 30px;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: linear-gradient(168deg, var(--blush-veil), transparent 62%), var(--panel-2);
    text-align: center;
    overflow: hidden;
}

/* Derselbe Schimmer wie hinter einer leeren Stelle – die Fläche soll
   einladen und nicht wie ein Formular am Seitenende wirken. */
.rate::before {
    content: "";
    position: absolute;
    z-index: -1;
    top: -190px;
    left: 50%;
    width: 420px; height: 420px;
    transform: translateX(-50%);
    border-radius: 50%;
    background: radial-gradient(circle, var(--blush-veil) 0%, transparent 70%);
    filter: blur(40px);
    pointer-events: none;
}

.rate-ask {
    font-size: 24px;
    font-weight: 200;
    line-height: 1.25;
    letter-spacing: -.02em;
    text-wrap: balance;
}

.rate-picks {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 12px;
}

.rate-pick {
    display: inline-flex;
    align-items: center;
    gap: 11px;
    height: 52px;
    padding: 0 26px;
    border: 1px solid var(--blush-soft);
    border-radius: 99px;
    background: #fff;
    color: var(--text);
    font-family: 'Roboto Condensed', sans-serif;
    font-size: 13px;
    letter-spacing: .11em;
    text-transform: uppercase;
    cursor: pointer;
    transition: transform .16s ease, border-color .16s ease,
                background .16s ease, color .16s ease, box-shadow .22s ease;
}

.rate-pick:hover {
    transform: translateY(-2px);
    border-color: var(--blush);
    box-shadow: 0 16px 30px -20px var(--blush);
}

.rate-emoji { font-size: 16px; line-height: 1; }

/* Die gewählte Antwort füllt sich – sie ist ab jetzt eine Aussage und
   keine Möglichkeit mehr. */
.rate-pick.is-on {
    border-color: transparent;
    background: var(--blush);
    color: var(--blush-ink);
    box-shadow: 0 18px 34px -20px var(--blush);
}

.rate-pick.is-on:hover { transform: translateY(-2px); }

/* Was nicht gewählt wurde, tritt zurück, sobald eine Antwort steht. */
.rate.is-rated .rate-pick:not(.is-on) { border-color: var(--line); background: transparent; color: var(--text-faint); }
.rate.is-rated .rate-pick:not(.is-on):hover { border-color: var(--blush-soft); background: #fff; color: var(--text); }

.rate.is-busy { pointer-events: none; }

/* Gelocked: die Wahl bleibt sichtbar, die anderen Wege verblassen. */
.rate.is-locked .rate-pick { cursor: default; }
.rate.is-locked .rate-pick:hover { transform: none; box-shadow: none; border-color: var(--line); }
.rate.is-locked .rate-pick:not(.is-on) { opacity: .38; }
.rate.is-locked .rate-pick.is-on:hover { border-color: transparent; box-shadow: 0 18px 34px -20px var(--blush); }

/* Der Satz darunter steht auf einer Zeile, solange die Breite reicht –
   er ist eine Beiläufigkeit und soll keinen Block bilden. */
.rate-note {
    max-width: 100%;
    color: var(--text-faint);
    font-size: 13px;
    line-height: 1.65;
    text-wrap: balance;
}

.rate-note b { color: var(--blush); font-weight: 500; font-variant-numeric: tabular-nums; }

/* Die Frist als Haarlinie: sie läuft leer, und wenn sie leer ist, steht
   die Bewertung. Ohne Bewertung ist sie gar nicht erst zu sehen. */
.rate-track {
    width: 132px; height: 2px;
    border-radius: 99px;
    background: var(--line);
    opacity: 0;
    overflow: hidden;
    transition: opacity .3s ease;
}

.rate.is-rated:not(.is-locked) .rate-track { opacity: 1; }

.rate-fill {
    display: block;
    height: 100%;
    border-radius: 99px;
    background: var(--blush);
    transition: width 1s linear;
}

/* Der Sprung im Augenblick des Wählens – einmal, dann ist er fertig. */
@keyframes rate-pop {
    0%   { transform: translateY(-2px) scale(1); }
    45%  { transform: translateY(-3px) scale(1.07); }
    100% { transform: translateY(0) scale(1); }
}

.rate-pick.is-pop { animation: rate-pop .42s cubic-bezier(.2, .8, .2, 1); }

@media (prefers-reduced-motion: reduce) {
    .rate-pick, .rate-fill, .rate-track { transition: none; }
    .rate-pick:hover { transform: none; }
    .rate-pick.is-pop { animation: none; }
}

@media (max-width: 620px) {
    .rate { margin-top: 42px; padding: 30px 20px 24px; gap: 16px; }
    .rate-ask { font-size: 21px; }
    .rate-picks { flex-direction: column; align-self: stretch; }
    .rate-pick { justify-content: center; width: 100%; }
}

/* Zugabe für den Zeiger: die Karte neigt sich leicht zu ihm hin. Die
   Klasse setzt das Skript – ohne es bleibt alles flach. */
.unlocks.has-tilt .unlock {
    transform: perspective(1000px)
               rotateX(var(--tilt-x, 0deg))
               rotateY(var(--tilt-y, 0deg));
    transition: transform .55s cubic-bezier(.2, .8, .2, 1), box-shadow .35s ease;
}

/* Unter dem Zeiger folgt sie fast unmittelbar; losgelassen schwingt sie
   mit der längeren Dauer von oben zurück. */
.unlocks.has-tilt .unlock.is-tilt {
    transform: perspective(1000px)
               rotateX(var(--tilt-x, 0deg))
               rotateY(var(--tilt-y, 0deg))
               scale(1.02);
    transition: transform .12s ease-out, box-shadow .35s ease;
    box-shadow: 0 30px 56px -30px rgba(96, 32, 60, .6);
    z-index: 1;
}

.unlocks.has-tilt .unlock.is-bereit.is-tilt { box-shadow: 0 34px 62px -28px var(--blush); }

/* ── Ein neues Level ─────────────────────────────────────────── */
/* Die Karte steht in der Mitte, nimmt aber keine Klicks an und legt sich
   auch nicht über die Seite: es soll ein Aufleuchten sein, kein Dialog,
   den man wegräumen muss. */

.levelup {
    position: fixed;
    inset: 0;
    z-index: 70;
    display: grid;
    place-items: center;
    padding: 24px;
    pointer-events: none;
}

.levelup-card {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    min-width: 320px;
    padding: 48px 78px 44px;
    border-radius: var(--r-xl);
    background: var(--glass);
    border: 1px solid var(--glass-edge);
    backdrop-filter: blur(30px) saturate(160%);
    -webkit-backdrop-filter: blur(30px) saturate(160%);
    box-shadow: 0 50px 110px -40px rgba(96, 32, 60, .55), 0 0 70px -24px var(--blush);
    text-align: center;
    opacity: 0;
    transform: translateY(14px) scale(.94);
    transition: opacity .5s ease, transform .5s cubic-bezier(.2, .8, .2, 1);
}

.levelup.is-in .levelup-card  { opacity: 1; transform: none; }
.levelup.is-out .levelup-card { opacity: 0; transform: translateY(-8px) scale(.98); transition: opacity .5s ease, transform .5s ease; }

/* Der Schein hinter dem Emoji */
.levelup-halo {
    position: absolute;
    top: 45%; left: 50%;
    width: 420px; height: 420px;
    border-radius: 50%;
    background: radial-gradient(circle, var(--blush-veil) 0%, transparent 68%);
    opacity: 0;
    transform: translate(-50%, -50%) scale(.5);
}

.levelup.is-in .levelup-halo { animation: levelup-halo 1.5s cubic-bezier(.2, .8, .2, 1) forwards; }

@keyframes levelup-halo {
    0%   { opacity: 0;   transform: translate(-50%, -50%) scale(.5); }
    45%  { opacity: 1;   transform: translate(-50%, -50%) scale(1.06); }
    100% { opacity: .55; transform: translate(-50%, -50%) scale(1); }
}

/* Acht Funken, die nach aussen treiben */
.levelup-sparks { position: absolute; top: 45%; left: 50%; width: 0; height: 0; }

.levelup-sparks i {
    position: absolute;
    top: -4px; left: -4px;
    width: 7px; height: 7px;
    border-radius: 50%;
    /* Der weiche Ton verschwand auf dem hellen Blatt – hier trägt der
       kräftige, dazu ein kleiner Schein. */
    background: var(--blush);
    box-shadow: 0 0 10px -1px var(--blush);
    opacity: 0;
}

/* Jeder Funke fliegt genau einmal und bringt seine eigenen Werte mit:
   Winkel, Weite, Dauer und Start stehen als Custom Properties am Element
   (siehe xp.js). Die Würfe überlagern sich dadurch, statt aufeinander zu
   warten – drei Runden desselben Funkens sahen nach Pause aus. */
.levelup.is-in .levelup-sparks i {
    animation: levelup-spark var(--t, 1s) cubic-bezier(.2, .8, .2, 1) var(--d, 0ms) both;
}

@keyframes levelup-spark {
    0%   { opacity: 0; transform: rotate(var(--a)) translateY(-18px) scale(.4); }
    18%  { opacity: 1; }
    100% { opacity: 0; transform: rotate(var(--a)) translateY(calc(var(--r, 142px) * -1)) scale(1); }
}

.levelup-eyebrow {
    position: relative;
    font-size: 11px;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--text-faint);
}

.levelup-emoji {
    position: relative;
    margin: 16px 0 16px;
    font-size: 76px;
    line-height: 1;
}

.levelup.is-in .levelup-emoji { animation: levelup-pop .7s cubic-bezier(.2, 1.5, .4, 1) both; }

@keyframes levelup-pop {
    0%   { opacity: 0; transform: scale(.4) rotate(-8deg); }
    60%  { opacity: 1; transform: scale(1.1) rotate(2deg); }
    100% { opacity: 1; transform: none; }
}

.levelup-name {
    position: relative;
    margin-bottom: 22px;
    font-size: 34px;
    font-weight: 200;
    line-height: 1.15;
    letter-spacing: -.02em;
    color: var(--text);
}

.levelup-chip {
    position: relative;
    display: inline-flex;
    align-items: center;
    height: 30px;
    padding: 0 17px;
    border-radius: 99px;
    background: var(--blush);
    color: var(--blush-ink);
    font-family: 'Roboto Condensed', sans-serif;
    font-size: 11px;
    letter-spacing: .14em;
    text-transform: uppercase;
}

/* Wer weniger Bewegung will, bekommt die Nachricht trotzdem – nur ohne
   Lauf, ohne Funken und ohne Aufblitzen. */
@media (prefers-reduced-motion: reduce) {
    .levelup-card,
    .levelup.is-in .levelup-card,
    .levelup.is-out .levelup-card { transform: none; transition: opacity .3s ease; }

    .levelup.is-in .levelup-halo,
    .levelup.is-in .levelup-emoji,
    .levelup.is-in .levelup-sparks i { animation: none; }

    .levelup-halo  { opacity: .5; }
    .levelup-emoji { opacity: 1; }
    .levelup-sparks { display: none; }

    .xpbar.is-levelup .xpbar-fill::after { display: none; }
}

/* ── Der Zettel für frisch verdiente XP ──────────────────────── */

.gain {
    position: fixed;
    right: 30px;
    bottom: 30px;
    z-index: 50;
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 14px 22px;
    border-radius: 99px;
    background: var(--glass);
    border: 1px solid var(--glass-edge);
    backdrop-filter: blur(30px) saturate(155%);
    -webkit-backdrop-filter: blur(30px) saturate(155%);
    box-shadow: var(--lift);
    font-size: 13px;
    color: var(--text-dim);
    animation: gain-in .5s cubic-bezier(.2, .8, .2, 1), gain-out .7s ease 5s forwards;
}

.gain i { color: var(--blush); }
.gain b { color: var(--text); font-weight: 500; }

@keyframes gain-in  { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
@keyframes gain-out { to { opacity: 0; transform: translateY(10px); visibility: hidden; } }

@media (prefers-reduced-motion: reduce) {
    .gain { animation: none; }
}

@media (max-width: 1000px) {
    .scene-site { padding: 16px; }
    .shell { grid-template-columns: 1fr; gap: 16px; }
    .side { position: static; flex-direction: row; align-items: center; gap: 16px;
            min-height: 0; padding: 14px 16px; overflow-x: auto; }

    /* Mit Skript übernimmt der Hamburger, und die Spalte verschwindet. */
    .has-nav-toggle .side { display: none; }

    .has-nav-toggle .topbar {
        position: sticky;
        top: 16px;
        z-index: 60;
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 16px;
        padding: 10px 10px 10px 18px;
        border-radius: var(--r-xl);
        background: var(--glass);
        border: 1px solid var(--glass-edge);
        backdrop-filter: blur(30px) saturate(155%);
        -webkit-backdrop-filter: blur(30px) saturate(155%);
        box-shadow: var(--lift);
    }

    /* Im Kopf steht das Guthaben schon – die Karte hier wäre dieselbe
       Zahl ein zweites Mal. Ohne Skript gibt es keinen Kopf, dann bleibt
       sie stehen. */
    .has-nav-toggle .tally { display: none; }
    .side-brand { padding: 0; }
    .side-nav { flex-direction: row; }
    .side-item span:not(.side-lock) { display: none; }
    .side-item { padding: 0 13px; }
    .side-foot { margin: 0 0 0 auto; flex-direction: row; }
    /* Schmal reicht der Platz nicht für zwei klebende Zeilen: der Kopf
       bleibt oben, die Leiste läuft mit dem Inhalt mit. */
    .topline { position: static; gap: 16px; margin-bottom: 16px; }
    .xpbar { gap: 18px; padding: 14px 18px; }
    .tally { padding: 14px 18px; }
    .sheet { padding: 36px 26px 34px; }
    .headline { font-size: 32px; }
    .levelup-card { min-width: 0; padding: 36px 42px 32px; }
    .levelup-emoji { font-size: 60px; }
    .levelup-name { font-size: 27px; }
    .levelup-halo { width: 320px; height: 320px; }
    .unlocks { margin-top: 26px; gap: 14px; }
    .unlock { padding: 22px; }
    .choices.is-pair { flex-direction: column; }
    .opened-head { margin-top: 32px; padding-top: 22px; }
    .opened { margin-top: 16px; }
    .subhead { font-size: 22px; }
    .gain { right: 16px; left: 16px; bottom: 16px; justify-content: center; }
}

@media (max-width: 620px) {
    .topline { flex-direction: column; gap: 12px; }
    .tally { justify-content: flex-start; }
    .tally::after { right: auto; left: 14px; }
    .tally::before { right: auto; left: 28px; }
    .xpbar { flex-wrap: wrap; gap: 11px 16px; }
    .xpbar-who { flex-basis: 100%; }
    .xpbar-track { order: 2; flex-basis: 100%; }
    .xpbar-count {
        order: 3;
        flex-basis: 100%;
        flex-direction: row;
        justify-content: space-between;
        align-items: baseline;
        gap: 10px;
        text-align: left;
    }
}

/* ── Die Zahl neben einem Bereich ─────────────────────────────────
   Ungelesenes in der Navigation. Sie steht nur da, wenn es etwas zu
   zählen gibt – siehe unlock_badge().
   ────────────────────────────────────────────────────────────── */

.side-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 20px;
    margin-left: auto;
    padding: 0 6px;
    border-radius: 99px;
    background: var(--blush);
    color: var(--blush-ink);
    font-family: 'Roboto Condensed', sans-serif;
    font-size: 11px;
    letter-spacing: .02em;
}

.side-item.is-active .side-badge { background: var(--blush); color: var(--blush-ink); }
.sheet-item .side-badge { margin-left: 0; }

/* Der Wimpel an einem frisch freigeschalteten Bereich. Er trägt ein
   Wort statt einer Zahl und geht beim ersten Besuch von selbst weg –
   deshalb darf er kurz auf sich aufmerksam machen. */
.side-badge.is-fresh {
    padding: 0 9px;
    letter-spacing: .08em;
    text-transform: uppercase;
    font-size: 9.5px;
    animation: fresh-pulse 2.4s ease-in-out infinite;
}

@keyframes fresh-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(0, 0, 0, 0); }
    50%      { box-shadow: 0 0 0 4px var(--blush-veil); }
}

@media (prefers-reduced-motion: reduce) {
    .side-badge.is-fresh { animation: none; }
}

/* ──────────────────────────────────────────────────────────────
   Inbox – der Faden.

   Ein Blatt, das den Bildschirm ausfüllt: oben, wer da ist, in der
   Mitte der Verlauf, unten das Feld. Gescrollt wird der Verlauf und
   nicht die Seite, damit das Feld immer in Reichweite bleibt.
   ────────────────────────────────────────────────────────────── */

.talk-sheet {
    /* Rand oben und unten, die Leiste darüber und ihr Abstand. */
    --talk-around: 168px;

    position: relative;
    display: flex;
    flex-direction: column;
    height: calc(100svh - var(--talk-around));
    min-height: 460px;
    padding: 22px 26px 18px;
    border-radius: var(--r-xl);
    background: var(--glass);
    border: 1px solid var(--glass-edge);
    backdrop-filter: blur(30px) saturate(155%);
    -webkit-backdrop-filter: blur(30px) saturate(155%);
    box-shadow: var(--lift);
}

/* ── Wer da ist ──────────────────────────────────────────────── */

.talk-head {
    display: flex;
    align-items: center;
    gap: 14px;
    flex: none;
    padding: 4px 6px 16px;
    border-bottom: 1px solid rgba(255, 255, 255, .85);
}

.talk-face {
    display: grid;
    place-items: center;
    flex: none;
    width: 42px; height: 42px;
    border-radius: 50%;
    background: var(--blush);
    color: var(--blush-ink);
    font-size: 13px;
    letter-spacing: .04em;
    box-shadow: 0 12px 24px -12px var(--blush);
}

.talk-who { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.talk-name { font-size: 16px; letter-spacing: -.01em; }

/* Der Satz bleibt eine Zeile: er wechselt beim Nachhören, und eine
   Zeile, die dabei umbricht, ruckelt den ganzen Kopf. */
.talk-state {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
    font-size: 11.5px;
    color: var(--text-faint);
    white-space: nowrap;
}

.talk-state > span:last-child { overflow: hidden; text-overflow: ellipsis; }

.talk-dot {
    flex: none;
    width: 7px; height: 7px;
    border-radius: 50%;
    background: var(--text-faint);
    opacity: .5;
}

/* Anwesend: ein ruhiger Puls, kein Blinken. */
.talk-state.is-here { color: var(--text-dim); }

.talk-state.is-here .talk-dot {
    background: oklch(0.72 0.16 150);
    opacity: 1;
    animation: talk-live 2.4s ease-in-out infinite;
}

@keyframes talk-live {
    0%, 100% { box-shadow: 0 0 0 0 oklch(0.72 0.16 150 / .55); }
    70%      { box-shadow: 0 0 0 7px oklch(0.72 0.16 150 / 0); }
}

.talk-tally {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-left: auto;
    font-family: 'Roboto Condensed', sans-serif;
    font-size: 14px;
    color: var(--text-faint);
}

.talk-tally span { display: inline-flex; align-items: center; gap: 7px; }
.talk-tally i { font-size: 12px; }
.talk-tally b { font-weight: 500; color: var(--text-dim); }

/* ── Der Verlauf ─────────────────────────────────────────────────
   Oben läuft er weich aus, damit die Blasen nicht an der Kante
   abgeschnitten wirken, wenn darüber noch etwas liegt.
   ────────────────────────────────────────────────────────────── */

.talk {
    flex: 1 1 auto;
    min-height: 0;
    padding: 16px 6px 6px;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 22px);
    mask-image: linear-gradient(to bottom, transparent 0, #000 22px);
}

.talk-thread { display: flex; flex-direction: column; }

.talk .empty { margin: auto 0; }

/* ── Eine Blase ──────────────────────────────────────────────── */

.msg { display: flex; align-items: flex-end; gap: 10px; padding-bottom: 3px; }
.msg.is-open { margin-top: 15px; }
.msg.is-grouped { margin-top: 2px; }
.msg.is-mine { flex-direction: row-reverse; }

.msg-face {
    display: grid;
    place-items: center;
    flex: none;
    width: 31px; height: 31px;
    margin-bottom: 2px;
    border-radius: 50%;
    background: var(--panel-3);
    color: var(--text-dim);
    font-size: 10.5px;
    letter-spacing: .04em;
}

.msg.is-mine .msg-face { background: var(--blush); color: var(--blush-ink); }

/* Angehängt heisst: dasselbe Gesicht wie eine Zeile höher – also
   keines. Der Platz bleibt trotzdem stehen. */
.msg.is-grouped .msg-face { visibility: hidden; }

.msg-body { position: relative; max-width: min(74%, 560px); }

.bubble {
    display: flow-root;
    padding: 11px 16px 10px;
    border-radius: 19px;
    background: #fff;
    border: 1px solid var(--line);
    color: var(--text);
    font-size: 15px;
    line-height: 1.62;
    overflow-wrap: anywhere;
    box-shadow: 0 12px 24px -22px rgba(96, 32, 60, .8);
}

.msg.is-open.is-yours .bubble { border-top-left-radius: 7px; }
.msg.is-open.is-mine  .bubble { border-top-right-radius: 7px; }

.msg.is-mine .bubble {
    background: var(--blush);
    border-color: transparent;
    color: var(--blush-ink);
    box-shadow: 0 18px 32px -22px var(--blush);
}

/* In der eigenen Blase liegt alles auf Blush – die Effekte bekommen
   dafür ihre beiden Regler neu gesetzt. */
.msg.is-mine .bubble .fx {
    --fx-ink: var(--blush-ink);
    --fx-hot: oklch(0.97 0.03 350);
}

/* Die Zeit steht am Ende des Textes und rutscht auf die nächste Zeile,
   wenn die letzte voll ist – deshalb ein Float und keine eigene Reihe. */
.msg-time {
    float: right;
    margin: 8px 0 -1px 12px;
    font-family: 'Roboto Condensed', sans-serif;
    font-size: 11px;
    letter-spacing: .03em;
    color: var(--text-faint);
}

.msg.is-mine .msg-time { color: oklch(1 0 0 / .68); }

.msg-text strong { font-weight: 500; }
.msg-text em { font-style: italic; }

/* ── Das Herz ────────────────────────────────────────────────────
   Es hängt am äusseren unteren Rand der Blase und zeigt sich, wenn der
   Zeiger die Nachricht berührt. Ist es vergeben, bleibt es stehen.
   ────────────────────────────────────────────────────────────── */

.heart {
    position: absolute;
    bottom: -12px;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    height: 24px;
    padding: 0 9px;
    border-radius: 99px;
    background: #fff;
    border: 1px solid var(--line);
    color: var(--text-faint);
    box-shadow: 0 8px 18px -10px rgba(96, 32, 60, .55);
    opacity: 0;
    transform: scale(.8) translateY(5px);
    transition: opacity .18s ease, transform .2s cubic-bezier(.2, .8, .2, 1),
                color .18s ease, border-color .18s ease;
}

.msg.is-yours .heart { right: -8px; }
.msg.is-mine  .heart { left: -8px; }

.msg-body:hover .heart,
.heart:focus-within,
.heart.is-on { opacity: 1; transform: none; }

.heart.is-on { color: var(--blush); border-color: var(--blush-soft); }

/* An der eigenen Nachricht gibt es nichts zu drücken – dort erscheint
   das Herz erst, wenn es eines gibt. */
.heart.is-still:not(.is-on) { display: none; }

.heart > button {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    cursor: pointer;
}

.heart i { font-size: 11.5px; }
.heart b { font-family: 'Roboto Condensed', sans-serif; font-size: 11px; font-weight: 500; }
.heart b:empty { display: none; }

.heart.is-pop i { animation: heart-pop .5s cubic-bezier(.2, .8, .2, 1); }

@keyframes heart-pop {
    0%   { transform: scale(1); }
    35%  { transform: scale(1.5) rotate(-9deg); }
    60%  { transform: scale(.92); }
    100% { transform: scale(1); }
}

/* Die Herzen, die beim Vergeben aufsteigen. Winkel und Dauer kommen
   je Stück aus dem Skript. */
.heart-fly {
    position: absolute;
    bottom: -6px;
    z-index: 3;
    color: var(--blush);
    font-size: 12px;
    pointer-events: none;
    animation: heart-fly var(--t, 1.1s) ease-out forwards;
}

.msg.is-yours .heart-fly { right: 4px; }
.msg.is-mine  .heart-fly { left: 4px; }

@keyframes heart-fly {
    0%   { opacity: 0; transform: translate(0, 0) scale(.6); }
    20%  { opacity: 1; }
    100% { opacity: 0; transform: translate(var(--x, 0), -58px) scale(1.15); }
}

/* ── Trennlinien ─────────────────────────────────────────────── */

.talk-day { display: flex; justify-content: center; margin: 20px 0 6px; }

.talk-day span {
    padding: 5px 14px;
    border-radius: 99px;
    background: rgba(255, 255, 255, .72);
    border: 1px solid var(--glass-edge);
    font-size: 10px;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--text-faint);
}

.talk-fresh {
    display: flex;
    align-items: center;
    gap: 14px;
    margin: 20px 2px 4px;
    color: var(--blush);
    font-size: 10px;
    letter-spacing: .18em;
    text-transform: uppercase;
}

.talk-fresh::before,
.talk-fresh::after { content: ""; flex: 1; height: 1px; background: var(--blush-soft); }

/* ── Sie schreibt gerade ─────────────────────────────────────── */

.msg.is-typing .bubble { padding: 13px 17px; }

.dots { display: inline-flex; align-items: center; gap: 5px; }

.dots i {
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--text-faint);
    animation: dots 1.4s ease-in-out infinite;
}

.dots i:nth-child(2) { animation-delay: .18s; }
.dots i:nth-child(3) { animation-delay: .36s; }

@keyframes dots {
    0%, 60%, 100% { opacity: .3; transform: translateY(0); }
    30%           { opacity: 1;  transform: translateY(-3px); }
}

/* Eine frisch eingetroffene Blase steigt kurz auf. */
.msg.is-new { animation: msg-in .42s cubic-bezier(.2, .8, .2, 1); }

@keyframes msg-in {
    from { opacity: 0; transform: translateY(10px) scale(.97); }
    to   { opacity: 1; transform: none; }
}

/* ── Das Feld ────────────────────────────────────────────────── */

.talk-form {
    display: flex;
    align-items: flex-end;
    gap: 12px;
    flex: none;
    padding-top: 16px;
    border-top: 1px solid rgba(255, 255, 255, .85);
}

.talk-field { position: relative; flex: 1 1 auto; min-width: 0; }

.talk-field textarea {
    display: block;
    width: 100%;
    max-height: 172px;
    padding: 13px 18px;
    border: 1px solid var(--line);
    border-radius: 22px;
    background: #fff;
    color: var(--text);
    font: inherit;
    font-size: 15px;
    line-height: 1.5;
    resize: none;
    transition: border-color .18s ease, box-shadow .18s ease;
}

.talk-field textarea:focus {
    outline: 0;
    border-color: var(--blush-soft);
    box-shadow: 0 0 0 4px var(--blush-veil);
}

.talk-field textarea::placeholder { color: var(--text-faint); }

.talk-left {
    position: absolute;
    right: 16px;
    bottom: 7px;
    font-family: 'Roboto Condensed', sans-serif;
    font-size: 11px;
    color: var(--text-faint);
    pointer-events: none;
}

.talk-send {
    display: grid;
    place-items: center;
    flex: none;
    width: 46px; height: 46px;
    border: 0;
    border-radius: 50%;
    background: var(--blush);
    color: var(--blush-ink);
    font-size: 16px;
    cursor: pointer;
    box-shadow: 0 16px 30px -16px var(--blush);
    transition: transform .18s cubic-bezier(.2, .8, .2, 1), opacity .18s ease;
}

.talk-send:hover { transform: translateY(-2px); }
.talk-send:disabled { opacity: .35; cursor: default; transform: none; box-shadow: none; }

/* Der Flieger hebt kurz ab, wenn die Nachricht weg ist. */
.talk-send.is-away i { animation: send-off .5s ease-in forwards; }

@keyframes send-off {
    0%   { transform: translate(0, 0) scale(1); opacity: 1; }
    60%  { transform: translate(16px, -20px) scale(.7); opacity: 0; }
    61%  { transform: translate(-14px, 12px) scale(.7); opacity: 0; }
    100% { transform: translate(0, 0) scale(1); opacity: 1; }
}

.talk-hint {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 20px;
    margin: 11px 4px 0;
    font-size: 11px;
    color: var(--text-faint);
}

/* Ging etwas nicht durch, sagt es das Feld selbst – der Text steht
   dabei unverändert weiter da. */
.talk-field.is-bad textarea { border-color: var(--danger); box-shadow: 0 0 0 4px var(--danger-veil); }

/* Kommt etwas an, während oben gelesen wird, wartet es als Zettel. */
.talk-jump {
    position: absolute;
    left: 50%;
    bottom: 104px;
    z-index: 4;
    display: inline-flex;
    align-items: center;
    gap: 9px;
    height: 36px;
    padding: 0 17px;
    border: 0;
    border-radius: 99px;
    background: var(--blush);
    color: var(--blush-ink);
    font: inherit;
    font-size: 12px;
    letter-spacing: .02em;
    cursor: pointer;
    transform: translateX(-50%);
    box-shadow: 0 16px 30px -14px var(--blush);
    animation: jump-in .3s cubic-bezier(.2, .8, .2, 1);
}

.talk-jump i { font-size: 11px; }

@keyframes jump-in {
    from { opacity: 0; transform: translate(-50%, 10px); }
    to   { opacity: 1; transform: translateX(-50%); }
}

.talk-hint span { display: inline-flex; align-items: center; gap: 7px; }
.talk-hint i { font-size: 11px; }
.talk-hint-xp { color: var(--blush); opacity: .8; }

/* Auf dem Touchscreen gibt es kein Darüberfahren: das Herz steht
   leise da, statt sich zu verstecken. */
@media (hover: none) {
    .msg.is-yours .heart { opacity: .5; transform: none; }
    .talk-hint { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    .talk-state.is-here .talk-dot,
    .talk-jump,
    .msg.is-new,
    .talk-send.is-away i { animation: none; }
    .heart, .talk-send { transition: none; }
}

@media (max-width: 1000px) {
    .talk-sheet {
        --talk-around: 210px;
        padding: 16px 16px 14px;
        min-height: 420px;
    }

    .talk-head { padding-bottom: 13px; gap: 11px; }
    .talk-face { width: 36px; height: 36px; font-size: 12px; }
    .talk-tally { gap: 12px; font-size: 13px; }
    .msg-body { max-width: 84%; }
    .talk { padding: 14px 2px 4px; }
}

@media (max-width: 620px) {
    .talk-sheet { --talk-around: 240px; }
    .talk-tally span:first-child { display: none; }
    .bubble { font-size: 14.5px; }
}

/* ── Telegram ────────────────────────────────────────────────────
   Eine Bühne für eine einzige Nachricht. Sie steht in der Mitte, halb
   über dem Jahr, das blass dahinterliegt; nur das Gesicht des Absenders
   tritt zur Seite – nach links oder nach rechts, je nachdem, wer
   geschrieben hat.

   Die Blase selbst ist die des Chats – dieselben Klassen, dieselbe Form.
   Neu ist nur, dass sie wachsen darf: wie gross der Text steht, sagt
   seine Länge (.is-xl bis .is-sm), damit ein Zuruf von drei Zeichen
   dieselbe Fläche füllt wie ein Brief von vier Seiten.

   Drei Züge, zwei Klassen: `has-arrival` deckt alles ausser dem Tippen
   zu, `has-hold` hält danach noch das Gespräch zurück – erst die Sache,
   dann ihr Zusammenhang.
   ────────────────────────────────────────────────────────────── */

.tg { margin-top: 34px; }

.tg-stage { position: relative; padding: 8px 0; }

/* ── Der Knopf davor ─────────────────────────────────────────── */

.tg-call { padding: 46px 0 40px; text-align: center; }

.tg-call-note {
    max-width: 42ch;
    margin: 0 auto 30px;
    color: var(--text-faint);
    font-size: 14px;
    line-height: 1.7;
}

.tg-go {
    display: inline-flex;
    align-items: center;
    gap: 15px;
    height: 68px;
    padding: 0 38px 0 16px;
    border: 0;
    border-radius: 99px;
    background: var(--blush);
    color: var(--blush-ink);
    font: inherit;
    font-size: 17px;
    letter-spacing: .01em;
    cursor: pointer;
    box-shadow: 0 22px 44px -18px var(--blush);
    transform: perspective(700px)
               rotateX(var(--tilt-x, 0deg))
               rotateY(var(--tilt-y, 0deg));
    transition: transform .2s cubic-bezier(.2, .8, .2, 1),
                box-shadow .3s ease, filter .2s ease;
}

.tg-go:hover { filter: brightness(1.04); box-shadow: 0 28px 54px -18px var(--blush); }

/* Das Zeichen sitzt im eigenen Kreis – der Knopf ist gross genug, dass
   ein Icon daneben sonst verloren stünde. */
.tg-go-mark {
    display: grid;
    place-items: center;
    flex: none;
    width: 44px; height: 44px;
    border-radius: 50%;
    background: rgba(255, 255, 255, .34);
    font-size: 17px;
}

.tg-go:disabled,
.is-busy .tg-go { opacity: .6; cursor: default; }

/* Abgeholt: der Knopf fährt nach oben aus dem Bild, die Bühne nimmt
   seinen Platz ein. */
.tg-call.is-away {
    opacity: 0;
    transform: translateY(-26px);
    transition: opacity .34s ease, transform .38s cubic-bezier(.4, 0, 1, 1);
    pointer-events: none;
}

/* Und von unten kommt, was getippt wird. */
.tg-typing:not([hidden]) { animation: tg-rise .5s cubic-bezier(.2, .8, .2, 1); }

@keyframes tg-rise {
    from { opacity: 0; transform: translateY(28px); }
    to   { opacity: 1; transform: none; }
}

/* ── Solange getippt wird ────────────────────────────────────── */

/* Alles andere bleibt weg – auch das Datum und der Zeitstrahl: sie
   verrieten die Antwort, bevor die Frage gestellt ist. */
.tg.has-arrival .tg-flow,
.tg.has-arrival .tg-when,
.tg.has-arrival .tg-line,
.tg.has-arrival .tg-place,
.tg.has-arrival .rate,
.tg.has-arrival .tg-again { display: none; }

.tg-typing {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 18px;
    min-height: 260px;
}

/* Wer da schreibt, steht darüber – im Chat sagt es das Gesicht neben der
   Blase, hier steht die Blase in der Mitte und hat keine Seite. */
.tg-typing-who {
    display: inline-flex;
    align-items: center;
    gap: 11px;
    color: var(--text-faint);
    font-size: 13.5px;
    letter-spacing: .01em;
}

.tg-typing-who .msg-face { margin: 0; }

.tg-typing .bubble {
    padding: 27px 36px;
    border-radius: 28px;
    box-shadow: 0 20px 40px -28px rgba(96, 32, 60, .8);
}

.tg-typing .dots { gap: 9px; }
.tg-typing .dots i { width: 11px; height: 11px; }

/* ── Der Fluss ───────────────────────────────────────────────── */

.tg-flow {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 18px;
}

.tg-side {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    width: 100%;
}

/* ── Die Nachricht, in gross ─────────────────────────────────── */

/* Die Nachricht beginnt ein Stück weiter unten, damit das Jahr hinter
   ihr oben hervorschaut. Läge sie mittig darauf, verdeckte sie es ganz –
   und ein Hintergrund, den man nicht sieht, ist keiner. Die Mindesthöhe
   hält das Jahr im Kasten, auch wenn die Blase kurz ist. */
.tg-main {
    position: relative;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    width: 100%;
    min-height: clamp(200px, 24vw, 280px);
    padding: clamp(58px, 7.5vw, 104px) 0 18px;
}

.tg-year {
    position: absolute;
    top: 0; left: 50%;
    z-index: 0;
    font-family: 'Roboto Condensed', sans-serif;
    font-size: clamp(120px, 19vw, 220px);
    font-weight: 300;
    letter-spacing: -.04em;
    line-height: 1;
    color: var(--blush);
    opacity: .09;
    transform: translateX(-50%);
    pointer-events: none;
    user-select: none;
}

.tg-msg {
    position: relative;
    z-index: 1;
    align-items: flex-start;
    justify-content: center;
    width: 100%;
}

.tg-msg .msg-face { margin-top: 4px; margin-bottom: 0; }
.tg-msg .msg-body { max-width: min(80%, 640px); }

.tg-msg .bubble {
    padding: 20px 26px 18px;
    border-radius: 24px;
    line-height: 1.6;
}

.tg-msg.is-open.is-yours .bubble { border-top-left-radius: 8px; }
.tg-msg.is-open.is-mine  .bubble { border-top-right-radius: 8px; }

/* Die vier Stufen. Kurz heisst gross – ein einzelnes Wort soll die
   Bühne füllen und nicht darin verschwinden. */
.tg-msg.is-xl .bubble { font-size: clamp(30px, 5vw, 46px); line-height: 1.25; font-weight: 200; letter-spacing: -.02em; }
.tg-msg.is-lg .bubble { font-size: clamp(22px, 3.4vw, 30px); line-height: 1.38; font-weight: 250; letter-spacing: -.01em; }
.tg-msg.is-md .bubble { font-size: 19px; line-height: 1.55; }
.tg-msg.is-sm .bubble { font-size: 16px; line-height: 1.75; }

/* Ein langer Brief bekommt eine breitere Spalte – bei 4000 Zeichen ist
   die schmale Blase eine Säule. */
.tg-msg.is-sm .msg-body { max-width: min(92%, 720px); }

/* Bei den grossen Stufen sitzt die Uhrzeit sonst wie eine zweite
   Überschrift daneben. */
.tg-msg.is-xl .msg-time,
.tg-msg.is-lg .msg-time { margin-top: 14px; }

.tg-msg.is-new { animation: tg-land .6s cubic-bezier(.2, .8, .2, 1); }

@keyframes tg-land {
    0%   { opacity: 0; transform: translateY(16px) scale(.96); }
    60%  { opacity: 1; }
    100% { opacity: 1; transform: none; }
}

/* ── Das Gespräch darum herum ────────────────────────────────── */

/* Keine Blasen: was davor und danach stand, ist der Zusammenhang und
   nicht die Sache. Eine helle Karte, die auf dem Blatt liegt. */
.tg-ctx {
    position: relative;
    width: 100%;
    max-width: 540px;
    padding: 13px 62px 13px 20px;
    border-radius: 16px;
    background: rgba(255, 255, 255, .58);
    border: 1px solid rgba(255, 255, 255, .9);
    color: var(--text-dim);
    font-size: 13.5px;
    line-height: 1.65;
    overflow-wrap: anywhere;
    box-shadow: 0 14px 28px -20px rgba(96, 32, 60, .55);
    transition: opacity .55s ease, transform .55s cubic-bezier(.2, .8, .2, 1);
    transition-delay: calc(var(--i, 1) * .16s);
}

/* Zurückgehalten, bis die Nachricht steht. Beim Zudecken ohne Verzug –
   die Staffelung gehört dem Auftritt. */
.tg.has-hold .tg-ctx {
    opacity: 0;
    transform: translateY(9px);
    transition-delay: 0s;
}

.tg-ctx-who {
    display: block;
    margin-bottom: 2px;
    font-size: 10.5px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--text-faint);
}

.tg-ctx.is-mine .tg-ctx-who { color: var(--blush); }

.tg-ctx-time {
    position: absolute;
    top: 14px; right: 20px;
    font-family: 'Roboto Condensed', sans-serif;
    font-size: 10.5px;
    letter-spacing: .04em;
    color: var(--text-faint);
}

.tg-ctx-text strong { font-weight: 500; }
.tg-ctx-text em { font-style: italic; }

/* ── Wann das war ────────────────────────────────────────────── */

.tg-when { margin-top: 34px; text-align: center; }
.tg-when b { display: block; font-size: 17px; font-weight: 300; color: var(--text); }
.tg-when span { display: block; margin-top: 4px; color: var(--text-faint); font-size: 13px; }

/* Der Zeitstrahl: wo im Archiv sie liegt. Er sagt dasselbe wie die Zahl
   darunter, nur schneller. */
.tg-line {
    display: flex;
    align-items: center;
    gap: 14px;
    max-width: 460px;
    margin: 26px auto 0;
}

.tg-line-end {
    flex: none;
    font-family: 'Roboto Condensed', sans-serif;
    font-size: 11px;
    letter-spacing: .06em;
    color: var(--text-faint);
}

.tg-line-rail {
    position: relative;
    flex: 1 1 auto;
    height: 2px;
    border-radius: 2px;
    background: var(--blush-soft);
}

.tg-line-dot {
    position: absolute;
    top: 50%;
    width: 9px; height: 9px;
    margin-left: -4.5px;
    border-radius: 50%;
    background: var(--blush);
    transform: translateY(-50%);
    box-shadow: 0 0 0 5px var(--blush-veil);
}

.tg-place {
    margin-top: 14px;
    text-align: center;
    font-family: 'Roboto Condensed', sans-serif;
    font-size: 12px;
    letter-spacing: .04em;
    color: var(--text-faint);
}

.tg-again {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 9px;
    margin-top: 30px;
    color: var(--text-faint);
    font-size: 13px;
}

.tg-again i { font-size: 12px; }

@media (prefers-reduced-motion: reduce) {
    .tg-msg.is-new,
    .tg-typing:not([hidden]) { animation: none; }
    .tg-ctx, .tg-go, .tg-call.is-away { transition: none; }
}

@media (max-width: 1000px) {
    .tg-msg .msg-body,
    .tg-msg.is-sm .msg-body { max-width: 84%; }
    .tg-msg .bubble { padding: 17px 20px 15px; }
    .tg-typing { min-height: 200px; }
}

@media (max-width: 620px) {
    .tg-go { width: 100%; justify-content: center; padding-right: 22px; }
    .tg-year { font-size: 96px; }
    .tg-main { min-height: 170px; padding-top: 46px; }
    .tg-msg.is-xl .bubble { font-size: 28px; }
    .tg-msg.is-lg .bubble { font-size: 21px; }
    .tg-ctx { padding-right: 20px; }
    .tg-ctx-time { position: static; display: block; margin-top: 4px; }
}

/* ── Später ──────────────────────────────────────────────────────
   Antworten ist freiwillig. Der Knopf dazu steht unter der Antwort
   und nicht neben ihr: er ist die Ausnahme, nicht die zweite Wahl.
   ────────────────────────────────────────────────────────────── */

.ask-later {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 14px;
    margin-top: 26px;
}

.later-btn {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    height: 36px;
    padding: 0 16px;
    border: 1px solid transparent;
    border-radius: 99px;
    background: none;
    color: var(--text-faint);
    font: inherit;
    font-size: 12.5px;
    cursor: pointer;
    transition: border-color .25s ease, color .25s ease, background .25s ease;
}

.later-btn:hover {
    border-color: var(--line);
    background: #fff;
    color: var(--text-dim);
}

.later-btn i { font-size: 12px; }

.later-note { color: var(--text-faint); font-size: 11.5px; }

/* ── Geschenke ───────────────────────────────────────────────────
   Oben neben der Leiste, in derselben Höhe wie das Guthaben: es ist
   ja dasselbe Thema. Alle paar Sekunden ein kurzer Ruck – etwas, das
   dauernd zappelt, wird zur Tapete; ein Wink alle sechs Sekunden
   bleibt ein Wink.
   ────────────────────────────────────────────────────────────── */

.gifts { display: flex; align-items: stretch; gap: 12px; flex: none; }

/* Das Formular ist selbst ein Flex-Kind und gäbe seine Höhe sonst nicht
   weiter – der Knopf darin bliebe so hoch wie sein Inhalt, und die Reihe
   stünde schief. */
.gifts > form { display: flex; align-items: stretch; }

.gift {
    position: relative;
    display: grid;
    place-items: center;
    /* Gleich hoch wie Leiste und Guthaben, und daraus quadratisch: die
       Höhe gibt die Reihe vor, die Breite folgt ihr. */
    height: 100%;
    width: auto;
    aspect-ratio: 1;
    min-width: 60px;
    padding: 0;
    border-radius: var(--r-lg);
    border: 1px solid var(--glass-edge);
    background: var(--glass);
    backdrop-filter: blur(30px) saturate(155%);
    -webkit-backdrop-filter: blur(30px) saturate(155%);
    box-shadow: var(--lift);
    color: var(--blush);
    font-size: 24px;
    cursor: pointer;
    animation: gift-wink 6s ease-in-out infinite;
    transition: transform .3s cubic-bezier(.2, .8, .2, 1), box-shadow .3s ease;
}

.gift:hover {
    transform: translateY(-2px) scale(1.03);
    box-shadow: var(--lift), 0 0 34px -10px var(--blush);
    animation-play-state: paused;
}

.gift i { position: relative; z-index: 1; }

/* Der Schein darunter atmet mit dem Ruck mit. */
.gift-glow {
    position: absolute;
    inset: -18%;
    border-radius: 50%;
    background: radial-gradient(circle, var(--blush-veil) 0%, transparent 70%);
    animation: gift-breathe 6s ease-in-out infinite;
}

@keyframes gift-wink {
    0%, 62%, 100% { transform: rotate(0deg); }
    66%  { transform: rotate(-7deg) scale(1.04); }
    72%  { transform: rotate(6deg)  scale(1.04); }
    78%  { transform: rotate(-4deg) scale(1.02); }
    84%  { transform: rotate(3deg); }
    90%  { transform: rotate(0deg); }
}

@keyframes gift-breathe {
    0%, 55%, 100% { opacity: .35; transform: scale(.9); }
    70%           { opacity: 1;   transform: scale(1.1); }
}

/* ── Die Karte nach dem Auspacken ────────────────────────────────
   Dasselbe Glas wie beim Levelwechsel, aber sie bleibt stehen: ein
   Level erreicht man im Vorbeigehen, ein Geschenk will gelesen sein.
   ────────────────────────────────────────────────────────────── */

.giftbox {
    pointer-events: auto;
    background: rgba(60, 20, 40, .22);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
    animation: giftbox-veil .5s ease forwards;
}

.giftbox.is-out { animation: giftbox-gone .45s ease forwards; }

.giftbox-card {
    /* Breiter als die Karte beim Levelwechsel: dort stehen drei Zeilen,
       hier steht ein Text, und der will eine Zeilenlänge, die sich lesen
       lässt. Die andere Karte bleibt, wie sie ist – sie misst sich an
       ihrem Inhalt, und der ist kurz. */
    width: min(660px, 100%);
    max-width: min(660px, 100%);
    padding: 46px 64px 38px;
    opacity: 0;
    animation: giftbox-in .6s cubic-bezier(.2, .8, .2, 1) .05s forwards;
}

.giftbox-mark {
    display: grid;
    place-items: center;
    width: 96px; height: 96px;
    margin: 6px 0 22px;
    border-radius: 50%;
    background: var(--blush-veil);
    color: var(--blush);
    font-size: 40px;
    animation: giftbox-pop .9s cubic-bezier(.2, 1.5, .4, 1) .2s backwards;
}

.giftbox-word {
    margin-top: 18px;
    color: var(--text-dim);
    font-size: 14.5px;
    line-height: 1.7;
    text-wrap: pretty;
}

.giftbox-word p + p { margin-top: .8em; }

.giftbox-close {
    margin-top: 26px;
    padding: 11px 26px;
    border-radius: 99px;
    background: var(--blush);
    color: var(--blush-ink);
    font-size: 12px;
    letter-spacing: .12em;
    text-transform: uppercase;
    text-decoration: none;
    box-shadow: 0 16px 32px -16px var(--blush);
}

.giftbox-close:hover { filter: brightness(1.06); }

@keyframes giftbox-veil { from { opacity: 0 } to { opacity: 1 } }
@keyframes giftbox-gone { to { opacity: 0 } }
@keyframes giftbox-in {
    from { opacity: 0; transform: translateY(14px) scale(.96); }
    to   { opacity: 1; transform: none; }
}
@keyframes giftbox-pop {
    from { opacity: 0; transform: scale(.4) rotate(-12deg); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .gift, .gift-glow { animation: none; }
    .giftbox-card { opacity: 1; animation: none; }
    .giftbox-mark { animation: none; }
}

@media (max-width: 640px) {
    .gift { min-width: 52px; font-size: 20px; }
    .giftbox-card { padding: 36px 26px 30px; }
}
