/* brolis-schools — application styles.
 *
 * Design notes worth keeping:
 *  - Colours are declared as tokens on :root and re-declared under
 *    prefers-color-scheme: dark. School staff work in bright rooms and students
 *    on their own phones at night; both get a designed surface rather than an
 *    inverted one.
 *  - Every interactive element has a visible :focus-visible ring. Keyboard is
 *    the primary input for the administrative screens, which are dense tables.
 *  - Touch targets are at least 44px. Students use this on phones.
 *  - Layout is fluid: the subject tables scroll inside their own container so
 *    the page body never scrolls sideways.
 */

:root {
	--bg: #f6f7f9;
	--surface: #ffffff;
	--surface-alt: #f0f2f5;
	--border: #d8dce3;
	--text: #1b1f24;
	--text-muted: #5b636e;
	--accent: #2f6f4f;
	--accent-text: #ffffff;
	--accent-soft: #e6f1ea;
	--danger: #b3261e;
	--danger-soft: #fbeae9;
	--warn: #8a5a00;
	--warn-soft: #fdf3e2;
	--ok: #2f6f4f;
	--focus: #1a73e8;
	--radius: 10px;
	--gap: 1rem;
	--shadow: 0 1px 2px rgba(16, 24, 40, 0.06), 0 1px 3px rgba(16, 24, 40, 0.1);
	/* Tells the browser to paint its OWN widgets — the checkbox tick, the select
	   arrow, scrollbars, the caret — in the matching scheme. Without it those
	   stay light-mode while everything around them is dark. */
	color-scheme: light;
}

@media (prefers-color-scheme: dark) {
	:root {
		--bg: #14171a;
		--surface: #1c2024;
		--surface-alt: #23282d;
		--border: #333a42;
		--text: #e8eaed;
		--text-muted: #9aa3ad;
		--accent: #6fbf92;
		--accent-text: #10231a;
		--accent-soft: #1d3227;
		--danger: #f2836b;
		--danger-soft: #33211d;
		--warn: #e0b062;
		--warn-soft: #302716;
		--ok: #6fbf92;
		--focus: #7cb0f5;
		--shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
		color-scheme: dark;
	}
}

*, *::before, *::after { box-sizing: border-box; }

body {
	margin: 0;
	background: var(--bg);
	color: var(--text);
	font: 16px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
	-webkit-text-size-adjust: 100%;
}

h1, h2, h3 { line-height: 1.25; margin: 0 0 0.5rem; }
h1 { font-size: 1.5rem; }
h2 { font-size: 1.2rem; }

a { color: var(--accent); }
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible {
	outline: 3px solid var(--focus);
	outline-offset: 2px;
}

.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;
}

/* --- chrome --- */

.topbar {
	display: flex; flex-wrap: wrap; align-items: center; gap: var(--gap);
	padding: 0.75rem 1rem;
	background: var(--surface);
	border-bottom: 1px solid var(--border);
}
.brand { font-weight: 650; text-decoration: none; color: var(--text); }
.context { color: var(--text-muted); font-size: 0.9rem; }
.context .year { margin-left: 0.5rem; padding-left: 0.5rem; border-left: 1px solid var(--border); }
.topbar .user { margin-left: auto; display: flex; align-items: center; gap: 0.75rem; font-size: 0.9rem; }
.user-role { color: var(--text-muted); }

.impersonation-banner {
	padding: 0.6rem 1rem;
	background: var(--warn-soft);
	color: var(--warn);
	border-bottom: 1px solid var(--border);
}

.mainnav { background: var(--surface); border-bottom: 1px solid var(--border); }
.mainnav ul {
	display: flex; flex-wrap: wrap; gap: 0.25rem;
	margin: 0; padding: 0 0.5rem; list-style: none;
}
.mainnav a {
	display: block; padding: 0.75rem 0.9rem;
	min-height: 44px;
	color: var(--text-muted); text-decoration: none;
	border-bottom: 2px solid transparent;
}
.mainnav a:hover { color: var(--text); background: var(--surface-alt); }
.mainnav a[aria-current="page"] { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }

.page { max-width: 68rem; margin: 0 auto; padding: 1.25rem 1rem 4rem; }
.page-head { margin-bottom: 1rem; }

/* --- cards --- */

.card {
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	box-shadow: var(--shadow);
	padding: 1rem;
	margin-bottom: 1rem;
}
.card.empty { color: var(--text-muted); text-align: center; padding: 2rem 1rem; }
.hint { color: var(--text-muted); font-size: 0.9rem; margin: 0.25rem 0 0.75rem; }
.muted { color: var(--text-muted); }

/* --- buttons --- */

/* .button-link is an <a> that must read as a button — the download links on the
   reports screen, and "cancel" beside a save. It shares every rule with <button>
   so the two never drift apart visually. */
button, .button-link {
	font: inherit;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: 44px;
	padding: 0.55rem 0.9rem;
	border-radius: var(--radius);
	border: 1px solid var(--border);
	background: var(--surface);
	color: var(--text);
	text-decoration: none;
	cursor: pointer;
	transition: background 120ms ease, border-color 120ms ease;
}
button:hover:not(:disabled), .button-link:hover { background: var(--surface-alt); }
button:active:not(:disabled), .button-link:active { transform: translateY(1px); }
button:disabled { opacity: 0.55; cursor: not-allowed; }

/* --- busy state --- */

/* A button that is merely dimmed reads as BROKEN, not as working. Every control
   that fires a Datastar action swaps its label for a spinner and a verb while
   the request is in flight, so a slow server looks slow rather than dead.
   The signal comes from data-indicator, which Datastar maintains itself. */
.busy-label {
	display: inline-flex;
	align-items: center;
	gap: 0.45rem;
}

/* Sized in em so it tracks whatever font-size the button it sits in uses --
   the dense table rows shrink their buttons, and a fixed px ring there looks
   pasted on. currentColor for the same reason: it works on .primary (white on
   green) and on a plain button without a second rule. */
.spinner {
	width: 1em;
	height: 1em;
	flex: none;
	border-radius: 50%;
	border: 2px solid currentColor;
	/* One transparent quadrant is what makes the rotation legible; a full ring
	   would spin invisibly. */
	border-top-color: transparent;
	opacity: 0.85;
	animation: spin 700ms linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* A status strip for screens where disabling the control is not enough on its
   own -- the student's subject grid is forty buttons, and dimming all of them
   says less than one line of text does. Reserves no space when idle: Datastar
   sets display:none through data-show. */
.saving-bar {
	position: sticky;
	top: 0;
	z-index: 5;
	display: flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0.5rem 0.75rem;
	margin-bottom: var(--gap);
	border: 1px solid var(--accent);
	border-radius: var(--radius);
	background: var(--accent-soft);
	color: var(--text);
	font-size: 0.9rem;
}

/* Deliberately NOT element-qualified. `button.primary` could never match
   <a class="button-link primary">, which is how the reports screen marks its
   main download — that link previously rendered as bare green text. */
.primary {
	background: var(--accent);
	border-color: var(--accent);
	color: var(--accent-text);
	font-weight: 600;
}
.primary:hover:not(:disabled) { filter: brightness(1.07); }

/* Destructive actions. Muted until hovered rather than permanently loud: these
   sit next to ordinary saves in dense admin tables, and a wall of red reads as
   an error state rather than as a set of available actions. */
.danger {
	background: var(--danger-soft);
	border-color: var(--danger);
	color: var(--danger);
	font-weight: 600;
}
.danger:hover:not(:disabled) { background: var(--danger); color: var(--surface); }

/* Row-level actions inside admin tables. Below the 44px floor by intent: the
   floor exists for the student screens, which are used on phones, while these
   appear only on the desktop administrative tables. */
.small { min-height: 36px; padding: 0.3rem 0.6rem; font-size: 0.85rem; }

/* --- form controls --- */

/* Styled at ELEMENT level on purpose. These rules used to be scoped to
   `.auth-card input, .auth-card select`, which left every control outside the
   login card — i.e. all of the administrative screens — with browser-default
   chrome: white boxes sitting on the themed surface. Checkboxes and radios are
   excluded by listing types explicitly; they are sized below instead. */
input[type="text"],
input[type="number"],
input[type="password"],
input[type="email"],
input[type="search"],
input[type="date"],
select,
textarea {
	font: inherit;
	width: 100%;
	min-height: 44px;
	padding: 0.5rem 0.6rem;
	border: 1px solid var(--border);
	border-radius: var(--radius);
	background: var(--surface);
	color: var(--text);
	transition: border-color 120ms ease;
}
textarea {
	/* A textarea sizes from its rows attribute, so a min-height would fight the
	   markup. Vertical-only resize keeps it from breaking the grid column. */
	min-height: 0;
	resize: vertical;
	line-height: 1.5;
}
input:hover:not(:disabled), select:hover:not(:disabled), textarea:hover:not(:disabled) {
	border-color: var(--text-muted);
}
input:disabled, select:disabled, textarea:disabled {
	opacity: 0.55;
	cursor: not-allowed;
}
::placeholder { color: var(--text-muted); opacity: 1; }

/* The bulk-roster paste box: monospaced so the "Pavardė Vardas KLASĖ" columns
   line up while the user checks what they pasted. */
textarea.paste { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.9rem; }

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

.form-grid {
	display: grid;
	gap: 0.85rem 1rem;
	grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
	align-items: start;
	/* A form that runs the full width of a 68rem card is hard to scan across —
	   the eye loses the label/control pairing. Cards themselves stay full width. */
	max-width: 62rem;
}
/* A count field holds two digits; matching the width of a name field makes the
   form read as though it expects far more input than it does. */
.field input[type="number"] { max-width: 10rem; }
.field { display: flex; flex-direction: column; gap: 0.3rem; min-width: 0; }
.field > label { font-size: 0.85rem; font-weight: 600; color: var(--text-muted); }
/* A field that owns the whole row rather than one column — descriptions. */
.field.wide { grid-column: 1 / -1; }
/* A checkbox labels itself, so this field is one aligned row, not a stack. */
.field.check { justify-content: center; }

.form-actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.5rem;
	margin-top: 1rem;
}

/* --- checkboxes --- */

/* One primitive for every labelled checkbox in the application. Before this the
   markup carried three different structures and none of them had any rule, so a
   22-subject checklist collapsed into a single paragraph of running text. */
.checklist {
	display: grid;
	gap: 0.25rem;
	grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
	margin: 0.5rem 0 0;
}
/* Short labels — class names — pack tighter than subject rows. */
.checklist.inline { grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); }

/* What a rollover carries forward. One column, because the options are a
 * hierarchy and a two-column grid would put a child beside a parent it does
 * not belong to. */
.copy-options { border: 0; margin: 1rem 0 0; padding: 0; }
.copy-options legend { font-weight: 600; padding: 0; }
.copy-options .checklist { grid-template-columns: 1fr; }
/* The indent IS the dependency: these appear only while their prerequisite is
 * ticked, and the rule marks which parent they hang from. */
.check-row.nested { margin-left: 1.5rem; border-left: 2px solid var(--border); }

.check-row {
	display: grid;
	grid-template-columns: auto 1fr;
	align-items: center;
	gap: 0.1rem 0.6rem;
	min-height: 44px;
	padding: 0.35rem 0.5rem;
	border: 1px solid transparent;
	border-radius: var(--radius);
	cursor: pointer;
	font-size: 1rem;
	font-weight: 400;
	color: var(--text);
}
/* The box spans both rows so it stays centred against a name + meta stack. */
.check-row > input { grid-row: 1 / -1; }
.check-row:hover { background: var(--surface-alt); }
.check-name { grid-column: 2; min-width: 0; }
.check-meta { grid-column: 2; color: var(--text-muted); font-size: 0.85rem; }

/* :has() carries the checked and focus states up to the whole row. Where it is
   unsupported the row simply stays flat — the native box still shows its own
   tick and focus ring, so nothing becomes unusable. */
.check-row:has(input:checked) { background: var(--accent-soft); border-color: var(--accent); }
.check-row:has(input:focus-visible) { outline: 3px solid var(--focus); outline-offset: 2px; }

input[type="checkbox"], input[type="radio"] {
	width: 1.15rem;
	height: 1.15rem;
	margin: 0;
	accent-color: var(--accent);
	cursor: pointer;
}

/* The row-select column in the plans table. Shrink-to-fit and centred, because
   a control is not a figure — this cell used to be .num, i.e. right-aligned. */
.check-cell { width: 1px; text-align: center; }

/* --- track picker --- */

.track-list { display: grid; gap: 0.6rem; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }
.track {
	display: flex; flex-direction: column; align-items: flex-start; gap: 0.25rem;
	text-align: left; padding: 0.85rem;
}
.track-name { font-weight: 600; }
.track-desc { color: var(--text-muted); font-size: 0.88rem; }
.track.selected, .track[aria-checked="true"] {
	border-color: var(--accent);
	background: var(--accent-soft);
}

/* --- subject chooser --- */

.category { border: 0; border-top: 1px solid var(--border); margin: 0; padding: 0.75rem 0 0; }
.category:first-of-type { border-top: 0; }
.category legend { font-weight: 600; padding: 0; display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.category-name { font-size: 1rem; }
.category-count { font-weight: 500; font-size: 0.85rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }

/* The status word beside a category, and the tint on the group it belongs to.
 *
 * Two colours, because they call for opposite actions: yellow means the student
 * still has to choose from this group, red means they chose too many and one
 * has to go. The word is rendered too -- a border colour is invisible to a
 * screen reader and ambiguous to a colourblind reader, so it carries emphasis
 * rather than meaning. */
.category-flag { font-size: 0.78rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; }
.flag-need { color: var(--warn); }
.flag-over { color: var(--danger); }

.category-need, .category-over {
	border-left: 3px solid transparent;
	padding-left: 0.6rem;
	margin-left: -0.6rem;
	border-radius: 0 0 4px 4px;
}
.category-need { border-left-color: var(--warn); background: var(--warn-soft); }
.category-over { border-left-color: var(--danger); background: var(--danger-soft); }

/* A <legend> renders in the gap in its fieldset's border, so it sits on the
 * PAGE background rather than the group's. Untinted, a flagged group reads as
 * two separate things: a heading, and a coloured box under it. Tinting the
 * legend to match closes the seam. */
.category-need > legend, .category-over > legend {
	padding-inline: 0.4rem;
	border-radius: 4px 4px 0 0;
}
.category-need > legend { background: var(--warn-soft); }
.category-over > legend { background: var(--danger-soft); }

.badge {
	font-size: 0.72rem; font-weight: 600; text-transform: uppercase;
	letter-spacing: 0.04em; padding: 0.15rem 0.45rem; border-radius: 999px;
}
.badge.required { background: var(--accent-soft); color: var(--accent); }

.subjects { list-style: none; margin: 0.5rem 0 0; padding: 0; display: grid; gap: 0.4rem; grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr)); }
.subject { width: 100%; display: flex; align-items: center; gap: 0.5rem; text-align: left; }
.subject .subject-name { flex: 1; }
.subject .level {
	font-size: 0.78rem; font-weight: 600;
	padding: 0.1rem 0.4rem; border-radius: 4px;
	background: var(--surface-alt); color: var(--text-muted);
}
.subject .hours { font-variant-numeric: tabular-nums; color: var(--text-muted); font-size: 0.85rem; }
.subject.selected, .subject[aria-pressed="true"] {
	border-color: var(--accent);
	background: var(--accent-soft);
}
.subject[aria-pressed="true"] .level { background: var(--accent); color: var(--accent-text); }

/* --- the subject chooser: one row per subject, courses on the right ---
 *
 * M, 2026-08-30: "Nepatogu, kai dalykai greta išdėstyti. Buvo patogiau, kai yra
 * dalykas ir tada langelyje renkiesi kursą arba valandas." So .subjects stops
 * being a grid of tiles here and becomes a single column of rows. The .subject
 * rules above still apply to nothing on this page -- they are kept because
 * printed and read-only lists elsewhere still use that shape. */
.subjects:has(.chooser-row) { display: block; }
.chooser-row {
	display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap;
	padding: 0.4rem 0.55rem;
	border: 1px solid transparent; border-radius: var(--radius);
}
.chooser-row + .chooser-row { border-top-color: var(--border); }
.chooser-row .subject-name { flex: 1 1 12rem; min-width: 0; }
/* The chosen row is tinted, not only its button: at a glance a student is
   looking for "which subjects am I taking", and the answer should be legible
   from the left edge rather than from a badge on the right. */
.chooser-row.chosen { background: var(--accent-soft); border-color: var(--accent); }

.variants { display: flex; gap: 0.35rem; flex-wrap: wrap; }
.variant {
	display: flex; align-items: center; gap: 0.45rem;
	/* 44px minimum, so a course is a comfortable touch target on a phone --
	   these sit side by side and are the smallest controls on the page. */
	min-height: 2.75rem; padding: 0.35rem 0.7rem;
	text-align: left;
}
.variant .level {
	font-size: 0.85rem; font-weight: 700;
	min-width: 1.6rem; text-align: center;
	padding: 0.1rem 0.4rem; border-radius: 4px;
	background: var(--surface-alt); color: var(--text-muted);
}
.variant .hours { font-variant-numeric: tabular-nums; color: var(--text-muted); font-size: 0.85rem; }
.variant.selected, .variant[aria-pressed="true"] {
	border-color: var(--accent); background: var(--accent);
	color: var(--accent-text);
}
.variant[aria-pressed="true"] .level {
	background: var(--accent-text); color: var(--accent);
}
.variant[aria-pressed="true"] .hours { color: var(--accent-text); }
/* A subject with one course is a plain yes/no, so it does not compete visually
   with the rows where there is an actual choice to make. */
.variant.single { font-weight: 500; }
.variant[disabled] { opacity: 0.65; cursor: default; }

@media (max-width: 30rem) {
	/* The courses drop under the name rather than squeezing it to nothing. */
	.chooser-row .subject-name { flex-basis: 100%; }
}

/* --- validation --- */

.validation .totals { display: flex; flex-wrap: wrap; gap: 1.25rem; margin-bottom: 0.75rem; }
.total { display: flex; align-items: baseline; gap: 0.35rem; }
.total .label { color: var(--text-muted); font-size: 0.85rem; }
.total .value { font-size: 1.4rem; font-weight: 650; font-variant-numeric: tabular-nums; }
.total .unit { color: var(--text-muted); font-size: 0.8rem; }

.ok { color: var(--ok); font-weight: 600; }
.problems-intro { margin: 0 0 0.4rem; font-weight: 600; }
.problems { margin: 0 0 0.75rem; padding-left: 1.1rem; }
.problem { margin: 0.2rem 0; }
.problem-missing, .problem-count { color: var(--danger); }
.problem-hours, .problem-rule { color: var(--warn); }
.state { color: var(--text-muted); font-size: 0.9rem; margin: 0.5rem 0 0; }

/* --- tables --- */

/* Wide tables scroll inside their own container, so the page body never
   scrolls horizontally on a phone. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; border-radius: var(--radius); }
table.data {
	width: 100%; border-collapse: collapse;
	background: var(--surface); font-size: 0.92rem;
	border: 1px solid var(--border);
}
table.data th, table.data td { padding: 0.55rem 0.7rem; text-align: left; border-bottom: 1px solid var(--border); }
table.data thead th { background: var(--surface-alt); font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-muted); position: sticky; top: 0; }
table.data tbody tr:hover { background: var(--surface-alt); }
table.data .num { text-align: right; font-variant-numeric: tabular-nums; }

/* A number edited IN the table cell -- group sizes on the subjects page.
 *
 * M, 2026-08-30: "Patogiau buvo, kai iš karto skaičiukus pakeiti ir grupė
 * susidaro." It is sized to its content rather than stretching to the column,
 * so a row of forty subjects does not become a wall of input boxes, and it
 * keeps the full-height focus ring the global input rules give it. */
table.data input.inline-num {
	width: 4.5rem; padding: 0.3rem 0.4rem;
	text-align: right; font-variant-numeric: tabular-nums;
}
/* The spinners are noise at this size and steal half the width in Chrome; the
   number is typed, and the arrows are still reachable with the keyboard. */
table.data input.inline-num::-webkit-outer-spin-button,
table.data input.inline-num::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
table.data input.inline-num { -moz-appearance: textfield; appearance: textfield; }
table.data tfoot th, table.data tfoot td { background: var(--surface-alt); font-weight: 650; }

tr.undersubscribed { background: var(--danger-soft); }
tr.undersubscribed:hover { background: var(--danger-soft); filter: brightness(0.98); }
.warn { display: block; font-size: 0.72rem; color: var(--danger); font-weight: 600; }

/* --- filter chips --- */

.chips { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0.75rem 0 0; }
.chip {
	display: inline-flex; align-items: center; gap: 0.4rem;
	min-height: 44px; padding: 0.4rem 0.8rem;
	border: 1px solid var(--border); border-radius: 999px;
	background: var(--surface); color: var(--text-muted);
	text-decoration: none; font-size: 0.9rem;
}
.chip:hover { background: var(--surface-alt); color: var(--text); }
.chip.active, .chip[aria-current="page"] {
	background: var(--accent-soft); border-color: var(--accent);
	color: var(--accent); font-weight: 600;
}
.chip .count {
	font-variant-numeric: tabular-nums; font-size: 0.8rem;
	background: var(--surface-alt); border-radius: 999px; padding: 0.05rem 0.4rem;
}
.chip.active .count { background: var(--accent); color: var(--accent-text); }

/* --- state + status badges --- */

.ok-badge { background: var(--accent-soft); color: var(--accent); }
.bad-badge { background: var(--danger-soft); color: var(--danger); }
.warn-badge { background: var(--warn-soft); color: var(--warn); }
.muted-badge { background: var(--surface-alt); color: var(--text-muted); }
.state-draft { background: var(--surface-alt); color: var(--text-muted); }
.state-submitted { background: var(--warn-soft); color: var(--warn); }
.state-approved { background: var(--accent-soft); color: var(--accent); }
.state-returned { background: var(--danger-soft); color: var(--danger); }

/* --- conflict dashboard --- */

.conflict h3 { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; margin-bottom: 0.4rem; }
.reason-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.35rem; }
.reason-list li { display: flex; justify-content: space-between; gap: 1rem; padding: 0.35rem 0; border-bottom: 1px solid var(--border); }
.reason-list .count { font-variant-numeric: tabular-nums; font-weight: 650; }

/* --- track cards --- */

.card-grid { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr)); }
.track-card h2 { margin-bottom: 0.4rem; }
.stats { display: flex; gap: 1.5rem; margin: 0.75rem 0 0; }
.stats div { display: flex; flex-direction: column; }
.stats dt { color: var(--text-muted); font-size: 0.8rem; }
.stats dd { margin: 0; font-size: 1.25rem; font-weight: 650; font-variant-numeric: tabular-nums; }

/* An audit row hidden from school administrators, visible only to the platform
   owner. Marked rather than merely listed, so it is never mistaken for a row
   the school can also see. */
tr.hidden-row { opacity: 0.72; }
tr.hidden-row th[scope="row"]::after {
	content: " (paslėpta)";
	color: var(--text-muted); font-weight: 400; font-size: 0.8rem;
}

.nowrap { white-space: nowrap; }

/* --- odds and ends the markup already referenced --- */

/* Topbar identity links. Muted like .user-role rather than accent-coloured, so
   the header reads as one strip of context instead of three competing links. */
.user-name, .logout { color: var(--text-muted); text-decoration: none; }
.user-name:hover, .logout:hover { color: var(--text); text-decoration: underline; }

/* The banner on a locked plan: stated, not alarming — the plan being locked is
   the normal end state, not an error. */
.readonly {
	margin: 0;
	padding: 0.6rem 0.75rem;
	background: var(--surface-alt);
	border-radius: var(--radius);
}

/* Result of a bulk roster paste. */
.import-summary { margin-bottom: 0.75rem; }
.import-summary p { margin: 0; font-weight: 600; }

/* A warning that stays inside a .hint paragraph — the PDF renderer being down
   degrades the feature rather than breaking it. */
.warn-note { color: var(--warn); }

.detail-head { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: var(--gap); }

/* Subject names added to / removed from a plan in the change history, and the
   neutral .chip-tag for listing things that simply are (a teacher's classes).
   No +/− prefix on the first two: the history table labels its own columns
   "Pridėta" and "Pašalinta", so the colour reinforces the heading rather than
   being the only thing carrying the meaning. */
.chip-added, .chip-removed, .chip-tag {
	display: inline-block;
	margin: 0.1rem 0.2rem 0.1rem 0;
	padding: 0.1rem 0.45rem;
	border-radius: 999px;
	font-size: 0.8rem;
}
.chip-added { background: var(--accent-soft); color: var(--accent); }
.chip-removed { background: var(--danger-soft); color: var(--danger); }
.chip-tag { background: var(--surface-alt); color: var(--text-muted); }

.reason { min-width: 0; }

/* The inline "change classes" disclosure in the teachers table. */
.reassign summary { cursor: pointer; padding: 0.35rem 0; min-height: 44px; display: flex; align-items: center; }
.reassign > div { padding-top: 0.5rem; }

/* --- auth --- */

.auth-body { display: grid; place-items: center; min-height: 100vh; padding: 1rem; }
.auth-main { width: 100%; max-width: 26rem; }
.auth-card {
	background: var(--surface); border: 1px solid var(--border);
	border-radius: var(--radius); box-shadow: var(--shadow);
	padding: 1.5rem; display: flex; flex-direction: column;
}
.auth-card h1 { margin-bottom: 1rem; }
.auth-card label { font-size: 0.85rem; font-weight: 600; margin-top: 0.75rem; }
.auth-card input, .auth-card select { margin-top: 0.25rem; }
.auth-card .primary { margin-top: 1.25rem; }
.auth-card hr { width: 100%; border: 0; border-top: 1px solid var(--border); margin: 1.25rem 0 0; }
.alt { margin: 1rem 0 0; font-size: 0.9rem; color: var(--text-muted); text-align: center; }

.error { color: var(--danger); background: var(--danger-soft); padding: 0.6rem 0.75rem; border-radius: var(--radius); margin: 0.5rem 0 0; }
.locked { color: var(--danger); font-weight: 600; }

/* The school picker on the claim screen, shown only when a person's details
 * matched more than one school's roster.
 *
 * Full-width stacked buttons rather than a <select>: this is the one moment
 * the choice matters, there are usually two of them, and a dropdown would hide
 * the answer behind an extra tap on the phone most students use. */
.school-choice { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 0.75rem; }
.school-choice .button-link { justify-content: center; text-align: center; }

/* --- toast --- */

.toast {
	position: fixed; inset-inline: 1rem; bottom: 1rem;
	max-width: 30rem; margin-inline: auto;
	background: var(--danger-soft); color: var(--danger);
	border: 1px solid var(--danger); border-radius: var(--radius);
	padding: 0.75rem 1rem; box-shadow: var(--shadow);
	z-index: 50;
}

/* --- collapsible sections ---
 *
 * A <details> rather than a Datastar-toggled panel: the browser already gets
 * the keyboard and screen-reader behaviour right, and a signal for this would
 * ship to the backend on every action for nothing.
 *
 * The summary is a 44px target because the whole point is that a phone user
 * can open it. list-style is removed and a marker drawn instead, so the
 * triangle looks the same in every engine. */

.collapsible {
	border: 1px solid var(--border);
	border-radius: var(--radius);
	background: var(--surface-alt);
	margin-top: var(--gap);
	padding: 0 1rem;
}
.collapsible > summary {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	min-height: 44px;
	margin-inline: -1rem;
	padding-inline: 1rem;
	font-weight: 600;
	cursor: pointer;
	list-style: none;
	border-radius: var(--radius);
}
.collapsible > summary::-webkit-details-marker { display: none; }
.collapsible > summary::before {
	content: "▸";
	color: var(--text-muted);
	transition: transform 0.15s ease;
}
.collapsible[open] > summary::before { transform: rotate(90deg); }
.collapsible > summary:hover { color: var(--accent); }
.collapsible[open] { padding-bottom: 1rem; }
.collapsible > summary .count {
	margin-inline-start: auto;
	font-weight: 400;
	color: var(--text-muted);
}
.collapsible h3 { margin-top: 1rem; font-size: 1rem; }

/* A create form that is BOTH a page-level card and collapsible.
 *
 * Every "nauja xxx" form is one of these — M, 2026-08-27: "Visur, kur yra
 * 'nauja xxx' turi būti suskleidžiama, kad nesimaišytų kai nereikia." Stated
 * explicitly rather than left to the cascade: .collapsible is declared after
 * .card, so it would otherwise win the background and these would read as
 * nested panels rather than as the top-level cards they are.
 *
 * The summary carries the weight an <h2> used to, since that is the heading
 * these replaced. */
.collapsible.card {
	background: var(--surface);
	box-shadow: var(--shadow);
}
.collapsible.card > summary { font-size: 1.05rem; }

/* --- grouped tables ---
 *
 * Subjects are banded by area, with a header row wherever the area changes.
 * The band stays inside ONE table so the hour totals in the foot keep their
 * meaning; only the row is styled differently.
 *
 * position: sticky is deliberately NOT used: these tables scroll horizontally
 * inside .table-scroll, and a sticky group header detaches from its rows when
 * the container scrolls sideways. */

.data.grouped tbody .group-head > th {
	background: var(--surface-alt);
	border-top: 2px solid var(--border);
	padding-top: 0.75rem;
	padding-bottom: 0.75rem;
	font-size: 0.95rem;
}
.data.grouped tbody tr:first-child .group-head > th { border-top: 0; }
.group-name { font-weight: 700; }
.group-info {
	margin-inline-start: 0.5rem;
	font-weight: 400;
	font-size: 0.85rem;
	color: var(--text-muted);
}
.category legend .group-info { margin-inline-start: 0.4rem; }

/* A person on the roster who has not signed in yet. Muted rather than
 * warned-about: not having registered is normal in September, not a fault. */
.unclaimed-row > th, .unclaimed-row > td { color: var(--text-muted); }

/* --- working in another academic year ---
 *
 * Warned, not merely noted. Every list on screen belongs to a year nobody else
 * is in, and an administrator who forgets that reads an empty catalogue as this
 * year's data having disappeared. */

.year-banner {
	background: var(--warn-soft);
	color: var(--warn);
	border-bottom: 1px solid var(--warn);
	padding: 0.6rem 1rem;
	font-size: 0.9rem;
	text-align: center;
}
.year-banner a { color: inherit; font-weight: 600; }

/* --- code editors ---
 *
 * The document template textareas. Monospace with tabs left wide enough to see
 * nesting, and no spellcheck underline over markup. */

textarea.code, .code-sample {
	font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
	font-size: 0.85rem;
	line-height: 1.5;
	tab-size: 2;
	white-space: pre;
	overflow-wrap: normal;
	overflow-x: auto;
}

/* The read-only worked example. It scrolls in its own box rather than widening
 * the page — a template line is far longer than a phone is wide, and a <pre>
 * that pushes the body sideways breaks every other screen with it. */
.code-sample {
	margin: 0 0 1rem;
	padding: 0.75rem;
	max-height: 24rem;
	overflow: auto;
	background: var(--surface-alt);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	color: var(--text);
}

/* --- grouped navigation ---
 *
 * The menu holds fourteen destinations under seven entries — M, pastebin4:
 * "Pagrindiniame meniu punktus reikėtų protingai suspausti iki 7 punktų."
 * Four of the seven are <details> dropdowns, which is what buys the room for
 * "Dalykų sritys" and "Kursai" to be in the menu at all.
 *
 * A <details> and not a scripted menu, so the keyboard operation and the
 * expanded state are the browser's. They share a name attribute, which makes
 * them an exclusive accordion — opening one closes the rest — again with no
 * script. What that costs is that clicking the page does not dismiss an open
 * panel; clicking its heading again does, and that is the trade for a menu
 * that needs no client-side state at all.
 *
 * The panel is absolutely positioned at EVERY width. Leaving it in flow on
 * narrow screens was tried first, on the theory that an overlay would cover the
 * menu items that wrapped beneath it; the screenshot showed the real cost --
 * the open <li> grows to the panel's height while its short siblings stay put,
 * so the menu bar gains a column of empty space beside the panel. Covering the
 * wrapped row is what a menu is supposed to do, and only one can be open. */

.mainnav { position: relative; }
.mainnav .navgroup { position: relative; }
.mainnav .navgroup > summary {
	display: flex;
	align-items: center;
	gap: 0.35rem;
	min-height: 44px;
	padding: 0.75rem 0.9rem;
	color: var(--text-muted);
	font: inherit;
	cursor: pointer;
	list-style: none;
	border-bottom: 2px solid transparent;
}
.mainnav .navgroup > summary::-webkit-details-marker { display: none; }
.mainnav .navgroup > summary::after {
	content: "▾";
	font-size: 0.8em;
	transition: transform 0.15s ease;
}
.mainnav .navgroup[open] > summary::after { transform: rotate(180deg); }
.mainnav .navgroup > summary:hover { color: var(--text); background: var(--surface-alt); }
.mainnav .navgroup > summary:focus-visible {
	outline: 3px solid var(--focus);
	outline-offset: -3px;
}
/* The heading of the section you are IN. Not aria-current — that belongs to the
   link inside — so this is a visual echo of it and nothing more. */
.mainnav .navgroup > summary.holds-current {
	color: var(--accent);
	border-bottom-color: var(--accent);
	font-weight: 600;
}

.mainnav .navmenu {
	position: absolute;
	z-index: 20;
	top: 100%;
	inset-inline-start: 0.25rem;
	min-width: 12rem;
	display: flex;
	flex-direction: column;
	gap: 0;
	margin: 0;
	padding: 0.25rem;
	list-style: none;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	box-shadow: var(--shadow);
}
.mainnav .navmenu a {
	white-space: nowrap;
	border-bottom: none;
	border-radius: calc(var(--radius) - 4px);
}

/* On a phone the panel spans the whole bar instead of hanging off one item.
 *
 * Anchored to the heading it belongs to, a 12rem panel opened from the
 * right-hand entry runs past the viewport: measured 20px of horizontal body
 * scroll at 390px, which this stylesheet's own rule forbids. Making .navgroup
 * static hands the containing block to .mainnav, so top:100% means "under the
 * menu bar" and the panel can be pinned to both edges. */
@media (max-width: 39.99rem) {
	.mainnav .navgroup { position: static; }
	.mainnav .navmenu {
		inset-inline: 0.5rem;
		min-width: 0;
	}
}

/* --- editable and reorderable rows ---
 *
 * Two screens edit inside the table: subjects open one row at a time, subject
 * areas are all editable at once. */

/* The subject name, which is the control that opens its editor. Styled as the
   link it replaced rather than as a button: it still navigates, in the sense
   that matters to the person clicking it. */
.row-toggle {
	min-height: 0;
	padding: 0;
	font: inherit;
	font-weight: inherit;
	color: var(--accent);
	background: none;
	border: none;
	text-align: start;
	cursor: pointer;
}
.row-toggle:hover { text-decoration: underline; }

.editor-row > td { background: var(--surface-alt); padding: 0; }
.row-editor { padding: 1rem; border-inline-start: 3px solid var(--accent); }
.row-editor .form-grid { margin-bottom: 0.5rem; }

/* Rows that can be dragged. The cursor and the handle column are the only
   affordance a pointer user gets, so both are explicit; keyboard users get the
   arrow buttons, which are real controls rather than a fallback. */
.data.reorderable tbody tr[draggable="true"] { cursor: grab; }
.data.reorderable tbody tr[draggable="true"]:active { cursor: grabbing; }
.data.reorderable td.reorder {
	display: flex;
	gap: 0.25rem;
	width: 1%;
	white-space: nowrap;
}
.data.reorderable td.reorder button {
	min-width: 36px;
	padding-inline: 0.4rem;
}
.data.reorderable td input[type="text"] { width: 100%; min-width: 8rem; }

/* --- motion --- */

@media (prefers-reduced-motion: reduce) {
	*, *::before, *::after {
		animation-duration: 0.01ms !important;
		transition-duration: 0.01ms !important;
	}
	/* The blanket rule above would leave the spinner frozen mid-rotation -- a
	   ring with a gap in it, which reads as a rendering fault rather than as
	   progress. Hide it; the busy VERB beside it carries the whole meaning,
	   which is why every busy label has one. */
	.spinner { display: none; }
}
