/* ===========================================================================
   CodeWords – Design-System

   Mobile first und app-artig: Die Seite selbst scrollt NIE. Die Hülle ist ein
   100dvh-Raster aus Kopf / Inhalt / Tab-Leiste; gescrollt wird ausschließlich
   innerhalb der Inhaltsfläche. Deshalb steht hier overflow:hidden am <body> –
   das ist Absicht, kein Versehen.

   ---------------------------------------------------------------------------
   FARBEN

   Marke:  #701743 (Beere) · #fed90e (Gelb) · #da5148 (Koralle)

   Alle Paarungen sind gegen WCAG gerechnet; die Werte stehen als Kommentar an
   den Tokens. Drei Fallen, die dabei aufgefallen sind:

     - #da5148 erreicht auf dem dunklen Grund nur 4,1:1 und auf der Markenfläche
       #701743 sogar nur 2,8:1. Als TEXT ist die Farbe damit unbrauchbar. Sie
       trägt hier ausschließlich Flächen, Ränder und Symbole; für Fehlertexte
       gibt es die aufgehellte Variante --neg-text (8,9:1).
     - #701743 ist als ganzflächiger Hintergrund zu satt – es bliebe kein Raum
       für Abstufungen darüber. Der Grund ist deshalb ein sehr dunkles
       Beerenschwarz, und #701743 ist die gehobene Markenfläche darauf.
     - #fed90e trägt NIE weiße oder helle Schrift. Auf Gelb steht immer
       --accent-ink (12,7:1).

   ---------------------------------------------------------------------------
   GELB IST RESERVIERT

   Im Gitter bedeutet Gelb „gesuchtes Wort". Außerhalb des Gitters trägt es nur
   der EINE primäre Knopf eines Bildschirms. Wo eine Farbe beliebig verteilt
   wird, zeigt sie nirgends mehr hin – und hier zeigt sie auf das, worum das
   ganze Spiel geht.

   ---------------------------------------------------------------------------
   KEIN KARTENZUSTAND HÄNGT ALLEIN AN DER FARBE

   Jeder Zustand trägt zusätzlich ein Zeichen: gesucht einen gefüllten Punkt,
   verboten ein durchgestrichenes Feld, verbraucht eine Durchstreichung und
   halbe Deckkraft. Wer Farben nicht unterscheidet, verlöre hier sonst das
   Spiel – und zwar ohne zu merken, warum.
   =========================================================================== */

/* Die Schriften werden in index.html per <link> geladen, nicht hier per
   @import: Ein @import wird erst entdeckt, wenn dieses Stylesheet geparst ist,
   und reiht die Schriftdatei damit dahinter ein. Als <link> im Kopf findet der
   Browser sie sofort und lädt beides nebeneinander. */

:root {
    /* --- Maße und Rhythmus (4/8-Raster) --- */
    --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
    --sp-5: 20px; --sp-6: 24px; --sp-8: 32px; --sp-12: 48px;

    --radius-sm: 10px;
    --radius:    16px;
    --radius-lg: 22px;
    --radius-xl: 28px;

    --tap: 44px;                       /* Mindestgröße für Tippziele */
    --tabbar-h: 64px;
    --topbar-h: 56px;
    --sidebar-w: 248px;

    --font:     'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
    --font-card:'Fredoka', 'Inter', system-ui, sans-serif;
    --font-num: 'JetBrains Mono', ui-monospace, 'SFMono-Regular', Consolas, monospace;

    /* Bewegung: 150–300 ms mit plattformnaher Kurve. */
    --ease:      cubic-bezier(.2, .8, .3, 1);
    --ease-back: cubic-bezier(.34, 1.4, .64, 1);
    --dur-fast: 140ms;
    --dur:      220ms;
    --dur-slow: 380ms;

    --z-sheet: 100;
    --z-toast: 200;
}

/* --- Dunkel (Standard) --- */
:root, :root[data-theme="dark"] {
    color-scheme: dark;

    --bg:        #1b0812;   /* Beerenschwarz */
    --bg-2:      #120509;   /* tiefer als bg: Vertiefungen, Eingabefelder */
    --surface:   #2e0c1c;   /* Karten, Listenzeilen */
    --surface-2: #3d1126;   /* gehobene Fläche, gedrückter Zustand */
    --brand:     #701743;   /* Markenfläche – nie als Text */
    --border:    #4a1e30;   /* NUR Ränder – als Text nur 2,4:1 */
    --border-2:  #63283f;   /* betonter Rand, aktive Karte */
    --text:      #f7eef3;   /* 16,7:1 auf bg */
    --muted:     #c39fb2;   /*  8,2:1 auf bg */

    --accent:      #fed90e; /* 13,9:1 auf bg · 8,1:1 auf brand */
    --accent-ink:  #2b1119; /* Schrift AUF Gelb – 12,7:1 */
    --accent-soft: rgba(254, 217, 14, .13);
    --accent-line: rgba(254, 217, 14, .32);

    --pos:      #7fd67f;    /* 10,1:1 – gewonnen, Treffer */
    --neg:      #da5148;    /* NUR Fläche/Rand – als Text nur 4,1:1 */
    --neg-text: #f5978e;    /*  8,9:1 – dafür Fehlertexte */
    --pos-soft: rgba(127, 214, 127, .13);
    --neg-soft: rgba(218, 81, 72, .16);

    --scrim:  rgba(10, 3, 6, .70);
    --shadow:    0 8px 24px -10px rgba(0, 0, 0, .75);
    --shadow-lg: 0 24px 56px -20px rgba(0, 0, 0, .85);
    --ring: rgba(254, 217, 14, .48);

    /* --- Die Kartenfarben ---
       Sie sind in BEIDEN Themes gleich. Eine Karte bedeutet dasselbe, egal wie
       hell der Bildschirm ist – und ein Gelb, das im hellen Theme ein anderes
       Gelb wäre, hieße: Der Spieler muss zweimal lernen, was er sieht. */
    --card-neutral: #e8dcc8; /* warmes Creme; Schrift --card-ink = 13,0:1 */
    --card-target:  #fed90e; /* Schrift --card-ink = 12,7:1 */
    --card-forbid:  #120609; /* Schrift --card-neutral = 14,9:1 */
    --card-spent:   #4a1e30; /* aufgedeckt-neutral; Schrift --muted = 5,9:1 */
    --card-ink:     #2b1119; /* Schrift auf Creme und Gelb */
}

/* --- Hell --- */
:root[data-theme="light"] {
    color-scheme: light;

    --bg:        #fdf7fa;
    --bg-2:      #f3e6ed;
    --surface:   #ffffff;
    --surface-2: #f8edf2;
    --brand:     #701743;
    --border:    #e3ccd8;   /* NUR Ränder – als Text 1,5:1 */
    --border-2:  #c9a3b6;
    --text:      #2b1119;   /* 15,1:1 auf bg */
    --muted:     #7a5164;   /*  6,4:1 auf bg */

    /* Gelb auf Weiß ist unlesbar (1,4:1). Im hellen Theme trägt der Akzent
       deshalb NIE Text – nur Flächen mit dunkler Schrift darauf. Wo eine
       Akzentfarbe als Text gebraucht wird, steht die Marke: #701743 erreicht
       auf Weiß 11,4:1. */
    --accent:      #fed90e;
    --accent-ink:  #2b1119;
    --accent-text: #701743; /* 11,4:1 auf bg – Akzent ALS TEXT */
    --accent-soft: rgba(254, 217, 14, .30);
    --accent-line: rgba(112, 23, 67, .28);

    --pos:      #1f7a3d;    /* 5,1:1 auf bg */
    --neg:      #da5148;
    --neg-text: #b3231a;    /* 5,6:1 auf bg */
    --pos-soft: rgba(31, 122, 61, .12);
    --neg-soft: rgba(218, 81, 72, .14);

    --scrim:  rgba(43, 17, 25, .45);
    --shadow:    0 8px 24px -12px rgba(112, 23, 67, .28);
    --shadow-lg: 0 24px 56px -22px rgba(112, 23, 67, .34);
    --ring: rgba(112, 23, 67, .40);

    --card-neutral: #e8dcc8;
    --card-target:  #fed90e;
    --card-forbid:  #120609;
    --card-spent:   #d8c6ce;
    --card-ink:     #2b1119;
}

/* Im dunklen Theme ist der Akzent auch als Text brauchbar; die Zeile hier
   erspart jeder Regel eine Fallunterscheidung. */
:root, :root[data-theme="dark"] { --accent-text: var(--accent); }

/* ===========================================================================
   Grundlagen
   =========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    height: 100%;
}

body {
    /* 16px ist die Untergrenze: Darunter zoomt iOS beim Fokus in ein Feld. */
    font: 400 16px/1.5 var(--font);
    color: var(--text);
    background: var(--bg);

    height: 100dvh;
    overflow: hidden;
    overscroll-behavior: none;
    -webkit-font-smoothing: antialiased;
    -webkit-tap-highlight-color: transparent;
}

h1, h2, h3 {
    font-family: var(--font-card);
    font-weight: 600;
    line-height: 1.2;
    margin: 0;
}
h1 { font-size: 1.5rem; }
h2 { font-size: 1.2rem; }
h3 { font-size: 1.05rem; }

p { margin: 0 0 var(--sp-3); }

a { color: var(--accent-text); }

/* Das hidden-Attribut muss jede Anzeigeregel schlagen. Ohne diese Zeile
   gewinnt jedes spätere display:flex – und heraus kommt ein Element, das laut
   Markup versteckt ist und trotzdem dasteht. */
[hidden] { display: none !important; }

.num {
    font-family: var(--font-num);
    font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------------------
   SYMBOLE

   Die Sammlung ist auf Konturen gezeichnet: fill:none, stroke:currentColor.
   Beides steht zwar am Wurzelelement von sprite.svg, aber ein <use> erbt NUR
   die Attribute des <symbol> – nicht die des umgebenden <svg> der Quelldatei.
   Ohne diese Regel erscheint jedes Symbol als schwarze Fläche.

   Sie gilt deshalb für JEDES svg der App, nicht nur für .icon: In index.html
   stehen die Symbole der Tab-Leiste ohne Klasse, und genau dort fiel es auf.
   Ausgenommen ist allein die Wortmarke, die ihre Flächen ausdrücklich füllt.
   --------------------------------------------------------------------------- */
svg:not(.wordmark) {
    fill: none;
    stroke: currentColor;
    stroke-width: 1.75;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.icon    { width: 22px; height: 22px; flex: none; }
.icon-sm { width: 18px; height: 18px; }
.icon-lg { width: 28px; height: 28px; }

.wordmark { width: 180px; height: auto; margin-bottom: var(--sp-4); }

:focus-visible {
    outline: 2px solid var(--accent-text);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

/* ===========================================================================
   Hülle: Kopf / Inhalt / Tab-Leiste
   =========================================================================== */

#app {
    display: grid;
    grid-template-rows: auto 1fr auto;
    grid-template-areas: "top" "main" "nav";
    height: 100dvh;
}

.topbar {
    grid-area: top;
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: var(--topbar-h);
    padding: var(--sp-2) var(--sp-4);
    padding-top: max(var(--sp-2), env(safe-area-inset-top));
    background: var(--bg);
    border-bottom: 1px solid var(--border);
}

.topbar h1 { flex: 1; font-size: 1.15rem; }

.topbar-actions { display: flex; gap: var(--sp-1); align-items: center; }

main, .content {
    grid-area: main;
    overflow-y: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    padding: var(--sp-4);
    padding-bottom: var(--sp-8);
}

.tabbar {
    grid-area: nav;
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    background: var(--bg);
    border-top: 1px solid var(--border);
    padding-bottom: env(safe-area-inset-bottom);
}

.tabbar a {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: var(--tabbar-h);
    color: var(--muted);
    text-decoration: none;
    font-size: .72rem;
    position: relative;
    transition: color var(--dur-fast) var(--ease);
}

.tabbar a[aria-current="page"] { color: var(--accent-text); }

.tabbar a svg { width: 22px; height: 22px; }

/* Das Abzeichen an der Glocke. */
.badge {
    position: absolute;
    top: 8px;
    left: 55%;
    min-width: 17px;
    height: 17px;
    padding: 0 4px;
    border-radius: 9px;
    background: var(--neg);
    color: #fff;               /* 4,0:1 – nur zwei Ziffern, groß genug */
    font: 700 11px/17px var(--font-num);
    text-align: center;
}

/* Ab 900px wird aus der Tab-Leiste eine Seitenleiste – gleiches Markup. */
@media (min-width: 900px) {
    #app {
        grid-template-columns: var(--sidebar-w) 1fr;
        grid-template-rows: auto 1fr;
        grid-template-areas: "nav top" "nav main";
    }
    .tabbar {
        grid-auto-flow: row;
        grid-auto-columns: none;
        align-content: start;
        gap: var(--sp-1);
        padding: var(--sp-4) var(--sp-3);
        border-top: 0;
        border-right: 1px solid var(--border);
    }
    .tabbar a {
        flex-direction: row;
        justify-content: flex-start;
        gap: var(--sp-3);
        min-height: var(--tap);
        padding: 0 var(--sp-3);
        border-radius: var(--radius-sm);
        font-size: .95rem;
    }
    .tabbar a[aria-current="page"] { background: var(--surface); }
    .badge { position: static; margin-left: auto; }
    main { padding: var(--sp-6); }
    .inhalt { max-width: 760px; margin-inline: auto; }
}

/* ===========================================================================
   Bausteine
   =========================================================================== */

.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: var(--sp-4);
    margin-bottom: var(--sp-3);
}

.card-link {
    display: block;
    color: inherit;
    text-decoration: none;
    transition: border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.card-link:active { transform: scale(.99); }
.card-link.dran { border-color: var(--accent-line); background: var(--surface-2); }

.zeile { display: flex; align-items: center; gap: var(--sp-3); }
.zeile-fuell { flex: 1; min-width: 0; }

.kopfzeile {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--sp-3);
    margin-bottom: var(--sp-2);
}

.klein { font-size: .85rem; color: var(--muted); }
.mini  { font-size: .75rem; color: var(--muted); }

.avatar {
    width: 36px; height: 36px;
    border-radius: 50%;
    object-fit: cover;
    background: var(--surface-2);
    flex: none;
}
.avatar-fallback {
    display: grid; place-items: center;
    font-family: var(--font-card);
    font-weight: 600;
    color: var(--muted);
}

/* --- Knöpfe --- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    min-height: var(--tap);
    padding: 0 var(--sp-4);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text);
    font: 600 .95rem var(--font);
    cursor: pointer;
    transition: transform var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.btn:active { transform: scale(.97); }
.btn:disabled { opacity: .45; cursor: not-allowed; transform: none; }

/* Der eine gelbe Knopf je Bildschirm – siehe Kopfkommentar. */
.btn-primary {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-ink);
}

.btn-quiet { background: transparent; border-color: transparent; color: var(--muted); }

.btn-danger { border-color: var(--neg); color: var(--neg-text); }

.btn-icon {
    min-width: var(--tap);
    padding: 0;
    background: transparent;
    border-color: transparent;
    color: var(--text);
}
.btn-icon svg { width: 22px; height: 22px; }

.btn-block { width: 100%; }

/* --- Eingaben --- */

.feld { display: block; margin-bottom: var(--sp-3); }
.feld > span { display: block; font-size: .85rem; color: var(--muted); margin-bottom: var(--sp-1); }

input[type="text"], input[type="number"], input[type="search"], select, textarea {
    width: 100%;
    min-height: var(--tap);
    padding: 0 var(--sp-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--bg-2);
    color: var(--text);
    font: 400 16px var(--font);
}
textarea { padding: var(--sp-3); min-height: 120px; resize: vertical; line-height: 1.5; }

/* Kästchen in der Markenfarbe statt im Systemblau. Sie kommen nur in der
   Verwaltung vor, fielen dort aber als einziges Blau auf. Die Farbe des
   Häkchens wählt der Browser selbst nach Kontrast – auf Gelb wird sie dunkel. */
input[type="checkbox"], input[type="radio"] {
    accent-color: var(--accent);
    width: 20px;
    height: 20px;
}

input:focus-visible, select:focus-visible, textarea:focus-visible {
    outline: none;
    border-color: var(--accent-text);
    box-shadow: 0 0 0 3px var(--ring);
}

.feld-fehler { color: var(--neg-text); font-size: .85rem; margin-top: var(--sp-1); }

/* --- Zustände ---

   Die Klassen heißen .empty und .loading, nicht .leer und .laden: So erzeugt
   sie util.js, und die Datei bleibt zwischen den Projekten wortgleich. Sie
   stehen weiter unten bei den übernommenen Bausteinen.

   Hier stand einmal zusätzlich ein deutsches Paar .leer/.laden/.fehler. Es
   wurde von niemandem erzeugt – aber .leer kollidierte mit .tag.leer im
   Archivkalender und gab jedem freien Tag 48 px Innenabstand. Die Zeilen des
   Kalenders waren dadurch zweieinhalbmal zu hoch, ohne dass irgendwo ein
   Fehler auftauchte. */

.chip {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-1);
    padding: 2px var(--sp-2);
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--muted);
    font-size: .75rem;
    font-weight: 600;
}
.chip-dran   { background: var(--accent-soft); color: var(--accent-text); }
.chip-won    { background: var(--pos-soft);    color: var(--pos); }
.chip-lost   { background: var(--neg-soft);    color: var(--neg-text); }

/* ===========================================================================
   DAS GITTER – der Kern der App
   =========================================================================== */

.grid-kopf {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    margin-bottom: var(--sp-3);
    flex-wrap: wrap;
}

/* Der aktuelle Hinweis. Groß, weil er das Einzige ist, worauf der Codeknacker
   schaut, bevor er das Gitter liest.

   ZWEI ZEILEN, nicht eine: Wort und Zahl oben, der Tippzähler darunter. In
   einer Reihe blieben dem Wort auf 375 px nach Abzug von Zahl und Zähler rund
   150 px – „PROBEHINWEIS" brach dort mitten im Wort um. Das Hinweiswort ist
   die wichtigste Angabe des Bildschirms und bekommt deshalb die volle Breite. */
.hinweis {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: var(--sp-2) var(--sp-3);
    width: 100%;
    padding: var(--sp-3) var(--sp-4);
    border-radius: var(--radius);
    background: var(--surface-2);
    border: 1px solid var(--border);
}
.hinweis-wort {
    font-family: var(--font-card);
    font-weight: 600;
    /* Die Untergrenze ist bewusst niedrig: Lieber ein kleiner geschriebenes
       langes Wort als eines, das über zwei Zeilen zerfällt. */
    font-size: clamp(1.05rem, 6.5vw, 1.6rem);
    letter-spacing: .01em;
    min-width: 0;
    /* break-word statt anywhere: Getrennt wird erst, wenn das Wort auch allein
       auf der Zeile nicht passt – und dann an einer Silbengrenze. */
    overflow-wrap: break-word;
    hyphens: auto;
}
.hinweis-zahl {
    display: grid;
    place-items: center;
    width: 40px; height: 40px;
    border-radius: 50%;
    background: var(--accent);
    color: var(--accent-ink);
    font: 700 1.15rem/1 var(--font-num);
    flex: none;
}
/* Der Zähler läuft über beide Spalten in die zweite Zeile. */
.hinweis-zusatz { grid-column: 1 / -1; }

.gitter {
    display: grid;
    /* minmax(0, 1fr), nicht 1fr – siehe die Begründung beim Kalender weiter
       unten: Sonst bestimmt die Höhe der Karten die Mindestbreite der Spalten
       und das Gitter wächst über den Bildschirm hinaus. */
    grid-template-columns: repeat(var(--kante, 5), minmax(0, 1fr));
    gap: var(--sp-2);
    /* Das Gitter ist quadratisch und soll nie breiter werden als hoch – sonst
       läuft es auf dem Tablet aus dem Bild. */
    max-width: min(100%, 68vh);
    margin-inline: auto;
}

.karte {
    /* aspect-ratio statt fester Höhe: Das Raster passt sich jeder Breite an,
       ohne dass irgendwo eine Zahl in Pixeln steht. */
    aspect-ratio: 1 / 1;
    min-width: 0;
    display: grid;
    place-items: center;
    padding: var(--sp-1);
    border: 0;
    border-radius: var(--radius-sm);
    background: var(--card-neutral);
    color: var(--card-ink);
    font-family: var(--font-card);
    font-weight: 600;
    /* Die längsten Begriffe der Liste haben 14 Zeichen und müssen bei 375 px
       Breite lesbar bleiben. */
    font-size: clamp(.58rem, 2.4vw, .95rem);
    line-height: 1.1;
    text-align: center;
    overflow-wrap: anywhere;
    hyphens: auto;
    cursor: default;
    position: relative;
    overflow: hidden;
    transition: transform var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}

/* Antippbar nur, wenn man auch tippen darf. */
.karte.tippbar { cursor: pointer; }
.karte.tippbar:active { transform: scale(.96); box-shadow: inset 0 0 0 2px var(--accent-line); }

.karte.gesucht  { background: var(--card-target); color: var(--card-ink); }
.karte.verboten { background: var(--card-forbid); color: var(--card-neutral); box-shadow: inset 0 0 0 2px var(--neg); }

/* Aufgedeckt und neutral: verbraucht. Halbe Deckkraft UND Durchstreichung –
   die Farbe allein trüge die Information nicht. */
.karte.verbraucht {
    background: var(--card-spent);
    color: var(--muted);
    text-decoration: line-through;
    text-decoration-thickness: 2px;
}

/* Das Zeichen oben links – die zweite, farbunabhängige Kodierung. */
.karte::before {
    content: "";
    position: absolute;
    top: 5px; left: 5px;
    width: 8px; height: 8px;
    border-radius: 50%;
    opacity: 0;
}
.karte.gesucht::before  { opacity: 1; background: var(--card-ink); }
.karte.verboten::before {
    opacity: 1;
    background: transparent;
    border: 2px solid var(--neg);
    width: 11px; height: 11px;
}

/* Bereits aufgedeckte Karten sind für den Codegeber weiterhin erkennbar, aber
   erledigt: Ein dünner Rahmen sagt „diese hier ist schon offen", ohne die
   Rollenfarbe zu verlieren. */
.karte.offen { box-shadow: inset 0 0 0 3px var(--bg); }
.karte.offen.verboten { box-shadow: inset 0 0 0 3px var(--neg); }

/* Das Aufdecken. Nur die eben umgeschlagene Karte trägt die Klasse – ein
   Gitter, in dem beim Laden 25 Karten kippen, wäre Zirkus. */
@keyframes karte-kippt {
    0%   { transform: rotateY(0deg)   scale(1); }
    50%  { transform: rotateY(90deg)  scale(1.06); }
    100% { transform: rotateY(0deg)   scale(1); }
}
.karte.kippt { animation: karte-kippt var(--dur) var(--ease); }

.gitter-fuss {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    margin-top: var(--sp-4);
    flex-wrap: wrap;
}

/* Der Fortschrittszähler: wie viele gesuchte Wörter noch fehlen. */
.punkte {
    display: flex;
    align-items: baseline;
    gap: var(--sp-1);
    font-family: var(--font-num);
    font-size: 1.1rem;
    font-weight: 700;
}
.punkte small { font-size: .75rem; color: var(--muted); font-weight: 400; }

/* --- Der Hinweisverlauf --- */

.verlauf { list-style: none; margin: 0; padding: 0; }
.verlauf li {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-2) 0;
    border-bottom: 1px solid var(--border);
}
.verlauf li:last-child { border-bottom: 0; }
.verlauf-wort { font-family: var(--font-card); font-weight: 600; flex: 1; min-width: 0; }

/* Die Tipps einer Runde als Punktreihe – auf einen Blick, wie sie ausging. */
.tipps { display: flex; gap: 3px; }
.tipp {
    width: 10px; height: 10px;
    border-radius: 50%;
    background: var(--card-spent);
}
.tipp.target    { background: var(--accent); }
.tipp.neutral   { background: var(--muted); }
.tipp.forbidden { background: var(--neg); }

/* ===========================================================================
   Archivkalender
   =========================================================================== */

.monat { margin-bottom: var(--sp-6); }
.monat h3 { margin-bottom: var(--sp-2); color: var(--muted); font-size: .9rem; }

/* minmax(0, 1fr) statt 1fr: Grid-Spalten sind sonst mindestens so breit wie
   der Inhalt ihrer Items, und ein Item mit aspect-ratio leitet diese
   Mindestbreite aus seiner HÖHE ab. Das Raster wuchs dadurch auf ein
   Mehrfaches der Bildschirmbreite – sichtbar blieben drei von sieben Spalten.
   Dasselbe gilt für das Wortgitter oben. */
.kalender {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: var(--sp-1);
}

.kalender .wochentag {
    text-align: center;
    font-size: .7rem;
    color: var(--muted);
    padding-bottom: var(--sp-1);
}

.tag {
    aspect-ratio: 1 / 1;
    min-width: 0;
    display: grid;
    place-items: center;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text);
    font: 600 .8rem var(--font-num);
    cursor: pointer;
    position: relative;
    transition: transform var(--dur-fast) var(--ease);
}
.tag:active { transform: scale(.94); }
.tag:disabled { opacity: .25; cursor: not-allowed; transform: none; }

.tag.won     { background: var(--pos-soft);    border-color: var(--pos); }
.tag.lost    { background: var(--neg-soft);    border-color: var(--neg); }
.tag.running { background: var(--accent-soft); border-color: var(--accent-line); }
.tag.leer    { background: transparent; color: var(--muted); border-style: dashed; }

/* Die Punktzahl unten in der Kachel – bei gewonnenen Tagen das Ergebnis. */
.tag small {
    position: absolute;
    bottom: 2px;
    font-size: .6rem;
    font-weight: 400;
    color: var(--muted);
}

/* ===========================================================================
   Statistik
   =========================================================================== */

.kennzahlen {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
    gap: var(--sp-2);
    margin-bottom: var(--sp-4);
}

.kennzahl {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: var(--sp-3);
    text-align: center;
}
.kennzahl b {
    display: block;
    font: 700 1.4rem/1.2 var(--font-num);
    font-variant-numeric: tabular-nums;
}
.kennzahl span { font-size: .72rem; color: var(--muted); }

.verlaufsstreifen { display: flex; gap: 3px; margin-top: var(--sp-2); }
.verlaufsstreifen i {
    flex: 1;
    height: 22px;
    border-radius: 3px;
    background: var(--card-spent);
}
.verlaufsstreifen i.won  { background: var(--pos); }
.verlaufsstreifen i.lost { background: var(--neg); }

.rangliste { width: 100%; border-collapse: collapse; }
.rangliste th, .rangliste td {
    text-align: left;
    padding: var(--sp-2);
    border-bottom: 1px solid var(--border);
    font-size: .9rem;
}
.rangliste th { color: var(--muted); font-weight: 600; font-size: .75rem; }
.rangliste td.zahl { text-align: right; font-family: var(--font-num); }
.rangliste tr.eigen { background: var(--accent-soft); }

/* ===========================================================================
   Bausteine der übernommenen Module

   ui.js, util.js, install.js, offline.js, sw-update.js und patchnotes.js sind
   aus LifeHub übernommen und sollen zwischen den Projekten WORTGLEICH bleiben –
   eine Korrektur dort soll hier ankommen, ohne dass jemand zwei Fassungen
   abgleicht. Deshalb tragen sie ihre Klassennamen mit, und dieser Abschnitt
   bedient sie mit den Tokens von CodeWords.

   Wer hier etwas umbenennt, muss es in sechs JavaScript-Dateien nachziehen.
   =========================================================================== */

/* --- Hilfsklassen --- */

.grow  { flex: 1; min-width: 0; }
.muted { color: var(--muted); }
.row   { display: flex; align-items: center; gap: var(--sp-3); }
.stack { display: flex; flex-direction: column; gap: var(--sp-2); }

.ellipsis {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.text-danger { color: var(--neg-text); }

/* Aliasse auf die Knopfnamen der übernommenen Module. */
.btn-ghost { background: transparent; border-color: transparent; color: var(--muted); }
.btn-sm    { min-height: 34px; padding: 0 var(--sp-3); font-size: .85rem; }
.icon-btn {
    display: inline-grid;
    place-items: center;
    min-width: var(--tap);
    min-height: var(--tap);
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text);
    cursor: pointer;
}
.icon-btn svg { width: 22px; height: 22px; }

.list { display: flex; flex-direction: column; }
.list-row {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    width: 100%;
    min-height: var(--tap);
    padding: var(--sp-3) 0;
    border: 0;
    border-bottom: 1px solid var(--border);
    background: transparent;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}
.list-row:last-child { border-bottom: 0; }
.list-title { font-weight: 600; }

.empty { text-align: center; padding: var(--sp-12) var(--sp-4); color: var(--muted); }
.empty h2 { margin-bottom: var(--sp-2); color: var(--text); }

/* --- Blatt (Bottom Sheet) --- */

.sheet-scrim {
    position: fixed;
    inset: 0;
    background: var(--scrim);
    z-index: var(--z-sheet);
    display: grid;
    align-items: end;
    opacity: 0;
    transition: opacity var(--dur) var(--ease);
}
.sheet-scrim.open { opacity: 1; }
@media (min-width: 700px) { .sheet-scrim { place-items: center; } }

.sheet {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: var(--z-sheet);
    display: flex;
    flex-direction: column;
    max-height: 85dvh;
    background: var(--surface);
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    box-shadow: var(--shadow-lg);
    transform: translateY(100%);
    transition: transform var(--dur) var(--ease);
}
.sheet.open { transform: translateY(0); }

@media (min-width: 700px) {
    .sheet {
        left: 50%; right: auto; bottom: auto; top: 50%;
        width: min(92vw, 460px);
        border-radius: var(--radius-lg);
        transform: translate(-50%, -46%);
        opacity: 0;
    }
    .sheet.open { transform: translate(-50%, -50%); opacity: 1; }
}

/* Kopf und Fuß stehen fest, nur der Rumpf scrollt – dadurch bleibt der
   Speichern-Knopf auch bei offener Tastatur erreichbar. */
.sheet-head {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-3) var(--sp-4);
    border-bottom: 1px solid var(--border);
}
.sheet-body {
    padding: var(--sp-4);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}
.sheet-foot {
    padding: var(--sp-4);
    padding-bottom: max(var(--sp-4), env(safe-area-inset-bottom));
    border-top: 1px solid var(--border);
}

/* --- Meldungsstreifen --- */

.toast {
    position: fixed;
    left: 50%;
    bottom: calc(var(--tabbar-h) + var(--sp-4) + env(safe-area-inset-bottom));
    transform: translate(-50%, 12px);
    opacity: 0;
    z-index: var(--z-toast);
    max-width: min(90vw, 420px);
    padding: var(--sp-3) var(--sp-4);
    border-radius: var(--radius-sm);
    background: var(--surface-2);
    border: 1px solid var(--border-2);
    box-shadow: var(--shadow-lg);
    font-size: .9rem;
    transition: transform var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.toast.show { transform: translate(-50%, 0); opacity: 1; }
.toast.error { border-color: var(--neg); color: var(--neg-text); }
.toast-action {
    margin-left: var(--sp-3);
    background: none;
    border: 0;
    color: var(--accent-text);
    font: 600 .9rem var(--font);
    cursor: pointer;
}

/* --- Verbindungsstreifen --- */

.offline-bar {
    position: fixed;
    left: 0; right: 0;
    top: 0;
    z-index: var(--z-toast);
    padding: var(--sp-2) var(--sp-4);
    background: var(--neg);
    color: #fff;                /* 4,0:1 auf #da5148 – kurzer Streifen, fett */
    font: 600 .85rem var(--font);
    text-align: center;
    transform: translateY(-100%);
    transition: transform var(--dur) var(--ease);
}
.offline-bar.show { transform: translateY(0); }
.offline-bar.is-sending { background: var(--surface-2); color: var(--text); }

body.has-offline-bar .topbar { padding-top: calc(var(--sp-2) + 32px); }

/* --- Aktionsbanner (neue Fassung verfügbar) --- */

.action-banner {
    position: fixed;
    left: 50%;
    bottom: calc(var(--tabbar-h) + var(--sp-4) + env(safe-area-inset-bottom));
    transform: translateX(-50%);
    z-index: var(--z-toast);
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    max-width: min(92vw, 460px);
    padding: var(--sp-3) var(--sp-4);
    border-radius: var(--radius);
    background: var(--surface-2);
    border: 1px solid var(--border-2);
    box-shadow: var(--shadow-lg);
    font-size: .9rem;
}

/* --- Ladezustände --- */

.loading { display: grid; place-items: center; gap: var(--sp-3); padding: var(--sp-12) 0; color: var(--muted); }

.spinner {
    width: 28px; height: 28px;
    border: 3px solid var(--border);
    border-top-color: var(--accent-text);
    border-radius: 50%;
    animation: dreht .8s linear infinite;
}
@keyframes dreht { to { transform: rotate(360deg); } }

/* Platzhalter statt Spinner, wo die Form der Antwort schon feststeht: Ein
   Umriss, der zur kommenden Liste passt, wirkt kürzer als ein Kreisel. */
.skeleton { display: flex; flex-direction: column; gap: var(--sp-3); }
.sk {
    background: linear-gradient(90deg, var(--surface) 25%, var(--surface-2) 37%, var(--surface) 63%);
    background-size: 400% 100%;
    border-radius: var(--radius-sm);
    animation: sk-welle 1.4s ease infinite;
}
@keyframes sk-welle { from { background-position: 100% 0; } to { background-position: -100% 0; } }

.sk-list  { display: flex; flex-direction: column; gap: var(--sp-3); }
.sk-row   { display: flex; align-items: center; gap: var(--sp-3); }
.sk-icon  { width: 36px; height: 36px; border-radius: 50%; flex: none; }
.sk-line  { height: 12px; flex: 1; }
.sk-sm    { height: 10px; max-width: 40%; }
.sk-dot   { width: 10px; height: 10px; border-radius: 50%; flex: none; }
.sk-value { width: 56px; height: 16px; flex: none; }

/* --- Installationshinweis und Patchnotes --- */

.install-benefits, .install-steps { list-style: none; margin: 0 0 var(--sp-4); padding: 0; }
.install-benefits li, .install-steps li {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-2) 0;
}
.install-steps { counter-reset: schritt; }
.install-steps li::before {
    counter-increment: schritt;
    content: counter(schritt);
    display: grid;
    place-items: center;
    width: 24px; height: 24px;
    border-radius: 50%;
    background: var(--accent);
    color: var(--accent-ink);
    font: 700 .8rem var(--font-num);
    flex: none;
}

.patchnotes { white-space: pre-wrap; line-height: 1.6; }

/* Das Bento-Raster gibt es in CodeWords nicht – der Platzhalter aus util.js
   fällt auf eine schlichte Spalte zurück. */
.bento { display: flex; flex-direction: column; gap: var(--sp-3); }

/* ===========================================================================
   Ergebnisschirm
   =========================================================================== */

.ergebnis { text-align: center; padding: var(--sp-6) 0; }
.ergebnis h2 {
    font-size: clamp(1.6rem, 8vw, 2.2rem);
    margin-bottom: var(--sp-2);
}
.ergebnis .punktzahl {
    font: 700 clamp(2.6rem, 16vw, 4rem)/1 var(--font-num);
    color: var(--accent-text);
    display: block;
    margin: var(--sp-3) 0 var(--sp-1);
}

/* ===========================================================================
   Bewegung sparsam
   =========================================================================== */

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important;
    }
    /* Das Kippen der Karte trägt Bedeutung – es zeigt, WELCHE Karte sich eben
       geändert hat. Ohne Bewegung übernimmt das ein kurzes Aufleuchten. */
    .karte.kippt { animation: none; box-shadow: inset 0 0 0 3px var(--accent-line); }
}
