body { font-family: 'Encode Sans', sans-serif; }
/* The app header + breadcrumb bar are sticky (appHeader in
   shared.templ), so an #anchor jump would otherwise park the
   target underneath them. scroll-padding-top reserves their
   height for EVERY in-page anchor at once — the sections keep
   their own scroll-mt-4 as extra breathing room.
   The bar grows as the identity grid wraps, so the reservation
   follows Tailwind's breakpoints (measured heights: 175px at
   1 column, 135px at sm/2 columns, 95px at lg/3 columns).
   Deliberately three constants rather than a JS-measured custom
   property — no observer to keep alive, and each value carries
   ~25px of slack over what it compensates. */
html { scroll-padding-top: 11.5rem; }
@media (min-width: 640px)  { html { scroll-padding-top: 9rem; } }
@media (min-width: 1024px) { html { scroll-padding-top: 6rem; } }
[x-cloak] { display: none !important; }
/* Drag-over feedback for every upload surface: toggled by the
   multiupload facility (zones + multi-upload-target markers)
   and by ddDragOver/ddDragLeave (dropzone.templ forms).
   !important so the state wins over the zone's own Tailwind
   border/background utilities regardless of stylesheet order. */
.multiupload-hover {
	border-color: color-mix(in oklab, var(--color-primary) 50%, transparent) !important;
	background-color: color-mix(in oklab, var(--color-primary) 5%, transparent) !important;
}
/* Okno szczegółów (szczegolyModal): przełącznik pełnego ekranu.
   Ukryty checkbox + :has(), bez linii JS — ten sam chwyt co
   „Cała szerokość" w podglądzie tabel. Nadpisuje daisyUI, które
   trzyma .modal-box na width: 91% i max-height: calc(100vh - 5em). */
/* <dialog> niesie z resetu margin: 16px auto — przy oknie na
   92vh niewidoczny, przy 100vh zsuwa je poza dolną krawędź. */
dialog.xmlpro-okno:has(> .xmlpro-okno-max:checked) { margin: 0; }
dialog.xmlpro-okno:has(> .xmlpro-okno-max:checked) > .modal-box {
	width: 100vw; max-width: 100vw;
	height: 100vh; max-height: 100vh;
	border-radius: 0;
}
/* Podgląd arkusza (goxlsxgo) w oknie szczegółów.
   W trybie pełnoekranowym okno ma height:100vh, więc kontrolka może wypełnić
   je do końca — jej domyślny korek --xg-max-h: 70vh zostawiałby wtedy pas
   pustego miejsca pod tabelą i mniej wierszy, niż jest miejsca. Poza pełnym
   ekranem korek zostaje: okno ma tam wysokość automatyczną (max-h-90vh), więc
   bez niego siatka rozepchnęłaby się na całą wysokość arkusza. */
/* Podgląd arkusza (goxlsxgo) osadzony na stronie doboru próby.

   Kontrolka niesie własny korek --xg-max-h: 70vh, żeby w kontenerze o
   wysokości automatycznej nie rozepchnęła się na całą wysokość arkusza. Tyle
   że nad podglądem stoi jeszcze pół strony formularza, więc 70vh wypycha DOLNĄ
   krawędź kontrolki pod kadr — a tam siedzą dwie rzeczy, bez których nie da
   się pracować: poziomy pasek przewijania (kolumna kwot bywa daleko za H) i
   pasek zakładek arkuszy. Kółkiem myszy tego nie obejdziesz: kursor stoi nad
   siatką, a ta ma overscroll-behavior: contain i dziesiątki tysięcy wierszy do
   przewinięcia.

   Wysokość ustala więc host — dokładnie tak, jak przewiduje komentarz przy
   --xg-max-h w kontrolce. height, nie max-height, bo resize ustawia inline
   height, a max-height i tak by ją przycięła: uchwyt dałoby się ciągnąć tylko
   w dół. Stała, nie calc(100vh − …), bo to, co stoi nad podglądem, zmienia się
   z trybem źródła i szerokością okna; przewidywalne 24 rem (≈ 17 wierszy)
   plus uchwyt i pełny ekran kontrolki załatwiają resztę. */
#sheet-preview-host .xg { --xg-max-h: none; }
/* `:not(.xg-max)` jest OBOWIĄZKOWE, nie ozdobne: selektor z identyfikatorem
   wygrywa z regułą pełnego ekranu w arkuszu kontrolki, więc bez tego rozwinięty
   arkusz zostałby wysoki na swoje 24 rem pośrodku pustego ekranu. */
#sheet-preview-host .xg:not(.xg-max) .xg-scroll { height: 24rem; resize: vertical; }
/* Nakładka pełnego ekranu pod naszym nagłówkiem, nie nad nim — tak samo jak
   przy siatkach (patrz .gg-xmlpro.gg-maximized niżej). */
#sheet-preview-host .xg.xg-max { z-index: 50; }
/* Ikona mówi, co się stanie po kliknięciu: rozwiń ↔ zwiń. */
dialog.xmlpro-okno .xmlpro-ikona-min { display: none; }
dialog.xmlpro-okno:has(> .xmlpro-okno-max:checked) .xmlpro-ikona-max { display: none; }
dialog.xmlpro-okno:has(> .xmlpro-okno-max:checked) .xmlpro-ikona-min { display: flex; }
/* xml.pro skin for gogridgo grids */
/* overflow: clip (nie hidden) — przycina do zaokrąglonych rogów
   tak samo, ale NIE tworzy scroll-containera, więc
   position:sticky paska TopScroll przykleja się do viewportu,
   nie do wnętrza grida. */
.gg-xmlpro { background: var(--color-base-100); border: 1px solid var(--color-base-200); border-radius: 0.5rem; overflow: clip; }
/* Pasek TopScroll przypina się tuż POD sticky nagłówkiem
   strony (97px), nie pod samą krawędzią okna. Dozorca ładuje ten
   sam plik, ale jego nagłówek nie jest sticky — panel nadpisuje
   zmienną na 0 we własnym <head> (panel/layout.templ). */
.gg-xmlpro .gg-top-scroll-bar { --gg-topscroll-top: 97px; }
/* Tint najechanej KOMÓRKI w trybie klik=filtr — motywowy
   (primary), wyraźnie ponad subtelnym hoverem wiersza. */
.gg-xmlpro[data-gg-adv-filters]:not([data-rowclick]):has(.gg-filter-row:not([hidden])) tbody td.gg-td:hover:not(.gg-td-summary) { background: color-mix(in oklab, var(--color-primary) 12%, transparent); }
/* Tryb pełnoekranowy tabeli: nad sticky nagłówkiem strony
   (z-30) i w kolorach motywu. */
.gg-xmlpro.gg-maximized { z-index: 50; background: var(--color-base-100); }
.gg-xmlpro.gg-maximized thead th { background: var(--color-base-200); }
/* Wiersz filtrów blisko nagłówków: skinowy padding .gg-th
   (0.75rem) rozpychał linię filtrów w pionie. */
.gg-xmlpro .gg-filter-row .gg-filter-th { padding-top: 0.15rem; padding-bottom: 0.4rem; }
/* Bez text-transform: uppercase — wersaliki zabijały czytelność
   camelCase z XML (KodKonta → KODKONTA); prawdziwy casing. */
.gg-xmlpro .gg-th { font-size: 0.75rem; font-weight: 600; background: var(--color-base-300); color: color-mix(in oklab, var(--color-base-content) 85%, transparent); border-bottom: 1px solid var(--color-base-200); padding: 0.75rem 1rem; vertical-align: top; }
/* Vertical separators between header columns + top-alignment so a
   HeaderWrap caption stacked over 2-3 lines stays visually distinct
   from its neighbours instead of blending into one block
   (bug.xml.pro #28/#29 — "tytuły kolumn zlały się"). Header row only;
   body cells stay borderless for a clean read. */
.gg-xmlpro thead th + th { border-left: 1px solid color-mix(in oklab, var(--color-base-200) 70%, transparent); }
.gg-xmlpro .gg-td { color: color-mix(in oklab, var(--color-base-content) 70%, transparent); border-bottom: 1px solid var(--color-base-100); padding: 0.625rem 1rem; }
.gg-xmlpro .gg-row:hover .gg-td { background: color-mix(in oklab, var(--color-base-200) 40%, transparent); }
/* Summary row: gogridgo's base CSS hardcodes light-mode grays
   (#f3f4f6 / #d1d5db / #1f2937) which look whitish in dark
   themes. Override with daisyUI semantic tokens so it tracks
   the active theme like the rest of the skin does. */
.gg-xmlpro .gg-row-summary > .gg-td-summary {
	background: var(--color-base-200);
	color: color-mix(in oklab, var(--color-base-content) 90%, transparent);
	border-top: 1px solid var(--color-base-300);
	border-bottom: 1px solid var(--color-base-300);
}
.gg-xmlpro .gg-row-summary:hover > .gg-td-summary { background: var(--color-base-200); }
/* Hover on a copyable total (click copies it to the clipboard).
   gogridgo's own rule is a hardcoded #e5e7eb and outranks the
   theme override above — one class heavier — so the dark skin
   has to answer at matching specificity, or the cell turns
   light grey under the pointer. */
.gg-xmlpro .gg-row-summary > .gg-td-summary[data-gg-copy]:hover { background: var(--color-base-300); }
.gg-xmlpro .gg-pager { border-top: 1px solid var(--color-base-200); }
.gg-xmlpro .gg-pager-info { color: color-mix(in oklab, var(--color-base-content) 40%, transparent); }
.gg-xmlpro .gg-page-link:hover { background: color-mix(in oklab, var(--color-base-200) 50%, transparent); }
.gg-xmlpro .gg-page-current { background: var(--color-primary); color: var(--color-primary-content); }
.gg-xmlpro .gg-page-disabled { color: color-mix(in oklab, var(--color-base-content) 15%, transparent); }
			.gg-xmlpro .gg-page-gap { color: color-mix(in oklab, var(--color-base-content) 30%, transparent); }
.gg-xmlpro .gg-rn { color: color-mix(in oklab, var(--color-base-content) 30%, transparent); }
.gg-xmlpro .gg-sel-th { border-bottom: 1px solid var(--color-base-200); }
.gg-xmlpro .gg-sel-td { border-bottom: 1px solid var(--color-base-100); }
.gg-xmlpro .gg-sel-cb { accent-color: var(--color-primary); }
.gg-xmlpro .gg-act-btn,
.gg-xmlpro .gg-bulk-btn { color: color-mix(in oklab, var(--color-base-content) 70%, transparent); }
.gg-xmlpro .gg-act-btn:hover,
.gg-xmlpro .gg-bulk-btn:hover:not(:disabled) { background: color-mix(in oklab, var(--color-base-200) 60%, transparent); color: color-mix(in oklab, var(--color-base-content) 80%, transparent); }
.gg-xmlpro .gg-act-menu-btn { color: color-mix(in oklab, var(--color-base-content) 60%, transparent); }
.gg-xmlpro .gg-act-menu-btn:hover { background: color-mix(in oklab, var(--color-base-200) 60%, transparent); color: color-mix(in oklab, var(--color-base-content) 90%, transparent); }
.gg-xmlpro .gg-act-menu { background: var(--color-base-100); border-color: var(--color-base-200); box-shadow: 0 4px 16px color-mix(in oklab, var(--color-base-content) 18%, transparent); }
.gg-xmlpro .gg-act-menu-sep { background: var(--color-base-200); }
.gg-xmlpro .gg-badge-ok      { background: #dcfce7; color: #166534; }
.gg-xmlpro .gg-badge-err     { background: #fee2e2; color: #991b1b; }
.gg-xmlpro .gg-badge-pending { background: #fef3c7; color: #92400e; }
.gg-xmlpro .gg-badge-info    { background: #dbeafe; color: #1e40af; }
.gg-xmlpro .gg-badge svg { width: 0.75rem; height: 0.75rem; flex-shrink: 0; }
/* Condensed face on numeric and date cells. gogridgo's
   autoAlignForDBType emits font-variant-numeric:tabular-nums
   inline on the <td> of INTEGER/DECIMAL/NUMERIC/DOUBLE/FLOAT/
   REAL/DATE/TIME/TIMESTAMP/DATETIME columns; this rule swaps
   the font on those cells without needing a per-cell class.
   Deliberately NOT monospace: in a mono face the decimal comma
   is as wide as a digit (0.600 em), which is what actually made
   amount columns too wide — JetBrains Mono was already one of
   the narrowest monos going. Condensed + the inline tabular-nums
   keeps digits in a perfect column while the comma drops to
   0.263 em, so a full amount needs ~25% less width. Falls back
   to the regular width if Condensed fails to load. */
.gg-xmlpro td.gg-num,
.gg-xmlpro td[style*="tabular-nums"] {
	font-family: 'Encode Sans Condensed', 'Encode Sans', sans-serif;
}
/* Dense mode typography (reports, workshop preview, raw SQL
   table previews): the semi-condensed width of the SAME family
   fits ~8% more characters per column without changing the
   look, and the (wide) mono numerics drop one point so a full
   amount column needs less width. Regular-width grids — file
   pickers, project lists — are untouched. */
.gg-xmlpro.gg-dense .gg-th,
.gg-xmlpro.gg-dense .gg-td,
.gg-xmlpro.gg-dense .gg-filter-input {
	font-family: 'Encode Sans Semi Condensed', 'Encode Sans', sans-serif;
}
/* Numeric/date cells go one width narrower still — this selector
   must outrank the semi-condensed rule above, which is (0,3,0)
   and would otherwise win over the plain condensed rule.
   No font-size override any more: the old 0.8125rem was there to
   claw back width from the wide mono numerals, and with a
   condensed face the numbers fit at the row's own 0.875rem
   (gogridgo.css .gg-grid) — narrower AND a touch more legible. */
.gg-xmlpro.gg-dense .gg-td.gg-num,
.gg-xmlpro.gg-dense .gg-td[style*="tabular-nums"] {
	font-family: 'Encode Sans Condensed', 'Encode Sans', sans-serif;
}
/* Numeric-cell colour, theme-aware. Used when a grid opts
   in via gogridgo.NumericColor("var(--xmlpro-numeric-fg)").
   Hex values can't adapt to dark mode — the light navy
   becomes invisible on the dark base-100 background — so
   we declare the colour as a CSS variable here and flip
   its value per theme. Light: dark navy (Tailwind blue-
   900). Dark: soft sky-blue (Tailwind blue-300) — clearly
   blue, readable on dark bg, still calmer than the body
   default. */
:root              { --xmlpro-numeric-fg: #1e3a8a; }
[data-theme="dark"] { --xmlpro-numeric-fg: #93c5fd; }
/* Komórka, która różni się między dwoma plikami (porównanie
   JPK_KR „nowy | stary"): delikatny bursztyn, ten sam odcień
   co arkusz xlsx (FDE68A), przyciemniony w trybie ciemnym. */
:root              { --xmlpro-diff-bg: rgba(253, 230, 138, 0.55); }
[data-theme="dark"] { --xmlpro-diff-bg: rgba(217, 119, 6, 0.30); }
/* Pasek rozmiaru (Column.DataBar) — ten sam chwyt co wyżej:
   kolor jako zmienna, przełączana motywem. Kontrolka miesza go
   z przezroczystością (14%/38%), więc na jasnym tle wystarczy
   nasycony błękit, a na ciemnym trzeba jaśniejszego, żeby mieszanka
   w ogóle odkleiła się od tła. Rodzina barw ta sama co liczby
   w tych kolumnach, bo pasek jest tłem TEJ liczby, nie osobnym
   sygnałem. */
.gg-xmlpro .gg-bar              { --gg-bar-color: #2563eb; }
[data-theme="dark"] .gg-xmlpro .gg-bar { --gg-bar-color: #60a5fa; }
/* Dark-mode overrides for the hardcoded status-badge palette
   (everything else in this stylesheet uses daisyUI semantic
   variables and adapts automatically). */
[data-theme="dark"] .gg-xmlpro .gg-badge-ok      { background: #14532d; color: #bbf7d0; }
[data-theme="dark"] .gg-xmlpro .gg-badge-err     { background: #7f1d1d; color: #fecaca; }
[data-theme="dark"] .gg-xmlpro .gg-badge-pending { background: #78350f; color: #fde68a; }
[data-theme="dark"] .gg-xmlpro .gg-badge-info    { background: #1e3a8a; color: #bfdbfe; }
/* Okres month-occupancy strip (month_timeline.go): structural
   colours as theme-aware vars so the inline-styled cells adapt
   to dark mode — same reason as --xmlpro-numeric-fg. Gridlines
   and the year divider track the foreground at low opacity; the
   divider sits clearly above the gridline but softer than a hard
   rule, and dims further in dark mode ("less light"). Covered-
   month green follows the gg-badge-ok palette per theme. */
.gg-xmlpro {
	--mt-grid: color-mix(in oklab, var(--color-base-content) 18%, transparent);
	--mt-sep:  color-mix(in oklab, var(--color-base-content) 42%, transparent);
	--mt-fill: #dcfce7;
	--mt-mark: #166534;
}
[data-theme="dark"] .gg-xmlpro {
	--mt-sep:  color-mix(in oklab, var(--color-base-content) 30%, transparent);
	--mt-fill: #14532d;
	--mt-mark: #bbf7d0;
}
/* Barwy znaku „xml.pro" (komponent wordmark w web/templates/pages) —
   te same, co gfx/logo.svg i talia z docs/marketing: stalowe „xml",
   jasna kropka, „pro" w kolorze tekstu strony. To NIE jest kolor motywu:
   stal i jasny błękit SĄ logo, więc stoją na sztywno, a ogon dziedziczy
   base-content, żeby znak został czytelny w ciemnym motywie. W ciemnym
   stal rozjaśniamy — #4682b4 na ciemnym base-100 daje ~3,4:1, za mało
   pod najmniejsze rozmiary. Rozmiar i grubość zawsze u wywołującego. */
:root { --brand-steel: #4682b4; --brand-light: #b0c4de; }
[data-theme="dark"] { --brand-steel: #6fa8dc; }
.wordmark-x   { color: var(--brand-steel); }
.wordmark-dot { color: var(--brand-light); }

/* Shared full-page blurred desk backdrop behind the sign-in and sign-up
   pages (authShell in auth.templ). Fixed + slightly scaled so the blur's
   soft edges fall off-screen; a base-100 scrim over it keeps the
   foreground (form card) legible. Both themes — darker in dark. */
.desk-bg {
	position: fixed; inset: 0; z-index: 0;
	background: url('/desk-bg.jpg') center / cover no-repeat;
	filter: blur(5px) brightness(.95);
	transform: scale(1.1);
}
.desk-bg::after {
	content: "";
	position: absolute; inset: 0;
	background: color-mix(in oklab, var(--color-base-100) 50%, transparent);
}
[data-theme="dark"] .desk-bg        { filter: blur(5px) brightness(.55); }
[data-theme="dark"] .desk-bg::after { background: color-mix(in oklab, var(--color-base-100) 60%, transparent); }
/* Small frosted "← back" button. Readable over the desk-bg photo
   (bg-base-100 + blur) yet subtle on plain pages. Shared by the
   backLink component and the hand-rolled back links. */
.back-btn {
	display: inline-flex; align-items: center; gap: .25rem;
	padding: .25rem .625rem; border-radius: .5rem;
	font-size: .75rem; line-height: 1rem;
	color: color-mix(in oklab, var(--color-base-content) 65%, transparent);
	background: color-mix(in oklab, var(--color-base-100) 80%, transparent);
	-webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
	border: 1px solid color-mix(in oklab, var(--color-base-300) 60%, transparent);
	transition: background-color .15s, color .15s, border-color .15s;
}
.back-btn:hover { background: var(--color-base-100); color: var(--color-base-content); }
/* Breadcrumb bar (appHeader → breadcrumbBar). Ancestor nodes are
   muted links that brighten on hover; the current (last) node is a
   solid, slightly heavier label. Ancestors cap their width so a long
   company or file name ellipsizes instead of pushing the trail wide.
   The CURRENT node has no cap: it is the page title, and 22rem cut
   „Kontrola kursów w księgach (faktury × zapisy)" two letters short
   of the end. It still ellipsizes on a narrow window — <ol> and <li>
   carry min-w-0, so flex-shrink takes over there. */
.crumb-link { max-width: 22rem; }
/* min-width:0 is what lets the inner .truncate span actually
   shrink: a flex item's automatic minimum size is its content,
   so without this the crumb refuses to get smaller and the
   ellipsis never appears. The span itself is exempt already —
   `truncate` sets overflow:hidden, which zeroes that minimum. */
.crumb-link, .crumb-current { min-width: 0; }
.crumb-link { display: inline-flex; align-items: center; gap: .25rem; }
.crumb-link {
	color: color-mix(in oklab, var(--color-base-content) 65%, transparent);
	text-decoration: none;
	transition: color .15s;
}
.crumb-link:hover { color: var(--color-primary); text-decoration: underline; text-underline-offset: 2px; }
/* Current (last) crumb: bold + accent colour + clearly larger than the
   trail — with its lucide icon it IS the page title (the H1 blocks are
   hidden). Icon inherits the accent colour. */
.crumb-current {
	color: var(--color-primary); font-weight: 600; font-size: 1rem;
	/* Own line-height, because font-size is raised here and the
	   inherited one is not. The bar is text-xs — 12px type in a 16px
	   line — so a crumb bumped to 16px kept a 16px line box, i.e.
	   exactly the font size. The label sits in a .truncate span, and
	   truncate is overflow:hidden, so the 2px the glyphs need below
	   the baseline were cut off: „Dobór próby" lost the tails of ó, p
	   and y. Only titles WITH descenders showed it, which is why it
	   survived this long.

	   1.2 (19.2px) is the measured minimum that clears it — 1.15 still
	   cut — and the only value that costs nothing: the row is already
	   19px+ tall from the 1.125rem icon, so the bar stays 36.2px.
	   1.25 would clear it too and grow the bar by 0.8px. */
	line-height: 1.2;
	display: inline-flex; align-items: center; gap: 0.375rem;
}
.crumb-current-icon { width: 1.125rem; height: 1.125rem; flex-shrink: 0; }
/* Log-out root of the trail — an icon-only button. Muted at rest,
   turns error-red on hover to flag that it signs out (distinct from
   the plain "up one level" crumb links). */
.crumb-logout {
	display: inline-flex; align-items: center;
	color: color-mix(in oklab, var(--color-base-content) 50%, transparent);
	padding: .1rem; border-radius: .25rem;
	transition: color .15s, background-color .15s;
}
.crumb-logout:hover { color: var(--color-error); background: color-mix(in oklab, var(--color-error) 10%, transparent); }
/* Bare-arrow "up one level" glued after the last crumb — borderless
   sibling of the labeled .crumb-back chip on the right. */
.crumb-up {
	display: inline-flex; align-items: center;
	color: color-mix(in oklab, var(--color-base-content) 50%, transparent);
	padding: .1rem; border-radius: .25rem;
	transition: color .15s, background-color .15s;
}
.crumb-up:hover { color: var(--color-primary); background: color-mix(in oklab, var(--color-primary) 10%, transparent); }
/* Back control on the right edge of the trail (one level up per the
   breadcrumbs). Chip-shaped rather than a bare link so it reads as
   an action and stays distinguishable from the crumb links it sits
   in line with. */
.crumb-back {
	display: inline-flex; align-items: center; gap: .3rem;
	font-size: .75rem; line-height: 1rem;
	color: color-mix(in oklab, var(--color-base-content) 65%, transparent);
	background: color-mix(in oklab, var(--color-base-100) 60%, transparent);
	border: 1px solid color-mix(in oklab, var(--color-base-content) 12%, transparent);
	padding: .15rem .5rem; border-radius: .3rem;
	text-decoration: none; cursor: pointer;
	transition: color .15s, background-color .15s, border-color .15s;
}
.crumb-back:hover {
	color: var(--color-primary);
	background: var(--color-base-100);
	border-color: color-mix(in oklab, var(--color-primary) 35%, transparent);
}
/* The logout variant occupies the same slot on root pages — red on
   hover, matching .crumb-logout, so "signs you out" stays visually
   distinct from "goes up one level". */
.crumb-back-logout:hover {
	color: var(--color-error);
	background: color-mix(in oklab, var(--color-error) 10%, transparent);
	border-color: color-mix(in oklab, var(--color-error) 35%, transparent);
}
/* Readable form fields. The minimalist underline-only inputs read
   as plain text and users miss them — give each field a filled
   background, a delicate full border and a clear focus ring.
   Theme-aware via daisyUI semantic tokens. Apply `.xmlpro-field`
   to text/date/number inputs, selects and textareas. */
.xmlpro-field {
	width: 100%;
	background: color-mix(in oklab, var(--color-base-200) 55%, transparent);
	border: 1px solid color-mix(in oklab, var(--color-base-content) 16%, transparent);
	border-radius: 0.5rem;
	padding: 0.5rem 0.75rem;
	font-size: 0.875rem;
	line-height: 1.375rem;
	color: var(--color-base-content);
	transition: border-color .15s, background .15s, box-shadow .15s;
}
.xmlpro-field::placeholder { color: color-mix(in oklab, var(--color-base-content) 40%, transparent); }
.xmlpro-field:hover { border-color: color-mix(in oklab, var(--color-base-content) 28%, transparent); }
.xmlpro-field:focus {
	outline: none;
	background: var(--color-base-100);
	border-color: color-mix(in oklab, var(--color-primary) 55%, transparent);
	box-shadow: 0 0 0 3px color-mix(in oklab, var(--color-primary) 12%, transparent);
}
/* ── App semantic component layer ─────────────────────────────
   Named classes over the most-repeated Tailwind utility clusters
   so markup stays clean (class="xmlpro-card" instead of six
   utilities) and a spacing/colour tweak lives in one place. Plain
   CSS + daisyUI tokens (theme-aware), same convention as
   .xmlpro-field / .gg-xmlpro above — no build step. Extend as
   patterns repeat; each class mirrors its utility cluster 1:1. */
/* rounded-lg border border-base-300 bg-base-100 overflow-hidden */
.xmlpro-card {
	background: var(--color-base-100);
	border: 1px solid var(--color-base-300);
	border-radius: 0.5rem;
	overflow: hidden;
}
/* flex items-center gap-2 px-3 py-2 bg-base-300/60 — section/card header bar */
.xmlpro-panel-head {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0.5rem 0.75rem;
	background: color-mix(in oklab, var(--color-base-300) 60%, transparent);
}
/* group flex items-stretch hover:bg-base-200/40 transition-colors —
   one file row; its action cluster reveals on hover/focus (below). */
.xmlpro-filerow {
	display: flex;
	align-items: stretch;
	transition: background-color .15s;
}
.xmlpro-filerow:hover { background: color-mix(in oklab, var(--color-base-200) 40%, transparent); }
/* flex items-center gap-0.5 pr-2 shrink-0 opacity-0
   group-hover:opacity-100 focus-within:opacity-100 transition-opacity */
.xmlpro-filerow-actions {
	display: flex;
	align-items: center;
	gap: 0.125rem;
	padding-right: 0.5rem;
	flex-shrink: 0;
	opacity: 0;
	transition: opacity .15s;
}
.xmlpro-filerow:hover .xmlpro-filerow-actions,
.xmlpro-filerow:focus-within .xmlpro-filerow-actions { opacity: 1; }
/* Every button gets a delicate border (like the form fields), so
   borderless ghost buttons (Importuj, Anuluj, …) read as buttons.
   daisyUI ghost buttons have a transparent border; .btn (later in
   source than the CDN) makes it visible. Solid colour buttons keep
   their own stronger border via daisyUI's higher-specificity
   .btn-neutral/.btn-primary rules. */
.btn { border-color: color-mix(in oklab, var(--color-base-content) 20%, transparent); }
.btn-ghost:hover { border-color: color-mix(in oklab, var(--color-base-content) 32%, transparent); }
/* Mute the format-placeholder ("rrrr-mm-dd") shown when a type=date
   input is empty (its :invalid state), so it reads as a placeholder,
   not a value. Chromium shadow pseudo — unreachable by Tailwind. */
.date-input-muted:invalid::-webkit-datetime-edit { color: rgba(127, 127, 127, 0.5); }

/* Per-type accent, ONE palette for the whole app. Set --szc on any
   element via its szc-* class; the Szafa branch <li> passes it down to
   its chip, icon and descendant leaves' row wash, and the Biurko's
   function chips read it directly. Fixed hues (brand-distinguishing,
   not theme-derived); ~600-700 level so white chip text stays legible.
   Semantics: ledgers indigo→violet (KR/KR_PD), taxes amber+teal
   (VAT/WB), invoices rose (FA), goods stone (MAG), e-SF emerald.
   Bare class selectors on purpose — scoping them to .szafa-branch is
   what kept the function tree from reusing the same colours. */
.szc-kr   { --szc: #4f46e5; } /* indigo  — księgi rachunkowe */
.szc-krpd { --szc: #7c3aed; } /* violet  — KR podatek dochodowy */
.szc-vat  { --szc: #b45309; } /* amber   — VAT */
.szc-fa   { --szc: #e11d48; } /* rose    — faktury */
.szc-mag  { --szc: #57534e; } /* stone   — magazyny */
.szc-wb   { --szc: #0f766e; } /* teal    — wyciągi bankowe */
.szc-esf  { --szc: #047857; } /* emerald — e-SF */

/* ── Szafa: drzewko plików — skin of the gotreego tree ──────────────
   Structure (nesting, <details>, chevron, checkboxes, tri-state, keyboard,
   branch memory, context-menu announcement) is gotreego's; below is only
   how it dresses here. The row LAYOUT — which cell is how wide, and
   which right edge they share — is declared as gotreego columns in
   szafa_tree.go and generated by the control, so what is left below is
   colour, type and the card. --szc is set on each branch <li>
   (szc-* class) and inherited by descendant leaves' wash.
   (History: table → wardrobe → hand-rolled tree → gotreego; the hover
   action cluster, the row-wide select overlay and the inline rename went
   with the last step — actions live in the toolbar and the context menu.) */
.szafa-tree-toolbar {
	display: flex; align-items: center; gap: .3rem;
	padding: .3rem .5rem;
	border-bottom: 1px solid color-mix(in oklab, var(--color-base-content) 10%, transparent);
}
/* Left group: the pane title. margin-right:auto pins it left and the
   actions right. */
/* The pane title. Named for the row it opens, not for what used to sit
   in it — with the control's real .gtg-levels group now in this same
   toolbar, the old .szafa-tree-levels would name the wrong thing. */
.szafa-tree-title {
	display: flex; align-items: center; gap: .5rem;
	/* margin-right:auto pins the title left and everything else right;
	   min-width:0 lets it TRUNCATE rather than push. In a narrow pane
	   the row holds the title, the level buttons and „Więcej ▾" — and
	   the title is the one of the three you have already read, so it
	   is the one that gives way. Without this the row wrapped to two
	   lines below ~410px and the pane heads stopped pairing. */
	margin-right: auto; min-width: 0;
}
/* Toolbar links render without href while nothing fits them (an <a>
   without href is inert by spec); keep the button look regardless. */
.szafa-tree-toolbar .szafa-akcja-link { cursor: pointer; }
.szafa-tree-toolbar .szafa-akcja-link[aria-disabled="true"] { cursor: default; }
/* Context menu items are the same elements as the toolbar's, minus the
   button chrome: a daisyUI .menu row. */
#szafa-menu [data-akcja] { display: flex; align-items: center; }
#szafa-menu [data-akcja][disabled], #szafa-menu [data-akcja][aria-disabled="true"] { opacity: .4; }

.szafa-tree {
	--gtg-row-pad-y: .2rem;
	--gtg-row-pad-x: .35rem;
	--gtg-row-gap: .4rem;
	/* the gap between cells of a row (gotreego --gtg-cell-gap). Wider
	   than the control's default: these rows are a table, not a label. */
	--gtg-cell-gap: .85rem;
	--gtg-guide-x: .79rem;
	--gtg-indent: .7rem;
	--gtg-row-hover: color-mix(in oklab, var(--color-base-200) 55%, transparent);
	--gtg-guide: color-mix(in oklab, var(--color-base-content) 12%, transparent);
	--gtg-focus: color-mix(in oklab, var(--color-primary) 60%, transparent);
	--gtg-sel-wash: transparent; /* the leaf card below draws its own selected state */
	padding: .35rem;
}
.szafa-tree summary.gtg-row { font-size: .78rem; }
/* RowClickSelects("both") makes a click on a shelf row select its files
   instead of folding it, so the chevron is the ONLY way left to fold
   with a mouse. 14px is too small for that job: pad the hit box and
   pull the padding back out with a negative margin, so the target grows
   without a single row moving. */
.szafa-tree .gtg-chevron {
	box-sizing: content-box;
	padding: .4rem; margin: -.4rem;
}
/* A shelf's cells wear the file card's box in transparent, so both row
   kinds end on the SAME right edge. One rule instead of the four margin
   corrections this used to take — the card is what made branch content
   stop 7px short of a file's, so give the branch the same box. */
.szafa-tree summary .gtg-cells { padding: .12rem .4rem; border: 1px solid transparent; }
.szafa-tree .gtg-chk > input { border-radius: .2rem; }
/* Bar above the tree: „Zaznacz wszystko" + count + the company caption
   (the old super-root). Inverted caption — the one line saying WHOSE
   books these are; --bc on --b1 so it inverts with the theme. */
.szafa-tree .gtg-bar {
	/* padding-right = the file card's border + padding, so the count here
	   ends on the same x as every size below. The bar is NOT a tree row,
	   so the column model does not reach it — this stays hand-tuned. */
	padding: .3rem calc(.4rem + 1px) .3rem .5rem; margin: 0 .35rem;
	border-radius: .375rem;
	background: color-mix(in oklab, var(--color-base-200) 35%, transparent);
	font-size: .82rem; font-weight: 700; color: color-mix(in oklab, var(--color-base-content) 85%, transparent);
}
.szafa-tree .gtg-bar-caption { flex: 1 1 auto; min-width: 0; }
.szafa-company-icon { color: color-mix(in oklab, var(--color-base-content) 55%, transparent); }
.szafa-company-name {
	min-width: 0; padding: .05rem .45rem; border-radius: .3rem;
	background: var(--color-base-content); color: var(--color-base-100);
}
.szafa-tree .gtg-bar .badge { margin-left: auto; }
/* Empty shelf: kept visible so the taxonomy still reads (an empty [WB]
   shelf tells you where WB files would land), dimmed. It is a real
   childless FOLDER now (gotreego FolderField), marked .gtg-empty by the
   control, instead of a leaf faked back into a shelf. Its chevron is
   hidden rather than removed: a disclosure control that discloses
   nothing is a false promise, but its width still has to be paid or the
   shelf's label would not line up with its siblings'. */
.szafa-tree .gtg-empty > .gtg-d > summary.gtg-row { opacity: .5; }
.szafa-tree .gtg-empty .gtg-chevron { visibility: hidden; }

/* Branch label run. Widths come from the column declarations. */
.szafa-tree .gtg-c-ogon .badge, .szafa-tree .gtg-bar .badge { min-width: 1.75rem; justify-content: center; }
.szafa-tlabel { font-weight: 600; color: color-mix(in oklab, var(--color-base-content) 80%, transparent); }
.szafa-tree .szafa-shelf-dim {
	font-weight: 400; text-transform: none; letter-spacing: 0;
	font-size: .66rem; color: color-mix(in oklab, var(--color-base-content) 40%, transparent);
}
/* Never shrinks. Twelve tiles are a reading, not decoration: squeezed
   they stop being countable, and a strip that changes width also
   stops being comparable with the strip above it. The file name is
   what gives way instead — it truncates, and its tooltip still
   carries it whole.

   Below 31rem of pane even an empty name leaves too little for
   year + okres + strip + size, and the size would be clipped off
   the card's right edge. So the strip goes all at once rather than
   degrading: full twelve tiles or none, on branch rows and file
   rows together, so the two never disagree. */
/* The strip's 10rem and the tail's 4.6rem are column declarations now,
   and so is the fact that a shelf's tail lands in the same column as a
   file's size: they are ONE column (ogon), not two boxes dragged into
   line with four margin corrections.

   Below 31rem of pane even an empty name leaves too little for
   year + okres + strip + size, and the size would be clipped off the
   card's right edge. So the strip goes all at once rather than
   degrading: full twelve tiles or none, on branch rows and file rows
   together, so the two never disagree. display:none does not fight the
   generated flex rule — different property, no specificity contest. */
.szafa-tree { container-type: inline-size; }
@container (max-width: 31rem) {
	.szafa-tree .gtg-c-pokrycie { display: none; }
}
.szafa-tree .gtg-c-pokrycie > div { padding: 0; }

/* Leaf content: one slim card around the control's cell container, so
   values stack one under another (bug.xml.pro #103). The checkbox sits
   before it inside .gtg-row; the card takes the rest of the row. */
.szafa-tree .gtg-leaf > .gtg-row { padding: .1rem var(--gtg-row-pad-x); }
.szafa-tree .gtg-leaf .gtg-cells {
	overflow: hidden;
	padding: .12rem .4rem; min-height: 1.85rem;
	border: 1px solid color-mix(in oklab, var(--color-base-content) 10%, transparent); border-radius: 6px;
	font-size: .8rem;
	background: color-mix(in srgb, var(--szc, var(--color-base-content)) 13%, transparent);
	transition: background-color .15s, box-shadow .15s, border-color .15s;
}
/* Selected file: darker, thicker edge — an INSET ring, so selecting
   never shifts the rows below. Driven by the control's checkbox state.

   Deliberately `.gtg-leaf` only. The same rule used to catch a shelf's
   summary row as well (both row kinds carry .gtg-row), and a shelf is
   wider than a file card and its box — `summary .gtg-cells` above — has
   no radius, so ticking a whole branch filled the pane with rectangular
   blots the file cards drowned in. A branch says what it is through its
   own checkbox, „part" state included. To bring the old look back,
   delete `.gtg-leaf > ` from the selector. */
.szafa-tree .gtg-leaf > .gtg-row:has(> .gtg-chk > .gtg-sel:checked) > .gtg-cells {
	border-color: color-mix(in oklab, var(--color-base-content) 50%, transparent);
	box-shadow: inset 0 0 0 2px color-mix(in oklab, var(--color-base-content) 50%, transparent);
	background: color-mix(in srgb, var(--szc, var(--color-base-content)) 22%, transparent);
}
/* Only colour and type here: every width, every truncation and the
   tabular figures come from the column declarations. */
.szafa-tree .szafa-leaf-year { font-weight: 600; }
.szafa-tree .szafa-leaf-okres { font-size: .72rem; color: color-mix(in oklab, var(--color-base-content) 60%, transparent); }
.szafa-tree .szafa-leaf-typ { font-size: .72rem; color: color-mix(in oklab, var(--color-base-content) 70%, transparent); }
.szafa-tree .szafa-leaf-wyr { font-size: .72rem; color: color-mix(in oklab, var(--color-base-content) 60%, transparent); }
/* Per-file caution chip (szafaWarnBadge) — the duplicate marker: red
   octagon-alert + short "CRC" prefix, left of the coverage strip. Fixed
   rose hues so it reads the same in every daisyUI theme. */
.szafa-tree .szafa-warn {
	flex: 0 0 auto; display: inline-flex; align-items: center; gap: .22rem; cursor: help;
	padding: .04rem .32rem; border-radius: .35rem;
	color: #e11d48; background: color-mix(in srgb, #e11d48 14%, transparent);
}
/* Ikony werdyktów (szafaWerdykty): tarcza XSD + lista kontroli.
   Kolor z klas daisy (text-success/error/warning), szary = /30. */
.szafa-tree .szafa-werdykty { flex: 0 0 auto; display: inline-flex; align-items: center; gap: .25rem; }
.szafa-tree .szafa-werdykt { display: inline-flex; align-items: center; }
.szafa-tree a.szafa-werdykt:hover { opacity: .75; }
.szafa-tree .szafa-warn-txt { font-size: .66rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-weight: 600; letter-spacing: .02em; }
.szafa-tree .szafa-size { font-size: .72rem; white-space: nowrap; color: color-mix(in oklab, var(--color-base-content) 50%, transparent); }
/* Licznik plików na półce — ukryty. Kolumna „ogon" ma dwa znaczenia:
   rozmiar na pliku i liczba plików na gałęzi nadrzędnej; sama liczba
   niczego nie mówiła obok rozmiarów, a przy zwiniętej gałęzi i tak jest
   nieczytelna. Ukrywana jest wyłącznie plakietka, nie komórka — komórka
   trzyma szerokość, dzięki której pasek pokrycia gałęzi stoi dokładnie
   nad paskami plików. Wartość surowa jedzie dalej do eksportu (CSV/XLSX),
   bo to CSS, nie zmiana modelu wiersza. Gałąź rysuje się jako <summary>,
   plik jako <div> — stąd selektor trafia tylko w gałęzie. */
.szafa-tree summary.gtg-row .gtg-c-ogon .badge { display: none; }

/* Transient toast (bottom-centre), raised via an OOB swap into
   #xmlpro-toast. It fades itself out with a CSS animation — no JS —
   ending visibility:hidden so the faded card stops intercepting
   clicks. A new toast replaces the host content and animates afresh. */
.xmlpro-toast-host {
	position: fixed; left: 50%; bottom: 1.25rem; transform: translateX(-50%);
	z-index: 60; pointer-events: none;
}
.xmlpro-toast {
	pointer-events: auto;
	display: flex; align-items: center; gap: .75rem;
	padding: .5rem .6rem .5rem 1rem;
	background: var(--color-neutral); color: var(--color-neutral-content);
	border-radius: 8px; box-shadow: 0 6px 20px rgba(0,0,0,.22);
	font-size: .8rem; white-space: nowrap;
	animation: xmlproToastLife 7s ease forwards;
}
.xmlpro-toast-action {
	font-weight: 600; text-decoration: underline; text-underline-offset: 2px;
	padding: .1rem .3rem; border-radius: 4px; cursor: pointer;
}
.xmlpro-toast-action:hover { background: color-mix(in oklab, var(--color-neutral-content) 15%, transparent); }
@keyframes xmlproToastLife {
	0%   { opacity: 0; transform: translateY(8px); }
	5%   { opacity: 1; transform: none; }
	88%  { opacity: 1; transform: none; }
	100% { opacity: 0; visibility: hidden; }
}
/* Indeterminate busy-dialog bar (xmlproDialog.busy, #95): a segment
   sweeps back and forth — honest "working" signal without faking a
   percentage the server never reports. */
@keyframes xmlproBusyBar {
	0%   { margin-left: -35%; }
	100% { margin-left: 100%; }
}

/* ── Biurko: split screen (files ⇆ functions) ─────────────────────
   The panel width is one custom property with a responsive default;
   the splitter overrides it inline. Keeping the default in CSS (not
   JS) means the page is correctly sized before any script runs. */
.fn-panel { --pulpit-panel-w: 100%; width: var(--pulpit-panel-w); }
@media (min-width: 1024px) {
	.fn-panel { --pulpit-panel-w: 30rem; }
	#pulpit-split { gap: 0; }

	/* Each pane scrolls on its own. The two are usually of very
	   different length, so on one document scroll the shorter one
	   ran away up the screen and left an empty column. Instead the
	   PAGE stops scrolling and the panes become their own
	   scrollports — the app header and both pane heads stay put.
	   Only from lg: below it the panes stack, and a locked screen
	   with two scrollbars in a column is nobody's idea of a phone.
	   min-height:0 three times over is the flex trap — a flex item
	   floors at its content height unless told otherwise, and the
	   chain would grow past the viewport with overflow never firing. */
	.biurko-shell { height: 100dvh; overflow: hidden; }
	.biurko-main { display: flex; flex-direction: column; min-height: 0; overflow: hidden; }
	#pulpit-split { flex: 1 1 auto; min-height: 0; }
	.biurko-pane {
		min-height: 0;
		overflow-y: auto;
		/* Reaching the end of one pane must not start scrolling
		   anything else — there is nothing behind it to scroll. */
		overscroll-behavior: contain;
		/* Reserve the scrollbar's width whether or not it shows, so
		   ticking a row (which can shorten the tree) never reflows
		   the columns beside it. A no-op under overlay scrollbars. */
		scrollbar-gutter: stable;
		/* Keyboard focus must clear the sticky head below. */
		scroll-padding-top: var(--pulpit-head-h, 2.4375rem);
	}

	/* The function pane's htmx veil (funkcje.templ) is a direct child
	   of the scrollport, where `absolute inset-0` sizes to the
	   scrollport but sits at the TOP of the scrolled content and
	   rides away. A zero-height sticky box pinned to the scrollport,
	   painting the same wash downwards, stays where the eye is. */
	.biurko-pane > .htmx-indicator {
		position: sticky; top: 0; height: 0; z-index: 10;
	}
	.biurko-pane > .htmx-indicator::before {
		content: ""; position: absolute; left: 0; right: 0; top: 0;
		height: 100dvh; border-radius: .5rem;
		background: color-mix(in oklab, var(--color-base-100) 40%, transparent);
	}
}

/* Printing wants the whole desk, not one screenful of it. */
@media print {
	.biurko-shell { height: auto; overflow: visible; }
	.biurko-main { display: block; overflow: visible; }
	.biurko-pane { overflow: visible; }
}
@media (min-width: 1280px) { .fn-panel { --pulpit-panel-w: 34rem; } }
@media (min-width: 1536px) { .fn-panel { --pulpit-panel-w: 40rem; } }

/* Drag handle. 12px of hit area, a 2px visible rule in the middle —
   a thin line is easy to see and hard to grab, so the two are
   separated. touch-action:none keeps a touch drag from scrolling
   the page instead of resizing. */
.pulpit-grip {
	flex: 0 0 auto; width: 12px; margin: 0 .25rem; align-self: stretch;
	cursor: col-resize; touch-action: none; position: relative;
	background: transparent; border: 0; border-radius: 3px;
}
.pulpit-grip::before {
	content: ""; position: absolute; top: .25rem; bottom: .25rem;
	left: 50%; width: 2px; transform: translateX(-50%);
	background: color-mix(in oklab, var(--color-base-content) 12%, transparent); border-radius: 2px;
	transition: background .12s ease;
}
.pulpit-grip:hover::before,
.pulpit-grip:focus-visible::before,
.pulpit-resizing .pulpit-grip::before { background: color-mix(in oklab, var(--color-primary) 55%, transparent); }
.pulpit-grip:focus-visible { outline: 2px solid color-mix(in oklab, var(--color-primary) 50%, transparent); outline-offset: 2px; }
/* While dragging, the col-resize cursor must win everywhere and no
   text may be selected — otherwise the drag highlights the tree. */
.pulpit-resizing, .pulpit-resizing * { cursor: col-resize !important; user-select: none !important; }

/* ── Menu funkcji: skin of the gotreego tree (.fn-tree) ─────────────
   The tree's STRUCTURE (nesting, <details>, chevron, guide line, focus
   ring, branch memory, keyboard) is gotreego's; everything below is
   only how that structure dresses on this pane. Row contents keep
   their own Tailwind classes (funkcje.templ). */

/* Type scale. A leaf sitting inside a report SUB-folder is one level
   deeper in the hierarchy, and a slightly smaller face says so while
   fitting more of the catalogue on screen. Sub-folder captions step
   down the same way. Nesting-based, so it holds at any depth ≥ 3. */
/* Sizes track the file tree opposite (.8rem names, .78rem branches), so
   the two panes read at one density. Branch captions step down by
   NESTING rather than by a level passed into the template — one rule
   per depth instead of an if in the row. */
.fn-leaf .fn-title { font-size: .8125rem; line-height: 1.3; }
.fn-tree .gtg-c-nazwa { font-size: .875rem; font-weight: 500; }
.fn-tree .gtg-children .gtg-c-nazwa { font-size: .72rem; }
.fn-tree .gtg-children .gtg-children .fn-title { font-size: .78rem; }
.fn-tree .gtg-children .gtg-children .gtg-c-nazwa { font-size: .66rem; }
/* The row's own padding, not the caption's: the whole row is the tree
   item now, so the box that reacts to hover and focus is the box that
   has to be the right height. */
.fn-tree .gtg-leaf > .gtg-row { padding-top: .2rem; padding-bottom: .2rem; }
/* Leaves never had a hover in the control — only branches do — and the
   wrapper that used to carry it is gone. Added HERE rather than to
   gotreego.css, where it would silently give the file tree's cards a
   second highlight on top of their own. */
.fn-tree .gtg-leaf > .gtg-row:hover { background: var(--gtg-row-hover); }
/* The caption cell holds two stacked lines (title + description), so it
   opts out of the control's single-line link geometry and truncates the
   title instead, which is the line that can actually be long. */
.fn-tree .gtg-c-funkcja { display: block; min-width: 0; }
.fn-tree .gtg-c-funkcja .gtg-a, .fn-tree .gtg-c-funkcja .gtg-a-off {
	display: flex; flex-direction: column; justify-content: center;
	white-space: normal; overflow: visible; width: 100%;
}

/* Geometry. The old renderer indented by a hardcoded padding-left per
   depth (66px / 102px) and so stopped at three levels; gotreego indents
   by nesting, and these variables reproduce the same landmarks:

     branch    12 padding + 14 chevron + 8 gap + 24 chip + 8 gap
               → chip at 34, TEXT at 66
     level 2   child list offset 54 → a leaf's px-3 puts its icon at 66
     level 3   a further 36 → icons at 102

   The guide line sits under the parent chevron's centre (12 + 7 = 19px);
   the indent is the remainder of each offset. */
.fn-tree {
	--gtg-row-pad-x: .75rem;
	--gtg-row-pad-y: .3rem;
	--gtg-row-gap: .5rem;
	--gtg-row-radius: 0;
	--gtg-guide-x: 1.1875rem;   /* 19px */
	--gtg-indent: 2.1875rem;    /* 54 − 19 = 35px */
	--gtg-row-hover: color-mix(in oklab, var(--color-base-200) 60%, transparent);
	--gtg-guide: color-mix(in oklab, var(--color-base-content) 10%, transparent);
	--gtg-focus: color-mix(in oklab, var(--color-primary) 60%, transparent);
}
.fn-tree .gtg-children .gtg-children { --gtg-indent: 1.0625rem; } /* 36 − 19 = 17px */
/* Where a pane hosts the tree control's level buttons instead of
   letting it draw a bar of its own (gotreego HTMLParts): the Menu's
   heading and the Repozytorium's toolbar. Both need the same two
   things, because the group now sits outside .gtg and the control's
   own variables do not reach it — a hover that belongs to this
   surface, and an active pill in the theme's accent rather than the
   control's neutral slate fallback, which reads as a foreign object
   on a tinted head, and badly in a dark theme.

   The vertical fit is NOT here: the Menu head leaves 18px for a 24px
   group and corrects it with -my-1 on its own div, while the file
   toolbar leaves 28px and needs nothing. One class, two rows, two
   different amounts of room. */
.xmlpro-levels-slot {
	--gtg-row-hover: color-mix(in oklab, var(--color-base-content) 8%, transparent);
	--gtg-level-active-bg: var(--color-primary);
	--gtg-level-active-fg: var(--color-primary-content);
}
/* Each group used to open with a hairline (border-t border-base-200). */
.fn-tree .gtg-children { border-top: 1px solid var(--color-base-200); }
/* Sub-folder rows: the tinted, tighter band the report trees use. */
.fn-tree .gtg-children > .gtg-branch > .gtg-d > summary.gtg-row {
	background: color-mix(in oklab, var(--color-base-200) 40%, transparent);
	padding-top: .2rem; padding-bottom: .2rem;
	gap: .375rem;
}
.fn-tree .gtg-children > .gtg-branch > .gtg-d > summary.gtg-row:hover { background: var(--color-base-200); }
/* The leaf <li> is only a wrapper: the control's row owns hover and
   padding, so no extra box around it. */
.fn-tree .gtg-leaf { margin: 0; }

/* ── Format chip — ONE component, two forms (pages.FormatChip) ──────
   The format code in inversion on its family colour. Rendered in both
   trees and, from here on, anywhere else that names a format.

   It used to be built twice, independently, and had drifted into two
   different families, sizes, letter-spacings and radii — visibly not
   the same object. What is left below as a difference is only what the
   two contexts genuinely require.

   Colour arrives two ways and both must keep working: the menu puts a
   szc-* class on the chip itself, the file tree lets --szc cascade from
   the branch <li>. var(--szc, …) serves both.

   No text-transform anywhere: „e-SF" carries a lowercase e, and
   uppercasing would break the very match this chip exists to make.
   JPK subtypes are already upper-case in the data. */
.fmt-chip {
	display: inline-flex; align-items: center; justify-content: center;
	flex-shrink: 0;
	background: var(--szc, #334155); color: #fff;
	font-weight: 700;
	font-variant-numeric: tabular-nums;
}
/* Kolumnowy — file tree. One width for every chip, so the labels behind
   them (Księgi rachunkowe, VAT, Faktury…) start in one column instead of
   stepping 136→150px with the code's length. 2.75rem clears the widest,
   „e-SF" at 39.7px. Size inherited from the row (.78rem). */
.fmt-chip.fmt-chip-kol {
	min-width: 2.75rem;
	padding: .1rem .36rem; border-radius: 3px;
	line-height: 1.15; letter-spacing: .03em;
}
/* Zwarty — menu. Natural width and a small monospace face, because a
   pair of formats wears two chips plus an operator inside the 92px
   .fn-badges column and all three have to fit. Monospace keeps codes of equal length equally wide,
   so the cluster reads as a column even without a fixed width. */
.fmt-chip.fmt-chip-zw {
	padding: 0 .3rem; border-radius: 4px;
	/* 1.15rem, not 1.35: the chip was the tallest thing in a menu row and
	   alone set its height (28px); at 18px it sits inside the text line. */
	line-height: 1.15rem; letter-spacing: .01em;
	font-size: .625rem;
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
/* A run of chips under a sentence — the dialog that explains which
   formats a row is missing. Its own class rather than .fn-badges,
   whose fixed 84px width exists for the menu's column and would clip
   here. */
.fmt-chip-row { display: flex; gap: .3rem; margin-top: .6rem; }

/* The operator between two chips („/" = albo, „+" = oraz). Quiet and
   narrow: it is grammar, not a label — the chips are what the eye
   should land on. Same monospace as the compact chip so the run
   keeps one rhythm, and no shrinking, or a squeezed column would
   drop the one character that says which reading applies. */
.fmt-op {
	flex-shrink: 0; align-self: center;
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: .625rem; font-weight: 700; line-height: 1;
	color: color-mix(in oklab, var(--color-base-content) 45%, transparent);
}

/* A greyed row dims its whole content; the chip would otherwise stay
   at full strength and pull the eye to the one thing you cannot use.
   Scoped away from .fmt-chip-row: chips in the dialog explain what is
   missing and are the point of the window, not a dimmed leftover. */
[aria-disabled="true"] .fmt-chip, [aria-disabled="true"] .fmt-op,
.fn-dim .fmt-chip, .fn-dim .fmt-op { opacity: .75; }
.fmt-chip-row .fmt-chip { opacity: 1; }

/* .fn-dim marks the ONE thing that greys a row in the menu-first
   layout: this project holds nothing the function could ever run
   against. Deliberately not tied to the selection — a row that
   merely needs different files ticked stays at full strength and
   explains itself when clicked. Opacity only, no geometry: the
   pane's stillness is the feature. */
.fn-dim { opacity: .6; }

/* The chips form a COLUMN between the icon and the caption, so every
   title starts at the same x and the formats read straight down.

   Width comes from measuring every cluster the catalogue can produce,
   not from the single-chip rows: two formats mean two chips AND the
   operator between them, and the widest pair — „KR_PD" + „e-SF"
   (Bilans: sprawozdanie ↔ księgi) — measures 84.3px, which is what
   pushed this column past the old 5.25rem/84px. 5.75rem/92px keeps
   ~8px of slack, enough for one more character of code on either
   side. Reference: „XLSX / KR_PD" 84.3, „JPK / e-SF" 72.1,
   „FA + KR" 53.7, a lone „KR" 21.8.

   A leaf with no format (AsystentBR) keeps the empty column, or its
   title would step out of line. */
/* The 5.75rem is a column declaration now (funkcjeColumns); what is
   left here is how the chips sit inside it. */
.fn-badges {
	display: flex; align-items: center; gap: .125rem;
}
/* The trailing note („2 pliki", „2023 → 2024"). Its own column now, so
   it needs its own type — a plain cell inherits the row's, and the note
   was reading LOUDER than the function it annotates. Tabular figures
   come from the column declaration. */
.fn-tree .fn-nota { font-size: .75rem; color: color-mix(in oklab, var(--color-primary) 80%, transparent); }

/* Both panes open with a header of the same height, so the file tree
   and the action menu start on one line. --pulpit-head-h is the single
   number; change it once and both follow. */
/* …and, since the panes lost their frames and fills, the rule under
   each title is the ONLY division left in the frame. Both are drawn
   here, from one declaration: measured, they were visibly different
   (base-200 solid against base-content at 10%, ≈.96 vs ≈.93 over the
   page) — a difference nobody would have noticed while three other
   edges were competing with them. */
/* The tint belongs to the HEADER STRIP, not to the pane. Stripping the
   panes' fills removed this too, which was one distinction short: the
   jumpiness came from the bodies sitting in white cards, while the two
   strips are a matched pair and have to look like one. Copied from
   .xmlpro-panel-head rather than invented, and declared here so both
   read it from one place. This rule follows that one in the sheet, so
   it wins for .fn-panel without !important. */
.fn-panel .xmlpro-panel-head,
.szafa-tree-toolbar {
	--pulpit-head-h: 2.4375rem;
	min-height: var(--pulpit-head-h);
	border-bottom: 1px solid color-mix(in oklab, var(--color-base-content) 10%, transparent);
	background: color-mix(in oklab, var(--color-base-300) 60%, transparent);
}
/* From lg the panes are their own scrollports (see the Biurko block),
   so both strips stick to the top of their own pane — a long tree used
   to carry „Zaznacz wszystko", the level buttons and the action menu
   off the screen. Same breakpoint as the lock, and not a step earlier:
   below it the scrollport is the DOCUMENT, and a sticky strip would
   park itself under the app header instead.
   The tint stops being translucent here, because rows now drive UNDER
   the strip and would show through color-mix(in oklab, var(--color-base-300) 60%, transparent). The colour is
   unchanged — the same two washes, laid over their own copy of the
   page ground (base-200/30 over base-100) instead of borrowing
   whatever happens to be behind. */
@media (min-width: 1024px) {
	.fn-panel .xmlpro-panel-head,
	.szafa-tree-toolbar {
		position: sticky; top: 0; z-index: 5;
		background:
			linear-gradient(color-mix(in oklab, var(--color-base-300) 60%, transparent), color-mix(in oklab, var(--color-base-300) 60%, transparent)),
			linear-gradient(color-mix(in oklab, var(--color-base-200) 30%, transparent), color-mix(in oklab, var(--color-base-200) 30%, transparent)),
			var(--color-base-100);
	}
}
.szafa-tree-toolbar { align-items: center; }

/* Function rows: descriptions and unavailability reasons are hidden by
   default (the „Opisy" switch reveals them). Hidden VISUALLY only —
   the box collapses to the standard screen-reader-only geometry, so
   assistive tech still announces why a function is unavailable. This
   is what holds the row height constant: the two texts differ in
   length, and letting them into the flow made rows jump on every
   selection change. */
.fn-panel:not(.fn-desc-on) .fn-desc {
	position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
	overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
/* Shown, but pinned to exactly two lines: descriptions and reasons are
   different lengths, so a free-flowing block would bring the row-height
   jumping straight back in the descriptive view. Two lines fit every
   string in the catalogue; anything longer stays in title=. */
.fn-panel.fn-desc-on .fn-desc {
	display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
	overflow: hidden; min-height: 2rem;
}
.fn-panel.fn-desc-on .fn-desc-toggle { color: var(--color-primary); }

/* Dane czekające na odświeżenie.
 *
 * Panel odświeżany z opóźnieniem (htmx ze zwłoką, odczyt liczony na serwerze)
 * przez kilka sekund pokazuje wynik POPRZEDNIEGO wyboru, niczym tego nie
 * zaznaczając. Przygaszamy go na ten czas — treść zostaje na miejscu, więc
 * układ się nie rusza, ale nikt nie weźmie starych liczb za aktualne.
 *
 * Bez przejścia i bez animacji: to stan danych, nie efekt. */
.is-stale { opacity: .45; }

/* --- panel etapów (steprail.templ) --- */

/* Panel stoi w rynnie po LEWEJ, a miejsce pod niego jest zarezerwowane —
 * `main` dostaje wcięcie równe jego szerokości. Nakładka byłaby tu zła: tabele
 * wyniku i historii idą pełną szerokością (świadomie, #231), więc panel
 * zasłaniałby im pierwsze kolumny.
 *
 * Poniżej 1280 px panelu nie ma wcale i strona wygląda dokładnie jak przedtem.
 * Wąskie okno nie ma z czego oddać 13 rem, a zwijanie do paska ikon to osobna
 * praca, której na razie nikt nie potrzebuje. */
.xmlpro-steprail { display: none; }

@media (min-width: 1280px) {
	.xmlpro-steprail {
		display: block;
		position: fixed;
		/* Niżej niż sam nagłówek: panel ma zaczynać się mniej więcej tam, gdzie
		   pierwsza karta treści, a nie doklejać się do paska z tytułem
		   (uwaga Michała). Nagłówek z okruszkami kończy się w okolicy 5 rem,
		   `main` dokłada swoje wcięcie. */
		top: 8rem;
		left: 1rem;
		width: 12.5rem;
		max-height: calc(100vh - 9.5rem);
		overflow-y: auto;
		z-index: 20;
		font-size: 0.8125rem;
	}
	.xmlpro-steprail-host { padding-left: 14.5rem; }
}

.xmlpro-steprail-title {
	font-weight: 600;
	font-size: 0.6875rem;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: color-mix(in srgb, currentColor 55%, transparent);
	padding: 0 0.25rem 0.625rem;
}

.xmlpro-steprail-list { list-style: none; margin: 0; padding: 0; }

/* Pionowa kreska łączy kolejne etapy w jeden ciąg — bez niej lista wygląda
 * jak zbiór odnośników, a ma wyglądać jak droga. Rysuje ją każdy wiersz poza
 * ostatnim, od dołu swojego kółka do wiersza niżej.
 *
 * Kreska CIĄGŁA tam, gdzie etap jest już za nami, PRZERYWANA dalej: to samo
 * rozróżnienie, co ptaszek kontra numer, tylko czytelne kątem oka. */
.xmlpro-steprail-row { position: relative; }
.xmlpro-steprail-row + .xmlpro-steprail-row { margin-top: 0.25rem; }
.xmlpro-steprail-row:not(:last-child)::after {
	content: "";
	position: absolute;
	/* Środek kółka: wcięcie pozycji + połowa jego szerokości. */
	left: calc(0.25rem + 0.6875rem);
	/* Od środka TEGO kółka do środka następnego. Odcinek biegnący pod kółkami
	   zasłaniają one same — mają nieprzezroczyste tło i wyższą warstwę. Liczenie
	   „od dołu kółka do góry następnego" dawało przy ciasnych wierszach cztery
	   piksele kreski, czyli nic. */
	top: 50%;
	height: 100%;
	border-left: 1px dashed var(--color-base-300);
}
.xmlpro-steprail-row:has(.xmlpro-steprail-item[data-stan="zrobione"])::after {
	border-left-style: solid;
	border-left-color: color-mix(in srgb, var(--color-success) 55%, transparent);
}

.xmlpro-steprail-item {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0.25rem;
	border-radius: 0.375rem;
	color: inherit;
	text-decoration: none;
	line-height: 1.3;
}
.xmlpro-steprail-item:hover { background: var(--color-base-200); }

/* Kółko z numerem etapu. Numer wpisuje szablon, ptaszek dokłada CSS przy
 * stanie „zrobione" — numer i tak nie jest już wtedy potrzebny.
 *
 * Stała szerokość w każdym stanie, żeby kreska łącząca trafiała zawsze w ten
 * sam punkt i etykiety nie drgały przy zmianie stanu.
 *
 * ZASADA, która tu wraca: kółko musi mieć tło KRYJĄCE w KAŻDYM stanie. To ono,
 * a nie kolejność warstw, przecina kreskę biegnącą od środka do środka — więc
 * ani mieszanie koloru z `transparent`, ani `opacity` na pozycji nie wchodzą
 * w grę (przez jedno i drugie kreska prześwituje). */
.xmlpro-steprail-mark {
	position: relative;
	z-index: 1;
	flex: 0 0 auto;
	width: 1.375rem;
	height: 1.375rem;
	border: 1px solid var(--color-base-300);
	border-radius: 999px;
	background: var(--color-base-100);
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 0.6875rem;
	font-weight: 600;
	color: color-mix(in srgb, currentColor 60%, transparent);
}

.xmlpro-steprail-item[data-stan="zrobione"] .xmlpro-steprail-mark {
	border-color: var(--color-success);
	/* Zmieszane z TŁEM KARTY, nie z pustką: „12% zieleni i 88% transparent"
	   to nie jasnozielone tło, tylko dziura, przez którą widać kreskę. */
	background: color-mix(in srgb, var(--color-success) 12%, var(--color-base-100));
	color: var(--color-success);
	/* Ptaszek ZAMIAST numeru: etap zrobiony nie potrzebuje już swojego miejsca
	   w kolejce, a znak czyta się szybciej niż cyfra. */
	font-size: 0;
}
.xmlpro-steprail-item[data-stan="zrobione"] .xmlpro-steprail-mark::before {
	content: "✓";
	font-size: 0.75rem;
}

.xmlpro-steprail-item[aria-current="true"] {
	background: var(--color-base-200);
	font-weight: 600;
}
.xmlpro-steprail-item[aria-current="true"] .xmlpro-steprail-mark {
	border-color: var(--color-primary);
	background: var(--color-primary);
	color: var(--color-primary-content);
}

/* Etap, do którego nie ma jeszcze po co skakać: sekcja jest pusta.
 *
 * Wygaszamy KOLORAMI, nie `opacity`: przezroczystość nałożona na całą pozycję
 * rozcieńcza też tło kółka i kreska spod spodu zaczyna przez nie prześwitywać
 * (uwaga Michała ze zrzutu). */
.xmlpro-steprail-item[data-stan="nieaktywny"] {
	pointer-events: none;
	color: color-mix(in srgb, currentColor 40%, transparent);
}
.xmlpro-steprail-item[data-stan="nieaktywny"] .xmlpro-steprail-mark {
	border-color: color-mix(in srgb, var(--color-base-300) 60%, var(--color-base-100));
	color: color-mix(in srgb, currentColor 45%, transparent);
}

/* Przejście bez numeru (historia): kółko zostaje puste i mniejsze, bo to
   miejsce, do którego się wraca, a nie etap do odhaczenia. */
.xmlpro-steprail-item:not([data-etap]) .xmlpro-steprail-mark {
	width: 0.875rem;
	height: 0.875rem;
	margin: 0 0.25rem;
	border-style: dashed;
}

.xmlpro-steprail-count {
	margin-top: 0.625rem;
	padding: 0.5rem 0.25rem 0;
	border-top: 1px solid var(--color-base-300);
	font-size: 0.6875rem;
	color: color-mix(in srgb, currentColor 55%, transparent);
}

/* --- komunikat w treści strony (inline_message.templ) --- */

/* Zamykanie bez JavaScriptu: ukryte pole wyboru w pasku, etykieta z ✕ obok
 * treści, a chowa się CAŁE opakowanie — razem z jego odstępem, żeby po
 * komunikacie nie została pusta szpara.
 *
 * Ten sam wzorzec, co przełącznik pełnego ekranu w oknie szczegółów
 * (dialog.xmlpro-okno wyżej) i „cała szerokość" w podglądzie tabel. Trzeci raz
 * w repo, za każdym razem bez ani jednej linii skryptu. */
.xmlpro-msg-wrap:has(.xmlpro-msg-x:checked) { display: none; }

.xmlpro-msg-x-btn {
	position: absolute;
	top: 0.3rem;
	right: 0.4rem;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 1.4rem;
	height: 1.4rem;
	border-radius: 0.375rem;
	color: color-mix(in oklab, currentColor 55%, transparent);
	cursor: pointer;
	line-height: 1;
}
.xmlpro-msg-x-btn:hover {
	color: currentColor;
	background: color-mix(in oklab, currentColor 10%, transparent);
}

/* Znikanie po czasie. Zwijamy wiersz siatki z 1fr do 0fr — inaczej zostałaby
 * dziura po komunikacie, a strona podskoczyłaby dopiero przy następnej
 * podmianie. `visibility: hidden` na końcu, żeby wyblakły pasek przestał łapać
 * kliknięcia (to samo rozstrzygnięcie, co przy toaście niżej).
 *
 * Granica wobec toasta jest celowa: toast potwierdza AKCJĘ i znika zawsze,
 * komunikat w treści objaśnia STAN i domyślnie zostaje. */
.xmlpro-msg-fade {
	display: grid;
	grid-template-rows: 1fr;
	animation: xmlproMsgFade 0.45s ease 8s forwards;
}
.xmlpro-msg-fade > * { overflow: hidden; min-height: 0; }

@keyframes xmlproMsgFade {
	to {
		grid-template-rows: 0fr;
		opacity: 0;
		margin-bottom: 0;
		visibility: hidden;
	}
}

/* --- kreator kroków (gowizardgo) --- */

/* Kontrolka rysuje samą strukturę i bierze kolory z currentColor oraz zmiennych
 * --gwz-*. Tutaj podpinamy je pod motyw aplikacji — to całe „skórowanie".
 *
 * --gwz-bg musi być KRYJĄCY: kółko z numerem zasłania kreskę biegnącą za nim,
 * więc kolor zmieszany z `transparent` byłby tu dziurą, nie odcieniem. */
/* `.gwz.xmlpro-wizard`, nie samo `.xmlpro-wizard`: arkusz kontrolki ładuje się
 * PO app.css, a jej domyślniki siedzą na `.gwz` — czyli selektorze o tej samej
 * sile. Przy remisie wygrywa późniejszy, więc skórka pisana jednym słowem
 * przegrywała w komplecie i po cichu: ptaszki wychodziły czarne zamiast
 * zielonych, a błękit bieżącego kroku nie pojawiał się wcale. Druga klasa
 * podnosi siłę i rozstrzyga to niezależnie od kolejności wpięcia arkuszy. */
.gwz.xmlpro-wizard {
	--gwz-bg: var(--color-base-100);
	--gwz-line: var(--color-base-300);
	--gwz-edge: var(--color-base-300);
	--gwz-hover: var(--color-base-200);
	--gwz-muted: color-mix(in srgb, currentColor 55%, transparent);
	--gwz-done: var(--color-success);
	--gwz-current: var(--color-primary);
	--gwz-current-text: var(--color-primary-content);
	/* Bieżący krok ma być widać jednym rzutem oka, a nie po przeczytaniu —
	 * pogrubiony tekst i kółko w inwersji to za mało (uwaga Michała).
	 * `info` jest błękitne i IDENTYCZNE w obu motywach, więc jedna wartość
	 * wystarcza; domieszka z `transparent`, bo kółko rysuje własne, kryjące. */
	--gwz-current-wash: color-mix(in oklab, var(--color-info) 22%, transparent);
}
