/**
 * Die öffentliche Abruf-Seite: ein Blatt aus einem Einmalblock.
 *
 * Der One-Time-Pad ist der Vorfahr dieses Verfahrens — ein Schlüssel, einmal
 * benutzt, danach vernichtet. Deshalb liegt hier ein Blatt Papier auf einem
 * Tisch: perforierte Oberkante, Briefkopf, ein geschwärztes Feld, und am Ende
 * ein Stempel. Alles andere bleibt still; das Auffällige soll die Schwärzung
 * sein, die abgezogen wird, und der Stempel, der danach landet.
 *
 * Eigenständig gehalten, in derselben Logik wie die Fehlerseite 404: keine
 * Abhängigkeit zu einem Theme-Build, keine Webfonts, überhaupt nichts von
 * außerhalb. Wer einen Passwort-Link öffnet, soll eine funktionierende Seite
 * sehen — auch während am Frontend gebaut wird.
 */

/* ---------------------------------------------------------------- Farben --
 * Hell: Umweltpapier auf einem etwas dunkleren Tisch, schwarze Tinte,
 * Durchschlag-Violett für die Linien (die Farbe des Blaupausen- und
 * Kohlepapiers — der Farbe einer Kopie, die es genau einmal gibt) und
 * oxidiertes Stempelrot.
 *
 * Dunkel: kein abgedunkeltes Papier, sondern ein **Negativ** — Mikrofilm.
 * Auch das ist zeitgenau: One-Time-Pads wurden auf Mikrofilm verteilt. Ein
 * Blatt Papier bei Nacht wäre eine Ausrede, ein Negativ ist eine Entscheidung.
 */
:root {
    color-scheme: light dark;

    /* --- Papier ---------------------------------------------------------- */
    --tisch: #DEDCD2;
    --blatt: #F1EFE7;
    --tinte: #1A1915;
    --tinte-leise: #5F5C50;
    --durchschlag: #3F3A8F;
    --stempel: #A32E24;
    --raster: #CBC7B6;
    /* Eigenes Token, nicht --tinte: Im Negativ wäre eine schwarze Fläche eine
       weiße, und ein handtellergroßer weißer Block blendet. Die Schwärzung
       bleibt dort ein Grauschleier — sichtbar verdeckend, aber nicht laut. */
    --schwaerzung: #1B1A16;
    /* Ausgeschrieben statt color-mix(): Diese Seite soll auch in einem Browser
       tragen, der neuere Farbfunktionen nicht kennt. */
    --feld-grund: #E9E7DE;
    --blatt-schatten: 0 1px 2px rgba(26, 25, 21, .06), 0 8px 28px rgba(26, 25, 21, .07);

    /* --- Mikrofilm -------------------------------------------------------
     * Die Farbwerte des Negativs stehen hier EINMAL. Angewendet werden sie
     * unten an zwei Stellen: einmal über die Systemeinstellung, einmal über
     * den Schalter. Zwei Blöcke mit ausgeschriebenen Werten würden früher
     * oder später auseinanderlaufen — und der Unterschied fiele erst auf,
     * wenn jemand beide Wege durchprobiert. So kann er es nicht.
     *
     * Der Abstand zwischen Tisch und Blatt ist mit Absicht größer als man
     * denken würde: Im Negativ trägt kein Schatten die Kante, das muss die
     * Helligkeit allein machen — sonst verschwindet die Perforation. */
    --dunkel-tisch: #060708;
    --dunkel-blatt: #191C20;
    --dunkel-tinte: #E9E8E1;
    --dunkel-tinte-leise: #918F86;
    --dunkel-durchschlag: #8A84D6;
    --dunkel-stempel: #E4685C;
    --dunkel-raster: #2C2F33;
    --dunkel-schwaerzung: #9E9C94;
    --dunkel-feld-grund: #1C1F23;
}

/* Ohne ausdrückliche Wahl folgt die Seite dem System. */
@media (prefers-color-scheme: dark) {
    :root:not([data-farbe="hell"]) {
        color-scheme: dark;
        --tisch: var(--dunkel-tisch);
        --blatt: var(--dunkel-blatt);
        --tinte: var(--dunkel-tinte);
        --tinte-leise: var(--dunkel-tinte-leise);
        --durchschlag: var(--dunkel-durchschlag);
        --stempel: var(--dunkel-stempel);
        --raster: var(--dunkel-raster);
        --schwaerzung: var(--dunkel-schwaerzung);
        --feld-grund: var(--dunkel-feld-grund);
        --blatt-schatten: none;
    }
}

/* Mit ausdrücklicher Wahl gilt die Wahl — auch gegen die Systemeinstellung. */
:root[data-farbe="dunkel"] {
    color-scheme: dark;
    --tisch: var(--dunkel-tisch);
    --blatt: var(--dunkel-blatt);
    --tinte: var(--dunkel-tinte);
    --tinte-leise: var(--dunkel-tinte-leise);
    --durchschlag: var(--dunkel-durchschlag);
    --stempel: var(--dunkel-stempel);
    --raster: var(--dunkel-raster);
    --schwaerzung: var(--dunkel-schwaerzung);
    --feld-grund: var(--dunkel-feld-grund);
    --blatt-schatten: none;
}

:root[data-farbe="hell"] {
    color-scheme: light;
}

* {
    box-sizing: border-box;
}

/* Die Zustände werden über `data-zustand` am <main> geschaltet. Ohne diese
   Zeile könnte eine display-Regel weiter unten die Steuerung aushebeln. */
[hidden] {
    display: none !important;
}

body {
    margin: 0;
    min-height: 100vh;
    display: grid;
    place-items: center;
    padding: clamp(1rem, 4vw, 3rem);
    background: var(--tisch);
    color: var(--tinte);
    font: 400 1rem/1.6 system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
    -webkit-text-size-adjust: 100%;
}

/* ------------------------------------------------------------ Farbschalter -- */

/* Liegt auf dem Tisch, nicht auf dem Blatt: Die Darstellung ist eine Sache der
 * Betrachterin, kein Teil des Dokuments. Deshalb außerhalb des <main>, im
 * Quelltext dahinter — wer mit der Tastatur ankommt, soll zuerst an das
 * Passwort und den Kopieren-Knopf kommen, nicht an eine Einstellung.
 *
 * Das Zeichen ist eine halb gefüllte Scheibe: dieselbe Sache in Positiv und
 * Negativ, also genau das, was der Klick tut. Beim Umschalten dreht sie sich
 * um 180° — die Hälften tauschen die Plätze. Ohne JavaScript bleibt der
 * Schalter versteckt, weil er dann nichts könnte. */
.farbschalter {
    position: fixed;
    top: clamp(.75rem, 2vw, 1.5rem);
    right: clamp(.75rem, 2vw, 1.5rem);
    display: grid;
    place-items: center;
    width: 2.5rem;
    height: 2.5rem;
    padding: 0;
    border: 1px solid transparent;
    background: transparent;
    color: var(--tinte-leise);
    cursor: pointer;
    transition: color .15s, border-color .15s;
}

.farbschalter:hover {
    color: var(--tinte);
    border-color: var(--raster);
}

.farbschalter:focus-visible {
    outline: 2px solid var(--durchschlag);
    outline-offset: 2px;
}

.farbschalter svg {
    display: block;
}

:root[data-farbe="dunkel"] .farbschalter svg {
    rotate: 180deg;
}

/* --- Der Übergang ------------------------------------------------------
 *
 * Die neue Fassung wischt über die alte, in Leserichtung — dieselbe Bewegung
 * und dieselbe Beschleunigung, mit der sich die Schwärzung vom Feld zieht.
 * Die Seite hat damit EINE Geste, die sie an zwei Stellen benutzt, statt zwei
 * verschiedener.
 *
 * Bewusst kein Überblenden: Papier und Mikrofilm liegen an den beiden Enden
 * der Helligkeit, ihre Mitte ist ein trübes Grau, in dem für einen Moment
 * nichts mehr lesbar ist. Beim Wischen ist jedes Pixel entweder das eine oder
 * das andere.
 *
 * Und bewusst nicht über `transition` auf den Farbtokens: Dann müsste jede
 * Farbe einzeln durch ihre Zwischentöne, und der Browser hätte auf jedem
 * Element zu rechnen statt einmal auf zwei Standbildern.
 *
 * Ohne die View-Transitions-API passiert der Wechsel schlagartig. Das ist kein
 * Notbehelf, sondern die Voreinstellung, auf die alles andere aufsetzt.
 */
::view-transition-old(root),
::view-transition-new(root) {
    /* Die Voreinstellung des Browsers rechnet die beiden Standbilder additiv
       zusammen, damit ein Kreuzblenden sauber aussieht. Hier liegen sie
       hart übereinander — additiv wäre falsch. */
    mix-blend-mode: normal;
}

/* Die alte Fassung liegt still da und wird überdeckt. */
::view-transition-old(root) {
    animation: none;
}

::view-transition-new(root) {
    animation: farbe-wischen .42s cubic-bezier(.65, .01, .28, 1);
}

/* Beide Enden ausgeschrieben. Ohne das `to` liefe der Weg gegen `clip-path:
   none`, und zwischen einem inset() und `none` kann der Browser nicht
   rechnen — er springt dann auf halber Strecke um, statt zu wischen. */
@keyframes farbe-wischen {
    from {
        clip-path: inset(0 100% 0 0);
    }

    to {
        clip-path: inset(0 0 0 0);
    }
}

/* Der Schalter bekommt einen eigenen Namen und damit einen eigenen Übergang:
   Er soll sich drehen und nicht mit der Seite verblassen — ein Knopf muss den
   Druck quittieren, sonst wirkt er tot. */
.farbschalter {
    view-transition-name: farbschalter;
}

::view-transition-old(farbschalter) {
    animation: schalter-ab .38s cubic-bezier(.65, .01, .28, 1) both;
}

::view-transition-new(farbschalter) {
    animation: schalter-an .38s cubic-bezier(.65, .01, .28, 1) both;
}

@keyframes schalter-ab {
    to {
        rotate: 90deg;
        opacity: 0;
    }
}

@keyframes schalter-an {
    from {
        rotate: -90deg;
        opacity: 0;
    }
}

/* ----------------------------------------------------------------- Blatt -- */

.blatt {
    position: relative;
    width: 100%;
    max-width: 33rem;
    padding: clamp(2.25rem, 1.5rem + 3vw, 3.25rem) clamp(1.5rem, 1rem + 3vw, 3rem) clamp(1.75rem, 1.25rem + 2vw, 2.5rem);
    background: var(--blatt);
    border: 1px solid var(--raster);
    box-shadow: var(--blatt-schatten);
}

/* Die Perforation der Oberkante: aus dem Blatt gestanzte Halbkreise in der
   Farbe des Tisches. Der einzige Zierrat auf der Seite, und er hat einen
   Job — er sagt in einem Blick, dass dieses Blatt von einem Block abgerissen
   wurde und es kein zweites davon gibt. */
.blatt::before {
    content: "";
    position: absolute;
    inset: -1px -1px auto -1px;
    height: 10px;
    background:
        radial-gradient(circle at 7px 0, var(--tisch) 5.5px, transparent 6px) 0 0 / 14px 10px repeat-x;
}

/* --------------------------------------------------------------- Kopfzeile -- */

/* Der Briefkopf. Dicktengleich, weil es keine Prosa ist, sondern eine Angabe:
   Von hier kommt der Link. Das ist das Einzige, was die Seite über ihre
   Herkunft weiß — und das Einzige, wonach jemand vor dem Klick fragen würde.
 *
 * Durchschlag-Violett, und das ist eine Regel und keine Laune: Auf einem
 * Formular ist alles VORGEDRUCKTE in der Farbe des Kohlepapiers, alles
 * EINGETRAGENE in schwarzer Tinte. Hier hält sich die ganze Seite daran —
 * Briefkopf, Kopflinie und der Rahmen des Feldes sind violett, der Inhalt
 * schwarz. */
.herkunft {
    margin: 0 0 clamp(1.75rem, 1rem + 3vw, 2.5rem);
    padding-bottom: .8125rem;
    border-bottom: 1px solid var(--durchschlag);
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
    font-size: .6875rem;
    letter-spacing: .2em;
    text-transform: uppercase;
    color: var(--durchschlag);
}

/* Bewusst kleiner gehalten, als eine Überschrift sein dürfte: Der größte Text
   auf diesem Blatt soll das Passwort sein, nicht die Ansage darüber. */
.titel {
    margin: 0 0 .625rem;
    font-size: clamp(1.4375rem, 1.15rem + 1.35vw, 1.875rem);
    font-weight: 600;
    line-height: 1.15;
    letter-spacing: -.018em;
    text-wrap: balance;
}

.vorspann {
    margin: 0 0 clamp(1.75rem, 1.25rem + 2vw, 2.25rem);
    max-width: 26rem;
    font-size: .9375rem;
    line-height: 1.65;
    color: var(--tinte-leise);
}

/* ------------------------------------------------------------------ Feld -- */

/* Das Feld ist von Anfang an da und von Anfang an geschwärzt. Die Mindesthöhe
   trägt es, bevor Text darin steht — die Seite weiß vor dem Abruf nicht, wie
   lang der Text ist, und die Schwärzung soll darüber nichts behaupten. */
.feld {
    position: relative;
    min-height: 4.75rem;
    display: flex;
    align-items: center;
    padding: 1.125rem 1.25rem;
    /* Vorgedruckt, also violett (siehe .herkunft). */
    border: 1px solid var(--durchschlag);
    background: var(--feld-grund);
}

/* Der größte Text der Seite. Ein Passwort wird Zeichen für Zeichen gelesen,
   nicht als Wort — deshalb dicktengleich und mit etwas Sperrung, damit
   0 und O, 1 und l auseinanderzuhalten sind. */
.feld-text {
    margin: 0;
    width: 100%;
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
    font-size: clamp(1.125rem, 1rem + .55vw, 1.3125rem);
    letter-spacing: .02em;
    line-height: 1.6;
    /* Ein umgebrochenes Passwort ist lesbar, ein abgeschnittenes nicht. */
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    /* Ein Klick markiert alles — für alle, die nicht den Knopf nehmen. */
    user-select: all;
}

.feld-text:focus-visible {
    outline: 2px solid var(--durchschlag);
    outline-offset: 4px;
}

/* Die Schwärzung. Sie liegt über dem Feld und zieht sich beim Abruf nach
   rechts weg — in Leserichtung, so wie man einen Streifen abzieht. Das ist
   der eine Moment, den diese Seite hat; alles andere hält still. */
.feld-schwaerzung {
    position: absolute;
    inset: 0;
    /* Feine Schrägschraffur wie im Innenfutter eines Sicherheitsumschlags.
       Ohne sie liest sich die Fläche im Negativ wie ein gesperrtes Eingabefeld;
       mit ihr wie etwas, das absichtlich zugedeckt ist. */
    background:
        repeating-linear-gradient(
            -45deg,
            rgba(255, 255, 255, .075) 0 1px,
            transparent 1px 7px
        ),
        var(--schwaerzung);
    transform-origin: right center;
    transition: transform .42s cubic-bezier(.65, .01, .28, 1);
}

[data-zustand="offen"] .feld-schwaerzung,
[data-zustand="offen"] .feld-rubbel {
    transform: scaleX(0);
}

/* --- Freirubbeln --------------------------------------------------------
 *
 * Zugabe, nicht Voraussetzung: Das JavaScript legt eine Leinwand über das
 * Feld und blendet die gemalte Schwärzung darunter aus. Ziehen rubbelt frei,
 * Tippen deckt auf wie der Knopf — und der Knopf bleibt ohnehin stehen. Wo
 * es kein Canvas gibt, passiert nichts von alledem und die Seite verhält
 * sich wie vorher.
 *
 * Ein Rubbellos ist übrigens genau das Richtige für diese Seite: Man kann es
 * nicht zurückrubbeln. */
.feld--rubbelbar .feld-schwaerzung {
    display: none;
}

.feld-rubbel {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    /* Ohne das scrollt die Seite mit, statt dass gerubbelt wird. */
    touch-action: none;
    /* Und ohne das zieht eine echte Maus eine Textmarkierung über das Feld,
       statt zu rubbeln. */
    user-select: none;
    -webkit-user-select: none;
    cursor: grab;
    transform-origin: right center;
    transition: transform .42s cubic-bezier(.65, .01, .28, 1);
}

.feld-rubbel:active {
    cursor: grabbing;
}

/* Angefasst wird nur, solange auch etwas zu holen ist.
 *
 * `[data-zustand]` davor ist nicht überflüssig, sondern der ganze Punkt:
 * `:not([data-zustand="bereit"]) .feld-rubbel` würde auf JEDEN Vorfahren
 * passen, der das Attribut nicht auf "bereit" stehen hat — und `body` und
 * `html` haben es überhaupt nicht. Die Regel hätte damit immer gegolten und
 * die Fläche wäre für eine echte Maus nie erreichbar gewesen. Mit
 * `[data-zustand]` bleibt nur das <main> übrig, das den Zustand wirklich
 * trägt. */
[data-zustand]:not([data-zustand="bereit"]) .feld-rubbel {
    pointer-events: none;
    cursor: default;
}

/* -------------------------------------------------------------- Aktionen -- */

.aktionen {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-top: 1.25rem;
}

/* Umrissen, nicht gefüllt. Die einzige geschlossene Fläche auf dem Blatt soll
   die Schwärzung sein — ein zweiter schwarzer Block daneben nähme ihr genau
   die Bedeutung, auf die es hier ankommt. */
.knopf {
    padding: .6875rem 1.375rem;
    border: 1px solid var(--tinte);
    background: transparent;
    color: var(--tinte);
    font: 500 .875rem/1 inherit;
    font-family: inherit;
    letter-spacing: .005em;
    cursor: pointer;
    transition: background .15s, color .15s;
}

.knopf:hover {
    background: var(--tinte);
    color: var(--blatt);
}

.knopf:focus-visible {
    outline: 2px solid var(--durchschlag);
    outline-offset: 3px;
}

.knopf[disabled] {
    opacity: .45;
    cursor: default;
}

/* Dicktengleich: Es ist eine Maschinenmeldung, kein Satz. */
.laufband {
    margin: 0;
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
    font-size: .8125rem;
    letter-spacing: .08em;
    color: var(--tinte-leise);
}

/* ------------------------------------------------------- Hinweise, Fehler -- */

.hinweis {
    margin: 1rem 0 0;
    max-width: 27rem;
    font-size: .8125rem;
    line-height: 1.6;
    color: var(--tinte-leise);
}

.meldung {
    margin: 0;
    padding-left: 1.0625rem;
    /* Statt eines Kastens: ein Randstrich, wie eine Anstreichung am Blattrand. */
    border-left: 3px solid var(--stempel);
    max-width: 28rem;
    font-size: .9375rem;
    line-height: 1.6;
}

noscript .meldung {
    margin-bottom: 1.75rem;
}

/* -------------------------------------------------------------- Stempel -- */

/* Was hier steht, ist wahr und nicht mehr: Ein Abruf wurde verbraucht, um
   diese Uhrzeit. Ob es der letzte war, sagt der Server bewusst nicht — also
   behauptet der Stempel es auch nicht. */
.stempel {
    margin: clamp(1.75rem, 1.25rem + 2vw, 2.5rem) 0 0;
    align-self: flex-end;
    display: flex;
    flex-direction: column;
    gap: .125rem;
    width: fit-content;
    margin-left: auto;
    padding: .625rem 1.125rem .5625rem;
    border: 2px solid var(--stempel);
    color: var(--stempel);
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
    text-align: center;
    rotate: -3deg;
    opacity: .92;
    animation: stempel-aufsetzen .28s cubic-bezier(.2, .9, .3, 1.1) .34s backwards;
}

.stempel-wort {
    font-size: .8125rem;
    font-weight: 600;
    letter-spacing: .22em;
    text-transform: uppercase;
    /* Der Buchstabenabstand schiebt das Wort optisch nach links; das holt es
       zurück in die Mitte des Rahmens. */
    text-indent: .22em;
}

.stempel-zeit {
    font-size: .6875rem;
    letter-spacing: .12em;
    opacity: .8;
    text-indent: .12em;
}

@keyframes stempel-aufsetzen {
    from {
        opacity: 0;
        scale: 1.14;
        rotate: -7deg;
    }
}

/* ------------------------------------------------------------- Zustände -- */

/* Alles Zustandsabhängige ist zunächst aus; `data-zustand` schaltet an, was
   gerade gilt. Ohne JavaScript bleibt `start` stehen — dann ist außer dem
   <noscript>-Hinweis nichts zu sehen, und das ist ehrlich. */
.feld,
.knopf,
.laufband,
.hinweis,
#pwpush-error,
.stempel {
    display: none;
}

[data-zustand="bereit"] .feld,
[data-zustand="laedt"] .feld,
[data-zustand="offen"] .feld {
    display: flex;
}

[data-zustand="bereit"] #pwpush-reveal,
[data-zustand="offen"] #pwpush-copy {
    display: inline-block;
}

[data-zustand="laedt"] .laufband {
    display: block;
}

[data-zustand="bereit"] [data-rolle="hinweis-bereit"],
[data-zustand="offen"] [data-rolle="hinweis-offen"] {
    display: block;
}

[data-zustand="fehler"] #pwpush-error,
[data-zustand="kein-link"] #pwpush-error {
    display: block;
}

/* Wo nichts zu holen ist, hat „Kopier dir den Text“ nichts verloren. Die
   Meldung übernimmt dann allein. */
[data-zustand="fehler"] .vorspann,
[data-zustand="kein-link"] .vorspann {
    display: none;
}

[data-zustand="offen"] .stempel,
[data-zustand="fehler"] .stempel {
    display: flex;
}

/* ---------------------------------------------------------------- Kleines -- */

@media (max-width: 26rem) {
    .aktionen {
        flex-direction: column;
        align-items: stretch;
    }

    .laufband {
        text-align: center;
    }

    .stempel {
        margin-left: 0;
    }
}

/* Wer weniger Bewegung eingestellt hat, bekommt dieselben Zustände ohne den
   Weg dorthin. Die Schwärzung verschwindet, der Stempel liegt da. */
@media (prefers-reduced-motion: reduce) {
    .feld-schwaerzung,
    .feld-rubbel {
        transition: opacity .01s;
    }

    [data-zustand="offen"] .feld-schwaerzung,
    [data-zustand="offen"] .feld-rubbel {
        transform: none;
        opacity: 0;
    }

    .stempel {
        animation: none;
    }

    .knopf {
        transition: none;
    }

    /* Das JavaScript umgeht die Überblendung hier ohnehin. Diese Zeilen sind
       der zweite Riegel, falls doch einmal eine ausgelöst wird. */
    ::view-transition-old(root),
    ::view-transition-new(root),
    ::view-transition-old(farbschalter),
    ::view-transition-new(farbschalter) {
        animation: none;
    }
}
