/* admin.officeplanner.pro — browser UI, plain CSS, no framework.
   Colors are defined exclusively here as variables - no raw hex anywhere
   else in the markup/JS. */

:root {
	--bg: #f4f5f7;
	--surface: #ffffff;
	--border: #e4e6eb;
	--text: #1c2230;
	--text-muted: #6b7280;
	--primary: #2454ff;
	--primary-hover: #1a3fd6;
	--on-primary: #ffffff;		/* text on the primary fill - dark in the dark scheme, where the fill is light */
	--danger: #c53030;
	--danger-bg: #fbe7e7;
	--success: #166a35;
	--success-bg: #e6f6ec;
	--warn: #92650a;
	--warn-bg: #fdf1d8;
	--partly-bg: #e5ebff;
	/* --warn is tuned for small text on a light badge background - as a
	   large solid fill (progress bars, dots) it reads muddy/brown instead of
	   as a clear amber warning. Separate, brighter variable for those. */
	--warn-fill: #e8960b;
	--radius: 10px;
	--feedback-image-height: 9rem;
	--radius-lg: 18px;		/* login card / auth inputs - softer than the 10px used inside the app */
	--radius-md: 12px;
	--input-h: 3.4667rem;		/* touch-friendly auth fields, taller than the dense in-app forms */
	--field-icon: #9aa2b1;	/* leading/trailing glyphs inside an input - never the text colour, they must recede */
	--info: #ffc700;
	--info-ink: #4a3b00;
	--tooltip-bg: #1f2430;
	--tooltip-ink: #ffffff;
	--dialog-backdrop: rgba(20, 24, 34, .48);
	--shadow: 0 1px 2px rgba(20, 24, 34, .06), 0 4px 16px rgba(20, 24, 34, .06);
	--z-tooltip: 3000;
	/* Layout (2026-10-23): the content takes the room beside the sidebar up to
	   --content-max; forms keep a readable width; a compact table is as wide as
	   its content but not narrower than --table-compact-min. */
	--content-max: 96rem;
	--form-max: 42.6667rem;
	--table-compact-min: 32rem;
	--action-gap: .5333rem;
	--intro-max: 75ch;	/* line length of a section introduction - readable, not across the whole card */
	--kb-nav-width: 18.6667rem;	/* knowledge base: the column of search and categories (280px) */
	--kb-article-max: 80ch;	/* knowledge base: line length of an article */
	--transition: 120ms ease;
	--topbar-h: 4rem;
	--sidebar-bg: #182238;
	--sidebar-text: #dfe3ee;
	--sidebar-text-muted: #8891a8;
	--sidebar-text-strong: #ffffff;	/* the brand and the active entry */
	--logo-name: var(--text);	/* the name in the product logo (BRAND_LOGO); its tiles keep their colours */
	--sidebar-logo-width: 11rem;
	/* The block under the sidebar logo (client, 2026-11-05): which app, in the portal
	   also whose account - left-aligned with the navigation, icon on the nav's icon column. */
	--sidebar-context-padding: .9333rem 1.3333rem;		/* the label starts where the section headings do */
	--sidebar-context-reading-indent: 3px;		/* the width of a nav entry's marker: the icon stands on the nav icons' column */
	--sidebar-context-label-size: .72rem;
	--sidebar-context-gap: .4rem;
	--sidebar-context-reading-size: .92rem;
	--sidebar-context-icon-size: 1.05rem;
	--sidebar-context-icon-gap: .6667rem;
	--auth-logo-width: 16rem;
	--sidebar-backdrop: rgba(20, 24, 34, .45);	/* behind the off-canvas sidebar on narrow screens */
	--sidebar-hover-bg: rgba(255, 255, 255, .07);
	--sidebar-border: rgba(255, 255, 255, .08);
	/* Deliberately darker/receding, NOT the hover/active highlight above -
	   a section label must never look like an interactive/active nav item
	   (real mistake made 2026-09-07: reused --sidebar-hover-bg here first). */
	--sidebar-section-bg: rgba(0, 0, 0, .18);
	/* The nav number that asks for a look (NAV_ITEMS `alert`, page "System"): red, never the quiet count. */
	--nav-alert-bg: var(--danger);
	--nav-alert-ink: #fff;
	/* Cards with a round icon and filled readings (featureCardHtml(), the
	   account page): the portal's block, same names and values. */
	--feature-icon-radius: 50%;
	--feature-icon-size: 3.2rem;
	--feature-icon-glyph-size: 1.6rem;
	--feature-icon-bg: #eef1ff;
	--feature-card-gap: 1.6rem;
	--feature-title-size: 1.3333rem;
	--feature-title-gap: .9333rem;
	--feature-section-gap: .9333rem;
	--feature-label-width: 17rem;
	--feature-label-bg: #f0f2f5;
	--feature-value-bg: #f8f9fb;
	--feature-cell-padding: .5333rem .9333rem;
	--feature-corner: 6px;
	--feature-rule: 1px solid var(--border);
	--feature-row-rule: 1px solid var(--surface);
	--feature-label-weight: 600;
	--feature-info-gap: .5333rem;
	/* A reading in a toolbar (toolbarReadingHtml()): a quiet box, smaller than a button. */
	--toolbar-reading-padding: .2667rem .6667rem;
	--toolbar-reading-gap: .5333rem;
	/* The colour scheme switch at the end of the top bar (themeToggleHtml()). */
	--theme-toggle-size: 2.1333rem;
	--theme-toggle-icon-size: 1.2rem;
}

/* Dark scheme (client, 2026-11-05): only colour tokens change, never a size -
   the values of the Windows app's dark theme, the same block as the portal.
   Set by theme.js on <html data-theme="dark">. Not pure black: a black
   ground under white text produces halation. */
:root[data-theme="dark"] {
	color-scheme: dark;
	--bg: #161920;
	--surface: #1e222b;
	--border: #2d323d;
	--text: #e4e7ec;
	--text-muted: #99a1af;
	--primary: #5b8cff;		/* lifted: the light scheme's blue has too little contrast here */
	--primary-hover: #7aa2ff;
	--on-primary: #0e1729;
	--danger: #e5645a;
	--danger-bg: #2c1d1d;
	--success: #3ec46d;
	--success-bg: #17291f;
	--warn: #e8b93a;
	--warn-bg: #2e2715;
	--partly-bg: #1f2a4d;
	--warn-fill: #f0a04b;
	--field-icon: #6b7385;
	--info: #d8a800;
	--info-ink: #241c00;
	--tooltip-bg: #39414f;		/* lighter than the page, so it stands off it */
	--tooltip-ink: #f2f4f8;
	--shadow: 0 1px 2px rgba(0, 0, 0, .35), 0 4px 16px rgba(0, 0, 0, .45);
	--dialog-backdrop: rgba(0, 0, 0, .6);
	--sidebar-bg: #11151d;
	--sidebar-backdrop: rgba(0, 0, 0, .55);
	--nav-alert-ink: #2a0f0c;
	--feature-icon-bg: #1c2740;
	--feature-label-bg: #262b35;
	--feature-value-bg: #222731;
}

* { box-sizing: border-box; }

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

a { color: var(--primary); text-decoration: none; }
a:hover { text-decoration: underline; }

h1, h2, h3 { margin: 0 0 .6667rem; font-weight: 650; }
h1 { font-size: 1.4rem; }
h2 { font-size: 1.15rem; }
h3 { font-size: 1rem; }

p { margin: 0 0 .5333rem; }

/* --- App shell: full-height left sidebar + top bar + content column ---
   Same information architecture as a conventional admin panel (e.g. Plesk):
   a dark, full-height sidebar (logo/brand + navigation) on the left; the top
   bar only spans the remaining width and only carries account-level
   controls (language, user, logout), right-aligned. */

.shell { display: flex; min-height: 100vh; align-items: stretch; }
.content-column { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.main-area { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.topbar {
	height: var(--topbar-h);
	position: sticky;
	top: 0;
	z-index: 50;
	display: flex;
	align-items: center;
	gap: 1.0667rem;
	padding: 0 1.6rem;
	background: var(--surface);
	border-bottom: 1px solid var(--border);
}
.topbar .hamburger { display: none; }
.topbar-brand-mobile { display: none; font-weight: 700; font-size: 1rem; }
.topbar-spacer { flex: 1; }
.topbar select {
	max-width: 9.3333rem;
	padding: .2667rem .5333rem;
	font-size: .82rem;
	border-color: transparent;
	background: none;
}
.topbar select:hover { border-color: var(--border); }
/* The colour scheme switch - the product page's toggle: a round button whose
   icon names the scheme a click switches to (UI-PATTERNS "Shell"). */
.theme-toggle {
	display: inline-flex; align-items: center; justify-content: center; flex: none;
	width: var(--theme-toggle-size); height: var(--theme-toggle-size);
	padding: 0; border: 1px solid var(--border); border-radius: 50%;
	background: none; color: var(--text); cursor: pointer;
}
.theme-toggle:hover, .theme-toggle:focus-visible { background: var(--bg); border-color: var(--primary); color: var(--primary); }
.theme-toggle svg { width: var(--theme-toggle-icon-size); height: var(--theme-toggle-icon-size); }

/* User menu: name/role click-target, dropdown holds actions (currently just
   Logout) - matches the usual "account name opens a menu" admin-panel
   pattern instead of a permanently visible logout button. */
.user-menu { position: relative; }
.user-menu-trigger {
	display: flex;
	align-items: center;
	gap: .5333rem;
	border: none;
	background: none;
	padding: .4rem .5333rem;
	border-radius: 8px;
	cursor: pointer;
}
.user-menu-trigger:hover { background: var(--bg); border-color: transparent; }
.user-menu-trigger .who { display: flex; flex-direction: column; align-items: flex-end; line-height: 1.25; }
.user-menu-trigger .name { font-size: .85rem; font-weight: 600; color: var(--text); }
.user-menu-trigger .role { font-size: .72rem; color: var(--text-muted); text-transform: capitalize; }
.user-menu-trigger .chevron { font-size: .7rem; color: var(--text-muted); }
.user-menu-trigger .user-icon { font-size: 1.15rem; line-height: 1; }
.user-menu-dropdown .menu-icon { display: inline-block; width: 1.3em; }
.user-menu-dropdown {
	position: absolute;
	top: calc(100% + .4rem);
	right: 0;
	min-width: 10.6667rem;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 8px;
	box-shadow: var(--shadow);
	padding: .4rem;
	z-index: 60;
}
.user-menu-dropdown[hidden] { display: none; }
.user-menu-dropdown button {
	display: block;
	width: 100%;
	text-align: left;
	border: none;
	background: none;
	padding: .5333rem .6667rem;
	border-radius: 6px;
	font-size: .88rem;
}
.user-menu-dropdown button:hover { background: var(--bg); border-color: transparent; }

.sidebar {
	flex: 0 0 15.3333rem;
	width: 15.3333rem;
	background: var(--sidebar-bg);
	color: var(--sidebar-text);
	display: flex;
	flex-direction: column;
	position: sticky;
	top: 0;
	height: 100vh;
	overflow-y: auto;
}
.sidebar .brand {
	padding: 1.3333rem 1.3333rem 1.0667rem;
	font-weight: 700;
	font-size: 1.05rem;
	color: var(--sidebar-text-strong);
	border-bottom: 1px solid var(--sidebar-border);
}
/* Under the logo: the app's name as a small label and - portal - the
   customer number as a reading (GENERAL_RULES 6.1: no frame, no hover, no
   hand cursor, so it never looks like a button or a menu entry). */
.sidebar-context {
	display: flex; flex-direction: column; gap: var(--sidebar-context-gap);
	padding: var(--sidebar-context-padding);
	border-bottom: 1px solid var(--sidebar-border);
}
.sidebar-context-label {
	font-size: var(--sidebar-context-label-size); font-weight: 700;
	letter-spacing: .04em; text-transform: uppercase;
	color: var(--sidebar-text-muted);
}
.sidebar-context-reading {
	display: flex; align-items: center; gap: var(--sidebar-context-icon-gap);
	padding-left: var(--sidebar-context-reading-indent);
	font-size: var(--sidebar-context-reading-size); font-weight: 500;
	color: var(--sidebar-text);
}
.sidebar-context-icon { display: inline-flex; flex: none; color: var(--sidebar-text-muted); }
.sidebar-context-icon svg { width: var(--sidebar-context-icon-size); height: var(--sidebar-context-icon-size); }
.sidebar nav { flex: 1; display: flex; flex-direction: column; gap: .1333rem; padding: 1.0667rem 0; }
.sidebar nav a {
	display: flex;
	align-items: center;
	gap: .6667rem;
	padding: .6667rem 1.3333rem;
	border-left: 3px solid transparent;
	color: var(--sidebar-text);
	font-size: .92rem;
	font-weight: 500;
}
.sidebar nav a .nav-icon { font-size: 1.05rem; line-height: 1; width: 1.3em; text-align: center; flex: none; }
.nav-section {
	margin-top: .8rem;
	padding: .5333rem 1.3333rem;
	font-size: .72rem;
	font-weight: 700;
	letter-spacing: .04em;
	text-transform: uppercase;
	color: var(--sidebar-text-muted);
	background: var(--sidebar-section-bg);
}
.nav-section:first-child { margin-top: 0; }
.sidebar nav a:hover { background: var(--sidebar-hover-bg); text-decoration: none; }
.sidebar nav a.active { background: var(--sidebar-hover-bg); border-left-color: var(--primary); color: var(--sidebar-text-strong); font-weight: 600; }
.nav-badge {
	margin-left: auto;
	min-width: 1.6em;
	text-align: center;
	background: rgba(255, 255, 255, .1);
	color: var(--sidebar-text-muted);
	font-size: .75rem;
	font-weight: 600;
	padding: .0667rem .5333rem;
	border-radius: 999px;
}
.sidebar nav a.active .nav-badge { background: rgba(255, 255, 255, .22); color: var(--sidebar-text-strong); }
.nav-badge-alert, .sidebar nav a.active .nav-badge-alert { background: var(--nav-alert-bg); color: var(--nav-alert-ink); }

/* Drawer backdrop - hidden on desktop, switched on in the responsive block
   below (the sidebar becomes an off-canvas drawer there, opened by the
   hamburger in the top bar). */
.sidebar-backdrop { display: none; }

/* --- Layout --- */

main.wrap { max-width: var(--content-max); margin: 0 auto; padding: 1.6rem; width: 100%; box-sizing: border-box; flex: 1; }

.breadcrumb-trail { display: flex; align-items: center; gap: .4rem; font-size: .85rem; color: var(--text-muted); margin-bottom: .6667rem; }
.breadcrumb-trail a { color: var(--text-muted); }
.breadcrumb-trail a:hover { color: var(--primary); }
.breadcrumb-trail .sep { color: var(--border); }

.customer-number {
	display: inline-block;
	font-size: .78rem;
	font-weight: 500;
	color: var(--text-muted);
	background: var(--bg);
	border: 1px solid var(--border);
	border-radius: 999px;
	padding: .1333rem .6667rem;
	margin-left: .6667rem;
	vertical-align: middle;
}

.card {
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	box-shadow: var(--shadow);
	padding: 1.6rem;
	margin-bottom: 1.3333rem;
}
.card + .card { margin-top: 1.3333rem; }
.row { display: flex; gap: .8rem; align-items: center; flex-wrap: wrap; }
.row.between { justify-content: space-between; }
.stack { display: flex; flex-direction: column; gap: .8rem; }
.spacer { flex: 1; }

/* --- Forms (stacked) --- */

form.stacked { display: flex; flex-direction: column; gap: .9333rem; max-width: 32rem; }
.field { display: flex; flex-direction: column; gap: .2667rem; }
.field.inline { flex-direction: row; align-items: center; gap: .5333rem; }
/* Two fields side by side (postcode/city, country/VAT ID). The markup used this
   class since 2026-09-06, but the RULE only ever existed in portal.officeplanner.pro's style.css
   - here those rows silently fell back to stacking, and the "send test email"
   form had no gap between field and button at all (user feedback 2026-09-07,
   with screenshots). Same values as the portal's copy: the two apps share no
   code, only conventions (CLAUDE.md, sections "Repository boundaries" and "Design tokens"). */
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: .9333rem; }
/* One field plus its own action button - the button keeps its natural width
   instead of being stretched across half the row. */
.field-row.with-action { grid-template-columns: 1fr auto; }
label { font-size: .85rem; font-weight: 600; color: var(--text-muted); }
input, select, textarea {
	font: inherit;
	padding: .5333rem .6667rem;
	border: 1px solid var(--border);
	border-radius: 6px;
	background: var(--surface);
	color: var(--text);
}
input:focus, select:focus, textarea:focus { outline: 2px solid var(--primary); outline-offset: -1px; }
textarea { resize: vertical; min-height: 4rem; }
/* Explanatory note under something. Two contexts, two spacings: a hint INSIDE
   a .field belongs to that field's input and only needs the field's own 4px
   gap; everywhere else (after a button, a key/value display, a whole form) it
   is a standalone paragraph and needs normal breathing room.

   The -8px that used to sit on the .field variant overshot badly: the field's
   flex gap adds 4px, so the net offset was MINUS 4px and the hint ran into the
   input's bottom border (user feedback 2026-09-07, screenshot of the
   notification-address field). A flex gap is not a margin - it cannot be
   cancelled out by one. */
.hint { font-size: .8rem; color: var(--text-muted); margin-top: .4rem; }
.field > .hint { margin-top: 0; }
.info-icon {
	display: inline-flex; align-items: center; justify-content: center;
	width: 1rem; height: 1rem; margin-left: .2667rem;
	border-radius: 50%;
	background: var(--info); color: var(--info-ink);
	font-family: Georgia, "Times New Roman", serif;
	font-size: .75rem; font-weight: 700; font-style: normal;
	line-height: 1; cursor: help; vertical-align: middle;
}
.info-icon:hover, .info-icon:focus-visible { background: var(--border); color: var(--text); }
/* A reading in a toolbar (toolbarReadingHtml()): label and value beside the
   action that changes it. A reading, never a button: no hover, no hand cursor. */
.toolbar-reading {
	display: inline-flex;
	align-items: center;
	gap: var(--toolbar-reading-gap);
	padding: var(--toolbar-reading-padding);
	border: var(--feature-rule);
	border-radius: var(--feature-corner);
	background: var(--feature-value-bg);
	cursor: default;
}
.toolbar-reading-label { color: var(--text-muted); }
.info-tip {
	position: fixed; z-index: var(--z-tooltip);
	pointer-events: none;
	max-width: 24rem;
	padding: .5333rem .8rem;
	border-radius: var(--radius-md);
	background: var(--tooltip-bg); color: var(--tooltip-ink);
	font-size: .8rem; font-weight: 400;
	line-height: 1.45; text-align: left; white-space: pre-line;
	box-shadow: var(--shadow);
	opacity: 0; visibility: hidden;
	transition: opacity var(--transition);
}
.info-tip.is-open { opacity: 1; visibility: visible; }
/* margin-top so an error banner never sticks directly to whatever came
   before it (e.g. a button right above it) - same root-cause fix as
   .hint's. Harmless while the container is still empty (no error yet). */
.form-error {
	background: var(--danger-bg); color: var(--danger);
	padding: .5333rem .8rem; border: 1px solid var(--danger); border-left-width: 4px;
	border-radius: 6px; font-size: .9rem;
	margin-top: .8rem;
}
/* The success counterpart. app.js has been writing <p class="form-success">
   since 2026-09-07 (signature saved, test mail sent), but the rule existed only
   in portal.officeplanner.pro's style.css - so every confirmation here rendered as unstyled body
   text and read as "nothing happened" (user feedback 2026-09-07). */
.form-success {
	background: var(--success-bg); color: var(--success);
	padding: .5333rem .8rem; border: 1px solid var(--success); border-left-width: 4px;
	border-radius: 6px; font-size: .9rem;
	margin-top: .8rem;
}

.system-notice {
	padding: .6667rem .8rem;
	border: 1px solid;
	border-left-width: 4px;
	border-radius: 6px;
	font-size: .9rem;
	font-weight: 600;
	margin: .8rem 0;
}
.system-notice.success { color: var(--success); background: var(--success-bg); border-color: var(--success); }
.system-notice.warning { color: var(--warn); background: var(--warn-bg); border-color: var(--warn); }
form.stacked > .system-notice { margin: 0; }

/* Confirmation for actions that repaint the page. An inline .form-success only
   survives where the surrounding form stays on screen; most writes here go
   through "paint from server" (docs/UI-PATTERNS.md, "Data and routing"), which rewrites #app completely -
   so saving a customer, booking a module or storing the email settings
   confirmed nothing at all (user feedback 2026-09-07: "beim Speichern kommen
   keine Bestätigungen"). This sits on <body>, outside the repainted area, and
   fades out by itself. */
.toast {
	position: fixed;
	right: 1.6rem;
	bottom: 1.6rem;
	z-index: 70;
	max-width: 24rem;
	padding: .8rem 1.0667rem;
	border-radius: var(--radius);
	border: 1px solid var(--success);
	background: var(--success-bg);
	color: var(--success);
	box-shadow: var(--shadow);
	font-size: .9rem;
	font-weight: 600;
	opacity: 0;
	transform: translateY(.5333rem);
	transition: opacity .18s ease, transform .18s ease;
}
.toast.show { opacity: 1; transform: none; }
/* Row actions inside an expanded license (book/confirm/terminate a module,
   payments) have no form and therefore no place for an inline error - they
   report through the same channel as their success. */
.toast.error { border-color: var(--danger); background: var(--danger-bg); color: var(--danger); }

.confirmation-dialog {
	width: min(92vw, 32rem);
	max-width: 32rem;
	padding: 0;
	border: 0;
	border-radius: var(--radius);
	background: var(--surface);
	color: var(--text);
	box-shadow: var(--shadow);
}
.confirmation-dialog::backdrop { background: var(--dialog-backdrop); }
.confirmation-dialog-panel { padding: 1.6rem; }
.confirmation-dialog-panel p { color: var(--text-muted); margin-bottom: 1.3333rem; }
.confirmation-dialog-panel .field.inline { margin-bottom: 1.3333rem; }
.confirmation-dialog-actions { display: flex; justify-content: flex-end; gap: .8rem; flex-wrap: wrap; }
/* askLeave(): the changes listed, "Discard changes" apart on the left. */
.confirmation-dialog-actions .apart { margin-right: auto; }
.leave-changes { margin: 0 0 1.3333rem; padding-left: 1.2rem; color: var(--text-muted); }
.leave-changes li + li { margin-top: .2667rem; }

/* Persistent per-customer warning (e.g. "no license key yet") - shown above
   the tabs on every tab, not just where the underlying fact lives. */
.customer-alert {
	display: flex; align-items: center; justify-content: space-between; gap: .8rem;
	background: var(--warn-bg); border: 1px solid var(--warn); border-radius: var(--radius);
	padding: .6667rem 1.0667rem; margin-bottom: 1.0667rem; color: var(--warn); font-weight: 600; font-size: .88rem;
}
.customer-alert button.link { color: var(--warn); white-space: nowrap; }

/* --- Buttons --- */

button, .btn {
	font: inherit;
	cursor: pointer;
	border: 1px solid var(--border);
	background: var(--surface);
	color: var(--text);
	padding: .5333rem .9333rem;
	border-radius: 6px;
}
button:hover, .btn:hover { border-color: var(--primary); }
button.primary { background: var(--primary); border-color: var(--primary); color: var(--on-primary); }
button.primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); }
button.danger { color: var(--danger); border-color: var(--danger-bg); }
button.danger:hover { border-color: var(--danger); }
/* A step waiting for someone (release a version): the colours of a warning badge. */
button.warn { color: var(--warn); background: var(--warn-bg); border-color: var(--warn); }
button.warn:hover { border-color: var(--warn-fill); }
.danger-zone { border-color: var(--danger); }
.danger-zone h2 { color: var(--danger); }
.customer-delete-retention {
	display: flex;
	align-items: flex-start;
	gap: 0.5rem;
	margin: 1rem 0;
}
.customer-delete-retention input { flex: none; margin-top: 0.2rem; }
.customer-delete-confirmation {
	display: flex;
	align-items: flex-end;
	gap: 0.75rem;
	max-width: 34rem;
}
.customer-delete-confirmation .field { flex: 1; margin: 0; }
.customer-delete-confirmation button { flex: none; }
@media (max-width: 42rem) {
	.customer-delete-confirmation { align-items: stretch; flex-direction: column; }
}
button.link { border: none; background: none; color: var(--primary); padding: .1333rem .2667rem; }
button.link:hover { text-decoration: underline; }
button:disabled { opacity: .5; cursor: default; }
button.small, .btn.small { padding: .2667rem .6667rem; font-size: .85rem; }

/* --- Tables --- */

table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: .6667rem .5333rem; border-bottom: 1px solid var(--border); vertical-align: middle; }
th { font-size: .8rem; text-transform: uppercase; letter-spacing: .02em; color: var(--text-muted); font-weight: 600; }
/* Tables (2026-10-23): a list fills its card; column titles, amounts and
   actions never wrap - a wide table scrolls in its .table-scroll instead. An
   action cell stays a table cell (".row" on a td made it a flex box, which
   wrapped its buttons); its buttons stand side by side. A table with few
   columns is .table--compact: as wide as its content, so label and amount
   do not drift apart. */
th, td.nowrap, td.amount, td.row, td.actions { white-space: nowrap; }
td.row, td.actions { display: table-cell; }
td.row > * + *, td.actions > * + * { margin-left: var(--action-gap); }
th.amount, td.amount { text-align: right; font-variant-numeric: tabular-nums; }
table.table--compact { width: auto; min-width: var(--table-compact-min); }
/* An action column only as wide as its buttons: the free width goes to the
   descriptive columns, the actions stand at the right edge. */
td.row, td.actions { width: 1%; white-space: nowrap; }
.form-content { width: 100%; max-width: var(--form-max); }

tr:last-child td { border-bottom: none; }
tr.clickable { cursor: pointer; }
tr.clickable:hover { background: var(--bg); }
/* Chevron column: signals "expands into detail" before the user ever
   hovers, not just on hover (user feedback: "looks like that's all there
   is" without it). */
td.expand-chevron { width: 1.6rem; text-align: center; color: var(--text-muted); }

/* Wide tables scroll inside their own box instead of forcing the page wide -
   important on narrow screens (customer list, dashboard waiting list). */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* --- Badges --- */

.badge {
	display: inline-block;
	font-size: .78rem;
	font-weight: 600;
	padding: .1333rem .6667rem;
	border-radius: 6px; /* same radius as buttons/inputs - rectangular chip, not a pill */
	/* Same neutral border as button/.btn, not a saturated status-color border -
	   colored fill + text already carry the status, the border is just the
	   shared, minimal chrome every clickable-looking element in this UI has. */
	border: 1px solid var(--border);
	white-space: nowrap;
}
.badge.ok, .badge.active, .badge.paid { background: var(--success-bg); color: var(--success); }
.badge.fail, .badge.deactivated { background: var(--danger-bg); color: var(--danger); }
.badge.warn, .badge.terminated, .badge.open, .badge.overdue { background: var(--warn-bg); color: var(--warn); }
.badge.neutral { background: var(--bg); color: var(--text-muted); }
.badge.partly { background: var(--partly-bg); color: var(--primary); }

/* --- License key --- */

.activation-key {
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	font-size: 1.1rem;
	font-weight: 700;
	letter-spacing: .03em;
	background: var(--bg);
	border: 1px solid var(--border);
	border-radius: 6px;
	padding: .5333rem .9333rem;
	display: inline-block;
	min-width: 20ch;
	text-align: center;
	cursor: pointer;
	transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}

.activation-key:hover { border-color: var(--primary); }
.activation-key:focus-visible { outline: .15rem solid var(--primary); outline-offset: .15rem; }
.activation-key.copied { background: var(--success); border-color: var(--success); color: var(--surface); }
.clipboard-helper { position: fixed; inset-inline-start: -100vw; }

/* QR code above, key text below, both centered (user feedback 2026-09-06:
   was left-aligned with the key text above the QR code). */
.license-key-block { display: flex; flex-direction: column; align-items: center; gap: .8rem; margin-top: .8rem; }

/* --- Tabs (customer detail) --- */

.tabs { display: flex; gap: .2667rem; border-bottom: 1px solid var(--border); margin-bottom: 1.3333rem; }
.tabs button {
	border: none;
	border-bottom: 2px solid transparent;
	border-radius: 0;
	background: none;
	padding: .6667rem .2667rem;
	margin-right: 1.3333rem;
	font-weight: 600;
	color: var(--text-muted);
}
.tabs button:hover { border-color: var(--border); color: var(--text); }
.tabs button.active { color: var(--primary); border-color: var(--primary); }

/* --- Detail lists (master data, customer database) ---
   One label/value row per field instead of loose <div>s, so every value
   lines up on the same column (user feedback: inconsistent, unaligned
   master-data layout). */

.detail-list { display: flex; flex-direction: column; }
.row-item {
	display: grid;
	grid-template-columns: 10.6667rem 1fr;
	gap: 1.0667rem;
	padding: .6667rem 0;
	border-bottom: 1px solid var(--border);
}
.row-item:last-child { border-bottom: none; }
.dt { font-size: .82rem; color: var(--text-muted); font-weight: 600; }
.dd { font-size: .92rem; color: var(--text); word-break: break-word; }

/* --- Dashboard / activation status ---
   A "total" split into "activated" + "waiting" as one bar per row, not two
   side-by-side tiles - a customer/license can be counted in both "total" and
   "waiting" at once, and separate tiles made that look like a contradiction
   instead of a subset relationship (user feedback, see CLAUDE.md). */

.activation-row + .activation-row { margin-top: 1.4667rem; padding-top: 1.4667rem; border-top: 1px solid var(--border); }
.activation-row__head { display: flex; align-items: baseline; justify-content: space-between; gap: .8rem; margin-bottom: .5333rem; }
.activation-row__label { font-size: .95rem; font-weight: 650; }
.activation-row__total { font-size: 1.6rem; font-weight: 650; line-height: 1; font-variant-numeric: proportional-nums; }
.activation-bar { display: flex; height: .6667rem; border-radius: 999px; overflow: hidden; background: var(--border); }
.activation-bar__seg--done { background: var(--success); }
.activation-bar__seg--waiting { background: var(--warn-fill); }
.activation-bar__seg--empty { flex-basis: 100%; }
.activation-legend { display: flex; flex-wrap: wrap; gap: .5333rem 1.3333rem; margin-top: .6667rem; font-size: .85rem; color: var(--text-muted); }
.activation-legend__item { display: flex; align-items: center; gap: .4rem; }
.activation-legend__item--attention { color: var(--warn); font-weight: 600; }
.activation-legend .dot { width: .5333rem; height: .5333rem; border-radius: 50%; flex: none; }
.dot--done { background: var(--success); }
.dot--waiting { background: var(--warn-fill); }
.activation-row__hint { margin: .6667rem 0 0; font-size: .78rem; color: var(--text-muted); }

.dashboard-booking-alert {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1.0667rem;
	margin-bottom: 1.0667rem;
	padding: 1.0667rem;
	border: 1px solid var(--warn);
	border-left-width: 5px;
	border-radius: var(--radius);
	background: var(--warn-bg);
	color: var(--warn);
}
.dashboard-booking-alert__copy { display: flex; flex-direction: column; gap: .2667rem; }
.dashboard-booking-alert__copy strong { display: flex; align-items: center; gap: .5333rem; }
.dashboard-booking-alert__copy > span { font-size: .88rem; }
.dashboard-booking-alert button { flex: none; }
.tabs .tab-attention { margin-left: .2667rem; vertical-align: middle; }

/* --- Layout utilities (CLAUDE.md, section "Design tokens": spacing/width values live here as
   utility classes, never as inline style="" in app.js - the one accepted
   exception is a value computed at runtime from real data, e.g. a
   percentage, since there is no fixed design value to put in a token). --- */

.mt-0 { margin-top: 0; }
.mt-8 { margin-top: .5333rem; }
.mt-12 { margin-top: .8rem; }
.mt-14 { margin-top: .9333rem; }
.mt-16 { margin-top: 1.0667rem; }
.mt-20 { margin-top: 1.3333rem; }
.mt-24 { margin-top: 1.6rem; }
.mb-16 { margin-bottom: 1.0667rem; }
.max-w-420 { max-width: 28rem; }
.max-w-480 { max-width: 32rem; }
.align-end { align-items: end; }
.form-error.warn { background: var(--warn-bg); color: var(--warn); }
/* Expanded license-detail row (renderLicenseTable's clickable rows). */
.license-detail-card { margin: .5333rem 0 1.0667rem; }
.license-detail-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1.3333rem;
	margin-bottom: 1.3333rem;
}
.license-detail-header h3 { margin: 0; }
.license-status-field {
	display: flex;
	align-items: center;
	gap: .5333rem;
	color: var(--text);
	font-size: .85rem;
	font-weight: 600;
}
.license-status-field select {
	min-width: 7.3333rem;
	padding: .4rem 2rem .4rem .6rem;
	font-size: .85rem;
}
.quota-summary {
	display: grid;
	gap: .5333rem;
	max-width: 29.3333rem;
	margin-bottom: 1.0667rem;
}
.quota-summary-row {
	display: grid;
	grid-template-columns: minmax(7.3333rem, 9.3333rem) max-content;
	align-items: center;
	gap: 1.0667rem;
}
.quota-summary-label { color: var(--text-muted); }
.email-template-list { display: grid; gap: .6667rem; }
.email-template-item { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
.email-template-item summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1.0667rem;
	padding: .8rem .9333rem;
	cursor: pointer;
	font-weight: 600;
}
.email-template-summary-meta { display: flex; align-items: center; gap: .4rem; }
/* Spans its item: the dividing line runs across, the two language columns
   share the width instead of the 32rem of a one-column stacked form. */
form.email-template-form { max-width: none; padding: 1.0667rem; border-top: 1px solid var(--border); }
/* The editor of a legal text takes the width of its card (renderLegalTexts()). */
form.legal-text-form { max-width: none; }
.kb-body.legal-text-body { max-width: none; }
/* The comparison of two versions of a text (legalChangesHtml(), terms § 13 (1)): what went struck through on red, what came on green. */
.legal-changes { display: flex; flex-direction: column; gap: .8rem; margin-bottom: 1.0667rem; }
.legal-change { border-left: 3px solid var(--border); padding-left: .8rem; }
.legal-change-removed, .legal-change-added { padding: .4rem .6667rem; border-radius: var(--radius); margin-top: .2667rem; }
.legal-change-removed { background: var(--danger-bg); text-decoration: line-through; }
.legal-change-added { background: var(--success-bg); }
/* A card folded to its header (legal texts, client 2026-10-28; message templates and delivery log, 2026-10-29): the header is the summary, a click unfolds the card. */
.fold-card > summary { list-style: none; cursor: pointer; margin-bottom: 0; }
.fold-card > summary::-webkit-details-marker { display: none; }
.fold-card > summary h2::before { content: '\25B8'; display: inline-block; margin-right: .5333rem; color: var(--text-muted); transition: transform var(--transition); }
.fold-card[open] > summary { margin-bottom: 1.0667rem; }
.fold-card[open] > summary h2::before { transform: rotate(90deg); }
/* A preview of generated text (the greeting, client 2026-10-29; layout by Codex): a reading, never a control - a quiet filled box, its columns under the fields above. */
.preview-box { padding: .9333rem; border: var(--feature-rule); border-radius: var(--feature-corner); background: var(--feature-value-bg); }
.preview-box > h3 { margin: 0 0 .8rem; }
.preview-language { display: flex; flex-direction: column; gap: .5333rem; }
.preview-line { display: flex; flex-direction: column; gap: .1333rem; }
/* The heading of a card whose results fold (message templates, delivery log; client 2026-10-29): a button that reads as the heading, ▸/▾ before it; search and filters below it stay visible. */
button.fold-toggle { border: none; background: none; padding: 0; font: inherit; color: inherit; display: inline-flex; align-items: center; }
button.fold-toggle::before { content: '\25B8'; display: inline-block; margin-right: .5333rem; color: var(--text-muted); transition: transform var(--transition); }
button.fold-toggle[aria-expanded="true"]::before { transform: rotate(90deg); }
button.fold-toggle:hover { color: var(--primary); }
.email-language-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.3333rem; }
.email-placeholders code { display: inline-block; margin: .1333rem .2667rem .1333rem 0; }
.update-notice {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
}
.update-notice button { flex: none; }
/* In a folded card's header the notice takes a line of its own below title and buttons. */
.fold-card > summary.section-title { flex-wrap: wrap; }
.fold-card > summary .update-notice { flex: 1 0 100%; margin-top: .8rem; cursor: default; }
.field-row.with-action.test-email-form {
	grid-template-columns: minmax(16.25rem, 26.25rem) auto;
	max-width: 32.5rem;
}
.test-email-form input, .test-email-form button { height: 2.25rem; }
.email-log-filters {
	display: flex;
	flex-direction: column;
	gap: .75rem;
	margin-bottom: 1rem;
	padding: .875rem;
	border: .0625rem solid var(--border);
	border-radius: var(--radius-md);
	background: var(--bg);
}
.email-log-filter-main {
	display: grid;
	grid-template-columns: minmax(13.75rem, 2fr) minmax(7.8125rem, 1fr) minmax(10.9375rem, 1.4fr) auto auto;
	align-items: end;
	gap: .625rem;
}
.email-log-page-size select { min-width: 4.75rem; }
.email-log-filter-submit { padding-left: .875rem; padding-right: .875rem; }
.email-log-period-row {
	display: grid;
	grid-template-columns: minmax(0, 1.25fr) minmax(21.875rem, 1fr);
	gap: .75rem;
	padding-top: .75rem;
	border-top: .0625rem solid var(--border);
}
.email-log-period-group {
	min-width: 0;
}
.email-log-group-label {
	display: block;
	margin: 0 0 .375rem .125rem;
	color: var(--text-muted);
	font-size: .85rem;
	font-weight: 600;
}
.email-log-periods, .email-log-date-fields {
	height: 4.625rem;
	padding: .625rem;
	border: .0625rem solid var(--border);
	border-radius: .5rem;
	background: var(--surface);
}
.email-log-periods, .email-log-pagination {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
}
.email-log-periods { align-items: flex-end; gap: .375rem; }
.email-log-periods button, .email-log-date-fields input, .email-log-date-fields button {
	height: 2rem;
	font-size: .85rem;
	line-height: 1.2;
}
.email-log-period.active { border-color: var(--primary); color: var(--primary); background: var(--bg); }
.email-log-custom-period.active .email-log-date-fields { border-color: var(--primary); }
.email-log-date-fields {
	display: grid;
	grid-template-columns: minmax(7.8125rem, 1fr) minmax(7.8125rem, 1fr) auto;
	align-items: end;
	gap: .5rem;
}
.email-log-date-fields label { font-size: .75rem; }
.email-log-date-fields input { width: 100%; min-width: 0; padding: .25rem .5rem; }
.email-log-date-fields input::-webkit-calendar-picker-indicator {
	width: 1rem;
	height: 1rem;
	margin-left: .25rem;
	padding: 0;
}
.email-log-pagination { gap: .5rem; }
.email-log-pagination { justify-content: space-between; margin-top: .875rem; color: var(--text-muted); font-size: .9rem; }
.email-message-detail-row > td { padding: 0; background: var(--bg); }
.email-message-details { padding: .9333rem 1.0667rem; }
.email-attempts ul { margin: .6667rem 0; padding-left: 1.3333rem; color: var(--text-muted); }
/* The holder row exists for every license, empty while collapsed - and an empty
   cell still inherits the table's own `th, td` padding and bottom border, so
   each license row carried ~20px of dead space and a second border line under
   it (found 2026-09-07 by the "class without a rule" check). Zero here; when
   expanded, .license-detail-card brings its own margin. */
.license-detail-row > td { padding: 0; border-bottom: none; }

/* --- Misc --- */

/* `hidden` wins over the display of .field, .field-row and the like, so a
   form part shown only for one choice (condition term, scope) really goes. */
[hidden] { display: none !important; }
.muted { color: var(--text-muted); }
.empty { color: var(--text-muted); font-style: italic; padding: .8rem 0; }
.searchbar { display: flex; gap: .5333rem; margin-bottom: 1.0667rem; }
.searchbar input[type="search"] { flex: 1; }
/* Product filters of the customer list (productFiltersHtml()): one select per item in a grid, grouped by kind. */
.product-filters { margin-bottom: 1.0667rem; }
.product-filter-group + .product-filter-group { margin-top: .8rem; }
.product-filter-group h4 { margin: 0 0 .4rem; }
.product-filter-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr)); gap: .5333rem .8rem; }
.product-filter-grid .field { margin: 0; }
.product-filter-grid select.is-set { border-color: var(--primary); }
/* A button group with one chosen (GENERAL_RULES 6.3: a choice, not tabs). */
.choice-group { display: inline-flex; flex-wrap: wrap; gap: .375rem; }
.choice-group button.active { border-color: var(--primary); color: var(--primary); background: var(--bg); }
/* Beside the search of a list the views stay on one line. */
.searchbar .choice-group { flex-wrap: nowrap; }
.searchbar .choice-group button { white-space: nowrap; }
.product-filter-summary { margin: 0 0 .8rem; color: var(--text-muted); }
.section-title { display: flex; align-items: center; justify-content: space-between; margin-bottom: .8rem; }
/* The short introduction under an area's heading (sectionIntro()). */
.section-intro { color: var(--text-muted); margin: 0 0 .9333rem; max-width: var(--intro-max); }
/* A negative amount - a discount, a deduction - in red, the same in admin and portal. */
.amount-negative { color: var(--danger); }
.center { display: flex; align-items: center; justify-content: center; min-height: 60vh; }

/* --- Public auth screens (login / password forgotten / new password) ------
   Redesigned 2026-09-07 to the client's mockup: one centred card with the
   brand line, a rule, a short intro, icon-led fields, a show-password eye and
   a full-width primary action. Same markup and class names in admin.officeplanner.pro and
   portal.officeplanner.pro so both masks stay recognisably one product. Every value here is a
   token or a class - nothing inline in app.js (CLAUDE.md, section "Design tokens"). */

.auth-card {
	width: 100%;
	max-width: 30.6667rem;
	padding: 2.1333rem;
	border-radius: var(--radius-lg);
}

/* Language picker sits in the card's top-right corner - the same place it
   occupies in the top bar once you are logged in, so it does not move. */
.auth-lang { display: flex; justify-content: flex-end; margin-bottom: .2667rem; }
.auth-lang select {
	max-width: 10rem;
	padding: .2667rem .5333rem;
	font-size: .82rem;
	border-color: transparent;
	background: none;
}
.auth-lang select:hover { border-color: var(--border); }

/* The product logo on the login card, above the subtitle. */
.auth-logo { margin: 0 auto; max-width: var(--auth-logo-width); }
.logo-primary { fill: var(--logo-name); }
.brand-logo, .auth-logo svg { display: block; }
.brand-logo svg, .auth-logo svg { display: block; width: 100%; height: auto; }
/* The sidebar is dark in both schemes, so the name there is always light. */
/* The logo alone and centred in its own band (client, 2026-11-05): its
   built-in margin is the same on both sides, so it lines up with nothing
   it would visibly miss. */
.sidebar .brand-logo { --logo-name: var(--sidebar-text-strong); max-width: var(--sidebar-logo-width); margin-inline: auto; }
.auth-brand { display: flex; align-items: center; justify-content: center; gap: .8rem; }
.auth-brand svg { width: 2.1333rem; height: 2.1333rem; color: var(--primary); flex: none; }
.auth-brand h1 { margin: 0; font-size: 1.45rem; letter-spacing: -.01em; }
.auth-sub { margin: .5333rem 0 0; text-align: center; color: var(--text-muted); }
.auth-rule { width: 60%; margin: 1.3333rem auto; border: 0; border-top: 1px solid var(--border); }
.auth-intro { margin: 0 0 1.4667rem; text-align: center; color: var(--text-muted); line-height: 1.5; }

/* Input with a leading icon and (optionally) a trailing button inside it.
   The icon is absolutely positioned so the input keeps its own full width -
   a flex row would make the input narrower than every other field. */
.input-icon { position: relative; display: flex; align-items: center; }
.input-icon input {
	width: 100%;
	height: var(--input-h);
	padding-left: 2.9333rem;
	padding-right: 2.9333rem;
	border-radius: var(--radius-md);
}
.input-icon svg { width: 1.2rem; height: 1.2rem; }
.input-icon__lead {
	position: absolute; left: .9333rem;
	display: flex; color: var(--field-icon);
	pointer-events: none;	/* clicking the icon must focus the field, not swallow the click */
}
.input-icon__toggle {
	position: absolute; right: .5333rem;
	display: flex; align-items: center;
	padding: .5333rem; width: auto;
	border: none; background: none; color: var(--field-icon);
	border-radius: 8px;
}
.input-icon__toggle:hover { color: var(--text); background: var(--bg); border-color: transparent; }

.field-aside { display: flex; justify-content: flex-end; margin-top: .5333rem; }
.field-aside a { font-size: .88rem; font-weight: 600; }

.btn-arrow {
	display: inline-flex; align-items: center; justify-content: center; gap: .6667rem;
	height: var(--input-h);
	border-radius: var(--radius-md);
	font-size: 1rem; font-weight: 600;
}
.btn-arrow svg { width: 1.2rem; height: 1.2rem; }

/* "oder" rule: the line is drawn by the pseudo-elements, so the label stays
   centred whatever its translation is. */
.auth-or {
	display: flex; align-items: center; gap: .8rem;
	margin: 1.4667rem 0 1.0667rem;
	color: var(--text-muted); font-size: .9rem;
}
.auth-or::before, .auth-or::after { content: ""; flex: 1; border-top: 1px solid var(--border); }

.auth-foot {
	display: flex; align-items: center; justify-content: center; gap: .5333rem;
	margin: 0; text-align: center; color: var(--text-muted);
}
.auth-foot svg { width: 1.2rem; height: 1.2rem; color: var(--field-icon); flex: none; }

/* --- Password rules checklist (2026-09-07, client spec) -------------------
   Sits under a "new password" field and ticks off while typing. Muted until a
   rule is met, then the accent colour - the unmet state must not read as an
   error, nothing is wrong yet while someone is still typing. */

.pw-rules {
	margin-top: -.2667rem;
	padding: .8rem .9333rem;
	background: var(--bg);
	border: 1px solid var(--border);
	border-radius: var(--radius-md);
	font-size: .85rem;
}
.pw-rules__title { margin: 0 0 .5333rem; color: var(--text-muted); font-weight: 600; }
.pw-rules ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .4rem; }
.pw-rule { display: flex; align-items: center; gap: .5333rem; color: var(--text-muted); }
.pw-rule__mark { display: flex; }
.pw-rule svg { width: 1rem; height: 1rem; }
.pw-rule--ok { color: var(--success); }

/* A new password in a normal form (newPasswordFieldsHtml()): the field as high
   as the others, the generator beside it, the generated value below - the
   portal's blocks of the same names. */
.input-icon--compact input { height: auto; padding: .5333rem 2.4rem .5333rem .8rem; border-radius: 6px; }
.input-icon--compact .input-icon__toggle { right: .2667rem; padding: .2667rem; }
.input-icon--compact svg { width: 1.1rem; height: 1.1rem; }
.pw-row { display: flex; align-items: center; gap: .5333rem; }
.pw-row .input-icon { flex: 1 1 auto; min-width: 0; }
.generated-password { display: flex; align-items: center; gap: .8rem; margin-top: .5333rem; }
.generated-password[hidden] { display: none; }
.generated-password code { padding: .4rem .6667rem; border: 1px solid var(--border); border-radius: var(--feature-corner); background: var(--bg); font-size: .95rem; letter-spacing: .04em; }

/* --- Account page and staff accounts (featureCardHtml(), renderStaffPage()) --- */
.feature-card { display: grid; grid-template-columns: var(--feature-icon-size) minmax(0, 1fr); gap: var(--feature-card-gap); }
.feature-card-icon { width: var(--feature-icon-size); height: var(--feature-icon-size); display: flex; align-items: center; justify-content: center; border-radius: var(--feature-icon-radius); color: var(--primary); background: var(--feature-icon-bg); }
.feature-card-icon svg { width: var(--feature-icon-glyph-size); height: var(--feature-icon-glyph-size); }
.feature-card-content { min-width: 0; }
.feature-card-content > h2 { display: flex; align-items: center; flex-wrap: wrap; gap: var(--feature-info-gap); font-size: var(--feature-title-size); margin-bottom: var(--feature-title-gap); }
.detail-list--filled { border-radius: var(--feature-corner); overflow: hidden; }
.detail-list--filled .row-item { grid-template-columns: var(--feature-label-width) minmax(0, 1fr); gap: 0; padding: 0; border-bottom: var(--feature-row-rule); }
.detail-list--filled .row-item:last-child { border-bottom: none; }
.detail-list--filled .dt, .detail-list--filled .dd { padding: var(--feature-cell-padding); }
.detail-list--filled .dt { background: var(--feature-label-bg); }
.detail-list--filled .dd { background: var(--feature-value-bg); }
.change-form { border-top: var(--feature-rule); margin-top: var(--feature-section-gap); padding-top: var(--feature-section-gap); }
.change-form label { color: var(--text); font-weight: var(--feature-label-weight); }
.change-form h3 { margin: 0 0 .8rem; font-size: 1rem; }
/* The rights of an account: a plain list in a reading, a group of checkboxes in the staff form. */
.permission-summary { margin: 0; padding-left: 1.0667rem; }
.permission-summary li + li { margin-top: .2667rem; }
.permission-choice { display: flex; flex-direction: column; gap: .5333rem; margin: 0; padding: .8rem .9333rem; border: var(--feature-rule); border-radius: var(--feature-corner); }
.permission-choice[hidden] { display: none; }
.permission-choice legend { padding: 0 .2667rem; font-size: .85rem; font-weight: 600; color: var(--text-muted); }
.permission-choice .link { align-self: flex-start; }

@media (max-width: 56.25rem) {
	.detail-list--filled .row-item { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 48rem) {
	.feature-card { display: block; }
	.feature-card-icon { float: left; margin-right: var(--feature-info-gap); }
	.feature-card-content > h2 { min-height: var(--feature-icon-size); display: flex; align-items: center; }
}

textarea { min-height: 6.4rem; resize: vertical; font-family: inherit; }

@media (max-width: 30rem) {
	.auth-card { padding: 1.6rem 1.3333rem; }
	.auth-brand h1 { font-size: 1.2rem; }
	.auth-brand svg { width: 1.7333rem; height: 1.7333rem; }
	.email-log-periods, .email-log-period-group .email-log-date-fields { height: auto; }
	.email-log-period-group .email-log-date-fields { grid-template-columns: 1fr; }
	.email-log-date-fields button { justify-self: start; }
}
footer.foot { text-align: center; padding: 1.6rem; color: var(--text-muted); font-size: .8rem; }

.hamburger {
	border: none;
	background: none;
	font-size: 1.4rem;
	line-height: 1;
	padding: .1333rem .4rem;
	color: var(--text);
}
.hamburger:hover { border-color: transparent; }

/* --- Responsive: sidebar becomes an off-canvas drawer below 60rem ---
   The sidebar's fixed 230px column only makes sense once there's room for
   it beside the content; below that it slides in under the (still visible,
   still sticky) top bar instead. */

@media (max-width: 56.25rem) {
	.topbar .hamburger { display: inline-flex; align-items: center; }
	.topbar-brand-mobile { display: block; }
	.user-menu-trigger .role { display: none; }

	.sidebar {
		position: fixed;
		top: var(--topbar-h);
		left: 0;
		width: 15.3333rem;
		height: calc(100vh - var(--topbar-h));
		transform: translateX(-100%);
		transition: transform .2s ease;
		box-shadow: var(--shadow);
		z-index: 40;
	}
	.sidebar.open { transform: translateX(0); }

	.sidebar-backdrop {
		display: block;
		position: fixed;
		top: var(--topbar-h);
		left: 0;
		right: 0;
		bottom: 0;
		background: var(--sidebar-backdrop);
		opacity: 0;
		pointer-events: none;
		transition: opacity .2s ease;
		z-index: 30;
	}
	.sidebar-backdrop.open { opacity: 1; pointer-events: auto; }

	main.wrap { padding: 1.0667rem; }
	.field-row, .field-row.with-action { grid-template-columns: 1fr; }
	.toast { right: 1.0667rem; left: 1.0667rem; bottom: 1.0667rem; max-width: none; }
	.tabs { overflow-x: auto; }
	.dashboard-booking-alert { align-items: stretch; flex-direction: column; }
	.dashboard-booking-alert button { align-self: flex-start; }
	.section-title { flex-wrap: wrap; gap: .5333rem; }
	.update-notice { align-items: stretch; flex-direction: column; }
	.update-notice button { align-self: flex-start; }
	.field-row.with-action.test-email-form { grid-template-columns: 1fr; max-width: none; }
	.email-log-filter-main { grid-template-columns: 1fr; }
	.email-log-period-row { grid-template-columns: 1fr; }
	.email-log-date-fields { grid-template-columns: 1fr 1fr auto; }
	.email-log-pagination { align-items: flex-start; flex-direction: column; }
	.row-item { grid-template-columns: 1fr; gap: .1333rem; padding: .5333rem 0; }
	.license-detail-header { align-items: flex-start; flex-direction: column; gap: .6667rem; }
	.quota-summary-row { grid-template-columns: 1fr; gap: .2667rem; }
	.email-language-grid { grid-template-columns: 1fr; }
	.activation-row__total { font-size: 1.3rem; }
	.activation-legend { gap: .4rem .9333rem; font-size: .8rem; }
	form.stacked { max-width: none; }
}

/* --- Knowledge base (UI-PATTERNS "Knowledge base") ---
   Two columns inside the content area: search and folding categories on the
   left, the article on the right. The article keeps a readable line length;
   below the sidebar breakpoint the columns stack. */

.kb-layout { display: grid; grid-template-columns: var(--kb-nav-width) minmax(0, 1fr); gap: 1.3333rem; align-items: start; }
.kb-nav { padding: 1.0667rem; margin-bottom: 0; }
.kb-nav .searchbar { margin-bottom: .5333rem; }
.kb-category { border-top: 1px solid var(--border); }
.kb-category summary {
	display: flex; align-items: center; justify-content: space-between; gap: .5333rem;
	padding: .6667rem .2667rem;
	font-weight: 600; font-size: .92rem;
	cursor: pointer; list-style: none;
	transition: color var(--transition);
}
.kb-category summary::-webkit-details-marker { display: none; }
.kb-category summary::after { content: '\25B8'; color: var(--text-muted); transition: transform var(--transition); }
.kb-category[open] summary::after { transform: rotate(90deg); }
.kb-category summary:hover { color: var(--primary); }
.kb-topics { list-style: none; margin: 0 0 .5333rem; padding: 0; }
.kb-topic {
	display: block;
	padding: .4rem .8rem;
	border-left: 3px solid transparent;
	border-radius: 0 var(--radius) var(--radius) 0;
	color: var(--text); font-size: .92rem;
	transition: background var(--transition), color var(--transition);
}
.kb-topic:hover { background: var(--bg); text-decoration: none; }
.kb-topic.active { background: var(--bg); border-left-color: var(--primary); color: var(--primary); font-weight: 600; }
.kb-article { margin-bottom: 0; }
.kb-category-label { font-size: .8rem; text-transform: uppercase; letter-spacing: .02em; color: var(--text-muted); margin-bottom: .2667rem; }
.kb-body { max-width: var(--kb-article-max); }
.kb-body h2 { margin-top: 1.6rem; font-size: 1.05rem; }
.kb-body h3, .kb-body h4 { margin-top: 1.0667rem; }
.kb-body p { margin-bottom: .8rem; }
.kb-body ul, .kb-body ol { margin: 0 0 .8rem; padding-left: 1.4rem; }
.kb-body li { margin-bottom: .2667rem; }
.kb-body li > ul { margin-top: .2667rem; }
.kb-body code { padding: 0 .2667rem; border: 1px solid var(--border); border-radius: 4px; background: var(--bg); font-size: .9em; }
.kb-body .kb-note { padding: .5333rem .8rem; border-left: 3px solid var(--info); border-radius: 0 var(--radius) var(--radius) 0; background: var(--bg); }
.kb-body .table-scroll { margin-bottom: .8rem; }
.kb-table { width: auto; min-width: var(--table-compact-min); }
.kb-table th { text-transform: none; letter-spacing: 0; font-size: .85rem; }
.kb-table td { white-space: normal; vertical-align: top; }

/* The book beside a page title: a quiet link to the matching article. */
.help-link {
	display: inline-flex; align-items: center; justify-content: center;
	width: 1.6rem; height: 1.6rem; margin-left: .4rem;
	border-radius: 50%;
	color: var(--text-muted);
	vertical-align: middle;
	transition: color var(--transition), background var(--transition);
}
.help-link svg { width: 1.0667rem; height: 1.0667rem; }
.help-link:hover, .help-link:focus-visible { color: var(--primary); background: var(--bg); text-decoration: none; }

@media (max-width: 56.25rem) {
	.kb-layout { grid-template-columns: 1fr; }
}

/* Customer reports: the staff page (renderFeedbackPage()). */
.feedback-text { white-space: pre-wrap; overflow-wrap: anywhere; }
.feedback-images { display: flex; flex-wrap: wrap; gap: .8rem; margin-top: .8rem; }
.feedback-image { margin: 0; display: flex; flex-direction: column; gap: .4rem; }
.feedback-image img { height: var(--feedback-image-height); max-width: 100%; border: 1px solid var(--border); border-radius: var(--radius); object-fit: contain; }
.feedback-history { margin: 0; padding-left: 1.2rem; color: var(--text-muted); font-size: .9rem; }

/* An area with tabs (renderNavSection()): the area's heading stands above the tabs. */
.section-panel h1 { display: none; }
[data-tab-part][hidden] { display: none; }

/* A form showing saved values, locked until "Edit" (lockSavedForm()): the values read, nothing invites a change. */
form[data-saved] input:disabled, form[data-saved] select:disabled, form[data-saved] textarea:disabled { background: var(--bg); color: var(--text); opacity: 1; cursor: default; }
form[data-saved] button[hidden] { display: none; }

/* A table inside a form takes the room it needs: no scrollbar for lack of width (client, 2026-11-10). */
form.stacked:has(.table-scroll) { max-width: none; }

/* Below the tabs of an area: who does this, a person or the system (areaNoteHtml()). */
.area-note { display: flex; flex-wrap: wrap; align-items: center; gap: .5333rem; margin: 0 0 1.0667rem; color: var(--text-muted); }
[data-system-part][hidden] { display: none; }
