/*!
 * Akademia Elektryczna GOLED — plansza, paleta, miernik, warstwa wiedzy.
 * wp_enqueue_style('goled-circuit-academy')
 * tylko strony Akademii
 * @cache po kazdej zmianie podnies @version — LiteSpeed (Combine/UCSS) i przegladarka
 *        odswieza wtedy ten plik oraz partie, w ktorych sie znajduje.
 * @version auto (filemtime / GOLED_CA_VERSION) · 2026-09-23
 */

/*
 * GOLED Circuit Academy - styles for the interactive circuit board.
 *
 * Everything visual is driven by the variables below, so the child theme can
 * restyle the whole module without touching the engine:
 *   --circuit-accent, --circuit-wire, --circuit-live, --circuit-success,
 *   --circuit-danger, --circuit-part-bg, --circuit-grid-color ...
 */

.ca-app {
  --circuit-text: currentColor;
  --circuit-muted: var(--text-muted, rgba(128, 128, 128, 0.75));
  --circuit-accent: var(--goled-accent, var(--primary-color, #feb900));
  --circuit-accent-contrast: #17130a;
  --circuit-wire: var(--circuit-text);
  --circuit-live: var(--circuit-accent);
  --circuit-success: #2e9d52;
  --circuit-danger: #c84242;
  --circuit-part-bg: var(--card-bg, rgba(127, 127, 127, 0.08));
  --circuit-part-stroke: currentColor;
  --circuit-part-muted: var(--circuit-muted);
  --circuit-label: var(--circuit-text);
  --circuit-label-halo: var(--bg-color, #111111);
  --circuit-spark: var(--circuit-accent);
  --circuit-card-bg: var(--card-bg, var(--bg-color, #111111));
  --circuit-grid-color: rgba(128, 128, 128, 0.28);
  --circuit-radius: 14px;
  --circuit-radius-sm: 10px;
  --circuit-gap: 16px;
  --circuit-shadow: 0 10px 30px rgba(0, 0, 0, 0.18);
  /* Szerokosc pasa przewijania strony na waskim ekranie (patrz .ca-scroll).
     Jedno pokretlo: tyle rezerwuje .ca-stage po prawej i tyle ma sam pas.
     Zmierzone na 390 px: 24 px (a nawet 20 px) zwezalo plansze tak, ze kable
     "Czterech punktow" wychodzily poza obszar rysowania; 16 px trzyma je
     w kadrze z zapasem, a pas laczy sie z marginesem strony, wiec przewijalny
     pasek przy krawedzi ma w praktyce ~32 px. */
  --ca-scroll-gutter: 16px;

  display: flex;
  flex-direction: column;
  gap: var(--circuit-gap);
  color: var(--circuit-text);
  margin: 24px 0;
}

body.light-mode .ca-app,
.ca-app.is-light {
  --circuit-grid-color: rgba(0, 0, 0, 0.14);
}

/* ---------- header ---------- */

.ca-head {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.ca-head[hidden] {
  display: none;
}

.ca-kicker {
  margin: 0;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--circuit-accent);
}

.ca-title {
  margin: 0;
  font-size: clamp(1.4rem, 1.1rem + 1.2vw, 2rem);
  line-height: 1.2;
}

.ca-desc {
  margin: 0;
  max-width: 70ch;
  color: var(--circuit-muted);
}

/* ---------- lesson tabs ---------- */

.ca-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.ca-tabs[hidden] {
  display: none;
}

.ca-tab,
.ca-tab:visited {
  border: 1px solid var(--border-color, rgba(127, 127, 127, 0.3));
  background: var(--circuit-part-bg);
  color: inherit;
  padding: 8px 14px;
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
  text-decoration: none;
  transition: border-color 0.2s ease, background 0.2s ease, color 0.2s ease;
}

.ca-tab:hover {
  border-color: var(--circuit-accent);
}

.ca-tab.is-active,
.ca-tab[aria-current='true'] {
  background: var(--circuit-accent);
  border-color: var(--circuit-accent);
  color: var(--circuit-accent-contrast);
  font-weight: 600;
}

/* ---------- layout ---------- */

.ca-work {
  display: grid;
  /* Kolumna z elementami rosnie razem z miejscem (200 -> 320 px), a plansza
     nigdy nie jest wezsza niz przy stalych 220 px. */
  grid-template-columns: clamp(200px, 23%, 320px) minmax(0, 1fr);
  gap: var(--circuit-gap);
  align-items: stretch;
}

.ca-stage {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

.ca-board {
  position: relative;
  display: flex;
  flex-direction: column;
  /* Ramka rosnie o wysokosc dolnego paska; wymiary obszaru rysowania sa nizej. */
  height: auto;
  border: 1px solid var(--border-color, rgba(127, 127, 127, 0.28));
  border-radius: var(--circuit-radius);
  background: transparent;
  overflow: hidden;
  /* Blokade gestow zdejmujemy z calej ramki: trzyma ja dopiero obszar rysowania
     ponizej. Dzieki temu dolny pasek (i same krawedzie ramki) przewijaja strone
     gestem palca - na telefonie plansza zajmuje prawie caly ekran, wiec inaczej
     nie dalo sie zjechac wyzej ani nizej bez trafienia w waski margines. */
}

/* Obszar rysowania. Mierzy go silnik (`Board.measure`), wiec konczy sie
   dokladnie tam, gdzie zaczyna sie dolny pasek - obwod nigdy nie wchodzi pod
   pasek, a pasek nie zabiera mu miejsca. */
.ca-board-stage {
  position: relative;
  min-height: 420px;
  height: clamp(420px, 62vh, 620px);
  touch-action: none;
}

.ca-board .joint-paper {
  background: transparent;
}

/* JointJS writes pixel dimensions onto its own container: keep it out of flow
   so the CSS sized board wrapper stays stable. */
.ca-board-canvas {
  position: absolute;
  top: 0;
  left: 0;
}

/* Odczyt tego, co pod kursorem (ciaglosc przewodu, opis elementu). Lezy jako
   nakladka przy dolnej krawedzi planszy, wiec nie zajmuje miejsca w ukladzie
   i nie przesuwa paska statusu, gdy sie pojawia. */
.ca-board-stage > .ca-inspector {
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: 10px;
  z-index: 3;
  margin: 0;
  padding: 6px 10px;
  border: 1px solid var(--border-color, rgba(127, 127, 127, 0.3));
  border-radius: var(--circuit-radius-sm, 10px);
  background: var(--circuit-card-bg, var(--bg-color, #111111));
  font-size: 0.9rem;
  line-height: 1.4;
  color: var(--circuit-muted);
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.18s ease, transform 0.18s ease;
  pointer-events: none;
}

.ca-board-stage > .ca-inspector.is-visible {
  opacity: 1;
  transform: none;
}

/* ---------- palette ---------- */

.ca-palette {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
  border: 1px solid var(--border-color, rgba(127, 127, 127, 0.28));
  border-radius: var(--circuit-radius);
  background: var(--circuit-part-bg);
}

.ca-palette-title {
  margin: 0;
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--circuit-muted);
}

.ca-parts {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ca-parts[hidden] {
  display: none;
}

.ca-part {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--border-color, rgba(127, 127, 127, 0.3));
  border-radius: var(--circuit-radius-sm);
  background: var(--circuit-part-bg);
  cursor: grab;
  touch-action: none;
  user-select: none;
  transition: border-color 0.2s ease, transform 0.15s ease;
}

.ca-part:hover,
.ca-part:focus-visible {
  border-color: var(--circuit-accent);
  transform: translateY(-1px);
}

.ca-part.is-exhausted {
  opacity: 0.45;
  cursor: not-allowed;
}

.ca-part-count {
  margin-left: auto;
  font-size: 0.75rem;
  color: var(--circuit-muted);
  white-space: nowrap;
}

.ca-part-label {
  font-size: 0.92rem;
}

.ca-part-icon {
  flex: 0 0 auto;
  width: 30px;
  height: 20px;
  border: 1px solid currentColor;
  border-radius: 6px;
  position: relative;
}

.ca-part-icon::after {
  content: '';
  position: absolute;
  inset: 3px;
  border-radius: 4px;
  background: var(--circuit-accent);
  opacity: 0.55;
}

.ca-part-icon--switch::after {
  background: linear-gradient(90deg, var(--circuit-accent) 40%, transparent 40%);
}

.ca-part-icon--bulb {
  border-radius: 50%;
  width: 22px;
  height: 22px;
}

.ca-part-icon--bulb::after {
  border-radius: 50%;
  opacity: 0.75;
}

/* Ikony nowych elementow. Klasa powstaje z rodzaju elementu, dlatego tasma ma
   "ledStrip" (wielka litera w srodku), a nie "ledstrip". */
.ca-part-icon--mains::after {
  background:
    radial-gradient(circle at 38% 50%, var(--circuit-accent) 0 2px, transparent 2px),
    radial-gradient(circle at 62% 50%, var(--circuit-accent) 0 2px, transparent 2px);
  opacity: 1;
}

.ca-part-icon--psu::after {
  background: linear-gradient(
    90deg,
    var(--circuit-accent) 0 42%,
    transparent 42% 58%,
    var(--circuit-accent) 58%
  );
  opacity: 0.75;
}

.ca-part-icon--ledStrip {
  height: 14px;
  border-radius: 4px;
}

.ca-part-icon--ledStrip::after {
  inset: 2px;
  border-radius: 2px;
  background: repeating-linear-gradient(
    90deg,
    var(--circuit-accent) 0 8px,
    transparent 8px 12px
  );
  opacity: 0.85;
}

.ca-palette-note {
  margin: 0;
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--circuit-muted);
}

/* Podpowiedz korzysta z wolnego dolu kolumny z elementami: trzyma sie dolnej
   krawedzi, a w waskiej kolumnie dostaje ciasniejszy padding. */
.ca-palette .ca-hint {
  margin-top: auto;
  padding: 12px;
}

.ca-ghost {
  position: fixed;
  top: 0;
  left: 0;
  /* Rozmiar ustawia `buildDragGhost()` w pikselach, w rozmiarze elementu
     przeskalowanym jak plansza - wspolnie dla palety i dla demo. */
  border: 2px dashed var(--circuit-accent, #feb900);
  border-radius: 14px;
  background: rgba(254, 185, 0, 0.12);
  pointer-events: none;
  z-index: 9999;
  opacity: 0.9;
}

/* ---------- SVG parts ---------- */

.ca-board svg text {
  font-family: inherit;
  user-select: none;
}

.joint-element {
  cursor: move;
}

.joint-link {
  cursor: pointer;
}

.joint-element [joint-selector='body'] {
  fill: var(--circuit-part-bg);
  stroke: var(--circuit-part-stroke);
  stroke-width: 2;
}

/* Podpisy leza pod elementem. Halo (stroke pod tekstem) trzyma czytelnosc,
   gdy przewod przebiega tuz pod elementem. */
.joint-element [joint-selector='label'] {
  fill: var(--circuit-label);
  opacity: 0.8;
  font-size: 12px;
  letter-spacing: 0.1em;
  text-anchor: middle;
  dominant-baseline: middle;
  paint-order: stroke;
  stroke: var(--circuit-label-halo);
  stroke-width: 3px;
  stroke-linejoin: round;
}

/* Bieguny zostaja wewnatrz elementu, na osi styku */
.joint-element [joint-selector='polarityPlus'],
.joint-element [joint-selector='polarityMinus'],
.joint-element [joint-selector='plus'],
.joint-element [joint-selector='minus'],
.joint-element [joint-selector='labelN'],
.joint-element [joint-selector='labelL'] {
  fill: var(--circuit-label);
  stroke: none;
  opacity: 0.9;
  font-size: 12px;
  text-anchor: middle;
  dominant-baseline: middle;
}

.joint-cell [joint-selector='leadNeg'],
.joint-cell [joint-selector='leadPos'],
.joint-cell [joint-selector='leadCommon'],
.joint-cell [joint-selector='leadA'],
.joint-cell [joint-selector='leadB'],
.joint-cell [joint-selector='leadIn1'],
.joint-cell [joint-selector='leadOut1'],
.joint-cell [joint-selector='leadIn2'],
.joint-cell [joint-selector='leadOut2'] {
  stroke: var(--circuit-part-stroke);
  stroke-width: 4;
  stroke-linecap: round;
}

.joint-type-ca-battery [joint-selector='cellLong'] {
  stroke: var(--circuit-accent);
  stroke-width: 4;
  stroke-linecap: round;
}

.joint-type-ca-battery [joint-selector='cellShort'] {
  stroke: var(--circuit-accent);
  stroke-width: 4;
  stroke-linecap: round;
  stroke-dasharray: 6 3;
}

.joint-type-ca-bulb [joint-selector='glass'] {
  fill: rgba(127, 127, 127, 0.12);
  stroke: var(--circuit-part-stroke);
  stroke-width: 2;
  transition: fill 0.35s ease, stroke 0.35s ease;
}

.joint-type-ca-bulb [joint-selector='filament'] {
  fill: none;
  stroke: var(--circuit-part-muted);
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: stroke 0.35s ease;
}

.joint-type-ca-bulb [joint-selector='glow'] {
  fill: var(--circuit-accent);
  opacity: 0;
  transition: opacity 0.35s ease;
  pointer-events: none;
  transform-box: fill-box;
  transform-origin: center;
}

.joint-type-ca-bulb [joint-selector='base'] {
  fill: rgba(127, 127, 127, 0.25);
  stroke: var(--circuit-part-stroke);
  stroke-width: 2;
}

.joint-type-ca-bulb [joint-selector='baseRing1'],
.joint-type-ca-bulb [joint-selector='baseRing2'] {
  stroke: var(--circuit-part-muted);
  stroke-width: 2;
}

.joint-type-ca-bulb.is-lit [joint-selector='glow'] {
  opacity: 0.85;
  animation: ca-glow-pulse 2.6s ease-in-out infinite;
}

.joint-type-ca-bulb.is-lit [joint-selector='glass'] {
  fill: rgba(255, 226, 140, 0.92);
  stroke: var(--circuit-accent);
}

.joint-type-ca-bulb.is-lit [joint-selector='filament'] {
  stroke: #fff8e1;
}

.joint-type-ca-led.is-lit [joint-selector='triangle'] {
  fill: var(--circuit-accent);
}

.joint-type-ca-switch [joint-selector='contactA'],
.joint-type-ca-switch [joint-selector='contactB'] {
  fill: var(--circuit-part-stroke);
  stroke: none;
}

.joint-type-ca-switch [joint-selector='leverClosed'],
.joint-type-ca-switch [joint-selector='leverOpen'] {
  stroke: var(--circuit-accent);
  stroke-width: 5;
  stroke-linecap: round;
}

.joint-type-ca-switch.is-open [joint-selector='leverOpen'] {
  stroke: var(--circuit-danger);
}

.joint-type-ca-switch.is-open [joint-selector='body'] {
  stroke: var(--circuit-danger);
}

/* Przelacznik schodowy: dzwignia pokazuje wybrane wyjscie */
.joint-type-ca-twowayswitch [joint-selector='commonContact'],
.joint-type-ca-twowayswitch [joint-selector='throwA'],
.joint-type-ca-twowayswitch [joint-selector='throwB'] {
  fill: var(--circuit-part-stroke);
  stroke: none;
}

.joint-type-ca-twowayswitch [joint-selector='leverA'],
.joint-type-ca-twowayswitch [joint-selector='leverB'] {
  stroke: var(--circuit-accent);
  stroke-width: 5;
  stroke-linecap: round;
}

/* Przelacznik krzyzowy: w pozycji prostej dwie kreski, w krzyzowej X */
.joint-type-ca-crossswitch [joint-selector='contactIn1'],
.joint-type-ca-crossswitch [joint-selector='contactOut1'],
.joint-type-ca-crossswitch [joint-selector='contactIn2'],
.joint-type-ca-crossswitch [joint-selector='contactOut2'] {
  fill: var(--circuit-part-stroke);
  stroke: none;
}

.joint-type-ca-crossswitch [joint-selector='leverIn1'],
.joint-type-ca-crossswitch [joint-selector='leverIn2'],
.joint-type-ca-crossswitch [joint-selector='leverCross1'],
.joint-type-ca-crossswitch [joint-selector='leverCross2'] {
  stroke: var(--circuit-accent);
  stroke-width: 5;
  stroke-linecap: round;
}

.joint-type-ca-crossswitch.is-crossed [joint-selector='body'] {
  stroke: var(--circuit-accent);
}

/* Bezpiecznik: symbol z kreska; przepalony sygnalizujemy kolorem zagrozenia */
.joint-type-ca-fuse [joint-selector='fuseBox'] {
  fill: none;
  stroke: var(--circuit-part-stroke);
  stroke-width: 2;
}

.joint-type-ca-fuse [joint-selector='fuseLine'] {
  stroke: var(--circuit-accent);
  stroke-width: 3;
  stroke-linecap: round;
}

/* Bezpiecznik wyjety z gniazda: w symbolu jest przerwa, a obudowa jest
   kreskowana - widac, ze element nie przewodzi i czeka na wlozenie. */
.joint-type-ca-fuse.is-open [joint-selector='fuseBox'] {
  stroke-dasharray: 4 3;
}

.joint-type-ca-fuse.is-open [joint-selector='label'] {
  opacity: 0.65;
}

/* Element uszkodzony (przepalona zarowka, przepalony bezpiecznik) */
.joint-element.is-broken [joint-selector='body'] {
  stroke: var(--circuit-danger);
}

.joint-element.is-broken [joint-selector='label'] {
  fill: var(--circuit-danger);
  opacity: 1;
}

.joint-type-ca-fuse.is-broken [joint-selector='fuseLine'] {
  stroke: var(--circuit-danger);
  stroke-dasharray: 5 4;
}

.joint-type-ca-resistor [joint-selector='zigzag'] {
  fill: none;
  stroke: var(--circuit-accent);
  stroke-width: 3;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.joint-type-ca-led [joint-selector='triangle'],
.joint-type-ca-led [joint-selector='bar'] {
  fill: none;
  stroke: var(--circuit-accent);
  stroke-width: 3;
  stroke-linejoin: round;
}

/* Gniazdo 230 V: obudowa z dwoma otworami i znakiem napiecia przemiennego */
.joint-type-ca-mains [joint-selector='slotTop'],
.joint-type-ca-mains [joint-selector='slotBottom'] {
  fill: var(--circuit-part-stroke);
}

.joint-type-ca-mains [joint-selector='wave'] {
  fill: none;
  stroke: var(--circuit-accent);
  stroke-width: 2;
  stroke-linecap: round;
}

/* Zasilacz: wejscie 230 V po lewej, wyjscie stalopradowe po prawej. Obudowa
   jest jedna (bez wewnetrznego prostokata), a polowy rozrozniaja symbole. */
.joint-type-ca-psu [joint-selector='wave'] {
  fill: none;
  stroke: var(--circuit-accent);
  stroke-width: 2.5;
  stroke-linecap: round;
}

/* Symbol DC rysujemy kreskami, a nie znakiem „=": krotsza kreska z kroju
   pisma byla znacznie lzejsza od fali AC obok. */
.joint-type-ca-psu [joint-selector='dcTop'],
.joint-type-ca-psu [joint-selector='dcBottom'] {
  fill: var(--circuit-accent);
  stroke: none;
}

.joint-type-ca-psu [joint-selector='labelAc'],
.joint-type-ca-psu [joint-selector='labelDc'] {
  font-size: 10px;
  text-anchor: middle;
  dominant-baseline: middle;
  fill: var(--circuit-part-muted);
}

.joint-type-ca-psu [joint-selector='labelDc'] {
  fill: var(--circuit-accent);
}

/* Litery zaciskow zasilacza o stopien mniejsze od symboli: wersaliki L/N przy
   tej samej wielkosci optycznie dominowaly nad znakami +/−. */
.joint-type-ca-psu [joint-selector='labelN'],
.joint-type-ca-psu [joint-selector='labelL'] {
  font-size: 10px;
}

/* Tasma LED: pasek z diodami. Swieci, gdy plynie przez nia prad. */
.joint-type-ca-ledstrip [joint-selector='tape'] {
  fill: rgba(127, 127, 127, 0.18);
  stroke: var(--circuit-part-stroke);
  stroke-width: 2;
  transition: fill 0.35s ease, stroke 0.35s ease;
}

.joint-type-ca-ledstrip [joint-selector='chip1'],
.joint-type-ca-ledstrip [joint-selector='chip2'],
.joint-type-ca-ledstrip [joint-selector='chip3'],
.joint-type-ca-ledstrip [joint-selector='chip4'] {
  fill: var(--circuit-part-muted);
  opacity: 0.7;
}

.joint-type-ca-ledstrip.is-lit [joint-selector='tape'],
.joint-type-ca-ledstrip.is-overload-work [joint-selector='tape'],
.joint-type-ca-ledstrip.is-overload-flicker [joint-selector='tape'] {
  fill: rgba(255, 226, 140, 0.92);
  stroke: var(--circuit-accent);
}

.joint-type-ca-ledstrip.is-lit [joint-selector='chip1'],
.joint-type-ca-ledstrip.is-lit [joint-selector='chip2'],
.joint-type-ca-ledstrip.is-lit [joint-selector='chip3'],
.joint-type-ca-ledstrip.is-lit [joint-selector='chip4'] {
  fill: var(--circuit-accent);
  opacity: 1;
}

/* Przepiecie: tasma migocze, a po wybuchu zostaje spalona. Ten sam wyglad ma
   tasma uszkodzona w ukladzie startowym (poziom 16) - bez korpusu nie robi
   tego za nas regula is-broken na body. */
.joint-type-ca-ledstrip.is-overload-flicker [joint-selector='tape'] {
  animation: ca-strip-flicker 240ms steps(2, end) infinite;
}

.joint-type-ca-ledstrip.is-overload-boom [joint-selector='tape'],
.joint-type-ca-ledstrip.is-broken [joint-selector='tape'] {
  fill: rgba(70, 70, 70, 0.45);
  stroke: var(--circuit-danger);
  stroke-dasharray: 6 4;
}

/* ---------- ports ---------- */

.joint-port-body [joint-selector='portBody'] {
  fill: var(--circuit-accent);
  stroke: var(--circuit-part-stroke);
  stroke-width: 2;
  transition: transform 0.15s ease, stroke-width 0.15s ease;
  transform-box: fill-box;
  transform-origin: center;
}

.joint-port-body:hover [joint-selector='portBody'],
.joint-port-body [joint-selector='portBody'].available-magnet {
  stroke-width: 3;
  transform: scale(1.15);
}

.available-magnet {
  fill: var(--circuit-accent);
  stroke: var(--circuit-accent);
}

.available-cell .joint-port-body [joint-selector='portBody'] {
  r: 9;
}

.joint-port-body [joint-selector='portHit'] {
  cursor: crosshair;
}

/* ---------- wires ---------- */

.joint-link [joint-selector='line'] {
  stroke: var(--circuit-wire);
  stroke-width: 4;
  transition: stroke 0.3s ease, stroke-width 0.3s ease;
}

.joint-link.is-live [joint-selector='line'] {
  stroke: var(--circuit-live);
  stroke-dasharray: 10 12;
  animation: ca-current 0.9s linear infinite;
}

/* Dwa kable razem: para kabli z jednego zacisku jedzie obok siebie (oba
   w swoim kolorze) - kazdy oryginalnej szerokosci, z przerwa, w ktorej
   przeswituje tlo. Sciezke wyznacza plansza (Board.markBundles) na calej
   trasie kabla, a jego wlasny obrys jest wyciety maska. */
.joint-link [joint-selector='bundle'] {
  fill: none;
  stroke: var(--circuit-wire);
  stroke-width: 4;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: stroke 0.3s ease, stroke-width 0.3s ease;
}

/* gdy plynie prad, fala idzie takze po wspolnym odcinku */
.joint-link.is-live [joint-selector='bundle'] {
  stroke: var(--circuit-live);
  stroke-dasharray: 10 12;
  animation: ca-current 0.9s linear infinite;
}

/* Kabel narysowany odwrotnie do kierunku pradu (np. powrot z odbiornika do
   minusa prowadzony od minusa do odbiornika): animacja biegnie po narysowanej
   sciezce, wiec odwracamy ja, zeby fala szla po nim tak samo, jak po sasiednich
   kablach. Ustawiamy wylacznie animation-direction - krotka `animation: none`
   przy najechaniu i w prefers-reduced-motion dalej gasi fale, a szybsza fala
   w celebracji zachowuje kierunek. */
.joint-link.is-live.is-flow-reverse [joint-selector='line'],
.joint-link.is-live.is-flow-reverse [joint-selector='bundle'] {
  animation-direction: reverse;
}

/* Najechanie na przewód = zapowiedź usunięcia (klik usuwa przewód).
   Odcinek wspólny (bundle) i reszta trasy (line) zmieniają się tak samo:
   ta sama barwa, ta sama szerokość i to samo tempo przejścia - inaczej widać
   granicę między nimi, a kabel wygląda na dwa kawałki. */
.joint-link.is-hovered [joint-selector='line'],
.joint-link.is-hovered [joint-selector='bundle'] {
  stroke: var(--circuit-danger);
  stroke-width: 5;
  stroke-dasharray: none;
  animation: none;
}

/* Uchwyty końców (w warstwie narzędzi) podświetlają się pod kursorem */
.joint-tools [data-tool-name='target-arrowhead'],
.joint-tools [data-tool-name='source-arrowhead'] {
  cursor: grab;
}

.joint-tools [data-tool-name='target-arrowhead']:hover,
.joint-tools [data-tool-name='source-arrowhead']:hover {
  stroke: var(--circuit-accent);
  stroke-width: 3;
}

.ca-board .available {
  filter: none;
}

/* ---------- status bar ---------- */

.ca-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  /* Pasek przylega do ramki planszy: bez wlasnego obramowania, a jedynym
     separatorem jest tlo. Kolor stanu niesie gorna krawedz (przy `is-idle`
     przezroczysta), wiec nic nie zdubluje ramki. */
  border: 0;
  border-top: 2px solid transparent;
  border-radius: 0 0 var(--circuit-radius) var(--circuit-radius);
  background: var(--circuit-part-bg);
  transition: border-color 0.25s ease;
}

.ca-message {
  min-width: 0;
}

.ca-status-title {
  margin: 0;
  font-weight: 600;
}

.ca-status-detail {
  margin: 4px 0 0;
  font-size: 0.9rem;
  color: var(--circuit-muted);
  line-height: 1.5;
}

.ca-bar.is-success {
  border-top-color: var(--circuit-success);
}

.ca-bar.is-open,
.ca-bar.is-missing,
/* obwód przewodzi, ale cel lekcji nie jest jeszcze spełniony - ton informacyjny,
   żeby nie wyglądało to ani na zaliczenie, ani na błąd */
.ca-bar.is-almost {
  border-top-color: var(--circuit-accent);
}

.ca-bar.is-short {
  border-top-color: var(--circuit-danger);
}

/* przepiecie: element pali się na oczach gracza - pasek ostrzega tak samo
   mocno jak przy zwarciu */
.ca-bar.is-hazard {
  border-top-color: var(--circuit-danger);
}

.ca-actions {
  display: flex;
  gap: 8px;
  margin-left: auto;
}

.ca-btn {
  font: inherit;
  font-size: 0.9rem;
  padding: 9px 16px;
  border-radius: 999px;
  border: 1px solid var(--circuit-accent);
  background: transparent;
  color: inherit;
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease;
  text-wrap: nowrap;
}

.ca-btn:hover {
  background: var(--circuit-accent);
  color: var(--circuit-accent-contrast);
}

.ca-btn-primary {
  background: var(--circuit-accent);
  border-color: var(--circuit-accent);
  color: var(--circuit-accent-contrast);
  font-weight: 600;
}

.ca-btn-primary:hover {
  filter: brightness(1.08);
}

/* ---------- miernik ---------- */

/* Tryb miernika: przycisk jest wcisniety, a zaciski czekaja na sondy. */
.ca-btn-meter.is-active {
  background: var(--circuit-accent);
  border-color: var(--circuit-accent);
  color: var(--circuit-accent-contrast);
  font-weight: 600;
}

/* Poziom, którego nie da się zaliczyć bez pomiaru („Szukanie przerwy”,
   „Zmierz napięcie baterii”): przycisk miernika delikatnie pulsuje, dopóki
   gracz go nie włączy — samo najechanie na przewody nie zalicza poziomu. */
.ca-btn-meter.is-required {
  border-color: var(--circuit-accent);
  background: color-mix(in srgb, var(--circuit-accent) 14%, transparent);
  animation: ca-meter-required 2.4s ease-in-out infinite;
}

.ca-btn-meter.is-required:hover {
  background: var(--circuit-accent);
  color: var(--circuit-accent-contrast);
}

@keyframes ca-meter-required {
  0%,
  100% {
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--circuit-accent) 40%, transparent);
  }
  50% {
    box-shadow: 0 0 0 6px color-mix(in srgb, var(--circuit-accent) 0%, transparent);
  }
}

/* Wyswietlacz miernika: mala szybka w rogu planszy, jak w prawdziwym
   mierniku. Pokazuje wartosc, krotka etykiete i przylozone sondy. */
.ca-meter {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 3;
  display: grid;
  gap: 6px;
  /* Stala szerokosc: odczyt nie rozciaga szybki ani nie przesuwa sasiadow. */
  width: 152px;
  box-sizing: border-box;
  padding: 7px 9px 9px;
  border: 1px solid var(--border-color, rgba(127, 127, 127, 0.3));
  border-radius: var(--circuit-radius-sm, 10px);
  background: var(--circuit-card-bg, var(--bg-color, #111111));
  box-shadow: var(--circuit-shadow);
  /* Wyswietlacz jest tylko odczytem: nie przechwytuje klikniec w zaciski,
     ktore moga znalezc sie pod nim. */
  pointer-events: none;
  animation: ca-meter-in 200ms ease-out both;
}

.ca-meter[hidden] {
  display: none;
}

.ca-meter-head {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  font-size: 0.62rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--circuit-muted);
}

.ca-meter-screen {
  padding: 6px 8px;
  border: 1px solid color-mix(in srgb, var(--circuit-text) 14%, transparent);
  border-radius: 6px;
  background: color-mix(in srgb, var(--circuit-text) 8%, transparent);
}

.ca-meter-value {
  margin: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 1.25rem;
  line-height: 1.1;
  letter-spacing: 0.04em;
  text-align: right;
  color: var(--circuit-accent);
  font-variant-numeric: tabular-nums;
}

.ca-meter-caption {
  margin: 3px 0 0;
  font-size: 0.68rem;
  line-height: 1.3;
  text-align: right;
  color: var(--circuit-muted);
}

.ca-meter-probes {
  display: flex;
  gap: 6px;
}

.ca-meter-probe {
  padding: 1px 6px;
  border: 1px solid var(--circuit-muted);
  border-radius: 999px;
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  color: var(--circuit-muted);
  opacity: 0.5;
  transition: opacity 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}

.ca-meter-probe.is-placed {
  opacity: 1;
}

.ca-meter-probe--red.is-placed {
  border-color: var(--circuit-danger);
  color: var(--circuit-danger);
}

.ca-meter-probe--black.is-placed {
  border-color: var(--circuit-part-stroke);
  color: var(--circuit-text);
}

@keyframes ca-meter-in {
  0% {
    opacity: 0;
    transform: translateY(-4px);
  }
  100% {
    opacity: 1;
    transform: none;
  }
}

.ca-board-canvas.is-meter-mode [joint-selector='portBody'] {
  stroke: var(--circuit-accent);
  stroke-width: 3;
}

/* Znaczniki sond miernika: czerwona (V) i czarna (COM) */
.ca-probe {
  position: absolute;
  z-index: 2;
  width: 20px;
  height: 20px;
  margin: -10px 0 0 -10px;
  border-radius: 50%;
  border: 3px solid var(--circuit-part-stroke);
  pointer-events: none;
  animation: ca-probe-in 220ms ease-out both;
}

.ca-probe--red {
  border-color: var(--circuit-danger);
  background: color-mix(in srgb, var(--circuit-danger) 22%, transparent);
}

.ca-probe--black {
  border-color: var(--circuit-part-stroke);
  background: color-mix(in srgb, var(--circuit-part-stroke) 20%, transparent);
}

@keyframes ca-probe-in {
  0% {
    opacity: 0;
    transform: scale(0.4);
  }
  60% {
    opacity: 1;
    transform: scale(1.15);
  }
  100% {
    opacity: 1;
    transform: scale(1);
  }
}

/* ---------- hint + education ---------- */

.ca-hint {
  padding: 14px 16px;
  border: 1px dashed var(--circuit-accent);
  border-radius: var(--circuit-radius);
  font-size: 0.92rem;
  line-height: 1.55;
  /* Wejscie panelu: fade in, lekkie wsuniecie z gory i pojedynczy rozblysk
     ramki. Rozblysk gra raz, przy pojawieniu sie panelu. */
  animation: ca-hint-in 480ms ease-out both;
}

/* Tresc podpowiedzi podmienia sie przy kazdym kliknieciu, a nowe wezly
   animuja sie same - bez udzialu JS. */
.ca-hint > * {
  animation: ca-hint-text-in 240ms ease-out both;
}

.ca-hint p {
  margin: 0 0 6px;
}

.ca-hint p:last-child {
  margin-bottom: 0;
}

.ca-hint ul {
  margin: 0;
  padding-left: 18px;
}

.ca-hint-title {
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--circuit-accent);
}

.ca-education {
  font-size: 0.95rem;
  line-height: 1.65;
  color: var(--circuit-muted);
}

.ca-education p {
  margin: 0 0 12px;
}

.ca-education-title {
  margin: 0 0 8px;
  font-size: 1rem;
  color: var(--circuit-text);
}

/* ---------- zaliczenie poziomu ---------- */

.ca-board-stage .ca-sparks {
  position: absolute;
  inset: 0;
  z-index: 4;
  overflow: hidden;
  pointer-events: none;
}

.ca-spark {
  position: absolute;
  width: var(--size, 6px);
  height: var(--size, 6px);
  margin: calc(var(--size, 6px) / -2);
  border-radius: 50%;
  background: var(--circuit-spark);
  box-shadow: 0 0 8px var(--circuit-spark);
  opacity: 0;
  animation: ca-spark 900ms cubic-bezier(0.2, 0.7, 0.3, 1) forwards;
  animation-delay: var(--delay, 0ms);
}

.ca-board-stage.ca-celebrating .joint-link.is-live [joint-selector='line'] {
  stroke-width: 5;
  animation-duration: 0.32s;
}

.ca-board-stage.ca-celebrating .joint-link.is-live [joint-selector='bundle'] {
  stroke-width: 5;
  animation-duration: 0.32s;
}

.ca-board-stage.ca-celebrating .joint-type-ca-bulb.is-lit [joint-selector='glow'] {
  animation: ca-bulb-flash 900ms ease-out 2;
}

.ca-board-stage.ca-celebrating .joint-type-ca-led.is-lit [joint-selector='triangle'] {
  animation: ca-led-flash 900ms ease-out 2;
}

.ca-complete {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 6;
  width: min(360px, calc(100% - 32px));
  padding: 20px 22px 18px;
  border: 1px solid var(--circuit-success);
  border-radius: 18px;
  background: var(--circuit-card-bg);
  box-shadow: var(--circuit-shadow);
  text-align: center;
  transform: translate(-50%, -50%);
  animation: ca-card-in 320ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

.ca-complete-badge svg {
  display: block;
  width: 56px;
  height: 56px;
  margin: 0 auto 6px;
}

.ca-complete-badge circle {
  fill: none;
  stroke: var(--circuit-success);
  stroke-width: 2.5;
  opacity: 0.45;
}

.ca-complete-badge path {
  fill: none;
  stroke: var(--circuit-success);
  stroke-width: 4;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 44;
  stroke-dashoffset: 44;
  animation: ca-check 520ms 180ms ease-out forwards;
}

.ca-complete-title {
  margin: 0 0 2px;
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--circuit-success);
}

.ca-complete-sub {
  margin: 0 0 14px;
  font-size: 0.9rem;
  color: var(--circuit-muted);
}

.ca-complete-note {
  margin: 0 0 14px;
  font-size: 0.85rem;
  color: var(--circuit-muted);
}

.ca-complete-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
}

/* Przycisk "Nastepna lekcja" ma strzalke z Font Awesome (motyw laduje FA6),
   wiec ukladamy tekst i ikone w rzedzie z malym odstepem. */
.ca-complete-next {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.ca-complete-next .fa-solid {
  font-size: 0.95em;
}

/* ---------- przepiecie ---------- */

/* Karta porazki dziedziczy forme karty zaliczenia (klasy `ca-complete*`) i
   zmienia tylko to, co musi: barwy zagrozenia, wykrzyknik zamiast haczyka
   i jedna akcje - "Od nowa". */
.ca-complete.ca-fail {
  z-index: 7;
  border-color: var(--circuit-danger);
  animation: ca-card-in 320ms cubic-bezier(0.2, 0.8, 0.2, 1), ca-fail-shake 440ms 140ms ease-out;
}

.ca-fail .ca-complete-title {
  color: var(--circuit-danger);
}

.ca-fail .ca-complete-badge circle {
  stroke: var(--circuit-danger);
}

/* Wykrzyknik to krotkie kreski: pojawiaja sie razem z odznaka, a nie kresla
   sie animacja haczyka (ta ma dlugosc sciezki znaczka zaliczenia). */
.ca-fail .ca-complete-badge path {
  stroke: var(--circuit-danger);
  stroke-dasharray: none;
  stroke-dashoffset: 0;
  animation: none;
}

.ca-fail .ca-complete-badge svg {
  animation: ca-fail-badge 420ms 100ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}

/* Nakladka blokuje plansze: jedyna droga dalej jest przycisk karty. Iskry
   wybuchu leza nad nia, ale pod sama karta. */
.ca-fail-backdrop {
  position: absolute;
  inset: 0;
  z-index: 5;
  background: radial-gradient(circle at 50% 50%, rgba(200, 66, 66, 0.22), rgba(0, 0, 0, 0) 62%);
}

.ca-board-stage.ca-failing .ca-sparks {
  z-index: 6;
}

/* Wybuch: blysk w miejscu palonego elementu i wstrzas planszy */
.ca-board-stage.ca-boom {
  animation: ca-boom-shake 380ms ease-out;
}

.ca-boom-flash {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  background: radial-gradient(
    circle at var(--ca-boom-x, 50%) var(--ca-boom-y, 50%),
    rgba(255, 236, 170, 0.9),
    rgba(255, 150, 60, 0.45) 30%,
    rgba(200, 66, 66, 0) 66%
  );
  animation: ca-boom-flash 620ms ease-out forwards;
}

@keyframes ca-fail-badge {
  from {
    opacity: 0;
    transform: scale(0.72);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes ca-fail-shake {
  0%,
  100% {
    transform: translate(-50%, -50%);
  }
  20% {
    transform: translate(calc(-50% - 5px), -50%);
  }
  40% {
    transform: translate(calc(-50% + 5px), -50%);
  }
  60% {
    transform: translate(calc(-50% - 3px), -50%);
  }
  80% {
    transform: translate(calc(-50% + 3px), -50%);
  }
}

@keyframes ca-boom-flash {
  0% {
    opacity: 0;
  }
  12% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

@keyframes ca-boom-shake {
  0%,
  100% {
    transform: translateX(0);
  }
  15% {
    transform: translateX(-6px) rotate(-0.3deg);
  }
  30% {
    transform: translateX(5px) rotate(0.3deg);
  }
  45% {
    transform: translateX(-4px);
  }
  60% {
    transform: translateX(3px);
  }
  80% {
    transform: translateX(-2px);
  }
}

@keyframes ca-strip-flicker {
  0%,
  100% {
    opacity: 1;
  }
  35% {
    opacity: 0.2;
  }
  55% {
    opacity: 0.85;
  }
  75% {
    opacity: 0.35;
  }
}

/* ---------- animations ---------- */

@keyframes ca-current {
  from {
    stroke-dashoffset: 22;
  }
  to {
    stroke-dashoffset: 0;
  }
}

@keyframes ca-glow-pulse {
  0%,
  100% {
    opacity: 0.75;
  }
  50% {
    opacity: 0.98;
  }
}

@keyframes ca-spark {
  0% {
    opacity: 0;
    transform: translate(0, 0) scale(0.4);
  }
  15% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate(var(--dx, 0), var(--dy, 0)) scale(1.05);
  }
}

@keyframes ca-bulb-flash {
  0% {
    opacity: 0.85;
    transform: scale(1);
  }
  45% {
    opacity: 1;
    transform: scale(1.16);
  }
  100% {
    opacity: 0.9;
    transform: scale(1);
  }
}

@keyframes ca-led-flash {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.35;
  }
}

@keyframes ca-card-in {
  from {
    opacity: 0;
    transform: translate(-50%, -46%) scale(0.96);
  }
  to {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1);
  }
}

/* Podpowiedz: fade in + wsuniecie + pojedynczy rozblysk ramki (przy braku
   wsparcia dla `color-mix` zostaje sam fade in i wsuniecie). */
@keyframes ca-hint-in {
  0% {
    opacity: 0;
    transform: translateY(-6px);
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--circuit-accent) 35%, transparent);
  }
  40% {
    opacity: 1;
    transform: translateY(0);
    box-shadow: 0 0 20px 2px color-mix(in srgb, var(--circuit-accent) 35%, transparent);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--circuit-accent) 0%, transparent);
  }
}

@keyframes ca-hint-text-in {
  from {
    opacity: 0;
    transform: translateY(4px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes ca-check {
  to {
    stroke-dashoffset: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .joint-link.is-live [joint-selector='line'],
  .joint-type-ca-bulb.is-lit [joint-selector='glow'],
  .ca-board-stage.ca-celebrating .joint-link.is-live [joint-selector='line'],
  .ca-board-stage.ca-celebrating .joint-type-ca-bulb.is-lit [joint-selector='glow'],
  .ca-board-stage.ca-celebrating .joint-type-ca-led.is-lit [joint-selector='triangle'],
  .ca-complete,
  .ca-complete-badge path,
  .ca-probe,
  .ca-meter,
  .joint-link [joint-selector='bundle'],
  .ca-hint,
  .ca-hint > * {
    animation: none;
  }

  .ca-board-stage > .ca-inspector {
    transition: none;
    transform: none;
  }

  .ca-complete-badge path {
    stroke-dashoffset: 0;
  }

  .ca-sparks {
    display: none;
  }

  /* przepiecie: bez migotania, blysku i wstrzasu - zostaje sam komunikat */
  .ca-complete.ca-fail,
  .ca-fail .ca-complete-badge svg,
  .ca-board-stage.ca-boom,
  .ca-boom-flash,
  .joint-type-ca-ledstrip.is-overload-flicker [joint-selector='tape'] {
    animation: none;
  }

  /* przycisk wymaganego miernika: bez pulsu, ale z widoczną obwódką */
  .ca-btn-meter.is-required {
    animation: none;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--circuit-accent) 22%, transparent);
  }
}

/* ---------- mobile ---------- */

@media (max-width: 860px) {
  .ca-work {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Rezerwa na pas przewijania strony. Siedzi na kolumnie planszy, a nie na
     .ca-work: paleta zostaje szeroka jak reszta tresci, zweza sie tylko plansza
     (pas nalezy do jej kolumny i przewija strone, nie plansze). */
  .ca-stage {
    padding-right: var(--ca-scroll-gutter);
  }

  /* elementy trafiaja nad plansze: na pustej planszy od razu widac, co dodac */
  .ca-palette {
    order: 0;
  }

  .ca-parts {
    flex-direction: row;
    overflow-x: auto;
    padding: 4px 0;
    -webkit-overflow-scrolling: touch;
  }

  .ca-part {
    flex: 0 0 auto;
    min-width: 132px;
    /* Na mobile elementy dodaje sie dotknieciem, a pasek ma sie przewijac
       palcem - bez tego gest przewijania zjadalby przeciaganie z palety. */
    touch-action: pan-x;
  }

  /* Przy kilku lekcjach pigulki przewijaja sie w poziomie zamiast rosnac w pionie */
  .ca-tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    padding-bottom: 4px;
    -webkit-overflow-scrolling: touch;
  }

  .ca-tab {
    flex: 0 0 auto;
  }

  .ca-board-stage {
    height: clamp(360px, 58vh, 520px);
  }

  .joint-port-body [joint-selector='portHit'] {
    r: 24;
  }

  /* Wyswietlacz miernika na waskim ekranie: mniejszy, ale nadal w rogu. */
  .ca-meter {
    top: 8px;
    right: 8px;
    padding: 6px 8px 8px;
  }

  .ca-meter-value {
    font-size: 1.1rem;
  }

  .ca-bar {
    align-items: stretch;
  }

  .ca-actions {
    width: 100%;
    margin-left: 0;
  }

  .ca-btn {
    flex: 1 1 0;
  }
}


/* =========================================================================
   Server rendered parts (WordPress page template + shortcode)
   ========================================================================= */

.ca-page {
  margin: 0 auto;
  /* Akademia korzysta z pelnej szerokosci jak reszta sklepu; miary tekstu
     trzymaja osobne limity w znakach na akapitach. */
  padding: 8px clamp(16px, 1.5vw, 32px) 48px;
}

/*
 * Hub: hero jest pelnokrwisty.
 *
 * Padding strony i gorny margines `.ca-app` wcinaja tlo hero o 24-32 px od
 * krawedzi, przez co widac jego odciecie. Wyjatkowo dla hubu zdejmujemy oba,
 * a wciecia boczne wracaja na katalog (`.ca-lesson-list`). Odstep hero <->
 * katalog zostaje bez zmian: `.ca-app` trzyma swoj `gap`, a lista swoj wlasny
 * `margin-top`, dokladnie tak jak przed zmiana. Dolny rytm (48 px strony
 * + `margin-bottom` aplikacji) tez zostaje, bo niczego nie odcina.
 *
 * Lekcje nie maja `ca-page--hub`, wiec ich uklad jest nietkniety - tak samo jak
 * regula `@media (max-width: 1200px)` dla `.ca-app`.
 */
.ca-page--hub {
  padding-top: 0;
  padding-inline: 0;
}

.ca-page--hub > .ca-app.ca-app--hub {
  margin-top: 0;
  /*
   * Siatka dwoch kolumn (plansza + panel boczny) jest ukladam lekcji i wlacza sie
   * od 1400 px. Hub ma inna tresc, wiec ukladamy go jawnie jako jedna kolumne -
   * na wypadek, gdyby na stronie lezala jeszcze wygenerowana kopia arkusza bez
   * warunku `:not(.ca-app--hub)` (np. inline critical CSS z cache).
   */
  display: flex;
  flex-direction: column;
  gap: var(--circuit-gap);
}

.ca-page--hub > .ca-app.ca-app--hub > .ca-lesson-list {
  padding-inline: clamp(16px, 1.5vw, 32px);
}

/* Boczna kolumna lekcji (edukacja + FAQ). Ponizej 1400 px to zwykly stos,
   powyzej - druga kolumna siatki `.ca-page > .ca-app`. */
.ca-aside {
  display: flex;
  flex-direction: column;
  gap: var(--circuit-gap);
}

.ca-btn.ca-demo-replay:hover {
    color: #feb900;
}

/* Link powrotu do hubu Akademii. Na desktopie PHP wklada go do primary-menu
   (patrz `.ca-back-item` w bloku ponizej), a na mobile ma go przycisk
   `.ca-hub-btn`. Tu sa tylko wspolne podstawy wygladu. */
.ca-back {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 14px 7px 10px;
  border: 1px solid var(--border-color, rgba(127, 127, 127, 0.3));
  background: var(--circuit-part-bg);
  color: inherit;
  font-size: 0.9rem;
  text-decoration: none;
  transition: border-color 0.2s ease, color 0.2s ease;
}

.ca-back:hover,
.ca-back:focus-visible {
  border-color: var(--circuit-accent);
  color: var(--circuit-accent);
}

.ca-back-icon {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
}
/* -------------------------------------------------------------------------
   Pasek lekcji (desktop) i przycisk powrotu (mobile)
   ---------------------------------------------------------------------- */

/* Desktop: przyklejony slider z lekcjami. Mobile: ukryty - tam powrot do hubu
   daje osobny przycisk w stylu `.goled-category-bar-btn`. */
.ca-lesson-bar,
.ca-hub-bar {
  display: none;
}

/* Elementy paska nie moga zalezec od globalnego box-sizing motywu. */
.ca-lesson-bar,
.ca-lesson-bar *,
.ca-hub-bar,
.ca-hub-bar * {
  box-sizing: border-box;
}

/* Rozmiar ikony obowiazuje i w pasku desktopowym, i w mobilnym przycisku. */
.ca-lesson-bar-icon {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
}

@media (min-width: 1201px) {
  .ca-lesson-bar {
    position: sticky;
    top: var(--ca-bar-top, 0px);
    z-index: 900;
    display: flex;
    align-items: center;
    gap: 6px;
    border-bottom: 1px solid var(--border-color, rgba(127, 127, 127, 0.28));
    background: var(--bg-color, #111111);
    color: var(--text-color, inherit);
    font-size: 0.95rem;
  }

  /* Jezyk hoveru z naglowka motywu: delikatne tlo i akcent, bez ramek. */
  .ca-lesson-bar .ca-tab,
  .ca-lesson-bar-arrow {
    border: 0;
    background: transparent;
    color: inherit;
    box-shadow: none;
    transition: background 0.2s ease, color 0.2s ease;
  }

  .ca-lesson-bar .ca-tab:hover,
  .ca-lesson-bar .ca-tab:focus-visible,
  .ca-lesson-bar-arrow:hover:not(:disabled),
  .ca-lesson-bar-arrow:focus-visible,
  .ca-lesson-bar-arrow:focus {
    background: var(--search-bg, rgba(255, 255, 255, 0.05)) !important;
    color: var(--primary-color, #feb900) !important;
  }

  /* Link powrotu siedzi w primary-menu (dokłada go PHP), wiec pasek lekcji nie
     rezerwuje na niego miejsca i tor z lekcjami ma cala jego szerokosc. Styl
     przejmuje metryki pozycji menu: uppercase, 0.95rem, padding 15px 20px,
     hover w akcencie na `--search-bg`. */
  .main-nav {
    position: relative;
  }

  .ca-back-item {
    position: absolute;
    top: 50%;
    left: 20px;
    transform: translateY(-50%) translateX(-25px);
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .ca-back-item .ca-back {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 15px 20px;
    border: 0;
    background: transparent;
    color: var(--text-color, inherit);
    font-size: 0.95rem;
    font-weight: 500;
    letter-spacing: 1px;
    text-transform: uppercase;
    text-decoration: none;
    transition: color 0.3s ease, background 0.2s ease;
  }

  .ca-back-item .ca-back:hover,
  .ca-back-item .ca-back:focus-visible {
    background: var(--search-bg, rgba(255, 255, 255, 0.05));
    color: var(--primary-color, #feb900);
  }

  .ca-lesson-bar-track {
    position: relative;
    /* Gdy lekcje sie mieszcza, tor jest szerokosci tresci i wysrodkowany jak
       nawigacja motywu; gdy sie nie mieszcza, zweza sie do szerokosci paska i
       przewija. `justify-content: center` odpadło - przy przeladowanym torze
       nie da sie wtedy doscrollowac do poczatku listy. */
    flex: 0 1 auto;
    margin-inline: auto;
    min-width: 0;
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }

  .ca-lesson-bar-track::-webkit-scrollbar {
    display: none;
  }

  .ca-lesson-bar-track:focus-visible {
    outline: 2px solid var(--primary-color, #feb900);
    outline-offset: 2px;
    border-radius: 8px;
  }

  /* Bloczki maja naturalna szerokosc tekstu i staly padding - rowna szerokosc
     (wyliczana z najszerszej lekcji) okazala sie meczaca dla oka. */
  .ca-lesson-bar .ca-tab {
    flex: 0 0 auto;
    padding: 12px 18px;
    font-size: 0.95rem;
    text-align: center;
    white-space: nowrap;
    scroll-snap-align: center;
  }

  /* Aktualna lekcja: subtelnie (delikatne tlo + akcent + pogrubienie),
     bez pelnego wypelnienia, bo pasek widac caly czas. */
  .ca-lesson-bar .ca-tab.is-active,
  .ca-lesson-bar .ca-tab[aria-current='page'] {
    background: color-mix(in srgb, var(--primary-color, #feb900) 16%, transparent);
    color: var(--primary-color, #feb900);
    font-weight: 600;
  }

  .ca-lesson-bar-arrow {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 50px;
    height: 50px;
    padding: 0;
    cursor: pointer;
    transition: background 0.2s ease, color 0.2s ease, opacity 0.2s ease;
    border-radius: 0;
  }

  .ca-lesson-bar-arrow:disabled {
    opacity: 0.3;
    cursor: default;
    background: initial;
  }

  /* Gdy wszystkie lekcje sie mieszcza, strzalki sa zbedne */
  .ca-lesson-bar:not(.is-scrollable) .ca-lesson-bar-arrow {
    display: none;
  }

}

/* Mobile / tablet: sam przycisk powrotu do hubu, przyklejony pod mobilnym
   headerem (70 px), w stylu paska kategorii sklepu. */
@media (max-width: 1200px) {
  .ca-hub-bar {
    display: block;
    position: sticky;
    top: 70px;
    z-index: 100;
    background: var(--card-bg, rgba(127, 127, 127, 0.08));
    border-bottom: 1px solid var(--border-color, rgba(127, 127, 127, 0.28));
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  }

  .ca-hub-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 14px 16px;
    background: transparent;
    border: none;
    color: var(--text-color, inherit);
    font-size: 0.95rem;
    font-weight: 500;
    text-decoration: none;
    transition: background 0.2s ease, color 0.2s ease;
  }

  .ca-hub-btn:hover,
  .ca-hub-btn:focus-visible {
    background: var(--input-bg, rgba(127, 127, 127, 0.12));
    color: var(--primary-color, #feb900);
  }
}


.ca-hero h1 {
  margin: 0 0 8px;
}

.ca-hero p {
  margin: 0 0 8px;
  max-width: 72ch;
  color: var(--circuit-muted);
}

.ca-tabs a.ca-tab {
  text-decoration: none;
}

.ca-embed-title {
  margin: 0 0 4px;
}

.ca-embed-desc {
  margin: 0 0 12px;
  color: var(--circuit-muted);
}

/* Notka bezpieczenstwa dla lekcji z napieciem sieciowym (dane lekcji: mains).
   Lekka, ale czytelna: wyciszony tekst z cienkim paskiem ostrzezenia. */
.ca-safety-note {
  padding: 10px 0 10px 14px;
  border-left: 2px solid var(--circuit-danger, #c84242);
  font-size: 0.84rem;
  line-height: 1.6;
  color: var(--circuit-muted, rgba(128, 128, 128, 0.85));
}

.ca-education-box,
.ca-faq {
  padding: 18px 20px;
  border: 1px solid var(--border-color, rgba(127, 127, 127, 0.28));
  border-radius: var(--circuit-radius, 14px);
  background: var(--circuit-part-bg, rgba(127, 127, 127, 0.06));
  line-height: 1.65;
}

.ca-faq {
  margin-top: 0 !important;
}

.ca-education-heading,
.ca-faq-heading {
  margin: 0 0 10px;
  font-size: 1.05rem;
}

.ca-education-box p {
  margin: 0 0 12px;
  color: var(--circuit-muted, rgba(128, 128, 128, 0.85));
}

.ca-education-box p:last-child {
  margin-bottom: 0;
}

.ca-faq-item {
  border-top: 1px solid var(--border-color, rgba(127, 127, 127, 0.2));
  padding: 10px 0;
}

.ca-faq-item:first-of-type {
  border-top: 0;
}

.ca-faq-item summary {
  cursor: pointer;
  font-weight: 600;
}

.ca-faq-item p {
  margin: 8px 0 0;
  color: var(--circuit-muted, rgba(128, 128, 128, 0.85));
}

.ca-lesson-list {
  margin-top: 28px;
}

.ca-lesson-list h2 {
  margin: 0 0 12px;
  font-size: 1.15rem;
}

.ca-lesson-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
  gap: 14px;
}

.ca-lesson-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  gap: 6px 14px;
  padding: 16px 18px;
  border: 1px solid var(--border-color, rgba(127, 127, 127, 0.28));
  border-radius: var(--circuit-radius, 14px);
  background: var(--circuit-part-bg, rgba(127, 127, 127, 0.06));
  text-decoration: none;
  color: inherit;
  transition: border-color 0.2s ease, transform 0.15s ease;
}

.ca-lesson-card:hover {
  border-color: var(--circuit-accent, #feb900);
  transform: translateY(-2px);
}

.ca-lesson-card.is-active {
  border-color: var(--circuit-accent, #feb900);
}

.ca-lesson-card strong {
  display: block;
  margin-bottom: 6px;
}

.ca-lesson-num {
  grid-row: 1 / span 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 34px;
  padding: 4px 6px;
  border: 1px solid var(--border-color, rgba(127, 127, 127, 0.3));
  border-radius: var(--circuit-radius-sm, 10px);
  background: var(--circuit-part-bg, rgba(127, 127, 127, 0.06));
  color: var(--circuit-accent, #feb900);
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.04em;
}

.ca-lesson-card.is-active .ca-lesson-num {
  border-color: var(--circuit-accent, #feb900);
}

.ca-lesson-desc {
  font-size: 0.88rem;
  color: var(--circuit-muted, rgba(128, 128, 128, 0.85));
}


/* ---------- warstwa wiedzy pod aplikacja (SEO/AEO) ---------- */

.ca-knowledge-title {
  margin: 0 0 12px 20px;
  font-size: 1.15rem;
}

.ca-knowledge-lead {
  margin: 0 0 12px 20px;
  max-width: 78ch;
  color: var(--circuit-muted, rgba(128, 128, 128, 0.85));
}

.ca-knowledge-lead:last-of-type{
  margin-bottom: 24px;
}

.ca-knowledge-heading {
  margin: 0 0 8px;
  font-size: 1.02rem;
}

.ca-knowledge-list {
  margin: 0;
  padding-left: 22px;
  color: var(--circuit-muted, rgba(128, 128, 128, 0.85));
}

.ca-knowledge-list li {
  margin-bottom: 6px;
}

.ca-knowledge-list li:last-child {
  margin-bottom: 0;
}

.ca-knowledge-terms {
  margin: 0;
  color: var(--circuit-muted, rgba(128, 128, 128, 0.85));
}

.ca-knowledge-terms dt {
  margin-top: 10px;
  color: var(--circuit-text);
  font-weight: 600;
}

.ca-knowledge-terms dt:first-child {
  margin-top: 0;
}

.ca-knowledge-terms dd {
  margin: 2px 0 0;
}

/* Tabela porownawcza: naglowek wiersza jest nazwa przypadku z danych. */
.ca-knowledge-table {
  width: 100%;
  margin: 0;
  border-collapse: collapse;
  font-size: 0.92rem;
}

.ca-knowledge-table th,
.ca-knowledge-table td {
  padding: 10px 12px;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--border-color, rgba(127, 127, 127, 0.2));
}

.ca-knowledge-table thead th {
  border-bottom-color: var(--circuit-accent, #feb900);
  color: var(--circuit-text);
}

.ca-knowledge-table tbody th {
  color: var(--circuit-text);
  font-weight: 600;
}

.ca-knowledge-table td {
  color: var(--circuit-muted, rgba(128, 128, 128, 0.85));
}

.ca-knowledge-qa {
  margin-bottom: 14px;
}

.ca-knowledge-qa:last-child {
  margin-bottom: 0;
}

.ca-knowledge-qa h4 {
  margin: 0 0 4px;
  font-size: 0.98rem;
}

.ca-knowledge-qa p {
  margin: 0;
  color: var(--circuit-muted, rgba(128, 128, 128, 0.85));
}

.ca-knowledge-figure {
  margin: 0 0 20px;
}

.ca-knowledge-figure img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--border-color, rgba(127, 127, 127, 0.28));
  border-radius: var(--circuit-radius, 14px);
}

.ca-knowledge-figure figcaption {
  margin-top: 8px;
  font-size: 0.85rem;
  color: var(--circuit-muted, rgba(128, 128, 128, 0.85));
}

/* =========================================================================
   Wspolny komponent accordionow (.ca-acc): warstwa wiedzy, rozwiazanie
   i zwijana lista lekcji. To wylacznie prezentacja - cala tresc renderuje PHP
   i jest w DOM od pierwszego zadania (bez JS). Tla, ramki i akcent pochodza
   z tokenow motywu, wiec dziala tak samo w trybie ciemnym i jasnym.
   ========================================================================= */

.ca-acc {
  margin: 0 0 6px;
  background: rgba(127, 127, 127, 0.05);
  border: 1px solid var(--border-color, rgba(127, 127, 127, 0.22));
  border-radius: var(--circuit-radius-sm, 10px);
  transition: border-color 0.2s ease;
}

.ca-acc:last-child {
  margin-bottom: 0;
}

.ca-acc > summary {
  display: flex;
  align-items: center;
  gap: 12px;
  /* <summary> liczy sie jak content-box (UA), wiec min-height dodawalby sie do
     paddingu i wiersz robilby sie o ~26 px wyzszy. Ustawiamy box model wprost
     w komponencie, niezaleznie od globalnych reguł motywu. */
  box-sizing: border-box;
  min-height: 52px;
  padding: 15px 18px;
  border-radius: calc(var(--circuit-radius-sm, 10px) - 1px);
  color: var(--circuit-text);
  cursor: pointer;
  list-style: none;
  transition: background-color 0.2s ease;
}

.ca-acc > summary::-webkit-details-marker {
  display: none;
}

.ca-acc > summary:hover {
  background: rgba(127, 127, 127, 0.04);
}

.ca-acc > summary:focus-visible {
  outline: 2px solid var(--circuit-accent, #feb900);
  outline-offset: 2px;
}

/* Zloty akcent jako detal: tint ramki przy najechaniu i w stanie otwartym. */
.ca-acc:hover,
.ca-acc[open] {
  border-color: color-mix(in srgb, var(--circuit-accent, #feb900) 40%, var(--border-color, rgba(127, 127, 127, 0.22)));
}

.ca-acc-title,
.ca-acc > summary > .ca-knowledge-heading {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
}

.ca-acc-meta {
  flex: 0 0 auto;
  font-size: 0.78rem;
  font-weight: 400;
  color: var(--circuit-muted, rgba(128, 128, 128, 0.85));
}

.ca-acc-icon {
  display: inline-flex;
  flex: 0 0 auto;
  color: var(--circuit-muted, rgba(128, 128, 128, 0.85));
  transition: color 0.2s ease;
}

.ca-acc-icon::before {
  content: '';
  display: block;
  width: 7px;
  height: 7px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform 0.2s ease;
}

.ca-acc[open] .ca-acc-icon::before {
  transform: rotate(-135deg);
}

.ca-acc[open] .ca-acc-icon {
  color: var(--circuit-accent, #feb900);
}

.ca-acc-body {
  padding: 0 18px 18px;
}

.ca-acc-body .ca-knowledge-figure {
  margin: 0;
}

/* Ostatni wiersz wiedzy: gotowy uklad. Swiadomie odrobine mocniejszy sygnal,
   bo to decyzja uzytkownika i celowy spoiler. */
.ca-acc--solution {
  border-color: color-mix(in srgb, var(--circuit-accent, #feb900) 30%, var(--border-color, rgba(127, 127, 127, 0.22)));
}

.ca-acc--solution[open] {
  border-color: color-mix(in srgb, var(--circuit-accent, #feb900) 55%, var(--border-color, rgba(127, 127, 127, 0.22)));
}

.ca-acc-lead-icon {
  display: inline-flex;
  flex: 0 0 auto;
  color: var(--circuit-muted, rgba(128, 128, 128, 0.85));
  font-size: 0.95rem;
  transition: color 0.2s ease;
}

.ca-acc--solution:hover .ca-acc-lead-icon,
.ca-acc--solution[open] .ca-acc-lead-icon {
  color: var(--circuit-accent, #feb900);
}

/* Zwijana pelna lista lekcji na stronie lekcji: ten sam wiersz, a sama siatka
   dostaje padding (bez dodatkowego wrappera). */
.ca-acc--lessons-more {
  margin-top: 28px;
}

.ca-acc--lessons-more > .ca-lesson-grid {
  padding: 0 18px 18px;
}

@media (max-width: 480px) {
  .ca-acc > summary {
    gap: 10px;
    padding: 13px 14px;
  }

  .ca-acc-body,
  .ca-acc--lessons-more > .ca-lesson-grid {
    padding: 0 14px 14px;
  }

  /* Podpis "Schemat ukladu" nie miesci sie obok tytulu i chevronu - na
     najwezszych ekranach go chowamy; znaczenie niesie ikona oka. */
  .ca-acc-meta {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ca-acc,
  .ca-acc > summary,
  .ca-acc-icon,
  .ca-acc-icon::before,
  .ca-acc-lead-icon {
    transition: none;
  }
}

/* Katalog hubu w grupach tematycznych (grupy nie maja wlasnych adresow) */
.ca-lesson-group {
  margin-top: 22px;
}

.ca-lesson-group-title {
  margin: 0 0 4px;
  font-size: 1.02rem;
}

.ca-lesson-group-intro {
  margin: 0 0 12px;
  max-width: 78ch;
  color: var(--circuit-muted, rgba(128, 128, 128, 0.85));
}

/* Powiazane lekcje: opisowy anchor + nazwa lekcji obok */
.ca-related {
  margin-top: 28px;
}

.ca-related h2 {
  margin: 0 0 12px;
  font-size: 1.15rem;
}

.ca-related-list {
  display: grid;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ca-related-list li {
  display: grid;
  gap: 2px;
}

.ca-related-list a {
  color: var(--circuit-accent, #feb900);
  text-decoration: none;
}

.ca-related-list a:hover {
  text-decoration: underline;
}

.ca-related-lesson {
  font-size: 0.85rem;
  color: var(--circuit-muted, rgba(128, 128, 128, 0.85));
}

/* Nawigacja kursu: poprzednia i nastepna lekcja jako linki */
.ca-prevnext {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(156px, 1fr));
  gap: 12px;
  margin-top: 28px;
}

.ca-prevnext-link {
  display: grid;
  gap: 4px;
  padding: 14px 16px;
  border: 1px solid var(--border-color, rgba(127, 127, 127, 0.28));
  border-radius: var(--circuit-radius, 14px);
  background: var(--circuit-part-bg, rgba(127, 127, 127, 0.06));
  text-decoration: none;
  color: inherit;
  transition: border-color 0.2s ease;
}

.ca-prevnext-link:hover {
  border-color: var(--circuit-accent, #feb900);
}

.ca-prevnext-link--next {
  text-align: right;
}

.ca-prevnext-label {
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--circuit-muted, rgba(128, 128, 128, 0.85));
}

/* =========================================================================
   Szerokie ekrany (1920 px i wiecej)
   ========================================================================= */

@media (min-width: 1400px) {
  /* Lekcja: plansza z paleta w glownej kolumnie, edukacja i FAQ z boku.
     Hub ma ten sam uklad `.ca-page > .ca-app`, ale to inna tresc - hero, demo
     i katalog ida jedna kolumna - wiec siatka rozjezdzalaby mu hero (dwa
     pierwsze elementy trafialyby do kolumn obok siebie). Wyjatek po klasie,
     ktora PHP nadaje wylacznie hubowi (`goled_ca_render_hub()`). */
  .ca-page > .ca-app:not(.ca-app--hub) {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(340px, 440px);
    column-gap: var(--circuit-gap);
  }

  .ca-page > .ca-app > .ca-head,
  .ca-page > .ca-app > .ca-lesson-list,
  .ca-page > .ca-app > .ca-knowledge,
  .ca-page > .ca-app > .ca-related,
  .ca-page > .ca-app > .ca-prevnext,
  .ca-page > .ca-app > .ca-lessons-more {
    grid-column: 1 / -1;
  }

  /* Tresc wpisana w edytorze hubu tez zajmuje cala szerokosc, a akapity
     trzymaja miare czytania jak pozostale teksty Akademii. */
  .ca-page > .ca-app > .ca-education-box {
    grid-column: 1 / -1;
  }

  .ca-page > .ca-app > .ca-education-box > p {
    max-width: 90ch;
  }

  .ca-page > .ca-app > .ca-board-host {
    grid-column: 1;
  }

  .ca-page > .ca-app > .ca-aside {
    grid-column: 2;
    padding-top: 25px;
  }

  /* Wiecej miejsca w pionie, zeby powiekszony obwod mial gdzie urosnac. */
  .ca-board-stage {
    height: clamp(520px, 62vh, 780px);
  }

  /* Hub: trzy kolumny kart, jak na blogu. */
  .ca-lesson-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (min-width: 1900px) {
  .ca-page > .ca-app:not(.ca-app--hub) {
    grid-template-columns: minmax(0, 1fr) minmax(340px, 560px);
  }
}

@media (max-width: 1399px){
  .ca-knowledge-title{
    margin-top: 20px;
  }
}

@media (max-width: 1200px){
  .ca-app:has(.ca-hero) {
    margin-top: 70px;
    margin-bottom: 0;
    gap: 4px;
  }
}

@media (max-width: 760px) {
  .ca-lesson-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Wezsze telefony: rzad akcji dzieli sie rowno tylko wtedy, gdy najdluzsza
   etykieta ("Podpowiedz") zmiesci sie w swoim udziale - po odjeciu pasa
   przewijania strony brakowalo na to kilku pikseli. */
@media (max-width: 420px) {
  .ca-btn {
    font-size: 0.8rem;
    padding: 9px 10px;
  }
}

/* ---------- pas przewijania strony (waski ekran) ---------- */

/* Na telefonie plansza zajmuje prawie cala szerokosc, a jej obszar rysowania
   blokuje gesty (touch-action: none), wiec gest nad plansza nie przewija strony
   i nie da sie zjechac wyzej ani nizej. Pas lezy POZA ramka planszy (jest
   rodzenstwem .ca-board w .ca-stage), dlatego jego touch-action: pan-y dziala:
   przegladarka przewija strone natywnie, z bezwladnoscia, bez zadnej logiki
   gestow w JS. Rezerwe na pas robi .ca-stage w media query powyzej, wiec paleta
   zostaje szeroka, a zweza sie tylko kolumna planszy. */
.ca-scroll {
  position: absolute;
  top: 0;
  bottom: 0;
  /* Start dokladnie na prawej krawedzi planszy: rezerwe (padding-right) robi
     .ca-stage, wiec zawierajacy blok pasa jest o tyle szerszy od planszy. */
  left: calc(100% - var(--ca-scroll-gutter));
  /* Szerokosc ustawia App z realnej odleglosci od krawedzi okna (patrz
     AcademyApp.syncScrollRail), dzieki czemu pas obejmuje cala wolna przestrzen
     po prawej - razem z paddingiem .ca-page (clamp) i ewentualnym auto-marginesem.
     Wartosc ponizsza to tylko zapas na pierwsza klatke przed pomiarem. */
  width: var(--ca-scroll-width, calc(var(--ca-scroll-gutter) + 8px));
  display: none;
  touch-action: pan-y;
  z-index: 5;
}

/* Wskazowka wysrodkowana w pasie: cienka linia wygaszona na koncach, ktora
   delikatnie "oddycha" (pulsowanie krycia) - mowi, ze w tym miejscu mozna
   przewijac strone, nie udajac przy tym strzalki paska przewijania planszy. */
.ca-scroll-hint {
  position: absolute;
  left: 50%;
  top: 18px;
  bottom: 18px;
  width: 1px;
  transform: translateX(-50%);
  background: linear-gradient(
    to bottom,
    transparent,
    var(--circuit-muted) 22%,
    var(--circuit-muted) 78%,
    transparent
  );
  pointer-events: none;
  animation: ca-scroll-hint-breathe 2.6s ease-in-out infinite;
}

@keyframes ca-scroll-hint-breathe {
  0%,
  100% {
    opacity: 0.2;
  }
  50% {
    opacity: 0.5;
  }
}

/* Pas istnieje tylko w ukladzie waskim; na szerokim ekranie nie ma go w ukladzie
   (media query jest tutaj, a nie w sekcji mobile wyzej, bo regula bazowa stoi
   nizej w pliku i inaczej nadpisalaby display). */
@media (max-width: 860px) {
  .ca-scroll {
    display: block;
  }
}

/* ---------- pasek przewijania planszy ---------- */

/* Nakladka na obszarze rysowania: pojawia sie tylko po tej stronie, po ktorej
   obwod lekcji nie miesci sie w widoku. Lezy NAD plansza (a nie obok niej),
   wiec nie zabiera jej szerokosci - w odroznieniu od marginesu. Przewijanie
   jest wydzielone do paska, bo swobodne przesuwanie samej planszy konczyloby
   sie przypadkowym odjechaniem widoku podczas rysowania kabli. */
.ca-pan {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
}

.ca-pan-rail {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 32px;
  display: none;
  align-items: center;
  justify-content: center;
  pointer-events: auto;
  touch-action: none;
  cursor: grab;
}

.ca-pan.is-left .ca-pan-rail.ca-pan--left,
.ca-pan.is-right .ca-pan-rail.ca-pan--right {
  display: flex;
}

.ca-pan-rail.is-dragging {
  cursor: grabbing;
}

.ca-pan-rail.ca-pan--left {
  left: 0;
  background: linear-gradient(to right, var(--circuit-label-halo, #111111) 35%, transparent);
}

.ca-pan-rail.ca-pan--right {
  right: 0;
  background: linear-gradient(to left, var(--circuit-label-halo, #111111) 35%, transparent);
}

/* Strzalka: delikatna, wolno sunaca w strone ukrytej tresci */
.ca-pan-arrow {
  width: 9px;
  height: 9px;
  border-top: 2px solid var(--circuit-muted, rgba(128, 128, 128, 0.75));
  border-right: 2px solid var(--circuit-muted, rgba(128, 128, 128, 0.75));
}

.ca-pan-rail.ca-pan--left .ca-pan-arrow {
  transform: rotate(-135deg);
  animation: ca-pan-hint-left 2.4s ease-in-out infinite;
}

.ca-pan-rail.ca-pan--right .ca-pan-arrow {
  transform: rotate(45deg);
  animation: ca-pan-hint-right 2.4s ease-in-out infinite;
}

@keyframes ca-pan-hint-right {
  0%,
  100% {
    margin-left: -6px;
    opacity: 0.25;
  }
  50% {
    margin-left: 6px;
    opacity: 0.45;
  }
}

@keyframes ca-pan-hint-left {
  0%,
  100% {
    margin-left: 6px;
    opacity: 0.25;
  }
  50% {
    margin-left: -6px;
    opacity: 0.45;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ca-pan-rail.ca-pan--left .ca-pan-arrow,
  .ca-pan-rail.ca-pan--right .ca-pan-arrow,
  .ca-scroll-hint {
    animation: none;
    opacity: 0.35;
  }
}

/* ==========================================================================
   Hero hubu: tytul i demo na jednym ekranie

   Naglowek motywu nie jest znany w CSS, a wartosc ustawiona dopiero po
   pierwszym malowaniu (np. z JS) zmienialaby wysokosc hero, czyli wracalby CLS.
   Dlatego odejmujemy stala zmierzona na zywo - czyli pelna odleglosc od gornej
   krawedzi okna do hero, a nie sam naglowek: 183 px na desktopie (tyle ma
   naglowek motywu razem z belka gorna) i 113 px na waskich ekranach, gdzie
   motyw podmienia naglowek na mobilny przy 1200 px i zostaje sam jego offset.
   W obu liczbach nie ma juz paddingu `.ca-page` ani marginesu `.ca-app`, bo hub
   jest pelnokrwisty (patrz `ca-page--hub`). Jesli motyw zmieni naglowek,
   poprawiamy te dwie liczby - pilnuje tego pomiar CLS na zywo.
   ========================================================================== */

.ca-hub-hero {
  --ca-hero-top: 183px;
  /* rezerwa na blok tekstowy hero: obszar rysowania demo ustępuje mu miejsca,
     zeby naglowek + tytul + ramka trafialy w jeden ekran. Zmierzona tresc
     (kicker + H1 + lead): 177 px w szerokiej kolumnie z 67ch leadem, 214 px na
     390 px - rezerwa jest wieksza od pomiaru, bo musi pokryc takze chwile, gdy
     motyw renderuje jeszcze fontem zapasowym, a po podmianie fontu tekst rosnie.
     Nadwyzka nie marnuje sie: naglowek hero centruje tresc, wiec dzieli sie po
     polowie i to ona daje odstep nad kickerem - taki sam, jaki pas wylewu daje
     pod plansza (patrz --ca-demo-bleed). Na wezszych niz 460 px rezerwa idzie
     w gore, bo tam lead zawija sie do pieciu linii. */
  --ca-hero-title: 265px;
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* Hero bierze cala szerokosc (swiatlo w tle ma gdzie oddychac), ale tresc
     i demonstrator trzymaja jedna, wezsza kolumne - patrz `.ca-hub-hero > *`. */
  width: 100%;
  /*
   * Obcinamy na krawedzi hero, nie na ramce demo: pas przewijania strony
   * (`.ca-scroll`, 24 px) siega 8 px poza prawa krawedz widoku i musi byc
   * przyciety, a jednoczesnie rozmycie koncowki ma sie rozlac poza ramke demo.
   * Hero jest szeroki na caly ekran, wiec tu obciecie jest na krawedzi okna.
   */
  overflow: hidden;
  /* Rytm pionowy: tytul i demonstrator sa jedna grupa, wiec dzieli je mniejszy
     odstep niz otoczenie - caly oddech wokol kompozycji daje wysrodkowanie.
     Tej samej wartosci uzywa wzor obszaru rysowania (`--ca-demo-stage`), dzieki
     czemu rezerwa wysokosci ramki nadal rownowazy sie z wysokoscia po montazu. */
  --ca-hero-gap: clamp(16px, 2.2vh, 28px);
  gap: var(--ca-hero-gap);
  /* Jedno pole swiatla dla calej kompozycji: cieply blask pod plansza laczy ja
     z tytulem, a nad tytulem siedzi tylko sladowa poswiata. To sprawia, ze tekst
     i symulator czytaja sie jako jeden hero, a nie tekst + osobny prostokat.

     Wazne: kazdy gradient gasnie do zera PRZED krawedzia hero (gorny juz na
     samej gornej krawedzi, dolny okolo 47 px nad dolna), wiec zaden brzeg nie
     odcina swiatla widoczna linia. */
  background:
    radial-gradient(62% 40% at 50% 66%, color-mix(in srgb, var(--circuit-accent) 5%, transparent), transparent 74%),
    radial-gradient(96% 28% at 50% 18%, color-mix(in srgb, var(--circuit-accent) 5%, transparent), transparent 64%);
  /* `svh`, nie `dvh`: gdy na telefonie chowa sie pasek adresu, `dvh` zmienialoby
     wysokosc ramki demo i plansza przeliczalaby sie w trakcie przewijania. */
  min-height: calc(100vh - var(--ca-hero-top));
  min-height: calc(100svh - var(--ca-hero-top));
}

/* Typografia hero: tylko tutaj, naglowek lekcji zostaje bez zmian. */
.ca-hub-hero .ca-kicker {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 10.5px;
  letter-spacing: 0.24em;
  opacity: 0.85;
}

/* Krotka kreska przed kickerem - akcent bez jaskrawosci. */
.ca-hub-hero .ca-kicker::before {
  content: '';
  width: 22px;
  height: 1px;
  background: currentColor;
  opacity: 0.45;
}

.ca-hub-hero .ca-title {
  margin: 0;
  font-size: clamp(2.05rem, 1.28rem + 2.8vw, 3.6rem);
  line-height: 1.02;
  letter-spacing: -0.025em;
  text-wrap: balance;
  /* Cien nie jest dekoracja: tytul siedzi na polu swiatla, a bez niego krawedzie
     liter na jasniejszym tle traca kontrast. Wartosci celowo male. */
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.35), 0 2px 26px rgba(0, 0, 0, 0.55);
}

/* Opis jako lead o ograniczonej miarze; drugi akapit to wyciszona meta-linia,
   zeby caly blok dalo sie przeskanowac w 2-3 sekundy. */
.ca-hub-hero .ca-desc {
  margin: 0;
  max-width: 67ch;
  font-size: clamp(1rem, 0.94rem + 0.28vw, 1.15rem);
  line-height: 1.55;
  text-shadow: 0 1px 14px rgba(0, 0, 0, 0.5);
}

/* W hero demo jest ostatnim elementem i to `gap` oddziela je od tytulu, wiec
   dolny margines sekcji tylko przesuwalby wysrodkowanie i dodawal 26 px do
   wysokosci calosci (przez co hero nie miescilby sie w ekranie). */
.ca-hub-hero .ca-demo-section {
  margin-bottom: 0;
}

/* Miara czytania tresci wpisanej w edytorze hubu. Reguła dla szerokich ekranow
   celuje w `.ca-page > .ca-app > .ca-education-box`, a po opakowaniu w hero ten
   blok jest glebiej, wiec powtarzamy limit tutaj. */
.ca-hub-hero .ca-education-box > p {
  max-width: 90ch;
}

/* Ta sama rezerwa jest wysokoscia samego tytulu. Dzieki temu podmiana fontu
   motywu (tytul rosnie wtedy o kilkanascie pikseli) nie zmienia wysokosci
   ukladu, a skoro calosc jest wysrodkowana, ramka demo stoi w miejscu - bez
   tego tekst przesuwal ja o kilka pikseli i wracal CLS (ok. 0,008). */
.ca-hub-hero .ca-head {
  min-height: var(--ca-hero-title);
  /* Rezerwa jest wieksza od zmierzonej tresci, a nadwyzka rozlozona po polowie
     zamiast ladowac cala pod meta-linia - inaczej powiekszala odstep do demo. */
  justify-content: center;
  gap: 12px;
}

/* ---------- choreografia wejscia ---------- */

/* Krotka sekwencja przy pierwszym wejsciu: kicker, tytul, opis, demo. Animujemy
   wylacznie `opacity` i `transform`, wiec uklad (i rezerwa wysokosci) stoi od
   pierwszej klatki - to nie moze wprowadzic CLS. Demo startuje dopiero po
   zakonczeniu tego wjazdu (patrz `AUTO_DELAY_MS` w `demo.ts`). */
@keyframes ca-hero-in {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* Tytul wchodzi dodatkowo z lekkiego rozmycia: to jedyny element, ktory moze
   sobie na to pozwolic (jeden przebieg na wejsciu), a daje efekt ustawiania
   ostrosci - czytelnie premium bez ruchu, ktory przesuwalby uklad (blur nie
   zmienia geometrii, wiec rezerwa wysokosci i CLS zostaja bez zmian). */
@keyframes ca-hero-in-soft {
  from {
    opacity: 0;
    transform: translateY(12px);
    filter: blur(5px);
  }
  to {
    opacity: 1;
    transform: none;
    /* `none`, a nie `blur(0)`: po animacji nie zostaje zbedna warstwa
       kompozycji na tytule (fill-mode both utrzymuje wartosc koncowa). */
    filter: none;
  }
}

.ca-hub-hero .ca-kicker {
  animation: ca-hero-in 420ms cubic-bezier(0.2, 0.7, 0.3, 1) both;
}

.ca-hub-hero .ca-title {
  animation: ca-hero-in-soft 640ms cubic-bezier(0.2, 0.7, 0.3, 1) 110ms both;
}

.ca-hub-hero .ca-desc {
  animation: ca-hero-in 560ms cubic-bezier(0.2, 0.7, 0.3, 1) 240ms both;
}

/* Meta-linia wchodzi po leadzie, zeby blok czytal sie zdanie po zdaniu. */
.ca-hub-hero .ca-demo-section {
  animation: ca-hero-in 680ms cubic-bezier(0.2, 0.7, 0.3, 1) 400ms both;
}

@media (max-width: 1200px) {
  .ca-hub-hero {
    --ca-hero-top: 113px;
  }
}

@media (max-width: 860px) {
  /* Telefon i tablet: hero ma zmiescic sie w pierwszym ekranie (paleta, cala
     plansza i pasek krokow), wiec odstep tekst <-> demo jest mniejszy, a rezerwa
     naglowka schodzi z 265 px do zmierzonej tresci + zapas. Nadwyzka rezerwy
     byla widoczna jako pustka nad kickerem i pod akapitem - na desktopie jest
     zaplanowanym oddechem, na telefonie zjadalaby pol ekranu. */
  .ca-hub-hero {
    --ca-hero-gap: clamp(12px, 1.6vh, 20px);
    --ca-hero-title: 175px;
  }
}

@media (max-width: 460px) {
  .ca-hub-hero {
    /* Zmierzona tresc naglowka na 390 px szerokosci to 214 px - rezerwa zostaje
       buforem na podmiane fontu, ale bez 86 px pustki. */
    --ca-hero-title: 240px;
  }
}

/* ==========================================================================
   Demo na hubie (autopilot)

   Demo to prawdziwa instancja pierwszej lekcji. Ta warstwa dokłada jedynie
   ramke, kursor, podglad przeciaganego elementu, podglad prowadzonego przewodu
   i karte koncowa. Wszystkie pozycje liczy JS w runtime, wiec nie ma tu zadnych
   stalych odstepow zaleznych od ukladu.
   ========================================================================== */

.ca-demo-section {
  margin: 0 0 26px;
}

.ca-demo {
  /* Kolory koncowki demo. Ciemny motyw przygasza do czerni, jasny - do bieli:
     jedna zmienna na motyw, bo czarny welon i bialy tekst na jasnej stronie
     zostawialy szara plame i nieczytelny komunikat (patrz regula nizej). */
  --ca-demo-veil: #000000;
  --ca-demo-finish-text: #ffffff;
  /* Rezerwa wysokosci ramki demo: dokladnie tyle, ile zajmuje zamontowana
     aplikacja, zeby katalog pod plansza nie zjechal, gdy bundle dojdzie po
     pierwszym malowaniu (patrz sprawdzenie "rezerwa" w e2e/demo.mjs).

     Skladowe (box-sizing: border-box, wiec to pelna wysokosc calosci):
       2 ramki planszy (2) - demo nie ma juz wlasnej ramki ani paddingu, bo
       jest czescia hero (patrz nizej), a na waskich ekranach dochodzi kolumna
       elementow (110) i odstep (16).
     Do tego obszar rysowania i pasek krokow o stalej wysokosci. */
  /* Obszar rysowania rosnie z oknem, ale ustępuje miejsca naglowkowi motywu
     i tytulowi hero (`.ca-hub-hero`), zeby naglowek + tytul + ramka demo
     trafialy w jeden ekran, a katalog zaczynal sie pod zgieciem. Odejmujemy tez
     pas wylewu (--ca-demo-bleed), czyli odstep pod plansza: dzieki temu dol
     planszy nie dotyka zgiecia, tylko zostaje nad nim taki sam oddech, jaki
     tekst ma nad soba. Dolna granica na waskich ekranach to 300 px: ponizej niej
     zaciski przestaja byc klikalne palcem. Na szerokich moze byc nizsza, bo tam
     obwod obsluguje sie myszka. */
  --ca-demo-floor: 240px;
  --ca-demo-mobile-step: 0px;
  --ca-demo-stage: clamp(
    var(--ca-demo-floor),
    calc(100svh - var(--ca-hero-top, 0px) - var(--ca-hero-title, 0px) - var(--ca-hero-gap, var(--circuit-gap)) - var(--ca-demo-chrome) - var(--ca-demo-step) - var(--ca-demo-mobile-step) - var(--ca-demo-bleed)),
    500px
  );
  --ca-demo-step: 60px;
  --ca-demo-chrome: 2px;
  /* Szerokosc kolumny elementow w ukladzie dwukolumnowym. Jedna wartosc dla
     siatki i dla komunikatu koncowego, ktory centruje sie wzgledem obszaru
     rysowania, a nie wzgledem calej ramki demo. */
  --ca-demo-palette: clamp(180px, 19%, 240px);
  /* Pas wylewu pod plansza robi dwie rzeczy:
     1. jest odstepem pod plansza - tyle dzieli dol ramki od zgiecia (mniej
        wiecej tyle samo, ile wynosi oddech nad tytulem: nadwyzka rezerwy
        tytulu dzieli sie po polowie, a sam pas jest odejmowany od obszaru
        rysowania),
     2. daje miejsce, w ktorym wygasaja efekty koncowki (rozmycie ~4 px i cien
        planszy ~30 px - patrz box-shadow nizej). Bez niego hero obcina je na
        swojej krawedzi, zostawiajac pozioma linie na styku z katalogiem.
     Wchodzi do rezerwy wysokosci, wiec nie rusza CLS ani rownosci
     "przed montowaniem = po montowaniu". */
  --ca-demo-bleed: 52px;
  /* O ile w gore od srodka obszaru rysowania siedzi komunikat koncowy. Pod
     plansza lezy jeszcze pasek krokow (nie nalezy do "obrazu" ukladu), a nad
     komunikatem jest swiatlo i obwod - srodek geometryczny planszy wypada
     odrobine za nisko. Box komunikatu jest wiec krotszy od dolu o DWA razy tyle
     (tresc jest wysrodkowana, wiec srodek idzie w gore o polowe wartosci).
     Na waskich ekranach plansza schodzi pod zgiecie, dlatego tam zmienna rosnie
     - patrz blok @media (max-width: 860px) nizej. */
  --ca-demo-finish-lift: 50px;
  position: relative;
  /* Demo nie jest juz osobnym prostokatem: to czesc jednej kompozycji hero.
     Ramke i tlo niesie sama plansza (`.ca-board`), a warstwe tekstowa laczy
     z nia wspolne tlo hero i odstep. */
  /* Wlasnej ramki ani tla nie ma (obudowe niesie plansza), zostaje tylko dolny
     pas wylewu - patrz zmienna wyzej i inset przygaszenia nizej. */
  padding: var(--ca-demo-mobile-step) 0 var(--ca-demo-bleed);
  border: 0;
  background: none;
  /*
   * Bez `overflow: hidden`: rozmycie koncowki ma sie rozlac poza ramke demo,
   * a nie ucinac na jej krawedzi. Kursor i podglady i tak zostaja w srodku
   * (podglad przeciagania wisi na `document.body`, a podglad przewodu jest
   * prawdziwym linkiem wewnatrz planszy, ktora ma wlasne obciecie).
   */
  min-height: calc(
    var(--ca-demo-chrome) + var(--ca-demo-step) + var(--ca-demo-mobile-step) + var(--ca-demo-stage) + var(--ca-demo-bleed)
  );
}

/* Wysokosc ramki nie moze zalezec od tresci komunikatu: pasek statusu aplikacji
   rosnie o kilkadziesiat pikseli, gdy pojawi sie dluzsze zdanie, i to wlasnie
   wypychalo strone w chwili zapalenia zarowki. W demo jego role przejmuje pasek
   krokow (stala wysokosc), a notka palety dublowalaby jego tresc. */
.ca-demo .ca-bar,
.ca-demo .ca-palette-note {
  display: none;
}

/* Planta pod rozmycie: `filter` na samym kontenerze aplikacji nie rusza
   geometrii ani trafien wskaznika (w odroznieniu od `transform`), wiec plansza
   dalej dziala, a wyglad zmienia sie dopiero po zakonczeniu odtwarzania. */
.ca-demo .ca-demo-app {
  filter: blur(0);
  transition: filter 780ms ease;
}

/* Na hubie plansza moze byc nizsza niz na lekcji: demo ma zachecac, a nie
   wypychac liste lekcji poza ekran. */
.ca-demo .ca-board-stage {
  /* Bez `min-height` z bazowego `.ca-board-stage` (420 px), bo to zablokowaloby
     kurczenie sie ramki na nizszych ekranach. */
  min-height: 0;
  height: var(--ca-demo-stage);
}

/* Plansza w demo dostaje miekki cien: odrywa ja od swiatla hero bez zmiany
   obwodki, wiec demonstrator czyta sie jako warstwa nad tlem, a nie jako kolejny
   prostokat. Cien nie wplywa na uklad, wiec rezerwa wysokosci zostaje bez zmian. */
.ca-hub-hero .ca-demo .ca-board {
  /* Cien MUSI wygasnac w pasie wylewu (--ca-demo-bleed), inaczej hero obcina go
     na swojej krawedzi: rozpietosc = offset - spread + blur, wiec 20 - 24 + 34
     = 30 px < 32 px. Wiekszy cien (0 44 90 -52) siegal 82 px i wlasnie on
     rysowal widoczna linie pod plansza. */
  box-shadow: 0 20px 34px -24px rgba(0, 0, 0, 0.92);
}

/* W demo kolumna elementow nie jest druga ramka obok planszy: dwa kafelki
   wystarcza, a panel z wlasnym tlem odbijal uwage od demonstratora. Zostaje sam
   podpis i kafelki. Na waskim ekranie ten sam panel to 28 px paddingu, ktore
   wchodza w budzet hero, wiec regula obowiazuje w calym demo - lekcje zachowuja
   swoj panel. */
.ca-demo .ca-palette {
  padding: 0;
  border: 0;
  background: none;
}

/* Plansza demo nie jest rysowana palcem (rysuje autopilot), wiec nie ma tu
   rezerwy na lekcjowy pas przewijania ani samego pasa - a gest po planszy ma
   przewijac strone. Lekcje zachowuja oba mechanizmy: kolumna planszy z rezerwa
   i pas .ca-scroll, a obszar rysowania z gestami wylaczonymi. */
.ca-demo .ca-stage {
  padding-right: 0;
}

.ca-demo .ca-board-stage {
  touch-action: pan-y;
}

/* W demo `.ca-app` nie potrzebuje swoich marginesow: hero ma wlasny odstep,
   a 48 px marginesu zabieralo miejsce obszarowi rysowania. */
.ca-demo .ca-app {
  margin: 0;
}

/* Wezsza kolumna elementow w demo: plansza dostaje szerokosc, a obwod rosnie
   wizualnie zamiast gubic sie w pustym polu. Tylko w ukladzie dwukolumnowym -
   na waskich ekranach `.ca-work` jest jedna kolumna z paleta nad plansza
   (`@media (max-width: 860px)` w arkuszu Akademii). */
@media (min-width: 861px) {
  .ca-demo .ca-work {
    grid-template-columns: var(--ca-demo-palette) minmax(0, 1fr);
  }
}

/* Na bardzo szerokich ekranach plansza nie rozciaga sie na cala szerokosc:
   obwod zostaje w wysrodkowanej kolumnie, a nadmiar idzie w oddech wokol hero
   (inaczej akcja gubi sie w pustym polu planszy). */
.ca-hub-hero .ca-demo .ca-work {
  width: 100%;
  max-width: 1120px;
  margin-inline: auto;
}

/* Nakladka (kursor, krok, zakonczenie) trzyma sie tej samej kolumny co demo,
   zeby „Pomij” i komunikat koncowy nie uciekaly na skraj szerokiego ekranu:
   wszystkie dzieci hero trzymaja jedna, wysrodkowana kolumne, wiec tytul
   i plansza startuja z tej samej osi. */
.ca-hub-hero > * {
  width: 100%;
  /* Kolumna dobrana tak, zeby obwod wypelnial plansze wizualnie zamiast gubic
     sie w pustce: przy 1920 px plansza ma ~860 px i obwod zajmuje ~95% jej
     szerokosci. */
  max-width: 1120px;
  margin-inline: auto;
}

/* Demo nie renderuje naglowka ani zakladek (head/nav = false), wiec nie
   zostawiamy po nich miejsca, nawet gdyby motyw nadpisal reguly. */
.ca-demo .ca-head,
.ca-demo .ca-tabs {
  display: none;
}

.ca-demo-app {
  position: relative;
}

.ca-demo-overlay {
  position: absolute;
  inset: 0;
  z-index: 6;
  pointer-events: none;
}

/* W trakcie odtwarzania zbiera klikniecia, zeby przypadkowy gest nie popsul
   sekwencji. Aplikacja dostaje dodatkowo `inert`. */
.ca-demo-block {
  position: absolute;
  inset: 0;
  pointer-events: auto;
}

.ca-demo-block[hidden] {
  display: none;
}

.ca-demo-cursor {
  position: absolute;
  top: 0;
  left: 0;
  width: 24px;
  height: 24px;
  will-change: transform;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.45));
}

.ca-demo-cursor[hidden] {
  display: none;
}

.ca-demo-cursor svg {
  display: block;
  transform-origin: 4px 2px;
  transition: transform 0.12s ease;
}

/* krotkie "wcisniecie" zamiast przeskoku */
.ca-demo-cursor.is-pressed svg {
  transform: scale(0.86);
}

/* W trakcie prowadzenia kabla kursor dostaje delikatna aureole: widac, ze w tym
   momencie trzyma koncowke przewodu, a nie tylko najezdza na element. */
.ca-demo-cursor.is-wiring svg {
  filter: drop-shadow(0 0 7px color-mix(in srgb, var(--circuit-accent) 75%, transparent));
}

.ca-demo-skip {
  position: absolute;
  top: 14px;
  right: 16px;
  padding: 6px 12px;
  border: 1px solid var(--border-color, rgba(127, 127, 127, 0.35));
  border-radius: 999px;
  background: var(--circuit-card-bg, rgba(17, 17, 17, 0.75));
  color: inherit;
  font: inherit;
  font-size: 0.82rem;
  cursor: pointer;
  pointer-events: auto;
  /* Ucieczka z animacji jest wazna, ale nie pierwsza: przygaszona do najechania,
     zeby nie konkurowala z tytulem hero. */
  opacity: 0.72;
  transition: opacity 0.2s ease, border-color 0.2s ease;
}

.ca-demo-skip:hover {
  border-color: var(--circuit-accent);
  opacity: 1;
}

.ca-demo-skip[hidden] {
  display: none;
}

/* ---------- podglad przeciagania i zaciski w demo ---------- */

/* Pod kursorem jedzie prawdziwy komponent (markup zdjety z planszy), wiec to, co
   widac, jest tym samym, co za chwile pojawi sie na planszy. Cien odrywa go od
   tla, ale nie udaje osobnego UI. */
.ca-demo-shape {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9999;
  pointer-events: none;
  opacity: 0.96;
  filter: drop-shadow(0 12px 26px rgba(0, 0, 0, 0.5));
}

.ca-demo-shape svg {
  display: block;
  overflow: visible;
}

.ca-demo-shape svg text {
  font-family: inherit;
}

/* Zarys awaryjny, gdy markup nie zdazyl powstac (ten sam jezyk co w palecie). */
.ca-demo-shape--outline {
  border: 1.5px dashed var(--circuit-accent);
  border-radius: var(--circuit-radius-sm, 10px);
  background: color-mix(in srgb, var(--circuit-accent) 12%, transparent);
}

/* Zacisk podswietlany w trakcie prowadzenia kabla: zrodlowy na starcie, docelowy
   tuz przed snapem. Tylko obwodka i poswiata, wiec geometria i trafienia palca
   zostaja bez zmian. */
.ca-board [port].is-demo-target [joint-selector='portBody'] {
  stroke: var(--circuit-accent);
  stroke-width: 3;
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--circuit-accent) 55%, transparent));
}

/* ---------- pasek krokow demo ---------- */

/* Stoi w miejscu paska statusu aplikacji (doklejony do .ca-board) i ma stala
   wysokosc, wiec zmiana kroku nie rusza ukladu strony. Animacje ida wylacznie
   po `transform`, `opacity` i kolorach - nigdy po rozmiarach. */
.ca-demo-steps {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: var(--ca-demo-step, 60px);
  padding: 0 clamp(14px, 2vw, 26px);
  border-top: 2px solid transparent;
  border-radius: 0 0 var(--circuit-radius) var(--circuit-radius);
  background: var(--circuit-part-bg);
  overflow: hidden;
  transition: border-color 0.25s ease;
}

/* Gorna krawedz niesie ton stanu - tak samo, jak niosl go dawny pasek statusu -
   a miekka poswiata nad nia odrywa pasek od obszaru rysowania bez ramki. */
.ca-demo-steps:not([data-step='done']) {
  border-top-color: var(--circuit-accent);
  box-shadow: 0 -16px 30px -22px color-mix(in srgb, var(--circuit-accent) 70%, transparent);
}

.ca-demo-steps[data-step='done'] {
  border-top-color: var(--circuit-success);
  box-shadow: 0 -16px 30px -22px color-mix(in srgb, var(--circuit-success) 70%, transparent);
}

.ca-demo-steps-track {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(6px, 1vw, 14px);
  min-width: 0;
  margin: 0;
  padding: 0;
}

/* Kropka nie niesie juz numeru: przed nami jest konturem, obecna akcentowym
   rdzeniem z aureola, a zrobiona haczykiem. Bez obwodki z cyfra pasek
   przestaje czytac sie jak kreator krokow, a zaczyna jak wskaznik procesu. */
.ca-demo-step-dot {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 15px;
  height: 15px;
  border: 1px solid color-mix(in srgb, var(--circuit-muted) 55%, transparent);
  border-radius: 50%;
  transition: background-color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease, opacity 0.3s ease;
}

.ca-demo-step-dot:not(.is-active):not(.is-done) {
  opacity: 0.5;
}

.ca-demo-step-dot.is-active {
  border-color: var(--circuit-accent);
  background: radial-gradient(circle, var(--circuit-accent) 0 3px, transparent 3.5px);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--circuit-accent) 14%, transparent);
}

.ca-demo-step-dot.is-done {
  border-color: color-mix(in srgb, var(--circuit-accent) 50%, transparent);
  background: color-mix(in srgb, var(--circuit-accent) 14%, transparent);
  box-shadow: none;
}

.ca-demo-step-dot.is-done::before {
  content: '';
  width: 8px;
  height: 4px;
  margin-top: -2px;
  border-left: 1.5px solid var(--circuit-accent);
  border-bottom: 1.5px solid var(--circuit-accent);
  transform: rotate(-45deg);
}

/* Wiersz kroku jest pigulka: obecny krok dostaje tlo i obwodke z akcentu, reszta
   zostaje czystym tekstem. Jedyny wyroznik to stan, nie ramka wokol kazdego
   kroku - dlatego pasek czyta sie lekko zamiast formularzowo. */
.ca-demo-step-label {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  /* Krok nie kurczy sie nigdy: gdy pasek byl zapelniony co do piksela, flex-shrink
     scinal etykiety o ulamek piksela, a wtedy text-overflow wymienial ogon slowa
     na wielokropek (Bateria na plans... zamiast planszy). Pasek ma zapas
     szerokosci na kazdej szerokosci ekranu, wiec shrink nie jest potrzebny. */
  flex: 0 0 auto;
  margin: 0;
  padding: 7px 11px;
  border: 1px solid transparent;
  border-radius: 999px;
  color: var(--circuit-muted);
  font-size: 0.83rem;
  line-height: 1.2;
  opacity: 0.55;
  transition: color 0.3s ease, opacity 0.3s ease, background-color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease, transform 0.3s ease;
}

.ca-demo-step-index {
  font-size: 0.68rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
  opacity: 0.7;
}

.ca-demo-step-text {
  display: block;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.ca-demo-step-label.is-done {
  opacity: 0.75;
}

.ca-demo-step-label.is-done .ca-demo-step-index {
  opacity: 0.5;
}

.ca-demo-step-label.is-active {
  color: var(--circuit-text);
  font-weight: 600;
  opacity: 1;
  background: color-mix(in srgb, var(--circuit-accent) 9%, transparent);
  border-color: color-mix(in srgb, var(--circuit-accent) 24%, transparent);
  box-shadow: 0 12px 28px -18px color-mix(in srgb, var(--circuit-accent) 90%, transparent);
}

.ca-demo-step-label.is-active .ca-demo-step-index {
  color: var(--circuit-accent);
  opacity: 1;
}

/* Region zywy paska: ten sam komunikat slyszany, niewidoczny na ekranie. */
.ca-demo-step-live {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Waski, ale jeszcze dwukolumnowy uklad (861-1100 px): plansza jest tam
   najwezsza, wiec krok skraca sie do samej nazwy i ciasniejszej pigulki. */
@media (max-width: 1100px) {
  .ca-demo-step-label {
    padding: 6px 10px;
    font-size: 0.8rem;
  }

  .ca-demo-step-index {
    display: none;
  }
}

/* Jeden krok nad paleta. Wysokosc miejsca rezerwuje `.ca-demo` juz w HTML,
   przed montazem JS; zmiana tekstu i odhaczenie nie poruszaja planszy. */
.ca-demo-mobile-step {
  display: none;
}

@media (max-width: 860px) {
  .ca-demo {
    --ca-demo-step: 0px;
    --ca-demo-mobile-step: 40px;
    --ca-demo-floor: 240px;
    --ca-demo-chrome: 98px;
    --ca-demo-bleed: 40px;
  }

  .ca-demo .ca-demo-steps,
  .ca-demo .ca-demo-skip {
    display: none;
  }

  .ca-demo-mobile-step {
    position: absolute;
    inset: 0 16px auto;
    z-index: 7;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    height: var(--ca-demo-mobile-step);
    color: var(--circuit-text);
    font-size: 0.84rem;
    font-weight: 600;
    line-height: 1.25;
    text-shadow: 0 1px 10px rgba(0, 0, 0, 0.22);
    pointer-events: none;
  }

  .ca-demo-mobile-check {
    display: grid;
    place-items: center;
    flex: 0 0 17px;
    width: 17px;
    height: 17px;
    border: 1px solid var(--circuit-accent);
    border-radius: 5px;
    background: color-mix(in srgb, var(--circuit-accent) 10%, transparent);
  }

  .ca-demo-mobile-step.is-checked .ca-demo-mobile-check {
    background: var(--circuit-accent);
    box-shadow: 0 0 13px color-mix(in srgb, var(--circuit-accent) 24%, transparent);
    transition: background-color 220ms ease, box-shadow 220ms ease;
  }

  .ca-demo-mobile-step.is-checked .ca-demo-mobile-check::after {
    content: '';
    width: 8px;
    height: 5px;
    margin-top: -2px;
    border-left: 2px solid var(--circuit-accent-contrast);
    border-bottom: 2px solid var(--circuit-accent-contrast);
    transform: rotate(-45deg);
  }
}

/* Zakonczenie: po krotkiej chwili plansza lekko sie rozmywa i przygasa, a
   komunikat wychodzi na srodek. To koniec animacji, nie modal - rozwiazany
   obwod ma zostac czytelny w tle, dlatego rozmycie jest male, a przygaszenie
   delikatne (tekst niesie wlasny cien i jest czytelny na obu motywach). */
.ca-demo.is-done .ca-demo-app {
  filter: blur(4px);
}


/*
 * Przygaszenie jest miekkim pasmem, a nie winieta: gradient idzie tylko w pionie
 * (i wygladza sie na obu koncach), a poziomo wygasza go maska - dlatego nigdzie
 * nie ma krawedzi, ani okraglego zwezenia, ktore zostawial gradient radialny.
 *
 * Wazne: warstwa konczy sie DOKLADNIE na obrzezbie demo (inset 0 w pionie) i ma
 * tam zerowe krycie. Demo jest ostatnim elementem hero, wiec przy wypelnionym
 * ekranie jego dolna krawedz pokrywa sie z dolna krawedzia hero - a hero obcina
 * (potrzebuje tego pas przewijania na telefonie). Wylew w dol konczylby sie
 * cieciem na wysokosci ~17% krycia, czyli widoczna pozioma linia na styku
 * z katalogiem (zmierzone przed poprawka: 14 -> 17 w jednym rzedzie pikseli).
 * Rozmycie ma osobny pas wylewu w regule .ca-demo (--ca-demo-bleed), wiec ono
 * tez wygasa wewnatrz hero. Wylew poziomy jest maly (14%), bo na telefonie demo
 * jest tak szerokie jak ekran.
 */
.ca-demo.is-done::after {
  content: '';
  position: absolute;
  inset: 0 -14%;
  z-index: 5;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    transparent 0%,
    color-mix(in srgb, var(--ca-demo-veil) 13%, transparent) 8%,
    color-mix(in srgb, var(--ca-demo-veil) 34%, transparent) 28%,
    color-mix(in srgb, var(--ca-demo-veil) 40%, transparent) 50%,
    color-mix(in srgb, var(--ca-demo-veil) 34%, transparent) 72%,
    color-mix(in srgb, var(--ca-demo-veil) 12%, transparent) 91%,
    transparent 100%
  );
  -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 16%, #000 84%, transparent 100%);
  mask-image: linear-gradient(to right, transparent 0%, #000 16%, #000 84%, transparent 100%);
  animation: ca-demo-veil 760ms ease-out both;
}

/* Jasny motyw: przygaszenie jest biale, a komunikat ciemny - dokladnie odwrotnie
   niz na ciemnym. Bez tego jasna strona dostawala szary welon i bialy tekst. */
body.light-mode .ca-demo,
html.light-mode .ca-demo {
  --ca-demo-veil: #ffffff;
  --ca-demo-finish-text: #16181c;
}

/*
 * Jednorazowy rozblysk akcentu pod komunikatem: krotko mocniejszy, potem zostaje
 * jako delikatna poswiata sukcesu. Warstwa lezy nad przygaszeniem, a pod tekstem
 * (nakladka z komunikatem ma wlasny poziom i jest pozniej w drzewie), wiec nic
 * nie przeslania.
 */
.ca-demo.is-done::before {
  content: '';
  position: absolute;
  /* Ten sam obwod co komunikat (patrz nizej), zeby poswiata siedziala dokladnie
     za tekstem. Gradient i tak gasnie do zera w srodku warstwy, wiec obwod nie
     potrzebuje wylewu. */
  left: calc(var(--ca-demo-palette) + var(--circuit-gap));
  right: 0;
  top: 0;
  bottom: calc(var(--ca-demo-bleed) + 2 * var(--ca-demo-finish-lift));
  z-index: 6;
  pointer-events: none;
  background: radial-gradient(58% 52% at 50% 50%, color-mix(in srgb, var(--circuit-accent) 16%, transparent), transparent 72%);
  opacity: 0;
  animation: ca-demo-bloom 1500ms cubic-bezier(0.2, 0.7, 0.3, 1) both;
}

.ca-demo-finish {
  position: absolute;
  /* Komunikat centruje sie wzgledem obszaru rysowania (kolumna z plansza),
     a nie calej ramki demo: inaczej wypada w lewo o szerokosc kolumny elementow
     i w dol o polowe pasa wylewu (zmierzone przed poprawka na 1440 px: 114 px
     w lewo i 26 px w dol). Lewa krawedz = kolumna elementow + odstep, prawa =
     prawa krawedz demo, gora = gora planszy, dol = dol planszy (bez pasa wylewu,
     ktory jest tylko odstepem pod nia). */
  left: calc(var(--ca-demo-palette) + var(--circuit-gap));
  right: 0;
  top: 0;
  /* Dol ustawia `--ca-demo-finish-lift` (patrz zmienna w regule .ca-demo). */
  bottom: calc(var(--ca-demo-bleed) + 2 * var(--ca-demo-finish-lift));
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 24px;
  color: var(--ca-demo-finish-text, #ffffff);
  text-align: center;
  pointer-events: auto;
  animation: ca-demo-finish-in 720ms 180ms cubic-bezier(0.16, 0.9, 0.3, 1) both;
}

.ca-demo-finish-title {
  margin: 0;
  font-size: clamp(1.5rem, 3.4vw, 2.2rem);
  font-weight: 800;
  letter-spacing: -0.015em;
  /* Trzy warstwy cienia: ostrosc litery, ciemny "stojak" pod nia i szeroka
     poswiata, ktora oddziela tekst od rozmytej planszy. Kolor idzie z motywu
     (ciemny welon = ciemny cien, jasny welon = bialy), wiec regula jest jedna. */
  text-shadow:
    0 1px 2px color-mix(in srgb, var(--ca-demo-veil) 62%, transparent),
    0 4px 12px color-mix(in srgb, var(--ca-demo-veil) 46%, transparent),
    0 18px 48px color-mix(in srgb, var(--ca-demo-veil) 58%, transparent);
}

.ca-demo-finish-note {
  margin: 0;
  max-width: 42ch;
  font-size: 0.95rem;
  line-height: 1.5;
  opacity: 0.88;
  text-shadow:
    0 1px 2px color-mix(in srgb, var(--ca-demo-veil) 58%, transparent),
    0 6px 20px color-mix(in srgb, var(--ca-demo-veil) 50%, transparent);
}

.ca-demo-finish-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  margin-top: 14px;
}

.ca-demo-idle {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 16px 18px;
  border-radius: var(--circuit-radius);
  background: color-mix(in srgb, var(--ca-demo-veil) 42%, transparent);
  color: var(--ca-demo-finish-text, #ffffff);
  text-align: center;
  pointer-events: auto;
}

.ca-demo-idle-note {
  margin: 0;
  max-width: 46ch;
  font-size: 0.9rem;
  opacity: 0.85;
}

.ca-demo-finish-actions .ca-btn,
.ca-demo-idle .ca-btn {
  width: auto;
  text-decoration: none;
  /* Przyciski stoja na rozmytej planszy: polprzezroczyste tlo odcina je od
     przypadkowego kontrastu pod nimi, a rozmycie tla trzyma je "nad" plansza. */
  background: color-mix(in srgb, var(--ca-demo-veil) 34%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  transition: background 0.2s ease, color 0.2s ease, box-shadow 0.2s ease, filter 0.2s ease;
}

/* Glowne CTA zostaje wypelnione akcentem (i tylko ono) - to ono ma prowadzic
   wzrok, dlatego dostaje jeszcze miekka poswiate pod soba. */
.ca-demo-finish-actions .ca-btn-primary,
.ca-demo-idle .ca-btn-primary {
  background: var(--circuit-accent);
  box-shadow: 0 14px 36px -14px color-mix(in srgb, var(--circuit-accent) 85%, transparent);
}

.ca-demo-finish-actions .ca-btn-primary:focus{
  color: initial;
}

@keyframes ca-demo-veil {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/* Jednorazowy blysk akcentu; konczy jako delikatna, stala poswiata. */
@keyframes ca-demo-bloom {
  0% {
    opacity: 0;
    transform: scale(0.94);
  }
  18% {
    opacity: 1;
    transform: scale(1);
  }
  100% {
    opacity: 0.35;
    transform: scale(1.04);
  }
}

@keyframes ca-demo-finish-in {
  from {
    opacity: 0;
    transform: translateY(14px) scale(0.985);
    filter: blur(4px);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
    filter: none;
  }
}

/* Podswietlenie kafelka, ktory demo wlasnie "bierze" z palety. */
.ca-part.is-demo-hover {
  border-color: var(--circuit-accent);
  box-shadow: 0 0 0 1px var(--circuit-accent), 0 10px 24px rgba(254, 185, 0, 0.16);
}

@media (max-width: 640px) {
  .ca-demo {
    /* ten sam sklad obudowy co w bloku 860 px: rzad elementow 80 px + odstep 16
       + 2 ramki planszy (patrz komentarz przy @media 860 px) */
    --ca-demo-chrome: 98px;
  }

  .ca-demo-idle {
    padding: 14px;
  }

  /* Na telefonie ramka jest nizsza, wiec komunikat koncowy dostaje mniejsze
     odstepy - inaczej przyciski nie mieszcza sie pod tekstem. */
  .ca-demo-finish {
    gap: 10px;
    padding: 18px;
  }
}

/* Waske ekrany: kolumna elementow jest nad plansza (jeden rzad), wiec komunikat
   koncowy przesuwa sie w dol o wysokosc tego rzedu i odstep, a nie w prawo.
   `--ca-demo-chrome` to wlasnie kolumna elementow + odstep + dwie ramki planszy,
   wiec bez ramek daje gorna krawedz obszaru rysowania. Blok stoi po regulach
   bazowych, zeby nadpisac ich pozycje (rowna specyficznosc, decyduje kolejnosc). */
@media (max-width: 860px) {
  /* Na telefonie plansza schodzi pod zgiecie (obszar rysowania ma twarde dno
     300 px, a nad plansza stoi jeszcze rzad elementow), dlatego:
     - komunikat moze korzystac z calej wysokosci demo (rzad elementow jest
       w stanie koncowym wyszarzony i niewidoczny - patrz nizej), a jego polozenie
       dostraja JS do WIDOCZNEJ czesci planszy (DemoController.fitFinishCard);
     - 10 px to wartosc zapasowa, dla trybu bez animacji, gdzie JS nie mierzy
       (na telefonie plansza i komunikat mieszcza sie juz w pierwszym ekranie,
       wiec przesuniecie jest minimalne). */
  .ca-demo {
    --ca-demo-finish-lift: 10px;
  }

  .ca-demo-finish,
  .ca-demo.is-done::before {
    left: 0;
    right: 0;
    top: 0;
  }

  /* Rzad elementow jest po zakonczeniu martwy (demo sie skonczylo) i rozmyty,
     a na telefonie stoi tuz nad komunikatem - gasnie pierwszy, zeby nie
     zostawial szarej plamy nad tytulem. `visibility` wylacza go takze z trafien
     wskaznika, a ze idzie z opoznieniem, fade jest widoczny. */
  .ca-demo .ca-palette {
    transition: opacity 420ms ease;
  }

  .ca-demo.is-done .ca-palette {
    opacity: 0;
    visibility: hidden;
    transition: opacity 420ms ease, visibility 0s linear 420ms;
  }
}

/* Telefon: jeden uklad hero. Druga czesc akapitu przechodzi pod demonstrator,
   a pojedynczy krok stoi nad paleta. Wszystko jest w HTML przed
   montazem aplikacji, wiec zmiana miejsca tekstu nie powoduje CLS. */
@media (max-width: 600px) {
  .ca-hub-hero {
    --ca-hero-title: 158px;
    --ca-hero-gap: 0px;
    justify-content: flex-start;
  }

  .ca-hub-hero > .ca-head,
  .ca-hub-hero > .ca-head > .ca-desc {
    display: contents;
  }

  .ca-hub-hero .ca-kicker {
    order: 0;
    width: 100%;
    box-sizing: border-box;
    padding: 20px 16px 0;
    font-size: 10px;
  }

  .ca-hub-hero .ca-title {
    order: 1;
    width: 100%;
    box-sizing: border-box;
    margin-top: 10px;
    padding-inline: 16px;
    font-size: clamp(1.8rem, 7.4vw, 2.15rem);
    line-height: 1.08;
  }

  .ca-hub-hero .ca-desc-lead {
    order: 2;
    width: 100%;
    box-sizing: border-box;
    margin-top: 10px;
    padding-inline: 16px;
    color: var(--circuit-muted);
    font-size: 0.95rem;
    line-height: 1.45;
    text-shadow: 0 1px 14px rgba(0, 0, 0, 0.5);
    animation: ca-hero-in 560ms cubic-bezier(0.2, 0.7, 0.3, 1) 240ms both;
  }

  .ca-hub-hero .ca-demo-section {
    order: 3;
    margin-top: 16px;
  }

  .ca-hub-hero .ca-desc-detail {
    order: 4;
    width: 100%;
    box-sizing: border-box;
    padding: 0 16px 24px;
    color: var(--circuit-muted);
    font-size: 0.95rem;
    line-height: 1.5;
  }

  .ca-hub-hero .ca-education-box {
    order: 5;
  }

  .ca-demo {
    --ca-demo-bleed: 28px;
    /* 300 px pozwala zmiescic cala zarowke przy dolnym limicie skali planszy. */
    --ca-demo-floor: 300px;
    --ca-demo-stage: clamp(
      var(--ca-demo-floor),
      calc(100svh - var(--ca-hero-top) - var(--ca-hero-title) - 16px - var(--ca-demo-chrome) - var(--ca-demo-mobile-step) - var(--ca-demo-bleed)),
      440px
    );
  }

  .ca-hub-hero .ca-demo .ca-work {
    max-width: 100%;
    box-sizing: border-box;
    padding-inline: 12px;
  }

}

@media (prefers-reduced-motion: reduce) {
  .ca-demo-cursor svg {
    transition: none;
  }

  /* Wejscie hero bez animacji - tresc jest od razu na miejscu. */
  .ca-hub-hero .ca-kicker,
  .ca-hub-hero .ca-title,
  .ca-hub-hero .ca-desc,
  .ca-hub-hero .ca-demo-section {
    animation: none;
  }

  .ca-demo .ca-demo-app {
    transition: none;
  }

  .ca-demo.is-done::after,
  .ca-demo.is-done::before,
  .ca-demo-finish {
    animation: none;
  }

  /* pasek krokow: zmiana stanu bez przejsc i bez zjazdu etykiety */
  .ca-demo-steps,
  .ca-demo-step-dot,
  .ca-demo-step-index,
  .ca-demo-step-label,
  .ca-demo-mobile-check,
  .ca-demo-mobile-step.is-checked .ca-demo-mobile-check {
    transition: none;
  }

  /* wejscie hero: tytul nie rozmywa sie, tresc jest od razu ostrym tekstem */
  .ca-hub-hero .ca-title {
    filter: none;
  }

  .ca-hub-hero .ca-desc-lead {
    animation: none;
  }
}
