/* ============================================================
 * app.css: SRVY shared UI (login design system)
 *
 * This is the HR Cluster login design system, reused verbatim so SRVY, HR Cluster,
 * and future products stay visually synchronized. Tokens (colour/space/radius/
 * shadow/type) and the login / form / button components are kept identical to
 * HR Cluster's main.css; keep them in sync rather than forking values here.
 * ============================================================ */

/* ── Design tokens ───────────────────────────────────────── */
:root {
    /* ── ONE IDENTITY ────────────────────────────────────────────────────────
       The semantic names stay; their VALUES are now the respondent survey's,
       so admin, analytics, login and the public pages speak the same language
       as the survey and the subscription emails. Sourced verbatim from
       survey.css — it is the benchmark, so it is the source, not a copy.

       "Delicate" is a rule, not a mood: BLUE MEANS INTERACTIVE OR IDENTITY.
       Primary actions, links, focus, the active nav item. Text stays near-black
       (#111827), borders stay hairline blue-grey, surfaces stay white. If a
       blue appears on something you cannot click, it is a bug. */
    --color-bg:            #f7f9fc;   /* = --survey-surface-muted (cool, not warm grey) */
    --color-surface:       #ffffff;   /* = --survey-surface */
    --color-white:         #ffffff;
    --color-border:        #dfe5ec;   /* = --survey-border */
    --color-border-strong: #c8d1dc;   /* = --survey-border-strong */
    --color-text:          #111827;   /* = --survey-text */
    --color-text-muted:    #5f6b7a;   /* = --survey-text-secondary */
    --color-text-light:    #98a2b3;   /* respondent tertiary (email theme text_3) */
    --color-accent:        #2563eb;   /* = --survey-blue */
    --color-accent-hover:  #1d4ed8;   /* = --survey-blue-hover */
    --color-accent-active: #1e40af;   /* = --survey-blue-active */
    --color-accent-subtle: #eff6ff;   /* = --survey-blue-soft */
    --color-accent-wash:   #dfe9fb;   /* one step deeper: selected rows/items */
    --color-accent-soft:   #eff6ff;   /* was a lone indigo #eef2ff; joins the blue family */
    --color-accent-ring:   rgba(37, 99, 235, 0.30);  /* = --survey-blue-ring */
    --color-danger:        #b42318;   /* = --survey-error */

    --color-flash-error-bg:     #fdecea;
    --color-flash-error-text:   #7f0000;
    --color-flash-error-border: #ef9a9a;
    /* Success messaging tint (flash + qb-toast): the platform's stronger green
       trio, mirrored from the error trio above with today's literal values. */
    --color-flash-success-bg:     #eef7ee;
    --color-flash-success-text:   #1e6b2e;
    --color-flash-success-border: #cfe6cf;

    /* Warning: a setting is saved and valid, but is not doing anything yet, the state
       between "fine" and "broken", which the other two variants cannot express. */
    --color-flash-warning-bg:     #fdf6e3;
    --color-flash-warning-text:   #7a5200;
    --color-flash-warning-border: #ecd9a8;

    /* ── Shared primitive layer (docs/design-system-proposal.md §2.3) ──
       app.css loads on every surface (survey.css and the public/respondent
       shells load on top of it), so primitives defined here are shared by both
       identities. Values are today's rendered values verbatim — introducing
       these tokens is a visual no-op. */

    /* Spacing: THE one 8px-flavored numeric scale (both vocabularies express
       it; the numeric scale is canonical, §2.3). Previously survey.css carried
       --rs-1…16 for the respondent surface only, minus --rs-5, an undefined
       token (§1.5-1) that invalidated .respondent-matrix__company margins. */
    --rs-1: 0.25rem;  --rs-2: 0.5rem;  --rs-3: 0.75rem; --rs-4: 1rem;
    --rs-5: 1.25rem;  --rs-6: 1.5rem;  --rs-8: 2rem;    --rs-12: 3rem;
    --rs-16: 4rem;

    /* Named spacing vocabulary: aliases onto the numeric scale during the
       migration (§2.3). Computed values are unchanged (0.25/0.5/1/1.5/2/3rem). */
    --space-xs:  var(--rs-1);
    --space-sm:  var(--rs-2);
    --space-md:  var(--rs-4);
    --space-lg:  var(--rs-6);
    --space-xl:  var(--rs-8);
    --space-2xl: var(--rs-12);

    /* Semantic status colors (design system Appendix A): the canonical
       five-semantic vocabulary — success / info / warning / danger / neutral —
       for every status expression (badges, job badges, flash, toasts,
       changelog type pills). Values are the badge/job palette verbatim.
       Surfaces that historically tint a semantic differently (flash/toast
       success above, changelog update/fixed) keep their exact legacy values
       via local overrides; consolidating tints across surfaces is a separate,
       explicitly reviewed visual change (Appendix A). No sixth status color
       may be added without amending the specification. */
    --status-success-text: #16794b;  --status-success-bg: #e7f5ee;
    --status-info-text:    #1d4ed8;  --status-info-bg:    #eff6ff;
    --status-warning-text: #b25000;  --status-warning-bg: #fff4e5;
    --status-danger-text:  #b42318;  --status-danger-bg:  #fef3f2;
    --status-neutral-text: #5f6b7a;  --status-neutral-bg: #eef1f6;

    /* Focus role (platform identity): the 2px near-black ring every platform
       :focus-visible treatment draws. The respondent identity codifies its own
       ring as --survey-focus-ring in survey.css (§2.3). */
    --focus-ring: 2px solid var(--color-accent);

    /* Motion roles (§2.3): fast = respondent micro-interactions (0.12s),
       base = platform control transitions (0.15s), slow = overlays, banners
       and toasts (0.25s). Introduced here for components to adopt as they
       migrate; existing literal transitions are unchanged in PR-1. */
    --motion-fast: 0.12s ease;
    --motion-base: 0.15s ease;
    --motion-slow: 0.25s ease;

    --font-size-sm:   0.875rem;
    --font-size-base: 1rem;
    --font-size-lg:   1.125rem;
    --font-size-xl:   1.25rem;
    --font-size-2xl:  1.5rem;

    --radius-sm: 3px;
    --radius-md: 8px;
    --radius-lg: 10px;

    --shadow-sm: 0 1px 3px rgba(0,0,0,0.07);
    --shadow-md: 0 2px 8px rgba(0,0,0,0.10);

    --sidebar-width: 220px;
    --topbar-height: 48px;

    /* ── Platform Visual Refresh role vocabulary (proposal §4, slice 2) ──
       These name the roles later slices consume; introducing them is a visual
       no-op because NOTHING references them yet (a custom property nothing reads
       has no rendering effect). Values are the §4 TARGETS — the point at which a
       later slice first consumes a role IS that slice's intended visible change,
       reviewed there, not here. The only tokens this slice also *consumes* are the
       verbatim ones below (changelog pills + landing/changelog literals), each at
       today's exact computed value. */

    /* Type roles: the platform ramp as named steps. Half-step weights (620/650)
       are the respondent surface's optical tuning, named here so the platform can
       share them rather than rediscover them (§4). display is the public hero
       role; page-title is the admin content headline slices 4+ add; section sits
       between it and the existing tiny-uppercase card label. */
    --type-display:    clamp(1.75rem, 1.2rem + 2.4vw, 2.5rem);
    --type-page-title: 1.375rem;   /* ~22px */
    --type-section:    1.125rem;   /* ~18px */
    --type-card-label: var(--font-size-sm);
    --type-body:       var(--font-size-base);
    --type-support:    var(--font-size-sm);
    --type-micro:      0.75rem;
    --font-weight-page-title: 650;
    --font-weight-emphasis:   620;

    /* Rhythm roles: the whitespace budget slices 4-6 spend. page-entry is the
       platform analogue of the respondent 24→72px entry (tuned smaller for
       operational screens); section-gap vs card-gap distinguishes group spacing
       from within-group spacing (both equal today's --rs values so adoption is
       controllable); the measures are reading-width caps. */
    --page-entry:   clamp(1rem, 2.5vw, 2.5rem);
    --section-gap:  var(--rs-8);
    --card-gap:     var(--rs-4);
    --measure-full: 76rem;
    --measure-read: 48rem;

    /* Density budgets (§4): two named breathing levels, operational (dense
       lists/tables/builder) vs editorial (overviews/settings/landing). Defined
       for slices 4-6 to reference; unused here. */
    --density-operational: var(--rs-3);
    --density-editorial:   var(--rs-6);

    /* Sequential-blue chart ramp derived from the respondent accent family
       (#2563eb), for analytics magnitude (slice 10, §4 Charts). Defined, unused —
       state is expressed with the --status-* set, never this ramp. */
    --chart-seq-1: #eff6ff;
    --chart-seq-2: #bfdbfe;
    --chart-seq-3: #60a5fa;
    --chart-seq-4: #2563eb;
    --chart-seq-5: #1e40af;

    /* Changelog CATEGORICAL pill palette (Appendix A: a changelog-local accent
       set, explicitly NOT statuses). Today's literal hex, verbatim, hoisted out
       of the component rules so the set lives in one place. Consumed below —
       a computed no-op. added/security/deprecated stay on the --status-* set. */
    --cl-cat-module-bg: #f0f0f0;  --cl-cat-module-text: #555555;
    --cl-cat-update-bg: #e7f0fb;  --cl-cat-update-text: #1c5fb0;
    --cl-cat-core-bg:   #ede7f6;  --cl-cat-core-text:   #5e35b1;
    --cl-cat-fixed-bg:  #fff4e0;  --cl-cat-fixed-text:  #a15c00;
    --cl-cat-removed-bg:#f6e9e8;  --cl-cat-removed-text:#8a4a45;

    /* Landing + changelog literal type values, hoisted verbatim (slice 2 re-
       expresses "literal rems → tokens at identical computed values"). Named
       locally rather than mapped onto the semantic roles above because at TODAY's
       values they do not yet match a role — slice 7 (public surface) retunes
       these onto --type-display et al., and that retune is the visible change.
       Until then these hold exactly the numbers the rules carried before. */
    --landing-wordmark-size:    1.5rem;
    --landing-wordmark-track:   0.18em;
    /* Slice 7: the front door now speaks at the benchmark's volume. The title
       adopts the display role (the same clamp curve the respondent hero uses,
       now in the same palette as the survey too) and the lead grows one step, so the
       public page is no longer the quietest type in the product. The clamp's
       floor is deliberately close to the old fixed size, so phones gain almost
       nothing in height — the hero + footer must still clear a 390x844 viewport
       on load (see the landing block's own note). */
    --landing-title-size:       var(--type-display);
    --landing-lead-size:        1.125rem;
    --landing-link-size:        0.95rem;
    --landing-foot-size:        0.8rem;
    /* Changelog: the page title joins the same display role; entry titles take
       the section role so a long editorial list has a clear second level. */
    --cl-title-size:            var(--type-display);
    --cl-entry-title-size:      var(--type-section);
    --cl-pill-size:             0.78rem;
}

/* ── Base ────────────────────────────────────────────────── */
* { box-sizing: border-box; }

body {
    margin: 0;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
    font-size: var(--font-size-base);
    line-height: 1.5;
    color: var(--color-text);
    background: var(--color-bg);
}

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

/* ── Utilities (shared with HR Cluster) ──────────────────── */
.text-small  { font-size: var(--font-size-sm); }
.text-muted  { color: var(--color-text-muted); }
.text-center { text-align: center; }
.mt-sm { margin-top: var(--space-sm); }
.mt-md { margin-top: var(--space-md); }
.mt-lg { margin-top: var(--space-lg); }

/* ── Login shell + card ──────────────────────────────────── */
.login-shell {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    background: transparent; /* particles canvas shows through */
    padding: var(--space-lg);
    position: relative;
    z-index: 1;
}

.login-box {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: var(--space-2xl);
    width: 100%;
    max-width: 380px;
}

.login-brand {
    text-align: center;
    margin-bottom: var(--space-xl);
}

.login-logo {
    height: 56px;
    width: auto;
    display: block;
    margin: 0 auto var(--space-sm);
}

.login-brand-name {
    font-size: var(--font-size-xl);
    font-weight: 700;
    letter-spacing: -0.02em;
    margin: 0 0 var(--space-xs);
}

.login-brand p {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    margin-top: var(--space-xs);
}

.login-remember {
    display: flex;
    align-items: center;
    gap: var(--space-xs);
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
    cursor: pointer;
    user-select: none;
}
.login-remember input { margin: 0; cursor: pointer; }

.login-box .btn-primary {
    width: 100%;
    justify-content: center;
    min-height: 48px;
    font-size: var(--font-size-base);
    margin-top: var(--space-md);
}

/* ── Forms ───────────────────────────────────────────────── */
.form-group { margin-bottom: var(--space-md); }

/* THE checkbox/radio label row (visual refresh slice 1). Previously two names
   coexisted (.check-inline / .checkbox-inline) and NEITHER had a base rule, so
   bare uses rendered unstyled — the audit's dual-idiom defect. .checkbox-inline
   is canonical; scoped variants below only add context extras (nowrap, margins,
   the vpick picker's row treatment). Do not reintroduce .check-inline. */
.checkbox-inline { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
.form-row { display: flex; gap: var(--space-md); flex-wrap: wrap; }
.form-row > .form-group { flex: 1 1 12rem; }

.form-group label {
    display: block;
    font-size: 0.82rem;
    font-weight: 500;
    margin-bottom: var(--space-xs);
    color: var(--color-text-muted);
}

.form-control {
    display: block;
    width: 100%;
    padding: 6px 10px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    /* Inherit the page font: without this a <textarea> falls back to the browser's
       monospace UA default, so consent copy and other multi-line fields rendered in a
       typewriter face while single-line inputs did not. */
    font-family: inherit;
    font-size: var(--font-size-base);
    color: var(--color-text);
    background: var(--color-white);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
    min-height: 38px;
}

.form-control:focus {
    outline: none;
    border-color: var(--color-text);
    box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.05);
}

.form-control::placeholder { color: var(--color-text-light); }

/* ── Buttons ─────────────────────────────────────────────── */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-xs);
    padding: 0 14px;
    border-radius: var(--radius-md);
    font-size: var(--font-size-sm); /* 0.875rem = 14px on desktop; a token so button labels track the denser mobile app-shell scale */
    font-weight: 500;
    border: 1px solid transparent;
    cursor: pointer;
    text-decoration: none;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
    white-space: nowrap;
    min-height: 36px;
}
.btn:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }
.btn:active:not(:disabled) { transform: scale(0.98); }
.btn:disabled, .btn[disabled] { opacity: 0.5; cursor: not-allowed; pointer-events: none; }

.btn-primary {
    background: var(--color-accent);
    color: var(--color-white);
    border-color: var(--color-accent);
}
.btn-primary:hover {
    background: var(--color-accent-hover);
    text-decoration: none;
    color: var(--color-white);
}

/* ── Flash (error) ───────────────────────────────────────── */
.flash {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-sm);
    padding: var(--space-sm) var(--space-md);
    border-radius: var(--radius-md);
    border: 1px solid transparent;
    font-size: var(--font-size-sm);
    margin-bottom: var(--space-md);
}
.flash-error {
    background: var(--color-flash-error-bg);
    color: var(--color-flash-error-text);
    border-color: var(--color-flash-error-border);
}
.flash-success {
    background: var(--color-flash-success-bg);
    color: var(--color-flash-success-text);
    border-color: var(--color-flash-success-border);
}
.flash-warning {
    background: var(--color-flash-warning-bg);
    color: var(--color-flash-warning-text);
    border-color: var(--color-flash-warning-border);
}

/* ── Auth flows (forgot / reset / invite) + inline actions ── */
.auth-intro { color: var(--color-text-muted); font-size: var(--font-size-sm); line-height: 1.6; margin: 0 0 var(--space-md); }
.auth-aux { margin: var(--space-md) 0 0; font-size: var(--font-size-sm); }
/* Stacked, centred login links (forgot password + sibling portal), HR Cluster parity. */
.login-aux { margin: var(--space-md) 0 0; font-size: var(--font-size-sm); text-align: center; }
.login-aux a { display: block; }
.login-aux a + a { margin-top: var(--space-xs); }
.section-title { font-size: var(--font-size-lg); font-weight: 600; margin: var(--space-lg) 0 var(--space-sm); }
.btn-link {
    background: none; border: none; padding: 0; cursor: pointer;
    color: var(--color-text-muted); font-size: var(--font-size-sm); text-decoration: underline;
}
.btn-link:hover { color: var(--color-text); }
.btn-link-danger:hover { color: var(--color-danger); }

/* ── Login: mobile adjustments (matches HR Cluster) ─────── */
@media (max-height: 700px) {
    .login-shell { padding: var(--space-md) var(--space-md); }
    .login-box { padding: var(--space-lg) var(--space-lg); }
    .login-brand { margin-bottom: var(--space-md); }
    .login-logo { height: 40px; }
    .login-brand-name { font-size: var(--font-size-lg); }
}

/* ── Site footer (legal links): HR Cluster design system ── */
.site-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: var(--space-sm);
    padding: var(--space-lg) var(--space-xl);
    font-size: 0.75rem;
    color: var(--color-text-muted);
    margin-top: var(--space-xl);
    position: relative;   /* sit above the particle canvas on login/landing */
    z-index: 1;
}
.site-footer a { color: var(--color-text-muted); text-decoration: none; }
.site-footer a:hover { color: var(--color-text); text-decoration: underline; }
.site-footer .dot { color: var(--color-text-light); }
.site-footer .footer-placeholder { color: var(--color-text-muted); cursor: default; }
/* On login pages the footer hugs the bottom under the centred card. */
body.login-page .site-footer { margin-top: 0; flex-shrink: 0; }

/* Compact language switcher: subtle, no pill. Placed as a top-right page control. */
.lang-menu { position: relative; display: inline-block; }
.lang-menu > summary {
    list-style: none;
    cursor: pointer;
    color: var(--color-text-muted);
}
.lang-menu > summary::-webkit-details-marker { display: none; }
.lang-menu > summary::after { content: " \25BE"; font-size: 0.7em; }
.lang-menu > summary:hover,
.lang-menu[open] > summary { color: var(--color-text); }
.lang-menu-list {
    position: absolute;
    top: 100%;
    right: 0;
    margin-top: 6px;
    min-width: 120px;
    display: flex;
    flex-direction: column;
    gap: 1px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
    padding: 4px;
    z-index: 20;
}
.lang-menu-list a {
    padding: 5px 10px;
    border-radius: var(--radius-sm);
    color: var(--color-text-muted);
    white-space: nowrap;
}
.lang-menu-list a:hover { background: var(--color-accent-subtle); color: var(--color-text); text-decoration: none; }
.lang-menu-list a.active { color: var(--color-text); font-weight: 600; }

/* Global controls region: fixed top-right on all public-facing pages. A single
   consistent home for global UI controls (language now; more later). Flex so
   additional controls line up alongside the language switcher. */
.page-controls {
    position: fixed;
    top: var(--space-md);
    right: var(--space-lg);
    z-index: 40;
    display: flex;
    align-items: center;
    gap: var(--space-md);
    font-size: 0.75rem;
    color: var(--color-text-muted);
}
/* Inside the authenticated shell topbar the region flows to the right of the bar
   (the sticky opaque topbar would occlude a fixed overlay). */
.app-topbar .page-controls {
    position: static;
    margin-left: auto;
    top: auto;
    right: auto;
    z-index: auto;
}

/* ── App shell (sidebar + topbar + content): HR Cluster design system ── */
.app-shell { display: flex; min-height: 100vh; overflow-x: hidden; }

.app-sidebar {
    width: var(--sidebar-width);
    height: 100vh;
    background: var(--color-surface);
    border-right: 1px solid var(--color-border);
    display: flex;
    flex-direction: column;
    position: fixed;
    top: 0;
    left: 0;
    z-index: 100;
    overflow: hidden;
}

.app-main {
    margin-left: var(--sidebar-width);
    flex: 1;
    min-height: 100vh;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.app-topbar {
    height: var(--topbar-height);
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    display: flex;
    align-items: center;
    padding: 0 var(--space-lg);
    gap: var(--space-md);
    position: sticky;
    top: 0;
    z-index: 50;
}
/* The page title: one <h1>, in the topbar, at every viewport. It reads as the page
   heading rather than a chrome label, so it takes the page-title type role. */
.app-topbar .page-title {
    font-size: var(--type-page-title);
    font-weight: var(--font-weight-page-title);
    letter-spacing: -0.01em;
    line-height: 1.2;
    margin: 0;
}
/* Mobile-only element: the topbar brand lives in the DOM at all widths but only
   surfaces on phones (see the mobile block), because on desktop the brand sits in
   the sidebar. */
.topbar-brand { display: none; }
/* `.content-title` retired in visual refresh slice 4: the shell now renders the
   page's <h1> through the PageHeader component at every viewport, so the former
   hidden-then-shown title element no longer exists. */

/* Compact workspace content density (HR Cluster's operational tier): the portals
   are operational workspaces, not document pages, so content sits close to the
   topbar and sidebar. Card internal padding is unaffected. */
/* Visual refresh slice 4: the content region gains an ENTRY - breathing room above
   the page headline, clamped so phones stay tight and desktops open up.
   It is deliberately NOT width-capped. Slice 4 briefly capped it at 76rem on the
   argument that the cap "only bites on ultra-wide displays"; that is exactly the
   case that matters, and on a wide monitor it left a large dead gutter beside a
   data-dense admin. The portals are operational workspaces - tables and builders
   want the width. Reading measures stay OPT-IN per block (`.measure`,
   `.measure--read`), not imposed on the shell. */
.app-content {
    flex: 1;
    /* Tight top gap. The page-entry clamp (up to 40px) existed to give a CONTENT
       headline room to breathe; the title now lives in the topbar, so the content
       starts straight at its first real block and that entry space just pushed the
       pill nav and cards down the page. */
    padding: var(--space-md) 12px;
    width: 100%;
    min-width: 0;
}

.sidebar-toggle { display: none; }
.nav-close, .nav-footer-links { display: none; }

.sidebar-brand {
    padding: var(--space-lg) var(--space-md);
    border-bottom: 1px solid var(--color-border);
    display: flex;
    align-items: center;
    gap: var(--space-sm);
}
.sidebar-logo { height: 24px; width: auto; display: block; }
.sidebar-brand > span { display: flex; align-items: baseline; min-width: 0; }
.sidebar-brand .brand-name {
    font-weight: 700;
    font-size: var(--font-size-lg);
    letter-spacing: -0.02em;
    color: var(--color-text);
    line-height: 1.1;
}
.sidebar-brand .account-name {
    font-size: var(--font-size-sm);
    font-weight: 400;
    color: var(--color-text-muted);
    /* Inline, not block: the portal name sits beside the wordmark on one line
       rather than wrapping under it. */
    display: inline;
    margin-left: 0.4em;
}

.sidebar-nav {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: var(--space-sm) 0;
    display: flex;
    flex-direction: column;
    gap: 1px;
}
.sidebar-nav a {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    padding: 8px calc(12px + var(--space-sm));
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
    border-radius: 0;
    margin: 0;
    transition: color 0.15s ease, background 0.15s ease;
}
/* Hover reads exactly like active minus the bar: same wash, same full width. */
.sidebar-nav a:hover { color: var(--color-text); background: var(--color-accent-wash); text-decoration: none; }
/* Active: the same wash as hover, plus the accent bar and a heavier label. Every
   nav item is full-bleed by default now, so no state has to undo geometry and
   nothing shifts between rest, hover and active. */
.sidebar-nav a.active {
    color: var(--color-text);
    font-weight: 600;
    background: var(--color-accent-wash);
}
.sidebar-nav a.active:hover { background: var(--color-accent-wash); }
.sidebar-nav a.active::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0; bottom: 0;
    width: 3px; border-radius: 0;
    background: var(--color-accent);
}

.sidebar-footer {
    flex: 0 0 auto;
    padding: var(--space-md);
    border-top: 1px solid var(--color-border);
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
}
.sidebar-footer .nav-user-name { display: block; margin-bottom: var(--space-xs); color: var(--color-text); }
.sidebar-footer a { color: var(--color-text-muted); }
.sidebar-footer a:hover { color: var(--color-danger); text-decoration: none; }

/* Empty-state placeholder for thin/coming-soon sections */
.empty-state {
    border: 1px dashed var(--color-border-strong);
    border-radius: var(--radius-lg);
    padding: var(--space-2xl) var(--space-lg);
    text-align: center;
    color: var(--color-text-muted);
    background: var(--color-surface);
}
.empty-state h2 { margin: 0 0 var(--space-xs); color: var(--color-text); font-size: var(--font-size-lg); }

/* ── Module content: page head, cards, tables, detail, forms ── */
/* Action bar above module content: title lives in the topbar, so this holds
   the page's primary action (right-aligned). */
/* Page header action bar: header actions are ONE right-aligned group, buttons
   adjacent with a consistent small gap (never space-between, which would spread a
   two-button group to opposite edges). */
.page-head { display: flex; align-items: center; justify-content: flex-end; gap: var(--space-sm); margin-bottom: var(--space-sm); flex-wrap: wrap; }
/* A back button sitting at the left edge of a page-head action row (detail pages
   without a pill bar): the auto margin absorbs the free space so the back button
   stays left while the action buttons stay pinned right. Mirrors the workspace-bar
   back button used on the pill-nav pages. */
.page-head-back { margin-right: auto; }

/* Secondary sits ON the surface rather than floating as a transparent outline:
   against the cool page a ghost button read as unplaced. White fill + hairline
   border makes it a real control, one step below the primary. */
/* Secondary is a true outline of the primary: same solid accent, drawn rather than
   filled. That makes the pair read as one family - "Ny utgave" filled, "Rediger
   undersøkelse" and the workspace back button outlined - instead of a blue button
   next to grey-bordered ones. */
.btn-secondary { background: var(--color-surface); color: var(--color-accent); border-color: var(--color-accent); }
.btn-secondary:hover { background: var(--color-accent-subtle); border-color: var(--color-accent-hover);
    text-decoration: none; color: var(--color-accent-hover); }

/* Destructive action. It is NOT a filled red slab: a solid danger button shouts
   louder than the page's actual primary action and, next to a blue primary and a
   white secondary, it read as a third competing weight. Quiet by default — danger
   text on the surface with a danger-tinted hairline — and it fills only on hover,
   at the moment of intent. (The old rule also carried a hardcoded #a93226 hover
   from the retired warm palette.) */
.btn-danger { background: var(--color-surface); color: var(--color-danger); border-color: var(--status-danger-bg); }
.btn-danger:hover { background: var(--color-danger); border-color: var(--color-danger);
    text-decoration: none; color: var(--color-white); }

/* Cards, HR Cluster module pattern: white panels, never content on the gray bg */
/* Slice 4: the resting card RECEDES to match the benchmark's flat, line-based
   surfaces — border and whitespace separate, elevation no longer competes.
   Only the ADMIN resting card drops it here: floating things keep `--shadow-md`
   (modal, nav drawer), and the public changelog legend keeps `--shadow-sm` until
   the public surface is composed in its own slice. */
.card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: var(--space-lg); }
.card + .card { margin-top: var(--card-gap); }
/* The question builder wraps its page cards in a [data-qb-pages] container (for the
   drag-and-drop reorder), which breaks the adjacent-sibling `.card + .card` spacing:
   the reorder bar above and any orphan card below would otherwise sit flush against
   the page cards. Restore the same inter-card gap across that wrapper boundary. */
.qb-reorder-bar { margin-bottom: var(--space-md); }
[data-qb-pages] + .card { margin-top: var(--space-md); }
.card-title { font-size: var(--font-size-sm); font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--color-text-muted); margin: 0 0 var(--space-md); }
.card .empty { color: var(--color-text-muted); margin: 0; }

/* Tables inside cards: rows bleed to the card edges */
.card:has(> .table-wrap) {
    overflow: hidden; /* Clip flush table rows to card radius without clipping non-table card popovers. */
}
.card .table-wrap { margin: 0 calc(-1 * var(--space-lg)); }
.card .table-wrap:last-child { margin-bottom: calc(-1 * var(--space-lg)); }
.card > .table-wrap:first-child { margin-top: calc(-1 * var(--space-lg)); }
/* Wide tables (e.g. the 6-column Brreg "Recent runs") scroll horizontally within
   their card instead of being clipped by the shell on narrow viewports. `auto` keeps
   the scrollbar hidden while the table fits, so desktop layout is unchanged. */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.data-table { width: 100%; border-collapse: collapse; }
.data-table thead th { font-size: var(--font-size-sm); font-weight: 500; color: var(--color-text-muted); text-align: left; padding: var(--space-sm) var(--space-md); border-bottom: 1px solid var(--color-border-strong); }
.data-table td { padding: var(--space-sm) var(--space-md); vertical-align: middle; }
/* One owner per divider: thead draws the header/first-row line, `tr + tr` draws each
   inter-row line: the first row has no top border of its own, so no doubled hairline.
   Hierarchy is carried by border weight: the header uses the darker border-strong so it
   reads as the section boundary, while inter-row lines drop to the lighter border for a
   quieter body. Result reads header → rows → hover instead of one flat grid. */
.data-table tbody tr + tr td { border-top: 1px solid var(--color-border); }
.data-table th:first-child, .data-table td:first-child { padding-left: var(--space-lg); }
.data-table th:last-child, .data-table td:last-child { padding-right: var(--space-lg); }
.data-table tr.clickable { cursor: pointer; }
.data-table tr.clickable:hover { background: var(--color-accent-subtle); }
.data-table .name { font-weight: 600; }
.data-table .col-right { text-align: right; white-space: nowrap; }

/* Sortable headers (sortable-table.js). The whole <th> is the click target. A faint
   neutral chevron shows the column CAN sort; it turns solid and directional on the
   active column. Enhancement-only: with no JS the header is a plain label. */
.data-table th.is-sortable { cursor: pointer; user-select: none; white-space: nowrap; }
.data-table th.is-sortable:hover { color: var(--color-text); }
.data-table th.is-sortable::after {
    content: "\2195";           /* ↕ neutral */
    display: inline-block;
    margin-left: 6px;
    font-size: 0.85em;
    color: var(--color-text-muted);
    opacity: 0.4;
}
.data-table th.is-sortable:hover::after { opacity: 0.7; }
.data-table th.is-sorted-asc::after { content: "\2191"; opacity: 1; color: var(--color-accent); }   /* ↑ */
.data-table th.is-sorted-desc::after { content: "\2193"; opacity: 1; color: var(--color-accent); }  /* ↓ */
.data-table th.is-sortable:focus-visible { outline: var(--focus-ring); outline-offset: -2px; }

/* Visually-hidden but announced to assistive tech (e.g. the emptied "Detaljer" header). */
.sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
/* Participating-companies weight column: sit the "Vekt" header directly over the
   right-aligned number in the 6rem weight input below, not at the far column edge. The
   header span mirrors the input's box (6rem, right-aligned) and is inset by the input's
   border(1px)+padding(10px) so the label lines up with the value, not the input's outer
   edge. The 6rem/11px here track the weight input's width + .form-control padding. */
.data-table th.col-weight { text-align: left; white-space: nowrap; }
.data-table th.col-weight > span { display: inline-block; box-sizing: border-box; width: 6rem; padding-right: 11px; text-align: right; }
/* Participating-companies table: let the company name absorb the slack so the Vekt /
   Dybde / action columns size to their content instead of stretching wide (they hold a
   6rem input, a checkbox and an icon button). Scoped so other .data-table uses are unaffected. */
.companies-table td:first-child, .companies-table th:first-child { width: 100%; }
/* Priority: keep the checkbox and its "Prioritet" label on one line (the Dybde column
   sizes to fit) instead of stacking in the tight, content-width column. */
.companies-table .checkbox-inline { white-space: nowrap; }

/* Question tables (the survey builder AND the Library's Collection pages): pin the ID /
   Type / action columns to identical widths across every page table so the columns line up
   down the whole survey/collection (each page is its own table, which otherwise auto-sizes
   per page). The action column is wide enough to hold the reorder + move + flow + remove
   group on one line; Spørsmål (2nd) absorbs the remainder. */
.qb-question-table { table-layout: fixed; }
.qb-question-table th:nth-child(1), .qb-question-table td:nth-child(1) { width: 9rem; }
.qb-question-table th:nth-child(3), .qb-question-table td:nth-child(3) { width: 15rem; }
.qb-question-table th:nth-child(4), .qb-question-table td:nth-child(4) { width: 26rem; }
.qb-question-table td { overflow-wrap: anywhere; }

/* List filter bar (e.g. the Companies search + status filter): a single row of
   controls above a table. The search field flexes to fill; the select and buttons
   size to content. Wraps on narrow viewports so the search box drops to full width. */
.filter-bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-sm); margin-bottom: var(--space-md); }
.filter-bar .form-control { width: auto; display: inline-block; }
.filter-bar .filter-search { flex: 1 1 16rem; min-width: 0; }
/* A filter bar whose controls are selects rather than a search box labels them, so the row
   reads "Question [..] Break down by [..]" instead of two unexplained dropdowns. */
.filter-bar label { color: var(--color-text-muted); font-size: var(--font-size-sm); }

/* ── Response statistics table (survey Responses section) ──
   A tabulation is read by comparing figures DOWN a column, so every numeric cell uses
   tabular numerals: proportional digits make 111 look narrower than 999 and the eye stops
   trusting the column. The question select is allowed to grow because a prompt is long and
   a truncated one is unidentifiable; the subject column stays on one line so a company name
   cannot wrap into two rows' worth of height beside single-digit counts. */
.stats-table th.col-right, .stats-table td.col-right { font-variant-numeric: tabular-nums; }
.stats-table td.name { white-space: nowrap; }
/* A count of zero is real data, so it is shown rather than dashed out, but it is muted so the
   shape of a distribution still reads at a glance across a six-column grid. */
.stats-table td.is-zero { color: var(--color-text-muted); }
.filter-bar .stats-question { flex: 1 1 24rem; min-width: 0; max-width: 40rem; }
/* Both selects are sized by their longest option, and a question prompt is long enough to
   crowd everything else out of the row. The question select gets the space (identifying it is
   the point) and the breakdown select is capped and allowed to shrink. */
.filter-bar .stats-breakdown { flex: 0 1 18rem; min-width: 0; }

/* ── Pill sub-navigation (ported verbatim from HR Cluster main.css) ──
   `.pill-nav` is the segmented container; `.pill` is each item; `.pill.is-active`
   is the active state. Used for the Survey Instance workspace sub-nav (Overview,
   Questions, …). Kept identical to HR Cluster's canonical component; do not fork
   the visual values here. All tokens below already exist in :root; the active
   background is HR Cluster's literal #ebebeb. */
.pill-nav {
    display: inline-flex;
    flex-wrap: wrap;
    background: var(--color-surface);
    border: 1px solid var(--color-accent);
    border-radius: var(--radius-lg);
    padding: 3px;
    gap: 2px;
}
/* Horizontal-scroll variant for pill rows that exceed a narrow viewport
   (the workspace nav carries 6 pills). */
.pill-nav-scrollable {
    overflow-x: auto;
    flex-wrap: nowrap;
    max-width: 100%;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
}
.pill-nav-scrollable::-webkit-scrollbar { height: 0; display: none; }
.pill-nav-scrollable .pill { white-space: nowrap; flex-shrink: 0; }

/* ── Pill-nav overflow chevrons (ported from HR Cluster main.css) ──
   A silently-scrolling pill row gives no hint that tabs are hidden
   off the edge. This two-mode component adds left/right chevron
   controls that appear only when there is more to scroll to, so an
   overflowing row (the 7-pill survey workspace on a phone) reads as
   scrollable. Behaviour is driven by pill-nav.js; the classes below
   are its structural + visual contract. Copied faithfully from HR
   Cluster; the branding is SRVY's own (accent panel border, muted
   chevrons, --focus-ring), the behaviour is HR Cluster's.

   FLAT MODE (pills fit): `.pill-nav-wrap` and `.pill-nav-viewport`
   are `display:contents` — no boxes of their own, so the `.pill-nav`
   inside renders exactly as a bare scrollable track, unchanged.

   OVERFLOW MODE (JS sets `.has-overflow`): the wrap promotes to the
   visible bordered panel, the viewport becomes the clipping scroll
   container, the `.pill-nav` becomes a transparent inner track, and
   the chevrons become real flex columns flanking the viewport:
     .pill-nav-wrap.has-overflow      ← panel (owns the border)
       .pill-nav-scroll-left          (column, divider on right)
       .pill-nav-viewport             (flex:1; overflow-x:auto)
         .pill-nav                     (transparent track)
       .pill-nav-scroll-right         (column, divider on left)
   The chevrons are real columns, not overlays, so no pill can render
   under them and bleed past the divider. */
.pill-nav-wrap,
.pill-nav-viewport {
    display: contents;
}

/* Overflow active: promote the wrap to the bordered panel (the same
   accent outline + surface fill the flat `.pill-nav` carries) and the
   viewport to the clipping scroll container. Only the inner track owns
   vertical padding, so the panel height matches the flat pill row. */
.pill-nav-wrap.has-overflow {
    display: inline-flex;
    flex-wrap: nowrap;
    align-items: stretch;
    background: var(--color-surface);
    border: 1px solid var(--color-accent);
    border-radius: var(--radius-lg);
    padding: 0;
    min-width: 0;
    max-width: 100%;
    /* Clip child corners to the rounded shell. */
    overflow: hidden;
}

.pill-nav-wrap.has-overflow > .pill-nav-viewport {
    display: block;
    flex: 1 1 0;
    min-width: 0;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -ms-overflow-style: none;
    -webkit-overflow-scrolling: touch;
    padding: 0;
}
.pill-nav-wrap.has-overflow > .pill-nav-viewport::-webkit-scrollbar { height: 0; display: none; }

/* Track inside the viewport: keeps the 3px inner padding of `.pill-nav`
   so the visual rhythm matches flat mode; sheds the panel chrome
   (bg/border/radius) — those live on the wrap now. Overrides
   `.pill-nav-scrollable`'s own overflow-x so the viewport, not the
   track, is the scroll source in overflow mode. `margin:0 !important`
   defeats any inline margin that would inflate the panel height. */
.pill-nav-wrap.has-overflow > .pill-nav-viewport > .pill-nav {
    display: inline-flex;
    flex-wrap: nowrap;
    background: transparent;
    border: 0;
    border-radius: 0;
    padding: 3px;
    margin: 0 !important;
    gap: 2px;
    max-width: none;
    min-width: 0;
    overflow: visible;
}

/* Control zones: real flex columns, not overlays. Surface face, a
   1px divider on the inner edge. `align-self:center` + explicit height
   keeps the chevron's visible block at pill height, so the panel reads
   as a compact pill row rather than a tall panel edge. */
.pill-nav-scroll-left,
.pill-nav-scroll-right {
    flex: 0 0 34px;
    align-self: center;
    height: 30px;
    min-height: 0;
    border: 0;
    cursor: pointer;
    color: var(--color-text-muted);
    font-size: 1.4rem;
    line-height: 1;
    font-family: inherit;
    font-weight: 600;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 0;
    background: var(--color-surface);
    transition: color 0.15s ease;
    /* Optical centring: the ‹ › glyphs sit entirely above the baseline (the font's
       ~6px descent is empty), so flex-centring the line box leaves the ink ~0.15em
       low — it reads as bottom-heavy in the control zone. Nudge it up so the glyph
       is vertically centred. Measured against the Segoe UI / -apple-system stack. */
    transform: translateY(-0.15em);
}

.pill-nav-scroll-left  { order: 0; border-right: 1px solid var(--color-border); }
.pill-nav-viewport     { order: 1; }
.pill-nav-scroll-right { order: 2; border-left:  1px solid var(--color-border); }

/* Chevrons render only in overflow mode AND when the matching
   direction can still be scrolled — never in flat mode. */
.pill-nav-wrap.has-overflow.is-overflowing-left  > .pill-nav-scroll-left  { display: flex; }
.pill-nav-wrap.has-overflow.is-overflowing-right > .pill-nav-scroll-right { display: flex; }

.pill-nav-scroll-left:hover,
.pill-nav-scroll-right:hover {
    color: var(--color-text);
}

.pill-nav-scroll-left:focus-visible,
.pill-nav-scroll-right:focus-visible {
    outline: var(--focus-ring);
    outline-offset: -2px;
}

.pill {
    display: inline-block;
    padding: 5px 14px;
    font-size: var(--font-size-sm);
    font-weight: 500;
    letter-spacing: 0.03em;
    color: var(--color-accent);
    text-decoration: none;
    border: 1px solid transparent;
    border-radius: calc(var(--radius-lg) - 3px);
    transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease;
    cursor: pointer;
    background: transparent;
}
.pill:hover { color: var(--color-accent-hover); background: var(--color-accent-subtle); text-decoration: none; }
/* The selected tab is a segmented-control: it LIFTS off the track onto the white
   surface and takes the accent for its label. The old rule painted a hardcoded warm
   grey (#ebebeb) left over from the monochrome palette, which is why it read as a
   foreign element once the surfaces went cool. Weight drops 700 -> 600: the lift and
   the accent already say "selected", so the extra boldness was a third signal. */
/* The selected tab is FILLED: solid accent, white label, the same treatment as the
   primary button, so "the section you are in" is as unambiguous as "the action you
   can take". A white pill with a blue label was legible but quiet; inside an
   accent-bordered track it asked the eye to work out which of the two blues meant
   selected. */
.pill.is-active {
    color: var(--color-white);
    font-weight: 600;
    background: var(--color-accent);
    border-color: var(--color-accent);
}
.pill.is-active:hover { color: var(--color-white); background: var(--color-accent-hover);
    border-color: var(--color-accent-hover); }
/* Workspace bar: pill sub-nav (left) + optional section action(s) (right) on one
   row, heading a Program- or Instance-workspace section. */
.workspace-bar { display: flex; align-items: center; justify-content: space-between; gap: var(--space-md); flex-wrap: wrap; margin-bottom: var(--space-lg); }
.workspace-actions { display: flex; align-items: center; gap: var(--space-sm); }
/* Back button + pills share the left of the workspace bar as one group (the back
   button replaces the old standalone "&larr; …" link that used to sit below the
   content). They wrap together on narrow widths; actions stay pinned right. */
.workspace-lead { display: flex; align-items: center; gap: var(--space-sm); flex-wrap: wrap; min-width: 0; }
/* Match the back button to the pill nav so the two read as one control group: the
   align-self:stretch gives it the pill nav's height (the bordered .pill-nav is taller
   than a default .btn-sm; align-self wins over the row's align-items:center and needs no
   hard-coded height), and it borrows the pill nav's grey fill, light border and 10px
   radius (overriding .btn-secondary's transparent/strong-border and .btn's 8px — all
   earlier in source, equal specificity, so these win). Height, .btn-sm padding, the
   &larr; icon and .btn-secondary:hover (higher specificity) are all preserved, so the
   arrow keeps "back" reading as an action rather than another tab. */
.workspace-back {
    flex: 0 0 auto;
    align-self: stretch;
    /* Geometry only. This rule used to re-declare background and border-color, which
       is why the back button kept a grey hairline on the page background while every
       other secondary button moved to the accent outline - it was overriding
       .btn-secondary rather than extending it. It now inherits the secondary style
       and only sets what makes it sit in the workspace bar. */
    border-radius: var(--radius-lg);
}

/* Detail field rows inside a card */
.detail-table { width: 100%; border-collapse: collapse; }
.detail-table tr + tr th, .detail-table tr + tr td { border-top: 1px solid var(--color-border); }
.detail-table th, .detail-table td { padding: var(--space-sm) 0; text-align: left; font-weight: normal; vertical-align: middle; }
.detail-table th { width: 180px; color: var(--color-text-muted); font-size: var(--font-size-sm); padding-right: var(--space-md); }
/* Shareable-link cell: URL + Copy button on one row, wrapping on narrow widths.
   The URL reads as a normal proportional link, not monospace/code. */
.share-cell { display: inline-flex; align-items: center; gap: var(--space-sm); flex-wrap: wrap; }
.share-cell .share-url { word-break: break-all; }

/* Company search-to-add control (Companies pill): the HR Cluster searchable
   assignment pattern, rendered as an inline results list beneath the input. */
.company-search { position: relative; }
.company-search-input { width: 100%; }
.company-search-results { margin-top: var(--space-sm); border: 1px solid var(--color-border); border-radius: var(--radius-md); overflow: hidden; }
.company-search-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-md); margin: 0; padding: var(--space-sm) var(--space-md); border-top: 1px solid var(--color-border); }
.company-search-row:first-child { border-top: 0; }
.company-search-name { font-weight: 500; }
.company-search-meta { display: block; margin-top: 1px; color: var(--color-text-muted); font-weight: 400; font-size: var(--font-size-sm); }
.company-search-empty { margin: 0; padding: var(--space-sm) var(--space-md); color: var(--color-text-muted); }

/* Pending company suggestions (respondent-driven review queue) */
.pending-suggestion { padding: var(--space-md) 0; border-top: 1px solid var(--color-border); }
.pending-suggestion:first-of-type { border-top: 0; padding-top: var(--space-sm); }
.pending-head { display: flex; align-items: center; gap: var(--space-sm); }
.pending-head .name { font-weight: 500; }
.pending-head .badge { background: var(--status-neutral-bg); color: var(--status-neutral-text); }
.pending-actions { display: flex; flex-wrap: wrap; gap: var(--space-lg); margin-top: var(--space-sm); }
.pending-col { flex: 1 1 18rem; min-width: 15rem; }
.pending-col-label { font-size: var(--font-size-sm); color: var(--color-text-muted); margin-bottom: var(--space-xs); }
.pending-action { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-sm); margin: 0; }
.pending-action .form-control { flex: 1 1 8rem; min-width: 8rem; }

/* Status badge: every variant maps to an Appendix A semantic. */
.badge { display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: var(--font-size-sm); }
/* Neutral count/tag chip (visual refresh slice 1): the non-status sibling of .badge.
   Status semantics stay with the Badge registry; a chip is just a quiet grouping
   pill (tag names, "+N more" counts) and must never carry a status color. */
.chip { display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: var(--font-size-sm); background: var(--status-neutral-bg); color: var(--status-neutral-text); }
.badge-active { background: var(--status-success-bg); color: var(--status-success-text); }
.badge-inactive { background: var(--status-neutral-bg); color: var(--status-neutral-text); }
/* Survey lifecycle: draft (neutral) · published (success) · closed (danger) */
.badge-draft { background: var(--status-neutral-bg); color: var(--status-neutral-text); }
.badge-published { background: var(--status-success-bg); color: var(--status-success-text); }
.badge-closed { background: var(--status-danger-bg); color: var(--status-danger-text); }

/* Background-job lifecycle (Core\View\JobStatus): running info, success, partial warning, failed danger */
.badge-job-running { background: var(--status-info-bg); color: var(--status-info-text); }
.badge-job-success { background: var(--status-success-bg); color: var(--status-success-text); }
.badge-job-partial { background: var(--status-warning-bg); color: var(--status-warning-text); }
.badge-job-failed  { background: var(--status-danger-bg); color: var(--status-danger-text); }

/* Inline action form inside a table row (e.g. revoke a participating company) */
.inline-form { display: inline; margin: 0; }

/* Per-row action cluster (question builder rows, revoke/settings rows, flow
   conditions). All controls — reorder arrows, the move-to-page selector, flow,
   remove — form ONE right-aligned group that wraps cleanly on narrow widths,
   instead of the full-width selector floating onto its own row. inline-flex keeps
   the cluster inline where it sits next to text (flow conditions) while still
   flex-wrapping its own children. */
.row-actions { display: inline-flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--space-xs); max-width: 100%; vertical-align: middle; }
.row-actions .inline-form { display: inline-flex; align-items: center; gap: var(--space-xs); }
/* The move-to-page select sizes to content and sits beside its button (not block/full-width). */
.row-actions .form-control { width: auto; display: inline-block; }
/* Compact form control matching btn-sm height (font-size held at base to avoid iOS focus zoom). */
/* Compact control for BUTTON ROWS, not forms. `.form-control` alone left it
   display:block/width:100% with a pale hairline, so the "move to page" select
   stretched and read as unstyled next to the accent-outlined buttons it sits
   with. It now sizes to its content and wears the same outline as those
   buttons, because in that row it IS one of them. Form inputs proper keep the
   quiet hairline — a page of blue-bordered fields would be loud. */
.form-control-sm {
    display: inline-flex;
    align-items: center;
    width: auto;
    /* Geometry copied from .btn-sm, not approximated: this select sits IN a button
       row, so it has to match its neighbours exactly - same height, padding, type
       size, radius and accent outline. It was 36px against their 28px, which is
       what made the row look uneven. */
    min-height: 28px;
    height: 28px;
    padding: 0 8px;
    font-size: var(--font-size-sm);
    line-height: 1;
    border-radius: var(--radius-md);
    border-color: var(--color-accent);
    color: var(--color-accent);
}
.form-control-sm:hover { border-color: var(--color-accent-hover); color: var(--color-accent-hover); }

/* Forms inside a card: compact, capped width */
.form-card { max-width: 36rem; }
.form-card .form-group:last-of-type { margin-bottom: 0; }
/* A card's row of actions. Wraps like .row-actions does: a card can hold two full-label
   buttons side by side (e.g. "Publiser og åpne for respondenter" + "Deaktiver"), which at
   phone widths would otherwise be squeezed past the container edge rather than stacking. */
.form-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-sm); margin-top: var(--space-lg); }
.field-error { color: var(--color-danger); font-size: var(--font-size-sm); margin-top: 4px; }

/* Progressive-disclosure block under a toggle checkbox (e.g. email-collection Mål +
   preview). The top margin gives the checkbox some breathing room from the revealed
   fields; [hidden] keeps it out of the flow until the toggle is on. */
.collect-detail { margin-top: var(--space-lg); }
.collect-detail[hidden] { display: none; }

/* The row under a consent textarea: the explicit/fallback note on the left, the
   "Reset to default" link pushed to the right. Renders fine with only the link
   (the required default-locale field has no note). */
.field-aside { display: flex; align-items: baseline; gap: var(--space-sm); margin-top: 4px; }
.field-aside .sub { margin: 0; }
.field-aside .consent-reset { margin-left: auto; flex: none; white-space: nowrap; font-size: var(--font-size-sm); }
.field-hint { color: var(--color-text-muted); font-size: var(--font-size-sm); margin-top: 4px; }
/* Mandatory-question marker in the builder overview (matches the respondent's "*"). */
.req-mark { color: var(--color-danger); font-weight: 600; margin-left: 2px; }

/* ── Flow builder: controls sharing a row ─────────────────────
   Flex's default `stretch` inflates a button sharing a row with labelled controls to the
   height of the whole label+control stack (measured: 62px next to 38px selects) — which is
   why "Add action" towered over its selects. Bottom-align the row and match the control
   height, so the three read as one set of controls on a shared baseline.
   The :has() guard leaves button-ONLY rows (e.g. the display-condition submit) at their
   natural size; where :has() is unsupported the row still bottom-aligns, which is the
   important half. */
.flow-form .form-row { align-items: flex-end; }
.flow-form .form-row:has(> label > .form-control) > .btn { min-height: 38px; }

/* ── Flow builder: the RULE ───────────────────────────────────
   The summary is the thing being authored, so it stays visible and reads in aligned
   columns (subject / operator / values). The editor that produces it is collapsed
   (.flow-editor) because it is only needed while editing. */
.flow-rule__lead { color: var(--color-text-muted); font-size: var(--font-size-sm); margin: 0 0 var(--space-sm); }
/* Grid, not table: the AND must span every column to read as joining the rows (a table-row
   conjunction lands in column 1 and looks like it belongs to the row above it). `display:
   contents` on the row promotes its spans to grid items so the columns align across rows. */
/* Columns are sized to CONTENT (`auto`), not to a fixed max. A `minmax(_, 20rem)` track
   inflates to its 20rem growth limit whenever there is free space — grid's "maximize tracks"
   step feeds non-fr tracks before fr ones — so a short subject like "Companies used" sat in a
   320px column and the rule read as scattered fragments instead of a sentence. `auto` stops at
   max-content; .flow-rule__subject's max-width caps that contribution so a long question label
   still wraps instead of eating the row. The free space goes to the trailing spacer, which
   keeps the row controls aligned with the card's Remove button. */
.flow-rule {
    display: grid;
    grid-template-columns: auto auto auto 1fr auto;
    align-items: baseline;
    column-gap: var(--space-md);
    row-gap: 2px;
    margin: 0 0 var(--space-md);
}
.flow-rule__row { display: contents; }
.flow-rule__subject { font-weight: 500; max-width: 20rem; }
/* Empty spacer cell: absorbs the free space between the rule and its controls. */
.flow-rule__gap { grid-column: 4; }
.flow-rule__op { color: var(--color-text-muted); white-space: nowrap; }
.flow-rule__values { font-weight: 500; min-width: 0; overflow-wrap: anywhere; }
.flow-rule__more { color: var(--color-text-muted); font-weight: 400; white-space: nowrap; }
.flow-rule__controls { white-space: nowrap; justify-self: end; }
.flow-rule__controls .inline-form { display: inline-block; }
.flow-rule__conj {
    grid-column: 1 / -1;
    color: var(--color-text-light);
    font-size: var(--font-size-sm);
    letter-spacing: 0.04em;
    padding: 2px 0;
}

/* Narrow screens: the columns must STACK. Squeezed into ~90px each they wrap one word per
   line and the rule becomes unreadable — worse than no columns at all. */
@media (max-width: 720px) {
    .flow-rule { display: block; }
    .flow-rule__row {
        display: block;
        padding: var(--space-sm) 0;
        border-bottom: 1px solid var(--color-border);
        position: relative;
    }
    .flow-rule__row > span { display: block; }
    .flow-rule__op { font-size: var(--font-size-sm); }
    .flow-rule__controls { justify-self: auto; margin-top: var(--space-xs); }
    .flow-rule__conj { padding: var(--space-xs) 0; }
    /* Stacked rows have no columns to space apart: the spacer would just add a blank line. */
    .flow-rule__gap { display: none; }
    .flow-rule__subject { max-width: none; }
}

/* Disclosures: the condition editor and the advanced condition types. Both are closed by
   default, which is what keeps the page compact — with JS off they still open/close. */
.flow-editor { margin-top: var(--space-sm); }
.flow-editor__toggle,
.flow-advanced__toggle {
    cursor: pointer;
    display: inline-block;
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
    padding: var(--space-xs) 0;
    user-select: none;
}
/* `display: inline-block` drops the native list-item marker, which would leave the toggle
   looking like inert grey text — the disclosure is the whole affordance for a collapsed
   editor. Supply the marker explicitly (same pattern as .lang-menu's summary) and rotate it
   on open, so the control reads as expandable in both states. */
.flow-editor__toggle::-webkit-details-marker,
.flow-advanced__toggle::-webkit-details-marker { display: none; }
/* No transition on the marker: toggling <details> restyles the summary's pseudo-element and
   the animation never reaches its end state, leaving the marker stuck at 0deg — i.e. open and
   closed would look identical. Snapping is correct here, and honest. */
.flow-editor__toggle::before,
.flow-advanced__toggle::before {
    content: "\25B8";
    display: inline-block;
    width: 0.9em;
    margin-right: 2px;
}
details[open] > .flow-editor__toggle::before,
details[open] > .flow-advanced__toggle::before { transform: rotate(90deg); }
.flow-editor__toggle:hover,
.flow-advanced__toggle:hover { color: var(--color-text); }
/* Match .btn:focus-visible so keyboard focus reads the same across the page. */
.flow-editor__toggle:focus-visible,
.flow-advanced__toggle:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }
.flow-editor__body { padding-top: var(--space-sm); }
.flow-advanced { margin-top: var(--space-sm); }
.flow-advanced__body { padding-top: var(--space-sm); }

/* ── Company name history ─────────────────────────────────────────────────────────
   The card answers "what is this company called, and what was it called before", so it
   is composed as that answer: current name as the hero, then the rename we can date,
   then the names it has also been known by. Each section names its qualifier ONCE in a
   heading, so the names form a scannable column instead of a list of records each
   carrying its own subtitle. */
.nh-label {
    /* The label is not the information: the name is. Sentence case (NOT .card-title's
       uppercase+tracking, which would compete with the card heading) and muted, so it
       reads as a quiet index to the value beneath rather than a heading in its own right. */
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
    margin-bottom: 2px;
}
.nh-current { font-size: var(--font-size-lg); font-weight: 600; line-height: 1.3; }
/* One step down the scale from --space-lg: three short sections separated by 24px read
   as a tall card holding very little. 16px keeps them distinct without the sprawl. */
.nh-group { margin-top: var(--space-md); }
.nh-list { list-style: none; margin: 0; padding: 0; }
.nh-item + .nh-item { margin-top: var(--space-sm); }
.nh-item__name { font-weight: 600; }
/* The date sits UNDER the name, not beside it: the name is the identity, the date is
   what happened to it. Side by side they compete for the same line. */
.nh-item__when { display: block; font-size: var(--font-size-sm); color: var(--color-text-muted); }
/* Earlier names are historical identities, not arbitrary list items, bullets would add
   generic-list noise to a stack that is already unambiguous. Plain stack, no markers. */
.nh-item--plain { font-weight: 400; line-height: 1.45; }

/* "View sync details": how SRVY processed each change, and when each earlier name was
   retrieved — operational, so it collapses out of the narrative. Same disclosure
   affordance as the flow editor's, so there is one marker convention page-wide. */
.sync-details { margin-top: var(--space-sm); border-top: 1px solid var(--color-border); padding-top: var(--space-sm); }
.sync-details__toggle {
    cursor: pointer;
    display: inline-block;
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
    padding: var(--space-xs) 0;
    user-select: none;
}
.sync-details__toggle::-webkit-details-marker { display: none; }
/* Marker AFTER the label (the .lang-menu convention rather than the flow editor's
   leading caret): the summary reads as a short phrase, so the caret trails it as a
   hint instead of indenting the text away from the section labels above. */
.sync-details__toggle::after {
    content: "\25B8";
    display: inline-block;
    margin-left: var(--space-xs);
}
.sync-details[open] > .sync-details__toggle::after { content: "\25BE"; }
.sync-details[open] > .sync-details__toggle { color: var(--color-text); }
.sync-details__toggle:hover { color: var(--color-text); }
.sync-details__toggle:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }
.sync-details__note {
    margin: var(--space-xs) 0 var(--space-md);
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
}
.sync-details .nh-label { margin-top: var(--space-md); }
.nh-events { list-style: none; margin: 0; padding: 0; font-size: var(--font-size-sm); }
.nh-event { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-sm); padding: var(--space-xs) 0; }
.nh-event + .nh-event { border-top: 1px solid var(--color-border); }
.nh-event__what { font-weight: 600; }
.nh-event__diff { color: var(--color-text); }
/* Push the timestamp to the right on wide rows, but let it wrap under on narrow ones. */
.nh-event__meta { margin-left: auto; color: var(--color-text-muted); white-space: nowrap; }

/* Question + operator on one line: the selects say what they are, so no labels above them. */
.flow-cond-line { display: flex; gap: var(--space-sm); flex-wrap: wrap; align-items: center; }
.flow-cond-line__field { display: block; }
.flow-cond-line__field--subject { flex: 1 1 22rem; }
.flow-cond-line__field--op { flex: 0 1 12rem; }
.flow-cond-submit { margin-top: var(--space-md); }

/* Display-condition value checkboxes (add-condition form): one bordered group per source.
   The wrap needs its own bottom margin: .cond-values only sets a TOP one, and the submit
   button that follows is a plain .form-row (no margin), so without this the button sits
   flush against the group's border. Shared by the flow + display add-condition forms. */
.cond-values-wrap { margin-bottom: var(--space-md); }
.cond-values { border: 0.5px solid var(--color-border); border-radius: var(--radius-md); padding: 6px 12px 10px; margin: 8px 0 0; }
.cond-values__legend { font-size: var(--font-size-sm, 13px); color: var(--color-text-muted, #6b6b6b); padding: 0 4px; }
.cond-values .checkbox-inline { margin: 6px 14px 0 0; }

/* ── Condition VALUE picker (JS enhancement of the checkbox group) ────────────
   The checkboxes are the source of truth; the picker only moves them into a popup.
   Everything here is scoped to .vpick, which exists only once the script has run — so with
   JS off none of it applies and the plain .cond-values grid renders exactly as before.
   Placed AFTER the .cond-values rules on purpose: `.vpick__list .checkbox-inline` and
   `.cond-values .checkbox-inline` have EQUAL specificity, so source order decides, and the
   fieldset carries both classes. */
.vpick { position: relative; max-width: 34rem; }
.vpick__field {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-sm);
    width: 100%;
    text-align: left;
    cursor: pointer;
    font: inherit;
}
.vpick__field::after { content: "\25BE"; color: var(--color-text-muted); flex: none; }
.vpick__value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vpick__value.is-empty { color: var(--color-text-light); }
.vpick.is-open .vpick__field { border-color: var(--color-text); }
.vpick__panel {
    position: absolute;
    z-index: 20;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
    padding: var(--space-sm);
}
.vpick__search { margin-bottom: var(--space-sm); }
/* The moved fieldset: one option per row, scrollable, so a long list stays usable. */
.vpick__list {
    border: 0;
    margin: 0;
    padding: 0;
    max-height: 15rem;
    overflow-y: auto;
}
.vpick__list .cond-values__legend { display: none; }
.vpick__list .checkbox-inline {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    margin: 0;
    padding: 5px 6px;
    border-radius: var(--radius-sm);
    cursor: pointer;
}
/* The filter marks non-matches with the `hidden` attribute; an author `display` beats the
   UA's [hidden] rule, so it must be turned off explicitly or filtering does nothing. */
.vpick__list .checkbox-inline[hidden] { display: none; }
.vpick__list .checkbox-inline:hover { background: var(--color-accent-subtle); }
.vpick__list .checkbox-inline:focus-within {
    background: var(--color-accent-subtle);
    outline: var(--focus-ring);
    outline-offset: -2px;
}
.vpick__empty { margin: var(--space-sm) 0 0; }
.vpick__foot { display: flex; justify-content: space-between; gap: var(--space-sm); margin-top: var(--space-sm); }


/* Builder "Conditional: Shown when …" note under a question that has display conditions. */
.qb-cond-note { margin-top: 3px; font-size: var(--font-size-sm); color: var(--color-text-muted); line-height: 1.4; }
.qb-cond-badge {
    display: inline-block;
    padding: 0 6px;
    border-radius: 4px;
    background: var(--color-accent-soft);
    color: var(--color-accent);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.02em;
    vertical-align: 1px;
}

/* Card header row: card title + inline action (e.g. "Add user") */
.card-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-md); margin-bottom: var(--space-md); }
.card-head .card-title { margin: 0; }
/* The right-aligned action cluster in a card head (move arrows, Preview, Edit, Remove).
   Mirrors .row-actions so its buttons/forms are evenly spaced instead of touching. */
.card-head-actions { display: inline-flex; align-items: center; flex-wrap: wrap; gap: var(--space-xs); }
.btn-sm { min-height: 28px; padding: 0 10px; font-size: var(--font-size-sm); }
/* Compact icon-only button (e.g. the survey-builder delete). Square, centered glyph;
   the accessible name lives on the button's aria-label. */
.qb-icon-btn { padding: 0; width: 28px; min-width: 28px; display: inline-flex; align-items: center; justify-content: center; }
.qb-icon { display: block; }

/* Async-action toasts + aria-live announcements for the survey builder. Fixed, stacked
   bottom-right; the container never intercepts clicks, individual toasts are inert too. */
.qb-toasts { position: fixed; right: var(--space-lg); bottom: var(--space-lg); z-index: 1200; display: flex; flex-direction: column; gap: var(--space-sm); pointer-events: none; max-width: min(360px, calc(100vw - 2 * var(--space-lg))); }
.qb-toast { pointer-events: none; padding: var(--space-sm) var(--space-md); border-radius: var(--radius-md); border: 1px solid transparent; font-size: var(--font-size-sm); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12); opacity: 1; transform: translateY(0); transition: opacity 0.25s ease, transform 0.25s ease; }
.qb-toast--ok { background: var(--color-flash-success-bg); color: var(--color-flash-success-text); border-color: var(--color-flash-success-border); }
.qb-toast--err { background: var(--color-flash-error-bg); color: var(--color-flash-error-text); border-color: var(--color-flash-error-border); }
.qb-toast--out { opacity: 0; transform: translateY(6px); }
@media (prefers-reduced-motion: reduce) { .qb-toast { transition: none; } }

/* Radio group (e.g. company role) */
.form-label { display: block; font-size: 0.82rem; font-weight: 500; margin-bottom: var(--space-xs); color: var(--color-text-muted); }
.radio-group { display: flex; flex-direction: column; gap: var(--space-sm); }
.form-group label.radio { display: flex; align-items: center; gap: var(--space-sm); margin-bottom: 0; font-size: var(--font-size-base); font-weight: 400; color: var(--color-text); cursor: pointer; }

/* Access/help note (e.g. "This user will get access to …") */
.form-note { color: var(--color-text-muted); font-size: var(--font-size-sm); margin: 0 0 var(--space-md); }

/* Muted supporting note, usable at any level (visual refresh slice 1). `.sub` was
   previously styled only inside .data-table/.lang-opt scopes, so the ops-page
   top-level notes silently rendered as plain body text — the audit's unstyled-note
   defect. One base rule now owns the treatment; the former scoped duplicates are
   retired (identical declarations, computed no-op there). */
.sub { color: var(--color-text-muted); font-size: var(--font-size-sm); }

/* Language card: the additional-language checkbox list (Survey Overview). */
.lang-opts { display: flex; flex-direction: column; gap: var(--space-xs); }
.lang-opt { display: flex; align-items: center; gap: var(--space-xs); }
.lang-opt--disabled { color: var(--color-text-muted); }

/* ── Modal (shared SRVY pattern, matches HR Cluster) ── */
.modal-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.45); z-index: 1000; align-items: center; justify-content: center; padding: var(--space-md); }
.modal-overlay.open { display: flex; }
.modal { background: var(--color-surface); border-radius: var(--radius-lg); box-shadow: 0 8px 40px rgba(0,0,0,0.18); width: 100%; max-width: 460px; max-height: 90vh; overflow: hidden; display: flex; flex-direction: column; }
.modal > form { display: flex; flex-direction: column; flex: 1; min-height: 0; overflow: hidden; }
.modal-header { flex-shrink: 0; display: flex; align-items: center; justify-content: space-between; gap: var(--space-md); padding: var(--space-lg) var(--space-lg) var(--space-md); border-bottom: 1px solid var(--color-border); }
.modal-title { font-size: var(--font-size-lg); font-weight: 600; margin: 0; }
.modal-close { background: none; border: none; font-size: 1.5rem; cursor: pointer; color: var(--color-text-muted); line-height: 1; padding: 0 var(--space-xs); border-radius: var(--radius-sm); }
.modal-close:hover { color: var(--color-text); }
.modal-close:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }
.modal-body { flex: 1; overflow-y: auto; min-height: 0; padding: var(--space-lg); }
.modal-body .form-group:last-of-type { margin-bottom: 0; }
.modal-footer { flex-shrink: 0; display: flex; align-items: center; justify-content: flex-end; gap: var(--space-sm); padding: var(--space-md) var(--space-lg); border-top: 1px solid var(--color-border); }

/* ── Question Library option list editor (design system batch 4) ──
   The JS-enhanced options field: an in-form row list with drag/button reordering,
   plus an add/edit dialog built from the shared .modal component above. This block
   replaces the editor's former inline raw-px/rgba styles; the compact row-action
   buttons keep their deliberately-tight 2px vertical padding (sub-token by design:
   they sit inside a 36px form-control row). */
.opt-editor-list { border: 1px solid var(--color-border); border-radius: var(--radius-md); }
.opt-editor-empty { padding: var(--space-sm) var(--space-md); }
.opt-editor-row { display: flex; align-items: center; gap: var(--space-xs); padding: var(--space-xs) var(--space-sm); }
.opt-editor-row + .opt-editor-row { border-top: 1px solid var(--color-border); }
.opt-editor-row.is-dragging { opacity: .4; }
.opt-editor-row .form-control { flex: 1 1 auto; min-width: 0; }
.opt-editor-handle { cursor: grab; opacity: .5; flex: 0 0 auto; }
.opt-editor-act { flex: 0 0 auto; white-space: nowrap; }
.opt-editor-act .btn { padding: 2px var(--space-sm); min-width: 0; margin-left: var(--space-xs); }
.opt-editor-add { margin-top: var(--space-sm); }
.opt-editor-adv { margin-top: var(--space-xs); }
.opt-editor-adv summary { cursor: pointer; }
.opt-editor-adv .checkbox-inline { margin-top: var(--space-sm); }
.opt-editor-note { margin-top: var(--space-xs); }
.opt-editor-adv .form-group { margin-top: var(--space-sm); }

/* ── App shell, mobile (single top app bar + slide-in nav drawer) ──
   One 56px app bar carries the whole shell: [☰] SRVY Admin ........ [Language].
   The sidebar keeps no visible bar of its own; it collapses to `display: contents`
   so its only surviving child, the fixed nav overlay, still opens over everything.
   The page title drops out of the bar and into the content area below. */
@media (max-width: 768px) {
    .app-shell { flex-direction: column; }

    /* Mobile typography scale (Admin + Analytics share this shell): the workspace
       chrome reads ~10% denser than desktop to match HR Cluster's density. Only the
       type-scale tokens are redefined, and only on .app-shell, so desktop and the
       public/login/landing surfaces are untouched, and spacing tokens are untouched.
       Plain body and table-cell text inherits from <body> (16px) rather than a token,
       so reading text keeps its comfortable size; only elements that explicitly use a
       token scale down. --font-size-base is deliberately held at 1rem: it drives inputs,
       and a sub-16px input font triggers focus zoom on iOS Safari. */
    .app-shell {
        --font-size-sm: 0.8rem;    /* 14px  -> 12.8px : table headers, pills, buttons, badges, hints, sidebar footer */
        --font-size-lg: 1rem;      /* 18px  -> 16px   : topbar brand, section/modal titles, empty-state heading */
        --font-size-xl: 1.125rem;  /* 20px  -> 18px   : page title (content <h1>) */
    }
    /* Form labels are literal (not a token) on desktop; bring them down in step. */
    .app-shell .form-group label,
    .app-shell .form-label { font-size: 0.75rem; }

    /* Sidebar has no box on mobile: brand + footer hide, the nav overlay remains. */
    .app-sidebar { display: contents; }
    .sidebar-brand { display: none; }
    .sidebar-footer { display: none; }

    /* The single mobile app bar. */
    .app-topbar {
        height: 56px;
        padding: 0 var(--space-sm) 0 4px;
        gap: var(--space-sm);
    }
    .sidebar-toggle {
        display: flex; align-items: center; justify-content: center; flex: 0 0 auto;
        background: none; border: none; cursor: pointer; color: var(--color-text);
        width: 44px; height: 44px; font-size: 1.5rem; line-height: 1; border-radius: var(--radius-sm);
    }
    .sidebar-toggle:active { background: var(--color-accent-subtle); }
    /* The topbar carries the hamburger and the page title on phones; the brand would
       be a third element competing for the same row, and the sidebar drawer already
       shows it. So the brand yields here rather than the title. */
    .app-topbar .topbar-brand { display: none; }
    .app-topbar .page-title {
        min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
        font-size: var(--font-size-lg);
    }


    /* Nav drawer overlay (unchanged behavior). */
    .sidebar-nav {
        display: none; position: fixed; inset: 0; background: var(--color-surface);
        z-index: 999; padding: 3.5rem var(--space-md) var(--space-xl); gap: 2px; overflow-y: auto;
    }
    .sidebar-nav.open { display: flex; }
    .sidebar-nav a { font-size: 0.9rem; font-weight: 500; padding: 12px var(--space-md); margin: 0; }
    .sidebar-nav.open .nav-close { display: flex; }
    .nav-close {
        position: absolute; top: 4px; right: 4px; width: 44px; height: 44px;
        align-items: center; justify-content: center; background: none; border: none;
        font-size: 1.85rem; cursor: pointer; color: var(--color-text); line-height: 1; border-radius: var(--radius-sm);
    }
    .app-main { margin-left: 0; }

    /* Denser content on phones: less gutter, a right-sized welcome/placeholder card,
       and a lighter footer so the page reads as intentional rather than stretched. */
    .app-content { padding: var(--space-md) var(--space-sm); }
    .empty-state { padding: var(--space-xl) var(--space-md); }
    .site-footer { padding: var(--space-md); margin-top: var(--space-md); }

    /* Responsive column utility: drop secondary table columns on phones so the admin
       list tables (Companies, Surveys, Survey editions) stay within the viewport
       without horizontal scroll. The name column and the right-aligned Detaljer
       action always remain. Shared by all three tables rather than reimplemented. */
    .hide-sm { display: none; }
}

/* ── Cookie consent banner (GA consent-gated via srvy:cookie-consent) ── */
.cookie-banner {
    position: fixed;
    left: var(--space-md);
    right: var(--space-md);
    bottom: var(--space-md);
    z-index: 9999;
    max-width: 640px;
    margin: 0 auto;
    padding: var(--space-md) var(--space-lg);
    background: var(--color-text);
    color: var(--color-white);
    border-radius: var(--radius-md);
    box-shadow: 0 10px 30px rgba(0,0,0,0.25);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-sm) var(--space-md);
    font-size: 0.85rem;
    line-height: 1.5;
    transform: translateY(16px);
    opacity: 0;
    pointer-events: none;
    transition: opacity .25s ease, transform .25s ease;
}
.cookie-banner.is-visible { opacity: 1; transform: translateY(0); pointer-events: auto; }
.cookie-banner__text { flex: 1 1 260px; margin: 0; }
.cookie-banner__actions { display: flex; gap: var(--space-sm); flex: 0 0 auto; }
.cookie-banner__btn {
    appearance: none;
    border: 1px solid rgba(255,255,255,0.22);
    background: transparent;
    color: var(--color-white);
    padding: var(--space-sm) var(--space-md);
    border-radius: var(--radius-sm);
    font: inherit;
    cursor: pointer;
    transition: background .15s ease, border-color .15s ease;
}
.cookie-banner__btn:hover,
.cookie-banner__btn:focus-visible { background: rgba(255,255,255,0.10); outline: none; }
.cookie-banner__btn--primary { background: var(--color-white); border-color: var(--color-white); color: var(--color-text); }
.cookie-banner__btn--primary:hover,
.cookie-banner__btn--primary:focus-visible { background: rgba(255,255,255,0.85); border-color: rgba(255,255,255,0.85); }
/* 560px is the standard content-reflow breakpoint (§2.3); the banner's former
   520px one-off folds into it (design system batch 5). */
@media (max-width: 560px) {
    .cookie-banner {
        left: var(--space-sm);
        right: var(--space-sm);
        bottom: var(--space-sm);
        padding: var(--space-sm) var(--space-md);
        flex-wrap: nowrap;
        gap: var(--space-sm);
    }
    .cookie-banner__text { flex: 1 1 auto; min-width: 0; font-size: 0.75rem; line-height: 1.35; }
    .cookie-banner__actions { gap: var(--space-xs); }
    .cookie-banner__btn { padding: var(--space-xs) var(--space-sm); font-size: 0.75rem; }
}
@media (prefers-reduced-motion: reduce) {
    .cookie-banner { transition: none; }
}

/* ── Changelog ──────────────────────────────────────────────
   Shared by the public /changelog page and the admin editor. Type/module pills
   use the same tokens as the rest of the design system. */

/* Public page layout */
.changelog-page { position: relative; z-index: 1; padding: var(--space-xl) var(--space-lg) var(--space-2xl); }
/* Slice 7: the changelog's reading width joins the shared measure token instead
   of carrying its own near-identical literal. */
.changelog-inner { max-width: var(--measure-read); margin: 0 auto; }
.cl-back { margin: 0 0 var(--space-lg); font-size: var(--font-size-sm); }
.cl-back a { color: var(--color-text-muted); }
.cl-back a:hover { color: var(--color-text); text-decoration: none; }
.cl-title { font-size: var(--cl-title-size); font-weight: var(--font-weight-page-title);
    letter-spacing: -0.015em; line-height: 1.15; margin: 0 0 var(--space-sm); }
.cl-subtitle { color: var(--color-text-muted); font-size: var(--landing-lead-size);
    margin: 0 0 var(--space-2xl); }
.cl-empty { color: var(--color-text-muted); }

/* Legend */
.cl-legend { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md);
    padding: var(--space-lg); margin-bottom: var(--space-2xl); }
.cl-legend-title { font-size: var(--font-size-sm); font-weight: 600; text-transform: uppercase; letter-spacing: .05em;
    color: var(--color-text-muted); margin: 0 0 var(--space-md); }
.cl-legend-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: var(--space-sm) var(--space-lg); }
.cl-legend-item { display: flex; align-items: baseline; gap: var(--space-sm); }
.cl-legend-text { color: var(--color-text-muted); font-size: var(--font-size-sm); }

/* Year groups + entries */
/* Slice 7 year rhythm: the break BETWEEN years is the page's largest, so it is
   expressed as space above the next year rather than a uniform gap below every
   one — the last year no longer trails 3rem of emptiness before the footer. */
.cl-year + .cl-year { margin-top: var(--space-2xl); }
.cl-year-label { font-size: var(--type-section); font-weight: var(--font-weight-emphasis);
    margin: 0 0 var(--space-sm); padding-bottom: var(--space-xs);
    border-bottom: 1px solid var(--color-border-strong); }
.cl-entry { padding: var(--space-xl) 0; border-bottom: 1px solid var(--color-border); }
.cl-entry:last-child { border-bottom: none; }
.cl-entry-meta { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-sm); margin-bottom: var(--space-sm); }
.cl-date { color: var(--color-text-light); font-size: var(--font-size-sm); }
.cl-entry-title { font-size: var(--cl-entry-title-size); font-weight: var(--font-weight-emphasis);
    margin: 0 0 var(--space-sm); line-height: 1.35; }
.cl-entry-body { color: var(--color-text-muted); line-height: 1.65; margin: 0; }
.cl-entry-tech { color: var(--color-text-light); font-size: var(--font-size-sm); margin: var(--space-sm) 0 0; }
.cl-entry-tech span { font-weight: 600; }

/* Pills: type (coloured) + module (neutral) */
.cl-pill { display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: var(--cl-pill-size); font-weight: 600;
    line-height: 1.5; white-space: nowrap; }
.cl-pill-module { background: var(--cl-cat-module-bg); color: var(--cl-cat-module-text); font-weight: 500; }
/* Type pills: added/security/deprecated map to the Appendix A status semantics.
   update/fixed/core_update/removed are the changelog-local CATEGORICAL accent set
   (--cl-cat-*, defined in :root; not statuses — cross-surface consolidation is a
   separate reviewed change). */
.cl-type-added       { background: var(--status-success-bg); color: var(--status-success-text); }
.cl-type-update      { background: var(--cl-cat-update-bg); color: var(--cl-cat-update-text); }
.cl-type-core_update { background: var(--cl-cat-core-bg); color: var(--cl-cat-core-text); }
.cl-type-fixed       { background: var(--cl-cat-fixed-bg); color: var(--cl-cat-fixed-text); }
.cl-type-security    { background: var(--status-danger-bg); color: var(--status-danger-text); }
.cl-type-deprecated  { background: var(--status-neutral-bg); color: var(--status-neutral-text); }
.cl-type-removed     { background: var(--cl-cat-removed-bg); color: var(--cl-cat-removed-text); }

/* Admin editor */
.cl-cell-date { color: var(--color-text-muted); font-size: var(--font-size-sm); white-space: nowrap; }
.cl-cell-modules { display: flex; flex-wrap: wrap; gap: var(--space-xs); margin-top: var(--space-xs); }
.cl-actions { display: flex; align-items: center; justify-content: flex-end; gap: var(--space-md); flex-wrap: wrap; }
.cl-actions .inline-form { display: inline; margin: 0; }
.cl-form-row { display: flex; gap: var(--space-md); }
.cl-form-row .form-group { flex: 1; }
.cl-fieldset-label { display: block; margin-bottom: var(--space-xs); color: var(--color-text-muted); font-size: var(--font-size-sm); }
.cl-checks { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: var(--space-xs) var(--space-md); }
.cl-check { display: flex; align-items: center; gap: var(--space-xs); font-size: var(--font-size-sm); }
.cl-check-block { margin-top: var(--space-sm); }
.modal.modal-wide { max-width: 620px; }
/* 560px is the standard content-reflow breakpoint (§2.3); the changelog's former
   520px one-off folds into it (design system §3.4 PR-3). */
@media (max-width: 560px) {
    .cl-form-row { flex-direction: column; gap: 0; }
}

/* ── Public legal pages (terms / privacy / cookies / security / dpa) ──
   Rendered through PublicChrome by LegalPage. Same calm content-column layout as
   the changelog (shared --measure-read), tuned for long-form prose: a comfortable
   line length, generous heading rhythm, and a stacked-card table on mobile. */
.legal-page { position: relative; z-index: 1; padding: var(--space-xl) var(--space-lg) var(--space-2xl); }
.legal-inner { max-width: var(--measure-read); margin: 0 auto; }
.legal-back { margin: 0 0 var(--space-lg); font-size: var(--font-size-sm); }
.legal-back a { color: var(--color-text-muted); }
.legal-back a:hover { color: var(--color-text); text-decoration: none; }
.legal-title { font-size: var(--cl-title-size); font-weight: var(--font-weight-page-title);
    letter-spacing: -0.015em; line-height: 1.15; margin: 0 0 var(--space-xs); }
.legal-updated { color: var(--color-text-light); font-size: var(--font-size-sm); margin: 0 0 var(--space-2xl); }

/* Body: hand-authored trusted HTML (h2/h3, p, ul/ol, strong, a, code, table). */
.legal-body { color: var(--color-text); font-size: var(--font-size-base); line-height: 1.7; }
.legal-body h2 { font-size: var(--type-section); font-weight: var(--font-weight-emphasis);
    line-height: 1.3; margin: var(--space-2xl) 0 var(--space-sm); }
.legal-body h3 { font-size: var(--font-size-lg); font-weight: var(--font-weight-emphasis);
    margin: var(--space-lg) 0 var(--space-xs); }
.legal-body p { margin: 0 0 var(--space-md); }
.legal-body ul, .legal-body ol { margin: 0 0 var(--space-md); padding-left: var(--space-lg); }
.legal-body li { margin-bottom: var(--space-xs); }
.legal-body li:last-child { margin-bottom: 0; }
.legal-body a { color: var(--color-accent); text-decoration: none; }
.legal-body a:hover { text-decoration: underline; }
.legal-body strong { font-weight: var(--font-weight-emphasis); }
.legal-body code { font-size: 0.9em; background: var(--color-accent-subtle);
    color: var(--color-text); padding: 1px 5px; border-radius: var(--radius-sm); }

/* Tables (the cookies list + the DPA subprocessor annex). Fixed layout, quiet
   header, striped rows; collapses to stacked cards under the reflow breakpoint. */
.legal-table { width: 100%; border-collapse: collapse; table-layout: fixed;
    font-size: var(--font-size-sm); margin: var(--space-md) 0 var(--space-lg); }
.legal-table col.sp-provider { width: 20%; }
.legal-table col.sp-purpose { width: 18%; }
.legal-table col.sp-data { width: 28%; }
.legal-table col.sp-location { width: 14%; }
.legal-table col.sp-safeguards { width: 20%; }
.legal-table th, .legal-table td { padding: var(--space-xs) var(--space-sm); text-align: left;
    vertical-align: top; word-wrap: break-word; overflow-wrap: break-word; }
.legal-table th { font-size: 0.74rem; font-weight: 600; letter-spacing: 0.04em;
    text-transform: uppercase; color: var(--color-text-muted);
    border-bottom: 1px solid var(--color-border); }
.legal-table td { border-bottom: 1px solid var(--color-border); }
.legal-table tbody tr:nth-child(even) td { background: var(--color-bg); }
.legal-table tbody tr:last-child td { border-bottom: none; }
.legal-table code { background: none; padding: 0; }
.legal-table .sp-entity { display: block; color: var(--color-text-muted);
    font-size: 0.78rem; margin-top: 2px; }

@media (max-width: 560px) {
    .legal-body h2 { margin-top: var(--space-xl); }
    /* Each table row becomes a labelled stacked card. */
    .legal-table, .legal-table colgroup, .legal-table col, .legal-table thead,
    .legal-table tbody, .legal-table tr, .legal-table th, .legal-table td {
        display: block; width: auto; }
    .legal-table thead { position: absolute; left: -9999px; top: auto;
        width: 1px; height: 1px; overflow: hidden; }
    .legal-table tbody tr { border: 1px solid var(--color-border); border-radius: var(--radius-md);
        margin-bottom: var(--space-sm); padding: var(--space-sm); background: var(--color-surface); }
    .legal-table tbody tr:nth-child(even) td { background: transparent; }
    .legal-table td { padding: var(--space-xs) 0; border-bottom: none; }
    .legal-table td::before { content: attr(data-label); display: block;
        font-size: 0.68rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em;
        color: var(--color-text-muted); margin-bottom: 2px; }
    .legal-table td:first-child { font-size: var(--font-size-base); margin-bottom: var(--space-xs); }
    .legal-table td:first-child::before { display: none; }
}

/* ── Generic public 404 (styled through PublicChrome; design system §3.4 PR-2) ──
   Same calm content-column layout as the public changelog. */
.nf-page { position: relative; z-index: 1; padding: var(--space-2xl) var(--space-lg); }
.nf-inner { max-width: 46rem; margin: 0 auto; }
.nf-title { font-size: 1.6rem; font-weight: 600; margin: 0 0 var(--space-xs); }
.nf-body { color: var(--color-text-muted); margin: 0 0 var(--space-lg); }
.nf-home { font-size: var(--font-size-sm); }

/* ── Public landing (moved out of Site.php's inline <style>; design system §3.4 PR-3) ──
   Landing-specific layout; tokens and the shared footer come from the sections above.
   Uses 100dvh (not 100vh) so the page is measured against the visible viewport,
   excluding mobile browser chrome: the footer is on screen on load without scrolling.
   Vertical rhythm is deliberately tight so the whole hero + footer clears an iPhone
   Safari viewport (390x844 class). */
body.landing { min-height: 100dvh; }
.landing-center { flex: 1; display: flex; align-items: center; justify-content: center;
    position: relative; z-index: 1; padding: var(--space-lg); }
/* 35rem (560px), widened from 34rem: the benchmarking hero carries more copy than
   the one it replaced, and at the old width the lead stacked into seven short lines —
   a tall column on a very wide canvas. A modest widening, not a redesign: the block
   is the outer bound, and the heading and lead set their own measures below, so this
   spreads the copy rather than letting every element run the full width. */
.landing-content { max-width: 35rem; width: 100%; box-sizing: border-box; }
.landing-content .wordmark { font-size: var(--landing-wordmark-size); font-weight: 700;
    letter-spacing: var(--landing-wordmark-track); margin: 0 0 1.75rem; }
/* The hero headline is the brand statement, so it carries the accent rather than
   body ink. This is the one deliberate exception to "blue means interactive": on
   the landing the line IS the identity. */
/* Weight 600, not the shared 650 role: at display size and in the accent, a lighter
   cut reads more refined while staying prominent. Landing-only — admin headings keep
   the role value. */
/* 23ch keeps the headline off the block's full width so it breaks into two balanced
   lines instead of one long weak one. Deliberately NOT forced onto a single line: at
   display size that would stretch the hero wide and thin. In ch, so the measure
   tracks the display clamp as the font resizes. */
.landing-content h1 { font-size: var(--landing-title-size); font-weight: 600;
    letter-spacing: -0.015em; line-height: 1.15; margin: 0 0 1rem; color: var(--color-accent);
    max-width: 23ch; }
/* The lead is a reading line, so it gets a measure of its own rather than
   running the full hero width. 57ch, up from 34ch: 34ch was sized for the old
   two-sentence lead and broke this one into seven short lines. 57ch takes
   essentially the block's available width and sits mid-band for readability.
   Line count is deliberately NOT an invariant here — it falls out of how long each
   language runs, so English and Swedish land on four lines and the ~5% longer
   Norwegian and Danish on five. Chasing four everywhere would mean sizing the
   desktop composition for the longest translation. */
.landing-content .lead { color: var(--color-text-muted); line-height: 1.6; margin: 0 0 1.75rem;
    font-size: var(--landing-lead-size); max-width: 57ch; }
/* Orientation for returning participants, sitting directly above the buttons it
   explains. Muted and at link size so it reads as a caption on the sign-in pair
   rather than a second lead. It owns the gap the nav used to open, so the nav's
   own margin collapses when the note precedes it. No 34ch measure here: unlike the
   lead this is one short caption, and the reading-line cap would wrap it in two. */
.landing-content .signin-note { color: var(--color-text-muted); font-size: var(--landing-link-size);
    line-height: 1.5; margin: 2rem 0 .75rem; }
.landing-content .links { display: flex; gap: .75rem; align-items: center; flex-wrap: wrap; margin-top: 2rem; }
.landing-content .signin-note + .links { margin-top: 0; }
.landing-content .links a { text-decoration: none; font-size: var(--landing-link-size); }
/* Slice 7: the product's most important public action stops being an outline.
   Primary is filled platform-black; secondary takes the outline it vacated, so
   the two sign-in paths read as a real primary/secondary pair instead of a
   button next to a muted link. */
.landing-content .links .primary { padding: .6rem 1.15rem; border: 1px solid var(--color-accent);
    border-radius: var(--radius-md); background: var(--color-accent); color: var(--color-white); }
.landing-content .links .primary:hover { background: var(--color-accent-hover);
    border-color: var(--color-accent-hover); text-decoration: none; }
/* Same outline treatment as every other secondary control: accent border, accent
   label, white fill. The landing links are .primary/.secondary rather than .btn
   classes, so they do not inherit .btn-secondary - this keeps them in step by hand. */
.landing-content .links .secondary { padding: .6rem 1.15rem; border: 1px solid var(--color-accent);
    border-radius: var(--radius-md); color: var(--color-accent); background: var(--color-surface); }
.landing-content .links .secondary:hover { background: var(--color-accent-subtle);
    border-color: var(--color-accent-hover); color: var(--color-accent-hover); text-decoration: none; }
.landing-content .foot { color: var(--color-text-light); font-size: var(--landing-foot-size); margin-top: 3rem; }
/* The SRVY mark, at full strength. It used to be faded (opacity .52, then .42) as a
   "subtle trust mark"; the brand mark should not be translucent — it renders as the
   pure black logo it is. */
.landing-content .backed-mark { height: 24px; width: auto; display: block; margin: 0 0 0.85rem; }
/* The footer sits in normal flow (not fixed); on the landing it keeps
   only a small gap from the hero and trims its own vertical padding. */
body.landing .site-footer { margin-top: var(--space-lg); padding: var(--space-md) var(--space-xl); }
/* Phone viewports: shave the gaps and ease the heading/line-height so the footer
   stays visible on load. Optimise whitespace, don't shrink the type wholesale, so
   the page still reads as calm and premium. 560px is the standard content-reflow
   breakpoint (§2.3); the landing's former 480px one-off folds into it. */
@media (max-width: 560px) {
    .landing-center { padding: var(--space-md); }
    .landing-content .backed-mark { height: 22px; margin: 0 0 0.7rem; }
    .landing-content .wordmark { margin: 0 0 1rem; }
    /* Phones keep the tighter hero: the display clamp's floor still applies, but
       the margins stay small so hero + footer clear a 390x844 viewport on load. */
    /* Phones drop both desktop measures: at this width the viewport is already the
       tighter constraint, and a ch cap would only add ragged breaks. */
    .landing-content h1 { margin: 0 0 .7rem; line-height: 1.2; max-width: none; }
    .landing-content .lead { line-height: 1.55; margin: 0 0 1.35rem; font-size: 1rem; max-width: none; }
    /* The note adds a line to the hero, so it takes the tightened rhythm too and the
       page still clears a 390x844 viewport without scrolling. */
    .landing-content .signin-note { margin: 1.35rem 0 .6rem; }
    body.landing .site-footer { margin-top: var(--space-md); padding: var(--space-md) var(--space-lg); }
}

/* ── Front-page (Forside) card + tabbed text editor ───────────────────────── */
/* The text preview is now the full width of the card: its Edit action moved up into
   .card-head-actions beside Preview, so there is no second column to lay out against. */
.fp-current { min-width: 0; }
.fp-current__heading { font-weight: 600; margin-bottom: var(--space-xs); }
.fp-current__heading--muted { font-weight: 400; color: var(--color-text-muted); }

/* Preview: clearly a preview, never the editable page. */
.fp-preview { margin: 0; border: 1px solid var(--color-border); border-radius: var(--radius-sm); background: var(--color-accent-subtle); padding: var(--space-sm) var(--space-md); }
.fp-preview__cap { display: block; font-size: var(--font-size-sm); color: var(--color-text-muted); text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: var(--space-xs); }
.fp-preview__body p { margin: 0 0 var(--space-sm); }
.fp-preview__body p:last-child { margin-bottom: 0; }
.fp-preview__body strong { font-weight: 700; }
.fp-preview__body em { font-style: italic; }
.fp-preview__body ul { margin: 0 0 var(--space-sm); padding-left: 1.4em; list-style: disc; }
.fp-preview__body ul:last-child { margin-bottom: 0; }
/* Card preview: render the real thing, truncate visually. */
.fp-preview--clamped { margin-top: var(--space-xs); }
.fp-preview--clamped .fp-preview__body { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }

/* Language tabs. */
.fp-tabs { display: flex; flex-wrap: wrap; gap: var(--space-xs); border-bottom: 1px solid var(--color-border); margin-bottom: var(--space-md); }
.fp-tab { background: none; border: none; border-bottom: 2px solid transparent; padding: var(--space-xs) var(--space-sm); cursor: pointer; color: var(--color-text-muted); font: inherit; margin-bottom: -1px; }
.fp-tab:hover { color: var(--color-text); }
.fp-tab.is-active { color: var(--color-text); border-bottom-color: var(--color-accent); font-weight: 600; }
.fp-tab:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }

.fp-pane { display: none; }
.fp-pane.is-active { display: block; }

/* Formatting toolbar. */
.fmt-toolbar { display: flex; gap: var(--space-xs); margin-bottom: var(--space-xs); }
.fmt-btn { min-width: 2rem; height: 2rem; padding: 0 var(--space-xs); border: 1px solid var(--color-border); background: var(--color-surface); border-radius: var(--radius-sm); cursor: pointer; color: var(--color-text); line-height: 1; }
.fmt-btn:hover { background: var(--color-accent-subtle); border-color: var(--color-border-strong); }
.fmt-btn:focus-visible { outline: var(--focus-ring); outline-offset: 1px; }

.fp-editor { resize: none; overflow: hidden; min-height: 5rem; }

/* The Email-branding card's hand-built header mock lived here. It is gone: the card
   now links to a Preview that renders the REAL invitation through EmailTemplates,
   so there is no second copy of the email's look to keep in sync. */

/* ── Composition components (Platform Visual Refresh slice 3) ─────────────────
   The first consumers of the slice-2 role vocabulary. Every selector below is
   NEW: no existing rule is touched, so introducing them changes nothing until a
   page adopts one. Slice 3 adopts them on Settings only; slice 4 rolls the page
   header out shell-wide, slice 10 is the stat/meter consumer. */

/* Page header: the editorial entry (§4 Hierarchy). */
.page-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-md);
    margin: 0 0 var(--section-gap);
}
.page-header__title {
    font-size: var(--type-page-title);
    font-weight: var(--font-weight-page-title);
    letter-spacing: -0.01em;
    line-height: 1.25;
    margin: 0;
}
.page-header__lead {
    color: var(--color-text-muted);
    font-size: var(--type-body);
    line-height: 1.55;
    margin: var(--space-xs) 0 0;
    max-width: var(--measure-read);
}
.page-header__actions { display: flex; align-items: center; gap: var(--space-sm); flex-shrink: 0; }
/* A page's own toolbar (`.page-head` action row, `.pill-nav`) belongs to the header
   REGION, not to the content below it: give it the tight gap, and keep the full
   section gap for the real content that follows. Without this the headline and its
   own actions read as two unrelated blocks (slice 4). */
/* `.pill-nav-wrap` covers the case where pill-nav.js has wrapped the nav for its
   overflow chevrons — the direct sibling is then the wrap, not the bare nav. */
.page-header:has(+ .page-head), .page-header:has(+ .pill-nav), .page-header:has(+ .pill-nav-wrap) { margin-bottom: var(--space-sm); }

/* Section: the grouping level between page header and card label.
   Symmetric margins so a section separates from whatever precedes it, not just
   from what follows — the first real adoption (slice 6, Survey Overview) put a
   section directly under a summary table and the heading collided with it.
   Adjacent margins collapse, so two consecutive sections still sit one
   --section-gap apart rather than two. */
.section { margin: var(--section-gap) 0; }
.section:first-child { margin-top: 0; }
.section:last-child { margin-bottom: 0; }
.section__title {
    font-size: var(--type-section);
    font-weight: var(--font-weight-emphasis);
    line-height: 1.3;
    margin: 0 0 var(--space-sm);
}
.section__lead {
    color: var(--color-text-muted);
    line-height: 1.55;
    margin: 0 0 var(--space-md);
    max-width: var(--measure-read);
}
/* Blocks inside a section separate by the card gap, so a section of cards keeps
   today's rhythm while a section of open content inherits the same beat. */
.section__body > * + * { margin-top: var(--card-gap); }

/* Content measure: reading-width caps, with an explicit full-bleed opt-out for
   content that earns the full width (wide tables, the survey builder). */
.measure { max-width: var(--measure-full); }
.measure--read { max-width: var(--measure-read); }
.full-bleed { max-width: none; }

/* Stats: figures lead, colour stays out of magnitude (§4 Status presentation). */
.stat-row { display: flex; flex-wrap: wrap; gap: var(--space-lg) var(--space-2xl); margin: 0 0 var(--space-md); }
.stat { min-width: 6rem; }
.stat__value {
    font-size: var(--type-page-title);
    font-weight: var(--font-weight-page-title);
    font-variant-numeric: tabular-nums;
    line-height: 1.15;
}
.stat__label { color: var(--color-text-muted); font-size: var(--type-support); margin-top: var(--space-xs); }
.stat__meta { color: var(--color-text-light); font-size: var(--type-micro); margin-top: 2px; }

/* Stat cards: the same figures as tiles. auto-fit means the row reflows on its own
   (4 across on a desk, 2 on a tablet, 1 on a phone) with no count baked into the CSS,
   so adding a fifth card never needs a breakpoint. */
.stat-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: var(--card-gap);
}
/* Grid gap owns the spacing here; `.card + .card` would add a second, vertical-only
   one and break the row's alignment. */
.stat-cards > .card + .card { margin-top: 0; }
/* A linked tile is a card that happens to be an <a>: no underline, no link colour,
   the whole block is the target. */
a.stat-card { display: block; color: inherit; text-decoration: none; }
a.stat-card:hover { border-color: var(--color-accent); }
a.stat-card:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }

/* The dashboard greeting: a quiet line above the figures, not a heading (the page
   title is the shell's single h1). */
.dashboard-welcome { color: var(--color-text-muted); margin: 0 0 var(--card-gap); }

/* Meter: the respondent progress idiom, platform-themed. Single hue: magnitude
   is never good/bad, so the fill never changes colour with the value. */
.meter { display: flex; align-items: center; gap: var(--space-sm); }
.meter__track {
    flex: 1;
    height: 6px;
    border-radius: 999px;
    background: var(--color-border);
    overflow: hidden;
}
.meter__value { height: 100%; border-radius: 999px; background: var(--color-accent); }
.meter__pct {
    font-variant-numeric: tabular-nums;
    font-size: var(--type-support);
    color: var(--color-text-muted);
    min-width: 3.5ch;
    text-align: right;
}

/* Phones: the header stacks, actions drop under the text (560px is the standard
   content-reflow breakpoint, design system §2.3). */
@media (max-width: 560px) {
    .page-header { flex-direction: column; gap: var(--space-sm); }
    .page-header__actions { width: 100%; }
    .stat-row { gap: var(--space-md) var(--space-lg); }
}

/* ── Methodology white paper (screen) ─────────────────────────────────────────
   The on-screen presentation of the document served by Modules/Methodology.

   These class names are shared with Core/Document/DocumentStylesheet, which styles
   the same markup for PAGED MEDIA. The duplication is deliberate and is not worth
   removing: the exported document is rendered by a worker with no web server and no
   access to this file, so it has to carry its own stylesheet, and the two have
   genuinely different jobs (a scrolling column against a bound A4 document). What
   they must share is the MARKUP, and they do, because one renderer produces it.

   Reading measure is capped rather than filling the content column. Long-form prose
   set to a 1200px container is measurably harder to read, and this is a document
   people are asked to read carefully. */

.doc-layout { display: grid; grid-template-columns: 250px minmax(0, 1fr); gap: var(--space-xl); align-items: start; }
.doc-layout__main { min-width: 0; }

/* The contents rail: the section's own navigation, so the global sidebar does not
   have to carry forty entries on every page of the product. */
.doc-rail { position: sticky; top: var(--space-md); font-size: var(--type-support); }
.doc-rail__home { display: block; font-weight: 600; margin-bottom: var(--space-sm); }
.doc-rail__chapters, .doc-rail__appendices, .doc-rail__sections { list-style: none; margin: 0; padding: 0; }
.doc-rail__chapters > li { margin-bottom: var(--space-xs); }
.doc-rail a { display: block; padding: 4px 8px; border-radius: var(--radius-sm); color: var(--color-text); text-decoration: none; }
.doc-rail a:hover { background: var(--color-accent-subtle); }
.doc-rail__chapter.is-active > a { background: var(--color-accent-wash); font-weight: 600; }
.doc-rail__num { color: var(--color-text-muted); display: inline-block; min-width: 1.4em; }
.doc-rail__sections { margin: 2px 0 var(--space-sm) var(--space-md); border-left: 1px solid var(--color-border); }
.doc-rail__sections a { color: var(--color-text-muted); font-size: var(--type-micro); }
.doc-rail__sections li.is-active > a { color: var(--color-accent); font-weight: 600; }
.doc-rail__label { margin: var(--space-md) 0 var(--space-xs); font-size: var(--type-micro); text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-text-muted); }

.doc-article { line-height: 1.65; }
.doc-article > .doc-section + .doc-section { margin-top: var(--space-xl); padding-top: var(--space-lg); border-top: 1px solid var(--color-border); }
.doc-chapter__title { font-size: var(--type-page-title); margin: 0 0 var(--space-xs); }
.doc-chapter__summary { color: var(--color-text-muted); margin: 0 0 var(--space-lg); max-width: 68ch; }
.doc-section__title { font-size: var(--type-section); margin: 0 0 var(--space-xs); }
.doc-section__number { color: var(--color-text-muted); }
.doc-subheading { font-size: var(--type-body); margin: var(--space-lg) 0 var(--space-xs); }
.doc-p, .doc-list { margin: 0 0 var(--space-sm); max-width: 68ch; }
.doc-lead { font-size: 1.0625rem; margin: 0 0 var(--space-md); max-width: 68ch; }
.doc-list { padding-left: var(--space-md); }
.doc-list li { margin-bottom: 4px; }

/* Status badges. The colour is a hint; the label carries the meaning, so the badge
   is still unambiguous in a monochrome print or to a colour-blind reader. */
.doc-status { display: flex; align-items: baseline; gap: var(--space-sm); flex-wrap: wrap; margin: 0 0 var(--space-md); font-size: var(--type-support); }
.doc-badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 999px;
    border: 1px solid var(--color-border-strong);
    background: var(--color-bg);
    font-size: var(--type-micro);
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    white-space: nowrap;
}
.doc-badge--status-implemented { background: var(--color-flash-success-bg); border-color: var(--color-flash-success-border); color: var(--color-flash-success-text); }
.doc-badge--status-approved { background: var(--color-accent-subtle); border-color: var(--color-accent-wash); color: var(--color-accent-active); }
.doc-badge--status-experimental { background: var(--color-flash-warning-bg); border-color: var(--color-flash-warning-border); color: var(--color-flash-warning-text); }
.doc-badge--status-deprecated { background: var(--color-flash-error-bg); border-color: var(--color-flash-error-border); color: var(--color-flash-error-text); }
.doc-status__explained { color: var(--color-text-muted); }

.doc-formula { margin: var(--space-md) 0; padding: var(--space-sm) var(--space-md); background: var(--color-bg); border-left: 2px solid var(--color-accent); border-radius: 0 var(--radius-sm) var(--radius-sm) 0; overflow-x: auto; }
.doc-formula__expr { font-size: 1.0625rem; white-space: nowrap; }
.doc-formula__where { display: grid; grid-template-columns: minmax(6ch, 16ch) 1fr; gap: 4px var(--space-md); margin: var(--space-sm) 0 0; font-size: var(--type-support); color: var(--color-text-muted); }
.doc-formula__where dt { font-weight: 700; color: var(--color-text); margin: 0; }
.doc-formula__where dd { margin: 0; }
.doc-formula figcaption { margin-top: var(--space-xs); font-size: var(--type-support); color: var(--color-text-muted); }

.doc-example { margin: var(--space-md) 0; padding: var(--space-md); border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-surface); }
.doc-example__title { margin: 0 0 var(--space-sm); font-size: var(--type-card-label); }
.doc-example__given { display: grid; grid-template-columns: minmax(12ch, 28ch) 1fr; gap: 4px var(--space-md); margin: 0 0 var(--space-sm); font-size: var(--type-support); }
.doc-example__given dt { color: var(--color-text-muted); margin: 0; }
.doc-example__given dd { margin: 0; font-weight: 700; font-variant-numeric: tabular-nums; }

.doc-callout { margin: var(--space-md) 0; padding: var(--space-sm) var(--space-md); border-left: 2px solid var(--color-border-strong); background: var(--color-bg); border-radius: 0 var(--radius-sm) var(--radius-sm) 0; max-width: 72ch; }
.doc-callout p { margin: 2px 0 0; }
.doc-callout__label { font-size: var(--type-micro); font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--color-text-muted); }
.doc-callout--note { border-left-color: var(--color-accent); background: var(--color-accent-subtle); }
.doc-callout--warning { border-left-color: var(--color-danger); background: var(--color-flash-error-bg); }
.doc-callout--warning .doc-callout__label { color: var(--color-flash-error-text); }
.doc-callout--limit, .doc-callout--translation { border-left-color: var(--color-flash-warning-text); background: var(--color-flash-warning-bg); }
.doc-callout--limit .doc-callout__label, .doc-callout--translation .doc-callout__label { color: var(--color-flash-warning-text); }

/* Wide tables scroll inside their own container: the page body must never scroll
   horizontally, and several of these tables have six columns. */
.doc-table-wrap { margin: var(--space-md) 0; overflow-x: auto; }
.doc-table { width: 100%; border-collapse: collapse; font-size: var(--type-support); }
.doc-table caption { text-align: left; font-size: var(--type-support); color: var(--color-text-muted); padding-bottom: var(--space-xs); }
.doc-table th, .doc-table td { border: 1px solid var(--color-border); padding: 6px 10px; text-align: left; vertical-align: top; }
.doc-table thead th { background: var(--color-bg); font-size: var(--type-micro); text-transform: uppercase; letter-spacing: 0.03em; color: var(--color-text-muted); white-space: nowrap; }
.doc-table tbody th { font-weight: 700; }
.doc-table td { font-variant-numeric: tabular-nums; }
.doc-table__note { margin: var(--space-xs) 0 0; font-size: var(--type-support); color: var(--color-text-muted); }

.doc-decision { margin: var(--space-lg) 0; padding: var(--space-md); border: 1px solid var(--color-border-strong); border-radius: var(--radius-md); }
.doc-decision__title { margin: 0 0 4px; font-size: var(--type-micro); text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-text-muted); }
.doc-decision__topic { margin: 0 0 var(--space-sm); font-weight: 700; }
.doc-decision__table td:last-child { font-variant-numeric: normal; }
.doc-verdict { font-size: var(--type-micro); font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; white-space: nowrap; }
.doc-verdict--chosen { color: var(--color-flash-success-text); }
.doc-verdict--rejected { color: var(--color-danger); }
.doc-verdict--considered { color: var(--color-text-muted); }
.doc-decision__row--chosen th { background: var(--color-flash-success-bg); }
.doc-decision__rationale { margin: var(--space-sm) 0 0; max-width: 68ch; }
.doc-decision__meta { margin: var(--space-xs) 0 0; font-size: var(--type-support); color: var(--color-text-muted); }

.doc-meta { margin: var(--space-md) 0 0; padding: var(--space-sm) 0 0; border-top: 1px solid var(--color-border); font-size: var(--type-support); }
.doc-meta__title { margin: 0 0 var(--space-xs); font-size: var(--type-micro); text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-text-muted); }
.doc-meta dl { display: grid; grid-template-columns: minmax(10ch, 22ch) 1fr; gap: 4px var(--space-md); margin: 0; }
.doc-meta dt { color: var(--color-text-muted); margin: 0; }
.doc-meta dd { margin: 0; }
.doc-paths { list-style: none; margin: 0; padding: 0; }
.doc-paths code { font-size: var(--type-micro); word-break: break-all; }
.doc-gap { color: var(--color-flash-warning-text); }

.doc-figure { margin: var(--space-lg) 0; }
.doc-figure svg { max-width: 100%; height: auto; display: block; }
.doc-figure figcaption { margin-top: var(--space-xs); font-size: var(--type-support); color: var(--color-text-muted); }

.doc-references { list-style: none; margin: var(--space-sm) 0; padding: 0; font-size: var(--type-support); }
.doc-reference { margin-bottom: var(--space-xs); padding-left: var(--space-md); text-indent: calc(-1 * var(--space-md)); }
.doc-reference__title { font-style: italic; }
.doc-reference__note { color: var(--color-text-muted); }
.doc-glossary { margin: 0; }
.doc-glossary dt { font-weight: 700; margin-top: var(--space-sm); }
.doc-glossary dd { margin: 2px 0 0; max-width: 68ch; }
.doc-glossary__see { color: var(--color-text-muted); font-size: var(--type-support); }

/* Overview page */
.doc-summary { display: flex; flex-wrap: wrap; gap: var(--space-lg); align-items: center; }
.doc-summary__versions { display: grid; grid-template-columns: auto auto; gap: 2px var(--space-sm); margin: 0; font-size: var(--type-support); }
.doc-summary__versions dt { color: var(--color-text-muted); margin: 0; }
.doc-summary__versions dd { margin: 0; font-weight: 700; }
.doc-summary__note { flex: 1 1 32ch; margin: 0; font-size: var(--type-support); color: var(--color-text-muted); }
.doc-status-summary { list-style: none; display: flex; flex-wrap: wrap; gap: var(--space-sm); margin: 0; padding: 0; font-variant-numeric: tabular-nums; }
.doc-coverage__title, .doc-chapter-card__title { font-size: var(--type-section); margin: 0 0 var(--space-xs); }
.doc-chapter-card__summary { color: var(--color-text-muted); margin: 0 0 var(--space-sm); max-width: 68ch; }
.doc-chapter-card__sections { list-style: none; margin: 0; padding: 0; font-size: var(--type-support); }
.doc-chapter-card__sections li { display: flex; align-items: baseline; gap: var(--space-sm); padding: 3px 0; flex-wrap: wrap; }
.doc-chapter-card__num { color: var(--color-text-muted); font-variant-numeric: tabular-nums; }
.doc-capability { font-size: var(--type-support); color: var(--color-text-muted); border: 1px dashed var(--color-border-strong); border-radius: var(--radius-sm); padding: 6px 10px; }

/* The rail folds above the content on narrow viewports rather than being hidden:
   on a phone it is the only way to move between chapters. */
@media (max-width: 900px) {
    .doc-layout { grid-template-columns: minmax(0, 1fr); gap: var(--space-md); }
    .doc-rail { position: static; border-bottom: 1px solid var(--color-border); padding-bottom: var(--space-sm); }
    .doc-rail__chapters { display: flex; flex-wrap: wrap; gap: 4px; }
    .doc-rail__sections { display: none; }
}
