/* rsd app stylesheet — dependency-free, dark-first with a persisted light toggle
   (M10). Colour is driven entirely by CSS custom properties: :root holds the dark
   palette, [data-theme="light"] overrides it. The pre-paint script in the page
   <head> sets data-theme before first paint, so there is no flash. Keep new colours
   as semantic vars (never hardcode a hex that must differ between themes). */

:root {
	color-scheme: dark;
	--bg: #15171c;
	--panel: #1d2027;
	--panel-2: #12141a;
	--border: #2a2e37;
	--border-2: #353b46;
	--text: #e6e6e6;
	--muted: #8b94a3;
	--heading: #ffffff;   /* brand / active-nav / input text — must invert in light */
	--nav-link: #b9c2d0;  /* header nav + menu item text */
	--nav-hover-bg: rgba(255, 255, 255, .06);
	--nav-active-bg: rgba(59, 130, 246, .18);
	--row-hover: rgba(255, 255, 255, .02);
	--link: #8fb4ff;      /* inline text links (a.btnlink) */
	--code-bg: #0c0f16;   /* <pre>/<code> background */
	--code-text: #cdd6e4;
	--accent: #3b82f6;
	--accent-hover: #2f6fe0;
	--green: #2fbf71;
	--green-bg: #16302a;
	--green-border: #2c5d4f;
	--ok-text: #8be0bc;
	--red: #e0565d;
	--red-bg: #3a1d20;
	--red-border: #6b2b30;
	--err-text: #f3b9bd;
	--yellow: #f0c34a;
	--yellow-bg: #322a14;
	--yellow-border: #6b5a23;
	--warn-text: #f0dca0;
	--shadow: rgba(0, 0, 0, .45);
	--affix-bg: #262b34;  /* unit suffix inside an input (slightly lighter than the field) */
}

[data-theme="light"] {
	color-scheme: light;
	--bg: #eef1f6;
	--panel: #ffffff;
	--panel-2: #f4f6fa;
	--border: #dde2eb;
	--border-2: #c6cedb;
	--text: #1d2430;
	--muted: #5d6675;
	--heading: #0f1622;
	--nav-link: #475063;
	--nav-hover-bg: rgba(15, 22, 34, .05);
	--nav-active-bg: rgba(37, 99, 235, .12);
	--row-hover: rgba(15, 22, 34, .03);
	--link: #1d63d6;
	--code-bg: #f0f3f8;
	--code-text: #2a3140;
	--accent: #2563eb;
	--accent-hover: #1d4ed8;
	--green: #178a52;
	--green-bg: #e4f6ec;
	--green-border: #b7e3c9;
	--ok-text: #15784a;
	--red: #c8362d;
	--red-bg: #fbe8e7;
	--red-border: #f0c2bf;
	--err-text: #b3261e;
	--yellow: #b6850c;
	--yellow-bg: #fbf2d6;
	--yellow-border: #ead8a2;
	--warn-text: #876607;
	--shadow: rgba(15, 22, 34, .14);
	--affix-bg: #e7ebf2;  /* unit suffix inside an input (slightly darker than the field) */
}

* { box-sizing: border-box; }

body {
	margin: 0;
	font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
	background: var(--bg);
	color: var(--text);
	line-height: 1.45;
}

/* ---- header / nav ---- */
header {
	display: flex;
	align-items: center;
	gap: 1rem;
	padding: .6rem 1.1rem;
	background: var(--panel);
	border-bottom: 1px solid var(--border);
}
/* three zones: brand (left) · main-nav (center) · user-menu (right). The side
   zones flex equally so the center nav stays centered regardless of their width. */
header .header-left { flex: 1 1 0; }
header .brand {
	font-weight: 700;
	color: var(--heading);
	display: inline-flex;
	align-items: center;
	gap: .5rem;
	text-decoration: none;
}
header .brand:hover { color: var(--accent); }
header .brand .fa-solid { color: var(--accent); }
/* The shield doubles as a fleet-health beacon: green when all visible repos are
   OK, red while any alert is active. (The bell keeps the warn/error nuance.) */
header .brand.health-ok .fa-solid { color: var(--ok-text); }
header .brand.health-alert .fa-solid { color: var(--err-text); }

header .main-nav { display: flex; gap: .5rem; align-items: center; justify-content: center; }
header .main-nav a { color: var(--nav-link); text-decoration: none; display: inline-flex; align-items: center; gap: .4rem; font-weight: 600; font-size: .9rem; padding: .4rem .7rem; border-radius: 7px; }
header .main-nav a:hover { color: var(--heading); background: var(--nav-hover-bg); }
header .main-nav a.active { color: var(--heading); background: var(--nav-active-bg); }

/* right zone: theme toggle + user dropdown, pushed to the far right */
header .header-right { flex: 1 1 0; display: flex; align-items: center; justify-content: flex-end; gap: .6rem; }

/* theme toggle (sun in dark, moon in light — shows the mode you switch TO) */
.theme-toggle {
	margin: 0; padding: .4rem .6rem; background: transparent;
	border: 1px solid var(--border-2); border-radius: 7px;
	color: var(--nav-link); cursor: pointer; line-height: 1; font-size: .95rem;
}
.theme-toggle:hover { color: var(--heading); border-color: var(--accent); background: var(--nav-hover-bg); }
.theme-toggle .icon-to-dark { display: none; }
[data-theme="light"] .theme-toggle .icon-to-light { display: none; }
[data-theme="light"] .theme-toggle .icon-to-dark { display: inline-block; }

/* user dropdown (native <details>, no JS) */
.user-menu { position: relative; display: flex; }
.user-menu > summary {
	list-style: none;
	cursor: pointer;
	display: inline-flex;
	align-items: center;
	gap: .5rem;
	padding: .35rem .65rem;
	border: 1px solid var(--border-2);
	border-radius: 7px;
	color: var(--muted);
	font-size: .85rem;
}
.user-menu > summary::-webkit-details-marker { display: none; }
.user-menu > summary:hover,
.user-menu[open] > summary { color: var(--heading); border-color: var(--accent); }
.user-menu > summary .fa-user { color: var(--accent); }
.user-menu > summary .role { color: var(--muted); }
.user-menu .caret { font-size: .7em; transition: transform .15s ease; }
.user-menu[open] .caret { transform: rotate(180deg); }

.user-menu .menu {
	position: absolute;
	top: calc(100% + .4rem);
	right: 0;
	min-width: 210px;
	background: var(--panel);
	border: 1px solid var(--border-2);
	border-radius: 8px;
	padding: .35rem;
	display: flex;
	flex-direction: column;
	gap: .1rem;
	box-shadow: 0 10px 28px var(--shadow);
	z-index: 30;
}
.user-menu .menu form { margin: 0; }
.user-menu .menu a,
.user-menu .menu button {
	display: flex;
	align-items: center;
	gap: .55rem;
	width: 100%;
	margin: 0;
	padding: .5rem .6rem;
	border: 0;
	border-radius: 6px;
	background: none;
	color: var(--nav-link);
	font-size: .85rem;
	font-weight: 500;
	text-align: left;
	text-decoration: none;
	cursor: pointer;
}
.user-menu .menu a:hover,
.user-menu .menu button:hover { background: var(--panel-2); color: var(--heading); }
.user-menu .menu .fa-solid { width: 1rem; text-align: center; color: var(--muted); }

main { margin: 1.6rem 1.5rem; }

/* ---- cards ---- */
.card {
	background: var(--panel);
	border: 1px solid var(--border);
	border-radius: 10px;
	padding: 1.25rem 1.5rem;
}
.card + .card { margin-top: 1rem; }

/* a detail page's header sub-block: the entity's dashboard-side identity (parent link,
   id, when it was added), one item per row. */
.entity-meta { display: flex; flex-direction: column; align-items: flex-start; gap: .25rem; }
.entity-meta > span { display: inline-flex; align-items: center; gap: .35rem; }

/* detail summary grid: 2–3 mini-cards side by side, wrapping to one column when
   the lane gets narrow. Used by the repo/server detail pages to group properties. */
.detail-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); gap: 1rem; margin: 1rem 0; align-items: stretch; }
.detail-grid > .card { margin-top: 0; }
.detail-grid > .card + .card { margin-top: 0; }
.detail-grid + .card { margin-top: 1rem; }
.subcard { padding: 1rem 1.15rem; display: flex; flex-direction: column; }
.subcard .card-head { margin-bottom: .5rem; }
.subcard .card-head h2 { margin: 0; font-size: 1.02rem; font-weight: 600; display: inline-flex; align-items: center; gap: .5rem; }
.subcard .card-head h2 .fa-solid { color: var(--accent); font-size: .9em; }
.subcard table.kv th { width: 1%; white-space: nowrap; padding-right: 1.2rem; font-weight: 500; }
.subcard table.kv td { word-break: break-word; }
/* margin-top:auto pins the note to the card's bottom edge when the grid stretches
   it taller than its content (equal-height row). */
.subcard-note { font-size: .78rem; margin: auto 0 0; padding-top: .6rem; }

.narrow { max-width: 440px; margin: 4rem auto; }
/* the server form carries long fields (base URL, 64-hex TLS pin, shoutrrr URLs)
   that don't breathe at 440px — give it a wider lane than the auth/user forms. */
.narrow.narrow-lg { max-width: 640px; }
.card-head { display: flex; align-items: center; gap: .6rem; justify-content: space-between; margin-bottom: 1rem; }
.card-head h1 { margin: 0; }
.card-head h1 .head-count { font-size: .8rem; font-weight: 400; color: var(--muted); }
.head-actions { display: flex; gap: .5rem; align-items: center; }

h1 { font-size: 1.3rem; margin-top: 0; display: inline-flex; align-items: center; gap: .55rem; }
h1 .fa-solid { color: var(--accent); font-size: .95em; }

/* ---- forms ---- */
label { display: block; margin: .8rem 0 .3rem; font-size: .82rem; color: #aab3c0; }
input[type="text"], input[type="password"], input[type="email"], input[type="number"], input[type="search"], select, textarea {
	width: 100%;
	padding: .55rem .65rem;
	background: var(--panel-2);
	border: 1px solid var(--border-2);
	border-radius: 7px;
	color: var(--heading);
	font-size: .95rem;
}
textarea { resize: vertical; font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; font-size: .85rem; }
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--accent); }

/* cron field: the text input plus M/W/D shorthand buttons (@monthly/@weekly/@daily) on one line */
.cron-field { display: flex; align-items: stretch; gap: .4rem; }
.cron-field input { flex: 1 1 auto; min-width: 0; }
.cron-macros { display: flex; gap: .3rem; flex: 0 0 auto; }
.cron-macros .cron-macro { margin: 0; min-width: 2.2rem; justify-content: center; font-weight: 600; }

/* checkbox rows: a flex line, NOT a full-width stretched control */
label.checkline {
	display: flex;
	align-items: center;
	gap: .55rem;
	margin: .9rem 0 0;
	font-size: .9rem;
	color: var(--text);
	cursor: pointer;
}
label.checkline input[type="checkbox"] {
	width: 16px;
	height: 16px;
	margin: 0;
	flex: 0 0 auto;
	accent-color: var(--accent);
	cursor: pointer;
}

/* per-server scope picker on the user form */
.scope-list {
	margin: .9rem 0 0;
	padding: .6rem .8rem .8rem;
	border: 1px solid var(--border-2);
	border-radius: 7px;
}
.scope-list[hidden] { display: none; }
.scope-list legend { padding: 0 .4rem; font-size: .82rem; color: var(--muted); }
.scope-list label.checkline { margin: .5rem 0 0; }

/* ---- settings page ---- */
.settings-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
	gap: .35rem 1.5rem;
}
.settings-grid .field { margin-bottom: .55rem; min-width: 0; }
.settings-grid label { margin-top: .35rem; }
.field-hint { display: block; color: var(--muted); font-size: .72rem; line-height: 1.35; margin-top: .25rem; }

/* a card's field groups: each group is its own grid (so it starts on a new row)
   under a quiet label; sibling grids share the auto-fit track count, so columns
   still line up down the card. */
.settings-group {
	margin: 1rem 0 .1rem;
	font-size: .72rem;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: .05em;
	color: var(--muted);
}
.subhead + .settings-group { margin-top: .2rem; }
.settings-group + .settings-grid { margin-bottom: .3rem; }

/* one card per job kind, laid out side by side: four across on a very wide screen,
   two on a laptop, one full-width lane when the viewport is narrow. min(100%, …)
   keeps the single-column case from overflowing on a phone. */
.job-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 21rem), 1fr)); gap: 1rem; margin-top: 1rem; }
/* the row is a plain <div>, so `.card + .card` does not apply to whatever follows it */
.job-cards + .card { margin-top: 1rem; }
.job-cards > .card { margin-top: 0; display: flex; flex-direction: column; }
.job-card-desc { font-size: .78rem; margin: -.3rem 0 .9rem; }
/* the picker sits at the card's bottom edge, so cards in a row line their grids up
   even when their field lists differ in length. The gap above it is padding, not
   margin: `auto` collapses to 0 in the tallest card of the row, which would leave
   its picker butting straight against the last field. */
.job-card .jobwin { margin-top: auto; }

/* ---- job start-window picker (7 weekdays × 24 hours) ----
   A grid of toggle cells; the hidden input beside it carries the encoded window and
   is the only thing that submits. user-select:none keeps a drag from selecting text.
   The cells are plain <td> (not buttons): 168 focus stops per grid would swamp
   keyboard navigation, so the weekday/hour headers are the coarse-grained handles. */
.jobwin { padding-top: 1.1rem; user-select: none; }
.jobwin-head { display: flex; align-items: baseline; justify-content: space-between; gap: .5rem; margin-bottom: .4rem; }
.jobwin-title { font-size: .82rem; color: var(--muted); display: inline-flex; align-items: center; gap: .1rem; }
.jobwin-count { font-size: .72rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.jobwin-grid { table-layout: fixed; border-collapse: separate; border-spacing: 1px; }
.jobwin-grid th { padding: 0; border: 0; text-transform: none; letter-spacing: 0; font-size: .68rem; font-weight: 500; text-align: center; }
.jobwin-grid tbody tr:hover { background: none; }
.jobwin-grid .jw-corner { width: 1.6rem; }
.jobwin-grid .jw-day, .jobwin-grid .jw-hour { cursor: pointer; }
.jobwin-grid .jw-day:hover, .jobwin-grid .jw-hour:hover { color: var(--heading); }
.jobwin-grid .jw-hour { text-align: right; padding-right: .3rem; font-variant-numeric: tabular-nums; line-height: 1; }
.jw-cell { padding: 0; border: 0; height: 13px; border-radius: 2px; background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--border-2); cursor: pointer; }
.jw-cell.on { background: var(--accent); box-shadow: none; }
.jw-cell:hover { box-shadow: inset 0 0 0 1px var(--accent); }
.jobwin-foot { display: flex; align-items: center; gap: .5rem; margin-top: .5rem; flex-wrap: wrap; }
.jobwin-foot button { margin-top: 0; }
.jobwin-foot .field-hint { margin-top: 0; }

/* ---- buttons ---- */
button, .btn {
	display: inline-flex;
	align-items: center;
	gap: .45rem;
	margin-top: 1.15rem;
	padding: .55rem 1rem;
	background: var(--accent);
	color: #fff;
	border: 0;
	border-radius: 7px;
	cursor: pointer;
	/* <button> does not inherit the body font/line-height (it defaults to the UA
	   Arial + line-height:normal), which made a <button> shorter than a sibling
	   <a class="btn">. Normalize both so paired buttons match height. */
	font-family: inherit;
	line-height: 1.45;
	font-weight: 600;
	font-size: .9rem;
	text-decoration: none;
}
button:hover, .btn:hover { background: var(--accent-hover); }
button.secondary, .btn.secondary { background: var(--border-2); }
button.secondary:hover, .btn.secondary:hover { background: #424a57; }
button.danger, .btn.danger { background: #b4232a; }
button.danger:hover, .btn.danger:hover { background: #cb2a32; }
button.small { margin: 0; padding: .35rem .65rem; font-size: .82rem; }
.btn.small { margin: 0; padding: .35rem .7rem; font-size: .82rem; }
.btn.small.disabled { opacity: .45; pointer-events: none; }
/* A lone FontAwesome glyph only makes a 1em line box, so an icon-only button came
   out shorter than an icon+text sibling (whose label fills the 1.45 line box). Give
   button icons the button's line-height so both reserve an equal box and match height. */
.btn i { line-height: 1.45; }
/* The display rule above defeats the UA `[hidden] { display: none }`, which left
   JS-hidden buttons (the pager's no-JS "Apply" fallback) visible. Restore it. */
button[hidden], .btn[hidden] { display: none; }

/* ---- pagination control (shared `pager` component) ---- */
.pager { display: flex; align-items: center; justify-content: space-between; gap: .6rem 1.25rem; margin-top: 1rem; flex-wrap: wrap; font-size: .85rem; }
.pager-range { white-space: nowrap; }

/* numbered page strip: first/prev · 1 2 3 … N · next/last. Ghost buttons (no chrome
   until hover) with the current page filled, echoing the active-nav accent. */
.pager-nav { display: flex; align-items: center; gap: .15rem; }
.pager-pages { display: flex; align-items: center; gap: .15rem; margin: 0; padding: 0; list-style: none; }
.pager-step, .pager-page {
	display: inline-flex; align-items: center; justify-content: center;
	min-width: 2rem; height: 2rem; padding: 0 .5rem;
	border-radius: 7px; font-size: .85rem; font-weight: 500;
	color: var(--text); text-decoration: none; cursor: pointer; user-select: none;
	transition: background .12s, color .12s;
}
.pager-step { color: var(--muted); }
.pager-step:hover, .pager-page:hover { background: var(--border-2); color: var(--text); }
.pager-step.disabled { opacity: .35; pointer-events: none; }
.pager-page.current, .pager-page.current:hover { background: var(--accent); color: #fff; font-weight: 600; cursor: default; }
.pager-ellipsis {
	display: inline-flex; align-items: flex-end; justify-content: center;
	min-width: 1.4rem; height: 2rem; color: var(--muted); user-select: none;
}

.pager-size { display: flex; align-items: center; gap: .45rem; margin: 0; white-space: nowrap; }
.pager-size select { width: auto; padding: .3rem .55rem; font-size: .82rem; }
.pager-size-label { margin: 0; }

/* keep the Cancel link clearly separated from the primary submit button */
form button[type="submit"] + a.btnlink { margin-left: 1.5rem; }

/* ---- tables ---- */
table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: .55rem .6rem; border-bottom: 1px solid var(--border); font-size: .9rem; vertical-align: middle; }
th { color: var(--muted); font-weight: 600; font-size: .78rem; text-transform: uppercase; letter-spacing: .03em; }
tbody tr:hover { background: var(--row-hover); }

/* click-to-sort headers: the inner link fills the cell so the whole header is a
   target; an arrow shows the active direction, a dimmed neutral glyph the rest. */
th.sortable { padding: 0; }
th.sortable > a { display: flex; align-items: center; gap: .4rem; padding: .55rem .6rem; color: inherit; text-decoration: none; cursor: pointer; white-space: nowrap; }
th.sortable > a:hover { color: var(--text); }
th.sortable .sort-ind { font-size: .72rem; flex: 0 0 auto; opacity: .3; }
th.sortable:hover .sort-ind { opacity: .6; }
th.sorted > a { color: var(--text); }
th.sorted .sort-ind { opacity: .95; color: var(--accent); }

/* key/value detail table (repo detail): left-aligned keys, no uppercase */
table.kv th { width: 14rem; text-transform: none; letter-spacing: 0; color: var(--muted); font-weight: 600; vertical-align: top; }
table.kv td { color: var(--text); }
table.kv tbody tr:hover { background: none; }
table.kv tr:last-child th, table.kv tr:last-child td { border-bottom: 0; }

/* the executed restic command line in a kv cell (Job detail) */
code.cmd { background: var(--code-bg); border: 1px solid var(--border); border-radius: 4px; padding: .1rem .35rem; font-size: .82rem; word-break: break-all; color: var(--code-text); }

/* an entity id in a list's identity column (Jobs): monospaced so the fixed-width ids
   line up, quiet enough not to outshout the columns that carry meaning. */
code.idcode { font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; font-size: .78rem; letter-spacing: -.01em; white-space: nowrap; }

/* kv cell with a right-aligned relative note ("in 3 days" / "3 days ago" / "(exists)"):
   the value keeps the left edge, the note hugs the right so a column of them aligns. */
.kv-line { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.kv-delta { color: var(--muted); font-size: .78rem; white-space: nowrap; }
.kv-delta.good { color: var(--ok-text); }
.kv-delta.pending { color: var(--warn-text); }
.kv-delta.bad { color: var(--err-text); }

/* unstyled stacked list (e.g. notification URLs in a kv cell) */
.plain-list { list-style: none; padding: 0; margin: 0; }
.plain-list li { padding: .1rem 0; }
.plain-list li code { word-break: break-all; }

/* notification URL row: masked text + reveal toggle + test button on one line */
.notify-url { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.notify-url .notify-url-text { flex: 1 1 auto; min-width: 0; word-break: break-all; }
/* keep the reveal toggle + test button at their natural width — only the text flexes */
.notify-url .btn, .notify-url form.inline { flex-shrink: 0; }

/* server-form notification editor: one full-width input + remove button per URL */
.notify-url-list { display: flex; flex-direction: column; gap: .4rem; }
.notify-url-edit { display: flex; align-items: center; gap: .5rem; }
.notify-url-edit input { flex: 1 1 auto; min-width: 0; font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; font-size: .85rem; }
.notify-url-edit .notify-url-del { flex: 0 0 auto; }
#notify-url-add { margin-top: .55rem; }

/* ---- batch-add grid (M11) ---- */
.batch-grid { min-width: 56rem; }
.batch-grid th, .batch-grid td { padding: .4rem .4rem; vertical-align: top; }
.batch-grid input, .batch-grid select { margin: 0; padding: .4rem .5rem; font-size: .85rem; }
.batch-grid select { min-width: 9rem; }
.batch-grid input[type="text"], .batch-grid input[type="password"] { min-width: 8.5rem; }
.batch-grid input[type="number"] { min-width: 5rem; }
/* cells are top-aligned (so error rows keep inputs at top with the error below);
   nudge the lone checkbox down so it centers on the input band, not the cell top */
.batch-grid td.batch-enabled { text-align: center; }
.batch-grid td.batch-enabled input { width: 16px; height: 16px; accent-color: var(--accent); margin-top: .43rem; }
/* the rowend holds only the remove button (+ optional error text below it): middle
   centers the button on the inputs on normal rows, and is a no-op when an error makes
   this the tallest cell, so the button stays top-aligned with the error flowing under */
.batch-grid td.batch-rowend { white-space: nowrap; vertical-align: middle; }
.batch-grid tbody tr:hover { background: none; }
.batch-grid tr.row-error td { background: var(--red-bg); }
.batch-grid tr.row-error td:first-child { box-shadow: inset 3px 0 0 var(--red-border); }
.row-err { color: var(--err-text); font-size: .75rem; margin-top: .3rem; max-width: 16rem; white-space: normal; }
.batch-actions { display: flex; gap: .8rem; align-items: center; margin-top: 1rem; }
.batch-created { list-style: none; padding: 0; margin: 0 0 1rem; }
.batch-created li { padding: .2rem 0; font-size: .9rem; }
.batch-created li .fa-circle-check { color: var(--ok-text); }

/* ---- batch-edit (select + shared-field editor) ---- */
/* step 1: the row-select checkbox column */
th.selcol, td.selcol { width: 2.2rem; text-align: center; }
.selcol input[type="checkbox"] { width: 16px; height: 16px; margin: 0; accent-color: var(--accent); cursor: pointer; }
/* step 2: the chosen-repository summary */
.batchedit-targets { margin: .2rem 0 1rem; }
.batchedit-targets summary { cursor: pointer; color: var(--muted); font-size: .85rem; }
.batchedit-targets ul { margin: .5rem 0 0; font-size: .88rem; }
/* step 2: one editable field = left-hand "change this" toggle + its control(s) */
.batchedit-row { display: flex; align-items: flex-start; gap: .8rem; padding: .8rem 0; border-bottom: 1px solid var(--border); }
.batchedit-row:last-of-type { border-bottom: 0; }
.bf-check { flex: 0 0 auto; margin: 0; padding-top: 1.05rem; }
.bf-check input[type="checkbox"] { width: 16px; height: 16px; margin: 0; accent-color: var(--accent); cursor: pointer; }
.bf-body { flex: 1 1 auto; min-width: 0; }
.bf-body label { margin-top: 0; }
.bf-body label.checkline { margin-top: .35rem; }
/* a control whose toggle is off is disabled (will not submit); dim it to read as inert */
.batchedit-row .bf-control:disabled { opacity: .4; cursor: not-allowed; }
.batchedit-row:has(.bf-toggle:not(:checked)) .bf-body { opacity: .55; }

/* ---- banners ---- */
.banner {
	display: flex;
	align-items: flex-start;
	gap: .6rem;
	padding: .7rem 1rem;
	border-radius: 7px;
	margin-bottom: 1rem;
	font-size: .9rem;
}
/* preserve multi-line / preformatted error detail (e.g. a formatted exerr) and let
   long redacted URLs wrap instead of overflowing the banner */
.banner-text { white-space: pre-wrap; word-break: break-word; min-width: 0; }
.banner.error  { background: var(--red-bg);    border: 1px solid var(--red-border);    color: var(--err-text); }
.banner.notice { background: var(--green-bg);  border: 1px solid var(--green-border);  color: var(--ok-text); }
.banner.lock   { background: var(--yellow-bg); border: 1px solid var(--yellow-border); color: var(--warn-text); }

/* ---- status tags ---- */
.tag {
	display: inline-flex;
	align-items: center;
	gap: .35rem;
	padding: .12rem .5rem;
	border-radius: 999px;
	font-size: .72rem;
	border: 1px solid var(--border-2);
	color: var(--muted);
	white-space: nowrap;
}
.tag .fa-solid { font-size: .85em; }
.tag.ok   { background: var(--green-bg);  border-color: var(--green-border);  color: var(--ok-text); }
.tag.off  { background: var(--red-bg);     border-color: var(--red-border);     color: var(--err-text); }
.tag.warn { background: var(--yellow-bg);  border-color: var(--yellow-border);  color: var(--warn-text); }
/* a queued job held back by its kind's allowed-start window: still Queued in the
   database, so it reads informational (accent) rather than as a warning */
.tag.delayed { background: var(--nav-active-bg); border-color: var(--accent); color: var(--link); }
/* a repository tag rendered read-only (list rows, detail) — sits next to the name */
.tag-badge { margin-left: .35rem; vertical-align: middle; }

/* ---- inspection progress ring (repository list "Inspection" column) ----
   A thin border-only donut: a faint full-circle track with a coloured arc on top whose
   length is the inspected fraction. The arc length is set per-row via the SVG
   stroke-dasharray presentation attribute (not a style — keeps it within the CSP); the
   radius is chosen so the circumference is 100, so the dasharray reads as a percentage.
   stroke-linecap stays butt so a 0% arc draws nothing. */
.inspect { display: inline-flex; align-items: center; gap: .4rem; }
.inspect-ring { flex: 0 0 auto; }
.inspect-track { stroke: var(--border-2); }
.inspect-arc { stroke: var(--accent); transition: stroke-dasharray .2s ease; }
.inspect-ring.complete .inspect-arc { stroke: var(--green); }
.inspect-count { font-variant-numeric: tabular-nums; color: var(--muted); font-size: .82rem; white-space: nowrap; }

/* queued-jobs badge (repository list "Queued" column) — neutral, count-only */
.queued-badge {
	display: inline-flex;
	align-items: center;
	gap: .3rem;
	padding: .08rem .45rem;
	border-radius: 999px;
	font-size: .72rem;
	font-variant-numeric: tabular-nums;
	border: 1px solid var(--border-2);
	background: var(--panel-2);
	color: var(--text);
	white-space: nowrap;
}
.queued-badge .fa-solid { font-size: .85em; color: var(--muted); }

/* ---- list search (dashboard quick-filter + the /repositories search form) ---- */
/* One look for both: a magnifier-prefixed field at the top of the list card. The
   dashboard variant filters the rendered rows in the browser (app.js); the paginated
   variant is a GET form, so it also carries a submit button and a Clear link. */
.list-search { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem; margin: 0 0 .9rem; }
.list-search .ls-field { position: relative; display: flex; align-items: center; flex: 0 1 26rem; min-width: 12rem; }
.list-search .ls-field .fa-magnifying-glass { position: absolute; left: .7rem; color: var(--muted); font-size: .85rem; pointer-events: none; }
.list-search .ls-input { padding-left: 2rem; font-size: .88rem; }
.list-search button, .list-search .btn { margin-top: 0; }
.list-search .ls-reset { font-size: .82rem; display: inline-flex; align-items: center; gap: .3rem; }
.list-search .ls-count { white-space: nowrap; }
/* Firefox has no native clear affordance for type=search; the Esc hint in the
   no-match note covers it, so nothing else is needed here. */
.ls-empty { margin: 0 0 .9rem; }

/* ---- repository tags ---- */
/* filter-bars: stacked Tags row + (optional) Servers row above a repository list */
.filter-bars {
	display: flex;
	flex-direction: column;
	gap: .45rem;
	margin: 0 0 1rem;
}
.filter-bars .tag-filter-bar { margin: 0; }
/* filter-bar: one clickable pill per tag/server, shown above a repository list */
.tag-filter-bar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: .4rem;
	margin: 0 0 1rem;
}
/* the filter-bar label column is fixed-width so the Tags/Servers rows align */
.filter-bars .tag-filter-label {
	min-width: 3.5rem;
}
.tag-filter-label { color: var(--muted); font-size: .78rem; margin-right: .15rem; }
.tag-filter, .tag-filter-clear {
	display: inline-flex;
	align-items: center;
	gap: .35rem;
	padding: .18rem .6rem;
	border-radius: 999px;
	font-size: .76rem;
	border: 1px solid var(--border-2);
	color: var(--nav-link);
	background: transparent;
	text-decoration: none;
	cursor: pointer;
}
.tag-filter .fa-solid, .tag-filter-clear .fa-solid { font-size: .85em; }
.tag-filter:hover { background: var(--nav-hover-bg); color: var(--heading); }
.tag-filter.active { background: var(--accent); border-color: var(--accent); color: #fff; }
.tag-filter-clear { color: var(--muted); }
.tag-filter-clear:hover { background: var(--nav-hover-bg); color: var(--heading); }

/* chip-style tag editor (repo form, detail inline editor, batch-edit) */
.tag-input {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: .35rem;
	width: 100%;
	padding: .35rem .4rem;
	background: var(--panel-2);
	border: 1px solid var(--border-2);
	border-radius: 7px;
	cursor: text;
}
.tag-input:focus-within { border-color: var(--accent); }
.tag-chip {
	display: inline-flex;
	align-items: center;
	gap: .3rem;
	padding: .12rem .25rem .12rem .55rem;
	border-radius: 999px;
	font-size: .76rem;
	background: var(--nav-active-bg);
	border: 1px solid var(--border-2);
	color: var(--heading);
}
.tag-rm {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	margin: 0;
	padding: 0;
	width: 16px;
	height: 16px;
	border-radius: 999px;
	background: transparent;
	color: var(--muted);
	font-size: .7rem;
	line-height: 1;
	cursor: pointer;
}
.tag-rm:hover { background: var(--red-bg); color: var(--err-text); }
.tag-entry {
	flex: 1 1 6rem;
	min-width: 6rem;
	margin: 0;
	padding: .2rem .25rem;
	background: transparent;
	border: 0;
	color: var(--heading);
	font-size: .9rem;
}
.tag-entry:focus { outline: none; }
.tag-entry:disabled { cursor: not-allowed; }

/* detail-page inline tag editor */
.repo-tags { margin-top: 1rem; }
.repo-tags-head { font-size: .82rem; color: var(--muted); margin-bottom: .4rem; }
.repo-tags-head .fa-solid { margin-right: .35rem; }
.tag-edit-form { display: flex; flex-wrap: wrap; align-items: flex-start; gap: .6rem; }
.tag-edit-form .tag-input { flex: 1 1 18rem; }
.tag-edit-form button { margin-top: 0; }

/* batch-edit tags: the add/remove/replace mode radios */
.bf-tagmode { display: flex; flex-wrap: wrap; gap: 1rem; margin: .2rem 0 .5rem; }
.radio-inline { display: inline-flex; align-items: center; gap: .35rem; margin: 0; font-size: .85rem; color: var(--text); cursor: pointer; }
.radio-inline input[type="radio"] { width: 15px; height: 15px; margin: 0; accent-color: var(--accent); cursor: pointer; }

/* ---- misc ---- */
.muted { color: var(--muted); font-size: .82rem; }
.notif-when { white-space: pre; }
.sched-line { margin: .2rem 0 .6rem; }
.sched-line code { background: var(--code-bg); border: 1px solid var(--border); border-radius: 4px; padding: 0 .3rem; }
.row-actions { display: flex; gap: .4rem; align-items: center; }
form.inline { display: inline; margin: 0; }
a.btnlink { color: var(--link); text-decoration: none; white-space: nowrap; }
a.btnlink:hover { text-decoration: underline; }
/* a disabled repository's server + name are struck through (in the fleet/repo lists)
   instead of carrying a badge: still a working link, but muted (grey, like the path
   column) rather than link-blue. The strike survives hover (where btnlink underlines). */
a.btnlink.repo-disabled { color: var(--muted); text-decoration: line-through; }
a.btnlink.repo-disabled:hover { text-decoration: line-through underline; }
.danger-zone { display: flex; gap: .6rem; }
.validate-zone { display: flex; gap: .6rem; align-items: center; margin: .4rem 0 .6rem; }
.restic-output {
	background: var(--code-bg); border: 1px solid var(--border); border-radius: 6px;
	padding: .6rem .7rem; margin: .5rem 0; max-height: 18rem; overflow: auto;
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .78rem;
	white-space: pre-wrap; word-break: break-word; color: var(--code-text);
}
.spacer { margin-left: auto; }
.integrity-output { margin: .4rem 0 .2rem; }
.integrity-output > summary { cursor: pointer; color: var(--muted, #8b93a7); font-size: .82rem; }
.integrity-output > summary:hover { color: inherit; }

/* ---- dashboard (M8 alerts surfacing) ---- */
.stat-row { display: flex; flex-wrap: wrap; gap: .9rem; margin: .2rem 0 .2rem; }
.stat {
	flex: 1 1 8rem; min-width: 8rem;
	background: var(--panel-2); border: 1px solid var(--border); border-radius: 9px;
	padding: .7rem .9rem;
}
.stat .num { font-size: 1.6rem; font-weight: 700; line-height: 1.1; }
.stat .lbl { color: var(--muted); font-size: .8rem; }
.stat.warn { background: var(--yellow-bg); border-color: var(--yellow-border); }
.stat.warn .num { color: var(--warn-text); }
.stat.good { background: var(--green-bg); border-color: var(--green-border); }
.stat.good .num { color: var(--ok-text); }

.alert-list { display: flex; flex-direction: column; gap: .55rem; }
.alert-card {
	display: flex; align-items: flex-start; gap: .8rem;
	background: var(--panel-2); border: 1px solid var(--border); border-left-width: 4px;
	border-radius: 8px; padding: .7rem .9rem;
}
.alert-card.sev-warn { border-left-color: var(--yellow); }
.alert-card.sev-error { border-left-color: var(--red); }
.alert-card .ac-body { flex: 1 1 auto; min-width: 0; }
.alert-card .ac-title { font-weight: 600; }
/* Alert messages are multi-line (a concise condition + a repo-status block); keep
   the newlines but collapse incidental whitespace and wrap long lines. */
.alert-card .ac-msg { color: var(--muted); font-size: .85rem; margin-top: .15rem; white-space: pre-line; overflow-wrap: anywhere; }
.alert-card .ac-meta { color: var(--muted); font-size: .78rem; margin-top: .25rem; display: flex; flex-wrap: wrap; gap: .8rem; }
.alert-card .ac-side { display: flex; flex-direction: column; align-items: flex-end; gap: .4rem; white-space: nowrap; }

/* The whole-card "you have active warnings" banner pulses to draw the eye. The
   animation respects prefers-reduced-motion (steady amber, no pulsing). */
.flash-banner {
	display: flex; align-items: center; gap: .7rem;
	border: 1px solid var(--yellow-border); background: var(--yellow-bg); color: var(--warn-text);
	border-radius: 10px; padding: .8rem 1.1rem; margin-bottom: 1rem; font-weight: 600;
	animation: rsd-flash 1.4s ease-in-out infinite;
}
.flash-banner .fa-solid { color: var(--yellow); }
/* system-degraded banner (/api/health): more severe than alerts, so red + static */
.degraded-banner {
	display: flex; align-items: center; gap: .7rem;
	border: 1px solid var(--red-border); background: var(--red-bg); color: var(--err-text);
	border-radius: 10px; padding: .8rem 1.1rem; margin-bottom: 1rem; font-weight: 600;
}
.degraded-banner .fa-solid { color: var(--red); }
@keyframes rsd-flash {
	0%, 100% { box-shadow: 0 0 0 0 rgba(240, 195, 74, 0.0); border-color: var(--yellow-border); }
	50%      { box-shadow: 0 0 0 4px rgba(240, 195, 74, 0.18); border-color: var(--yellow); }
}
@media (prefers-reduced-motion: reduce) {
	.flash-banner { animation: none; }
}
.ack-note { color: var(--muted); font-size: .78rem; }
.tag.state-resolved { background: var(--panel-2); border-color: var(--border-2); color: var(--muted); }
.tag.state-active { background: var(--yellow-bg); border-color: var(--yellow-border); color: var(--warn-text); }
/* Past (resolved) rows are muted so the active ones at the top of the list lead. */
tr.alert-resolved td { opacity: .62; }

/* ---- header alert bell (M8) ---- */
.nav-bell { position: relative; }
.nav-bell .bell-badge {
	display: inline-flex; align-items: center; justify-content: center;
	min-width: 1.15rem; height: 1.15rem; padding: 0 .35rem; margin-left: .15rem;
	border-radius: 999px; font-size: .72rem; font-weight: 700; line-height: 1;
	background: var(--yellow); color: #1a1505; vertical-align: middle;
}
/* An active count tints the bell amber and pulses it to draw the eye; an Error-
   severity alert escalates to red. Respects prefers-reduced-motion. */
.nav-bell.has-alerts { color: var(--warn-text); }
.nav-bell.has-alerts .fa-bell { animation: rsd-bell 1.6s ease-in-out infinite; transform-origin: 50% 0; }
.nav-bell.has-error { color: var(--err-text); }
.nav-bell.has-error .bell-badge { background: var(--red); color: #fff; }
@keyframes rsd-bell {
	0%, 60%, 100% { transform: rotate(0); }
	70% { transform: rotate(12deg); }
	80% { transform: rotate(-10deg); }
	90% { transform: rotate(6deg); }
}
@media (prefers-reduced-motion: reduce) {
	.nav-bell.has-alerts .fa-bell { animation: none; }
}

/* ---- header jobs queue badge ---- a neutral, informational running+queued count
   (no colour/severity/animation, unlike the alert bell above). */
.nav-jobs { position: relative; }
.nav-jobs .jobs-badge {
	display: inline-flex; align-items: center; justify-content: center;
	min-width: 1.15rem; height: 1.15rem; padding: 0 .35rem; margin-left: .15rem;
	border-radius: 999px; font-size: .72rem; font-weight: 700; line-height: 1;
	background: var(--border-2); color: var(--nav-link); vertical-align: middle;
}

/* ---- M10 dashboard: top cards + repositories table ---- */
.dash-cards { display: grid; grid-template-columns: repeat(5, 1fr); gap: .9rem; margin-bottom: 1rem; }
.dash-card {
	display: flex; align-items: center; gap: .9rem;
	background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
	padding: 1rem 1.1rem; color: inherit; text-decoration: none;
}
.dash-card:hover { border-color: var(--border-2); background: var(--row-hover); }
.dash-card .dc-icon {
	flex: 0 0 auto; width: 2.6rem; height: 2.6rem; border-radius: 9px;
	display: inline-flex; align-items: center; justify-content: center; font-size: 1.15rem;
	background: var(--panel-2); color: var(--muted);
}
.dash-card .dc-icon .fa-solid { color: inherit; font-size: 1em; }
.dash-card .dc-body { min-width: 0; }
.dash-card .dc-num { font-size: 1.7rem; font-weight: 700; line-height: 1.05; }
.dash-card .dc-num.text { font-size: 1.15rem; padding-top: .25rem; }
.dash-card .dc-lbl { color: var(--muted); font-size: .8rem; }
.dash-card.is-warn { border-color: var(--yellow-border); }
.dash-card.is-warn .dc-icon { background: var(--yellow-bg); color: var(--yellow); }
.dash-card.is-warn .dc-num { color: var(--warn-text); }
.dash-card.is-running .dc-icon { background: var(--green-bg); color: var(--green); }
.dash-card.is-running .dc-num { color: var(--ok-text); }
/* the build-info card is informational (not a link): keep it quiet, no hover lift */
.dash-card.build-card { cursor: default; }
.dash-card.build-card:hover { border-color: var(--border); background: var(--panel); }
.dash-card.build-card .dc-num { font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace); }
/* the warnings card pulses while >0 to draw the eye (same motion as the banner) */
.dash-card.flash { animation: rsd-flash 1.4s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .dash-card.flash { animation: none; } }

/* the main repositories table can overflow horizontally on a phone — let it scroll
   instead of squishing, and keep status/time cells on one line. The padding (cancelled
   by the negative margin so layout is unchanged) reclaims room from the surrounding
   gap so a hover tooltip inside the scroller isn't clipped by the overflow box. */
.scroll-x { overflow-x: auto; padding: 1rem .6rem; margin: -1rem -.6rem; }
.repo-table th, .repo-table td { white-space: nowrap; }
.repo-table td.col-name { white-space: normal; }
.repo-sep { color: var(--muted); margin: 0 .15rem; }
/* on a wide desktop the dashboard table fits, so drop the scroller entirely and let
   the status/integrity hover tooltips show in full; phones keep the scroll (and have
   no hover anyway). */
@media (hover: hover) and (min-width: 1100px) {
	.dash-scroll { overflow: visible; padding: 0; margin: 0; }
}

/* ---- hover tooltips (CSS, dependency-free, multi-line) ----
   Any element with a data-tip attribute shows that text as a styled bubble on hover.
   white-space:pre-wrap keeps authored line breaks (the hand-aligned status/integrity
   tips stay aligned) but also wraps long prose and URLs at max-width; overflow-wrap
   :anywhere breaks an unspaced URL so it can't blow past the cap. The bubble sits
   above + centred by default; .tip-start / .tip-end anchor it to the trigger's left /
   right edge so a status badge in the first table column (or an integrity badge in the
   last) doesn't run off the side of the page. Interactive triggers keep their own
   cursor — only non-interactive ones get the help cursor. data-tip cannot render on
   <input> (no ::after on void elements), so those keep a native title. */
.tip { cursor: help; }
[data-tip] { position: relative; cursor: help; }
a[data-tip], button[data-tip], label[data-tip] { cursor: pointer; }
[data-tip]::after {
	content: attr(data-tip);
	position: absolute;
	left: 50%;
	bottom: calc(100% + 8px);
	transform: translateX(-50%);
	white-space: pre-wrap;
	/* size to content (widest authored line) up to the cap, instead of letting the
	   absolute box shrink-to-fit to the narrow trigger and wrap a word per line. */
	width: max-content;
	max-width: min(24rem, 90vw);
	overflow-wrap: anywhere;
	font: .72rem/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
	text-align: left;
	color: var(--text);
	background: var(--panel);
	border: 1px solid var(--border-2);
	border-radius: 8px;
	padding: .5rem .65rem;
	box-shadow: 0 8px 24px var(--shadow);
	z-index: 60;
	opacity: 0;
	visibility: hidden;
	transition: opacity .12s ease, visibility .12s ease;
	pointer-events: none;
}
[data-tip]:hover::after, [data-tip]:focus-visible::after { opacity: 1; visibility: visible; }
[data-tip].tip-start::after { left: 0; right: auto; transform: none; }
[data-tip].tip-end::after { left: auto; right: 0; transform: none; }

/* ---- info icon (circled "?" beside a label) ----
   In a settings .field the tooltip is anchored to the column (full-width) and dropped
   just below the label, so it never runs off the page whichever grid column it's in;
   elsewhere (the narrow repo/server forms) it anchors to the icon and opens below. */
.info-icon {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	margin-left: .35rem;
	color: var(--muted);
	font-size: .82em;
	vertical-align: middle;
	cursor: help;
}
.info-icon:hover { color: var(--accent); }
.info-icon[data-tip]::after { left: 0; right: auto; top: calc(100% + 8px); bottom: auto; transform: none; }
.field { position: relative; }
.field .info-icon { position: static; }   /* so its tip anchors to .field, not the icon */
.field .info-icon[data-tip]::after { top: 1.6rem; }

/* ---- input with a unit suffix (settings) ----
   input + unit share one border; the unit sits in its own slightly-contrasting cell
   on the right, so the unit is never part of the typed value. */
.input-unit {
	display: flex;
	align-items: stretch;
	width: 100%;
	background: var(--panel-2);
	border: 1px solid var(--border-2);
	border-radius: 7px;
	overflow: hidden;
}
.input-unit:focus-within { border-color: var(--accent); }
.input-unit > input {
	flex: 1 1 auto;
	min-width: 0;
	border: 0;
	border-radius: 0;
	background: transparent;
}
.input-unit > input:focus { outline: none; border: 0; }
.input-unit > .unit {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	padding: 0 .6rem;
	font-size: .8rem;
	color: var(--muted);
	background: var(--affix-bg);
	border-left: 1px solid var(--border-2);
	white-space: nowrap;
	user-select: none;
}

/* subheadings used inside multi-section cards (Jobs page etc.) */
h2.subhead { font-size: 1.02rem; margin: .2rem 0 .8rem; display: inline-flex; align-items: center; gap: .5rem; }
h2.subhead .fa-solid { color: var(--accent); font-size: .9em; }
.errortext { color: var(--err-text); }

/* ---- REST API page (personal API keys + endpoint documentation) ----
   Method badge, endpoint rows and the collapsible per-endpoint docs. Endpoint rows
   are one flex line (method · path · title · required role) shared by the create
   form's checkboxes and the documentation accordion, so the two read identically. */
.api-method {
	flex: 0 0 auto;
	min-width: 3.2rem;
	padding: .1rem .4rem;
	border-radius: 4px;
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: .68rem;
	font-weight: 700;
	letter-spacing: .04em;
	text-align: center;
	color: #fff;
}
.api-method.get { background: var(--green); }
.api-method.post { background: var(--accent); }
.api-path {
	background: var(--code-bg); border: 1px solid var(--border); border-radius: 4px;
	padding: 0 .35rem; font-size: .8rem; color: var(--code-text); white-space: nowrap;
}
.api-route-title { color: var(--muted); font-size: .82rem; min-width: 0; overflow-wrap: anywhere; }

.api-routes label.checkline { margin: .45rem 0 0; gap: .5rem; flex-wrap: wrap; }
.api-routes label.checkline:first-of-type { margin-top: .2rem; }

/* the grant tags in the key table wrap instead of stretching the column */
.api-grants { display: flex; flex-wrap: wrap; gap: .3rem; }

.api-endpoint { border: 1px solid var(--border); border-radius: 8px; margin: .5rem 0; background: var(--panel-2); }
.api-endpoint > summary {
	display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
	padding: .55rem .75rem; cursor: pointer; list-style: none;
}
.api-endpoint > summary::-webkit-details-marker { display: none; }
/* plain unicode caret rather than a FontAwesome glyph — no dependency on the
   vendored kit's font-family name, which changes between FA releases */
.api-endpoint > summary::after {
	content: "\25be"; /* ▾ */
	margin-left: auto; color: var(--muted); font-size: .9rem; transition: transform .12s ease;
}
.api-endpoint[open] > summary::after { transform: rotate(180deg); }
.api-endpoint > summary:hover { background: var(--nav-hover-bg); }
.api-endpoint[open] > summary { border-bottom: 1px solid var(--border); }
.api-endpoint-body { padding: .3rem .75rem .8rem; }
.api-endpoint-body > p { margin: .6rem 0; font-size: .88rem; }
.api-endpoint-body table { margin: .4rem 0 .2rem; }
.api-endpoint-body label { margin-top: .8rem; }

/* ---- confirmation dialog (destructive actions) ----
   One <dialog> built by app.js and reused by every form carrying data-confirm. */
.confirm-dialog {
	width: min(30rem, calc(100vw - 2rem));
	padding: 1.1rem 1.25rem 1.25rem;
	border: 1px solid var(--border-2);
	border-radius: 11px;
	background: var(--panel);
	color: var(--text);
	box-shadow: 0 18px 44px var(--shadow);
}
.confirm-dialog::backdrop { background: rgba(0, 0, 0, .55); }
.confirm-dialog .cd-title {
	display: flex; align-items: center; gap: .5rem;
	margin: 0 0 .6rem; font-size: 1.05rem; color: var(--heading);
}
.confirm-dialog .cd-title i { color: var(--red); }
.confirm-dialog .cd-msg { margin: 0; font-size: .9rem; }
.confirm-dialog .cd-actions { display: flex; justify-content: flex-end; gap: .6rem; margin-top: 1.3rem; }
.confirm-dialog .cd-actions button { margin-top: 0; }

/* ---- responsive ---- */
@media (max-width: 1100px) { .dash-cards { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 700px) { .dash-cards { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 460px) { .dash-cards { grid-template-columns: 1fr; } }
@media (max-width: 720px) {
	header { flex-wrap: wrap; }
	header .main-nav { order: 3; flex: 1 1 100%; justify-content: flex-start; flex-wrap: wrap; }
	main { margin: 1rem .8rem; }
	.card { padding: 1rem 1rem; }
}
