/* sieve_shell.css — the Nocturne tokens, and the admin shell built on them:
   sidebar, top bar, phone tab bar, the panels they open, and the handful of
   shared controls (buttons, dialog) that Nocturne pages use.

   Loaded on every admin page after sieve_admin.css.  Bump the ?v= param in
   templates/admin/base_site.html after editing (non-manifest static storage).

   Two rules for anything added here:

   1. Take every colour, radius and shadow from the tokens below.  They
      started as the Nocturne design system's own values; the ground has
      since been deepened and the mid greys lifted for contrast, and the
      surfaces are glass (see LIQUID GLASS at the end of this file).

   2. Prefix classes with sv-.  Nocturne ships .btn / .card / .table / .nav,
      and so does Bootstrap, which the rest of the admin still stands on.
      The system's component classes are ported here under sv- rather than
      linked as-is so the two cannot collide. */

/* ══ TOKENS (Nocturne) ════════════════════════════════════════════════ */
:root {
    /* A deeper ground and brighter ink than Nocturne shipped with: glass
       needs something dark to sit on, and text needs to stay crisp on it. */
    --color-bg: #090b16;
    --color-surface: #181b2e;          /* the opaque stand-in, where glass cannot be used */
    --color-text: #f6f7fc;
    --color-accent: #a598ff;
    --color-divider: rgb(255 255 255 / .2);

    /* Tonal ramps — one shared OKLCH lightness scale, so the same step of
       any role carries the same visual weight. */
    --color-neutral-100: #f6f7ff;
    --color-neutral-200: #ebedf9;
    --color-neutral-300: #dcdfee;
    --color-neutral-400: #c6cadc;
    --color-neutral-500: #aab0c6;
    --color-neutral-600: #8c92ab;
    --color-neutral-700: #6c7189;
    /* The two darkest steps are only ever hairlines and faint fills.  As
       white at low alpha they read as an edge on any glass, light or dark. */
    --color-neutral-800: rgb(255 255 255 / .15);
    --color-neutral-900: rgb(255 255 255 / .075);

    --color-accent-100: #f5f4ff;
    --color-accent-200: #e7e5fe;
    --color-accent-300: #d2cefd;
    --color-accent-400: #bdb3ff;
    --color-accent-500: #a598ff;
    --color-accent-600: #8576e0;
    --color-accent-700: #6a5cc0;
    /* Tints, not paints: the accent laid thinly over whatever glass is beneath. */
    --color-accent-800: rgb(150 134 255 / .34);
    --color-accent-900: rgb(150 134 255 / .2);

    --font-heading: "Inter", system-ui, sans-serif;
    --font-heading-weight: 500;
    --font-body: "Inter", system-ui, sans-serif;

    --space-1: 2.8px;
    --space-2: 5.6px;
    --space-3: 8.4px;
    --space-4: 11.2px;
    --space-6: 16.8px;
    --space-8: 22.4px;

    --radius-sm: 6px;
    --radius-md: 12px;
    --radius-lg: 20px;

    /* Glass.  A pane is a thin wash of white, brightest at its top-left
       corner where the light catches it, over a blurred and saturated copy
       of whatever is behind.  Its edge is a specular line along the top, a
       hairline all round, and a soft shadow that lifts it off the ground. */
    --glass: linear-gradient(150deg, rgb(255 255 255 / .115), rgb(255 255 255 / .04) 46%, rgb(255 255 255 / .07));
    --glass-strong: linear-gradient(150deg, rgb(255 255 255 / .17), rgb(255 255 255 / .08) 46%, rgb(255 255 255 / .11)), rgb(12 14 30 / .72);
    /* A pane on the ground has only the soft ambient light behind it, which
       a blur leaves unchanged; all a blur adds there is Chrome's mirrored
       edge, a bright band about three radii wide.  So panes only lift the
       colour (per pixel, no edge), and the blur is kept for layers that
       float over content: the top bar, menus, dialogs, toasts, the dock. */
    --glass-blur: saturate(150%);
    --glass-blur-float: blur(14px) saturate(150%);
    --shadow-sm: inset 0 1px 0 rgb(255 255 255 / .26), inset 0 0 0 1px rgb(255 255 255 / .11), 0 14px 34px -14px rgb(0 0 0 / .6);
    --shadow-md: inset 0 1px 0 rgb(255 255 255 / .32), inset 0 0 0 1px rgb(255 255 255 / .16), 0 20px 46px -12px rgb(0 0 0 / .72);
    --shadow-lg: inset 0 1px 0 rgb(255 255 255 / .36), inset 0 0 0 1px rgb(255 255 255 / .2), 0 34px 80px -12px rgb(0 0 0 / .82);
    /* What the glass refracts: three soft lights on the deep ground, fixed to the window. */
    --ambient:
        radial-gradient(58vw 54vh at 6% -6%, rgb(122 96 255 / .42), transparent 64%),
        radial-gradient(46vw 48vh at 98% 4%, rgb(32 176 214 / .26), transparent 62%),
        radial-gradient(56vw 56vh at 72% 106%, rgb(214 78 164 / .24), transparent 64%),
        radial-gradient(40vw 40vh at 30% 70%, rgb(60 90 220 / .14), transparent 70%),
        var(--color-bg);

    /* Status hues, as "L C H" so a tint is oklch(var(--st-…) / alpha). */
    --st-paid: 0.8 0.16 160;
    --st-partial: 0.84 0.14 80;
    --st-overdue: 0.7 0.2 22;
    --st-due: 0.75 0.15 289;

    --sv-side-w: 236px;
    --sv-top-h: 56px;
    --sv-ease: cubic-bezier(.16, 1, .3, 1);
}

/* ══ SCOPE ═════════════════════════════════════════════════════════════
   sieve_admin.css pins Nunito on html, body, span, button, … with
   !important.  Anything inside the shell or a Nocturne page is Inter. */
.sv-scope,
.sv-scope :is(a, span, button, input, label, p, li, div, small, strong, kbd, pre,
              h1, h2, h3, h4, h5, h6, td, th, section, header, nav) {
    font-family: var(--font-body) !important;
}
.sv-scope { color: var(--color-text); font-size: 13px; line-height: 1.55; }
.sv-scope *, .sv-scope *::before, .sv-scope *::after { box-sizing: border-box; }
.sv-scope :is(h1, h2, h3, h4, h5, h6) {
    font-weight: var(--font-heading-weight);
    line-height: 1.12; letter-spacing: -0.015em; margin: 0;
    color: var(--color-text);
}
.sv-scope :focus { outline: none; }
.sv-scope :focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.sv-scope ::selection { background: color-mix(in srgb, var(--color-accent) 30%, transparent); }

@keyframes sv-rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@keyframes sv-in   { from { opacity: 0; transform: translateX(12px); } to { opacity: 1; transform: none; } }
@keyframes sv-fade { from { opacity: 0; } to { opacity: 1; } }

/* ══ LAYOUT ════════════════════════════════════════════════════════════
   AdminLTE lays .app-wrapper out as a grid with named areas.  The rail and
   top bar take the sidebar and header areas; Jazzmin's own navbar is
   hidden rather than removed (it has no template block to override). */
#jazzy-navbar { display: none !important; }
/* adminlte.js appends this whenever the body carries .sidebar-open, which
   Jazzmin sets in the template.  With no .app-sidebar to collapse it would
   sit over the page on a phone and swallow every tap. */
.sidebar-overlay { display: none !important; }
.app-wrapper { grid-template-columns: auto minmax(0, 1fr) !important; }

/* ══ SIDEBAR ══════════════════════════════════════════════════════════
   Everything, always expanded: one scrolling list of every section, grouped
   as the admin groups them.  It keeps its place between pages (see
   sieve_shell.js) so the list does not jump back to the top on each click. */
.sv-side {
    grid-area: lte-app-sidebar;
    position: sticky; top: 0; align-self: start; z-index: 1038;
    width: var(--sv-side-w); height: 100vh; height: 100dvh;
    display: flex; flex-direction: column; gap: 14px; padding: 18px 0 12px;
    background: linear-gradient(180deg, var(--color-surface), var(--color-bg) 60%);
    box-shadow: 1px 0 0 var(--color-neutral-800);
    view-transition-name: sv-side;
}
.sv-side__brand {
    flex: none; display: flex; align-items: center; gap: 10px; padding: 0 20px 4px;
    font-size: 15px; font-weight: 500; color: var(--color-text) !important; text-decoration: none !important;
}
.sv-side__brand > span {
    width: 28px; height: 28px; border-radius: 8px; display: grid; place-items: center; font-size: 15px;
    color: var(--color-accent-300);
    box-shadow: inset 0 0 0 1px var(--color-accent), 0 0 16px color-mix(in srgb, var(--color-accent) 30%, transparent);
    transition: box-shadow .3s;
}
.sv-side__brand:hover > span { box-shadow: inset 0 0 0 1px var(--color-accent-400), 0 0 22px color-mix(in srgb, var(--color-accent) 55%, transparent); }

.sv-side__nav {
    flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
    display: flex; flex-direction: column; gap: 18px; padding: 4px 12px 12px;
    scrollbar-width: thin; scrollbar-color: var(--color-neutral-800) transparent;
    -webkit-mask-image: linear-gradient(to bottom, transparent, #000 10px, #000 calc(100% - 18px), transparent);
    mask-image: linear-gradient(to bottom, transparent, #000 10px, #000 calc(100% - 18px), transparent);
}
.sv-side__group { display: flex; flex-direction: column; gap: 1px; }
.sv-side__label { padding: 0 10px 6px; font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--color-neutral-600); }
.sv-side__item {
    position: relative; flex: none; display: flex; align-items: center; gap: 10px;
    min-height: 30px; padding: 4px 10px; border-radius: 6px;
    font-size: 13px; line-height: 1.3; text-decoration: none !important;
    color: var(--color-neutral-400) !important;
    transition: background .18s, color .18s, transform .25s var(--sv-ease);
}
.sv-side__item > i { flex: none; font-size: 16px; color: var(--color-neutral-600); transition: color .18s; }
.sv-side__item > span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sv-side__item > b {
    flex: none; padding: 0 6px; border-radius: 99px; font-size: 10.5px; font-weight: 400; line-height: 1.6;
    background: oklch(var(--st-overdue) / .18); color: oklch(0.82 0.1 22);
}
.sv-side__item:hover { background: var(--color-neutral-900); color: var(--color-neutral-200) !important; transform: translateX(2px); }
.sv-side__item:hover > i { color: var(--color-neutral-400); }
.sv-side__item:active { background: var(--color-neutral-800); }
.sv-side__item.is-active { background: var(--color-accent-900); color: var(--color-accent-100) !important; transform: none; }
.sv-side__item.is-active > i { color: var(--color-accent-400); }
/* A short accent mark on the current page — solid, as short marks are. */
.sv-side__item.is-active::before {
    content: ""; position: absolute; left: -12px; top: 7px; bottom: 7px; width: 2px; border-radius: 0 2px 2px 0;
    background: var(--color-accent); box-shadow: 0 0 10px var(--color-accent);
}

.sv-side__foot { flex: none; display: flex; flex-direction: column; gap: 8px; padding: 0 12px; }
.sv-side__foot .sv-btn { height: 34px; font-size: 13px; }
.sv-side__user {
    display: flex; align-items: center; gap: 10px; width: 100%; padding: 10px; border: 0; border-radius: 8px;
    text-align: left; cursor: pointer; background: transparent; color: var(--color-text);
    box-shadow: inset 0 0 0 1px var(--color-neutral-800); transition: background .2s, box-shadow .2s;
}
.sv-side__user:hover, .sv-side__user[aria-expanded="true"] { background: var(--color-neutral-900); box-shadow: inset 0 0 0 1px var(--color-neutral-700); }
.sv-side__user > span:nth-child(2) { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.2; }
.sv-side__user strong { font-size: 13px; font-weight: 400; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sv-side__user small { font-size: 11px; color: var(--color-neutral-500); }
.sv-side__user > i { color: var(--color-neutral-600); }

.sv-avatar {
    width: 26px; height: 26px; flex: none; padding: 0; border: 0; border-radius: 99px;
    display: grid; place-items: center; font-size: 12px; font-weight: 500; line-height: 1;
    background: var(--color-accent-900); color: var(--color-accent-300);
}
button.sv-avatar { cursor: pointer; transition: box-shadow .2s; }
button.sv-avatar:hover, button.sv-avatar[aria-expanded="true"] { box-shadow: 0 0 0 1px var(--color-accent-700); }
.sv-dot {
    position: absolute; top: 6px; right: 6px; width: 7px; height: 7px; border-radius: 99px; pointer-events: none;
    background: oklch(var(--st-overdue)); box-shadow: 0 0 8px oklch(var(--st-overdue));
}

/* The phone tab bar; hidden until the sidebar no longer fits. */
.sv-tabs { display: none; }

/* ══ TOP BAR ═══════════════════════════════════════════════════════════
   The rail is dark in both themes, as the old sidebar was.  The top bar
   sits on the page, so on the pages that still follow the light/dark
   picker it takes its ground and ink from that theme. */
.sv-topbar {
    --t-bg: var(--sk-bg, var(--color-bg));
    --t-text: var(--color-text);
    --t-muted: var(--color-neutral-500);
    --t-line: var(--color-neutral-800);
    --t-accent: var(--color-accent-300);
    --t-hover: var(--color-neutral-900);

    grid-area: lte-app-header;
    position: sticky; top: 0; z-index: 1034;
    height: var(--sv-top-h); min-width: 0;
    display: flex; align-items: center; gap: 14px; padding: 0 28px;
    font-size: 13px; color: var(--t-muted);
    background: color-mix(in srgb, var(--t-bg) 84%, transparent);
    -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
    box-shadow: 0 1px 0 var(--t-line);
}
:root[data-theme="light"] body:not(.sv-nocturne) .sv-topbar {
    --t-text: var(--sk-text-1); --t-muted: var(--sk-text-3);
    --t-line: var(--sk-border-2); --t-accent: var(--color-accent-600);
    --t-hover: rgba(15, 22, 40, .06);
}
@media (prefers-color-scheme: light) {
    :root:not([data-theme]) body:not(.sv-nocturne) .sv-topbar {
        --t-text: var(--sk-text-1); --t-muted: var(--sk-text-3);
        --t-line: var(--sk-border-2); --t-accent: var(--color-accent-600);
        --t-hover: rgba(15, 22, 40, .06);
    }
}
body.sv-nocturne .sv-topbar { --t-bg: var(--color-bg); }

.sv-crumbs { display: flex; align-items: center; gap: 14px; min-width: 0; flex: 0 1 auto; }
.sv-crumbs > i { flex: none; font-size: 13px; }
.sv-crumbs a { color: var(--t-muted) !important; text-decoration: none !important; white-space: nowrap; transition: color .2s; }
.sv-crumbs a:hover { color: var(--t-text) !important; }
.sv-crumbs__here { color: var(--t-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }

.sv-switch { position: relative; min-width: 0; }
.sv-switch__btn {
    display: flex; align-items: center; gap: 8px; max-width: 100%;
    height: 30px; padding: 0 10px; border: 0; border-radius: 8px;
    font-size: 13px; cursor: pointer;
    background: transparent; color: var(--t-text);
    box-shadow: inset 0 0 0 1px var(--t-line);
    transition: background .2s;
}
.sv-switch__btn:hover, .sv-switch__btn[aria-expanded="true"] { background: var(--t-hover); }
.sv-switch__btn > span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sv-switch__btn > i:first-child { color: var(--t-accent); }
.sv-switch__btn > i:last-child { color: var(--t-muted); }

/* When the bar is crowded (a long page title, the clock) it is the search box
   that gives way, down to its icon and a few letters.  It used to be fixed at
   280px, which left the New button's wrapper as the only thing that could
   shrink: the button then spilled out of it and sat on top of the bell. */
.sv-search {
    margin-left: auto; flex: 0 1 280px; min-width: 96px;
    display: flex; align-items: center; gap: 8px;
    height: 32px; padding: 0 10px; border: 0; border-radius: 8px;
    font-size: 13px; text-align: left; cursor: pointer;
    background: transparent; color: var(--t-muted);
    box-shadow: inset 0 0 0 1px var(--t-line);
    transition: background .2s, color .2s;
}
.sv-search:hover { background: var(--t-hover); color: var(--t-text); }
.sv-search > span { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sv-search > kbd { flex: none; }
.sv-scope kbd {
    margin-left: auto; flex: none; padding: 1px 6px; border-radius: 4px;
    font-size: 11px; line-height: 1.5; white-space: nowrap;
    background: transparent; color: inherit;
    box-shadow: inset 0 0 0 1px var(--t-line, var(--color-neutral-800));
}
.sv-topbar__icon {
    position: relative; flex: none; width: 32px; height: 32px; border-radius: 8px;
    display: grid; place-items: center; font-size: 18px;
    color: var(--t-muted) !important; text-decoration: none !important;
    transition: background .2s, color .2s;
}
.sv-topbar__icon:hover { background: var(--t-hover); color: var(--t-text) !important; }
.sv-topbar__me { display: none; }
.sv-clock { flex: none; margin-left: 4px; font-size: 13px; color: var(--t-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.sv-new { flex: none; }
.sv-new .sv-btn { height: 32px; font-size: 13px; padding-block: 0; white-space: nowrap; }
.sv-menu--right { left: auto; right: 0; }

/* ══ PANELS ════════════════════════════════════════════════════════════ */
.sv-menu, .sv-pop {
    z-index: 1046; padding: 6px; border-radius: 10px;
    display: flex; flex-direction: column; gap: 1px;
    background: var(--color-surface); color: var(--color-text);
    box-shadow: var(--shadow-md);
    animation: sv-fade .15s both;
}
.sv-menu { position: absolute; top: calc(100% + 8px); left: 0; min-width: 220px; max-height: 60vh; overflow: auto; }
.sv-pop { position: fixed; left: calc(var(--sv-side-w) + 8px); bottom: 12px; width: 250px; }
.sv-menu a, .sv-pop a, .sv-pop form button {
    display: flex; align-items: center; gap: 8px; width: 100%;
    padding: 7px 10px; border: 0; border-radius: 6px;
    font-size: 13px; text-align: left; cursor: pointer;
    background: transparent; color: var(--color-neutral-300) !important;
    text-decoration: none !important; transition: background .15s, color .15s;
}
.sv-menu a:hover, .sv-pop a:hover, .sv-pop form button:hover {
    background: color-mix(in srgb, var(--color-accent) 10%, transparent);
    color: var(--color-text) !important;
}
.sv-menu a.is-current { color: var(--color-accent-300) !important; }
.sv-menu a > i { color: var(--color-neutral-500); }
.sv-pop form { margin: 0; }
.sv-pop__who {
    display: flex; align-items: center; gap: 10px; padding: 8px 10px 10px;
    margin-bottom: 4px; box-shadow: 0 1px 0 var(--color-neutral-800);
}
.sv-pop__who > span:last-child { display: flex; flex-direction: column; min-width: 0; }
.sv-pop__who strong { font-size: 13px; font-weight: 500; }
.sv-pop__who small { font-size: 11.5px; color: var(--color-neutral-500); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sv-pop__row {
    display: flex; align-items: center; justify-content: space-between;
    padding: 5px 10px; font-size: 13px; color: var(--color-neutral-400);
}

.sv-flyout {
    position: fixed; left: 0; top: 0; bottom: 0; z-index: 1036;
    width: min(620px, 100vw);
    display: flex; flex-direction: column;
    background: linear-gradient(180deg, var(--color-surface), var(--color-bg));
    box-shadow: 1px 0 0 var(--color-neutral-800), 30px 0 70px rgba(0, 0, 0, .5);
    animation: sv-flyout .3s var(--sv-ease) both;
}
@keyframes sv-flyout { from { opacity: 0; transform: translateX(-16px); } to { opacity: 1; transform: none; } }
.sv-flyout__head {
    flex: none; height: var(--sv-top-h); padding: 0 16px 0 24px;
    display: flex; align-items: center; justify-content: space-between;
    font-size: 11px; letter-spacing: .16em; text-transform: uppercase;
    color: var(--color-accent-300); box-shadow: 0 1px 0 var(--color-neutral-800);
}
.sv-flyout__body { flex: 1; min-height: 0; overflow-y: auto; padding: 20px 24px 28px; }
/* The columns sit on an inner element of natural height.  On the scrolling
   box itself a fixed height makes multi-column layout grow sideways. */
.sv-flyout__cols { columns: 2 220px; column-gap: 28px; }
.sv-flyout__group { break-inside: avoid; margin-bottom: 22px; display: flex; flex-direction: column; }
.sv-flyout__group h6 {
    padding: 0 8px 6px; font-size: 10.5px; font-weight: 500;
    letter-spacing: .14em; text-transform: uppercase; color: var(--color-neutral-500);
}
.sv-flyout__group a {
    padding: 5px 8px; border-radius: 6px; font-size: 13.5px;
    color: var(--color-neutral-300) !important; text-decoration: none !important;
    transition: background .15s, color .15s;
}
.sv-flyout__group a.is-current { color: var(--color-accent-300) !important; }
.sv-flyout__group a:hover { background: color-mix(in srgb, var(--color-accent) 10%, transparent); color: var(--color-text) !important; }
/* Rows are flex, which would out-rank the hidden attribute the filter sets. */
.sv-flyout__group[hidden], .sv-flyout__group a[hidden], .sv-flyout__none[hidden] { display: none !important; }
.sv-flyout__group a { display: flex; align-items: center; gap: 10px; }
.sv-flyout__group a > i:first-child { flex: none; font-size: 17px; color: var(--color-neutral-400); }
.sv-flyout__group a > span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sv-flyout__group a > b {
    flex: none; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 10px; display: grid; place-items: center;
    font-size: 11px; font-weight: 600; background: oklch(var(--st-overdue) / .2); color: oklch(0.86 0.1 22);
}
.sv-flyout__group a > .ph-caret-right { flex: none; font-size: 13px; color: var(--color-neutral-600); }
.sv-flyout__find {
    flex: none; margin: 12px 24px 0; height: 40px; padding: 0 12px; border-radius: 12px;
    display: flex; align-items: center; gap: 8px; cursor: text;
    background: rgb(255 255 255 / .06); box-shadow: inset 0 0 0 1px rgb(255 255 255 / .12);
    transition: box-shadow .2s;
}
.sv-flyout__find:focus-within { box-shadow: inset 0 0 0 1px var(--color-accent), 0 0 0 4px color-mix(in srgb, var(--color-accent) 16%, transparent); }
.sv-flyout__find > i { flex: none; font-size: 16px; color: var(--color-neutral-400); }
/* html body: the admin skin dresses every search input (its own ground, ring
   and radius) with the same weight; the label is the field here, and the
   input inside it must be bare. */
html body .sv-flyout__find > input, html body .sv-flyout__find > input:focus {
    flex: 1; min-width: 0; height: 100%; padding: 0 !important; margin: 0; border: 0 !important; border-radius: 0 !important;
    outline: 0 !important; box-shadow: none !important; background: transparent !important;
    font: inherit; font-size: 14px; color: var(--color-text) !important; caret-color: var(--color-accent);
    -webkit-appearance: none; appearance: none;
}
.sv-flyout__find > input::placeholder { color: var(--color-neutral-500); }
.sv-flyout__find > input::-webkit-search-cancel-button { filter: invert(1) opacity(.6); }
.sv-flyout__none { margin: 8px 4px; font-size: 14px; color: var(--color-neutral-400); }

.sv-palette {
    position: fixed; inset: 0; z-index: 1060;
    display: grid; place-items: start center; padding: 14vh 16px 0;
    background: color-mix(in srgb, #000 45%, transparent);
    -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
    animation: sv-fade .15s both;
}
.sv-palette__box {
    width: min(580px, 100%); border-radius: var(--radius-lg); overflow: hidden;
    background: var(--color-surface); box-shadow: var(--shadow-lg);
    animation: sv-rise .3s var(--sv-ease) both;
}
.sv-palette__field {
    display: flex; align-items: center; gap: 10px; height: 50px; margin: 0; padding: 0 14px;
    font-size: 17px; color: var(--color-neutral-500);
    box-shadow: 0 1px 0 var(--color-neutral-800);
}
html body .sv-palette__field input {
    flex: 1; min-width: 0; height: 100%; padding: 0; border: 0 !important; outline: 0 !important;
    font-size: 15px; background: transparent !important; color: var(--color-text) !important;
    box-shadow: none !important; caret-color: var(--color-accent);
}
.sv-palette__field input::placeholder { color: var(--color-neutral-600); }
.sv-palette__field input::-webkit-search-cancel-button { display: none; }
.sv-palette__list { max-height: min(52vh, 420px); overflow-y: auto; padding: 6px; }
.sv-palette__group {
    padding: 8px 10px 4px; font-size: 10.5px; letter-spacing: .14em;
    text-transform: uppercase; color: var(--color-neutral-600);
}
.sv-palette__item {
    display: flex; align-items: center; gap: 10px; width: 100%;
    padding: 8px 10px; border: 0; border-radius: 8px;
    font-size: 13.5px; text-align: left; cursor: pointer;
    background: transparent; color: var(--color-neutral-300) !important;
    text-decoration: none !important;
}
.sv-palette__item > i { flex: none; font-size: 16px; color: var(--color-neutral-500); }
.sv-palette__item small { margin-left: auto; padding-left: 12px; font-size: 11.5px; color: var(--color-neutral-600); white-space: nowrap; }
.sv-palette__item.is-active {
    background: var(--color-accent-900); color: var(--color-accent-100) !important;
    box-shadow: inset 0 0 0 1px var(--color-accent-700);
}
.sv-palette__item.is-active > i { color: var(--color-accent-300); }
.sv-palette__empty { padding: 22px 12px; text-align: center; color: var(--color-neutral-500); }

/* ══ BUTTONS (Nocturne .btn, as .sv-btn) ═══════════════════════════════
   The primary is an accent outline, never a fill. */
.sv-btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    padding: var(--space-2) calc(var(--space-3) * 1.2);
    border: 1px solid transparent; border-radius: var(--radius-md);
    font-size: 14px; font-weight: var(--font-heading-weight); line-height: 1.2;
    cursor: pointer; text-decoration: none !important;
    background: transparent; color: var(--color-text) !important;
    transition: background .15s, border-color .15s, color .15s;
}
.sv-btn:disabled, .sv-btn[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }
.sv-btn-primary { color: var(--color-accent) !important; border-color: var(--color-accent); }
.sv-btn-primary:not(:disabled):hover { background: color-mix(in srgb, var(--color-accent) 12%, transparent); }
.sv-btn-primary:not(:disabled):active { background: color-mix(in srgb, var(--color-accent) 22%, transparent); }
.sv-btn-secondary { border-color: var(--color-divider); }
.sv-btn-secondary:not(:disabled):hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); }
.sv-btn-secondary:not(:disabled):active { background: color-mix(in srgb, var(--color-text) 14%, transparent); }
.sv-btn-ghost { color: var(--color-accent) !important; padding-inline: var(--space-1); }
.sv-btn-ghost:not(:disabled):hover { background: color-mix(in srgb, var(--color-accent) 10%, transparent); }
.sv-btn-ghost:not(:disabled):active { background: color-mix(in srgb, var(--color-accent) 18%, transparent); }
.sv-btn-icon { width: 36px; height: 36px; padding: 0; }
.sv-btn-block { width: 100%; }

/* ══ DIALOG (Nocturne .dialog, as .sv-dialog) ══════════════════════════ */
.sv-dialog-backdrop {
    position: fixed; inset: 0; z-index: 1058;
    display: grid; place-items: center; padding: var(--space-4);
    background: color-mix(in srgb, #000 50%, transparent);
    -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
    animation: sv-fade .15s both;
}
.sv-dialog {
    width: min(460px, 100%); display: flex; flex-direction: column; gap: var(--space-3);
    padding: var(--space-8); border-radius: var(--radius-lg);
    background: var(--color-surface); box-shadow: var(--shadow-lg);
    animation: sv-rise .3s var(--sv-ease) both;
}
.sv-dialog-title { font-size: 20px; font-weight: var(--font-heading-weight); line-height: 1.2; }
.sv-dialog-body { font-size: 14px; color: var(--color-neutral-300); }
.sv-dialog-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-2); }

/* ══ SMALL SHARED PIECES ═══════════════════════════════════════════════ */
.sv-kicker { font-size: 11px; letter-spacing: .16em; text-transform: uppercase; color: var(--color-accent-300); }

/* The SMS reminder confirm (sieve_shell.js, SieveRemind). */
.sv-sms { display: flex; flex-direction: column; gap: 10px; }
.sv-sms__to { display: flex; justify-content: space-between; gap: 12px; font-size: 13px; }
.sv-sms__to > span:first-child { color: var(--color-neutral-500); }
.sv-sms__msg {
    margin: 0; padding: 12px 14px; border-radius: 10px; font-size: 13.5px; line-height: 1.5;
    white-space: pre-wrap; color: var(--color-neutral-200);
    background: color-mix(in srgb, var(--color-bg) 60%, transparent);
    box-shadow: inset 0 0 0 1px var(--color-neutral-800);
}
.sv-sms__why { font-size: 13px; color: oklch(0.86 0.1 80); }
.sv-sms__why.is-error { color: oklch(0.84 0.1 22); }
/* ══ TOASTS ═══════════════════════════════════════════════════════════
   What the server has to say, once: templates/_toasts.html. Glass, top
   centre; the edge and the icon take the colour of what it is. One that is
   not an error leaves by itself (the line along its foot is how long it has
   left, and resting the pointer on it holds it); an error stays.

   Centred on the window, just under the top bar. They stood at the top
   right and covered the dashboard's date and clock; in the middle they
   clear it at any width from a laptop's up, and they stay off the top bar,
   whose search box and longer breadcrumbs reach the middle on a laptop. */
.sv-toasts {
    position: fixed; z-index: 1085; top: var(--sv-toast-top, 14px); left: 0; right: 0;
    padding: 0 16px; display: flex; flex-direction: column; align-items: center; gap: 10px;
    pointer-events: none;
}
body:has(.sv-topbar) { --sv-toast-top: calc(var(--sv-top-h) + 12px); }
.sv-toast {
    --tone: var(--st-due); --ink: oklch(0.88 0.08 289); --life: 7s;
    position: relative; pointer-events: auto; overflow: hidden; width: min(440px, 100%);
    display: grid; grid-template-columns: 34px minmax(0, 1fr) auto; gap: 12px; align-items: start;
    padding: 12px 12px 14px 12px; border-radius: 16px;
    background: var(--glass-strong); -webkit-backdrop-filter: var(--glass-blur-float); backdrop-filter: var(--glass-blur-float);
    box-shadow: inset 0 1px 0 rgb(255 255 255 / .22), inset 0 0 0 1px oklch(var(--tone) / .38),
                0 18px 44px -16px rgb(0 0 0 / .75), 0 0 34px -14px oklch(var(--tone) / .7);
    color: var(--color-text); font-size: 13.5px; line-height: 1.45;
    animation: sv-toast-in .5s var(--sv-ease) both, sv-toast-out .45s ease-in var(--life) forwards;
}
.sv-toast[data-level="success"] { --tone: var(--st-paid);    --ink: oklch(0.87 0.1 160); }
.sv-toast[data-level="warning"] { --tone: var(--st-partial); --ink: oklch(0.88 0.1 80); }
.sv-toast[data-level="error"]   { --tone: var(--st-overdue); --ink: oklch(0.86 0.1 22);
    animation: sv-toast-in .5s var(--sv-ease) both, sv-toast-nudge .45s ease .5s; }
.sv-toast:hover, .sv-toast:hover .sv-toast__life { animation-play-state: paused; }
.sv-toast__icon {
    width: 34px; height: 34px; border-radius: 11px; display: grid; place-items: center; color: var(--ink);
    background: oklch(var(--tone) / .16); box-shadow: inset 0 0 0 1px oklch(var(--tone) / .45);
}
.sv-toast__icon svg { width: 19px; height: 19px; }
.sv-toast__icon i { font-size: 19px; }
.sv-toast p { margin: 0; padding-top: 6px; min-width: 0; overflow-wrap: anywhere; color: var(--color-neutral-200); }
.sv-toast p a { color: var(--color-accent-300); }
.sv-toast__x {
    width: 28px; height: 28px; margin-top: 3px; border: 0; border-radius: 9px; display: none; place-items: center;
    background: none; color: var(--color-neutral-500); cursor: pointer; transition: background .15s, color .15s;
}
.sv-toast__x svg { width: 15px; height: 15px; }
.sv-toast__x:hover { background: rgb(255 255 255 / .09); color: var(--color-text); }
/* Shown only where the script that works it is on the page. */
.sv-toasts.is-live .sv-toast__x { display: grid; }
.sv-toast__life {
    position: absolute; left: 0; right: 0; bottom: 0; height: 2px; transform-origin: left;
    background: linear-gradient(90deg, oklch(var(--tone)), oklch(var(--tone) / .3));
    animation: sv-toast-life var(--life) linear forwards;
}
.sv-toast[data-level="error"] .sv-toast__life { display: none; }
.sv-toast.is-leaving { animation: sv-toast-out .3s ease-in forwards; }
@keyframes sv-toast-in    { from { opacity: 0; transform: translate3d(0, -18px, 0) scale(.96); } to { opacity: 1; transform: none; } }
@keyframes sv-toast-out   { to { opacity: 0; transform: translate3d(0, -12px, 0) scale(.96); visibility: hidden; } }
@keyframes sv-toast-life  { to { transform: scaleX(0); } }
@keyframes sv-toast-nudge { 0%, 100% { transform: none; } 25% { transform: translateX(-5px); } 55% { transform: translateX(4px); } 80% { transform: translateX(-2px); } }
@media (max-width: 767.98px) { .sv-toasts { padding: 0 12px; } .sv-toast { width: 100%; } }
@media (prefers-reduced-motion: reduce) {
    .sv-toast, .sv-toast[data-level="error"] { animation: sv-toast-out .01s linear var(--life) forwards; }
    .sv-toast[data-level="error"] { animation: none; }
}

/* ══ MOTION BETWEEN PAGES ══════════════════════════════════════════════
   Same-origin navigations cross-fade instead of flashing.  The sidebar and
   top bar are named, so they hold still while the page changes beneath
   them. */
@view-transition { navigation: auto; }
.sv-topbar { view-transition-name: sv-topbar; }
.app-main { view-transition-name: sv-main; }
::view-transition-old(sv-main) { animation: sv-leave .16s ease-in both; }
::view-transition-new(sv-main) { animation: sv-arrive .34s var(--sv-ease) both; }
@keyframes sv-leave  { to { opacity: 0; transform: translateY(-6px); } }
@keyframes sv-arrive { from { opacity: 0; transform: translateY(10px); } }

/* ══ NARROW: TAB BAR ═══════════════════════════════════════════════════
   Below a laptop the sidebar gives way to a tab bar along the bottom: the
   four daily destinations plus More, which opens the full list. */
@media (max-width: 991.98px) {
    .sv-side { display: none; }
    .sv-tabs {
        position: fixed; inset: auto 0 0 0; z-index: 1038;
        height: calc(58px + env(safe-area-inset-bottom, 0px)); padding: 0 4px env(safe-area-inset-bottom, 0px);
        display: flex; justify-content: space-around; align-items: center;
        background: color-mix(in srgb, var(--color-bg) 90%, transparent);
        -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
        box-shadow: 0 -1px 0 var(--color-neutral-800);
        view-transition-name: sv-tabs;
    }
    .sv-tabs__item {
        position: relative; min-width: 58px; height: 50px; padding: 0 6px; border: 0; border-radius: 10px;
        display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
        font-size: 21px; cursor: pointer; text-decoration: none !important;
        background: transparent; color: var(--color-neutral-500) !important; transition: color .2s, background .2s;
    }
    .sv-tabs__item > span:last-child { font-size: 10px; line-height: 1; }
    .sv-tabs__item:active, .sv-tabs__item[aria-expanded="true"] { background: var(--color-neutral-900); }
    .sv-tabs__item.is-active { color: var(--color-accent-300) !important; }
    .sv-tabs .sv-dot { top: 7px; right: 16px; }

    .app-main { padding-bottom: calc(66px + env(safe-area-inset-bottom, 0px)); }
    .app-footer { display: none; }
    .sv-topbar__me { display: grid; }
    /* The menu as a sheet rising from the tab bar.  Its own near-opaque
       ground, not the shell's glass: the lit background showed through and
       took the contrast out of every word on it. */
    .sv-flyout {
        top: auto; left: 0; right: 0; width: auto;
        bottom: calc(58px + env(safe-area-inset-bottom, 0px));
        max-height: calc(100vh - 58px - env(safe-area-inset-bottom, 0px) - 28px);
        max-height: calc(100dvh - 58px - env(safe-area-inset-bottom, 0px) - 28px);
        border-radius: 22px 22px 0 0;
        background: linear-gradient(180deg, rgb(28 31 52 / .98), rgb(13 15 28 / .99)) !important;
        box-shadow: inset 0 1px 0 rgb(255 255 255 / .18), 0 -28px 70px rgb(0 0 0 / .6);
        animation: sv-sheet .36s var(--sv-ease) both;
    }
    /* The handle a sheet is expected to have. */
    .sv-flyout::before {
        content: ""; flex: none; align-self: center; width: 38px; height: 4px; margin-top: 8px; border-radius: 2px;
        background: rgb(255 255 255 / .28);
    }
    .sv-flyout__head {
        height: 46px; padding: 0 10px 0 20px; box-shadow: none;
        font-size: 17px; font-weight: 600; letter-spacing: -.01em; text-transform: none; color: var(--color-text);
    }
    .sv-flyout__find { margin: 2px 16px 0; height: 44px; font-size: 16px; }
    html body .sv-flyout__find > input { font-size: 16px; }   /* 16px: iOS zooms the page for anything smaller */
    .sv-flyout__body { padding: 14px 16px 22px; overscroll-behavior: contain; }
    .sv-flyout__group {
        margin-bottom: 14px; padding: 4px; border-radius: 16px;
        background: rgb(255 255 255 / .05); box-shadow: inset 0 0 0 1px rgb(255 255 255 / .1);
    }
    .sv-flyout__group h6 {
        margin: 0; padding: 10px 12px 6px; font-size: 11px; font-weight: 600; letter-spacing: .12em;
        color: var(--color-accent-300);
    }
    .sv-flyout__group a {
        position: relative; min-height: 48px; padding: 0 10px; gap: 12px; border-radius: 12px;
        font-size: 15.5px; color: var(--color-text) !important;
    }
    /* A hairline between rows, inset to where the words begin. */
    .sv-flyout__group a + a::before {
        content: ""; position: absolute; top: 0; left: 54px; right: 10px; height: 1px; background: rgb(255 255 255 / .07);
    }
    .sv-flyout__group a > i:first-child {
        width: 32px; height: 32px; border-radius: 9px; display: grid; place-items: center; font-size: 18px;
        color: var(--color-accent-200); background: color-mix(in srgb, var(--color-accent) 16%, transparent);
        box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-accent) 30%, transparent);
    }
    .sv-flyout__group a > .ph-caret-right { color: var(--color-neutral-500); }
    .sv-flyout__group a:active { background: rgb(255 255 255 / .08); }
    .sv-flyout__group a.is-current {
        color: #fff !important; background: color-mix(in srgb, var(--color-accent) 22%, transparent);
        box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-accent) 55%, transparent);
    }
    .sv-flyout__group a.is-current::before, .sv-flyout__group a.is-current + a::before,
    .sv-flyout__group a.is-first::before { display: none; }
    .sv-flyout__group a.is-current > i:first-child { color: #fff; background: color-mix(in srgb, var(--color-accent) 45%, transparent); }
    .sv-pop { left: auto; right: 12px; top: calc(var(--sv-top-h) + 6px); bottom: auto; }
}
@media (max-width: 767.98px) {
    .sv-topbar { padding: 0 14px; gap: 8px; }
    .sv-crumbs { gap: 8px; flex: 1 1 auto; }
    .sv-crumbs > a, .sv-crumbs > a + i { display: none; }
    .sv-clock { display: none; }
    .sv-search { flex: none; min-width: 0; width: 32px; padding: 0; justify-content: center; box-shadow: none; font-size: 18px; }
    .sv-search > span, .sv-search > kbd { display: none; }
    .sv-new .sv-btn > span { display: none; }
    .sv-new .sv-btn { width: 32px; padding: 0; }
    .sv-flyout__cols { columns: 1; }
    .sv-palette { padding-top: 8vh; }
}
@keyframes sv-sheet { from { opacity: 0; transform: translateY(32px); } }
/* The sheet is the phone's menu.  At laptop width the sidebar is the menu and
   there is no tab bar to have opened this from: if it is open when the window
   widens (a tablet turned on its side, a window dragged wider) it must not be
   left on the page, half under the sidebar.  The script closes it as well. */
@media (min-width: 992px) {
    .sv-flyout { display: none !important; }
}
/* A wide phone on its side, or a small tablet: two columns of cards. */
@media (min-width: 640px) and (max-width: 991.98px) {
    .sv-flyout__cols { columns: 2 280px; column-gap: 14px; }
}

/* Entrances land on their end state and the loops stop.  (Shortening the
   duration instead would turn every infinite animation into a strobe.) */
@media (prefers-reduced-motion: reduce) {
    .sv-scope, .sv-scope *, .sv-scope *::before, .sv-scope *::after {
        animation: none !important; transition-duration: .01ms !important;
    }
}

/* ══ LIQUID GLASS ══════════════════════════════════════════════════════
   The ground is one fixed field of soft light; nothing between it and a
   pane of glass may be opaque, or the glass has nothing to show.  Page
   stylesheets paint their cards with var(--glass) and var(--shadow-sm); the
   shell's own surfaces are set here. */
/* The light is painted once, on its own layer pinned to the window. As a
   fixed background it was repainted on every scroll frame, and each pane
   of glass re-blurred that repaint, so the panes flickered. */
html body { background: var(--color-bg) !important; }
html body::before { content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none; background: var(--ambient); }
html body .app-wrapper, html body .app-main, html body .app-content { background: transparent !important; }

.sv-side {
    background: linear-gradient(180deg, rgb(255 255 255 / .085), rgb(255 255 255 / .03));
    -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
    box-shadow: inset -1px 0 0 rgb(255 255 255 / .12), 12px 0 40px -20px rgb(0 0 0 / .6);
}
.sv-topbar {
    background: linear-gradient(180deg, rgb(255 255 255 / .075), rgb(255 255 255 / .025)) !important;
    -webkit-backdrop-filter: var(--glass-blur-float); backdrop-filter: var(--glass-blur-float);
    box-shadow: inset 0 -1px 0 rgb(255 255 255 / .12);
}
.sv-tabs { background: linear-gradient(180deg, rgb(255 255 255 / .1), rgb(255 255 255 / .04)), rgb(9 11 22 / .6) !important; }
.sv-menu, .sv-pop, .sv-flyout, .sv-palette__box, .sv-dialog, .sv-toast {
    background: var(--glass-strong) !important;
    -webkit-backdrop-filter: var(--glass-blur-float); backdrop-filter: var(--glass-blur-float);
}
@media (max-width: 991.98px) {
    html body .sv-flyout { background: linear-gradient(180deg, rgb(28 31 52 / .98), rgb(13 15 28 / .99)) !important; }
}
.sv-menu, .sv-pop { box-shadow: var(--shadow-md); border-radius: 16px; }
.sv-side__item { border-radius: 10px; }
.sv-side__item.is-active { box-shadow: inset 0 1px 0 rgb(255 255 255 / .16), inset 0 0 0 1px rgb(165 152 255 / .3); }
.sv-side__user, .sv-search { border-radius: 12px; }

/* Controls are capsules: a bead of glass, lit along its top edge. */
.sv-btn { border-radius: 999px; padding-inline: 14px; }
.sv-btn-icon { padding-inline: 0; }
.sv-btn-primary {
    background: linear-gradient(180deg, rgb(165 152 255 / .26), rgb(165 152 255 / .12));
    border-color: rgb(165 152 255 / .6); color: #fff !important;
    box-shadow: inset 0 1px 0 rgb(255 255 255 / .28), 0 6px 18px -8px rgb(140 120 255 / .7);
}
.sv-btn-primary:not(:disabled):hover { background: linear-gradient(180deg, rgb(165 152 255 / .4), rgb(165 152 255 / .2)); }
.sv-btn-secondary { background: rgb(255 255 255 / .07); box-shadow: inset 0 1px 0 rgb(255 255 255 / .18); }
.sv-btn-ghost { color: var(--color-accent-400) !important; }

@media (prefers-reduced-transparency: reduce) {
    :root { --glass: linear-gradient(180deg, #1c2036, #151829); --glass-strong: linear-gradient(180deg, #232744, #1a1e34); --glass-blur: none; --glass-blur-float: none; }
}
