/**
 * Komponenty — utility CSS classes pro opakované UI prvky.
 * Filozofie: minimum classes, kompozitelné. Inspirováno shadcn/ui patternem.
 */

/* =====================
 * BUTTON
 * ===================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--spacing-2);
  height: 2.5rem;
  padding: 0 var(--spacing-4);
  font-size: var(--text-sm);
  font-weight: var(--font-medium);
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  transition: all var(--transition-fast);
  white-space: nowrap;
  cursor: pointer;
  text-decoration: none;
  /* CZ: Základ `.btn` byl do 17. 8. 2026 bez pozadí, rámečku i barvy textu — tlačítko bez
     varianty (`class="btn btn-sm"`) tak splynulo s podkladem a vypadalo jako text. Přesně na to
     narazil majitel: „brief a research pořád nemá button pro generování" — tlačítko tam bylo,
     jen nebylo vidět. Základ proto vypadá jako `.btn-outline`; varianty (`primary`, `danger`,
     `ghost`, `outline`) ho dál přepisují, protože stojí v souboru níž.
     EN: Until 17 Aug 2026 the `.btn` base had no background, border or text colour — a button with
     no variant (`class="btn btn-sm"`) blended into the surface and looked like text. That is
     exactly what the owner hit: "brief and research still has no generate button" — the button was
     there, it just was not visible. The base therefore looks like `.btn-outline`; the variants
     (`primary`, `danger`, `ghost`, `outline`) still override it, being further down the file. */
  background-color: var(--color-surface-base);
  border-color: var(--color-border-default);
  color: var(--color-text-primary);
}
.btn:hover:not(:disabled) { background-color: var(--color-surface-muted); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn:hover { text-decoration: none; }

.btn-primary {
  /* CZ: „Strong" accent jako pozadí — bílý text na něm = 4.98:1 (na #FF6B35 jen 2.84:1).
     EN: "Strong" accent background — white text on it = 4.98:1 (on #FF6B35 only 2.84:1). */
  background-color: var(--color-accent-strong);
  color: var(--color-text-inverse);
  box-shadow: var(--shadow-sm);
}
.btn-primary:hover:not(:disabled) { background-color: var(--color-accent-strong-hover); }
/* CZ: Stisknutý stav — jemné ztmavení/posun pro hmatatelnou zpětnou vazbu.
   EN: Pressed state — subtle darken/shift for tactile feedback. */
.btn-primary:active:not(:disabled) { background-color: var(--color-accent-strong-hover); transform: translateY(1px); }

.btn-secondary {
  background-color: var(--color-brand-primary);
  color: var(--color-on-brand-primary);
  box-shadow: var(--shadow-sm);
}
.btn-secondary:hover:not(:disabled) { background-color: var(--color-brand-primary-hover); }
.btn-secondary:active:not(:disabled) { background-color: var(--color-brand-primary-hover); transform: translateY(1px); }
/* CZ: Společný stisknutý stav pro ostatní varianty. EN: Shared pressed state for other variants. */
.btn-outline:active:not(:disabled),
.btn-ghost:active:not(:disabled),
.btn-danger:active:not(:disabled) { transform: translateY(1px); }

.btn-outline {
  background-color: transparent;
  border-color: var(--color-border-default);
  color: var(--color-text-primary);
}
.btn-outline:hover:not(:disabled) { background-color: var(--color-surface-muted); }

.btn-ghost {
  background-color: transparent;
  color: var(--color-text-primary);
}
.btn-ghost:hover:not(:disabled) { background-color: var(--color-surface-muted); }

.btn-danger {
  background-color: var(--color-danger);
  color: var(--color-text-inverse);
}
.btn-danger:hover:not(:disabled) { opacity: 0.9; }

.btn-sm { height: 2rem; padding: 0 var(--spacing-3); font-size: var(--text-xs); }
.btn-lg { height: 3rem; padding: 0 var(--spacing-6); font-size: var(--text-base); }
.btn-icon { width: 2.5rem; padding: 0; }

.btn-block { width: 100%; }

/* CZ: Na dotykových zařízeních zvětši malé ikonové/kompaktní cíle na WCAG 2.5.5 minimum
       (44×44px) — desktop myš zůstává kompaktní. EN: On touch devices enlarge small icon /
       compact targets to the WCAG 2.5.5 minimum (44×44px) — desktop mouse stays compact. */
@media (pointer: coarse) {
  .btn-icon, .btn-sm { min-height: 44px; min-width: 44px; }
}

/* =====================
 * INPUT
 * ===================== */
.input {
  display: block;
  width: 100%;
  height: 2.5rem;
  padding: 0 var(--spacing-3);
  font-size: var(--text-sm);
  background-color: var(--color-surface-base);
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-md);
  color: var(--color-text-primary);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.input::placeholder { color: var(--color-text-muted); }
.input:focus { outline: none; border-color: var(--color-accent-strong); box-shadow: var(--focus-ring); }
.input:disabled { opacity: 0.5; cursor: not-allowed; }
.input.is-invalid { border-color: var(--color-danger); }
.input.is-invalid:focus { box-shadow: var(--focus-ring-error); }

textarea.input { height: auto; min-height: 5rem; padding: var(--spacing-2) var(--spacing-3); resize: vertical; }
select.input { appearance: none; background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='%238A93A4' d='M3 5l5 5 5-5z'/></svg>"); background-repeat: no-repeat; background-position: right var(--spacing-3) center; padding-right: var(--spacing-8); }

/* =====================
 * FORM FIELD (label + input + error)
 * ===================== */
.field { display: flex; flex-direction: column; gap: var(--spacing-2); margin-bottom: var(--spacing-4); }
.field-label { font-size: var(--text-sm); font-weight: var(--font-medium); color: var(--color-text-primary); }
.field-required { color: var(--color-accent-text); margin-inline-start: var(--spacing-1); }
.field-hint { font-size: var(--text-xs); color: var(--color-text-muted); }
.field-error { font-size: var(--text-xs); color: var(--color-danger); font-weight: var(--font-medium); }

/* =====================
 * CARD
 * ===================== */
.card {
  background-color: var(--color-surface-base);
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.card-header { padding: var(--spacing-6); border-bottom: 1px solid var(--color-border-default); }
.card-body { padding: var(--spacing-6); }
.card-footer { padding: var(--spacing-4) var(--spacing-6); border-top: 1px solid var(--color-border-default); background-color: var(--color-surface-subtle); }
.card-title { font-size: var(--text-lg); font-weight: var(--font-semibold); font-family: var(--font-heading); }
.card-description { font-size: var(--text-sm); color: var(--color-text-muted); margin-top: var(--spacing-1); }

/* =====================
 * DATA CARD (KPI tile)
 * ===================== */
.data-card { background: var(--color-surface-base); border: 1px solid var(--color-border-default); border-radius: var(--radius-lg); padding: var(--spacing-5); transition: box-shadow var(--transition-fast); }
.data-card:hover { box-shadow: var(--shadow-md); }
.data-card-header { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--spacing-3); }
.data-card-label { font-size: var(--text-sm); font-weight: var(--font-medium); color: var(--color-text-secondary); }
.data-card-value { font-family: var(--font-heading); font-size: var(--text-3xl); font-weight: var(--font-bold); margin-top: var(--spacing-1); }
.data-card-delta { font-size: var(--text-xs); font-weight: var(--font-medium); margin-top: var(--spacing-1); }
/* CZ: Klikatelná KPI dlaždice — karta, která je zároveň odkaz nebo tlačítko (rozbalí seznam
       pod sebou). Resetuje nativní vzhled <button>, aby vypadala stejně jako <a> varianta,
       a kurzorem + hoverem dává najevo, že se na ni dá kliknout (dřív to byl mrtvý <div>).
   EN: A clickable KPI tile — a card that is also a link or a button (expands a list below it).
       Resets the native <button> look so it matches the <a> variant, and signals clickability
       via cursor + hover (it used to be a dead <div>). */
.card-clickable {
  display: block;
  width: 100%;
  cursor: pointer;
  font: inherit;
  color: inherit;
  text-align: inherit;
  text-decoration: none;
  background: var(--color-surface-base);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.card-clickable:hover { border-color: var(--color-brand-accent); box-shadow: var(--shadow-md); }
@media (prefers-reduced-motion: reduce) { .card-clickable { transition: none; } }
/* CZ: Směr růstu/poklesu nesmí být jen barvou (barvoslepost) — přidáváme i tvarový znak
       ▲/▼/— jako prefix. EN: Growth/drop direction must not rely on color alone (color
       blindness) — prepend a shape glyph ▲/▼/— as a non-color cue. */
.data-card-delta.up { color: var(--color-success); }
.data-card-delta.up::before { content: "▲\00a0"; }
.data-card-delta.down { color: var(--color-danger); }
.data-card-delta.down::before { content: "▼\00a0"; }
.data-card-delta.neutral { color: var(--color-text-muted); }
.data-card-delta.neutral::before { content: "—\00a0"; }
.data-card-icon { width: 2.5rem; height: 2.5rem; display: flex; align-items: center; justify-content: center; background: var(--color-brand-accent-soft); color: var(--color-accent-strong); border-radius: var(--radius-md); flex-shrink: 0; }

/* =====================
 * ALERT / BADGE
 * ===================== */
.alert { padding: var(--spacing-3) var(--spacing-4); border-radius: var(--radius-md); font-size: var(--text-sm); border: 1px solid; }
/* CZ: Text alertu = tmavá „*-text" varianta (čitelná na světlém tintu, ~6:1); border zůstává
       sytá sémantická barva pro barevné odlišení. V dark modu se oba tokeny přepínají.
   EN: Alert text = the dark "*-text" variant (readable on the light tint, ~6:1); border keeps
       the saturated semantic color for hue cueing. Both tokens flip in dark mode. */
.alert-info    { background: var(--color-info-soft);    border-color: var(--color-info);    color: var(--color-info-text); }
.alert-success { background: var(--color-success-soft); border-color: var(--color-success); color: var(--color-success-text); }
.alert-warning { background: var(--color-warning-soft); border-color: var(--color-warning); color: var(--color-warning-text); }
.alert-danger  { background: var(--color-danger-soft);  border-color: var(--color-danger);  color: var(--color-danger-text); }

.badge { display: inline-flex; align-items: center; padding: var(--spacing-1) var(--spacing-2); border-radius: var(--radius-full); font-size: var(--text-xs); font-weight: var(--font-medium); }
.badge-primary { background: var(--color-brand-primary); color: var(--color-on-brand-primary); }
.badge-accent  { background: var(--color-brand-accent-soft); color: var(--color-accent-text); }
.badge-muted   { background: var(--color-surface-muted); color: var(--color-text-secondary); }
/* CZ: Stavové badge — zelený „hotovo" a oranžový „varování" (stavové tokeny, dark-mode-safe).
   EN: Status badges — green "done" and orange "warning" (status tokens, dark-mode-safe). */
.badge-success { background: var(--color-success-soft); color: var(--color-success-text); }
.badge-warning { background: var(--color-warning-soft); color: var(--color-warning-text); }
/* CZ: Odznak pro zamítnuté / kritické události v auditu. Tokeny `--color-danger-*` už existovaly
       (mají i dark override), chyběla jen utility třída — proto se nezavádí žádná nová barva.
   EN: Badge for denied / critical audit events. The `--color-danger-*` tokens already existed (with a
       dark override too), only the utility class was missing — so no new colour is introduced. */
.badge-danger  { background: var(--color-danger-soft); color: var(--color-danger-text); }

/* =============================================
 * TYP AKCE — barevný štítek (cms.EventType)
 * =============================================
 * CZ: Rozlišení typů akcí v kalendáři (rozhodnutí majitele 14. 8.). Třídy `.event-type-<klíč>`
 *     NASTAVUJÍ jen dvojici lokálních proměnných; barvu z nich čte štítek `.event-type-chip`
 *     i řádek akce ve widgetu Komunita. Klíče odpovídají `cms.models.EventTypeColor` —
 *     přidání barvy = nová hodnota v TextChoices + jedna třída tady, žádný hex v šabloně.
 *     Dvojice je vždy „tint pozadí + tmavý/světlý text téže rodiny", takže kontrast drží AA
 *     v obou režimech (dark override řeší `tokens.css` u podkladových tokenů).
 * EN: Telling event types apart in the calendar (owner decision 14 Aug). The
 *     `.event-type-<key>` classes only SET a pair of local variables; the colour is read from
 *     them by the `.event-type-chip` and by the event row in the Community widget. The keys
 *     match `cms.models.EventTypeColor` — adding a colour means one more TextChoices value
 *     plus one class here, never a hex in a template. The pair is always "tint background +
 *     the family's dark/light text", so the contrast holds AA in both themes (the dark
 *     override lives on the underlying tokens in `tokens.css`). */
.event-type-neutral { --event-type-soft: var(--color-surface-muted); --event-type-text: var(--color-text-secondary); }
.event-type-brand   { --event-type-soft: var(--sb-brand-soft);  --event-type-text: var(--sb-brand-text); }
.event-type-lime    { --event-type-soft: var(--sb-lime-soft);   --event-type-text: var(--sb-lime-text); }
.event-type-orange  { --event-type-soft: var(--sb-orange-soft); --event-type-text: var(--sb-orange-text); }
.event-type-blue    { --event-type-soft: var(--sb-blue-soft);   --event-type-text: var(--sb-blue-text); }
.event-type-dark    { --event-type-soft: var(--sb-dark-soft);   --event-type-text: var(--sb-dark-text); }

.event-type-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-1);
  padding: var(--spacing-1) var(--spacing-2);
  border-radius: var(--radius-full);
  font-size: var(--text-xs);
  font-weight: var(--font-medium);
  line-height: 1.2;
  background: var(--event-type-soft);
  color: var(--event-type-text);
}

/* CZ: Ikona se nesmí smrsknout ve flexu ani přetéct nad text. EN: The icon must not shrink
       in the flex row nor overflow the text. */
.event-type-chip svg { flex: none; }

/* =====================
 * META LIST (definiční seznam „popisek → hodnota")
 * CZ: `<dl class="meta-list">` s páry `<dt>`/`<dd>` — firemní údaje, brief preview, karta
 *     leadu, koučský panel klienta. Třídu šablony používaly, ale v CSS chyběla (no-op),
 *     takže se páry vykreslovaly jako výchozí odsazený `<dl>` bez zarovnaného sloupce.
 *     Na velmi úzké obrazovce (≤480 px) se dvousloupcová mřížka sbalí pod sebe.
 * EN: `<dl class="meta-list">` with `<dt>`/`<dd>` pairs — company details, brief preview,
 *     lead card, coach client panel. Templates used the class but the CSS was missing
 *     (no-op), so the pairs rendered as a default indented `<dl>` with no aligned column.
 *     On very narrow screens (≤480px) the two-column grid collapses to stacked rows.
 * ===================== */
.meta-list {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  gap: var(--spacing-1) var(--spacing-3);
  margin: 0;
  font-size: var(--text-sm);
}
.meta-list dt { color: var(--color-text-muted); font-weight: var(--font-medium); }
/* CZ: `overflow-wrap` — dlouhé e-maily/URL nesmí roztáhnout mřížku přes šířku karty. */
/* EN: `overflow-wrap` — long emails/URLs must not stretch the grid past the card width. */
.meta-list dd { margin: 0; min-width: 0; color: var(--color-text-primary); overflow-wrap: anywhere; }
@media (max-width: 480px) {
  .meta-list { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .meta-list dd { margin-bottom: var(--spacing-2); }
}

/* =====================
 * SPINNER (točící se indikace probíhající operace)
 * CZ: Kroužek s jedním zvýrazněným segmentem; barva segmentu = `currentColor`, takže si
 *     bere barvu textu okolo (dark-mode-safe bez vlastního tokenu). Používá `_ai_job.html`
 *     u pollingu AI úlohy — element je `aria-hidden`, stav nese sousední text („Generuji…").
 * EN: A ring with one highlighted segment; the segment uses `currentColor`, so it inherits
 *     the surrounding text color (dark-mode-safe without its own token). Used by
 *     `_ai_job.html` while polling an AI job — the element is `aria-hidden`, the adjacent
 *     text carries the state ("Generating…").
 * ===================== */
.spinner {
  display: inline-block;
  flex: 0 0 auto;
  width: 1rem;
  height: 1rem;
  border: 2px solid var(--color-border-strong);
  border-top-color: currentColor;
  border-radius: var(--radius-full);
  animation: spinner-rotate 0.8s linear infinite;
}
@keyframes spinner-rotate { to { transform: rotate(360deg); } }
/* CZ: Bez rotace, když si uživatel vyžádal omezení pohybu — kroužek zůstane statický
       (informaci nese text vedle, takže se nic neztrácí).
   EN: No rotation when the user asked for reduced motion — the ring stays static (the
       adjacent text carries the information, so nothing is lost). */
@media (prefers-reduced-motion: reduce) {
  .spinner { animation: none; }
}

/* =====================
 * UTILITIES
 * ===================== */
.container { max-width: var(--max-content-width); margin: 0 auto; padding: 0 var(--spacing-4); }
.container-narrow { max-width: 640px; margin: 0 auto; padding: 0 var(--spacing-4); }
.container-reading { max-width: var(--max-reading-width); margin: 0 auto; padding: 0 var(--spacing-4); }

.stack-1 > * + * { margin-top: var(--spacing-1); }
.stack-2 > * + * { margin-top: var(--spacing-2); }
.stack-3 > * + * { margin-top: var(--spacing-3); }
.stack-4 > * + * { margin-top: var(--spacing-4); }
.stack-6 > * + * { margin-top: var(--spacing-6); }
.stack-8 > * + * { margin-top: var(--spacing-8); }

.grid { display: grid; gap: var(--spacing-4); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 768px) {
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
}
@media (min-width: 769px) and (max-width: 1024px) {
  .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); }
}

.text-xs { font-size: var(--text-xs); }
.text-sm { font-size: var(--text-sm); }
.text-base { font-size: var(--text-base); }
.text-lg { font-size: var(--text-lg); }
.text-muted { color: var(--color-text-muted); }
.text-secondary { color: var(--color-text-secondary); }
.text-danger { color: var(--color-danger); }
.text-success { color: var(--color-success); }
.text-center { text-align: center; }
/* CZ: `.text-right` je logický protějšek `.text-center` (číselné sloupce tabulek).
   EN: `.text-right` is the logical counterpart of `.text-center` (numeric table columns). */
.text-right { text-align: right; }
.font-medium { font-weight: var(--font-medium); }
.font-semibold { font-weight: var(--font-semibold); }
.font-bold { font-weight: var(--font-bold); }
/* CZ: Přeškrtnutí splněného úkolu (wizard _actions) — dosud no-op třída bez efektu.
   EN: Strike-through for a completed task (wizard _actions) — previously a no-op class. */
.line-through { text-decoration: line-through; }
/* CZ: Hover podbarvení řádku (dotazníky, konzultace) — třída „hover:bg-surface-muted"
       se v šablonách používá, ale bez definice nic nedělala. EN: Row hover tint (surveys,
       consultations) — the "hover:bg-surface-muted" class was used but had no definition. */
.hover\:bg-surface-muted:hover { background: var(--color-surface-muted); }

.flex { display: flex; }
.flex-col { flex-direction: column; }
/* CZ: .flex-wrap / .items-start — šablony je používaly, ale utility v CSS chyběly (no-op).
       Doplněním se aktivuje zalamování akčních/filtračních lišt na mobilu (žádná změna na
       desktopu, kde se řádek vejde a nezalomí). EN: .flex-wrap / .items-start — templates
       already used these but the utilities were missing (no-op). Adding them activates
       wrapping of action/filter rows on mobile (no change on desktop, where the row fits). */
.flex-wrap { flex-wrap: wrap; }
/* CZ: Zalomení POUZE na mobilu (≤768px). Pro řádky s full-width `.input` (width:100%) — ty by
       při bezpodmínečném .flex-wrap skočily na vlastní řádek i na desktopu; takto zůstane
       desktop řádek nezměněný a na mobilu se pole přehledně nastackují pod sebe.
   EN: Wrap ONLY on mobile (≤768px). For rows with full-width `.input` (width:100%) — an
       unconditional .flex-wrap would push them to their own line on desktop too; this keeps the
       desktop row intact and stacks the fields on mobile. */
@media (max-width: 768px) { .flex-wrap-mobile { flex-wrap: wrap; } }
.items-start { align-items: flex-start; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.justify-center { justify-content: center; }
.gap-1 { gap: var(--spacing-1); }
.gap-2 { gap: var(--spacing-2); }
.gap-3 { gap: var(--spacing-3); }
.gap-4 { gap: var(--spacing-4); }
.gap-6 { gap: var(--spacing-6); }

.mb-2 { margin-bottom: var(--spacing-2); }
/* CZ: `.mb-3` / `.mt-3` scházely, přestože je šablony napříč aplikací používaly (wizard,
       obsah, finance, koučské partialy) — byly to no-op třídy, mezera se nevykreslila.
       Doplněno do škály, ať šablony nemusí sahat po inline stylu.
   EN: `.mb-3` / `.mt-3` were missing even though templates across the app used them (wizard,
       content, financials, coach partials) — they were no-op classes and no spacing rendered.
       Added to the scale so templates don't have to fall back to inline styles. */
.mb-3 { margin-bottom: var(--spacing-3); }
.mb-4 { margin-bottom: var(--spacing-4); }
.mb-6 { margin-bottom: var(--spacing-6); }
.mt-2 { margin-top: var(--spacing-2); }
.mt-3 { margin-top: var(--spacing-3); }
.mt-4 { margin-top: var(--spacing-4); }
.mt-6 { margin-top: var(--spacing-6); }
.mt-8 { margin-top: var(--spacing-8); }
.py-2 { padding-top: var(--spacing-2); padding-bottom: var(--spacing-2); }
.py-4 { padding-top: var(--spacing-4); padding-bottom: var(--spacing-4); }
.py-6 { padding-top: var(--spacing-6); padding-bottom: var(--spacing-6); }
.py-12 { padding-top: var(--spacing-12); padding-bottom: var(--spacing-12); }

.w-full { width: 100%; }
.max-w-md { max-width: 28rem; }
.max-w-lg { max-width: 32rem; }

/* ---------------------------------------------------------------------------
   CZ: Responzivní viditelnost — .desktop-only se skryje na mobilu (≤768px),
       .mobile-only se skryje na tabletu/desktopu (>768px). Pro hustá rozhraní
       (např. KB tabulka), která nedávají na malé obrazovce smysl → místo nich
       se ukáže .mobile-only hláška.
   EN: Responsive visibility — .desktop-only hides on mobile (≤768px), .mobile-only
       hides on tablet/desktop (>768px). For dense UIs (e.g. the KB table) that don't
       make sense on a small screen → a .mobile-only notice is shown instead.
   --------------------------------------------------------------------------- */
.mobile-only { display: none; }
@media (max-width: 768px) {
  .desktop-only { display: none !important; }
  .mobile-only { display: block; }
}

/* CZ: Alpine x-cloak — skryje element, dokud se Alpine neinicializuje (zamezí probliknutí).
   EN: Alpine x-cloak — hides the element until Alpine initializes (prevents flicker). */
[x-cloak] { display: none !important; }

/* ---------------------------------------------------------------------------
   CZ: .sr-only — vizuálně skryté, ale čtené screen readerem (labely, popisky).
   EN: .sr-only — visually hidden but exposed to screen readers (labels, hints).
   --------------------------------------------------------------------------- */
.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;
}

/* ---------------------------------------------------------------------------
   CZ: Skip link „Přeskočit na obsah" — skrytý, dokud nedostane focus klávesnicí
       (WCAG 2.4.1). Pak vyjede přes topbar a odkáže na <main id="main-content">.
   EN: "Skip to content" link — hidden until it receives keyboard focus (WCAG 2.4.1).
       Then it slides over the topbar and targets <main id="main-content">.
   --------------------------------------------------------------------------- */
.skip-link {
  position: fixed;
  top: var(--spacing-2);
  left: var(--spacing-2);
  z-index: var(--z-tooltip);
  padding: var(--spacing-2) var(--spacing-4);
  background: var(--color-accent-strong);
  color: var(--color-text-inverse);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  transform: translateY(-150%);
  transition: transform var(--transition-fast);
}
.skip-link:focus { transform: translateY(0); text-decoration: none; }

/* ---------------------------------------------------------------------------
   CZ: Modal / potvrzovací dialog ve stylu aplikace (nahrazuje nativní confirm()).
   EN: App-styled modal / confirmation dialog (replaces the native confirm()).
   --------------------------------------------------------------------------- */
.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--spacing-4);
  background-color: rgba(0, 0, 0, 0.5);
}
.modal {
  width: 100%;
  max-width: 28rem;
  background-color: var(--color-surface-base);
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg, var(--shadow-sm));
  padding: var(--spacing-6);
}
.modal-title {
  font-size: var(--text-lg, 1.125rem);
  font-weight: 600;
  margin: 0 0 var(--spacing-2) 0;
}
.modal-body {
  color: var(--color-text-secondary, inherit);
  margin: 0 0 var(--spacing-6) 0;
}
.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--spacing-3);
}

/* =====================
 * VOICE INPUT (diktování — mikrofon u textových polí)
 * Generuje JS partial templates/partials/voice_input.html.
 * ===================== */
.voice-input-wrapper {
  position: relative;
  display: block;
  width: 100%;
}
.voice-mic-btn {
  position: absolute;
  right: var(--spacing-2);
  bottom: var(--spacing-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--spacing-1);
  width: 1.9rem;
  height: 1.9rem;
  color: var(--color-text-muted);
  background-color: var(--color-surface-base);
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-md);
  cursor: pointer;
  z-index: 5;
  transition: color var(--transition-fast), border-color var(--transition-fast),
    background-color var(--transition-fast);
}
/* CZ: U jednořádkového inputu je pole nižší — vycentruj mikrofon svisle. */
/* EN: Single-line inputs are shorter — vertically center the mic. */
.voice-input-wrapper > input.input + .voice-mic-btn { bottom: 50%; transform: translateY(50%); }
.voice-mic-btn:hover { color: var(--color-accent-strong); border-color: var(--color-accent-strong); }
.voice-mic-btn.recording {
  color: var(--color-danger);
  border-color: var(--color-danger);
  animation: voice-pulse 1.5s infinite;
}
.voice-mic-btn.processing { color: var(--color-accent-strong); border-color: var(--color-accent-strong); }
.voice-mic-btn.processing svg { animation: voice-spin 1s linear infinite; }
.voice-timer {
  position: absolute;
  right: 2.6rem;
  bottom: var(--spacing-2);
  font-size: 0.6875rem;
  font-weight: var(--font-medium);
  font-family: var(--font-mono, monospace);
  color: var(--color-text-muted);
  background-color: var(--color-surface-base);
  border: 1px solid var(--color-border-default);
  padding: 0 var(--spacing-1);
  border-radius: var(--radius-sm);
  z-index: 5;
}
.voice-timer.danger { color: var(--color-danger); border-color: var(--color-danger); }
@keyframes voice-pulse {
  0% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.45); }
  70% { box-shadow: 0 0 0 6px rgba(239, 68, 68, 0); }
  100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); }
}
@keyframes voice-spin { to { transform: rotate(360deg); } }
/* CZ: Toast pro chyby hlasového vstupu (odmítnutý mikrofon, selhání přepisu, rate limit).
   EN: Toast for voice-input errors (denied mic, failed transcription, rate limit). */
.voice-toast {
  position: fixed;
  right: var(--spacing-4);
  bottom: var(--spacing-4);
  max-width: 20rem;
  z-index: 1000;
  padding: var(--spacing-2) var(--spacing-3);
  font-size: 0.8125rem;
  line-height: 1.35;
  color: var(--color-on-danger);
  background-color: var(--color-danger);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  opacity: 0;
  transform: translateY(0.5rem);
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.voice-toast.visible { opacity: 1; transform: translateY(0); }

/* CZ: Globální toast pro HTMX chyby (audit §2.2) — tiché selhání HTMX akce zobrazí uživateli
       chybu místo „nic se nestalo". Stohovatelný kontejner vpravo dole.
   EN: Global toast for HTMX errors (audit §2.2) — a silently failing HTMX action shows the user
       an error instead of "nothing happened". Stackable container in the bottom-right. */
.app-toast-container {
  position: fixed;
  right: var(--spacing-4);
  bottom: var(--spacing-4);
  z-index: 1000;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-2);
  pointer-events: none;
}
.app-toast {
  max-width: 20rem;
  padding: var(--spacing-2) var(--spacing-3);
  font-size: 0.8125rem;
  line-height: 1.35;
  color: var(--color-on-danger);
  background-color: var(--color-danger);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}

/* CZ: Filtr chips — klikatelné pilulky (kategorie videí ap.). Aktivní = brand plocha.
   EN: Filter chips — clickable pills (video categories, etc.). Active = brand surface. */
.chip {
  display: inline-flex;
  align-items: center;
  padding: var(--spacing-1) var(--spacing-3);
  border-radius: var(--radius-full);
  border: 1px solid var(--color-border-default);
  background: transparent;
  color: var(--color-text-secondary);
  font-size: var(--text-sm);
  font-weight: var(--font-medium);
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
}
.chip:hover { background: var(--color-surface-muted); color: var(--color-text-primary); text-decoration: none; }
.chip:focus-visible { outline: 2px solid var(--color-accent-text); outline-offset: 2px; }
.chip-active,
.chip-active:hover { background: var(--color-brand-primary); color: var(--color-on-brand-primary); border-color: transparent; }

/* =====================
   CZ: Onboarding wizard (member-community v3) — progress lišta + krokové řádky.
       Vše přes design tokeny (dark-mode-safe), žádné hardcoded hodnoty.
   EN: Onboarding wizard (member-community v3) — progress bar + step rows.
       All via design tokens (dark-mode-safe), no hardcoded values.
   ===================== */
.onb-progress-track {
  height: var(--spacing-2);
  border-radius: var(--radius-full);
  background: var(--color-surface-muted);
  overflow: hidden;
}
.onb-progress-fill {
  height: 100%;
  border-radius: var(--radius-full);
  background: var(--color-success);
  transition: width var(--transition-normal);
}
.onb-step {
  display: flex;
  align-items: flex-start;
  gap: var(--spacing-4);
  padding: var(--spacing-4);
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-lg);
  background: var(--color-surface-subtle);
}
.onb-step.is-done { border-color: var(--color-success); }
.onb-step.is-next { border-color: var(--color-accent-strong); }
.onb-step-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-full);
  background: var(--color-surface-muted);
  color: var(--color-text-secondary);
  font-weight: var(--font-semibold);
  font-size: var(--text-sm);
}
.onb-step.is-done .onb-step-num { background: var(--color-success); color: var(--color-text-inverse); }
.onb-step-body { flex: 1 1 auto; min-width: 0; }
.onb-step-title { font-weight: var(--font-semibold); font-size: var(--text-base); }
.onb-step-desc { font-size: var(--text-sm); color: var(--color-text-muted); margin-top: var(--spacing-1); }
.onb-step-action { flex: 0 0 auto; align-self: center; }
@media (max-width: 640px) {
  .onb-step { flex-wrap: wrap; }
  .onb-step-action { align-self: stretch; width: 100%; }
  .onb-step-action .btn { width: 100%; }
}

/* =====================
   CZ: Konzultační wizard (consult-wizard UX) — dvousloupcový layout se sticky datovým
       panelem, sticky spodní lišta s primární akcí fáze, řádky otevřených závazků
       a sbalitelné sekce datového panelu. Vše přes tokeny (dark-mode-safe), bez hexů.
   EN: Consultation wizard (consult-wizard UX) — two-column layout with a sticky data
       panel, sticky bottom bar holding the phase's primary action, open-commitment rows
       and collapsible data-panel sections. All via tokens (dark-mode-safe), no hexes.
   ===================== */

/* CZ: Poměr 2:3 — datový panel je užší než pracovní plocha; `minmax(0, …)` brání přetečení
       dlouhým obsahem (grid item má implicitní min-width:auto).
   EN: 2:3 ratio — the data panel is narrower than the work area; `minmax(0, …)` prevents
       overflow from long content (a grid item defaults to min-width:auto). */
.wizard-split {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
  gap: var(--spacing-6);
  align-items: start;
}
/* CZ: Sticky offset musí počítat s topbarem (`layout.css .topbar`: sticky, top:0,
       height `--header-height`, z-index `--z-sticky`). Datový panel nemá vlastní stacking
       context ani z-index, takže by se při scrollu zasunul POD lištu a schoval hlavičku
       karty i sbalovací tlačítko. Stejný vzor jako `.sidebar` (top: var(--header-height)).
       `max-height` je o topbar zkrácená, aby panel nepřetekl pod spodní hranu viewportu.
   EN: The sticky offset must account for the topbar (`layout.css .topbar`: sticky, top:0,
       height `--header-height`, z-index `--z-sticky`). The data panel has no stacking
       context and no z-index, so it would slide UNDER the bar and hide the card header and
       its collapse button. Same pattern as `.sidebar` (top: var(--header-height)).
       `max-height` is shortened by the topbar so the panel does not run past the viewport. */
.wizard-split-data {
  position: sticky;
  top: calc(var(--header-height) + var(--spacing-6));
  align-self: start;
  max-height: calc(100vh - var(--header-height) - var(--spacing-12));
  overflow-y: auto;
}
.wizard-split-work { min-width: 0; }
/* CZ: Pod 1024 px jeden sloupec a sticky vypnuté — na malé výšce by panel ukrajoval obrazovku.
   EN: Below 1024px a single column with sticky off — on short viewports it would eat the screen. */
@media (max-width: 1024px) {
  .wizard-split { grid-template-columns: 1fr; }
  .wizard-split-data { position: static; max-height: none; overflow-y: visible; }
}

/* CZ: Spodní lišta s jedinou primární akcí fáze („Zahájit" / „K výstupům" / „Publikovat").
       `bottom: 0` + `--z-sticky` ji drží nad obsahem při scrollu pracovního sloupce.
   EN: Bottom bar with the phase's single primary action ("Start" / "To outputs" / "Publish").
       `bottom: 0` + `--z-sticky` keeps it above the content while the work column scrolls. */
.wizard-actionbar {
  position: sticky;
  bottom: 0;
  z-index: var(--z-sticky);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-3);
  margin-top: var(--spacing-6);
  padding: var(--spacing-3) var(--spacing-4);
  background: var(--color-surface-subtle);
  border-top: 1px solid var(--color-border-default);
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
}
.wizard-actionbar-info {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}
/* CZ: `flex-wrap: wrap` je povinné — `.btn` má `white-space: nowrap`, takže dvojice
       „Zrušit konzultaci" + primární akce by na 320–360 px lištu přetekla a stránka by
       dostala vodorovný scroll.
   EN: `flex-wrap: wrap` is required — `.btn` is `white-space: nowrap`, so the "Cancel
       consultation" + primary action pair would overflow the bar at 320–360px and give the
       whole page a horizontal scrollbar. */
.wizard-actionbar-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--spacing-2);
  flex: 0 0 auto;
}
/* CZ: Tlačítka NEJSOU přímé flex položky — „Zrušit konzultaci" je uvnitř `<div>` z
       `partials/confirm_modal.html` a primární akce uvnitř `<form>`. Roztahovat se proto
       musí ten obal, a tlačítko v něm dostane `width: 100%`. Selektory jdou jen o jednu
       úroveň hluboko, takže se NEtýkají tlačítek uvnitř `.modal-overlay` (ta jsou hlouběji).
   EN: The buttons are NOT direct flex items — "Cancel consultation" sits inside a `<div>`
       from `partials/confirm_modal.html` and the primary action inside a `<form>`. So the
       wrapper must stretch and the button inside it gets `width: 100%`. The selectors reach
       exactly one level deep, so buttons inside `.modal-overlay` (deeper) are unaffected. */
@media (max-width: 640px) {
  .wizard-actionbar { align-items: stretch; }
  .wizard-actionbar-actions { width: 100%; }
  .wizard-actionbar-actions > .btn,
  .wizard-actionbar-actions > form,
  .wizard-actionbar-actions > div { flex: 1 1 auto; min-width: 0; }
  .wizard-actionbar-actions > form > .btn,
  .wizard-actionbar-actions > div > .btn { width: 100%; }
}

/* CZ: Řádek otevřeného závazku (úkol z minulé konzultace / výzva). Levý pruh nese stav,
       aby se dal odlišit i periferním viděním v dlouhém seznamu.
   EN: Open-commitment row (task from a past consultation / challenge). The left rail carries
       the state so it is distinguishable at a glance in a long list. */
.commitment-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--spacing-3);
  padding: var(--spacing-3) var(--spacing-4);
  background: var(--color-surface-base);
  border: 1px solid var(--color-border-default);
  border-left: 3px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  transition: background var(--transition-fast), border-color var(--transition-fast);
}
.commitment-row:hover { background: var(--color-surface-subtle); }
.commitment-row-body { flex: 1 1 auto; min-width: 0; }
.commitment-row-text {
  font-size: var(--text-sm);
  font-weight: var(--font-medium);
  color: var(--color-text-primary);
}
.commitment-row-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--spacing-2);
  margin-top: var(--spacing-1);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.commitment-row-due { font-weight: var(--font-medium); }
.commitment-row-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--spacing-2);
  flex: 0 0 auto;
}

/* CZ: Po termínu — sytý stav: červený rám, tint pozadí a `*-text` varianta (na tintu ~6:1).
       Signál NENÍ jen barevný (barvoslepost) — termín dostane i tvarový prefix ⚠.
   EN: Overdue — loud state: danger border, tinted background and the `*-text` variant
       (~6:1 on the tint). The cue is not color-only (color blindness) — the due date also
       gets a ⚠ shape prefix. */
.commitment-row.commitment-overdue {
  background: var(--color-danger-soft);
  border-color: var(--color-danger);
  border-left-color: var(--color-danger);
}
.commitment-row.commitment-overdue:hover { background: var(--color-danger-soft); }
.commitment-overdue .commitment-row-due {
  color: var(--color-danger-text);
  font-weight: var(--font-semibold);
}
.commitment-overdue .commitment-row-due::before { content: "⚠\00a0"; }

@media (max-width: 640px) {
  .commitment-row { flex-direction: column; align-items: stretch; }
  .commitment-row-actions { width: 100%; }
}

/* CZ: Hlavička sbalitelné sekce datového panelu (<button aria-expanded>) — šipka se otáčí
       podle `aria-expanded`, takže stav drží ARIA, ne CSS třída navíc.
   EN: Header of a collapsible data-panel section (<button aria-expanded>) — the caret rotates
       from `aria-expanded`, so ARIA owns the state instead of an extra CSS class. */
.wizard-section-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-2);
  width: 100%;
  padding: var(--spacing-2) 0;
  background: transparent;
  border: 0;
  color: var(--color-text-primary);
  font-family: var(--font-heading);
  font-size: var(--text-sm);
  font-weight: var(--font-semibold);
  text-align: start;
  cursor: pointer;
}
.wizard-section-toggle:hover { color: var(--color-accent-text); }
.wizard-section-toggle:focus-visible { outline: 2px solid var(--color-accent-text); outline-offset: 2px; }
.wizard-section-toggle::after {
  content: "▾";
  flex: 0 0 auto;
  color: var(--color-text-muted);
  transition: transform var(--transition-fast);
}
.wizard-section-toggle[aria-expanded="false"]::after { transform: rotate(-90deg); }

/* CZ: Seznamy poznámek / úkolů / navržených otázek ve wizardu. Šablony tyto třídy používaly,
       ale v CSS neexistovaly (no-op) — odrážky a odsazení proto řešil inline `style`.
       Definice je záměrně shodná s tím inline stylem, takže se nic nepřekreslí jinak;
       šablony můžou inline styl odstranit (viz požadavek na T2).
   EN: Note / action / suggested-question lists in the wizard. Templates used these classes
       but they did not exist in the CSS (no-op) — bullets and indentation were handled by an
       inline `style`. The definition deliberately matches that inline style, so nothing
       renders differently; the templates can then drop the inline style (see the T2 request). */
.note-list,
.action-list,
.question-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.note-item,
.action-item {
  padding: var(--spacing-2) 0;
  border-bottom: 1px solid var(--color-border-default);
}
/* CZ: Poslední řádek bez dělící linky — jinak vypadá seznam „useknutě" u paty karty. */
/* EN: No divider on the last row — otherwise the list looks cut off at the card footer. */
.note-item:last-child,
.action-item:last-child { border-bottom: 0; }

/* CZ: Bez animací, když si to uživatel přeje (prefers-reduced-motion).
   EN: No animation when the user asks for it (prefers-reduced-motion). */
@media (prefers-reduced-motion: reduce) {
  .commitment-row,
  .wizard-section-toggle::after { transition: none; }
}

/* ===========================================================================
 * STAVOVÉ KOMPONENTY / STATE COMPONENTS
 * CZ: Styly pro sdílené partialy `partials/empty_state.html`,
 *     `partials/htmx_indicator.html` a `partials/form_errors.html`.
 * EN: Styles for the shared partials `partials/empty_state.html`,
 *     `partials/htmx_indicator.html` and `partials/form_errors.html`.
 * =========================================================================== */

/* ---------------------------------------------------------------------------
   CZ: Prázdný stav seznamu — centrovaný blok s ikonou, nadpisem, popisem a CTA.
       Text je tlumený (muted), aby prázdno nekřičelo; nadpis zůstává v primární
       barvě, protože nese informaci, a CTA (.btn-primary) je vizuální kotva.
   EN: List empty state — a centered block with icon, title, description and CTA.
       The copy is muted so emptiness does not shout; the title keeps the primary
       color because it carries the information, and the CTA is the visual anchor.
   --------------------------------------------------------------------------- */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--spacing-2);
  padding: var(--spacing-10) var(--spacing-6);
  text-align: center;
  background: var(--color-surface-subtle);
  border: 1px dashed var(--color-border-default);
  border-radius: var(--radius-lg);
}

/* CZ: Ikona je dekorace — tlumená barva, ať nekonkuruje nadpisu ani CTA.
   EN: The icon is decoration — muted so it competes with neither title nor CTA. */
.empty-state-icon {
  font-size: var(--text-3xl);
  line-height: var(--leading-tight);
  color: var(--color-text-muted);
}

.empty-state-title {
  margin: 0;
  font-family: var(--font-heading);
  font-size: var(--text-lg);
  font-weight: var(--font-semibold);
  color: var(--color-text-primary);
}

.empty-state-message {
  max-width: var(--max-reading-width);
  margin: 0;
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
  color: var(--color-text-muted);
}

/* CZ: Odsazení CTA od textu — `gap` sám dává jen základní mezeru, tady chceme víc.
   EN: Extra room above the CTA — `gap` alone gives only the base spacing. */
.empty-state-action { margin-top: var(--spacing-2); }

/* CZ: Kompaktní varianta do karet a užších sloupců (méně vertikálního prostoru).
   EN: Compact variant for cards and narrow columns (less vertical padding). */
.empty-state-sm { padding: var(--spacing-6) var(--spacing-4); }
.empty-state-sm .empty-state-icon { font-size: var(--text-2xl); }
.empty-state-sm .empty-state-title { font-size: var(--text-base); }

/* ---------------------------------------------------------------------------
   CZ: HTMX indikátor načítání. Skrývání řeší base.css (`.htmx-indicator { opacity: 0 }`
       a `.htmx-request .htmx-indicator { opacity: 1 }`) — htmx přidá `.htmx-request`
       na spouštěcí prvek po dobu requestu. Tady se to jen doplňuje: schovaný
       indikátor nesmí chytat kliky (byl by neviditelná past nad obsahem).
       Záměrně NEpoužíváme `display: none`, jinak by přechod opacity neproběhl a
       rozbily by se existující indikátory v šablonách.
   EN: HTMX loading indicator. Hiding lives in base.css (`.htmx-indicator { opacity: 0 }`
       and `.htmx-request .htmx-indicator { opacity: 1 }`) — htmx puts `.htmx-request` on
       the triggering element for the duration of the request. This only completes it: a
       hidden indicator must not capture clicks (it would be an invisible trap over the
       content). We deliberately avoid `display: none`, which would kill the opacity
       transition and break the indicators already used across templates.
   --------------------------------------------------------------------------- */
.htmx-indicator { pointer-events: none; }
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator { pointer-events: auto; }

/* CZ: Vizuál sdíleného indikátoru — spinner + volitelný popisek na jednom řádku.
       Animaci nese `.spinner`, který má vlastní `prefers-reduced-motion` fallback výše.
   EN: Look of the shared indicator — spinner plus optional label on one line. The
       animation lives in `.spinner`, which has its own `prefers-reduced-motion` fallback. */
.loading-indicator {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-2);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  vertical-align: middle;
}
.loading-indicator-label { line-height: var(--leading-snug); }

/* CZ: Blokový režim — indikátor na vlastním řádku, vycentrovaný (typicky pod seznamem
       při „načíst další" nebo nad oblastí, kterou HTMX prohazuje).
   EN: Block mode — the indicator on its own centered line (typically under a list for
       "load more", or above the region HTMX swaps). */
.loading-indicator-block {
  display: flex;
  justify-content: center;
  width: 100%;
  padding: var(--spacing-4) 0;
}

/* ---------------------------------------------------------------------------
   CZ: Souhrn chyb nad formulářem (GOV.UK „error summary"). Kontrast: pozadí je světlý
       tint `--color-danger-soft`, text proto `--color-danger-text` (~6:1) — sytá
       `--color-danger` by na tintu dala jen ~3:1 a propadla by AA. Levý pruh navíc
       nese signál tvarem, ne jen barvou (barvoslepost).
   EN: Error summary above a form (GOV.UK style). Contrast: the background is the light
       `--color-danger-soft` tint, so the text uses `--color-danger-text` (~6:1) — the
       saturated `--color-danger` would be only ~3:1 on the tint and fail AA. The left
       rail carries the signal by shape, not by color alone (color blindness).
   --------------------------------------------------------------------------- */
.form-errors {
  padding: var(--spacing-4);
  margin-bottom: var(--spacing-4);
  background: var(--color-danger-soft);
  border: 1px solid var(--color-danger);
  border-left: 4px solid var(--color-danger);
  border-radius: var(--radius-md);
  color: var(--color-danger-text);
}
/* CZ: Blok se fokusuje programově (tabindex="-1") — focus musí být vidět.
   EN: The block is focused programmatically (tabindex="-1") — the focus must be visible. */
.form-errors:focus-visible {
  outline: 2px solid var(--color-danger);
  outline-offset: 2px;
}

.form-errors-title {
  margin: 0 0 var(--spacing-2);
  font-family: var(--font-heading);
  font-size: var(--text-base);
  font-weight: var(--font-semibold);
  color: var(--color-danger-text);
}

.form-errors-list {
  margin: 0;
  padding-left: var(--spacing-5);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
}
.form-errors-item + .form-errors-item { margin-top: var(--spacing-1); }

/* CZ: Odkazy na vadná pole — podtržení navíc k barvě, aby odkaz nebyl rozpoznatelný
       jen barvou (WCAG 1.4.1).
   EN: Links to the offending fields — underlined on top of the color, so the link is not
       identified by color alone (WCAG 1.4.1). */
.form-errors-link {
  color: var(--color-danger-text);
  font-weight: var(--font-medium);
  text-decoration: underline;
}
.form-errors-link:hover { text-decoration-thickness: 2px; }
.form-errors-link:focus-visible {
  outline: 2px solid var(--color-danger);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* ===========================================================================
 * ATOMICKÉ UTILITY / ATOMIC UTILITIES
 * CZ: Doplněk k utilitám výše — pokrývá inline `style=""` deklarace, které se
 *     v šablonách opakovaly nejčastěji (margin/padding/gap/šířky/flex/text).
 *     Cíl: šablona nemá důvod sáhnout po inline stylu. Všechny hodnoty jdou
 *     z tokenů (`--spacing-*`, `--text-*`, `--color-*`, `--radius-*`), takže
 *     dark mode i změna škály se propíší automaticky. Nic výše se nemění —
 *     tato sekce jen přidává. Výjimky bez tokenu: 0, 100%, auto, 1px rám
 *     a rem hodnoty `max-w-*` (drží se stávající škály `.max-w-md/.max-w-lg`).
 * EN: Companion to the utilities above — covers the inline `style=""` declarations
 *     that repeated most often across templates (margin/padding/gap/width/flex/text).
 *     Goal: a template never needs an inline style. All values come from tokens
 *     (`--spacing-*`, `--text-*`, `--color-*`, `--radius-*`), so dark mode and any
 *     scale change propagate automatically. Nothing above is modified — this section
 *     only adds. Token-less exceptions: 0, 100%, auto, the 1px border and the rem
 *     `max-w-*` values (matching the existing `.max-w-md/.max-w-lg` scale).
 * =========================================================================== */

/* ---------------------------------------------------------------------------
   CZ: MARGIN — `.m-0` je zdaleka nejčastější inline styl (reset `<p>`/`<h*>`/`<ul>`
       uvnitř karet). `.mx-auto` centruje blok s `max-width`.
   EN: MARGIN — `.m-0` is by far the most common inline style (resetting `<p>`/`<h*>`/
       `<ul>` inside cards). `.mx-auto` centers a block that has a `max-width`.
   --------------------------------------------------------------------------- */
.m-0 { margin: 0; }
.mx-auto { margin-left: auto; margin-right: auto; }
.ml-auto { margin-left: auto; }
.mt-0 { margin-top: 0; }
.mt-1 { margin-top: var(--spacing-1); }
.mt-5 { margin-top: var(--spacing-5); }
.mt-12 { margin-top: var(--spacing-12); }
.mb-0 { margin-bottom: 0; }
.mb-1 { margin-bottom: var(--spacing-1); }
.mb-5 { margin-bottom: var(--spacing-5); }
.mb-8 { margin-bottom: var(--spacing-8); }
.mb-12 { margin-bottom: var(--spacing-12); }
.ml-1 { margin-left: var(--spacing-1); }
.ml-2 { margin-left: var(--spacing-2); }
.mr-1 { margin-right: var(--spacing-1); }
.mr-2 { margin-right: var(--spacing-2); }

/* ---------------------------------------------------------------------------
   CZ: PADDING — `.p-*` je všesměrové, `.px-*`/`.py-*` osové, `.pt-*`/`.pb-*` jednostranné.
       Kombinace `padding: var(--spacing-2) var(--spacing-3)` = `.py-2 .px-3`.
   EN: PADDING — `.p-*` is all sides, `.px-*`/`.py-*` axial, `.pt-*`/`.pb-*` single side.
       The `padding: var(--spacing-2) var(--spacing-3)` combo = `.py-2 .px-3`.
   --------------------------------------------------------------------------- */
.p-0 { padding: 0; }
.p-1 { padding: var(--spacing-1); }
.p-2 { padding: var(--spacing-2); }
.p-3 { padding: var(--spacing-3); }
.p-4 { padding: var(--spacing-4); }
.p-5 { padding: var(--spacing-5); }
.p-6 { padding: var(--spacing-6); }
.px-0 { padding-left: 0; padding-right: 0; }
.px-1 { padding-left: var(--spacing-1); padding-right: var(--spacing-1); }
.px-2 { padding-left: var(--spacing-2); padding-right: var(--spacing-2); }
.px-3 { padding-left: var(--spacing-3); padding-right: var(--spacing-3); }
.px-4 { padding-left: var(--spacing-4); padding-right: var(--spacing-4); }
.px-6 { padding-left: var(--spacing-6); padding-right: var(--spacing-6); }
.py-0 { padding-top: 0; padding-bottom: 0; }
.py-1 { padding-top: var(--spacing-1); padding-bottom: var(--spacing-1); }
.py-3 { padding-top: var(--spacing-3); padding-bottom: var(--spacing-3); }
.py-8 { padding-top: var(--spacing-8); padding-bottom: var(--spacing-8); }
.pt-0 { padding-top: 0; }
.pt-1 { padding-top: var(--spacing-1); }
.pt-2 { padding-top: var(--spacing-2); }
.pt-3 { padding-top: var(--spacing-3); }
.pt-4 { padding-top: var(--spacing-4); }
.pb-0 { padding-bottom: 0; }
.pb-1 { padding-bottom: var(--spacing-1); }
.pb-2 { padding-bottom: var(--spacing-2); }
.pb-3 { padding-bottom: var(--spacing-3); }
.pb-4 { padding-bottom: var(--spacing-4); }

/* ---------------------------------------------------------------------------
   CZ: GAP — doplnění škály (`.gap-1`–`.gap-4`, `.gap-6` už existují výše).
       Pozor na mapování z inline hodnot: `.25rem` = `--spacing-1`, `.5rem` =
       `--spacing-2`, `.75rem` = `--spacing-3`, `1rem` = `--spacing-4`.
   EN: GAP — completes the scale (`.gap-1`–`.gap-4`, `.gap-6` already exist above).
       Mapping from raw inline values: `.25rem` = `--spacing-1`, `.5rem` =
       `--spacing-2`, `.75rem` = `--spacing-3`, `1rem` = `--spacing-4`.
   --------------------------------------------------------------------------- */
.gap-0 { gap: 0; }
.gap-5 { gap: var(--spacing-5); }
.gap-8 { gap: var(--spacing-8); }

/* ---------------------------------------------------------------------------
   CZ: DISPLAY — `.hidden` je tvrdé skrytí (nezaměňovat s `.sr-only`, které nechává
       obsah čitelný pro screen reader, ani s `[x-cloak]`, které řeší Alpine).
   EN: DISPLAY — `.hidden` hides outright (not to be confused with `.sr-only`, which
       keeps content readable for screen readers, nor with `[x-cloak]` for Alpine).
   --------------------------------------------------------------------------- */
.block { display: block; }
.inline-block { display: inline-block; }
.inline { display: inline; }
.inline-flex { display: inline-flex; }
.hidden { display: none; }

/* ---------------------------------------------------------------------------
   CZ: FLEX & GRID položky — `.min-w-0` je nutné u flex/grid potomků s dlouhým
       textem (implicitní `min-width: auto` jinak přeteče a stránka dostane
       vodorovný scroll). `.grid-full` roztáhne položku přes všechny sloupce.
   EN: FLEX & GRID items — `.min-w-0` is required on flex/grid children holding long
       text (the implicit `min-width: auto` otherwise overflows and gives the page a
       horizontal scrollbar). `.grid-full` spans an item across all columns.
   --------------------------------------------------------------------------- */
.flex-row { flex-direction: row; }
.flex-1 { flex: 1 1 0%; }
.flex-auto { flex: 1 1 auto; }
.flex-none { flex: 0 0 auto; }
.flex-shrink-0 { flex-shrink: 0; }
.min-w-0 { min-width: 0; }
.grid-full { grid-column: 1 / -1; }
.items-baseline { align-items: baseline; }
.items-end { align-items: flex-end; }
.items-stretch { align-items: stretch; }
.justify-start { justify-content: flex-start; }
.justify-end { justify-content: flex-end; }
.self-center { align-self: center; }
.self-start { align-self: flex-start; }
.self-end { align-self: flex-end; }

/* ---------------------------------------------------------------------------
   CZ: ROZMĚRY — `.max-w-*` navazuje na existující škálu `.max-w-md` (28rem) /
       `.max-w-lg` (32rem). `.max-w-reading` a `.max-w-content` jdou z layout tokenů.
   EN: SIZING — `.max-w-*` continues the existing `.max-w-md` (28rem) / `.max-w-lg`
       (32rem) scale. `.max-w-reading` and `.max-w-content` come from layout tokens.
   --------------------------------------------------------------------------- */
.w-auto { width: auto; }
.h-full { height: 100%; }
.h-auto { height: auto; }
.max-w-full { max-width: 100%; }
.max-w-sm { max-width: 24rem; }
.max-w-xl { max-width: 36rem; }
.max-w-2xl { max-width: 42rem; }
.max-w-3xl { max-width: 48rem; }
.max-w-4xl { max-width: 56rem; }
.max-w-5xl { max-width: 64rem; }
.max-w-reading { max-width: var(--max-reading-width); }
.max-w-content { max-width: var(--max-content-width); }

/* ---------------------------------------------------------------------------
   CZ: TYPOGRAFIE — dorovnání velikostní škály (`--text-xl` a výš) a zarovnání.
       Barvy textu jen přes tokeny; na světlém pozadí se pro accent používá
       `--color-accent-text` (4.98:1), NIKDY `--color-brand-accent` (2.84:1).
   EN: TYPOGRAPHY — completes the size scale (`--text-xl` and up) and alignment.
       Text colors via tokens only; on a light surface the accent is
       `--color-accent-text` (4.98:1), NEVER `--color-brand-accent` (2.84:1).
   --------------------------------------------------------------------------- */
.text-xl { font-size: var(--text-xl); }
.text-2xl { font-size: var(--text-2xl); }
.text-3xl { font-size: var(--text-3xl); }
.text-4xl { font-size: var(--text-4xl); }
.text-5xl { font-size: var(--text-5xl); }
.text-left { text-align: left; }
.text-primary { color: var(--color-text-primary); }
.text-inverse { color: var(--color-text-inverse); }
.text-accent { color: var(--color-accent-text); }
.text-warning { color: var(--color-warning-text); }
.text-info { color: var(--color-info-text); }
.text-inherit { color: inherit; }
.font-normal { font-weight: var(--font-normal); }
.leading-tight { line-height: var(--leading-tight); }
.leading-snug { line-height: var(--leading-snug); }
.leading-normal { line-height: var(--leading-normal); }
.uppercase { text-transform: uppercase; }
.no-underline { text-decoration: none; }
.underline { text-decoration: underline; }
/* CZ: Tabulární číslice — čísla ve sloupci se nerozjíždějí (finanční tabulky, KPI).
   EN: Tabular figures — numbers in a column stay aligned (financial tables, KPIs). */
.tabular-nums { font-variant-numeric: tabular-nums; }
.whitespace-nowrap { white-space: nowrap; }
.whitespace-pre-wrap { white-space: pre-wrap; }
.whitespace-pre-line { white-space: pre-line; }
/* CZ: Dlouhé e-maily/URL nesmí roztáhnout kartu — zalom i uprostřed slova.
   EN: Long emails/URLs must not stretch a card — allow mid-word breaking. */
.break-words { overflow-wrap: anywhere; word-break: break-word; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* CZ: Odkaz bez vizuálu odkazu — celá karta/řádek je klikatelná, ale text si drží
       barvu okolí. Používat JEN tam, kde je klikatelnost zřejmá z kontextu (celá
       karta), ne pro odkaz uvnitř odstavce — tam by chyběl netextový signál.
   EN: A link without link styling — the whole card/row is clickable while the text
       keeps the surrounding color. Use ONLY where clickability is obvious from the
       context (a whole card), never for an inline link inside a paragraph. */
.link-plain { color: inherit; text-decoration: none; }
.link-plain:hover { text-decoration: none; }

/* ---------------------------------------------------------------------------
   CZ: SEZNAMY — `.list-none` ruší jen odrážky; `.list-reset` navíc smaže margin a
       padding (nejčastější kombinace `list-style:none; margin:0; padding:0`).
   EN: LISTS — `.list-none` only drops the bullets; `.list-reset` also clears margin
       and padding (the common `list-style:none; margin:0; padding:0` trio).
   --------------------------------------------------------------------------- */
.list-none { list-style: none; }
.list-reset { list-style: none; margin: 0; padding: 0; }

/* ---------------------------------------------------------------------------
   CZ: RÁMY, POZADÍ, ZAOBLENÍ — vždy tokeny, aby dark mode přepnul barvu sám.
   EN: BORDERS, BACKGROUNDS, RADII — always tokens, so dark mode flips them for free.
   --------------------------------------------------------------------------- */
.border { border: 1px solid var(--color-border-default); }
.border-top { border-top: 1px solid var(--color-border-default); }
.border-bottom { border-bottom: 1px solid var(--color-border-default); }
.border-none { border: 0; }
.border-collapse { border-collapse: collapse; }
.rounded-sm { border-radius: var(--radius-sm); }
.rounded-md { border-radius: var(--radius-md); }
.rounded-lg { border-radius: var(--radius-lg); }
.rounded-xl { border-radius: var(--radius-xl); }
.rounded-full { border-radius: var(--radius-full); }
.bg-base { background: var(--color-surface-base); }
.bg-subtle { background: var(--color-surface-subtle); }
.bg-muted { background: var(--color-surface-muted); }
.bg-transparent { background: transparent; }

/* ---------------------------------------------------------------------------
   CZ: OSTATNÍ — `.overflow-x-auto` je povinný obal pro široké tabulky (jinak
       roluje celá stránka vodorovně, viz docs/FE_RESPONSIVE.md).
   EN: MISC — `.overflow-x-auto` is the mandatory wrapper for wide tables (otherwise
       the whole page scrolls horizontally, see docs/FE_RESPONSIVE.md).
   --------------------------------------------------------------------------- */
.overflow-x-auto { overflow-x: auto; max-width: 100%; }
.overflow-hidden { overflow: hidden; }
.relative { position: relative; }
.absolute { position: absolute; }
.object-cover { object-fit: cover; }
.cursor-pointer { cursor: pointer; }

/* CZ: --- AI obsah (.ai-content) — vyrenderovaný Markdown z briefů, shrnutí a chatu. ---
       Modely vracejí Markdown; filtr `ai_text` (web/templatetags/ai_extras.py) a klientský
       `renderAiMarkdown` (static/js/chat.js) ho převedou na HTML. Dřív se vypisoval syrově,
       takže uživatel viděl `## Nadpis` a `**tučně**`. Tyhle styly dávají výsledku rytmus —
       bez nich by nadpisy a odrážky splynuly s okolním textem.
   EN: --- AI content (.ai-content) — rendered Markdown from briefs, summaries and chat. ---
       Models return Markdown; the `ai_text` filter (web/templatetags/ai_extras.py) and the
       client-side `renderAiMarkdown` (static/js/chat.js) turn it into HTML. It used to be
       printed raw, so users saw `## Heading` and `**bold**`. These styles give the result
       rhythm — without them headings and bullets would blend into the surrounding text. */
.ai-content > *:first-child { margin-top: 0; }
.ai-content > *:last-child { margin-bottom: 0; }
.ai-content p { margin: 0 0 var(--spacing-3); line-height: var(--leading-normal); }
.ai-content h1,
.ai-content h2,
.ai-content h3,
.ai-content h4,
.ai-content h5,
.ai-content h6 {
  margin: var(--spacing-4) 0 var(--spacing-2);
  font-weight: var(--font-semibold);
  color: var(--color-text-primary);
  line-height: var(--leading-snug);
}
.ai-content h1 { font-size: var(--text-lg); }
.ai-content h2 { font-size: var(--text-lg); }
.ai-content h3 { font-size: var(--text-base); }
.ai-content h4,
.ai-content h5,
.ai-content h6 { font-size: var(--text-sm); }
.ai-content ul,
.ai-content ol { margin: 0 0 var(--spacing-3); padding-inline-start: var(--spacing-5); }
.ai-content li { margin-bottom: var(--spacing-1); line-height: var(--leading-normal); }
.ai-content strong { font-weight: var(--font-semibold); color: var(--color-text-primary); }
.ai-content a { color: var(--color-accent-text); }
.ai-content code {
  font-size: var(--text-sm);
  background: var(--color-surface-muted);
  padding: 0 var(--spacing-1);
  border-radius: var(--radius-sm);
}
.ai-content pre {
  background: var(--color-surface-muted);
  padding: var(--spacing-3);
  border-radius: var(--radius-md);
  overflow-x: auto;
  max-width: 100%;
  margin: 0 0 var(--spacing-3);
}
.ai-content pre code { background: none; padding: 0; }
.ai-content blockquote {
  margin: 0 0 var(--spacing-3);
  padding-inline-start: var(--spacing-3);
  border-inline-start: 3px solid var(--color-border-default);
  color: var(--color-text-muted);
}
.ai-content hr { border: 0; border-top: 1px solid var(--color-border-default); margin: var(--spacing-4) 0; }
/* CZ: Tabulky z Markdownu se na mobilu musí scrollovat, ne rozbít stránku.
   EN: Markdown tables must scroll on mobile rather than break the page. */
.ai-content table { width: 100%; border-collapse: collapse; margin: 0 0 var(--spacing-3); display: block; overflow-x: auto; }
.ai-content th,
.ai-content td { border: 1px solid var(--color-border-default); padding: var(--spacing-2); text-align: left; }

/* ==========================================================================
   CZ: Widgety dashboardu — kategorie SCL a stavy prázdna (2026-08-12)
       Nahradilo ECharts (1 030 900 B), který se na dashboard tahal kvůli dvěma
       widgetům. Vše přes tokeny, žádný hardcoded hex — jinak by to v dark mode
       zůstalo světlé (viz CLAUDE.md „Design tokeny").
   EN: Dashboard widgets — SCL categories and empty states (2026-08-12)
       Replaced ECharts (1,030,900 B), pulled in for just two widgets. Everything
       via tokens, no hardcoded hex — otherwise dark mode would stay light.
   ========================================================================== */

.scl-categories {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-3);
}

.scl-category { min-width: 0; }

.scl-bar-track {
  height: 8px;
  margin-top: var(--spacing-1);
  border-radius: var(--radius-full);
  background-color: var(--color-border-default);
  overflow: hidden;
}

.scl-bar-fill {
  height: 100%;
  border-radius: var(--radius-full);
  background-color: var(--color-brand-accent);
}

/* CZ: Odznak „ve vývoji" (`.badge-wip`) byl odstraněn 2026-08-13. Zavedl se ve fázi 0,
       ale při widgetizaci zmizel ze všech šablon a zbyla jen tahle nepoužívaná definice —
       ověřeno, že se v `templates/` nevyskytuje ani jednou. Prázdné stavy dnes vysvětlují
       situaci textem (`empty_state.html`, parametr `note`), ne odznakem.
   EN: The "in development" badge (`.badge-wip`) was removed on 2026-08-13. Introduced in
       phase 0, it vanished from every template during widgetization, leaving only this unused
       definition — verified absent from `templates/`. Empty states now explain the situation
       in words (`empty_state.html`, the `note` parameter) rather than with a badge. */

/* CZ: Poznámka pod prázdným stavem — vysvětluje, PROČ je prázdno (stav vývoje).
   EN: Note under an empty state — explains WHY it is empty (development status). */
.empty-state-note {
  margin-top: var(--spacing-3);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

/* ==========================================================================
   CZ: HERO PÁS dashboardu (redesign 2026-08-13)
       Tmavý brand povrch s teplým světlem (--gradient-hero). Jediné odvážné místo
       návrhu — brand oranžová tu funguje jako text i světlo, protože podklad je navy
       (kontrast ≈ 6:1); zbytek stránky zůstává klidný. Text VŽDY přes
       --color-on-brand-primary (bílá v obou režimech), NE --color-text-inverse.
   EN: The dashboard HERO band (redesign 2026-08-13)
       A dark brand surface with a warm light (--gradient-hero). The design's single
       bold spot — brand orange works here as text and light because the ground is navy
       (contrast ≈ 6:1); the rest of the page stays calm. Text ALWAYS via
       --color-on-brand-primary (white in both themes), NOT --color-text-inverse.
   ========================================================================== */

.dash-hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "main score"
    "setup setup";
  gap: var(--spacing-6);
  background-color: var(--color-surface-hero);
  background-image: var(--gradient-hero);
  color: var(--color-on-brand-primary);
  border-radius: var(--radius-widget);
  padding: var(--spacing-8);
  margin-bottom: var(--spacing-6);
}

/* CZ: V dark režimu má --color-surface-subtle stejnou hodnotu jako brand navy — hrana
       hero by splynula s pozadím stránky, proto 1px linka (viz zadání redesignu).
   EN: In dark mode --color-surface-subtle equals brand navy — the hero edge would merge
       with the page background, hence the 1px line (see the redesign brief). */
html.dark .dash-hero { border: 1px solid var(--color-border-default); }

.dash-hero-main { grid-area: main; min-width: 0; }

.dash-hero-date {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  opacity: 0.65;
  margin-bottom: var(--spacing-3);
}

.dash-hero-title {
  font-family: var(--font-heading);
  font-size: var(--text-3xl);
  font-weight: var(--font-bold);
  letter-spacing: var(--tracking-tight);
  line-height: 1.2;
  color: var(--color-on-brand-primary);
  margin-bottom: var(--spacing-6);
}

.dash-hero-title-accent { display: block; color: var(--color-brand-accent); }

.dash-hero-command {
  display: flex;
  align-items: center;
  gap: var(--spacing-3);
  background-color: var(--color-surface-hero-inset);
  border-radius: var(--radius-full);
  padding: var(--spacing-2) var(--spacing-2) var(--spacing-2) var(--spacing-5);
}

.dash-hero-prompt {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--color-on-brand-primary);
  opacity: 0.6;
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  padding: var(--spacing-2) 0;
}

.dash-hero-prompt:hover { opacity: 0.9; }

.dash-hero-ask { flex: none; border-radius: var(--radius-full); }

.dash-hero-chips {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--spacing-2);
  margin-top: var(--spacing-3);
}

.dash-hero-chip {
  display: inline-flex;
  align-items: center;
  padding: var(--spacing-1) var(--spacing-3);
  border-radius: var(--radius-full);
  background-color: var(--color-surface-hero-inset);
  color: var(--color-on-brand-primary);
  font-size: var(--text-xs);
  font-weight: var(--font-medium);
}

.dash-hero-note { font-size: var(--text-xs); opacity: 0.55; }

.dash-hero-score { grid-area: score; text-align: right; align-self: start; }

.dash-hero-score-label {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  opacity: 0.65;
  margin-bottom: var(--spacing-2);
}

.dash-hero-score-value {
  font-family: var(--font-heading);
  font-size: var(--text-5xl);
  font-weight: var(--font-bold);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.dash-hero-score-delta {
  display: inline-block;
  margin-top: var(--spacing-2);
  padding: var(--spacing-1) var(--spacing-3);
  border-radius: var(--radius-full);
  background-color: var(--color-surface-hero-inset);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
}

/* CZ: Proužek nastavení — tenká dráha přes celou šířku, oranžová jen jako PLOCHA výplně.
   EN: The setup strip — a thin full-width track; orange only as the fill SURFACE. */
.dash-hero-setup {
  grid-area: setup;
  display: flex;
  align-items: center;
  gap: var(--spacing-4);
  padding-top: var(--spacing-4);
  border-top: 1px solid var(--color-surface-hero-inset);
}

.dash-hero-setup-label { font-size: var(--text-sm); opacity: 0.8; flex: none; }

.dash-hero-setup-track {
  flex: 1 1 auto;
  display: flex;
  gap: var(--spacing-2);
}

.dash-hero-setup-seg {
  flex: 1 1 0;
  height: 3px;
  border-radius: var(--radius-full);
  background-color: var(--color-surface-hero-inset);
}

.dash-hero-setup-seg.is-done { background-color: var(--color-brand-accent); }

.dash-hero-setup-cta {
  flex: none;
  color: var(--color-on-brand-primary);
  font-weight: var(--font-semibold);
  font-size: var(--text-sm);
  text-decoration: none;
}

.dash-hero-setup-cta:hover { color: var(--color-brand-accent); }

/* CZ: Mobil — skóre pod uvítání, vše pod sebe; proužek se zalomí do sloupce. Hero se
       zhušťuje (menší titulek i skóre, dekorativní sloupky pryč), aby nezabíralo
       většinu první obrazovky telefonu.
   EN: Mobile — the score drops below the greeting; the strip stacks into a column. The
       hero densifies (smaller title and score, decorative bars gone) so it does not eat
       most of the phone's first screen. */
@media (max-width: 768px) {
  .dash-hero {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "main" "score" "setup";
    padding: var(--spacing-5);
  }
  .dash-hero-title { font-size: var(--text-2xl); margin-bottom: var(--spacing-4); }
  .dash-hero-score { text-align: left; }
  .dash-hero-score-value { font-size: var(--text-4xl); }
  /* CZ: Čistá dekorace — na telefonu jen přidává výšku. Vyšší specificita: základní
         `.dash-hero-bars { display: flex }` je v souboru definované AŽ ZA tímto media
         blokem a při shodné specificitě by vyhrálo.
     EN: Pure decoration — on a phone it only adds height. Higher specificity: the base
         `.dash-hero-bars { display: flex }` is defined AFTER this media block in the
         file and would win on equal specificity. */
  .dash-hero-score .dash-hero-bars { display: none; }
  .dash-hero-setup { flex-wrap: wrap; }
  .dash-hero-command { flex-wrap: wrap; padding: var(--spacing-3); }
  .dash-hero-prompt { white-space: normal; }
}

/* ==========================================================================
   CZ: KPI PÁS (redesign 2026-08-13) — čtyři metriky pod herem, bez rámečků a stínů,
       jen 1px dělítka. Nízká hustota schválně: rytmus stránky dělá kontrast
       hero → pás → karty. Na mobilu 2×2.
   EN: The KPI STRIP (2026-08-13 redesign) — four metrics under the hero, no borders
       or shadows, only 1px separators. Deliberately low density: the page rhythm
       comes from the hero → strip → cards contrast. 2×2 on mobile.
   ========================================================================== */

/* CZ: KPI pás má od 17. 8. 2026 ŠEST buněk, ne čtyři — přibyl čistý zisk a provozní cash flow
       (rozhodnutí majitele: hero má nést obrat, marži, zisk a cash flow, a prioritní kroky
       i další sezení mají zůstat). `auto-fit` s minimem 9rem místo pevných čtyř sloupců:
       při šesti buňkách na 1fr by se popisky jako „Provozní cash flow" zalomily doprostřed.
   EN: Since 17 Aug 2026 the KPI strip holds SIX cells, not four — net profit and operating cash
       flow were added (owner's decision: the hero carries revenue, margin, profit and cash flow,
       and the priority steps and next session stay). `auto-fit` with a 9rem minimum instead of a
       fixed four columns: at six cells on 1fr, captions like "Provozní cash flow" would wrap
       mid-phrase. */
.dash-kpi {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  margin-bottom: var(--spacing-8);
}

.dash-kpi-cell {
  padding: var(--spacing-2) var(--spacing-5);
  border-left: 1px solid var(--color-border-default);
  min-width: 0;
}

.dash-kpi-cell:first-child { border-left: 0; padding-left: 0; }

.dash-kpi-label {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--color-text-muted);
  margin-bottom: var(--spacing-2);
}

.dash-kpi-value {
  font-family: var(--font-heading);
  font-size: var(--text-2xl);
  font-weight: var(--font-bold);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

.dash-kpi-unit {
  font-size: var(--text-sm);
  font-weight: var(--font-medium);
  color: var(--color-text-muted);
  margin-left: var(--spacing-1);
}

.dash-kpi-empty { color: var(--color-text-muted); }

@media (max-width: 768px) {
  .dash-kpi {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    row-gap: var(--spacing-5);
  }
  /* CZ: V mřížce 2×2 začíná každý řádek bez dělítka; menší hodnoty, ať dlouhá čísla
         („1 000 000 tis. Kč") nezalamují buňku přes tři řádky.
     EN: In the 2×2 grid each row starts without a separator; smaller values so long
         figures ("1 000 000 tis. Kč") do not wrap a cell onto three lines. */
  .dash-kpi-cell:nth-child(odd) { border-left: 0; padding-left: 0; }
  .dash-kpi-cell { padding-right: var(--spacing-3); }
  .dash-kpi-value { font-size: var(--text-xl); }
}

/* ==========================================================================
   CZ: POHYB (redesign, fáze 6) — jeden přechod (--transition-emphasized) na všechno
       nové, střídmě. Vstupní animace jede JEN při prvním vykreslení stránky: třídu
       `dash-entering` nasazuje dashboard_widgets.js na <body> při DOMContentLoaded a
       po doběhnutí ji sundá — HTMX swapy (resize/reorder/remove/polling) už třídu
       nemají, takže plocha při mutacích neposkakuje. `prefers-reduced-motion` řeší
       globální pravidlo v base.css (animation/transition zkrátí) — neduplikovat.
   EN: MOTION (redesign, phase 6) — one transition (--transition-emphasized) for all
       new movement, sparingly. The entrance animation runs ONLY on the first page
       render: dashboard_widgets.js puts the `dash-entering` class on <body> at
       DOMContentLoaded and removes it when done — HTMX swaps (resize/reorder/remove/
       polling) lack the class, so the desk never jumps on mutations. The global rule
       in base.css handles `prefers-reduced-motion` (it shortens animation/transition)
       — do not duplicate it.
   ========================================================================== */

/* CZ: Užší vycentrovaný sloupec dashboardu (mockup 3) — stránka dýchá, karty se
       neroztahují přes celý viewport.
   EN: The dashboard's narrower centred column (mockup 3) — the page breathes and the
       cards no longer span the whole viewport. */
.dash-shell {
  /* CZ: 1320 px kvůli třem sloupcům (½+¼+¼) — v 1140 px by úzké sloupce byly ~250 px
         a obsah (taby Komunity, datumové řádky) by se lámal.
     EN: 1320 px for the three columns (½+¼+¼) — at 1140 px the narrow columns would be
         ~250 px and content (Community tabs, date rows) would wrap badly. */
  max-width: 1320px;
  margin: 0 auto;
}

/* CZ: Řádek akce v Komunitě — datumový čtvereček, obsah, mono „za X" štítek.
   EN: A Community event row — the date square, the body, a mono "in X" tag. */
.community-event {
  display: flex;
  align-items: flex-start;
  gap: var(--spacing-3);
  padding: var(--spacing-2) 0;
  border-bottom: 1px solid var(--color-border-default);
}

.community-event:last-child { border-bottom: 0; }

/* CZ: Datový čtvereček se barví podle typu akce (`.event-type-*` třída na stejném prvku);
       bez typu se `--event-type-*` nepřepíše a zůstane výchozí (šablona v takovém případě
       dosadí oranžovou rodinu, aby vzhled widgetu odpovídal stavu před 14. 8.).
   EN: The date square takes the event type's colour (an `.event-type-*` class on the same
       element); without a type `--event-type-*` stays at its default (the template then falls
       back to the orange family so the widget looks as it did before 14 Aug). */
.community-event-day {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: var(--radius-md);
  background-color: var(--event-type-soft);
  color: var(--event-type-text);
  font-family: var(--font-heading);
  font-weight: var(--font-bold);
}

.community-event-body { flex: 1 1 auto; min-width: 0; }

.community-event-until {
  flex: none;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--event-type-text);
  white-space: nowrap;
  padding-top: var(--spacing-1);
}

/* CZ: Plné outline tlačítko pod panelem (Všechny akce / skupiny / adresář, Napsat …).
   EN: The full-width outline button under a panel (All events / groups / directory, Write …). */
.community-more {
  display: block;
  width: 100%;
  text-align: center;
  margin-top: var(--spacing-4);
}

/* CZ: Tvůj tým — kulatý avatar s iniciálami (žádný obrázek).
   EN: Your team — a round initials avatar (no image). */
.team-member {
  display: flex;
  align-items: flex-start;
  gap: var(--spacing-3);
}

.team-member-body { min-width: 0; }

.team-avatar {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--radius-full);
  background-color: var(--sb-dark-soft);
  color: var(--sb-dark-text);
  font-family: var(--font-heading);
  font-weight: var(--font-semibold);
  text-transform: uppercase;
}

/* CZ: Dekorativní sloupky v hero (mockup) — pevné výšky přes nth-child, accent jako
       světlo. Žádná data, žádná knihovna; aria-hidden v šabloně.
   EN: The hero's decorative bars (mockup) — fixed nth-child heights, the accent as
       light. No data, no library; aria-hidden in the template. */
.dash-hero-bars {
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
  gap: 3px;
  height: 2rem;
  margin-top: var(--spacing-3);
}

.dash-hero-bars i {
  width: 5px;
  border-radius: var(--radius-sm);
  background-color: var(--color-brand-accent);
  opacity: 0.55;
  height: 20%;
}

.dash-hero-bars i:nth-child(2) { height: 30%; }
.dash-hero-bars i:nth-child(3) { height: 26%; }
.dash-hero-bars i:nth-child(4) { height: 42%; }
.dash-hero-bars i:nth-child(5) { height: 38%; }
.dash-hero-bars i:nth-child(6) { height: 55%; }
.dash-hero-bars i:nth-child(7) { height: 48%; }
.dash-hero-bars i:nth-child(8) { height: 70%; }
.dash-hero-bars i:nth-child(9) { height: 62%; opacity: 0.75; }
.dash-hero-bars i:nth-child(10) { height: 90%; opacity: 1; }

@keyframes dash-widget-in {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: translateY(0); }
}

@keyframes dash-bar-in {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

.dash-entering .widget { animation: dash-widget-in var(--transition-emphasized) both; }

/* CZ: Stagger ~70 ms — výchozí plocha má 7 karet, zbytek nastupuje s posledním krokem.
   EN: A ~70 ms stagger — the default desk has 7 cards; the rest enters with the last step. */
.dash-entering .widget:nth-child(2) { animation-delay: 70ms; }
.dash-entering .widget:nth-child(3) { animation-delay: 140ms; }
.dash-entering .widget:nth-child(4) { animation-delay: 210ms; }
.dash-entering .widget:nth-child(5) { animation-delay: 280ms; }
.dash-entering .widget:nth-child(6) { animation-delay: 350ms; }
.dash-entering .widget:nth-child(n+7) { animation-delay: 420ms; }

/* CZ: Progress bary narůstají od leva (TMF v bottlenecks, proužek nastavení v hero).
   EN: Progress bars grow from the left (TMF in bottlenecks, the hero setup strip). */
.dash-entering .scl-bar-fill,
.dash-entering .dash-hero-setup-seg.is-done {
  transform-origin: left;
  animation: dash-bar-in var(--transition-emphasized) both;
}

/* ==========================================================================
   CZ: Skládatelná plocha widgetů (2026-08-12)
       Mřížka o 12 sloupcích s DISKRÉTNÍMI velikostmi S/M/L — rozhodnutí majitele.
       Volný resize s přetokem sousedů by znamenal vendorovat GridStack; tohle
       jede celé na CSS gridu a pojmenovaných třídách, takže `test_css_classes.py`
       nemusí povolovat inline styly.
   EN: Composable widget desk (2026-08-12)
       A 12-column grid with DISCRETE S/M/L sizes — an owner decision. A free resize
       with neighbour reflow would mean vendoring GridStack; this rides entirely on
       CSS grid and named classes, so `test_css_classes.py` need not allow inline styles.
   ========================================================================== */

/* CZ: PLOCHA = TŘI PEVNÉ SLOUPCE ½ + ¼ + ¼ (2026-08-13, hustota dle mockupu 8 — dva
       úzké sloupce místo jednoho zkrátí stránku zhruba na polovinu). Sloupec karty nese
       pole `size` (l = široký, s = úzký A, m = úzký B); určuje ho přetažení. Sloupec je
       flex fronta karet pod sebou — díry z šířek ani výšek NEMOHOU vzniknout.
   EN: THE DESK = THREE FIXED COLUMNS ½ + ¼ + ¼ (2026-08-13, density per mockup 8 — two
       narrow columns instead of one roughly halve the page). A card's column rides the
       `size` field (l = wide, s = narrow A, m = narrow B); dragging decides it. A column
       is a flex queue of stacked cards — gaps from widths or heights CANNOT occur. */
.widget-desk {
  display: grid;
  grid-template-columns: 6fr 3fr 3fr;
  gap: var(--spacing-5);
  align-items: start;
}

.widget-col {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-5);
  min-width: 0;
}

/* CZ: Zatím bez vlastních pravidel — třídy drží JS/testy a budoucí per-sloupec styly.
   EN: No own rules yet — the classes serve JS/tests and future per-column styling. */
.widget-col-wide { min-width: 0; }
.widget-col-narrow { min-width: 0; }
.widget-col-narrow2 { min-width: 0; }

/* CZ: V režimu úprav je i PRÁZDNÝ sloupec viditelný cíl pro puštění karty.
   EN: In edit mode even an EMPTY column is a visible drop target. */
body.widgets-editing .widget-col {
  min-height: 6rem;
  outline: 1px dashed var(--color-border-default);
  outline-offset: 4px;
  border-radius: var(--radius-lg);
}

/* CZ: Střední šířky (tablet na šířku) — široký sloupec vlevo přes obě řady, úzké sloupce
       pod sebou vpravo. Explicitní umístění: auto-placement by dal druhý úzký sloupec
       POD široký a rozbil logiku stran.
   EN: Mid widths (landscape tablet) — the wide column left across both rows, the narrow
       columns stacked on the right. Explicit placement: auto-placement would put the
       second narrow column UNDER the wide one and break the sides. */
@media (max-width: 1099px) and (min-width: 769px) {
  .widget-desk { grid-template-columns: 2fr 1fr; }
  .widget-col-wide { grid-column: 1; grid-row: 1 / span 2; }
  .widget-col-narrow { grid-column: 2; grid-row: 1; }
  .widget-col-narrow2 { grid-column: 2; grid-row: 2; }
}

/* CZ: Mobil — sloupce pod sebou (nejdřív široký obsah, pak úzké) + hustší karty:
       menší mezery i vnitřní odsazení, ať se na obrazovku vejde víc obsahu.
   EN: Mobile — the columns stack (the wide content first, then the narrow ones) +
       denser cards: smaller gaps and padding so more content fits the screen. */
@media (max-width: 768px) {
  .widget-desk { grid-template-columns: minmax(0, 1fr); gap: var(--spacing-4); }
  .widget-col { gap: var(--spacing-4); }
  .widget > .card-header { padding: var(--spacing-4) var(--spacing-4) 0; }
  .widget > .card-body { padding: var(--spacing-3) var(--spacing-4) var(--spacing-4); }
}

/* CZ: Anatomie karty widgetu (redesign 2026-08-13) — radius 20 px, bez tvrdého rámečku,
       7px tečka barevné rodiny u titulku a 3px akcentní hrana VLEVO, která se vysune na
       :hover / :focus-within (scaleY 0→1). Horní gradientový pruh je pryč. Zvednutí
       karty: translateY(-3px) + --shadow-lift. Sytá barva je POUZE dekorativní; texty
       jedou přes „-text" tokeny.
   EN: Widget card anatomy (2026-08-13 redesign) — 20 px radius, no hard border, a 7px
       colour-family dot by the title and a 3px accent edge on the LEFT that slides in on
       :hover / :focus-within (scaleY 0→1). The top gradient bar is gone. Card lift:
       translateY(-3px) + --shadow-lift. Saturated colour is DECORATIVE ONLY; text uses
       the "-text" tokens. */
.widget {
  margin: 0;
  min-width: 0;
  position: relative;
  border: 0;
  border-radius: var(--radius-widget);
  box-shadow: var(--shadow-md);
  transition: transform var(--transition-emphasized), box-shadow var(--transition-emphasized);
}

/* CZ: Akcentní hrana vlevo — odsazená shora i zdola, vysouvá se od středu.
   EN: The left accent edge — inset top and bottom, scales out from the centre. */
.widget::before {
  content: "";
  position: absolute;
  top: var(--spacing-4);
  bottom: var(--spacing-4);
  left: 0;
  width: 3px;
  border-radius: var(--radius-full);
  transform: scaleY(0);
  transform-origin: center;
  transition: transform var(--transition-emphasized);
}

/* CZ: Zvednutí karty jen mimo režim úprav — v editaci má karta grab kurzor a přesun,
       poskakování pod kurzorem by se pralo se SortableJS.
   EN: The card lift only outside edit mode — while editing the card has the grab cursor
       and dragging; bouncing under the cursor would fight SortableJS. */
body:not(.widgets-editing) .widget:hover,
body:not(.widgets-editing) .widget:focus-within {
  transform: translateY(-3px);
  box-shadow: var(--shadow-lift);
}

.widget:hover::before,
.widget:focus-within::before { transform: scaleY(1); }

/* CZ: Tečka barevné rodiny před titulkem — 7 px, stejný vizuální jazyk jako dlaždice
       členské sekce.
   EN: The colour-family dot before the title — 7 px, the same visual language as the
       member-portal tiles. */
.widget .card-title::before {
  content: "";
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-right: var(--spacing-2);
  border-radius: var(--radius-full);
  vertical-align: middle;
}

.widget-accent-lime::before { background: var(--gradient-sb-lime); }
.widget-accent-orange::before { background: var(--gradient-sb-orange); }
.widget-accent-blue::before { background: var(--gradient-sb-blue); }
.widget-accent-dark::before { background: var(--gradient-sb-dark); }

.widget-accent-lime .card-title::before { background: var(--sb-lime); }
.widget-accent-orange .card-title::before { background: var(--sb-orange); }
.widget-accent-blue .card-title::before { background: var(--sb-blue); }
.widget-accent-dark .card-title::before { background: var(--sb-dark-light); }

/* CZ: Vzdušnější karta: hlavička bez dělicí čáry — obsah na ni plynule navazuje,
       karta nepůsobí jako orámovaná tabulka. Titulek --text-lg (globální .card-title),
       podnadpis --text-sm muted (globální .card-description).
   EN: An airier card: the header loses its divider — the body flows on from it, so the
       card stops reading as a framed table. Title --text-lg (global .card-title),
       subtitle --text-sm muted (global .card-description). */
.widget > .card-header {
  border-bottom: 0;
  padding: var(--spacing-5) var(--spacing-5) 0;
}

.widget > .card-body { padding: var(--spacing-4) var(--spacing-5) var(--spacing-5); }

/* CZ: Pill tlačítka a vstup AI promptu — tvarosloví členské sekce (plně kulaté rohy).
   EN: Pill buttons and the AI prompt input — the member portal's shapes (fully rounded). */
.widget .btn { border-radius: var(--radius-full); }

.widget-ai-prompt {
  display: block;
  text-decoration: none;
  color: var(--color-text-muted);
  border-radius: var(--radius-full);
}

.widget-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--spacing-3);
}

.widget-header-main { min-width: 0; }

/* CZ: Úchyt a akce jsou skryté, dokud se nezapne režim úprav — v běžném zobrazení
       má být karta čistá a nerozptylovat.
   EN: The handle and actions stay hidden until edit mode is on — in normal viewing the
       card should be clean and undistracting. */
.widget-actions {
  display: none;
  flex: none;
  align-items: center;
  gap: var(--spacing-2);
}

/* CZ: Režim úprav se zapíná JEDNOU třídou na <body>. Ne na mřížce: tu každá mutace
       (velikost, odebrání, přesun) vymění za novou, takže by třída zmizela spolu se starým
       elementem a ovládání by po první změně zmizelo pod rukama. <body> swap nenahrazuje.
   EN: Edit mode is switched by ONE class on <body>. Not on the grid: every mutation (resize,
       remove, reorder) replaces it, so the class would vanish with the old element and the
       controls would disappear under the user's hands after the first change. <body> survives. */
body.widgets-editing .widget-actions { display: flex; }

/* CZ: V režimu úprav se táhne za CELOU kartu (úchyt ⠿ zmizel) — kurzor to říká předem.
       Interaktivní prvky uvnitř si nechávají vlastní kurzor, klikání dál funguje
       (SortableJS `filter` v dashboard_widgets.js).
   EN: In edit mode the WHOLE card drags (the ⠿ handle is gone) — the cursor says so up
       front. Interactive elements inside keep their own cursor and clicking keeps working
       (the SortableJS `filter` in dashboard_widgets.js). */
body.widgets-editing .widget {
  outline: 1px dashed var(--color-border-default);
  outline-offset: 2px;
  cursor: grab;
  /* CZ: Tah za celou kartu by jinak vybíral text uvnitř — výběr se vypíná jen
         v režimu úprav, při čtení zůstává text normálně kopírovatelný.
     EN: Dragging the whole card would otherwise select its text — selection is off
         only in edit mode; while reading, text stays copyable as usual. */
  user-select: none;
}

body.widgets-editing .widget:active { cursor: grabbing; }

/* CZ: SortableJS třídy — stejné názvy jako u kanbanu výzev, ať se to chová konzistentně.
       Zvednutá karta dostává výraznější stín, protože klidový stav už má shadow-md.
   EN: SortableJS classes — the same names as the challenge kanban, for consistent behaviour.
       The lifted card gets the stronger shadow because resting cards already carry shadow-md. */
.widget-desk .sortable-ghost { opacity: 0.4; }
.widget-desk .sortable-chosen { box-shadow: var(--shadow-lg); }

/* CZ: Přepínač panelů ve sloučeném widgetu Komunita (role=tablist, šipky přes Alpine).
       Záložky nemizí — prázdný panel má vlastní prázdný stav, jinak by přepínač měnil šířku.
   EN: The panel switcher in the merged Community widget (role=tablist, arrow keys via
       Alpine). Tabs never vanish — an empty panel has its own empty state, otherwise the
       switcher would change width. */
.widget-tablist {
  display: flex;
  gap: var(--spacing-1);
  padding: var(--spacing-1);
  background-color: var(--color-surface-muted);
  border-radius: var(--radius-full);
  margin-bottom: var(--spacing-4);
  width: fit-content;
}

.widget-tab {
  border: 0;
  background: none;
  padding: var(--spacing-1) var(--spacing-3);
  border-radius: var(--radius-full);
  font-size: var(--text-sm);
  font-weight: var(--font-medium);
  color: var(--color-text-muted);
  cursor: pointer;
  transition: background-color var(--transition-fast), color var(--transition-fast);
}

.widget-tab.is-active {
  background-color: var(--color-surface-base);
  color: var(--color-text-primary);
  box-shadow: var(--shadow-sm);
}

/* CZ: Sloučený widget „Kde tě firma brzdí" — dvousloupcový rozpad jen s TMF daty
       (`.has-tmf`); pod 769 px pod sebe. Bez TMF jednosloupcově (nikdy prázdný sloupec).
   EN: The merged "Where the company slows you down" widget — a two-column split only with
       TMF data (`.has-tmf`); stacked below 769 px. Without TMF single-column (never an
       empty column). */
.bottlenecks { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--spacing-6); }

@media (min-width: 769px) {
  .bottlenecks.has-tmf { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .bottlenecks.has-tmf .bottlenecks-steps {
    border-left: 1px solid var(--color-border-default);
    padding-left: var(--spacing-6);
  }
}

.bottlenecks-tmf { min-width: 0; }

.bottlenecks-metric { margin-bottom: var(--spacing-4); }
.bottlenecks-metric:last-child { margin-bottom: 0; }

.bottlenecks-list { display: flex; flex-direction: column; gap: var(--spacing-4); }

.bottlenecks-step { display: flex; gap: var(--spacing-3); align-items: flex-start; }

.bottlenecks-step-num {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: var(--radius-full);
  background-color: var(--sb-blue-soft);
  color: var(--sb-blue-text);
  font-size: var(--text-xs);
  font-weight: var(--font-semibold);
}

.bottlenecks-step-body { min-width: 0; }

.bottlenecks-tmf-cta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-3);
  flex-wrap: wrap;
  margin-top: var(--spacing-4);
  padding-top: var(--spacing-4);
  border-top: 1px solid var(--color-border-default);
}

/* CZ: Vodorovný pás doporučených záznamů se scroll-snap — dlaždice s tintovaným coverem
       (nth-child střídá rodiny, žádná inline barva) a mono odznakem délky.
   EN: The horizontal recommended-recordings strip with scroll-snap — tiles with a tinted
       cover (nth-child rotates the families, no inline colours) and a mono duration badge. */
.video-strip {
  display: flex;
  gap: var(--spacing-4);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: var(--spacing-2);
}

.video-strip-tile {
  flex: 0 0 220px;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-2);
  text-decoration: none;
  color: var(--color-text-primary);
  min-width: 0;
}

.video-strip-cover {
  display: flex;
  align-items: flex-end;
  height: 96px;
  border-radius: var(--radius-lg);
  padding: var(--spacing-2);
}

.video-strip-tile:nth-child(4n+1) .video-strip-cover { background-color: var(--sb-orange-soft); }
.video-strip-tile:nth-child(4n+2) .video-strip-cover { background-color: var(--sb-lime-soft); }
.video-strip-tile:nth-child(4n+3) .video-strip-cover { background-color: var(--sb-blue-soft); }
.video-strip-tile:nth-child(4n+4) .video-strip-cover { background-color: var(--sb-dark-soft); }

.video-strip-duration {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  text-transform: uppercase;
  background-color: var(--color-surface-base);
  border-radius: var(--radius-sm);
  padding: 0 var(--spacing-1);
}

.video-strip-title { font-weight: var(--font-medium); font-size: var(--text-sm); }
.video-strip-reason { font-size: var(--text-xs); color: var(--color-text-muted); }

.widget-catalog {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-3);
}

.widget-catalog-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-3);
  padding-bottom: var(--spacing-3);
  border-bottom: 1px solid var(--color-border-default);
}

.widget-catalog-item:last-child { border-bottom: 0; padding-bottom: 0; }

/* CZ: Lišta úprav plochy — nad mřížkou, zarovnaná doprava, aby v běžném zobrazení
       nesoupeřila s obsahem karet o pozornost.
   EN: The desk edit bar — above the grid, right-aligned so it does not compete with the
       card content for attention in normal viewing. */
.widget-toolbar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: var(--spacing-2);
  margin-bottom: var(--spacing-3);
}

/* CZ: Nápověda k přetahování jde na začátek řádku (`order: -1`), aby se četla PŘED
       tlačítky, a na mobilu se zalomí na vlastní řádek.
   EN: The drag hint goes to the start of the row (`order: -1`) so it reads BEFORE the
       buttons, and wraps onto its own line on mobile. */
.widget-toolbar-hint {
  order: -1;
  margin: 0;
  margin-right: auto;
}

/* CZ: S/M/L tlačítka zanikla (varianta A — šířku určuje sloupec); zbývá jen ✕ odebrání.
   EN: The S/M/L buttons are gone (variant A — the column decides the width); only the
       ✕ removal remains. */
.widget-action-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.75rem;
  height: 1.75rem;
  padding: 0 var(--spacing-2);
  font-size: var(--text-xs);
  font-weight: 600;
  line-height: 1;
  color: var(--color-text-muted);
  background-color: var(--color-surface-base);
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.widget-action-btn:hover { background-color: var(--color-surface-muted); }

.modal-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--spacing-3);
  margin-bottom: var(--spacing-4);
}

.modal-header h3 {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: 600;
}

/* CZ: Paleta má 12 položek — na nízkém okně by přetekla mimo obrazovku, proto scroll uvnitř.
   EN: The palette holds 12 items — on a short window it would overflow off-screen, hence
       scrolling inside. */
#widget-catalog-body {
  max-height: 60vh;
  overflow-y: auto;
}

/* CZ: Vložený rezervační kalendář poskytovatele (Calendly). Pevná výška je nutná — rámec
       s cizím obsahem se sám nezmenší na obsah a bez ní by se sbalil na nulu.
       720 px je změřená výška rozcestníku typů schůzek na profilovém odkazu (6 položek, ověřeno
       v prohlížeči 17. 8. 2026). U odkazu na KONKRÉTNÍ typ události stačí ~520 px — až se odkazy
       zúží na jeden typ, dá se výška snížit.
   EN: The provider's embedded booking calendar (Calendly). A fixed height is required — a frame
       with foreign content does not shrink to its content and without it would collapse to zero.
       720px is the measured height of the event-type menu on a profile link (6 items, verified in
       a browser on 17 Aug 2026). A link to a SPECIFIC event type needs only ~520px — once the
       links narrow to one type, the height can come down. */
.booking-embed {
  display: grid;
  gap: var(--spacing-2);
  margin-top: var(--spacing-4);
  padding-top: var(--spacing-4);
  border-top: 1px solid var(--color-border-default);
}
.booking-embed iframe {
  width: 100%;
  height: 720px;
  border: 0;
  border-radius: var(--radius-lg);
  background: var(--color-surface-muted);
}
/* CZ: Na mobilu je Calendly stejně jednosloupcové a vyšší rámec jen nutí scrollovat uvnitř rámce.
   EN: On mobile Calendly is single-column anyway and a taller frame only forces scrolling inside
       the frame. */
@media (max-width: 640px) {
  .booking-embed iframe { height: 780px; }
}

/* CZ: Doporučená videa pod odpovědí v chatu. Dlaždice je NÁHLED S PROKLIKEM, ne přehrávač
       (rozhodnutí majitele 17. 8. 2026) — přehrávač v bublině natáhne stránku a na mobilu
       ukrojí půl obrazovky. Vodorovný pás se scroll-snapem, stejně jako pás na dashboardu.
   EN: Recommended videos under a chat answer. A tile is a THUMBNAIL WITH A LINK, not a player
       (owner's decision, 17 Aug 2026) — a player in the bubble stretches the page and eats half a
       phone screen. A horizontal scroll-snap strip, like the one on the dashboard. */
.chat-videos {
  margin-top: var(--spacing-3);
  padding-top: var(--spacing-3);
  border-top: 1px solid var(--color-border-default);
}
.chat-videos-title {
  margin: 0 0 var(--spacing-2);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.chat-videos-strip {
  display: flex;
  gap: var(--spacing-3);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: var(--spacing-1);
}
.chat-video-tile {
  flex: 0 0 12rem;
  scroll-snap-align: start;
  display: grid;
  gap: var(--spacing-1);
  text-decoration: none;
  color: var(--color-text-primary);
}
.chat-video-tile img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: var(--radius-md);
  background: var(--color-surface-muted);
}
.chat-video-title {
  font-size: var(--text-sm);
  font-weight: var(--font-medium);
  line-height: var(--leading-snug);
}
.chat-video-duration {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.chat-video-tile:hover .chat-video-title { text-decoration: underline; }
