/* ============================================================================
   HINOVEST — Components (calm institutional)
   ============================================================================ */

*, *::before, *::after { box-sizing: border-box; }

html, body { height: 100%; }
body {
    margin: 0;
    background: var(--color-void);
    color: var(--color-text-primary);
    font-family: var(--font-display);
    font-size: var(--text-base);
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    font-feature-settings: "cv05" 1, "ss01" 1;
}
a { color: inherit; text-decoration: none; }
button, input, select, textarea { font-family: inherit; }

/* Icon glyphs sit in an empty <i>, so their box height comes from body's font
   metrics, not the glyph. Vazirmatn's deep descent (see the RTL block below)
   inflates that box in Farsi and the glyph rides high in the extra space —
   line-height: 1 pins the box to the font-size, in both directions. */
.bi { line-height: 1; }

/* Mono numerals everywhere financial */
.mono, .num {
    font-family: var(--font-mono);
    font-feature-settings: "tnum" 1, "zero" 1;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.01em;
}

::selection { background: var(--color-accent-muted); }

/* Scrollbars — quiet */
* { scrollbar-width: thin; scrollbar-color: var(--color-border-strong) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-thumb {
    background: var(--color-border-mid);
    border: 2px solid transparent;
    background-clip: padding-box;
    border-radius: var(--radius-pill);
}
*::-webkit-scrollbar-thumb:hover { background: var(--color-border-strong); background-clip: padding-box; }

/* Universal focus-visible — an outline, not a halo.
   It used to be a two-layer box-shadow with a hard 4px ring, plus a
   border-radius forced onto whatever it landed on: around an icon, a row or a
   swatch that reads as a blue box drawn over the layout rather than a marker on
   the thing you are on. It also showed up in screenshots, because pressing a
   key — any key, a screenshot shortcut included — puts the browser in keyboard
   modality and the focused element lights up.

   outline follows the element's own border-radius and is never clipped by an
   ancestor's overflow, so it needs no radius of its own and no second layer to
   separate it from the background. 2px at a 2px offset still clears WCAG's
   focus-appearance minimum. */
:focus-visible {
    outline: 2px solid var(--color-accent-primary);
    outline-offset: 2px;
    box-shadow: none;
}

/* The sidebar is the first focusable thing on every page: eleven nav links plus
 * the topbar stand between a keyboard user and the content. Park the escape
 * hatch off-screen until it takes focus. */
.skip-link {
    position: fixed;
    inset-block-start: var(--space-3);
    inset-inline-start: var(--space-3);
    z-index: 300;
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-sm);
    background: var(--color-accent-primary);
    color: var(--color-accent-ink);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    text-decoration: none;
    transform: translateY(-200%);
    transition: transform 120ms ease;
}
/* Plain :focus, not :focus-visible — the skip link must appear for any focus that
 * reaches it, including programmatic focus restoration. */
.skip-link:focus { transform: translateY(0); }
#mainContent:focus { outline: none; }

/* A "back" arrow points at the start edge, which flips under RTL. */
html[dir="rtl"] .auth-back-icon { transform: scaleX(-1); }

/* Inputs that live INSIDE a styled pill (search boxes, tag fields): the universal ring
 * above draws a hard 4px halo around the bare input, stranded inside its own container.
 * Ring the pill instead — the treatment .tag-field already used. */
.topbar__search input:focus-visible,
.toolbar__search input:focus-visible,
.settings-filter input:focus-visible,
.tag-field input:focus-visible { box-shadow: none; }

.topbar__search,
.toolbar__search,
.settings-filter { transition: border-color 130ms ease, box-shadow 130ms ease, background 130ms ease; }
.topbar__search:hover,
.toolbar__search:hover,
.settings-filter:hover { border-color: var(--color-border-mid); }
.topbar__search:focus-within,
.toolbar__search:focus-within,
.settings-filter:focus-within {
    border-color: var(--color-accent-border);
    box-shadow: 0 0 0 3px var(--color-accent-muted);
    background: var(--color-surface-2);
}

/* ============================================================================
   SHELL
   ============================================================================ */
.shell { display: flex; min-height: 100vh; }

/* ---- Workspace backdrop ----
   The page behind the cards is not blank, and it is not wallpaper either: two
   soft washes pulled from the theme's own palette, and one equity curve lying
   along the bottom edge — the same line the dashboard draws, at a size and an
   opacity that make it scenery. It is the one graphic this product is about,
   so it is the one worth putting back there.

   Nothing crosses the reading area. An earlier version ruled the whole page
   like chart paper, and every menu that renders inline without a card of its
   own — the settings rail especially — ended up with grid lines running
   through its labels.

   Fixed, so the curve stays put while the content scrolls over it. z-index
   below zero rather than 0: .main and .content are not positioned, so a
   z-index: 0 pseudo-element would paint over their text. .shell is a plain
   flex container and makes no stacking context of its own, so the negative
   layers land between the body's background and everything in the shell.

   Behind a login only. The home, sign-in and sign-up pages have a backdrop of
   their own and .app-shell is exactly what tells the two apart. */
.app-shell::before,
.app-shell::after {
    content: "";
    position: fixed;
    pointer-events: none;
}
.app-shell::before {
    inset: 0;
    z-index: -2;
    background:
        radial-gradient(1200px 760px at 88% -10%, var(--bg-wash-a), transparent 60%),
        radial-gradient(1000px 680px at 2% 106%, var(--bg-wash-b), transparent 58%);
}
/* The curve is a mask, not an image: the shape is fixed and the colour comes
   from the theme, so a palette change repaints it with no second asset. */
.app-shell::after {
    inset: auto 0 0 0;
    height: min(46vh, 460px);
    z-index: -1;
    background: var(--bg-wash-a);
    -webkit-mask: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%201440%20400'%20preserveAspectRatio='none'%3E%3Cpath%20d='M0%20332%20C%2090%20322%20150%20300%20220%20306%20C%20300%20313%20342%20268%20420%20258%20C%20500%20248%20542%20288%20620%20278%20C%20700%20268%20742%20206%20830%20200%20C%20920%20194%20962%20234%201040%20218%20C%201130%20200%201182%20120%201270%2096%20C%201342%2078%201392%2060%201440%2044%20L1440%20400%20L0%20400%20Z'%20fill='white'%20fill-opacity='.11'/%3E%3Cpath%20d='M0%20332%20C%2090%20322%20150%20300%20220%20306%20C%20300%20313%20342%20268%20420%20258%20C%20500%20248%20542%20288%20620%20278%20C%20700%20268%20742%20206%20830%20200%20C%20920%20194%20962%20234%201040%20218%20C%201130%20200%201182%20120%201270%2096%20C%201342%2078%201392%2060%201440%2044'%20fill='none'%20stroke='white'%20stroke-width='2.5'%20stroke-linecap='round'%20stroke-linejoin='round'/%3E%3C/svg%3E") bottom / 100% 100% no-repeat;
            mask: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%201440%20400'%20preserveAspectRatio='none'%3E%3Cpath%20d='M0%20332%20C%2090%20322%20150%20300%20220%20306%20C%20300%20313%20342%20268%20420%20258%20C%20500%20248%20542%20288%20620%20278%20C%20700%20268%20742%20206%20830%20200%20C%20920%20194%20962%20234%201040%20218%20C%201130%20200%201182%20120%201270%2096%20C%201342%2078%201392%2060%201440%2044%20L1440%20400%20L0%20400%20Z'%20fill='white'%20fill-opacity='.11'/%3E%3Cpath%20d='M0%20332%20C%2090%20322%20150%20300%20220%20306%20C%20300%20313%20342%20268%20420%20258%20C%20500%20248%20542%20288%20620%20278%20C%20700%20268%20742%20206%20830%20200%20C%20920%20194%20962%20234%201040%20218%20C%201130%20200%201182%20120%201270%2096%20C%201342%2078%201392%2060%201440%2044'%20fill='none'%20stroke='white'%20stroke-width='2.5'%20stroke-linecap='round'%20stroke-linejoin='round'/%3E%3C/svg%3E") bottom / 100% 100% no-repeat;
}
/* A phone is mostly card, and the curve would only ever be seen through the
   gaps between them. Washes alone. */
@media (max-width: 720px) {
    .app-shell::after { display: none; }
}

/* ---- Sidebar ---- */
.sidebar {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    width: var(--sidebar-w);
    z-index: 40;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-4) var(--space-3);
    background: var(--color-surface-0);
    border-inline-end: 1px solid var(--color-border-subtle);
    transition: width 140ms cubic-bezier(0.22, 1, 0.36, 1);
}
.shell.is-collapsed .sidebar { width: var(--sidebar-w-collapsed); }

.brand {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    height: 40px;
    padding: 0 var(--space-2);
    margin-bottom: var(--space-2);
    overflow: hidden;
}
.brand__mark {
    flex: 0 0 30px;
    width: 30px; height: 30px;
    border-radius: var(--radius);
    display: grid; place-items: center;
    background: linear-gradient(160deg, var(--color-accent-glow), var(--color-accent-deep));
    color: var(--color-accent-ink);
    font-weight: var(--weight-bold);
    font-size: 15px;
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.25);
}
.brand__word {
    font-weight: var(--weight-bold);
    font-size: var(--text-md);
    letter-spacing: var(--tracking-wider);
    white-space: nowrap;
    background: linear-gradient(180deg, var(--color-accent-glow), var(--color-accent-deep));
    -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent;
}
.shell.is-collapsed .brand__word { opacity: 0; pointer-events: none; }

.nav { display: flex; flex-direction: column; gap: 1px; }
.nav__label {
    padding: var(--space-4) var(--space-3) var(--space-2);
    color: var(--color-text-tertiary);
    font-size: var(--text-2xs);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-wider);
    text-transform: uppercase;
    white-space: nowrap;
    overflow: hidden;
}
.shell.is-collapsed .nav__label { height: 12px; padding-block: 8px 4px; color: transparent; }

.nav__item {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    height: 36px;
    padding: 0 var(--space-3);
    border-radius: var(--radius-sm);
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    cursor: pointer;
    white-space: nowrap;
    transition: background 110ms ease, color 110ms ease;
}
.nav__item .ico { flex: 0 0 18px; width: 18px; text-align: center; font-size: 16px; opacity: 0.85; }
.nav__item span:not(.ico) { overflow: hidden; }
.shell.is-collapsed .nav__item span:not(.ico) { opacity: 0; }
.nav__item:hover { background: var(--color-surface-2); color: var(--color-text-primary); }
.nav__item.is-active { background: var(--color-accent-muted); color: var(--color-accent-glow); }
.nav__item.is-active .ico { opacity: 1; }
.nav__item.is-active::before {
    content: ""; position: absolute;
    inset-block: 8px; inset-inline-start: 0;
    width: 3px; border-radius: var(--radius-pill);
    background: var(--color-accent-primary);
}
.nav__badge {
    margin-inline-start: auto;
    min-width: 20px; height: 18px; padding: 0 6px;
    border-radius: var(--radius-pill);
    background: var(--color-surface-3);
    color: var(--color-text-secondary);
    font-size: var(--text-2xs); font-weight: var(--weight-semibold);
    display: grid; place-items: center;
}
.shell.is-collapsed .nav__badge { display: none; }

.sidebar__spacer { flex: 1; }

.userpill {
    display: flex; align-items: center; gap: var(--space-3);
    padding: var(--space-2);
    border-radius: var(--radius);
    border: 1px solid var(--color-border-subtle);
    background: var(--color-surface-1);
    overflow: hidden;
}
.userpill__avatar {
    flex: 0 0 32px; width: 32px; height: 32px;
    border-radius: var(--radius-pill);
    display: grid; place-items: center;
    background: var(--color-surface-3);
    color: var(--color-text-primary);
    font-weight: var(--weight-semibold); font-size: var(--text-sm);
}
/* flex: 1 so the name block absorbs the free space and the logout button lands at the
   pill's trailing edge instead of hugging the text. */
/* ---- Global search dropdown ---- */
.topbar__search-wrap { position: relative; display: flex; min-width: 0; }
.search-results {
    position: absolute; top: calc(100% + 6px); inset-inline-start: 0;
    width: min(420px, 92vw); max-height: 62vh; overflow-y: auto; z-index: 1200;
    padding: var(--space-2);
    border: 1px solid var(--color-border-mid); border-radius: var(--radius);
    background: var(--color-surface-1); box-shadow: var(--shadow-float);
}
.search-results__empty { margin: 0; padding: var(--space-3); font-size: var(--text-sm); color: var(--color-text-tertiary); }
.search-group + .search-group { margin-top: var(--space-2); border-top: 1px solid var(--color-border-subtle); padding-top: var(--space-2); }
.search-group__head {
    display: flex; align-items: center; gap: 6px; padding: 4px var(--space-2);
    font-size: var(--text-2xs); font-weight: 700; text-transform: uppercase;
    letter-spacing: var(--tracking-wide); color: var(--color-text-tertiary);
}
.search-group__head span { margin-inline-start: auto; font-family: var(--font-mono); font-weight: 500; }
/* "Clear all" sits in the notifications header, after the count. */
.notifications-clear {
    margin-inline-start: var(--space-2); padding: 0; border: 0; background: none;
    font: inherit; text-transform: none; letter-spacing: 0; cursor: pointer;
    color: var(--color-text-tertiary);
}
.notifications-clear:hover { color: var(--color-loss); }
/* The bell is a peek: one line per notice, however long the message is. The
   whole text is on /notifications/, which is what "See all" is for. */
.notifications-panel .search-hit__sub {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The way out of the peek and into the full list. */
.notifications-all {
    display: block; padding: 6px var(--space-2); margin-top: var(--space-2);
    border-top: 1px solid var(--color-border-subtle);
    font-size: var(--text-xs); text-align: center; text-decoration: none;
    color: var(--color-accent-glow);
}
.notifications-all:hover { background: var(--color-accent-muted); }
.search-hit {
    display: flex; flex-direction: column; gap: 1px;
    padding: 6px var(--space-2); border-radius: var(--radius-sm);
    text-decoration: none; color: var(--color-text-primary);
}
.search-hit:hover, .search-hit.is-cursor { background: var(--color-accent-muted); }
.search-hit__label { font-size: var(--text-sm); }
.search-hit__sub { font-size: var(--text-2xs); color: var(--color-text-tertiary); }

/* ---- Notifications bell ---- */
/* The topbar's trailing controls, as one row with its own rhythm. The gap is
   the 8px minimum between touch targets — the clustering comes from the
   hairline, not from crowding the buttons together. */
.topbar__tools { display: flex; align-items: center; gap: var(--space-2); }
.topbar__bell-wrap { position: relative; display: flex; }
.notifications-panel { inset-inline-start: auto; inset-inline-end: 0; width: min(360px, 92vw); }
/* The unread marker: a coloured dot with the count in it, ringed in the bar's
   own background so it reads as a dot sitting on the bell rather than a smudge
   on the icon. --color-danger was never defined, so this was quietly falling
   back to the accent and looked like decoration. */
.topbar__bell-badge {
    position: absolute; inset-block-start: 0; inset-inline-end: 0;
    min-width: 16px; height: 16px; padding: 0 4px; border-radius: 999px;
    display: flex; align-items: center; justify-content: center;
    font-family: var(--font-mono); font-size: var(--text-2xs); font-weight: 700; line-height: 1;
    background: var(--color-loss); color: #fff;
    box-shadow: 0 0 0 2px var(--color-surface-1);
    /* A few pulses when the count appears, then rest — a badge that never
       stops animating charges its attention cost forever. */
    animation: bell-pulse 2s ease-out 3;
}
@keyframes bell-pulse {
    0%   { box-shadow: 0 0 0 2px var(--color-surface-1), 0 0 0 2px var(--color-loss-muted); }
    70%  { box-shadow: 0 0 0 2px var(--color-surface-1), 0 0 0 8px transparent; }
    100% { box-shadow: 0 0 0 2px var(--color-surface-1), 0 0 0 8px transparent; }
}
@media (prefers-reduced-motion: reduce) {
    .topbar__bell-badge { animation: none; }
}
/* And one dot per unread line, so the panel says which of them are new. */
.search-hit.is-unread .search-hit__label { font-weight: 600; }
.search-hit.is-unread .search-hit__label::before {
    content: ""; display: inline-block; vertical-align: middle;
    width: 7px; height: 7px; margin-inline-end: 6px; border-radius: 999px;
    background: var(--color-loss);
}

.userpill__meta { flex: 1; min-width: 0; overflow: hidden; }
.userpill__logout { flex: none; margin-inline-start: auto; }
.userpill__meta strong { display: block; font-size: var(--text-sm); font-weight: var(--weight-semibold); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.userpill__meta span { display: block; font-size: var(--text-xs); color: var(--color-text-tertiary); white-space: nowrap; }
.shell.is-collapsed .userpill__meta { display: none; }

/* ---- Main column ---- */
.main {
    flex: 1;
    min-width: 0;
    margin-inline-start: var(--sidebar-w);
    transition: margin-inline-start 140ms cubic-bezier(0.22, 1, 0.36, 1);
    display: flex;
    flex-direction: column;
}
.shell.is-collapsed .main { margin-inline-start: var(--sidebar-w-collapsed); }

/* ---- Topbar ---- */
.topbar {
    position: sticky; top: 0; z-index: 30;
    height: var(--topbar-h);
    display: flex; align-items: center; gap: var(--space-3);
    padding: 0 var(--space-5);
    background: color-mix(in srgb, var(--color-void) 88%, transparent);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border-bottom: 1px solid var(--color-border-subtle);
}
.topbar__toggle {
    width: 32px; height: 32px; flex: 0 0 32px;
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-sm);
    background: transparent; color: var(--color-text-secondary);
    display: grid; place-items: center; cursor: pointer;
    transition: background 110ms ease, color 110ms ease, border-color 110ms ease;
}
.topbar__toggle:hover { background: var(--color-surface-2); color: var(--color-text-primary); }

.crumb { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); color: var(--color-text-tertiary); }
.crumb b { color: var(--color-text-primary); font-weight: var(--weight-semibold); }
.crumb .sep { opacity: 0.5; }

.topbar__search {
    display: flex; align-items: center; gap: var(--space-2);
    width: min(320px, 30vw);
    height: 34px; padding: 0 var(--space-3);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-sm);
    background: var(--color-surface-1);
    color: var(--color-text-tertiary);
    margin-inline-start: var(--space-4);
}
.topbar__search input {
    flex: 1; min-width: 0; border: 0; background: transparent;
    color: var(--color-text-primary); font-size: var(--text-sm); outline: none;
}
.topbar__search .bi-search { color: var(--color-text-tertiary); transition: color 130ms ease; }
.topbar__search:focus-within .bi-search { color: var(--color-accent-glow); }
.topbar__spacer { flex: 1; }

/* ---- Workspace tabs ---- */
/* Frozen under the topbar, which is itself sticky at 0: the strip is how you
   change page, and it was scrolling away exactly when a long page made you want
   it. box-sizing is border-box everywhere, so the topbar's height token already
   covers its border and there is no seam to close. Below the topbar's z-index,
   so its blur and its dropdowns still pass over this. */
/* The bar is the wrapper; the nav inside it is only the part that scrolls, so
   the arrows can stand outside the scroll and stay put. */
.tabs-bar {
    position: sticky; top: var(--topbar-h); z-index: 29;
    display: flex; align-items: center; gap: var(--space-1);
    padding: var(--space-2) var(--space-5) 0;
    border-bottom: 1px solid var(--color-border-subtle);
    background: var(--color-void);
}
.tabs {
    display: flex; align-items: center; gap: var(--space-1);
    flex: 1;
    /* Without this a flex child refuses to go below its content width, and the
       strip pushed the whole page wider instead of scrolling inside itself. */
    min-width: 0;
    /* Sideways only. One axis auto and the other visible makes the visible one
       auto too, which left the strip a whole scrollable pixel tall -- and the
       wheel spent it, sliding the tabs up and down inside their own bar. */
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
/* Shown only while the strip has more tabs than it can hold -- see
   syncTabOverflow in app-shell.js. */
.tabs-bar__scroll {
    flex: 0 0 auto;
    align-self: flex-end;
    display: grid;
    place-items: center;
    width: 26px;
    height: 38px;
    padding: 0;
    border: 1px solid var(--color-border-subtle);
    border-bottom: 0;
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    background: var(--color-surface-0);
    color: var(--color-text-secondary);
    cursor: pointer;
    transition: color 110ms ease, background 110ms ease, border-color 110ms ease;
}
.tabs-bar__scroll:hover:not(:disabled) {
    background: var(--color-surface-1);
    color: var(--color-text-primary);
    border-color: var(--color-accent-border);
}
.tabs-bar__scroll:disabled {
    opacity: 0.35;
    cursor: default;
}
/* One pair of chevrons, mirrored: the start of the strip is the right-hand
   side when the page reads right to left. */
html[dir="rtl"] .tabs-bar__scroll .bi { transform: scaleX(-1); }
.tab {
    position: relative;
    display: inline-flex; align-items: center; gap: var(--space-2);
    height: 38px; padding: 0 var(--space-3);
    border: 0; background: transparent;
    color: var(--color-text-secondary);
    font-size: var(--text-sm); font-weight: var(--weight-medium);
    cursor: pointer; white-space: nowrap;
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    transition: color 110ms ease, background 110ms ease;
}
.tab .ico { font-size: 14px; opacity: 0.7; }
.tab:hover { color: var(--color-text-primary); background: var(--color-surface-1); }
.tab.is-active { color: var(--color-text-primary); }
.tab.is-active::after {
    content: ""; position: absolute;
    inset-inline: 6px; bottom: -1px; height: 2px;
    background: var(--color-accent-primary); border-radius: 2px 2px 0 0;
}
.tab__close {
    width: 16px; height: 16px; padding: 0; border: 0; border-radius: var(--radius-xs);
    background: transparent; color: var(--color-text-tertiary);
    display: grid; place-items: center; cursor: pointer; font-size: 13px; line-height: 1;
    opacity: 0; transition: opacity 110ms ease, background 110ms ease;
}
.tab:hover .tab__close, .tab.is-active .tab__close { opacity: 1; }
.tab__close:hover { background: var(--color-surface-3); color: var(--color-text-primary); }

/* ---- Content scroll region ---- */
.content {
    flex: 1;
    padding: var(--space-6) var(--space-5) var(--space-12);
    max-width: 1480px;
    width: 100%;
    margin: 0 auto;
}

/* ============================================================================
   PAGE HEADER
   ============================================================================ */
.page-head {
    display: flex; align-items: flex-end; justify-content: space-between;
    gap: var(--space-4); flex-wrap: wrap;
    margin-bottom: var(--space-6);
}
.page-head__eyebrow {
    display: inline-flex; align-items: center; gap: var(--space-2);
    color: var(--color-text-tertiary);
    font-size: var(--text-xs); font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-wider); text-transform: uppercase;
    margin-bottom: var(--space-2);
}
/* Accent, not profit: green/red are reserved for money in a trading product. */
.page-head__eyebrow .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--color-accent-primary); }
.page-head h1 { margin: 0; font-size: var(--text-3xl); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tight); }
.page-head__sub { margin: var(--space-2) 0 0; color: var(--color-text-secondary); font-size: var(--text-sm); max-width: 60ch; }
/* One height for everything in the header's action row. A 36px button beside a
   32px segmented control beside a 42px button is the difference between a row
   of controls and three unrelated boxes that happen to be adjacent — and the
   three pages each used to pick a different number. */
.page-head__actions {
    display: flex; align-items: center; justify-content: flex-end;
    gap: var(--space-2); flex-wrap: wrap;
}
.page-head__actions > .btn,
.page-head__actions > .btn-group,
.page-head__actions > .segment { height: 38px; }
.page-head__actions > .btn-group > .btn { height: 100%; }
.page-head__actions > .segment { padding: 4px; }
.page-head__actions > .segment button { height: 100%; border-radius: calc(var(--radius-sm) - 4px); }
/* Room for a label, so "Export" and "Add trade" are the same size object. */
.page-head__actions > .btn { min-width: 104px; }
.page-head__actions > .btn-group > .btn { min-width: 0; }
/* On a phone the row is a stack, and a stack of buttons that each stop at
   their own text width is a ragged left edge. Give them the width instead. */
@media (max-width: 720px) {
    .page-head { align-items: flex-start; }
    .page-head__actions { width: 100%; }
    .page-head__actions > .btn,
    .page-head__actions > .btn-group,
    .page-head__actions > .segment { flex: 1 1 auto; }
    .page-head__actions > .btn-group > .btn { flex: 1 1 auto; }
    .page-head__actions > .segment button { flex: 1 1 auto; justify-content: center; }
}

/* ============================================================================
   BUTTONS
   ============================================================================ */
.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
    height: 36px; padding: 0 var(--space-4);
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
    font-size: var(--text-sm); font-weight: var(--weight-semibold);
    cursor: pointer; white-space: nowrap; user-select: none;
    transition: background 110ms ease, color 110ms ease, border-color 110ms ease, box-shadow 110ms ease, scale 110ms ease;
}
.btn:active { scale: 0.96; }
@media (prefers-reduced-motion: reduce) { .btn:active { scale: none; } }
.btn .ico { font-size: 15px; }
/* The one button on the page that is meant to be found first. The inner top
   highlight gives the fill an edge instead of a flat sticker, and the wide,
   tinted, downward shadow lifts it off the card without a second colour. */
.btn--primary {
    background: var(--color-accent-primary);
    color: var(--color-accent-ink);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.22),
        0 1px 2px rgba(0, 0, 0, 0.22),
        0 8px 18px -10px var(--color-accent-primary);
}
.btn--primary:hover {
    background: var(--color-accent-glow);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.26),
        0 1px 2px rgba(0, 0, 0, 0.22),
        0 10px 22px -10px var(--color-accent-primary);
}
.btn--primary:active { background: var(--color-accent-deep); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.28); }
/* Not transparent: a secondary action sitting beside a primary one has to read
   as a button, not as a link that happens to have a border. It gets a surface
   of its own, one step below the card it sits on. */
.btn--ghost {
    background: var(--color-surface-1);
    color: var(--color-text-primary);
    border-color: var(--color-border-mid);
    box-shadow: var(--shadow-sm);
}
.btn--ghost:hover { background: var(--color-surface-2); border-color: var(--color-border-strong); }
.btn--ghost:active { background: var(--color-surface-3); box-shadow: none; }
.btn--subtle { background: var(--color-surface-2); color: var(--color-text-primary); border-color: var(--color-border-subtle); }
.btn--subtle:hover { background: var(--color-surface-3); border-color: var(--color-border-mid); }
.btn--danger { background: var(--color-loss-muted); color: var(--color-loss); border-color: color-mix(in srgb, var(--color-loss) 32%, transparent); }
.btn--danger:hover { background: color-mix(in srgb, var(--color-loss) 20%, transparent); border-color: color-mix(in srgb, var(--color-loss) 50%, transparent); }
.btn--sm { height: 30px; padding: 0 var(--space-3); font-size: var(--text-xs); }
.btn[disabled] { opacity: 0.45; pointer-events: none; box-shadow: none; }

/* ---- Action clusters ----
   Buttons that belong together are joined into one control instead of being
   spaced apart like unrelated ones: a shared surface, hairlines between the
   members, and only the outer corners rounded. Used for the secondary actions
   in a page header, which used to sit as three same-weight boxes in a row with
   no clue which two were the pair. */
.btn-group {
    display: inline-flex;
    align-items: center;
    border: 1px solid var(--color-border-mid);
    border-radius: var(--radius-sm);
    background: var(--color-surface-1);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
}
.btn-group > .btn {
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
}
.btn-group > .btn + .btn { border-inline-start: 1px solid var(--color-border-subtle); }
.btn-group > .btn:hover { background: var(--color-surface-2); }
.btn-group > .btn:active { background: var(--color-surface-3); scale: none; }
/* The ring is drawn by box-shadow, which overflow: hidden on the group would
   clip to a sliver. Pull the focused member onto its own layer instead. */
.btn-group > .btn:focus-visible { position: relative; z-index: 1; border-radius: var(--radius-xs); }

/* A hairline between two clusters that do different jobs — filters on one
   side, actions on the other. Its own element rather than a ::before on the
   next button, so it survives the buttons being reordered or wrapped. */
.actions-rule {
    align-self: stretch;
    width: 1px;
    min-height: 24px;
    margin-inline: var(--space-1);
    background: var(--color-border-mid);
}
/* Wrapped onto its own line, a vertical rule between two rows means nothing. */
@media (max-width: 720px) { .actions-rule { display: none; } }

.iconbtn {
    width: 34px; height: 34px; flex: 0 0 34px;
    display: grid; place-items: center;
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-sm);
    background: var(--color-surface-1); color: var(--color-text-secondary);
    cursor: pointer; font-size: 16px;
    transition: background 110ms ease, color 110ms ease, border-color 110ms ease;
}
/* The glyph itself sits ~2px left of its own box centre — a bootstrap-icons
   quirk, same in every direction — so the box+arrow pictograph rides visibly
   off-centre inside a square button no matter how the box is centred. */
.bi-box-arrow-right { margin-left: 2px; }
.iconbtn:hover { background: var(--color-surface-2); color: var(--color-text-primary); border-color: var(--color-border-mid); }
.iconbtn.is-active { background: var(--color-accent-muted); color: var(--color-accent-glow); border-color: var(--color-accent-border); }

/* segmented control */
.segment {
    display: inline-flex; padding: 3px;
    background: var(--color-surface-1);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-sm);
    gap: 2px;
}
.segment button {
    /* Concentric with the 7px container: inner = outer − 3px padding. */
    height: 26px; padding: 0 var(--space-3); border: 0; border-radius: calc(var(--radius-sm) - 3px);
    background: transparent; color: var(--color-text-secondary);
    font-size: var(--text-xs); font-weight: var(--weight-semibold); cursor: pointer;
    display: inline-flex; align-items: center; gap: 6px;
    transition: background 110ms ease, color 110ms ease;
}
.segment button:hover { color: var(--color-text-primary); }
.segment button.is-active { background: var(--color-surface-3); color: var(--color-text-primary); }

/* ============================================================================
   CARDS
   ============================================================================ */
.card {
    background: var(--color-surface-1);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
}
.card__head {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: var(--space-4); padding: var(--space-5) var(--space-5) 0;
}
.card__eyebrow {
    color: var(--color-text-tertiary);
    font-size: var(--text-2xs); font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-wider); text-transform: uppercase;
}
.card__head h2,
.card__head h3 { margin: 4px 0 0; font-size: var(--text-lg); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-tight); }
.card__head p { margin: 4px 0 0; color: var(--color-text-secondary); font-size: var(--text-sm); }
.card__body { padding: var(--space-5); }

/* ============================================================================
   KPI STRIP
   ============================================================================ */
.kpi-strip {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    background: var(--color-surface-1);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-lg);
    overflow: hidden;
    margin-bottom: var(--space-5);
}
.kpi {
    padding: var(--space-5);
    border-inline-end: 1px solid var(--color-border-subtle);
    display: flex; flex-direction: column; gap: var(--space-3);
}
.kpi:last-child { border-inline-end: 0; }
.kpi__label {
    display: inline-flex; align-items: center; gap: var(--space-2);
    color: var(--color-text-secondary); font-size: var(--text-xs);
    font-weight: var(--weight-medium);
}
.kpi__label .ico { font-size: 14px; color: var(--color-text-tertiary); }
.kpi__value {
    font-family: var(--font-mono); font-size: var(--text-3xl);
    font-weight: var(--weight-semibold); line-height: 1;
    letter-spacing: var(--tracking-tight);
    font-variant-numeric: tabular-nums;
}
.kpi__foot { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.kpi__delta {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: var(--text-xs); font-weight: var(--weight-semibold);
    font-family: var(--font-mono); font-variant-numeric: tabular-nums;
}
.kpi__delta .ico { font-size: 13px; }
.kpi__hint { color: var(--color-text-tertiary); font-size: var(--text-2xs); }

/* spark */
.kpi__spark { height: 24px; width: 100%; display: block; }

/* ============================================================================
   SEMANTIC TEXT — never color alone (icon/sign accompany)
   ============================================================================ */
.is-pos { color: var(--color-profit); }
.is-neg { color: var(--color-loss); }
.is-warn { color: var(--color-warning); }
.is-info { color: var(--color-info); }
.muted { color: var(--color-text-secondary); }
.dim { color: var(--color-text-tertiary); }

/* badges */
.badge {
    display: inline-flex; align-items: center; gap: 5px;
    height: 22px; padding: 0 8px;
    border-radius: var(--radius-sm);
    font-size: var(--text-2xs); font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-wide);
    border: 1px solid transparent;
}
.badge .ico { font-size: 12px; }
.badge--pos { background: var(--color-profit-muted); color: var(--color-profit); }
.badge--neg { background: var(--color-loss-muted); color: var(--color-loss); }
.badge--warn { background: var(--color-warning-muted); color: var(--color-warning); }
.badge--info { background: var(--color-info-muted); color: var(--color-info); }
.badge--neutral { background: var(--color-surface-3); color: var(--color-text-secondary); }
/* Direction is not money: green/red stay reserved for P&L, so a losing LONG
   never shows a green badge beside a red number in the same row. */
.badge--long { background: var(--color-info-muted); color: var(--color-info); }
.badge--short { background: var(--color-warning-muted); color: var(--color-warning); }
.badge--outline { background: transparent; border-color: var(--color-border-mid); color: var(--color-text-secondary); }

/* ============================================================================
   EQUITY CHART
   ============================================================================ */
.chart-wrap { position: relative; }
.chart-svg { width: 100%; height: 300px; display: block; overflow: visible; }
.chart-grid line { stroke: var(--chart-grid); stroke-width: 1; }
.chart-axis text { fill: var(--chart-axis); font-family: var(--font-mono); font-size: 11px; }
.chart-area { fill: url(#equityFill); }
.chart-line { fill: none; stroke: var(--color-accent-primary); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.chart-dot { fill: var(--color-surface-1); stroke: var(--color-accent-primary); stroke-width: 2; }
.chart-cursor { stroke: var(--color-border-strong); stroke-width: 1; stroke-dasharray: 3 3; }
.chart-tip {
    position: absolute; pointer-events: none; z-index: 5;
    padding: 8px 10px; border-radius: var(--radius-sm);
    background: var(--color-surface-3);
    border: 1px solid var(--color-border-mid);
    box-shadow: var(--shadow-float);
    font-size: var(--text-xs); white-space: nowrap;
    transform: translate(-50%, -120%); transition: opacity 90ms ease; opacity: 0;
}
.chart-tip b { font-family: var(--font-mono); font-size: var(--text-sm); }

/* ---- Brand mark ---------------------------------------------------------- */
/* The RADAR logo comes in two inks and neither one works on both grounds:
   white disappears on parchment, black disappears on midnight. The white set
   is the default because eleven of the seventeen themes are dark, and the six
   light ones take the black set below.

   content: on an <img> swaps the picture without touching a single template,
   and there are fourteen places that draw this logo. A browser that does not
   support it shows the src it was given - the old mark, which is the wrong
   brand but never a broken image. */
img[src*="logo_image"] {
    content: url("../../images/radar-mark-light.png");
}
img[src*="logo_text"] {
    content: url("../../images/radar-text-light.png");
}
[data-theme="parchment"] img[src*="logo_image"],
[data-theme="daylight"] img[src*="logo_image"],
[data-theme="sandstone"] img[src*="logo_image"],
[data-theme="mint"] img[src*="logo_image"],
[data-theme="lavender"] img[src*="logo_image"],
[data-theme="porcelain"] img[src*="logo_image"] {
    content: url("../../images/radar-mark-dark.png");
}
[data-theme="parchment"] img[src*="logo_text"],
[data-theme="daylight"] img[src*="logo_text"],
[data-theme="sandstone"] img[src*="logo_text"],
[data-theme="mint"] img[src*="logo_text"],
[data-theme="lavender"] img[src*="logo_text"],
[data-theme="porcelain"] img[src*="logo_text"] {
    content: url("../../images/radar-text-dark.png");
}

/* ---- The mark, sweeping -------------------------------------------------- */
/* The beam is the logo's own stroke, not a line drawn over it. The artwork was
   split in two: radar-body-* is the mark with the beam lifted out, and
   radar-beam-* is that stroke alone on a square canvas whose centre is the
   dish's dot - so rotating the canvas about its own centre rotates the beam
   about the dot, which is the one pivot that looks right.

   Where the beam crossed the dish's arcs the body would have been left with a
   notch; the arcs are circles about the same dot, so the gap was filled from
   the same picture turned about it (see the script's note in the commit).

   Nothing leaves the mark: the beam is clipped to the quadrant the arcs cover,
   so it comes in at the top, crosses them, and goes out at the lower right,
   and overflow:hidden keeps even the clip's corners inside the logo's box. */
.radar-mark {
    position: relative;
    display: inline-flex;
    flex: none;
    line-height: 0;
    overflow: hidden;
    isolation: isolate;
}
.radar-mark img {
    display: block;
    height: 100%;
    width: auto;
    /* The body, so the beam is not drawn twice. */
    content: url("../../images/radar-body-light.png");
}
[data-theme="parchment"] .radar-mark img,
[data-theme="daylight"] .radar-mark img,
[data-theme="sandstone"] .radar-mark img,
[data-theme="mint"] .radar-mark img,
[data-theme="lavender"] .radar-mark img,
[data-theme="porcelain"] .radar-mark img {
    content: url("../../images/radar-body-dark.png");
}
.radar-mark::after {
    content: "";
    position: absolute;
    /* The dot: 32% across the artwork, 47.5% down it. */
    left: 32%;
    top: 47.5%;
    /* The beam's canvas is 524 of the artwork's 569 units tall. */
    height: 92%;
    aspect-ratio: 1;
    transform: translate(-50%, -50%) rotate(0deg);
    background: url("../../images/radar-beam-light.png") center / contain no-repeat;
    clip-path: polygon(50% 50%, 50% 0, 100% 0, 100% 62%);
    pointer-events: none;
}
[data-theme="parchment"] .radar-mark::after,
[data-theme="daylight"] .radar-mark::after,
[data-theme="sandstone"] .radar-mark::after,
[data-theme="mint"] .radar-mark::after,
[data-theme="lavender"] .radar-mark::after,
[data-theme="porcelain"] .radar-mark::after {
    background-image: url("../../images/radar-beam-dark.png");
}
/* The beam rests at -43.5 degrees in the artwork, so the turn starts there and
   comes back to it: a full circle, ending where the logo sits still. */
@keyframes radarSweep {
    from { transform: translate(-50%, -50%) rotate(0deg); }
    to   { transform: translate(-50%, -50%) rotate(360deg); }
}
/* The beam is always there - it is part of the logo. What hovering, or
   standing in for a spinner, adds is the turning. */
.radar-mark:hover::after,
.radar-mark--scan::after {
    animation: radarSweep 3s linear infinite;
}
.radar-mark--scan::after {
    animation-duration: 2s;
}
/* Not turning means the logo as drawn, beam included. */
html.reduce-motion .radar-mark::after,
html.reduce-motion .radar-mark:hover::after,
html.reduce-motion .radar-mark--scan::after {
    animation: none;
}
@media (prefers-reduced-motion: reduce) {
    .radar-mark::after,
    .radar-mark:hover::after,
    .radar-mark--scan::after { animation: none; }
}

/* chart toolbar */
.chart-stat-row { display: flex; gap: var(--space-6); flex-wrap: wrap; margin-top: var(--space-4); padding-top: var(--space-4); border-top: 1px solid var(--color-border-subtle); }
.chart-stat { display: flex; flex-direction: column; gap: 2px; }
.chart-stat .k { color: var(--color-text-tertiary); font-size: var(--text-2xs); text-transform: uppercase; letter-spacing: var(--tracking-wide); }
.chart-stat .v { font-family: var(--font-mono); font-size: var(--text-md); font-weight: var(--weight-semibold); }

/* ============================================================================
   GRID HELPERS
   ============================================================================ */
.grid-2 { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: var(--space-5); }
.grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: var(--space-5); }
.stack { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-5); }
.dash-grid { display: grid; grid-template-columns: minmax(0, 2.2fr) minmax(300px, 1fr); gap: var(--space-5); align-items: start; }

/* metric group */
.metric-list { display: grid; gap: 1px; background: var(--color-border-subtle); border-radius: var(--radius); overflow: hidden; }
.metric-row {
    display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
    padding: var(--space-3) var(--space-4);
    background: var(--color-surface-1);
}
.metric-row__k { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--color-text-secondary); font-size: var(--text-sm); }
.metric-row__k .ico { color: var(--color-text-tertiary); font-size: 14px; }
.metric-row__v { font-family: var(--font-mono); font-size: var(--text-sm); font-weight: var(--weight-semibold); font-variant-numeric: tabular-nums; }

/* ---- Global loading overlay (long-running full-page actions) -------------- */
.app-loading {
    position: fixed; inset: 0; z-index: 3000;
    display: flex; align-items: center; justify-content: center;
    background: color-mix(in srgb, var(--color-void) 60%, transparent);
    backdrop-filter: blur(2px);
}
.app-loading[hidden] { display: none; }
.app-loading__card {
    display: flex; align-items: center; gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
    border: 1px solid var(--color-border-mid); border-radius: var(--radius);
    background: var(--color-surface-1); box-shadow: var(--shadow-float);
}
.app-loading__msg { font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--color-text-primary); }
/* The card names the action and spins beside it — "Sign in", not "Signing in…".
   The three dots were the app telling you it was busy in punctuation; the icon
   says the same thing and keeps moving while it does. */
.app-loading__spinner { font-size: 22px; color: var(--color-accent-primary); }
button.is-loading, .btn.is-loading { opacity: 0.7; cursor: progress; }

/* An inline "this is still arriving" — a table body waiting for its rows, a
   panel waiting for its content. Same icon, same speed, at text size. */
.is-loading-inline {
    display: inline-flex; align-items: center; gap: var(--space-2);
    color: var(--color-text-tertiary);
}
.is-loading-inline .aico-loader { color: var(--color-accent-primary); }

/* ============================================================================
   RULE WARNING (trades table)
   ============================================================================ */
/* One line from the warning system. Clipped to the column: the whole point is
   to notice it at a glance, and the title attribute carries the full text. */
.trade-warn {
    display: inline-flex; align-items: center; gap: 6px;
    max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    color: var(--color-loss); font-size: var(--text-xs);
}
.trade-warn .bi { flex: none; }


/* The operator's message, opened once on arrival. A <dialog>, so the backdrop,
   the focus trap and Escape come from the browser. */
dialog.announce {
    max-width: min(460px, 92vw); padding: var(--space-5);
    border: 1px solid var(--color-border-mid); border-radius: var(--radius-lg);
    background: var(--color-surface-1); color: var(--color-text-primary);
    box-shadow: var(--shadow-card);
}
dialog.announce::backdrop { background: rgba(0, 0, 0, 0.45); }
.announce__head { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-3); }
.announce__head i { font-size: 20px; color: var(--color-accent-glow); }
.announce__head strong { font-size: var(--text-lg); }
.announce--warn .announce__head i { color: var(--color-loss); }
.announce--success .announce__head i { color: var(--color-profit); }
.announce__body { margin: 0 0 var(--space-4); white-space: pre-wrap; unicode-bidi: plaintext; color: var(--color-text-secondary); }
dialog.announce form { display: flex; justify-content: flex-end; }


/* The eye, ported from lucide-animated's eye/eye-off. Those ship as React
   components on motion/react, and this frontend has no React, no bundler and
   no npm to install one into -- so it is the same lucide geometry and the same
   two gestures, each written as one keyframe. The eye blinks: the lid flattens
   to a tenth of its height and the pupil shrinks with it. The eye-off wipes:
   the slash redraws itself along its own length. Both fire on hover and on
   keyboard focus, which is when the originals animate too.

   Inlined at each of its three call sites rather than referenced from one
   <symbol>: an animation has to select the lid and the pupil separately, and
   document CSS does not reach inside a <use> element's shadow tree. */
.ico-eye, .ico-msg, .ico-user, .ico-key, .ico-phone, .ico-plus, .ico-login, .ico-upload {
    width: 1em; height: 1em; vertical-align: -.125em;
    fill: none; stroke: currentColor; stroke-width: 2;
    stroke-linecap: round; stroke-linejoin: round;
}
.ico-eye > * { transform-box: fill-box; transform-origin: 50% 50%; }
.ico-eye .slash { stroke-dasharray: 1 1; stroke-dashoffset: 0; }
/* Anchored on the button, not on the icon's own parent. The reveal eye is a
   direct child of its button, but the send-code bubble sits in a wrapper span,
   and `:hover >` asks the wrapper to be hovered — which only happens when the
   pointer is on the 12px icon itself, not anywhere else on the button. A
   descendant of the button is what "hovering the button" actually means. */
button:hover .ico-eye .lid, button:focus-visible .ico-eye .lid { animation: icoEyeBlink .4s ease-in-out; }
button:hover .ico-eye .pupil, button:focus-visible .ico-eye .pupil { animation: icoEyePupil .4s ease-in-out; }
button:hover .ico-eye .slash, button:focus-visible .ico-eye .slash { animation: icoEyeSlash .6s ease-in-out; }
@keyframes icoEyeBlink { 50% { transform: scaleY(.1); opacity: .3; } }
@keyframes icoEyePupil { 50% { transform: scale(.3); opacity: .3; } }
@keyframes icoEyeSlash { from { stroke-dashoffset: 1; } }
@media (prefers-reduced-motion: reduce) {
    .ico-eye .lid, .ico-eye .pupil, .ico-eye .slash { animation: none !important; }
}

/* message-square and message-square-check, from the same lucide-animated set
   as the eye above and ported the same way. The bubble nudges -- a 7-degree
   wobble either side at 1.05 scale, the spring the original settles with read
   as one keyframe -- and the tick draws itself along its own length the moment
   it appears, which is when the code has actually gone out. */
button:hover .ico-msg, button:focus-visible .ico-msg { animation: icoMsgNudge .5s ease-in-out; }
.ico-msg .tick { stroke-dasharray: 1; animation: icoMsgTick .4s ease-in-out; }
@keyframes icoMsgNudge {
    0%, 100% { transform: rotate(0) scale(1); }
    33% { transform: rotate(-7deg) scale(1.05); }
    66% { transform: rotate(7deg) scale(1.05); }
}
@keyframes icoMsgTick { from { stroke-dashoffset: 1; opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
    .ico-msg, .ico-msg .tick { animation: none !important; }
}

/* user and key, same set and same port as the eye and the bubble above. The
   user draws itself: the head first, the shoulders a fifth of a second behind
   it, which is the delay the original gives that path. The key turns in the
   lock and lets go -- the original holds its turn while hovered and unwinds on
   the way out, and a single keyframe that goes and comes back says the same
   thing without needing a state to leave. */
.ico-user .head, .ico-user .body { stroke-dasharray: 1; }
.ico-user .head { transform-box: fill-box; transform-origin: 50% 50%; }
:hover > .ico-user .head, :focus-visible > .ico-user .head { animation: icoUserHead .4s ease-in-out; }
:hover > .ico-user .body, :focus-visible > .ico-user .body { animation: icoUserBody .4s ease-in-out .2s both; }
:hover > .ico-key, :focus-visible > .ico-key { animation: icoKeyTurn .8s ease-in-out; }
@keyframes icoUserHead { from { stroke-dashoffset: 1; opacity: 0; transform: scale(.5); } }
@keyframes icoUserBody { from { stroke-dashoffset: 1; opacity: 0; } }
@keyframes icoKeyTurn {
    0%, 100% { transform: rotate(0); }
    25% { transform: rotate(-33deg); }
    50% { transform: rotate(-25deg); }
    75% { transform: rotate(-28deg); }
}
@media (prefers-reduced-motion: reduce) {
    .ico-user .head, .ico-user .body, .ico-key { animation: none !important; }
}

/* The handset rings: it snaps to ten degrees and shakes itself back to
   straight, swelling a tenth on the way — the rotate and scale tracks the
   original runs together over the same 0.9s. */
:hover > .ico-phone, :focus-visible > .ico-phone { animation: icoPhoneRing .9s ease-in-out; }
@keyframes icoPhoneRing {
    0%   { transform: rotate(10deg) scale(1); }
    25%  { transform: rotate(20deg) scale(1.1); }
    50%  { transform: rotate(-10deg) scale(1.1); }
    75%  { transform: rotate(10deg) scale(1.1); }
    100% { transform: rotate(0) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
    .ico-phone { animation: none !important; }
}

/* The plus turns a half turn, which for a plus lands exactly where it started
   — the original springs to 180 and holds, and the symmetry is why it can. The
   login arrow steps through the door and back, the 3-unit nudge the original
   gives the arrow and the bar together. */
:hover > .ico-plus, :focus-visible > .ico-plus { animation: icoPlusTurn .5s cubic-bezier(.34, 1.4, .64, 1); }
:hover > .ico-login .arrow, :focus-visible > .ico-login .arrow,
:hover > .ico-login .bar, :focus-visible > .ico-login .bar { animation: icoLoginIn .4s ease-in-out; }
@keyframes icoPlusTurn { from { transform: rotate(0); } to { transform: rotate(180deg); } }
@keyframes icoLoginIn { 50% { transform: translateX(3px); } }
@media (prefers-reduced-motion: reduce) {
    .ico-plus, .ico-login .arrow, .ico-login .bar { animation: none !important; }
}

/* Upload: the arrow lifts out of the tray and drops back, the one gesture the
   original plays. Only the arrow moves -- the tray it leaves behind is what
   makes the lift read as a lift. */
:hover > .ico-upload .arrow, :focus-visible > .ico-upload .arrow { animation: icoUploadOut .55s ease-in-out; }
@keyframes icoUploadOut { 45% { transform: translateY(-3px); } }
@media (prefers-reduced-motion: reduce) {
    .ico-upload .arrow { animation: none !important; }
}

/* Press, for every icon above. A separate animation name on purpose: a mouse
   is already hovering by the time it presses, so the hover rule's animation
   has both fired and finished, and re-declaring the same name changes nothing
   the browser would restart from. A different name is a different animation,
   which is what makes it play. It also covers the case hover cannot reach at
   all — a touch screen, where these icons would otherwise never move. Last in
   the file so it wins the tie against the :hover rules while held; letting go
   hands the icon back to its own gesture. */
button:active .ico-eye, button:active .ico-msg,
:active > .ico-user, :active > .ico-key, :active > .ico-phone,
:active > .ico-plus, :active > .ico-login, :active > .ico-upload { animation: icoPress .28s ease-out; }
@keyframes icoPress { 50% { transform: scale(.82); } }
@media (prefers-reduced-motion: reduce) {
    button:active .ico-eye, button:active .ico-msg,
    :active > .ico-user, :active > .ico-key, :active > .ico-phone,
    :active > .ico-plus, :active > .ico-login, :active > .ico-upload { animation: none !important; }
}

/* ── The animated lucide set ──────────────────────────────────────────────
   Geometry and gestures both come from lucide-animated.com's registry: each
   icon there is a React component whose parts carry framer-motion variants,
   and each variant is written here as the keyframe that does the same thing.
   Their springs (stiffness/damping) become cubic-beziers that overshoot once,
   which is what a spring of that stiffness looks like.

   The icons are inline rather than a sprite because nearly every gesture moves
   a part -- the arrow inside download, the lid on the trash, the eyes on the
   bot -- and document CSS cannot select inside a <use> shadow tree.

   HOVER is the trigger, as on their own site: the control that holds the icon,
   hovered or keyboard-focused. Held (:active) plays the same gesture, which is
   the only way a touch screen ever sees one. */
.aico {
    width: 1em; height: 1em; vertical-align: -.125em; flex: none;
    fill: none; stroke: currentColor; stroke-width: 2;
    stroke-linecap: round; stroke-linejoin: round;
    overflow: visible;
}
.aico > * { transform-box: fill-box; transform-origin: 50% 50%; }
.nav__item .aico, .settings-nav a .aico, .settings-nav__label .aico { width: 1em; height: 1em; }

/* One list, used by every rule below: a control, hovered, focused or held. */
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-chart-line .d1 { animation: aiDraw .3s ease-out .15s both; }
/* languages redraws itself in four beats, a tenth of a second apart — the
   accent over the A, then the rule under it, then the stroke, then the
   glyph beside it. Their stagger, as four delays. */
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-languages .d1 { animation: aiDraw .45s ease-out both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-languages .d2 { animation: aiDraw .45s ease-out .1s both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-languages .d3 { animation: aiDraw .45s ease-out .2s both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-languages .d4 path { animation: aiDraw .45s ease-out .3s both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-cpu .pv path { animation: aiPinsY .5s ease-in-out 2; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-cpu .ph path { animation: aiPinsX .5s ease-in-out 2; }
/* Each pin stretches about its own middle. Scaling the group instead would
   have slid the four pins apart rather than lengthening any of them. */
.aico-cpu path { transform-box: fill-box; transform-origin: 50% 50%; }
/* Nobody at lucide-animated animated the headphones, so this one is ours: the
   two cups take turns pressing outward the way a pair does when someone puts
   them on, and the band flexes down a hair to follow. Parts moving against
   each other, rather than the whole icon nodding. */
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-headphones .cupL { animation: aiCupL .7s ease-in-out; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-headphones .cupR { animation: aiCupR .7s ease-in-out .12s both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-headphones .band { animation: aiBand .7s ease-in-out; }
.aico-headphones path { transform-box: fill-box; transform-origin: 50% 50%; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-gauge .needle { animation: aiNeedle .7s cubic-bezier(.34, 1.4, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-search { animation: aiSearch 1s cubic-bezier(.34, 1.3, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-bell { animation: aiBell .5s ease-in-out; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-moon { animation: aiMoon 1.2s ease-in-out; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) :is(.aico-settings, .aico-cog) { animation: aiHalfTurn .9s cubic-bezier(.34, 1.32, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-plus { animation: aiHalfTurn .7s cubic-bezier(.34, 1.4, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-download .p1 { animation: aiArrowDown .5s cubic-bezier(.34, 1.5, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-logout .p1 { animation: aiStepOut .4s ease-in-out; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-send .p1 { animation: aiSendFly .5s ease-in-out; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-send .p2 { animation: aiSendTrail .55s ease-in-out .1s; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-check .d1,
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-check-check .d1 { animation: aiDrawPop .4s ease-out; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-check-check .d2 { animation: aiDrawPop .4s ease-out .1s both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-x .d1 { animation: aiDraw .4s ease-in-out; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-x .d2 { animation: aiDraw .4s ease-in-out .2s both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-clock .p1 { animation: aiHourHand .6s cubic-bezier(.4, 0, .2, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-clock .p2 { animation: aiMinuteHand .5s ease-in-out; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-maximize .p1 { animation: aiCornerTL .5s cubic-bezier(.34, 1.4, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-maximize .p2 { animation: aiCornerTR .5s cubic-bezier(.34, 1.4, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-maximize .p3 { animation: aiCornerBL .5s cubic-bezier(.34, 1.4, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-maximize .p4 { animation: aiCornerBR .5s cubic-bezier(.34, 1.4, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-shrink .p1 { animation: aiPullTL .5s cubic-bezier(.34, 1.4, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-shrink .p2 { animation: aiPullTR .5s cubic-bezier(.34, 1.4, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-shrink .p3 { animation: aiPullBL .5s cubic-bezier(.34, 1.4, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-shrink .p4 { animation: aiPullBR .5s cubic-bezier(.34, 1.4, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-dollar .d1 { animation: aiDraw .6s ease-in-out; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-dollar .d2 { animation: aiDrawBack .4s ease-in-out .5s both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-link .p1,
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-link .p2 { animation: aiLinkTug 1s ease-in-out; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-panels .p1 { animation: aiPanelTop .5s ease-out; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-panels .p2 { animation: aiPanelLeft .5s ease-out .3s both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-panels .p3 { animation: aiPanelRight .5s ease-out .4s both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-home .d1 { animation: aiDraw .6s ease-in-out; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-menu .p1 { animation: aiMenuTop .55s cubic-bezier(.34, 1.4, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-menu .p2 { animation: aiMenuMid .55s ease-in-out; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-menu .p3 { animation: aiMenuBottom .55s cubic-bezier(.34, 1.4, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-ticket .p1 { animation: aiTicketLeft .5s cubic-bezier(.34, 1.4, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-ticket .p2 { animation: aiTicketRight .5s cubic-bezier(.34, 1.4, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-sort-az .p1,
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-sort-za .p1 { animation: aiSwapDown .55s cubic-bezier(.34, 1.32, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-sort-az .p2,
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-sort-za .p2 { animation: aiSwapUp .55s cubic-bezier(.34, 1.32, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-user-plus .p1 { animation: aiPlusIn .45s ease-out .25s both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-user-round-plus .d1 { animation: aiDraw .2s ease-out .3s both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-user-round-plus .d2 { animation: aiDraw .2s ease-out .6s both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-delete .p1 { animation: aiLid .4s cubic-bezier(.34, 1.5, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-delete .p2 { animation: aiCan .4s cubic-bezier(.34, 1.5, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-chart-pie .p1 { animation: aiSlice .5s cubic-bezier(.34, 1.5, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-copy .p1 { animation: aiCopyFront .5s cubic-bezier(.34, 1.4, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-copy .p2 { animation: aiCopyBack .5s cubic-bezier(.34, 1.4, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-mic .p1 { animation: aiCapsule .6s ease-in-out; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-mic-off .d1 { animation: aiDraw .4s ease-in-out .15s both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-connect .p1 { animation: aiPlugOut .5s cubic-bezier(.34, 1.4, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-connect .p2 { animation: aiSocketOut .5s cubic-bezier(.34, 1.4, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-bot .p1,
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-bot-chat .p2 { animation: aiBlink .5s ease-in-out .1s; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-bot-chat { animation: aiBotWobble 1s ease-in-out; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-bot-chat .p1 { animation: aiBubble .6s ease-in-out 2; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-sparkles .p1 { animation: aiSparkle 1s ease-in-out; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-clipboard-heart .p1 { animation: aiHeartBeat .8s ease-in-out; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-sparkles .p2 { animation: aiStarBlink 2s cubic-bezier(.34, 1.2, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-sparkles .p3 { animation: aiStarBlink 2s cubic-bezier(.34, 1.2, .64, 1) .15s both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-sliders :is(.p1, .k1) { animation: aiSlideStart .5s cubic-bezier(.34, 1.5, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-sliders :is(.p4, .k2) { animation: aiSlideEndFar .5s cubic-bezier(.34, 1.5, .64, 1); }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-sliders .k3 { animation: aiSlideStartFar .5s cubic-bezier(.34, 1.5, .64, 1); }

/* Staggered fades: the sun's rays, the calendar's dots, the language strokes --
   one keyframe, eight delays. */
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-sun :is(.r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8) { animation: aiFadeIn .3s ease-out both; }
.aico-sun .r1 { animation-delay: .1s; } .aico-sun .r2 { animation-delay: .2s; } .aico-sun .r3 { animation-delay: .3s; } .aico-sun .r4 { animation-delay: .4s; }
.aico-sun .r5 { animation-delay: .5s; } .aico-sun .r6 { animation-delay: .6s; } .aico-sun .r7 { animation-delay: .7s; } .aico-sun .r8 { animation-delay: .8s; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-calendar :is(.r1, .r2, .r3, .r4, .r5, .r6) { animation: aiDotDim .4s ease-in-out both; }
.aico-calendar .r1 { animation-delay: 0s; } .aico-calendar .r2 { animation-delay: .1s; } .aico-calendar .r3 { animation-delay: .2s; }
.aico-calendar .r4 { animation-delay: .3s; } .aico-calendar .r5 { animation-delay: .4s; } .aico-calendar .r6 { animation-delay: .5s; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-languages :is(.r1, .r2, .r3, .r4) { animation: aiDraw .5s ease-out both; }
.aico-languages .r1 { animation-delay: 0s; } .aico-languages .r2 { animation-delay: .1s; } .aico-languages .r3 { animation-delay: .2s; } .aico-languages .r4 { animation-delay: .3s; }

/* The spinner is the one that never stops, and the only one that does not wait
   to be hovered. */
.aico-loader .p1 { animation: aiSpin .8s linear infinite; transform-origin: 12px 12px; }

@keyframes aiCupL { 0%, 100% { transform: translateX(0) scale(1); } 45% { transform: translateX(-1.4px) scale(1.12); } }
@keyframes aiCupR { 0%, 100% { transform: translateX(0) scale(1); } 45% { transform: translateX(1.4px) scale(1.12); } }
@keyframes aiBand { 0%, 100% { transform: scaleX(1) translateY(0); } 45% { transform: scaleX(1.06) translateY(.6px); } }
@keyframes aiNeedle { 0%, 100% { transform: translate(0, 0) rotate(0); } 55% { transform: translate(.5px, 3px) rotate(72deg); } }
@keyframes aiPinsY { 50% { transform: scaleY(1.5); opacity: .8; } }
@keyframes aiPinsX { 50% { transform: scaleX(1.5); opacity: .8; } }
@keyframes aiSearch { 0%, 100% { transform: translate(0, 0); } 33% { transform: translate(0, -4px); } 66% { transform: translate(-3px, 0); } }
@keyframes aiBell { 0%, 100% { transform: rotate(0); } 25% { transform: rotate(-10deg); } 50% { transform: rotate(10deg); } 75% { transform: rotate(-10deg); } }
@keyframes aiMoon { 0%, 100% { transform: rotate(0); } 20% { transform: rotate(-10deg); } 40% { transform: rotate(10deg); } 60% { transform: rotate(-5deg); } 80% { transform: rotate(5deg); } }
@keyframes aiHalfTurn { from { transform: rotate(0); } to { transform: rotate(180deg); } }
@keyframes aiSpin { to { transform: rotate(360deg); } }
@keyframes aiArrowDown { 55% { transform: translateY(2px); } }
@keyframes aiStepOut { 0%, 100% { transform: translateX(0); } 35% { transform: translateX(-3px); } 70% { transform: translateX(2px); } }
@keyframes aiSendFly { 55% { transform: translate(3px, -3px) scale(.8); } }
@keyframes aiSendTrail { from { stroke-dashoffset: 1; opacity: 0; } 60% { opacity: 1; } to { stroke-dashoffset: 0; opacity: 0; } }
@keyframes aiDraw { from { stroke-dashoffset: 1; opacity: 0; } 10% { opacity: 1; } }
@keyframes aiDrawBack { from { stroke-dashoffset: -1; opacity: 0; } 10% { opacity: 1; } }
@keyframes aiDrawPop { from { stroke-dashoffset: 1; opacity: 0; transform: scale(.5); } 10% { opacity: 1; } }
@keyframes aiHourHand { to { transform: rotate(360deg); } }
@keyframes aiMinuteHand { 55% { transform: rotate(45deg); } }
@keyframes aiCornerTL { 55% { transform: translate(-2px, -2px); } }
@keyframes aiCornerTR { 55% { transform: translate(2px, -2px); } }
@keyframes aiCornerBL { 55% { transform: translate(-2px, 2px); } }
@keyframes aiCornerBR { 55% { transform: translate(2px, 2px); } }
@keyframes aiPullTL { 55% { transform: translate(-1px, -1px); } }
@keyframes aiPullTR { 55% { transform: translate(1px, -1px); } }
@keyframes aiPullBL { 55% { transform: translate(-1px, 1px); } }
@keyframes aiPullBR { 55% { transform: translate(1px, 1px); } }
@keyframes aiLinkTug { 0%, 100% { transform: rotate(0); } 25% { transform: rotate(-5deg); } 60% { transform: rotate(0); } }
@keyframes aiPanelTop { from { opacity: 0; transform: translateY(-5px); } 40% { opacity: 1; } }
@keyframes aiPanelLeft { from { opacity: 0; transform: translateX(-10px); } 50% { opacity: 1; } }
@keyframes aiPanelRight { from { opacity: 0; transform: translateX(10px); } 50% { opacity: 1; } }
@keyframes aiMenuTop { 55% { transform: translateY(6px) rotate(45deg); } }
@keyframes aiMenuMid { 40%, 70% { opacity: 0; } }
@keyframes aiMenuBottom { 55% { transform: translateY(-6px) rotate(-45deg); } }
@keyframes aiTicketLeft { 55% { transform: translateX(-3px); } }
@keyframes aiTicketRight { 55% { transform: translateX(3px) rotate(4deg); } }
@keyframes aiSwapDown { 55% { transform: translateY(10px); } }
@keyframes aiSwapUp { 55% { transform: translateY(-10px); } }
@keyframes aiPlusIn { from { opacity: 0; transform: scale(0) rotate(-90deg); } 70% { opacity: 1; transform: scale(1.15) rotate(0); } }
@keyframes aiLid { 55% { transform: translateY(-1.1px); } }
@keyframes aiCan { 55% { transform: translateY(.5px); } }
@keyframes aiSlice { 55% { transform: translate(1.1px, -1.1px); } }
@keyframes aiCopyFront { 55% { transform: translate(-3px, -3px); } }
@keyframes aiCopyBack { 55% { transform: translate(3px, 3px); } }
@keyframes aiCapsule { 0%, 100% { transform: translateY(0); } 25% { transform: translateY(-3px); } 50% { transform: translateY(0); } 75% { transform: translateY(-2px); } }
@keyframes aiPlugOut { 55% { transform: translate(3px, -3px); } }
@keyframes aiSocketOut { 55% { transform: translate(-3px, 3px); } }
@keyframes aiBlink { 50% { transform: scaleY(.1); } }
@keyframes aiBotWobble { 0%, 100% { transform: rotate(0) translateY(0) scale(1); } 25% { transform: rotate(-3deg) translateY(1.5px) scale(1.03); } 55% { transform: rotate(3deg) translateY(-1.5px) scale(1.03); } }
@keyframes aiBubble { 50% { transform: scale(1.04); } }
@keyframes aiSparkle { 0%, 100% { fill: none; transform: translateY(0); } 25% { fill: currentColor; transform: translateY(-1px); } 60% { fill: currentColor; transform: translateY(0); } }
@keyframes aiStarBlink { 0% { opacity: 0; } 16% { opacity: 1; } 32%, 84% { opacity: 0; } 100% { opacity: 1; } }
@keyframes aiSlideStart { 55% { transform: translateX(-5px); } }
@keyframes aiSlideStartFar { 55% { transform: translateX(-4px); } }
@keyframes aiSlideEndFar { 55% { transform: translateX(5px); } }
@keyframes aiFadeIn { from { opacity: 0; } }
@keyframes aiDotDim { 50% { opacity: .3; } }
@keyframes aiHeartBeat { 0%, 100% { transform: scale(1); } 22% { transform: scale(1.22); } 44% { transform: scale(0.96); } 66% { transform: scale(1.12); } }

/* ---- Settings rail icons ----
   Every one of these moves a part of itself rather than being scaled as a
   whole. A column of eighteen rows that each grow on hover reads as a
   column that flinches; a column where the gear turns, the flag lifts and
   the signal leaves the mast reads as a column that answers. */
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-user-cog .spin { animation: aiSpin 1.1s ease-in-out; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-palette .c1 { animation: aiDotDim .7s ease-in-out 0s; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-palette .c2 { animation: aiDotDim .7s ease-in-out 0.08s; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-palette .c3 { animation: aiDotDim .7s ease-in-out 0.16s; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-palette .c4 { animation: aiDotDim .7s ease-in-out 0.24s; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-crosshair .pv path { animation: aiPinsY .5s ease-in-out 2; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-crosshair .ph path { animation: aiPinsX .5s ease-in-out 2; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-flag .cloth { animation: aiFlagWave .9s ease-in-out; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-smile .d1 { animation: aiDraw .45s ease-out both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-smile .eyes { animation: aiBlink .6s ease-in-out .3s; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-notebook .rings path { animation: aiPinsX .55s ease-in-out; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-id-card .d1 { animation: aiDraw .4s ease-out both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-id-card .d2 { animation: aiDraw .4s ease-out .1s both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-wallet .c1 { animation: aiDotDim .8s ease-in-out 2; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-workflow .d1 { animation: aiDraw .45s ease-out both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-shield-alert .alert { animation: aiDotDim .55s ease-in-out 2; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-triangle-alert .alert { animation: aiDotDim .55s ease-in-out 2; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-radio-tower .w1 { animation: aiFadeIn .5s ease-out both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-radio-tower .w2 { animation: aiFadeIn .5s ease-out .15s both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-message-square .d1 { animation: aiDraw .35s ease-out both; }
:is(a, button, label, summary, .chip, .tab, .nav__item, .workspace-tab, [role="button"]):is(:hover, :focus-visible, :active) .aico-message-square .d2 { animation: aiDraw .35s ease-out .12s both; }
/* The cloth pivots at the pole, so the transform origin is the left edge. */
.aico-flag .cloth { transform-origin: 0% 50%; }
.aico-notebook .rings path, .aico-crosshair :is(.pv, .ph) path { transform-box: fill-box; transform-origin: 50% 50%; }
@keyframes aiFlagWave { 30% { transform: skewY(-6deg) translateY(-.5px); } 65% { transform: skewY(3deg); } }
/* Dash-drawn parts need a unit-length dash to travel along. */
.aico-smile .d1, .aico-id-card :is(.d1, .d2), .aico-workflow .d1,
.aico-message-square :is(.d1, .d2) { stroke-dasharray: 1; }

/* Anything that draws itself needs a dash to draw along, and pathLength="1" in
   the markup is what makes one unit mean the whole stroke. */
.aico :is(.d1, .d2, .r1, .r2, .r3, .r4, .p2) { stroke-dasharray: 1; }
.aico-languages :is(.d3, .d4 path) { stroke-dasharray: 1; }

/* loader is the exception to the hover rule above: it exists only while
   something is running, so it spins from the moment it is in the document. */
.aico-loader .spin { animation: aiSpin .8s linear infinite; }
@keyframes aiSpin { to { transform: rotate(360deg); } }
.aico-calendar :is(.r1, .r2, .r3, .r4, .r5, .r6), .aico-sun :is(.r1, .r2, .r3, .r4) { stroke-dasharray: none; }
.aico-send .p2 { stroke-dasharray: 2 2; }
.aico-sliders .p2, .aico-ticket .p2, .aico-copy .p2, .aico-link .p2, .aico-connect .p2,
.aico-sort-az .p2, .aico-sort-za .p2, .aico-panels .p2, .aico-bot-chat .p2, .aico-sparkles .p2,
.aico-maximize .p2, .aico-shrink .p2, .aico-clock .p2, .aico-delete .p2 { stroke-dasharray: none; }

@media (prefers-reduced-motion: reduce) {
    .aico, .aico * { animation: none !important; }
    /* Except the loader. Every other icon animates as decoration and can be
       stopped; this one is the only sign the app is still working, and a
       frozen spinner reads as a hang. Slowed, not silenced. */
    .aico-loader .spin { animation: aiSpin 2.4s linear infinite !important; }
}
/* The same answer for the switch in Settings → Appearance. The machine's
   preference and the app's own switch have to reach the same rules, and this
   half was missing: reduce-motion only ever blanket-shortened durations, which
   left the icons hovering, twitching and drawing themselves at 0.001ms rather
   than holding still. */
html.reduce-motion .aico,
html.reduce-motion .aico * { animation: none !important; }
html.reduce-motion .aico-loader .spin { animation: aiSpin 2.4s linear infinite !important; }
html.reduce-motion .settings-nav a .ico,
html.reduce-motion .settings-nav__label .ico { transform: none !important; }

/* Weak/fair/strong, live as you type. Always rendered rather than toggled in
   on the first keystroke, at rest with empty gray bars, so the field below it
   does not jump down the moment someone starts typing. */
.pwd-strength { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-2); }
.pwd-strength__bars { display: flex; gap: 4px; flex: 1; }
.pwd-strength__bars span { height: 4px; flex: 1; border-radius: var(--radius-pill); background: var(--color-border-mid); transition: background-color 160ms ease; }
.pwd-strength__label { min-width: 34px; font-size: var(--text-2xs); color: var(--color-text-tertiary); white-space: nowrap; }
