/* KurnikProfi.UI.Shared — shared CSS for Blazor components (Web + MAUI) */

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

.blazor-error-boundary::after {
    content: "An error has occurred.";
}

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

/* ─────────────────────────────────────────────────────────────────
   forge-page__lead — shared page-header pattern across all routes:
   small accent-gradient icon chip + title + subtitle. Mirrors the
   About page's hero block so every top-level page reads with the
   same visual rhythm. Use:
       <div class="forge-page__lead">
           <span class="forge-page__lead-mark"><ForgeIcon Name="…"/></span>
           <div>
               <h1 class="forge-page__title">…</h1>
               <p class="forge-page__subtitle">…</p>
           </div>
       </div>
   ──────────────────────────────────────────────────────────────── */
.forge-page__lead {
    display: flex;
    align-items: flex-start;
    gap: var(--s-3, 12px);
    margin-top: var(--s-2, 8px);
}

.forge-page__lead-mark {
    display: grid;
    place-items: center;
    width: 56px;
    height: 56px;
    border-radius: 14px;
    flex-shrink: 0;
    color: white;
    background: linear-gradient(135deg,
        oklch(var(--accent-l) var(--accent-c) var(--accent-h)) 0%,
        oklch(0.55 0.18 calc(var(--accent-h) + 30)) 100%);
    box-shadow: 0 12px 24px oklch(var(--accent-l) var(--accent-c) var(--accent-h) / 0.28),
                inset 0 1px 0 rgba(255, 255, 255, 0.3);
}

.forge-page__lead-mark svg {
    width: 22px !important;
    height: 22px !important;
}

/* ─────────────────────────────────────────────────────────────────
   Page fade-in — standard subtle entrance for any top-level page
   that wants it. Applied automatically to .forge-page and to the
   account-management header so navigation transitions feel
   consistent across the app. Honours prefers-reduced-motion.
   ──────────────────────────────────────────────────────────────── */
@keyframes forge-page-fade {
    /* End with `transform: none` (NOT `translateY(0)`). Per CSS spec any
       non-`none` transform — even a zero translation — creates a
       containing block for descendants. With `animation-fill-mode: backwards`
       the final keyframe value sticks once the animation ends, so a leftover
       `transform: translateY(0)` on `.forge-page` would TRAP every
       `position: fixed` descendant (notably ForgeDialog's `.forge-backdrop`)
       inside the page rectangle — visible bug: the launcher's mode-picker
       dialog dimmed only the page content, not the topbar/sidebar. */
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
}

/* Apply the fade to the WHOLE page tree on Manage routes (not only the
   header) so the entire content slides in together. Manage pages don't
   have a single `.forge-page` wrapper, so we target the AuthorizeRouteView's
   immediate children via the page-router's render output marker. */
.forge-page,
.forge-page-fade-in,
.forge-manage-header,
.forge-page__header + *,
[class*="forge-profile-list"],
[class*="forge-sessions-list"],
.forge-auth-form,
.forge-manage-actions,
.forge-manage-empty,
.forge-manage-skeleton,
.forge-alert {
    animation: forge-page-fade 320ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
}

/* Stagger so child blocks ride in slightly behind the header. */
.forge-page__header + * { animation-delay: 60ms; }
[class*="forge-profile-list"] { animation-delay: 80ms; }
[class*="forge-sessions-list"] { animation-delay: 80ms; }
.forge-manage-actions { animation-delay: 120ms; }

@media (prefers-reduced-motion: reduce) {
    .forge-page,
    .forge-page-fade-in,
    .forge-manage-header,
    .forge-page__header + *,
    [class*="forge-profile-list"],
    [class*="forge-sessions-list"],
    .forge-auth-form,
    .forge-manage-actions,
    .forge-manage-empty,
    .forge-manage-skeleton,
    .forge-alert {
        animation: none;
    }
}

/* ─────────────────────────────────────────────────────────────────
   NotFound page — illustrated, accent-aware, sits inside MainLayout
   ──────────────────────────────────────────────────────────────── */
.forge-not-found {
    min-height: calc(100vh - 200px);
    display: flex;
    align-items: center;
    justify-content: center;
}
.forge-not-found__inner {
    max-width: 560px;
    width: 100%;
    text-align: center;
    padding: 32px 24px;
}
.forge-not-found__art {
    width: 100%;
    max-width: 480px;
    height: auto;
    margin: 0 auto 32px;
    display: block;
}
.forge-not-found__title {
    font-family: var(--font-display, 'Outfit', system-ui, sans-serif);
    font-size: clamp(28px, 4vw, 38px);
    font-weight: 600;
    letter-spacing: -0.02em;
    margin: 0 0 12px;
    color: var(--fg-1, var(--text-t1));
}
.forge-not-found__lead {
    font-size: 15px;
    line-height: 1.6;
    color: var(--fg-3, var(--text-t3));
    margin: 0 0 24px;
}
.forge-not-found__actions {
    display: flex;
    gap: 8px;
    justify-content: center;
    flex-wrap: wrap;
    margin-bottom: 24px;
}
.forge-not-found__debug {
    font-size: 12px;
    color: var(--fg-3, var(--text-t3));
    text-align: left;
    background: var(--surface-s2);
    border: 1px solid var(--border-b1);
    border-radius: 8px;
    padding: 8px 12px;
    margin: 0 auto;
    max-width: 480px;
}
.forge-not-found__debug summary {
    cursor: pointer;
    user-select: none;
    list-style: none;
}
.forge-not-found__debug summary::-webkit-details-marker { display: none; }
.forge-not-found__debug code {
    display: block;
    margin-top: 8px;
    padding: 8px 10px;
    background: var(--surface-s3);
    border-radius: 6px;
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    word-break: break-all;
    color: var(--text-t2);
}

/* ─────────────────────────────────────────────────────────────────
   Nav group — collapsible sidebar section (e.g. Account → Profile,
   Password, 2FA, Passkeys, Sessions). The component is a plain
   <button>+<div> pair (NOT <details>) so the open state stays in C#
   and the CSS Grid 0fr↔1fr animation actually plays — HTML's native
   <details> element fights this animation by setting display:none on
   its content slot. Toggle visibility via the `.is-open` class on
   the wrapping `.nav-group` element (driven by code-behind
   `_accountOpen` field).
   ──────────────────────────────────────────────────────────────── */
.nav-group { margin: 0; padding: 0; }

/* Summary is a real <button> — keep our own visual styling, no UA chrome. */
.nav-group__summary {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 10px;
    margin-bottom: 1px;
    width: 100%;
    border-radius: 8px;
    border: 1px solid transparent;
    background: transparent;
    cursor: pointer;
    color: var(--fg-2);
    font-family: inherit;
    font-size: 13px;
    font-weight: 500;
    text-align: left;
    user-select: none;
    transition:
        background var(--t-fast) var(--ease-out),
        color var(--t-fast) var(--ease-out),
        padding var(--t-collapse) var(--ease-out-expo),
        gap var(--t-collapse) var(--ease-out-expo);
}
.nav-group__summary:hover {
    background: var(--bg-3);
    color: var(--fg-1);
}
.nav-group__summary .nav-icon { flex-shrink: 0; }
.nav-group__summary .nav-label {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}

.nav-group__chevron {
    transition: transform 200ms cubic-bezier(0.16, 1, 0.3, 1);
    flex-shrink: 0;
    color: var(--fg-3);
}
.nav-group.is-open > .nav-group__summary .nav-group__chevron {
    transform: rotate(180deg);
    color: var(--fg-2);
}

/* Open/close animation — CSS Grid `grid-template-rows: 0fr ↔ 1fr` pattern.
   Universal browser support, no `interpolate-size` dependency. The outer
   .nav-group__items is the grid; .nav-group__inner is the single grid row
   that holds the actual flex layout and clips overflow during the slide. */
.nav-group__items {
    display: grid;
    grid-template-rows: 0fr;
    opacity: 0;
    transition:
        grid-template-rows 200ms cubic-bezier(0.16, 1, 0.3, 1),
        opacity 160ms ease;
}
.nav-group__inner {
    display: flex;
    flex-direction: column;
    padding: 4px 0 4px 18px;
    gap: 1px;
    border-left: 1px dashed var(--line);
    margin-left: 18px;
    overflow: hidden;
    min-height: 0;
}
.nav-group.is-open > .nav-group__items {
    grid-template-rows: 1fr;
    opacity: 1;
}
.nav-group__inner .nav-item {
    padding: 7px 10px;
    font-size: 12.5px;
}
.nav-group__inner .nav-icon {
    width: 20px;
    height: 20px;
}
.nav-group__inner .nav-icon svg {
    width: 13px !important;
    height: 13px !important;
}

/* Collapsed-rail centring of the summary — symmetric 18 px padding so the
   28 px nav-icon container lands at exactly `(64 − 28) / 2` from each
   edge of the rail. Matches .nav-item collapsed-state padding so icons
   stay in a single visual column when the rail is closed. */
[data-collapsed="true"] .nav-group__summary {
    gap: 0;
    padding: 9px 18px;
}
[data-collapsed="true"] .nav-group__summary .nav-label,
[data-collapsed="true"] .nav-group__chevron {
    opacity: 0;
    max-width: 0;
    pointer-events: none;
}
/* In rail mode the submenu has nowhere to render — keep it collapsed even if
   the user previously toggled `is-open`. (Click handler navigates instead.) */
[data-collapsed="true"] .nav-group.is-open > .nav-group__items {
    grid-template-rows: 0fr;
    opacity: 0;
}

/* ────────────────────────────────────────────────────────────────────
   Forge Hero — top-of-dashboard welcome surface.

   Reference design language: Apple Mail "Hello, {name}" greeting,
   Linear's gradient mesh hero, Adobe Spectrum's restrained motion +
   refined typography. The result is a single section that anchors
   every dashboard with a personalised, time-aware greeting + the
   three numbers a manager wants on the very first glance, plus the
   two actions they reach for most. Animated orbs in the background
   add subtle motion without competing with the content; the dot
   grid texture grounds the surface so it doesn't feel weightless.

   Theme-aware in every layer — the gradient mesh pulls from
   `--accent-l --accent-c --accent-h` so the hero flips colour with
   the user's accent preset and custom hue. Light / dark theme
   handled by `--surface-elevated` / `--fg-1` / `--fg-3` automatically.

   Mobile-first: at <= 760 px the visual orbs hide, the metric grid
   stacks, the action buttons fill the available width. The hero
   remains the focal element on every screen size without exceeding
   one viewport on phones.
   ──────────────────────────────────────────────────────────────────── */

.forge-hero {
    position: relative;
    margin-bottom: var(--s-6, 24px);
    padding: var(--s-7, 32px);
    border-radius: var(--r-lg, 14px);
    background:
        radial-gradient(80% 100% at 100% 0%,
            color-mix(in oklab, oklch(var(--accent-l, 0.62) var(--accent-c, 0.18) var(--accent-h, 235)) 18%, transparent),
            transparent 60%),
        radial-gradient(60% 80% at 0% 100%,
            color-mix(in oklab, oklch(var(--accent-l, 0.62) var(--accent-c, 0.18) var(--accent-h, 235)) 10%, transparent),
            transparent 50%),
        var(--surface-elevated, var(--surface-s1));
    border: 1px solid var(--line, var(--border-b1));
    overflow: hidden;
    isolation: isolate;
}

/* Dot-grid texture on the right half — subtle visual ground that
   doesn't compete with the typography. mask-image fades it into the
   left half so content doesn't sit on top of dots. */
.forge-hero::before {
    content: "";
    position: absolute;
    top: 0; right: 0;
    width: 60%; height: 100%;
    background-image: radial-gradient(circle at 1px 1px,
        color-mix(in oklab, var(--fg-1, currentColor) 7%, transparent) 1px,
        transparent 0);
    background-size: 24px 24px;
    -webkit-mask-image: linear-gradient(to right, transparent, black 60%);
            mask-image: linear-gradient(to right, transparent, black 60%);
    pointer-events: none;
    z-index: 0;
}

.forge-hero__content {
    position: relative;
    z-index: 2;
    /* Text content sits in the left ~60 % of the hero so the right
       40 % stays clear for the visual orbs + dot grid. Without this
       cap, the metric strip stretched across the whole viewport on
       wide monitors and the right side felt empty. */
    max-width: min(640px, 60%);
}

/* Date pill — JetBrains Mono uppercase tag floating above the greeting. */
.forge-hero__date-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    border-radius: 999px;
    background: color-mix(in oklab, var(--fg-1, currentColor) 6%, transparent);
    border: 1px solid color-mix(in oklab, var(--fg-1, currentColor) 8%, transparent);
    font-family: var(--font-mono, monospace);
    font-size: 11px;
    font-weight: 500;
    color: var(--fg-2, var(--text-t2));
    text-transform: uppercase;
    letter-spacing: 0.06em;
}
.forge-hero__date-pill > svg { color: var(--accent); }

/* Greeting — display typeface, large, two-tone. The user's name is
   painted with a 135° accent gradient so the eye lands there first. */
.forge-hero__greeting {
    margin: var(--s-3, 12px) 0 0;
    font-family: var(--font-display, "Outfit", system-ui, sans-serif);
    font-size: clamp(28px, 4vw, 44px);
    font-weight: 700;
    letter-spacing: -0.025em;
    line-height: 1.1;
    color: var(--fg-1, var(--text-t1));
}
.forge-hero__greeting-prefix {
    color: var(--fg-2, var(--text-t2));
    font-weight: 500;
}
.forge-hero__greeting-name {
    background: linear-gradient(135deg,
        var(--accent),
        color-mix(in oklab, var(--accent) 40%, var(--fg-1, currentColor)));
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
}

.forge-hero__lead {
    margin: var(--s-3, 12px) 0 0;
    font-size: 14px;
    color: var(--fg-3, var(--text-t3));
    max-width: 560px;
    line-height: 1.5;
}

/* Three-up metric strip below the greeting. The featured metric (left)
   takes ~1.5x the width and 2x the typographic weight of the secondary
   pair on the right; that's the visual hierarchy a manager scans on
   the first glance. */
.forge-hero__metrics {
    display: grid;
    grid-template-columns: 1.5fr 1fr 1fr;
    gap: var(--s-5, 20px);
    margin-top: var(--s-7, 32px);
    padding-top: var(--s-5, 20px);
    border-top: 1px dashed var(--line, var(--border-b1));
}

.forge-hero__metric-label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: var(--font-mono, monospace);
    font-size: 11px;
    color: var(--fg-3, var(--text-t3));
    letter-spacing: 0.06em;
    text-transform: uppercase;
    margin-bottom: 8px;
}
.forge-hero__metric-label > svg { color: var(--accent); }

.forge-hero__metric-value {
    font-family: var(--font-display, "Outfit", system-ui, sans-serif);
    font-weight: 600;
    color: var(--fg-1, var(--text-t1));
    letter-spacing: -0.025em;
    line-height: 1;
}
.forge-hero__metric--featured .forge-hero__metric-value {
    font-size: clamp(36px, 4.6vw, 52px);
}
.forge-hero__metric:not(.forge-hero__metric--featured) .forge-hero__metric-value {
    font-size: clamp(20px, 2.4vw, 28px);
}

.forge-hero__metric-sub {
    margin-top: 8px;
    font-size: 12px;
    color: var(--fg-3, var(--text-t3));
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.forge-hero__metric-delta {
    display: inline-flex;
    align-items: center;
    padding: 2px 6px;
    border-radius: 6px;
    font-family: var(--font-mono, monospace);
    font-size: 11px;
    font-weight: 500;
}
.forge-hero__metric-delta--up {
    color: var(--ok, oklch(0.65 0.15 155));
    background: color-mix(in oklab, var(--ok, oklch(0.65 0.15 155)) 14%, transparent);
}
.forge-hero__metric-delta--down {
    color: var(--bad, oklch(0.6 0.22 25));
    background: color-mix(in oklab, var(--bad, oklch(0.6 0.22 25)) 14%, transparent);
}

.forge-hero__actions {
    display: flex;
    gap: var(--s-3, 12px);
    margin-top: var(--s-6, 24px);
    flex-wrap: wrap;
}

/* Visual element on the right — three blurred orbs that drift with a
   slow ease-in-out, creating subtle ambient motion that reads as
   "alive" without demanding attention. The orbs blend with the
   gradient mesh so they feel painted onto the surface, not floating. */
.forge-hero__visual {
    position: absolute;
    top: 0; right: 0;
    width: 50%;
    height: 100%;
    pointer-events: none;
    z-index: 1;
    overflow: hidden;
}

.forge-hero__orb {
    position: absolute;
    border-radius: 999px;
    background: radial-gradient(circle at 30% 30%,
        color-mix(in oklab, oklch(var(--accent-l, 0.62) var(--accent-c, 0.18) var(--accent-h, 235)) 70%, transparent),
        color-mix(in oklab, oklch(var(--accent-l, 0.62) var(--accent-c, 0.18) var(--accent-h, 235)) 22%, transparent) 55%,
        transparent 80%);
    filter: blur(36px);
    opacity: 0.85;
    will-change: transform;
}

.forge-hero__orb--lg {
    width: 360px; height: 360px;
    top: -100px; right: -80px;
    animation: forge-hero-orb-drift 18s ease-in-out infinite;
}
.forge-hero__orb--md {
    width: 220px; height: 220px;
    top: 130px; right: 160px;
    animation: forge-hero-orb-drift 14s ease-in-out infinite reverse;
    opacity: 0.65;
}
.forge-hero__orb--sm {
    width: 130px; height: 130px;
    bottom: 30px; right: 260px;
    animation: forge-hero-orb-drift 16s ease-in-out infinite;
    animation-delay: -5s;
    opacity: 0.7;
}

/* Decorative concentric ring set on the right edge — three nested
   thin accent rings that rotate slowly. Reads as a stylised "core"
   anchored in the empty visual area; pairs with the drift orbs to
   give the right side something for the eye to rest on without
   competing with the typography on the left. */
.forge-hero__rings {
    position: absolute;
    top: 50%;
    right: 8%;
    transform: translateY(-50%);
    width: 320px;
    height: 320px;
    pointer-events: none;
    opacity: 0.35;
}
.forge-hero__rings span {
    position: absolute;
    inset: 0;
    border: 1px solid color-mix(in oklab,
        oklch(var(--accent-l, 0.62) var(--accent-c, 0.18) var(--accent-h, 235)) 60%, transparent);
    border-radius: 999px;
    animation: forge-hero-ring-rotate 24s linear infinite;
}
.forge-hero__rings span:nth-child(2) {
    inset: 16%;
    border-color: color-mix(in oklab,
        oklch(var(--accent-l, 0.62) var(--accent-c, 0.18) var(--accent-h, 235)) 40%, transparent);
    border-style: dashed;
    animation-duration: 32s;
    animation-direction: reverse;
}
.forge-hero__rings span:nth-child(3) {
    inset: 36%;
    border-color: color-mix(in oklab,
        oklch(var(--accent-l, 0.62) var(--accent-c, 0.18) var(--accent-h, 235)) 70%, transparent);
    border-style: dotted;
    animation-duration: 18s;
}

@keyframes forge-hero-ring-rotate {
    0%   { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}

@keyframes forge-hero-orb-drift {
    0%, 100% { transform: translate3d(0, 0, 0); }
    50%      { transform: translate3d(-22px, 16px, 0); }
}

@media (prefers-reduced-motion: reduce) {
    .forge-hero__orb,
    .forge-hero__rings span { animation: none; }
}

/* Mobile — drop the visual decoration, stack metrics, full-width CTAs.
   Typography still scales fluidly via clamp() above. The content cap
   that holds text to 60 % on wide screens is lifted to 100 % so the
   greeting + metrics fill the viewport. */
@media (max-width: 760px) {
    .forge-hero {
        padding: var(--s-6, 24px) var(--s-5, 20px);
    }
    .forge-hero__content {
        max-width: 100%;
    }
    .forge-hero__metrics {
        grid-template-columns: 1fr;
        gap: var(--s-4, 16px);
        margin-top: var(--s-5, 20px);
        padding-top: var(--s-4, 16px);
    }
    .forge-hero__visual,
    .forge-hero__rings { display: none; }
    .forge-hero__actions { flex-direction: column; align-items: stretch; }
    .forge-hero__actions > * { width: 100%; }
    .forge-hero::before { width: 100%; opacity: 0.6; }
}

/* ────────────────────────────────────────────────────────────────────
   Forge Popover — top-bar dropdown surface (notifications + account menu).

   Two surfaces share this CSS: the bell-trigger notifications dropdown,
   and the avatar-trigger account dropdown. Both anchor to the right
   edge of the viewport (`top: calc(100% + 10px); right: 0`) so the
   panel grows leftward into the topbar instead of crossing the right
   viewport boundary on narrow screens. Backdrop is a transparent
   intercept layer (no dim) — top-bar dropdowns never carry the visual
   weight of a modal. The fade + slide-in arc matches ForgeLanguageMenu
   so all topbar dropdowns feel native to the same overlay vocabulary.

   Theme-aware via design tokens — surface, line, fg, accent all flip
   with the active theme + accent preset + accentHue.
   ──────────────────────────────────────────────────────────────────── */

.forge-popover {
    position: relative;
    display: inline-flex;
}

.forge-popover__trigger {
    background: transparent;
    border: 1px solid transparent;
    cursor: pointer;
    padding: 6px;
    border-radius: var(--r-md, 8px);
    color: var(--fg-2, var(--text-t2));
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
    position: relative;
}
.forge-popover__trigger:hover,
.forge-popover.is-open > .forge-popover__trigger {
    background: var(--surface-elevated, var(--surface-s1));
    border-color: var(--line, var(--border-b1));
    color: var(--fg-1, var(--text-t1));
}

/* Avatar trigger — no border / padding chrome around the avatar itself,
   just a subtle hover ring that picks up the accent on focus / open. */
.forge-popover__trigger--avatar {
    padding: 2px;
    border-radius: 999px;
}
.forge-popover__trigger--avatar:focus-visible,
.forge-popover.is-open > .forge-popover__trigger--avatar {
    box-shadow: 0 0 0 2px color-mix(in oklab, var(--accent) 60%, transparent);
}

/* Notification badge — small circular accent counter on the bell trigger. */
.forge-popover__badge {
    position: absolute;
    top: -2px; right: -2px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--accent);
    color: var(--bg-0, white);
    font-size: 10px;
    font-weight: 600;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    box-shadow: 0 0 0 2px var(--bg-0, var(--background));
}

/* Backdrop — transparent click-out interceptor below the panel. */
.forge-popover__backdrop {
    position: fixed;
    inset: 0;
    z-index: 90;
    background: transparent;
}

/* Main panel surface. min-width keeps the layout stable when an
   empty-state replaces a populated list; max-width prevents the panel
   from running offscreen on small viewports (combined with right: 0
   anchoring at the trigger). */
.forge-popover__panel {
    position: absolute;
    top: calc(100% + 10px);
    right: 0;
    z-index: 100;
    min-width: 320px;
    max-width: min(380px, calc(100vw - 32px));
    background: var(--surface-elevated, var(--surface-s1));
    border: 1px solid var(--line, var(--border-b1));
    border-radius: var(--r-lg, 12px);
    box-shadow:
        0 16px 40px -16px color-mix(in oklab, var(--fg-1, currentColor) 30%, transparent),
        0 0 0 1px color-mix(in oklab, var(--fg-1, currentColor) 4%, transparent);
    overflow: hidden;
    transform-origin: top right;
    animation: forge-popover-in 240ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
}

@keyframes forge-popover-in {
    0%   { opacity: 0; transform: translateY(-8px) scale(0.96); }
    100% { opacity: 1; transform: translateY(0)    scale(1); }
}

/* Header row — title on the left, optional action on the right. Used
   by both the notifications panel ("Notifications" + "Mark all read")
   and any future panel that needs a labeled section. */
.forge-popover__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 16px 10px;
}
.forge-popover__title {
    font-family: var(--font-display, inherit);
    font-size: 14px;
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--fg-1, var(--text-t1));
}
.forge-popover__action {
    background: transparent;
    border: 0;
    padding: 4px 8px;
    color: var(--accent);
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    border-radius: var(--r-sm, 6px);
    transition: background 140ms ease;
}
.forge-popover__action:hover {
    background: color-mix(in oklab, var(--accent) 10%, transparent);
}

/* Hairline divider used between sections inside the panel. */
.forge-popover__divider {
    height: 1px;
    background: var(--line, var(--border-b1));
}

/* Empty-state block (notifications "you're all caught up", etc.). */
.forge-popover__empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 28px 24px 24px;
    text-align: center;
}
.forge-popover__empty > svg {
    color: color-mix(in oklab, var(--accent) 60%, transparent);
    opacity: 0.7;
}
.forge-popover__empty-title {
    margin: 6px 0 0;
    font-size: 13px;
    font-weight: 500;
    color: var(--fg-1, var(--text-t1));
}
.forge-popover__empty-hint {
    margin: 0;
    font-size: 12px;
    color: var(--fg-3, var(--text-t3));
}

/* Account-specific identity header — avatar + name + email. */
.forge-popover__identity {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 16px;
}
.forge-popover__identity-text {
    min-width: 0;
    flex: 1;
}
.forge-popover__identity-name {
    font-family: var(--font-display, inherit);
    font-size: 14px;
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--fg-1, var(--text-t1));
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.forge-popover__identity-email {
    font-size: 12px;
    color: var(--fg-3, var(--text-t3));
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    margin-top: 2px;
}

/* Item list — keyboard-navigable menu items. Hover paints a subtle
   accent wash; the active accent flips with the user's chosen
   preset / hue. */
.forge-popover__list {
    list-style: none;
    padding: 6px 0;
    margin: 0;
}
.forge-popover__list li { margin: 0; }
.forge-popover__list a,
.forge-popover__list button {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 9px 16px;
    background: transparent;
    border: 0;
    color: var(--fg-1, var(--text-t1));
    font-size: 13px;
    font-family: inherit;
    cursor: pointer;
    text-align: left;
    text-decoration: none;
    transition: background 140ms ease, color 140ms ease;
}
.forge-popover__list a:hover,
.forge-popover__list button:hover,
.forge-popover__list a:focus-visible,
.forge-popover__list button:focus-visible {
    background: color-mix(in oklab, var(--accent) 10%, transparent);
    color: var(--fg-1, var(--text-t1));
    outline: none;
}
.forge-popover__list svg {
    color: var(--fg-3, var(--text-t3));
    flex-shrink: 0;
}
.forge-popover__list a:hover svg,
.forge-popover__list button:hover svg {
    color: var(--accent);
}

/* Sign-out footer — destructive-tinted accent for the danger action. */
.forge-popover__footer-action {
    padding: 6px 0;
}
.forge-popover__signout-form {
    margin: 0;
}
.forge-popover__signout {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 10px 16px;
    background: transparent;
    border: 0;
    color: var(--bad, oklch(0.6 0.22 25));
    font-size: 13px;
    font-weight: 500;
    font-family: inherit;
    cursor: pointer;
    text-align: left;
    transition: background 140ms ease;
}
.forge-popover__signout:hover {
    background: color-mix(in oklab, var(--bad, oklch(0.6 0.22 25)) 12%, transparent);
}
.forge-popover__signout svg {
    color: currentColor;
    flex-shrink: 0;
}

@media (prefers-reduced-motion: reduce) {
    .forge-popover__panel { animation: none; }
}

/* ────────────────────────────────────────────────────────────────────
   Forge Hero — entrance choreography.

   The hero has been static so far — every other top-level content block
   on the dashboard rises into place with the existing forge-page fade,
   so the hero looked frozen while everything below it animated in. Now
   each layer of the hero arrives in its own beat:

     1. card        110 ms — opacity 0 → 1, translateY 14 → 0, scale .98 → 1
     2. date pill    20 ms after card    — fade + small lift
     3. greeting     80 ms after card    — fade + lift
     4. lead        180 ms after card    — fade + lift
     5. metrics     280 ms after card    — fade + lift, staggered per cell
     6. actions     420 ms after card    — fade + lift
     7. orbs       (continuous drift, already running on a separate loop)

   prefers-reduced-motion stops the cascade and renders everything at the
   final state immediately.
   ──────────────────────────────────────────────────────────────────── */

@keyframes forge-hero-card-in {
    0%   { opacity: 0; transform: translateY(14px) scale(0.985); }
    100% { opacity: 1; transform: translateY(0)    scale(1); }
}
@keyframes forge-hero-row-in {
    0%   { opacity: 0; transform: translateY(10px); }
    100% { opacity: 1; transform: translateY(0); }
}

.forge-hero {
    animation: forge-hero-card-in 580ms cubic-bezier(0.22, 1, 0.36, 1) both;
}
.forge-hero__date-pill { animation: forge-hero-row-in 480ms cubic-bezier(0.22, 1, 0.36, 1)  20ms both; }
.forge-hero__greeting  { animation: forge-hero-row-in 540ms cubic-bezier(0.22, 1, 0.36, 1)  80ms both; }
.forge-hero__lead      { animation: forge-hero-row-in 540ms cubic-bezier(0.22, 1, 0.36, 1) 180ms both; }
.forge-hero__metrics > * {
    animation: forge-hero-row-in 560ms cubic-bezier(0.22, 1, 0.36, 1) 280ms both;
}
.forge-hero__metrics > *:nth-child(2) { animation-delay: 340ms; }
.forge-hero__metrics > *:nth-child(3) { animation-delay: 400ms; }
.forge-hero__actions   { animation: forge-hero-row-in 540ms cubic-bezier(0.22, 1, 0.36, 1) 460ms both; }

/* Subtle conic-gradient halo behind the metrics — hairline accent ring
   that signals "this is the headline content." Painted via ::after on
   the content wrapper so it's behind the metrics but above the
   background mesh. */
.forge-hero__content::after {
    content: "";
    position: absolute;
    inset: -2px;
    pointer-events: none;
    background:
        conic-gradient(
            from 200deg at 30% 110%,
            transparent 0deg,
            color-mix(in oklab, var(--accent) 18%, transparent) 60deg,
            transparent 120deg
        );
    opacity: 0.5;
    filter: blur(40px);
    z-index: -1;
}

/* When a viewer dialog is open above the hero, freeze its orbs so the
   ambient motion doesn't bleed through the lightbox backdrop and pull
   the eye away from the dialog. */
body:has(.forge-lightbox) .forge-hero__orb {
    animation-play-state: paused;
}

@media (prefers-reduced-motion: reduce) {
    .forge-hero,
    .forge-hero__date-pill,
    .forge-hero__greeting,
    .forge-hero__lead,
    .forge-hero__metrics > *,
    .forge-hero__actions {
        animation: none !important;
    }
}

/* ────────────────────────────────────────────────────────────────────
   Forge Files List — modelled on the Todo table layout.

   Each row is a 4-column grid: thumbnail / meta / etag / actions.
   Forge Motion lifecycle classes (forge-list-item--inserted /
   --updated / --deleting) drive the same insert / delete choreography
   the Todo table uses. The thumbnail is a button (so keyboard users
   can open the viewer with Enter), and clicking either the thumbnail
   or the file name opens the in-app lightbox below. The action group
   is right-aligned via grid layout (justify-self: end) so the trash
   button always sits flush to the right edge regardless of how long
   the file path or ETag is.
   ──────────────────────────────────────────────────────────────────── */

.forge-files-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 6px 0;
}

.forge-files-list__row {
    display: grid;
    grid-template-columns: 64px 1fr auto auto;
    gap: 14px;
    align-items: center;
    padding: 10px 12px;
    border-radius: var(--r-md, 10px);
    border: 1px solid var(--line, var(--border-b1));
    background: color-mix(in oklab, var(--surface-elevated, var(--surface-s1)) 96%, var(--accent) 4%);
    transition: border-color 140ms ease, background 140ms ease;
}
.forge-files-list__row:hover {
    border-color: var(--line-strong, var(--border-b2));
    background: var(--surface-elevated, var(--surface-s1));
}

.forge-files-list__thumb {
    width: 64px;
    height: 64px;
    border-radius: var(--r-sm, 8px);
    background: color-mix(in oklab, var(--accent) 8%, var(--bg-2, transparent));
    border: 1px solid color-mix(in oklab, var(--accent) 16%, transparent);
    overflow: hidden;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--accent);
    padding: 0;
    transition: transform 180ms cubic-bezier(0.22, 1, 0.36, 1), border-color 180ms ease;
}
.forge-files-list__thumb:hover {
    transform: scale(1.04);
    border-color: var(--accent);
}
.forge-files-list__thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.forge-files-list__meta {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.forge-files-list__name {
    background: transparent;
    border: 0;
    padding: 0;
    font-family: var(--font-display, inherit);
    font-size: 14px;
    font-weight: 500;
    color: var(--fg-1, var(--text-t1));
    text-align: left;
    cursor: pointer;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    transition: color 140ms ease;
}
.forge-files-list__name:hover { color: var(--accent); }
.forge-files-list__path {
    font-family: var(--font-mono, monospace);
    font-size: 11px;
    color: var(--fg-3, var(--text-t3));
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.forge-files-list__etag {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 8px;
    border-radius: 6px;
    background: color-mix(in oklab, var(--fg-1, currentColor) 5%, transparent);
    font-family: var(--font-mono, monospace);
    font-size: 11px;
    color: var(--fg-3, var(--text-t3));
}
.forge-files-list__etag > svg { color: color-mix(in oklab, var(--accent) 70%, var(--fg-3)); }

.forge-files-list__actions {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    justify-self: end;
}

.forge-files-list__icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: 8px;
    background: transparent;
    border: 1px solid transparent;
    color: var(--fg-2, var(--text-t2));
    cursor: pointer;
    text-decoration: none;
    transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
}
.forge-files-list__icon-btn:hover {
    background: color-mix(in oklab, var(--accent) 10%, transparent);
    border-color: color-mix(in oklab, var(--accent) 24%, transparent);
    color: var(--accent);
}
.forge-files-list__icon-btn--danger:hover {
    background: color-mix(in oklab, var(--bad, oklch(0.6 0.22 25)) 14%, transparent);
    border-color: color-mix(in oklab, var(--bad, oklch(0.6 0.22 25)) 28%, transparent);
    color: var(--bad, oklch(0.6 0.22 25));
}

@media (max-width: 600px) {
    .forge-files-list__row {
        grid-template-columns: 48px 1fr auto;
        gap: 10px;
        padding: 8px;
    }
    .forge-files-list__thumb { width: 48px; height: 48px; }
    .forge-files-list__etag { display: none; }
}

/* Batch summary chip — replaces the single target-path input when the
   user picks more than one file at once. Reads as a calm informational
   row ("12 files ready — each saved as uploads/{name}") so the user
   knows what the upload button is about to do. */
.forge-files-batch {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    border-radius: 10px;
    background: color-mix(in oklab, var(--accent) 8%, transparent);
    border: 1px solid color-mix(in oklab, var(--accent) 22%, transparent);
    color: var(--fg-1, var(--text-t1));
    font-size: 13px;
    line-height: 1.4;
}
.forge-files-batch > svg { color: var(--accent); flex-shrink: 0; }

/* ────────────────────────────────────────────────────────────────────
   Forge Lightbox — true full-screen image viewer.

   Reference design language: Apple Photos full-screen, Linear's image
   modal, Adobe Lightroom's Loupe. The dialog occupies the entire
   viewport (no panel border, no max-width); the image takes the full
   available area; a translucent top bar floats over the image with
   filename + counter + actions; chevron arrows on the left + right
   edges cycle between images. ArrowLeft / ArrowRight + Esc work via
   the keydown handler on the dialog div.

   Theme-aware: the backdrop reads --bg-0 + --accent for the radial
   wash behind non-image file cards; the chrome (top bar, arrows)
   pulls --surface-elevated / --line / --fg-* tokens.
   ──────────────────────────────────────────────────────────────────── */

.forge-lightbox {
    position: fixed;
    inset: 0;
    z-index: 1000;
    background:
        radial-gradient(60% 60% at 50% 50%,
            color-mix(in oklab, var(--accent) 8%, transparent),
            transparent 70%),
        color-mix(in oklab, var(--bg-0, black) 92%, transparent);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    display: flex;
    flex-direction: column;
    animation: forge-lightbox-in 220ms cubic-bezier(0.22, 1, 0.36, 1) both;
    outline: none;
}

@keyframes forge-lightbox-in {
    0%   { opacity: 0; }
    100% { opacity: 1; }
}

/* Top bar — translucent, floats over the image. Click-stop so clicking
   on the bar doesn't dismiss the lightbox. */
.forge-lightbox__topbar {
    position: relative;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 20px;
    background: color-mix(in oklab, var(--surface-elevated, var(--surface-s1)) 80%, transparent);
    border-bottom: 1px solid color-mix(in oklab, var(--line, var(--border-b1)) 60%, transparent);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    z-index: 2;
}
.forge-lightbox__title {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    flex: 1;
    min-width: 0;
    font-family: var(--font-display, inherit);
    font-size: 14px;
    font-weight: 600;
    color: var(--fg-1, var(--text-t1));
}
.forge-lightbox__title > svg { color: var(--accent); flex-shrink: 0; }
.forge-lightbox__title > span:not(.forge-lightbox__counter) {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.forge-lightbox__counter {
    flex-shrink: 0;
    padding: 3px 8px;
    border-radius: 999px;
    background: color-mix(in oklab, var(--accent) 14%, transparent);
    color: var(--accent);
    font-family: var(--font-mono, monospace);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: 0.04em;
}

.forge-lightbox__topbar-actions {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
}
.forge-lightbox__topbar-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: 10px;
    background: color-mix(in oklab, var(--fg-1, currentColor) 4%, transparent);
    border: 1px solid color-mix(in oklab, var(--fg-1, currentColor) 8%, transparent);
    color: var(--fg-1, var(--text-t1));
    cursor: pointer;
    text-decoration: none;
    transition: background 140ms ease, border-color 140ms ease, transform 140ms ease;
}
.forge-lightbox__topbar-btn:hover {
    background: color-mix(in oklab, var(--accent) 14%, transparent);
    border-color: color-mix(in oklab, var(--accent) 36%, transparent);
    color: var(--accent);
}

/* Stage — fills the remaining viewport. Click on empty space (outside
   the image) closes the lightbox via the parent's @onclick. */
.forge-lightbox__stage {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 32px 80px;
    overflow: hidden;
    position: relative;
}

.forge-lightbox__image {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    border-radius: var(--r-md, 10px);
    box-shadow:
        0 24px 80px -24px color-mix(in oklab, oklch(0 0 0) 70%, transparent),
        0 0 0 1px color-mix(in oklab, var(--fg-1, currentColor) 6%, transparent);
    cursor: default;
    animation: forge-lightbox-image-in 320ms cubic-bezier(0.22, 1, 0.36, 1) both;
}

@keyframes forge-lightbox-image-in {
    0%   { opacity: 0; transform: scale(0.97); }
    100% { opacity: 1; transform: scale(1); }
}

.forge-lightbox__file-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
    padding: 48px;
    border-radius: var(--r-lg, 14px);
    background: var(--surface-elevated, var(--surface-s1));
    border: 1px solid var(--line, var(--border-b1));
    text-align: center;
    box-shadow: 0 24px 80px -24px color-mix(in oklab, oklch(0 0 0) 60%, transparent);
}
.forge-lightbox__file-card > svg { color: var(--accent); opacity: 0.7; }
.forge-lightbox__file-name {
    font-family: var(--font-mono, monospace);
    font-size: 14px;
    color: var(--fg-2, var(--text-t2));
    word-break: break-all;
}
.forge-lightbox__download {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 20px;
    border-radius: 10px;
    background: var(--accent);
    color: var(--bg-0, white);
    text-decoration: none;
    font-size: 13px;
    font-weight: 500;
    transition: filter 140ms ease;
}
.forge-lightbox__download:hover { filter: brightness(1.08); }

/* Prev / Next chevron buttons — float on the left + right edges of
   the stage. Always visible (not hover-revealed) so touch users can
   tap them; on desktop the larger hit area makes them easy mouse
   targets. */
.forge-lightbox__nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    border-radius: 999px;
    background: color-mix(in oklab, var(--surface-elevated, var(--surface-s1)) 70%, transparent);
    border: 1px solid color-mix(in oklab, var(--line, var(--border-b1)) 70%, transparent);
    color: var(--fg-1, var(--text-t1));
    cursor: pointer;
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    transition: background 140ms ease, border-color 140ms ease, transform 200ms cubic-bezier(0.22, 1, 0.36, 1);
    z-index: 3;
}
.forge-lightbox__nav:hover {
    background: var(--surface-elevated, var(--surface-s1));
    border-color: color-mix(in oklab, var(--accent) 50%, transparent);
    color: var(--accent);
    transform: translateY(-50%) scale(1.06);
}
.forge-lightbox__nav--prev { left: 24px; }
.forge-lightbox__nav--next { right: 24px; }

@media (max-width: 600px) {
    .forge-lightbox__stage { padding: 16px 16px 24px; }
    .forge-lightbox__nav {
        width: 44px;
        height: 44px;
    }
    .forge-lightbox__nav--prev { left: 12px; }
    .forge-lightbox__nav--next { right: 12px; }
}

@media (prefers-reduced-motion: reduce) {
    .forge-lightbox,
    .forge-lightbox__image { animation: none !important; }
    .forge-lightbox__nav:hover { transform: translateY(-50%); }
}


/* ════════════════════════════════════════════════════════════════════════════
   Page header — small-viewport refinement.
   Forge ships a default rule that drops `forge-page__actions` to full width on
   ≤ 720 px (flex-direction: column on the parent `.forge-page__header`) and
   left-aligns the buttons. The Todo + Onboarding pages put the primary CTA in
   the action group; left-alignment after a column wrap reads as "the button
   migrated"; users naturally expect the CTA to stay on the right edge. Override
   to keep the action row right-aligned regardless of breakpoint, AND let the
   inner buttons wrap onto their own row when needed without squashing the title.
   ════════════════════════════════════════════════════════════════════════════ */
/* Right-align the page actions at EVERY breakpoint. The Forge framework
   default is `justify-content: flex-start`, which leaves wrapped action rows
   left-aligned within the actions container even though the parent
   `.forge-page__header` uses `justify-content: space-between` to push the
   block to the right edge. Without this override, on mid-size displays
   (around 760-1100 px) where the row wraps but the page is wide enough that
   the framework's mobile rules don't kick in, individual buttons end up
   visually mis-aligned (e.g. the [+ Nový úkol] button sits flush right but
   the tabs sit flush LEFT inside the same .forge-page__actions block). */
.forge-page__header > .forge-page__actions {
    justify-content: flex-end;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2);
}

@media (max-width: 720px) {
    .forge-page__header > .forge-page__actions {
        gap: var(--s-2);
        row-gap: var(--s-2);
    }
}


/* ════════════════════════════════════════════════════════════════════════════
   Password policy hint card — appears above the change-password form to tell
   the user the rules upfront. Uses the manage-card visual language with a
   clean bullet list of requirements.
   ════════════════════════════════════════════════════════════════════════════ */
.forge-manage-card--policy {
    background: color-mix(in oklab, var(--accent) 5%, var(--surface-s1, var(--bg-2)));
    border-color: color-mix(in oklab, var(--accent) 22%, var(--line));
}

.forge-password-policy {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: var(--s-2) var(--s-4);
    font-size: 13px;
    color: var(--fg-2);
}

.forge-password-policy li {
    position: relative;
    padding-inline-start: var(--s-4);
}

.forge-password-policy li::before {
    content: "✓";
    position: absolute;
    inset-inline-start: 0;
    top: 0;
    color: var(--accent);
    font-weight: 700;
}

/* ════════════════════════════════════════════════════════════════════════════
   Live password policy checklist — rendered directly under the New password
   input on the change-password page. Each row reflects ONE of the 5 ASP.NET
   Identity PasswordOptions rules (min length, uppercase, lowercase, digit,
   non-alphanumeric). Rows start in `.is-unmet` state (muted circle icon, fg-3
   text) and flip to `.is-met` when the rule is satisfied (green check icon,
   normal text). Smooth color transition makes the satisfaction visible.
   ════════════════════════════════════════════════════════════════════════════ */
.forge-password-checklist {
    list-style: none;
    margin: var(--s-2) 0 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: var(--s-1) var(--s-3);
    font-size: 13px;
}

.forge-password-checklist__item {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    line-height: 1.4;
    transition:
        color 180ms ease,
        opacity 180ms ease;
}

.forge-password-checklist__item.is-unmet {
    color: var(--fg-3);
    opacity: 0.85;
}

.forge-password-checklist__item.is-unmet svg {
    color: var(--fg-3);
    opacity: 0.6;
}

.forge-password-checklist__item.is-met {
    color: var(--ok, var(--fg-1));
    font-weight: var(--typography-weights-medium, 500);
}

.forge-password-checklist__item.is-met svg {
    color: var(--ok);
    /* Subtle pop when the rule is satisfied — small scale-in burst. */
    animation: forge-policy-met 320ms cubic-bezier(0.16, 1, 0.3, 1) both;
}

@keyframes forge-policy-met {
    0%   { transform: scale(0.6);  opacity: 0; }
    60%  { transform: scale(1.15); opacity: 1; }
    100% { transform: scale(1);    opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .forge-password-checklist__item,
    .forge-password-checklist__item.is-met svg {
        transition: none;
        animation: none;
    }
}

/* ════════════════════════════════════════════════════════════════════════════
   Page transition — single canonical entrance for every routed page. Each page
   wrapper that opts in via the `forge-page-fade-in` class gets the same arc
   (16 px lift + opacity, 280 ms cubic-bezier(0.16, 1, 0.3, 1) ease-out-expo).
   Setting it here in app.css overrides the per-page rules that drifted apart
   over time (Home + Dashboard at 280 ms, About at 600 ms, etc.). Reduced motion
   short-circuits to a flat opacity flip.
   ════════════════════════════════════════════════════════════════════════════ */
.forge-page-fade-in {
    animation: forge-page-fade-in 280ms cubic-bezier(0.16, 1, 0.3, 1) both;
}

@keyframes forge-page-fade-in {
    from {
        opacity: 0;
        transform: translateY(16px);
    }
    to {
        opacity: 1;
        /* `transform: none` — see comment on @keyframes forge-page-fade above.
           A zero-valued transform still creates a containing block for
           descendants, trapping position:fixed dialogs inside the page. */
        transform: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .forge-page-fade-in {
        animation: none;
        opacity: 1;
        transform: none;
    }
}

/* ════════════════════════════════════════════════════════════════════════════
   ForgeOnboardingLauncher — new chip-style design (icon + text "Průvodce").
   Replaces the earlier double-circle icon button (user flagged as too large
   and visually out of place next to the page title). The chip mirrors
   Forge's secondary CTAs — same vertical rhythm as the topbar's search /
   account chips, fits in the title row without dominating it.
   ════════════════════════════════════════════════════════════════════════════ */
.forge-onboarding-launcher__chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px 4px 8px;
  height: 26px;
  background: var(--accent-soft);
  color: var(--accent-base, var(--accent));
  border: 1px solid color-mix(in oklab, var(--accent-base, var(--accent)) 30%, transparent);
  border-radius: 999px;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.01em;
  line-height: 1;
  cursor: pointer;
  transition:
    background var(--motion-durationFast) var(--motion-easing),
    color var(--motion-durationFast) var(--motion-easing),
    transform var(--motion-durationFast) var(--motion-easing),
    border-color var(--motion-durationFast) var(--motion-easing);
}
.forge-onboarding-launcher__chip:hover {
  background: color-mix(in oklab, var(--accent-soft) 60%, var(--accent-base, var(--accent)));
  color: var(--accent-on-base, #fff);
  border-color: var(--accent-base, var(--accent));
}
.forge-onboarding-launcher__chip:active {
  transform: scale(0.96);
}
.forge-onboarding-launcher__chip:focus-visible {
  outline: 2px solid var(--accent-base, var(--accent));
  outline-offset: 2px;
}
.forge-onboarding-launcher__chip-label {
  font-feature-settings: "ss01" 1;
}

/* Completed state — green tick + softer green tint. */
.forge-onboarding-launcher__chip.is-completed {
  background: color-mix(in oklab, var(--ok, #4caf50) 12%, transparent);
  color: var(--ok, #4caf50);
  border-color: color-mix(in oklab, var(--ok, #4caf50) 28%, transparent);
}
.forge-onboarding-launcher__chip.is-completed:hover {
  background: color-mix(in oklab, var(--ok, #4caf50) 25%, transparent);
}

/* In-title variant: keep the same chip, just shrink a touch so it doesn't
   compete with the h1. */
.forge-onboarding-launcher--in-title .forge-onboarding-launcher__chip {
  height: 24px;
  padding: 3px 9px 3px 7px;
  font-size: 11.5px;
  font-weight: 600;
}

/* Reduced-motion: kill the active-scale animation. */
@media (prefers-reduced-motion: reduce) {
  .forge-onboarding-launcher__chip,
  .forge-onboarding-launcher__chip:active {
    transition: none;
    transform: none;
  }
}

/* ─────────────────────────────────────────────────────────────────────
   Welcome page — setup wizard stepper + split layout.

   Stepper renders a horizontal track of step bullets above the form.
   States — is-done, is-active, is-pending — drive a token-aware tint so
   the same component re-skins with every accent / theme switch. The
   labels collapse to icon-only under 640 px so the stepper stays inside
   narrow viewports without truncating mid-word.
   ───────────────────────────────────────────────────────────────────── */
.forge-wizard-stepper {
  display: flex;
  align-items: stretch;
  gap: var(--spacing-2);
  padding: var(--spacing-2) 0 var(--spacing-3);
  border-bottom: 1px solid var(--border-b1);
  margin-bottom: var(--spacing-3);
  overflow-x: auto;
}
.forge-wizard-stepper__step {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-2);
  flex: 1 1 auto;
  min-width: 0;
  position: relative;
}
.forge-wizard-stepper__step + .forge-wizard-stepper__step::before {
  content: "";
  position: absolute;
  left: -10px;
  top: 50%;
  width: 8px;
  height: 1px;
  background: var(--border-b1);
}
.forge-wizard-stepper__dot {
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border: 2px solid var(--border-b1);
  background: var(--surface-s1);
  color: var(--text-t3);
  font-weight: 600;
  font-size: 12px;
  flex-shrink: 0;
}
.forge-wizard-stepper__label {
  font-size: 12px;
  color: var(--text-t3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.forge-wizard-stepper__step.is-done .forge-wizard-stepper__dot {
  background: var(--accent);
  color: var(--accent-fg, white);
  border-color: var(--accent);
}
.forge-wizard-stepper__step.is-active .forge-wizard-stepper__dot {
  border-color: var(--accent);
  color: var(--accent);
}
.forge-wizard-stepper__step.is-active .forge-wizard-stepper__label,
.forge-wizard-stepper__step.is-done .forge-wizard-stepper__label {
  color: var(--text-t1);
  font-weight: 500;
}
@media (max-width: 639.98px) {
  .forge-wizard-stepper__label {
    display: none;
  }
}

/* Two-column page layout used on /welcome — main checklist + side card. */
.forge-page__split {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--spacing-4);
}
@media (min-width: 960px) {
  .forge-page__split {
    grid-template-columns: 2fr 1fr;
    align-items: start;
  }
}
.forge-page__split-aside .forge-card__bullets {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-2);
}
.forge-page__split-aside .forge-card__bullets li {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-2);
  font-size: 13px;
}
.forge-page__split-aside .forge-card__bullets a {
  color: var(--accent);
  text-decoration: none;
}
.forge-page__split-aside .forge-card__bullets a:hover {
  text-decoration: underline;
}

/* Wizard form-helper classes — stack with variable gap + radio group. */
.forge-stack {
  display: flex;
  flex-direction: column;
}
.forge-stack--gap-3 { gap: var(--spacing-3); }
.forge-stack--gap-4 { gap: var(--spacing-4); }

.forge-fieldset {
  border: 1px solid var(--border-b1);
  border-radius: var(--radius-md);
  padding: var(--spacing-3);
  margin: 0;
}
.forge-fieldset__legend {
  padding: 0 var(--spacing-2);
  font-size: 12px;
  font-weight: 600;
  color: var(--text-t3);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.forge-radio-group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-3);
}
.forge-radio {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-1);
  font-size: 13px;
  cursor: pointer;
}

/* Review list — definition-list shaped, two-column responsive. */
.forge-review-list {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--spacing-2) var(--spacing-3);
  margin: 0;
}
.forge-review-list dt {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-t3);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.forge-review-list dd {
  margin: 0;
  font-size: 14px;
  color: var(--text-t1);
}

/* In-page checklist button — match the link styling so the visual rhythm
   doesn't change between "navigate" (anchor) and "open dialog" (button)
   actions in the same checklist. */
.forge-checklist__action-button {
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
  font: inherit;
}
.forge-checklist__action-button:hover {
  text-decoration: underline;
}

/* ─────────────────────────────────────────────────────────────────────
   /offline-todos — counter strip + list. Counters are pill-style cards
   in a responsive grid; the warning + danger states tint via accent +
   semantic warn/danger so the same component re-skins with theme switches.
   ───────────────────────────────────────────────────────────────────── */
.forge-offline-counters {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: var(--spacing-3);
  margin: var(--spacing-3) 0 var(--spacing-4);
}
.forge-offline-counter {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--spacing-3);
  border: 1px solid var(--border-b1);
  border-radius: var(--radius-md);
  background: var(--surface-s1);
}
.forge-offline-counter__value {
  font-size: 24px;
  font-weight: 700;
  color: var(--text-t1);
  font-variant-numeric: tabular-nums;
}
.forge-offline-counter__label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-t3);
}
.forge-offline-counter.is-warning {
  background: color-mix(in oklab, var(--warn, #f59e0b) 8%, var(--surface-s1));
  border-color: color-mix(in oklab, var(--warn, #f59e0b) 28%, transparent);
}
.forge-offline-counter.is-warning .forge-offline-counter__value {
  color: var(--warn, #f59e0b);
}
.forge-offline-counter.is-danger {
  background: color-mix(in oklab, var(--bad, #ef4444) 8%, var(--surface-s1));
  border-color: color-mix(in oklab, var(--bad, #ef4444) 28%, transparent);
}
.forge-offline-counter.is-danger .forge-offline-counter__value {
  color: var(--bad, #ef4444);
}

.forge-offline-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-2);
}
.forge-offline-list__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-3);
  padding: var(--spacing-3);
  border: 1px solid var(--border-b1);
  border-radius: var(--radius-md);
  background: var(--surface-s1);
}
.forge-offline-list__item--failed {
  border-color: color-mix(in oklab, var(--bad, #ef4444) 30%, transparent);
  background: color-mix(in oklab, var(--bad, #ef4444) 6%, var(--surface-s1));
}
.forge-offline-list__main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.forge-offline-list__meta {
  font-size: 12px;
  color: var(--text-t3);
}
.forge-offline-list__actions {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-2);
  flex-shrink: 0;
}

.forge-page__section-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-t1);
  margin: 0;
}

/* Realtime activity feed banner — pulsing live-dot indicator.
   Used by OfflineTodosPage.razor when SignalR pushes land. */
.forge-realtime-feed__pulse {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 0 var(--accent);
  animation: forge-realtime-pulse 2s infinite;
  flex-shrink: 0;
}

@keyframes forge-realtime-pulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 60%, transparent); }
  70%  { box-shadow: 0 0 0 8px color-mix(in srgb, var(--accent) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 0%, transparent); }
}

@media (prefers-reduced-motion: reduce) {
  .forge-realtime-feed__pulse { animation: none; }
}

/* P.3 — pending-sync banner above the /todo list.
   Renders only when Outbox.CountPendingAsync > 0. Forge-timeless layout:
   accent-tinted soft surface, decorative pulse-ring on the cloud-off
   icon (gated by [data-motion="reduced"] + prefers-reduced-motion),
   inline CTA on the right. Forge token vocabulary throughout — flips
   across the 5 accent presets and light/dark theme without hardcoded
   colours. */
.forge-pending-sync-banner {
  display: flex;
  align-items: center;
  gap: var(--spacing-3);
  padding: 12px 16px;
  margin: 0 0 var(--spacing-4);
  border-radius: 14px;
  border: 1px solid color-mix(in oklch, var(--accent) 22%, transparent);
  background: color-mix(in oklch, var(--accent) 6%, var(--surface-s1));
  box-shadow: 0 1px 0 0 color-mix(in oklch, var(--accent) 8%, transparent) inset;
}

.forge-pending-sync-banner__mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 10px;
  background: color-mix(in oklch, var(--accent) 14%, var(--surface-s2));
  color: var(--accent-strong);
  flex-shrink: 0;
  position: relative;
}

.forge-pending-sync-banner__mark::after {
  /* Subtle outward pulse — same vocabulary as forge-realtime-feed__pulse.
     Tells the user "data is waiting to leave" without screaming for
     attention. */
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: 0 0 0 0 var(--accent);
  animation: forge-pending-sync-pulse 2.6s ease-out infinite;
  pointer-events: none;
}

.forge-pending-sync-banner__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 0;
}

.forge-pending-sync-banner__title {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-t1);
  line-height: 1.3;
}

.forge-pending-sync-banner__hint {
  font-size: 12.5px;
  color: var(--text-t3);
  line-height: 1.4;
}

.forge-pending-sync-banner__link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 12px;
  border-radius: 999px;
  background: color-mix(in oklch, var(--accent) 16%, transparent);
  color: var(--accent-strong);
  font-size: 12.5px;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--motion-durationBase, 200ms) var(--motion-easing, ease);
}

.forge-pending-sync-banner__link:hover,
.forge-pending-sync-banner__link:focus-visible {
  background: color-mix(in oklch, var(--accent) 24%, transparent);
  outline: none;
}

@keyframes forge-pending-sync-pulse {
  0% {
    box-shadow: 0 0 0 0 color-mix(in oklch, var(--accent) 60%, transparent);
  }
  60%, 100% {
    box-shadow: 0 0 0 8px color-mix(in oklch, var(--accent) 0%, transparent);
  }
}

/* Forge motion + effects gates — disable the pulse animation when the
   user picked Reduced motion in the Tweaks panel (data-motion="reduced")
   OR when the OS-level prefers-reduced-motion is on AND the user hasn't
   explicitly opted into Full motion. Also flatten the soft inset
   shadow under [data-effects="minimal"] so the banner reads as a
   plain bordered chip. */
:root[data-motion="reduced"] .forge-pending-sync-banner__mark::after {
  animation: none;
}
@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion="full"]) .forge-pending-sync-banner__mark::after {
    animation: none;
  }
}
:root[data-effects="minimal"] .forge-pending-sync-banner {
  box-shadow: none;
}

/* P.3b / Q.1 — per-row "Pending sync" pill on offline-saved TodoItem rows.
   Visual vocabulary matches the page-level forge-pending-sync-banner:
   accent-tinted background, soft border, pulse-ring on the icon. The pill
   is smaller (font-size: 10.5px, padding: 3px 8px) so it sits cleanly
   next to the status chip in the card header without dominating it. */
.forge-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 8px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
  border: 1px solid color-mix(in oklch, var(--accent) 22%, transparent);
  background: color-mix(in oklch, var(--accent) 8%, var(--surface-s1));
  color: var(--accent-strong);
}

.forge-pill--pending-sync {
  /* Soft outward pulse on the cloud-off icon mirrors the page banner.
     Subtle by design — the user already saw the snackbar + banner; the
     pill is a calm persistent reminder. */
  position: relative;
}
.forge-pill--pending-sync::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  box-shadow: 0 0 0 0 var(--accent);
  pointer-events: none;
  animation: forge-pending-sync-pulse 2.6s ease-out infinite;
}

/* Q.13 — failed sync pill. Same shape as pending-sync but uses the --bad
   token vocabulary (semantic danger color, theme-aware). The pulse is
   slightly faster (1.8s vs 2.6s) to read as "needs attention" without
   tripping into anxiety territory — Forge's "timeless awesome design"
   keeps urgency calm. */
.forge-pill--failed {
  position: relative;
  border-color: color-mix(in oklch, var(--bad) 34%, transparent);
  background: color-mix(in oklch, var(--bad) 10%, var(--surface-s1));
  color: var(--bad);
}
.forge-pill--failed::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  box-shadow: 0 0 0 0 var(--bad);
  pointer-events: none;
  animation: forge-failed-pulse 1.8s ease-out infinite;
}

@keyframes forge-failed-pulse {
  0%   { box-shadow: 0 0 0 0   color-mix(in oklch, var(--bad) 38%, transparent); }
  70%  { box-shadow: 0 0 0 7px color-mix(in oklch, var(--bad)  0%, transparent); }
  100% { box-shadow: 0 0 0 0   color-mix(in oklch, var(--bad)  0%, transparent); }
}

:root[data-motion="reduced"] .forge-pill--pending-sync::before,
:root[data-motion="reduced"] .forge-pill--failed::before {
  animation: none;
}
@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion="full"]) .forge-pill--pending-sync::before,
  :root:not([data-motion="full"]) .forge-pill--failed::before {
    animation: none;
  }
}

/* R.2 — "pending delete" pill: a server record the user deleted while offline,
   queued in the outbox and waiting for the drain to remove it server-side.
   Danger vocabulary (--bad token, theme-aware), trash icon, NO pulse — a
   delete is a calm, settled intent, not an alarm (contrast the --failed pill,
   which pulses because it genuinely needs attention). */
.forge-pill--pending-delete {
  border-color: color-mix(in oklch, var(--bad) 32%, transparent);
  background: color-mix(in oklch, var(--bad) 9%, var(--surface-s1));
  color: var(--bad);
}

/* R.2 — a row with a queued Delete stays VISIBLE but reads as "on its way
   out": gently faded so it recedes without disappearing. The danger pill is
   the bright semantic anchor; the fade is calm (0.7, not a harsh strike) per
   Forge's timeless-awesome restraint. Survives until the drain completes and
   the realtime push / LoadAsync drops the row. Works on both the card root
   and the table <tr> (opacity needs no positioning). */
.is-pending-delete {
  opacity: 0.7;
  transition: opacity var(--motion-durationBase, 200ms) var(--motion-easing, ease);
}
:root[data-motion="reduced"] .is-pending-delete {
  transition: none;
}

/* Q.13 — clickable counter chips on /offline-todos. The existing
   .forge-offline-counter renders four KPI tiles; making them act as
   filter toggles for "show cached / pending / failed / all" is the
   highest-impact UX touch the user asked for ("kliknutim na LOKALNE V
   CACHE CEKAJICI OUTBOX, SELHALO"). The non-active counters fade slightly
   and the active one gets an accent ring. */
.forge-offline-counter[data-filterable="true"] {
  cursor: pointer;
  transition: background var(--motion-durationFast) var(--motion-easing),
              border-color var(--motion-durationFast) var(--motion-easing),
              opacity var(--motion-durationFast) var(--motion-easing);
}
.forge-offline-counter[data-filterable="true"]:hover {
  background: color-mix(in oklch, var(--accent) 4%, var(--surface-s1));
}
.forge-offline-counter[data-filterable="true"][aria-pressed="false"] {
  opacity: 0.62;
}
.forge-offline-counter[data-filterable="true"][aria-pressed="true"] {
  border-color: color-mix(in oklch, var(--accent) 42%, transparent);
  background: color-mix(in oklch, var(--accent) 9%, var(--surface-s1));
}
