/* ============================================================================
   Korpo Tłumacz — index.css
   Układ świadomie naśladuje Tłumacza Google (parodia formy). Nazwa i logotyp
   są własne — to parodia, nie podszywanie się pod Google.
   ============================================================================ */

:root {
  /* Natywne scrollbary (okno słownika, Korpodex) mają iść za motywem
     STRONY, nie systemu — meta color-scheme nie widzi data-theme,
     właściwość CSS tak. Dublowana w obu blokach ciemnych niżej. */
  color-scheme: light;
  --blue: #4285f4; --red: #ea4335; --yellow: #fbbc05; --green: #34a853;
  --bg: #fff;
  --surface: #fff;
  --container: #f0f4f9;
  --container-2: #e9eef6;
  /* Podkład hovera ikon musi czytać się i na białym panelu źródłowym,
     i na szarym panelu wyniku. Kryjący kolor znika na jednym z nich —
     przezroczysty atrament działa na obu. */
  --hover-ink: rgba(31, 31, 31, .08);
  --text: #1f1f1f;
  --text-2: #444746;
  --text-3: #5f6368;
  --outline: #c4c7c5;
  --outline-2: #dadce0;
  --primary: #0b57d0;
  --primary-cont: #d3e3fd;
  --danger: #b3261e;
  --shadow: 0 1px 3px rgba(60,64,67,.3), 0 4px 8px 3px rgba(60,64,67,.15);
  --radius: 24px;
  /* Jeden system ruchu na całą stronę. Krzywa „emphasized" z Material 3:
     szybki start, miękkie dojście — dzięki temu wszystko sprawia wrażenie
     tego samego interfejsu, a nie zlepka animacji. */
  /* Wbudowane ease-* są za słabe — te krzywe pochodzą z animations.dev.
     ease-in na UI nie występuje w tym pliku i nie powinno się pojawić. */
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --t-press: 140ms;   /* wciśnięcie przycisku: 100–160 */
  --t-hover: 140ms;   /* hover, kolory */
  --t-pop: 200ms;     /* popovery i listy rozwijane: 150–250 */
  --t-move: 250ms;    /* przemieszczanie po ekranie */
  --t-toast: 400ms;   /* toast ma własne tempo, wolniejsze od reszty */
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, "Helvetica Neue", sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #1b1b1b; --surface: #1f1f1f; --container: #2d2f31; --container-2: #35373a;
    --hover-ink: rgba(232, 234, 237, .11);
    --text: #e3e3e3; --text-2: #c4c7c5; --text-3: #9aa0a6;
    --outline: #5f6368; --outline-2: #3c4043;
    --primary: #a8c7fa; --primary-cont: #0842a0; --danger: #f2b8b5;
    --shadow: 0 1px 3px rgba(0,0,0,.5), 0 4px 8px 3px rgba(0,0,0,.35);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #1b1b1b; --surface: #1f1f1f; --container: #2d2f31; --container-2: #35373a;
    --hover-ink: rgba(232, 234, 237, .11);
  --text: #e3e3e3; --text-2: #c4c7c5; --text-3: #9aa0a6;
  --outline: #5f6368; --outline-2: #3c4043;
  --primary: #a8c7fa; --primary-cont: #0842a0; --danger: #f2b8b5;
  --shadow: 0 1px 3px rgba(0,0,0,.5), 0 4px 8px 3px rgba(0,0,0,.35);
}

* { box-sizing: border-box; }
/* Nasze klasy ustawiają display: grid/flex, co bije domyślne [hidden]
   z arkusza przeglądarki. Bez tego "ukryte" przyciski są widoczne. */
[hidden] { display: none !important; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0; background: var(--bg); color: var(--text);
  font-family: var(--font); font-size: 16px; line-height: 1.5;
  overflow-x: hidden;
}
button { font: inherit; color: inherit; }
a { color: var(--primary); }

/* Link pomijający to pierwsza rzecz, jaką dostaje ktoś nawigujący klawiaturą,
   więc musi być czytelny w obu trybach. Biały tekst na var(--primary) działał
   tylko w jasnym: w ciemnym --primary to BLADY błękit (#a8c7fa), bo służy tam
   za kolor tekstu na ciemnym tle, nie za tło. Wychodziło białe na bladym
   błękicie — 1,72:1, czyli poniżej wszystkiego.

   Para kontenerowa (tło --primary-cont, tekst --primary) odwraca się razem
   z motywem sama, więc nie trzeba drugiej reguły pod ciemny: 4,92:1 w jasnym,
   5,32:1 w ciemnym. Tak samo działają .tab.on i .chip.primary. */
.skip {
  position: absolute; left: -9999px; top: 0; z-index: 50;
  background: var(--primary-cont); color: var(--primary);
  padding: 10px 16px; border-radius: 0 0 8px 0;
}
.skip:focus { left: 0; }

/* ---------- nagłówek ---------- */
.hdr { display: flex; align-items: center; gap: 4px; padding: 8px 12px; }
.hdr-sp { flex: 1; }
.ic {
  width: 40px; height: 40px; border: 0; background: transparent; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer; color: var(--text-2); flex: none;
}
.ic { position: relative; }
/* Tło hovera jest osobną warstwą, bo ma wjeżdżać skalą, a skala na przycisku
   ciągnęłaby za sobą ikonę. Ikona ma wtedy własny, mniejszy ruch. */
.ic::before {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  background: var(--hover-ink); opacity: 0; transform: scale(.7);
  pointer-events: none;
}
.ic svg { position: relative; width: 22px; height: 22px; fill: currentColor; }
.ic.sm { width: 34px; height: 34px; }
.ic.sm svg { width: 19px; height: 19px; }
.ic[aria-pressed="true"], .ic.on { color: var(--primary); }
.i-moon { display: none; }
:root[data-theme="dark"] .i-sun, html[data-theme="dark"] .i-sun { display: none; }
:root[data-theme="dark"] .i-moon, html[data-theme="dark"] .i-moon { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .i-sun { display: none; }
  :root:not([data-theme="light"]) .i-moon { display: block; }
}

.logo { display: flex; align-items: baseline; gap: 7px; text-decoration: none; margin-left: 4px; }
.wm { font-size: 25px; letter-spacing: -1px; font-weight: 500; }
.wm b { font-weight: 500; }
.c1 { color: var(--blue); } .c2 { color: var(--red); } .c3 { color: var(--yellow); }
.c4 { color: var(--blue); } .c5 { color: var(--green); }
.wm2 { font-size: 21px; color: var(--text-3); font-weight: 400; }

.avatar {
  width: 32px; height: 32px; border-radius: 50%; border: 0; margin-left: 6px; flex: none;
  background: linear-gradient(135deg, var(--blue), var(--green));
  color: #fff; font-size: 12px; font-weight: 600; cursor: pointer; letter-spacing: .5px;
}

/* ---------- popovery ---------- */
.pop {
  position: absolute; right: 12px; top: 58px; z-index: 30; width: min(320px, calc(100vw - 24px));
  background: var(--surface); border: 1px solid var(--outline-2); border-radius: 16px;
  box-shadow: var(--shadow); padding: 14px 16px;
}
.pop[hidden] { display: none; }
.pop h2 { margin: 0 0 10px; font-size: 15px; font-weight: 600; }
.pop .row { display: flex; align-items: center; gap: 10px; padding: 7px 0; font-size: 14px; cursor: pointer; }
.pop .row.col { display: block; cursor: default; }
.pop .row.col input { width: 100%; margin: 8px 0 2px; }
.pop .row.col small { color: var(--text-3); font-size: 12px; }
.pop hr { border: 0; border-top: 1px solid var(--outline-2); margin: 10px 0; }
.pop-note { margin: 0; font-size: 12px; color: var(--text-3); }
.apps-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; }
.app {
  border: 0; background: transparent; border-radius: 12px; padding: 12px 4px; cursor: pointer;
  display: grid; gap: 6px; justify-items: center; font-size: 11px; color: var(--text-2);
}
.app span { font-size: 22px; }
.app:hover { background: var(--container); }

/* ---------- główna kolumna ---------- */
main { --gutter: 16px; max-width: 1720px; margin: 0 auto; padding: 0 var(--gutter) 64px; }

/* Pasek zakładek przewija się w poziomie, więc musi dochodzić do krawędzi
   ekranu. Kiedy urywał się na wcięciu <main>, wyglądał na skończony — nikt
   nie zgadywał, że da się go przesunąć. Teraz wychodzi poza wcięcie ujemnym
   marginesem, a to samo wcięcie wraca do środka jako padding: w spoczynku
   pierwsza zakładka stoi w jednej linii z treścią, a ostatnia znika za
   krawędzią i widać, że jest czego szukać dalej. */
.tabs {
  display: flex; gap: 10px; overflow-x: auto; scrollbar-width: none;
  margin-inline: calc(-1 * var(--gutter, 16px));
  padding: 6px var(--gutter, 16px) 18px;
  scroll-padding-inline: var(--gutter, 16px);
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  display: inline-flex; align-items: center; gap: 8px; white-space: nowrap;
  border: 1px solid var(--outline); background: transparent; color: var(--text-2);
  padding: 8px 15px; border-radius: 20px; cursor: pointer; font-size: 14px;
}
.tab svg { width: 18px; height: 18px; fill: currentColor; }
.tab:hover { background: var(--container); }
.tab.on { background: var(--primary-cont); border-color: transparent; color: var(--primary); }
:root[data-theme="dark"] .tab.on { color: #d3e3fd; }

/* ---------- karta tłumacza ---------- */
.card { border-radius: var(--radius); }
.langbar { display: grid; grid-template-columns: 1fr 56px 1fr; align-items: center; gap: 0; }
.langwrap { display: flex; align-items: center; min-width: 0; }
.more {
  display: none; width: 32px; height: 32px; flex: none; border: 0; background: transparent;
  border-radius: 50%; cursor: pointer; color: var(--text-2); place-items: center;
}
.more:hover { background: var(--container); }
.more svg { width: 20px; height: 20px; fill: currentColor; }
.langs { display: flex; align-items: center; gap: 2px; overflow-x: auto; scrollbar-width: none; min-width: 0; }
.langs::-webkit-scrollbar { display: none; }
.lang {
  border: 0; background: transparent; cursor: pointer; padding: 10px 12px 12px;
  color: var(--text-2); font-size: 14px; white-space: nowrap; border-bottom: 3px solid transparent;
  border-radius: 8px 8px 0 0;
}
.lang:hover { background: var(--container); }
.lang.on { color: var(--primary); border-bottom-color: var(--primary); font-weight: 500; }
.swap {
  width: 40px; height: 40px; margin: 0 auto; border: 0; background: transparent; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer; color: var(--text-2);
}
.swap:hover { background: var(--container); }
.swap svg { width: 22px; height: 22px; fill: currentColor; }

/* minmax(0, 1fr) zamiast 1fr: element siatki ma domyślnie min-width auto,
   więc pasek ikon w panelu potrafił rozepchnąć kolumnę szerzej niż ekran
   i cała strona dostawała poziomy pasek przewijania na 320 px. */
.panes { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 14px; align-items: stretch; }
.pane { position: relative; display: flex; flex-direction: column; min-height: 216px; padding: 14px 16px 8px; }
.pane-src { background: var(--surface); border: 1px solid var(--outline); border-radius: var(--radius); }
.pane-dst { background: var(--container); border: 1px solid transparent; border-radius: var(--radius); }

#src {
  flex: 1; width: 100%; border: 0; outline: 0; resize: none; background: transparent;
  color: var(--text); font: 400 24px/1.4 var(--font); padding: 0 30px 8px 0; min-height: 130px;
}
#src::placeholder { color: var(--text-3); }
.clear {
  position: absolute; right: 12px; top: 12px; width: 34px; height: 34px; border: 0;
  background: transparent; border-radius: 50%; cursor: pointer; color: var(--text-3);
  display: grid; place-items: center;
}
.clear:hover { background: var(--container-2); }
.clear svg { width: 20px; height: 20px; fill: currentColor; }

.out {
  flex: 1; font-size: 24px; line-height: 1.4; white-space: pre-wrap; overflow-wrap: anywhere;
  min-height: 130px; padding-bottom: 8px;
}
.out .ph { color: var(--text-3); }
.out mark { background: transparent; color: inherit; box-shadow: inset 0 -2px 0 var(--primary); }

.pbar { display: flex; align-items: center; gap: 2px; min-height: 40px; }
.pbar .sp { flex: 1; }
.count { font-size: 13px; color: var(--text-3); padding-right: 4px; }
.conf {
  font-size: 12px; color: var(--text-3); padding-right: 6px;
  white-space: nowrap; min-width: 0; overflow: hidden; text-overflow: ellipsis;
}
.score {
  font-size: 12px; font-weight: 500; color: var(--primary); padding-right: 8px;
  white-space: nowrap;
  /* Ustępuje ikonom, zamiast rozpychać panel: na najwęższym ekranie liczba
     może się przyciąć, ale strona nie może zacząć się przewijać. */
  min-width: 0; overflow: hidden; text-overflow: ellipsis;
}
/* Zdanie i dwie akcje to trzy osobne rzeczy, a jako zwykły tekst łamały się
   w przypadkowym miejscu: „Udostępnij" zostawało na końcu wiersza, a „inny
   przykład" schodziło niżej z wcięciem po nowej linii w HTML-u. Flex łamie
   je jako całości i wyrównuje do lewej. */
.demohint {
  margin: 0 0 4px; font-size: 12px; color: var(--text-3); font-style: italic;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 10px;
}
.ic.sm[disabled] { opacity: .35; cursor: default; }
/* Na wąskim ekranie w pasku jest sześć ikon — plakietka pewności ustępuje. */
@media (max-width: 480px) {
  .conf { display: none; }
  /* Zostaje sama liczba — opis ustępuje ikonom. */
  .score-opt { display: none; }
  .pbar { gap: 0; }
  .ic.sm { width: 32px; height: 32px; }
}
/* Dotyk: 32 px to o wiele za mało na kciuk (wytyczne mówią 44), a to jest
   dokładnie pasek udostępniania — pętla wzrostu tej strony. 40 px z małym
   odstępem mieści się nawet na 320 px: 6×40 + 5×2 = 250 px. Kompakt 32 px
   zostaje tam, gdzie jest kursor, czyli tam, gdzie nikt nie pudłuje. */
@media (max-width: 480px) and (pointer: coarse) {
  .pbar { gap: 2px; }
  .ic.sm { width: 40px; height: 40px; }
}
.disc { text-align: right; font-size: 12px; color: var(--text-3); margin: 8px 2px 0; font-style: italic; }

/* ---------- pasek AI ---------- */
.quickchips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 18px; }
.ai { margin-top: 14px; display: grid; gap: 10px; }
.ai-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.chip {
  border: 1px solid var(--outline); background: transparent; color: var(--text-2);
  padding: 9px 16px; border-radius: 20px; cursor: pointer; font-size: 14px;
  display: inline-flex; align-items: center; text-decoration: none; line-height: 1.2;
}
a.chip[aria-disabled="true"] { opacity: .45; pointer-events: none; }
.chip:hover { background: var(--container); }
.chip.primary { background: var(--primary-cont); border-color: transparent; color: var(--primary); font-weight: 500; }
:root[data-theme="dark"] .chip.primary { color: #d3e3fd; }
.chip.danger { border-color: var(--danger); color: var(--danger); }
.chip[disabled] { opacity: .45; cursor: not-allowed; }
.chip.on { background: var(--primary-cont); border-color: transparent; color: var(--primary); }
.ai-note { font-size: 12px; color: var(--text-3); }
.ai-label { font-size: 13px; color: var(--text-3); }
/* Jeden wiersz działań. Grupy oddziela kreska, nie nowa linia — na wąskim
   ekranie wiersz zawija się sam, a kreski chowają się na zawinięciu, bo
   pionowa krecha na początku linii wygląda jak usterka. */
.actions {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 8px 10px; margin: 12px 0 4px;
}
.act-grp { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
/* Ciaśniejsze pigułki wyłącznie w tym pasku: dziewięć akcji obok siebie musi
   zmieścić się w jednym wierszu na typowym ekranie, a poza paskiem chipy
   zostają takie, jakie były. */
.actions .chip { font-size: 13px; padding: 7px 12px; gap: 6px; }
.act-sep { width: 1px; align-self: stretch; min-height: 20px; background: var(--outline-2); }
/* Kreski mają sens tylko wtedy, gdy pasek jest jednym wierszem. Po zawinięciu
   ostatnia zostaje na końcu linii i wygląda jak usterka renderowania. Chowamy
   je przez visibility, nie display — inaczej zwolniona szerokość potrafiłaby
   zmieścić pasek z powrotem w jednym wierszu, kreski wróciłyby, pasek znów by
   się zawinął i tak w kółko. */
.actions.wrapped .act-sep { visibility: hidden; }

/* Pełne „Przetłumacz w ChatGPT" mieści się w linii dopiero od 1440 px. Na
   1280 px — czyli na najzwyklejszym laptopie — łamało pasek na dwa wiersze
   i spychało „Ale o co chodzi?!" pod krawędź ekranu, a to był cały powód,
   dla którego pasek scaliliśmy. Sam „ChatGPT" obok „Kopiuj prompt" mówi to
   samo, a oddaje ~90 px na przycisk. Pełna nazwa zostaje w title. */
@media (max-width: 1400px) { .actions .dlugie { display: none; } }
@media (max-width: 720px) { .act-sep { display: none; } }

/* Przycisk dzielony: „Model lokalny” włącza silnik, lista obok wybiera wariant.
   Jedna kontrolka wizualnie, dwie funkcjonalnie — dlatego lista jest natywnym
   <select>, a nie własnym menu: działa z klawiatury i z czytnikiem ekranu. */
.split { display: inline-flex; align-items: stretch; }
.split-main { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.split-sel {
  font: inherit; font-size: 13px; color: var(--text);
  background: var(--surface); border: 1px solid var(--outline);
  border-left: 0; border-radius: 0 20px 20px 0;
  padding: 0 8px 0 10px; max-width: 15ch; cursor: pointer;
}
.split-sel:hover { background: var(--container); }
.split-sel[hidden] { display: none; }
.split:has(.split-sel[hidden]) .split-main,
.split-main:only-child { border-radius: 20px; }

/* .segbtn brał obramowanie z otaczającego .seg, którego już nie ma — bez tego
   „Słownik" i „Model lokalny" wyglądały jak zwykły tekst obok pigułek.
   W pasku działań dostają własną ramkę, żeby były widocznie klikalne. */
.actions .segbtn {
  border: 1px solid var(--outline); background: var(--surface);
  border-radius: 20px; padding: 8px 14px; font-size: 13px;
}
.actions .segbtn[disabled] { opacity: .55; }
.actions .split-main { border-right: 0; border-radius: 20px 0 0 20px; }
.actions .split:has(.split-sel[hidden]) .split-main { border-right: 1px solid var(--outline); border-radius: 20px; }

.seg { display: inline-flex; border: 1px solid var(--outline); border-radius: 20px; overflow: hidden; }
.segbtn {
  border: 0; background: transparent; color: var(--text-2); padding: 8px 15px;
  font-size: 14px; cursor: pointer; display: inline-flex; align-items: center; gap: 7px;
}
.segbtn + .segbtn { border-left: 1px solid var(--outline); }
.segbtn:hover:not([disabled]) { background: var(--container); }
.segbtn.on { background: var(--primary-cont); color: var(--primary); font-weight: 500; }
:root[data-theme="dark"] .segbtn.on { color: #d3e3fd; }
.segbtn[disabled] { opacity: .5; cursor: default; }
/* Lista wariantów modelu. Natywny <select> zamiast własnego rozwijanego
   menu: działa z klawiatury, z czytnika ekranu i mieści się na 320 px,
   a wygląd i tak sprowadza się do ramki w kształcie chipa. */
.modelsel {
  font: inherit; font-size: 13px; color: var(--text-1);
  background: var(--surface); border: 1px solid var(--outline);
  border-radius: 20px; padding: 5px 28px 5px 12px; cursor: pointer;
  appearance: none; max-width: 320px;
  background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%235f6368'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 6px center; background-size: 16px;
}
.modelsel:hover { background-color: var(--container); }
.modelsel:disabled { opacity: .5; cursor: default; }
:root[data-theme="dark"] .modelsel {
  background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%239aa0a6'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E");
}
.segbtn .dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--text-3); flex: none;
}
.segbtn.busy .dot { background: var(--yellow); animation: pulse 1.1s ease-in-out infinite; }
.segbtn.rdy .dot { background: var(--green); }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .25; } }
.linkbtn {
  border: 0; background: transparent; color: var(--primary); font-size: 12px;
  cursor: pointer; text-decoration: underline; padding: 2px 4px;
}
.think {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; color: var(--text-3); padding-right: 6px;
}
/* kropka pulsuje, dopóki model myśli — widać, że coś się dzieje */
.think::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%; flex: none;
  background: var(--primary); animation: pulse 1s ease-in-out infinite;
}
.out.stale { opacity: .5; }
/* Plakietka Bielika przed procentem: szara, gdy tylko podpisuje autora;
   żółta, gdy post przebił wejście — to jest moment na screenshot. */
.ratiod {
  display: inline-block; margin-right: 6px; padding: 0 7px; border-radius: 999px;
  font-size: 11px; font-weight: 600; line-height: 18px; letter-spacing: .02em;
  background: var(--container-2); color: var(--text-2);
}
.ratiod.on { background: var(--yellow); color: #1f1f1f; }
.reroll { white-space: nowrap; }
.reroll[hidden] { display: none; }
.prog { display: flex; align-items: center; gap: 10px; font-size: 12px; color: var(--text-3); }
.prog[hidden] { display: none; }
.prog-bar { flex: 1; max-width: 420px; height: 4px; background: var(--container-2); border-radius: 2px; overflow: hidden; }
.prog-bar i { display: block; height: 100%; width: 0; background: var(--primary); }

/* ---------- skróty ---------- */
/* Ciasno, żeby „Ale o co chodzi?!" mieściło się nad zgięciem na desktopie. */
/* Pięć skrótów przy odstępie 44 px potrzebuje 456 px — na telefonie to o sto
   z górą za dużo, więc rząd musi móc się złamać, a odstęp zejść. */
.quick { display: flex; flex-wrap: wrap; justify-content: center; gap: 44px 44px; margin: 26px 0 4px; }
@media (max-width: 700px) { .quick { gap: 22px 26px; } }
.round { text-decoration: none; }
.round {
  border: 0; background: transparent; cursor: pointer; display: grid; gap: 8px; justify-items: center;
  font-size: 13px; color: var(--text-2);
}
.rc {
  width: 56px; height: 56px; border-radius: 50%; border: 1px solid var(--outline);
  display: grid; place-items: center;
}
.round:hover .rc { background: var(--container); }
.rc svg { width: 24px; height: 24px; fill: currentColor; }
.round.on .rc { background: var(--primary-cont); border-color: transparent; color: var(--primary); }

.list { max-width: 760px; margin: 18px auto 0; display: grid; gap: 8px; }
.list[hidden] { display: none; }
.li {
  display: grid; grid-template-columns: 1fr auto; gap: 10px; align-items: center;
  background: var(--container); border-radius: 14px; padding: 10px 14px; font-size: 14px;
}
.li b { font-weight: 500; }
.li p { margin: 2px 0 0; color: var(--text-3); font-size: 13px; }
.li .txt { cursor: pointer; overflow: hidden; }
.li-empty { text-align: center; color: var(--text-3); font-size: 14px; padding: 18px; }

/* ---------- zachęta do zjazdu ----------
   Pod aplikacją jest osiem sekcji treści, ale nic nad zgięciem tego nie
   zdradza. Fala opacity spływa po trzech szewronach w dół — ruch sugeruje
   kierunek, choć żaden piksel się nie przesuwa. */
/* Podpowiedź jak z ekranu wczytywania gry — nad zachętą, cicho i szaro. */
.hint-tip {
  text-align: center; font-size: 13px; color: var(--text-3);
  margin: 18px auto -22px; padding: 0 18px; max-width: 460px;
}
.scrollhint {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  margin: 26px auto 0; padding: 10px 18px;
  background: none; border: none; border-radius: 20px; cursor: pointer;
  color: var(--text-3); font-size: 14px; font-family: inherit;
}
.scrollhint:hover { color: var(--text-2); background: var(--hover-ink); }
@keyframes hintFala { 0%, 70%, 100% { opacity: .25; } 25% { opacity: 1; } }
.scrollhint path { opacity: .25; animation: hintFala 2.1s ease-in-out infinite; }
.scrollhint path:nth-of-type(2) { animation-delay: .18s; }
.scrollhint path:nth-of-type(3) { animation-delay: .36s; }

/* ---------- toast ---------- */
.toast {
  position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%);
  background: #323232; color: #fff; padding: 12px 18px; border-radius: 8px; font-size: 14px;
  z-index: 60; max-width: min(560px, calc(100vw - 32px)); box-shadow: var(--shadow);
}
.toast[hidden] { display: none; }

/* ---------- trofeum (Ocena roczna) ----------
   Popup jak zdobycz na konsoli. Osobny element od toastu: inny róg, mogą
   wisieć naraz i mówią o czym innym. Klik prowadzi do sekcji celów. */
.trophy {
  /* env(): na iPhonie bez ramki pasek gestów zjada dolne ~20 px —
     viewport-fit=cover w meta odsłania wcięcia, a FAB-y je omijają. */
  position: fixed; right: 20px; bottom: calc(26px + env(safe-area-inset-bottom, 0px));
  display: flex; align-items: center; gap: 12px;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--outline-2); border-radius: 16px;
  padding: 12px 18px; font-size: 14px; z-index: 61;
  max-width: min(420px, calc(100vw - 32px));
  box-shadow: var(--shadow); cursor: pointer;
}
.trophy[hidden] { display: none; }
.trophy-ico { font-size: 26px; }
.trophy-txt { display: flex; flex-direction: column; gap: 2px; }
.trophy-head { font-size: 12px; color: var(--text-3); display: flex; align-items: center; gap: 6px; }
.trophy-name { font-weight: 600; }
@media (max-width: 560px) { .trophy { right: 16px; bottom: calc(78px + env(safe-area-inset-bottom, 0px)); } }

.trof-tier {
  display: inline-block; padding: 1px 8px; border-radius: 999px;
  font-size: 11px; font-weight: 600; letter-spacing: .02em;
}
.trof-tier.tt1 { background: #f3e2d1; color: #8a5a2b; }
.trof-tier.tt2 { background: #e8ebee; color: #5b636b; }
.trof-tier.tt3 { background: linear-gradient(100deg, #f6c945, #ef8f3c 55%, #c8752b); color: #2b1d05; }
.trof-tier.tt4 { background: linear-gradient(100deg, #bfeaf5, #7fc4dd 60%, #5aa7c9); color: #0b2b36; }
:root[data-theme="dark"] .trof-tier.tt1 { background: #4a3421; color: #e8c9a8; }
:root[data-theme="dark"] .trof-tier.tt2 { background: #3c4043; color: #cdd3d8; }

/* Ten sam kaganiec co na Korpodeksie (.kdx), i z tego samego powodu. Trzydzieści
   jeden kart rozciągało sekcję do 3678 px na telefonie — cztery i pół ekranu na
   listę, którą się przegląda, a nie czyta od deski do deski. Korpodex mieści
   sto pięćdziesiąt jeden kart w 795 px właśnie dlatego, że przewija się
   wewnątrz; nie było powodu, żeby sąsiednia sekcja robiła inaczej.

   Wszystkie 31 zostaje w dokumencie — chodzi o wysokość, nie o treść. */
.trofea {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px;
  max-height: min(70vh, 620px); overflow: auto; overscroll-behavior: contain;
  padding: 12px; border: 1px solid var(--outline-2); border-radius: 12px;
}
.trof {
  display: flex; flex-direction: column; gap: 8px;
  background: var(--container); border-radius: 16px; padding: 14px 16px;
}
.trof.lock { opacity: .62; }
.trof-meta { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.trof-date { font-size: 12px; color: var(--text-3); font-variant-numeric: tabular-nums; }
.trof-name { font-weight: 600; font-size: 15px; color: var(--text); }
.trof-cel { font-size: 13px; color: var(--text-3); line-height: 1.45; margin-top: auto; }

/* ---------- treść pod aplikacją ---------- */
.below { max-width: 860px; margin: 0 auto; padding: 72px 16px 96px; }
.below section { margin-bottom: 56px; scroll-margin-top: 20px; }
.below h2 { font-size: 26px; line-height: 1.25; margin: 0 0 14px; font-weight: 500; }
.below h3 { font-size: 17px; margin: 34px 0 10px; font-weight: 600; }
.below p { margin: 0 0 14px; color: var(--text-2); }
.below ul { color: var(--text-2); padding-left: 22px; }
.below li { margin-bottom: 7px; }
.hr { border: 0; border-top: 1px solid var(--outline-2); margin: 0 0 56px; }

.money {
  display: grid; gap: 4px; background: var(--container); border-radius: 16px; padding: 18px 20px; margin: 0 0 22px;
}
.money b { font-size: 30px; font-weight: 500; letter-spacing: -.5px; }
.money span { font-size: 13px; color: var(--text-3); }

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

/* ---------- rzadkość przykładów ----------
   Znaczek pojawia się tylko wtedy, gdy jest co pokazać. Legendarny ma
   wyglądać na zdobycz, a nie na kolejną etykietę. */
.rare {
  display: inline-block; margin: 0 6px; padding: 1px 8px; border-radius: 999px;
  font-size: 11.5px; font-weight: 600; letter-spacing: .02em; vertical-align: 1px;
}
/* Kolory z konwencji growej: niebieski rzadki, fioletowy unikat,
   złoty legendarny. Rozpoznawalne bez czytania podpisu. */
.rare.r2 { background: var(--primary-cont); color: var(--primary); }
.rare.r3 { background: #ede4fb; color: #6b32c9; }
.rare.r4 {
  background: linear-gradient(100deg, #f6c945, #ef8f3c 55%, #c8752b);
  color: #2b1d05;
  box-shadow: 0 1px 6px rgba(239, 143, 60, .45);
}
:root[data-theme="dark"] .rare.r2 { color: #d3e3fd; }
:root[data-theme="dark"] .rare.r3 { background: #3a2a5e; color: #d9c7fb; }
.demo-count {
  float: right; font-size: 11.5px; color: var(--text-3);
  font-variant-numeric: tabular-nums;
}
.demo-count.done { color: var(--green); font-weight: 600; }
@media (max-width: 560px) { .demo-count { float: none; margin-left: 6px; } }

/* ---------- Korpodex ----------
   Zakryta karta wygląda jak zdanie z odtajnionego dokumentu: czarne bary
   zamiast słów, ale plakietka rzadkości na wierzchu — widać, na co się
   poluje, nie widać pointy. */
.kdx-top {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin: 14px 0 18px;
}
.kdx-progress { display: flex; align-items: center; gap: 10px; flex: 1 1 200px; min-width: 180px; }
.kdx-bar {
  flex: 1; height: 8px; border-radius: 999px;
  background: var(--container-2); overflow: hidden;
}
.kdx-fill { height: 100%; width: 0; border-radius: 999px; background: var(--primary); }
.kdx-count { font-size: 13.5px; color: var(--text-3); font-variant-numeric: tabular-nums; }
.kdx-tiers { display: flex; gap: 8px; flex-wrap: wrap; }
.kdx-tier {
  padding: 3px 10px; border: 1px solid var(--outline-2); border-radius: 999px;
  font-size: 12px; color: var(--text-3); font-variant-numeric: tabular-nums;
}
.kdx-tier.r2 { background: var(--primary-cont); color: var(--primary); border-color: transparent; }
.kdx-tier.r3 { background: #ede4fb; color: #6b32c9; border-color: transparent; }
.kdx-tier.r4 {
  background: linear-gradient(100deg, #f6c945, #ef8f3c 55%, #c8752b);
  color: #2b1d05; border-color: transparent; font-weight: 600;
}
:root[data-theme="dark"] .kdx-tier.r2 { color: #d3e3fd; }
:root[data-theme="dark"] .kdx-tier.r3 { background: #3a2a5e; color: #d9c7fb; }
/* Własne okno przewijania jak przy słowniku: 151 kart nie może odpychać
   reszty strony o cztery ekrany w dół. */
.kdx {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 12px;
  max-height: min(70vh, 620px); overflow: auto; overscroll-behavior: contain;
  padding: 12px; border: 1px solid var(--outline-2); border-radius: 12px;
}
.kdx-card {
  display: flex; flex-direction: column; gap: 8px;
  background: var(--container); border-radius: 16px; padding: 14px 16px;
}
.kdx-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.kdx-meta .rare { margin: 0; }
.kdx-num { font-size: 12px; color: var(--text-3); font-variant-numeric: tabular-nums; }
.kdx-new {
  font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 999px;
  background: var(--primary-cont); color: var(--primary);
}
:root[data-theme="dark"] .kdx-new { color: #d3e3fd; }
.kdx-q { margin: 0; font-size: 14px; line-height: 1.45; color: var(--text); overflow-wrap: break-word; }
.kdx-card.lock .kdx-q {
  color: var(--text-3); letter-spacing: 1px; user-select: none; opacity: .55;
}
.kdx-dir { font-size: 12px; color: var(--text-3); margin-top: auto; }
.kdx-act { display: flex; gap: 14px; }
@media (max-width: 480px) { .kdx { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); } }

/* ---------- spis treści ----------
   Część redakcyjna ma piętnaście sekcji i kilkanaście ekranów. Bez spisu
   jedyną nawigacją było przewijanie w ciemno. Na szerokim ekranie stoi
   przyklejony z boku, na wąskim zwija się do jednego przycisku. */
.toc { margin: 0 0 32px; }
.toc-toggle {
  display: flex; width: 100%; align-items: center; justify-content: space-between;
  gap: 8px; padding: 12px 14px; border: 1px solid var(--outline-2);
  background: var(--surface); color: var(--text); border-radius: 12px;
  font: 500 14px/1 inherit; font-family: inherit; cursor: pointer;
}
.toc-toggle svg { width: 18px; height: 18px; flex: none; transition: transform var(--t-pop) var(--ease-out); }
.toc-toggle[aria-expanded="true"] svg { transform: rotate(180deg); }
.toc-list {
  list-style: none; margin: 10px 0 0; padding: 0;
  display: grid; gap: 2px;
}
.toc-list a {
  display: block; padding: 6px 10px; border-radius: 0 8px 8px 0;
  font-size: 13.5px; line-height: 1.4; color: var(--text-2); text-decoration: none;
  border-left: 2px solid transparent;
}
.toc-list a:hover { background: var(--container); color: var(--text); }
/* Aktywna pozycja: ta sama kreska w kolorze wiodącym co wszędzie indziej. */
.toc-list a.on { color: var(--primary); border-left-color: var(--primary); font-weight: 500; }
.toc-top {
  display: inline-block; margin-top: 12px; font-size: 13px;
  color: var(--primary); text-decoration: none;
}
.toc-top::before { content: '↑'; margin-right: 6px; }
.toc-top:hover { text-decoration: underline; }

/* Zwijanie dotyczy wyłącznie wąskiego ekranu. Reguła bez media query
   wygrywała specyficznością z desktopowym odsłonięciem (`:not(.open)` to
   dodatkowa klasa), więc spis miał zero wysokości i przyklejał się jako
   niewidoczny pasek. */
@media (max-width: 1119px) {
  .toc:not(.open) .toc-list, .toc:not(.open) .toc-top { display: none; }
}

@media (min-width: 1120px) {
  .below {
    max-width: 1180px;
    display: grid; grid-template-columns: 216px minmax(0, 860px);
    gap: 56px; align-items: start;
  }
  .toc { position: sticky; top: 24px; margin: 0; max-height: calc(100vh - 48px); overflow-y: auto; }
  .toc-toggle { display: none; }              /* na desktopie zawsze otwarty */
  .toc .toc-list, .toc .toc-top { display: block; }
  .toc-list { display: grid; }
}

/* ---------- zestawienie kwot ----------
   Dwie liczby obok siebie to cała teza tej sekcji. Wcześniej stały jedna
   pod drugą, rozdzielone akapitem, i porównanie ginęło. */
.vs-money { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin: 0 0 14px; }
.vs-money .money { margin: 0; align-content: start; }
.money span { font-size: 14px; color: var(--text-2); font-weight: 500; }
.money small { font-size: 12.5px; color: var(--text-3); line-height: 1.5; display: block; margin-top: 4px; }
.money.zero b { color: var(--green); }
:root[data-theme="dark"] .money.zero b { color: #7ee2a8; }
.vs-cta { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 0 0 18px !important; }
.vs-hint { font-size: 13px; color: var(--text-3); }
@media (max-width: 700px) {
  .vs-money { grid-template-columns: 1fr; }
}

/* Oddech wokół bloków cytatów: tekst po nich nie ma się kleić do kart. */
.cards { display: grid; gap: 12px; margin: 18px 0 24px; }
.q {
  background: var(--container); border-radius: 16px; padding: 16px 18px;
  display: grid; gap: 10px;
}
.q blockquote { margin: 0; font-size: 17px; line-height: 1.45; }
.q .meta { font-size: 12px; color: var(--text-3); display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.q .meta .tag {
  background: var(--container-2); border-radius: 20px; padding: 2px 9px; font-size: 11px;
}
.q .meta .tag.rep { color: var(--primary); }
.q .use { justify-self: start; border: 1px solid var(--outline); background: transparent;
  border-radius: 20px; padding: 6px 14px; font-size: 13px; cursor: pointer; color: var(--text-2); }
.q .use:hover { background: var(--surface); }

table.dict { width: 100%; border-collapse: collapse; font-size: 14px; }
table.dict th, table.dict td { text-align: left; padding: 9px 10px; border-bottom: 1px solid var(--outline-2); vertical-align: top; }
table.dict th { color: var(--text-3); font-weight: 500; font-size: 12px; text-transform: uppercase; letter-spacing: .4px; }


/* Formy poboczne stoją obok formy podstawowej, przygaszone: hasło to jedno
   słowo, a nie pięć wierszy. Szukanie obejmuje je tak samo. */
/* Warianty stoją obok hasła przygaszone — ale scalanie synonimów sprawiło,
   że „restrukturyzacja" zebrała ich trzydzieści i jeden wiersz urósł do
   1230 px na telefonie, czyli półtora ekranu na jedno hasło. Kolumna „po
   ludzku" wyjeżdżała wtedy poza ekran, więc wchodząc z Google na
   /slownik-restrukturyzacja nie było widać tego jednego słowa, po które
   ktoś przyszedł: „zwolnienia".

   Mediana wiersza to 61 px, powyżej 150 px jest ich siedemnaście — obcięcie
   do trzech linijek dotyka więc garstki, a resztę tabeli zostawia bez zmian.
   Formy zostają w DOM, bo na tym stoi wyszukiwarka, Ctrl+F i indeks Google;
   znika tylko ich wysokość. */
.dict .alt {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
  overflow: hidden;
  margin-top: 2px; font-size: 12px; color: var(--text-3);
}
/* Wiersz ma własną kotwicę (#slownik-bag), więc musi się zatrzymać pod
   paskiem, a nie za nim. */
/* Wszystkie hasła siedzą w DOM — inaczej Ctrl+F ich nie znajdzie, kotwica do
   ukrytego hasła nie zadziała, a wyszukiwarka zobaczy ułamek słownika.

   Bez content-visibility, choć samo się prosi. Na 966 wierszach psuło
   malowanie przyklejonych pigułek wyjścia: przestawały się pojawiać na
   ekranie, mimo że miały poprawny box, łapały kliknięcia i przechodziły
   checkVisibility(). Wiersze tabeli są proste, więc pełne renderowanie
   kosztuje tyle, ile pokazał pomiar niżej — mniej niż zepsuty interfejs. */
table.dict tr[id] { scroll-margin-top: 80px; }
table.dict tr:target td { background: var(--primary-cont); }table.dict td:first-child { font-weight: 500; width: 42%; }
/* Tabela dostaje własne okno przewijania zamiast rozpychać stronę na
   pięćdziesiąt tysięcy pikseli. Wszystkie hasła zostają w dokumencie, więc
   Ctrl+F je znajduje, kotwice działają, a wyszukiwarka widzi całość — tyle że
   strona ma znowu ludzką długość. */
.dict-wrap {
  overflow: auto;
  /* Wyżej niż pozostałe okna przewijania: rozwinięte hasło niesie definicję,
     kartę 600px i wiersz guzików. Przy 620px „agile" z całą rodziną form
     wypychało nagłówek hasła poza widok, a guziki lądowały na krawędzi. */
  max-height: min(76vh, 690px);
  border: 1px solid var(--outline-2); border-radius: 12px;
  overscroll-behavior: contain;
}
.dict-sep td {
  padding: 10px 12px 6px;
  font-size: 12px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-3);
  background: var(--surface);
  border-top: 1px solid var(--outline-2);
}
.dict-wrap thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--bg);
}
#dictSearch {
  width: 100%; padding: 12px 16px; border-radius: 24px; border: 1px solid var(--outline);
  background: var(--surface); color: var(--text); font: inherit; font-size: 15px; margin-bottom: 14px;
}
.dict-count { font-size: 13px; color: var(--text-3); margin: 16px 0 0; }

/* Rozwinięcie hasła: definicja słownikowa i udostępnianie. Wiersz z hasłem
   jest klikalny, więc dostaje kursor i hover; panel dzieli tło z wierszem,
   żeby czytały się jako jedno hasło. */
table.dict tbody tr[id] { cursor: pointer; }
table.dict tbody tr[id]:hover td { background: var(--container-2); }
table.dict tbody tr.otwarte td { background: var(--primary-cont); border-bottom-color: transparent; }
.dict-def td { background: var(--primary-cont); padding: 2px 10px 12px; }
.dict-def-txt { margin: 0 0 8px; font-size: 13.5px; line-height: 1.55; color: var(--text); max-width: 680px; }
.dict-def-pos { color: var(--text-3); }
.dict-zajawka { display: block; margin-top: 3px; font-size: 12.5px; line-height: 1.45; color: var(--text-3); max-width: 460px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Szerokość równa karcie wyżej (.dict-karta ma max-width: 600px), żeby oba
   guziki mieściły się w jej obrysie. */
.dict-def-akcje { display: flex; gap: 8px; align-items: center; max-width: 600px; margin-top: 10px; }

/* Ten sam kształt co „Przetłumacz to" w kartach przykładów (.q .use):
   obrys, zaokrąglenie 20px, przezroczyste tło. Guziki w treści mają wyglądać
   tak samo niezależnie od sekcji, w której stoją.

   Nazwy klas są polskie i neutralne — wtyczki blokujące zdejmują elementy,
   w których czytają „vote", „like" albo „favorite". */
.dictbtn {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--outline); color: var(--text-2);
  border-radius: 20px; padding: 6px 14px; font-size: 13px; line-height: 1.2;
  cursor: pointer; font-family: inherit;
  /* Lekko biały na tle panelu, pełny biały pod kursorem i na fokusie —
     guzik ma odcinać się od niebieskiego tła rozwinięcia, ale nie krzyczeć. */
  background: color-mix(in srgb, var(--surface) 55%, transparent);
}
.dictbtn:hover { background: var(--surface); }
.dictbtn:focus-visible { background: var(--surface); }
.dictbtn svg { width: 15px; height: 15px; fill: currentColor; flex: none; }
.dict-gwiazdka.on { color: var(--primary); border-color: transparent; background: var(--primary-cont); }
:root[data-theme="dark"] .dict-gwiazdka.on { color: #d3e3fd; }

/* Jedna kolumna, jeden wiersz na pozycję: favikona, tytuł, domena. Ikony
   są nasze — pobrane raz i leżące w repozytorium — więc strona dalej nie
   odpytuje cudzych serwerów przy każdym wejściu. Ładują się leniwie, bo
   sekcja źródeł leży daleko na dole i większość czytelników tam nie dojedzie. */
/* Źródła są dowodem, a nie lekturą: trzydzieści parę wierszy rozpychało
   stronę pod tabelą słownika, a czyta je ten, kto sprawdza. Zwinięte do paska
   favikon widać od razu, kto o tym pisał — Demagog, OKO.press, RMF, TVN —
   i to jest cała informacja, po którą sięga większość. Reszta po kliknięciu.

   <details> z rozmysłu: działa bez JavaScriptu, Ctrl+F w przeglądarce sam
   rozwija zamkniętą sekcję, a link do kotwicy dalej działa. */
.srcs-det > summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 12px; border: 1px solid var(--outline-2); border-radius: 12px;
  transition: background var(--t-hover);
}
.srcs-det > summary::-webkit-details-marker { display: none; }
.srcs-det > summary:hover { background: var(--container); }
.srcs-det > summary:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.src-strip { display: flex; align-items: center; gap: 6px; min-width: 0; overflow: hidden; }
.src-strip img { width: 16px; height: 16px; border-radius: 3px; flex: 0 0 auto; }
.src-label { font-size: 13px; color: var(--text-3); margin-inline-start: auto; }
/* Strzałka obraca się przy otwarciu — jedyny ruch, jakiego ta sekcja potrzebuje. */
.src-label::after {
  content: ' \25be'; display: inline-block; transition: transform var(--t-move);
}
.srcs-det[open] > summary .src-label::after { transform: rotate(180deg); }
.srcs-det[open] > summary { margin-bottom: 6px; }

.srcs { display: grid; gap: 1px; }
.srcs a {
  display: grid; grid-template-columns: 16px 1fr auto;
  align-items: baseline; gap: 10px;
  padding: 8px 10px; border-radius: 8px;
  text-decoration: none; color: var(--text); font-size: 14px;
}
.srcs a:hover { background: var(--container); }
.srcs a img { width: 16px; height: 16px; border-radius: 3px; align-self: center; }
.srcs a .noico {
  width: 16px; height: 16px; border-radius: 3px; align-self: center;
  background: var(--container-2);
}
.srcs a b { font-weight: 400; min-width: 0; }
.srcs a span { font-size: 12px; color: var(--text-3); overflow-wrap: anywhere; }
@media (max-width: 560px) {
  .srcs a { grid-template-columns: 16px 1fr; }
  .srcs a span { grid-column: 2; }
}

/* Numer i seria „Korpo dnia" — jedyna liczba na tej stronie, która ma
   znaczyć „wróć jutro". */
.kd-stan { display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: baseline; margin: 10px 0 14px; }
.kd-nr { font-weight: 500; color: var(--text); }
.kd-seria { font-size: 13px; color: var(--text-3); }

.note { background: var(--container); border-radius: 16px; padding: 18px 20px; font-size: 14px; color: var(--text-2); }
.note strong { color: var(--text); }
footer { text-align: center; padding: 32px 16px 60px; color: var(--text-3); font-size: 13px; }
footer a { color: var(--text-3); }
/* Zaproszenie do dorzucenia słowa jest jedyną prośbą na tej stronie, więc
   dostaje trochę więcej kontrastu niż reszta stopki — ale nie tyle, żeby
   konkurować z tłumaczem. */
.dorzuc { color: var(--text-2); max-width: 62ch; margin: 0 auto 18px; }
.dorzuc a { color: var(--primary); }
.dorzuc b { color: var(--text); font-weight: 500; }

/* ---------- responsywność ---------- */
@media (max-width: 900px) {
  main { --gutter: 12px; padding: 0 var(--gutter) 40px; }
  .panes { grid-template-columns: minmax(0, 1fr); }
  .langbar { grid-template-columns: 1fr auto 1fr; }
  .swap { width: 36px; height: 36px; }
  #src, .out { font-size: 19px; }
  .pane { min-height: 168px; }
  .quick { gap: 30px; margin: 40px 0 8px; }
  .below { padding: 48px 14px 72px; }
  .below h2 { font-size: 22px; }
  .money b { font-size: 25px; }
}
@media (max-width: 560px) {
  .hdr { padding: 6px 8px; }
  .wm { font-size: 21px; } .wm2 { font-size: 18px; }
  #btnApps { display: none; }
  .lang { padding: 9px 8px 11px; font-size: 13px; }
  /* Trzy zakładki po lewej potrzebują 226 px, a mają 163 px nawet na
     iPhonie 13, więc pasek zostaje listą rozwijaną: widać wybrany język,
     reszta chodzi pod chevronem. */
  .more { display: grid; }
  .langwrap:not(.open) .lang { display: none; }
  /* zwinięta etykieta musi się zmieścić w połowie 320-pikselowego ekranu */
  .langwrap:not(.open) .lang.on {
    display: block; font-size: 12.5px; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .langwrap.open { flex-wrap: wrap; }
  .langwrap.open .langs { flex-wrap: wrap; }
  .langbar { grid-template-columns: 1fr 40px 1fr; align-items: start; }
  .swap { margin-top: 2px; }
  /* Zwinięty pasek to lista rozwijana, a nie zakładki — jeden widoczny
     język na stronę, więc wskaźnik nie ma czego wskazywać. */
  .langwrap:not(.open) .langs .ind { opacity: 0 !important; }
}
/* Ozdobniki dokłada się tylko w stronę korpomowy. Przy tłumaczeniu na
   ludzki te trzy ustawienia nic nie zmieniają, więc zamiast udawać, że
   działają, gasną i mówią dlaczego. */
.row.na-korpo-only { opacity: .45; }
.row.na-korpo-only input { pointer-events: none; }

:focus-visible { outline: 3px solid var(--primary); outline-offset: 2px; border-radius: 4px; }

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

/* ---------- bio autora ---------- */
.bio { display: grid; grid-template-columns: 220px 1fr; gap: 26px; align-items: start; }
/* Tekst biogramu jest znacznie dłuższy od zdjęcia — bez sticky lewa kolumna
   to pół ekranu pustki. Wyłączone poniżej 700 px, gdzie kolumny się składają. */
.bio-pic { margin: 0; position: sticky; top: 24px; }
.bio-pic img {
  width: 100%; height: auto; border-radius: 20px; display: block;
  box-shadow: 0 1px 2px rgba(0,0,0,.12), 0 6px 18px rgba(0,0,0,.10);
}
.bio-pic figcaption {
  margin-top: 10px; font-size: 11.5px; line-height: 1.4; color: var(--text-3); font-style: italic;
}
.bio-txt p { margin: 0 0 13px; }
.bio-txt strong { color: var(--text); }
.bio-prompt {
  border-left: 3px solid var(--primary); padding-left: 14px; color: var(--text-2);
  font-size: 14px; margin-top: 18px !important;
}
.bio-links { display: flex; flex-wrap: wrap; gap: 8px; margin: 18px 0 10px; }
.bio-hire { font-size: 13px; color: var(--text-3); margin: 0 !important; }
@media (max-width: 700px) {
  .bio { grid-template-columns: 1fr; gap: 18px; }
  .bio-pic { max-width: 190px; position: static; }
}

/* ---------- deklaracja kontra wykonanie ---------- */
.vs { display: grid; gap: 10px; }
.vs-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.vs-a, .vs-b {
  border-radius: 16px; padding: 15px 17px; font-size: 14.5px; line-height: 1.5;
}
.vs-a p, .vs-b p { margin: 9px 0 0; }
.vs-a { background: var(--container); color: var(--text-2); }
.vs-b { background: var(--container-2); color: var(--text); }
.vs-tag {
  align-self: start; font-size: 10.5px; text-transform: uppercase; letter-spacing: .7px;
  font-weight: 600; color: var(--text-3); background: var(--surface);
  border-radius: 20px; padding: 3px 10px;
}
.vs-tag.warn { color: var(--danger); }
@media (max-width: 700px) { .vs-row { grid-template-columns: 1fr; } }

/* ---------- definicje pojęć ---------- */
.defs { margin-bottom: 20px; }
/* Bez odstępu w gridzie: gap rozdzielał także dt od dd, a że oba mają
   to samo tło, prześwit czytał się jako kreska przecinająca kafelek
   w poprzek. Odstęp między hasłami robi margines na dt. */
.defs dl { margin: 0; display: grid; gap: 0; }
.defs dt {
  font-weight: 600; font-size: 15px; color: var(--text);
  padding: 14px 18px 0; background: var(--container);
  border-radius: 16px 16px 0 0; margin-top: 10px;
}
.defs dd {
  margin: 0; padding: 6px 18px 15px; background: var(--container);
  border-radius: 0 0 16px 16px; font-size: 14.5px; line-height: 1.55; color: var(--text-2);
}
.defs dd em {
  color: var(--text); font-style: normal; background: var(--container-2);
  /* Wąski odstęp z rozmysłem: przy 6 px przecinek po „chipie" odskakiwał
     od słowa i wyglądał jak zgubiony. 4 px starczy, żeby tło czytało się
     jako etykieta, a interpunkcja nadal trzymała się wyrazu. */
  border-radius: 5px; padding: 1px 4px;
}

/* ---------- mocna teza ---------- */
.take {
  border-left: 3px solid var(--primary); background: var(--container);
  border-radius: 0 16px 16px 0; padding: 18px 22px; margin: 18px 0;
}
.take p { margin: 0; font-size: 15.5px; line-height: 1.6; }
.aside { font-size: 13.5px; color: var(--text-3); font-style: italic; }


/* ---------- łańcuch zleceń ---------- */
/* Numerowana lista, w której numer jest kółkiem na pionowej linii — czytelnie
   pokazuje, że to sekwencja, a nie zbiór punktów. */
.chain { list-style: none; margin: 18px 0 20px; padding: 0 0 0 30px; position: relative; counter-reset: ch; }
.chain::before {
  content: ''; position: absolute; left: 10px; top: 12px; bottom: 12px; width: 2px;
  background: linear-gradient(var(--primary), var(--primary) 55%, var(--text-3));
  opacity: .5; border-radius: 2px;
}
.chain li { position: relative; margin: 0 0 16px; counter-increment: ch; }
.chain li:last-child { margin-bottom: 0; }
.chain li::before {
  content: counter(ch); position: absolute; left: -30px; top: 1px;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--primary); color: #fff; font-size: 12px; font-weight: 600;
  display: grid; place-items: center;
  box-shadow: 0 0 0 4px var(--bg);
}
.chain li:last-child::before { background: var(--text-3); }
.chain b { display: block; font-size: 15.5px; font-weight: 600; }
.chain span { display: block; font-size: 14.5px; color: var(--text-2); line-height: 1.55; margin-top: 2px; }

/* ---------- pasek „dokument roboczy" ----------
   Żart polega na tym, że przycisk „Nie udostępniaj" udostępnia. Pasek
   wchodzi dopiero po decyzji JS, żeby bez skryptu nie wisiał na stałe. */
/* Nie sticky: na telefonie ten pasek ma 168 px i przykrywał listę ustawień.
   To jednorazowa notka, więc może odjechać razem ze stroną. */
.wip { background: var(--container); border-bottom: 1px solid var(--outline-2); }
/* Decyzja odczytana w <head>, więc pasek nigdy nie mruga ani nie przesuwa strony. */
html.wip-off .wip { display: none; }
.wip-in {
  max-width: 1100px; margin: 0 auto; padding: 10px 16px;
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
}
.wip-txt { margin: 0; flex: 1 1 320px; font-size: 13px; line-height: 1.5; color: var(--text-2); }
.wip-txt strong { color: var(--text); }
/* Trzy przyciski mają razem 353 px, więc na 320 px muszą się złamać.
   Z „flex: none" wystawały poza ekran i cała strona dostawała poziomy
   pasek przewijania — na najwęższym telefonie, czyli tam, gdzie boli. */
.wip-btns { display: flex; gap: 8px; flex: 0 1 auto; flex-wrap: wrap; }
.wip-wys, .wip-ok {
  border-radius: 999px; padding: 7px 14px; font-size: 13px; font-weight: 500;
  cursor: pointer; font-family: inherit; white-space: nowrap;
}
.wip-wys { border: 1px solid var(--outline); background: var(--surface); color: var(--text); }
.wip-ok { border: 0; background: var(--primary-cont); color: var(--primary); }
:root[data-theme="dark"] .wip-ok { color: #d3e3fd; }

/* ---------- pominięcie słownika ----------
   Tabela ma kilkaset wierszy. Kto jej nie potrzebuje, ma jedno kliknięcie
   do wyjścia; pojawia się dopiero, gdy słownik faktycznie jest na ekranie. */
/* Dwa wyjścia z tabeli: w górę do tłumacza i w dół poza słownik. Stoją
   razem w jednym pasku, bo to jedna decyzja: „nie chcę tu być". */
/* Każda pigułka jest osobnym elementem o pozycji fixed, bez wspólnego
   opakowania. To nie jest kaprys: opakowanie o pozycji fixed przestawało się
   malować — box, trafienia myszy i checkVisibility() nadal je widziały, ale na
   ekranie go nie było. Tak samo kończył się transform i translate na elemencie
   fixed oraz ustawienie naraz left i right. Działa to, co działało wcześniej:
   pojedynczy element, left w procentach, przesunięcie marginesem. */
.skipdict {
  position: fixed; bottom: calc(20px + env(safe-area-inset-bottom, 0px)); z-index: 55;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 10px 16px; border-radius: 999px;
  background: var(--text); color: var(--bg);
  font-size: 13.5px; font-weight: 500; text-decoration: none;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .22);
  white-space: nowrap;
  opacity: 0; transform: translateY(8px); pointer-events: none;
  transition: opacity var(--t-pop) var(--ease-out), transform var(--t-pop) var(--ease-out);
}
.skipdict.on { opacity: 1; transform: none; pointer-events: auto; }
.skipdict.alt { background: var(--surface); color: var(--text); border: 1px solid var(--outline); }
.skipdict:active { transform: scale(.97); }
.skipdict svg { width: 16px; height: 16px; }
@media (max-width: 560px) {
  .skipdict { bottom: calc(14px + env(safe-area-inset-bottom, 0px)); padding: 9px 13px; font-size: 13px; }
}

/* ---------- zanegowana stopka finansowania ---------- */
.fund {
  max-width: 760px; margin: 0 auto 18px; padding: 14px 16px;
  border: 1px dashed var(--outline); border-radius: 12px;
  font-size: 12.5px; line-height: 1.6; color: var(--text-3); text-align: left;
}
.fund b { color: var(--text-2); }

/* ============================================================================
   Ruch

   Reguły z animations.dev (Emil Kowalski). Streszczenie tego, czego się tu
   trzymam, bo inaczej za pół roku ktoś to rozjedzie:

   • rusza się wyłącznie transform i opacity — reszta wymusza layout i paint,
   • nigdy ease-in na UI: zaczyna wolno dokładnie w chwili, gdy użytkownik
     patrzy. Domyślnie mocne ease-out,
   • UI poniżej 300 ms. 180 ms wydaje się szybsze niż 400 ms, bo jest szybsze,
   • im częściej ktoś czegoś używa, tym mniej ruchu. Rzeczy klikane co chwilę
     nie animują się wcale,
   • nic nie wjeżdża od scale(0) — w realnym świecie nic nie powstaje z niczego,
   • hover z ruchem tylko tam, gdzie naprawdę jest kursor: dotyk zgłasza
     fałszywy hover przy każdym tapnięciu i element zostaje przesunięty,
   • przejścia, nie keyframes, dla wszystkiego, co da się odpalić dwa razy
     w sekundę — przejście przecelowuje z bieżącej wartości, keyframe startuje
     od zera,
   • wyjście tą samą drogą co wejście,
   • przy prefers-reduced-motion zostają wygaszenia, znika przemieszczanie.
     „Mniej i łagodniej”, a nie „zero”.
   ========================================================================== */

/* --- naciśnięcie ---------------------------------------------------------
   Jedyna reakcja, która musi paść w tej samej klatce co dotknięcie.
   scale() skaluje też dzieci — etykieta i ikona jadą razem, i dlatego
   czyta się to jako fizyczne wciśnięcie, a nie jako zmiana tła. */
.ic, .tab, .chip, .lang, .swap, .round, .segbtn, .use, .app, .more, .clear, .linkbtn, .dictbtn {
  transition:
    background-color var(--t-hover) ease,
    color var(--t-hover) ease,
    border-color var(--t-hover) ease,
    box-shadow var(--t-hover) ease,
    transform var(--t-press) var(--ease-out);
}
.ic:active, .swap:active, .clear:active, .more:active, .app:active { transform: scale(.96); }
.tab:active, .chip:active, .segbtn:active, .use:active, .linkbtn:active,
.dictbtn:active { transform: scale(.97); }
.round:active .rc { transform: scale(.96); }
.rc { transition: background-color var(--t-hover) ease, border-color var(--t-hover) ease,
                  transform var(--t-press) var(--ease-out); }

/* --- hover ---------------------------------------------------------------
   Bramka jest obowiązkowa. Bez niej tapnięcie w telefonie zostawia kartę
   uniesioną, a strzałkę zamiany obróconą do góry nogami. */
@media (hover: hover) and (pointer: fine) {
  .chip:hover:not([disabled]), .use:hover {
    transform: translateY(-1px);
    box-shadow: 0 2px 8px rgba(0, 0, 0, .07);
  }
  .q:hover, .srcs a:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 16px rgba(0, 0, 0, .08);
  }
  .bio-pic img:hover { transform: scale(1.02) rotate(-.6deg); }
  /* Podkład wjeżdża skalą, ikona unosi się o piksel. Dwa ruchy o różnej
     skali czytają się jak jeden gest, a nie jak podmiana tła. */
  .ic:hover:not([disabled])::before { opacity: 1; transform: scale(1); }
  .ic:hover:not([disabled]) svg { transform: translateY(-1px); }
}
.ic::before {
  transition: opacity var(--t-hover) ease, transform var(--t-hover) var(--ease-out);
}
.ic svg { transition: transform var(--t-press) var(--ease-out); }

/* --- nagroda za kliknięcie ------------------------------------------------
   Klik w X albo LinkedIna otwiera nowe okno, więc na stronie nie zostaje
   żaden ślad, że cokolwiek się stało. Jedno odbicie wystarczy: potwierdza,
   że przycisk zadziałał, i nie każe na siebie czekać. */
.ic.sm.sent svg { animation: sentPop 380ms var(--ease-out); }
@keyframes sentPop {
  0%   { transform: scale(1); }
  38%  { transform: scale(1.26); }
  100% { transform: scale(1); }
}
/* Zapisanie do kolekcji to zdobycz, więc gwiazdka dostaje przeskok
   z lekkim przechyleniem i zejściem poniżej rozmiaru — bez tego odbicie
   wygląda jak błąd renderowania, a nie jak reakcja. */
.ic.sm.saved svg { animation: savePop 460ms var(--ease-out); }
@keyframes savePop {
  0%   { transform: scale(1) rotate(0deg); }
  28%  { transform: scale(1.34) rotate(-9deg); }
  58%  { transform: scale(.93) rotate(5deg); }
  100% { transform: scale(1) rotate(0deg); }
}
.q, .srcs a, .money, .take, .vs-a, .vs-b {
  transition: transform var(--t-hover) var(--ease-out), box-shadow var(--t-hover) var(--ease-out);
}
.bio-pic img { transition: transform var(--t-move) var(--ease-out); }

/* --- pole źródłowe ------------------------------------------------------- */
.pane-src { transition: border-color var(--t-hover) ease, box-shadow var(--t-hover) ease; }
.pane-src:focus-within { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary); }

/* --- wskaźnik zakładek ---------------------------------------------------
   Jeden pasek, który przejeżdża pod wybrany język, zamiast dwóch osobnych
   animacji (jedna chowa, druga pokazuje). Pozycję liczy JS i podaje przez
   translateX/scaleX, więc animuje się sam transform. */
.langs { position: relative; }
.langs .ind {
  position: absolute; left: 0; bottom: 0; height: 3px; width: 1px;
  border-radius: 3px 3px 0 0; background: var(--primary);
  transform-origin: left bottom; opacity: 0;
  transition: transform var(--t-move) var(--ease-in-out), opacity var(--t-hover) ease;
  pointer-events: none;
}
/* Pierwsze ustawienie nie ma skąd przyjechać — nie animujemy go. */
.langs.no-anim .ind { transition: none; }
.lang { border-bottom-color: transparent !important; }

/* --- wynik ---------------------------------------------------------------
   Samą animację odpala WAAPI w index.js: jest przerywalna i nie wymaga
   sztuczki z wymuszaniem reflow, żeby zrestartować keyframe. */
.out { transition: opacity var(--t-pop) ease, filter var(--t-pop) ease; }
/* Tekst czekający na wynik modelu. Sama przygaszona opacity czyta się jak
   „wyłączone"; śladowe rozmycie czyta się jak „to się właśnie zmienia".
   Trzymamy się jednego piksela — filtr na tekście potrafi być drogi. */
.out.stale { opacity: .5; filter: blur(1px); }

/* --- kopiowanie: ikona zamienia się w ptaszka ---------------------------- */
.ic.sm.on { color: var(--primary); }
.ic.sm.done { color: var(--green); }
.ic.sm.done svg { animation: checkIn var(--t-pop) var(--ease-out); }
@keyframes checkIn {
  from { opacity: 0; transform: scale(.9); }
  to   { opacity: 1; transform: none; }
}

/* --- popovery ------------------------------------------------------------
   Wyjeżdżają z przycisku, który je otworzył (transform-origin), a nie
   ze środka ekranu. Przejście, nie keyframe, bo menu da się przełączyć
   dwa razy pod rząd. */
.pop {
  transform-origin: top right;
  opacity: 0; transform: scale(.96) translateY(-4px);
  transition: opacity var(--t-pop) var(--ease-out), transform var(--t-pop) var(--ease-out);
}
.pop.on { opacity: 1; transform: none; }

/* --- toast ---------------------------------------------------------------
   Wolniej i na ease zamiast ease-out: to jedyny element, który ma prawo
   być elegancki zamiast szybki. Wychodzi tą samą drogą, którą wszedł.
   translateY(100%) liczy się od własnej wysokości, więc działa niezależnie
   od długości komunikatu. */
.toast {
  opacity: 0; transform: translate(-50%, calc(100% + 26px));
  transition: opacity var(--t-toast) ease, transform var(--t-toast) ease;
}
.toast.on { opacity: 1; transform: translate(-50%, 0); }
/* Trofeum wjeżdża tą samą drogą co toast, tylko z prawego rogu. */
.trophy {
  opacity: 0; transform: translateY(calc(100% + 26px));
  transition: opacity var(--t-toast) ease, transform var(--t-toast) ease;
}
.trophy.on { opacity: 1; transform: translateY(0); }

/* --- pasek postępu -------------------------------------------------------
   Ruch ciągły → linear. Postęp nie ma prawa zwalniać na końcu. */
.prog-bar i { transition: width var(--t-move) var(--ease-out); position: relative; overflow: hidden; }
.prog-bar i::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .45), transparent);
  animation: shimmer 1.4s linear infinite;
}
@keyframes shimmer { from { transform: translateX(-100%); } to { transform: translateX(100%); } }

/* --- sekcje przy przewijaniu ---------------------------------------------
   Wolno tylko dlatego, że to część redakcyjna pod aplikacją — czyli
   powierzchnia, którą się czyta raz, a nie narzędzie używane codziennie.
   Odpala się jeden raz; ponowne animowanie przy każdym przewinięciu to
   interfejs walczący z czytelnikiem. */
/* Sterowane przewijaniem, nie JavaScriptem: przeglądarka liczy postęp sama,
   poza wątkiem głównym, więc nie ma szarpania przy scrollu. Co ważniejsze,
   nie ma stanu „obserwator nie zdążył" — a to on zostawiał na telefonie
   sekcje niewidoczne i wielkie dziury w środku strony.
   Gdzie tego nie ma (starsze Safari), blok się nie stosuje i wszystko jest
   po prostu widoczne. */
.below section { opacity: 1; }
@supports (animation-timeline: view()) {
  html[data-motion="on"] .below section {
    animation: sectionIn linear both;
    animation-timeline: view();
    /* Zakres w pikselach, nie w procentach. Procent liczy się od wysokości
       sekcji, a słownik ma 16 000 px — przy 35% trzeba by przewinąć pół
       kilometra, zanim zrobi się widoczny. 260 px to 260 px niezależnie
       od tego, jak wysoka jest sekcja. */
    animation-range: entry 0px entry 260px;
  }
  @keyframes sectionIn {
    from { opacity: 0; transform: translateY(12px); }
    to   { opacity: 1; transform: none; }
  }
}

/* --- motyw --------------------------------------------------------------- */
body, .pane-src, .pane-dst, .defs dt, .defs dd, .chain li::before {
  transition: background-color var(--t-move) ease, color var(--t-move) ease,
              border-color var(--t-move) ease;
}

/* --- ustawienie systemowe ------------------------------------------------
   Mniej i łagodniej, nie zero: wygaszenia i kolory zostają, bo pomagają
   zrozumieć, co się zmieniło. Znika przemieszczanie i skalowanie. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-property: opacity, background-color, color, border-color, box-shadow !important;
    transition-duration: var(--t-hover) !important;
  }
  .ic:active, .swap:active, .clear:active, .more:active, .app:active,
  .tab:active, .chip:active, .segbtn:active, .use:active, .linkbtn:active, .dictbtn:active,
  .round:active .rc, .chip:hover, .use:hover, .q:hover,
  .srcs a:hover, .bio-pic img:hover,
  .ic:hover:not([disabled]) svg, .ic:hover:not([disabled])::before { transform: none; }
  /* Podkład hovera zostaje, tyle że bez wjazdu — samo wygaszenie. */
  .ic:hover:not([disabled])::before { opacity: 1; }
  .toast { transform: translate(-50%, 0); }
  .trophy { transform: translateY(0); }
  .scrollhint path { animation: none; opacity: .6; }
  .out.stale { filter: none; }
  .pop { transform: none; }
  html[data-motion="on"] .below section { animation: none; opacity: 1; transform: none; }
  .prog-bar i::after { display: none; }
}

/* --- dotyk: żadna kontrolka poniżej 16 px --------------------------------- */
/* iOS Safari powiększa całą stronę, kiedy dostanie focus na polu mniejszym
   niż 16 px — i po schowaniu klawiatury nie wraca do poprzedniej skali.
   Użytkownik zostaje z przyciętą stroną i musi sam odszczypywać. To twardy
   próg WebKita, nie kwestia gustu, więc na dotyku podnosimy każdą kontrolkę
   do 16 px. Selektory z id muszą stać tutaj z id, bo samo `input` przegrywa
   z `#dictSearch` na specyficzności. Na myszy projekt zostaje bez zmian. */
@media (pointer: coarse) {
  input, textarea, select { font-size: 16px; }
  #dictSearch, #modelPick { font-size: 16px; }
}

/* Zamknięta karta jest klikalna — odsyła do tłumacza, gdzie karty się
   odkrywają. Bez tego kursor mówił „tu nic nie ma", a to nieprawda. */
.kdx-card.lock { cursor: pointer; }

/* ---------- dialog „wróć z komputera" ----------
   Telefon nie udźwignie modelu; wyszarzony przycisk wyglądał na zepsuty.
   Natywny <dialog>: fokus, Escape i backdrop robi przeglądarka. */
.dlg {
  border: 1px solid var(--outline-2); border-radius: 20px; padding: 24px 26px;
  max-width: min(420px, calc(100vw - 48px));
  background: var(--surface); color: var(--text);
  box-shadow: 0 18px 48px rgba(0, 0, 0, .28);
}
.dlg::backdrop { background: rgba(0, 0, 0, .45); }
.dlg h2 { margin: 0 0 10px; font-size: 19px; font-weight: 500; }
.dlg p { margin: 0 0 10px; font-size: 14.5px; line-height: 1.5; color: var(--text-2); }
.dlg-cicho { color: var(--text-3); font-size: 13px; }
.dlg-akcje {
  display: flex; align-items: center; justify-content: flex-end;
  gap: 8px; margin-top: 16px; flex-wrap: wrap;
}
.dlg .linkbtn { font-size: 13.5px; min-height: 44px; }
.dlg-ok {
  border: 0; border-radius: 999px; padding: 12px 18px; min-height: 44px;
  background: var(--primary); color: #fff;
  font-size: 14px; font-weight: 500; cursor: pointer;
}

/* ---------- arkusz udostępniania ----------
   Natywny arkusz systemu bywa nieobecny albo cichy, a to jedyna droga,
   którą ta strona rośnie. Własny arkusz stawia kanały wprost i pokazuje
   podgląd karty — nikt nie wysyła w ciemno czegoś, czego nie widział. */
.dlg-wys {
  max-width: min(460px, calc(100vw - 32px));
  max-height: calc(100dvh - 40px);
  padding: 18px 18px 20px;
  overflow: auto;
  overscroll-behavior: contain;
}
.wys-head {
  display: flex; align-items: center; gap: 12px; margin-bottom: 14px;
}
.dlg-wys h2 { margin: 0; flex: 1 1 auto; }
.wys-x {
  flex: none; width: 40px; height: 40px; border: 0; border-radius: 999px;
  background: none; color: var(--text-2); cursor: pointer;
  display: grid; place-items: center;
}
.wys-x svg { width: 20px; height: 20px; fill: currentColor; }
.wys-x:hover { background: var(--container); color: var(--text); }

.wys-body { display: grid; gap: 14px; }

/* Podgląd: tekst wpisu nad kartą, dokładnie w tej kolejności, w jakiej
   zobaczy to odbiorca na X-ie czy Slacku. */
.wys-prev { display: grid; gap: 10px; min-width: 0; }
.wys-prev[hidden] { display: none; }   /* display:grid zjadłby atrybut hidden */
.wys-txt {
  margin: 0; font-size: 13.5px; line-height: 1.45; color: var(--text-2);
  white-space: pre-wrap; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 6; line-clamp: 6;
  -webkit-box-orient: vertical; overflow: hidden;
}
/* Na niskim ekranie podgląd ustępuje kanałom: pod nim ma się jeszcze
   zmieścić pięć guzików, a to one są tu do klikania. Karta schodzi wtedy
   do postaci zwartej — miniatura obok tytułu, dokładnie tak, jak wąskie
   unfurle pokazuje Slack. Pełna karta zjadała cały ekran telefonu
   w poziomie i „Skopiuj link" lądował poniżej krawędzi. */
@media (max-height: 720px) {
  .wys-txt { -webkit-line-clamp: 2; line-clamp: 2; }
  .wys-karta { display: flex; align-items: stretch; }
  .wys-obrazek { width: 116px; flex: none; }
  .wys-meta {
    border-top: 0; border-left: 1px solid var(--outline-2);
    flex: 1 1 auto; min-width: 0; align-content: center; padding: 6px 10px;
  }
}
.wys-karta {
  border: 1px solid var(--outline-2); border-radius: 14px; overflow: hidden;
  background: var(--container);
}
.wys-obrazek {
  aspect-ratio: 1200 / 630; background: var(--container-2);
  display: block; overflow: hidden;
}
.wys-obrazek img { width: 100%; height: 100%; object-fit: cover; display: block; }
.wys-meta {
  display: grid; gap: 2px; padding: 8px 12px 10px;
  border-top: 1px solid var(--outline-2);
}
.wys-host { font-size: 11.5px; color: var(--text-3); }
.wys-tytul {
  font-size: 13px; font-weight: 500; color: var(--text);
  display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2;
  -webkit-box-orient: vertical; overflow: hidden;
}

.wys-akcje { display: grid; gap: 6px; align-content: start; }
.wys-btn {
  display: flex; align-items: center; gap: 12px; width: 100%;
  min-height: 48px; padding: 10px 16px;
  border: 1px solid var(--outline-2); border-radius: 14px;
  background: var(--surface); color: var(--text);
  font: inherit; font-size: 14.5px; text-align: left; cursor: pointer;
}
.wys-btn[hidden] { display: none; }
.wys-ico { width: 22px; height: 22px; flex: none; fill: currentColor; }
.wys-btn span { min-width: 0; }
.wys-btn:hover { background: var(--container); }
.wys-btn:active { background: var(--container-2); }
.wys-btn.sent { border-color: var(--primary); color: var(--primary); }

/* Na szerokim ekranie podgląd i kanały stają obok siebie: pionowa kolumna
   pięciu guzików pod kartą wychodziła na desktopie poza ekran. */
@media (min-width: 700px) and (min-height: 560px) {
  .dlg-wys { max-width: 720px; padding: 22px 24px 24px; }
  .wys-body { grid-template-columns: 1fr 1fr; gap: 22px; align-items: start; }
  /* Bez podglądu (plik z dysku, karta nie do pobrania) kanały biorą całą szerokość. */
  .wys-prev[hidden] + .wys-akcje { grid-column: 1 / -1; }
}
@media (max-width: 380px) {
  .wys-btn { font-size: 13.5px; gap: 10px; padding: 10px 12px; }
}

/* ---------- karta hasła w treści ----------
   Obrazki niosą się dalej niż tekst, a Grafika Google to osobny strumień
   wejść. Karta stoi tuż pod definicją, bo bliskość tekstu też jest sygnałem,
   i prowadzi wprost do PNG — „kopiuj adres obrazka" ma dawać plik. */
.dict-karta {
  display: block; max-width: 600px; margin: 14px 0 4px;
  border: 1px solid var(--outline-2); border-radius: 12px; overflow: hidden;
  background: var(--container);
}
.dict-karta img { display: block; width: 100%; height: auto; }
@media (hover: hover) and (pointer: fine) {
  .dict-karta:hover { border-color: var(--outline); }
}
