/* ══════════════════════════════════════════════════════════════
   crossword-grid — Stylesheet des CrabsWords-Bretts.

   Repo-fähig: KEINE App-Variablen, KEINE App-Selektoren (kein
   `body.game-mode`, kein `html.light-theme`). Alles Farbige läuft über
   `--cw-*`-Tokens mit Vorgaben; die App setzt sie je Theme. Alles
   Layoutige beschränkt sich auf die eigenen Elemente — wo das Brett im
   Bildschirm sitzt, entscheidet die App.
   ══════════════════════════════════════════════════════════════ */

.cw-root {
  --cw-cell-bg:       #ECEBE7;   /* leeres Buchstabenfeld */
  --cw-cell-border:   #C9C6BE;
  --cw-void:          transparent; /* Feld ohne Buchstabe: gar nichts */
  --cw-text:          #241F1C;
  --cw-selected:      #241F1C;   /* Rahmen des gewählten Worts */
  --cw-selected-inner: rgba(255, 255, 255, 0.9);  /* heller Saum darunter */
  --cw-found-default: #5B4B8A;   /* wenn der Aufrufer keine Farbe nennt */
  --cw-found-text:    #FBF3E3;   /* Schrift auf einem gefundenen Feld */
  --cw-multi:         #8A857C;   /* mehrere, aber nicht alle — neutral */
  --cw-all:           #241F1C;   /* ALLE haben es — erledigt */

  position: relative;
  display: flex;
  justify-content: center;
}

/* Der Ausschnitt. Er beschneidet das Brett; geschoben wird das Brett darin.
   `touch-action: none`, weil das Modul die Zeiger selbst führt — sonst
   deutete der Browser dieselbe Bewegung zusätzlich als Scrollen oder als
   Zurück-Geste. */
.cw-view {
  position: relative;
  overflow: hidden;
  touch-action: none;
  overscroll-behavior: none;
}

/* Senkrecht ziehen gehört der SEITE, nicht dem Brett (`verticalDrag: 'page'`).
   Für ein Brett, das eine Auswertung ist statt einer Eingabefläche: dort
   liegt es mitten in einer Seite, die man durchscrollen will, und es sperrte
   sonst genau auf seiner Höhe jede Wischgeste. Waagerecht bleibt beim Modul —
   ein breites Brett muss sich weiterhin schieben lassen, und waagerecht scrollt
   in dieser App ohnehin nichts (siehe overscroll-behavior-x in style.css).

   `overscroll-behavior-y` MUSS hier zurück auf `auto`: der Ausschnitt ist wegen
   `overflow: hidden` ein Scroll-Behälter, auch wenn in ihm nichts zu scrollen
   ist. `none` heißt dann nicht „ich scrolle nicht", sondern „und niemand sonst"
   — die Wischgeste blieb im Brett hängen und die Seite rührte sich keinen Punkt.
   Das `pan-y` darüber gab das Ziehen an den Browser ab, und die Zeile darunter
   nahm es ihm wieder weg. Waagerecht bleibt `none`: dort ist die Nachbargeste
   die Zurück-Navigation, und die darf ein Brett nie auslösen. */
.cw-root--vdrag-page .cw-view {
  touch-action: pan-y;
  overscroll-behavior-x: none;
  overscroll-behavior-y: auto;
}

/* … aber nur, solange das Brett ganz hineinpasst. Ragt es hinaus, liegt etwas
   darunter, das allein durch Schieben zu sehen ist — dann gehört die Geste
   wieder dem Brett, sonst scrollt einem die Seite unter dem Finger weg, statt
   dass sich das Rätsel bewegt. Die Klasse setzt `markEdges`, und sie hängt an
   der GRÖSSE, nicht am Stand: sonst wechselte der Besitzer mitten im Ziehen.

   Die Seite bleibt trotzdem überall erreichbar — links und rechts vom Brett
   liegt dafür ein fingerkuppenbreiter Streifen (siehe --crab-solution-inset in
   style.css). */
.cw-root--vdrag-page.cw-root--vpannable .cw-view {
  touch-action: none;
  overscroll-behavior-y: none;
}

.cw-root--pannable .cw-view {
  cursor: grab;
}
.cw-root--pannable .cw-view:active {
  cursor: grabbing;
}

/* Das Brett selbst: ein Raster fester Feldgröße. Es wird als GANZES
   verschoben (`transform`), nie umgebrochen — die Spaltenzahl ist die
   Gitterbreite und darf sich nie an den Ausschnitt anpassen, sonst stünden
   die Wörter nicht mehr da, wo der Server sie hingelegt hat. */
.cw-board {
  position: absolute;
  top: 0;
  left: 0;
  display: grid;
  grid-template-columns: repeat(var(--cw-cols, 1), var(--cw-cell, 32px));
  grid-auto-rows: var(--cw-cell, 32px);
  gap: 1px;
  will-change: transform;
}

.cw-cell {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--cw-cell-bg);
  border: 1px solid var(--cw-cell-border);
  border-radius: 3px;
  box-sizing: border-box;
  /* Die Schrift wächst mit dem Feld, bleibt aber im Feld. */
  font-size: calc(var(--cw-cell, 32px) * 0.58);
  line-height: 1;
  font-weight: 600;
  color: var(--cw-text);
  user-select: none;
  -webkit-user-select: none;
}

/* Ein Feld ohne Buchstabe ist kein Feld — es ist Zwischenraum. Kein Rahmen,
   keine Fläche, und vor allem kein Tap-Ziel. */
.cw-cell--void {
  background: var(--cw-void);
  border-color: transparent;
  pointer-events: none;
}

/* Ein Tipp macht den Buchstaben FETT — und er bleibt es, auch wenn das Wort
   später gefunden wird. Er ist geschenkt, nicht erarbeitet, und das soll man
   ihm ansehen.
   Die FLÄCHE rührt er nicht an: die gehört den Findern und sagt, wer das Feld
   hat. Früher übermalte der Tipp sie hell — dann stand da „niemand hat es",
   während es längst jemandem gehörte. */
.cw-cell--hinted {
  font-weight: 800;
}

.cw-cell--found {
  background: var(--cw-found, var(--cw-found-default));
  border-color: var(--cw-found, var(--cw-found-default));
  color: var(--cw-found-text);
}

/* Von MEHREREN gefunden, aber nicht von allen: keine einzelne Spielerfarbe
   stimmt mehr, also ein neutraler Ton. */
.cw-cell--multi {
  background: var(--cw-multi);
  border-color: var(--cw-multi);
}

/* Von ALLEN gefunden: dort ist für niemanden mehr etwas zu holen. Deutlich
   dunkler als Grau, sonst ließe sich „fertig" nicht von „mehrere" trennen. */
.cw-cell--all {
  background: var(--cw-all);
  border-color: var(--cw-all);
  color: var(--cw-found-text);
}

/* Die Markierung des gewählten Worts: EIN Rahmen um das ganze Wort, nicht um
   jedes Feld. Ein Rahmen je Feld zeigt die Bestandteile statt des Worts.

   Sie wird VOLLSTÄNDIG AUSSERHALB der Felder gezeichnet — das Element ist exakt
   so groß wie das Wort, hat weder Rand noch Fläche, und beide Ringe liegen als
   Schlagschatten davor. Damit bleibt die Darstellung der Buchstaben unberührt:
   Feldfarbe, Fundfarbe und Schrift sehen genauso aus wie ohne Auswahl. Ein
   früherer Versuch mit einem inneren Saum fraß sichtbar in die Felder hinein.

   Zwei Ringe, weil einer immer mit dem konkurriert, was darunter liegt: außen
   kräftig und dunkel, darum herum ein heller Saum. So trennt die Markierung
   gegen dunkle wie gegen helle Umgebung. */
.cw-selection {
  position: absolute;
  pointer-events: none;              /* rein optisch — Tipps gehören den Feldern */
  border-radius: 5px;
  box-shadow: 0 0 0 2px var(--cw-selected),
              0 0 0 4px var(--cw-selected-inner);
  transition: left 60ms linear, top 60ms linear, width 60ms linear, height 60ms linear;
}
.cw-selection[hidden] { display: none; }

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

/* Ein frisch aufgedeckter Buchstabe blinkt einmal auf. Rein optisch und
   bewusst kurz — er darf den nächsten Tipp nicht überlagern. */
@keyframes cw-pop {
  0%   { transform: scale(1);    }
  40%  { transform: scale(1.18); }
  100% { transform: scale(1);    }
}
.cw-cell.cw-pop {
  animation: cw-pop 260ms ease-out;
}

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


/* Ausblenden an den Rändern, an denen es weitergeht. Ohne dieses Zeichen sieht
   ein abgeschnittenes Gitter aus wie ein zu Ende gezeichnetes — man sucht das
   fehlende Wort dann gar nicht erst.

   Umgesetzt als Maske über beide Achsen: `mask-composite: intersect` lässt
   nur durch, was BEIDE Masken durchlassen — sonst hübe die eine Achse die
   Verblassung der anderen wieder auf. Die Breite ist 0, wo es nicht
   weitergeht; dann ist die Maske dort wirkungslos. */
.cw-view {
  --cw-fade: 22px;
  --cw-fade-l: 0px;
  --cw-fade-r: 0px;
  --cw-fade-t: 0px;
  --cw-fade-b: 0px;
  -webkit-mask-image:
    linear-gradient(to right,  transparent 0, #000 var(--cw-fade-l),
                    #000 calc(100% - var(--cw-fade-r)), transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 var(--cw-fade-t),
                    #000 calc(100% - var(--cw-fade-b)), transparent 100%);
  -webkit-mask-composite: source-in;
          mask-image:
    linear-gradient(to right,  transparent 0, #000 var(--cw-fade-l),
                    #000 calc(100% - var(--cw-fade-r)), transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 var(--cw-fade-t),
                    #000 calc(100% - var(--cw-fade-b)), transparent 100%);
          mask-composite: intersect;
}
.cw-view[data-more-left="true"]   { --cw-fade-l: var(--cw-fade); }
.cw-view[data-more-right="true"]  { --cw-fade-r: var(--cw-fade); }
.cw-view[data-more-top="true"]    { --cw-fade-t: var(--cw-fade); }
.cw-view[data-more-bottom="true"] { --cw-fade-b: var(--cw-fade); }
