/* E-Dani Pro Suite | Unified Component Standards (v2.0)
   Single source of truth for buttons, forms, and core UI components.
*/

:root {
    /* Color Palette */
    --primary-color: #001f54; /* Navy Blue - Corporate */
    --accent-color: #fd7e14; /* Orange - Action/Focus */
    --success-color: #28a745; /* Green - Success (IW green, adopted suite-wide 2026-06-21) */
    --danger-color: #dc3545; /* Red - Delete/Error (IW red, adopted suite-wide 2026-06-21) */
    --warning-color: #f59e0b; /* Yellow - Warning (unchanged; owner said red/green) */
    --info-color: #3b82f6;    /* Blue - Info/Planned/Future */
    
    /* Surface Colors */
    --bg-color: #f8fafc;
    --surface-color: #ffffff;
    --text-color: #0f172a;
    --text-muted: #64748b;
    --border-color: rgba(0, 0, 0, 0.08);
    
    /* Layout Constants */
    --border-radius-lg: 24px;
    --border-radius-md: 12px;
    --border-radius-sm: 8px;
    --spacing-xs: 4px;
    --spacing-sm: 8px;
    --spacing-md: 16px;
    --spacing-lg: 24px;
    --spacing-xl: 32px;

/* ==== SUITE SPACING SCALE (TASK-023, 2026-07-26) ====================================
   THE SCALE THAT ACTUALLY DESCRIBES THE SUITE. The --spacing-xs..xl set above declares a
   4/8/16/24/32 scale, but a measurement of every spacing declaration in shared/ found that
   only 27% of values land on it: the real, de-facto scale is 2px-based. The most-used values
   are 12px (30x), 8px (23x), 10px (23x), 4px (15x), 15px (14x), 6px (13x), 2px and 20px
   (12x each), 16px (11x). A standard nobody can inherit from is why every page ended up
   hand-edited for mobile — you cannot inherit from a scale that does not match reality.

   These tokens were introduced by a BYTE-EQUIVALENT codemod: each px literal was replaced by
   a token holding the IDENTICAL value, never rounded to a "nicer" one. Proof of no-op:
   resolving every var() back to px reproduces the previous file byte-for-byte (see
   tests/suite/unit/spacing_scale_integrity.unit.js).

   OFF-GRID VALUES ARE DELIBERATELY STILL LITERALS — 15px (14x), 14px (12x), 18px, 5px, 3px,
   25px, 30px and friends. Snapping them onto the grid would silently change appearance across
   every page in the suite, which this change must not do. They are the backlog of real design
   decisions, to be made deliberately and visually, not by a regex.

   The --spacing-* set above is KEPT and untouched: 13 existing declarations still use it, and
   its values (4/8/16/24) all exist in this scale anyway. Do not "unify" the two by rewriting
   the old names — that would change those 13 computed values. */
    --space-3xs: 2px;
    --space-2xs: 4px;
    --space-xs:  6px;
    --space-sm:  8px;
    --space-md:  10px;
    --space-lg:  12px;
    --space-xl:  16px;
    --space-2xl: 20px;
    --space-3xl: 24px;

    --radius-xs:  4px;
    --radius-sm:  6px;
    --radius-md:  8px;
    --radius-lg:  12px;
    --radius-xl:  16px;
    --radius-2xl: 20px;
    --radius-3xl: 24px;
}

/* ==== RESPONSIVE DENSITY TIERS (TASK-023 Phase 4, 2026-08-05) =======================
   MOBILE SPACING IS NOW AUTOMATIC. This is the piece Phases 1-3 built the machinery for
   and never wired up.

   Owner, 2026-08-05: "Unify the responsive design for the suite. Mobile and tablet is very
   important. Shared design! Mobile padding must be much less than desktop otherwise we waste
   space." That is the answer to the question TASK-023 sat on as needs-owner since 2026-07-26
   ("review the compact values before wider rollout").

   WHY A MEDIA QUERY AND NOT THE .density-compact CLASS. Phase 2 deliberately made density a
   CLASS, reasoning that density and viewport are different questions — true for a DESKTOP
   dense screen like IW 1:1, and that opt-in stays below. But it left mobile with nothing by
   default, and ten days later the class had ZERO adopters in the whole suite. A standard that
   every page must opt into is a standard every page forgets. Viewport-driven density needs no
   adopter: it is simply true.

   HOW IT REACHES EVERYTHING WITHOUT A CASCADE FIGHT. This is a VALUE change, not a selector
   fight, so app `!important` rules are irrelevant to it — every declaration in the suite that
   already reads var(--space-*) / var(--spacing-*) tightens at once, in all seven apps, with no
   per-app edit. suite_components.css :root is the SOLE declarer of these 24 tokens (verified
   2026-08-05: no app CSS and no later-loaded shared CSS redeclares any of them), so these
   blocks are unopposed. The rules that do NOT move are those written as literal px — they are
   enumerated and handled per app, and are the reason a token tier alone was never going to be
   the whole job.

   DESKTOP IS UNTOUCHED, BY CONSTRUCTION. Neither block carries min-width, so nothing at
   >=1025px can resolve differently for any token, in any app. The owner said desktop is
   finished; this cannot reach it.

   THE VALUES ARE NOT INVENTED — THEY ARE IW's. The ladder is .density-compact's, verbatim, whose
   docblock records that it reproduces the hand-measured spacing of IW 1:1 — the screen the owner
   designed himself and holds up as the mobile target. Confirmed independently on 2026-08-05 by
   measuring IW's inline styles (which win the cascade, so they are what renders): dominant padding
   7px 10px, dominant gaps 8px and 10px. This tier resolves --space-sm/--space-lg to 6px/10px and
   --space-md/--space-lg to 8px/10px — inside IW's band on both axes.

   PLACEMENT IS A SILENT TRAP. This block must stay BELOW the base :root (line 5) and ABOVE
   .density-compact. Above the base :root, spacing_scale_integrity.unit.js validates the MOBILE set
   as the desktop scale and goes red (measured: 8 failures). Below .density-compact, a tier :root
   would out-rank `class="density-compact"` on source order at equal specificity and silently
   disable the opt-in. Neither mistake produces an error — only wrong pixels.

   RADIUS IS DELIBERATELY NOT STEPPED. The owner asked for less wasted space; a corner radius
   costs none. Stepping it would change the look for no gain in room.

   What the owner actually sees: .card padding 24px on desktop -> 12px on tablet AND phone, and
   the same step on every shared button, form field, modal and table in all seven apps. */
@media (max-width: 1024px) {
    :root {
        --space-3xs: 2px;
        --space-2xs: 2px;
        --space-xs:  4px;
        --space-sm:  6px;
        --space-md:  8px;
        --space-lg:  10px;
        --space-xl:  12px;
        --space-2xl: 16px;
        --space-3xl: 20px;

        --spacing-xs: 4px;
        --spacing-sm: 6px;
        --spacing-md: 12px;
        --spacing-lg: 20px;
        --spacing-xl: 24px;
    }
}

/* NO SECOND (PHONE) TIER — deliberate, and measured. An earlier draft stepped a second time at
   <=600px (md 6 / lg 8 / xl 10). The owner then specified the target directly: "on mobile i want
   the gaps to be like in IW" (2026-08-05).

   So IW's REAL values were measured rather than assumed — its inline styles, which win the cascade
   and are therefore what actually renders:
       padding  7px 10px (45x) · 8px 12px (27x) · 8px 10px (27x) · 6px 8px (20x) · 6px 10px (18x)
       gap      8px (56x) · 10px (39x) · 6px (24x) · 4px (20x) · 12px (14x)
   IW's band is vertical 5-8px, horizontal 8-12px, gaps 6-10px. The ONE tier above lands inside it
   exactly (sm/lg = 6px 10px; gaps md/lg = 8px/10px — IW's two most-used gaps). The second step
   fell BELOW the band (gaps 6/8, padding 4px 8px), i.e. tighter than the screen the owner is
   holding up as the target. Being denser than the reference is not "like IW".

   One tier is therefore the correct expression of the directive, not a simplification of it: IW's
   density is a single band, so one band models it. Phone and tablet share it. */

/* ==== DENSITY MODE (TASK-023) =======================================================
   HOW TO MAKE A DENSE SCREEN FIT A PHONE WITHOUT HAND-EDITING ITS SPACING.

   Put `class="density-compact"` on any container. Every shared component inside it — cards,
   buttons, form fields, modals, tables — tightens by one step on the scale, because custom
   properties INHERIT and the components consume the tokens. That is the whole mechanism: no
   per-page CSS, no !important, no duplicated media queries. It composes with any layout.

   ⚠️ A NO-OP BELOW 1024px SINCE TASK-023 Phase 4 (2026-08-05). The mobile tier above now carries
   exactly these values, so adding this class to a container changes nothing on a phone or tablet;
   it still does its full job on DESKTOP, which is the case it was built for (IW 1:1 is dense on a
   desktop too — that is why Phase 2 made density a class rather than a media query, and that
   reasoning still stands). Harmless, so left as-is. (.density-comfortable needed a real fix, not
   just a note: as 24 unconditional DESKTOP constants it would have re-inflated a mobile subtree
   all the way back to desktop spacing. See below.)

   WHY A CLASS AND NOT A MEDIA QUERY. Density and viewport are different questions. IW's 1:1
   coaching screen is dense on a DESKTOP too — that density is the design the owner wants, and
   binding compact to `max-width:1024px` would throw it away above 1024px while giving desktop
   dense screens no way to opt in. A class lets a page choose, at any viewport; a page that
   wants "compact only on phones" nests it in its own media query.

   THE VALUES ARE NOT INVENTED. They are one step down this scale, which reproduces the
   measured spacing of IW 1:1 — the screen that was designed by hand and is the reference for
   "good on mobile". Its dominant padding is 7px 10px; compact md/lg here are 8px/10px. Its
   radii are 3px and 6px; compact xs/md are 3px and 6px. So adopting compact gets a page to
   roughly where 1:1 already is, without copying 1:1's inline styles.

   KNOWN LIMIT, do not be surprised by it: this reaches only spacing expressed through the
   tokens. It CANNOT touch (a) inline `style="padding:..."` attributes — IW 1:1's own controls
   are still inline-styled, so 1:1 itself will not visibly change until those are removed, and
   (b) any local rule using `!important`, which still beats a tokenised shared rule regardless
   of tokens (ideenwerkstatt.css has 374 such declarations, helferwerkstatt.css 345). Those two
   are TASK-008's removal program, NOT something tokens fix. */
.density-compact {
    --space-3xs: 2px;   /* floor: 2px is already the tightest meaningful gap */
    --space-2xs: 2px;
    --space-xs:  4px;
    --space-sm:  6px;
    --space-md:  8px;
    --space-lg:  10px;
    --space-xl:  12px;
    --space-2xl: 16px;
    --space-3xl: 20px;

    --radius-xs:  3px;
    --radius-sm:  4px;
    --radius-md:  6px;
    --radius-lg:  8px;
    --radius-xl:  12px;
    --radius-2xl: 16px;
    --radius-3xl: 20px;

    /* The LEGACY --spacing- and --border-radius- names must be stepped down too, or density
       reaches only part of the suite. 13 shared declarations still consume these — notably
       `.card { padding: var(--spacing-lg) }` — and they are deliberately kept (see the scale
       comment above: rewriting their values would change those 13 computed results globally).
       Overriding them HERE is safe precisely because it is scoped to a compact container. */
    --spacing-xs: 4px;   /* 4  -> 4  (floor) */
    --spacing-sm: 6px;   /* 8  -> 6  */
    --spacing-md: 12px;  /* 16 -> 12 */
    --spacing-lg: 20px;  /* 24 -> 20 */
    --spacing-xl: 24px;  /* 32 -> 24 */

    --border-radius-sm: 6px;   /* 8  -> 6  */
    --border-radius-md: 8px;   /* 12 -> 8  */
    --border-radius-lg: 20px;  /* 24 -> 20 */
}

/* Escape hatch: restore full spacing for a subtree inside a compact container (e.g. a modal
   that should stay comfortable even though the page behind it is dense). */
.density-comfortable {
    --space-3xs: 2px;
    --space-2xs: 4px;
    --space-xs:  6px;
    --space-sm:  8px;
    --space-md:  10px;
    --space-lg:  12px;
    --space-xl:  16px;
    --space-2xl: 20px;
    --space-3xl: 24px;

    --radius-xs:  4px;
    --radius-sm:  6px;
    --radius-md:  8px;
    --radius-lg:  12px;
    --radius-xl:  16px;
    --radius-2xl: 20px;
    --radius-3xl: 24px;

    --spacing-xs: 4px;
    --spacing-sm: 8px;
    --spacing-md: 16px;
    --spacing-lg: 24px;
    --spacing-xl: 32px;

    --border-radius-sm: 8px;
    --border-radius-md: 12px;
    --border-radius-lg: 24px;
}

/* ==== .density-comfortable MUST FOLLOW THE TIER (TASK-023 Phase 4, 2026-08-05) ======
   The block above is 24 UNCONDITIONAL DESKTOP CONSTANTS. That was correct while :root had a
   single value set — but the moment :root became tier-aware above, this escape hatch became a
   trap: a class on a container beats an inherited custom property unconditionally, so ANY
   subtree carrying .density-comfortable would jump straight back to DESKTOP spacing on a phone
   (--spacing-lg 16 -> 24, --space-md 6 -> 10). That is the exact opposite of the directive that
   motivated the tier.

   THE RULE: .density-comfortable mirrors ITS TIER's :root. It means "undo a .density-compact
   ancestor", NOT "become a desktop". So outside a compact subtree it is a no-op at every
   viewport — which is precisely what its docblock above says it is for. Note it must NOT be
   "one step up from the tier": that would make an un-nested comfortable INFLATE past the tier
   and reintroduce a milder version of this same bug.

   Latent, not live — .density-comfortable currently has zero adopters suite-wide. This is
   prevention, and it is cheaper here than as a bug report from the owner's phone later.

   Radii are repeated at their desktop values on purpose: the tier does not step radius, so
   these are no-ops, but writing all 24 tokens in every block is what keeps the set complete
   and reviewable (4 blocks x 24 tokens = 96 declarations). */
@media (max-width: 1024px) {
    .density-comfortable {
        --space-3xs: 2px;  --space-2xs: 2px;  --space-xs:  4px;
        --space-sm:  6px;  --space-md:  8px;  --space-lg:  10px;
        --space-xl:  12px; --space-2xl: 16px; --space-3xl: 20px;

        --radius-xs:  4px;  --radius-sm:  6px;  --radius-md:  8px;  --radius-lg: 12px;
        --radius-xl:  16px; --radius-2xl: 20px; --radius-3xl: 24px;

        --spacing-xs: 4px; --spacing-sm: 6px; --spacing-md: 12px;
        --spacing-lg: 20px; --spacing-xl: 24px;

        --border-radius-sm: 8px; --border-radius-md: 12px; --border-radius-lg: 24px;
    }
}

/* (No <=600px counterpart — there is only one mobile tier. See the note above the tier blocks.) */

/* :root resumes here — the density classes above had to sit OUTSIDE :root (they are ordinary
   selectors, not the root scope), so the original :root block is closed above and reopened
   here. Everything below continues exactly as before; this is purely where the block was cut. */
:root {
/* ==== END SUITE SPACING SCALE ==== */

    /* Motion — the suite-standard transition timings. Use var(--transition-base) etc.
       instead of hardcoding, so every app animates the same way. */
    --transition-fast: 0.15s ease;
    --transition-base: 0.2s cubic-bezier(0.4, 0, 0.2, 1);
    --transition-slow: 0.3s cubic-bezier(0.4, 0, 0.2, 1);

    /* RESPONSIVE — the ONE canonical suite breakpoint set. Every app must use ONLY these
       (today they diverge: 1000/1200/1400/640/720/800/900/1300/1600/1800 etc. — all to be
       migrated). 1024 = sidebar→bottom-nav (mobile shell) · 768 = tablet · 600 = phone ·
       480 = small phone. (CSS can't tokenize @media values; these are the agreed standard.) */
}

/* ==========================================================================
   DRIVE SELECTOR DROPDOWN (Top Bar) — Light Green Accent
   Drive selector dropdown in top bar for multi-root ownCloud folder selection.
   Applies to the select element that shows available drive roots.
   ========================================================================== */

.sf-drive-select {
    /* Dropdown menu background — light green accent */
    background-color: #e8f5e9 !important;
    color: #1b5e20 !important;
}

/* Base Button Properties */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-sm);
    font-family: inherit;
    font-weight: 700;
    font-size: 14px;
    border: none;
    cursor: pointer;
    transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
    white-space: nowrap;
    text-decoration: none;
}

.btn:active {
    transform: scale(0.98);
}

.btn:disabled, .btn.disabled {
    opacity: 0.6;
    cursor: not-allowed;
    pointer-events: none;
}

/* Primary Button (Standard Action) */
.btn-primary {
    background: var(--primary-color);
    color: white;
    padding: var(--space-lg) var(--space-3xl);
    border-radius: var(--border-radius-sm);
    height: 40px;
    box-shadow: 0 4px 6px -1px rgba(0, 31, 84, 0.1);
}

.btn-primary:hover {
    background: #001538;
    box-shadow: 0 6px 10px -2px rgba(0, 31, 84, 0.2);
}

/* Secondary Button (Alternative Action) */
.btn-secondary {
    background: rgba(0, 31, 84, 0.05);
    color: var(--primary-color);
    padding: var(--space-lg) var(--space-3xl);
    border-radius: var(--border-radius-sm);
    height: 40px;
}

.btn-secondary:hover {
    background: rgba(0, 31, 84, 0.1);
}

/* Success Button (Save/Confirm) */
.btn-success {
    background: var(--success-color) !important;
    color: white !important;
    padding: var(--space-lg) var(--space-3xl);
    border-radius: var(--border-radius-sm);
    height: 40px;
    border: none !important;
}

.btn-success:hover {
    background: #218838 !important;  /* darker IW green */
}

/* Danger Button (Delete/Cancel) */
.btn-danger {
    background: var(--danger-color);
    color: white;
    padding: var(--space-lg) var(--space-3xl);
    border-radius: var(--border-radius-sm);
    height: 40px;
}

.btn-danger:hover {
    background: #c82333;  /* darker IW red */
}

/* Small Button Variant */
.btn-sm {
    padding: var(--space-xs) var(--space-lg);
    font-size: 12px;
    height: 32px;
    border-radius: var(--radius-sm);
}

/* ==========================================================================
   FORM COMPONENTS (GOLD STANDARD)
   ========================================================================== */

.form-group {
    display: flex;
    flex-direction: column;
    gap: var(--space-xs);
    margin-bottom: var(--spacing-md);
}

.form-group label {
    /* Gold-standard form label (uppercase-muted) — the pattern Schule, Chat, HW and
       IW all independently adopted. Promoted to shared so new apps + the skeleton get
       it for free and the per-app copies become removable. Existing apps that override
       .form-group label are unaffected (their rule wins by load order). */
    font-size: 11px;
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.form-input, .form-select, .form-textarea {
    width: 100%;
    padding: 0 var(--space-xl);
    background: white;
    border: 1px solid var(--border-color);
    border-radius: var(--border-radius-sm);
    font-family: inherit;
    font-size: 14px;
    color: var(--text-color);
    transition: all 0.2s;
}

.form-input, .form-select {
    height: 40px;
}

.form-textarea {
    padding: var(--space-lg) var(--space-xl);
    min-height: 100px;
    resize: vertical;
}

.form-input:focus, .form-select:focus, .form-textarea:focus {
    outline: none;
    border-color: var(--accent-color);
    box-shadow: 0 0 0 3px rgba(253, 126, 20, 0.1);
}

/* Readonly/Disabled State */
.form-input:disabled, .form-input[readonly] {
    background: #f1f5f9;
    color: var(--text-muted);
    cursor: not-allowed;
    border-color: #e2e8f0;
}

/* RAW-ELEMENT BASELINE (P1) — so a NEW app (or the skeleton) gets the gold-standard
   control look WITHOUT writing a single global rule, killing the reason each app grew
   its own `select{…!important}` ruleset. Deliberately NO width here (leaving layout to
   the app / .form-* classes) so this stays near-inert in existing apps: their own
   select/input rules load later and win; any non-conflicting prop that leaks is a
   harmless move toward the shared standard. P1b removes the per-app global rules
   one app at a time, verified in the real app. */
select,
textarea,
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]):not([type="submit"]):not([type="button"]):not([type="image"]) {
    height: 40px;
    padding: 0 var(--space-xl);
    background: white;
    border: 1px solid var(--border-color);
    border-radius: var(--border-radius-sm);
    font-family: inherit;
    font-size: 14px;
    color: var(--text-color);
    transition: all 0.2s;
}
textarea {
    height: auto;
    min-height: 100px;
    padding: var(--space-lg) var(--space-xl);
    resize: vertical;
}
select:focus,
textarea:focus,
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]):not([type="submit"]):not([type="button"]):not([type="image"]):focus {
    outline: none;
    border-color: var(--accent-color);
    box-shadow: 0 0 0 3px rgba(253, 126, 20, 0.1);
}

/* iOS auto-zoom prevention (from IW): fields <16px make Safari zoom on focus.
   Mobile-only so it never disturbs desktop. Apps with their own rule still win. */
@media (max-width: 768px) {
    select,
    textarea,
    input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]):not([type="submit"]):not([type="button"]):not([type="image"]) {
        font-size: 16px;
    }
}

/* ==========================================================================
   SEARCH INPUT (GOLD STANDARD — promoted from IW's Buchhaltung filter bar,
   2026-07-24, TASK-011 slice 1). Icon-prefixed search box — opt-in, additive,
   no existing selector touched. Usage:
     <div class="search-input-wrap">
       <input class="form-input" type="text" placeholder="Suche...">
     </div>
   ========================================================================== */
.search-input-wrap { position: relative; }
.search-input-wrap input { padding-left: 34px !important; }
.search-input-wrap::before {
    content: '🔍';
    position: absolute;
    left: 11px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 13px;
    opacity: .55;
    pointer-events: none;
}

/* ==========================================================================
   MIC INPUT BUTTON (dictation feature, 2026-08-21). A SINGLE floating button,
   shown/positioned by shared/suite_mic_input.js over the currently-focused
   eligible text field via getBoundingClientRect() + position:fixed —
   deliberately NOT a wrapper div around the field (wrapping every field the
   suite has would risk breaking existing flex/grid layouts that expect the
   field as a direct child). No markup change on any field, anywhere. Round
   icon button modeled on .suite-notification-close.
   ========================================================================== */
.mic-input-btn {
    position: fixed;
    background: #fff;
    border: none;
    color: #94a3b8;
    cursor: pointer;
    font-size: 15px;
    line-height: 1;
    padding: 0;
    width: 26px;
    height: 26px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    transition: background 0.15s, color 0.15s;
    /* 2026-08-21, found in review: a field is very often INSIDE a modal (admin.php/index.php
       edit dialogs), and the suite's modal layer sits at z-index 50000-100000 (see
       shared/suite_modals.css and suite_ai_panel.js's own z-index:50010 comment). z-index:40
       would render the button BEHIND every modal — present in the DOM, invisible, unclickable.
       150000 clears the highest modal layer (100000) with room, and stays well below the toast
       host (#toast-container, z-index 2000000) so an error toast is never hidden by this button. */
    z-index: 150000;
    box-shadow: 0 1px 3px rgba(0,0,0,0.12);
}
.mic-input-btn:hover { background: #f1f5f9; color: #334155; }

.mic-input-btn.recording {
    color: #dc2626;
    animation: mic-input-pulse 1.2s ease-in-out infinite;
}
.mic-input-btn.transcribing {
    color: #94a3b8;
    cursor: default;
    pointer-events: none;
    animation: mic-input-spin 0.8s linear infinite;
}
@keyframes mic-input-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.4; }
}
@keyframes mic-input-spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

/* Live dictation preview bubble (BUG-1118, 2026-08-26) — a rough, rolling draft shown ABOVE
   the mic button while recording, never inside the field itself (shared/suite_mic_input.js's
   showPreview()/hidePreview()). Same z-index as the button itself — it only ever appears
   while the button is visible and recording, so it must clear the same modal layer. Styled
   as an obviously-not-final scratchpad (italic, muted) so it reads as a draft, not a
   commitment — the accurate text only lands in the field once recording stops. */
.mic-input-preview {
    position: fixed;
    z-index: 150000;
    background: #1e293b;
    color: #cbd5e1;
    font-style: italic;
    font-size: 12px;
    line-height: 1.4;
    padding: var(--space-sm) var(--space-md);
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.25);
    pointer-events: none;
    word-wrap: break-word;
}

/* ==========================================================================
   STATUS PILLS & BADGES (GOLD STANDARD — promoted from IW)
   Pill-shaped status tags + count badges. IW keeps its own .status-pill by
   load order; this is the shared standard for every other app + new apps.
   Use: <span class="status-pill ok">Aktiv</span> · <span class="badge info">3</span>
   ========================================================================== */

.status-pill, .badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-xs);
    padding: var(--space-2xs) var(--space-lg);
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
    line-height: 1.4;
    white-space: nowrap;
    border: 1px solid transparent;
}

/* Tones — colour set matched to IW's status palette + the suite toast tones */
.status-pill.ok,      .badge.ok      { background: #dcfce7; color: #14532d; border-color: #bbf7d0; }
.status-pill.info,    .badge.info    { background: #eff6ff; color: #0056b3; border-color: #dbeafe; }
.status-pill.warning, .badge.warning { background: #fff7ed; color: #c2410c; border-color: #ffedd5; }
.status-pill.danger,  .badge.danger  { background: #fee2e2; color: #991b1b; border-color: #fecaca; }
.status-pill.neutral, .badge.neutral { background: #f1f5f9; color: #475569; border-color: #e2e8f0; }

/* Solid count badge (e.g. unread / checklist totals — IW .gc-chk-total pattern) */
.badge-count {
    display: inline-block;
    background: var(--danger-color);
    color: #fff;
    border-radius: 999px;
    padding: 1px var(--space-sm);
    font-size: 11px;
    font-weight: 700;
    line-height: 1.5;
}
.badge-count.full { background: var(--success-color); }

/* Section header (visual consolidation #5) — the suite-standard page/section title row:
   title (left) + optional view-toggle group + primary action (right). Additive/opt-in — apps
   adopt with: <div class="section-header"><h2>Titel</h2><div class="section-header-actions">…</div></div>
   Anchored on Schule's .page-section h2 (24px/800) + the IW/Schule "title left, actions right" layout,
   so future views get a consistent header for free instead of re-inventing h1/h2/h3 per app. */
.section-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-lg);
    margin-bottom: var(--space-2xl);
}
.section-header > h2 {
    font-size: 24px;
    font-weight: 800;
    color: var(--primary-color);
    margin: 0;
    letter-spacing: -0.01em;
}
.section-header-actions {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    flex-wrap: wrap;
}

/* NOTE: a shared `.table` standard is deliberately NOT added here. Apps (esp. HW)
   style tables via bare `th/td` element rules + the `.table` class, so a scoped
   `.table th` would OVERRIDE their real tables (higher specificity) — i.e. it
   changes existing apps, which is P2 (per-app, verified), not inert additive work.
   Close gap D when migrating each app's tables onto the shared standard. */

/* ==========================================================================
   CARD COMPONENTS (GOLD STANDARD)
   ========================================================================== */

.card {
    background: rgba(255, 255, 255, 0.85);
    border-radius: var(--border-radius-lg);
    box-shadow: 0 10px 30px -5px rgba(0, 0, 0, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.4);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    padding: var(--spacing-lg);
    display: flex;
    flex-direction: column;
    gap: var(--spacing-md);
}

.card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding-bottom: var(--spacing-md);
    border-bottom: 1px solid var(--border-color);
}

.card-header h2, .card-header h3 {
    margin: 0;
    color: var(--primary-color);
    font-weight: 800;
}

.card-body {
    /* Contains the main card content */
    flex: 1;
}

/* ==========================================================================
   DRAG & DROP UPLOADER (GOLD STANDARD)
   Shared dropzone so apps stop reinventing it (HW `.drag-over` on #filesView,
   IW inline `.iw-col-drop.drag-over`). The upload target stays app-specific;
   only the UI + drag states are shared. Toggle `.drag-over` from dragenter/
   dragleave/drop handlers.
   ========================================================================== */

.dropzone {
    border: 2px dashed rgba(0, 0, 0, 0.18);
    border-radius: var(--border-radius-md);
    background: #f8fafc;
    padding: 28px var(--space-2xl);
    text-align: center;
    color: var(--text-muted);
    cursor: pointer;
    transition: border-color 0.2s, background 0.2s, color 0.2s;
}
.dropzone:hover {
    border-color: var(--accent-color);
    color: var(--text-color);
}
.dropzone.drag-over {
    border-color: var(--accent-color);
    background: rgba(253, 126, 20, 0.08);
    color: var(--accent-color);
}
.dropzone-icon { font-size: 32px; display: block; margin-bottom: var(--space-sm); opacity: 0.8; }
.dropzone-title { font-weight: 700; color: var(--text-color); }
.dropzone-hint { font-size: 13px; margin-top: var(--space-2xs); }
.dropzone input[type="file"] { display: none; }

/* List of picked/dropped files (shared look) */
.dropzone-files { list-style: none; margin: var(--space-lg) 0 0; padding: 0; text-align: left; }
.dropzone-files li {
    display: flex; align-items: center; gap: var(--space-sm);
    padding: var(--space-xs) var(--space-md); margin-bottom: var(--space-xs);
    background: white; border: 1px solid var(--border-color);
    border-radius: var(--border-radius-sm); font-size: 13px; color: var(--text-color);
}

/* ==========================================================================
   TOAST NOTIFICATIONS — brief auto-dismiss feedback messages
   ========================================================================== */

#toast-container {
    position: fixed;
    bottom: 30px;
    right: 30px;
    display: flex;
    flex-direction: column;
    gap: var(--space-md);
    z-index: 2000000;
    pointer-events: none;
}

.toast {
    pointer-events: auto;
    min-width: 220px;
    max-width: 360px;
    padding: var(--space-lg) 18px;
    background: rgba(248, 250, 252, 0.72);
    backdrop-filter: blur(18px) saturate(160%);
    -webkit-backdrop-filter: blur(18px) saturate(160%);
    border: 1px solid rgba(15, 23, 42, 0.08);
    border-radius: var(--radius-xl);
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.10);
    color: #1e293b;
    font-size: 14px;
    font-weight: 500;
    display: flex;
    align-items: center;
    gap: var(--space-md);
    animation: toast-slide-in 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
    transition: box-shadow 0.2s ease, transform 0.2s ease;
}

.toast-icon {
    font-size: 15px;
    font-weight: 700;
    flex-shrink: 0;
    opacity: 0.65;
}

.toast-message {
    flex: 1;
    line-height: 1.4;
    overflow-wrap: break-word;
    word-break: break-word;
    min-width: 0;
}

.toast.success { background: rgba(220, 252, 231, 0.72); color: #14532d; }
.toast.error   { background: rgba(254, 226, 226, 0.72); color: #7f1d1d; }
.toast.warning { background: rgba(254, 243, 199, 0.72); color: #78350f; }
.toast.info    { background: rgba(219, 234, 254, 0.72); color: #1e3a5f; }

.toast.chat {
    background: rgba(238, 242, 255, 0.72);
    color: #312e81;
    cursor: pointer;
}
.toast.chat:hover { background: rgba(238, 242, 255, 0.88); }
.toast.chat.call {
    background: rgba(254, 226, 226, 0.72);
    border-color: rgba(220, 38, 38, 0.25);
    color: #7f1d1d;
    animation: toast-pulse 0.6s ease-in-out 3;
}

@keyframes toast-pulse {
    0%, 100% { transform: translateX(0); }
    25%       { transform: translateX(-4px); }
    75%       { transform: translateX(4px); }
}

.toast.fade-out {
    opacity: 0;
    transform: translateX(40px) scale(0.95);
}

@keyframes toast-slide-in {
    from { opacity: 0; transform: translateX(60px) scale(0.95); }
    to   { opacity: 1; transform: translateX(0)    scale(1); }
}

.toast:hover {
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.14), 0 2px 6px rgba(0, 0, 0, 0.08);
    transform: translateY(-2px);
}

/* ==========================================================================
   NOTIFICATION CARDS — persistent reminders, stay until dismissed
   ========================================================================== */

#notification-container {
    position: fixed;
    top: 24px;
    right: 24px;
    display: flex;
    flex-direction: column;
    gap: 14px;
    z-index: 1999999;
    pointer-events: none;
    max-width: 420px;
    width: calc(100vw - 48px);
}

.suite-notification {
    pointer-events: auto;
    background: #ffffff;
    border: 1px solid rgba(0, 0, 0, 0.10);
    border-radius: 14px;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.14), 0 2px 8px rgba(0, 0, 0, 0.08);
    overflow: hidden;
    animation: notif-drop-in 0.35s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
    transition: box-shadow 0.2s ease, transform 0.2s ease;
}

.suite-notification:hover {
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.18), 0 4px 12px rgba(0, 0, 0, 0.10);
}

.suite-notification-accent {
    height: 4px;
    background: #94a3b8;
}
.suite-notification.success .suite-notification-accent { background: #16a34a; }
.suite-notification.error   .suite-notification-accent { background: #dc2626; }
.suite-notification.warning .suite-notification-accent { background: #d97706; }
.suite-notification.info    .suite-notification-accent { background: #2563eb; }
.suite-notification.reminder .suite-notification-accent { background: #7c3aed; }
/* TASK-286 — release announcements. Without this the type simply falls through to the base grey
   above (a neutral card, not a broken one), but a new version is the one notification that is
   good news, and it should not arrive looking like a system message. */
.suite-notification.release .suite-notification-accent { background: #0891b2; }

.suite-notification-body {
    padding: var(--space-xl) 18px 18px;
    display: flex;
    gap: 14px;
    align-items: flex-start;
}

.suite-notification-icon {
    font-size: 17px;
    font-weight: 700;
    flex-shrink: 0;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #e2e8f0;
    color: #475569;
}
.suite-notification.success .suite-notification-icon { background: #dcfce7; color: #16a34a; }
.suite-notification.error   .suite-notification-icon { background: #fee2e2; color: #dc2626; }
.suite-notification.warning .suite-notification-icon { background: #fef3c7; color: #d97706; }
.suite-notification.info    .suite-notification-icon { background: #dbeafe; color: #2563eb; }
.suite-notification.reminder .suite-notification-icon { background: #ede9fe; color: #7c3aed; }
.suite-notification.release .suite-notification-icon { background: #cffafe; color: #0891b2; }

.suite-notification-content {
    flex: 1;
    min-width: 0;
}

.suite-notification-title {
    font-size: 14px;
    font-weight: 700;
    color: #0f172a;
    margin: 0 0 var(--space-2xs);
    line-height: 1.3;
}

.suite-notification-message {
    font-size: 13px;
    font-weight: 400;
    color: #475569;
    line-height: 1.5;
    margin: 0;
}

.suite-notification-close {
    background: none;
    border: none;
    color: #94a3b8;
    cursor: pointer;
    font-size: 18px;
    line-height: 1;
    padding: 0;
    flex-shrink: 0;
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    transition: background 0.15s, color 0.15s;
    margin-top: -2px;
}
.suite-notification-close:hover {
    background: #f1f5f9;
    color: #334155;
}

.suite-notification.fade-out {
    opacity: 0;
    transform: translateX(60px) scale(0.95);
    transition: opacity 0.3s ease, transform 0.3s ease;
}

@keyframes notif-drop-in {
    from { opacity: 0; transform: translateY(-20px) scale(0.97); }
    to   { opacity: 1; transform: translateY(0)     scale(1); }
}


/* ============================================================
   CHAT HOVER POPUP (Sidebar)
   ============================================================ */

.chat-hover-popup {
    position: fixed;
    left: 0; /* JS will set this */
    top: 0;  /* JS will set this */
    width: 300px;
    background: #ffffff;
    border: 1px solid #e2e8f0;
    border-radius: var(--radius-lg);
    box-shadow: 0 12px 40px rgba(0,0,0,0.18), 0 2px 8px rgba(0,0,0,0.08);
    padding: var(--space-xl);
    z-index: 99999;
    animation: chat-popup-in 0.15s ease-out forwards;
}

@keyframes chat-popup-in {
    from { opacity: 0; transform: translateX(6px); }
    to   { opacity: 1; transform: translateX(0); }
}

.chat-hover-popup-title {
    font-size: 12px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: #5c6bc0;
    margin-bottom: var(--space-md);
    border-bottom: 1px solid #f1f5f9;
    padding-bottom: var(--space-sm);
}

.chat-hover-popup-body {
    font-size: 14px;
    color: #1e293b;
    line-height: 1.5;
    margin-bottom: var(--space-lg);
    min-height: 22px;
}

.chat-hover-popup-body .popup-msg-row {
    display: flex;
    align-items: flex-start;
    gap: var(--space-sm);
    padding: var(--space-2xs) 0;
}
.chat-hover-popup-body .popup-msg-badge {
    background: #e53935;
    color: white;
    border-radius: 10px;
    font-size: 11px;
    font-weight: 800;
    padding: var(--space-3xs) 7px;
    flex-shrink: 0;
    line-height: 1.4;
}
.chat-hover-popup-body .popup-msg-text {
    color: #334155;
    font-size: 13px;
    font-weight: 500;
    line-height: 1.4;
    word-break: break-word;
}

.chat-hover-popup-btn {
    display: block;
    text-align: center;
    background: #5c6bc0;
    color: white !important;
    border-radius: var(--radius-md);
    padding: var(--space-sm) 14px;
    font-size: 13px;
    font-weight: 700;
    text-decoration: none;
    transition: background 0.15s;
}
.chat-hover-popup-btn:hover { background: #3949ab; }

/* ======================================================
   INCOMING CALL BANNER (suite-wide shared)
====================================================== */
.incoming-call-banner {
    position: fixed;
    top: 20px;
    right: 20px;
    z-index: 9999;
    background: #1a1a2e;
    color: #fff;
    border-radius: 14px;
    box-shadow: 0 8px 32px rgba(0,0,0,0.45);
    padding: var(--space-xl) var(--space-2xl);
    display: flex;
    align-items: center;
    gap: 14px;
    min-width: 320px;
    /* min-width 320px + right:20px overhangs the left edge on a narrow viewport.
       Cap to the viewport minus both 20px gutters. NB per CSS, min-width still beats
       max-width below 360px viewport — narrowing min-width is a separate decision. */
    max-width: calc(100vw - 40px);
    animation: icb-slide-in 0.35s cubic-bezier(0.34,1.56,0.64,1);
}
@keyframes icb-slide-in {
    from { transform: translateX(120%); opacity: 0; }
    to   { transform: translateX(0);    opacity: 1; }
}
.icb-icon { font-size: 2rem; animation: icb-pulse 1s ease-in-out infinite; }
@keyframes icb-pulse {
    0%, 100% { transform: scale(1); }
    50%       { transform: scale(1.15); }
}
.icb-info   { flex: 1; }
.icb-name   { font-weight: 700; font-size: 1rem; }
.icb-label  { font-size: 0.78rem; color: #aaa; margin-top: 3px; }
.icb-btn    { border: none; border-radius: var(--radius-md); padding: 9px var(--space-xl); cursor: pointer; font-size: 0.85rem; font-weight: 600; }
.icb-join   { background: #4caf50; color: #fff; }
.icb-join:hover   { background: #43a047; }
.icb-decline      { background: #e53935; color: #fff; margin-left: var(--space-xs); }
.icb-decline:hover { background: #c62828; }

/* ======================================================
   MESSAGE RECEIVED BANNER (suite-wide shared)
====================================================== */
.suite-msg-banner {
    position: fixed;
    top: 20px;
    right: 20px;
    z-index: 9998;
    background: #1a1a2e;
    color: #fff;
    border-radius: 14px;
    box-shadow: 0 8px 32px rgba(0,0,0,0.45);
    padding: 14px 18px;
    display: flex;
    align-items: center;
    gap: var(--space-lg);
    min-width: 280px;
    max-width: 380px;
    cursor: pointer;
    animation: icb-slide-in 0.35s cubic-bezier(0.34,1.56,0.64,1);
    transition: opacity 0.3s;
}
.suite-msg-banner.fade-out { opacity: 0; }
.smb-icon { font-size: 1.6rem; flex-shrink: 0; }
.smb-info { flex: 1; overflow: hidden; }
.smb-label { font-weight: 700; font-size: 0.9rem; }
.smb-preview {
    font-size: 0.78rem;
    color: #aaa;
    margin-top: var(--space-3xs);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ======================================================
   MOBILE OVERRIDES
====================================================== */
@media (max-width: 1024px) {
    .card {
        padding: var(--space-xl);
        border-radius: var(--radius-2xl);
    }
}

/* ======================================================
   CONTEXT MENU  (shared/suite_contextmenu.js)
   Right-click on a card or row. One engine, one look, every app.
   Owner 2026-08-04: "so right click on certain cards or rows would bring up a menu".
   Positioned by JS on <body> — never inside the card, or a transformed/overflow-hidden
   ancestor clips it (the containing-block trap).
====================================================== */
.suite-menu {
    position: fixed;
    z-index: 10000;
    min-width: 200px;
    max-width: 320px;
    padding: var(--space-2xs);
    border-radius: 10px;
    background: var(--surface, #fff);
    border: 1px solid var(--border, rgba(127, 127, 127, .24));
    box-shadow: 0 10px 34px rgba(0, 0, 0, .18);
    font-size: 13px;
    /* The menu owns right-click inside itself, so a stray one cannot reopen the app menu on top. */
    user-select: none;
}
.suite-menu[hidden] { display: none; }

.suite-menu__item {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    width: 100%;
    padding: var(--space-xs) var(--space-md);
    border: 0;
    border-radius: 6px;
    background: none;
    color: var(--text, inherit);
    font: inherit;
    text-align: left;
    cursor: pointer;
}
.suite-menu__item:hover:not([disabled]),
.suite-menu__item:focus-visible {
    background: var(--primary, #4f46e5);
    color: #fff;
    outline: none;
}
.suite-menu__item[disabled] { opacity: .42; cursor: default; }

.suite-menu__icon  { width: 1.15em; text-align: center; flex: none; }
.suite-menu__label { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.suite-menu__hint  { flex: none; font-size: 11px; opacity: .65; margin-left: var(--space-md); }

/* Destructive actions read as destructive BEFORE the click, not after. */
.suite-menu__item--danger { color: var(--danger, #dc2626); }
.suite-menu__item--danger:hover:not([disabled]),
.suite-menu__item--danger:focus-visible { background: var(--danger, #dc2626); color: #fff; }

.suite-menu__sep {
    height: 1px;
    margin: var(--space-2xs) var(--space-xs);
    background: var(--border, rgba(127, 127, 127, .24));
}

@media (prefers-color-scheme: dark) {
    .suite-menu {
        background: var(--surface, #1e1f25);
        box-shadow: 0 10px 34px rgba(0, 0, 0, .5);
    }
}


/* ══════════════════════════════════════════════════════════════════════════════════════════════
   FILTER ROW — the suite's standard list-filter bar
   ══════════════════════════════════════════════════════════════════════════════════════════════
   PROMOTED FROM IW (TASK-215). These classes were born inline in
   `ideenwerkstatt/www/ideenwerkstatt.html` (~:180) and existed nowhere else, so a second app
   wanting IW's filter bar had exactly two options: copy the block, or do without. Both are wrong
   under this repo's standing rule that a new app should look identical with ZERO per-app CSS —
   a local fork silently stops inheriting later shared changes.

   Copied here VERBATIM (same values, same shape) so promoting it cannot change how IW renders.
   IW still carries its own inline copy and is therefore unaffected by this addition; retiring
   that copy in favour of this one is a separate, deliberate slice — deliberately NOT bundled
   here, because IW's Stammdaten is being changed by another session right now and a cascade
   change under someone else's feet is how a "harmless" refactor becomes their bug.

   Structure:  .filter-row > .filter-col[.wide] > small + (input|select)
   .filter-col-group wraps controls that belong together; .filter-date-pair is the two-date case.  */
.filter-row {
    display: flex;
    flex-wrap: wrap;
    gap: 15px;
    position: relative;
    z-index: 10;
    margin-top: 15px;
    align-items: flex-end;
}
.filter-col {
    display: flex;
    flex-direction: column;
    gap: 4px;
    flex: 1;
    min-width: 140px;
}
.filter-col.wide {
    flex: 2;
    min-width: 200px;
}
.filter-col input,
.filter-col select {
    width: 100%;
    height: 36px;
    padding: 6px 15px;
    font-size: 13px;
    border: 1px solid #ced4da;
    border-radius: 20px;
    background: white;
    outline: none;
    transition: border-color 0.2s;
}
.filter-col input:focus,
.filter-col select:focus {
    border-color: #007bff;
}
/* The uppercase micro-label above each control — this is what makes a filter bar readable
   instead of a row of anonymous boxes. */
.filter-col small {
    font-weight: 700;
    color: #999;
    font-size: 10px;
    display: block;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-left: 5px;
}
.filter-col-group {
    display: flex;
    gap: 15px;
    border-radius: 30px;
    background: #f8f9fa;
    border: 1px solid transparent;
    transition: all 0.3s ease;
    align-items: flex-end;
    margin-left: -10px;
    padding-left: 10px;
    padding-right: 10px;
}

/* "Start ab" + "Ende bis" wrapper. Desktop: transparent, so the two date cols behave as
   normal flex siblings. COMPLETES the promotion above (TASK-216) — the base .filter-row/
   .filter-col/.filter-col-group block was promoted from IW earlier (cdfa25a6) without this
   part. Carried verbatim from ideenwerkstatt.html's former inline <style> block. */
.filter-date-pair { display: contents; }

@media (max-width: 1024px) {
    /* Two short date fields share one row */
    .filter-date-pair {
        display: grid;
        grid-template-columns: 1fr 1fr;
        /* BUG-780: was a bare "10px" literal — tokenized safely because this rule and the
           :root mobile tier (immediately above, same shared/suite_components.css) share the
           IDENTICAL max-width:1024px breakpoint, so --space-lg resolves to exactly 10px
           everywhere this rule can ever be active (verified live at 1024x900: unchanged). */
        gap: var(--space-lg);
        width: 100%;
    }
    .filter-date-pair > .filter-col { min-width: 0; }

    /* iOS Safari renders <input type="date"> at an intrinsic width and ignores
       width:100% — which pushed Start/Ende past the screen edge. -webkit-appearance:none
       makes Safari treat it as a normal box that honors the width. */
    .filter-col input[type="date"] {
        -webkit-appearance: none;
        appearance: none;
        width: 100% !important;
        min-width: 0 !important;
        max-width: 100% !important;
        box-sizing: border-box !important;
    }
}

/* TASK-216 reconciliation: the earlier partial promotion (cdfa25a6) also added a
   `@media (max-width: 700px) { .filter-col { min-width: 100%; } }` rule IW never authored.
   Measured: IW's inline <style> block always won that specific tie (equal (0,1,0)
   specificity, later in document order), so the 700px rule was dead for IW the whole time
   it existed — removing the inline block would have made it go live in the 601-700px band
   for the first time, a real rendering change outside a "pure refactor". No other consumer
   depends on it (`grep -rln "filter-row\|filter-col"` finds only HW's hw_controlling.html,
   which scopes its own `#controlling .filter-col` overrides at higher specificity and is
   unaffected either way). Dropped rather than kept — re-add only as a deliberate decision
   with its own evidence if a future consumer needs it. */

/* ══════════════════════════════════════════════════════════════════════════════════════════════
   STAMMDATEN-STYLE LIST HEADER (TASK-216)
   ══════════════════════════════════════════════════════════════════════════════════════════════
   PROMOTED FROM IW. Ideenwerkstatt's Teilnehmer-Stammdaten header (title+count, STATUS legend
   pill, view-toggle group, labelled search, filter row) was the richest list header in the
   suite and was entirely private to `ideenwerkstatt/www/ideenwerkstatt.html` — inline styles on
   every element. Promoted verbatim (same values) so IW renders identically; IW's own inline
   styles are removed in the same change. Consumed by IW; Schule wiring is TASK-215.

   Structure:
     .card.list-header
       > .list-header-row (title+legend left | actions+tabs right)
           > .list-header-left > .list-header-title (h3) + .list-header-count (span) + .status-legend
           > .list-header-actions (view toggles, divider, action buttons)
       > .list-header-search (label + input)
       > .filter-row (see FILTER ROW section above)

   NOT promoted: the view-toggle buttons' own look (bg/radius/color). That comes from IW's
   page-wide bare `button` element selector (ideenwerkstatt.css ~line 287), not from a
   `.toggle-btn` class (which carries zero CSS in IW today — confirmed empty). Adding a shared
   `.toggle-btn` skin would outrank that bare selector (a class beats a type selector) and
   would also restyle IW's Zeitnachweis and group-coaching toggle groups, well outside this
   task's header-only scope. Measured skin, for whoever wires Schule's buttons to match:
   background #e0f2ff, color rgb(0,31,84), border-radius 6px, font-weight 600, padding 8px 12px,
   border 1px solid transparent, transition all 0.2s, hover background var(--primary-color)/#fff,
   intrinsic height 34px. The `.active` class IW's JS already toggles on these buttons is
   currently visually INERT (no CSS targets `.toggle-btn.active` or `button.active` anywhere) —
   a pre-existing IW quirk, not introduced or fixed here (fixing it would change IW's render). */

.list-header {
    padding-bottom: 20px;
    margin-bottom: 12px;
}

.list-header-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding-bottom: 6px;
    border-bottom: 1px solid var(--border-color);
    flex-wrap: wrap;
}

.list-header-left {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
}

.list-header-title {
    margin: 0;
    font-size: 1.4em;
    color: var(--primary-color);
    letter-spacing: -0.5px;
    white-space: nowrap;
}

.list-header-count {
    font-size: 13px;
    font-weight: 400;
    color: var(--text-muted);
    margin-left: 8px;
}

.status-legend {
    display: flex;
    align-items: center;
    gap: 10px;
    background: var(--bg-secondary, #f8f9fa);
    padding: 5px 14px;
    border-radius: 20px;
    border: 1px solid var(--border-color);
}

.status-legend-label {
    font-size: 10px;
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.status-legend-item {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 11px;
    font-weight: 600;
}
/* Exact hex values carried verbatim from IW's former inline styles — not design tokens,
   deliberately not swapped for CSS variables so the promotion cannot drift the rendered color. */
.status-legend-item.is-active { color: #0056b3; }
.status-legend-item.is-finished { color: #1e7e34; }
.status-legend-item.is-planned { color: #c2410c; }

.status-legend-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    display: inline-block;
}
.status-legend-dot.is-active { background: #007bff; }
.status-legend-dot.is-finished { background: #27ae60; }
.status-legend-dot.is-planned { background: #e67e22; }

.list-header-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
    flex-wrap: wrap;
}

.desktop-divider {
    width: 1px;
    height: 22px;
    background: var(--border-color);
}

.list-header-search {
    margin-top: 2px;
}

.list-header-search-label {
    font-size: 10px;
    font-weight: 700;
    color: #999;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-left: 5px;
}

/* Tighten the filter row's default 15px margin-top when it directly follows the search box —
   scoped to this header (0,2,0) so it never touches HW's own #controlling .filter-row or any
   other bare .filter-row consumer. */
.list-header .filter-row {
    margin-top: 2px;
}

/* #qStamm carries `!important` here because it must beat suite_components.css's own
   `input:not([type="checkbox"])...` gold-standard input rule (specificity (0,8,1) from its
   eight :not() clauses) — a plain class is only (0,1,0) and would silently lose padding/
   border/border-radius/transition to that rule once #qStamm's old inline style="" attribute
   (which used to win unconditionally) is gone. Same pattern IW itself already uses for this
   exact input (`#qStamm { height: 42px !important; }` in ideenwerkstatt.css, unaffected by
   this class either way — an ID + !important always wins over a class). Measured: `height`
   and `font-size` are NOT overridden here because they already resolve to the same value
   either way (42px comes from that separate ID rule regardless; font-size is 14px on both
   sides), and `color`/`background` are left alone because the gold-standard rule already
   supplies the same rendered value IW had (rgb(15,23,42) text / white background). */
.list-header-search-input {
    width: 100%;
    height: 40px;
    padding: 0 18px !important;
    font-size: 14px;
    border: 1px solid #ced4da !important;
    border-radius: 20px !important;
    background: white;
    outline: none;
    transition: border-color 0.2s !important;
    box-sizing: border-box;
    margin-top: 2px;
}

/* Modifier for a shrink-to-content .filter-col (e.g. a lone Status select with no long
   label) — the mirror of .filter-col.wide above. */
.filter-col.narrow {
    flex: 0;
    min-width: auto;
    justify-content: flex-end;
}

/* Modifier for a single compact select inside a .filter-col — adds to (never conflicts
   with) .filter-col select's own background/border/border-radius/padding/height, so only
   the delta needs re-stating. Scoped via the .filter-col ancestor class for (0,2,0)
   specificity, deliberately without needing a type selector. */
.filter-col .filter-select-compact {
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
    min-width: 120px;
    cursor: pointer;
}

/* TASK-215 → row-level status dot. The list-header component itself lives ABOVE (promoted from
   IW under TASK-216); this is the one piece that was genuinely missing from it — IW's
   .status-legend-dot styles the dot in the LEGEND, and a list also needs one per ROW.
   ⚠️ 2026-08-13: TASK-215 originally appended a SECOND .list-header/.status-legend block here,
   written before TASK-216's promotion landed. Loading later, it silently overrode IW's own
   header — turning the outer wrapper into a flex row, cutting its padding 20px→6px and adding a
   second border under the one .list-header-row already draws. Measured on dev, not theorised.
   Deleted; Schule consumes IW's structure instead. One component, one definition. */
.status-dot-row { width: 8px; height: 8px; border-radius: 50%; display: inline-block; margin-right: 6px; }
